Merge "Increasing GPS_LOCK default privacy level"
diff --git a/Android.bp b/Android.bp
index d94bd84..93e6963 100644
--- a/Android.bp
+++ b/Android.bp
@@ -103,6 +103,8 @@
         "core/java/android/app/backup/IRestoreObserver.aidl",
         "core/java/android/app/backup/IRestoreSession.aidl",
         "core/java/android/app/backup/ISelectBackupTransportCallback.aidl",
+        "core/java/android/app/role/IRoleManager.aidl",
+        "core/java/android/app/role/IRoleManagerCallback.aidl",
         "core/java/android/app/slice/ISliceManager.aidl",
         "core/java/android/app/slice/ISliceListener.aidl",
         "core/java/android/app/timedetector/ITimeDetectorService.aidl",
@@ -148,6 +150,8 @@
         "core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl",
         "core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl",
         "core/java/android/database/IContentObserver.aidl",
+        "core/java/android/debug/IAdbManager.aidl",
+        "core/java/android/debug/IAdbTransport.aidl",
         ":libcamera_client_aidl",
         ":libcamera_client_framework_aidl",
         "core/java/android/hardware/IConsumerIrService.aidl",
@@ -177,6 +181,7 @@
         "core/java/android/hardware/input/IInputManager.aidl",
         "core/java/android/hardware/input/IInputDevicesChangedListener.aidl",
         "core/java/android/hardware/input/ITabletModeChangedListener.aidl",
+        "core/java/android/hardware/iris/IIrisService.aidl",
         "core/java/android/hardware/location/IActivityRecognitionHardware.aidl",
         "core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl",
         "core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl",
@@ -233,8 +238,7 @@
         "core/java/android/os/IDeviceIdentifiersPolicyService.aidl",
         "core/java/android/os/IDeviceIdleController.aidl",
         "core/java/android/os/IHardwarePropertiesManager.aidl",
-        "core/java/android/os/IIncidentManager.aidl",
-        "core/java/android/os/IIncidentReportStatusListener.aidl",
+        ":libincident_aidl",
         "core/java/android/os/IMaintenanceActivityListener.aidl",
         "core/java/android/os/IMessenger.aidl",
         "core/java/android/os/INetworkActivityListener.aidl",
@@ -247,8 +251,7 @@
         "core/java/android/os/IRecoverySystemProgressListener.aidl",
         "core/java/android/os/IRemoteCallback.aidl",
         "core/java/android/os/ISchedulingPolicyService.aidl",
-        "core/java/android/os/IStatsCompanionService.aidl",
-        "core/java/android/os/IStatsManager.aidl",
+        ":statsd_aidl",
         "core/java/android/os/ISystemUpdateManager.aidl",
         "core/java/android/os/IThermalEventListener.aidl",
         "core/java/android/os/IThermalService.aidl",
@@ -259,6 +262,7 @@
         "core/java/android/os/storage/IStorageEventListener.aidl",
         "core/java/android/os/storage/IStorageShutdownObserver.aidl",
         "core/java/android/os/storage/IObbActionListener.aidl",
+        "core/java/android/rolecontrollerservice/IRoleControllerService.aidl",
         ":keystore_aidl",
         "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
         "core/java/android/service/autofill/IAutoFillService.aidl",
@@ -283,6 +287,8 @@
         "core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl",
         "core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl",
         "core/java/android/service/gatekeeper/IGateKeeperService.aidl",
+        "core/java/android/service/intelligence/IIntelligenceService.aidl",
+
         "core/java/android/service/notification/INotificationListener.aidl",
         "core/java/android/service/notification/IStatusBarNotificationHolder.aidl",
         "core/java/android/service/notification/IConditionListener.aidl",
@@ -339,6 +345,7 @@
         "core/java/android/view/autofill/IAutoFillManager.aidl",
         "core/java/android/view/autofill/IAutoFillManagerClient.aidl",
         "core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
+        "core/java/android/view/intelligence/IIntelligenceManager.aidl",
         "core/java/android/view/IApplicationToken.aidl",
         "core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl",
         "core/java/android/view/IDockedStackListener.aidl",
@@ -591,6 +598,8 @@
         "telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl",
         "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl",
         "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl",
+        "wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl",
+        "wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl",
         "wifi/java/android/net/wifi/ISoftApCallback.aidl",
         "wifi/java/android/net/wifi/ITrafficStateCallback.aidl",
         "wifi/java/android/net/wifi/IWifiManager.aidl",
@@ -689,6 +698,7 @@
     ],
 
     static_libs: [
+        "apex_aidl_interface-java",
         "framework-protos",
         "mediaplayer2-protos",
         "android.hidl.base-V1.0-java",
@@ -696,6 +706,8 @@
         "android.hardware.contexthub-V1.0-java",
         "android.hardware.health-V1.0-java-constants",
         "android.hardware.thermal-V1.0-java-constants",
+        "android.hardware.thermal-V1.1-java",
+        "android.hardware.thermal-V2.0-java",
         "android.hardware.tv.input-V1.0-java-constants",
         "android.hardware.usb-V1.0-java-constants",
         "android.hardware.usb-V1.1-java-constants",
@@ -721,6 +733,22 @@
 
 }
 
+filegroup {
+    name: "libincident_aidl",
+    srcs: [
+        "core/java/android/os/IIncidentManager.aidl",
+        "core/java/android/os/IIncidentReportStatusListener.aidl",
+    ],
+}
+
+filegroup {
+    name: "statsd_aidl",
+    srcs: [
+        "core/java/android/os/IStatsCompanionService.aidl",
+        "core/java/android/os/IStatsManager.aidl",
+    ],
+}
+
 java_library {
     name: "framework",
     defaults: ["framework-defaults"],
@@ -738,6 +766,16 @@
     ],
 }
 
+// A host library containing the inspector annotations for inspector-annotation-processor.
+java_library_host {
+    name: "inspector-annotation",
+    srcs: [
+        "core/java/android/view/inspector/InspectableChildren.java",
+        "core/java/android/view/inspector/InspectableNodeName.java",
+        "core/java/android/view/inspector/InspectableProperty.java",
+    ],
+}
+
 // A host library including just UnsupportedAppUsage.java so that the annotation
 // processor can also use this annotation.
 java_library_host {
@@ -931,12 +969,14 @@
         "core/proto/android/os/batterytype.proto",
         "core/proto/android/os/cpufreq.proto",
         "core/proto/android/os/cpuinfo.proto",
+        "core/proto/android/os/data.proto",
         "core/proto/android/os/kernelwake.proto",
         "core/proto/android/os/pagetypeinfo.proto",
         "core/proto/android/os/procrank.proto",
         "core/proto/android/os/ps.proto",
         "core/proto/android/os/system_properties.proto",
         "core/proto/android/util/event_log_tags.proto",
+        "core/proto/android/util/log.proto",
     ],
 
     // Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool
@@ -1546,6 +1586,10 @@
 droidstubs {
     name: "hiddenapi-mappings",
     defaults: ["metalava-api-stubs-default"],
+    srcs: [
+        ":openjdk_java_files",
+        ":non_openjdk_java_files",
+    ],
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 5c21221..ec3366c 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,20 +1,5 @@
 [Hook Scripts]
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
-                  -fw core/
-                      graphics/java/android
-                      packages/PrintRecommendationService/
-                      packages/PrintSpooler/
-                      packages/PackageInstaller/
-                      packages/SystemUI/
-                      services/print/
-                      services/usb/
-                      telephony/
-                      tests/ActivityViewTest/
-                      tests/LotsOfApps/
-                      tests/NativeProcessesMemoryTest/
-                      tests/OdmApps/
-                      tests/SystemMemoryTest/
-                      wifi/
 
 api_lint_hook = ${REPO_ROOT}/frameworks/base/tools/apilint/apilint_sha.sh ${PREUPLOAD_COMMIT}
 
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
index d18aa51..62dd124 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
@@ -17,7 +17,6 @@
 package android.graphics.perftests;
 
 import android.graphics.Outline;
-import android.graphics.RecordingCanvas;
 import android.graphics.RenderNode;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
@@ -62,7 +61,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         RenderNode node = RenderNode.create("LinearLayout", null);
         while (state.keepRunning()) {
-            node.isValid();
+            node.hasDisplayList();
         }
     }
 
@@ -71,8 +70,8 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         RenderNode node = RenderNode.create("LinearLayout", null);
         while (state.keepRunning()) {
-            RecordingCanvas canvas = node.start(100, 100);
-            node.end(canvas);
+            node.startRecording(100, 100);
+            node.endRecording();
         }
     }
 
@@ -80,17 +79,16 @@
     public void testStartEndDeepHierarchy() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         RenderNode[] nodes = new RenderNode[30];
-        RecordingCanvas[] canvases = new RecordingCanvas[nodes.length];
         for (int i = 0; i < nodes.length; i++) {
             nodes[i] = RenderNode.create("LinearLayout", null);
         }
 
         while (state.keepRunning()) {
             for (int i = 0; i < nodes.length; i++) {
-                canvases[i] = nodes[i].start(100, 100);
+                nodes[i].startRecording(100, 100);
             }
             for (int i = nodes.length - 1; i >= 0; i--) {
-                nodes[i].end(canvases[i]);
+                nodes[i].endRecording();
             }
         }
     }
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index d8d4a6e..5be0cb0 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -38,6 +38,23 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class BinderCallsStatsPerfTest {
+    private static final int DEFAULT_BUCKET_SIZE = 1000;
+    static class FakeCpuTimeBinderCallsStats extends BinderCallsStats {
+        private int mTimeMs;
+
+        FakeCpuTimeBinderCallsStats() {
+            super(new BinderCallsStats.Injector());
+            setDeviceState(new CachedDeviceState(false, false).getReadonlyClient());
+        }
+
+        protected long getThreadTimeMicro() {
+            return mTimeMs++;
+        }
+
+        protected long getElapsedRealtimeMicro() {
+            return mTimeMs++;
+        }
+    }
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -57,28 +74,50 @@
     @Test
     public void timeCallSession() {
         mBinderCallsStats.setDetailedTracking(true);
-        runScenario();
+        runScenario(DEFAULT_BUCKET_SIZE);
     }
 
     @Test
     public void timeCallSessionOnePercentSampling() {
         mBinderCallsStats.setDetailedTracking(false);
         mBinderCallsStats.setSamplingInterval(100);
-        runScenario();
+        runScenario(DEFAULT_BUCKET_SIZE);
     }
 
     @Test
     public void timeCallSessionTrackingDisabled() {
         mBinderCallsStats.setDetailedTracking(false);
-        runScenario();
+        runScenario(DEFAULT_BUCKET_SIZE);
     }
 
-    private void runScenario() {
+    @Test
+    public void timeCallSession_1000_buckets_cpuNotRecorded() {
+        mBinderCallsStats = new FakeCpuTimeBinderCallsStats();
+        mBinderCallsStats.setSamplingInterval(1);
+        runScenario(/* max bucket size */ 1000);
+    }
+
+    @Test
+    public void timeCallSession_500_buckets_cpuNotRecorded() {
+        mBinderCallsStats = new FakeCpuTimeBinderCallsStats();
+        mBinderCallsStats.setSamplingInterval(1);
+        runScenario(/* max bucket size */ 500);
+    }
+
+    @Test
+    public void timeCallSession_100_buckets_cpuNotRecorded() {
+        mBinderCallsStats = new FakeCpuTimeBinderCallsStats();
+        mBinderCallsStats.setSamplingInterval(1);
+        runScenario(/* max bucket size */ 100);
+    }
+
+    // There will be a warmup time of maxBucketSize to initialize the map of CallStat.
+    private void runScenario(int maxBucketSize) {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         Binder b = new Binder();
         while (state.keepRunning()) {
-            for (int i = 0; i < 1000; i++) {
-                CallSession s = mBinderCallsStats.callStarted(b, i % 100);
+            for (int i = 0; i < 10000; i++) {
+                CallSession s = mBinderCallsStats.callStarted(b, i % maxBucketSize);
                 mBinderCallsStats.callEnded(s, 0, 0);
             }
         }
diff --git a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
new file mode 100644
index 0000000..9034034
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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 android.os;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.KernelCpuThreadReader;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Performance tests collecting per-thread CPU data.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class KernelCpuThreadReaderPerfTest {
+    @Rule
+    public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private final KernelCpuThreadReader mKernelCpuThreadReader = KernelCpuThreadReader.create();
+
+    @Test
+    public void timeReadCurrentProcessCpuUsage() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        assertNotNull(mKernelCpuThreadReader);
+        while (state.keepRunning()) {
+            this.mKernelCpuThreadReader.getCurrentProcessCpuUsage();
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java b/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
new file mode 100644
index 0000000..ad9fb5f
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 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 android.text;
+
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class CanvasDrawTextTest {
+    private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
+
+    private static final TextPaint PAINT = new TextPaint();
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private TextPerfUtils mTextUtil = new TextPerfUtils();
+
+    @Before
+    public void setUp() {
+        mTextUtil.resetRandom(0 /* seed */);
+    }
+
+    @Test
+    public void drawText_LongText_SmallWindow() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final String text = mTextUtil.nextRandomParagraph(
+                WORD_LENGTH, 4 * 1024 * 1024 /* 4mb text */).toString();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        final RenderNode child = RenderNode.create("child", null);
+        child.setLeftTopRightBottom(50, 50, 100, 100);
+
+        RecordingCanvas canvas = node.start(100, 100);
+        node.end(canvas);
+        canvas = child.start(50, 50);
+        child.end(canvas);
+
+        final Random r = new Random(0);
+
+        while (state.keepRunning()) {
+            int start = r.nextInt(text.length() - 100);
+            canvas.drawText(text, start, start + 100, 0, 0, PAINT);
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index e224fa3..35d3802 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -245,10 +245,11 @@
             state.pauseTiming();
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -261,10 +262,11 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -277,10 +279,11 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -293,11 +296,12 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -310,11 +314,12 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -328,10 +333,11 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -345,10 +351,11 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -362,11 +369,12 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -380,11 +388,12 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
diff --git a/apct-tests/perftests/core/src/android/text/TextPerfUtils.java b/apct-tests/perftests/core/src/android/text/TextPerfUtils.java
index 22e516a..2a98ebf 100644
--- a/apct-tests/perftests/core/src/android/text/TextPerfUtils.java
+++ b/apct-tests/perftests/core/src/android/text/TextPerfUtils.java
@@ -65,18 +65,23 @@
     }
 
     public CharSequence nextRandomParagraph(int wordLen, boolean applyRandomStyle, String setStr) {
-        return nextRandomParagraph(wordLen, applyRandomStyle, UnicodeSetToArray(setStr));
+        return nextRandomParagraph(wordLen, PARA_LENGTH, applyRandomStyle,
+                UnicodeSetToArray(setStr));
     }
 
     public CharSequence nextRandomParagraph(int wordLen, boolean applyRandomStyle) {
-        return nextRandomParagraph(wordLen, applyRandomStyle, ALPHABET);
+        return nextRandomParagraph(wordLen, PARA_LENGTH, applyRandomStyle, ALPHABET);
     }
 
-    public CharSequence nextRandomParagraph(int wordLen, boolean applyRandomStyle,
+    public CharSequence nextRandomParagraph(int wordLen, int paraLength) {
+        return nextRandomParagraph(wordLen, paraLength, false /* no style */, ALPHABET);
+    }
+
+    public CharSequence nextRandomParagraph(int wordLen, int paraLength, boolean applyRandomStyle,
             String[] charSet) {
         ArrayList<Character> chars = new ArrayList<>();
         ArrayList<Integer> wordOffsets = new ArrayList<>();
-        for (int i = 0; i < PARA_LENGTH; i++) {
+        for (int i = 0; i < paraLength; i++) {
             if (i % (wordLen + 1) == wordLen) {
                 chars.add(' ');
                 wordOffsets.add(chars.size());
diff --git a/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java b/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
new file mode 100644
index 0000000..0c1f289
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2018 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 android.util;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Predicate;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ArraySetPerfTest {
+    private static final int NUM_ITERATIONS = 100;
+    private static final int SET_SIZE_SMALL = 10;
+    private static final int SET_SIZE_LARGE = 50;
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void testValueAt_InBounds() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        ArraySet<Integer> set = new ArraySet<>();
+        set.add(0);
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                set.valueAt(0);
+            }
+        }
+    }
+
+    @Test
+    public void testValueAt_OutOfBounds_Negative() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        ArraySet<Integer> set = new ArraySet<>();
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                try {
+                    set.valueAt(-1);
+                } catch (ArrayIndexOutOfBoundsException expected) {
+                    // expected
+                }
+            }
+        }
+    }
+
+    /**
+     * Tests the case where ArraySet could index into its array even though the index is out of
+     * bounds.
+     */
+    @Test
+    public void testValueAt_OutOfBounds_EdgeCase() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        ArraySet<Integer> set = new ArraySet<>();
+        set.add(0);
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                try {
+                    set.valueAt(1);
+                } catch (ArrayIndexOutOfBoundsException expected) {
+                    // expected
+                }
+            }
+        }
+    }
+
+    /**
+     * This is the same code as testRemoveIf_Small_* without the removeIf in order to measure
+     * the performance of the rest of the code in the loop.
+     */
+    @Test
+    public void testRemoveIf_Small_Base() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Predicate<Integer> predicate = (i) -> i % 2 == 0;
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                ArraySet<Integer> set = new ArraySet<>();
+                for (int j = 0; j < SET_SIZE_SMALL; ++j) {
+                    set.add(j);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testRemoveIf_Small_RemoveNothing() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Predicate<Integer> predicate = (i) -> false;
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                ArraySet<Integer> set = new ArraySet<>();
+                for (int j = 0; j < SET_SIZE_SMALL; ++j) {
+                    set.add(j);
+                }
+                set.removeIf(predicate);
+            }
+        }
+    }
+
+    @Test
+    public void testRemoveIf_Small_RemoveAll() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Predicate<Integer> predicate = (i) -> true;
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                ArraySet<Integer> set = new ArraySet<>();
+                for (int j = 0; j < SET_SIZE_SMALL; j++) {
+                    set.add(j);
+                }
+                set.removeIf(predicate);
+            }
+        }
+    }
+
+    @Test
+    public void testRemoveIf_Small_RemoveHalf() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Predicate<Integer> predicate = (i) -> i % 2 == 0;
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                ArraySet<Integer> set = new ArraySet<>();
+                for (int j = 0; j < SET_SIZE_SMALL; ++j) {
+                    set.add(j);
+                }
+                set.removeIf(predicate);
+            }
+        }
+    }
+
+    /**
+     * This is the same code as testRemoveIf_Large_* without the removeIf in order to measure
+     * the performance of the rest of the code in the loop.
+     */
+    @Test
+    public void testRemoveIf_Large_Base() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Predicate<Integer> predicate = (i) -> i % 2 == 0;
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                ArraySet<Integer> set = new ArraySet<>();
+                for (int j = 0; j < SET_SIZE_LARGE; ++j) {
+                    set.add(j);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testRemoveIf_Large_RemoveNothing() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Predicate<Integer> predicate = (i) -> false;
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                ArraySet<Integer> set = new ArraySet<>();
+                for (int j = 0; j < SET_SIZE_LARGE; ++j) {
+                    set.add(j);
+                }
+                set.removeIf(predicate);
+            }
+        }
+    }
+
+    @Test
+    public void testRemoveIf_Large_RemoveAll() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Predicate<Integer> predicate = (i) -> true;
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                ArraySet<Integer> set = new ArraySet<>();
+                for (int j = 0; j < SET_SIZE_LARGE; ++j) {
+                    set.add(j);
+                }
+                set.removeIf(predicate);
+            }
+        }
+    }
+
+    @Test
+    public void testRemoveIf_Large_RemoveHalf() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Predicate<Integer> predicate = (i) -> i % 2 == 0;
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                ArraySet<Integer> set = new ArraySet<>();
+                for (int j = 0; j < SET_SIZE_LARGE; ++j) {
+                    set.add(j);
+                }
+                set.removeIf(predicate);
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
index 6159da4..dc4d4bd 100644
--- a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
@@ -155,7 +155,7 @@
     }
 
     private void updateAndValidateDisplayList(View view) {
-        boolean hasDisplayList = view.updateDisplayListIfDirty().isValid();
+        boolean hasDisplayList = view.updateDisplayListIfDirty().hasDisplayList();
         assertTrue(hasDisplayList);
     }
 
diff --git a/api/current.txt b/api/current.txt
index 870c020..dc383fd 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -769,6 +769,7 @@
     field public static final int insetRight = 16843192; // 0x10101b8
     field public static final int insetTop = 16843193; // 0x10101b9
     field public static final int installLocation = 16843447; // 0x10102b7
+    field public static final int interactiveUiTimeout = 16844181; // 0x1010595
     field public static final int interpolator = 16843073; // 0x1010141
     field public static final int isAlwaysSyncable = 16843571; // 0x1010333
     field public static final int isAsciiCapable = 16843753; // 0x10103e9
@@ -941,7 +942,6 @@
     field public static final int minSdkVersion = 16843276; // 0x101020c
     field public static final int minWidth = 16843071; // 0x101013f
     field public static final int minimumHorizontalAngle = 16843901; // 0x101047d
-    field public static final int minimumUiTimeout = 16844175; // 0x101058f
     field public static final int minimumVerticalAngle = 16843902; // 0x101047e
     field public static final int mipMap = 16843725; // 0x10103cd
     field public static final int mirrorForRtl = 16843726; // 0x10103ce
@@ -965,6 +965,7 @@
     field public static final int nextFocusRight = 16842978; // 0x10100e2
     field public static final int nextFocusUp = 16842979; // 0x10100e3
     field public static final int noHistory = 16843309; // 0x101022d
+    field public static final int nonInteractiveUiTimeout = 16844175; // 0x101058f
     field public static final int normalScreens = 16843397; // 0x1010285
     field public static final int notificationTimeout = 16843651; // 0x1010383
     field public static final int numColumns = 16843032; // 0x1010118
@@ -1209,6 +1210,7 @@
     field public static final int shareInterpolator = 16843195; // 0x10101bb
     field public static final int sharedUserId = 16842763; // 0x101000b
     field public static final int sharedUserLabel = 16843361; // 0x1010261
+    field public static final int shell = 16844180; // 0x1010594
     field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
     field public static final int shortcutId = 16844072; // 0x1010528
     field public static final int shortcutLongLabel = 16844074; // 0x101052a
@@ -2885,12 +2887,14 @@
     method public int getCapabilities();
     method public deprecated java.lang.String getDescription();
     method public java.lang.String getId();
-    method public int getMinimumUiTimeoutMillis();
+    method public int getInteractiveUiTimeoutMillis();
+    method public int getNonInteractiveUiTimeoutMillis();
     method public android.content.pm.ResolveInfo getResolveInfo();
     method public java.lang.String getSettingsActivityName();
     method public java.lang.String loadDescription(android.content.pm.PackageManager);
     method public java.lang.CharSequence loadSummary(android.content.pm.PackageManager);
-    method public void setMinimumUiTimeoutMillis(int);
+    method public void setInteractiveUiTimeoutMillis(int);
+    method public void setNonInteractiveUiTimeoutMillis(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
     field public static final int CAPABILITY_CAN_PERFORM_GESTURES = 32; // 0x20
@@ -5217,6 +5221,7 @@
     ctor public Notification(android.os.Parcel);
     method public android.app.Notification clone();
     method public int describeContents();
+    method public android.app.PendingIntent getAppOverlayIntent();
     method public int getBadgeIconType();
     method public java.lang.String getChannelId();
     method public java.lang.String getGroup();
@@ -5446,6 +5451,7 @@
     method public android.app.Notification.Style getStyle();
     method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
+    method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setBadgeIconType(int);
     method public android.app.Notification.Builder setCategory(java.lang.String);
@@ -5663,6 +5669,7 @@
   public final class NotificationChannel implements android.os.Parcelable {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
     method public boolean canBypassDnd();
+    method public boolean canOverlayApps();
     method public boolean canShowBadge();
     method public int describeContents();
     method public void enableLights(boolean);
@@ -5678,6 +5685,7 @@
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
     method public boolean hasUserSetImportance();
+    method public void setAllowAppOverlay(boolean);
     method public void setBypassDnd(boolean);
     method public void setDescription(java.lang.String);
     method public void setGroup(java.lang.String);
@@ -5697,6 +5705,7 @@
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
     ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
+    method public boolean canOverlayApps();
     method public android.app.NotificationChannelGroup clone();
     method public int describeContents();
     method public java.util.List<android.app.NotificationChannel> getChannels();
@@ -5704,6 +5713,7 @@
     method public java.lang.String getId();
     method public java.lang.CharSequence getName();
     method public boolean isBlocked();
+    method public void setAllowAppOverlay(boolean);
     method public void setDescription(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
@@ -7281,6 +7291,18 @@
 
 }
 
+package android.app.role {
+
+  public final class RoleManager {
+    method public android.content.Intent createRequestRoleIntent(java.lang.String);
+    method public boolean isRoleAvailable(java.lang.String);
+    method public boolean isRoleHeld(java.lang.String);
+    field public static final java.lang.String ROLE_DIALER = "android.app.role.DIALER";
+    field public static final java.lang.String ROLE_SMS = "android.app.role.SMS";
+  }
+
+}
+
 package android.app.slice {
 
   public final class Slice implements android.os.Parcelable {
@@ -9625,6 +9647,7 @@
     field public static final java.lang.String PRINT_SERVICE = "print";
     field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1
     field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions";
+    field public static final java.lang.String ROLE_SERVICE = "role";
     field public static final java.lang.String SEARCH_SERVICE = "search";
     field public static final java.lang.String SENSOR_SERVICE = "sensor";
     field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
@@ -10814,6 +10837,7 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     method public static java.lang.CharSequence getCategoryTitle(android.content.Context, int);
+    method public boolean isProfileableByShell();
     method public boolean isVirtualPreload();
     method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager);
     field public static final int CATEGORY_AUDIO = 1; // 0x1
@@ -11155,12 +11179,17 @@
 
   public static class PackageInstaller.Session implements java.io.Closeable {
     method public void abandon();
+    method public void addChildSessionId(int);
     method public void close();
     method public void commit(android.content.IntentSender);
     method public void fsync(java.io.OutputStream) throws java.io.IOException;
+    method public int[] getChildSessionIds();
     method public java.lang.String[] getNames() throws java.io.IOException;
+    method public int getParentSessionId();
+    method public boolean isMultiPackage();
     method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
     method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
+    method public void removeChildSessionId(int);
     method public void removeSplit(java.lang.String) throws java.io.IOException;
     method public void setStagingProgress(float);
     method public void transfer(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -11181,20 +11210,24 @@
     method public android.graphics.Bitmap getAppIcon();
     method public java.lang.CharSequence getAppLabel();
     method public java.lang.String getAppPackageName();
+    method public int[] getChildSessionIds();
     method public int getInstallLocation();
     method public int getInstallReason();
     method public java.lang.String getInstallerPackageName();
     method public int getMode();
     method public int getOriginatingUid();
     method public android.net.Uri getOriginatingUri();
+    method public int getParentSessionId();
     method public float getProgress();
     method public android.net.Uri getReferrerUri();
     method public int getSessionId();
     method public long getSize();
     method public boolean isActive();
+    method public boolean isMultiPackage();
     method public boolean isSealed();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
+    field public static final int INVALID_ID = -1; // 0xffffffff
   }
 
   public static class PackageInstaller.SessionParams implements android.os.Parcelable {
@@ -11205,6 +11238,7 @@
     method public void setAppPackageName(java.lang.String);
     method public void setInstallLocation(int);
     method public void setInstallReason(int);
+    method public void setMultiPackage();
     method public void setOriginatingUid(int);
     method public void setOriginatingUri(android.net.Uri);
     method public void setReferrerUri(android.net.Uri);
@@ -13413,6 +13447,7 @@
     method public boolean clipRect(float, float, float, float);
     method public boolean clipRect(int, int, int, int);
     method public void concat(android.graphics.Matrix);
+    method public void disableZ();
     method public void drawARGB(int, int, int, int);
     method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint);
     method public void drawArc(float, float, float, float, float, float, boolean, android.graphics.Paint);
@@ -13447,6 +13482,7 @@
     method public void drawRect(android.graphics.RectF, android.graphics.Paint);
     method public void drawRect(android.graphics.Rect, android.graphics.Paint);
     method public void drawRect(float, float, float, float, android.graphics.Paint);
+    method public void drawRenderNode(android.graphics.RenderNode);
     method public void drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint);
     method public void drawRoundRect(float, float, float, float, float, float, android.graphics.Paint);
     method public void drawText(char[], int, int, float, float, android.graphics.Paint);
@@ -13458,6 +13494,7 @@
     method public void drawTextRun(char[], int, int, int, int, float, float, boolean, android.graphics.Paint);
     method public void drawTextRun(java.lang.CharSequence, int, int, int, int, float, float, boolean, android.graphics.Paint);
     method public void drawVertices(android.graphics.Canvas.VertexMode, int, float[], int, float[], int, int[], int, short[], int, int, android.graphics.Paint);
+    method public void enableZ();
     method public boolean getClipBounds(android.graphics.Rect);
     method public final android.graphics.Rect getClipBounds();
     method public int getDensity();
@@ -13854,6 +13891,7 @@
     field public static final int RAW_SENSOR = 32; // 0x20
     field public static final int RGB_565 = 4; // 0x4
     field public static final int UNKNOWN = 0; // 0x0
+    field public static final int Y8 = 538982489; // 0x20203859
     field public static final int YUV_420_888 = 35; // 0x23
     field public static final int YUV_422_888 = 39; // 0x27
     field public static final int YUV_444_888 = 40; // 0x28
@@ -14447,6 +14485,9 @@
     ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
   }
 
+  public final class RecordingCanvas extends android.graphics.Canvas {
+  }
+
   public final class Rect implements android.os.Parcelable {
     ctor public Rect();
     ctor public Rect(int, int, int, int);
@@ -14583,6 +14624,77 @@
     method public final boolean next(android.graphics.Rect);
   }
 
+  public class RenderNode {
+    method public int computeApproximateMemoryUsage();
+    method public static android.graphics.RenderNode create(java.lang.String);
+    method public void discardDisplayList();
+    method public void endRecording();
+    method public float getAlpha();
+    method public int getAmbientShadowColor();
+    method public int getBottom();
+    method public float getCameraDistance();
+    method public boolean getClipToOutline();
+    method public float getElevation();
+    method public int getHeight();
+    method public void getInverseMatrix(android.graphics.Matrix);
+    method public int getLeft();
+    method public void getMatrix(android.graphics.Matrix);
+    method public float getPivotX();
+    method public float getPivotY();
+    method public int getRight();
+    method public float getRotation();
+    method public float getRotationX();
+    method public float getRotationY();
+    method public float getScaleX();
+    method public float getScaleY();
+    method public int getSpotShadowColor();
+    method public int getTop();
+    method public float getTranslationX();
+    method public float getTranslationY();
+    method public float getTranslationZ();
+    method public int getWidth();
+    method public boolean hasDisplayList();
+    method public boolean hasIdentityMatrix();
+    method public boolean hasOverlappingRendering();
+    method public boolean hasShadow();
+    method public boolean isForceDarkAllowed();
+    method public boolean isPivotExplicitlySet();
+    method public boolean offsetLeftAndRight(int);
+    method public boolean offsetTopAndBottom(int);
+    method public boolean resetPivot();
+    method public boolean setAlpha(float);
+    method public boolean setAmbientShadowColor(int);
+    method public boolean setBottom(int);
+    method public boolean setCameraDistance(float);
+    method public boolean setClipBounds(android.graphics.Rect);
+    method public boolean setClipToBounds(boolean);
+    method public boolean setClipToOutline(boolean);
+    method public boolean setElevation(float);
+    method public boolean setForceDarkAllowed(boolean);
+    method public boolean setHasOverlappingRendering(boolean);
+    method public boolean setLeft(int);
+    method public boolean setLeftTopRightBottom(int, int, int, int);
+    method public boolean setOutline(android.graphics.Outline);
+    method public boolean setPivotX(float);
+    method public boolean setPivotY(float);
+    method public boolean setProjectBackwards(boolean);
+    method public boolean setProjectionReceiver(boolean);
+    method public boolean setRight(int);
+    method public boolean setRotation(float);
+    method public boolean setRotationX(float);
+    method public boolean setRotationY(float);
+    method public boolean setScaleX(float);
+    method public boolean setScaleY(float);
+    method public boolean setSpotShadowColor(int);
+    method public boolean setTop(int);
+    method public boolean setTranslationX(float);
+    method public boolean setTranslationY(float);
+    method public boolean setTranslationZ(float);
+    method public boolean setUseCompositingLayer(boolean, android.graphics.Paint);
+    method public android.graphics.RecordingCanvas startRecording(int, int);
+    method public android.graphics.RecordingCanvas startRecording();
+  }
+
   public class Shader {
     ctor public deprecated Shader();
     method public boolean getLocalMatrix(android.graphics.Matrix);
@@ -14671,10 +14783,10 @@
 
   public static class Typeface.CustomFallbackBuilder {
     ctor public Typeface.CustomFallbackBuilder(android.graphics.fonts.FontFamily);
+    method public android.graphics.Typeface.CustomFallbackBuilder addCustomFallback(android.graphics.fonts.FontFamily);
     method public android.graphics.Typeface build();
-    method public android.graphics.Typeface.CustomFallbackBuilder setFallback(java.lang.String);
-    method public android.graphics.Typeface.CustomFallbackBuilder setItalic(boolean);
-    method public android.graphics.Typeface.CustomFallbackBuilder setWeight(int);
+    method public android.graphics.Typeface.CustomFallbackBuilder setStyle(android.graphics.fonts.FontStyle);
+    method public android.graphics.Typeface.CustomFallbackBuilder setSystemFallback(java.lang.String);
   }
 
   public class Xfermode {
@@ -14881,7 +14993,7 @@
     method public final int getLevel();
     method public int getMinimumHeight();
     method public int getMinimumWidth();
-    method public abstract int getOpacity();
+    method public abstract deprecated int getOpacity();
     method public android.graphics.Insets getOpticalInsets();
     method public void getOutline(android.graphics.Outline);
     method public boolean getPadding(android.graphics.Rect);
@@ -15319,9 +15431,8 @@
     method public java.nio.ByteBuffer getBuffer();
     method public java.io.File getFile();
     method public android.os.LocaleList getLocaleList();
-    method public int getSlant();
+    method public android.graphics.fonts.FontStyle getStyle();
     method public int getTtcIndex();
-    method public int getWeight();
   }
 
   public static class Font.Builder {
@@ -16068,10 +16179,10 @@
 
   public class BiometricManager {
     method public int canAuthenticate();
-    field public static final int ERROR_NONE = 0; // 0x0
-    field public static final int ERROR_NO_BIOMETRICS = 11; // 0xb
-    field public static final int ERROR_NO_HARDWARE = 12; // 0xc
-    field public static final int ERROR_UNAVAILABLE = 1; // 0x1
+    field public static final int BIOMETRIC_ERROR_NO_BIOMETRICS = 11; // 0xb
+    field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
+    field public static final int BIOMETRIC_ERROR_UNAVAILABLE = 1; // 0x1
+    field public static final int BIOMETRIC_SUCCESS = 0; // 0x0
   }
 
   public class BiometricPrompt {
@@ -16195,6 +16306,7 @@
     method public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeys();
     method public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeysNeedingPermission();
     method public java.util.Set<java.lang.String> getPhysicalCameraIds();
+    method public android.hardware.camera2.params.RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(int);
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_MODES;
@@ -16857,6 +16969,33 @@
     field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
   }
 
+  public final class RecommendedStreamConfigurationMap {
+    method public java.util.Set<android.util.Size> getHighResolutionOutputSizes(int);
+    method public java.util.Set<android.util.Range<java.lang.Integer>> getHighSpeedVideoFpsRanges();
+    method public java.util.Set<android.util.Range<java.lang.Integer>> getHighSpeedVideoFpsRangesFor(android.util.Size);
+    method public java.util.Set<android.util.Size> getHighSpeedVideoSizes();
+    method public java.util.Set<android.util.Size> getHighSpeedVideoSizesFor(android.util.Range<java.lang.Integer>);
+    method public java.util.Set<java.lang.Integer> getInputFormats();
+    method public java.util.Set<android.util.Size> getInputSizes(int);
+    method public java.util.Set<java.lang.Integer> getOutputFormats();
+    method public long getOutputMinFrameDuration(int, android.util.Size);
+    method public <T> long getOutputMinFrameDuration(java.lang.Class<T>, android.util.Size);
+    method public java.util.Set<android.util.Size> getOutputSizes(int);
+    method public <T> java.util.Set<android.util.Size> getOutputSizes(java.lang.Class<T>);
+    method public long getOutputStallDuration(int, android.util.Size);
+    method public <T> long getOutputStallDuration(java.lang.Class<T>, android.util.Size);
+    method public int getRecommendedUseCase();
+    method public java.util.Set<java.lang.Integer> getValidOutputFormatsForInput(int);
+    method public boolean isOutputSupportedFor(int);
+    method public boolean isOutputSupportedFor(android.view.Surface);
+    field public static final int USECASE_PREVIEW = 0; // 0x0
+    field public static final int USECASE_RAW = 5; // 0x5
+    field public static final int USECASE_RECORD = 1; // 0x1
+    field public static final int USECASE_SNAPSHOT = 3; // 0x3
+    field public static final int USECASE_VIDEO_SNAPSHOT = 2; // 0x2
+    field public static final int USECASE_ZSL = 4; // 0x4
+  }
+
   public final class RggbChannelVector {
     ctor public RggbChannelVector(float, float, float, float);
     method public void copyTo(float[], int);
@@ -17382,6 +17521,63 @@
     field public static final int VOWEL_JAMO = 2; // 0x2
   }
 
+  public static abstract interface UCharacter.IndicPositionalCategory {
+    field public static final int BOTTOM = 1; // 0x1
+    field public static final int BOTTOM_AND_LEFT = 2; // 0x2
+    field public static final int BOTTOM_AND_RIGHT = 3; // 0x3
+    field public static final int LEFT = 4; // 0x4
+    field public static final int LEFT_AND_RIGHT = 5; // 0x5
+    field public static final int NA = 0; // 0x0
+    field public static final int OVERSTRUCK = 6; // 0x6
+    field public static final int RIGHT = 7; // 0x7
+    field public static final int TOP = 8; // 0x8
+    field public static final int TOP_AND_BOTTOM = 9; // 0x9
+    field public static final int TOP_AND_BOTTOM_AND_RIGHT = 10; // 0xa
+    field public static final int TOP_AND_LEFT = 11; // 0xb
+    field public static final int TOP_AND_LEFT_AND_RIGHT = 12; // 0xc
+    field public static final int TOP_AND_RIGHT = 13; // 0xd
+    field public static final int VISUAL_ORDER_LEFT = 14; // 0xe
+  }
+
+  public static abstract interface UCharacter.IndicSyllabicCategory {
+    field public static final int AVAGRAHA = 1; // 0x1
+    field public static final int BINDU = 2; // 0x2
+    field public static final int BRAHMI_JOINING_NUMBER = 3; // 0x3
+    field public static final int CANTILLATION_MARK = 4; // 0x4
+    field public static final int CONSONANT = 5; // 0x5
+    field public static final int CONSONANT_DEAD = 6; // 0x6
+    field public static final int CONSONANT_FINAL = 7; // 0x7
+    field public static final int CONSONANT_HEAD_LETTER = 8; // 0x8
+    field public static final int CONSONANT_INITIAL_POSTFIXED = 9; // 0x9
+    field public static final int CONSONANT_KILLER = 10; // 0xa
+    field public static final int CONSONANT_MEDIAL = 11; // 0xb
+    field public static final int CONSONANT_PLACEHOLDER = 12; // 0xc
+    field public static final int CONSONANT_PRECEDING_REPHA = 13; // 0xd
+    field public static final int CONSONANT_PREFIXED = 14; // 0xe
+    field public static final int CONSONANT_SUBJOINED = 15; // 0xf
+    field public static final int CONSONANT_SUCCEEDING_REPHA = 16; // 0x10
+    field public static final int CONSONANT_WITH_STACKER = 17; // 0x11
+    field public static final int GEMINATION_MARK = 18; // 0x12
+    field public static final int INVISIBLE_STACKER = 19; // 0x13
+    field public static final int JOINER = 20; // 0x14
+    field public static final int MODIFYING_LETTER = 21; // 0x15
+    field public static final int NON_JOINER = 22; // 0x16
+    field public static final int NUKTA = 23; // 0x17
+    field public static final int NUMBER = 24; // 0x18
+    field public static final int NUMBER_JOINER = 25; // 0x19
+    field public static final int OTHER = 0; // 0x0
+    field public static final int PURE_KILLER = 26; // 0x1a
+    field public static final int REGISTER_SHIFTER = 27; // 0x1b
+    field public static final int SYLLABLE_MODIFIER = 28; // 0x1c
+    field public static final int TONE_LETTER = 29; // 0x1d
+    field public static final int TONE_MARK = 30; // 0x1e
+    field public static final int VIRAMA = 31; // 0x1f
+    field public static final int VISARGA = 32; // 0x20
+    field public static final int VOWEL = 33; // 0x21
+    field public static final int VOWEL_DEPENDENT = 34; // 0x22
+    field public static final int VOWEL_INDEPENDENT = 35; // 0x23
+  }
+
   public static abstract interface UCharacter.JoiningGroup {
     field public static final int AFRICAN_FEH = 86; // 0x56
     field public static final int AFRICAN_NOON = 87; // 0x57
@@ -18165,6 +18361,13 @@
     field public static final int ZANABAZAR_SQUARE_ID = 280; // 0x118
   }
 
+  public static abstract interface UCharacter.VerticalOrientation {
+    field public static final int ROTATED = 0; // 0x0
+    field public static final int TRANSFORMED_ROTATED = 1; // 0x1
+    field public static final int TRANSFORMED_UPRIGHT = 2; // 0x2
+    field public static final int UPRIGHT = 3; // 0x3
+  }
+
   public static abstract interface UCharacter.WordBreak {
     field public static final int ALETTER = 1; // 0x1
     field public static final int CR = 8; // 0x8
@@ -18336,6 +18539,8 @@
     field public static final int IDS_TRINARY_OPERATOR = 19; // 0x13
     field public static final int ID_CONTINUE = 15; // 0xf
     field public static final int ID_START = 16; // 0x10
+    field public static final int INDIC_POSITIONAL_CATEGORY = 4118; // 0x1016
+    field public static final int INDIC_SYLLABIC_CATEGORY = 4119; // 0x1017
     field public static final int INT_START = 4096; // 0x1000
     field public static final int JOINING_GROUP = 4102; // 0x1006
     field public static final int JOINING_TYPE = 4103; // 0x1007
@@ -18389,6 +18594,7 @@
     field public static final int UPPERCASE = 30; // 0x1e
     field public static final int UPPERCASE_MAPPING = 16396; // 0x400c
     field public static final int VARIATION_SELECTOR = 36; // 0x24
+    field public static final int VERTICAL_ORIENTATION = 4120; // 0x1018
     field public static final int WHITE_SPACE = 31; // 0x1f
     field public static final int WORD_BREAK = 4116; // 0x1014
     field public static final int XID_CONTINUE = 32; // 0x20
@@ -18919,6 +19125,7 @@
     method public int preceding(int);
     method public abstract int previous();
     method public void setText(java.lang.String);
+    method public void setText(java.lang.CharSequence);
     method public abstract void setText(java.text.CharacterIterator);
     field public static final int DONE = -1; // 0xffffffff
     field public static final int KIND_CHARACTER = 0; // 0x0
@@ -18947,24 +19154,31 @@
   }
 
   public static final class CaseMap.Fold extends android.icu.text.CaseMap {
+    method public java.lang.String apply(java.lang.CharSequence);
     method public <A extends java.lang.Appendable> A apply(java.lang.CharSequence, A, android.icu.text.Edits);
     method public android.icu.text.CaseMap.Fold omitUnchangedText();
     method public android.icu.text.CaseMap.Fold turkic();
   }
 
   public static final class CaseMap.Lower extends android.icu.text.CaseMap {
+    method public java.lang.String apply(java.util.Locale, java.lang.CharSequence);
     method public <A extends java.lang.Appendable> A apply(java.util.Locale, java.lang.CharSequence, A, android.icu.text.Edits);
     method public android.icu.text.CaseMap.Lower omitUnchangedText();
   }
 
   public static final class CaseMap.Title extends android.icu.text.CaseMap {
+    method public android.icu.text.CaseMap.Title adjustToCased();
+    method public java.lang.String apply(java.util.Locale, android.icu.text.BreakIterator, java.lang.CharSequence);
     method public <A extends java.lang.Appendable> A apply(java.util.Locale, android.icu.text.BreakIterator, java.lang.CharSequence, A, android.icu.text.Edits);
     method public android.icu.text.CaseMap.Title noBreakAdjustment();
     method public android.icu.text.CaseMap.Title noLowercase();
     method public android.icu.text.CaseMap.Title omitUnchangedText();
+    method public android.icu.text.CaseMap.Title sentences();
+    method public android.icu.text.CaseMap.Title wholeString();
   }
 
   public static final class CaseMap.Upper extends android.icu.text.CaseMap {
+    method public java.lang.String apply(java.util.Locale, java.lang.CharSequence);
     method public <A extends java.lang.Appendable> A apply(java.util.Locale, java.lang.CharSequence, A, android.icu.text.Edits);
     method public android.icu.text.CaseMap.Upper omitUnchangedText();
   }
@@ -18972,7 +19186,6 @@
   public final class CollationElementIterator {
     method public int getMaxExpansion(int);
     method public int getOffset();
-    method public deprecated int hashCode();
     method public int next();
     method public int previous();
     method public static int primaryOrder(int);
@@ -19085,7 +19298,6 @@
     method public static android.icu.text.CurrencyPluralInfo getInstance(android.icu.util.ULocale);
     method public android.icu.util.ULocale getLocale();
     method public android.icu.text.PluralRules getPluralRules();
-    method public deprecated int hashCode();
     method public void setCurrencyPluralPattern(java.lang.String, java.lang.String);
     method public void setLocale(android.icu.util.ULocale);
     method public void setPluralRules(java.lang.String);
@@ -19375,7 +19587,6 @@
     method public boolean firstDateInPtnIsLaterDate();
     method public java.lang.String getFirstPart();
     method public java.lang.String getSecondPart();
-    method public deprecated java.lang.String toString();
   }
 
   public class DateTimePatternGenerator implements java.lang.Cloneable android.icu.util.Freezable {
@@ -19535,6 +19746,8 @@
     ctor public DecimalFormatSymbols(java.util.Locale);
     ctor public DecimalFormatSymbols(android.icu.util.ULocale);
     method public java.lang.Object clone();
+    method public static android.icu.text.DecimalFormatSymbols forNumberingSystem(java.util.Locale, android.icu.text.NumberingSystem);
+    method public static android.icu.text.DecimalFormatSymbols forNumberingSystem(android.icu.util.ULocale, android.icu.text.NumberingSystem);
     method public static java.util.Locale[] getAvailableLocales();
     method public android.icu.util.Currency getCurrency();
     method public java.lang.String getCurrencySymbol();
@@ -19644,11 +19857,15 @@
     method public android.icu.text.Edits.Iterator getFineIterator();
     method public boolean hasChanges();
     method public int lengthDelta();
+    method public android.icu.text.Edits mergeAndAppend(android.icu.text.Edits, android.icu.text.Edits);
+    method public int numberOfChanges();
     method public void reset();
   }
 
   public static final class Edits.Iterator {
     method public int destinationIndex();
+    method public int destinationIndexFromSourceIndex(int);
+    method public boolean findDestinationIndex(int);
     method public boolean findSourceIndex(int);
     method public boolean hasChange();
     method public int newLength();
@@ -19656,6 +19873,7 @@
     method public int oldLength();
     method public int replacementIndex();
     method public int sourceIndex();
+    method public int sourceIndexFromDestinationIndex(int);
   }
 
   public abstract class IDNA {
@@ -20058,6 +20276,7 @@
     method public int getRadix();
     method public boolean isAlgorithmic();
     method public static boolean isValidDigitString(java.lang.String);
+    field public static final android.icu.text.NumberingSystem LATIN;
   }
 
   public class PluralFormat extends android.icu.text.UFormat {
@@ -20095,7 +20314,6 @@
     method public java.util.Set<java.lang.String> getKeywords();
     method public java.util.Collection<java.lang.Double> getSamples(java.lang.String);
     method public double getUniqueKeywordValue(java.lang.String);
-    method public deprecated int hashCode();
     method public static android.icu.text.PluralRules parseDescription(java.lang.String) throws java.text.ParseException;
     method public java.lang.String select(double);
     field public static final android.icu.text.PluralRules DEFAULT;
@@ -20338,7 +20556,6 @@
     ctor public StringPrepParseException(java.lang.String, int, java.lang.String, int);
     ctor public StringPrepParseException(java.lang.String, int, java.lang.String, int, int);
     method public int getError();
-    method public deprecated int hashCode();
     field public static final int ACE_PREFIX_ERROR = 6; // 0x6
     field public static final int BUFFER_OVERFLOW_ERROR = 9; // 0x9
     field public static final int CHECK_BIDI_ERROR = 4; // 0x4
@@ -20969,6 +21186,7 @@
 
   public class Currency extends android.icu.util.MeasureUnit {
     ctor protected Currency(java.lang.String);
+    method public static android.icu.util.Currency fromJavaCurrency(java.util.Currency);
     method public static java.util.Set<android.icu.util.Currency> getAvailableCurrencies();
     method public static java.lang.String[] getAvailableCurrencyCodes(android.icu.util.ULocale, java.util.Date);
     method public static java.lang.String[] getAvailableCurrencyCodes(java.util.Locale, java.util.Date);
@@ -20994,6 +21212,7 @@
     method public java.lang.String getSymbol(java.util.Locale);
     method public java.lang.String getSymbol(android.icu.util.ULocale);
     method public static boolean isAvailable(java.lang.String, java.util.Date, java.util.Date);
+    method public java.util.Currency toJavaCurrency();
     field public static final int LONG_NAME = 1; // 0x1
     field public static final int PLURAL_LONG_NAME = 2; // 0x2
     field public static final int SYMBOL_NAME = 0; // 0x0
@@ -21009,6 +21228,8 @@
   public class CurrencyAmount extends android.icu.util.Measure {
     ctor public CurrencyAmount(java.lang.Number, android.icu.util.Currency);
     ctor public CurrencyAmount(double, android.icu.util.Currency);
+    ctor public CurrencyAmount(java.lang.Number, java.util.Currency);
+    ctor public CurrencyAmount(double, java.util.Currency);
     method public android.icu.util.Currency getCurrency();
   }
 
@@ -22611,6 +22832,7 @@
     field public static final int ENCODING_AC3 = 5; // 0x5
     field public static final int ENCODING_AC4 = 17; // 0x11
     field public static final int ENCODING_DEFAULT = 1; // 0x1
+    field public static final int ENCODING_DOLBY_MAT = 19; // 0x13
     field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
     field public static final int ENCODING_DTS = 7; // 0x7
     field public static final int ENCODING_DTS_HD = 8; // 0x8
@@ -22824,6 +23046,8 @@
     method public java.util.Map<java.util.Locale, java.lang.String> getLabels();
     method public java.util.Locale getLocale();
     method public int getMasteringIndication();
+    method public int getPresentationId();
+    method public int getProgramId();
     method public boolean hasAudioDescription();
     method public boolean hasDialogueEnhancement();
     method public boolean hasSpokenSubtitles();
@@ -22834,6 +23058,18 @@
     field public static final int MASTERING_NOT_INDICATED = 0; // 0x0
   }
 
+  public static class AudioPresentation.Builder {
+    ctor public AudioPresentation.Builder(int);
+    method public android.media.AudioPresentation build();
+    method public android.media.AudioPresentation.Builder setHasAudioDescription(boolean);
+    method public android.media.AudioPresentation.Builder setHasDialogueEnhancement(boolean);
+    method public android.media.AudioPresentation.Builder setHasSpokenSubtitles(boolean);
+    method public android.media.AudioPresentation.Builder setLabels(java.util.Map<android.icu.util.ULocale, java.lang.String>);
+    method public android.media.AudioPresentation.Builder setLocale(android.icu.util.ULocale);
+    method public android.media.AudioPresentation.Builder setMasteringIndication(int);
+    method public android.media.AudioPresentation.Builder setProgramId(int);
+  }
+
   public class AudioRecord implements android.media.AudioRouting {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
@@ -28497,7 +28733,7 @@
     enum_constant public static final android.net.wifi.SupplicantState UNINITIALIZED;
   }
 
-  public class WifiConfiguration implements android.os.Parcelable {
+  public deprecated class WifiConfiguration implements android.os.Parcelable {
     ctor public WifiConfiguration();
     method public int describeContents();
     method public android.net.ProxyInfo getHttpProxy();
@@ -28509,9 +28745,11 @@
     field public java.lang.String SSID;
     field public java.util.BitSet allowedAuthAlgorithms;
     field public java.util.BitSet allowedGroupCiphers;
+    field public java.util.BitSet allowedGroupMgmtCiphers;
     field public java.util.BitSet allowedKeyManagement;
     field public java.util.BitSet allowedPairwiseCiphers;
     field public java.util.BitSet allowedProtocols;
+    field public java.util.BitSet allowedSuiteBCiphers;
     field public android.net.wifi.WifiEnterpriseConfig enterpriseConfig;
     field public boolean hiddenSSID;
     field public boolean isHomeProviderNetwork;
@@ -28535,6 +28773,7 @@
 
   public static class WifiConfiguration.GroupCipher {
     field public static final int CCMP = 3; // 0x3
+    field public static final int GCMP_256 = 5; // 0x5
     field public static final int TKIP = 2; // 0x2
     field public static final deprecated int WEP104 = 1; // 0x1
     field public static final deprecated int WEP40 = 0; // 0x0
@@ -28542,9 +28781,18 @@
     field public static final java.lang.String varName = "group";
   }
 
+  public static class WifiConfiguration.GroupMgmtCipher {
+    field public static final int BIP_CMAC_256 = 0; // 0x0
+    field public static final int BIP_GMAC_128 = 1; // 0x1
+    field public static final int BIP_GMAC_256 = 2; // 0x2
+  }
+
   public static class WifiConfiguration.KeyMgmt {
     field public static final int IEEE8021X = 3; // 0x3
     field public static final int NONE = 0; // 0x0
+    field public static final int OWE = 9; // 0x9
+    field public static final int SAE = 8; // 0x8
+    field public static final int SUITE_B_192 = 10; // 0xa
     field public static final int WPA_EAP = 2; // 0x2
     field public static final int WPA_PSK = 1; // 0x1
     field public static final java.lang.String[] strings;
@@ -28553,6 +28801,7 @@
 
   public static class WifiConfiguration.PairwiseCipher {
     field public static final int CCMP = 2; // 0x2
+    field public static final int GCMP_256 = 3; // 0x3
     field public static final int NONE = 0; // 0x0
     field public static final deprecated int TKIP = 1; // 0x1
     field public static final java.lang.String[] strings;
@@ -28651,7 +28900,8 @@
   }
 
   public class WifiManager {
-    method public int addNetwork(android.net.wifi.WifiConfiguration);
+    method public deprecated int addNetwork(android.net.wifi.WifiConfiguration);
+    method public boolean addNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>, android.app.PendingIntent);
     method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
     method public static int calculateSignalLevel(int, int);
     method public deprecated void cancelWps(android.net.wifi.WifiManager.WpsCallback);
@@ -28659,10 +28909,10 @@
     method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String);
     method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String);
     method public android.net.wifi.WifiManager.WifiLock createWifiLock(java.lang.String);
-    method public boolean disableNetwork(int);
-    method public boolean disconnect();
-    method public boolean enableNetwork(int, boolean);
-    method public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
+    method public deprecated boolean disableNetwork(int);
+    method public deprecated boolean disconnect();
+    method public deprecated boolean enableNetwork(int, boolean);
+    method public deprecated java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
     method public android.net.wifi.WifiInfo getConnectionInfo();
     method public android.net.DhcpInfo getDhcpInfo();
     method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations();
@@ -28673,22 +28923,23 @@
     method public boolean isEnhancedPowerReportingSupported();
     method public boolean isP2pSupported();
     method public boolean isPreferredNetworkOffloadSupported();
-    method public boolean isScanAlwaysAvailable();
+    method public deprecated boolean isScanAlwaysAvailable();
     method public boolean isTdlsSupported();
     method public boolean isWifiEnabled();
     method public deprecated boolean pingSupplicant();
-    method public boolean reassociate();
-    method public boolean reconnect();
-    method public boolean removeNetwork(int);
+    method public deprecated boolean reassociate();
+    method public deprecated boolean reconnect();
+    method public deprecated boolean removeNetwork(int);
+    method public boolean removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
     method public void removePasspointConfiguration(java.lang.String);
     method public deprecated boolean saveConfiguration();
     method public void setTdlsEnabled(java.net.InetAddress, boolean);
     method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
-    method public boolean setWifiEnabled(boolean);
+    method public deprecated boolean setWifiEnabled(boolean);
     method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler);
     method public deprecated boolean startScan();
     method public deprecated void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
-    method public int updateNetwork(android.net.wifi.WifiConfiguration);
+    method public deprecated int updateNetwork(android.net.wifi.WifiConfiguration);
     field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
     field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
     field public static final deprecated int ERROR_AUTHENTICATING = 1; // 0x1
@@ -28762,6 +29013,29 @@
     method public abstract deprecated void onSucceeded();
   }
 
+  public class WifiNetworkConfigBuilder {
+    ctor public WifiNetworkConfigBuilder();
+    method public android.net.NetworkSpecifier buildNetworkSpecifier();
+    method public android.net.wifi.WifiNetworkSuggestion buildNetworkSuggestion();
+    method public android.net.wifi.WifiNetworkConfigBuilder setBssid(android.net.MacAddress);
+    method public android.net.wifi.WifiNetworkConfigBuilder setBssidPattern(android.net.MacAddress, android.net.MacAddress);
+    method public android.net.wifi.WifiNetworkConfigBuilder setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+    method public android.net.wifi.WifiNetworkConfigBuilder setIsAppInteractionRequired();
+    method public android.net.wifi.WifiNetworkConfigBuilder setIsHiddenSsid();
+    method public android.net.wifi.WifiNetworkConfigBuilder setIsMetered();
+    method public android.net.wifi.WifiNetworkConfigBuilder setIsUserInteractionRequired();
+    method public android.net.wifi.WifiNetworkConfigBuilder setPriority(int);
+    method public android.net.wifi.WifiNetworkConfigBuilder setPskPassphrase(java.lang.String);
+    method public android.net.wifi.WifiNetworkConfigBuilder setSsid(java.lang.String);
+    method public android.net.wifi.WifiNetworkConfigBuilder setSsidPattern(android.os.PatternMatcher);
+  }
+
+  public final class WifiNetworkSuggestion implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.wifi.WifiNetworkSuggestion> CREATOR;
+  }
+
   public deprecated class WpsInfo implements android.os.Parcelable {
     ctor public deprecated WpsInfo();
     ctor public deprecated WpsInfo(android.net.wifi.WpsInfo);
@@ -29753,8 +30027,7 @@
     field public static final int EGL_WINDOW_BIT = 4; // 0x4
   }
 
-  public class EGL15 {
-    ctor public EGL15();
+  public final class EGL15 {
     method public static int eglClientWaitSync(android.opengl.EGLDisplay, android.opengl.EGLSync, int, long);
     method public static android.opengl.EGLImage eglCreateImage(android.opengl.EGLDisplay, android.opengl.EGLContext, int, long, long[], int);
     method public static android.opengl.EGLSurface eglCreatePlatformPixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, java.nio.Buffer, long[], int);
@@ -33073,6 +33346,7 @@
     field public static java.lang.String DIRECTORY_PICTURES;
     field public static java.lang.String DIRECTORY_PODCASTS;
     field public static java.lang.String DIRECTORY_RINGTONES;
+    field public static java.lang.String DIRECTORY_SCREENSHOTS;
     field public static final java.lang.String MEDIA_BAD_REMOVAL = "bad_removal";
     field public static final java.lang.String MEDIA_CHECKING = "checking";
     field public static final java.lang.String MEDIA_EJECTING = "ejecting";
@@ -33924,6 +34198,7 @@
     field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps";
     field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
     field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY = "no_install_unknown_sources_globally";
+    field public static final java.lang.String DISALLOW_INTELLIGENCE_CAPTURE = "no_intelligence_capture";
     field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
     field public static final java.lang.String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media";
     field public static final java.lang.String DISALLOW_NETWORK_RESET = "no_network_reset";
@@ -34180,7 +34455,7 @@
   }
 
   public final class StorageVolume implements android.os.Parcelable {
-    method public android.content.Intent createAccessIntent(java.lang.String);
+    method public deprecated android.content.Intent createAccessIntent(java.lang.String);
     method public int describeContents();
     method public java.lang.String getDescription(android.content.Context);
     method public java.lang.String getState();
@@ -35419,6 +35694,15 @@
     method public static java.lang.String getLastOutgoingCall(android.content.Context);
     field public static final int ANSWERED_EXTERNALLY_TYPE = 7; // 0x7
     field public static final int BLOCKED_TYPE = 6; // 0x6
+    field public static final java.lang.String BLOCK_REASON = "block_reason";
+    field public static final int BLOCK_REASON_BLOCKED_NUMBER = 3; // 0x3
+    field public static final int BLOCK_REASON_CALL_SCREENING_SERVICE = 1; // 0x1
+    field public static final int BLOCK_REASON_DIRECT_TO_VOICEMAIL = 2; // 0x2
+    field public static final int BLOCK_REASON_NOT_BLOCKED = 0; // 0x0
+    field public static final int BLOCK_REASON_NOT_IN_CONTACTS = 7; // 0x7
+    field public static final int BLOCK_REASON_PAY_PHONE = 6; // 0x6
+    field public static final int BLOCK_REASON_RESTRICTED_NUMBER = 5; // 0x5
+    field public static final int BLOCK_REASON_UNKNOWN_NUMBER = 4; // 0x4
     field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number";
     field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri";
     field public static final java.lang.String CACHED_MATCHED_NUMBER = "matched_number";
@@ -35428,6 +35712,8 @@
     field public static final java.lang.String CACHED_NUMBER_TYPE = "numbertype";
     field public static final java.lang.String CACHED_PHOTO_ID = "photo_id";
     field public static final java.lang.String CACHED_PHOTO_URI = "photo_uri";
+    field public static final java.lang.String CALL_SCREENING_APP_NAME = "call_screening_app_name";
+    field public static final java.lang.String CALL_SCREENING_COMPONENT_NAME = "call_screening_component_name";
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/calls";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/calls";
@@ -36716,6 +37002,7 @@
     method public android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], android.os.Bundle) throws java.io.FileNotFoundException;
     method public abstract android.database.Cursor queryDocument(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
     method public android.database.Cursor queryRecentDocuments(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
+    method public android.database.Cursor queryRecentDocuments(java.lang.String, java.lang.String[], android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public abstract android.database.Cursor queryRoots(java.lang.String[]) throws java.io.FileNotFoundException;
     method public android.database.Cursor querySearchDocuments(java.lang.String, java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
     method public void removeDocument(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
@@ -36800,14 +37087,23 @@
 
   public final class MediaStore {
     ctor public MediaStore();
+    method public static android.net.Uri createPending(android.content.Context, android.provider.MediaStore.PendingParams);
+    method public static java.util.Set<java.lang.String> getAllVolumeNames(android.content.Context);
     method public static android.net.Uri getDocumentUri(android.content.Context, android.net.Uri);
     method public static android.net.Uri getMediaScannerUri();
     method public static android.net.Uri getMediaUri(android.content.Context, android.net.Uri);
     method public static java.lang.String getVersion(android.content.Context);
+    method public static java.lang.String getVolumeName(android.net.Uri);
+    method public static android.provider.MediaStore.PendingSession openPending(android.content.Context, android.net.Uri);
+    method public static android.net.Uri setIncludePending(android.net.Uri);
     field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
     field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
+    field public static final java.lang.String ACTION_REVIEW = "android.provider.action.REVIEW";
+    field public static final java.lang.String ACTION_REVIEW_SECURE = "android.provider.action.REVIEW_SECURE";
     field public static final java.lang.String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
     field public static final java.lang.String AUTHORITY = "media";
+    field public static final android.net.Uri AUTHORITY_URI;
+    field public static final java.lang.String EXTRA_BRIGHTNESS = "android.provider.extra.BRIGHTNESS";
     field public static final java.lang.String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
     field public static final java.lang.String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion";
     field public static final java.lang.String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen";
@@ -36933,7 +37229,7 @@
   public static final class MediaStore.Audio.Media implements android.provider.MediaStore.Audio.AudioColumns {
     ctor public MediaStore.Audio.Media();
     method public static android.net.Uri getContentUri(java.lang.String);
-    method public static android.net.Uri getContentUriForPath(java.lang.String);
+    method public static deprecated android.net.Uri getContentUriForPath(java.lang.String);
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/audio";
     field public static final java.lang.String DEFAULT_SORT_ORDER = "title_key";
     field public static final java.lang.String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/audio";
@@ -37055,13 +37351,31 @@
     field public static final java.lang.String DATE_ADDED = "date_added";
     field public static final java.lang.String DATE_MODIFIED = "date_modified";
     field public static final java.lang.String DISPLAY_NAME = "_display_name";
+    field public static final java.lang.String HASH = "_hash";
     field public static final java.lang.String HEIGHT = "height";
+    field public static final java.lang.String IS_PENDING = "is_pending";
     field public static final java.lang.String MIME_TYPE = "mime_type";
+    field public static final java.lang.String OWNER_PACKAGE_NAME = "owner_package_name";
     field public static final java.lang.String SIZE = "_size";
     field public static final java.lang.String TITLE = "title";
     field public static final java.lang.String WIDTH = "width";
   }
 
+  public static class MediaStore.PendingParams {
+    ctor public MediaStore.PendingParams(android.net.Uri, java.lang.String, java.lang.String);
+    method public void setPrimaryDirectory(java.lang.String);
+    method public void setSecondaryDirectory(java.lang.String);
+  }
+
+  public static class MediaStore.PendingSession implements java.lang.AutoCloseable {
+    method public void abandon();
+    method public void close();
+    method public void notifyProgress(int);
+    method public android.os.ParcelFileDescriptor open() throws java.io.FileNotFoundException;
+    method public java.io.OutputStream openOutputStream() throws java.io.FileNotFoundException;
+    method public android.net.Uri publish();
+  }
+
   public static final class MediaStore.Video {
     ctor public MediaStore.Video();
     method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
@@ -37146,6 +37460,7 @@
     field public static final java.lang.String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS";
     field public static final java.lang.String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS";
     field public static final java.lang.String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS";
+    field public static final java.lang.String ACTION_APP_USAGE_SETTINGS = "android.settings.action.APP_USAGE_SETTINGS";
     field public static final java.lang.String ACTION_BATTERY_SAVER_SETTINGS = "android.settings.BATTERY_SAVER_SETTINGS";
     field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
     field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
@@ -37191,7 +37506,7 @@
     field public static final java.lang.String ACTION_SETTINGS = "android.settings.SETTINGS";
     field public static final java.lang.String ACTION_SHOW_REGULATORY_INFO = "android.settings.SHOW_REGULATORY_INFO";
     field public static final java.lang.String ACTION_SOUND_SETTINGS = "android.settings.SOUND_SETTINGS";
-    field public static final java.lang.String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS = "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS";
+    field public static final deprecated java.lang.String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS = "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS";
     field public static final java.lang.String ACTION_SYNC_SETTINGS = "android.settings.SYNC_SETTINGS";
     field public static final java.lang.String ACTION_USAGE_ACCESS_SETTINGS = "android.settings.USAGE_ACCESS_SETTINGS";
     field public static final java.lang.String ACTION_USER_DICTIONARY_SETTINGS = "android.settings.USER_DICTIONARY_SETTINGS";
@@ -37598,6 +37913,7 @@
     field public static final java.lang.String AUTH_TYPE = "authtype";
     field public static final deprecated java.lang.String BEARER = "bearer";
     field public static final java.lang.String CARRIER_ENABLED = "carrier_enabled";
+    field public static final java.lang.String CARRIER_ID = "carrier_id";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String CURRENT = "current";
     field public static final java.lang.String DEFAULT_SORT_ORDER = "name ASC";
@@ -41543,6 +41859,7 @@
     field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
     field public static final int PROPERTY_RTT = 1024; // 0x400
     field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
+    field public static final int PROPERTY_VOIP_AUDIO_MODE = 4096; // 0x1000
     field public static final int PROPERTY_WIFI = 8; // 0x8
   }
 
@@ -42169,6 +42486,7 @@
     method public java.lang.String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
     method public boolean handleMmi(java.lang.String);
     method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
+    method public boolean isDefaultCallScreeningApp(android.content.ComponentName);
     method public boolean isInCall();
     method public boolean isInManagedCall();
     method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
@@ -42177,12 +42495,14 @@
     method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
     method public void placeCall(android.net.Uri, android.os.Bundle);
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
+    method public void requestChangeDefaultCallScreeningApp(android.content.ComponentName);
     method public void showInCallScreen(boolean);
     method public void silenceRinger();
     method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
     field public static final java.lang.String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER";
     field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
     field public static final java.lang.String ACTION_CONFIGURE_PHONE_ACCOUNT = "android.telecom.action.CONFIGURE_PHONE_ACCOUNT";
+    field public static final java.lang.String ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED = "android.telecom.action.DEFAULT_CALL_SCREENING_APP_CHANGED";
     field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
     field public static final deprecated java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
     field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
@@ -42199,9 +42519,11 @@
     field public static final java.lang.String EXTRA_CALL_NETWORK_TYPE = "android.telecom.extra.CALL_NETWORK_TYPE";
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME = "android.telecom.extra.DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME";
     field public static final java.lang.String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_INCOMING_VIDEO_STATE = "android.telecom.extra.INCOMING_VIDEO_STATE";
+    field public static final java.lang.String EXTRA_IS_DEFAULT_CALL_SCREENING_APP = "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP";
     field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telecom.extra.NOTIFICATION_COUNT";
     field public static final java.lang.String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
     field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
@@ -42388,6 +42710,7 @@
     field public static final java.lang.String KEY_CALL_BARRING_VISIBILITY_BOOL = "call_barring_visibility_bool";
     field public static final java.lang.String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array";
     field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
+    field public static final java.lang.String KEY_CARRIER_CALL_SCREENING_APP_STRING = "call_screening_app";
     field public static final java.lang.String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS = "carrier_data_call_permanent_failure_strings";
     field public static final java.lang.String KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT = "carrier_default_wfc_ims_mode_int";
     field public static final java.lang.String KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT = "carrier_default_wfc_ims_roaming_mode_int";
@@ -42503,6 +42826,7 @@
     field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
     field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
+    field public static final java.lang.String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool";
     field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
     field public static final java.lang.String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
     field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
@@ -42574,12 +42898,23 @@
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityLte> CREATOR;
   }
 
+  public final class CellIdentityNr extends android.telephony.CellIdentity {
+    method public int getChannelNumber();
+    method public java.lang.String getMccString();
+    method public java.lang.String getMncString();
+    method public int getPci();
+    method public int getTac();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityNr> CREATOR;
+  }
+
   public final class CellIdentityTdscdma extends android.telephony.CellIdentity {
     method public int getCid();
     method public int getCpid();
     method public int getLac();
     method public java.lang.String getMccString();
     method public java.lang.String getMncString();
+    method public java.lang.String getMobileNetworkOperator();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR;
   }
@@ -42608,6 +42943,7 @@
     field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
     field public static final int CONNECTION_UNKNOWN = 2147483647; // 0x7fffffff
     field public static final android.os.Parcelable.Creator<android.telephony.CellInfo> CREATOR;
+    field public static final int UNAVAILABLE = 2147483647; // 0x7fffffff
   }
 
   public final class CellInfoCdma extends android.telephony.CellInfo implements android.os.Parcelable {
@@ -42631,6 +42967,13 @@
     field public static final android.os.Parcelable.Creator<android.telephony.CellInfoLte> CREATOR;
   }
 
+  public final class CellInfoNr extends android.telephony.CellInfo {
+    method public android.telephony.CellIdentity getCellIdentity();
+    method public android.telephony.CellSignalStrength getCellSignalStrength();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.CellInfoNr> CREATOR;
+  }
+
   public final class CellInfoWcdma extends android.telephony.CellInfo implements android.os.Parcelable {
     method public android.telephony.CellIdentityWcdma getCellIdentity();
     method public android.telephony.CellSignalStrengthWcdma getCellSignalStrength();
@@ -42697,6 +43040,21 @@
     field public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthLte> CREATOR;
   }
 
+  public final class CellSignalStrengthNr extends android.telephony.CellSignalStrength implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getAsuLevel();
+    method public int getCsiRsrp();
+    method public int getCsiRsrq();
+    method public int getCsiSinr();
+    method public int getDbm();
+    method public int getLevel();
+    method public int getSsRsrp();
+    method public int getSsRsrq();
+    method public int getSsSinr();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthNr> CREATOR;
+  }
+
   public final class CellSignalStrengthWcdma extends android.telephony.CellSignalStrength implements android.os.Parcelable {
     method public int describeContents();
     method public int getAsuLevel();
@@ -42904,6 +43262,7 @@
     field public static final int LISTEN_CELL_LOCATION = 16; // 0x10
     field public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80
     field public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40
+    field public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
     field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
     field public static final int LISTEN_NONE = 0; // 0x0
     field public static final int LISTEN_SERVICE_STATE = 1; // 0x1
@@ -42969,6 +43328,7 @@
     method public int getLevel();
     method public boolean isGsm();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int INVALID = 2147483647; // 0x7fffffff
   }
 
   public final class SmsManager {
@@ -43140,7 +43500,7 @@
     method public static int getDefaultSmsSubscriptionId();
     method public static int getDefaultSubscriptionId();
     method public static int getDefaultVoiceSubscriptionId();
-    method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions(int);
+    method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
     method public static int getSlotIndex(int);
     method public static int[] getSubscriptionIds(int);
     method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
@@ -43214,6 +43574,8 @@
     method public int getCallState();
     method public android.os.PersistableBundle getCarrierConfig();
     method public deprecated android.telephony.CellLocation getCellLocation();
+    method public java.util.Map<java.lang.Integer, java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList();
+    method public java.util.Map<java.lang.Integer, java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList(int);
     method public int getDataActivity();
     method public int getDataNetworkType();
     method public int getDataState();
@@ -43267,6 +43629,7 @@
     method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
     method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
     method public boolean isConcurrentVoiceAndDataSupported();
+    method public boolean isCurrentEmergencyNumber(java.lang.String);
     method public boolean isDataEnabled();
     method public boolean isDataRoamingEnabled();
     method public boolean isHearingAidCompatibilitySupported();
@@ -45734,6 +46097,7 @@
     ctor public deprecated Scene(android.view.ViewGroup, android.view.ViewGroup);
     method public void enter();
     method public void exit();
+    method public static android.transition.Scene getCurrentScene(android.view.View);
     method public static android.transition.Scene getSceneForLayout(android.view.ViewGroup, int, android.content.Context);
     method public android.view.ViewGroup getSceneRoot();
     method public void setEnterAction(java.lang.Runnable);
@@ -45885,7 +46249,8 @@
   }
 
   public class TransitionValues {
-    ctor public TransitionValues();
+    ctor public deprecated TransitionValues();
+    ctor public TransitionValues(android.view.View);
     field public final java.util.Map<java.lang.String, java.lang.Object> values;
     field public android.view.View view;
   }
@@ -48218,6 +48583,7 @@
     ctor public TouchDelegate(android.graphics.Rect, android.view.View);
     method public android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo getTouchDelegateInfo();
     method public boolean onTouchEvent(android.view.MotionEvent);
+    method public boolean onTouchExplorationHoverEvent(android.view.MotionEvent);
     field public static final int ABOVE = 1; // 0x1
     field public static final int BELOW = 2; // 0x2
     field public static final int TO_LEFT = 4; // 0x4
@@ -49876,17 +50242,29 @@
     method public int getStableInsetLeft();
     method public int getStableInsetRight();
     method public int getStableInsetTop();
+    method public android.graphics.Insets getStableInsets();
     method public int getSystemWindowInsetBottom();
     method public int getSystemWindowInsetLeft();
     method public int getSystemWindowInsetRight();
     method public int getSystemWindowInsetTop();
+    method public android.graphics.Insets getSystemWindowInsets();
     method public boolean hasInsets();
     method public boolean hasStableInsets();
     method public boolean hasSystemWindowInsets();
+    method public android.view.WindowInsets inset(int, int, int, int);
     method public boolean isConsumed();
     method public boolean isRound();
-    method public android.view.WindowInsets replaceSystemWindowInsets(int, int, int, int);
-    method public android.view.WindowInsets replaceSystemWindowInsets(android.graphics.Rect);
+    method public deprecated android.view.WindowInsets replaceSystemWindowInsets(int, int, int, int);
+    method public deprecated android.view.WindowInsets replaceSystemWindowInsets(android.graphics.Rect);
+  }
+
+  public static class WindowInsets.Builder {
+    ctor public WindowInsets.Builder();
+    ctor public WindowInsets.Builder(android.view.WindowInsets);
+    method public android.view.WindowInsets build();
+    method public android.view.WindowInsets.Builder setDisplayCutout(android.view.DisplayCutout);
+    method public android.view.WindowInsets.Builder setStableInsets(android.graphics.Insets);
+    method public android.view.WindowInsets.Builder setSystemWindowInsets(android.graphics.Insets);
   }
 
   public abstract interface WindowManager implements android.view.ViewManager {
@@ -50142,7 +50520,7 @@
     method public deprecated java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList();
     method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int);
     method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList();
-    method public int getMinimumUiTimeoutMillis();
+    method public int getRecommendedTimeoutMillis(int, int);
     method public void interrupt();
     method public static boolean isAccessibilityButtonSupported();
     method public boolean isEnabled();
@@ -50151,6 +50529,9 @@
     method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
     method public boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
     method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    field public static final int FLAG_CONTENT_CONTROLS = 4; // 0x4
+    field public static final int FLAG_CONTENT_ICONS = 1; // 0x1
+    field public static final int FLAG_CONTENT_TEXT = 2; // 0x2
   }
 
   public static abstract interface AccessibilityManager.AccessibilityStateChangeListener {
@@ -51319,8 +51700,112 @@
 
 }
 
+package android.view.intelligence {
+
+  public final class IntelligenceManager {
+    method public void disableContentCapture();
+    method public android.content.ComponentName getIntelligenceServiceComponentName();
+    method public boolean isContentCaptureEnabled();
+    field public static final int FLAG_USER_INPUT = 1; // 0x1
+  }
+
+}
+
 package android.view.textclassifier {
 
+  public final class ConversationActions implements android.os.Parcelable {
+    ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationActions.ConversationAction>);
+    method public int describeContents();
+    method public java.util.List<android.view.textclassifier.ConversationActions.ConversationAction> getConversationActions();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR;
+    field public static final java.lang.String HINT_FOR_IN_APP = "in_app";
+    field public static final java.lang.String HINT_FOR_NOTIFICATION = "notification";
+    field public static final java.lang.String TYPE_CALL_PHONE = "call_phone";
+    field public static final java.lang.String TYPE_CREATE_REMINDER = "create_reminder";
+    field public static final java.lang.String TYPE_OPEN_URL = "open_url";
+    field public static final java.lang.String TYPE_SEND_EMAIL = "send_email";
+    field public static final java.lang.String TYPE_SEND_SMS = "send_sms";
+    field public static final java.lang.String TYPE_SHARE_LOCATION = "share_location";
+    field public static final java.lang.String TYPE_TEXT_REPLY = "text_reply";
+    field public static final java.lang.String TYPE_TRACK_FLIGHT = "track_flight";
+    field public static final java.lang.String TYPE_VIEW_CALENDAR = "view_calendar";
+    field public static final java.lang.String TYPE_VIEW_MAP = "view_map";
+  }
+
+  public static final class ConversationActions.ConversationAction implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.app.RemoteAction getAction();
+    method public float getConfidenceScore();
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getTextReply();
+    method public java.lang.String getType();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.ConversationAction> CREATOR;
+  }
+
+  public static final class ConversationActions.ConversationAction.Builder {
+    ctor public ConversationActions.ConversationAction.Builder(java.lang.String);
+    method public android.view.textclassifier.ConversationActions.ConversationAction build();
+    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setAction(android.app.RemoteAction);
+    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setConfidenceScore(float);
+    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setExtras(android.os.Bundle);
+    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setTextReply(java.lang.CharSequence);
+  }
+
+  public static final class ConversationActions.Message implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.app.Person getAuthor();
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getText();
+    method public java.time.ZonedDateTime getTime();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Message> CREATOR;
+  }
+
+  public static final class ConversationActions.Message.Builder {
+    ctor public ConversationActions.Message.Builder();
+    method public android.view.textclassifier.ConversationActions.Message build();
+    method public android.view.textclassifier.ConversationActions.Message.Builder setAuthor(android.app.Person);
+    method public android.view.textclassifier.ConversationActions.Message.Builder setComposeTime(java.time.ZonedDateTime);
+    method public android.view.textclassifier.ConversationActions.Message.Builder setExtras(android.os.Bundle);
+    method public android.view.textclassifier.ConversationActions.Message.Builder setText(java.lang.CharSequence);
+  }
+
+  public static final class ConversationActions.Request implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.List<android.view.textclassifier.ConversationActions.Message> getConversation();
+    method public java.util.List<java.lang.String> getHints();
+    method public int getMaxSuggestions();
+    method public android.view.textclassifier.ConversationActions.TypeConfig getTypeConfig();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Request> CREATOR;
+  }
+
+  public static final class ConversationActions.Request.Builder {
+    ctor public ConversationActions.Request.Builder(java.util.List<android.view.textclassifier.ConversationActions.Message>);
+    method public android.view.textclassifier.ConversationActions.Request build();
+    method public android.view.textclassifier.ConversationActions.Request.Builder setHints(java.util.List<java.lang.String>);
+    method public android.view.textclassifier.ConversationActions.Request.Builder setMaxSuggestions(int);
+    method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.ConversationActions.TypeConfig);
+  }
+
+  public static final class ConversationActions.TypeConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.Collection<java.lang.String> resolveTypes(java.util.Collection<java.lang.String>);
+    method public boolean shouldIncludeTypesFromTextClassifier();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.TypeConfig> CREATOR;
+  }
+
+  public static final class ConversationActions.TypeConfig.Builder {
+    ctor public ConversationActions.TypeConfig.Builder();
+    method public android.view.textclassifier.ConversationActions.TypeConfig build();
+    method public android.view.textclassifier.ConversationActions.TypeConfig.Builder includeTypesFromTextClassifier(boolean);
+    method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setExcludedTypes(java.util.Collection<java.lang.String>);
+    method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setIncludedTypes(java.util.Collection<java.lang.String>);
+  }
+
   public final class SelectionEvent implements android.os.Parcelable {
     method public static android.view.textclassifier.SelectionEvent createSelectionActionEvent(int, int, int);
     method public static android.view.textclassifier.SelectionEvent createSelectionActionEvent(int, int, int, android.view.textclassifier.TextClassification);
@@ -51375,6 +51860,7 @@
     method public float getConfidenceScore(java.lang.String);
     method public java.lang.String getEntity(int);
     method public int getEntityCount();
+    method public android.os.Bundle getExtras();
     method public deprecated android.graphics.drawable.Drawable getIcon();
     method public java.lang.String getId();
     method public deprecated android.content.Intent getIntent();
@@ -51390,6 +51876,7 @@
     method public android.view.textclassifier.TextClassification.Builder addAction(android.app.RemoteAction);
     method public android.view.textclassifier.TextClassification build();
     method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float);
+    method public android.view.textclassifier.TextClassification.Builder setExtras(android.os.Bundle);
     method public deprecated android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable);
     method public android.view.textclassifier.TextClassification.Builder setId(java.lang.String);
     method public deprecated android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
@@ -51402,6 +51889,7 @@
     method public int describeContents();
     method public android.os.LocaleList getDefaultLocales();
     method public int getEndIndex();
+    method public android.os.Bundle getExtras();
     method public java.time.ZonedDateTime getReferenceTime();
     method public int getStartIndex();
     method public java.lang.CharSequence getText();
@@ -51413,6 +51901,7 @@
     ctor public TextClassification.Request.Builder(java.lang.CharSequence, int, int);
     method public android.view.textclassifier.TextClassification.Request build();
     method public android.view.textclassifier.TextClassification.Request.Builder setDefaultLocales(android.os.LocaleList);
+    method public android.view.textclassifier.TextClassification.Request.Builder setExtras(android.os.Bundle);
     method public android.view.textclassifier.TextClassification.Request.Builder setReferenceTime(java.time.ZonedDateTime);
   }
 
@@ -51457,6 +51946,7 @@
     method public default int getMaxGenerateLinksTextLength();
     method public default boolean isDestroyed();
     method public default void onSelectionEvent(android.view.textclassifier.SelectionEvent);
+    method public default android.view.textclassifier.ConversationActions suggestConversationActions(android.view.textclassifier.ConversationActions.Request);
     method public default android.view.textclassifier.TextSelection suggestSelection(android.view.textclassifier.TextSelection.Request);
     method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
     field public static final java.lang.String HINT_TEXT_IS_EDITABLE = "android.text_is_editable";
@@ -54185,6 +54675,7 @@
     method public void setLong(int, java.lang.String, long);
     method public void setOnClickFillInIntent(int, android.content.Intent);
     method public void setOnClickPendingIntent(int, android.app.PendingIntent);
+    method public void setOnClickResponse(int, android.widget.RemoteViews.RemoteResponse);
     method public void setPendingIntentTemplate(int, android.app.PendingIntent);
     method public void setProgressBar(int, int, int, boolean);
     method public void setRelativeScrollPosition(int, int);
@@ -54205,6 +54696,7 @@
     method public void showPrevious(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.widget.RemoteViews> CREATOR;
+    field public static final java.lang.String EXTRA_SHARED_ELEMENT_BOUNDS = "android.widget.extra.SHARED_ELEMENT_BOUNDS";
   }
 
   public static class RemoteViews.ActionException extends java.lang.RuntimeException {
@@ -54212,6 +54704,13 @@
     ctor public RemoteViews.ActionException(java.lang.String);
   }
 
+  public static class RemoteViews.RemoteResponse {
+    ctor public RemoteViews.RemoteResponse();
+    method public android.widget.RemoteViews.RemoteResponse addSharedElement(int, java.lang.String);
+    method public static android.widget.RemoteViews.RemoteResponse fromFillInIntent(android.content.Intent);
+    method public static android.widget.RemoteViews.RemoteResponse fromPendingIntent(android.app.PendingIntent);
+  }
+
   public static abstract class RemoteViews.RemoteView implements java.lang.annotation.Annotation {
   }
 
@@ -54808,6 +55307,9 @@
     method public android.os.LocaleList getTextLocales();
     method public android.text.PrecomputedText.Params getTextMetricsParams();
     method public float getTextScaleX();
+    method public android.graphics.drawable.Drawable getTextSelectHandle();
+    method public android.graphics.drawable.Drawable getTextSelectHandleLeft();
+    method public android.graphics.drawable.Drawable getTextSelectHandleRight();
     method public float getTextSize();
     method public int getTotalPaddingBottom();
     method public int getTotalPaddingEnd();
@@ -54936,6 +55438,12 @@
     method public void setTextLocales(android.os.LocaleList);
     method public void setTextMetricsParams(android.text.PrecomputedText.Params);
     method public void setTextScaleX(float);
+    method public void setTextSelectHandle(android.graphics.drawable.Drawable);
+    method public void setTextSelectHandle(int);
+    method public void setTextSelectHandleLeft(android.graphics.drawable.Drawable);
+    method public void setTextSelectHandleLeft(int);
+    method public void setTextSelectHandleRight(android.graphics.drawable.Drawable);
+    method public void setTextSelectHandleRight(int);
     method public void setTextSize(float);
     method public void setTextSize(int, float);
     method public final void setTransformationMethod(android.text.method.TransformationMethod);
@@ -73297,10 +73805,13 @@
     method public abstract void beginHandshake() throws javax.net.ssl.SSLException;
     method public abstract void closeInbound() throws javax.net.ssl.SSLException;
     method public abstract void closeOutbound();
+    method public java.lang.String getApplicationProtocol();
     method public abstract java.lang.Runnable getDelegatedTask();
     method public abstract boolean getEnableSessionCreation();
     method public abstract java.lang.String[] getEnabledCipherSuites();
     method public abstract java.lang.String[] getEnabledProtocols();
+    method public java.lang.String getHandshakeApplicationProtocol();
+    method public java.util.function.BiFunction<javax.net.ssl.SSLEngine, java.util.List<java.lang.String>, java.lang.String> getHandshakeApplicationProtocolSelector();
     method public javax.net.ssl.SSLSession getHandshakeSession();
     method public abstract javax.net.ssl.SSLEngineResult.HandshakeStatus getHandshakeStatus();
     method public abstract boolean getNeedClientAuth();
@@ -73317,6 +73828,7 @@
     method public abstract void setEnableSessionCreation(boolean);
     method public abstract void setEnabledCipherSuites(java.lang.String[]);
     method public abstract void setEnabledProtocols(java.lang.String[]);
+    method public void setHandshakeApplicationProtocolSelector(java.util.function.BiFunction<javax.net.ssl.SSLEngine, java.util.List<java.lang.String>, java.lang.String>);
     method public abstract void setNeedClientAuth(boolean);
     method public void setSSLParameters(javax.net.ssl.SSLParameters);
     method public abstract void setUseClientMode(boolean);
@@ -73375,6 +73887,7 @@
     ctor public SSLParameters(java.lang.String[]);
     ctor public SSLParameters(java.lang.String[], java.lang.String[]);
     method public java.security.AlgorithmConstraints getAlgorithmConstraints();
+    method public java.lang.String[] getApplicationProtocols();
     method public java.lang.String[] getCipherSuites();
     method public java.lang.String getEndpointIdentificationAlgorithm();
     method public boolean getNeedClientAuth();
@@ -73384,6 +73897,7 @@
     method public final boolean getUseCipherSuitesOrder();
     method public boolean getWantClientAuth();
     method public void setAlgorithmConstraints(java.security.AlgorithmConstraints);
+    method public void setApplicationProtocols(java.lang.String[]);
     method public void setCipherSuites(java.lang.String[]);
     method public void setEndpointIdentificationAlgorithm(java.lang.String);
     method public void setNeedClientAuth(boolean);
@@ -73488,9 +74002,12 @@
     ctor protected SSLSocket(java.lang.String, int, java.net.InetAddress, int) throws java.io.IOException, java.net.UnknownHostException;
     ctor protected SSLSocket(java.net.InetAddress, int, java.net.InetAddress, int) throws java.io.IOException;
     method public abstract void addHandshakeCompletedListener(javax.net.ssl.HandshakeCompletedListener);
+    method public java.lang.String getApplicationProtocol();
     method public abstract boolean getEnableSessionCreation();
     method public abstract java.lang.String[] getEnabledCipherSuites();
     method public abstract java.lang.String[] getEnabledProtocols();
+    method public java.lang.String getHandshakeApplicationProtocol();
+    method public java.util.function.BiFunction<javax.net.ssl.SSLSocket, java.util.List<java.lang.String>, java.lang.String> getHandshakeApplicationProtocolSelector();
     method public javax.net.ssl.SSLSession getHandshakeSession();
     method public abstract boolean getNeedClientAuth();
     method public javax.net.ssl.SSLParameters getSSLParameters();
@@ -73503,6 +74020,7 @@
     method public abstract void setEnableSessionCreation(boolean);
     method public abstract void setEnabledCipherSuites(java.lang.String[]);
     method public abstract void setEnabledProtocols(java.lang.String[]);
+    method public void setHandshakeApplicationProtocolSelector(java.util.function.BiFunction<javax.net.ssl.SSLSocket, java.util.List<java.lang.String>, java.lang.String>);
     method public abstract void setNeedClientAuth(boolean);
     method public void setSSLParameters(javax.net.ssl.SSLParameters);
     method public abstract void setUseClientMode(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 7e51082..da1adac 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24,6 +24,7 @@
     field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
     field public static final java.lang.String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
     field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
+    field public static final java.lang.String BIND_INTELLIGENCE_SERVICE = "android.permission.BIND_INTELLIGENCE_SERVICE";
     field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
     field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
@@ -93,14 +94,17 @@
     field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
     field public static final java.lang.String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
     field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO";
+    field public static final java.lang.String MANAGE_ACCESSIBILITY = "android.permission.MANAGE_ACCESSIBILITY";
     field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
     field public static final java.lang.String MANAGE_APP_OPS_RESTRICTIONS = "android.permission.MANAGE_APP_OPS_RESTRICTIONS";
     field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
     field public static final java.lang.String MANAGE_AUTO_FILL = "android.permission.MANAGE_AUTO_FILL";
     field public static final java.lang.String MANAGE_CARRIER_OEM_UNLOCK_STATE = "android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE";
     field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
+    field public static final java.lang.String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
     field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
+    field public static final java.lang.String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
     field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
     field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
     field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
@@ -291,8 +295,13 @@
   }
 
   public class AppOpsManager {
+    method public java.util.List<android.app.AppOpsManager.HistoricalPackageOps> getAllHistoricPackagesOps(java.lang.String[], long, long);
+    method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long);
     method public static java.lang.String[] getOpStrs();
     method public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, java.lang.String, int[]);
+    method public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOpStrs(java.lang.String[]);
+    method public static int opToDefaultMode(java.lang.String);
+    method public static java.lang.String opToPermission(java.lang.String);
     method public void setMode(java.lang.String, int, java.lang.String, int);
     method public void setUidMode(java.lang.String, int, int);
     field public static final java.lang.String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
@@ -338,6 +347,39 @@
     field public static final java.lang.String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
     field public static final java.lang.String OPSTR_WRITE_SMS = "android:write_sms";
     field public static final java.lang.String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
+    field public static final int UID_STATE_BACKGROUND = 4; // 0x4
+    field public static final int UID_STATE_CACHED = 5; // 0x5
+    field public static final int UID_STATE_FOREGROUND = 3; // 0x3
+    field public static final int UID_STATE_FOREGROUND_SERVICE = 2; // 0x2
+    field public static final int UID_STATE_PERSISTENT = 0; // 0x0
+    field public static final int UID_STATE_TOP = 1; // 0x1
+  }
+
+  public static final class AppOpsManager.HistoricalOpEntry implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getAccessCount(int);
+    method public long getAccessDuration(int);
+    method public long getBackgroundAccessCount();
+    method public long getBackgroundAccessDuration();
+    method public long getBackgroundRejectCount();
+    method public long getForegroundAccessCount();
+    method public long getForegroundAccessDuration();
+    method public long getForegroundRejectCount();
+    method public java.lang.String getOp();
+    method public long getRejectCount(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOpEntry> CREATOR;
+  }
+
+  public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.app.AppOpsManager.HistoricalOpEntry getEntry(java.lang.String);
+    method public android.app.AppOpsManager.HistoricalOpEntry getEntryAt(int);
+    method public int getEntryCount();
+    method public java.lang.String getPackageName();
+    method public int getUid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalPackageOps> CREATOR;
   }
 
   public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
@@ -573,6 +615,14 @@
 
 }
 
+package android.app.assist {
+
+  public static class AssistStructure.ViewNode {
+    ctor public AssistStructure.ViewNode();
+  }
+
+}
+
 package android.app.backup {
 
   public class BackupDataInput {
@@ -802,6 +852,26 @@
 
 }
 
+package android.app.role {
+
+  public final class RoleManager {
+    method public void addRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+    method public boolean addRoleHolderFromController(java.lang.String, java.lang.String);
+    method public void clearRoleHoldersAsUser(java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+    method public java.util.List<java.lang.String> getRoleHolders(java.lang.String);
+    method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
+    method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+    method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String);
+    field public static final java.lang.String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
+  }
+
+  public abstract interface RoleManagerCallback {
+    method public abstract void onFailure();
+    method public abstract void onSuccess();
+  }
+
+}
+
 package android.app.usage {
 
   public final class CacheQuotaHint implements android.os.Parcelable {
@@ -850,9 +920,11 @@
     method public int getAppStandbyBucket(java.lang.String);
     method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets();
     method public void registerAppUsageObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, android.app.PendingIntent);
+    method public void registerUsageSessionObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit, android.app.PendingIntent, android.app.PendingIntent);
     method public void setAppStandbyBucket(java.lang.String, int);
     method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>);
     method public void unregisterAppUsageObserver(int);
+    method public void unregisterUsageSessionObserver(int);
     method public void whitelistAppTemporarily(java.lang.String, long, android.os.UserHandle);
     field public static final java.lang.String EXTRA_OBSERVER_ID = "android.app.usage.extra.OBSERVER_ID";
     field public static final java.lang.String EXTRA_TIME_LIMIT = "android.app.usage.extra.TIME_LIMIT";
@@ -987,8 +1059,9 @@
     field public static final java.lang.String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
     field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
     field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
-    field public static final java.lang.String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
+    field public static final java.lang.String ACTION_REVIEW_APP_PERMISSION_USAGE = "android.intent.action.REVIEW_APP_PERMISSION_USAGE";
     field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
+    field public static final java.lang.String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
     field public static final java.lang.String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
     field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
     field public static final java.lang.String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
@@ -1148,6 +1221,7 @@
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
     method public abstract void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
     method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
+    method public void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName);
     method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
     method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
@@ -3541,7 +3615,7 @@
     field public byte id;
   }
 
-  public class WifiConfiguration implements android.os.Parcelable {
+  public deprecated class WifiConfiguration implements android.os.Parcelable {
     method public boolean hasNoInternetAccess();
     method public boolean isEphemeral();
     method public boolean isNoInternetAccessExpected();
@@ -3567,11 +3641,16 @@
     method public int getWifiApState();
     method public boolean isDeviceToApRttSupported();
     method public boolean isDeviceToDeviceRttSupported();
+    method public boolean isOweSupported();
     method public boolean isPortableHotspotSupported();
     method public boolean isWifiApEnabled();
     method public boolean isWifiScannerSupported();
+    method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler);
+    method public boolean isWpa3SaeSupported();
+    method public boolean isWpa3SuiteBSupported();
     method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
     method public boolean startScan(android.os.WorkSource);
+    method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
     field public static final int CHANGE_REASON_ADDED = 0; // 0x0
     field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
     field public static final int CHANGE_REASON_REMOVED = 1; // 0x1
@@ -3599,6 +3678,18 @@
     method public abstract void onSuccess();
   }
 
+  public static abstract interface WifiManager.NetworkRequestMatchCallback {
+    method public abstract void onMatch(java.util.List<android.net.wifi.WifiConfiguration>);
+    method public abstract void onUserSelectionCallbackRegistration(android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback);
+    method public abstract void onUserSelectionConnectFailure(android.net.wifi.WifiConfiguration);
+    method public abstract void onUserSelectionConnectSuccess(android.net.wifi.WifiConfiguration);
+  }
+
+  public static abstract interface WifiManager.NetworkRequestUserSelectionCallback {
+    method public abstract void reject();
+    method public abstract void select(android.net.wifi.WifiConfiguration);
+  }
+
   public class WifiNetworkConnectionStatistics implements android.os.Parcelable {
     ctor public WifiNetworkConnectionStatistics(int, int);
     ctor public WifiNetworkConnectionStatistics();
@@ -3741,6 +3832,14 @@
 
 }
 
+package android.net.wifi.p2p {
+
+  public class WifiP2pManager {
+    method public void factoryReset(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+  }
+
+}
+
 package android.net.wifi.rtt {
 
   public static final class RangingRequest.Builder {
@@ -4154,8 +4253,11 @@
     method public deprecated int getUserRestrictionSource(java.lang.String, android.os.UserHandle);
     method public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(java.lang.String, android.os.UserHandle);
     method public boolean hasRestrictedProfiles();
+    method public boolean isAdminUser();
+    method public boolean isGuestUser();
     method public boolean isManagedProfile();
     method public boolean isManagedProfile(int);
+    method public boolean isPrimaryUser();
     method public boolean isRestrictedProfile();
     field public static final java.lang.String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
     field public static final deprecated java.lang.String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
@@ -4436,6 +4538,10 @@
   public final class Settings {
     field public static final java.lang.String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
+    field public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10; // 0xa
+    field public static final int USER_SETUP_PERSONALIZATION_NOT_STARTED = 0; // 0x0
+    field public static final int USER_SETUP_PERSONALIZATION_PAUSED = 2; // 0x2
+    field public static final int USER_SETUP_PERSONALIZATION_STARTED = 1; // 0x1
   }
 
   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
@@ -4446,10 +4552,13 @@
     field public static final java.lang.String CARRIER_APP_NAMES = "carrier_app_names";
     field public static final java.lang.String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
     field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
+    field public static final java.lang.String DEVICE_DEMO_MODE = "device_demo_mode";
+    field public static final java.lang.String DEVICE_PROVISIONING_MOBILE_DATA_ENABLED = "device_provisioning_mobile_data";
     field public static final java.lang.String EUICC_PROVISIONED = "euicc_provisioned";
     field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
     field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
     field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
+    field public static final java.lang.String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
     field public static final java.lang.String SMS_ACCESS_RESTRICTION_ENABLED = "sms_access_restriction_enabled";
     field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on";
     field public static final java.lang.String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
@@ -4460,15 +4569,23 @@
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
     method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
+    field public static final java.lang.String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
     field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
     field public static final java.lang.String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
+    field public static final java.lang.String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
+    field public static final java.lang.String DOZE_ALWAYS_ON = "doze_always_on";
     field public static final java.lang.String HUSH_GESTURE_USED = "hush_gesture_used";
     field public static final java.lang.String INSTANT_APPS_ENABLED = "instant_apps_enabled";
+    field public static final java.lang.String LAST_SETUP_SHOWN = "last_setup_shown";
+    field public static final java.lang.String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
+    field public static final java.lang.String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
     field public static final java.lang.String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count";
+    field public static final java.lang.String USER_SETUP_COMPLETE = "user_setup_complete";
+    field public static final java.lang.String USER_SETUP_PERSONALIZATION_STATE = "user_setup_personalization_state";
     field public static final java.lang.String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
   }
 
@@ -4507,6 +4624,19 @@
 
 }
 
+package android.rolecontrollerservice {
+
+  public abstract class RoleControllerService extends android.app.Service {
+    ctor public RoleControllerService();
+    method public abstract void onAddRoleHolder(java.lang.String, java.lang.String, android.app.role.RoleManagerCallback);
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract void onClearRoleHolders(java.lang.String, android.app.role.RoleManagerCallback);
+    method public abstract void onRemoveRoleHolder(java.lang.String, java.lang.String, android.app.role.RoleManagerCallback);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.rolecontrollerservice.RoleControllerService";
+  }
+
+}
+
 package android.security.keystore {
 
   public abstract class AttestationUtils {
@@ -4765,6 +4895,36 @@
 
 }
 
+package android.service.intelligence {
+
+  public abstract class IntelligenceService extends android.app.Service {
+    ctor public IntelligenceService();
+    method public abstract void onContentCaptureEvent(android.service.intelligence.InteractionSessionId, java.util.List<android.view.intelligence.ContentCaptureEvent>);
+    method public void onCreateInteractionSession(android.service.intelligence.InteractionContext, android.service.intelligence.InteractionSessionId);
+    method public void onDestroyInteractionSession(android.service.intelligence.InteractionSessionId);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.intelligence.IntelligenceService";
+  }
+
+  public final class InteractionContext implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.content.ComponentName getActivityComponent();
+    method public int getDisplayId();
+    method public int getFlags();
+    method public int getTaskId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.intelligence.InteractionContext> CREATOR;
+    field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1
+    field public static final int FLAG_DISABLED_BY_FLAG_SECURE = 2; // 0x2
+  }
+
+  public final class InteractionSessionId implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.intelligence.InteractionSessionId> CREATOR;
+  }
+
+}
+
 package android.service.notification {
 
   public final class Adjustment implements android.os.Parcelable {
@@ -5286,6 +5446,7 @@
 
   public class CarrierConfigManager {
     method public static android.os.PersistableBundle getDefaultConfig();
+    method public void overrideConfig(int, android.os.PersistableBundle);
     method public void updateConfigForPhoneId(int, java.lang.String);
     field public static final java.lang.String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
   }
@@ -5407,8 +5568,12 @@
     method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
     field public static final java.lang.String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
     field public static final java.lang.String ACTION_REFRESH_SUBSCRIPTION_PLANS = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
-    field public static final android.net.Uri ENHANCED_4G_ENABLED_CONTENT_URI;
+    field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
+    field public static final android.net.Uri VT_ENABLED_CONTENT_URI;
     field public static final android.net.Uri WFC_ENABLED_CONTENT_URI;
+    field public static final android.net.Uri WFC_MODE_CONTENT_URI;
+    field public static final android.net.Uri WFC_ROAMING_ENABLED_CONTENT_URI;
+    field public static final android.net.Uri WFC_ROAMING_MODE_CONTENT_URI;
   }
 
   public final class SubscriptionPlan implements android.os.Parcelable {
@@ -5492,6 +5657,7 @@
     method public int getRadioPowerState();
     method public int getSimApplicationState();
     method public int getSimCardState();
+    method public int getSupportedRadioAccessFamily();
     method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
     method public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
     method public android.os.Bundle getVisualVoicemailSettings();
@@ -5499,10 +5665,15 @@
     method public boolean handlePinMmi(java.lang.String);
     method public boolean handlePinMmiForSubscriber(int, java.lang.String);
     method public boolean isDataConnectivityPossible();
+    method public deprecated boolean isIdle();
+    method public deprecated boolean isOffhook();
     method public deprecated boolean isRadioOn();
+    method public deprecated boolean isRinging();
     method public boolean isVideoCallingEnabled();
     method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
     method public boolean needsOtaServiceProvisioning();
+    method public boolean rebootRadio();
+    method public boolean resetRadioConfig();
     method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
     method public void setCarrierDataEnabled(boolean);
     method public void setDataActivationState(int);
@@ -5554,6 +5725,24 @@
     field public static final int NETWORK_MODE_TDSCDMA_WCDMA = 14; // 0xe
     field public static final int NETWORK_MODE_WCDMA_ONLY = 2; // 0x2
     field public static final int NETWORK_MODE_WCDMA_PREF = 0; // 0x0
+    field public static final int NETWORK_TYPE_BITMASK_1xRTT = 128; // 0x80
+    field public static final int NETWORK_TYPE_BITMASK_CDMA = 16; // 0x10
+    field public static final int NETWORK_TYPE_BITMASK_EDGE = 4; // 0x4
+    field public static final int NETWORK_TYPE_BITMASK_EHRPD = 16384; // 0x4000
+    field public static final int NETWORK_TYPE_BITMASK_EVDO_0 = 32; // 0x20
+    field public static final int NETWORK_TYPE_BITMASK_EVDO_A = 64; // 0x40
+    field public static final int NETWORK_TYPE_BITMASK_EVDO_B = 4096; // 0x1000
+    field public static final int NETWORK_TYPE_BITMASK_GPRS = 2; // 0x2
+    field public static final int NETWORK_TYPE_BITMASK_GSM = 65536; // 0x10000
+    field public static final int NETWORK_TYPE_BITMASK_HSDPA = 256; // 0x100
+    field public static final int NETWORK_TYPE_BITMASK_HSPA = 1024; // 0x400
+    field public static final int NETWORK_TYPE_BITMASK_HSPAP = 32768; // 0x8000
+    field public static final int NETWORK_TYPE_BITMASK_HSUPA = 512; // 0x200
+    field public static final int NETWORK_TYPE_BITMASK_LTE = 8192; // 0x2000
+    field public static final int NETWORK_TYPE_BITMASK_LTE_CA = 524288; // 0x80000
+    field public static final int NETWORK_TYPE_BITMASK_TD_SCDMA = 131072; // 0x20000
+    field public static final int NETWORK_TYPE_BITMASK_UMTS = 8; // 0x8
+    field public static final int NETWORK_TYPE_BITMASK_UNKNOWN = 1; // 0x1
     field public static final int RADIO_POWER_OFF = 0; // 0x0
     field public static final int RADIO_POWER_ON = 1; // 0x1
     field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
@@ -5743,6 +5932,7 @@
     field public static final int RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 2; // 0x2
     field public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1; // 0x1
     field public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 4; // 0x4
+    field public static final int RESULT_CALLER_NOT_ALLOWED = -3; // 0xfffffffd
     field public static final int RESULT_EUICC_NOT_FOUND = -2; // 0xfffffffe
     field public static final int RESULT_OK = 0; // 0x0
     field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff
@@ -5828,7 +6018,18 @@
     method public int getTimeSeconds();
     method public int getToA();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CDIV_CF_REASON_ALL = 4; // 0x4
+    field public static final int CDIV_CF_REASON_ALL_CONDITIONAL = 5; // 0x5
+    field public static final int CDIV_CF_REASON_BUSY = 1; // 0x1
+    field public static final int CDIV_CF_REASON_NOT_LOGGED_IN = 6; // 0x6
+    field public static final int CDIV_CF_REASON_NOT_REACHABLE = 3; // 0x3
+    field public static final int CDIV_CF_REASON_NO_REPLY = 2; // 0x2
+    field public static final int CDIV_CF_REASON_UNCONDITIONAL = 0; // 0x0
     field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallForwardInfo> CREATOR;
+    field public static final int STATUS_ACTIVE = 1; // 0x1
+    field public static final int STATUS_NOT_ACTIVE = 0; // 0x0
+    field public static final int TYPE_OF_ADDRESS_INTERNATIONAL = 145; // 0x91
+    field public static final int TYPE_OF_ADDRESS_UNKNOWN = 129; // 0x81
   }
 
   public final class ImsCallProfile implements android.os.Parcelable {
@@ -5856,6 +6057,7 @@
     method public void setCallExtra(java.lang.String, java.lang.String);
     method public void setCallExtraBoolean(java.lang.String, boolean);
     method public void setCallExtraInt(java.lang.String, int);
+    method public void setCallRestrictCause(int);
     method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
     method public void updateCallType(android.telephony.ims.ImsCallProfile);
     method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -6164,6 +6366,13 @@
   public final class ImsSsData implements android.os.Parcelable {
     ctor public ImsSsData(int, int, int, int, int);
     method public int describeContents();
+    method public android.telephony.ims.ImsCallForwardInfo[] getCallForwardInfo();
+    method public int getRequestType();
+    method public int getResult();
+    method public int getServiceClass();
+    method public int getServiceType();
+    method public android.telephony.ims.ImsSsInfo[] getSuppServiceInfo();
+    method public int getTeleserviceType();
     method public boolean isTypeBarring();
     method public boolean isTypeCf();
     method public boolean isTypeClip();
@@ -6176,6 +6385,16 @@
     method public boolean isTypeUnConditional();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsData> CREATOR;
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+    field public static final int SERVICE_CLASS_DATA = 2; // 0x2
+    field public static final int SERVICE_CLASS_DATA_CIRCUIT_ASYNC = 32; // 0x20
+    field public static final int SERVICE_CLASS_DATA_CIRCUIT_SYNC = 16; // 0x10
+    field public static final int SERVICE_CLASS_DATA_PACKET_ACCESS = 64; // 0x40
+    field public static final int SERVICE_CLASS_DATA_PAD = 128; // 0x80
+    field public static final int SERVICE_CLASS_FAX = 4; // 0x4
+    field public static final int SERVICE_CLASS_NONE = 0; // 0x0
+    field public static final int SERVICE_CLASS_SMS = 8; // 0x8
+    field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
     field public static final int SS_ACTIVATION = 0; // 0x0
     field public static final int SS_ALL_BARRING = 18; // 0x12
     field public static final int SS_ALL_DATA_TELESERVICES = 3; // 0x3
@@ -6212,13 +6431,31 @@
     field public static final int SS_WAIT = 12; // 0xc
   }
 
+  public static class ImsSsData.Builder {
+    ctor public ImsSsData.Builder(int, int, int, int, int);
+    method public android.telephony.ims.ImsSsData build();
+    method public android.telephony.ims.ImsSsData.Builder setCallForwardingInfo(android.telephony.ims.ImsCallForwardInfo[]);
+    method public android.telephony.ims.ImsSsData.Builder setSuppServiceInfo(android.telephony.ims.ImsSsInfo[]);
+  }
+
   public final class ImsSsInfo implements android.os.Parcelable {
-    ctor public ImsSsInfo(int, java.lang.String);
+    ctor public deprecated ImsSsInfo(int, java.lang.String);
     method public int describeContents();
-    method public java.lang.String getIcbNum();
+    method public int getClirInterrogationStatus();
+    method public int getClirOutgoingState();
+    method public deprecated java.lang.String getIcbNum();
+    method public java.lang.String getIncomingCommunicationBarringNumber();
     method public int getProvisionStatus();
     method public int getStatus();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CLIR_OUTGOING_DEFAULT = 0; // 0x0
+    field public static final int CLIR_OUTGOING_INVOCATION = 1; // 0x1
+    field public static final int CLIR_OUTGOING_SUPPRESSION = 2; // 0x2
+    field public static final int CLIR_STATUS_NOT_PROVISIONED = 0; // 0x0
+    field public static final int CLIR_STATUS_PROVISIONED_PERMANENT = 1; // 0x1
+    field public static final int CLIR_STATUS_TEMPORARILY_ALLOWED = 4; // 0x4
+    field public static final int CLIR_STATUS_TEMPORARILY_RESTRICTED = 3; // 0x3
+    field public static final int CLIR_STATUS_UNKNOWN = 2; // 0x2
     field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsInfo> CREATOR;
     field public static final int DISABLED = 0; // 0x0
     field public static final int ENABLED = 1; // 0x1
@@ -6228,6 +6465,21 @@
     field public static final int SERVICE_PROVISIONING_UNKNOWN = -1; // 0xffffffff
   }
 
+  public static class ImsSsInfo.Builder {
+    ctor public ImsSsInfo.Builder(int);
+    method public android.telephony.ims.ImsSsInfo build();
+    method public android.telephony.ims.ImsSsInfo.Builder setClirInterrogationStatus(int);
+    method public android.telephony.ims.ImsSsInfo.Builder setClirOutgoingState(int);
+    method public android.telephony.ims.ImsSsInfo.Builder setIncomingCommunicationBarringNumber(java.lang.String);
+    method public android.telephony.ims.ImsSsInfo.Builder setProvisionStatus(int);
+  }
+
+  public static abstract class ImsSsInfo.ClirInterrogationStatus implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ImsSsInfo.ClirOutgoingState implements java.lang.annotation.Annotation {
+  }
+
   public final class ImsStreamMediaProfile implements android.os.Parcelable {
     ctor public ImsStreamMediaProfile(int, int, int, int, int);
     method public void copyFrom(android.telephony.ims.ImsStreamMediaProfile);
@@ -6708,6 +6960,48 @@
 
 }
 
+package android.view.accessibility {
+
+  public final class AccessibilityManager {
+    method public void performAccessibilityShortcut();
+  }
+
+}
+
+package android.view.intelligence {
+
+  public final class ContentCaptureEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getEventTime();
+    method public int getFlags();
+    method public android.view.autofill.AutofillId getId();
+    method public java.lang.CharSequence getText();
+    method public int getType();
+    method public android.view.intelligence.ViewNode getViewNode();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.intelligence.ContentCaptureEvent> CREATOR;
+    field public static final int TYPE_ACTIVITY_PAUSED = 3; // 0x3
+    field public static final int TYPE_ACTIVITY_RESUMED = 2; // 0x2
+    field public static final int TYPE_ACTIVITY_STARTED = 1; // 0x1
+    field public static final int TYPE_ACTIVITY_STOPPED = 4; // 0x4
+    field public static final int TYPE_VIEW_ADDED = 5; // 0x5
+    field public static final int TYPE_VIEW_REMOVED = 6; // 0x6
+    field public static final int TYPE_VIEW_TEXT_CHANGED = 7; // 0x7
+  }
+
+  public final class IntelligenceManager {
+    method public java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
+    method public java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
+    method public void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
+    method public void setPackageContentCaptureEnabled(java.lang.String, boolean);
+  }
+
+  public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
+    method public android.view.autofill.AutofillId getParentAutofillId();
+  }
+
+}
+
 package android.webkit {
 
   public abstract class CookieManager {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 4e7a114..4beb699 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -116,9 +116,6 @@
   public class TelephonyManager {
     method public deprecated void answerRingingCall();
     method public deprecated boolean endCall();
-    method public deprecated boolean isIdle();
-    method public deprecated boolean isOffhook();
-    method public deprecated boolean isRinging();
     method public deprecated void silenceRinger();
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 463c9d3..b2cf4c8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -78,6 +78,8 @@
   }
 
   public class AppOpsManager {
+    method public java.util.List<android.app.AppOpsManager.HistoricalPackageOps> getAllHistoricPackagesOps(java.lang.String[], long, long);
+    method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long);
     method public static int getNumOps();
     method public static java.lang.String[] getOpStrs();
     method public boolean isOperationActive(int, int, java.lang.String);
@@ -135,12 +137,42 @@
     field public static final int OP_SYSTEM_ALERT_WINDOW = 24; // 0x18
   }
 
+  public static final class AppOpsManager.HistoricalOpEntry implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getAccessCount(int);
+    method public long getAccessDuration(int);
+    method public long getBackgroundAccessCount();
+    method public long getBackgroundAccessDuration();
+    method public long getBackgroundRejectCount();
+    method public long getForegroundAccessCount();
+    method public long getForegroundAccessDuration();
+    method public long getForegroundRejectCount();
+    method public java.lang.String getOp();
+    method public long getRejectCount(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOpEntry> CREATOR;
+  }
+
+  public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.app.AppOpsManager.HistoricalOpEntry getEntry(java.lang.String);
+    method public android.app.AppOpsManager.HistoricalOpEntry getEntryAt(int);
+    method public int getEntryCount();
+    method public java.lang.String getPackageName();
+    method public int getUid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalPackageOps> CREATOR;
+  }
+
   public static abstract interface AppOpsManager.OnOpActiveChangedListener {
     method public abstract void onOpActiveChanged(int, int, java.lang.String, boolean);
   }
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
+    method public int getUserLockedFields();
+    method public void lockFields(int);
     method public void setBlocked(boolean);
+    field public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 2; // 0x2
   }
 
   public class NotificationManager {
@@ -176,10 +208,12 @@
     method public int getActivityType();
     method public android.graphics.Rect getAppBounds();
     method public android.graphics.Rect getBounds();
+    method public int getRotation();
     method public int getWindowingMode();
     method public void setActivityType(int);
     method public void setAppBounds(android.graphics.Rect);
     method public void setBounds(android.graphics.Rect);
+    method public void setRotation(int);
     method public void setTo(android.app.WindowConfiguration);
     method public void setWindowingMode(int);
     method public void writeToParcel(android.os.Parcel, int);
@@ -188,6 +222,7 @@
     field public static final int ACTIVITY_TYPE_RECENTS = 3; // 0x3
     field public static final int ACTIVITY_TYPE_STANDARD = 1; // 0x1
     field public static final int ACTIVITY_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int ROTATION_UNDEFINED = -1; // 0xffffffff
     field public static final int WINDOWING_MODE_FREEFORM = 5; // 0x5
     field public static final int WINDOWING_MODE_FULLSCREEN = 1; // 0x1
     field public static final int WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY = 4; // 0x4
@@ -575,12 +610,6 @@
     method public static boolean isEncodingLinearPcm(int);
   }
 
-  public final class AudioPresentation {
-    ctor public AudioPresentation(int, int, java.util.Map<java.lang.String, java.lang.String>, java.lang.String, int, boolean, boolean, boolean);
-    method public int getPresentationId();
-    method public int getProgramId();
-  }
-
   public final class BufferingParams implements android.os.Parcelable {
     method public int describeContents();
     method public int getInitialMarkMs();
@@ -957,6 +986,7 @@
 
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
+    field public static final java.lang.String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
     field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
     field public static final java.lang.String AUTOFILL_SERVICE = "autofill_service";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
@@ -1183,6 +1213,10 @@
 
 package android.telephony {
 
+  public class CarrierConfigManager {
+    method public void overrideConfig(int, android.os.PersistableBundle);
+  }
+
   public class MbmsDownloadSession implements java.lang.AutoCloseable {
     field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA = "mbms-download-service-override";
   }
@@ -1205,6 +1239,7 @@
 
   public class TelephonyManager {
     method public int getCarrierIdListVersion();
+    method public boolean isRttSupported();
     method public void refreshUiccProfile();
     method public void setCarrierTestOverride(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff
@@ -1320,6 +1355,14 @@
 
 }
 
+package android.util {
+
+  public final class ArraySet<E> implements java.util.Collection java.util.Set {
+    method public E valueAtUnchecked(int);
+  }
+
+}
+
 package android.util.proto {
 
   public final class EncodedBuffer {
@@ -1604,6 +1647,17 @@
 
 package android.view.accessibility {
 
+  public final class AccessibilityManager {
+    method public void addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, android.os.Handler);
+    method public java.lang.String getAccessibilityShortcutService();
+    method public void performAccessibilityShortcut();
+    method public void removeAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
+  }
+
+  public static abstract interface AccessibilityManager.AccessibilityServicesStateChangeListener {
+    method public abstract void onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager);
+  }
+
   public class AccessibilityNodeInfo implements android.os.Parcelable {
     method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
     method public void writeToParcelNoRecycle(android.os.Parcel, int);
diff --git a/cmds/idmap2/.clang-format b/cmds/idmap2/.clang-format
new file mode 100644
index 0000000..c91502a
--- /dev/null
+++ b/cmds/idmap2/.clang-format
@@ -0,0 +1,7 @@
+BasedOnStyle: Google
+ColumnLimit: 100
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+PointerAlignment: Left
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
new file mode 100644
index 0000000..5a6c813
--- /dev/null
+++ b/cmds/idmap2/Android.bp
@@ -0,0 +1,191 @@
+// Copyright (C) 2018 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.
+
+cc_library {
+    name: "libidmap2",
+    host_supported: true,
+    tidy: true,
+    tidy_flags: [
+        "-system-headers",
+        "-warnings-as-errors=*",
+    ],
+    srcs: [
+        "libidmap2/BinaryStreamVisitor.cpp",
+        "libidmap2/CommandLineOptions.cpp",
+        "libidmap2/FileUtils.cpp",
+        "libidmap2/Idmap.cpp",
+        "libidmap2/PrettyPrintVisitor.cpp",
+        "libidmap2/RawPrintVisitor.cpp",
+        "libidmap2/ResourceUtils.cpp",
+        "libidmap2/Xml.cpp",
+        "libidmap2/ZipFile.cpp",
+    ],
+    export_include_dirs: ["include"],
+    target: {
+        android: {
+            static: {
+                enabled: false,
+            },
+            shared_libs: [
+                "libandroidfw",
+                "libbase",
+                "libutils",
+                "libziparchive",
+            ],
+        },
+        host: {
+            shared: {
+                enabled: false,
+            },
+            static_libs: [
+                "libandroidfw",
+                "libbase",
+                "libutils",
+                "libziparchive",
+            ],
+        },
+    },
+}
+
+cc_test {
+    name: "idmap2_tests",
+    host_supported: true,
+    tidy: true,
+    tidy_flags: [
+        "-system-headers",
+        "-warnings-as-errors=*",
+    ],
+    srcs: [
+        "tests/BinaryStreamVisitorTests.cpp",
+        "tests/CommandLineOptionsTests.cpp",
+        "tests/FileUtilsTests.cpp",
+        "tests/Idmap2BinaryTests.cpp",
+        "tests/IdmapTests.cpp",
+        "tests/Main.cpp",
+        "tests/PrettyPrintVisitorTests.cpp",
+        "tests/RawPrintVisitorTests.cpp",
+        "tests/ResourceUtilsTests.cpp",
+        "tests/XmlTests.cpp",
+        "tests/ZipFileTests.cpp",
+    ],
+    required: [
+        "idmap2",
+    ],
+    static_libs: ["libgmock"],
+    target: {
+        android: {
+            shared_libs: [
+                "libandroidfw",
+                "libbase",
+                "libidmap2",
+                "liblog",
+                "libutils",
+                "libz",
+                "libziparchive",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libandroidfw",
+                "libbase",
+                "libidmap2",
+                "liblog",
+                "libutils",
+                "libziparchive",
+            ],
+            shared_libs: [
+                "libz",
+            ],
+        },
+    },
+    data: ["tests/data/**/*.apk"],
+}
+
+cc_binary {
+    name: "idmap2",
+    host_supported: true,
+    tidy: true,
+    tidy_flags: [
+        "-system-headers",
+        "-warnings-as-errors=*",
+    ],
+    srcs: [
+        "idmap2/Create.cpp",
+        "idmap2/Dump.cpp",
+        "idmap2/Lookup.cpp",
+        "idmap2/Main.cpp",
+        "idmap2/Scan.cpp",
+        "idmap2/Verify.cpp",
+    ],
+    target: {
+        android: {
+            shared_libs: [
+                "libandroidfw",
+                "libbase",
+                "libidmap2",
+                "libutils",
+                "libziparchive",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libandroidfw",
+                "libbase",
+                "libidmap2",
+                "liblog",
+                "libutils",
+                "libziparchive",
+            ],
+            shared_libs: [
+                "libz",
+            ],
+        },
+    },
+}
+
+cc_binary {
+    name: "idmap2d",
+    host_supported: false,
+    tidy: true,
+    tidy_checks: [
+        // remove google-default-arguments or clang-tidy will complain about
+        // the auto-generated file IIdmap2.cpp
+        "-google-default-arguments",
+    ],
+    tidy_flags: [
+        "-system-headers",
+        "-warnings-as-errors=*",
+    ],
+    srcs: [
+        ":idmap2_aidl",
+        "idmap2d/Idmap2Service.cpp",
+        "idmap2d/Main.cpp",
+    ],
+    shared_libs: [
+        "libandroidfw",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libidmap2",
+        "libutils",
+        "libziparchive",
+    ],
+}
+
+filegroup {
+    name: "idmap2_aidl",
+    srcs: [
+        "idmap2d/aidl/android/os/IIdmap2.aidl",
+    ],
+}
diff --git a/cmds/idmap2/AndroidTest.xml b/cmds/idmap2/AndroidTest.xml
new file mode 100644
index 0000000..5147f4e
--- /dev/null
+++ b/cmds/idmap2/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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 idmap2_tests">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="idmap2_tests->/data/local/tmp/idmap2_tests" />
+    </target_preparer>
+    <option name="test-suite-tag" value="idmap2_tests" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="idmap2_tests" />
+    </test>
+</configuration>
diff --git a/cmds/idmap2/CPPLINT.cfg b/cmds/idmap2/CPPLINT.cfg
new file mode 100644
index 0000000..9dc6b4a
--- /dev/null
+++ b/cmds/idmap2/CPPLINT.cfg
@@ -0,0 +1,18 @@
+# Copyright (C) 2018 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.
+
+set noparent
+linelength=100
+root=..
+filter=+build/include_alpha
diff --git a/cmds/idmap2/OWNERS b/cmds/idmap2/OWNERS
new file mode 100644
index 0000000..23ec5ab
--- /dev/null
+++ b/cmds/idmap2/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+toddke@google.com
diff --git a/cmds/idmap2/TEST_MAPPING b/cmds/idmap2/TEST_MAPPING
new file mode 100644
index 0000000..26ccf03
--- /dev/null
+++ b/cmds/idmap2/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit" : [
+    {
+      "name" : "idmap2_tests"
+    }
+  ]
+}
diff --git a/cmds/idmap2/idmap2/Commands.h b/cmds/idmap2/idmap2/Commands.h
new file mode 100644
index 0000000..dcc69b3
--- /dev/null
+++ b/cmds/idmap2/idmap2/Commands.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef IDMAP2_IDMAP2_COMMANDS_H_
+#define IDMAP2_IDMAP2_COMMANDS_H_
+
+#include <string>
+#include <vector>
+
+bool Create(const std::vector<std::string>& args, std::ostream& out_error);
+bool Dump(const std::vector<std::string>& args, std::ostream& out_error);
+bool Lookup(const std::vector<std::string>& args, std::ostream& out_error);
+bool Scan(const std::vector<std::string>& args, std::ostream& out_error);
+bool Verify(const std::vector<std::string>& args, std::ostream& out_error);
+
+#endif  // IDMAP2_IDMAP2_COMMANDS_H_
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
new file mode 100644
index 0000000..291eaeb
--- /dev/null
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <sys/stat.h>   // umask
+#include <sys/types.h>  // umask
+#include <fstream>
+#include <memory>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "idmap2/BinaryStreamVisitor.h"
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/FileUtils.h"
+#include "idmap2/Idmap.h"
+
+using android::ApkAssets;
+using android::idmap2::BinaryStreamVisitor;
+using android::idmap2::CommandLineOptions;
+using android::idmap2::Idmap;
+
+bool Create(const std::vector<std::string>& args, std::ostream& out_error) {
+  std::string target_apk_path, overlay_apk_path, idmap_path;
+
+  const CommandLineOptions opts =
+      CommandLineOptions("idmap2 create")
+          .MandatoryOption("--target-apk-path",
+                           "input: path to apk which will have its resources overlaid",
+                           &target_apk_path)
+          .MandatoryOption("--overlay-apk-path",
+                           "input: path to apk which contains the new resource values",
+                           &overlay_apk_path)
+          .MandatoryOption("--idmap-path", "output: path to where to write idmap file",
+                           &idmap_path);
+  if (!opts.Parse(args, out_error)) {
+    return false;
+  }
+
+  const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  if (!target_apk) {
+    out_error << "error: failed to load apk " << target_apk_path << std::endl;
+    return false;
+  }
+
+  const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  if (!overlay_apk) {
+    out_error << "error: failed to load apk " << overlay_apk_path << std::endl;
+    return false;
+  }
+
+  const std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, out_error);
+  if (!idmap) {
+    return false;
+  }
+
+  umask(0133);  // u=rw,g=r,o=r
+  std::ofstream fout(idmap_path);
+  if (fout.fail()) {
+    out_error << "failed to open idmap path " << idmap_path << std::endl;
+    return false;
+  }
+  BinaryStreamVisitor visitor(fout);
+  idmap->accept(&visitor);
+  fout.close();
+  if (fout.fail()) {
+    out_error << "failed to write to idmap path " << idmap_path << std::endl;
+    return false;
+  }
+
+  return true;
+}
diff --git a/cmds/idmap2/idmap2/Dump.cpp b/cmds/idmap2/idmap2/Dump.cpp
new file mode 100644
index 0000000..c8cdcfa
--- /dev/null
+++ b/cmds/idmap2/idmap2/Dump.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Idmap.h"
+#include "idmap2/PrettyPrintVisitor.h"
+#include "idmap2/RawPrintVisitor.h"
+
+using android::idmap2::CommandLineOptions;
+using android::idmap2::Idmap;
+using android::idmap2::PrettyPrintVisitor;
+using android::idmap2::RawPrintVisitor;
+
+bool Dump(const std::vector<std::string>& args, std::ostream& out_error) {
+  std::string idmap_path;
+  bool verbose;
+
+  const CommandLineOptions opts =
+      CommandLineOptions("idmap2 dump")
+          .MandatoryOption("--idmap-path", "input: path to idmap file to pretty-print", &idmap_path)
+          .OptionalFlag("--verbose", "annotate every byte of the idmap", &verbose);
+  if (!opts.Parse(args, out_error)) {
+    return false;
+  }
+  std::ifstream fin(idmap_path);
+  const std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(fin, out_error);
+  fin.close();
+  if (!idmap) {
+    return false;
+  }
+
+  if (verbose) {
+    RawPrintVisitor visitor(std::cout);
+    idmap->accept(&visitor);
+  } else {
+    PrettyPrintVisitor visitor(std::cout);
+    idmap->accept(&visitor);
+  }
+
+  return true;
+}
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
new file mode 100644
index 0000000..1191e6a
--- /dev/null
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <algorithm>
+#include <fstream>
+#include <iterator>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ConfigDescription.h"
+#include "androidfw/ResourceUtils.h"
+#include "androidfw/StringPiece.h"
+#include "androidfw/Util.h"
+#include "utils/String16.h"
+#include "utils/String8.h"
+
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Idmap.h"
+#include "idmap2/Xml.h"
+#include "idmap2/ZipFile.h"
+
+using android::ApkAssets;
+using android::ApkAssetsCookie;
+using android::AssetManager2;
+using android::ConfigDescription;
+using android::is_valid_resid;
+using android::kInvalidCookie;
+using android::Res_value;
+using android::ResStringPool;
+using android::ResTable_config;
+using android::String16;
+using android::String8;
+using android::StringPiece16;
+using android::base::StringPrintf;
+using android::idmap2::CommandLineOptions;
+using android::idmap2::IdmapHeader;
+using android::idmap2::ResourceId;
+using android::idmap2::Xml;
+using android::idmap2::ZipFile;
+using android::util::Utf16ToUtf8;
+
+namespace {
+std::pair<bool, ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am,
+                                                          const std::string& res,
+                                                          const std::string& fallback_package) {
+  // first, try to parse as a hex number
+  char* endptr = nullptr;
+  ResourceId resid;
+  resid = strtol(res.c_str(), &endptr, 16);
+  if (*endptr == '\0') {
+    return std::make_pair(true, resid);
+  }
+
+  // next, try to parse as a package:type/name string
+  resid = am.GetResourceId(res, "", fallback_package);
+  if (is_valid_resid(resid)) {
+    return std::make_pair(true, resid);
+  }
+
+  // end of the road: res could not be parsed
+  return std::make_pair(false, 0);
+}
+
+std::pair<bool, std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
+  Res_value value;
+  ResTable_config config;
+  uint32_t flags;
+  ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags);
+  if (cookie == kInvalidCookie) {
+    return std::make_pair(false, "");
+  }
+
+  std::string out;
+
+  // TODO(martenkongstad): use optional parameter GetResource(..., std::string*
+  // stacktrace = NULL) instead
+  out.append(StringPrintf("cookie=%d ", cookie));
+
+  out.append("config='");
+  out.append(config.toString().c_str());
+  out.append("' value=");
+
+  switch (value.dataType) {
+    case Res_value::TYPE_INT_DEC:
+      out.append(StringPrintf("%d", value.data));
+      break;
+    case Res_value::TYPE_INT_HEX:
+      out.append(StringPrintf("0x%08x", value.data));
+      break;
+    case Res_value::TYPE_INT_BOOLEAN:
+      out.append(value.data != 0 ? "true" : "false");
+      break;
+    case Res_value::TYPE_STRING: {
+      const ResStringPool* pool = am.GetStringPoolForCookie(cookie);
+      size_t len;
+      if (pool->isUTF8()) {
+        const char* str = pool->string8At(value.data, &len);
+        out.append(str, len);
+      } else {
+        const char16_t* str16 = pool->stringAt(value.data, &len);
+        out += Utf16ToUtf8(StringPiece16(str16, len));
+      }
+    } break;
+    default:
+      out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
+      break;
+  }
+  return std::make_pair(true, out);
+}
+
+std::pair<bool, std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
+  const auto zip = ZipFile::Open(apk_path);
+  if (!zip) {
+    return std::make_pair(false, "");
+  }
+  const auto entry = zip->Uncompress("AndroidManifest.xml");
+  if (!entry) {
+    return std::make_pair(false, "");
+  }
+  const auto xml = Xml::Create(entry->buf, entry->size);
+  if (!xml) {
+    return std::make_pair(false, "");
+  }
+  const auto tag = xml->FindTag("overlay");
+  if (!tag) {
+    return std::make_pair(false, "");
+  }
+  const auto iter = tag->find("targetPackage");
+  if (iter == tag->end()) {
+    return std::make_pair(false, "");
+  }
+  return std::make_pair(true, iter->second);
+}
+}  // namespace
+
+bool Lookup(const std::vector<std::string>& args, std::ostream& out_error) {
+  std::vector<std::string> idmap_paths;
+  std::string config_str, resid_str;
+  const CommandLineOptions opts =
+      CommandLineOptions("idmap2 lookup")
+          .MandatoryOption("--idmap-path", "input: path to idmap file to load", &idmap_paths)
+          .MandatoryOption("--config", "configuration to use", &config_str)
+          .MandatoryOption("--resid",
+                           "Resource ID (in the target package; '0xpptteeee' or "
+                           "'[package:]type/name') to look up",
+                           &resid_str);
+
+  if (!opts.Parse(args, out_error)) {
+    return false;
+  }
+
+  ConfigDescription config;
+  if (!ConfigDescription::Parse(config_str, &config)) {
+    out_error << "error: failed to parse config" << std::endl;
+    return false;
+  }
+
+  std::vector<std::unique_ptr<const ApkAssets>> apk_assets;
+  std::string target_path;
+  std::string target_package_name;
+  for (size_t i = 0; i < idmap_paths.size(); i++) {
+    const auto& idmap_path = idmap_paths[i];
+    std::fstream fin(idmap_path);
+    auto idmap_header = IdmapHeader::FromBinaryStream(fin);
+    fin.close();
+    if (!idmap_header) {
+      out_error << "error: failed to read idmap from " << idmap_path << std::endl;
+      return false;
+    }
+
+    if (i == 0) {
+      target_path = idmap_header->GetTargetPath().to_string();
+      auto target_apk = ApkAssets::Load(target_path);
+      if (!target_apk) {
+        out_error << "error: failed to read target apk from " << target_path << std::endl;
+        return false;
+      }
+      apk_assets.push_back(std::move(target_apk));
+
+      bool lookup_ok;
+      std::tie(lookup_ok, target_package_name) =
+          GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
+      if (!lookup_ok) {
+        out_error << "error: failed to parse android:targetPackage from overlay manifest"
+                  << std::endl;
+        return false;
+      }
+    } else if (target_path != idmap_header->GetTargetPath()) {
+      out_error << "error: different target APKs (expected target APK " << target_path << " but "
+                << idmap_path << " has target APK " << idmap_header->GetTargetPath() << ")"
+                << std::endl;
+      return false;
+    }
+
+    auto overlay_apk = ApkAssets::LoadOverlay(idmap_path);
+    if (!overlay_apk) {
+      out_error << "error: failed to read overlay apk from " << idmap_header->GetOverlayPath()
+                << std::endl;
+      return false;
+    }
+    apk_assets.push_back(std::move(overlay_apk));
+  }
+
+  // AssetManager2::SetApkAssets requires raw ApkAssets pointers, not unique_ptrs
+  std::vector<const ApkAssets*> raw_pointer_apk_assets;
+  std::transform(apk_assets.cbegin(), apk_assets.cend(), std::back_inserter(raw_pointer_apk_assets),
+                 [](const auto& p) -> const ApkAssets* { return p.get(); });
+  AssetManager2 am;
+  am.SetApkAssets(raw_pointer_apk_assets);
+  am.SetConfiguration(config);
+
+  ResourceId resid;
+  bool lookup_ok;
+  std::tie(lookup_ok, resid) = ParseResReference(am, resid_str, target_package_name);
+  if (!lookup_ok) {
+    out_error << "error: failed to parse resource ID" << std::endl;
+    return false;
+  }
+
+  std::string value;
+  std::tie(lookup_ok, value) = GetValue(am, resid);
+  if (!lookup_ok) {
+    out_error << StringPrintf("error: resource 0x%08x not found", resid) << std::endl;
+    return false;
+  }
+  std::cout << value << std::endl;
+
+  return true;
+}
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
new file mode 100644
index 0000000..5d9ea77
--- /dev/null
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <cstdlib>  // EXIT_{FAILURE,SUCCESS}
+#include <functional>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "idmap2/CommandLineOptions.h"
+
+#include "Commands.h"
+
+using android::idmap2::CommandLineOptions;
+
+typedef std::map<std::string, std::function<int(const std::vector<std::string>&, std::ostream&)>>
+    NameToFunctionMap;
+
+static void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) {
+  out << "usage: idmap2 [";
+  for (auto iter = commands.cbegin(); iter != commands.cend(); iter++) {
+    if (iter != commands.cbegin()) {
+      out << "|";
+    }
+    out << iter->first;
+  }
+  out << "]" << std::endl;
+}
+
+int main(int argc, char** argv) {
+  const NameToFunctionMap commands = {
+      {"create", Create}, {"dump", Dump}, {"lookup", Lookup}, {"scan", Scan}, {"verify", Verify},
+  };
+  if (argc <= 1) {
+    PrintUsage(commands, std::cerr);
+    return EXIT_FAILURE;
+  }
+  const std::unique_ptr<std::vector<std::string>> args =
+      CommandLineOptions::ConvertArgvToVector(argc - 1, const_cast<const char**>(argv + 1));
+  if (!args) {
+    std::cerr << "error: failed to parse command line options" << std::endl;
+    return EXIT_FAILURE;
+  }
+  const auto iter = commands.find(argv[1]);
+  if (iter == commands.end()) {
+    std::cerr << argv[1] << ": command not found" << std::endl;
+    PrintUsage(commands, std::cerr);
+    return EXIT_FAILURE;
+  }
+  return iter->second(*args, std::cerr) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
new file mode 100644
index 0000000..33c274e
--- /dev/null
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <dirent.h>
+#include <fstream>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/FileUtils.h"
+#include "idmap2/Idmap.h"
+#include "idmap2/Xml.h"
+#include "idmap2/ZipFile.h"
+
+#include "Commands.h"
+
+using android::idmap2::CommandLineOptions;
+using android::idmap2::Idmap;
+using android::idmap2::MemoryChunk;
+using android::idmap2::Xml;
+using android::idmap2::ZipFile;
+using android::idmap2::utils::FindFiles;
+
+namespace {
+std::unique_ptr<std::vector<std::string>> FindApkFiles(const std::vector<std::string>& dirs,
+                                                       bool recursive, std::ostream& out_error) {
+  const auto predicate = [](unsigned char type, const std::string& path) -> bool {
+    static constexpr size_t kExtLen = 4;  // strlen(".apk")
+    return type == DT_REG && path.size() > kExtLen &&
+           !path.compare(path.size() - kExtLen, kExtLen, ".apk");
+  };
+  // pass apk paths through a set to filter out duplicates
+  std::set<std::string> paths;
+  for (const auto& dir : dirs) {
+    const auto apk_paths = FindFiles(dir, recursive, predicate);
+    if (!apk_paths) {
+      out_error << "error: failed to open directory " << dir << std::endl;
+      return nullptr;
+    }
+    paths.insert(apk_paths->cbegin(), apk_paths->cend());
+  }
+  return std::unique_ptr<std::vector<std::string>>(
+      new std::vector<std::string>(paths.cbegin(), paths.cend()));
+}
+}  // namespace
+
+bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {
+  std::vector<std::string> input_directories;
+  std::string target_package_name, target_apk_path, output_directory;
+  bool recursive = false;
+
+  const CommandLineOptions opts =
+      CommandLineOptions("idmap2 scan")
+          .MandatoryOption("--input-directory", "directory containing overlay apks to scan",
+                           &input_directories)
+          .OptionalFlag("--recursive", "also scan subfolders of overlay-directory", &recursive)
+          .MandatoryOption("--target-package-name", "package name of target package",
+                           &target_package_name)
+          .MandatoryOption("--target-apk-path", "path to target apk", &target_apk_path)
+          .MandatoryOption("--output-directory",
+                           "directory in which to write artifacts (idmap files and overlays.list)",
+                           &output_directory);
+  if (!opts.Parse(args, out_error)) {
+    return false;
+  }
+
+  const auto apk_paths = FindApkFiles(input_directories, recursive, out_error);
+  if (!apk_paths) {
+    return false;
+  }
+
+  std::vector<std::string> interesting_apks;
+  for (const std::string& path : *apk_paths) {
+    std::unique_ptr<const ZipFile> zip = ZipFile::Open(path);
+    if (!zip) {
+      out_error << "error: failed to open " << path << " as a zip file" << std::endl;
+      return false;
+    }
+
+    std::unique_ptr<const MemoryChunk> entry = zip->Uncompress("AndroidManifest.xml");
+    if (!entry) {
+      out_error << "error: failed to uncompress AndroidManifest.xml from " << path << std::endl;
+      return false;
+    }
+
+    std::unique_ptr<const Xml> xml = Xml::Create(entry->buf, entry->size);
+    if (!xml) {
+      out_error << "error: failed to parse AndroidManifest.xml from " << path << std::endl;
+      continue;
+    }
+
+    const auto tag = xml->FindTag("overlay");
+    if (!tag) {
+      continue;
+    }
+
+    auto iter = tag->find("isStatic");
+    if (iter == tag->end() || std::stoul(iter->second) == 0u) {
+      continue;
+    }
+
+    iter = tag->find("targetPackage");
+    if (iter == tag->end() || iter->second != target_package_name) {
+      continue;
+    }
+
+    iter = tag->find("priority");
+    if (iter == tag->end()) {
+      continue;
+    }
+
+    const int priority = std::stoi(iter->second);
+    if (priority < 0) {
+      continue;
+    }
+
+    interesting_apks.insert(
+        std::lower_bound(interesting_apks.begin(), interesting_apks.end(), path), path);
+  }
+
+  std::stringstream stream;
+  for (auto iter = interesting_apks.cbegin(); iter != interesting_apks.cend(); ++iter) {
+    const std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, *iter);
+    if (!Verify(std::vector<std::string>({"--idmap-path", idmap_path}), out_error) &&
+        !Create(std::vector<std::string>({
+                    "--target-apk-path",
+                    target_apk_path,
+                    "--overlay-apk-path",
+                    *iter,
+                    "--idmap-path",
+                    idmap_path,
+                }),
+                out_error)) {
+      return false;
+    }
+    stream << idmap_path << std::endl;
+  }
+
+  std::cout << stream.str();
+
+  return true;
+}
diff --git a/cmds/idmap2/idmap2/Verify.cpp b/cmds/idmap2/idmap2/Verify.cpp
new file mode 100644
index 0000000..b5fa438
--- /dev/null
+++ b/cmds/idmap2/idmap2/Verify.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <fstream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Idmap.h"
+
+using android::idmap2::CommandLineOptions;
+using android::idmap2::IdmapHeader;
+
+bool Verify(const std::vector<std::string>& args, std::ostream& out_error) {
+  std::string idmap_path;
+  const CommandLineOptions opts =
+      CommandLineOptions("idmap2 verify")
+          .MandatoryOption("--idmap-path", "input: path to idmap file to verify", &idmap_path);
+  if (!opts.Parse(args, out_error)) {
+    return false;
+  }
+
+  std::ifstream fin(idmap_path);
+  const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
+  fin.close();
+  if (!header) {
+    out_error << "error: failed to parse idmap header" << std::endl;
+    return false;
+  }
+
+  return header->IsUpToDate(out_error);
+}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
new file mode 100644
index 0000000..cf72cb9
--- /dev/null
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <sys/stat.h>   // umask
+#include <sys/types.h>  // umask
+#include <unistd.h>
+
+#include <cerrno>
+#include <cstring>
+#include <fstream>
+#include <memory>
+#include <ostream>
+#include <string>
+
+#include "android-base/macros.h"
+#include "utils/String8.h"
+#include "utils/Trace.h"
+
+#include "idmap2/BinaryStreamVisitor.h"
+#include "idmap2/FileUtils.h"
+#include "idmap2/Idmap.h"
+
+#include "idmap2d/Idmap2Service.h"
+
+using android::binder::Status;
+using android::idmap2::BinaryStreamVisitor;
+using android::idmap2::Idmap;
+using android::idmap2::IdmapHeader;
+
+namespace {
+
+static constexpr const char* kIdmapCacheDir = "/data/resource-cache";
+
+Status ok() {
+  return Status::ok();
+}
+
+Status error(const std::string& msg) {
+  LOG(ERROR) << msg;
+  return Status::fromExceptionCode(Status::EX_NONE, msg.c_str());
+}
+
+}  // namespace
+
+namespace android {
+namespace os {
+
+Status Idmap2Service::getIdmapPath(const std::string& overlay_apk_path,
+                                   int32_t user_id ATTRIBUTE_UNUSED, std::string* _aidl_return) {
+  assert(_aidl_return);
+  *_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+  return ok();
+}
+
+Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
+                                  int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
+  assert(_aidl_return);
+  const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+  if (unlink(idmap_path.c_str()) == 0) {
+    *_aidl_return = true;
+    return ok();
+  } else {
+    *_aidl_return = false;
+    return error("failed to unlink " + idmap_path + ": " + strerror(errno));
+  }
+}
+
+Status Idmap2Service::createIdmap(const std::string& target_apk_path,
+                                  const std::string& overlay_apk_path, int32_t user_id,
+                                  std::unique_ptr<std::string>* _aidl_return) {
+  assert(_aidl_return);
+  std::stringstream trace;
+  trace << __FUNCTION__ << " " << target_apk_path << " " << overlay_apk_path << " "
+        << std::to_string(user_id);
+  ATRACE_NAME(trace.str().c_str());
+  std::cout << trace.str() << std::endl;
+
+  _aidl_return->reset(nullptr);
+
+  const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+  std::ifstream fin(idmap_path);
+  const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
+  fin.close();
+  // do not reuse error stream from IsUpToDate below, or error messages will be
+  // polluted with irrelevant data
+  std::stringstream dev_null;
+  if (header && header->IsUpToDate(dev_null)) {
+    return ok();
+  }
+
+  const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  if (!target_apk) {
+    return error("failed to load apk " + target_apk_path);
+  }
+
+  const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  if (!overlay_apk) {
+    return error("failed to load apk " + overlay_apk_path);
+  }
+
+  std::stringstream err;
+  const std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, err);
+  if (!idmap) {
+    return error(err.str());
+  }
+
+  umask(0133);  // u=rw,g=r,o=r
+  std::ofstream fout(idmap_path);
+  if (fout.fail()) {
+    return error("failed to open idmap path " + idmap_path);
+  }
+  BinaryStreamVisitor visitor(fout);
+  idmap->accept(&visitor);
+  fout.close();
+  if (fout.fail()) {
+    return error("failed to write to idmap path " + idmap_path);
+  }
+
+  _aidl_return->reset(new std::string(idmap_path));
+  return ok();
+}
+
+}  // namespace os
+}  // namespace android
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
new file mode 100644
index 0000000..2b32042
--- /dev/null
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef IDMAP2_IDMAP2D_IDMAP2SERVICE_H_
+#define IDMAP2_IDMAP2D_IDMAP2SERVICE_H_
+
+#include <android-base/unique_fd.h>
+#include <binder/BinderService.h>
+
+#include <memory>
+#include <string>
+
+#include "android/os/BnIdmap2.h"
+
+namespace android {
+namespace os {
+class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
+ public:
+  static char const* getServiceName() {
+    return "idmap";
+  }
+
+  binder::Status getIdmapPath(const std::string& overlay_apk_path, int32_t user_id,
+                              std::string* _aidl_return);
+
+  binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
+                             bool* _aidl_return);
+
+  binder::Status createIdmap(const std::string& target_apk_path,
+                             const std::string& overlay_apk_path, int32_t user_id,
+                             std::unique_ptr<std::string>* _aidl_return);
+};
+}  // namespace os
+}  // namespace android
+
+#endif  // IDMAP2_IDMAP2D_IDMAP2SERVICE_H_
diff --git a/cmds/idmap2/idmap2d/Main.cpp b/cmds/idmap2/idmap2d/Main.cpp
new file mode 100644
index 0000000..d64a87b
--- /dev/null
+++ b/cmds/idmap2/idmap2d/Main.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
+
+#include <binder/BinderService.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+
+#include <cstdlib>  // EXIT_{FAILURE,SUCCESS}
+
+#include <iostream>
+#include <sstream>
+
+#include "android-base/macros.h"
+
+#include "Idmap2Service.h"
+
+using android::BinderService;
+using android::IPCThreadState;
+using android::ProcessState;
+using android::sp;
+using android::status_t;
+using android::os::Idmap2Service;
+
+int main(int argc ATTRIBUTE_UNUSED, char** argv ATTRIBUTE_UNUSED) {
+  IPCThreadState::self()->disableBackgroundScheduling(true);
+  status_t ret = BinderService<Idmap2Service>::publish();
+  if (ret != android::OK) {
+    return EXIT_FAILURE;
+  }
+  sp<ProcessState> ps(ProcessState::self());
+  ps->startThreadPool();
+  ps->giveThreadPoolName();
+  IPCThreadState::self()->joinThreadPool();
+  return EXIT_SUCCESS;
+}
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
new file mode 100644
index 0000000..5d19610
--- /dev/null
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 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 android.os;
+
+/**
+ * @hide
+ */
+interface IIdmap2 {
+  @utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
+  boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
+  @nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
+                                          @utf8InCpp String overlayApkPath, int userId);
+}
diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
new file mode 100644
index 0000000..2368aea
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_BINARYSTREAMVISITOR_H_
+#define IDMAP2_INCLUDE_IDMAP2_BINARYSTREAMVISITOR_H_
+
+#include <cstdint>
+#include <iostream>
+#include <string>
+
+#include "idmap2/Idmap.h"
+
+namespace android {
+namespace idmap2 {
+
+class BinaryStreamVisitor : public Visitor {
+ public:
+  explicit BinaryStreamVisitor(std::ostream& stream) : stream_(stream) {
+  }
+  virtual void visit(const Idmap& idmap);
+  virtual void visit(const IdmapHeader& header);
+  virtual void visit(const IdmapData& data);
+  virtual void visit(const IdmapData::Header& header);
+  virtual void visit(const IdmapData::TypeEntry& type_entry);
+
+ private:
+  void Write16(uint16_t value);
+  void Write32(uint32_t value);
+  void WriteString(const StringPiece& value);
+  std::ostream& stream_;
+};
+
+}  // namespace idmap2
+}  // namespace android
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_BINARYSTREAMVISITOR_H_
diff --git a/cmds/idmap2/include/idmap2/CommandLineOptions.h b/cmds/idmap2/include/idmap2/CommandLineOptions.h
new file mode 100644
index 0000000..f3aa68b
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/CommandLineOptions.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_COMMANDLINEOPTIONS_H_
+#define IDMAP2_INCLUDE_IDMAP2_COMMANDLINEOPTIONS_H_
+
+#include <functional>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace idmap2 {
+
+/*
+ * Utility class to convert a command line, including options (--path foo.txt),
+ * into data structures (options.path = "foo.txt").
+ */
+class CommandLineOptions {
+ public:
+  static std::unique_ptr<std::vector<std::string>> ConvertArgvToVector(int argc, const char** argv);
+
+  explicit CommandLineOptions(const std::string& name) : name_(name) {
+  }
+
+  CommandLineOptions& OptionalFlag(const std::string& name, const std::string& description,
+                                   bool* value);
+  CommandLineOptions& MandatoryOption(const std::string& name, const std::string& description,
+                                      std::string* value);
+  CommandLineOptions& MandatoryOption(const std::string& name, const std::string& description,
+                                      std::vector<std::string>* value);
+  CommandLineOptions& OptionalOption(const std::string& name, const std::string& description,
+                                     std::string* value);
+  bool Parse(const std::vector<std::string>& argv, std::ostream& outError) const;
+  void Usage(std::ostream& out) const;
+
+ private:
+  struct Option {
+    std::string name;
+    std::string description;
+    std::function<void(const std::string& value)> action;
+    enum {
+      COUNT_OPTIONAL,
+      COUNT_EXACTLY_ONCE,
+      COUNT_ONCE_OR_MORE,
+    } count;
+    bool argument;
+  };
+
+  mutable std::vector<Option> options_;
+  std::string name_;
+};
+
+}  // namespace idmap2
+}  // namespace android
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_COMMANDLINEOPTIONS_H_
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
new file mode 100644
index 0000000..05c6d31
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
+#define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace idmap2 {
+namespace utils {
+typedef std::function<bool(unsigned char type /* DT_* from dirent.h */, const std::string& path)>
+    FindFilesPredicate;
+std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
+                                                    const FindFilesPredicate& predicate);
+
+std::unique_ptr<std::string> ReadFile(int fd);
+
+std::unique_ptr<std::string> ReadFile(const std::string& path);
+
+}  // namespace utils
+}  // namespace idmap2
+}  // namespace android
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
new file mode 100644
index 0000000..837b7c5
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+/*
+ * # idmap file format (current version)
+ *
+ * idmap             := header data*
+ * header            := magic version target_crc overlay_crc target_path overlay_path
+ * data              := data_header data_block*
+ * data_header       := target_package_id types_count
+ * data_block        := target_type overlay_type entry_count entry_offset entry*
+ * overlay_path      := string
+ * target_path       := string
+ * entry             := <uint32_t>
+ * entry_count       := <uint16_t>
+ * entry_offset      := <uint16_t>
+ * magic             := <uint32_t>
+ * overlay_crc       := <uint32_t>
+ * overlay_type      := <uint16_t>
+ * string            := <uint8_t>[256]
+ * target_crc        := <uint32_t>
+ * target_package_id := <uint16_t>
+ * target_type       := <uint16_t>
+ * types_count       := <uint16_t>
+ * version           := <uint32_t>
+ *
+ *
+ * # idmap file format changelog
+ * ## v1
+ * - Identical to idmap v1.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
+#define IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "android-base/macros.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
+
+namespace android {
+namespace idmap2 {
+
+class Idmap;
+class Visitor;
+
+// use typedefs to let the compiler warn us about implicit casts
+typedef uint32_t ResourceId;  // 0xpptteeee
+typedef uint8_t PackageId;    // pp in 0xpptteeee
+typedef uint8_t TypeId;       // tt in 0xpptteeee
+typedef uint16_t EntryId;     // eeee in 0xpptteeee
+
+static constexpr const ResourceId kPadding = 0xffffffffu;
+
+static constexpr const EntryId kNoEntry = 0xffffu;
+
+// magic number: all idmap files start with this
+static constexpr const uint32_t kIdmapMagic = android::kIdmapMagic;
+
+// current version of the idmap binary format; must be incremented when the format is changed
+static constexpr const uint32_t kIdmapCurrentVersion = android::kIdmapCurrentVersion;
+
+// strings in the idmap are encoded char arrays of length 'kIdmapStringLength' (including mandatory
+// terminating null)
+static constexpr const size_t kIdmapStringLength = 256;
+
+class IdmapHeader {
+ public:
+  static std::unique_ptr<const IdmapHeader> FromBinaryStream(std::istream& stream);
+
+  inline uint32_t GetMagic() const {
+    return magic_;
+  }
+
+  inline uint32_t GetVersion() const {
+    return version_;
+  }
+
+  inline uint32_t GetTargetCrc() const {
+    return target_crc_;
+  }
+
+  inline uint32_t GetOverlayCrc() const {
+    return overlay_crc_;
+  }
+
+  inline StringPiece GetTargetPath() const {
+    return StringPiece(target_path_);
+  }
+
+  inline StringPiece GetOverlayPath() const {
+    return StringPiece(overlay_path_);
+  }
+
+  // Invariant: anytime the idmap data encoding is changed, the idmap version
+  // field *must* be incremented. Because of this, we know that if the idmap
+  // header is up-to-date the entire file is up-to-date.
+  bool IsUpToDate(std::ostream& out_error) const;
+
+  void accept(Visitor* v) const;
+
+ private:
+  IdmapHeader() {
+  }
+
+  uint32_t magic_;
+  uint32_t version_;
+  uint32_t target_crc_;
+  uint32_t overlay_crc_;
+  char target_path_[kIdmapStringLength];
+  char overlay_path_[kIdmapStringLength];
+
+  friend Idmap;
+  DISALLOW_COPY_AND_ASSIGN(IdmapHeader);
+};
+
+class IdmapData {
+ public:
+  class Header {
+   public:
+    static std::unique_ptr<const Header> FromBinaryStream(std::istream& stream);
+
+    inline PackageId GetTargetPackageId() const {
+      return target_package_id_;
+    }
+
+    inline uint16_t GetTypeCount() const {
+      return type_count_;
+    }
+
+    void accept(Visitor* v) const;
+
+   private:
+    Header() {
+    }
+
+    PackageId target_package_id_;
+    uint16_t type_count_;
+
+    friend Idmap;
+    DISALLOW_COPY_AND_ASSIGN(Header);
+  };
+
+  class TypeEntry {
+   public:
+    static std::unique_ptr<const TypeEntry> FromBinaryStream(std::istream& stream);
+
+    inline TypeId GetTargetTypeId() const {
+      return target_type_id_;
+    }
+
+    inline TypeId GetOverlayTypeId() const {
+      return overlay_type_id_;
+    }
+
+    inline uint16_t GetEntryCount() const {
+      return entries_.size();
+    }
+
+    inline uint16_t GetEntryOffset() const {
+      return entry_offset_;
+    }
+
+    inline EntryId GetEntry(size_t i) const {
+      return i < entries_.size() ? entries_[i] : 0xffffu;
+    }
+
+    void accept(Visitor* v) const;
+
+   private:
+    TypeEntry() {
+    }
+
+    TypeId target_type_id_;
+    TypeId overlay_type_id_;
+    uint16_t entry_offset_;
+    std::vector<EntryId> entries_;
+
+    friend Idmap;
+    DISALLOW_COPY_AND_ASSIGN(TypeEntry);
+  };
+
+  static std::unique_ptr<const IdmapData> FromBinaryStream(std::istream& stream);
+
+  inline const std::unique_ptr<const Header>& GetHeader() const {
+    return header_;
+  }
+
+  inline const std::vector<std::unique_ptr<const TypeEntry>>& GetTypeEntries() const {
+    return type_entries_;
+  }
+
+  void accept(Visitor* v) const;
+
+ private:
+  IdmapData() {
+  }
+
+  std::unique_ptr<const Header> header_;
+  std::vector<std::unique_ptr<const TypeEntry>> type_entries_;
+
+  friend Idmap;
+  DISALLOW_COPY_AND_ASSIGN(IdmapData);
+};
+
+class Idmap {
+ public:
+  static std::string CanonicalIdmapPathFor(const std::string& absolute_dir,
+                                           const std::string& absolute_apk_path);
+
+  static std::unique_ptr<const Idmap> FromBinaryStream(std::istream& stream,
+                                                       std::ostream& out_error);
+
+  // In the current version of idmap, the first package in each resources.arsc
+  // file is used; change this in the next version of idmap to use a named
+  // package instead; also update FromApkAssets to take additional parameters:
+  // the target and overlay package names
+  static std::unique_ptr<const Idmap> FromApkAssets(const std::string& target_apk_path,
+                                                    const ApkAssets& target_apk_assets,
+                                                    const std::string& overlay_apk_path,
+                                                    const ApkAssets& overlay_apk_assets,
+                                                    std::ostream& out_error);
+
+  inline const std::unique_ptr<const IdmapHeader>& GetHeader() const {
+    return header_;
+  }
+
+  inline const std::vector<std::unique_ptr<const IdmapData>>& GetData() const {
+    return data_;
+  }
+
+  void accept(Visitor* v) const;
+
+ private:
+  Idmap() {
+  }
+
+  std::unique_ptr<const IdmapHeader> header_;
+  std::vector<std::unique_ptr<const IdmapData>> data_;
+
+  DISALLOW_COPY_AND_ASSIGN(Idmap);
+};
+
+class Visitor {
+ public:
+  virtual ~Visitor() {
+  }
+  virtual void visit(const Idmap& idmap) = 0;
+  virtual void visit(const IdmapHeader& header) = 0;
+  virtual void visit(const IdmapData& data) = 0;
+  virtual void visit(const IdmapData::Header& header) = 0;
+  virtual void visit(const IdmapData::TypeEntry& type_entry) = 0;
+};
+
+}  // namespace idmap2
+}  // namespace android
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
new file mode 100644
index 0000000..c388f4b
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_PRETTYPRINTVISITOR_H_
+#define IDMAP2_INCLUDE_IDMAP2_PRETTYPRINTVISITOR_H_
+
+#include <iostream>
+#include <memory>
+
+#include "androidfw/AssetManager2.h"
+
+#include "idmap2/Idmap.h"
+
+namespace android {
+
+class ApkAssets;
+
+namespace idmap2 {
+
+class PrettyPrintVisitor : public Visitor {
+ public:
+  explicit PrettyPrintVisitor(std::ostream& stream) : stream_(stream) {
+  }
+  virtual void visit(const Idmap& idmap);
+  virtual void visit(const IdmapHeader& header);
+  virtual void visit(const IdmapData& data);
+  virtual void visit(const IdmapData::Header& header);
+  virtual void visit(const IdmapData::TypeEntry& type_entry);
+
+ private:
+  std::ostream& stream_;
+  std::unique_ptr<const ApkAssets> target_apk_;
+  AssetManager2 target_am_;
+  PackageId last_seen_package_id_;
+};
+
+}  // namespace idmap2
+}  // namespace android
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_PRETTYPRINTVISITOR_H_
diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
new file mode 100644
index 0000000..7e33b3b
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RAWPRINTVISITOR_H_
+#define IDMAP2_INCLUDE_IDMAP2_RAWPRINTVISITOR_H_
+
+#include <iostream>
+#include <memory>
+#include <string>
+
+#include "androidfw/AssetManager2.h"
+
+#include "idmap2/Idmap.h"
+
+namespace android {
+
+class ApkAssets;
+
+namespace idmap2 {
+
+class RawPrintVisitor : public Visitor {
+ public:
+  explicit RawPrintVisitor(std::ostream& stream) : stream_(stream), offset_(0) {
+  }
+  virtual void visit(const Idmap& idmap);
+  virtual void visit(const IdmapHeader& header);
+  virtual void visit(const IdmapData& data);
+  virtual void visit(const IdmapData::Header& header);
+  virtual void visit(const IdmapData::TypeEntry& type_entry);
+
+ private:
+  void print(uint16_t value, const char* fmt, ...);
+  void print(uint32_t value, const char* fmt, ...);
+  void print(const std::string& value, const char* fmt, ...);
+
+  std::ostream& stream_;
+  std::unique_ptr<const ApkAssets> target_apk_;
+  AssetManager2 target_am_;
+  size_t offset_;
+  PackageId last_seen_package_id_;
+};
+
+}  // namespace idmap2
+}  // namespace android
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_RAWPRINTVISITOR_H_
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
new file mode 100644
index 0000000..88a835b
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
+#define IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
+
+#include <string>
+#include <utility>
+
+#include "android-base/macros.h"
+#include "androidfw/AssetManager2.h"
+
+#include "idmap2/Idmap.h"
+
+namespace android {
+namespace idmap2 {
+namespace utils {
+
+std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
+                                                            ResourceId resid);
+
+}  // namespace utils
+}  // namespace idmap2
+}  // namespace android
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Xml.h b/cmds/idmap2/include/idmap2/Xml.h
new file mode 100644
index 0000000..9ab5ec4
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/Xml.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_XML_H_
+#define IDMAP2_INCLUDE_IDMAP2_XML_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+#include "utils/String16.h"
+
+namespace android {
+namespace idmap2 {
+
+class Xml {
+ public:
+  static std::unique_ptr<const Xml> Create(const uint8_t* data, size_t size, bool copyData = false);
+
+  std::unique_ptr<std::map<std::string, std::string>> FindTag(const std::string& name) const;
+
+  ~Xml();
+
+ private:
+  Xml() {
+  }
+
+  mutable ResXMLTree xml_;
+
+  DISALLOW_COPY_AND_ASSIGN(Xml);
+};
+
+}  // namespace idmap2
+}  // namespace android
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_XML_H_
diff --git a/cmds/idmap2/include/idmap2/ZipFile.h b/cmds/idmap2/include/idmap2/ZipFile.h
new file mode 100644
index 0000000..328bd36
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/ZipFile.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
+#define IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "android-base/macros.h"
+#include "ziparchive/zip_archive.h"
+
+namespace android {
+namespace idmap2 {
+
+struct MemoryChunk {
+  size_t size;
+  uint8_t buf[0];
+
+  static std::unique_ptr<MemoryChunk> Allocate(size_t size);
+
+ private:
+  MemoryChunk() {
+  }
+};
+
+class ZipFile {
+ public:
+  static std::unique_ptr<const ZipFile> Open(const std::string& path);
+
+  std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const;
+  std::pair<bool, uint32_t> Crc(const std::string& entryPath) const;
+
+  ~ZipFile();
+
+ private:
+  explicit ZipFile(const ::ZipArchiveHandle handle) : handle_(handle) {
+  }
+
+  const ::ZipArchiveHandle handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(ZipFile);
+};
+
+}  // namespace idmap2
+}  // namespace android
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
new file mode 100644
index 0000000..29969a2
--- /dev/null
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <algorithm>
+#include <cstring>
+#include <string>
+
+#include "android-base/macros.h"
+
+#include "idmap2/BinaryStreamVisitor.h"
+
+namespace android {
+namespace idmap2 {
+
+void BinaryStreamVisitor::Write16(uint16_t value) {
+  uint16_t x = htodl(value);
+  stream_.write(reinterpret_cast<char*>(&x), sizeof(uint16_t));
+}
+
+void BinaryStreamVisitor::Write32(uint32_t value) {
+  uint32_t x = htodl(value);
+  stream_.write(reinterpret_cast<char*>(&x), sizeof(uint32_t));
+}
+
+void BinaryStreamVisitor::WriteString(const StringPiece& value) {
+  char buf[kIdmapStringLength];
+  memset(buf, 0, sizeof(buf));
+  memcpy(buf, value.data(), std::min(value.size(), sizeof(buf)));
+  stream_.write(buf, sizeof(buf));
+}
+
+void BinaryStreamVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
+  // nothing to do
+}
+
+void BinaryStreamVisitor::visit(const IdmapHeader& header) {
+  Write32(header.GetMagic());
+  Write32(header.GetVersion());
+  Write32(header.GetTargetCrc());
+  Write32(header.GetOverlayCrc());
+  WriteString(header.GetTargetPath());
+  WriteString(header.GetOverlayPath());
+}
+
+void BinaryStreamVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
+  // nothing to do
+}
+
+void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
+  Write16(header.GetTargetPackageId());
+  Write16(header.GetTypeCount());
+}
+
+void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& te) {
+  const uint16_t entryCount = te.GetEntryCount();
+
+  Write16(te.GetTargetTypeId());
+  Write16(te.GetOverlayTypeId());
+  Write16(entryCount);
+  Write16(te.GetEntryOffset());
+  for (uint16_t i = 0; i < entryCount; i++) {
+    EntryId entry_id = te.GetEntry(i);
+    Write32(entry_id != kNoEntry ? static_cast<uint32_t>(entry_id) : kPadding);
+  }
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
new file mode 100644
index 0000000..28c3797
--- /dev/null
+++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "android-base/macros.h"
+
+#include "idmap2/CommandLineOptions.h"
+
+namespace android {
+namespace idmap2 {
+
+std::unique_ptr<std::vector<std::string>> CommandLineOptions::ConvertArgvToVector(
+    int argc, const char** argv) {
+  return std::unique_ptr<std::vector<std::string>>(
+      new std::vector<std::string>(argv + 1, argv + argc));
+}
+
+CommandLineOptions& CommandLineOptions::OptionalFlag(const std::string& name,
+                                                     const std::string& description, bool* value) {
+  assert(value != nullptr);
+  auto func = [value](const std::string& arg ATTRIBUTE_UNUSED) -> void { *value = true; };
+  options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, false});
+  return *this;
+}
+
+CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
+                                                        const std::string& description,
+                                                        std::string* value) {
+  assert(value != nullptr);
+  auto func = [value](const std::string& arg) -> void { *value = arg; };
+  options_.push_back(Option{name, description, func, Option::COUNT_EXACTLY_ONCE, true});
+  return *this;
+}
+
+CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
+                                                        const std::string& description,
+                                                        std::vector<std::string>* value) {
+  assert(value != nullptr);
+  auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
+  options_.push_back(Option{name, description, func, Option::COUNT_ONCE_OR_MORE, true});
+  return *this;
+}
+
+CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
+                                                       const std::string& description,
+                                                       std::string* value) {
+  assert(value != nullptr);
+  auto func = [value](const std::string& arg) -> void { *value = arg; };
+  options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, true});
+  return *this;
+}
+
+bool CommandLineOptions::Parse(const std::vector<std::string>& argv, std::ostream& outError) const {
+  const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) {
+    return opt.count != Option::COUNT_OPTIONAL;
+  });
+  std::set<std::string> mandatory_opts;
+  std::transform(options_.begin(), pivot, std::inserter(mandatory_opts, mandatory_opts.end()),
+                 [](const Option& opt) -> std::string { return opt.name; });
+
+  const size_t argv_size = argv.size();
+  for (size_t i = 0; i < argv_size; i++) {
+    const std::string arg = argv[i];
+    if ("--help" == arg || "-h" == arg) {
+      Usage(outError);
+      return false;
+    }
+    bool match = false;
+    for (const Option& opt : options_) {
+      if (opt.name == arg) {
+        match = true;
+
+        if (opt.argument) {
+          i++;
+          if (i >= argv_size) {
+            outError << "error: " << opt.name << ": missing argument" << std::endl;
+            Usage(outError);
+            return false;
+          }
+        }
+        opt.action(argv[i]);
+        mandatory_opts.erase(opt.name);
+        break;
+      }
+    }
+    if (!match) {
+      outError << "error: " << arg << ": unknown option" << std::endl;
+      Usage(outError);
+      return false;
+    }
+  }
+
+  if (!mandatory_opts.empty()) {
+    for (auto iter = mandatory_opts.cbegin(); iter != mandatory_opts.cend(); ++iter) {
+      outError << "error: " << *iter << ": missing mandatory option" << std::endl;
+    }
+    Usage(outError);
+    return false;
+  }
+  return true;
+}
+
+void CommandLineOptions::Usage(std::ostream& out) const {
+  size_t maxLength = 0;
+  out << "usage: " << name_;
+  for (const Option& opt : options_) {
+    const bool mandatory = opt.count != Option::COUNT_OPTIONAL;
+    out << " ";
+    if (!mandatory) {
+      out << "[";
+    }
+    if (opt.argument) {
+      out << opt.name << " arg";
+      maxLength = std::max(maxLength, opt.name.size() + 4);
+    } else {
+      out << opt.name;
+      maxLength = std::max(maxLength, opt.name.size());
+    }
+    if (!mandatory) {
+      out << "]";
+    }
+    if (opt.count == Option::COUNT_ONCE_OR_MORE) {
+      out << " [" << opt.name << " arg [..]]";
+    }
+  }
+  out << std::endl << std::endl;
+  for (const Option& opt : options_) {
+    out << std::left << std::setw(maxLength);
+    if (opt.argument) {
+      out << (opt.name + " arg");
+    } else {
+      out << opt.name;
+    }
+    out << "    " << opt.description;
+    if (opt.count == Option::COUNT_ONCE_OR_MORE) {
+      out << " (can be provided multiple times)";
+    }
+    out << std::endl;
+  }
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp
new file mode 100644
index 0000000..4ac4c04
--- /dev/null
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "idmap2/FileUtils.h"
+
+namespace android {
+namespace idmap2 {
+namespace utils {
+
+std::unique_ptr<std::vector<std::string>> FindFiles(const std::string& root, bool recurse,
+                                                    const FindFilesPredicate& predicate) {
+  DIR* dir = opendir(root.c_str());
+  if (!dir) {
+    return nullptr;
+  }
+  std::unique_ptr<std::vector<std::string>> vector(new std::vector<std::string>());
+  struct dirent* dirent;
+  while ((dirent = readdir(dir))) {
+    const std::string path = root + "/" + dirent->d_name;
+    if (predicate(dirent->d_type, path)) {
+      vector->push_back(path);
+    }
+    if (recurse && dirent->d_type == DT_DIR && strcmp(dirent->d_name, ".") != 0 &&
+        strcmp(dirent->d_name, "..") != 0) {
+      auto sub_vector = FindFiles(path, recurse, predicate);
+      if (!sub_vector) {
+        closedir(dir);
+        return nullptr;
+      }
+      vector->insert(vector->end(), sub_vector->begin(), sub_vector->end());
+    }
+  }
+  closedir(dir);
+
+  return vector;
+}
+
+std::unique_ptr<std::string> ReadFile(const std::string& path) {
+  std::unique_ptr<std::string> str(new std::string());
+  std::ifstream fin(path);
+  str->append({std::istreambuf_iterator<char>(fin), std::istreambuf_iterator<char>()});
+  fin.close();
+  return str;
+}
+
+std::unique_ptr<std::string> ReadFile(int fd) {
+  std::unique_ptr<std::string> str(new std::string());
+  char buf[1024];
+  ssize_t r;
+  while ((r = read(fd, buf, sizeof(buf))) > 0) {
+    str->append(buf, r);
+  }
+  return r == 0 ? std::move(str) : nullptr;
+}
+
+}  // namespace utils
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
new file mode 100644
index 0000000..5a47e30
--- /dev/null
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <limits>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/AssetManager2.h"
+#include "utils/String16.h"
+#include "utils/String8.h"
+
+#include "idmap2/Idmap.h"
+#include "idmap2/ResourceUtils.h"
+#include "idmap2/ZipFile.h"
+
+namespace android {
+namespace idmap2 {
+
+#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
+
+#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
+
+struct MatchingResources {
+  void Add(ResourceId target_resid, ResourceId overlay_resid) {
+    TypeId target_typeid = EXTRACT_TYPE(target_resid);
+    if (map.find(target_typeid) == map.end()) {
+      map.emplace(target_typeid, std::set<std::pair<ResourceId, ResourceId>>());
+    }
+    map[target_typeid].insert(std::make_pair(target_resid, overlay_resid));
+  }
+
+  // target type id -> set { pair { overlay entry id, overlay entry id } }
+  std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>> map;
+};
+
+static bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
+  uint16_t value;
+  if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) {
+    *out = dtohl(value);
+    return true;
+  }
+  return false;
+}
+
+static bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
+  uint32_t value;
+  if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
+    *out = dtohl(value);
+    return true;
+  }
+  return false;
+}
+
+// a string is encoded as a kIdmapStringLength char array; the array is always null-terminated
+static bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) {
+  char buf[kIdmapStringLength];
+  memset(buf, 0, sizeof(buf));
+  if (!stream.read(buf, sizeof(buf))) {
+    return false;
+  }
+  if (buf[sizeof(buf) - 1] != '\0') {
+    return false;
+  }
+  memcpy(out, buf, sizeof(buf));
+  return true;
+}
+
+static ResourceId NameToResid(const AssetManager2& am, const std::string& name) {
+  return am.GetResourceId(name);
+}
+
+// TODO(martenkongstad): scan for package name instead of assuming package at index 0
+//
+// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
+// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
+// this assumption tends to work out. That said, the correct thing to do is to scan
+// resources.arsc for a package with a given name as read from the package manifest instead of
+// relying on a hard-coded index. This however requires storing the package name in the idmap
+// header, which in turn requires incrementing the idmap version. Because the initial version of
+// idmap2 is compatible with idmap, this will have to wait for now.
+static const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
+  const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
+  if (packages.empty()) {
+    return nullptr;
+  }
+  int id = packages[0]->GetPackageId();
+  return loaded_arsc.GetPackageById(id);
+}
+
+std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
+  std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader());
+
+  if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) ||
+      !Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) ||
+      !ReadString(stream, idmap_header->target_path_) ||
+      !ReadString(stream, idmap_header->overlay_path_)) {
+    return nullptr;
+  }
+
+  return std::move(idmap_header);
+}
+
+bool IdmapHeader::IsUpToDate(std::ostream& out_error) const {
+  if (magic_ != kIdmapMagic) {
+    out_error << base::StringPrintf("error: bad magic: actual 0x%08x, expected 0x%08x", magic_,
+                                    kIdmapMagic)
+              << std::endl;
+    return false;
+  }
+
+  if (version_ != kIdmapCurrentVersion) {
+    out_error << base::StringPrintf("error: bad version: actual 0x%08x, expected 0x%08x", version_,
+                                    kIdmapCurrentVersion)
+              << std::endl;
+    return false;
+  }
+
+  const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path_);
+  if (!target_zip) {
+    out_error << "error: failed to open target " << target_path_ << std::endl;
+    return false;
+  }
+
+  bool status;
+  uint32_t target_crc;
+  std::tie(status, target_crc) = target_zip->Crc("resources.arsc");
+  if (!status) {
+    out_error << "error: failed to get target crc" << std::endl;
+    return false;
+  }
+
+  if (target_crc_ != target_crc) {
+    out_error << base::StringPrintf(
+                     "error: bad target crc: idmap version 0x%08x, file system version 0x%08x",
+                     target_crc_, target_crc)
+              << std::endl;
+    return false;
+  }
+
+  const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path_);
+  if (!overlay_zip) {
+    out_error << "error: failed to open overlay " << overlay_path_ << std::endl;
+    return false;
+  }
+
+  uint32_t overlay_crc;
+  std::tie(status, overlay_crc) = overlay_zip->Crc("resources.arsc");
+  if (!status) {
+    out_error << "error: failed to get overlay crc" << std::endl;
+    return false;
+  }
+
+  if (overlay_crc_ != overlay_crc) {
+    out_error << base::StringPrintf(
+                     "error: bad overlay crc: idmap version 0x%08x, file system version 0x%08x",
+                     overlay_crc_, overlay_crc)
+              << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
+  std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
+
+  uint16_t target_package_id16;
+  if (!Read16(stream, &target_package_id16) || !Read16(stream, &idmap_data_header->type_count_)) {
+    return nullptr;
+  }
+  idmap_data_header->target_package_id_ = target_package_id16;
+
+  return std::move(idmap_data_header);
+}
+
+std::unique_ptr<const IdmapData::TypeEntry> IdmapData::TypeEntry::FromBinaryStream(
+    std::istream& stream) {
+  std::unique_ptr<IdmapData::TypeEntry> data(new IdmapData::TypeEntry());
+
+  uint16_t target_type16, overlay_type16, entry_count;
+  if (!Read16(stream, &target_type16) || !Read16(stream, &overlay_type16) ||
+      !Read16(stream, &entry_count) || !Read16(stream, &data->entry_offset_)) {
+    return nullptr;
+  }
+  data->target_type_id_ = target_type16;
+  data->overlay_type_id_ = overlay_type16;
+  for (uint16_t i = 0; i < entry_count; i++) {
+    ResourceId resid;
+    if (!Read32(stream, &resid)) {
+      return nullptr;
+    }
+    data->entries_.push_back(resid);
+  }
+
+  return std::move(data);
+}
+
+std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& stream) {
+  std::unique_ptr<IdmapData> data(new IdmapData());
+  data->header_ = IdmapData::Header::FromBinaryStream(stream);
+  if (!data->header_) {
+    return nullptr;
+  }
+  for (size_t type_count = 0; type_count < data->header_->GetTypeCount(); type_count++) {
+    std::unique_ptr<const TypeEntry> type = IdmapData::TypeEntry::FromBinaryStream(stream);
+    if (!type) {
+      return nullptr;
+    }
+    data->type_entries_.push_back(std::move(type));
+  }
+  return std::move(data);
+}
+
+std::string Idmap::CanonicalIdmapPathFor(const std::string& absolute_dir,
+                                         const std::string& absolute_apk_path) {
+  assert(absolute_dir.size() > 0 && absolute_dir[0] == "/");
+  assert(absolute_apk_path.size() > 0 && absolute_apk_path[0] == "/");
+  std::string copy(++absolute_apk_path.cbegin(), absolute_apk_path.cend());
+  replace(copy.begin(), copy.end(), '/', '@');
+  return absolute_dir + "/" + copy + "@idmap";
+}
+
+std::unique_ptr<const Idmap> Idmap::FromBinaryStream(std::istream& stream,
+                                                     std::ostream& out_error) {
+  std::unique_ptr<Idmap> idmap(new Idmap());
+
+  idmap->header_ = IdmapHeader::FromBinaryStream(stream);
+  if (!idmap->header_) {
+    out_error << "error: failed to parse idmap header" << std::endl;
+    return nullptr;
+  }
+
+  // idmap version 0x01 does not specify the number of data blocks that follow
+  // the idmap header; assume exactly one data block
+  for (int i = 0; i < 1; i++) {
+    std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
+    if (!data) {
+      out_error << "error: failed to parse data block " << i << std::endl;
+      return nullptr;
+    }
+    idmap->data_.push_back(std::move(data));
+  }
+
+  return std::move(idmap);
+}
+
+std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_path,
+                                                  const ApkAssets& target_apk_assets,
+                                                  const std::string& overlay_apk_path,
+                                                  const ApkAssets& overlay_apk_assets,
+                                                  std::ostream& out_error) {
+  AssetManager2 target_asset_manager;
+  if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) {
+    out_error << "error: failed to create target asset manager" << std::endl;
+    return nullptr;
+  }
+
+  AssetManager2 overlay_asset_manager;
+  if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true, false)) {
+    out_error << "error: failed to create overlay asset manager" << std::endl;
+    return nullptr;
+  }
+
+  const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
+  if (!target_arsc) {
+    out_error << "error: failed to load target resources.arsc" << std::endl;
+    return nullptr;
+  }
+
+  const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
+  if (!overlay_arsc) {
+    out_error << "error: failed to load overlay resources.arsc" << std::endl;
+    return nullptr;
+  }
+
+  const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
+  if (!target_pkg) {
+    out_error << "error: failed to load target package from resources.arsc" << std::endl;
+    return nullptr;
+  }
+
+  const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
+  if (!overlay_pkg) {
+    out_error << "error: failed to load overlay package from resources.arsc" << std::endl;
+    return nullptr;
+  }
+
+  const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path);
+  if (!target_zip) {
+    out_error << "error: failed to open target as zip" << std::endl;
+    return nullptr;
+  }
+
+  const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_apk_path);
+  if (!overlay_zip) {
+    out_error << "error: failed to open overlay as zip" << std::endl;
+    return nullptr;
+  }
+
+  std::unique_ptr<IdmapHeader> header(new IdmapHeader());
+  header->magic_ = kIdmapMagic;
+  header->version_ = kIdmapCurrentVersion;
+  bool crc_status;
+  std::tie(crc_status, header->target_crc_) = target_zip->Crc("resources.arsc");
+  if (!crc_status) {
+    out_error << "error: failed to get zip crc for target" << std::endl;
+    return nullptr;
+  }
+  std::tie(crc_status, header->overlay_crc_) = overlay_zip->Crc("resources.arsc");
+  if (!crc_status) {
+    out_error << "error: failed to get zip crc for overlay" << std::endl;
+    return nullptr;
+  }
+
+  if (target_apk_path.size() > sizeof(header->target_path_)) {
+    out_error << "error: target apk path \"" << target_apk_path << "\" longer that maximum size "
+              << sizeof(header->target_path_) << std::endl;
+    return nullptr;
+  }
+  memset(header->target_path_, 0, sizeof(header->target_path_));
+  memcpy(header->target_path_, target_apk_path.data(), target_apk_path.size());
+
+  if (overlay_apk_path.size() > sizeof(header->overlay_path_)) {
+    out_error << "error: overlay apk path \"" << overlay_apk_path << "\" longer that maximum size "
+              << sizeof(header->overlay_path_) << std::endl;
+    return nullptr;
+  }
+  memset(header->overlay_path_, 0, sizeof(header->overlay_path_));
+  memcpy(header->overlay_path_, overlay_apk_path.data(), overlay_apk_path.size());
+
+  std::unique_ptr<Idmap> idmap(new Idmap());
+  idmap->header_ = std::move(header);
+
+  // find the resources that exist in both packages
+  MatchingResources matching_resources;
+  const auto end = overlay_pkg->end();
+  for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
+    const ResourceId overlay_resid = *iter;
+    bool lookup_ok;
+    std::string name;
+    std::tie(lookup_ok, name) = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
+    if (!lookup_ok) {
+      continue;
+    }
+    // prepend "<package>:" to turn name into "<package>:<type>/<name>"
+    name = base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name.c_str());
+    const ResourceId target_resid = NameToResid(target_asset_manager, name);
+    if (target_resid == 0) {
+      continue;
+    }
+    matching_resources.Add(target_resid, overlay_resid);
+  }
+
+  // encode idmap data
+  std::unique_ptr<IdmapData> data(new IdmapData());
+  const auto types_end = matching_resources.map.cend();
+  for (auto ti = matching_resources.map.cbegin(); ti != types_end; ++ti) {
+    auto ei = ti->second.cbegin();
+    std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
+    type->target_type_id_ = EXTRACT_TYPE(ei->first);
+    type->overlay_type_id_ = EXTRACT_TYPE(ei->second);
+    type->entry_offset_ = EXTRACT_ENTRY(ei->first);
+    EntryId last_target_entry = kNoEntry;
+    for (; ei != ti->second.cend(); ++ei) {
+      if (last_target_entry != kNoEntry) {
+        int count = EXTRACT_ENTRY(ei->first) - last_target_entry - 1;
+        type->entries_.insert(type->entries_.end(), count, kNoEntry);
+      }
+      type->entries_.push_back(EXTRACT_ENTRY(ei->second));
+      last_target_entry = EXTRACT_ENTRY(ei->first);
+    }
+    data->type_entries_.push_back(std::move(type));
+  }
+
+  std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
+  data_header->target_package_id_ = target_pkg->GetPackageId();
+  data_header->type_count_ = data->type_entries_.size();
+  data->header_ = std::move(data_header);
+
+  idmap->data_.push_back(std::move(data));
+
+  return std::move(idmap);
+}
+
+void IdmapHeader::accept(Visitor* v) const {
+  assert(v != nullptr);
+  v->visit(*this);
+}
+
+void IdmapData::Header::accept(Visitor* v) const {
+  assert(v != nullptr);
+  v->visit(*this);
+}
+
+void IdmapData::TypeEntry::accept(Visitor* v) const {
+  assert(v != nullptr);
+  v->visit(*this);
+}
+
+void IdmapData::accept(Visitor* v) const {
+  assert(v != nullptr);
+  v->visit(*this);
+  header_->accept(v);
+  auto end = type_entries_.cend();
+  for (auto iter = type_entries_.cbegin(); iter != end; ++iter) {
+    (*iter)->accept(v);
+  }
+}
+
+void Idmap::accept(Visitor* v) const {
+  assert(v != nullptr);
+  v->visit(*this);
+  header_->accept(v);
+  auto end = data_.cend();
+  for (auto iter = data_.cbegin(); iter != end; ++iter) {
+    (*iter)->accept(v);
+  }
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
new file mode 100644
index 0000000..492e6f0
--- /dev/null
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <string>
+#include <utility>
+
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/ApkAssets.h"
+
+#include "idmap2/PrettyPrintVisitor.h"
+#include "idmap2/ResourceUtils.h"
+
+namespace android {
+namespace idmap2 {
+
+#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
+
+void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
+}
+
+void PrettyPrintVisitor::visit(const IdmapHeader& header) {
+  stream_ << "target apk path  : " << header.GetTargetPath() << std::endl
+          << "overlay apk path : " << header.GetOverlayPath() << std::endl;
+
+  target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
+  if (target_apk_) {
+    target_am_.SetApkAssets({target_apk_.get()});
+  }
+}
+
+void PrettyPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
+}
+
+void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED) {
+  last_seen_package_id_ = header.GetTargetPackageId();
+}
+
+void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& te) {
+  const bool target_package_loaded = !target_am_.GetApkAssets().empty();
+  for (uint16_t i = 0; i < te.GetEntryCount(); i++) {
+    const EntryId entry = te.GetEntry(i);
+    if (entry == kNoEntry) {
+      continue;
+    }
+
+    const ResourceId target_resid =
+        RESID(last_seen_package_id_, te.GetTargetTypeId(), te.GetEntryOffset() + i);
+    const ResourceId overlay_resid = RESID(last_seen_package_id_, te.GetOverlayTypeId(), entry);
+
+    stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid);
+    if (target_package_loaded) {
+      bool lookup_ok;
+      std::string name;
+      std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
+      if (lookup_ok) {
+        stream_ << " " << name;
+      }
+    }
+    stream_ << std::endl;
+  }
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
new file mode 100644
index 0000000..57cfc8e
--- /dev/null
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <cstdarg>
+#include <string>
+#include <utility>
+
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/ApkAssets.h"
+
+#include "idmap2/RawPrintVisitor.h"
+#include "idmap2/ResourceUtils.h"
+
+using android::ApkAssets;
+
+namespace android {
+namespace idmap2 {
+
+// verbatim copy fomr PrettyPrintVisitor.cpp, move to common utils
+#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
+
+void RawPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
+}
+
+void RawPrintVisitor::visit(const IdmapHeader& header) {
+  print(header.GetMagic(), "magic");
+  print(header.GetVersion(), "version");
+  print(header.GetTargetCrc(), "target crc");
+  print(header.GetOverlayCrc(), "overlay crc");
+  print(header.GetTargetPath().to_string(), "target path");
+  print(header.GetOverlayPath().to_string(), "overlay path");
+
+  target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
+  if (target_apk_) {
+    target_am_.SetApkAssets({target_apk_.get()});
+  }
+}
+
+void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
+}
+
+void RawPrintVisitor::visit(const IdmapData::Header& header) {
+  print(static_cast<uint16_t>(header.GetTargetPackageId()), "target package id");
+  print(header.GetTypeCount(), "type count");
+  last_seen_package_id_ = header.GetTargetPackageId();
+}
+
+void RawPrintVisitor::visit(const IdmapData::TypeEntry& te) {
+  const bool target_package_loaded = !target_am_.GetApkAssets().empty();
+
+  print(static_cast<uint16_t>(te.GetTargetTypeId()), "target type");
+  print(static_cast<uint16_t>(te.GetOverlayTypeId()), "overlay type");
+  print(static_cast<uint16_t>(te.GetEntryCount()), "entry count");
+  print(static_cast<uint16_t>(te.GetEntryOffset()), "entry offset");
+
+  for (uint16_t i = 0; i < te.GetEntryCount(); i++) {
+    const EntryId entry = te.GetEntry(i);
+    if (entry == kNoEntry) {
+      print(kPadding, "no entry");
+    } else {
+      const ResourceId target_resid =
+          RESID(last_seen_package_id_, te.GetTargetTypeId(), te.GetEntryOffset() + i);
+      const ResourceId overlay_resid = RESID(last_seen_package_id_, te.GetOverlayTypeId(), entry);
+      bool lookup_ok = false;
+      std::string name;
+      if (target_package_loaded) {
+        std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
+      }
+      if (lookup_ok) {
+        print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x %s", target_resid, overlay_resid,
+              name.c_str());
+      } else {
+        print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x", target_resid, overlay_resid);
+      }
+    }
+  }
+}
+
+void RawPrintVisitor::print(uint16_t value, const char* fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  std::string comment;
+  base::StringAppendV(&comment, fmt, ap);
+  va_end(ap);
+
+  stream_ << base::StringPrintf("%08zx:     %04x", offset_, value) << "  " << comment << std::endl;
+
+  offset_ += sizeof(uint16_t);
+}
+
+void RawPrintVisitor::print(uint32_t value, const char* fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  std::string comment;
+  base::StringAppendV(&comment, fmt, ap);
+  va_end(ap);
+
+  stream_ << base::StringPrintf("%08zx: %08x", offset_, value) << "  " << comment << std::endl;
+
+  offset_ += sizeof(uint32_t);
+}
+
+void RawPrintVisitor::print(const std::string& value, const char* fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  std::string comment;
+  base::StringAppendV(&comment, fmt, ap);
+  va_end(ap);
+
+  stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << ": " << value
+          << std::endl;
+
+  offset_ += kIdmapStringLength;
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
new file mode 100644
index 0000000..e98f843
--- /dev/null
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <string>
+#include <utility>
+
+#include "androidfw/StringPiece.h"
+#include "androidfw/Util.h"
+
+#include "idmap2/ResourceUtils.h"
+
+using android::StringPiece16;
+using android::util::Utf16ToUtf8;
+
+namespace android {
+namespace idmap2 {
+namespace utils {
+
+std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
+                                                            ResourceId resid) {
+  AssetManager2::ResourceName name;
+  if (!am.GetResourceName(resid, &name)) {
+    return std::make_pair(false, "");
+  }
+  std::string out;
+  if (name.type != nullptr) {
+    out.append(name.type, name.type_len);
+  } else {
+    out += Utf16ToUtf8(StringPiece16(name.type16, name.type_len));
+  }
+  out.append("/");
+  if (name.entry != nullptr) {
+    out.append(name.entry, name.entry_len);
+  } else {
+    out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
+  }
+  return std::make_pair(true, out);
+}
+
+}  // namespace utils
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/libidmap2/Xml.cpp b/cmds/idmap2/libidmap2/Xml.cpp
new file mode 100644
index 0000000..5543722
--- /dev/null
+++ b/cmds/idmap2/libidmap2/Xml.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "idmap2/Xml.h"
+
+namespace android {
+namespace idmap2 {
+
+std::unique_ptr<const Xml> Xml::Create(const uint8_t* data, size_t size, bool copyData) {
+  std::unique_ptr<Xml> xml(new Xml());
+  if (xml->xml_.setTo(data, size, copyData) != NO_ERROR) {
+    return nullptr;
+  }
+  return xml;
+}
+
+std::unique_ptr<std::map<std::string, std::string>> Xml::FindTag(const std::string& name) const {
+  const String16 tag_to_find(name.c_str(), name.size());
+  xml_.restart();
+  ResXMLParser::event_code_t type;
+  do {
+    type = xml_.next();
+    if (type == ResXMLParser::START_TAG) {
+      size_t len;
+      const String16 tag(xml_.getElementName(&len));
+      if (tag == tag_to_find) {
+        std::unique_ptr<std::map<std::string, std::string>> map(
+            new std::map<std::string, std::string>());
+        for (size_t i = 0; i < xml_.getAttributeCount(); i++) {
+          const String16 key16(xml_.getAttributeName(i, &len));
+          std::string key = String8(key16).c_str();
+
+          std::string value;
+          switch (xml_.getAttributeDataType(i)) {
+            case Res_value::TYPE_STRING: {
+              const String16 value16(xml_.getAttributeStringValue(i, &len));
+              value = String8(value16).c_str();
+            } break;
+            case Res_value::TYPE_INT_DEC:
+            case Res_value::TYPE_INT_HEX:
+            case Res_value::TYPE_INT_BOOLEAN: {
+              Res_value resValue;
+              xml_.getAttributeValue(i, &resValue);
+              value = std::to_string(resValue.data);
+            } break;
+            default:
+              return nullptr;
+          }
+
+          map->emplace(std::make_pair(key, value));
+        }
+        return map;
+      }
+    }
+  } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
+  return nullptr;
+}
+
+Xml::~Xml() {
+  xml_.uninit();
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp
new file mode 100644
index 0000000..3f2079a
--- /dev/null
+++ b/cmds/idmap2/libidmap2/ZipFile.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "idmap2/ZipFile.h"
+
+namespace android {
+namespace idmap2 {
+
+std::unique_ptr<MemoryChunk> MemoryChunk::Allocate(size_t size) {
+  void* ptr = ::operator new(sizeof(MemoryChunk) + size);
+  std::unique_ptr<MemoryChunk> chunk(reinterpret_cast<MemoryChunk*>(ptr));
+  chunk->size = size;
+  return chunk;
+}
+
+std::unique_ptr<const ZipFile> ZipFile::Open(const std::string& path) {
+  ::ZipArchiveHandle handle;
+  int32_t status = ::OpenArchive(path.c_str(), &handle);
+  if (status != 0) {
+    return nullptr;
+  }
+  return std::unique_ptr<ZipFile>(new ZipFile(handle));
+}
+
+ZipFile::~ZipFile() {
+  ::CloseArchive(handle_);
+}
+
+std::unique_ptr<const MemoryChunk> ZipFile::Uncompress(const std::string& entryPath) const {
+  ::ZipEntry entry;
+  int32_t status = ::FindEntry(handle_, ::ZipString(entryPath.c_str()), &entry);
+  if (status != 0) {
+    return nullptr;
+  }
+  std::unique_ptr<MemoryChunk> chunk = MemoryChunk::Allocate(entry.uncompressed_length);
+  status = ::ExtractToMemory(handle_, &entry, chunk->buf, chunk->size);
+  if (status != 0) {
+    return nullptr;
+  }
+  return chunk;
+}
+
+std::pair<bool, uint32_t> ZipFile::Crc(const std::string& entryPath) const {
+  ::ZipEntry entry;
+  int32_t status = ::FindEntry(handle_, ::ZipString(entryPath.c_str()), &entry);
+  return std::make_pair(status == 0, entry.crc32);
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/static-checks.sh b/cmds/idmap2/static-checks.sh
new file mode 100755
index 0000000..560ccb6
--- /dev/null
+++ b/cmds/idmap2/static-checks.sh
@@ -0,0 +1,121 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 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.
+#
+
+function _log()
+{
+    echo -e "$*" >&2
+}
+
+function _eval()
+{
+    local label="$1"
+    local cmd="$2"
+    local red="\e[31m"
+    local green="\e[32m"
+    local reset="\e[0m"
+
+    _log "${green}[ RUN      ]${reset} ${label}"
+    local output="$(eval "$cmd")"
+    if [[ -z "${output}" ]]; then
+        _log "${green}[       OK ]${reset} ${label}"
+        return 0
+    else
+        echo "${output}"
+        _log "${red}[  FAILED  ]${reset} ${label}"
+        errors=$((errors + 1))
+        return 1
+    fi
+}
+
+function _clang_format()
+{
+    local path
+    local errors=0
+
+    for path in $cpp_files; do
+        local output="$(clang-format -style=file "$path" | diff $path -)"
+        if [[ "$output" ]]; then
+            echo "$path"
+            echo "$output"
+            errors=1
+        fi
+    done
+    return $errors
+}
+
+function _bpfmt()
+{
+    local output="$(bpfmt -s -d $bp_files)"
+    if [[ "$output" ]]; then
+        echo "$output"
+        return 1
+    fi
+    return 0
+}
+
+function _cpplint()
+{
+    local cpplint="${ANDROID_BUILD_TOP}/tools/repohooks/tools/cpplint.py"
+    $cpplint --quiet $cpp_files
+}
+
+function _parse_args()
+{
+    local opts
+
+    opts="$(getopt -o cfh --long check,fix,help -- "$@")"
+    if [[ $? -ne 0 ]]; then
+        exit 1
+    fi
+    eval set -- "$opts"
+    while true; do
+        case "$1" in
+            -c|--check) opt_mode="check"; shift ;;
+            -f|--fix) opt_mode="fix"; shift ;;
+            -h|--help) opt_mode="help"; shift ;;
+            *) break ;;
+        esac
+    done
+}
+
+errors=0
+script="$(readlink -f "$BASH_SOURCE")"
+prefix="$(dirname "$script")"
+cpp_files="$(find "$prefix" -name '*.cpp' -or -name '*.h')"
+bp_files="$(find "$prefix" -name 'Android.bp')"
+opt_mode="check"
+
+_parse_args "$@"
+if [[ $opt_mode == "check" ]]; then
+    _eval "clang-format" "_clang_format"
+    _eval "bpfmt" "_bpfmt"
+    _eval "cpplint" "_cpplint"
+    exit $errors
+elif [[ $opt_mode == "fix" ]]; then
+    clang-format -style=file -i $cpp_files
+    bpfmt -s -w $bp_files
+    exit 0
+elif [[ $opt_mode == "help" ]]; then
+    echo "Run static analysis tools such as clang-format and cpplint on the idmap2"
+    echo "module. Optionally fix some of the issues found (--fix). Intended to be run"
+    echo "before merging any changes."
+    echo
+    echo "usage: $(basename $script) [--check|--fix|--help]"
+    exit 0
+else
+    exit 1
+fi
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
new file mode 100644
index 0000000..8b552dc
--- /dev/null
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/Idmap.h"
+
+#include "idmap2/BinaryStreamVisitor.h"
+#include "idmap2/Idmap.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
+  std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+  std::istringstream raw_stream(raw);
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap1 = Idmap::FromBinaryStream(raw_stream, error);
+  ASSERT_THAT(idmap1, NotNull());
+
+  std::stringstream stream;
+  BinaryStreamVisitor visitor(stream);
+  idmap1->accept(&visitor);
+
+  std::unique_ptr<const Idmap> idmap2 = Idmap::FromBinaryStream(stream, error);
+  ASSERT_THAT(idmap2, NotNull());
+
+  ASSERT_EQ(idmap1->GetHeader()->GetTargetCrc(), idmap2->GetHeader()->GetTargetCrc());
+  ASSERT_EQ(idmap1->GetHeader()->GetTargetPath(), idmap2->GetHeader()->GetTargetPath());
+  ASSERT_EQ(idmap1->GetData().size(), 1u);
+  ASSERT_EQ(idmap1->GetData().size(), idmap2->GetData().size());
+
+  const auto& data1 = idmap1->GetData()[0];
+  const auto& data2 = idmap2->GetData()[0];
+
+  ASSERT_EQ(data1->GetHeader()->GetTargetPackageId(), data2->GetHeader()->GetTargetPackageId());
+  ASSERT_EQ(data1->GetTypeEntries().size(), 2u);
+  ASSERT_EQ(data1->GetTypeEntries().size(), data2->GetTypeEntries().size());
+  ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(0), data2->GetTypeEntries()[0]->GetEntry(0));
+  ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(1), data2->GetTypeEntries()[0]->GetEntry(1));
+  ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(2), data2->GetTypeEntries()[0]->GetEntry(2));
+  ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(0), data2->GetTypeEntries()[1]->GetEntry(0));
+  ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(1), data2->GetTypeEntries()[1]->GetEntry(1));
+  ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(2), data2->GetTypeEntries()[1]->GetEntry(2));
+}
+
+TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) {
+  const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  ASSERT_THAT(target_apk, NotNull());
+
+  const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  ASSERT_THAT(overlay_apk, NotNull());
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  std::stringstream stream;
+  BinaryStreamVisitor visitor(stream);
+  idmap->accept(&visitor);
+  const std::string str = stream.str();
+  const StringPiece data(str);
+  std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(data);
+  ASSERT_THAT(loaded_idmap, NotNull());
+  ASSERT_EQ(loaded_idmap->TargetPackageId(), 0x7f);
+
+  const IdmapEntry_header* header = loaded_idmap->GetEntryMapForType(0x01);
+  ASSERT_THAT(header, NotNull());
+
+  EntryId entry;
+  bool success = LoadedIdmap::Lookup(header, 0x0000, &entry);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(entry, 0x0000);
+
+  header = loaded_idmap->GetEntryMapForType(0x02);
+  ASSERT_THAT(header, NotNull());
+
+  success = LoadedIdmap::Lookup(header, 0x0002, &entry);
+  ASSERT_FALSE(success);
+
+  success = LoadedIdmap::Lookup(header, 0x0003, &entry);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(entry, 0x0000);
+
+  success = LoadedIdmap::Lookup(header, 0x0004, &entry);
+  ASSERT_FALSE(success);
+
+  success = LoadedIdmap::Lookup(header, 0x0005, &entry);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(entry, 0x0001);
+
+  success = LoadedIdmap::Lookup(header, 0x0006, &entry);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(entry, 0x0002);
+
+  success = LoadedIdmap::Lookup(header, 0x0007, &entry);
+  ASSERT_FALSE(success);
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/tests/CommandLineOptionsTests.cpp b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
new file mode 100644
index 0000000..b04b256
--- /dev/null
+++ b/cmds/idmap2/tests/CommandLineOptionsTests.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "android-base/file.h"
+#include "androidfw/ApkAssets.h"
+#include "androidfw/Idmap.h"
+#include "androidfw/LoadedArsc.h"
+
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Idmap.h"
+
+#include "TestHelpers.h"
+
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(CommandLineOptionsTests, Flag) {
+  bool foo = true, bar = false;
+
+  CommandLineOptions opts =
+      CommandLineOptions("test").OptionalFlag("--foo", "", &foo).OptionalFlag("--bar", "", &bar);
+
+  std::ostream fakeStdErr(nullptr);
+  bool success = opts.Parse({"--foo", "--bar"}, fakeStdErr);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(foo);
+  ASSERT_TRUE(bar);
+
+  foo = bar = false;
+  success = opts.Parse({"--foo"}, fakeStdErr);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(foo);
+  ASSERT_FALSE(bar);
+}
+
+TEST(CommandLineOptionsTests, MandatoryOption) {
+  std::string foo, bar;
+  CommandLineOptions opts = CommandLineOptions("test")
+                                .MandatoryOption("--foo", "", &foo)
+                                .MandatoryOption("--bar", "", &bar);
+  std::ostream fakeStdErr(nullptr);
+  bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(foo, "FOO");
+  ASSERT_EQ(bar, "BAR");
+
+  success = opts.Parse({"--foo"}, fakeStdErr);
+  ASSERT_FALSE(success);
+}
+
+TEST(CommandLineOptionsTests, MandatoryOptionMultipleArgsButExpectedOnce) {
+  std::string foo;
+  CommandLineOptions opts = CommandLineOptions("test").MandatoryOption("--foo", "", &foo);
+  std::ostream fakeStdErr(nullptr);
+  bool success = opts.Parse({"--foo", "FIRST", "--foo", "SECOND"}, fakeStdErr);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(foo, "SECOND");
+}
+
+TEST(CommandLineOptionsTests, MandatoryOptionMultipleArgsAndExpectedOnceOrMore) {
+  std::vector<std::string> args;
+  CommandLineOptions opts = CommandLineOptions("test").MandatoryOption("--foo", "", &args);
+  std::ostream fakeStdErr(nullptr);
+  bool success = opts.Parse({"--foo", "FOO", "--foo", "BAR"}, fakeStdErr);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(args.size(), 2u);
+  ASSERT_EQ(args[0], "FOO");
+  ASSERT_EQ(args[1], "BAR");
+}
+
+TEST(CommandLineOptionsTests, OptionalOption) {
+  std::string foo, bar;
+  CommandLineOptions opts = CommandLineOptions("test")
+                                .OptionalOption("--foo", "", &foo)
+                                .OptionalOption("--bar", "", &bar);
+  std::ostream fakeStdErr(nullptr);
+  bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(foo, "FOO");
+  ASSERT_EQ(bar, "BAR");
+
+  success = opts.Parse({"--foo", "BAZ"}, fakeStdErr);
+  ASSERT_TRUE(success);
+  ASSERT_EQ(foo, "BAZ");
+
+  success = opts.Parse({"--foo"}, fakeStdErr);
+  ASSERT_FALSE(success);
+
+  success = opts.Parse({"--foo", "--bar", "BAR"}, fakeStdErr);
+  ASSERT_FALSE(success);
+
+  success = opts.Parse({"--foo", "FOO", "--bar"}, fakeStdErr);
+  ASSERT_FALSE(success);
+}
+
+TEST(CommandLineOptionsTests, CornerCases) {
+  std::string foo, bar;
+  bool baz = false;
+  CommandLineOptions opts = CommandLineOptions("test")
+                                .MandatoryOption("--foo", "", &foo)
+                                .OptionalFlag("--baz", "", &baz)
+                                .OptionalOption("--bar", "", &bar);
+  std::ostream fakeStdErr(nullptr);
+  bool success = opts.Parse({"--unexpected"}, fakeStdErr);
+  ASSERT_FALSE(success);
+
+  success = opts.Parse({"--bar", "BAR"}, fakeStdErr);
+  ASSERT_FALSE(success);
+
+  success = opts.Parse({"--baz", "--foo", "FOO"}, fakeStdErr);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(baz);
+  ASSERT_EQ(foo, "FOO");
+}
+
+TEST(CommandLineOptionsTests, ConvertArgvToVector) {
+  const char* argv[] = {
+      "program-name",
+      "--foo",
+      "FOO",
+      nullptr,
+  };
+  std::unique_ptr<std::vector<std::string>> v = CommandLineOptions::ConvertArgvToVector(3, argv);
+  ASSERT_EQ(v->size(), 2ul);
+  ASSERT_EQ((*v)[0], "--foo");
+  ASSERT_EQ((*v)[1], "FOO");
+}
+
+TEST(CommandLineOptionsTests, ConvertArgvToVectorNoArgs) {
+  const char* argv[] = {
+      "program-name",
+      nullptr,
+  };
+  std::unique_ptr<std::vector<std::string>> v = CommandLineOptions::ConvertArgvToVector(1, argv);
+  ASSERT_EQ(v->size(), 0ul);
+}
+
+TEST(CommandLineOptionsTests, Usage) {
+  std::string arg1, arg2, arg3, arg4;
+  bool arg5 = false, arg6 = false;
+  std::vector<std::string> arg7;
+  CommandLineOptions opts = CommandLineOptions("test")
+                                .MandatoryOption("--aa", "description-aa", &arg1)
+                                .OptionalFlag("--bb", "description-bb", &arg5)
+                                .OptionalOption("--cc", "description-cc", &arg2)
+                                .OptionalOption("--dd", "description-dd", &arg3)
+                                .MandatoryOption("--ee", "description-ee", &arg4)
+                                .OptionalFlag("--ff", "description-ff", &arg6)
+                                .MandatoryOption("--gg", "description-gg", &arg7);
+  std::stringstream stream;
+  opts.Usage(stream);
+  const std::string s = stream.str();
+  ASSERT_NE(s.find("usage: test --aa arg [--bb] [--cc arg] [--dd arg] --ee arg [--ff] --gg arg "
+                   "[--gg arg [..]]"),
+            std::string::npos);
+  ASSERT_NE(s.find("--aa arg    description-aa"), std::string::npos);
+  ASSERT_NE(s.find("--ff        description-ff"), std::string::npos);
+  ASSERT_NE(s.find("--gg arg    description-gg (can be provided multiple times)"),
+            std::string::npos);
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
new file mode 100644
index 0000000..0c6439a
--- /dev/null
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <dirent.h>
+#include <set>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "android-base/macros.h"
+
+#include "idmap2/FileUtils.h"
+
+#include "TestHelpers.h"
+
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+namespace utils {
+
+TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) {
+  const auto& root = GetTestDataPath();
+  auto v = utils::FindFiles(root, false,
+                            [](unsigned char type ATTRIBUTE_UNUSED,
+                               const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
+  ASSERT_THAT(v, NotNull());
+  ASSERT_EQ(v->size(), 4u);
+  ASSERT_EQ(
+      std::set<std::string>(v->begin(), v->end()),
+      std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target"}));
+}
+
+TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
+  const auto& root = GetTestDataPath();
+  auto v = utils::FindFiles(root, true, [](unsigned char type, const std::string& path) -> bool {
+    return type == DT_REG && path.size() > 4 && !path.compare(path.size() - 4, 4, ".apk");
+  });
+  ASSERT_THAT(v, NotNull());
+  ASSERT_EQ(v->size(), 4u);
+  ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
+            std::set<std::string>({root + "/target/target.apk", root + "/overlay/overlay.apk",
+                                   root + "/overlay/overlay-static-1.apk",
+                                   root + "/overlay/overlay-static-2.apk"}));
+}
+
+TEST(FileUtilsTests, ReadFile) {
+  int pipefd[2];
+  ASSERT_EQ(pipe(pipefd), 0);
+
+  ASSERT_EQ(write(pipefd[1], "foobar", 6), 6);
+  close(pipefd[1]);
+
+  auto data = ReadFile(pipefd[0]);
+  ASSERT_THAT(data, NotNull());
+  ASSERT_EQ(*data, "foobar");
+  close(pipefd[0]);
+}
+
+}  // namespace utils
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
new file mode 100644
index 0000000..5c4e857
--- /dev/null
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+/*
+ * The tests in this file operate on a higher level than the tests in the other
+ * files. Here, all tests execute the idmap2 binary and only depend on
+ * libidmap2 to verify the output of idmap2.
+ */
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>  // strerror
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "androidfw/PosixUtils.h"
+#include "idmap2/FileUtils.h"
+#include "idmap2/Idmap.h"
+
+#include "TestHelpers.h"
+
+using ::android::util::ExecuteBinary;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+class Idmap2BinaryTests : public Idmap2Tests {};
+
+static void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path,
+                        const std::string& overlay_apk_path) {
+  // check that the idmap file looks reasonable (IdmapTests is responsible for
+  // more in-depth verification)
+  ASSERT_EQ(idmap.GetHeader()->GetMagic(), kIdmapMagic);
+  ASSERT_EQ(idmap.GetHeader()->GetVersion(), kIdmapCurrentVersion);
+  ASSERT_EQ(idmap.GetHeader()->GetTargetPath(), target_apk_path);
+  ASSERT_EQ(idmap.GetHeader()->GetOverlayPath(), overlay_apk_path);
+  ASSERT_EQ(idmap.GetData().size(), 1u);
+}
+
+#define ASSERT_IDMAP(idmap_ref, target_apk_path, overlay_apk_path)                      \
+  do {                                                                                  \
+    ASSERT_NO_FATAL_FAILURE(AssertIdmap(idmap_ref, target_apk_path, overlay_apk_path)); \
+  } while (0)
+
+TEST_F(Idmap2BinaryTests, Create) {
+  // clang-format off
+  auto result = ExecuteBinary({"idmap2",
+                               "create",
+                               "--target-apk-path", GetTargetApkPath(),
+                               "--overlay-apk-path", GetOverlayApkPath(),
+                               "--idmap-path", GetIdmapPath()});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+
+  struct stat st;
+  ASSERT_EQ(stat(GetIdmapPath().c_str(), &st), 0);
+
+  std::stringstream error;
+  std::ifstream fin(GetIdmapPath());
+  std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(fin, error);
+  fin.close();
+
+  ASSERT_THAT(idmap, NotNull());
+  ASSERT_IDMAP(*idmap, GetTargetApkPath(), GetOverlayApkPath());
+
+  unlink(GetIdmapPath().c_str());
+}
+
+TEST_F(Idmap2BinaryTests, Dump) {
+  // clang-format off
+  auto result = ExecuteBinary({"idmap2",
+                               "create",
+                               "--target-apk-path", GetTargetApkPath(),
+                               "--overlay-apk-path", GetOverlayApkPath(),
+                               "--idmap-path", GetIdmapPath()});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+
+  // clang-format off
+  result = ExecuteBinary({"idmap2",
+                          "dump",
+                          "--idmap-path", GetIdmapPath()});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+  ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos);
+  ASSERT_NE(result->stdout.find("0x7f020003 -> 0x7f020000 string/str1"), std::string::npos);
+  ASSERT_NE(result->stdout.find("0x7f020005 -> 0x7f020001 string/str3"), std::string::npos);
+  ASSERT_EQ(result->stdout.find("00000210:     007f  target package id"), std::string::npos);
+
+  // clang-format off
+  result = ExecuteBinary({"idmap2",
+                          "dump",
+                          "--verbose",
+                          "--idmap-path", GetIdmapPath()});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+  ASSERT_NE(result->stdout.find("00000000: 504d4449  magic"), std::string::npos);
+  ASSERT_NE(result->stdout.find("00000210:     007f  target package id"), std::string::npos);
+
+  // clang-format off
+  result = ExecuteBinary({"idmap2",
+                          "dump",
+                          "--verbose",
+                          "--idmap-path", GetTestDataPath() + "/DOES-NOT-EXIST"});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_NE(result->status, EXIT_SUCCESS);
+
+  unlink(GetIdmapPath().c_str());
+}
+
+TEST_F(Idmap2BinaryTests, Scan) {
+  const std::string overlay_static_1_apk_path = GetTestDataPath() + "/overlay/overlay-static-1.apk";
+  const std::string overlay_static_2_apk_path = GetTestDataPath() + "/overlay/overlay-static-2.apk";
+  const std::string idmap_static_1_path =
+      Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_1_apk_path);
+  const std::string idmap_static_2_path =
+      Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_2_apk_path);
+
+  // single input directory, recursive
+  // clang-format off
+  auto result = ExecuteBinary({"idmap2",
+                               "scan",
+                               "--input-directory", GetTestDataPath(),
+                               "--recursive",
+                               "--target-package-name", "test.target",
+                               "--target-apk-path", GetTargetApkPath(),
+                               "--output-directory", GetTempDirPath()});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+  std::stringstream expected;
+  expected << idmap_static_1_path << std::endl;
+  expected << idmap_static_2_path << std::endl;
+  ASSERT_EQ(result->stdout, expected.str());
+
+  std::stringstream error;
+  auto idmap_static_1_raw_string = utils::ReadFile(idmap_static_1_path);
+  auto idmap_static_1_raw_stream = std::istringstream(*idmap_static_1_raw_string);
+  auto idmap_static_1 = Idmap::FromBinaryStream(idmap_static_1_raw_stream, error);
+  ASSERT_THAT(idmap_static_1, NotNull());
+  ASSERT_IDMAP(*idmap_static_1, GetTargetApkPath(), overlay_static_1_apk_path);
+
+  auto idmap_static_2_raw_string = utils::ReadFile(idmap_static_2_path);
+  auto idmap_static_2_raw_stream = std::istringstream(*idmap_static_2_raw_string);
+  auto idmap_static_2 = Idmap::FromBinaryStream(idmap_static_2_raw_stream, error);
+  ASSERT_THAT(idmap_static_2, NotNull());
+  ASSERT_IDMAP(*idmap_static_2, GetTargetApkPath(), overlay_static_2_apk_path);
+
+  unlink(idmap_static_2_path.c_str());
+  unlink(idmap_static_1_path.c_str());
+
+  // multiple input directories, non-recursive
+  // clang-format off
+  result = ExecuteBinary({"idmap2",
+                          "scan",
+                          "--input-directory", GetTestDataPath() + "/target",
+                          "--input-directory", GetTestDataPath() + "/overlay",
+                          "--target-package-name", "test.target",
+                          "--target-apk-path", GetTargetApkPath(),
+                          "--output-directory", GetTempDirPath()});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+  ASSERT_EQ(result->stdout, expected.str());
+  unlink(idmap_static_2_path.c_str());
+  unlink(idmap_static_1_path.c_str());
+
+  // the same input directory given twice, but no duplicate entries
+  // clang-format off
+  result = ExecuteBinary({"idmap2",
+                          "scan",
+                          "--input-directory", GetTestDataPath(),
+                          "--input-directory", GetTestDataPath(),
+                          "--recursive",
+                          "--target-package-name", "test.target",
+                          "--target-apk-path", GetTargetApkPath(),
+                          "--output-directory", GetTempDirPath()});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+  ASSERT_EQ(result->stdout, expected.str());
+  unlink(idmap_static_2_path.c_str());
+  unlink(idmap_static_1_path.c_str());
+
+  // no APKs in input-directory: ok, but no output
+  // clang-format off
+  result = ExecuteBinary({"idmap2",
+                          "scan",
+                          "--input-directory", GetTempDirPath(),
+                          "--target-package-name", "test.target",
+                          "--target-apk-path", GetTargetApkPath(),
+                          "--output-directory", GetTempDirPath()});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+  ASSERT_EQ(result->stdout, "");
+}
+
+TEST_F(Idmap2BinaryTests, Lookup) {
+  // clang-format off
+  auto result = ExecuteBinary({"idmap2",
+                               "create",
+                               "--target-apk-path", GetTargetApkPath(),
+                               "--overlay-apk-path", GetOverlayApkPath(),
+                               "--idmap-path", GetIdmapPath()});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+
+  // clang-format off
+  result = ExecuteBinary({"idmap2",
+                          "lookup",
+                          "--idmap-path", GetIdmapPath(),
+                          "--config", "",
+                          "--resid", "0x7f020003"});  // string/str1
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+  ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
+  ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
+
+  // clang-format off
+  result = ExecuteBinary({"idmap2",
+                          "lookup",
+                          "--idmap-path", GetIdmapPath(),
+                          "--config", "",
+                          "--resid", "test.target:string/str1"});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+  ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
+  ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
+
+  // clang-format off
+  result = ExecuteBinary({"idmap2",
+                          "lookup",
+                          "--idmap-path", GetIdmapPath(),
+                          "--config", "sv",
+                          "--resid", "test.target:string/str1"});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+  ASSERT_NE(result->stdout.find("overlay-1-sv"), std::string::npos);
+
+  unlink(GetIdmapPath().c_str());
+}
+
+TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) {
+  const std::string invalid_target_apk_path = GetTestDataPath() + "/DOES-NOT-EXIST";
+
+  // missing mandatory options
+  // clang-format off
+  auto result = ExecuteBinary({"idmap2",
+                               "create"});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_NE(result->status, EXIT_SUCCESS);
+
+  // missing argument to option
+  // clang-format off
+  result = ExecuteBinary({"idmap2",
+                          "create",
+                          "--target-apk-path", GetTargetApkPath(),
+                          "--overlay-apk-path", GetOverlayApkPath(),
+                          "--idmap-path"});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_NE(result->status, EXIT_SUCCESS);
+
+  // invalid target apk path
+  // clang-format off
+  result = ExecuteBinary({"idmap2",
+                          "create",
+                          "--target-apk-path", invalid_target_apk_path,
+                          "--overlay-apk-path", GetOverlayApkPath(),
+                          "--idmap-path", GetIdmapPath()});
+  // clang-format on
+  ASSERT_THAT(result, NotNull());
+  ASSERT_NE(result->status, EXIT_SUCCESS);
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
new file mode 100644
index 0000000..0379aa4
--- /dev/null
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <cstdio>  // fclose
+
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "android-base/macros.h"
+#include "androidfw/ApkAssets.h"
+
+#include "idmap2/BinaryStreamVisitor.h"
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/Idmap.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(IdmapTests, TestCanonicalIdmapPathFor) {
+  ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"),
+            "/foo/vendor@overlay@bar.apk@idmap");
+}
+
+TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
+  std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+  std::istringstream stream(raw);
+  std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
+  ASSERT_THAT(header, NotNull());
+  ASSERT_EQ(header->GetMagic(), 0x504d4449u);
+  ASSERT_EQ(header->GetVersion(), 0x01u);
+  ASSERT_EQ(header->GetTargetCrc(), 0x1234u);
+  ASSERT_EQ(header->GetOverlayCrc(), 0x5678u);
+  ASSERT_EQ(header->GetTargetPath().to_string(), "target.apk");
+  ASSERT_EQ(header->GetOverlayPath().to_string(), "overlay.apk");
+}
+
+TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) {
+  std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+  // overwrite the target path string, including the terminating null, with '.'
+  for (size_t i = 0x10; i < 0x110; i++) {
+    raw[i] = '.';
+  }
+  std::istringstream stream(raw);
+  std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
+  ASSERT_THAT(header, IsNull());
+}
+
+TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
+  const size_t offset = 0x210;
+  std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
+                  idmap_raw_data_len - offset);
+  std::istringstream stream(raw);
+
+  std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream);
+  ASSERT_THAT(header, NotNull());
+  ASSERT_EQ(header->GetTargetPackageId(), 0x7fu);
+  ASSERT_EQ(header->GetTypeCount(), 2u);
+}
+
+TEST(IdmapTests, CreateIdmapDataResourceTypeFromBinaryStream) {
+  const size_t offset = 0x214;
+  std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
+                  idmap_raw_data_len - offset);
+  std::istringstream stream(raw);
+
+  std::unique_ptr<const IdmapData::TypeEntry> data = IdmapData::TypeEntry::FromBinaryStream(stream);
+  ASSERT_THAT(data, NotNull());
+  ASSERT_EQ(data->GetTargetTypeId(), 0x02u);
+  ASSERT_EQ(data->GetOverlayTypeId(), 0x02u);
+  ASSERT_EQ(data->GetEntryCount(), 1u);
+  ASSERT_EQ(data->GetEntryOffset(), 0u);
+  ASSERT_EQ(data->GetEntry(0), 0u);
+}
+
+TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
+  const size_t offset = 0x210;
+  std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
+                  idmap_raw_data_len - offset);
+  std::istringstream stream(raw);
+
+  std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
+  ASSERT_THAT(data, NotNull());
+  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
+  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+  const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+  ASSERT_EQ(types.size(), 2u);
+
+  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02u);
+  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02u);
+  ASSERT_EQ(types[0]->GetEntryCount(), 1u);
+  ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
+  ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+
+  ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03u);
+  ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03u);
+  ASSERT_EQ(types[1]->GetEntryCount(), 3u);
+  ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
+  ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+  ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
+  ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+}
+
+TEST(IdmapTests, CreateIdmapFromBinaryStream) {
+  std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+  std::istringstream stream(raw);
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(stream, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  ASSERT_THAT(idmap->GetHeader(), NotNull());
+  ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449u);
+  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01u);
+  ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234u);
+  ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678u);
+  ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "target.apk");
+  ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlay.apk");
+
+  const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+  ASSERT_EQ(dataBlocks.size(), 1u);
+
+  const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
+  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+  const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+  ASSERT_EQ(types.size(), 2u);
+
+  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02u);
+  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02u);
+  ASSERT_EQ(types[0]->GetEntryCount(), 1u);
+  ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
+  ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+
+  ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03u);
+  ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03u);
+  ASSERT_EQ(types[1]->GetEntryCount(), 3u);
+  ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
+  ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+  ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
+  ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+}
+
+TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) {
+  std::string raw(reinterpret_cast<const char*>(idmap_raw_data),
+                  10);  // data too small
+  std::istringstream stream(raw);
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(stream, error);
+  ASSERT_THAT(idmap, IsNull());
+}
+
+TEST(IdmapTests, CreateIdmapFromApkAssets) {
+  const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  ASSERT_THAT(target_apk, NotNull());
+
+  const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  ASSERT_THAT(overlay_apk, NotNull());
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  ASSERT_THAT(idmap->GetHeader(), NotNull());
+  ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449u);
+  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01u);
+  ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xf5ad1d1d);
+  ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b);
+  ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
+  ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
+  ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
+
+  const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+  ASSERT_EQ(dataBlocks.size(), 1u);
+
+  const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+
+  ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fu);
+  ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2u);
+
+  const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
+  ASSERT_EQ(types.size(), 2u);
+
+  ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01u);
+  ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01u);
+  ASSERT_EQ(types[0]->GetEntryCount(), 1u);
+  ASSERT_EQ(types[0]->GetEntryOffset(), 0u);
+  ASSERT_EQ(types[0]->GetEntry(0), 0x0000u);
+
+  ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02u);
+  ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02u);
+  ASSERT_EQ(types[1]->GetEntryCount(), 4u);
+  ASSERT_EQ(types[1]->GetEntryOffset(), 3u);
+  ASSERT_EQ(types[1]->GetEntry(0), 0x0000u);
+  ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
+  ASSERT_EQ(types[1]->GetEntry(2), 0x0001u);
+  ASSERT_EQ(types[1]->GetEntry(3), 0x0002u);
+}
+
+TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
+  std::string target_apk_path(GetTestDataPath());
+  for (int i = 0; i < 32; i++) {
+    target_apk_path += "/target/../";
+  }
+  target_apk_path += "/target/target.apk";
+  ASSERT_GT(target_apk_path.size(), kIdmapStringLength);
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  ASSERT_THAT(target_apk, NotNull());
+
+  const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  ASSERT_THAT(overlay_apk, NotNull());
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+  ASSERT_THAT(idmap, IsNull());
+}
+
+TEST(IdmapTests, IdmapHeaderIsUpToDate) {
+  fclose(stderr);  // silence expected warnings from libandroidfw
+
+  const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  ASSERT_THAT(target_apk, NotNull());
+
+  const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  ASSERT_THAT(overlay_apk, NotNull());
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  std::stringstream stream;
+  BinaryStreamVisitor visitor(stream);
+  idmap->accept(&visitor);
+
+  std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
+  ASSERT_THAT(header, NotNull());
+  ASSERT_TRUE(header->IsUpToDate(error)) << error.str();
+
+  // magic: bytes (0x0, 0x03)
+  std::string bad_magic_string(stream.str());
+  bad_magic_string[0x0] = '.';
+  bad_magic_string[0x1] = '.';
+  bad_magic_string[0x2] = '.';
+  bad_magic_string[0x3] = '.';
+  std::stringstream bad_magic_stream(bad_magic_string);
+  std::unique_ptr<const IdmapHeader> bad_magic_header =
+      IdmapHeader::FromBinaryStream(bad_magic_stream);
+  ASSERT_THAT(bad_magic_header, NotNull());
+  ASSERT_NE(header->GetMagic(), bad_magic_header->GetMagic());
+  ASSERT_FALSE(bad_magic_header->IsUpToDate(error));
+
+  // version: bytes (0x4, 0x07)
+  std::string bad_version_string(stream.str());
+  bad_version_string[0x4] = '.';
+  bad_version_string[0x5] = '.';
+  bad_version_string[0x6] = '.';
+  bad_version_string[0x7] = '.';
+  std::stringstream bad_version_stream(bad_version_string);
+  std::unique_ptr<const IdmapHeader> bad_version_header =
+      IdmapHeader::FromBinaryStream(bad_version_stream);
+  ASSERT_THAT(bad_version_header, NotNull());
+  ASSERT_NE(header->GetVersion(), bad_version_header->GetVersion());
+  ASSERT_FALSE(bad_version_header->IsUpToDate(error));
+
+  // target crc: bytes (0x8, 0xb)
+  std::string bad_target_crc_string(stream.str());
+  bad_target_crc_string[0x8] = '.';
+  bad_target_crc_string[0x9] = '.';
+  bad_target_crc_string[0xa] = '.';
+  bad_target_crc_string[0xb] = '.';
+  std::stringstream bad_target_crc_stream(bad_target_crc_string);
+  std::unique_ptr<const IdmapHeader> bad_target_crc_header =
+      IdmapHeader::FromBinaryStream(bad_target_crc_stream);
+  ASSERT_THAT(bad_target_crc_header, NotNull());
+  ASSERT_NE(header->GetTargetCrc(), bad_target_crc_header->GetTargetCrc());
+  ASSERT_FALSE(bad_target_crc_header->IsUpToDate(error));
+
+  // overlay crc: bytes (0xc, 0xf)
+  std::string bad_overlay_crc_string(stream.str());
+  bad_overlay_crc_string[0xc] = '.';
+  bad_overlay_crc_string[0xd] = '.';
+  bad_overlay_crc_string[0xe] = '.';
+  bad_overlay_crc_string[0xf] = '.';
+  std::stringstream bad_overlay_crc_stream(bad_overlay_crc_string);
+  std::unique_ptr<const IdmapHeader> bad_overlay_crc_header =
+      IdmapHeader::FromBinaryStream(bad_overlay_crc_stream);
+  ASSERT_THAT(bad_overlay_crc_header, NotNull());
+  ASSERT_NE(header->GetOverlayCrc(), bad_overlay_crc_header->GetOverlayCrc());
+  ASSERT_FALSE(bad_overlay_crc_header->IsUpToDate(error));
+
+  // target path: bytes (0x10, 0x10f)
+  std::string bad_target_path_string(stream.str());
+  bad_target_path_string[0x10] = '\0';
+  std::stringstream bad_target_path_stream(bad_target_path_string);
+  std::unique_ptr<const IdmapHeader> bad_target_path_header =
+      IdmapHeader::FromBinaryStream(bad_target_path_stream);
+  ASSERT_THAT(bad_target_path_header, NotNull());
+  ASSERT_NE(header->GetTargetPath(), bad_target_path_header->GetTargetPath());
+  ASSERT_FALSE(bad_target_path_header->IsUpToDate(error));
+
+  // overlay path: bytes (0x110, 0x20f)
+  std::string bad_overlay_path_string(stream.str());
+  bad_overlay_path_string[0x110] = '\0';
+  std::stringstream bad_overlay_path_stream(bad_overlay_path_string);
+  std::unique_ptr<const IdmapHeader> bad_overlay_path_header =
+      IdmapHeader::FromBinaryStream(bad_overlay_path_stream);
+  ASSERT_THAT(bad_overlay_path_header, NotNull());
+  ASSERT_NE(header->GetOverlayPath(), bad_overlay_path_header->GetOverlayPath());
+  ASSERT_FALSE(bad_overlay_path_header->IsUpToDate(error));
+}
+
+class TestVisitor : public Visitor {
+ public:
+  explicit TestVisitor(std::ostream& stream) : stream_(stream) {
+  }
+
+  void visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
+    stream_ << "TestVisitor::visit(Idmap)" << std::endl;
+  }
+
+  void visit(const IdmapHeader& idmap ATTRIBUTE_UNUSED) {
+    stream_ << "TestVisitor::visit(IdmapHeader)" << std::endl;
+  }
+
+  void visit(const IdmapData& idmap ATTRIBUTE_UNUSED) {
+    stream_ << "TestVisitor::visit(IdmapData)" << std::endl;
+  }
+
+  void visit(const IdmapData::Header& idmap ATTRIBUTE_UNUSED) {
+    stream_ << "TestVisitor::visit(IdmapData::Header)" << std::endl;
+  }
+
+  void visit(const IdmapData::TypeEntry& idmap ATTRIBUTE_UNUSED) {
+    stream_ << "TestVisitor::visit(IdmapData::TypeEntry)" << std::endl;
+  }
+
+ private:
+  std::ostream& stream_;
+};
+
+TEST(IdmapTests, TestVisitor) {
+  std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+  std::istringstream stream(raw);
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(stream, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  std::stringstream test_stream;
+  TestVisitor visitor(test_stream);
+  idmap->accept(&visitor);
+
+  ASSERT_EQ(test_stream.str(),
+            "TestVisitor::visit(Idmap)\n"
+            "TestVisitor::visit(IdmapHeader)\n"
+            "TestVisitor::visit(IdmapData)\n"
+            "TestVisitor::visit(IdmapData::Header)\n"
+            "TestVisitor::visit(IdmapData::TypeEntry)\n"
+            "TestVisitor::visit(IdmapData::TypeEntry)\n");
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/tests/Main.cpp b/cmds/idmap2/tests/Main.cpp
new file mode 100644
index 0000000..f2469ea
--- /dev/null
+++ b/cmds/idmap2/tests/Main.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <string>
+
+#include "android-base/file.h"
+
+#include "gtest/gtest.h"
+
+#include "TestHelpers.h"
+
+namespace android {
+namespace idmap2 {
+
+const std::string GetTestDataPath() {
+  return base::GetExecutableDirectory() + "/tests/data";
+}
+
+}  // namespace idmap2
+}  // namespace android
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
new file mode 100644
index 0000000..da97792
--- /dev/null
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/Idmap.h"
+
+#include "idmap2/Idmap.h"
+#include "idmap2/PrettyPrintVisitor.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+using android::ApkAssets;
+
+namespace android {
+namespace idmap2 {
+
+TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
+  const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  ASSERT_THAT(target_apk, NotNull());
+
+  const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  ASSERT_THAT(overlay_apk, NotNull());
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  std::stringstream stream;
+  PrettyPrintVisitor visitor(stream);
+  idmap->accept(&visitor);
+
+  ASSERT_NE(stream.str().find("target apk path  : "), std::string::npos);
+  ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
+  ASSERT_NE(stream.str().find("0x7f010000 -> 0x7f010000 integer/int1\n"), std::string::npos);
+}
+
+TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
+  fclose(stderr);  // silence expected warnings from libandroidfw
+
+  std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+  std::istringstream raw_stream(raw);
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(raw_stream, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  std::stringstream stream;
+  PrettyPrintVisitor visitor(stream);
+  idmap->accept(&visitor);
+
+  ASSERT_NE(stream.str().find("target apk path  : "), std::string::npos);
+  ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
+  ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000\n"), std::string::npos);
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
new file mode 100644
index 0000000..c28ce2e
--- /dev/null
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <cstdio>  // fclose
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "idmap2/Idmap.h"
+#include "idmap2/RawPrintVisitor.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
+  const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  ASSERT_THAT(target_apk, NotNull());
+
+  const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  ASSERT_THAT(overlay_apk, NotNull());
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap =
+      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  std::stringstream stream;
+  RawPrintVisitor visitor(stream);
+  idmap->accept(&visitor);
+
+  ASSERT_NE(stream.str().find("00000000: 504d4449  magic\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("00000004: 00000001  version\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("00000008: f5ad1d1d  target crc\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("0000000c: d470336b  overlay crc\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("0000021c: 00000000  0x7f010000 -> 0x7f010000 integer/int1\n"),
+            std::string::npos);
+}
+
+TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
+  fclose(stderr);  // silence expected warnings from libandroidfw
+
+  std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
+  std::istringstream raw_stream(raw);
+
+  std::stringstream error;
+  std::unique_ptr<const Idmap> idmap = Idmap::FromBinaryStream(raw_stream, error);
+  ASSERT_THAT(idmap, NotNull());
+
+  std::stringstream stream;
+  RawPrintVisitor visitor(stream);
+  idmap->accept(&visitor);
+
+  ASSERT_NE(stream.str().find("00000000: 504d4449  magic\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("00000004: 00000001  version\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("00000008: 00001234  target crc\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("0000000c: 00005678  overlay crc\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("0000021c: 00000000  0x7f020000 -> 0x7f020000\n"), std::string::npos);
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp
new file mode 100644
index 0000000..0547fa0
--- /dev/null
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "androidfw/ApkAssets.h"
+#include "idmap2/ResourceUtils.h"
+
+#include "TestHelpers.h"
+
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+class ResourceUtilsTests : public Idmap2Tests {
+ protected:
+  void SetUp() override {
+    Idmap2Tests::SetUp();
+
+    apk_assets_ = ApkAssets::Load(GetTargetApkPath());
+    ASSERT_THAT(apk_assets_, NotNull());
+
+    am_.SetApkAssets({apk_assets_.get()});
+  }
+
+  const AssetManager2& GetAssetManager() {
+    return am_;
+  }
+
+ private:
+  AssetManager2 am_;
+  std::unique_ptr<const ApkAssets> apk_assets_;
+};
+
+TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
+  bool lookup_ok;
+  std::string name;
+  std::tie(lookup_ok, name) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
+  ASSERT_TRUE(lookup_ok);
+  ASSERT_EQ(name, "integer/int1");
+}
+
+TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) {
+  bool lookup_ok;
+  std::tie(lookup_ok, std::ignore) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
+  ASSERT_FALSE(lookup_ok);
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
new file mode 100644
index 0000000..18dc541
--- /dev/null
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef IDMAP2_TESTS_TESTHELPERS_H_
+#define IDMAP2_TESTS_TESTHELPERS_H_
+
+#include <string>
+
+namespace android {
+namespace idmap2 {
+
+const unsigned char idmap_raw_data[] = {
+    // IDMAP HEADER
+    // 0x0: magic
+    0x49, 0x44, 0x4d, 0x50,
+
+    // 0x4: version
+    0x01, 0x00, 0x00, 0x00,
+
+    // 0x8: target crc
+    0x34, 0x12, 0x00, 0x00,
+
+    // 0xc: overlay crc
+    0x78, 0x56, 0x00, 0x00,
+
+    // 0x10: target path "target.apk"
+    0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+    // 0x110: overlay path "overlay.apk"
+    0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+    // DATA HEADER
+    // 0x210: target package id
+    0x7f, 0x00,
+
+    // 0x212: types count
+    0x02, 0x00,
+
+    // DATA BLOCK
+    // 0x214: target type
+    0x02, 0x00,
+
+    // 0x216: overlay type
+    0x02, 0x00,
+
+    // 0x218: entry count
+    0x01, 0x00,
+
+    // 0x21a: entry offset
+    0x00, 0x00,
+
+    // 0x21c: entries
+    0x00, 0x00, 0x00, 0x00,
+
+    // DATA BLOCK
+    // 0x220: target type
+    0x03, 0x00,
+
+    // 0x222: overlay type
+    0x03, 0x00,
+
+    // 0x224: entry count
+    0x03, 0x00,
+
+    // 0x226: entry offset
+    0x03, 0x00,
+
+    // 0x228, 0x22c, 0x230: entries
+    0x00, 0x00, 0x00, 0x00,
+
+    0xff, 0xff, 0xff, 0xff,
+
+    0x01, 0x00, 0x00, 0x00};
+
+const unsigned int idmap_raw_data_len = 565;
+
+const std::string GetTestDataPath();
+
+class Idmap2Tests : public testing::Test {
+ protected:
+  virtual void SetUp() {
+#ifdef __ANDROID__
+    tmp_dir_path_ = "/data/local/tmp/idmap2-tests-XXXXXX";
+#else
+    tmp_dir_path_ = "/tmp/idmap2-tests-XXXXXX";
+#endif
+    EXPECT_NE(mkdtemp(const_cast<char*>(tmp_dir_path_.c_str())), nullptr)
+        << "Failed to create temporary directory: " << strerror(errno);
+    target_apk_path_ = GetTestDataPath() + "/target/target.apk";
+    overlay_apk_path_ = GetTestDataPath() + "/overlay/overlay.apk";
+    idmap_path_ = tmp_dir_path_ + "/a.idmap";
+  }
+
+  virtual void TearDown() {
+    EXPECT_EQ(rmdir(tmp_dir_path_.c_str()), 0)
+        << "Failed to remove temporary directory " << tmp_dir_path_ << ": " << strerror(errno);
+  }
+
+  const std::string& GetTempDirPath() {
+    return tmp_dir_path_;
+  }
+
+  const std::string& GetTargetApkPath() {
+    return target_apk_path_;
+  }
+
+  const std::string& GetOverlayApkPath() {
+    return overlay_apk_path_;
+  }
+
+  const std::string& GetIdmapPath() {
+    return idmap_path_;
+  }
+
+ private:
+  std::string tmp_dir_path_;
+  std::string target_apk_path_;
+  std::string overlay_apk_path_;
+  std::string idmap_path_;
+};
+
+}  // namespace idmap2
+}  // namespace android
+
+#endif  // IDMAP2_TESTS_TESTHELPERS_H_
diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp
new file mode 100644
index 0000000..97ff03e
--- /dev/null
+++ b/cmds/idmap2/tests/XmlTests.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <cstdio>  // fclose
+
+#include "idmap2/Xml.h"
+#include "idmap2/ZipFile.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(XmlTests, Create) {
+  auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+  ASSERT_THAT(zip, NotNull());
+
+  auto data = zip->Uncompress("AndroidManifest.xml");
+  ASSERT_THAT(data, NotNull());
+
+  auto xml = Xml::Create(data->buf, data->size);
+  ASSERT_THAT(xml, NotNull());
+
+  fclose(stderr);  // silence expected warnings from libandroidfw
+  const char* not_xml = "foo";
+  auto fail = Xml::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
+  ASSERT_THAT(fail, IsNull());
+}
+
+TEST(XmlTests, FindTag) {
+  auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+  ASSERT_THAT(zip, NotNull());
+
+  auto data = zip->Uncompress("res/xml/test.xml");
+  ASSERT_THAT(data, NotNull());
+
+  auto xml = Xml::Create(data->buf, data->size);
+  ASSERT_THAT(xml, NotNull());
+
+  auto attrs = xml->FindTag("c");
+  ASSERT_THAT(attrs, NotNull());
+  ASSERT_EQ(attrs->size(), 4u);
+  ASSERT_EQ(attrs->at("type_string"), "fortytwo");
+  ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42);
+  ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42);
+  ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0u);
+
+  auto fail = xml->FindTag("does-not-exist");
+  ASSERT_THAT(fail, IsNull());
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp
new file mode 100644
index 0000000..a504d31
--- /dev/null
+++ b/cmds/idmap2/tests/ZipFileTests.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <cstdio>  // fclose
+#include <string>
+#include <utility>
+
+#include "idmap2/ZipFile.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "TestHelpers.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace android {
+namespace idmap2 {
+
+TEST(ZipFileTests, BasicOpen) {
+  auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+  ASSERT_THAT(zip, NotNull());
+
+  fclose(stderr);  // silence expected warnings from libziparchive
+  auto fail = ZipFile::Open(GetTestDataPath() + "/does-not-exist");
+  ASSERT_THAT(fail, IsNull());
+}
+
+TEST(ZipFileTests, Crc) {
+  auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+  ASSERT_THAT(zip, NotNull());
+
+  bool status;
+  uint32_t crc;
+  std::tie(status, crc) = zip->Crc("AndroidManifest.xml");
+  ASSERT_TRUE(status);
+  ASSERT_EQ(crc, 0x762f3d24);
+
+  std::tie(status, std::ignore) = zip->Crc("does-not-exist");
+  ASSERT_FALSE(status);
+}
+
+TEST(ZipFileTests, Uncompress) {
+  auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+  ASSERT_THAT(zip, NotNull());
+
+  auto data = zip->Uncompress("assets/lorem-ipsum.txt");
+  ASSERT_THAT(data, NotNull());
+  const std::string lorem_ipsum("Lorem ipsum dolor sit amet.\n");
+  ASSERT_THAT(data->size, lorem_ipsum.size());
+  ASSERT_THAT(std::string(reinterpret_cast<const char*>(data->buf), data->size), lorem_ipsum);
+
+  auto fail = zip->Uncompress("does-not-exist");
+  ASSERT_THAT(fail, IsNull());
+}
+
+}  // namespace idmap2
+}  // namespace android
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
new file mode 100644
index 0000000..9f89d31
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="test.overlay">
+    <overlay
+        android:targetPackage="test.target" />
+</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml
new file mode 100644
index 0000000..39336cc
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic1.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="test.overlay.static1">
+    <overlay
+        android:targetPackage="test.target"
+        android:isStatic="true"
+        android:priority="1" />
+</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml
new file mode 100644
index 0000000..e1cc175
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifestStatic2.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="test.overlay.static2">
+    <overlay
+        android:targetPackage="test.target"
+        android:isStatic="true"
+        android:priority="2" />
+</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build
new file mode 100644
index 0000000..cba1086
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/build
@@ -0,0 +1,40 @@
+# Copyright (C) 2018 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.
+
+FRAMEWORK_RES_APK="$(gettop)/out/target/common/obj/APPS/framework-res_intermediates/package-export.apk"
+
+aapt2 compile --dir res -o compiled.flata
+
+aapt2 link \
+    --no-resource-removal \
+    -I "$FRAMEWORK_RES_APK" \
+    --manifest AndroidManifest.xml \
+    -o overlay.apk \
+    compiled.flata
+
+aapt2 link \
+    --no-resource-removal \
+    -I "$FRAMEWORK_RES_APK" \
+    --manifest AndroidManifestStatic1.xml \
+    -o overlay-static-1.apk \
+    compiled.flata
+
+aapt2 link \
+    --no-resource-removal \
+    -I "$FRAMEWORK_RES_APK" \
+    --manifest AndroidManifestStatic2.xml \
+    -o overlay-static-2.apk \
+    compiled.flata
+
+rm compiled.flata
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
new file mode 100644
index 0000000..9a0f487
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
new file mode 100644
index 0000000..3fc31c7
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay.apk b/cmds/idmap2/tests/data/overlay/overlay.apk
new file mode 100644
index 0000000..b4cd7cf
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/res/values-sv/values.xml b/cmds/idmap2/tests/data/overlay/res/values-sv/values.xml
new file mode 100644
index 0000000..eed0b3d
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/values-sv/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="str1">overlay-1-sv</string>
+    <string name="str4">overlay-4-sv</string>
+</resources>
diff --git a/cmds/idmap2/tests/data/overlay/res/values/values.xml b/cmds/idmap2/tests/data/overlay/res/values/values.xml
new file mode 100644
index 0000000..815d1a8
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/values/values.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="str1">overlay-1</string>
+    <string name="str3">overlay-3</string>
+    <integer name="int1">-1</integer>
+    <integer name="not_in_target">-1</integer>
+</resources>
diff --git a/cmds/idmap2/tests/data/target/AndroidManifest.xml b/cmds/idmap2/tests/data/target/AndroidManifest.xml
new file mode 100644
index 0000000..3a861b4
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="test.target">
+</manifest>
diff --git a/cmds/idmap2/tests/data/target/assets/lorem-ipsum.txt b/cmds/idmap2/tests/data/target/assets/lorem-ipsum.txt
new file mode 100644
index 0000000..d2cf010
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/assets/lorem-ipsum.txt
@@ -0,0 +1 @@
+Lorem ipsum dolor sit amet.
diff --git a/cmds/idmap2/tests/data/target/build b/cmds/idmap2/tests/data/target/build
new file mode 100644
index 0000000..8569c4f
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/build
@@ -0,0 +1,17 @@
+# Copyright (C) 2018 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.
+
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -A assets -o target.apk compiled.flata
+rm compiled.flata
diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml
new file mode 100644
index 0000000..56bf0d6
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/res/values/values.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="a">a</string>
+    <string name="b">b</string>
+    <string name="c">c</string>
+    <string name="str1">target-1</string>
+    <string name="str2">target-2</string>
+    <string name="str3">target-3</string>
+    <string name="str4">target-4</string>
+    <string name="x">x</string>
+    <string name="y">y</string>
+    <string name="z">z</string>
+    <integer name="int1">1</integer>
+</resources>
diff --git a/cmds/idmap2/tests/data/target/res/xml/test.xml b/cmds/idmap2/tests/data/target/res/xml/test.xml
new file mode 100644
index 0000000..0fe21c6
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/res/xml/test.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<a>
+    <b>
+        <c
+            type_string="fortytwo"
+            type_int_dec="42"
+            type_int_hex="0x2a"
+            type_int_boolean="true"
+            />
+    </b>
+</a>
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
new file mode 100644
index 0000000..18ecc27
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
diff --git a/cmds/incident/Android.bp b/cmds/incident/Android.bp
new file mode 100644
index 0000000..2a5ec5b
--- /dev/null
+++ b/cmds/incident/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2016 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.
+
+cc_binary {
+    name: "incident",
+
+    srcs: [
+        "main.cpp",
+        ":incident_sections",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libutils",
+        "libincident",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+    ],
+}
+
+genrule {
+    name: "incident_sections",
+    tools: ["incident-section-gen"],
+    out: ["incident_sections.cpp"],
+    cmd: "$(location incident-section-gen) incident > $(out)",
+}
diff --git a/cmds/incident/Android.mk b/cmds/incident/Android.mk
deleted file mode 100644
index 8615f9b..0000000
--- a/cmds/incident/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2016 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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-        main.cpp
-
-LOCAL_MODULE := incident
-
-LOCAL_SHARED_LIBRARIES := \
-        libbase \
-        libbinder \
-        libcutils \
-        liblog \
-        libutils \
-        libincident
-
-LOCAL_CFLAGS += \
-        -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-gen_src_dir := $(local-generated-sources-dir)
-
-gen := $(gen_src_dir)/incident_sections.cpp
-$(gen): $(HOST_OUT_EXECUTABLES)/incident-section-gen
-$(gen): PRIVATE_CUSTOM_TOOL = \
-    $(HOST_OUT_EXECUTABLES)/incident-section-gen incident > $@
-$(gen): $(HOST_OUT_EXECUTABLES)/incident-section-gen
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(gen)
-
-gen_src_dir:=
-gen:=
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp
new file mode 100644
index 0000000..1e970f4
--- /dev/null
+++ b/cmds/incidentd/Android.bp
@@ -0,0 +1,117 @@
+// Copyright (C) 2016 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.
+
+// =========
+// incidentd
+// =========
+
+cc_binary {
+    name: "incidentd",
+
+    srcs: [
+        "src/**/*.cpp",
+        ":incidentd_section_list",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+
+        // Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
+        "-Wno-error=implicit-fallthrough",
+
+        // optimize for size (protobuf glop can get big)
+        "-Os",
+        //"-g",
+        //"-O0",
+    ],
+
+    local_include_dirs: ["src"],
+    generated_headers: ["gen-platform-proto-constants"],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libdebuggerd_client",
+        "libdumputils",
+        "libincident",
+        "liblog",
+        "libprotoutil",
+        "libservices",
+        "libutils",
+    ],
+
+    init_rc: ["incidentd.rc"],
+}
+
+// ==============
+// incidentd_test
+// ==============
+
+cc_test {
+    name: "incidentd_test",
+    test_suites: ["device-tests"],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+
+        // Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
+        "-Wno-error=implicit-fallthrough",
+    ],
+
+    local_include_dirs: ["src"],
+    generated_headers: ["gen-platform-proto-constants"],
+
+    srcs: [
+        "tests/**/*.cpp",
+        "src/PrivacyBuffer.cpp",
+        "src/FdBuffer.cpp",
+        "src/Privacy.cpp",
+        "src/Reporter.cpp",
+        "src/Section.cpp",
+        "src/Throttler.cpp",
+        "src/incidentd_util.cpp",
+        "src/report_directory.cpp",
+    ],
+
+    data: ["testdata/**/*"],
+
+    static_libs: ["libgmock"],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libdebuggerd_client",
+        "libdumputils",
+        "libincident",
+        "liblog",
+        "libprotobuf-cpp-lite",
+        "libprotoutil",
+        "libservices",
+        "libutils",
+    ],
+}
+
+genrule {
+    name: "incidentd_section_list",
+    tools: ["incident-section-gen"],
+    out: ["section_list.cpp"],
+    cmd: "$(location incident-section-gen) incidentd > $(out)",
+}
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
deleted file mode 100644
index eba5586..0000000
--- a/cmds/incidentd/Android.mk
+++ /dev/null
@@ -1,156 +0,0 @@
-# Copyright (C) 2016 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# proto files used in incidentd to generate cppstream proto headers.
-PROTO_FILES:= \
-        frameworks/base/core/proto/android/os/backtrace.proto \
-        frameworks/base/core/proto/android/os/data.proto \
-        frameworks/base/core/proto/android/util/log.proto
-
-# ========= #
-# incidentd #
-# ========= #
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := incidentd
-
-LOCAL_SRC_FILES := $(call all-cpp-files-under, src) \
-
-LOCAL_CFLAGS += \
-        -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-# Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
-LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
-
-ifeq (debug,)
-    LOCAL_CFLAGS += \
-            -g -O0
-else
-    # optimize for size (protobuf glop can get big)
-    LOCAL_CFLAGS += \
-            -Os
-endif
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
-
-LOCAL_SHARED_LIBRARIES := \
-        libbase \
-        libbinder \
-        libdebuggerd_client \
-        libdumputils \
-        libincident \
-        liblog \
-        libprotoutil \
-        libservices \
-        libutils
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-
-gen_src_dir := $(local-generated-sources-dir)
-
-# generate section_list.cpp
-GEN_LIST := $(gen_src_dir)/src/section_list.cpp
-$(GEN_LIST): $(HOST_OUT_EXECUTABLES)/incident-section-gen
-$(GEN_LIST): PRIVATE_CUSTOM_TOOL = \
-    $(HOST_OUT_EXECUTABLES)/incident-section-gen incidentd > $@
-$(GEN_LIST): $(HOST_OUT_EXECUTABLES)/incident-section-gen
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN_LIST)
-GEN_LIST:=
-
-# generate cppstream proto, add proto files to PROTO_FILES
-GEN_PROTO := $(gen_src_dir)/proto.timestamp
-$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES)
-$(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir)
-$(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \
-    $(HOST_OUT_EXECUTABLES)/aprotoc --plugin=protoc-gen-cppstream=$(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream \
-        --cppstream_out=$(PRIVATE_GEN_SRC_DIR) -Iexternal/protobuf/src -I . \
-        $(PROTO_FILES) \
-    && touch $@
-$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN_PROTO)
-GEN_PROTO:=
-
-gen_src_dir:=
-
-LOCAL_INIT_RC := incidentd.rc
-
-include $(BUILD_EXECUTABLE)
-
-# ============== #
-# incidentd_test #
-# ============== #
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := incidentd_test
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_CFLAGS := -Werror -Wall -Wno-unused-variable -Wunused-parameter
-
-# Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
-LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
-
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
-
-LOCAL_SRC_FILES := $(call all-cpp-files-under, tests) \
-    src/PrivacyBuffer.cpp \
-    src/FdBuffer.cpp \
-    src/Privacy.cpp \
-    src/Reporter.cpp \
-    src/Section.cpp \
-    src/Throttler.cpp \
-    src/incidentd_util.cpp \
-    src/report_directory.cpp \
-
-LOCAL_STATIC_LIBRARIES := \
-    libgmock \
-
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    libbinder \
-    libdebuggerd_client \
-    libdumputils \
-    libincident \
-    liblog \
-    libprotobuf-cpp-lite \
-    libprotoutil \
-    libservices \
-    libutils \
-
-LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, testdata)
-
-LOCAL_MODULE_CLASS := NATIVE_TESTS
-gen_src_dir := $(local-generated-sources-dir)
-# generate cppstream proto for testing
-GEN_PROTO := $(gen_src_dir)/test.proto.timestamp
-$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES)
-$(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir)
-$(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \
-    $(HOST_OUT_EXECUTABLES)/aprotoc --plugin=protoc-gen-cppstream=$(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream \
-        --cppstream_out=$(PRIVATE_GEN_SRC_DIR) -Iexternal/protobuf/src -I . \
-        $(PROTO_FILES) \
-    && touch $@
-$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN_PROTO)
-GEN_PROTO:=
-
-gen_src_dir:=
-
-include $(BUILD_NATIVE_TEST)
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 94203f4f..a3cd8a3 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2015 The Android Open Source Project
+// Copyright (C) 2017 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.
@@ -42,6 +42,279 @@
 
 }
 
+cc_defaults {
+    name: "statsd_defaults",
+    aidl: {
+        include_dirs: ["frameworks/base/core/java"],
+    },
+
+    srcs: [
+        ":statsd_aidl",
+        "src/statsd_config.proto",
+        "src/FieldValue.cpp",
+        "src/hash.cpp",
+        "src/stats_log_util.cpp",
+        "src/anomaly/AlarmMonitor.cpp",
+        "src/anomaly/AlarmTracker.cpp",
+        "src/anomaly/AnomalyTracker.cpp",
+        "src/anomaly/DurationAnomalyTracker.cpp",
+        "src/anomaly/subscriber_util.cpp",
+        "src/condition/CombinationConditionTracker.cpp",
+        "src/condition/condition_util.cpp",
+        "src/condition/SimpleConditionTracker.cpp",
+        "src/condition/ConditionWizard.cpp",
+        "src/condition/StateTracker.cpp",
+        "src/config/ConfigKey.cpp",
+        "src/config/ConfigListener.cpp",
+        "src/config/ConfigManager.cpp",
+        "src/external/Perfetto.cpp",
+        "src/external/Perfprofd.cpp",
+        "src/external/StatsPuller.cpp",
+        "src/external/StatsCompanionServicePuller.cpp",
+        "src/external/SubsystemSleepStatePuller.cpp",
+        "src/external/ResourceHealthManagerPuller.cpp",
+        "src/external/ResourceThermalManagerPuller.cpp",
+        "src/external/StatsPullerManager.cpp",
+        "src/external/puller_util.cpp",
+        "src/logd/LogEvent.cpp",
+        "src/logd/LogListener.cpp",
+        "src/matchers/CombinationLogMatchingTracker.cpp",
+        "src/matchers/EventMatcherWizard.cpp",
+        "src/matchers/matcher_util.cpp",
+        "src/matchers/SimpleLogMatchingTracker.cpp",
+        "src/metrics/MetricProducer.cpp",
+        "src/metrics/EventMetricProducer.cpp",
+        "src/metrics/CountMetricProducer.cpp",
+        "src/metrics/DurationMetricProducer.cpp",
+        "src/metrics/duration_helper/OringDurationTracker.cpp",
+        "src/metrics/duration_helper/MaxDurationTracker.cpp",
+        "src/metrics/ValueMetricProducer.cpp",
+        "src/metrics/GaugeMetricProducer.cpp",
+        "src/metrics/MetricsManager.cpp",
+        "src/metrics/metrics_manager_util.cpp",
+        "src/packages/UidMap.cpp",
+        "src/storage/StorageManager.cpp",
+        "src/StatsLogProcessor.cpp",
+        "src/StatsService.cpp",
+        "src/statscompanion_util.cpp",
+        "src/subscriber/IncidentdReporter.cpp",
+        "src/subscriber/SubscriberReporter.cpp",
+        "src/HashableDimensionKey.cpp",
+        "src/guardrail/StatsdStats.cpp",
+        "src/socket/StatsSocketListener.cpp",
+        "src/shell/ShellSubscriber.cpp",
+        "src/shell/shell_config.proto",
+
+        ":perfprofd_aidl",
+    ],
+
+    local_include_dirs: [
+        "src",
+    ],
+
+    static_libs: [
+        "libhealthhalutils",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libincident",
+        "liblog",
+        "libutils",
+        "libservices",
+        "libprotoutil",
+        "libstatslog",
+        "libhardware",
+        "libhardware_legacy",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "android.frameworks.stats@1.0",
+        "android.hardware.health@2.0",
+        "android.hardware.power@1.0",
+        "android.hardware.power@1.1",
+        "android.hardware.thermal@1.0",
+        "libpackagelistparser",
+        "libsysutils",
+        "libcutils",
+    ],
+}
+
+// =========
+// statsd
+// =========
+
+cc_binary {
+    name: "statsd",
+    defaults: ["statsd_defaults"],
+
+    srcs: ["src/main.cpp"],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wno-unused-parameter",
+        // optimize for size (protobuf glop can get big)
+        "-Os",
+        // "-g",
+        // "-O0",
+    ],
+
+    product_variables: {
+        eng: {
+            // Enable sanitizer ONLY on eng builds
+            //sanitize: {
+            //    address: true,
+            //},
+        },
+        debuggable: {
+            // Add a flag to enable stats log printing from statsd on debug builds.
+            cflags: ["-DVERY_VERBOSE_PRINTING"],
+        },
+    },
+
+    proto: {
+        type: "lite",
+    },
+
+    shared_libs: ["libgtest_prod"],
+
+    vintf_fragments: ["android.frameworks.stats@1.0-service.xml"],
+
+    init_rc: ["statsd.rc"],
+}
+
+// ==============
+// statsd_test
+// ==============
+
+cc_test {
+    name: "statsd_test",
+    defaults: ["statsd_defaults"],
+    test_suites: ["device-tests"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wno-unused-function",
+        "-Wno-unused-parameter",
+    ],
+
+    srcs: [
+        "src/atom_field_options.proto",
+        "src/atoms.proto",
+        "src/stats_log.proto",
+        "src/shell/shell_data.proto",
+        "tests/AlarmMonitor_test.cpp",
+        "tests/anomaly/AlarmTracker_test.cpp",
+        "tests/anomaly/AnomalyTracker_test.cpp",
+        "tests/ConfigManager_test.cpp",
+        "tests/external/puller_util_test.cpp",
+        "tests/indexed_priority_queue_test.cpp",
+        "tests/LogEntryMatcher_test.cpp",
+        "tests/LogEvent_test.cpp",
+        "tests/MetricsManager_test.cpp",
+        "tests/StatsLogProcessor_test.cpp",
+        "tests/StatsService_test.cpp",
+        "tests/UidMap_test.cpp",
+        "tests/FieldValue_test.cpp",
+        "tests/condition/CombinationConditionTracker_test.cpp",
+        "tests/condition/SimpleConditionTracker_test.cpp",
+        "tests/condition/StateTracker_test.cpp",
+        "tests/metrics/OringDurationTracker_test.cpp",
+        "tests/metrics/MaxDurationTracker_test.cpp",
+        "tests/metrics/CountMetricProducer_test.cpp",
+        "tests/metrics/DurationMetricProducer_test.cpp",
+        "tests/metrics/EventMetricProducer_test.cpp",
+        "tests/metrics/ValueMetricProducer_test.cpp",
+        "tests/metrics/GaugeMetricProducer_test.cpp",
+        "tests/guardrail/StatsdStats_test.cpp",
+        "tests/metrics/metrics_test_helper.cpp",
+        "tests/statsd_test_util.cpp",
+        "tests/e2e/WakelockDuration_e2e_test.cpp",
+        "tests/e2e/MetricActivation_e2e_test.cpp",
+        "tests/e2e/MetricConditionLink_e2e_test.cpp",
+        "tests/e2e/Alarm_e2e_test.cpp",
+        "tests/e2e/Attribution_e2e_test.cpp",
+        "tests/e2e/GaugeMetric_e2e_push_test.cpp",
+        "tests/e2e/GaugeMetric_e2e_pull_test.cpp",
+        "tests/e2e/ValueMetric_pull_e2e_test.cpp",
+        "tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp",
+        "tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp",
+        "tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp",
+        "tests/e2e/Anomaly_count_e2e_test.cpp",
+        "tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
+        "tests/e2e/ConfigTtl_e2e_test.cpp",
+        "tests/e2e/PartialBucket_e2e_test.cpp",
+        "tests/shell/ShellSubscriber_test.cpp",
+    ],
+
+    static_libs: [
+        "libgmock",
+        "libplatformprotos",
+    ],
+
+    proto: {
+        type: "full",
+        include_dirs: ["external/protobuf/src"],
+    },
+
+    shared_libs: ["libprotobuf-cpp-full"],
+
+}
+
+//#############################
+// statsd micro benchmark
+//#############################
+
+cc_benchmark {
+    name: "statsd_benchmark",
+    defaults: ["statsd_defaults"],
+
+    srcs: [
+        "src/atom_field_options.proto",
+        "src/atoms.proto",
+        "src/stats_log.proto",
+        "benchmark/main.cpp",
+        "benchmark/hello_world_benchmark.cpp",
+        "benchmark/log_event_benchmark.cpp",
+        "benchmark/stats_write_benchmark.cpp",
+        "benchmark/filter_value_benchmark.cpp",
+        "benchmark/get_dimensions_for_condition_benchmark.cpp",
+        "benchmark/metric_util.cpp",
+        "benchmark/duration_metric_benchmark.cpp",
+    ],
+
+    proto: {
+        type: "full",
+        include_dirs: ["external/protobuf/src"],
+    },
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wno-unused-variable",
+        "-Wno-unused-function",
+
+        // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
+        "-Wno-varargs"
+    ],
+
+    static_libs: [
+        "libplatformprotos",
+    ],
+
+    shared_libs: [
+        "libgtest_prod",
+        "libstatslog",
+        "libprotobuf-cpp-full",
+    ],
+}
 
 // ====  java proto device library (for test only)  ==============================
 java_library {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
deleted file mode 100644
index 5818f5d..0000000
--- a/cmds/statsd/Android.mk
+++ /dev/null
@@ -1,319 +0,0 @@
-# Copyright (C) 2017 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-statsd_common_src := \
-    ../../core/java/android/os/IStatsCompanionService.aidl \
-    ../../core/java/android/os/IStatsManager.aidl \
-    src/statsd_config.proto \
-    src/FieldValue.cpp \
-    src/hash.cpp \
-    src/stats_log_util.cpp \
-    src/anomaly/AlarmMonitor.cpp \
-    src/anomaly/AlarmTracker.cpp \
-    src/anomaly/AnomalyTracker.cpp \
-    src/anomaly/DurationAnomalyTracker.cpp \
-    src/anomaly/subscriber_util.cpp \
-    src/condition/CombinationConditionTracker.cpp \
-    src/condition/condition_util.cpp \
-    src/condition/SimpleConditionTracker.cpp \
-    src/condition/ConditionWizard.cpp \
-    src/condition/StateTracker.cpp \
-    src/config/ConfigKey.cpp \
-    src/config/ConfigListener.cpp \
-    src/config/ConfigManager.cpp \
-    src/external/Perfetto.cpp \
-    src/external/Perfprofd.cpp \
-    src/external/StatsPuller.cpp \
-    src/external/StatsCompanionServicePuller.cpp \
-    src/external/SubsystemSleepStatePuller.cpp \
-    src/external/ResourceHealthManagerPuller.cpp \
-    src/external/ResourceThermalManagerPuller.cpp \
-    src/external/StatsPullerManager.cpp \
-    src/external/puller_util.cpp \
-    src/logd/LogEvent.cpp \
-    src/logd/LogListener.cpp \
-    src/matchers/CombinationLogMatchingTracker.cpp \
-    src/matchers/EventMatcherWizard.cpp \
-    src/matchers/matcher_util.cpp \
-    src/matchers/SimpleLogMatchingTracker.cpp \
-    src/metrics/MetricProducer.cpp \
-    src/metrics/EventMetricProducer.cpp \
-    src/metrics/CountMetricProducer.cpp \
-    src/metrics/DurationMetricProducer.cpp \
-    src/metrics/duration_helper/OringDurationTracker.cpp \
-    src/metrics/duration_helper/MaxDurationTracker.cpp \
-    src/metrics/ValueMetricProducer.cpp \
-    src/metrics/GaugeMetricProducer.cpp \
-    src/metrics/MetricsManager.cpp \
-    src/metrics/metrics_manager_util.cpp \
-    src/packages/UidMap.cpp \
-    src/storage/StorageManager.cpp \
-    src/StatsLogProcessor.cpp \
-    src/StatsService.cpp \
-    src/statscompanion_util.cpp \
-    src/subscriber/IncidentdReporter.cpp \
-    src/subscriber/SubscriberReporter.cpp \
-    src/HashableDimensionKey.cpp \
-    src/guardrail/StatsdStats.cpp \
-    src/socket/StatsSocketListener.cpp \
-    src/shell/ShellSubscriber.cpp \
-    src/shell/shell_config.proto
-
-# TODO(b/110563449): Once statsd is using a blueprint file, migrate to the proper filegroups.
-statsd_common_src += \
-    ../../../../system/extras/perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl
-
-statsd_common_c_includes := \
-    $(LOCAL_PATH)/src \
-    $(LOCAL_PATH)/../../libs/services/include
-
-statsd_common_aidl_includes := \
-    $(LOCAL_PATH)/../../core/java
-
-statsd_common_static_libraries := \
-    libhealthhalutils
-
-statsd_common_shared_libraries := \
-    libbase \
-    libbinder \
-    libincident \
-    liblog \
-    libutils \
-    libservices \
-    libprotoutil \
-    libstatslog \
-    libhardware \
-    libhardware_legacy \
-    libhidlbase \
-    libhidltransport \
-    libhwbinder \
-    android.frameworks.stats@1.0 \
-    android.hardware.health@2.0 \
-    android.hardware.power@1.0 \
-    android.hardware.power@1.1 \
-    android.hardware.thermal@1.0 \
-    libpackagelistparser \
-    libsysutils \
-    libcutils
-
-# =========
-# statsd
-# =========
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := statsd
-
-LOCAL_SRC_FILES := \
-    $(statsd_common_src) \
-    src/main.cpp
-
-LOCAL_CFLAGS += \
-    -Wall \
-    -Wextra \
-    -Werror \
-    -Wno-unused-parameter
-
-ifeq (debug,)
-    LOCAL_CFLAGS += \
-            -g -O0
-else
-    # optimize for size (protobuf glop can get big)
-    LOCAL_CFLAGS += \
-            -Os
-endif
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite
-
-LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
-LOCAL_C_INCLUDES += $(statsd_common_c_includes)
-
-LOCAL_STATIC_LIBRARIES := $(statsd_common_static_libraries)
-
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
-    libgtest_prod
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-
-# Enable sanitizer ONLY on eng builds.
-#ifeq ($(TARGET_BUILD_VARIANT),eng)
-#    LOCAL_CLANG := true
-#    LOCAL_SANITIZE := address
-#endif
-
-# Add a flag to enable stats log printing from statsd on debug builds.
-ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
-    LOCAL_CFLAGS += \
-        -DVERY_VERBOSE_PRINTING
-endif
-
-LOCAL_INIT_RC := statsd.rc
-
-include $(BUILD_EXECUTABLE)
-
-
-# ==============
-# statsd_test
-# ==============
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := statsd_test
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
-LOCAL_C_INCLUDES += $(statsd_common_c_includes)
-
-LOCAL_CFLAGS += \
-    -Wall \
-    -Werror \
-    -Wno-missing-field-initializers \
-    -Wno-unused-variable \
-    -Wno-unused-function \
-    -Wno-unused-parameter
-
-LOCAL_SRC_FILES := \
-    $(statsd_common_src) \
-    src/atom_field_options.proto \
-    src/atoms.proto \
-    src/stats_log.proto \
-    src/shell/shell_data.proto \
-    tests/AlarmMonitor_test.cpp \
-    tests/anomaly/AlarmTracker_test.cpp \
-    tests/anomaly/AnomalyTracker_test.cpp \
-    tests/ConfigManager_test.cpp \
-    tests/external/puller_util_test.cpp \
-    tests/indexed_priority_queue_test.cpp \
-    tests/LogEntryMatcher_test.cpp \
-    tests/LogEvent_test.cpp \
-    tests/MetricsManager_test.cpp \
-    tests/StatsLogProcessor_test.cpp \
-    tests/StatsService_test.cpp \
-    tests/UidMap_test.cpp \
-    tests/FieldValue_test.cpp \
-    tests/condition/CombinationConditionTracker_test.cpp \
-    tests/condition/SimpleConditionTracker_test.cpp \
-    tests/condition/StateTracker_test.cpp \
-    tests/metrics/OringDurationTracker_test.cpp \
-    tests/metrics/MaxDurationTracker_test.cpp \
-    tests/metrics/CountMetricProducer_test.cpp \
-    tests/metrics/DurationMetricProducer_test.cpp \
-    tests/metrics/EventMetricProducer_test.cpp \
-    tests/metrics/ValueMetricProducer_test.cpp \
-    tests/metrics/GaugeMetricProducer_test.cpp \
-    tests/guardrail/StatsdStats_test.cpp \
-    tests/metrics/metrics_test_helper.cpp \
-    tests/statsd_test_util.cpp \
-    tests/e2e/WakelockDuration_e2e_test.cpp \
-    tests/e2e/MetricActivation_e2e_test.cpp \
-    tests/e2e/MetricConditionLink_e2e_test.cpp \
-    tests/e2e/Alarm_e2e_test.cpp \
-    tests/e2e/Attribution_e2e_test.cpp \
-    tests/e2e/GaugeMetric_e2e_push_test.cpp \
-    tests/e2e/GaugeMetric_e2e_pull_test.cpp \
-    tests/e2e/ValueMetric_pull_e2e_test.cpp \
-    tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp \
-    tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp \
-    tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp \
-    tests/e2e/Anomaly_count_e2e_test.cpp \
-    tests/e2e/Anomaly_duration_sum_e2e_test.cpp \
-    tests/e2e/ConfigTtl_e2e_test.cpp \
-    tests/e2e/PartialBucket_e2e_test.cpp \
-    tests/shell/ShellSubscriber_test.cpp
-
-LOCAL_STATIC_LIBRARIES := \
-    $(statsd_common_static_libraries) \
-    libgmock \
-    libplatformprotos
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := full
-
-LOCAL_PROTOC_FLAGS := \
-    -Iexternal/protobuf/src
-
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
-                        libprotobuf-cpp-full
-
-include $(BUILD_NATIVE_TEST)
-
-##############################
-# statsd micro benchmark
-##############################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := statsd_benchmark
-
-LOCAL_SRC_FILES := $(statsd_common_src) \
-                    src/atom_field_options.proto \
-                    src/atoms.proto \
-                    src/stats_log.proto \
-                   benchmark/main.cpp \
-                   benchmark/hello_world_benchmark.cpp \
-                   benchmark/log_event_benchmark.cpp \
-                   benchmark/stats_write_benchmark.cpp \
-                   benchmark/filter_value_benchmark.cpp \
-                   benchmark/get_dimensions_for_condition_benchmark.cpp \
-                   benchmark/metric_util.cpp \
-                   benchmark/duration_metric_benchmark.cpp
-
-LOCAL_STATIC_LIBRARIES := \
-    $(statsd_common_static_libraries)
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := full
-
-LOCAL_PROTOC_FLAGS := \
-    -Iexternal/protobuf/src
-
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
-                        libprotobuf-cpp-full
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    platformprotoslite
-
-LOCAL_C_INCLUDES := $(statsd_common_c_includes)
-
-LOCAL_CFLAGS := -Wall \
-                -Werror \
-                -Wno-unused-parameter \
-                -Wno-unused-variable \
-                -Wno-unused-function \
-
-# Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
-LOCAL_CFLAGS += -Wno-varargs
-
-LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
-
-LOCAL_STATIC_LIBRARIES := \
-    $(statsd_common_static_libraries) \
-    libplatformprotos
-
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
-    libgtest_prod \
-    libstatslog
-
-LOCAL_MODULE_TAGS := eng tests
-
-include $(BUILD_NATIVE_BENCHMARK)
-
-
-statsd_common_src:=
-statsd_common_aidl_includes:=
-statsd_common_c_includes:=
-statsd_common_static_libraries:=
-statsd_common_shared_libraries:=
-
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/cmds/statsd/android.frameworks.stats@1.0-service.xml b/cmds/statsd/android.frameworks.stats@1.0-service.xml
new file mode 100644
index 0000000..bb02f66
--- /dev/null
+++ b/cmds/statsd/android.frameworks.stats@1.0-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+    <hal>
+        <name>android.frameworks.stats</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+            <name>IStats</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index fc1a61c..80ed807 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 #include "FieldValue.h"
 #include "HashableDimensionKey.h"
+#include "math.h"
 
 namespace android {
 namespace os {
@@ -174,6 +175,25 @@
     }
 }
 
+bool Value::isZero() const {
+    switch (type) {
+        case INT:
+            return int_value == 0;
+        case LONG:
+            return long_value == 0;
+        case FLOAT:
+            return fabs(float_value) <= std::numeric_limits<float>::epsilon();
+        case DOUBLE:
+            return fabs(double_value) <= std::numeric_limits<double>::epsilon();
+        case STRING:
+            return str_value.size() == 0;
+        case STORAGE:
+            return storage_value.size() == 0;
+        default:
+            return false;
+    }
+}
+
 bool Value::operator==(const Value& that) const {
     if (type != that.getType()) return false;
 
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 77163f9..a5d00ac 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -331,6 +331,8 @@
 
     std::string toString() const;
 
+    bool isZero() const;
+
     Type getType() const {
         return type;
     }
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index f4c70be..f0f5993 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -272,26 +272,25 @@
 }
 
 /*
- * onDumpReport dumps serialized ConfigMetricsReportList into outData.
+ * onDumpReport dumps serialized ConfigMetricsReportList into proto.
  */
 void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs,
                                      const bool include_current_partial_bucket,
+                                     const bool erase_data,
                                      const DumpReportReason dumpReportReason,
-                                     vector<uint8_t>* outData) {
+                                     ProtoOutputStream* proto) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
 
-    ProtoOutputStream proto;
-
     // Start of ConfigKey.
-    uint64_t configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
-    proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
-    proto.write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)key.GetId());
-    proto.end(configKeyToken);
+    uint64_t configKeyToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
+    proto->write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)key.GetId());
+    proto->end(configKeyToken);
     // End of ConfigKey.
 
     // Then, check stats-data directory to see there's any file containing
     // ConfigMetricsReport from previous shutdowns to concatenate to reports.
-    StorageManager::appendConfigMetricsReport(key, &proto);
+    StorageManager::appendConfigMetricsReport(key, proto);
 
     auto it = mMetricsManagers.find(key);
     if (it != mMetricsManagers.end()) {
@@ -301,14 +300,27 @@
 
         // Start of ConfigMetricsReport (reports).
         uint64_t reportsToken =
-                proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
+                proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
         onConfigMetricsReportLocked(key, dumpTimeStampNs, include_current_partial_bucket,
-                                    dumpReportReason, &proto);
-        proto.end(reportsToken);
+                                    erase_data, dumpReportReason, proto);
+        proto->end(reportsToken);
         // End of ConfigMetricsReport (reports).
     } else {
         ALOGW("Config source %s does not exist", key.ToString().c_str());
     }
+}
+
+/*
+ * onDumpReport dumps serialized ConfigMetricsReportList into outData.
+ */
+void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs,
+                                     const bool include_current_partial_bucket,
+                                     const bool erase_data,
+                                     const DumpReportReason dumpReportReason,
+                                     vector<uint8_t>* outData) {
+    ProtoOutputStream proto;
+    onDumpReport(key, dumpTimeStampNs, include_current_partial_bucket, erase_data,
+                 dumpReportReason, &proto);
 
     if (outData != nullptr) {
         outData->clear();
@@ -332,6 +344,7 @@
 void StatsLogProcessor::onConfigMetricsReportLocked(const ConfigKey& key,
                                                     const int64_t dumpTimeStampNs,
                                                     const bool include_current_partial_bucket,
+                                                    const bool erase_data,
                                                     const DumpReportReason dumpReportReason,
                                                     ProtoOutputStream* proto) {
     // We already checked whether key exists in mMetricsManagers in
@@ -348,7 +361,7 @@
     // First, fill in ConfigMetricsReport using current data on memory, which
     // starts from filling in StatsLogReport's.
     it->second->onDumpReport(dumpTimeStampNs, include_current_partial_bucket,
-                             &str_set, proto);
+                             erase_data, &str_set, proto);
 
     // Fill in UidMap if there is at least one metric to report.
     // This skips the uid map if it's an empty config.
@@ -442,7 +455,7 @@
     if (totalBytes >
         StatsdStats::kMaxMetricsBytesPerConfig) {  // Too late. We need to start clearing data.
         metricsManager.dropData(timestampNs);
-        StatsdStats::getInstance().noteDataDropped(key);
+        StatsdStats::getInstance().noteDataDropped(key, totalBytes);
         VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
     } else if ((totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) ||
                (mOnDiskDataConfigs.find(key) != mOnDiskDataConfigs.end())) {
@@ -479,7 +492,7 @@
     }
     ProtoOutputStream proto;
     onConfigMetricsReportLocked(key, timestampNs, true /* include_current_partial_bucket*/,
-                                dumpReportReason, &proto);
+                                true /* erase_data */, dumpReportReason, &proto);
     string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR,
          (long)getWallClockSec(), key.GetUid(), (long long)key.GetId());
     android::base::unique_fd fd(open(file_name.c_str(),
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 3e8b9b8..ecfd819 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -61,8 +61,11 @@
     size_t GetMetricsSize(const ConfigKey& key) const;
 
     void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
-                      const bool include_current_partial_bucket,
+                      const bool include_current_partial_bucket, const bool erase_data,
                       const DumpReportReason dumpReportReason, vector<uint8_t>* outData);
+    void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
+                      const bool include_current_partial_bucket, const bool erase_data,
+                      const DumpReportReason dumpReportReason, ProtoOutputStream* proto);
 
     /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
     void onAnomalyAlarmFired(
@@ -141,6 +144,7 @@
 
     void onConfigMetricsReportLocked(const ConfigKey& key, const int64_t dumpTimeStampNs,
                                      const bool include_current_partial_bucket,
+                                     const bool erase_data,
                                      const DumpReportReason dumpReportReason,
                                      util::ProtoOutputStream* proto);
 
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index ce28777..27685fc 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -46,6 +46,8 @@
 using namespace android;
 
 using android::base::StringPrintf;
+using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_MESSAGE;
 
 namespace android {
 namespace os {
@@ -58,6 +60,9 @@
 
 #define STATS_SERVICE_DIR "/data/misc/stats-service"
 
+// for StatsDataDumpProto
+const int FIELD_ID_REPORTS_LIST = 1;
+
 static binder::Status ok() {
     return binder::Status::ok();
 }
@@ -224,31 +229,48 @@
 }
 
 /**
- * Write debugging data about statsd.
+ * Write data from statsd.
+ * Format for statsdStats:  adb shell dumpsys stats --metadata [-v] [--proto]
+ * Format for data report:  adb shell dumpsys stats [anything other than --metadata] [--proto]
+ * Anything ending in --proto will be in proto format.
+ * Anything without --metadata as the first argument will be report information.
+ *     (bugreports call "adb shell dumpsys stats --dump-priority NORMAL -a --proto")
+ * TODO: Come up with a more robust method of enacting <serviceutils/PriorityDumper.h>.
  */
 status_t StatsService::dump(int fd, const Vector<String16>& args) {
     if (!checkCallingPermission(String16(kPermissionDump))) {
         return PERMISSION_DENIED;
     }
-
-    bool verbose = false;
-    bool proto = false;
-    if (args.size() > 0 && !args[0].compare(String16("-v"))) {
-        verbose = true;
+    int lastArg = args.size() - 1;
+    bool asProto = false;
+    if (lastArg >= 0 && !args[lastArg].compare(String16("--proto"))) { // last argument
+        asProto = true;
+        lastArg--;
     }
-    if (args.size() > 0 && !args[args.size()-1].compare(String16("--proto"))) {
-        proto = true;
+    if (args.size() > 0 && !args[0].compare(String16("--metadata"))) { // first argument
+        // Request is to dump statsd stats.
+        bool verbose = false;
+        if (lastArg >= 0 && !args[lastArg].compare(String16("-v"))) {
+            verbose = true;
+            lastArg--;
+        }
+        dumpStatsdStats(fd, verbose, asProto);
+    } else {
+        // Request is to dump statsd report data.
+        if (asProto) {
+            dumpIncidentSection(fd);
+        } else {
+            dprintf(fd, "Non-proto format of stats data dump not available; see proto version.\n");
+        }
     }
 
-    dump_impl(fd, verbose, proto);
-
     return NO_ERROR;
 }
 
 /**
  * Write debugging data about statsd in text or proto format.
  */
-void StatsService::dump_impl(int out, bool verbose, bool proto) {
+void StatsService::dumpStatsdStats(int out, bool verbose, bool proto) {
     if (proto) {
         vector<uint8_t> data;
         StatsdStats::getInstance().dumpStats(&data, false); // does not reset statsdStats.
@@ -262,6 +284,22 @@
 }
 
 /**
+ * Write stats report data in StatsDataDumpProto incident section format.
+ */
+void StatsService::dumpIncidentSection(int out) {
+    ProtoOutputStream proto;
+    for (const ConfigKey& configKey : mConfigManager->GetAllConfigKeys()) {
+        uint64_t reportsListToken =
+                proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS_LIST);
+        mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
+                                 true /* includeCurrentBucket */, false /* erase_data */,
+                                 ADB_DUMP, &proto);
+        proto.end(reportsListToken);
+        proto.flush(out);
+    }
+}
+
+/**
  * Implementation of the adb shell cmd stats command.
  */
 status_t StatsService::command(int in, int out, int err, Vector<String8>& args,
@@ -283,7 +321,7 @@
         }
 
         if (!args[0].compare(String8("dump-report"))) {
-            return cmd_dump_report(out, err, args);
+            return cmd_dump_report(out, args);
         }
 
         if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
@@ -546,7 +584,7 @@
     return UNKNOWN_ERROR;
 }
 
-status_t StatsService::cmd_dump_report(int out, int err, const Vector<String8>& args) {
+status_t StatsService::cmd_dump_report(int out, const Vector<String8>& args) {
     if (mProcessor != nullptr) {
         int argCount = args.size();
         bool good = false;
@@ -589,14 +627,13 @@
         if (good) {
             vector<uint8_t> data;
             mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
-                                     includeCurrentBucket, ADB_DUMP, &data);
+                                     includeCurrentBucket, true /* erase_data */, ADB_DUMP, &data);
             if (proto) {
                 for (size_t i = 0; i < data.size(); i ++) {
                     dprintf(out, "%c", data[i]);
                 }
             } else {
-                dprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str());
-                dprintf(out, "See the StatsLogReport in logcat...\n");
+                dprintf(out, "Non-proto stats data dump not currently supported.\n");
             }
             return android::OK;
         } else {
@@ -888,7 +925,7 @@
     VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
     ConfigKey configKey(ipc->getCallingUid(), key);
     mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/,
-                             GET_DATA_CALLED, output);
+                             true /* erase_data */, GET_DATA_CALLED, output);
     return Status::ok();
 }
 
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index cbf3429..4a5f05f 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -213,9 +213,14 @@
                                          uint32_t serial);
 
     /**
-     * Text or proto output of dumpsys.
+     * Proto output of statsd report data dumpsys, wrapped in a StatsDataDumpProto.
      */
-    void dump_impl(int outFd, bool verbose, bool proto);
+    void dumpIncidentSection(int outFd);
+
+    /**
+     * Text or proto output of statsdStats dumpsys.
+     */
+    void dumpStatsdStats(int outFd, bool verbose, bool proto);
 
     /**
      * Print usage information for the commands
@@ -240,7 +245,7 @@
     /**
      * Print the event log.
      */
-    status_t cmd_dump_report(int outFd, int err, const Vector<String8>& args);
+    status_t cmd_dump_report(int outFd, const Vector<String8>& args);
 
     /**
      * Print the mapping of uids to package names.
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index d52dec1..5620184 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -29,6 +29,7 @@
 import "frameworks/base/core/proto/android/server/enums.proto";
 import "frameworks/base/core/proto/android/service/procstats_enum.proto";
 import "frameworks/base/core/proto/android/stats/enums.proto";
+import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
 import "frameworks/base/core/proto/android/telecomm/enums.proto";
 import "frameworks/base/core/proto/android/telephony/enums.proto";
 import "frameworks/base/core/proto/android/view/enums.proto";
@@ -67,7 +68,7 @@
         ExcessiveCpuUsageReported excessive_cpu_usage_reported = 16;
         CachedKillReported cached_kill_reported = 17;
         ProcessMemoryStatReported process_memory_stat_reported = 18;
-        // 19 is available
+        LauncherUIChanged launcher_event = 19;
         BatterySaverModeStateChanged battery_saver_mode_state_changed = 20;
         DeviceIdleModeStateChanged device_idle_mode_state_changed = 21;
         DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22;
@@ -149,7 +150,7 @@
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10037
+    // Next: 10038
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000;
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -188,6 +189,7 @@
         ProcStats proc_stats_pkg_proc = 10034;
         ProcessCpuTime process_cpu_time = 10035;
         NativeProcessMemoryState native_process_memory_state = 10036;
+        CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -1439,6 +1441,14 @@
     optional State state = 1;
 }
 
+message LauncherUIChanged {
+    optional android.stats.launcher.LauncherAction action = 1;
+    optional android.stats.launcher.LauncherState src_state = 2;
+    optional android.stats.launcher.LauncherState dst_state = 3;
+    optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES];
+    optional bool is_swipe_up_enabled = 5;
+}
+
 /**
  * Logs when Settings UI has changed.
  *
@@ -2389,31 +2399,38 @@
     // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or
     // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups.
     optional int64 rss_high_watermark_in_bytes = 9;
+
+    // Elapsed real time when the process started.
+    // Value is read from /proc/PID/stat, field 22. 0 if read from per-app memory cgroups.
+    optional int64 start_time_nanos = 10;
 }
 
 /*
  * Logs the memory stats for a native process (from procfs).
  */
 message NativeProcessMemoryState {
-  // The uid if available. -1 means not available.
-  optional int32 uid = 1 [(is_uid) = true];
+    // The uid if available. -1 means not available.
+    optional int32 uid = 1 [(is_uid) = true];
 
-  // The process name.
-  optional string process_name = 2;
+    // The process name.
+    optional string process_name = 2;
 
-  // # of page-faults
-  optional int64 page_fault = 3;
+    // # of page-faults
+    optional int64 page_fault = 3;
 
-  // # of major page-faults
-  optional int64 page_major_fault = 4;
+    // # of major page-faults
+    optional int64 page_major_fault = 4;
 
-  // RSS
-  optional int64 rss_in_bytes = 5;
+    // RSS
+    optional int64 rss_in_bytes = 5;
 
-  // RSS high watermark.
-  // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or
-  // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups.
-  optional int64 rss_high_watermark_in_bytes = 6;
+    // RSS high watermark.
+    // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status.
+    optional int64 rss_high_watermark_in_bytes = 6;
+
+    // Elapsed real time when the process started.
+    // Value is read from /proc/PID/stat, field 22.
+    optional int64 start_time_nanos = 7;
 }
 
 /*
@@ -2527,10 +2544,17 @@
  * Binder stats will be reset every time the data is pulled. It means it can only be pulled by one
  * config on the device.
  *
- * Next tag: 14
+ * Next tag: 15
  */
 message BinderCalls {
+    // UID of the process responsible for the binder transaction. It will be set if the process
+    // executing the binder transaction attribute the transaction to another uid using
+    // Binder.setThreadWorkSource().
+    //
+    // If not set, the value will be -1.
     optional int32 uid = 1 [(is_uid) = true];
+    // UID of the process executing the binder transaction.
+    optional int32 direct_caller_uid = 14;
     // Fully qualified class name of the API call.
     //
     // This is a system server class name.
@@ -3108,4 +3132,30 @@
     optional int64 user_time_millis = 3;
     // Process cpu time in system space, cumulative from boot/process start
     optional int64 system_time_millis = 4;
-}
\ No newline at end of file
+}
+
+/**
+ * Pulls the CPU usage for each thread.
+ *
+ * Read from /proc/$PID/task/$TID/time_in_state files.
+ *
+ * TODO(mishaw): This is an experimental atom. Issues with big/little CPU frequencies, and
+ * time_in_state files not being present on some phones, have not been addressed. These should be
+ * considered before a public release.
+ */
+message CpuTimePerThreadFreq {
+    // UID that owns the process.
+    optional int32 uid = 1 [(is_uid) = true];
+    // ID of the process.
+    optional uint32 process_id = 2;
+    // ID of the thread.
+    optional uint32 thread_id = 3;
+    // Name of the process taken from `/proc/$PID/cmdline`.
+    optional string process_name = 4;
+    // Name of the thread taken from `/proc/$PID/task/$TID/comm`
+    optional string thread_name = 5;
+    // What frequency the CPU was running at, in KHz
+    optional uint32 frequency_khz = 6;
+    // Time spent in frequency in milliseconds, since thread start.
+    optional uint32 time_millis = 7;
+}
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 436a880..e3f251a 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -46,6 +46,7 @@
     if (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs) {
         (*data) = mCachedData;
         StatsdStats::getInstance().notePullFromCache(mTagId);
+        StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
         return true;
     }
     if (mMinPullIntervalNs > elapsedTimeNs - mLastPullTimeNs) {
@@ -55,7 +56,9 @@
     }
     mCachedData.clear();
     mLastPullTimeNs = elapsedTimeNs;
+    int64_t pullStartTimeNs = getElapsedRealtimeNs();
     bool ret = PullInternal(&mCachedData);
+    StatsdStats::getInstance().notePullTime(mTagId, getElapsedRealtimeNs() - pullStartTimeNs);
     for (const shared_ptr<LogEvent>& data : mCachedData) {
         data->setElapsedTimestampNs(elapsedTimeNs);
         data->setLogdWallClockTimestampNs(wallClockTimeNs);
@@ -64,6 +67,7 @@
       mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
       (*data) = mCachedData;
     }
+    StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
     return ret;
 }
 
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index ba626f8..9633980 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -167,13 +167,13 @@
         // process_memory_state
         {android::util::PROCESS_MEMORY_STATE,
          {{4, 5, 6, 7, 8, 9},
-          {2, 3},
+          {2, 3, 10},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
         // native_process_memory_state
         {android::util::NATIVE_PROCESS_MEMORY_STATE,
          {{3, 4, 5, 6},
-          {2},
+          {2, 7},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
         // temperature
@@ -234,6 +234,11 @@
             {{} /* additive fields */, {} /* non additive fields */,
              5 * NS_PER_SEC /* min cool-down in seconds*/,
              new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
+        {android::util::CPU_TIME_PER_THREAD_FREQ,
+         {{7},
+          {2, 3, 4, 5, 6},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index bf0bfec..a0d77d6 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -73,7 +73,8 @@
 const int FIELD_ID_CONFIG_STATS_ALERT_COUNT = 8;
 const int FIELD_ID_CONFIG_STATS_VALID = 9;
 const int FIELD_ID_CONFIG_STATS_BROADCAST = 10;
-const int FIELD_ID_CONFIG_STATS_DATA_DROP = 11;
+const int FIELD_ID_CONFIG_STATS_DATA_DROP_TIME = 11;
+const int FIELD_ID_CONFIG_STATS_DATA_DROP_BYTES = 21;
 const int FIELD_ID_CONFIG_STATS_DUMP_REPORT_TIME = 12;
 const int FIELD_ID_CONFIG_STATS_DUMP_REPORT_BYTES = 20;
 const int FIELD_ID_CONFIG_STATS_MATCHER_STATS = 13;
@@ -205,11 +206,11 @@
     it->second->broadcast_sent_time_sec.push_back(timeSec);
 }
 
-void StatsdStats::noteDataDropped(const ConfigKey& key) {
-    noteDataDropped(key, getWallClockSec());
+void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes) {
+    noteDataDropped(key, totalBytes, getWallClockSec());
 }
 
-void StatsdStats::noteDataDropped(const ConfigKey& key, int32_t timeSec) {
+void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec) {
     lock_guard<std::mutex> lock(mLock);
     auto it = mConfigStats.find(key);
     if (it == mConfigStats.end()) {
@@ -218,8 +219,10 @@
     }
     if (it->second->data_drop_time_sec.size() == kMaxTimestampCount) {
         it->second->data_drop_time_sec.pop_front();
+        it->second->data_drop_bytes.pop_front();
     }
     it->second->data_drop_time_sec.push_back(timeSec);
+    it->second->data_drop_bytes.push_back(totalBytes);
 }
 
 void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes) {
@@ -332,7 +335,8 @@
 
 void StatsdStats::updateMinPullIntervalSec(int pullAtomId, long intervalSec) {
     lock_guard<std::mutex> lock(mLock);
-    mPulledAtomStats[pullAtomId].minPullIntervalSec = intervalSec;
+    mPulledAtomStats[pullAtomId].minPullIntervalSec =
+            std::min(mPulledAtomStats[pullAtomId].minPullIntervalSec, intervalSec);
 }
 
 void StatsdStats::notePull(int pullAtomId) {
@@ -345,6 +349,30 @@
     mPulledAtomStats[pullAtomId].totalPullFromCache++;
 }
 
+void StatsdStats::notePullTime(int pullAtomId, int64_t pullTimeNs) {
+    lock_guard<std::mutex> lock(mLock);
+    auto& pullStats = mPulledAtomStats[pullAtomId];
+    pullStats.maxPullTimeNs = std::max(pullStats.maxPullTimeNs, pullTimeNs);
+    pullStats.avgPullTimeNs = (pullStats.avgPullTimeNs * pullStats.numPullTime + pullTimeNs) /
+                              (pullStats.numPullTime + 1);
+    pullStats.numPullTime += 1;
+}
+
+void StatsdStats::notePullDelay(int pullAtomId, int64_t pullDelayNs) {
+    lock_guard<std::mutex> lock(mLock);
+    auto& pullStats = mPulledAtomStats[pullAtomId];
+    pullStats.maxPullDelayNs = std::max(pullStats.maxPullDelayNs, pullDelayNs);
+    pullStats.avgPullDelayNs =
+        (pullStats.avgPullDelayNs * pullStats.numPullDelay + pullDelayNs) /
+            (pullStats.numPullDelay + 1);
+    pullStats.numPullDelay += 1;
+}
+
+void StatsdStats::notePullDataError(int pullAtomId) {
+    lock_guard<std::mutex> lock(mLock);
+    mPulledAtomStats[pullAtomId].dataError++;
+}
+
 void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
     lock_guard<std::mutex> lock(mLock);
 
@@ -382,6 +410,7 @@
     for (auto& config : mConfigStats) {
         config.second->broadcast_sent_time_sec.clear();
         config.second->data_drop_time_sec.clear();
+        config.second->data_drop_bytes.clear();
         config.second->dump_report_stats.clear();
         config.second->annotations.clear();
         config.second->matcher_stats.clear();
@@ -390,6 +419,17 @@
         config.second->metric_dimension_in_condition_stats.clear();
         config.second->alert_stats.clear();
     }
+    for (auto& pullStats : mPulledAtomStats) {
+        pullStats.second.totalPull = 0;
+        pullStats.second.totalPullFromCache = 0;
+        pullStats.second.avgPullTimeNs = 0;
+        pullStats.second.maxPullTimeNs = 0;
+        pullStats.second.numPullTime = 0;
+        pullStats.second.avgPullDelayNs = 0;
+        pullStats.second.maxPullDelayNs = 0;
+        pullStats.second.numPullDelay = 0;
+        pullStats.second.dataError = 0;
+    }
 }
 
 string buildTimeString(int64_t timeSec) {
@@ -421,8 +461,12 @@
             dprintf(out, "\tbroadcast time: %d\n", broadcastTime);
         }
 
-        for (const auto& dataDropTime : configStats->data_drop_time_sec) {
-            dprintf(out, "\tdata drop time: %d\n", dataDropTime);
+        auto dropTimePtr = configStats->data_drop_time_sec.begin();
+        auto dropBytesPtr = configStats->data_drop_bytes.begin();
+        for (int i = 0; i < (int)configStats->data_drop_time_sec.size();
+             i++, dropTimePtr++, dropBytesPtr++) {
+            dprintf(out, "\tdata drop time: %d with size %lld", *dropTimePtr,
+                    (long long)*dropBytesPtr);
         }
     }
     dprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size());
@@ -445,9 +489,13 @@
                     (long long)broadcastTime);
         }
 
-        for (const auto& dataDropTime : configStats->data_drop_time_sec) {
-            dprintf(out, "\tdata drop time: %s(%lld)\n", buildTimeString(dataDropTime).c_str(),
-                    (long long)dataDropTime);
+        auto dropTimePtr = configStats->data_drop_time_sec.begin();
+        auto dropBytesPtr = configStats->data_drop_bytes.begin();
+        for (int i = 0; i < (int)configStats->data_drop_time_sec.size();
+             i++, dropTimePtr++, dropBytesPtr++) {
+            dprintf(out, "\tdata drop time: %s(%lld) with %lld bytes\n",
+                    buildTimeString(*dropTimePtr).c_str(), (long long)*dropTimePtr,
+                    (long long)*dropBytesPtr);
         }
 
         for (const auto& dump : configStats->dump_report_stats) {
@@ -486,8 +534,14 @@
 
     dprintf(out, "********Pulled Atom stats***********\n");
     for (const auto& pair : mPulledAtomStats) {
-        dprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull,
-                (long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec);
+        dprintf(out,
+                "Atom %d->(total pull)%ld, (pull from cache)%ld, (min pull interval)%ld, (average "
+                "pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay nanos)%lld, "
+                "(max pull delay nanos)%lld, (data error)%ld\n",
+                (int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache,
+                (long)pair.second.minPullIntervalSec, (long long)pair.second.avgPullTimeNs,
+                (long long)pair.second.maxPullTimeNs, (long long)pair.second.avgPullDelayNs,
+                (long long)pair.second.maxPullDelayNs, pair.second.dataError);
     }
 
     if (mAnomalyAlarmRegisteredStats > 0) {
@@ -540,9 +594,15 @@
                      broadcast);
     }
 
-    for (const auto& drop : configStats.data_drop_time_sec) {
-        proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DATA_DROP | FIELD_COUNT_REPEATED,
-                     drop);
+    for (const auto& drop_time : configStats.data_drop_time_sec) {
+        proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DATA_DROP_TIME | FIELD_COUNT_REPEATED,
+                     drop_time);
+    }
+
+    for (const auto& drop_bytes : configStats.data_drop_bytes) {
+        proto->write(
+                FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_DATA_DROP_BYTES | FIELD_COUNT_REPEATED,
+                (long long)drop_bytes);
     }
 
     for (const auto& dump : configStats.dump_report_stats) {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index a8188c8..2008abd 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -43,6 +43,8 @@
 
     std::list<int32_t> broadcast_sent_time_sec;
     std::list<int32_t> data_drop_time_sec;
+    // Number of bytes dropped at corresponding time.
+    std::list<int64_t> data_drop_bytes;
     std::list<std::pair<int32_t, int64_t>> dump_report_stats;
 
     // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount.
@@ -169,7 +171,7 @@
     /**
      * Report a config's metrics data has been dropped.
      */
-    void noteDataDropped(const ConfigKey& key);
+    void noteDataDropped(const ConfigKey& key, const size_t totalBytes);
 
     /**
      * Report metrics data report has been sent.
@@ -261,16 +263,39 @@
     void setUidMapChanges(int changes);
     void setCurrentUidMapMemory(int bytes);
 
-    // Update minimum interval between pulls for an pulled atom
+    /*
+     * Updates minimum interval between pulls for an pulled atom.
+     */
     void updateMinPullIntervalSec(int pullAtomId, long intervalSec);
 
-    // Notify pull request for an atom
+    /*
+     * Notes an atom is pulled.
+     */
     void notePull(int pullAtomId);
 
-    // Notify pull request for an atom served from cached data
+    /*
+     * Notes an atom is served from puller cache.
+     */
     void notePullFromCache(int pullAtomId);
 
     /*
+     * Notify data error for pulled atom.
+     */
+    void notePullDataError(int pullAtomId);
+
+    /*
+     * Records time for actual pulling, not including those served from cache and not including
+     * statsd processing delays.
+     */
+    void notePullTime(int pullAtomId, int64_t pullTimeNs);
+
+    /*
+     * Records pull delay for a pulled atom, including those served from cache and including statsd
+     * processing delays.
+     */
+    void notePullDelay(int pullAtomId, int64_t pullDelayNs);
+
+    /*
      * Records when system server restarts.
      */
     void noteSystemServerRestart(int32_t timeSec);
@@ -300,9 +325,16 @@
     void dumpStats(int outFd) const;
 
     typedef struct {
-        long totalPull;
-        long totalPullFromCache;
-        long minPullIntervalSec;
+        long totalPull = 0;
+        long totalPullFromCache = 0;
+        long minPullIntervalSec = LONG_MAX;
+        int64_t avgPullTimeNs = 0;
+        int64_t maxPullTimeNs = 0;
+        long numPullTime = 0;
+        int64_t avgPullDelayNs = 0;
+        int64_t maxPullDelayNs = 0;
+        long numPullDelay = 0;
+        long dataError = 0;
     } PulledAtomStats;
 
 private:
@@ -350,7 +382,7 @@
 
     void resetInternalLocked();
 
-    void noteDataDropped(const ConfigKey& key, int32_t timeSec);
+    void noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec);
 
     void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes, int32_t timeSec);
 
@@ -366,6 +398,7 @@
     FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold);
     FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor);
     FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash);
+    FRIEND_TEST(StatsdStatsTest, TestPullAtomStats);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 6b8c12a..eddc86e 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -82,7 +82,9 @@
 
     // Create the service
     gStatsService = new StatsService(looper);
-    if (defaultServiceManager()->addService(String16("stats"), gStatsService) != 0) {
+    if (defaultServiceManager()->addService(String16("stats"), gStatsService, false,
+                IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO)
+            != 0) {
         ALOGE("Failed to add service as AIDL service");
         return -1;
     }
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index bd94800..5ca8814 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -143,6 +143,7 @@
 
 void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
+                                             const bool erase_data,
                                              std::set<string> *str_set,
                                              ProtoOutputStream* protoOutput) {
     if (include_current_partial_bucket) {
@@ -230,7 +231,9 @@
 
     protoOutput->end(protoToken);
 
-    mPastBuckets.clear();
+    if (erase_data) {
+        mPastBuckets.clear();
+    }
 }
 
 void CountMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 39d4ae2..1ac44264 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -56,6 +56,7 @@
 
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
+                            const bool erase_data,
                             std::set<string> *str_set,
                             android::util::ProtoOutputStream* protoOutput) override;
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index dd3402d..35deffe 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -453,6 +453,7 @@
 
 void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                                 const bool include_current_partial_bucket,
+                                                const bool erase_data,
                                                 std::set<string> *str_set,
                                                 ProtoOutputStream* protoOutput) {
     if (include_current_partial_bucket) {
@@ -541,7 +542,9 @@
     }
 
     protoOutput->end(protoToken);
-    mPastBuckets.clear();
+    if (erase_data) {
+        mPastBuckets.clear();
+    }
 }
 
 void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 12addb8..1b830a3 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -63,6 +63,7 @@
 
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
+                            const bool erase_data,
                             std::set<string> *str_set,
                             android::util::ProtoOutputStream* protoOutput) override;
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index afd8ec2..a18e406 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -105,6 +105,7 @@
 
 void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
+                                             const bool erase_data,
                                              std::set<string> *str_set,
                                              ProtoOutputStream* protoOutput) {
     if (mProto->size() <= 0) {
@@ -120,7 +121,9 @@
     protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS,
                        reinterpret_cast<char*>(buffer.get()->data()), buffer.get()->size());
 
-    mProto->clear();
+    if (erase_data) {
+        mProto->clear();
+    }
 }
 
 void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 7f7aa37..96adfdd 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -47,6 +47,7 @@
 
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
+                            const bool erase_data,
                             std::set<string> *str_set,
                             android::util::ProtoOutputStream* protoOutput) override;
     void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index f5a16e9..05103a9 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -182,6 +182,7 @@
 
 void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
+                                             const bool erase_data,
                                              std::set<string> *str_set,
                                              ProtoOutputStream* protoOutput) {
     VLOG("Gauge metric %lld report now...", (long long)mMetricId);
@@ -226,7 +227,6 @@
                            (long long)(NanoToMillis(pair.second)));
         protoOutput->end(wrapperToken);
     }
-    mSkippedBuckets.clear();
 
     for (const auto& pair : mPastBuckets) {
         const MetricDimensionKey& dimensionKey = pair.first;
@@ -304,7 +304,11 @@
     }
     protoOutput->end(protoToken);
 
-    mPastBuckets.clear();
+
+    if (erase_data) {
+        mPastBuckets.clear();
+        mSkippedBuckets.clear();
+    }
 }
 
 void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 99827bb..5866139 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -94,6 +94,7 @@
 private:
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
+                            const bool erase_data,
                             std::set<string> *str_set,
                             android::util::ProtoOutputStream* protoOutput) override;
     void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index b21fd50..127cbbd 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -133,10 +133,12 @@
     // This method clears all the past buckets.
     void onDumpReport(const int64_t dumpTimeNs,
                       const bool include_current_partial_bucket,
+                      const bool erase_data,
                       std::set<string> *str_set,
                       android::util::ProtoOutputStream* protoOutput) {
         std::lock_guard<std::mutex> lock(mMutex);
-        return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, str_set, protoOutput);
+        return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, erase_data,
+                str_set, protoOutput);
     }
 
     void clearPastBuckets(const int64_t dumpTimeNs) {
@@ -210,6 +212,7 @@
                                                   const int64_t eventTime) = 0;
     virtual void onDumpReportLocked(const int64_t dumpTimeNs,
                                     const bool include_current_partial_bucket,
+                                    const bool erase_data,
                                     std::set<string> *str_set,
                                     android::util::ProtoOutputStream* protoOutput) = 0;
     virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index f85ba1f..4244d5b 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -197,6 +197,7 @@
 
 void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs,
                                   const bool include_current_partial_bucket,
+                                  const bool erase_data,
                                   std::set<string> *str_set,
                                   ProtoOutputStream* protoOutput) {
     VLOG("=========================Metric Reports Start==========================");
@@ -206,11 +207,11 @@
             uint64_t token = protoOutput->start(
                     FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
             if (mHashStringsInReport) {
-                producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, str_set,
-                                       protoOutput);
+                producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
+                                       str_set, protoOutput);
             } else {
-                producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, nullptr,
-                                       protoOutput);
+                producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
+                                       nullptr, protoOutput);
             }
             protoOutput->end(token);
         } else {
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 649222ff..a4672b6 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -107,6 +107,7 @@
 
     virtual void onDumpReport(const int64_t dumpTimeNs,
                               const bool include_current_partial_bucket,
+                              const bool erase_data,
                               std::set<string> *str_set,
                               android::util::ProtoOutputStream* protoOutput);
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 192a54b..c8b1cf0 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -92,7 +92,9 @@
                                   : StatsdStats::kDimensionKeySizeHardLimit),
       mUseAbsoluteValueOnReset(metric.use_absolute_value_on_reset()),
       mAggregationType(metric.aggregation_type()),
-      mValueType(metric.aggregation_type() == ValueMetric::AVG ? DOUBLE : LONG) {
+      mUseDiff(metric.has_use_diff() ? metric.use_diff() : (mIsPulled ? true : false)),
+      mValueDirection(metric.value_direction()),
+      mSkipZeroDiffOutput(metric.skip_zero_diff_output()) {
     int64_t bucketSizeMills = 0;
     if (metric.has_bucket()) {
         bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
@@ -125,24 +127,25 @@
     }
     mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
     mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
-            HasPositionALL(metric.dimensions_in_condition());
+                          HasPositionALL(metric.dimensions_in_condition());
 
     flushIfNeededLocked(startTimeNs);
-    // Kicks off the puller immediately.
+
     if (mIsPulled) {
         mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
                                          mBucketSizeNs);
     }
 
-    // TODO: Only do this for partial buckets like first bucket. All other buckets should use
+    // Only do this for partial buckets like first bucket. All other buckets should use
     // flushIfNeeded to adjust start and end to bucket boundaries.
     // Adjust start for partial bucket
     mCurrentBucketStartTimeNs = startTimeNs;
-    if (mIsPulled) {
+    // Kicks off the puller immediately if condition is true and diff based.
+    if (mIsPulled && mCondition && mUseDiff) {
         pullLocked(startTimeNs);
     }
-    VLOG("value metric %lld created. bucket size %lld start_time: %lld",
-        (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs);
+    VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
+         (long long)mBucketSizeNs, (long long)mTimeBaseNs);
 }
 
 ValueMetricProducer::~ValueMetricProducer() {
@@ -170,6 +173,7 @@
 
 void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
+                                             const bool erase_data,
                                              std::set<string> *str_set,
                                              ProtoOutputStream* protoOutput) {
     VLOG("metric %lld dump report now...", (long long)mMetricId);
@@ -187,14 +191,14 @@
     // Fills the dimension path if not slicing by ALL.
     if (!mSliceByPositionALL) {
         if (!mDimensionsInWhat.empty()) {
-            uint64_t dimenPathToken = protoOutput->start(
-                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
+            uint64_t dimenPathToken =
+                    protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
             writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
             protoOutput->end(dimenPathToken);
         }
         if (!mDimensionsInCondition.empty()) {
-            uint64_t dimenPathToken = protoOutput->start(
-                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
+            uint64_t dimenPathToken =
+                    protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
             writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
             protoOutput->end(dimenPathToken);
         }
@@ -211,7 +215,6 @@
                            (long long)(NanoToMillis(pair.second)));
         protoOutput->end(wrapperToken);
     }
-    mSkippedBuckets.clear();
 
     for (const auto& pair : mPastBuckets) {
         const MetricDimensionKey& dimensionKey = pair.first;
@@ -221,15 +224,15 @@
 
         // First fill dimension.
         if (mSliceByPositionALL) {
-            uint64_t dimensionToken = protoOutput->start(
-                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
+            uint64_t dimensionToken =
+                    protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
             writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
             protoOutput->end(dimensionToken);
             if (dimensionKey.hasDimensionKeyInCondition()) {
-                uint64_t dimensionInConditionToken = protoOutput->start(
-                        FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
-                writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(),
-                                      str_set, protoOutput);
+                uint64_t dimensionInConditionToken =
+                        protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+                writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), str_set,
+                                      protoOutput);
                 protoOutput->end(dimensionInConditionToken);
             }
         } else {
@@ -237,8 +240,8 @@
                                            FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
             if (dimensionKey.hasDimensionKeyInCondition()) {
                 writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
-                                               FIELD_ID_DIMENSION_LEAF_IN_CONDITION,
-                                               str_set, protoOutput);
+                                               FIELD_ID_DIMENSION_LEAF_IN_CONDITION, str_set,
+                                               protoOutput);
             }
         }
 
@@ -256,28 +259,34 @@
                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
                                    (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
             }
-            if (mValueType == LONG) {
+            if (bucket.value.getType() == LONG) {
                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_LONG,
-                                   (long long)bucket.mValueLong);
+                                   (long long)bucket.value.long_value);
+                VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
+                     (long long)bucket.mBucketEndNs, (long long)bucket.value.long_value);
+            } else if (bucket.value.getType() == DOUBLE) {
+                protoOutput->write(FIELD_TYPE_DOUBLE | FIELD_ID_VALUE_DOUBLE,
+                                   bucket.value.double_value);
+                VLOG("\t bucket [%lld - %lld] count: %.2f", (long long)bucket.mBucketStartNs,
+                     (long long)bucket.mBucketEndNs, bucket.value.double_value);
             } else {
-                protoOutput->write(FIELD_TYPE_DOUBLE | FIELD_ID_VALUE_DOUBLE, bucket.mValueDouble);
+                VLOG("Wrong value type for ValueMetric output: %d", bucket.value.getType());
             }
             protoOutput->end(bucketInfoToken);
-            VLOG("\t bucket [%lld - %lld] count: %lld, %.2f", (long long)bucket.mBucketStartNs,
-                 (long long)bucket.mBucketEndNs, (long long)bucket.mValueLong, bucket.mValueDouble);
         }
         protoOutput->end(wrapperToken);
     }
     protoOutput->end(protoToken);
 
     VLOG("metric %lld dump report now...", (long long)mMetricId);
-    mPastBuckets.clear();
+    if (erase_data) {
+        mPastBuckets.clear();
+        mSkippedBuckets.clear();
+    }
 }
 
 void ValueMetricProducer::onConditionChangedLocked(const bool condition,
                                                    const int64_t eventTimeNs) {
-    mCondition = condition;
-
     if (eventTimeNs < mCurrentBucketStartTimeNs) {
         VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
              (long long)mCurrentBucketStartTimeNs);
@@ -286,9 +295,19 @@
 
     flushIfNeededLocked(eventTimeNs);
 
-    if (mIsPulled) {
+    // Pull on condition changes.
+    if (mIsPulled && (mCondition != condition)) {
         pullLocked(eventTimeNs);
     }
+
+    // when condition change from true to false, clear diff base
+    if (mUseDiff && mCondition && !condition) {
+        for (auto& slice : mCurrentSlicedBucket) {
+            slice.second.hasBase = false;
+        }
+    }
+
+    mCondition = condition;
 }
 
 void ValueMetricProducer::pullLocked(const int64_t timestampNs) {
@@ -303,30 +322,33 @@
     }
 }
 
+int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) {
+    return mTimeBaseNs + ((currentTimeNs - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
+}
+
 void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
     std::lock_guard<std::mutex> lock(mMutex);
 
-    if (mCondition == true || mConditionTrackerIndex < 0) {
+    if (mCondition) {
         if (allData.size() == 0) {
             return;
         }
         // For scheduled pulled data, the effective event time is snap to the nearest
-        // bucket boundary to make bucket finalize.
+        // bucket end. In the case of waking up from a deep sleep state, we will
+        // attribute to the previous bucket end. If the sleep was long but not very long, we
+        // will be in the immediate next bucket. Previous bucket may get a larger number as
+        // we pull at a later time than real bucket end.
+        // If the sleep was very long, we skip more than one bucket before sleep. In this case,
+        // if the diff base will be cleared and this new data will serve as new diff base.
         int64_t realEventTime = allData.at(0)->GetElapsedTimestampNs();
-        int64_t eventTime = mTimeBaseNs +
-            ((realEventTime - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
-
-        // close the end of the bucket
-        mCondition = false;
-        for (const auto& data : allData) {
-            data->setElapsedTimestampNs(eventTime - 1);
-            onMatchedLogEventLocked(0, *data);
+        int64_t bucketEndTime = calcPreviousBucketEndTime(realEventTime) - 1;
+        if (bucketEndTime < mCurrentBucketStartTimeNs) {
+            VLOG("Skip bucket end pull due to late arrival: %lld vs %lld", (long long)bucketEndTime,
+                 (long long)mCurrentBucketStartTimeNs);
+            return;
         }
-
-        // start a new bucket
-        mCondition = true;
         for (const auto& data : allData) {
-            data->setElapsedTimestampNs(eventTime);
+            data->setElapsedTimestampNs(bucketEndTime);
             onMatchedLogEventLocked(0, *data);
         }
     }
@@ -360,8 +382,8 @@
         StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > mDimensionHardLimit) {
-            ALOGE("ValueMetric %lld dropping data for dimension key %s",
-                (long long)mMetricId, newKey.toString().c_str());
+            ALOGE("ValueMetric %lld dropping data for dimension key %s", (long long)mMetricId,
+                  newKey.toString().c_str());
             return true;
         }
     }
@@ -390,10 +412,10 @@
     return v;
 }
 
-void ValueMetricProducer::onMatchedLogEventInternalLocked(
-        const size_t matcherIndex, const MetricDimensionKey& eventKey,
-        const ConditionKey& conditionKey, bool condition,
-        const LogEvent& event) {
+void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIndex,
+                                                          const MetricDimensionKey& eventKey,
+                                                          const ConditionKey& conditionKey,
+                                                          bool condition, const LogEvent& event) {
     int64_t eventTimeNs = event.GetElapsedTimestampNs();
     if (eventTimeNs < mCurrentBucketStartTimeNs) {
         VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
@@ -403,6 +425,14 @@
 
     flushIfNeededLocked(eventTimeNs);
 
+    // For pulled data, we already check condition when we decide to pull or
+    // in onDataPulled. So take all of them.
+    // For pushed data, just check condition.
+    if (!(mIsPulled || condition)) {
+        VLOG("ValueMetric skip event because condition is false");
+        return;
+    }
+
     if (hitGuardRailLocked(eventKey)) {
         return;
     }
@@ -415,71 +445,70 @@
     }
     Value value = getDoubleOrLong(event.getValues()[mField - 1].mValue);
 
-    Value diff;
-    bool hasDiff = false;
-    if (mIsPulled) {
-        // Always require condition for pulled events. In the case of no condition, only pull
-        // on bucket boundaries, in which we fake condition changes.
-        if (mCondition == true) {
-            if (!interval.startUpdated) {
-                interval.start = value;
-                interval.startUpdated = true;
-            } else {
-                // Skip it if there is already value recorded for the start. Happens when puller
-                // takes too long to finish. In this case we take the previous value.
-                VLOG("Already recorded value for this dimension %s", eventKey.toString().c_str());
-            }
-        } else {
-            // Generally we expect value to be monotonically increasing.
-            // If not, take absolute value or drop it, based on config.
-            if (interval.startUpdated) {
-                if (value >= interval.start) {
-                    diff = (value - interval.start);
-                    hasDiff = true;
+    if (mUseDiff) {
+        // no base. just update base and return.
+        if (!interval.hasBase) {
+            interval.base = value;
+            interval.hasBase = true;
+            return;
+        }
+        Value diff;
+        switch (mValueDirection) {
+            case ValueMetric::INCREASING:
+                if (value >= interval.base) {
+                    diff = value - interval.base;
+                } else if (mUseAbsoluteValueOnReset) {
+                    diff = value;
                 } else {
-                    if (mUseAbsoluteValueOnReset) {
-                        diff = value;
-                        hasDiff = true;
-                    } else {
-                        VLOG("Dropping data for atom %d, prev: %s, now: %s", mPullTagId,
-                             interval.start.toString().c_str(), value.toString().c_str());
-                    }
+                    VLOG("Unexpected decreasing value");
+                    StatsdStats::getInstance().notePullDataError(mPullTagId);
+                    interval.base = value;
+                    return;
                 }
-                interval.startUpdated = false;
-            } else {
-                VLOG("No start for matching end %s", value.toString().c_str());
-            }
+                break;
+            case ValueMetric::DECREASING:
+                if (interval.base >= value) {
+                    diff = interval.base - value;
+                } else if (mUseAbsoluteValueOnReset) {
+                    diff = value;
+                } else {
+                    VLOG("Unexpected increasing value");
+                    StatsdStats::getInstance().notePullDataError(mPullTagId);
+                    interval.base = value;
+                    return;
+                }
+                break;
+            case ValueMetric::ANY:
+                diff = value - interval.base;
+                break;
+            default:
+                break;
+        }
+        interval.base = value;
+        value = diff;
+    }
+
+    if (interval.hasValue) {
+        switch (mAggregationType) {
+            case ValueMetric::SUM:
+                // for AVG, we add up and take average when flushing the bucket
+            case ValueMetric::AVG:
+                interval.value += value;
+                break;
+            case ValueMetric::MIN:
+                interval.value = std::min(value, interval.value);
+                break;
+            case ValueMetric::MAX:
+                interval.value = std::max(value, interval.value);
+                break;
+            default:
+                break;
         }
     } else {
-        // for pushed events, only aggregate when sliced condition is true
-        if (condition == true || mConditionTrackerIndex < 0) {
-            diff = value;
-            hasDiff = true;
-        }
+        interval.value = value;
+        interval.hasValue = true;
     }
-    if (hasDiff) {
-        if (interval.hasValue) {
-            switch (mAggregationType) {
-                case ValueMetric::SUM:
-                // for AVG, we add up and take average when flushing the bucket
-                case ValueMetric::AVG:
-                    interval.value += diff;
-                    break;
-                case ValueMetric::MIN:
-                    interval.value = diff < interval.value ? diff : interval.value;
-                    break;
-                case ValueMetric::MAX:
-                    interval.value = diff > interval.value ? diff : interval.value;
-                    break;
-                default:
-                    break;
-            }
-        } else {
-            interval.value = diff;
-            interval.hasValue = true;
-        }
-        interval.sampleSize += 1;
-    }
+    interval.sampleSize += 1;
 
     // TODO: propgate proper values down stream when anomaly support doubles
     long wholeBucketVal = interval.value.long_value;
@@ -509,6 +538,10 @@
 
     if (numBucketsForward > 1) {
         VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
+        // take base again in future good bucket.
+        for (auto& slice : mCurrentSlicedBucket) {
+            slice.second.hasBase = false;
+        }
     }
     VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
          (long long)mCurrentBucketStartTimeNs);
@@ -531,8 +564,18 @@
         // The current bucket is large enough to keep.
         for (const auto& slice : mCurrentSlicedBucket) {
             if (slice.second.hasValue) {
-                info.mValueLong = slice.second.value.long_value;
-                info.mValueDouble = (double)slice.second.value.long_value / slice.second.sampleSize;
+                // skip the output if the diff is zero
+                if (mSkipZeroDiffOutput && mUseDiff && slice.second.value.isZero()) {
+                    continue;
+                }
+                if (mAggregationType != ValueMetric::AVG) {
+                    info.value = slice.second.value;
+                } else {
+                    double sum = slice.second.value.type == LONG
+                                         ? (double)slice.second.value.long_value
+                                         : slice.second.value.double_value;
+                    info.value.setDouble(sum / slice.second.sampleSize);
+                }
                 // it will auto create new vector of ValuebucketInfo if the key is not found.
                 auto& bucketList = mPastBuckets[slice.first];
                 bucketList.push_back(info);
@@ -578,7 +621,10 @@
     }
 
     // Reset counters
-    mCurrentSlicedBucket.clear();
+    for (auto& slice : mCurrentSlicedBucket) {
+        slice.second.hasValue = false;
+        slice.second.sampleSize = 0;
+    }
 }
 
 size_t ValueMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index b2f0b6f..3416afe 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -34,8 +34,7 @@
 struct ValueBucket {
     int64_t mBucketStartNs;
     int64_t mBucketEndNs;
-    int64_t mValueLong;
-    double mValueDouble;
+    Value value;
 };
 
 class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
@@ -54,35 +53,11 @@
     void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
                           const int64_t version) override {
         std::lock_guard<std::mutex> lock(mMutex);
-
-        if (mIsPulled && (mCondition == true || mConditionTrackerIndex < 0)) {
-            vector<shared_ptr<LogEvent>> allData;
-            mPullerManager->Pull(mPullTagId, eventTimeNs, &allData);
-            if (allData.size() == 0) {
-                // This shouldn't happen since this valuemetric is not useful now.
-            }
-
-            // Pretend the pulled data occurs right before the app upgrade event.
-            mCondition = false;
-            for (const auto& data : allData) {
-                data->setElapsedTimestampNs(eventTimeNs - 1);
-                onMatchedLogEventLocked(0, *data);
-            }
-
-            flushCurrentBucketLocked(eventTimeNs);
-            mCurrentBucketStartTimeNs = eventTimeNs;
-
-            mCondition = true;
-            for (const auto& data : allData) {
-                data->setElapsedTimestampNs(eventTimeNs);
-                onMatchedLogEventLocked(0, *data);
-            }
-        } else {
-            // For pushed value metric or pulled metric where condition is not true,
-            // we simply flush and reset the current bucket start.
-            flushCurrentBucketLocked(eventTimeNs);
-            mCurrentBucketStartTimeNs = eventTimeNs;
+        if (mIsPulled && mCondition) {
+            pullLocked(eventTimeNs - 1);
         }
+        flushCurrentBucketLocked(eventTimeNs);
+        mCurrentBucketStartTimeNs = eventTimeNs;
     };
 
 protected:
@@ -94,6 +69,7 @@
 private:
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
+                            const bool erase_data,
                             std::set<string> *str_set,
                             android::util::ProtoOutputStream* protoOutput) override;
     void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
@@ -116,6 +92,9 @@
 
     void dropDataLocked(const int64_t dropTimeNs) override;
 
+    // Calculate previous bucket end time based on current time.
+    int64_t calcPreviousBucketEndTime(const int64_t currentTimeNs);
+
     sp<StatsPullerManager> mPullerManager;
 
     const FieldMatcher mValueField;
@@ -130,11 +109,10 @@
 
     // internal state of a bucket.
     typedef struct {
-        // Pulled data always come in pair of <start, end>. This holds the value
-        // for start. The diff (end - start) is taken as the real value.
-        Value start;
-        // Whether the start data point is updated
-        bool startUpdated;
+        // Holds current base value of the dimension. Take diff and update if necessary.
+        Value base;
+        // Whether there is a base to diff to.
+        bool hasBase;
         // Current value, depending on the aggregation type.
         Value value;
         // Number of samples collected.
@@ -171,7 +149,11 @@
 
     const ValueMetric::AggregationType mAggregationType;
 
-    const Type mValueType;
+    const bool mUseDiff;
+
+    const ValueMetric::ValueDirection mValueDirection;
+
+    const bool mSkipZeroDiffOutput;
 
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
@@ -186,13 +168,13 @@
     FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
-    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3);
     FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
     FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
     FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
     FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
-    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSumSliced);
     FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
+    FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime);
+    FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 10ed7f3..4da3828 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -328,6 +328,7 @@
         optional bool is_valid = 9;
         repeated int32 broadcast_sent_time_sec = 10;
         repeated int32 data_drop_time_sec = 11;
+        repeated int64 data_drop_bytes = 21;
         repeated int32 dump_report_time_sec = 12;
         repeated int32 dump_report_data_size = 20;
         repeated MatcherStats matcher_stats = 13;
@@ -369,6 +370,11 @@
         optional int64 total_pull = 2;
         optional int64 total_pull_from_cache = 3;
         optional int64 min_pull_interval_sec = 4;
+        optional int64 average_pull_time_nanos = 5;
+        optional int64 max_pull_time_nanos = 6;
+        optional int64 average_pull_delay_nanos = 7;
+        optional int64 max_pull_delay_nanos = 8;
+        optional int64 data_error = 9;
     }
     repeated PulledAtomStats pulled_atom_stats = 10;
 
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 2498d9f..504c586 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -59,6 +59,11 @@
 const int FIELD_ID_TOTAL_PULL = 2;
 const int FIELD_ID_TOTAL_PULL_FROM_CACHE = 3;
 const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4;
+const int FIELD_ID_AVERAGE_PULL_TIME_NANOS = 5;
+const int FIELD_ID_MAX_PULL_TIME_NANOS = 6;
+const int FIELD_ID_AVERAGE_PULL_DELAY_NANOS = 7;
+const int FIELD_ID_MAX_PULL_DELAY_NANOS = 8;
+const int FIELD_ID_DATA_ERROR = 9;
 
 namespace {
 
@@ -434,6 +439,15 @@
                        (long long)pair.second.totalPullFromCache);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_PULL_INTERVAL_SEC,
                        (long long)pair.second.minPullIntervalSec);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_AVERAGE_PULL_TIME_NANOS,
+                       (long long)pair.second.avgPullTimeNs);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_TIME_NANOS,
+                       (long long)pair.second.maxPullTimeNs);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_AVERAGE_PULL_DELAY_NANOS,
+                       (long long)pair.second.avgPullDelayNs);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_DELAY_NANOS,
+                       (long long)pair.second.maxPullDelayNs);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DATA_ERROR, (long long)pair.second.dataError);
     protoOutput->end(token);
 }
 
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index b8f6850..61f31eb 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -21,6 +21,7 @@
 #include "HashableDimensionKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "guardrail/StatsdStats.h"
+#include "statslog.h"
 
 namespace android {
 namespace os {
@@ -87,6 +88,10 @@
 // Returns the truncated timestamp.
 int64_t truncateTimestampNsToFiveMinutes(int64_t timestampNs);
 
+inline bool isPushedAtom(int atomId) {
+    return atomId <= util::kMaxPushedAtomId && atomId > 1;
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index d5f81a59..5c46a29 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -270,6 +270,17 @@
   optional int64 min_bucket_size_nanos = 10;
 
   optional bool use_absolute_value_on_reset = 11 [default = false];
+
+  optional bool use_diff = 12;
+
+  enum ValueDirection {
+      INCREASING = 1;
+      DECREASING = 2;
+      ANY = 3;
+  }
+  optional ValueDirection value_direction = 13 [default = INCREASING];
+
+  optional bool skip_zero_diff_output = 14 [default = true];
 }
 
 message Alert {
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index f59ac1a..6384757 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -12,9 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "src/logd/LogEvent.h"
 #include <gtest/gtest.h>
 #include <log/log_event_list.h>
-#include "src/logd/LogEvent.h"
+#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
+#include "frameworks/base/core/proto/android/stats/launcher/launcher.pb.h"
 
 #ifdef __ANDROID__
 
@@ -22,6 +24,9 @@
 namespace os {
 namespace statsd {
 
+using std::string;
+using util::ProtoOutputStream;
+
 TEST(LogEventTest, TestLogParsing) {
     LogEvent event1(1, 2000);
 
@@ -390,6 +395,57 @@
 }
 
 
+TEST(LogEventTest, TestBinaryFieldAtom) {
+    Atom launcherAtom;
+    auto launcher_event = launcherAtom.mutable_launcher_event();
+    launcher_event->set_action(stats::launcher::LauncherAction::LONGPRESS);
+    launcher_event->set_src_state(stats::launcher::LauncherState::OVERVIEW);
+    launcher_event->set_dst_state(stats::launcher::LauncherState::ALLAPPS);
+
+    auto extension = launcher_event->mutable_extension();
+
+    auto src_target = extension->add_src_target();
+    src_target->set_type(stats::launcher::LauncherTarget_Type_ITEM_TYPE);
+    src_target->set_item(stats::launcher::LauncherTarget_Item_FOLDER_ICON);
+
+    auto dst_target = extension->add_dst_target();
+    dst_target->set_type(stats::launcher::LauncherTarget_Type_ITEM_TYPE);
+    dst_target->set_item(stats::launcher::LauncherTarget_Item_WIDGET);
+
+    string extension_str;
+    extension->SerializeToString(&extension_str);
+
+    LogEvent event1(Atom::kLauncherEventFieldNumber, 1000);
+
+    event1.write((int32_t)stats::launcher::LauncherAction::LONGPRESS);
+    event1.write((int32_t)stats::launcher::LauncherState::OVERVIEW);
+    event1.write((int64_t)stats::launcher::LauncherState::ALLAPPS);
+    event1.write(extension_str);
+    event1.init();
+
+    ProtoOutputStream proto;
+    event1.ToProto(proto);
+
+    std::vector<uint8_t> outData;
+    outData.resize(proto.size());
+    size_t pos = 0;
+    auto iter = proto.data();
+    while (iter.readBuffer() != NULL) {
+        size_t toRead = iter.currentToRead();
+        std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
+        pos += toRead;
+        iter.rp()->move(toRead);
+    }
+
+    std::string result_str(outData.begin(), outData.end());
+    std::string orig_str;
+    launcherAtom.SerializeToString(&orig_str);
+
+    EXPECT_EQ(orig_str, result_str);
+}
+
+
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index b6f635c..8864252 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -168,7 +168,7 @@
 
     // Expect to get no metrics, but snapshot specified above in uidmap.
     vector<uint8_t> bytes;
-    p.onDumpReport(key, 1, false, ADB_DUMP, &bytes);
+    p.onDumpReport(key, 1, false, true, ADB_DUMP, &bytes);
 
     ConfigMetricsReportList output;
     output.ParseFromArray(bytes.data(), bytes.size());
@@ -197,7 +197,7 @@
 
     // Expect to get no metrics, but snapshot specified above in uidmap.
     vector<uint8_t> bytes;
-    p.onDumpReport(key, 1, false, ADB_DUMP, &bytes);
+    p.onDumpReport(key, 1, false, true, ADB_DUMP, &bytes);
 
     ConfigMetricsReportList output;
     output.ParseFromArray(bytes.data(), bytes.size());
@@ -227,7 +227,7 @@
 
     // Expect to get no metrics, but snapshot specified above in uidmap.
     vector<uint8_t> bytes;
-    p.onDumpReport(key, 1, false, ADB_DUMP, &bytes);
+    p.onDumpReport(key, 1, false, true, ADB_DUMP, &bytes);
 
     ConfigMetricsReportList output;
     output.ParseFromArray(bytes.data(), bytes.size());
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index a8fcc81..5c47af7 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -144,8 +144,8 @@
     }
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -290,8 +290,8 @@
     }
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
index 75bd40f..a8914da 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
@@ -212,7 +212,7 @@
                 ConfigMetricsReportList reports;
                 vector<uint8_t> buffer;
                 processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                        ADB_DUMP, &buffer);
+                                        true, ADB_DUMP, &buffer);
                 EXPECT_TRUE(buffer.size() > 0);
                 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
                 backfillDimensionPath(&reports);
@@ -548,7 +548,7 @@
             ConfigMetricsReportList reports;
             vector<uint8_t> buffer;
             processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                    ADB_DUMP, &buffer);
+                                    true, ADB_DUMP, &buffer);
             EXPECT_TRUE(buffer.size() > 0);
             EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
             backfillDimensionPath(&reports);
@@ -798,7 +798,7 @@
             ConfigMetricsReportList reports;
             vector<uint8_t> buffer;
             processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                    ADB_DUMP, &buffer);
+                                    true, ADB_DUMP, &buffer);
             EXPECT_TRUE(buffer.size() > 0);
             EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
             backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
index c5a8a2e..621b6ed 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
@@ -130,8 +130,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -346,8 +346,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -530,8 +530,8 @@
 
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                                &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                                ADB_DUMP, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
         backfillDimensionPath(&reports);
@@ -732,8 +732,8 @@
 
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                                &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                                ADB_DUMP, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
         backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
index 5bcc9ee..9f8acaf 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
@@ -143,7 +143,7 @@
             ConfigMetricsReportList reports;
             vector<uint8_t> buffer;
             processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                    ADB_DUMP, &buffer);
+                                    true, ADB_DUMP, &buffer);
             EXPECT_TRUE(buffer.size() > 0);
             EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
             backfillDimensionPath(&reports);
@@ -438,7 +438,7 @@
             ConfigMetricsReportList reports;
             vector<uint8_t> buffer;
             processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                    ADB_DUMP, &buffer);
+                                    true, ADB_DUMP, &buffer);
             EXPECT_TRUE(buffer.size() > 0);
             EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
             backfillDimensionPath(&reports);
@@ -658,8 +658,8 @@
 
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                                &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                                ADB_DUMP, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
         backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index d7b9c11..2d090e0 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -123,8 +123,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -246,8 +246,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -350,8 +350,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 5c1ef01..71afedf 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -149,8 +149,8 @@
         }
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, ADB_DUMP,
-                                &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true,
+                                ADB_DUMP, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
         backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
index 0f13a4a..29e86f3 100644
--- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -167,8 +167,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index cc8894b..9349c85 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -199,8 +199,8 @@
     }
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -318,8 +318,8 @@
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
 
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 67acd61..3cb553f 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -46,7 +46,7 @@
     IPCThreadState* ipc = IPCThreadState::self();
     ConfigKey configKey(ipc->getCallingUid(), kConfigKey);
     processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/,
-                            ADB_DUMP, &output);
+                            true /* erase_data */, ADB_DUMP, &output);
     ConfigMetricsReportList reports;
     reports.ParseFromArray(output.data(), output.size());
     EXPECT_EQ(1, reports.reports_size());
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index f2e8f58..095b401 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -49,6 +49,7 @@
         CreateDimensions(android::util::TEMPERATURE, {2/* sensor name field */ });
     valueMetric->set_bucket(FIVE_MINUTES);
     valueMetric->set_use_absolute_value_on_reset(true);
+    valueMetric->set_skip_zero_diff_output(false);
     return config;
 }
 
@@ -117,8 +118,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -224,8 +225,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index b9d0c62..6d1317c 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -127,8 +127,8 @@
     FeedEvents(config, processor);
     vector<uint8_t> buffer;
     ConfigMetricsReportList reports;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -164,8 +164,8 @@
     FeedEvents(config, processor);
     vector<uint8_t> buffer;
     ConfigMetricsReportList reports;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -215,8 +215,8 @@
         processor->OnLogEvent(event.get());
     }
 
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -248,8 +248,8 @@
     FeedEvents(config, processor);
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
 
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -277,8 +277,8 @@
     FeedEvents(config, processor);
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -323,8 +323,8 @@
         processor->OnLogEvent(event.get());
     }
 
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 967ef3c..6069516 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -126,7 +126,7 @@
     stats.noteBroadcastSent(key);
 
     // data drop -> 1
-    stats.noteDataDropped(key);
+    stats.noteDataDropped(key, 123);
 
     // dump report -> 3
     stats.noteMetricsReportSent(key, 0);
@@ -142,6 +142,8 @@
     const auto& configReport = report.config_stats(0);
     EXPECT_EQ(2, configReport.broadcast_sent_time_sec_size());
     EXPECT_EQ(1, configReport.data_drop_time_sec_size());
+    EXPECT_EQ(1, configReport.data_drop_bytes_size());
+    EXPECT_EQ(123, configReport.data_drop_bytes(0));
     EXPECT_EQ(3, configReport.dump_report_time_sec_size());
     EXPECT_EQ(3, configReport.dump_report_data_size_size());
     EXPECT_EQ(1, configReport.annotation_size());
@@ -242,6 +244,39 @@
     EXPECT_TRUE(sensorAtomGood);
 }
 
+TEST(StatsdStatsTest, TestPullAtomStats) {
+    StatsdStats stats;
+
+    stats.updateMinPullIntervalSec(android::util::DISK_SPACE, 3333L);
+    stats.updateMinPullIntervalSec(android::util::DISK_SPACE, 2222L);
+    stats.updateMinPullIntervalSec(android::util::DISK_SPACE, 4444L);
+
+    stats.notePull(android::util::DISK_SPACE);
+    stats.notePullTime(android::util::DISK_SPACE, 1111L);
+    stats.notePullDelay(android::util::DISK_SPACE, 1111L);
+    stats.notePull(android::util::DISK_SPACE);
+    stats.notePullTime(android::util::DISK_SPACE, 3333L);
+    stats.notePullDelay(android::util::DISK_SPACE, 3335L);
+    stats.notePull(android::util::DISK_SPACE);
+    stats.notePullFromCache(android::util::DISK_SPACE);
+
+    vector<uint8_t> output;
+    stats.dumpStats(&output, false);
+    StatsdStatsReport report;
+    bool good = report.ParseFromArray(&output[0], output.size());
+    EXPECT_TRUE(good);
+
+    EXPECT_EQ(1, report.pulled_atom_stats_size());
+
+    EXPECT_EQ(android::util::DISK_SPACE, report.pulled_atom_stats(0).atom_id());
+    EXPECT_EQ(3, report.pulled_atom_stats(0).total_pull());
+    EXPECT_EQ(1, report.pulled_atom_stats(0).total_pull_from_cache());
+    EXPECT_EQ(2222L, report.pulled_atom_stats(0).min_pull_interval_sec());
+    EXPECT_EQ(2222L, report.pulled_atom_stats(0).average_pull_time_nanos());
+    EXPECT_EQ(3333L, report.pulled_atom_stats(0).max_pull_time_nanos());
+    EXPECT_EQ(2223L, report.pulled_atom_stats(0).average_pull_delay_nanos());
+    EXPECT_EQ(3335L, report.pulled_atom_stats(0).max_pull_delay_nanos());
+}
 
 TEST(StatsdStatsTest, TestAnomalyMonitor) {
     StatsdStats stats;
@@ -275,7 +310,7 @@
     int32_t newTimestamp = 10000;
 
     // now it should trigger removing oldest timestamp
-    stats.noteDataDropped(key, 10000);
+    stats.noteDataDropped(key, 123, 10000);
     stats.noteBroadcastSent(key, 10000);
     stats.noteMetricsReportSent(key, 0, 10000);
 
@@ -295,6 +330,7 @@
     // the last timestamp is the newest timestamp.
     EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back());
     EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back());
+    EXPECT_EQ(123, configStats->data_drop_bytes.back());
     EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().first);
 }
 
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 57aab97..ffa07081 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -47,7 +47,35 @@
 const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
 const int64_t bucket5StartTimeNs = bucketStartTimeNs + 4 * bucketSizeNs;
 const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs;
-const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+double epsilon = 0.001;
+
+/*
+ * Tests that the first bucket works correctly
+ */
+TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+
+    int64_t startTimeBase = 11;
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    // statsd started long ago.
+    // The metric starts in the middle of the bucket
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      -1, startTimeBase, 22, pullerManager);
+
+    EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
+    EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
+    EXPECT_EQ(60 * NS_PER_SEC + startTimeBase,
+              valueProducer.calcPreviousBucketEndTime(2 * 60 * NS_PER_SEC));
+    EXPECT_EQ(2 * 60 * NS_PER_SEC + startTimeBase,
+              valueProducer.calcPreviousBucketEndTime(3 * 60 * NS_PER_SEC));
+}
 
 /*
  * Tests that the first bucket works correctly
@@ -90,7 +118,7 @@
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
                 event->write(tagId);
                 event->write(3);
                 event->init();
@@ -114,12 +142,11 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
 
-    // startUpdated:true sum:0 start:11
-    EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(11, curInterval.start.long_value);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(11, curInterval.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(8, curInterval.value.long_value);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -131,12 +158,14 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // tartUpdated:false sum:12
-    EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
+
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(23, curInterval.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(12, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -147,12 +176,14 @@
     valueProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false sum:12
-    EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
+
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(36, curInterval.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(13, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
 }
 
 /*
@@ -170,7 +201,7 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(true));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
@@ -188,9 +219,9 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
 
-    EXPECT_EQ(true, curInterval.startUpdated);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(11, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(11, curInterval.start.long_value);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     allData.clear();
@@ -203,11 +234,11 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(10, curInterval.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(10, curInterval.value.long_value);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -218,11 +249,13 @@
     valueProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(36, curInterval.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(26, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
 }
 
 /*
@@ -257,9 +290,9 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
 
-    EXPECT_EQ(true, curInterval.startUpdated);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(11, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(11, curInterval.start.long_value);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     allData.clear();
@@ -272,7 +305,8 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(true, curInterval.startUpdated);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(10, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
@@ -285,11 +319,11 @@
     valueProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(36, curInterval.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(26, curInterval.value.long_value);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 }
 
 /*
@@ -309,21 +343,10 @@
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
     EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            // should not take effect
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-                event->write(tagId);
-                event->write(3);
-                event->init();
-                data->push_back(event);
-                return true;
-            }))
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
-                data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
                 event->write(tagId);
                 event->write(100);
                 event->init();
@@ -333,7 +356,7 @@
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
                 event->write(tagId);
                 event->write(120);
                 event->init();
@@ -349,8 +372,8 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     // startUpdated:false sum:0 start:100
-    EXPECT_EQ(100, curInterval.start.long_value);
-    EXPECT_EQ(true, curInterval.startUpdated);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(100, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
@@ -366,20 +389,20 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false sum:0 start:110
-    EXPECT_EQ(110, curInterval.start.long_value);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(110, curInterval.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(10, curInterval.value.long_value);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false sum:0 start:110
+    EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(10, curInterval.value.long_value);
-    EXPECT_EQ(false, curInterval.startUpdated);
+    EXPECT_EQ(false, curInterval.hasBase);
 }
 
 TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
@@ -401,9 +424,9 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
 
-    valueProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+    valueProducer.notifyAppUpgrade(bucketStartTimeNs + 150, "ANY.APP", 1, 1);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-    EXPECT_EQ(eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
 
     shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC);
     event2->write(1);
@@ -411,7 +434,7 @@
     event2->init();
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-    EXPECT_EQ(eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
 
     // Next value should create a new bucket.
     shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC);
@@ -435,11 +458,11 @@
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Return(false))
+            .WillOnce(Return(true))
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 149);
                 event->write(tagId);
                 event->write(120);
                 event->init();
@@ -451,7 +474,7 @@
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
-    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
     event->write(tagId);
     event->write(100);
     event->init();
@@ -460,21 +483,21 @@
     valueProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
 
-    valueProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+    valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-    EXPECT_EQ(eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs);
-    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValueLong);
+    EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].value.long_value);
 
     allData.clear();
-    event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
     event->write(tagId);
     event->write(150);
     event->init();
     allData.push_back(event);
     valueProducer.onDataPulled(allData);
-    EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-    EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
-    EXPECT_EQ(30L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mValueLong);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].value.long_value);
 }
 
 TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
@@ -490,11 +513,10 @@
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Return(false))
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
                 event->write(tagId);
                 event->write(100);
                 event->init();
@@ -504,7 +526,7 @@
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs - 100);
                 event->write(tagId);
                 event->write(120);
                 event->init();
@@ -523,7 +545,7 @@
     EXPECT_EQ(bucket2StartTimeNs-50, valueProducer.mCurrentBucketStartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
     EXPECT_EQ(bucketStartTimeNs, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
-    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValueLong);
+    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].value.long_value);
     EXPECT_FALSE(valueProducer.mCondition);
 }
 
@@ -565,7 +587,7 @@
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
 }
 
 TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
@@ -587,9 +609,7 @@
     event1->init();
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
     // has 1 slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
 
     valueProducer.onConditionChangedLocked(true, bucketStartTimeNs + 15);
     shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
@@ -600,6 +620,7 @@
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     EXPECT_EQ(20, curInterval.value.long_value);
 
@@ -629,7 +650,7 @@
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(50, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(50, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
 }
 
 TEST(ValueMetricProducerTest, TestAnomalyDetection) {
@@ -727,7 +748,7 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(true));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
@@ -747,9 +768,9 @@
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
 
     // startUpdated:true sum:0 start:11
-    EXPECT_EQ(true, curInterval.startUpdated);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(11, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(11, curInterval.start.long_value);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // pull 2 at correct time
@@ -764,11 +785,11 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     // tartUpdated:false sum:12
-    EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(23, curInterval.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(12, curInterval.value.long_value);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // pull 3 come late.
     // The previous bucket gets closed with error. (Has start value 23, no ending)
@@ -784,12 +805,12 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     // startUpdated:false sum:12
-    EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(36, curInterval.start.long_value);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(36, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
 }
 
 /*
@@ -810,12 +831,11 @@
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
     EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Return(false))
             // condition becomes true
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
                 event->write(tagId);
                 event->write(100);
                 event->init();
@@ -826,7 +846,7 @@
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20);
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
                 event->write(tagId);
                 event->write(120);
                 event->init();
@@ -841,17 +861,17 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false sum:0 start:100
-    EXPECT_EQ(100, curInterval.start.long_value);
-    EXPECT_EQ(true, curInterval.startUpdated);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(100, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // pull on bucket boundary come late, condition change happens before it
     valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(false, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(false, curInterval.hasBase);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(20, curInterval.value.long_value);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // Now the alarm is delivered.
@@ -866,8 +886,9 @@
     valueProducer.onDataPulled(allData);
 
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(false, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(false, curInterval.hasBase);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(20, curInterval.value.long_value);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 }
 
@@ -889,12 +910,11 @@
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
     EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Return(false))
             // condition becomes true
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
                 event->write(tagId);
                 event->write(100);
                 event->init();
@@ -905,7 +925,7 @@
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20);
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
                 event->write(tagId);
                 event->write(120);
                 event->init();
@@ -916,7 +936,7 @@
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 30);
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 25);
                 event->write(tagId);
                 event->write(130);
                 event->init();
@@ -932,24 +952,26 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     // startUpdated:false sum:0 start:100
-    EXPECT_EQ(100, curInterval.start.long_value);
-    EXPECT_EQ(true, curInterval.startUpdated);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(100, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // pull on bucket boundary come late, condition change happens before it
     valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(false, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(false, curInterval.hasBase);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(20, curInterval.value.long_value);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // condition changed to true again, before the pull alarm is delivered
     valueProducer.onConditionChanged(true, bucket2StartTimeNs + 25);
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(130, curInterval.start.long_value);
-    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(130, curInterval.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(20, curInterval.value.long_value);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // Now the alarm is delivered, but it is considered late, it has no effect
@@ -963,89 +985,10 @@
     valueProducer.onDataPulled(allData);
 
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(130, curInterval.start.long_value);
-    EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
-}
-
-/*
- * Test pulled event with non sliced condition. The pull on boundary come late because the puller is
- * very slow.
- */
-TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3) {
-    ValueMetric metric;
-    metric.set_id(metricId);
-    metric.set_bucket(ONE_MINUTE);
-    metric.mutable_value_field()->set_field(tagId);
-    metric.mutable_value_field()->add_child()->set_field(2);
-    metric.set_condition(StringToId("SCREEN_ON"));
-
-    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
-    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
-            .WillOnce(Return(false))
-            // condition becomes true
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
-                data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-                event->write(tagId);
-                event->write(100);
-                event->init();
-                data->push_back(event);
-                return true;
-            }))
-            // condition becomes false
-            .WillOnce(Invoke([](int tagId, int64_t timeNs,
-                                vector<std::shared_ptr<LogEvent>>* data) {
-                data->clear();
-                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 20);
-                event->write(tagId);
-                event->write(120);
-                event->init();
-                data->push_back(event);
-                return true;
-            }));
-
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
-
-    // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false sum:0 start:100
-    EXPECT_EQ(100, curInterval.start.long_value);
-    EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
-
-    // pull on bucket boundary come late, condition change happens before it.
-    // But puller is very slow in this one, so the data come after bucket finish
-    valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(false, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
-
-    // Alarm is delivered in time, but the pull is very slow, and pullers are called in order,
-    // so this one comes even later
-    vector<shared_ptr<LogEvent>> allData;
-    allData.clear();
-    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 30);
-    event->write(1);
-    event->write(110);
-    event->init();
-    allData.push_back(event);
-    valueProducer.onDataPulled(allData);
-
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(false, curInterval.startUpdated);
-    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(130, curInterval.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(20, curInterval.value.long_value);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 }
 
@@ -1088,7 +1031,7 @@
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
 }
 
 TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
@@ -1130,7 +1073,7 @@
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
 }
 
 TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
@@ -1175,7 +1118,7 @@
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(12.5, valueProducer.mPastBuckets.begin()->second.back().mValueDouble);
+    EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().value.double_value - 12.5) < epsilon);
 }
 
 TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
@@ -1217,67 +1160,75 @@
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(25, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(25, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
 }
 
-TEST(ValueMetricProducerTest, TestPushedAggregateSumSliced) {
-    string slicedConditionName = "UID";
-    const int conditionTagId = 2;
+TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
     ValueMetric metric;
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
     metric.mutable_value_field()->set_field(tagId);
-    metric.mutable_value_field()->add_child()->set_field(1);
-    metric.set_aggregation_type(ValueMetric::SUM);
-
-    metric.set_condition(StringToId(slicedConditionName));
-    MetricConditionLink* link = metric.add_links();
-    link->set_condition(StringToId(slicedConditionName));
-    buildSimpleAtomFieldMatcher(tagId, 2, link->mutable_fields_in_what());
-    buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
-
-    LogEvent event1(tagId, bucketStartTimeNs + 10);
-    event1.write(10);  // value
-    event1.write("111"); // uid
-    event1.init();
-    ConditionKey key1;
-    key1[StringToId(slicedConditionName)] =
-        {getMockedDimensionKey(conditionTagId, 2, "111")};
-
-    LogEvent event2(tagId, bucketStartTimeNs + 20);
-    event2.write(15);
-    event2.write("222");
-    event2.init();
-    ConditionKey key2;
-    key2[StringToId(slicedConditionName)] =
-        {getMockedDimensionKey(conditionTagId, 2, "222")};
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_aggregation_type(ValueMetric::MIN);
+    metric.set_use_diff(true);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse));
-    EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
-
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, -1, bucketStartTimeNs,
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
-
+    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+    event1->write(1);
+    event1->write(10);
+    event1->init();
+    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 15);
+    event2->write(1);
+    event2->write(15);
+    event2->init();
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+    // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(10, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
 
-    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(15, curInterval.value.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(5, curInterval.value.long_value);
+
+    // no change in data.
+    shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
+    event3->write(1);
+    event3->write(15);
+    event3->init();
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(15, curInterval.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+
+    shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 15);
+    event4->write(1);
+    event4->write(15);
+    event4->init();
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(15, curInterval.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
 
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(15, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+    EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tools/Android.mk b/cmds/statsd/tools/Android.mk
deleted file mode 100644
index 7253c96..0000000
--- a/cmds/statsd/tools/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (C) 2017 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.
-#
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-#Include the sub-makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/cmds/statsd/tools/dogfood/Android.bp b/cmds/statsd/tools/dogfood/Android.bp
new file mode 100644
index 0000000..bb494a6
--- /dev/null
+++ b/cmds/statsd/tools/dogfood/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2017 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.
+//
+//
+
+android_app {
+    name: "StatsdDogfood",
+    platform_apis: true,
+
+    srcs: ["src/**/*.java"],
+
+    resource_dirs: ["res"],
+    static_libs: [
+        "platformprotoslite",
+        "statsdprotolite",
+    ],
+
+    privileged: true,
+    dex_preopt: {
+        enabled: false,
+    },
+    certificate: "platform",
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk
deleted file mode 100644
index baf235b..0000000
--- a/cmds/statsd/tools/dogfood/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2017 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.
-#
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := StatsdDogfood
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \
-                               statsdprotolite
-
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_DEX_PREOPT := false
-LOCAL_CERTIFICATE := platform
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/cmds/statsd/tools/loadtest/Android.bp b/cmds/statsd/tools/loadtest/Android.bp
new file mode 100644
index 0000000..bf87fc5
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2017 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.
+//
+//
+
+android_app {
+    name: "StatsdLoadtest",
+    platform_apis: true,
+
+    srcs: ["src/**/*.java"],
+
+    resource_dirs: ["res"],
+    static_libs: [
+        "platformprotoslite",
+        "statsdprotolite",
+    ],
+
+    certificate: "platform",
+    privileged: true,
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk
deleted file mode 100644
index 219cd95..0000000
--- a/cmds/statsd/tools/loadtest/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2017 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.
-#
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := StatsdLoadtest
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \
-                               statsdprotolite
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 2d51038..e5764f0 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -2184,6 +2184,56 @@
 HPLandroid/graphics/drawable/VectorDrawable$VPath;->setPathData(Landroid/util/PathParser$PathData;)V
 HPLandroid/graphics/drawable/VectorDrawable$VectorDrawableState;->getProperty(Ljava/lang/String;)Landroid/util/Property;
 HPLandroid/graphics/drawable/VectorDrawable;->getOpticalInsets()Landroid/graphics/Insets;
+HPLandroid/graphics/text/LineBreaker;-><init>(III[ILandroid/graphics/text/LineBreaker$1;)V
+HPLandroid/graphics/text/LineBreaker;->computeLineBreaks(Landroid/graphics/text/MeasuredText;Landroid/graphics/text/LineBreaker$ParagraphConstraints;I)Landroid/graphics/text/LineBreaker$Result;
+HPLandroid/graphics/text/LineBreaker;->access$100()J
+HPLandroid/graphics/text/LineBreaker;->access$200(J)I
+HPLandroid/graphics/text/LineBreaker;->access$300(JI)I
+HPLandroid/graphics/text/LineBreaker;->access$400(JI)F
+HPLandroid/graphics/text/LineBreaker;->access$500(JI)F
+HPLandroid/graphics/text/LineBreaker;->access$600(JI)F
+HPLandroid/graphics/text/LineBreaker;->access$700(JI)I
+HPLandroid/graphics/text/LineBreaker$Builder;-><init>()V
+HPLandroid/graphics/text/LineBreaker$Builder;->setBreakStrategy(I)Landroid/graphics/text/LineBreaker$Builder;
+HPLandroid/graphics/text/LineBreaker$Builder;->setHyphenationFrequency(I)Landroid/graphics/text/LineBreaker$Builder;
+HPLandroid/graphics/text/LineBreaker$Builder;->setJustified(I)Landroid/graphics/text/LineBreaker$Builder;
+HPLandroid/graphics/text/LineBreaker$Builder;->setIndents([I)Landroid/graphics/text/LineBreaker$Builder;
+HPLandroid/graphics/text/LineBreaker$Builder;->build()Landroid/graphics/text/LineBreaker;
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;-><init>()V
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->setWidth(F)V
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->setIndent(FI)V
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->setTabStops([II)V
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->getWidth()F
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->getFirstWidth()F
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->getFirstWidthLineCount()I
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->getTabStops()[I
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->getDefaultTabStop()I
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->access$800(Landroid/graphics/text/LineBreaker$ParagraphConstraints;)F
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->access$900(Landroid/graphics/text/LineBreaker$ParagraphConstraints;)I
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->access$1000(Landroid/graphics/text/LineBreaker$ParagraphConstraints;)F
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->access$1100(Landroid/graphics/text/LineBreaker$ParagraphConstraints;)[I
+HPLandroid/graphics/text/LineBreaker$ParagraphConstraints;->access$1200(Landroid/graphics/text/LineBreaker$ParagraphConstraints;)I
+HPLandroid/graphics/text/LineBreaker$Result;-><init>(JLandroid/graphics/text/LineBreaker$1;)V
+HPLandroid/graphics/text/LineBreaker$Result;->getLineCount()I
+HPLandroid/graphics/text/LineBreaker$Result;->getLineBreakOffset(I)I
+HPLandroid/graphics/text/LineBreaker$Result;->getLineWidth(I)F
+HPLandroid/graphics/text/LineBreaker$Result;->getLineAscent(I)F
+HPLandroid/graphics/text/LineBreaker$Result;->getLineDescent(I)F
+HPLandroid/graphics/text/LineBreaker$Result;->hasLineTab(I)Z
+HPLandroid/graphics/text/LineBreaker$Result;->getLineHyphenEdit(I)I
+HPLandroid/graphics/text/MeasuredText;-><init>(J[CLandroid/graphics/text/MeasuredText$1;)V
+HPLandroid/graphics/text/MeasuredText;->getChars()[C
+HPLandroid/graphics/text/MeasuredText;->getWidth(II)F
+HPLandroid/graphics/text/MeasuredText;->getMemoryUsage()I
+HPLandroid/graphics/text/MeasuredText;->getBounds(IILandroid/graphics/Rect;)V
+HPLandroid/graphics/text/MeasuredText;->getCharWidthAt(I)F
+HPLandroid/graphics/text/MeasuredText;->getNativePtr()J
+HPLandroid/graphics/text/MeasuredText$Builder;-><init>([C)V
+HPLandroid/graphics/text/MeasuredText$Builder;->appendStyleRun(Landroid/graphics/Paint;IZ)Landroid/graphics/text/MeasuredText$Builder;
+HPLandroid/graphics/text/MeasuredText$Builder;->appendReplacementRun(Landroid/graphics/Paint;IF)Landroid/graphics/text/MeasuredText$Builder;
+HPLandroid/graphics/text/MeasuredText$Builder;->setComputeHyphenation(Z)Landroid/graphics/text/MeasuredText$Builder;
+HPLandroid/graphics/text/MeasuredText$Builder;->setComputeLayout(Z)Landroid/graphics/text/MeasuredText$Builder;
+HPLandroid/graphics/text/MeasuredText$Builder;->build()Landroid/graphics/text/MeasuredText;
 HPLandroid/hardware/Camera$AutoFocusCallback;->onAutoFocus(ZLandroid/hardware/Camera;)V
 HPLandroid/hardware/Camera$EventHandler;->handleMessage(Landroid/os/Message;)V
 HPLandroid/hardware/Camera$Parameters;->getFlashMode()Ljava/lang/String;
@@ -13145,7 +13195,7 @@
 HSPLandroid/app/ActivityManagerInternal;->notifyAppTransitionFinished()V
 HSPLandroid/app/ActivityManagerInternal;->notifyAppTransitionStarting(Landroid/util/SparseIntArray;J)V
 HSPLandroid/app/ActivityManagerInternal;->notifyDockedStackMinimizedChanged(Z)V
-HSPLandroid/app/ActivityManagerInternal;->notifyKeyguardFlagsChanged(Ljava/lang/Runnable;)V
+HSPLandroid/app/ActivityManagerInternal;->notifyKeyguardFlagsChanged(Ljava/lang/Runnable;I)V
 HSPLandroid/app/ActivityManagerInternal;->notifyKeyguardTrustedChanged()V
 HSPLandroid/app/ActivityManagerInternal;->notifyNetworkPolicyRulesUpdated(IJ)V
 HSPLandroid/app/ActivityManagerInternal;->onLocalVoiceInteractionStarted(Landroid/os/IBinder;Landroid/service/voice/IVoiceInteractionSession;Lcom/android/internal/app/IVoiceInteractor;)V
@@ -24163,7 +24213,90 @@
 HSPLandroid/icu/util/CharsTrie;->next(I)Landroid/icu/util/BytesTrie$Result;
 HSPLandroid/icu/util/CharsTrie;->nextImpl(II)Landroid/icu/util/BytesTrie$Result;
 HSPLandroid/icu/util/CharsTrie;->readValue(Ljava/lang/CharSequence;II)I
-HSPLandroid/icu/util/Currency$1;-><init>()V
+HSPLandroid/icu/util/CodePointTrie$Small8;->fromBinary(Ljava/nio/ByteBuffer;)Landroid/icu/util/CodePointTrie$Small8;
+HSPLandroid/icu/util/CodePointTrie$Small32;->fromBinary(Ljava/nio/ByteBuffer;)Landroid/icu/util/CodePointTrie$Small32;
+HSPLandroid/icu/util/CodePointTrie$Small16;->fromBinary(Ljava/nio/ByteBuffer;)Landroid/icu/util/CodePointTrie$Small16;
+HSPLandroid/icu/util/CodePointTrie$Fast8;->fromBinary(Ljava/nio/ByteBuffer;)Landroid/icu/util/CodePointTrie$Fast8;
+HSPLandroid/icu/util/CodePointTrie$Fast8;->bmpGet(I)I
+HSPLandroid/icu/util/CodePointTrie$Fast8;->get(I)I
+HSPLandroid/icu/util/CodePointTrie$Fast8;->suppGet(I)I
+HSPLandroid/icu/util/CodePointTrie$Fast32;->fromBinary(Ljava/nio/ByteBuffer;)Landroid/icu/util/CodePointTrie$Fast32;
+HSPLandroid/icu/util/CodePointTrie$Fast32;->bmpGet(I)I
+HSPLandroid/icu/util/CodePointTrie$Fast32;->get(I)I
+HSPLandroid/icu/util/CodePointTrie$Fast32;->suppGet(I)I
+HSPLandroid/icu/util/CodePointTrie$Fast16;->fromBinary(Ljava/nio/ByteBuffer;)Landroid/icu/util/CodePointTrie$Fast16;
+HSPLandroid/icu/util/CodePointTrie$Fast16;->bmpGet(I)I
+HSPLandroid/icu/util/CodePointTrie$Fast16;->get(I)I
+HSPLandroid/icu/util/CodePointTrie$Fast16;->suppGet(I)I
+HSPLandroid/icu/util/CodePointTrie$Small;->fromBinary(Landroid/icu/util/CodePointTrie$ValueWidth;Ljava/nio/ByteBuffer;)Landroid/icu/util/CodePointTrie$Small;
+HSPLandroid/icu/util/CodePointTrie$Small;->cpIndex(I)I
+HSPLandroid/icu/util/CodePointTrie$Small;->getType()Landroid/icu/util/CodePointTrie$Type;
+HSPLandroid/icu/util/CodePointTrie$Small;->stringIterator(Ljava/lang/CharSequence;I)Landroid/icu/util/CodePointMap$StringIterator;
+HSPLandroid/icu/util/CodePointTrie$Fast;->fromBinary(Landroid/icu/util/CodePointTrie$ValueWidth;Ljava/nio/ByteBuffer;)Landroid/icu/util/CodePointTrie$Fast;
+HSPLandroid/icu/util/CodePointTrie$Fast;->bmpGet(I)I
+HSPLandroid/icu/util/CodePointTrie$Fast;->cpIndex(I)I
+HSPLandroid/icu/util/CodePointTrie$Fast;->getType()Landroid/icu/util/CodePointTrie$Type;
+HSPLandroid/icu/util/CodePointTrie$Fast;->stringIterator(Ljava/lang/CharSequence;I)Landroid/icu/util/CodePointMap$StringIterator;
+HSPLandroid/icu/util/CodePointTrie$Fast;->suppGet(I)I
+HSPLandroid/icu/util/CodePointTrie$Data8;->getDataLength()I
+HSPLandroid/icu/util/CodePointTrie$Data8;->getFromIndex(I)I
+HSPLandroid/icu/util/CodePointTrie$Data8;->getValueWidth()Landroid/icu/util/CodePointTrie$ValueWidth;
+HSPLandroid/icu/util/CodePointTrie$Data8;->write(Ljava/io/DataOutputStream;)I
+HSPLandroid/icu/util/CodePointTrie$Data32;->getDataLength()I
+HSPLandroid/icu/util/CodePointTrie$Data32;->getFromIndex(I)I
+HSPLandroid/icu/util/CodePointTrie$Data32;->getValueWidth()Landroid/icu/util/CodePointTrie$ValueWidth;
+HSPLandroid/icu/util/CodePointTrie$Data32;->write(Ljava/io/DataOutputStream;)I
+HSPLandroid/icu/util/CodePointTrie$Data16;->getDataLength()I
+HSPLandroid/icu/util/CodePointTrie$Data16;->getFromIndex(I)I
+HSPLandroid/icu/util/CodePointTrie$Data16;->getValueWidth()Landroid/icu/util/CodePointTrie$ValueWidth;
+HSPLandroid/icu/util/CodePointTrie$Data16;->write(Ljava/io/DataOutputStream;)I
+HSPLandroid/icu/util/CodePointTrie$Data;->getDataLength()I
+HSPLandroid/icu/util/CodePointTrie$Data;->getFromIndex(I)I
+HSPLandroid/icu/util/CodePointTrie$Data;->getValueWidth()Landroid/icu/util/CodePointTrie$ValueWidth;
+HSPLandroid/icu/util/CodePointTrie$Data;->write(Ljava/io/DataOutputStream;)I
+HSPLandroid/icu/util/CodePointTrie$ValueWidth;->valueOf(Ljava/lang/String;)Landroid/icu/util/CodePointTrie$ValueWidth;
+HSPLandroid/icu/util/CodePointTrie$ValueWidth;->values()[Landroid/icu/util/CodePointTrie$ValueWidth;
+HSPLandroid/icu/util/CodePointTrie$Type;->valueOf(Ljava/lang/String;)Landroid/icu/util/CodePointTrie$Type;
+HSPLandroid/icu/util/CodePointTrie$Type;->values()[Landroid/icu/util/CodePointTrie$Type;
+HSPLandroid/icu/util/CodePointTrie;->fromBinary(Landroid/icu/util/CodePointTrie$Type;Landroid/icu/util/CodePointTrie$ValueWidth;Ljava/nio/ByteBuffer;)Landroid/icu/util/CodePointTrie;
+HSPLandroid/icu/util/CodePointTrie;->internalSmallIndex(Landroid/icu/util/CodePointTrie$Type;I)I
+HSPLandroid/icu/util/CodePointTrie;->maybeFilterValue(IIILandroid/icu/util/CodePointMap$ValueFilter;)I
+HSPLandroid/icu/util/CodePointTrie;->asciiGet(I)I
+HSPLandroid/icu/util/CodePointTrie;->cpIndex(I)I
+HSPLandroid/icu/util/CodePointTrie;->fastIndex(I)I
+HSPLandroid/icu/util/CodePointTrie;->get(I)I
+HSPLandroid/icu/util/CodePointTrie;->getRange(ILandroid/icu/util/CodePointMap$ValueFilter;Landroid/icu/util/CodePointMap$Range;)Z
+HSPLandroid/icu/util/CodePointTrie;->getType()Landroid/icu/util/CodePointTrie$Type;
+HSPLandroid/icu/util/CodePointTrie;->getValueWidth()Landroid/icu/util/CodePointTrie$ValueWidth;
+HSPLandroid/icu/util/CodePointTrie;->smallIndex(Landroid/icu/util/CodePointTrie$Type;I)I
+HSPLandroid/icu/util/CodePointTrie;->toBinary(Ljava/io/OutputStream;)I
+HSPLandroid/icu/util/CodePointMap$StringIterator;->getCodePoint()I
+HSPLandroid/icu/util/CodePointMap$StringIterator;->getIndex()I
+HSPLandroid/icu/util/CodePointMap$StringIterator;->getValue()I
+HSPLandroid/icu/util/CodePointMap$StringIterator;->next()Z
+HSPLandroid/icu/util/CodePointMap$StringIterator;->previous()Z
+HSPLandroid/icu/util/CodePointMap$StringIterator;->reset(Ljava/lang/CharSequence;I)V
+HSPLandroid/icu/util/CodePointMap$RangeIterator;->hasNext()Z
+HSPLandroid/icu/util/CodePointMap$RangeIterator;->next()Landroid/icu/util/CodePointMap$Range;
+HSPLandroid/icu/util/CodePointMap$RangeIterator;->next()Ljava/lang/Object;
+HSPLandroid/icu/util/CodePointMap$RangeIterator;->remove()V
+HSPLandroid/icu/util/CodePointMap$Range;->access$000(Landroid/icu/util/CodePointMap$Range;)I
+HSPLandroid/icu/util/CodePointMap$Range;->access$002(Landroid/icu/util/CodePointMap$Range;I)I
+HSPLandroid/icu/util/CodePointMap$Range;->access$100(Landroid/icu/util/CodePointMap$Range;)I
+HSPLandroid/icu/util/CodePointMap$Range;->access$102(Landroid/icu/util/CodePointMap$Range;I)I
+HSPLandroid/icu/util/CodePointMap$Range;->access$202(Landroid/icu/util/CodePointMap$Range;I)I
+HSPLandroid/icu/util/CodePointMap$Range;->getEnd()I
+HSPLandroid/icu/util/CodePointMap$Range;->getStart()I
+HSPLandroid/icu/util/CodePointMap$Range;->getValue()I
+HSPLandroid/icu/util/CodePointMap$Range;->set(III)V
+HSPLandroid/icu/util/CodePointMap$ValueFilter;->apply(I)I
+HSPLandroid/icu/util/CodePointMap$RangeOption;->valueOf(Ljava/lang/String;)Landroid/icu/util/CodePointMap$RangeOption;
+HSPLandroid/icu/util/CodePointMap$RangeOption;->values()[Landroid/icu/util/CodePointMap$RangeOption;
+HSPLandroid/icu/util/CodePointMap;->get(I)I
+HSPLandroid/icu/util/CodePointMap;->getRange(ILandroid/icu/util/CodePointMap$RangeOption;ILandroid/icu/util/CodePointMap$ValueFilter;Landroid/icu/util/CodePointMap$Range;)Z
+HSPLandroid/icu/util/CodePointMap;->getRange(ILandroid/icu/util/CodePointMap$ValueFilter;Landroid/icu/util/CodePointMap$Range;)Z
+HSPLandroid/icu/util/CodePointMap;->iterator()Ljava/util/Iterator;
+HSPLandroid/icu/util/CodePointMap;->stringIterator(Ljava/lang/CharSequence;I)Landroid/icu/util/CodePointMap$StringIterator;HSPLandroid/icu/util/Currency$1;-><init>()V
 HSPLandroid/icu/util/Currency$1;->createInstance(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
 HSPLandroid/icu/util/Currency$1;->createInstance(Ljava/lang/String;Ljava/lang/Void;)Landroid/icu/util/Currency;
 HSPLandroid/icu/util/Currency$CurrencyUsage;-><init>(Ljava/lang/String;I)V
@@ -24209,6 +24342,61 @@
 HSPLandroid/icu/util/MeasureUnit;->equals(Ljava/lang/Object;)Z
 HSPLandroid/icu/util/MeasureUnit;->hashCode()I
 HSPLandroid/icu/util/MeasureUnit;->internalGetInstance(Ljava/lang/String;Ljava/lang/String;)Landroid/icu/util/MeasureUnit;
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->addEntry([I[CIII)V
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->findEntry([III)I
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->findEntry([I[C[I[CII)I
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->makeHashCode(I)I
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->makeHashCode([CI)I
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->makeHashCode([II)I
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->modulo(II)I
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->nextIndex(II)I
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->extend([CIII)V
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->extend([IIII)V
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->findAllSameBlock([II)I
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->findBlock([C[CI)I
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->findBlock([C[II)I
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->findBlock([I[II)I
+HSPLandroid/icu/util/MutableCodePointTrie$MixedBlocks;->init(II)V
+HSPLandroid/icu/util/MutableCodePointTrie$AllSameBlocks;->add(III)V
+HSPLandroid/icu/util/MutableCodePointTrie$AllSameBlocks;->findMostUsed()I
+HSPLandroid/icu/util/MutableCodePointTrie$AllSameBlocks;->findOrAdd(III)I
+HSPLandroid/icu/util/MutableCodePointTrie;->access$000([II[III)Z
+HSPLandroid/icu/util/MutableCodePointTrie;->access$100([CI[III)Z
+HSPLandroid/icu/util/MutableCodePointTrie;->access$200([CI[CII)Z
+HSPLandroid/icu/util/MutableCodePointTrie;->access$300([IIII)Z
+HSPLandroid/icu/util/MutableCodePointTrie;->allValuesSameAs([IIII)Z
+HSPLandroid/icu/util/MutableCodePointTrie;->allocDataBlock(I)I
+HSPLandroid/icu/util/MutableCodePointTrie;->build(Landroid/icu/util/CodePointTrie$Type;Landroid/icu/util/CodePointTrie$ValueWidth;)Landroid/icu/util/CodePointTrie;
+HSPLandroid/icu/util/MutableCodePointTrie;->clear()V
+HSPLandroid/icu/util/MutableCodePointTrie;->compactData(I[IILandroid/icu/util/MutableCodePointTrie$MixedBlocks;)I
+HSPLandroid/icu/util/MutableCodePointTrie;->compactIndex(ILandroid/icu/util/MutableCodePointTrie$MixedBlocks;)I
+HSPLandroid/icu/util/MutableCodePointTrie;->compactTrie(I)I
+HSPLandroid/icu/util/MutableCodePointTrie;->compactWholeDataBlocks(ILandroid/icu/util/MutableCodePointTrie$AllSameBlocks;)I
+HSPLandroid/icu/util/MutableCodePointTrie;->ensureHighStart(I)V
+HSPLandroid/icu/util/MutableCodePointTrie;->equalBlocks([CI[CII)Z
+HSPLandroid/icu/util/MutableCodePointTrie;->equalBlocks([CI[III)Z
+HSPLandroid/icu/util/MutableCodePointTrie;->equalBlocks([II[III)Z
+HSPLandroid/icu/util/MutableCodePointTrie;->fillBlock(IIII)V
+HSPLandroid/icu/util/MutableCodePointTrie;->findAllSameBlock([IIIII)I
+HSPLandroid/icu/util/MutableCodePointTrie;->findHighStart()I
+HSPLandroid/icu/util/MutableCodePointTrie;->findSameBlock([CII[CII)I
+HSPLandroid/icu/util/MutableCodePointTrie;->fromCodePointMap(Landroid/icu/util/CodePointMap;)Landroid/icu/util/MutableCodePointTrie;
+HSPLandroid/icu/util/MutableCodePointTrie;->getAllSameOverlap([IIII)I
+HSPLandroid/icu/util/MutableCodePointTrie;->getDataBlock(I)I
+HSPLandroid/icu/util/MutableCodePointTrie;->getOverlap([CI[CII)I
+HSPLandroid/icu/util/MutableCodePointTrie;->getOverlap([CI[III)I
+HSPLandroid/icu/util/MutableCodePointTrie;->getOverlap([II[III)I
+HSPLandroid/icu/util/MutableCodePointTrie;->isStartOfSomeFastBlock(I[II)Z
+HSPLandroid/icu/util/MutableCodePointTrie;->maskValues(I)V
+HSPLandroid/icu/util/MutableCodePointTrie;->maybeFilterValue(IIILandroid/icu/util/CodePointMap$ValueFilter;)I
+HSPLandroid/icu/util/MutableCodePointTrie;->writeBlock(II)V
+HSPLandroid/icu/util/MutableCodePointTrie;->buildImmutable(Landroid/icu/util/CodePointTrie$Type;Landroid/icu/util/CodePointTrie$ValueWidth;)Landroid/icu/util/CodePointTrie;
+HSPLandroid/icu/util/MutableCodePointTrie;->clone()Landroid/icu/util/MutableCodePointTrie;
+HSPLandroid/icu/util/MutableCodePointTrie;->clone()Ljava/lang/Object;
+HSPLandroid/icu/util/MutableCodePointTrie;->get(I)I
+HSPLandroid/icu/util/MutableCodePointTrie;->getRange(ILandroid/icu/util/CodePointMap$ValueFilter;Landroid/icu/util/CodePointMap$Range;)Z
+HSPLandroid/icu/util/MutableCodePointTrie;->set(II)V
+HSPLandroid/icu/util/MutableCodePointTrie;->setRange(III)V
 HSPLandroid/icu/util/SimpleTimeZone;-><init>(ILjava/lang/String;IIIIIIIIIII)V
 HSPLandroid/icu/util/SimpleTimeZone;->clone()Ljava/lang/Object;
 HSPLandroid/icu/util/SimpleTimeZone;->cloneAsThawed()Landroid/icu/util/TimeZone;
@@ -31250,9 +31438,10 @@
 HSPLandroid/text/Layout;->setJustificationMode(I)V
 HSPLandroid/text/Layout;->shouldClampCursor(I)Z
 HSPLandroid/text/MeasuredParagraph;-><init>()V
-HSPLandroid/text/MeasuredParagraph;->applyMetricsAffectingSpan(Landroid/text/TextPaint;[Landroid/text/style/MetricAffectingSpan;IIJ)V
-HSPLandroid/text/MeasuredParagraph;->applyReplacementRun(Landroid/text/style/ReplacementSpan;IIJ)V
-HSPLandroid/text/MeasuredParagraph;->applyStyleRun(IIJ)V
+HPLandroid/text/MeasuredParagraph;->getMeasuredText()Landroid/graphics/text/MeasuredText;
+HSPLandroid/text/MeasuredParagraph;->applyMetricsAffectingSpan(Landroid/text/TextPaint;[Landroid/text/style/MetricAffectingSpan;IILandroid/graphics/text/MeasuredText$Builder;)V
+HSPLandroid/text/MeasuredParagraph;->applyReplacementRun(Landroid/text/style/ReplacementSpan;IILandroid/graphics/text/MeasuredText$Builder;)V
+HSPLandroid/text/MeasuredParagraph;->applyStyleRun(IILandroid/graphics/text/MeasuredText$Builder;)V
 HSPLandroid/text/MeasuredParagraph;->breakText(IZF)I
 HSPLandroid/text/MeasuredParagraph;->buildForBidi(Ljava/lang/CharSequence;IILandroid/text/TextDirectionHeuristic;Landroid/text/MeasuredParagraph;)Landroid/text/MeasuredParagraph;
 HSPLandroid/text/MeasuredParagraph;->buildForMeasurement(Landroid/text/TextPaint;Ljava/lang/CharSequence;IILandroid/text/TextDirectionHeuristic;Landroid/text/MeasuredParagraph;)Landroid/text/MeasuredParagraph;
@@ -31287,6 +31476,7 @@
 HSPLandroid/text/PackedObjectVector;->setValue(IILjava/lang/Object;)V
 HSPLandroid/text/PackedObjectVector;->size()I
 HSPLandroid/text/PrecomputedText$Params;-><init>(Landroid/text/TextPaint;Landroid/text/TextDirectionHeuristic;II)V
+HPLandroid/text/PrecomputedText;->getCharWidthAt(I)F
 HSPLandroid/text/PrecomputedText;->createMeasuredParagraphs(Ljava/lang/CharSequence;Landroid/text/PrecomputedText$Params;IIZ)[Landroid/text/PrecomputedText$ParagraphInfo;
 HSPLandroid/text/Selection$END;-><init>(Landroid/text/Selection$1;)V
 HSPLandroid/text/Selection$MEMORY;-><init>(Landroid/text/Selection$1;)V
@@ -32750,15 +32940,8 @@
 HSPLandroid/view/IWindowManager;->isWindowTraceEnabled()Z
 HSPLandroid/view/IWindowManager;->lockNow(Landroid/os/Bundle;)V
 HSPLandroid/view/IWindowManager;->openSession(Landroid/view/IWindowSessionCallback;Lcom/android/internal/view/IInputMethodClient;Lcom/android/internal/view/IInputContext;)Landroid/view/IWindowSession;
-HSPLandroid/view/IWindowManager;->overridePendingAppTransition(Ljava/lang/String;IILandroid/os/IRemoteCallback;)V
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionAspectScaledThumb(Landroid/graphics/GraphicBuffer;IIIILandroid/os/IRemoteCallback;Z)V
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionClipReveal(IIII)V
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionInPlace(Ljava/lang/String;I)V
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionMultiThumb([Landroid/view/AppTransitionAnimationSpec;Landroid/os/IRemoteCallback;Landroid/os/IRemoteCallback;Z)V
 HSPLandroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z)V
 HSPLandroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionScaleUp(IIII)V
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionThumb(Landroid/graphics/GraphicBuffer;IILandroid/os/IRemoteCallback;Z)V
 HSPLandroid/view/IWindowManager;->prepareAppTransition(IZ)V
 HSPLandroid/view/IWindowManager;->reenableKeyguard(Landroid/os/IBinder;)V
 HSPLandroid/view/IWindowManager;->refreshScreenCaptureDisabled(I)V
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index bbc3f35..0d9a393 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1,4 +1,6 @@
 Landroid/accessibilityservice/IAccessibilityServiceConnection$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accessibilityservice/IAccessibilityServiceConnection;
+Landroid/accounts/AccountManager$AmsTask;-><init>(Landroid/accounts/AccountManager;Landroid/app/Activity;Landroid/os/Handler;Landroid/accounts/AccountManagerCallback;)V
+Landroid/accounts/AccountManager$Future2Task;-><init>(Landroid/accounts/AccountManager;Landroid/os/Handler;Landroid/accounts/AccountManagerCallback;)V
 Landroid/accounts/IAccountAuthenticator$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/accounts/IAccountAuthenticator$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/accounts/IAccountAuthenticator$Stub;-><init>()V
@@ -30,6 +32,7 @@
 Landroid/app/ActivityManagerNative;-><init>()V
 Landroid/app/ActivityThread$AppBindData;-><init>()V
 Landroid/app/ActivityThread$CreateServiceData;-><init>()V
+Landroid/app/ActivityThread$H;-><init>(Landroid/app/ActivityThread;)V
 Landroid/app/admin/IDevicePolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/admin/IDevicePolicyManager;
 Landroid/app/admin/IDevicePolicyManager$Stub;->TRANSACTION_packageHasActiveAdmins:I
 Landroid/app/admin/IDevicePolicyManager$Stub;->TRANSACTION_removeActiveAdmin:I
@@ -212,6 +215,7 @@
 Landroid/app/job/IJobService;->stopJob(Landroid/app/job/JobParameters;)V
 Landroid/app/PackageDeleteObserver;-><init>()V
 Landroid/app/PackageInstallObserver;-><init>()V
+Landroid/app/ReceiverRestrictedContext;-><init>(Landroid/content/Context;)V
 Landroid/app/ResourcesManager$ActivityResources;-><init>()V
 Landroid/app/ResourcesManager;-><init>()V
 Landroid/app/TaskStackListener;-><init>()V
@@ -262,6 +266,7 @@
 Landroid/bluetooth/IBluetoothManagerCallback$Stub;-><init>()V
 Landroid/bluetooth/IBluetoothPbap$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothPbap;
 Landroid/bluetooth/IBluetoothStateChangeCallback$Stub;-><init>()V
+Landroid/content/ContentProviderProxy;->mRemote:Landroid/os/IBinder;
 Landroid/content/IClipboard$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/IClipboard$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IClipboard;
 Landroid/content/IContentService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -415,9 +420,14 @@
 Landroid/content/res/ConfigurationBoundResourceCache;-><init>()V
 Landroid/content/res/DrawableCache;-><init>()V
 Landroid/content/UndoManager;-><init>()V
+Landroid/database/BulkCursorProxy;->mRemote:Landroid/os/IBinder;
 Landroid/database/IContentObserver$Stub;-><init>()V
 Landroid/database/IContentObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/database/IContentObserver;
 Landroid/database/IContentObserver;->onChange(ZLandroid/net/Uri;I)V
+Landroid/database/sqlite/SQLiteConnectionPool;->$assertionsDisabled:Z
+Landroid/database/sqlite/SQLiteDatabase;->$assertionsDisabled:Z
+Landroid/filterfw/GraphEnvironment;->addReferences([Ljava/lang/Object;)V
+Landroid/hardware/camera2/utils/HashCodeHelpers;->hashCode([I)I
 Landroid/hardware/display/IDisplayManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/display/IDisplayManager;
 Landroid/hardware/display/IDisplayManager;->getDisplayInfo(I)Landroid/view/DisplayInfo;
 Landroid/hardware/fingerprint/IFingerprintService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -432,6 +442,9 @@
 Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager;
 Landroid/icu/impl/CurrencyData;-><init>()V
+Landroid/icu/impl/ICUResourceBundle;->getULocale()Landroid/icu/util/ULocale;
+Landroid/icu/impl/ICUResourceBundle;->getWithFallback(Ljava/lang/String;)Landroid/icu/impl/ICUResourceBundle;
+Landroid/icu/impl/IllegalIcuArgumentException;-><init>(Ljava/lang/String;)V
 Landroid/icu/text/ArabicShaping;-><init>(I)V
 Landroid/icu/text/ArabicShaping;->isAlefMaksouraChar(C)Z
 Landroid/icu/text/ArabicShaping;->isSeenTailFamilyChar(C)I
@@ -462,6 +475,8 @@
 Landroid/icu/util/UResourceBundle;->getType()I
 Landroid/icu/util/UResourceBundleIterator;->hasNext()Z
 Landroid/icu/util/UResourceBundleIterator;->next()Landroid/icu/util/UResourceBundle;
+Landroid/inputmethodservice/IInputMethodSessionWrapper;->mCaller:Lcom/android/internal/os/HandlerCaller;
+Landroid/inputmethodservice/IInputMethodWrapper;->mCaller:Lcom/android/internal/os/HandlerCaller;
 Landroid/location/ICountryDetector$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ICountryDetector;
 Landroid/location/ICountryListener$Stub;-><init>()V
 Landroid/location/IGeocodeProvider$Stub;-><init>()V
@@ -479,9 +494,11 @@
 Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager;
 Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I
 Landroid/location/ILocationManager;->getAllProviders()Ljava/util/List;
+Landroid/location/LocationManager$ListenerTransport;-><init>(Landroid/location/LocationManager;Landroid/location/LocationListener;Landroid/os/Looper;)V
 Landroid/Manifest$permission;->CAPTURE_SECURE_VIDEO_OUTPUT:Ljava/lang/String;
 Landroid/Manifest$permission;->CAPTURE_VIDEO_OUTPUT:Ljava/lang/String;
 Landroid/Manifest$permission;->READ_FRAME_BUFFER:Ljava/lang/String;
+Landroid/media/effect/SingleFilterEffect;-><init>(Landroid/media/effect/EffectContext;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/media/IAudioFocusDispatcher;->dispatchAudioFocusChange(ILjava/lang/String;)V
 Landroid/media/IAudioRoutesObserver$Stub;-><init>()V
 Landroid/media/IAudioService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -500,6 +517,7 @@
 Landroid/media/IRingtonePlayer;->play(Landroid/os/IBinder;Landroid/net/Uri;Landroid/media/AudioAttributes;FZ)V
 Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController;
 Landroid/media/MediaFile;-><init>()V
+Landroid/media/MediaScanner$MyMediaScannerClient;-><init>(Landroid/media/MediaScanner;)V
 Landroid/media/projection/IMediaProjectionManager;->hasProjectionPermission(ILjava/lang/String;)Z
 Landroid/media/session/ISessionManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/session/ISessionManager;
 Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -547,6 +565,7 @@
 Landroid/net/MobileLinkQualityInfo;-><init>()V
 Landroid/net/nsd/INsdManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/nsd/INsdManager;
 Landroid/net/nsd/INsdManager;->getMessenger()Landroid/os/Messenger;
+Landroid/net/sip/ISipSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/sip/ISipSession;
 Landroid/net/SntpClient;-><init>()V
 Landroid/net/wifi/IWifiManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/net/wifi/IWifiManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/wifi/IWifiManager;
@@ -637,6 +656,7 @@
 Landroid/os/BatteryStats;->computeBatteryTimeRemaining(J)J
 Landroid/os/BatteryStats;->computeBatteryUptime(JI)J
 Landroid/os/BatteryStats;->computeChargeTimeRemaining(J)J
+Landroid/os/BatteryStats;->dumpLine(Ljava/io/PrintWriter;ILjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/os/BatteryStats;->getBatteryUptime(J)J
 Landroid/os/BatteryStats;->getGlobalWifiRunningTime(JI)J
 Landroid/os/BatteryStats;->getMobileRadioActiveTime(JI)J
@@ -725,6 +745,7 @@
 Landroid/os/Environment;->buildExternalStorageAppFilesDirs(Ljava/lang/String;)[Ljava/io/File;
 Landroid/os/Environment;->buildExternalStorageAppMediaDirs(Ljava/lang/String;)[Ljava/io/File;
 Landroid/os/Environment;->buildExternalStorageAppObbDirs(Ljava/lang/String;)[Ljava/io/File;
+Landroid/os/Environment;->buildPaths([Ljava/io/File;[Ljava/lang/String;)[Ljava/io/File;
 Landroid/os/Environment;->getDataSystemDirectory()Ljava/io/File;
 Landroid/os/Environment;->getLegacyExternalStorageObbDirectory()Ljava/io/File;
 Landroid/os/Environment;->initForCurrentUser()V
@@ -930,6 +951,8 @@
 Landroid/os/ServiceManager;->sCache:Ljava/util/Map;
 Landroid/os/ServiceManager;->sServiceManager:Landroid/os/IServiceManager;
 Landroid/os/ServiceManagerNative;->asInterface(Landroid/os/IBinder;)Landroid/os/IServiceManager;
+Landroid/os/ServiceManagerProxy;->getService(Ljava/lang/String;)Landroid/os/IBinder;
+Landroid/os/ServiceManagerProxy;->mRemote:Landroid/os/IBinder;
 Landroid/os/ServiceSpecificException;-><init>(ILjava/lang/String;)V
 Landroid/os/SharedMemory;->getFd()I
 Landroid/os/ShellCommand;->peekNextArg()Ljava/lang/String;
@@ -1064,6 +1087,7 @@
 Landroid/os/WorkSource;->updateLocked(Landroid/os/WorkSource;ZZ)Z
 Landroid/os/ZygoteStartFailedEx;-><init>(Ljava/lang/String;)V
 Landroid/os/ZygoteStartFailedEx;-><init>(Ljava/lang/Throwable;)V
+Landroid/preference/PreferenceGroupAdapter;->getItem(I)Landroid/preference/Preference;
 Landroid/R$styleable;->ActionBar:[I
 Landroid/R$styleable;->ActionBar_background:I
 Landroid/R$styleable;->ActionBar_backgroundSplit:I
@@ -1346,6 +1370,17 @@
 Landroid/security/IKeystoreService;->sign(Ljava/lang/String;[B)[B
 Landroid/security/IKeystoreService;->ungrant(Ljava/lang/String;I)I
 Landroid/security/IKeystoreService;->verify(Ljava/lang/String;[B[B)I
+Landroid/security/keymaster/KeymasterBlobArgument;-><init>(ILandroid/os/Parcel;)V
+Landroid/security/keymaster/KeymasterBlobArgument;-><init>(I[B)V
+Landroid/security/keymaster/KeymasterBlobArgument;->blob:[B
+Landroid/security/keymaster/KeymasterBooleanArgument;-><init>(ILandroid/os/Parcel;)V
+Landroid/security/keymaster/KeymasterDateArgument;-><init>(ILandroid/os/Parcel;)V
+Landroid/security/keymaster/KeymasterIntArgument;-><init>(II)V
+Landroid/security/keymaster/KeymasterIntArgument;-><init>(ILandroid/os/Parcel;)V
+Landroid/security/keymaster/KeymasterIntArgument;->value:I
+Landroid/security/keymaster/KeymasterLongArgument;-><init>(IJ)V
+Landroid/security/keymaster/KeymasterLongArgument;-><init>(ILandroid/os/Parcel;)V
+Landroid/security/keymaster/KeymasterLongArgument;->value:J
 Landroid/service/carrier/ICarrierMessagingCallback$Stub;-><init>()V
 Landroid/service/carrier/ICarrierMessagingService;->filterSms(Landroid/service/carrier/MessagePdu;Ljava/lang/String;IILandroid/service/carrier/ICarrierMessagingCallback;)V
 Landroid/service/dreams/IDreamManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/dreams/IDreamManager;
@@ -1383,9 +1418,42 @@
 Landroid/service/wallpaper/IWallpaperEngine;->setVisibility(Z)V
 Landroid/service/wallpaper/IWallpaperService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/wallpaper/IWallpaperService;
 Landroid/speech/IRecognitionListener;->onEvent(ILandroid/os/Bundle;)V
+Landroid/telecom/Log;->i(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->w(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/telephony/CarrierMessagingServiceManager;-><init>()V
+Landroid/telephony/JapanesePhoneNumberFormatter;->format(Landroid/text/Editable;)V
+Landroid/telephony/SmsCbCmasInfo;->getCategory()I
+Landroid/telephony/SmsCbCmasInfo;->getCertainty()I
+Landroid/telephony/SmsCbCmasInfo;->getMessageClass()I
+Landroid/telephony/SmsCbCmasInfo;->getResponseType()I
+Landroid/telephony/SmsCbCmasInfo;->getSeverity()I
+Landroid/telephony/SmsCbCmasInfo;->getUrgency()I
+Landroid/telephony/SmsCbEtwsInfo;->getWarningType()I
+Landroid/telephony/SmsCbLocation;-><init>(Ljava/lang/String;)V
+Landroid/telephony/SmsCbLocation;-><init>(Ljava/lang/String;II)V
+Landroid/telephony/SmsCbLocation;->getCid()I
+Landroid/telephony/SmsCbLocation;->getLac()I
+Landroid/telephony/SmsCbLocation;->getPlmn()Ljava/lang/String;
+Landroid/telephony/SmsCbMessage;-><init>(Landroid/os/Parcel;)V
+Landroid/telephony/SmsCbMessage;->getCmasWarningInfo()Landroid/telephony/SmsCbCmasInfo;
+Landroid/telephony/SmsCbMessage;->getEtwsWarningInfo()Landroid/telephony/SmsCbEtwsInfo;
+Landroid/telephony/SmsCbMessage;->getGeographicalScope()I
+Landroid/telephony/SmsCbMessage;->getLanguageCode()Ljava/lang/String;
+Landroid/telephony/SmsCbMessage;->getLocation()Landroid/telephony/SmsCbLocation;
+Landroid/telephony/SmsCbMessage;->getMessageBody()Ljava/lang/String;
+Landroid/telephony/SmsCbMessage;->getMessageFormat()I
+Landroid/telephony/SmsCbMessage;->getSerialNumber()I
+Landroid/telephony/SmsCbMessage;->getServiceCategory()I
+Landroid/telephony/SmsCbMessage;->isCmasMessage()Z
+Landroid/telephony/SmsCbMessage;->isEmergencyMessage()Z
 Landroid/telephony/TelephonyManager$MultiSimVariants;->values()[Landroid/telephony/TelephonyManager$MultiSimVariants;
+Landroid/test/AndroidTestCase;->getTestContext()Landroid/content/Context;
+Landroid/test/AndroidTestCase;->setTestContext(Landroid/content/Context;)V
+Landroid/test/InstrumentationTestCase;->runMethod(Ljava/lang/reflect/Method;I)V
+Landroid/test/RepetitiveTest;->numIterations()I
 Landroid/util/Singleton;-><init>()V
+Landroid/util/XmlPullAttributes;-><init>(Lorg/xmlpull/v1/XmlPullParser;)V
+Landroid/util/XmlPullAttributes;->mParser:Lorg/xmlpull/v1/XmlPullParser;
 Landroid/view/accessibility/IAccessibilityInteractionConnectionCallback;->setFindAccessibilityNodeInfoResult(Landroid/view/accessibility/AccessibilityNodeInfo;I)V
 Landroid/view/accessibility/IAccessibilityInteractionConnectionCallback;->setFindAccessibilityNodeInfosResult(Ljava/util/List;I)V
 Landroid/view/accessibility/IAccessibilityInteractionConnectionCallback;->setPerformAccessibilityActionResult(ZI)V
@@ -1418,7 +1486,6 @@
 Landroid/view/IWindowManager;->getDockedStackSide()I
 Landroid/view/IWindowManager;->getInitialDisplayDensity(I)I
 Landroid/view/IWindowManager;->getInitialDisplaySize(ILandroid/graphics/Point;)V
-Landroid/view/IWindowManager;->getPendingAppTransition()I
 Landroid/view/IWindowManager;->hasNavigationBar()Z
 Landroid/view/IWindowManager;->isKeyguardLocked()Z
 Landroid/view/IWindowManager;->isKeyguardSecure()Z
@@ -1444,7 +1511,9 @@
 Landroid/view/IWindowSession;->setTransparentRegion(Landroid/view/IWindow;Landroid/graphics/Region;)V
 Landroid/view/IWindowSession;->wallpaperCommandComplete(Landroid/os/IBinder;Landroid/os/Bundle;)V
 Landroid/view/IWindowSession;->wallpaperOffsetsComplete(Landroid/os/IBinder;)V
+Landroid/view/RenderNodeAnimator;->setDuration(J)Landroid/view/RenderNodeAnimator;
 Landroid/view/View$AttachInfo$InvalidateInfo;-><init>()V
+Landroid/view/View$CheckForLongPress;-><init>(Landroid/view/View;)V
 Landroid/view/View$ListenerInfo;-><init>()V
 Landroid/view/ViewTreeObserver$InternalInsetsInfo;-><init>()V
 Landroid/webkit/CacheManager$CacheResult;-><init>()V
@@ -1454,10 +1523,102 @@
 Landroid/webkit/IWebViewUpdateService;->getCurrentWebViewPackageName()Ljava/lang/String;
 Landroid/webkit/IWebViewUpdateService;->getValidWebViewPackages()[Landroid/webkit/WebViewProviderInfo;
 Landroid/webkit/IWebViewUpdateService;->isFallbackPackage(Ljava/lang/String;)Z
+Landroid/widget/DigitalClock$FormatChangeObserver;-><init>(Landroid/widget/DigitalClock;)V
+Landroid/widget/QuickContactBadge$QueryHandler;-><init>(Landroid/widget/QuickContactBadge;Landroid/content/ContentResolver;)V
 Landroid/widget/RelativeLayout$DependencyGraph$Node;-><init>()V
-Landroid/widget/RemoteViews$OnClickHandler;-><init>()V
 Landroid/widget/ScrollBarDrawable;-><init>()V
+Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->clear()V
+Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->getRememberedPosition()I
+Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->inputDigit(C)Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->inputDigitAndRememberPosition(C)Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;->getDescriptionForNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Ljava/util/Locale;)Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;->getInstance()Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;
+Lcom/android/i18n/phonenumbers/NumberParseException;->getErrorType()Lcom/android/i18n/phonenumbers/NumberParseException$ErrorType;
+Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getDomesticCarrierCodeFormattingRule()Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getFormat()Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getLeadingDigitsPattern(I)Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getNationalPrefixFormattingRule()Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getPattern()Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->leadingDigitsPatternSize()I
+Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getCountryCode()I
+Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getGeneralDesc()Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;
+Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getNationalPrefixForParsing()Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getNationalPrefixTransformRule()Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getPreferredExtnPrefix()Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->hasNationalPrefix()Z
+Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->hasPreferredExtnPrefix()Z
+Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->intlNumberFormats()Ljava/util/List;
+Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->numberFormats()Ljava/util/List;
+Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadataCollection;-><init>()V
+Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadataCollection;->getMetadataList()Ljava/util/List;
+Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;->getNationalNumberPattern()Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_DEFAULT_COUNTRY:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
+Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITHOUT_PLUS_SIGN:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
+Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITH_IDD:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
+Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITH_PLUS_SIGN:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
+Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->values()[Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
+Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->clearCountryCode()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
+Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getCountryCode()I
+Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getCountryCodeSource()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
+Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getExtension()Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getNationalNumber()J
+Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->hasCountryCode()Z
+Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->hasExtension()Z
+Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->end()I
+Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->number()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
+Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->rawString()Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->start()I
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;->POSSIBLE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->EXACT_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NOT_A_NUMBER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NO_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NSN_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->SHORT_NSN_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->E164:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->INTERNATIONAL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->NATIONAL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->RFC3966:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->FIXED_LINE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->FIXED_LINE_OR_MOBILE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->MOBILE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PAGER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PERSONAL_NUMBER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PREMIUM_RATE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->SHARED_COST:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->TOLL_FREE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->UAN:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->VOICEMAIL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->VOIP:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->IS_POSSIBLE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->TOO_LONG:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->findNumbers(Ljava/lang/CharSequence;Ljava/lang/String;Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;J)Ljava/lang/Iterable;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->format(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;)Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->formatInOriginalFormat(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Ljava/lang/String;)Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getAsYouTypeFormatter(Ljava/lang/String;)Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getCountryCodeForRegion(Ljava/lang/String;)I
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getInstance()Lcom/android/i18n/phonenumbers/PhoneNumberUtil;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getNationalSignificantNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getNumberType(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getRegionCodeForNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Ljava/lang/String;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isNumberMatch(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isPossibleNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Z
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isPossibleNumberWithReason(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
+Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isValidNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Z
+Lcom/android/ims/ImsCall;->deflect(Ljava/lang/String;)V
+Lcom/android/ims/ImsCall;->isMultiparty()Z
+Lcom/android/ims/ImsCall;->reject(I)V
+Lcom/android/ims/ImsCall;->terminate(I)V
 Lcom/android/ims/ImsConfigListener$Stub;-><init>()V
+Lcom/android/ims/ImsEcbm;->exitEmergencyCallbackMode()V
+Lcom/android/ims/ImsManager;->getConfigInterface()Lcom/android/ims/ImsConfig;
+Lcom/android/ims/ImsManager;->getInstance(Landroid/content/Context;I)Lcom/android/ims/ImsManager;
+Lcom/android/ims/ImsManager;->isEnhanced4gLteModeSettingEnabledByUser(Landroid/content/Context;)Z
+Lcom/android/ims/ImsManager;->isNonTtyOrTtyOnVolteEnabled(Landroid/content/Context;)Z
+Lcom/android/ims/ImsManager;->isVolteEnabledByPlatform(Landroid/content/Context;)Z
+Lcom/android/ims/ImsUtInterface;->queryCallForward(ILjava/lang/String;Landroid/os/Message;)V
 Lcom/android/ims/internal/IImsCallSession$Stub;-><init>()V
 Lcom/android/ims/internal/IImsCallSession$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsCallSession;
 Lcom/android/ims/internal/IImsConfig$Stub;-><init>()V
@@ -1475,7 +1636,15 @@
 Lcom/android/ims/internal/IImsVideoCallCallback;->receiveSessionModifyResponse(ILandroid/telecom/VideoProfile;Landroid/telecom/VideoProfile;)V
 Lcom/android/ims/internal/IImsVideoCallProvider$Stub;-><init>()V
 Lcom/android/ims/internal/IImsVideoCallProvider;->setCallback(Lcom/android/ims/internal/IImsVideoCallCallback;)V
+Lcom/android/ims/internal/ImsVideoCallProviderWrapper;-><init>(Lcom/android/ims/internal/IImsVideoCallProvider;)V
 Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
+Lcom/android/internal/app/AlertActivity;-><init>()V
+Lcom/android/internal/app/AlertActivity;->mAlert:Lcom/android/internal/app/AlertController;
+Lcom/android/internal/app/AlertActivity;->mAlertParams:Lcom/android/internal/app/AlertController$AlertParams;
+Lcom/android/internal/app/AlertActivity;->setupAlert()V
+Lcom/android/internal/app/AssistUtils;-><init>(Landroid/content/Context;)V
+Lcom/android/internal/app/AssistUtils;->getAssistComponentForUser(I)Landroid/content/ComponentName;
+Lcom/android/internal/app/ChooserActivity;-><init>()V
 Lcom/android/internal/app/IAppOpsCallback$Stub;-><init>()V
 Lcom/android/internal/app/IAppOpsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/app/IAppOpsService$Stub$Proxy;->checkOperation(IILjava/lang/String;)I
@@ -1510,9 +1679,35 @@
 Lcom/android/internal/app/IBatteryStats;->getStatistics()[B
 Lcom/android/internal/app/IBatteryStats;->isCharging()Z
 Lcom/android/internal/app/IMediaContainerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IMediaContainerService;
+Lcom/android/internal/app/IntentForwarderActivity;->TAG:Ljava/lang/String;
 Lcom/android/internal/app/IVoiceInteractionManagerService$Stub$Proxy;->showSessionFromSession(Landroid/os/IBinder;Landroid/os/Bundle;I)Z
 Lcom/android/internal/app/IVoiceInteractionManagerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IVoiceInteractionManagerService;
 Lcom/android/internal/app/IVoiceInteractionManagerService;->getKeyphraseSoundModel(ILjava/lang/String;)Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel;
+Lcom/android/internal/app/LocaleHelper$LocaleInfoComparator;-><init>(Ljava/util/Locale;Z)V
+Lcom/android/internal/app/LocaleHelper$LocaleInfoComparator;->compare(Lcom/android/internal/app/LocaleStore$LocaleInfo;Lcom/android/internal/app/LocaleStore$LocaleInfo;)I
+Lcom/android/internal/app/LocaleHelper;->getDisplayCountry(Ljava/util/Locale;Ljava/util/Locale;)Ljava/lang/String;
+Lcom/android/internal/app/LocaleHelper;->getDisplayName(Ljava/util/Locale;Ljava/util/Locale;Z)Ljava/lang/String;
+Lcom/android/internal/app/LocaleHelper;->normalizeForSearch(Ljava/lang/String;Ljava/util/Locale;)Ljava/lang/String;
+Lcom/android/internal/app/LocalePicker$LocaleInfo;->getLocale()Ljava/util/Locale;
+Lcom/android/internal/app/LocalePicker;->getLocales()Landroid/os/LocaleList;
+Lcom/android/internal/app/LocalePicker;->updateLocale(Ljava/util/Locale;)V
+Lcom/android/internal/app/LocalePicker;->updateLocales(Landroid/os/LocaleList;)V
+Lcom/android/internal/app/LocaleStore$LocaleInfo;->getFullNameInUiLanguage()Ljava/lang/String;
+Lcom/android/internal/app/LocaleStore$LocaleInfo;->getFullNameNative()Ljava/lang/String;
+Lcom/android/internal/app/LocaleStore$LocaleInfo;->getId()Ljava/lang/String;
+Lcom/android/internal/app/LocaleStore$LocaleInfo;->getLocale()Ljava/util/Locale;
+Lcom/android/internal/app/LocaleStore$LocaleInfo;->getParent()Ljava/util/Locale;
+Lcom/android/internal/app/LocaleStore;->fillCache(Landroid/content/Context;)V
+Lcom/android/internal/app/LocaleStore;->getLevelLocales(Landroid/content/Context;Ljava/util/Set;Lcom/android/internal/app/LocaleStore$LocaleInfo;Z)Ljava/util/Set;
+Lcom/android/internal/app/LocaleStore;->getLocaleInfo(Ljava/util/Locale;)Lcom/android/internal/app/LocaleStore$LocaleInfo;
+Lcom/android/internal/app/NetInitiatedActivity;->handleNIVerify(Landroid/content/Intent;)V
+Lcom/android/internal/app/ResolverActivity;-><init>()V
+Lcom/android/internal/app/ResolverActivity;->mAdapter:Lcom/android/internal/app/ResolverActivity$ResolveListAdapter;
+Lcom/android/internal/app/ResolverActivity;->mPm:Landroid/content/pm/PackageManager;
+Lcom/android/internal/app/ResolverActivity;->onCreate(Landroid/os/Bundle;Landroid/content/Intent;Ljava/lang/CharSequence;[Landroid/content/Intent;Ljava/util/List;Z)V
+Lcom/android/internal/app/WindowDecorActionBar$TabImpl;->mCallback:Landroid/app/ActionBar$TabListener;
+Lcom/android/internal/app/WindowDecorActionBar;->mTabScrollView:Lcom/android/internal/widget/ScrollingTabContainerView;
+Lcom/android/internal/app/WindowDecorActionBar;->setShowHideAnimationEnabled(Z)V
 Lcom/android/internal/appwidget/IAppWidgetService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/appwidget/IAppWidgetService;
 Lcom/android/internal/appwidget/IAppWidgetService$Stub;->TRANSACTION_bindAppWidgetId:I
 Lcom/android/internal/appwidget/IAppWidgetService;->bindAppWidgetId(Ljava/lang/String;IILandroid/content/ComponentName;Landroid/os/Bundle;)Z
@@ -1520,17 +1715,80 @@
 Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetIds(Landroid/content/ComponentName;)[I
 Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetViews(Ljava/lang/String;I)Landroid/widget/RemoteViews;
 Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V
+Lcom/android/internal/database/SortCursor;-><init>([Landroid/database/Cursor;Ljava/lang/String;)V
+Lcom/android/internal/database/SortCursor;->mCursor:Landroid/database/Cursor;
+Lcom/android/internal/database/SortCursor;->mCursors:[Landroid/database/Cursor;
+Lcom/android/internal/http/HttpDateTime;->parse(Ljava/lang/String;)J
+Lcom/android/internal/location/GpsNetInitiatedHandler$GpsNiNotification;-><init>()V
+Lcom/android/internal/location/GpsNetInitiatedHandler$GpsNiNotification;->requestorId:Ljava/lang/String;
+Lcom/android/internal/location/GpsNetInitiatedHandler$GpsNiNotification;->requestorIdEncoding:I
+Lcom/android/internal/location/GpsNetInitiatedHandler$GpsNiNotification;->text:Ljava/lang/String;
+Lcom/android/internal/location/GpsNetInitiatedHandler$GpsNiNotification;->textEncoding:I
+Lcom/android/internal/location/GpsNetInitiatedHandler;->decodeString(Ljava/lang/String;ZI)Ljava/lang/String;
+Lcom/android/internal/location/GpsNetInitiatedHandler;->handleNiNotification(Lcom/android/internal/location/GpsNetInitiatedHandler$GpsNiNotification;)V
+Lcom/android/internal/location/GpsNetInitiatedHandler;->mIsHexInput:Z
 Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
 Lcom/android/internal/logging/MetricsLogger;-><init>()V
 Lcom/android/internal/net/LegacyVpnInfo;-><init>()V
 Lcom/android/internal/net/VpnConfig;-><init>()V
+Lcom/android/internal/os/AndroidPrintStream;-><init>(ILjava/lang/String;)V
+Lcom/android/internal/os/BaseCommand;-><init>()V
+Lcom/android/internal/os/BaseCommand;->mArgs:Landroid/os/ShellCommand;
 Lcom/android/internal/os/BatterySipper$DrainType;->values()[Lcom/android/internal/os/BatterySipper$DrainType;
+Lcom/android/internal/os/BinderInternal;->getContextObject()Landroid/os/IBinder;
+Lcom/android/internal/os/BinderInternal;->handleGc()V
+Lcom/android/internal/os/ClassLoaderFactory;->createClassloaderNamespace(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;ZZ)Ljava/lang/String;
 Lcom/android/internal/os/IDropBoxManagerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/os/IDropBoxManagerService;
 Lcom/android/internal/os/IDropBoxManagerService;->getNextEntry(Ljava/lang/String;JLjava/lang/String;)Landroid/os/DropBoxManager$Entry;
+Lcom/android/internal/os/ProcessCpuTracker$Stats;->name:Ljava/lang/String;
+Lcom/android/internal/os/ProcessCpuTracker$Stats;->rel_stime:I
+Lcom/android/internal/os/ProcessCpuTracker$Stats;->rel_uptime:J
+Lcom/android/internal/os/ProcessCpuTracker$Stats;->rel_utime:I
+Lcom/android/internal/os/ProcessCpuTracker;-><init>(Z)V
+Lcom/android/internal/os/ProcessCpuTracker;->countWorkingStats()I
+Lcom/android/internal/os/ProcessCpuTracker;->getWorkingStats(I)Lcom/android/internal/os/ProcessCpuTracker$Stats;
+Lcom/android/internal/os/ProcessCpuTracker;->update()V
+Lcom/android/internal/os/RuntimeInit;->commonInit()V
+Lcom/android/internal/os/RuntimeInit;->getApplicationObject()Landroid/os/IBinder;
+Lcom/android/internal/os/RuntimeInit;->initialized:Z
+Lcom/android/internal/os/RuntimeInit;->main([Ljava/lang/String;)V
+Lcom/android/internal/os/RuntimeInit;->mApplicationObject:Landroid/os/IBinder;
+Lcom/android/internal/os/ZygoteConnection$Arguments;-><init>([Ljava/lang/String;)V
+Lcom/android/internal/os/ZygoteConnection$Arguments;->effectiveCapabilities:J
+Lcom/android/internal/os/ZygoteConnection$Arguments;->gid:I
+Lcom/android/internal/os/ZygoteConnection$Arguments;->gids:[I
+Lcom/android/internal/os/ZygoteConnection$Arguments;->permittedCapabilities:J
+Lcom/android/internal/os/ZygoteConnection$Arguments;->remainingArgs:[Ljava/lang/String;
+Lcom/android/internal/os/ZygoteConnection$Arguments;->rlimits:Ljava/util/ArrayList;
+Lcom/android/internal/os/ZygoteConnection$Arguments;->uid:I
+Lcom/android/internal/os/ZygoteConnection;->applyUidSecurityPolicy(Lcom/android/internal/os/ZygoteConnection$Arguments;Landroid/net/Credentials;)V
+Lcom/android/internal/os/ZygoteConnection;->closeSocket()V
+Lcom/android/internal/os/ZygoteConnection;->getFileDesciptor()Ljava/io/FileDescriptor;
+Lcom/android/internal/os/ZygoteConnection;->intArray2d:[[I
+Lcom/android/internal/os/ZygoteConnection;->mSocket:Landroid/net/LocalSocket;
+Lcom/android/internal/os/ZygoteConnection;->mSocketOutStream:Ljava/io/DataOutputStream;
+Lcom/android/internal/os/ZygoteConnection;->peer:Landroid/net/Credentials;
+Lcom/android/internal/os/ZygoteConnection;->readArgumentList()[Ljava/lang/String;
+Lcom/android/internal/os/ZygoteInit;->main([Ljava/lang/String;)V
+Lcom/android/internal/os/ZygoteInit;->mResources:Landroid/content/res/Resources;
+Lcom/android/internal/os/ZygoteSecurityException;-><init>(Ljava/lang/String;)V
+Lcom/android/internal/policy/DecorView;->mLastBottomInset:I
+Lcom/android/internal/policy/DecorView;->mLastLeftInset:I
+Lcom/android/internal/policy/DecorView;->mLastRightInset:I
+Lcom/android/internal/policy/DecorView;->mWindow:Lcom/android/internal/policy/PhoneWindow;
 Lcom/android/internal/policy/IKeyguardService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/policy/IKeyguardService;
 Lcom/android/internal/policy/IKeyguardService;->doKeyguardTimeout(Landroid/os/Bundle;)V
 Lcom/android/internal/policy/IKeyguardService;->setKeyguardEnabled(Z)V
 Lcom/android/internal/policy/IKeyguardStateCallback$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/policy/IKeyguardStateCallback;
+Lcom/android/internal/policy/PhoneFallbackEventHandler;-><init>(Landroid/content/Context;)V
+Lcom/android/internal/policy/PhoneFallbackEventHandler;->mContext:Landroid/content/Context;
+Lcom/android/internal/policy/PhoneFallbackEventHandler;->mView:Landroid/view/View;
+Lcom/android/internal/policy/PhoneFallbackEventHandler;->onKeyDown(ILandroid/view/KeyEvent;)Z
+Lcom/android/internal/policy/PhoneFallbackEventHandler;->onKeyUp(ILandroid/view/KeyEvent;)Z
+Lcom/android/internal/policy/PhoneFallbackEventHandler;->startCallActivity()V
+Lcom/android/internal/policy/PhoneWindow;-><init>(Landroid/content/Context;)V
+Lcom/android/internal/policy/PhoneWindow;->mTitle:Ljava/lang/CharSequence;
+Lcom/android/internal/preference/YesNoPreference;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
 Lcom/android/internal/R$anim;->fade_in:I
 Lcom/android/internal/R$array;->config_autoBrightnessLcdBacklightValues:I
 Lcom/android/internal/R$array;->config_autoBrightnessLevels:I
@@ -1998,9 +2256,961 @@
 Lcom/android/internal/statusbar/IStatusBarService;->setIconVisibility(Ljava/lang/String;Z)V
 Lcom/android/internal/telecom/ITelecomService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telecom/ITelecomService;
 Lcom/android/internal/telecom/ITelecomService;->getCallState()I
+Lcom/android/internal/telephony/BaseCommands;->mCallStateRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mCallWaitingInfoRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mCatCallSetUpRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mCatCcAlphaRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mCatEventRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mCatProCmdRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mCatSessionEndRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mCdmaPrlChangedRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mCdmaSmsRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mCdmaSubscriptionChangedRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/BaseCommands;->mEmergencyCallbackModeRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mExitEmergencyCallbackModeRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mGsmBroadcastSmsRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mGsmSmsRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mHardwareConfigChangeRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mIccRefreshRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mIccSmsFullRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mIccStatusChangedRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mImsNetworkStateChangedRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mNITZTimeRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mOtaProvisionRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mPhoneRadioCapabilityChangedRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mPhoneType:I
+Lcom/android/internal/telephony/BaseCommands;->mPreferredNetworkType:I
+Lcom/android/internal/telephony/BaseCommands;->mResendIncallMuteRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mRestrictedStateRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mRilCellInfoListRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mRingbackToneRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mRingRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mSignalStrengthRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mSmsOnSimRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mSmsStatusRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mSrvccStateRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mSsnRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mSsRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mStateMonitor:Ljava/lang/Object;
+Lcom/android/internal/telephony/BaseCommands;->mSubscriptionStatusRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/BaseCommands;->mUnsolOemHookRawRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mUSSDRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/BaseCommands;->mVoiceRadioTechChangedRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/Call$State;->ACTIVE:Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/Call$State;->ALERTING:Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/Call$State;->DIALING:Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/Call$State;->DISCONNECTED:Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/Call$State;->DISCONNECTING:Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/Call$State;->HOLDING:Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/Call$State;->IDLE:Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/Call$State;->INCOMING:Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/Call$State;->isAlive()Z
+Lcom/android/internal/telephony/Call$State;->isRinging()Z
+Lcom/android/internal/telephony/Call$State;->values()[Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/Call$State;->WAITING:Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/Call;-><init>()V
+Lcom/android/internal/telephony/Call;->getConnections()Ljava/util/List;
+Lcom/android/internal/telephony/Call;->getEarliestConnection()Lcom/android/internal/telephony/Connection;
+Lcom/android/internal/telephony/Call;->getLatestConnection()Lcom/android/internal/telephony/Connection;
+Lcom/android/internal/telephony/Call;->getPhone()Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/Call;->getState()Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/Call;->hangup()V
+Lcom/android/internal/telephony/Call;->isIdle()Z
+Lcom/android/internal/telephony/Call;->isMultiparty()Z
+Lcom/android/internal/telephony/Call;->mConnections:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/Call;->mState:Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;-><init>(Lcom/android/internal/telephony/CallerInfoAsyncQuery;Landroid/content/Context;)V
+Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;-><init>()V
+Lcom/android/internal/telephony/CallerInfoAsyncQuery;->release()V
+Lcom/android/internal/telephony/CallForwardInfo;-><init>()V
+Lcom/android/internal/telephony/CallForwardInfo;->number:Ljava/lang/String;
+Lcom/android/internal/telephony/CallForwardInfo;->reason:I
+Lcom/android/internal/telephony/CallForwardInfo;->serviceClass:I
+Lcom/android/internal/telephony/CallForwardInfo;->status:I
+Lcom/android/internal/telephony/CallForwardInfo;->timeSeconds:I
+Lcom/android/internal/telephony/CallForwardInfo;->toa:I
+Lcom/android/internal/telephony/CallManager;->canConference(Lcom/android/internal/telephony/Call;I)Z
+Lcom/android/internal/telephony/CallManager;->canDial(Lcom/android/internal/telephony/Phone;)Z
+Lcom/android/internal/telephony/CallManager;->conference(Lcom/android/internal/telephony/Call;)V
+Lcom/android/internal/telephony/CallManager;->getActiveFgCall(I)Lcom/android/internal/telephony/Call;
+Lcom/android/internal/telephony/CallManager;->getActiveFgCallState(I)Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/CallManager;->getBackgroundCalls()Ljava/util/List;
+Lcom/android/internal/telephony/CallManager;->getBgCallConnections()Ljava/util/List;
+Lcom/android/internal/telephony/CallManager;->getBgPhone()Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/CallManager;->getContext()Landroid/content/Context;
+Lcom/android/internal/telephony/CallManager;->getDefaultPhone()Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/CallManager;->getFgCallConnections()Ljava/util/List;
+Lcom/android/internal/telephony/CallManager;->getFgPhone()Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/CallManager;->getFgPhone(I)Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/CallManager;->getFirstActiveBgCall()Lcom/android/internal/telephony/Call;
+Lcom/android/internal/telephony/CallManager;->getFirstActiveBgCall(I)Lcom/android/internal/telephony/Call;
+Lcom/android/internal/telephony/CallManager;->getFirstActiveRingingCall()Lcom/android/internal/telephony/Call;
+Lcom/android/internal/telephony/CallManager;->getFirstActiveRingingCall(I)Lcom/android/internal/telephony/Call;
+Lcom/android/internal/telephony/CallManager;->getInstance()Lcom/android/internal/telephony/CallManager;
+Lcom/android/internal/telephony/CallManager;->getPhoneInCall()Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/CallManager;->getRingingCalls()Ljava/util/List;
+Lcom/android/internal/telephony/CallManager;->getRingingPhone()Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/CallManager;->getState()Lcom/android/internal/telephony/PhoneConstants$State;
+Lcom/android/internal/telephony/CallManager;->getState(I)Lcom/android/internal/telephony/PhoneConstants$State;
+Lcom/android/internal/telephony/CallManager;->hasActiveBgCall()Z
+Lcom/android/internal/telephony/CallManager;->hasActiveBgCall(I)Z
+Lcom/android/internal/telephony/CallManager;->hasActiveFgCall()Z
+Lcom/android/internal/telephony/CallManager;->hasActiveFgCall(I)Z
+Lcom/android/internal/telephony/CallManager;->hasActiveRingingCall(I)Z
+Lcom/android/internal/telephony/CallManager;->hasMoreThanOneRingingCall()Z
+Lcom/android/internal/telephony/CallManager;->hasMoreThanOneRingingCall(I)Z
+Lcom/android/internal/telephony/CallManager;->mBackgroundCalls:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/CallManager;->mEmptyConnections:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/CallManager;->mForegroundCalls:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/CallManager;->mPhones:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/CallManager;->mRingingCalls:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/CallManager;->registerForDisconnect(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CallManager;->registerForNewRingingConnection(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CallManager;->registerForPreciseCallStateChanged(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CallManager;->registerPhone(Lcom/android/internal/telephony/Phone;)Z
+Lcom/android/internal/telephony/CallManager;->unregisterForDisconnect(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/CallManager;->unregisterForNewRingingConnection(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/CallManager;->unregisterForPreciseCallStateChanged(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/CallManager;->unregisterPhone(Lcom/android/internal/telephony/Phone;)V
+Lcom/android/internal/telephony/CallStateException;-><init>(Ljava/lang/String;)V
+Lcom/android/internal/telephony/CallTracker;-><init>()V
+Lcom/android/internal/telephony/CallTracker;->getState()Lcom/android/internal/telephony/PhoneConstants$State;
+Lcom/android/internal/telephony/CallTracker;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/CallTracker;->mCi:Lcom/android/internal/telephony/CommandsInterface;
+Lcom/android/internal/telephony/CallTracker;->mNeedsPoll:Z
+Lcom/android/internal/telephony/CallTracker;->mNumberConverted:Z
+Lcom/android/internal/telephony/CallTracker;->mPendingOperations:I
+Lcom/android/internal/telephony/CallTracker;->registerForVoiceCallEnded(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CarrierServiceBindHelper;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/CarrierServiceBindHelper;->mHandler:Landroid/os/Handler;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->CLOSE_CHANNEL:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->DISPLAY_TEXT:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->fromInt(I)Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->GET_CHANNEL_STATUS:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->GET_INKEY:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->GET_INPUT:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->LANGUAGE_NOTIFICATION:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->LAUNCH_BROWSER:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->OPEN_CHANNEL:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->PLAY_TONE:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->PROVIDE_LOCAL_INFORMATION:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->RECEIVE_DATA:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->REFRESH:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->SELECT_ITEM:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->SEND_DATA:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->SEND_DTMF:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->SEND_SMS:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->SEND_SS:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->SEND_USSD:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->SET_UP_CALL:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->SET_UP_EVENT_LIST:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->SET_UP_IDLE_MODE_TEXT:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->SET_UP_MENU:Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/AppInterface$CommandType;->values()[Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/CatCmdMessage$CallSettings;->callMsg:Lcom/android/internal/telephony/cat/TextMessage;
+Lcom/android/internal/telephony/cat/CatCmdMessage$CallSettings;->confirmMsg:Lcom/android/internal/telephony/cat/TextMessage;
+Lcom/android/internal/telephony/cat/CatCmdMessage$SetupEventListSettings;->eventList:[I
+Lcom/android/internal/telephony/cat/CatCmdMessage;->getCallSettings()Lcom/android/internal/telephony/cat/CatCmdMessage$CallSettings;
+Lcom/android/internal/telephony/cat/CatCmdMessage;->getCmdType()Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/CatCmdMessage;->geTextMessage()Lcom/android/internal/telephony/cat/TextMessage;
+Lcom/android/internal/telephony/cat/CatCmdMessage;->getSetEventList()Lcom/android/internal/telephony/cat/CatCmdMessage$SetupEventListSettings;
+Lcom/android/internal/telephony/cat/CatCmdMessage;->hasIconLoadFailed()Z
+Lcom/android/internal/telephony/cat/CatCmdMessage;->mCallSettings:Lcom/android/internal/telephony/cat/CatCmdMessage$CallSettings;
+Lcom/android/internal/telephony/cat/CatCmdMessage;->mCmdDet:Lcom/android/internal/telephony/cat/CommandDetails;
+Lcom/android/internal/telephony/cat/CatCmdMessage;->mInput:Lcom/android/internal/telephony/cat/Input;
+Lcom/android/internal/telephony/cat/CatCmdMessage;->mMenu:Lcom/android/internal/telephony/cat/Menu;
+Lcom/android/internal/telephony/cat/CatCmdMessage;->mTextMsg:Lcom/android/internal/telephony/cat/TextMessage;
+Lcom/android/internal/telephony/cat/CatLog;->d(Ljava/lang/Object;Ljava/lang/String;)V
+Lcom/android/internal/telephony/cat/CatLog;->d(Ljava/lang/String;Ljava/lang/String;)V
+Lcom/android/internal/telephony/cat/CatLog;->e(Ljava/lang/Object;Ljava/lang/String;)V
+Lcom/android/internal/telephony/cat/CatResponseMessage;->setEventDownload(I[B)V
+Lcom/android/internal/telephony/cat/CatService;->dispose()V
+Lcom/android/internal/telephony/cat/CatService;->isStkAppInstalled()Z
+Lcom/android/internal/telephony/cat/CatService;->mCmdIf:Lcom/android/internal/telephony/CommandsInterface;
+Lcom/android/internal/telephony/cat/CatService;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/cat/CatService;->mCurrntCmd:Lcom/android/internal/telephony/cat/CatCmdMessage;
+Lcom/android/internal/telephony/cat/CatService;->mHandlerThread:Landroid/os/HandlerThread;
+Lcom/android/internal/telephony/cat/CatService;->mMenuCmd:Lcom/android/internal/telephony/cat/CatCmdMessage;
+Lcom/android/internal/telephony/cat/CatService;->mMsgDecoder:Lcom/android/internal/telephony/cat/RilMessageDecoder;
+Lcom/android/internal/telephony/cat/CatService;->mSlotId:I
+Lcom/android/internal/telephony/cat/CatService;->mStkAppInstalled:Z
+Lcom/android/internal/telephony/cat/CatService;->mUiccController:Lcom/android/internal/telephony/uicc/UiccController;
+Lcom/android/internal/telephony/cat/CatService;->sendTerminalResponse(Lcom/android/internal/telephony/cat/CommandDetails;Lcom/android/internal/telephony/cat/ResultCode;ZILcom/android/internal/telephony/cat/ResponseData;)V
+Lcom/android/internal/telephony/cat/CatService;->sInstance:[Lcom/android/internal/telephony/cat/CatService;
+Lcom/android/internal/telephony/cat/CatService;->sInstanceLock:Ljava/lang/Object;
+Lcom/android/internal/telephony/cat/CommandDetails;->commandNumber:I
+Lcom/android/internal/telephony/cat/CommandDetails;->commandQualifier:I
+Lcom/android/internal/telephony/cat/CommandDetails;->compRequired:Z
+Lcom/android/internal/telephony/cat/CommandDetails;->typeOfCommand:I
+Lcom/android/internal/telephony/cat/CommandParams;-><init>(Lcom/android/internal/telephony/cat/CommandDetails;)V
+Lcom/android/internal/telephony/cat/CommandParams;->getCommandType()Lcom/android/internal/telephony/cat/AppInterface$CommandType;
+Lcom/android/internal/telephony/cat/CommandParams;->mCmdDet:Lcom/android/internal/telephony/cat/CommandDetails;
+Lcom/android/internal/telephony/cat/CommandParamsFactory;->dispose()V
+Lcom/android/internal/telephony/cat/CommandParamsFactory;->mIconLoader:Lcom/android/internal/telephony/cat/IconLoader;
+Lcom/android/internal/telephony/cat/CommandParamsFactory;->searchForNextTag(Lcom/android/internal/telephony/cat/ComprehensionTlvTag;Ljava/util/Iterator;)Lcom/android/internal/telephony/cat/ComprehensionTlv;
+Lcom/android/internal/telephony/cat/CommandParamsFactory;->searchForTag(Lcom/android/internal/telephony/cat/ComprehensionTlvTag;Ljava/util/List;)Lcom/android/internal/telephony/cat/ComprehensionTlv;
+Lcom/android/internal/telephony/cat/ComprehensionTlv;->getLength()I
+Lcom/android/internal/telephony/cat/ComprehensionTlv;->getRawValue()[B
+Lcom/android/internal/telephony/cat/ComprehensionTlv;->getTag()I
+Lcom/android/internal/telephony/cat/ComprehensionTlv;->getValueIndex()I
+Lcom/android/internal/telephony/cat/ComprehensionTlvTag;->ADDRESS:Lcom/android/internal/telephony/cat/ComprehensionTlvTag;
+Lcom/android/internal/telephony/cat/ComprehensionTlvTag;->ALPHA_ID:Lcom/android/internal/telephony/cat/ComprehensionTlvTag;
+Lcom/android/internal/telephony/cat/ComprehensionTlvTag;->COMMAND_DETAILS:Lcom/android/internal/telephony/cat/ComprehensionTlvTag;
+Lcom/android/internal/telephony/cat/ComprehensionTlvTag;->DEVICE_IDENTITIES:Lcom/android/internal/telephony/cat/ComprehensionTlvTag;
+Lcom/android/internal/telephony/cat/ComprehensionTlvTag;->ICON_ID:Lcom/android/internal/telephony/cat/ComprehensionTlvTag;
+Lcom/android/internal/telephony/cat/ComprehensionTlvTag;->RESULT:Lcom/android/internal/telephony/cat/ComprehensionTlvTag;
+Lcom/android/internal/telephony/cat/ComprehensionTlvTag;->SMS_TPDU:Lcom/android/internal/telephony/cat/ComprehensionTlvTag;
+Lcom/android/internal/telephony/cat/ComprehensionTlvTag;->TEXT_ATTRIBUTE:Lcom/android/internal/telephony/cat/ComprehensionTlvTag;
+Lcom/android/internal/telephony/cat/ComprehensionTlvTag;->TEXT_STRING:Lcom/android/internal/telephony/cat/ComprehensionTlvTag;
+Lcom/android/internal/telephony/cat/ComprehensionTlvTag;->USSD_STRING:Lcom/android/internal/telephony/cat/ComprehensionTlvTag;
+Lcom/android/internal/telephony/cat/ComprehensionTlvTag;->value()I
+Lcom/android/internal/telephony/cat/DeviceIdentities;->destinationId:I
+Lcom/android/internal/telephony/cat/DisplayTextParams;-><init>(Lcom/android/internal/telephony/cat/CommandDetails;Lcom/android/internal/telephony/cat/TextMessage;)V
+Lcom/android/internal/telephony/cat/DisplayTextParams;->mTextMsg:Lcom/android/internal/telephony/cat/TextMessage;
+Lcom/android/internal/telephony/cat/Duration$TimeUnit;->value()I
+Lcom/android/internal/telephony/cat/Duration;->timeInterval:I
+Lcom/android/internal/telephony/cat/Duration;->timeUnit:Lcom/android/internal/telephony/cat/Duration$TimeUnit;
+Lcom/android/internal/telephony/cat/GetInputParams;-><init>(Lcom/android/internal/telephony/cat/CommandDetails;Lcom/android/internal/telephony/cat/Input;)V
+Lcom/android/internal/telephony/cat/IconId;->recordNumber:I
+Lcom/android/internal/telephony/cat/IconLoader;->loadIcon(ILandroid/os/Message;)V
+Lcom/android/internal/telephony/cat/Menu;->titleAttrs:Ljava/util/List;
+Lcom/android/internal/telephony/cat/PlayToneParams;-><init>(Lcom/android/internal/telephony/cat/CommandDetails;Lcom/android/internal/telephony/cat/TextMessage;Lcom/android/internal/telephony/cat/Tone;Lcom/android/internal/telephony/cat/Duration;Z)V
+Lcom/android/internal/telephony/cat/ResponseData;-><init>()V
+Lcom/android/internal/telephony/cat/ResponseData;->format(Ljava/io/ByteArrayOutputStream;)V
+Lcom/android/internal/telephony/cat/ResultCode;->BACKWARD_MOVE_BY_USER:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->BEYOND_TERMINAL_CAPABILITY:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->BIP_ERROR:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->CMD_DATA_NOT_UNDERSTOOD:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->HELP_INFO_REQUIRED:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->LAUNCH_BROWSER_ERROR:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->NETWORK_CRNTLY_UNABLE_TO_PROCESS:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->NO_RESPONSE_FROM_USER:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->OK:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->PRFRMD_ICON_NOT_DISPLAYED:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->PRFRMD_LIMITED_SERVICE:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->PRFRMD_MODIFIED_BY_NAA:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->PRFRMD_NAA_NOT_ACTIVE:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->PRFRMD_TONE_NOT_PLAYED:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->PRFRMD_WITH_ADDITIONAL_EFS_READ:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->PRFRMD_WITH_MISSING_INFO:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->PRFRMD_WITH_MODIFICATION:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->PRFRMD_WITH_PARTIAL_COMPREHENSION:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->REQUIRED_VALUES_MISSING:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->TERMINAL_CRNTLY_UNABLE_TO_PROCESS:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->UICC_SESSION_TERM_BY_USER:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->USER_NOT_ACCEPT:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->USIM_CALL_CONTROL_PERMANENT:Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultCode;->value()I
+Lcom/android/internal/telephony/cat/ResultCode;->values()[Lcom/android/internal/telephony/cat/ResultCode;
+Lcom/android/internal/telephony/cat/ResultException;-><init>(Lcom/android/internal/telephony/cat/ResultCode;)V
+Lcom/android/internal/telephony/cat/RilMessage;-><init>(ILjava/lang/String;)V
+Lcom/android/internal/telephony/cat/RilMessage;->mData:Ljava/lang/Object;
+Lcom/android/internal/telephony/cat/RilMessage;->mId:I
+Lcom/android/internal/telephony/cat/RilMessageDecoder;->getInstance(Landroid/os/Handler;Lcom/android/internal/telephony/uicc/IccFileHandler;I)Lcom/android/internal/telephony/cat/RilMessageDecoder;
+Lcom/android/internal/telephony/cat/RilMessageDecoder;->mCmdParamsFactory:Lcom/android/internal/telephony/cat/CommandParamsFactory;
+Lcom/android/internal/telephony/cat/RilMessageDecoder;->mCurrentRilMessage:Lcom/android/internal/telephony/cat/RilMessage;
+Lcom/android/internal/telephony/cat/RilMessageDecoder;->mInstance:[Lcom/android/internal/telephony/cat/RilMessageDecoder;
+Lcom/android/internal/telephony/cat/RilMessageDecoder;->mStateStart:Lcom/android/internal/telephony/cat/RilMessageDecoder$StateStart;
+Lcom/android/internal/telephony/cat/RilMessageDecoder;->sendCmdForExecution(Lcom/android/internal/telephony/cat/RilMessage;)V
+Lcom/android/internal/telephony/cat/RilMessageDecoder;->sendStartDecodingMessageParams(Lcom/android/internal/telephony/cat/RilMessage;)V
+Lcom/android/internal/telephony/cat/SelectItemParams;-><init>(Lcom/android/internal/telephony/cat/CommandDetails;Lcom/android/internal/telephony/cat/Menu;Z)V
+Lcom/android/internal/telephony/cat/TextMessage;-><init>()V
+Lcom/android/internal/telephony/cat/TextMessage;->iconSelfExplanatory:Z
+Lcom/android/internal/telephony/cat/TextMessage;->text:Ljava/lang/String;
+Lcom/android/internal/telephony/cat/ValueObject;-><init>()V
+Lcom/android/internal/telephony/cat/ValueParser;->retrieveAlphaId(Lcom/android/internal/telephony/cat/ComprehensionTlv;)Ljava/lang/String;
+Lcom/android/internal/telephony/cat/ValueParser;->retrieveDeviceIdentities(Lcom/android/internal/telephony/cat/ComprehensionTlv;)Lcom/android/internal/telephony/cat/DeviceIdentities;
+Lcom/android/internal/telephony/cat/ValueParser;->retrieveTextAttribute(Lcom/android/internal/telephony/cat/ComprehensionTlv;)Ljava/util/List;
+Lcom/android/internal/telephony/cat/ValueParser;->retrieveTextString(Lcom/android/internal/telephony/cat/ComprehensionTlv;)Ljava/lang/String;
+Lcom/android/internal/telephony/cdma/CdmaCallWaitingNotification;->number:Ljava/lang/String;
+Lcom/android/internal/telephony/cdma/CdmaMmiCode;->makeEmptyNull(Ljava/lang/String;)Ljava/lang/String;
+Lcom/android/internal/telephony/cdma/CdmaMmiCode;->mSc:Ljava/lang/String;
+Lcom/android/internal/telephony/cdma/CdmaSMSDispatcher;->getFormat()Ljava/lang/String;
+Lcom/android/internal/telephony/cdma/CdmaSMSDispatcher;->handleCdmaStatusReport(Lcom/android/internal/telephony/cdma/SmsMessage;)V
+Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager;->getCdmaSubscriptionSource()I
+Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager;->getInstance(Landroid/content/Context;Lcom/android/internal/telephony/CommandsInterface;Landroid/os/Handler;ILjava/lang/Object;)Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager;
+Lcom/android/internal/telephony/cdma/EriManager$EriDisplayInformation;->mEriIconText:Ljava/lang/String;
+Lcom/android/internal/telephony/cdma/EriManager;->getEriDisplayInformation(II)Lcom/android/internal/telephony/cdma/EriManager$EriDisplayInformation;
+Lcom/android/internal/telephony/cdma/sms/BearerData$CodingException;-><init>(Ljava/lang/String;)V
+Lcom/android/internal/telephony/cdma/sms/BearerData$TimeStamp;-><init>()V
+Lcom/android/internal/telephony/cdma/sms/BearerData;-><init>()V
+Lcom/android/internal/telephony/cdma/sms/BearerData;->countAsciiSeptets(Ljava/lang/CharSequence;Z)I
+Lcom/android/internal/telephony/cdma/sms/BearerData;->decodeUserDataPayload(Lcom/android/internal/telephony/cdma/sms/UserData;Z)V
+Lcom/android/internal/telephony/cdma/sms/BearerData;->displayMode:I
+Lcom/android/internal/telephony/cdma/sms/BearerData;->encode(Lcom/android/internal/telephony/cdma/sms/BearerData;)[B
+Lcom/android/internal/telephony/cdma/sms/BearerData;->encode7bitAscii(Ljava/lang/String;Z)[B
+Lcom/android/internal/telephony/cdma/sms/BearerData;->getBitsForNumFields(II)I
+Lcom/android/internal/telephony/cdma/sms/BearerData;->hasUserDataHeader:Z
+Lcom/android/internal/telephony/cdma/sms/BearerData;->messageId:I
+Lcom/android/internal/telephony/cdma/sms/BearerData;->msgCenterTimeStamp:Lcom/android/internal/telephony/cdma/sms/BearerData$TimeStamp;
+Lcom/android/internal/telephony/cdma/sms/BearerData;->priority:I
+Lcom/android/internal/telephony/cdma/sms/BearerData;->priorityIndicatorSet:Z
+Lcom/android/internal/telephony/cdma/sms/BearerData;->userData:Lcom/android/internal/telephony/cdma/sms/UserData;
+Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;-><init>()V
+Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->digitMode:I
+Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->numberMode:I
+Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->numberOfDigits:I
+Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->numberPlan:I
+Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->parse(Ljava/lang/String;)Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;
+Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;-><init>()V
+Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;->bearerData:[B
+Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;->serviceCategory:I
+Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;->teleService:I
+Lcom/android/internal/telephony/cdma/sms/UserData;-><init>()V
+Lcom/android/internal/telephony/cdma/sms/UserData;->charToAscii:Landroid/util/SparseIntArray;
+Lcom/android/internal/telephony/cdma/sms/UserData;->msgEncoding:I
+Lcom/android/internal/telephony/cdma/sms/UserData;->msgEncodingSet:Z
+Lcom/android/internal/telephony/cdma/sms/UserData;->numFields:I
+Lcom/android/internal/telephony/cdma/sms/UserData;->payload:[B
+Lcom/android/internal/telephony/cdma/sms/UserData;->payloadStr:Ljava/lang/String;
+Lcom/android/internal/telephony/cdma/sms/UserData;->userDataHeader:Lcom/android/internal/telephony/SmsHeader;
+Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;-><init>()V
+Lcom/android/internal/telephony/cdma/SmsMessage;-><init>()V
+Lcom/android/internal/telephony/cdma/SmsMessage;->calculateLength(Ljava/lang/CharSequence;ZZ)Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;
+Lcom/android/internal/telephony/cdma/SmsMessage;->createFromEfRecord(I[B)Lcom/android/internal/telephony/cdma/SmsMessage;
+Lcom/android/internal/telephony/cdma/SmsMessage;->createFromPdu([B)Lcom/android/internal/telephony/cdma/SmsMessage;
+Lcom/android/internal/telephony/cdma/SmsMessage;->getIncomingSmsFingerprint()[B
+Lcom/android/internal/telephony/cdma/SmsMessage;->getMessageType()I
+Lcom/android/internal/telephony/cdma/SmsMessage;->getNextMessageId()I
+Lcom/android/internal/telephony/cdma/SmsMessage;->getNumOfVoicemails()I
+Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Lcom/android/internal/telephony/cdma/sms/UserData;Z)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
+Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Lcom/android/internal/telephony/cdma/sms/UserData;ZI)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
+Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;I[BZ)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
+Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLcom/android/internal/telephony/SmsHeader;)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
+Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLcom/android/internal/telephony/SmsHeader;I)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
+Lcom/android/internal/telephony/cdma/SmsMessage;->getTeleService()I
+Lcom/android/internal/telephony/cdma/SmsMessage;->isStatusReportMessage()Z
+Lcom/android/internal/telephony/cdma/SmsMessage;->mBearerData:Lcom/android/internal/telephony/cdma/sms/BearerData;
+Lcom/android/internal/telephony/cdma/SmsMessage;->mEnvelope:Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;
+Lcom/android/internal/telephony/cdma/SmsMessage;->parseSms()V
+Lcom/android/internal/telephony/cdma/SmsMessage;->privateGetSubmitPdu(Ljava/lang/String;ZLcom/android/internal/telephony/cdma/sms/UserData;)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
+Lcom/android/internal/telephony/CommandException$Error;->GENERIC_FAILURE:Lcom/android/internal/telephony/CommandException$Error;
+Lcom/android/internal/telephony/CommandException$Error;->PASSWORD_INCORRECT:Lcom/android/internal/telephony/CommandException$Error;
+Lcom/android/internal/telephony/CommandException$Error;->RADIO_NOT_AVAILABLE:Lcom/android/internal/telephony/CommandException$Error;
+Lcom/android/internal/telephony/CommandException$Error;->REQUEST_NOT_SUPPORTED:Lcom/android/internal/telephony/CommandException$Error;
+Lcom/android/internal/telephony/CommandException$Error;->SIM_PUK2:Lcom/android/internal/telephony/CommandException$Error;
+Lcom/android/internal/telephony/CommandException$Error;->SMS_FAIL_RETRY:Lcom/android/internal/telephony/CommandException$Error;
+Lcom/android/internal/telephony/CommandException;-><init>(Lcom/android/internal/telephony/CommandException$Error;)V
+Lcom/android/internal/telephony/CommandException;->fromRilErrno(I)Lcom/android/internal/telephony/CommandException;
+Lcom/android/internal/telephony/CommandException;->getCommandError()Lcom/android/internal/telephony/CommandException$Error;
+Lcom/android/internal/telephony/CommandException;->mError:Lcom/android/internal/telephony/CommandException$Error;
+Lcom/android/internal/telephony/CommandsInterface;->acceptCall(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->acknowledgeLastIncomingCdmaSms(ZILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->acknowledgeLastIncomingGsmSms(ZILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->changeBarringPassword(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->deleteSmsOnRuim(ILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->deleteSmsOnSim(ILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->dial(Ljava/lang/String;ILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->dial(Ljava/lang/String;ILcom/android/internal/telephony/UUSInfo;Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->exitEmergencyCallbackMode(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getBasebandVersion(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getCdmaBroadcastConfig(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getCDMASubscription(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getDataCallList(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getIccCardStatus(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getIMEISV(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getIMSI(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getLastDataCallFailCause(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getLastPdpFailCause(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getNetworkSelectionMode(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getOperator(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getPDPContextList(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getPreferredNetworkType(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getSignalStrength(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getSmscAddress(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->getVoiceRegistrationState(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->handleCallSetupRequestFromSim(ZLandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->iccIO(IILjava/lang/String;IIILjava/lang/String;Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->iccIOForApp(IILjava/lang/String;IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->invokeOemRilRequestRaw([BLandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->queryCallForwardStatus(IILjava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->queryCallWaiting(ILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->queryFacilityLock(Ljava/lang/String;Ljava/lang/String;ILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->queryTTYMode(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->registerForAvailable(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->registerForCdmaOtaProvision(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->registerForCellInfoList(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->registerForIccRefresh(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->registerForImsNetworkStateChanged(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->registerForNotAvailable(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->registerForOffOrNotAvailable(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->registerForOn(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->registerForRadioStateChanged(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->registerForRilConnected(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->reportSmsMemoryStatus(ZLandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->reportStkServiceIsRunning(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->requestIccSimAuthentication(ILjava/lang/String;Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->requestShutdown(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->sendDtmf(CLandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->sendEnvelope(Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->sendTerminalResponse(Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->setCallForward(IIILjava/lang/String;ILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->setCallWaiting(ZILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->setCdmaBroadcastActivation(ZLandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->setDataAllowed(ZLandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->setEmergencyCallbackMode(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setFacilityLock(Ljava/lang/String;ZLjava/lang/String;ILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->setNetworkSelectionModeAutomatic(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->setNetworkSelectionModeManual(Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnCallRing(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnCatCallSetUp(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnCatCcAlphaNotify(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnCatEvent(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnCatProactiveCmd(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnCatSessionEnd(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnIccRefresh(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnIccSmsFull(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnNewGsmBroadcastSms(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnNITZTime(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnSignalStrengthUpdate(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnSmsOnSim(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnSmsStatus(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setOnSuppServiceNotification(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/CommandsInterface;->setPhoneType(I)V
+Lcom/android/internal/telephony/CommandsInterface;->setPreferredNetworkType(ILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->setRadioPower(ZLandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->setSmscAddress(Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->setTTYMode(ILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->setUiccSubscription(IIIILandroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->supplyIccPin(Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->switchWaitingOrHoldingAndActive(Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->unregisterForAvailable(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/CommandsInterface;->unregisterForCdmaOtaProvision(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/CommandsInterface;->unregisterForOffOrNotAvailable(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/CommandsInterface;->unregisterForOn(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/CommandsInterface;->unregisterForRilConnected(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/CommandsInterface;->unregisterForVoiceRadioTechChanged(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/CommandsInterface;->writeSmsToRuim(ILjava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/CommandsInterface;->writeSmsToSim(ILjava/lang/String;Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/Connection$PostDialState;->CANCELLED:Lcom/android/internal/telephony/Connection$PostDialState;
+Lcom/android/internal/telephony/Connection$PostDialState;->COMPLETE:Lcom/android/internal/telephony/Connection$PostDialState;
+Lcom/android/internal/telephony/Connection$PostDialState;->NOT_STARTED:Lcom/android/internal/telephony/Connection$PostDialState;
+Lcom/android/internal/telephony/Connection$PostDialState;->STARTED:Lcom/android/internal/telephony/Connection$PostDialState;
+Lcom/android/internal/telephony/Connection$PostDialState;->WAIT:Lcom/android/internal/telephony/Connection$PostDialState;
+Lcom/android/internal/telephony/Connection$PostDialState;->WILD:Lcom/android/internal/telephony/Connection$PostDialState;
+Lcom/android/internal/telephony/Connection;-><init>(I)V
+Lcom/android/internal/telephony/Connection;->getAddress()Ljava/lang/String;
+Lcom/android/internal/telephony/Connection;->getCall()Lcom/android/internal/telephony/Call;
+Lcom/android/internal/telephony/Connection;->getConnectTime()J
+Lcom/android/internal/telephony/Connection;->getCreateTime()J
+Lcom/android/internal/telephony/Connection;->getDisconnectCause()I
+Lcom/android/internal/telephony/Connection;->getDisconnectTime()J
+Lcom/android/internal/telephony/Connection;->getDurationMillis()J
+Lcom/android/internal/telephony/Connection;->getState()Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/Connection;->getUserData()Ljava/lang/Object;
+Lcom/android/internal/telephony/Connection;->hangup()V
+Lcom/android/internal/telephony/Connection;->isAlive()Z
+Lcom/android/internal/telephony/Connection;->isIncoming()Z
+Lcom/android/internal/telephony/Connection;->LOG_TAG:Ljava/lang/String;
+Lcom/android/internal/telephony/Connection;->mAddress:Ljava/lang/String;
+Lcom/android/internal/telephony/Connection;->mCnapName:Ljava/lang/String;
+Lcom/android/internal/telephony/Connection;->mCnapNamePresentation:I
+Lcom/android/internal/telephony/Connection;->mDialString:Ljava/lang/String;
+Lcom/android/internal/telephony/Connection;->mDuration:J
+Lcom/android/internal/telephony/Connection;->mIsIncoming:Z
+Lcom/android/internal/telephony/Connection;->mNumberPresentation:I
+Lcom/android/internal/telephony/Connection;->setVideoState(I)V
+Lcom/android/internal/telephony/dataconnection/ApnContext;->getApnType()Ljava/lang/String;
+Lcom/android/internal/telephony/dataconnection/ApnContext;->getReason()Ljava/lang/String;
+Lcom/android/internal/telephony/dataconnection/ApnContext;->getState()Lcom/android/internal/telephony/DctConstants$State;
+Lcom/android/internal/telephony/dataconnection/ApnContext;->isConnectable()Z
+Lcom/android/internal/telephony/dataconnection/ApnContext;->isDisconnected()Z
+Lcom/android/internal/telephony/dataconnection/ApnContext;->isEnabled()Z
+Lcom/android/internal/telephony/dataconnection/ApnContext;->isReady()Z
+Lcom/android/internal/telephony/dataconnection/ApnContext;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/dataconnection/ApnContext;->mApnType:Ljava/lang/String;
+Lcom/android/internal/telephony/dataconnection/ApnContext;->mRefCount:I
+Lcom/android/internal/telephony/dataconnection/ApnContext;->mRefCountLock:Ljava/lang/Object;
+Lcom/android/internal/telephony/dataconnection/ApnContext;->setState(Lcom/android/internal/telephony/DctConstants$State;)V
+Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;->mApnContext:Lcom/android/internal/telephony/dataconnection/ApnContext;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->clearSettings()V
+Lcom/android/internal/telephony/dataconnection/DataConnection;->dumpToLog()V
+Lcom/android/internal/telephony/dataconnection/DataConnection;->initConnection(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)Z
+Lcom/android/internal/telephony/dataconnection/DataConnection;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mActivatingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mApnContexts:Ljava/util/HashMap;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mDcFailCause:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingErrorCreatingConnection:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectionErrorCreatingConnection;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectingState;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectParams:Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mId:I
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mInactiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcInactiveState;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mLinkProperties:Landroid/net/LinkProperties;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mNetworkInfo:Landroid/net/NetworkInfo;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mPhone:Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/dataconnection/DataConnection;->mRilRat:I
+Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllDisconnectCompleted(Lcom/android/internal/telephony/dataconnection/DcFailCause;)V
+Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfConnected(Ljava/lang/String;)V
+Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfDisconnectDcRetrying(Ljava/lang/String;)V
+Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyConnectCompleted(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;Lcom/android/internal/telephony/dataconnection/DcFailCause;Z)V
+Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyDisconnectCompleted(Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;Z)V
+Lcom/android/internal/telephony/dataconnection/DataConnection;->onConnect(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)V
+Lcom/android/internal/telephony/dataconnection/DataConnection;->tearDownData(Ljava/lang/Object;)V
+Lcom/android/internal/telephony/dataconnection/DataConnection;->updateTcpBufferSizes(I)V
+Lcom/android/internal/telephony/dataconnection/DcController;->lr(Ljava/lang/String;)V
+Lcom/android/internal/telephony/dataconnection/DcController;->mDcListActiveByCid:Ljava/util/HashMap;
+Lcom/android/internal/telephony/dataconnection/DcController;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
+Lcom/android/internal/telephony/dataconnection/DcController;->mDcTesterDeactivateAll:Lcom/android/internal/telephony/dataconnection/DcTesterDeactivateAll;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->ACTIVATION_REJECT_GGSN:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->ACTIVATION_REJECT_UNSPECIFIED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->APN_TYPE_CONFLICT:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->INSUFFICIENT_RESOURCES:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->MISSING_UNKNOWN_APN:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->NSAPI_IN_USE:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->ONLY_IPV4_ALLOWED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->ONLY_IPV6_ALLOWED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->ONLY_SINGLE_BEARER_ALLOWED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->OPERATOR_BARRED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->PROTOCOL_ERRORS:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->SERVICE_OPTION_NOT_SUBSCRIBED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->SERVICE_OPTION_NOT_SUPPORTED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->SERVICE_OPTION_OUT_OF_ORDER:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->UNKNOWN_PDP_ADDRESS_TYPE:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcFailCause;->USER_AUTHENTICATION:Lcom/android/internal/telephony/dataconnection/DcFailCause;
+Lcom/android/internal/telephony/dataconnection/DcTracker$RecoveryAction;->isAggressiveRecovery(I)Z
+Lcom/android/internal/telephony/dataconnection/DcTracker;->cancelReconnectAlarm(Lcom/android/internal/telephony/dataconnection/ApnContext;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpAllConnections(Ljava/lang/String;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpAllConnections(ZLjava/lang/String;)Z
+Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpConnection(ZLcom/android/internal/telephony/dataconnection/ApnContext;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->createAllApnList()V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->getActiveApnTypes()[Ljava/lang/String;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->getOverallState()Lcom/android/internal/telephony/DctConstants$State;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->getUiccRecords(I)Lcom/android/internal/telephony/uicc/IccRecords;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->isConnected()Z
+Lcom/android/internal/telephony/dataconnection/DcTracker;->isDisconnected()Z
+Lcom/android/internal/telephony/dataconnection/DcTracker;->isOnlySingleDcAllowed(I)Z
+Lcom/android/internal/telephony/dataconnection/DcTracker;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mAllApnSettings:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mApnContexts:Ljava/util/concurrent/ConcurrentHashMap;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mAttached:Ljava/util/concurrent/atomic/AtomicBoolean;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mAutoAttachOnCreation:Ljava/util/concurrent/atomic/AtomicBoolean;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mDataConnectionTracker:Landroid/os/Handler;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mDisconnectPendingCount:I
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mIccRecords:Ljava/util/concurrent/atomic/AtomicReference;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mIsPsRestricted:Z
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mIsScreenOn:Z
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mNetStatPollEnabled:Z
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mNetStatPollPeriod:I
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mPhone:Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mPrioritySortedApnContexts:Ljava/util/PriorityQueue;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mProvisioningSpinner:Landroid/app/ProgressDialog;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mResolver:Landroid/content/ContentResolver;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mState:Lcom/android/internal/telephony/DctConstants$State;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->mSubscriptionManager:Landroid/telephony/SubscriptionManager;
+Lcom/android/internal/telephony/dataconnection/DcTracker;->notifyDataConnection(Ljava/lang/String;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->notifyOffApnsOfAvailability(Ljava/lang/String;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentDataStallAlarm(Landroid/content/Intent;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentProvisioningApnAlarm(Landroid/content/Intent;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->onCleanUpAllConnections(Ljava/lang/String;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->onRecordsLoadedOrSubIdChanged()V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->onSetUserDataEnabled(Z)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Lcom/android/internal/telephony/dataconnection/ApnContext;)Z
+Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Ljava/lang/String;)Z
+Lcom/android/internal/telephony/dataconnection/DcTracker;->registerForAllDataDisconnected(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->registerSettingsObserver()V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->resetPollStats()V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->restartDataStallAlarm()V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->setInitialAttachApn()V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->setInternalDataEnabled(ZLandroid/os/Message;)Z
+Lcom/android/internal/telephony/dataconnection/DcTracker;->setPreferredApn(I)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->setRadio(Z)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->setupDataOnConnectableApns(Ljava/lang/String;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->startDataStallAlarm(Z)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->startNetStatPoll()V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->stopDataStallAlarm()V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->stopNetStatPoll()V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->unregisterForAllDataDisconnected(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/dataconnection/DcTracker;->updateRecords()V
+Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity;
+Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity;
+Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity;
+Lcom/android/internal/telephony/DctConstants$Activity;->DORMANT:Lcom/android/internal/telephony/DctConstants$Activity;
+Lcom/android/internal/telephony/DctConstants$Activity;->values()[Lcom/android/internal/telephony/DctConstants$Activity;
+Lcom/android/internal/telephony/DctConstants$State;->CONNECTED:Lcom/android/internal/telephony/DctConstants$State;
+Lcom/android/internal/telephony/DctConstants$State;->CONNECTING:Lcom/android/internal/telephony/DctConstants$State;
+Lcom/android/internal/telephony/DctConstants$State;->DISCONNECTING:Lcom/android/internal/telephony/DctConstants$State;
+Lcom/android/internal/telephony/DctConstants$State;->FAILED:Lcom/android/internal/telephony/DctConstants$State;
+Lcom/android/internal/telephony/DctConstants$State;->IDLE:Lcom/android/internal/telephony/DctConstants$State;
+Lcom/android/internal/telephony/DctConstants$State;->RETRYING:Lcom/android/internal/telephony/DctConstants$State;
+Lcom/android/internal/telephony/DctConstants$State;->SCANNING:Lcom/android/internal/telephony/DctConstants$State;
+Lcom/android/internal/telephony/DctConstants$State;->values()[Lcom/android/internal/telephony/DctConstants$State;
+Lcom/android/internal/telephony/DefaultPhoneNotifier;->mRegistry:Lcom/android/internal/telephony/ITelephonyRegistry;
+Lcom/android/internal/telephony/DriverCall$State;->ACTIVE:Lcom/android/internal/telephony/DriverCall$State;
+Lcom/android/internal/telephony/DriverCall$State;->ALERTING:Lcom/android/internal/telephony/DriverCall$State;
+Lcom/android/internal/telephony/DriverCall$State;->DIALING:Lcom/android/internal/telephony/DriverCall$State;
+Lcom/android/internal/telephony/DriverCall$State;->HOLDING:Lcom/android/internal/telephony/DriverCall$State;
+Lcom/android/internal/telephony/DriverCall$State;->INCOMING:Lcom/android/internal/telephony/DriverCall$State;
+Lcom/android/internal/telephony/DriverCall$State;->values()[Lcom/android/internal/telephony/DriverCall$State;
+Lcom/android/internal/telephony/DriverCall$State;->WAITING:Lcom/android/internal/telephony/DriverCall$State;
+Lcom/android/internal/telephony/DriverCall;-><init>()V
+Lcom/android/internal/telephony/DriverCall;->index:I
+Lcom/android/internal/telephony/DriverCall;->isMT:Z
+Lcom/android/internal/telephony/DriverCall;->isVoice:Z
+Lcom/android/internal/telephony/DriverCall;->name:Ljava/lang/String;
+Lcom/android/internal/telephony/DriverCall;->number:Ljava/lang/String;
+Lcom/android/internal/telephony/DriverCall;->numberPresentation:I
+Lcom/android/internal/telephony/DriverCall;->state:Lcom/android/internal/telephony/DriverCall$State;
+Lcom/android/internal/telephony/gsm/GsmCellBroadcastHandler$SmsCbConcatInfo;-><init>(Lcom/android/internal/telephony/gsm/SmsCbHeader;Landroid/telephony/SmsCbLocation;)V
+Lcom/android/internal/telephony/gsm/GsmCellBroadcastHandler$SmsCbConcatInfo;->matchesLocation(Ljava/lang/String;II)Z
+Lcom/android/internal/telephony/gsm/GsmCellBroadcastHandler;->mSmsCbPageMap:Ljava/util/HashMap;
+Lcom/android/internal/telephony/gsm/GsmInboundSmsHandler;->acknowledgeLastIncomingSms(ZILandroid/os/Message;)V
+Lcom/android/internal/telephony/gsm/GsmMmiCode;-><init>(Lcom/android/internal/telephony/GsmCdmaPhone;Lcom/android/internal/telephony/uicc/UiccCardApplication;)V
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->getCLIRMode()I
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->getScString()Ljava/lang/CharSequence;
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->isActivate()Z
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->isDeactivate()Z
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->isErasure()Z
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->isInterrogate()Z
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->isRegister()Z
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->isServiceCodeCallBarring(Ljava/lang/String;)Z
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->isServiceCodeCallForwarding(Ljava/lang/String;)Z
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->isTemporaryModeCLIR()Z
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->makeEmptyNull(Ljava/lang/String;)Ljava/lang/String;
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->mDialingNumber:Ljava/lang/String;
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->mIccRecords:Lcom/android/internal/telephony/uicc/IccRecords;
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->mPhone:Lcom/android/internal/telephony/GsmCdmaPhone;
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->mSc:Ljava/lang/String;
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->mSia:Ljava/lang/String;
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->mSib:Ljava/lang/String;
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->mSic:Ljava/lang/String;
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->newFromDialString(Ljava/lang/String;Lcom/android/internal/telephony/GsmCdmaPhone;Lcom/android/internal/telephony/uicc/UiccCardApplication;)Lcom/android/internal/telephony/gsm/GsmMmiCode;
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->processCode()V
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->siToServiceClass(Ljava/lang/String;)I
+Lcom/android/internal/telephony/gsm/GsmMmiCode;->sPatternSuppService:Ljava/util/regex/Pattern;
+Lcom/android/internal/telephony/gsm/GsmSmsAddress;-><init>([BII)V
+Lcom/android/internal/telephony/gsm/GsmSmsAddress;->isCphsVoiceMessageClear()Z
+Lcom/android/internal/telephony/gsm/GsmSmsAddress;->isCphsVoiceMessageSet()Z
+Lcom/android/internal/telephony/gsm/GsmSMSDispatcher;->getFormat()Ljava/lang/String;
+Lcom/android/internal/telephony/gsm/GsmSMSDispatcher;->mGsmInboundSmsHandler:Lcom/android/internal/telephony/gsm/GsmInboundSmsHandler;
+Lcom/android/internal/telephony/gsm/GsmSMSDispatcher;->sendSms(Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
+Lcom/android/internal/telephony/gsm/SimTlv;-><init>([BII)V
+Lcom/android/internal/telephony/gsm/SimTlv;->getData()[B
+Lcom/android/internal/telephony/gsm/SimTlv;->getTag()I
+Lcom/android/internal/telephony/gsm/SimTlv;->isValidObject()Z
+Lcom/android/internal/telephony/gsm/SimTlv;->mHasValidTlvObject:Z
+Lcom/android/internal/telephony/gsm/SimTlv;->nextObject()Z
+Lcom/android/internal/telephony/gsm/SmsCbHeader;-><init>([B)V
+Lcom/android/internal/telephony/gsm/SmsCbHeader;->getGeographicalScope()I
+Lcom/android/internal/telephony/gsm/SmsCbHeader;->getNumberOfPages()I
+Lcom/android/internal/telephony/gsm/SmsCbHeader;->getPageIndex()I
+Lcom/android/internal/telephony/gsm/SmsCbHeader;->getSerialNumber()I
+Lcom/android/internal/telephony/gsm/SmsCbHeader;->getServiceCategory()I
+Lcom/android/internal/telephony/gsm/SmsCbHeader;->mMessageIdentifier:I
+Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;-><init>([B)V
+Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->getByte()I
+Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->getUserData()[B
+Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->getUserDataUCS2(I)Ljava/lang/String;
+Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->mCur:I
+Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->mPdu:[B
+Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->mUserDataSeptetPadding:I
+Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;-><init>()V
+Lcom/android/internal/telephony/gsm/SmsMessage;-><init>()V
+Lcom/android/internal/telephony/gsm/SmsMessage;->calculateLength(Ljava/lang/CharSequence;Z)Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;
+Lcom/android/internal/telephony/gsm/SmsMessage;->createFromEfRecord(I[B)Lcom/android/internal/telephony/gsm/SmsMessage;
+Lcom/android/internal/telephony/gsm/SmsMessage;->createFromPdu([B)Lcom/android/internal/telephony/gsm/SmsMessage;
+Lcom/android/internal/telephony/gsm/SmsMessage;->encodeUCS2(Ljava/lang/String;[B)[B
+Lcom/android/internal/telephony/gsm/SmsMessage;->getStatus()I
+Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
+Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZI)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
+Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z[B)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
+Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z[BIII)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
+Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z[BIIII)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
+Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPduHead(Ljava/lang/String;Ljava/lang/String;BZLcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;)Ljava/io/ByteArrayOutputStream;
+Lcom/android/internal/telephony/gsm/SmsMessage;->isMWIClearMessage()Z
+Lcom/android/internal/telephony/gsm/SmsMessage;->isMwiDontStore()Z
+Lcom/android/internal/telephony/gsm/SmsMessage;->isMWISetMessage()Z
+Lcom/android/internal/telephony/gsm/SmsMessage;->isStatusReportMessage()Z
+Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->loadEfFilesFromUsim()Ljava/util/ArrayList;
+Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->mFh:Lcom/android/internal/telephony/uicc/IccFileHandler;
+Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->mLock:Ljava/lang/Object;
+Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->mPhoneBookRecords:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->reset()V
 Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;-><init>()V
+Lcom/android/internal/telephony/GsmCdmaCall;->attachFake(Lcom/android/internal/telephony/Connection;Lcom/android/internal/telephony/Call$State;)V
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->clearDisconnected()V
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->dialThreeWay(Ljava/lang/String;)Lcom/android/internal/telephony/Connection;
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->disableDataCallInEmergencyCall(Ljava/lang/String;)V
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->fakeHoldForegroundBeforeDial()V
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->getPhone()Lcom/android/internal/telephony/GsmCdmaPhone;
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->handleEcmTimer(I)V
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->isPhoneTypeGsm()Z
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->mBackgroundCall:Lcom/android/internal/telephony/GsmCdmaCall;
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->mForegroundCall:Lcom/android/internal/telephony/GsmCdmaCall;
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->mPendingMO:Lcom/android/internal/telephony/GsmCdmaConnection;
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->mPhone:Lcom/android/internal/telephony/GsmCdmaPhone;
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->mRingingCall:Lcom/android/internal/telephony/GsmCdmaCall;
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->mState:Lcom/android/internal/telephony/PhoneConstants$State;
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->obtainCompleteMessage()Landroid/os/Message;
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->obtainCompleteMessage(I)Landroid/os/Message;
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->setMute(Z)V
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->switchWaitingOrHoldingAndActive()V
+Lcom/android/internal/telephony/GsmCdmaCallTracker;->updatePhoneState()V
+Lcom/android/internal/telephony/GsmCdmaConnection$MyHandler;-><init>(Lcom/android/internal/telephony/GsmCdmaConnection;Landroid/os/Looper;)V
+Lcom/android/internal/telephony/GsmCdmaConnection;->acquireWakeLock()V
+Lcom/android/internal/telephony/GsmCdmaConnection;->createWakeLock(Landroid/content/Context;)V
+Lcom/android/internal/telephony/GsmCdmaConnection;->disconnectCauseFromCode(I)I
+Lcom/android/internal/telephony/GsmCdmaConnection;->fetchDtmfToneDelay(Lcom/android/internal/telephony/GsmCdmaPhone;)V
+Lcom/android/internal/telephony/GsmCdmaConnection;->findNextPCharOrNonPOrNonWCharIndex(Ljava/lang/String;I)I
+Lcom/android/internal/telephony/GsmCdmaConnection;->findPOrWCharToAppend(Ljava/lang/String;II)C
+Lcom/android/internal/telephony/GsmCdmaConnection;->formatDialString(Ljava/lang/String;)Ljava/lang/String;
+Lcom/android/internal/telephony/GsmCdmaConnection;->getState()Lcom/android/internal/telephony/Call$State;
+Lcom/android/internal/telephony/GsmCdmaConnection;->isPause(C)Z
+Lcom/android/internal/telephony/GsmCdmaConnection;->isPhoneTypeGsm()Z
+Lcom/android/internal/telephony/GsmCdmaConnection;->isWait(C)Z
+Lcom/android/internal/telephony/GsmCdmaConnection;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/GsmCdmaConnection;->maskDialString(Ljava/lang/String;)Ljava/lang/String;
+Lcom/android/internal/telephony/GsmCdmaConnection;->mIndex:I
+Lcom/android/internal/telephony/GsmCdmaConnection;->mOwner:Lcom/android/internal/telephony/GsmCdmaCallTracker;
+Lcom/android/internal/telephony/GsmCdmaConnection;->onConnectedInOrOut()V
+Lcom/android/internal/telephony/GsmCdmaConnection;->updateParent(Lcom/android/internal/telephony/GsmCdmaCall;Lcom/android/internal/telephony/GsmCdmaCall;)V
+Lcom/android/internal/telephony/GsmCdmaPhone$Cfu;-><init>(Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/GsmCdmaPhone;->exitEmergencyCallbackMode()V
+Lcom/android/internal/telephony/GsmCdmaPhone;->getCallTracker()Lcom/android/internal/telephony/CallTracker;
+Lcom/android/internal/telephony/GsmCdmaPhone;->getCdmaEriText()Ljava/lang/String;
+Lcom/android/internal/telephony/GsmCdmaPhone;->getEsn()Ljava/lang/String;
+Lcom/android/internal/telephony/GsmCdmaPhone;->getLine1Number()Ljava/lang/String;
+Lcom/android/internal/telephony/GsmCdmaPhone;->getPhoneType()I
+Lcom/android/internal/telephony/GsmCdmaPhone;->getServiceState()Landroid/telephony/ServiceState;
+Lcom/android/internal/telephony/GsmCdmaPhone;->getState()Lcom/android/internal/telephony/PhoneConstants$State;
+Lcom/android/internal/telephony/GsmCdmaPhone;->getSystemProperty(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+Lcom/android/internal/telephony/GsmCdmaPhone;->handleInCallMmiCommands(Ljava/lang/String;)Z
+Lcom/android/internal/telephony/GsmCdmaPhone;->isCfEnable(I)Z
+Lcom/android/internal/telephony/GsmCdmaPhone;->isEriFileLoaded()Z
+Lcom/android/internal/telephony/GsmCdmaPhone;->isInCall()Z
+Lcom/android/internal/telephony/GsmCdmaPhone;->isManualSelProhibitedInGlobalMode()Z
+Lcom/android/internal/telephony/GsmCdmaPhone;->isPhoneTypeGsm()Z
+Lcom/android/internal/telephony/GsmCdmaPhone;->isValidCommandInterfaceCFAction(I)Z
+Lcom/android/internal/telephony/GsmCdmaPhone;->isValidCommandInterfaceCFReason(I)Z
+Lcom/android/internal/telephony/GsmCdmaPhone;->logd(Ljava/lang/String;)V
+Lcom/android/internal/telephony/GsmCdmaPhone;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/GsmCdmaPhone;->mCT:Lcom/android/internal/telephony/GsmCdmaCallTracker;
+Lcom/android/internal/telephony/GsmCdmaPhone;->mEcmExitRespRegistrant:Landroid/os/Registrant;
+Lcom/android/internal/telephony/GsmCdmaPhone;->mEriManager:Lcom/android/internal/telephony/cdma/EriManager;
+Lcom/android/internal/telephony/GsmCdmaPhone;->mIccSmsInterfaceManager:Lcom/android/internal/telephony/IccSmsInterfaceManager;
+Lcom/android/internal/telephony/GsmCdmaPhone;->mIsimUiccRecords:Lcom/android/internal/telephony/uicc/IsimUiccRecords;
+Lcom/android/internal/telephony/GsmCdmaPhone;->mPendingMMIs:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/GsmCdmaPhone;->mSST:Lcom/android/internal/telephony/ServiceStateTracker;
+Lcom/android/internal/telephony/GsmCdmaPhone;->notifyPreciseCallStateChanged()V
+Lcom/android/internal/telephony/GsmCdmaPhone;->notifyServiceStateChanged(Landroid/telephony/ServiceState;)V
+Lcom/android/internal/telephony/GsmCdmaPhone;->setOnEcbModeExitResponse(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/GsmCdmaPhone;->syncClirSetting()V
 Lcom/android/internal/telephony/ICarrierConfigLoader;->getConfigForSubId(ILjava/lang/String;)Landroid/os/PersistableBundle;
+Lcom/android/internal/telephony/IccCard;->getState()Lcom/android/internal/telephony/IccCardConstants$State;
+Lcom/android/internal/telephony/IccCard;->registerForNetworkLocked(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/IccCard;->supplyNetworkDepersonalization(Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/IccCard;->supplyPin(Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/IccCard;->supplyPuk(Ljava/lang/String;Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/IccCardConstants$State;->ABSENT:Lcom/android/internal/telephony/IccCardConstants$State;
+Lcom/android/internal/telephony/IccCardConstants$State;->CARD_IO_ERROR:Lcom/android/internal/telephony/IccCardConstants$State;
+Lcom/android/internal/telephony/IccCardConstants$State;->NETWORK_LOCKED:Lcom/android/internal/telephony/IccCardConstants$State;
+Lcom/android/internal/telephony/IccCardConstants$State;->NOT_READY:Lcom/android/internal/telephony/IccCardConstants$State;
+Lcom/android/internal/telephony/IccCardConstants$State;->PERM_DISABLED:Lcom/android/internal/telephony/IccCardConstants$State;
+Lcom/android/internal/telephony/IccCardConstants$State;->PIN_REQUIRED:Lcom/android/internal/telephony/IccCardConstants$State;
+Lcom/android/internal/telephony/IccCardConstants$State;->PUK_REQUIRED:Lcom/android/internal/telephony/IccCardConstants$State;
+Lcom/android/internal/telephony/IccCardConstants$State;->READY:Lcom/android/internal/telephony/IccCardConstants$State;
+Lcom/android/internal/telephony/IccCardConstants$State;->UNKNOWN:Lcom/android/internal/telephony/IccCardConstants$State;
+Lcom/android/internal/telephony/IccCardConstants$State;->values()[Lcom/android/internal/telephony/IccCardConstants$State;
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->checkThread()V
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->DBG:Z
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->logd(Ljava/lang/String;)V
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mAdnCache:Lcom/android/internal/telephony/uicc/AdnRecordCache;
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mBaseHandler:Landroid/os/Handler;
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mCurrentApp:Lcom/android/internal/telephony/uicc/UiccCardApplication;
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mIs3gCard:Z
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mLock:Ljava/lang/Object;
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mPhone:Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mRecords:Ljava/util/List;
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mRecordSize:[I
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mSuccess:Z
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->updateEfForIccType(I)I
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->waitForResult(Ljava/util/concurrent/atomic/AtomicBoolean;)V
+Lcom/android/internal/telephony/IccProvider;-><init>()V
+Lcom/android/internal/telephony/IccProvider;->ADDRESS_BOOK_COLUMN_NAMES:[Ljava/lang/String;
+Lcom/android/internal/telephony/IccProvider;->DBG:Z
+Lcom/android/internal/telephony/IccProvider;->loadRecord(Lcom/android/internal/telephony/uicc/AdnRecord;Landroid/database/MatrixCursor;I)V
+Lcom/android/internal/telephony/IccProvider;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->copyMessageToIccEf(Ljava/lang/String;I[B[B)Z
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->disableCdmaBroadcastRange(II)Z
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->disableGsmBroadcastRange(II)Z
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->enableCdmaBroadcastRange(II)Z
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->enableGsmBroadcastRange(II)Z
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->enforceReceiveAndSend(Ljava/lang/String;)V
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->filterDestAddress(Ljava/lang/String;)Ljava/lang/String;
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->getAllMessagesFromIccEf(Ljava/lang/String;)Ljava/util/List;
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->getImsSmsFormat()Ljava/lang/String;
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->getPremiumSmsPermission(Ljava/lang/String;)I
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->injectSmsPdu([BLjava/lang/String;Landroid/app/PendingIntent;)V
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->isImsSmsSupported()Z
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->mAppOps:Landroid/app/AppOpsManager;
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->mCellBroadcastRangeManager:Lcom/android/internal/telephony/IccSmsInterfaceManager$CellBroadcastRangeManager;
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->mHandler:Landroid/os/Handler;
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->mLock:Ljava/lang/Object;
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->mPhone:Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->mSms:Ljava/util/List;
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->mSuccess:Z
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->sendData(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I[BLandroid/app/PendingIntent;Landroid/app/PendingIntent;)V
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->sendStoredMultipartText(Ljava/lang/String;Landroid/net/Uri;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->sendStoredText(Ljava/lang/String;Landroid/net/Uri;Ljava/lang/String;Landroid/app/PendingIntent;Landroid/app/PendingIntent;)V
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->setCdmaBroadcastConfig([Lcom/android/internal/telephony/cdma/CdmaSmsBroadcastConfigInfo;)Z
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->setCellBroadcastConfig([Lcom/android/internal/telephony/gsm/SmsBroadcastConfigInfo;)Z
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->setPremiumSmsPermission(Ljava/lang/String;I)V
+Lcom/android/internal/telephony/IccSmsInterfaceManager;->updateMessageOnIccEf(Ljava/lang/String;II[B)Z
+Lcom/android/internal/telephony/IIccPhoneBook$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Lcom/android/internal/telephony/IIccPhoneBook$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IIccPhoneBook;
+Lcom/android/internal/telephony/IIccPhoneBook;->getAdnRecordsInEf(I)Ljava/util/List;
+Lcom/android/internal/telephony/IIccPhoneBook;->getAdnRecordsInEfForSubscriber(II)Ljava/util/List;
+Lcom/android/internal/telephony/IIccPhoneBook;->getAdnRecordsSize(I)[I
+Lcom/android/internal/telephony/IIccPhoneBook;->getAdnRecordsSizeForSubscriber(II)[I
+Lcom/android/internal/telephony/IIccPhoneBook;->updateAdnRecordsInEfBySearch(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
 Lcom/android/internal/telephony/IMms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IMms;
+Lcom/android/internal/telephony/imsphone/ImsExternalCall;-><init>(Lcom/android/internal/telephony/Phone;Lcom/android/internal/telephony/imsphone/ImsExternalConnection;)V
+Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker$ExternalCallStateListener;-><init>(Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker;)V
+Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker$ExternalConnectionListener;-><init>(Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker;)V
+Lcom/android/internal/telephony/imsphone/ImsExternalConnection;->rebuildCapabilities()V
+Lcom/android/internal/telephony/imsphone/ImsExternalConnection;->setActive()V
+Lcom/android/internal/telephony/imsphone/ImsPhone$Cf;-><init>(Ljava/lang/String;ZLandroid/os/Message;)V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->getActionFromCFAction(I)I
+Lcom/android/internal/telephony/imsphone/ImsPhone;->getBackgroundCall()Lcom/android/internal/telephony/imsphone/ImsPhoneCall;
+Lcom/android/internal/telephony/imsphone/ImsPhone;->getCallForwardingOption(ILandroid/os/Message;)V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->getCallWaiting(Landroid/os/Message;)V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->getConditionFromCFReason(I)I
+Lcom/android/internal/telephony/imsphone/ImsPhone;->getForegroundCall()Lcom/android/internal/telephony/imsphone/ImsPhoneCall;
+Lcom/android/internal/telephony/imsphone/ImsPhone;->getRingingCall()Lcom/android/internal/telephony/imsphone/ImsPhoneCall;
+Lcom/android/internal/telephony/imsphone/ImsPhone;->getServiceState()Landroid/telephony/ServiceState;
+Lcom/android/internal/telephony/imsphone/ImsPhone;->getState()Lcom/android/internal/telephony/PhoneConstants$State;
+Lcom/android/internal/telephony/imsphone/ImsPhone;->handleEnterEmergencyCallbackMode()V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->handleExitEmergencyCallbackMode()V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->handleInCallMmiCommands(Ljava/lang/String;)Z
+Lcom/android/internal/telephony/imsphone/ImsPhone;->isCfEnable(I)Z
+Lcom/android/internal/telephony/imsphone/ImsPhone;->isUtEnabled()Z
+Lcom/android/internal/telephony/imsphone/ImsPhone;->isValidCommandInterfaceCFAction(I)Z
+Lcom/android/internal/telephony/imsphone/ImsPhone;->isValidCommandInterfaceCFReason(I)Z
+Lcom/android/internal/telephony/imsphone/ImsPhone;->isVolteEnabled()Z
+Lcom/android/internal/telephony/imsphone/ImsPhone;->mCT:Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;
+Lcom/android/internal/telephony/imsphone/ImsPhone;->mPendingMMIs:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/imsphone/ImsPhone;->mSS:Landroid/telephony/ServiceState;
+Lcom/android/internal/telephony/imsphone/ImsPhone;->notifyCallForwardingIndicator()V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->notifyPreciseCallStateChanged()V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->notifyUnknownConnection(Lcom/android/internal/telephony/Connection;)V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->onMMIDone(Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;)V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->sendErrorResponse(Landroid/os/Message;)V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->sendErrorResponse(Landroid/os/Message;Ljava/lang/Throwable;)V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->setCallForwardingOption(IILjava/lang/String;IILandroid/os/Message;)V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->setCallWaiting(ZLandroid/os/Message;)V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->setImsRegistered(Z)V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->setOnEcbModeExitResponse(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/imsphone/ImsPhone;->setServiceState(I)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCall;->attach(Lcom/android/internal/telephony/Connection;Lcom/android/internal/telephony/Call$State;)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCall;->attachFake(Lcom/android/internal/telephony/Connection;Lcom/android/internal/telephony/Call$State;)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCall;->getConnections()Ljava/util/List;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCall;->getImsCall()Lcom/android/ims/ImsCall;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCall;->hangup()V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCall;->merge(Lcom/android/internal/telephony/imsphone/ImsPhoneCall;Lcom/android/internal/telephony/Call$State;)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCall;->onHangupLocal()V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->addConnection(Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->clearDisconnected()V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->dial(Ljava/lang/String;ILandroid/os/Bundle;)Lcom/android/internal/telephony/Connection;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->dialPendingMO()V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->findConnection(Lcom/android/ims/ImsCall;)Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->getEcbmInterface()Lcom/android/ims/ImsEcbm;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->getUtInterface()Lcom/android/ims/ImsUtInterface;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->handleEcmTimer(I)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mAllowEmergencyVideoCalls:Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mBackgroundCall:Lcom/android/internal/telephony/imsphone/ImsPhoneCall;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mCallExpectedToResume:Lcom/android/ims/ImsCall;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mConnections:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mForegroundCall:Lcom/android/internal/telephony/imsphone/ImsPhoneCall;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mHandoverCall:Lcom/android/internal/telephony/imsphone/ImsPhoneCall;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mImsCallListener:Lcom/android/ims/ImsCall$Listener;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mImsManager:Lcom/android/ims/ImsManager;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mOnHoldToneId:I
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mOnHoldToneStarted:Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mPendingMO:Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mPendingUssd:Landroid/os/Message;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mPhone:Lcom/android/internal/telephony/imsphone/ImsPhone;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mRingingCall:Lcom/android/internal/telephony/imsphone/ImsPhoneCall;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mSwitchingFgAndBgCalls:Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mSyncHold:Ljava/lang/Object;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mUssdSession:Lcom/android/ims/ImsCall;
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->processCallStateChange(Lcom/android/ims/ImsCall;Lcom/android/internal/telephony/Call$State;I)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->processCallStateChange(Lcom/android/ims/ImsCall;Lcom/android/internal/telephony/Call$State;IZ)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->removeConnection(Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->setVideoCallProvider(Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;Lcom/android/ims/ImsCall;)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->switchAfterConferenceSuccess()V
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->updatePhoneState()V
+Lcom/android/internal/telephony/imsphone/ImsPhoneConnection$MyHandler;-><init>(Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;Landroid/os/Looper;)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->acquireWakeLock()V
+Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->createWakeLock(Landroid/content/Context;)V
+Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->getCall()Lcom/android/internal/telephony/imsphone/ImsPhoneCall;
+Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->getOwner()Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;
+Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->isMultiparty()Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->mDisconnected:Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->mImsCall:Lcom/android/ims/ImsCall;
+Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->mOwner:Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;
+Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->mParent:Lcom/android/internal/telephony/imsphone/ImsPhoneCall;
+Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->onDisconnect()Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->update(Lcom/android/ims/ImsCall;Lcom/android/internal/telephony/Call$State;)Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->getCLIRMode()I
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->getDialingNumber()Ljava/lang/String;
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->getErrorMessage(Landroid/os/AsyncResult;)Ljava/lang/CharSequence;
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->getScString()Ljava/lang/CharSequence;
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->isActivate()Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->isDeactivate()Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->isEmptyOrNull(Ljava/lang/CharSequence;)Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->isErasure()Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->isRegister()Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->isSupportedOverImsPhone()Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->isTemporaryModeCLIR()Z
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->mPhone:Lcom/android/internal/telephony/imsphone/ImsPhone;
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->newFromDialString(Ljava/lang/String;Lcom/android/internal/telephony/imsphone/ImsPhone;)Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->processCode()V
+Lcom/android/internal/telephony/imsphone/ImsPhoneMmiCode;->serviceClassToCFString(I)Ljava/lang/CharSequence;
+Lcom/android/internal/telephony/InboundSmsHandler$SmsBroadcastReceiver;-><init>(Lcom/android/internal/telephony/InboundSmsHandler;Lcom/android/internal/telephony/InboundSmsTracker;)V
+Lcom/android/internal/telephony/InboundSmsHandler$SmsBroadcastReceiver;->mDeleteWhere:Ljava/lang/String;
+Lcom/android/internal/telephony/InboundSmsHandler$SmsBroadcastReceiver;->mDeleteWhereArgs:[Ljava/lang/String;
+Lcom/android/internal/telephony/InboundSmsHandler;->acknowledgeLastIncomingSms(ZILandroid/os/Message;)V
+Lcom/android/internal/telephony/InboundSmsHandler;->deleteFromRawTable(Ljava/lang/String;[Ljava/lang/String;I)V
+Lcom/android/internal/telephony/InboundSmsHandler;->dispatchIntent(Landroid/content/Intent;Ljava/lang/String;ILandroid/os/Bundle;Landroid/content/BroadcastReceiver;Landroid/os/UserHandle;)V
+Lcom/android/internal/telephony/InboundSmsHandler;->dispatchNormalMessage(Lcom/android/internal/telephony/SmsMessageBase;)I
+Lcom/android/internal/telephony/InboundSmsHandler;->getPhone()Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/InboundSmsHandler;->handleInjectSms(Landroid/os/AsyncResult;)V
+Lcom/android/internal/telephony/InboundSmsHandler;->handleNewSms(Landroid/os/AsyncResult;)V
+Lcom/android/internal/telephony/InboundSmsHandler;->handleSmsWhitelisting(Landroid/content/ComponentName;)Landroid/os/Bundle;
+Lcom/android/internal/telephony/InboundSmsHandler;->isSkipNotifyFlagSet(I)Z
+Lcom/android/internal/telephony/InboundSmsHandler;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/InboundSmsHandler;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/InboundSmsHandler;->mCellBroadcastHandler:Lcom/android/internal/telephony/CellBroadcastHandler;
+Lcom/android/internal/telephony/InboundSmsHandler;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/InboundSmsHandler;->mDeliveringState:Lcom/android/internal/telephony/InboundSmsHandler$DeliveringState;
+Lcom/android/internal/telephony/InboundSmsHandler;->mDeviceIdleController:Landroid/os/IDeviceIdleController;
+Lcom/android/internal/telephony/InboundSmsHandler;->mIdleState:Lcom/android/internal/telephony/InboundSmsHandler$IdleState;
+Lcom/android/internal/telephony/InboundSmsHandler;->mPhone:Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/InboundSmsHandler;->mResolver:Landroid/content/ContentResolver;
+Lcom/android/internal/telephony/InboundSmsHandler;->mUserManager:Landroid/os/UserManager;
+Lcom/android/internal/telephony/InboundSmsHandler;->mWaitingState:Lcom/android/internal/telephony/InboundSmsHandler$WaitingState;
+Lcom/android/internal/telephony/InboundSmsHandler;->mWakeLock:Landroid/os/PowerManager$WakeLock;
+Lcom/android/internal/telephony/InboundSmsHandler;->mWapPush:Lcom/android/internal/telephony/WapPushOverSms;
+Lcom/android/internal/telephony/InboundSmsHandler;->processMessagePart(Lcom/android/internal/telephony/InboundSmsTracker;)Z
+Lcom/android/internal/telephony/InboundSmsHandler;->showNewMessageNotification()V
+Lcom/android/internal/telephony/InboundSmsHandler;->writeInboxMessage(Landroid/content/Intent;)Landroid/net/Uri;
+Lcom/android/internal/telephony/InboundSmsTracker;->getFormat()Ljava/lang/String;
+Lcom/android/internal/telephony/InboundSmsTracker;->getIndexOffset()I
+Lcom/android/internal/telephony/IntRangeManager;->mRanges:Ljava/util/ArrayList;
 Lcom/android/internal/telephony/IPhoneStateListener$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneStateListener;
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
@@ -2062,16 +3272,808 @@
 Lcom/android/internal/telephony/IWapPushManager;->addPackage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZZ)Z
 Lcom/android/internal/telephony/IWapPushManager;->deletePackage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
 Lcom/android/internal/telephony/IWapPushManager;->updatePackage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZZ)Z
+Lcom/android/internal/telephony/MccTable$MccEntry;->mIso:Ljava/lang/String;
+Lcom/android/internal/telephony/MccTable;->countryCodeForMcc(I)Ljava/lang/String;
+Lcom/android/internal/telephony/MccTable;->defaultLanguageForMcc(I)Ljava/lang/String;
+Lcom/android/internal/telephony/MccTable;->defaultTimeZoneForMcc(I)Ljava/lang/String;
+Lcom/android/internal/telephony/MccTable;->entryForMcc(I)Lcom/android/internal/telephony/MccTable$MccEntry;
+Lcom/android/internal/telephony/MccTable;->getLocaleForLanguageCountry(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
+Lcom/android/internal/telephony/MccTable;->smallestDigitsMccForMnc(I)I
+Lcom/android/internal/telephony/MmiCode$State;->CANCELLED:Lcom/android/internal/telephony/MmiCode$State;
+Lcom/android/internal/telephony/MmiCode$State;->COMPLETE:Lcom/android/internal/telephony/MmiCode$State;
+Lcom/android/internal/telephony/MmiCode$State;->FAILED:Lcom/android/internal/telephony/MmiCode$State;
+Lcom/android/internal/telephony/MmiCode$State;->PENDING:Lcom/android/internal/telephony/MmiCode$State;
+Lcom/android/internal/telephony/MmiCode;->getPhone()Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/Phone;->dispose()V
+Lcom/android/internal/telephony/Phone;->exitEmergencyCallbackMode()V
+Lcom/android/internal/telephony/Phone;->getActiveApnTypes()[Ljava/lang/String;
+Lcom/android/internal/telephony/Phone;->getCallTracker()Lcom/android/internal/telephony/CallTracker;
+Lcom/android/internal/telephony/Phone;->getCellLocation()Landroid/telephony/CellLocation;
+Lcom/android/internal/telephony/Phone;->getContext()Landroid/content/Context;
+Lcom/android/internal/telephony/Phone;->getDataConnectionState()Lcom/android/internal/telephony/PhoneConstants$DataState;
+Lcom/android/internal/telephony/Phone;->getIccCard()Lcom/android/internal/telephony/IccCard;
+Lcom/android/internal/telephony/Phone;->getIccFileHandler()Lcom/android/internal/telephony/uicc/IccFileHandler;
+Lcom/android/internal/telephony/Phone;->getIccSerialNumber()Ljava/lang/String;
+Lcom/android/internal/telephony/Phone;->getIccSmsInterfaceManager()Lcom/android/internal/telephony/IccSmsInterfaceManager;
+Lcom/android/internal/telephony/Phone;->getImsPhone()Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/Phone;->getIsimRecords()Lcom/android/internal/telephony/uicc/IsimRecords;
+Lcom/android/internal/telephony/Phone;->getMsisdn()Ljava/lang/String;
+Lcom/android/internal/telephony/Phone;->getNai()Ljava/lang/String;
+Lcom/android/internal/telephony/Phone;->getPhoneId()I
+Lcom/android/internal/telephony/Phone;->getPhoneName()Ljava/lang/String;
+Lcom/android/internal/telephony/Phone;->getPhoneType()I
+Lcom/android/internal/telephony/Phone;->getServiceStateTracker()Lcom/android/internal/telephony/ServiceStateTracker;
+Lcom/android/internal/telephony/Phone;->getSmscAddress(Landroid/os/Message;)V
+Lcom/android/internal/telephony/Phone;->getState()Lcom/android/internal/telephony/PhoneConstants$State;
+Lcom/android/internal/telephony/Phone;->getSubId()I
+Lcom/android/internal/telephony/Phone;->getSystemProperty(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+Lcom/android/internal/telephony/Phone;->getUiccCard()Lcom/android/internal/telephony/uicc/UiccCard;
+Lcom/android/internal/telephony/Phone;->getVideoState(Lcom/android/internal/telephony/Call;)I
+Lcom/android/internal/telephony/Phone;->invokeOemRilRequestRaw([BLandroid/os/Message;)V
+Lcom/android/internal/telephony/Phone;->invokeOemRilRequestStrings([Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/Phone;->isCspPlmnEnabled()Z
+Lcom/android/internal/telephony/Phone;->isUtEnabled()Z
+Lcom/android/internal/telephony/Phone;->isVideoEnabled()Z
+Lcom/android/internal/telephony/Phone;->isVolteEnabled()Z
+Lcom/android/internal/telephony/Phone;->isWifiCallingEnabled()Z
+Lcom/android/internal/telephony/Phone;->mCi:Lcom/android/internal/telephony/CommandsInterface;
+Lcom/android/internal/telephony/Phone;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/Phone;->mDcTracker:Lcom/android/internal/telephony/dataconnection/DcTracker;
+Lcom/android/internal/telephony/Phone;->mIccRecords:Ljava/util/concurrent/atomic/AtomicReference;
+Lcom/android/internal/telephony/Phone;->mImsPhone:Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/Phone;->mMmiRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/Phone;->mNotifier:Lcom/android/internal/telephony/PhoneNotifier;
+Lcom/android/internal/telephony/Phone;->mPhoneId:I
+Lcom/android/internal/telephony/Phone;->mSmsStorageMonitor:Lcom/android/internal/telephony/SmsStorageMonitor;
+Lcom/android/internal/telephony/Phone;->mUiccApplication:Ljava/util/concurrent/atomic/AtomicReference;
+Lcom/android/internal/telephony/Phone;->mUiccController:Lcom/android/internal/telephony/uicc/UiccController;
+Lcom/android/internal/telephony/Phone;->needsOtaServiceProvisioning()Z
+Lcom/android/internal/telephony/Phone;->notifyOtaspChanged(I)V
+Lcom/android/internal/telephony/Phone;->registerForDisconnect(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->registerForEcmTimerReset(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->registerForIncomingRing(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->registerForMmiComplete(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->registerForMmiInitiate(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->registerForNewRingingConnection(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->registerForPreciseCallStateChanged(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->registerForRingbackTone(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->registerForServiceStateChanged(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->registerForSimRecordsLoaded(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->registerForUnknownConnection(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->selectNetworkManually(Lcom/android/internal/telephony/OperatorInfo;ZLandroid/os/Message;)V
+Lcom/android/internal/telephony/Phone;->setNetworkSelectionModeAutomatic(Landroid/os/Message;)V
+Lcom/android/internal/telephony/Phone;->setOnEcbModeExitResponse(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->setOnPostDialCharacter(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/Phone;->setPreferredNetworkType(ILandroid/os/Message;)V
+Lcom/android/internal/telephony/Phone;->setSmscAddress(Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/Phone;->unregisterForDisconnect(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/Phone;->unregisterForEcmTimerReset(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/Phone;->unregisterForIncomingRing(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/Phone;->unregisterForMmiComplete(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/Phone;->unregisterForMmiInitiate(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/Phone;->unregisterForNewRingingConnection(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/Phone;->unregisterForPreciseCallStateChanged(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/Phone;->unregisterForRingbackTone(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/Phone;->unregisterForServiceStateChanged(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/Phone;->unregisterForSimRecordsLoaded(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/Phone;->unregisterForUnknownConnection(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/Phone;->unsetOnEcbModeExitResponse(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/PhoneConstants$DataState;->CONNECTED:Lcom/android/internal/telephony/PhoneConstants$DataState;
+Lcom/android/internal/telephony/PhoneConstants$DataState;->CONNECTING:Lcom/android/internal/telephony/PhoneConstants$DataState;
+Lcom/android/internal/telephony/PhoneConstants$DataState;->DISCONNECTED:Lcom/android/internal/telephony/PhoneConstants$DataState;
+Lcom/android/internal/telephony/PhoneConstants$DataState;->SUSPENDED:Lcom/android/internal/telephony/PhoneConstants$DataState;
+Lcom/android/internal/telephony/PhoneConstants$DataState;->values()[Lcom/android/internal/telephony/PhoneConstants$DataState;
+Lcom/android/internal/telephony/PhoneConstants$State;->IDLE:Lcom/android/internal/telephony/PhoneConstants$State;
+Lcom/android/internal/telephony/PhoneConstants$State;->OFFHOOK:Lcom/android/internal/telephony/PhoneConstants$State;
+Lcom/android/internal/telephony/PhoneConstants$State;->RINGING:Lcom/android/internal/telephony/PhoneConstants$State;
+Lcom/android/internal/telephony/PhoneConstants$State;->values()[Lcom/android/internal/telephony/PhoneConstants$State;
+Lcom/android/internal/telephony/PhoneConstants;->PRESENTATION_ALLOWED:I
+Lcom/android/internal/telephony/PhoneConstants;->PRESENTATION_PAYPHONE:I
+Lcom/android/internal/telephony/PhoneConstants;->PRESENTATION_RESTRICTED:I
+Lcom/android/internal/telephony/PhoneConstants;->PRESENTATION_UNKNOWN:I
+Lcom/android/internal/telephony/PhoneFactory;->calculatePreferredNetworkType(Landroid/content/Context;I)I
+Lcom/android/internal/telephony/PhoneFactory;->getDefaultPhone()Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/PhoneFactory;->getDefaultSubscription()I
+Lcom/android/internal/telephony/PhoneFactory;->getPhone(I)Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/PhoneFactory;->getPhones()[Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/PhoneFactory;->makeDefaultPhone(Landroid/content/Context;)V
+Lcom/android/internal/telephony/PhoneFactory;->sCommandsInterface:Lcom/android/internal/telephony/CommandsInterface;
+Lcom/android/internal/telephony/PhoneFactory;->sContext:Landroid/content/Context;
+Lcom/android/internal/telephony/PhoneFactory;->sMadeDefaults:Z
+Lcom/android/internal/telephony/PhoneFactory;->sPhoneNotifier:Lcom/android/internal/telephony/PhoneNotifier;
+Lcom/android/internal/telephony/PhoneInternalInterface$DataActivityState;->NONE:Lcom/android/internal/telephony/PhoneInternalInterface$DataActivityState;
+Lcom/android/internal/telephony/PhoneInternalInterface;->PREFERRED_NT_MODE:I
+Lcom/android/internal/telephony/PhoneNotifier;->notifyMessageWaitingChanged(Lcom/android/internal/telephony/Phone;)V
+Lcom/android/internal/telephony/PhoneNotifier;->notifySignalStrength(Lcom/android/internal/telephony/Phone;)V
+Lcom/android/internal/telephony/PhoneStateIntentReceiver;-><init>(Landroid/content/Context;Landroid/os/Handler;)V
+Lcom/android/internal/telephony/PhoneStateIntentReceiver;->getSignalStrengthDbm()I
+Lcom/android/internal/telephony/PhoneStateIntentReceiver;->mSignalStrength:Landroid/telephony/SignalStrength;
+Lcom/android/internal/telephony/PhoneStateIntentReceiver;->mWants:I
+Lcom/android/internal/telephony/PhoneStateIntentReceiver;->notifyServiceState(I)V
+Lcom/android/internal/telephony/PhoneStateIntentReceiver;->notifySignalStrength(I)V
+Lcom/android/internal/telephony/PhoneStateIntentReceiver;->registerIntent()V
+Lcom/android/internal/telephony/PhoneStateIntentReceiver;->unregisterIntent()V
+Lcom/android/internal/telephony/PhoneSubInfoController;->getDefaultSubscription()I
+Lcom/android/internal/telephony/PhoneSubInfoController;->getPhone(I)Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/PhoneSubInfoController;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/PhoneSubInfoController;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/PhoneSubInfoController;->mPhone:[Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/PhoneSwitcher;->activate(I)V
+Lcom/android/internal/telephony/PhoneSwitcher;->deactivate(I)V
+Lcom/android/internal/telephony/PhoneSwitcher;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/PhoneSwitcher;->mMaxActivePhones:I
+Lcom/android/internal/telephony/PhoneSwitcher;->mNumPhones:I
+Lcom/android/internal/telephony/PhoneSwitcher;->mPhones:[Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/ProxyController;->completeRadioCapabilityTransaction()V
+Lcom/android/internal/telephony/ProxyController;->getInstance()Lcom/android/internal/telephony/ProxyController;
+Lcom/android/internal/telephony/ProxyController;->logd(Ljava/lang/String;)V
+Lcom/android/internal/telephony/ProxyController;->mOldRadioAccessFamily:[I
+Lcom/android/internal/telephony/ProxyController;->mRadioCapabilitySessionId:I
+Lcom/android/internal/telephony/ProxyController;->mSetRadioAccessFamilyStatus:[I
+Lcom/android/internal/telephony/ProxyController;->mUniqueIdGenerator:Ljava/util/concurrent/atomic/AtomicInteger;
+Lcom/android/internal/telephony/ProxyController;->registerForAllDataDisconnected(ILandroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/ProxyController;->sendRadioCapabilityRequest(IIIILjava/lang/String;II)V
+Lcom/android/internal/telephony/ProxyController;->sProxyController:Lcom/android/internal/telephony/ProxyController;
+Lcom/android/internal/telephony/RadioCapability;->getRadioAccessFamily()I
+Lcom/android/internal/telephony/RetryManager;->configure(Ljava/lang/String;)Z
+Lcom/android/internal/telephony/RetryManager;->getRetryTimer()I
+Lcom/android/internal/telephony/RetryManager;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/RetryManager;->mApnType:Ljava/lang/String;
+Lcom/android/internal/telephony/RetryManager;->mFailFastInterApnDelay:J
+Lcom/android/internal/telephony/RetryManager;->mInterApnDelay:J
+Lcom/android/internal/telephony/RetryManager;->mPhone:Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/RIL;-><init>(Landroid/content/Context;II)V
+Lcom/android/internal/telephony/RIL;-><init>(Landroid/content/Context;IILjava/lang/Integer;)V
+Lcom/android/internal/telephony/RIL;->acquireWakeLock(Lcom/android/internal/telephony/RILRequest;I)V
+Lcom/android/internal/telephony/RIL;->clearRequestList(IZ)V
+Lcom/android/internal/telephony/RIL;->clearWakeLock(I)Z
+Lcom/android/internal/telephony/RIL;->decrementWakeLock(Lcom/android/internal/telephony/RILRequest;)V
+Lcom/android/internal/telephony/RIL;->findAndRemoveRequestFromList(I)Lcom/android/internal/telephony/RILRequest;
+Lcom/android/internal/telephony/RIL;->getResponseForTimedOutRILRequest(Lcom/android/internal/telephony/RILRequest;)Ljava/lang/Object;
+Lcom/android/internal/telephony/RIL;->hangupForegroundResumeBackground(Landroid/os/Message;)V
+Lcom/android/internal/telephony/RIL;->hangupWaitingOrBackground(Landroid/os/Message;)V
+Lcom/android/internal/telephony/RIL;->invokeOemRilRequestRaw([BLandroid/os/Message;)V
+Lcom/android/internal/telephony/RIL;->makeStaticRadioCapability()Lcom/android/internal/telephony/RadioCapability;
+Lcom/android/internal/telephony/RIL;->mRequestList:Landroid/util/SparseArray;
+Lcom/android/internal/telephony/RIL;->mTestingEmergencyCall:Ljava/util/concurrent/atomic/AtomicBoolean;
+Lcom/android/internal/telephony/RIL;->mWakeLock:Landroid/os/PowerManager$WakeLock;
+Lcom/android/internal/telephony/RIL;->notifyRegistrantsCdmaInfoRec(Lcom/android/internal/telephony/cdma/CdmaInformationRecords;)V
+Lcom/android/internal/telephony/RIL;->notifyRegistrantsRilConnectionChanged(I)V
+Lcom/android/internal/telephony/RIL;->requestToString(I)Ljava/lang/String;
+Lcom/android/internal/telephony/RIL;->responseToString(I)Ljava/lang/String;
+Lcom/android/internal/telephony/RIL;->retToString(ILjava/lang/Object;)Ljava/lang/String;
+Lcom/android/internal/telephony/RIL;->riljLog(Ljava/lang/String;)V
+Lcom/android/internal/telephony/RIL;->setRadioPower(ZLandroid/os/Message;)V
+Lcom/android/internal/telephony/RIL;->unsljLog(I)V
+Lcom/android/internal/telephony/RIL;->unsljLogMore(ILjava/lang/String;)V
+Lcom/android/internal/telephony/RIL;->unsljLogRet(ILjava/lang/Object;)V
+Lcom/android/internal/telephony/RIL;->unsljLogvRet(ILjava/lang/Object;)V
+Lcom/android/internal/telephony/RILConstants;->PREFERRED_NETWORK_MODE:I
+Lcom/android/internal/telephony/RILRequest;->mRequest:I
+Lcom/android/internal/telephony/RILRequest;->mResult:Landroid/os/Message;
+Lcom/android/internal/telephony/RILRequest;->mSerial:I
+Lcom/android/internal/telephony/RILRequest;->obtain(ILandroid/os/Message;)Lcom/android/internal/telephony/RILRequest;
+Lcom/android/internal/telephony/RILRequest;->onError(ILjava/lang/Object;)V
+Lcom/android/internal/telephony/RILRequest;->release()V
+Lcom/android/internal/telephony/RILRequest;->serialString()Ljava/lang/String;
+Lcom/android/internal/telephony/ServiceStateTracker;->fixUnknownMcc(Ljava/lang/String;I)Ljava/lang/String;
+Lcom/android/internal/telephony/ServiceStateTracker;->getCurrentDataConnectionState()I
+Lcom/android/internal/telephony/ServiceStateTracker;->getDesiredPowerState()Z
+Lcom/android/internal/telephony/ServiceStateTracker;->getPhoneId()I
+Lcom/android/internal/telephony/ServiceStateTracker;->getSystemProperty(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+Lcom/android/internal/telephony/ServiceStateTracker;->isConcurrentVoiceAndDataAllowed()Z
+Lcom/android/internal/telephony/ServiceStateTracker;->isGprsConsistent(II)Z
+Lcom/android/internal/telephony/ServiceStateTracker;->isImsRegistered()Z
+Lcom/android/internal/telephony/ServiceStateTracker;->isInHomeSidNid(II)Z
+Lcom/android/internal/telephony/ServiceStateTracker;->isInvalidOperatorNumeric(Ljava/lang/String;)Z
+Lcom/android/internal/telephony/ServiceStateTracker;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/ServiceStateTracker;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/ServiceStateTracker;->mAttachedRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/ServiceStateTracker;->mCi:Lcom/android/internal/telephony/CommandsInterface;
+Lcom/android/internal/telephony/ServiceStateTracker;->mCr:Landroid/content/ContentResolver;
+Lcom/android/internal/telephony/ServiceStateTracker;->mCurDataSpn:Ljava/lang/String;
+Lcom/android/internal/telephony/ServiceStateTracker;->mCurPlmn:Ljava/lang/String;
+Lcom/android/internal/telephony/ServiceStateTracker;->mCurShowPlmn:Z
+Lcom/android/internal/telephony/ServiceStateTracker;->mCurShowSpn:Z
+Lcom/android/internal/telephony/ServiceStateTracker;->mCurSpn:Ljava/lang/String;
+Lcom/android/internal/telephony/ServiceStateTracker;->mDataRoamingOffRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/ServiceStateTracker;->mDataRoamingOnRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/ServiceStateTracker;->mDefaultRoamingIndicator:I
+Lcom/android/internal/telephony/ServiceStateTracker;->mDesiredPowerState:Z
+Lcom/android/internal/telephony/ServiceStateTracker;->mDetachedRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/ServiceStateTracker;->mDeviceShuttingDown:Z
+Lcom/android/internal/telephony/ServiceStateTracker;->mEmergencyOnly:Z
+Lcom/android/internal/telephony/ServiceStateTracker;->mIccRecords:Lcom/android/internal/telephony/uicc/IccRecords;
+Lcom/android/internal/telephony/ServiceStateTracker;->mIntentReceiver:Landroid/content/BroadcastReceiver;
+Lcom/android/internal/telephony/ServiceStateTracker;->mIsSubscriptionFromRuim:Z
+Lcom/android/internal/telephony/ServiceStateTracker;->mMaxDataCalls:I
+Lcom/android/internal/telephony/ServiceStateTracker;->mNetworkAttachedRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/ServiceStateTracker;->mNewMaxDataCalls:I
+Lcom/android/internal/telephony/ServiceStateTracker;->mNewReasonDataDenied:I
+Lcom/android/internal/telephony/ServiceStateTracker;->mNewSS:Landroid/telephony/ServiceState;
+Lcom/android/internal/telephony/ServiceStateTracker;->mOnSubscriptionsChangedListener:Lcom/android/internal/telephony/ServiceStateTracker$SstSubscriptionsChangedListener;
+Lcom/android/internal/telephony/ServiceStateTracker;->mPhone:Lcom/android/internal/telephony/GsmCdmaPhone;
+Lcom/android/internal/telephony/ServiceStateTracker;->mPreferredNetworkType:I
+Lcom/android/internal/telephony/ServiceStateTracker;->mReasonDataDenied:I
+Lcom/android/internal/telephony/ServiceStateTracker;->mReportedGprsNoReg:Z
+Lcom/android/internal/telephony/ServiceStateTracker;->mRoamingIndicator:I
+Lcom/android/internal/telephony/ServiceStateTracker;->mSignalStrength:Landroid/telephony/SignalStrength;
+Lcom/android/internal/telephony/ServiceStateTracker;->mSpnUpdatePending:Z
+Lcom/android/internal/telephony/ServiceStateTracker;->mSS:Landroid/telephony/ServiceState;
+Lcom/android/internal/telephony/ServiceStateTracker;->mStartedGprsRegCheck:Z
+Lcom/android/internal/telephony/ServiceStateTracker;->mSubId:I
+Lcom/android/internal/telephony/ServiceStateTracker;->mSubscriptionController:Lcom/android/internal/telephony/SubscriptionController;
+Lcom/android/internal/telephony/ServiceStateTracker;->mSubscriptionManager:Landroid/telephony/SubscriptionManager;
+Lcom/android/internal/telephony/ServiceStateTracker;->mUiccApplcation:Lcom/android/internal/telephony/uicc/UiccCardApplication;
+Lcom/android/internal/telephony/ServiceStateTracker;->mUiccController:Lcom/android/internal/telephony/uicc/UiccController;
+Lcom/android/internal/telephony/ServiceStateTracker;->mVoiceRoamingOffRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/ServiceStateTracker;->mVoiceRoamingOnRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/ServiceStateTracker;->notifyDataRegStateRilRadioTechnologyChanged()V
+Lcom/android/internal/telephony/ServiceStateTracker;->notifySignalStrength()Z
+Lcom/android/internal/telephony/ServiceStateTracker;->pollState()V
+Lcom/android/internal/telephony/ServiceStateTracker;->powerOffRadioSafely(Lcom/android/internal/telephony/dataconnection/DcTracker;)V
+Lcom/android/internal/telephony/ServiceStateTracker;->reRegisterNetwork(Landroid/os/Message;)V
+Lcom/android/internal/telephony/ServiceStateTracker;->resetServiceStateInIwlanMode()V
+Lcom/android/internal/telephony/ServiceStateTracker;->setOperatorIdd(Ljava/lang/String;)V
+Lcom/android/internal/telephony/ServiceStateTracker;->setRoamingType(Landroid/telephony/ServiceState;)V
+Lcom/android/internal/telephony/ServiceStateTracker;->setSignalStrengthDefaultValues()V
+Lcom/android/internal/telephony/ServiceStateTracker;->updateOtaspState()V
+Lcom/android/internal/telephony/ServiceStateTracker;->updatePhoneObject()V
+Lcom/android/internal/telephony/ServiceStateTracker;->updateRoamingState()V
+Lcom/android/internal/telephony/ServiceStateTracker;->updateSpnDisplay()V
+Lcom/android/internal/telephony/ServiceStateTracker;->useDataRegStateForDataOnlyDevices()V
+Lcom/android/internal/telephony/sip/SipPhone$SipCall;->hold()V
+Lcom/android/internal/telephony/sip/SipPhone$SipCall;->switchWith(Lcom/android/internal/telephony/sip/SipPhone$SipCall;)V
+Lcom/android/internal/telephony/sip/SipPhone$SipCall;->unhold()V
+Lcom/android/internal/telephony/sip/SipPhone;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/sip/SipPhone;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/sip/SipPhone;->mBackgroundCall:Lcom/android/internal/telephony/sip/SipPhone$SipCall;
+Lcom/android/internal/telephony/sip/SipPhone;->mForegroundCall:Lcom/android/internal/telephony/sip/SipPhone$SipCall;
+Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->DBG:Z
+Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->mTranslationTableCDMA:Landroid/util/SparseIntArray;
+Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->mTranslationTableCommon:Landroid/util/SparseIntArray;
+Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->mTranslationTableGSM:Landroid/util/SparseIntArray;
+Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->translate(Ljava/lang/CharSequence;)Ljava/lang/String;
+Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->useCdmaFormatForMoSms()Z
+Lcom/android/internal/telephony/SmsApplication$SmsApplicationData;->mApplicationName:Ljava/lang/String;
+Lcom/android/internal/telephony/SmsApplication;->configurePreferredActivity(Landroid/content/pm/PackageManager;Landroid/content/ComponentName;I)V
+Lcom/android/internal/telephony/SmsApplication;->getApplicationCollection(Landroid/content/Context;)Ljava/util/Collection;
+Lcom/android/internal/telephony/SmsApplication;->getDefaultMmsApplication(Landroid/content/Context;Z)Landroid/content/ComponentName;
+Lcom/android/internal/telephony/SmsApplication;->getDefaultRespondViaMessageApplication(Landroid/content/Context;Z)Landroid/content/ComponentName;
+Lcom/android/internal/telephony/SmsApplication;->getDefaultSmsApplication(Landroid/content/Context;Z)Landroid/content/ComponentName;
+Lcom/android/internal/telephony/SmsApplication;->getSmsApplicationData(Ljava/lang/String;Landroid/content/Context;)Lcom/android/internal/telephony/SmsApplication$SmsApplicationData;
+Lcom/android/internal/telephony/SmsApplication;->isDefaultSmsApplication(Landroid/content/Context;Ljava/lang/String;)Z
+Lcom/android/internal/telephony/SmsApplication;->setDefaultApplication(Ljava/lang/String;Landroid/content/Context;)V
+Lcom/android/internal/telephony/SmsApplication;->shouldWriteMessageForPackage(Ljava/lang/String;Landroid/content/Context;)Z
+Lcom/android/internal/telephony/SmsBroadcastUndelivered;-><init>(Landroid/content/Context;Lcom/android/internal/telephony/gsm/GsmInboundSmsHandler;Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler;)V
+Lcom/android/internal/telephony/SMSDispatcher$ConfirmDialogListener;->mNegativeButton:Landroid/widget/Button;
+Lcom/android/internal/telephony/SMSDispatcher$ConfirmDialogListener;->mPositiveButton:Landroid/widget/Button;
+Lcom/android/internal/telephony/SMSDispatcher$ConfirmDialogListener;->mRememberUndoInstruction:Landroid/widget/TextView;
+Lcom/android/internal/telephony/SMSDispatcher$DataSmsSender;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
+Lcom/android/internal/telephony/SMSDispatcher$MultipartSmsSender;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Ljava/util/ArrayList;[Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
+Lcom/android/internal/telephony/SMSDispatcher$MultipartSmsSender;->sendSmsByCarrierApp(Ljava/lang/String;Lcom/android/internal/telephony/SMSDispatcher$MultipartSmsSenderCallback;)V
+Lcom/android/internal/telephony/SMSDispatcher$MultipartSmsSenderCallback;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Lcom/android/internal/telephony/SMSDispatcher$MultipartSmsSender;)V
+Lcom/android/internal/telephony/SMSDispatcher$SmsSenderCallback;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Lcom/android/internal/telephony/SMSDispatcher$SmsSender;)V
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->isMultipart()Z
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->mAppInfo:Landroid/content/pm/PackageInfo;
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->mData:Ljava/util/HashMap;
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->mDeliveryIntent:Landroid/app/PendingIntent;
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->mDestAddress:Ljava/lang/String;
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->mMessageRef:I
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->mMessageUri:Landroid/net/Uri;
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->mPersistMessage:Z
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->mSentIntent:Landroid/app/PendingIntent;
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->mTimestamp:J
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->onFailed(Landroid/content/Context;II)V
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->onSent(Landroid/content/Context;)V
+Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;->updateSentMessageStatus(Landroid/content/Context;I)V
+Lcom/android/internal/telephony/SMSDispatcher$TextSmsSender;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
+Lcom/android/internal/telephony/SMSDispatcher;->calculateLength(Ljava/lang/CharSequence;Z)Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;
+Lcom/android/internal/telephony/SMSDispatcher;->checkCallerIsPhoneOrCarrierApp()V
+Lcom/android/internal/telephony/SMSDispatcher;->deliveryPendingList:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/SMSDispatcher;->dispose()V
+Lcom/android/internal/telephony/SMSDispatcher;->getCarrierAppPackageName()Ljava/lang/String;
+Lcom/android/internal/telephony/SMSDispatcher;->getMultipartMessageText(Ljava/util/ArrayList;)Ljava/lang/String;
+Lcom/android/internal/telephony/SMSDispatcher;->getNextConcatenatedRef()I
+Lcom/android/internal/telephony/SMSDispatcher;->getSubId()I
+Lcom/android/internal/telephony/SMSDispatcher;->handleConfirmShortCode(ZLcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
+Lcom/android/internal/telephony/SMSDispatcher;->mCi:Lcom/android/internal/telephony/CommandsInterface;
+Lcom/android/internal/telephony/SMSDispatcher;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/SMSDispatcher;->mPhone:Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/SMSDispatcher;->mResolver:Landroid/content/ContentResolver;
+Lcom/android/internal/telephony/SMSDispatcher;->mTelephonyManager:Landroid/telephony/TelephonyManager;
+Lcom/android/internal/telephony/SMSDispatcher;->processSendSmsResponse(Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;II)V
+Lcom/android/internal/telephony/SMSDispatcher;->sendData(Ljava/lang/String;Ljava/lang/String;I[BLandroid/app/PendingIntent;Landroid/app/PendingIntent;)V
+Lcom/android/internal/telephony/SMSDispatcher;->sendMultipartSms(Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
+Lcom/android/internal/telephony/SMSDispatcher;->sendSms(Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
+Lcom/android/internal/telephony/SMSDispatcher;->sendSubmitPdu(Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
 Lcom/android/internal/telephony/SmsHeader$ConcatRef;-><init>()V
 Lcom/android/internal/telephony/SmsHeader$PortAddrs;-><init>()V
 Lcom/android/internal/telephony/SmsMessageBase;-><init>()V
+Lcom/android/internal/telephony/SmsResponse;-><init>(ILjava/lang/String;I)V
+Lcom/android/internal/telephony/SmsResponse;->mAckPdu:Ljava/lang/String;
+Lcom/android/internal/telephony/SmsResponse;->mErrorCode:I
+Lcom/android/internal/telephony/SmsResponse;->mMessageRef:I
+Lcom/android/internal/telephony/SmsStorageMonitor;->mCi:Lcom/android/internal/telephony/CommandsInterface;
+Lcom/android/internal/telephony/SmsUsageMonitor;-><init>(Landroid/content/Context;)V
+Lcom/android/internal/telephony/SmsUsageMonitor;->check(Ljava/lang/String;I)Z
+Lcom/android/internal/telephony/SubscriptionController;->broadcastDefaultDataSubIdChanged(I)V
+Lcom/android/internal/telephony/SubscriptionController;->colorArr:[I
+Lcom/android/internal/telephony/SubscriptionController;->enforceModifyPhoneState(Ljava/lang/String;)V
+Lcom/android/internal/telephony/SubscriptionController;->getActiveSubInfoCount(Ljava/lang/String;)I
+Lcom/android/internal/telephony/SubscriptionController;->getActiveSubscriptionInfo(ILjava/lang/String;)Landroid/telephony/SubscriptionInfo;
+Lcom/android/internal/telephony/SubscriptionController;->getActiveSubscriptionInfoList(Ljava/lang/String;)Ljava/util/List;
+Lcom/android/internal/telephony/SubscriptionController;->getDefaultDataSubId()I
+Lcom/android/internal/telephony/SubscriptionController;->getDefaultSmsSubId()I
+Lcom/android/internal/telephony/SubscriptionController;->getDefaultSubId()I
+Lcom/android/internal/telephony/SubscriptionController;->getDefaultVoiceSubId()I
+Lcom/android/internal/telephony/SubscriptionController;->getDummySubIds(I)[I
+Lcom/android/internal/telephony/SubscriptionController;->getInstance()Lcom/android/internal/telephony/SubscriptionController;
+Lcom/android/internal/telephony/SubscriptionController;->getPhoneId(I)I
+Lcom/android/internal/telephony/SubscriptionController;->getSubId(I)[I
+Lcom/android/internal/telephony/SubscriptionController;->getSubIdUsingPhoneId(I)I
+Lcom/android/internal/telephony/SubscriptionController;->getSubInfo(Ljava/lang/String;Ljava/lang/Object;)Ljava/util/List;
+Lcom/android/internal/telephony/SubscriptionController;->getSubInfoRecord(Landroid/database/Cursor;)Landroid/telephony/SubscriptionInfo;
+Lcom/android/internal/telephony/SubscriptionController;->isActiveSubId(I)Z
+Lcom/android/internal/telephony/SubscriptionController;->isSubInfoReady()Z
+Lcom/android/internal/telephony/SubscriptionController;->logd(Ljava/lang/String;)V
+Lcom/android/internal/telephony/SubscriptionController;->logdl(Ljava/lang/String;)V
+Lcom/android/internal/telephony/SubscriptionController;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/SubscriptionController;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/SubscriptionController;->mDefaultPhoneId:I
+Lcom/android/internal/telephony/SubscriptionController;->mLock:Ljava/lang/Object;
+Lcom/android/internal/telephony/SubscriptionController;->notifySubscriptionInfoChanged()V
+Lcom/android/internal/telephony/SubscriptionController;->setDefaultDataSubId(I)V
+Lcom/android/internal/telephony/SubscriptionController;->setDefaultFallbackSubId(I)V
+Lcom/android/internal/telephony/SubscriptionController;->setDefaultSmsSubId(I)V
+Lcom/android/internal/telephony/SubscriptionController;->setDefaultVoiceSubId(I)V
+Lcom/android/internal/telephony/SubscriptionController;->setPlmnSpn(IZLjava/lang/String;ZLjava/lang/String;)Z
+Lcom/android/internal/telephony/SubscriptionController;->updateAllDataConnectionTrackers()V
+Lcom/android/internal/telephony/SubscriptionController;->validateSubId(I)V
+Lcom/android/internal/telephony/SubscriptionInfoUpdater;->broadcastSimStateChanged(ILjava/lang/String;Ljava/lang/String;)V
+Lcom/android/internal/telephony/SubscriptionInfoUpdater;->isAllIccIdQueryDone()Z
+Lcom/android/internal/telephony/SubscriptionInfoUpdater;->logd(Ljava/lang/String;)V
+Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mCurrentlyActiveUserId:I
+Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mIccId:[Ljava/lang/String;
+Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mInsertSimState:[I
+Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mPackageManager:Landroid/content/pm/IPackageManager;
+Lcom/android/internal/telephony/SubscriptionInfoUpdater;->mPhone:[Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/SubscriptionInfoUpdater;->PROJECT_SIM_NUM:I
+Lcom/android/internal/telephony/SubscriptionInfoUpdater;->updateSubscriptionInfoByIccId()V
+Lcom/android/internal/telephony/TelephonyCapabilities;->supportsAdn(I)Z
+Lcom/android/internal/telephony/TelephonyProperties;->PROPERTY_ICC_OPERATOR_NUMERIC:Ljava/lang/String;
+Lcom/android/internal/telephony/test/InterpreterEx;-><init>(Ljava/lang/String;)V
+Lcom/android/internal/telephony/test/SimulatedCommands;->acceptCall(Landroid/os/Message;)V
+Lcom/android/internal/telephony/test/SimulatedCommands;->dial(Ljava/lang/String;ILandroid/os/Message;)V
+Lcom/android/internal/telephony/test/SimulatedCommands;->dial(Ljava/lang/String;ILcom/android/internal/telephony/UUSInfo;Landroid/os/Message;)V
+Lcom/android/internal/telephony/test/SimulatedCommands;->mDcSuccess:Z
+Lcom/android/internal/telephony/test/SimulatedCommands;->resultFail(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)V
+Lcom/android/internal/telephony/test/SimulatedCommands;->resultSuccess(Landroid/os/Message;Ljava/lang/Object;)V
+Lcom/android/internal/telephony/test/SimulatedCommands;->simulatedCallState:Lcom/android/internal/telephony/test/SimulatedGsmCallState;
+Lcom/android/internal/telephony/test/SimulatedCommands;->unimplemented(Landroid/os/Message;)V
+Lcom/android/internal/telephony/test/SimulatedCommandsVerifier;->getInstance()Lcom/android/internal/telephony/test/SimulatedCommandsVerifier;
+Lcom/android/internal/telephony/test/SimulatedCommandsVerifier;->setCallForward(IIILjava/lang/String;ILandroid/os/Message;)V
+Lcom/android/internal/telephony/test/SimulatedGsmCallState;->conference()Z
+Lcom/android/internal/telephony/test/SimulatedGsmCallState;->onChld(CC)Z
+Lcom/android/internal/telephony/test/SimulatedGsmCallState;->releaseActiveAcceptHeldOrWaiting()Z
+Lcom/android/internal/telephony/test/SimulatedGsmCallState;->releaseHeldOrUDUB()Z
+Lcom/android/internal/telephony/test/SimulatedGsmCallState;->separateCall(I)Z
+Lcom/android/internal/telephony/test/SimulatedGsmCallState;->switchActiveAndHeldOrWaiting()Z
+Lcom/android/internal/telephony/uicc/AdnRecord;-><init>(IILjava/lang/String;Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/AdnRecord;-><init>(IILjava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/AdnRecord;-><init>(II[B)V
+Lcom/android/internal/telephony/uicc/AdnRecord;-><init>(Ljava/lang/String;Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/AdnRecord;-><init>(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/AdnRecord;-><init>([B)V
+Lcom/android/internal/telephony/uicc/AdnRecord;->buildAdnString(I)[B
+Lcom/android/internal/telephony/uicc/AdnRecord;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/telephony/uicc/AdnRecord;->getEmails()[Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/AdnRecord;->getNumber()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/AdnRecord;->isEmpty()Z
+Lcom/android/internal/telephony/uicc/AdnRecord;->mAlphaTag:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/AdnRecord;->mEfid:I
+Lcom/android/internal/telephony/uicc/AdnRecord;->mEmails:[Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/AdnRecord;->mExtRecord:I
+Lcom/android/internal/telephony/uicc/AdnRecord;->mNumber:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/AdnRecord;->mRecordNumber:I
+Lcom/android/internal/telephony/uicc/AdnRecord;->setEmails([Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/AdnRecordCache;->extensionEfForEf(I)I
+Lcom/android/internal/telephony/uicc/AdnRecordCache;->getRecordsIfLoaded(I)Ljava/util/ArrayList;
+Lcom/android/internal/telephony/uicc/AdnRecordCache;->mAdnLikeWaiters:Landroid/util/SparseArray;
+Lcom/android/internal/telephony/uicc/AdnRecordCache;->mFh:Lcom/android/internal/telephony/uicc/IccFileHandler;
+Lcom/android/internal/telephony/uicc/AdnRecordCache;->mUserWriteResponse:Landroid/util/SparseArray;
+Lcom/android/internal/telephony/uicc/AdnRecordCache;->mUsimPhoneBookManager:Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;
+Lcom/android/internal/telephony/uicc/AdnRecordCache;->reset()V
+Lcom/android/internal/telephony/uicc/AdnRecordCache;->sendErrorResponse(Landroid/os/Message;Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/AdnRecordCache;->updateAdnByIndex(ILcom/android/internal/telephony/uicc/AdnRecord;ILjava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/AdnRecordLoader;-><init>(Lcom/android/internal/telephony/uicc/IccFileHandler;)V
+Lcom/android/internal/telephony/uicc/AdnRecordLoader;->getEFPath(I)Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/AdnRecordLoader;->loadFromEF(IIILandroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/AdnRecordLoader;->mFh:Lcom/android/internal/telephony/uicc/IccFileHandler;
+Lcom/android/internal/telephony/uicc/AdnRecordLoader;->updateEF(Lcom/android/internal/telephony/uicc/AdnRecord;IIILjava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;->APPSTATE_DETECTED:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;->APPSTATE_PIN:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;->APPSTATE_PUK:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;->APPSTATE_READY:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;->APPSTATE_SUBSCRIPTION_PERSO:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;->APPSTATE_UNKNOWN:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;->values()[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;->APPTYPE_CSIM:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;->APPTYPE_ISIM:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;->APPTYPE_RUIM:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;->APPTYPE_SIM:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;->APPTYPE_UNKNOWN:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;->APPTYPE_USIM:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;->values()[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;->PERSOSUBSTATE_SIM_NETWORK:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;->PERSOSUBSTATE_SIM_NETWORK_SUBSET:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;->PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;->PERSOSUBSTATE_SIM_SERVICE_PROVIDER:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;->PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;->PERSOSUBSTATE_UNKNOWN:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;->values()[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus;-><init>()V
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus;->AppTypeFromRILInt(I)Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
+Lcom/android/internal/telephony/uicc/IccCardApplicationStatus;->app_type:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
+Lcom/android/internal/telephony/uicc/IccCardStatus$CardState;->CARDSTATE_ABSENT:Lcom/android/internal/telephony/uicc/IccCardStatus$CardState;
+Lcom/android/internal/telephony/uicc/IccCardStatus$CardState;->CARDSTATE_ERROR:Lcom/android/internal/telephony/uicc/IccCardStatus$CardState;
+Lcom/android/internal/telephony/uicc/IccCardStatus$CardState;->CARDSTATE_PRESENT:Lcom/android/internal/telephony/uicc/IccCardStatus$CardState;
+Lcom/android/internal/telephony/uicc/IccCardStatus$CardState;->isCardPresent()Z
+Lcom/android/internal/telephony/uicc/IccCardStatus$PinState;->PINSTATE_DISABLED:Lcom/android/internal/telephony/uicc/IccCardStatus$PinState;
+Lcom/android/internal/telephony/uicc/IccCardStatus$PinState;->PINSTATE_ENABLED_BLOCKED:Lcom/android/internal/telephony/uicc/IccCardStatus$PinState;
+Lcom/android/internal/telephony/uicc/IccCardStatus$PinState;->PINSTATE_ENABLED_PERM_BLOCKED:Lcom/android/internal/telephony/uicc/IccCardStatus$PinState;
+Lcom/android/internal/telephony/uicc/IccCardStatus;->mApplications:[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus;
+Lcom/android/internal/telephony/uicc/IccCardStatus;->mCardState:Lcom/android/internal/telephony/uicc/IccCardStatus$CardState;
+Lcom/android/internal/telephony/uicc/IccCardStatus;->mCdmaSubscriptionAppIndex:I
+Lcom/android/internal/telephony/uicc/IccCardStatus;->mGsmUmtsSubscriptionAppIndex:I
+Lcom/android/internal/telephony/uicc/IccCardStatus;->mImsSubscriptionAppIndex:I
+Lcom/android/internal/telephony/uicc/IccCardStatus;->mUniversalPinState:Lcom/android/internal/telephony/uicc/IccCardStatus$PinState;
+Lcom/android/internal/telephony/uicc/IccFileHandler$LoadLinearFixedContext;-><init>(IILandroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccFileHandler$LoadLinearFixedContext;->mRecordSize:I
+Lcom/android/internal/telephony/uicc/IccFileHandler$LoadLinearFixedContext;->results:Ljava/util/ArrayList;
+Lcom/android/internal/telephony/uicc/IccFileHandler;->getEFLinearRecordSize(ILandroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccFileHandler;->getEFLinearRecordSize(ILjava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccFileHandler;->getEFPath(I)Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccFileHandler;->loadEFLinearFixed(IILandroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccFileHandler;->loadEFLinearFixed(ILjava/lang/String;ILandroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccFileHandler;->loadEFLinearFixedAll(ILandroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccFileHandler;->loadEFLinearFixedAll(ILjava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccFileHandler;->loadEFTransparent(ILandroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccFileHandler;->mAid:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccFileHandler;->mCi:Lcom/android/internal/telephony/CommandsInterface;
+Lcom/android/internal/telephony/uicc/IccFileHandler;->mParentApp:Lcom/android/internal/telephony/uicc/UiccCardApplication;
+Lcom/android/internal/telephony/uicc/IccFileHandler;->updateEFLinearFixed(II[BLjava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccFileHandler;->updateEFLinearFixed(ILjava/lang/String;I[BLjava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccFileHandler;->updateEFTransparent(I[BLandroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccIoResult;-><init>(IILjava/lang/String;)V
+Lcom/android/internal/telephony/uicc/IccIoResult;-><init>(II[B)V
+Lcom/android/internal/telephony/uicc/IccIoResult;->payload:[B
+Lcom/android/internal/telephony/uicc/IccIoResult;->success()Z
+Lcom/android/internal/telephony/uicc/IccIoResult;->sw1:I
+Lcom/android/internal/telephony/uicc/IccIoResult;->sw2:I
+Lcom/android/internal/telephony/uicc/IccRecords;->auth_rsp:Lcom/android/internal/telephony/uicc/IccIoResult;
+Lcom/android/internal/telephony/uicc/IccRecords;->getGid1()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRecords;->getIccId()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRecords;->getIccSimChallengeResponse(ILjava/lang/String;)Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRecords;->getIMSI()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRecords;->getMsisdnNumber()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRecords;->getOperatorNumeric()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRecords;->getRecordsLoaded()Z
+Lcom/android/internal/telephony/uicc/IccRecords;->getServiceProviderName()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRecords;->getUsimServiceTable()Lcom/android/internal/telephony/uicc/UsimServiceTable;
+Lcom/android/internal/telephony/uicc/IccRecords;->handleRefresh(Lcom/android/internal/telephony/uicc/IccRefreshResponse;)V
+Lcom/android/internal/telephony/uicc/IccRecords;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/IccRecords;->mAdnCache:Lcom/android/internal/telephony/uicc/AdnRecordCache;
+Lcom/android/internal/telephony/uicc/IccRecords;->mCi:Lcom/android/internal/telephony/CommandsInterface;
+Lcom/android/internal/telephony/uicc/IccRecords;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/uicc/IccRecords;->mDestroyed:Ljava/util/concurrent/atomic/AtomicBoolean;
+Lcom/android/internal/telephony/uicc/IccRecords;->mFh:Lcom/android/internal/telephony/uicc/IccFileHandler;
+Lcom/android/internal/telephony/uicc/IccRecords;->mGid1:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRecords;->mIccId:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRecords;->mImsi:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRecords;->mIsVoiceMailFixed:Z
+Lcom/android/internal/telephony/uicc/IccRecords;->mLock:Ljava/lang/Object;
+Lcom/android/internal/telephony/uicc/IccRecords;->mMncLength:I
+Lcom/android/internal/telephony/uicc/IccRecords;->mParentApp:Lcom/android/internal/telephony/uicc/UiccCardApplication;
+Lcom/android/internal/telephony/uicc/IccRecords;->mRecordsEventsRegistrants:Landroid/os/RegistrantList;
+Lcom/android/internal/telephony/uicc/IccRecords;->mRecordsToLoad:I
+Lcom/android/internal/telephony/uicc/IccRecords;->mSpn:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRecords;->mTelephonyManager:Landroid/telephony/TelephonyManager;
+Lcom/android/internal/telephony/uicc/IccRecords;->mVoiceMailNum:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRecords;->registerForNetworkSelectionModeAutomatic(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/uicc/IccRecords;->registerForNewSms(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/uicc/IccRecords;->registerForRecordsEvents(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/uicc/IccRecords;->registerForRecordsLoaded(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/uicc/IccRecords;->setMsisdnNumber(Ljava/lang/String;Ljava/lang/String;Landroid/os/Message;)V
+Lcom/android/internal/telephony/uicc/IccRecords;->setVoiceCallForwardingFlag(IZLjava/lang/String;)V
+Lcom/android/internal/telephony/uicc/IccRecords;->unregisterForNetworkSelectionModeAutomatic(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/uicc/IccRecords;->unregisterForNewSms(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/uicc/IccRecords;->unregisterForRecordsEvents(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/uicc/IccRecords;->unregisterForRecordsLoaded(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/uicc/IccRefreshResponse;-><init>()V
+Lcom/android/internal/telephony/uicc/IccRefreshResponse;->aid:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccRefreshResponse;->efId:I
+Lcom/android/internal/telephony/uicc/IccRefreshResponse;->refreshResult:I
+Lcom/android/internal/telephony/uicc/IccServiceTable;->getTag()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccServiceTable;->mServiceTable:[B
+Lcom/android/internal/telephony/uicc/IccUtils;->adnStringFieldToString([BII)Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccUtils;->bcdToString([BII)Ljava/lang/String;
 Lcom/android/internal/telephony/uicc/IccUtils;->bytesToHexString([B)Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccUtils;->cdmaBcdByteToInt(B)I
+Lcom/android/internal/telephony/uicc/IccUtils;->cdmaBcdToString([BII)Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccUtils;->gsmBcdByteToInt(B)I
+Lcom/android/internal/telephony/uicc/IccUtils;->hexCharToInt(C)I
+Lcom/android/internal/telephony/uicc/IccUtils;->hexStringToBytes(Ljava/lang/String;)[B
+Lcom/android/internal/telephony/uicc/IccUtils;->networkNameToString([BII)Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IccUtils;->parseToBnW([BI)Landroid/graphics/Bitmap;
+Lcom/android/internal/telephony/uicc/IccUtils;->parseToRGB([BIZ)Landroid/graphics/Bitmap;
+Lcom/android/internal/telephony/uicc/IsimRecords;->getIsimDomain()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IsimRecords;->getIsimImpi()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IsimRecords;->getIsimImpu()[Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IsimUiccRecords;->auth_rsp:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IsimUiccRecords;->fetchIsimRecords()V
+Lcom/android/internal/telephony/uicc/IsimUiccRecords;->isimTlvToString([B)Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IsimUiccRecords;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/IsimUiccRecords;->mIsimDomain:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IsimUiccRecords;->mIsimImpi:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IsimUiccRecords;->mIsimImpu:[Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IsimUiccRecords;->mIsimIst:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IsimUiccRecords;->mIsimPcscf:[Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/IsimUiccRecords;->mLock:Ljava/lang/Object;
+Lcom/android/internal/telephony/uicc/RuimRecords;->adjstMinDigits(I)I
+Lcom/android/internal/telephony/uicc/RuimRecords;->fetchRuimRecords()V
+Lcom/android/internal/telephony/uicc/RuimRecords;->getAssetLanguages(Landroid/content/Context;)[Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/RuimRecords;->getCsimSpnDisplayCondition()Z
+Lcom/android/internal/telephony/uicc/RuimRecords;->getMdn()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/RuimRecords;->getMdnNumber()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/RuimRecords;->getRUIMOperatorNumeric()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/RuimRecords;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/RuimRecords;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/RuimRecords;->mEFli:[B
+Lcom/android/internal/telephony/uicc/RuimRecords;->mEFpl:[B
+Lcom/android/internal/telephony/uicc/RuimRecords;->mMin:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/RuimRecords;->mNai:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/RuimRecords;->onGetCSimEprlDone(Landroid/os/AsyncResult;)V
+Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;->INIT:Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;
+Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;->READ_SPN_3GPP:Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;
+Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;->READ_SPN_CPHS:Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;
+Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;->READ_SPN_SHORT_CPHS:Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;
+Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;->values()[Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;
+Lcom/android/internal/telephony/uicc/SIMRecords;->fetchSimRecords()V
+Lcom/android/internal/telephony/uicc/SIMRecords;->getExtFromEf(I)I
+Lcom/android/internal/telephony/uicc/SIMRecords;->getMsisdnNumber()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/SIMRecords;->getOperatorNumeric()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/SIMRecords;->getSpnFsm(ZLandroid/os/AsyncResult;)V
+Lcom/android/internal/telephony/uicc/SIMRecords;->getVoiceMailNumber()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/SIMRecords;->isCphsMailboxEnabled()Z
+Lcom/android/internal/telephony/uicc/SIMRecords;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/SIMRecords;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/SIMRecords;->logv(Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/SIMRecords;->mEfCff:[B
+Lcom/android/internal/telephony/uicc/SIMRecords;->mEfCfis:[B
+Lcom/android/internal/telephony/uicc/SIMRecords;->mEfCPHS_MWI:[B
+Lcom/android/internal/telephony/uicc/SIMRecords;->mEfLi:[B
+Lcom/android/internal/telephony/uicc/SIMRecords;->mEfMWIS:[B
+Lcom/android/internal/telephony/uicc/SIMRecords;->mEfPl:[B
+Lcom/android/internal/telephony/uicc/SIMRecords;->mSpnDisplayCondition:I
+Lcom/android/internal/telephony/uicc/SIMRecords;->mUsimServiceTable:Lcom/android/internal/telephony/uicc/UsimServiceTable;
+Lcom/android/internal/telephony/uicc/SIMRecords;->mVmConfig:Lcom/android/internal/telephony/uicc/VoiceMailConstants;
+Lcom/android/internal/telephony/uicc/SIMRecords;->setVoiceCallForwardingFlag(IZLjava/lang/String;)V
+Lcom/android/internal/telephony/uicc/UiccCard;->getApplication(I)Lcom/android/internal/telephony/uicc/UiccCardApplication;
+Lcom/android/internal/telephony/uicc/UiccCard;->getApplicationByType(I)Lcom/android/internal/telephony/uicc/UiccCardApplication;
+Lcom/android/internal/telephony/uicc/UiccCard;->getApplicationIndex(I)Lcom/android/internal/telephony/uicc/UiccCardApplication;
+Lcom/android/internal/telephony/uicc/UiccCard;->getCardState()Lcom/android/internal/telephony/uicc/IccCardStatus$CardState;
+Lcom/android/internal/telephony/uicc/UiccCard;->getCarrierPackageNamesForIntent(Landroid/content/pm/PackageManager;Landroid/content/Intent;)Ljava/util/List;
+Lcom/android/internal/telephony/uicc/UiccCard;->getIccId()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/UiccCard;->getNumApplications()I
+Lcom/android/internal/telephony/uicc/UiccCard;->getOperatorBrandOverride()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/UiccCard;->isApplicationOnIcc(Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;)Z
+Lcom/android/internal/telephony/uicc/UiccCard;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/UiccCard;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/UiccCard;->mCardState:Lcom/android/internal/telephony/uicc/IccCardStatus$CardState;
+Lcom/android/internal/telephony/uicc/UiccCard;->mCi:Lcom/android/internal/telephony/CommandsInterface;
+Lcom/android/internal/telephony/uicc/UiccCard;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/uicc/UiccCard;->mLock:Ljava/lang/Object;
+Lcom/android/internal/telephony/uicc/UiccCard;->mPhoneId:I
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->dispose()V
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->getAid()Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->getAuthContext()I
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->getIccFileHandler()Lcom/android/internal/telephony/uicc/IccFileHandler;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->getIccRecords()Lcom/android/internal/telephony/uicc/IccRecords;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->getPersoSubState()Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->getPhoneId()I
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->getPin1State()Lcom/android/internal/telephony/uicc/IccCardStatus$PinState;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->getState()Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->getType()Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->loge(Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->mAid:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->mAppState:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->mAppType:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->mCi:Lcom/android/internal/telephony/CommandsInterface;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->mDestroyed:Z
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->mLock:Ljava/lang/Object;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->mPersoSubState:Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->mPin1State:Lcom/android/internal/telephony/uicc/IccCardStatus$PinState;
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->registerForReady(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->unregisterForReady(Landroid/os/Handler;)V
+Lcom/android/internal/telephony/uicc/UiccCardApplication;->update(Lcom/android/internal/telephony/uicc/IccCardApplicationStatus;Landroid/content/Context;Lcom/android/internal/telephony/CommandsInterface;)V
+Lcom/android/internal/telephony/uicc/UiccCarrierPrivilegeRules$TLV;->length:Ljava/lang/Integer;
+Lcom/android/internal/telephony/uicc/UiccCarrierPrivilegeRules$TLV;->value:Ljava/lang/String;
+Lcom/android/internal/telephony/uicc/UiccCarrierPrivilegeRules;->mLoadedCallback:Landroid/os/Message;
+Lcom/android/internal/telephony/uicc/UiccCarrierPrivilegeRules;->mState:Ljava/util/concurrent/atomic/AtomicInteger;
+Lcom/android/internal/telephony/uicc/UiccController;->getIccFileHandler(II)Lcom/android/internal/telephony/uicc/IccFileHandler;
+Lcom/android/internal/telephony/uicc/UiccController;->getIccRecords(II)Lcom/android/internal/telephony/uicc/IccRecords;
+Lcom/android/internal/telephony/uicc/UiccController;->getInstance()Lcom/android/internal/telephony/uicc/UiccController;
+Lcom/android/internal/telephony/uicc/UiccController;->getUiccCard(I)Lcom/android/internal/telephony/uicc/UiccCard;
+Lcom/android/internal/telephony/uicc/UiccController;->getUiccCardApplication(II)Lcom/android/internal/telephony/uicc/UiccCardApplication;
+Lcom/android/internal/telephony/uicc/UiccController;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/uicc/UiccController;->mCis:[Lcom/android/internal/telephony/CommandsInterface;
+Lcom/android/internal/telephony/uicc/UiccController;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/uicc/UiccController;->mInstance:Lcom/android/internal/telephony/uicc/UiccController;
+Lcom/android/internal/telephony/uicc/UiccController;->mLock:Ljava/lang/Object;
+Lcom/android/internal/telephony/uicc/UiccController;->registerForIccChanged(Landroid/os/Handler;ILjava/lang/Object;)V
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->ALLOWED_CSG_LISTS_AND_INDICATIONS:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->CFI_STATUS:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->CSG_DISPLAY_CONTROL:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->FDN:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->MBDN:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->MSISDN:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->MWI_STATUS:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->OPERATOR_CSG_LISTS_AND_INDICATIONS:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->OPERATOR_PLMN_LIST:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->PLMN_NETWORK_NAME:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->SDN:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->SM_OVER_IP:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->SM_SERVICE_PARAMS:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->SM_STORAGE:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;->SPN:Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;
+Lcom/android/internal/telephony/uicc/UsimServiceTable;->isAvailable(Lcom/android/internal/telephony/uicc/UsimServiceTable$UsimService;)Z
+Lcom/android/internal/telephony/uicc/VoiceMailConstants;-><init>()V
+Lcom/android/internal/telephony/UiccPhoneBookController;-><init>([Lcom/android/internal/telephony/Phone;)V
+Lcom/android/internal/telephony/UiccPhoneBookController;->getDefaultSubscription()I
+Lcom/android/internal/telephony/UiccPhoneBookController;->getIccPhoneBookInterfaceManager(I)Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;
+Lcom/android/internal/telephony/UiccPhoneBookController;->mPhone:[Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/UiccSmsController;->copyMessageToIccEfForSubscriber(ILjava/lang/String;I[B[B)Z
+Lcom/android/internal/telephony/UiccSmsController;->disableCellBroadcastForSubscriber(III)Z
+Lcom/android/internal/telephony/UiccSmsController;->disableCellBroadcastRangeForSubscriber(IIII)Z
+Lcom/android/internal/telephony/UiccSmsController;->enableCellBroadcastForSubscriber(III)Z
+Lcom/android/internal/telephony/UiccSmsController;->enableCellBroadcastRangeForSubscriber(IIII)Z
+Lcom/android/internal/telephony/UiccSmsController;->getAllMessagesFromIccEfForSubscriber(ILjava/lang/String;)Ljava/util/List;
+Lcom/android/internal/telephony/UiccSmsController;->getIccSmsInterfaceManager(I)Lcom/android/internal/telephony/IccSmsInterfaceManager;
+Lcom/android/internal/telephony/UiccSmsController;->getImsSmsFormatForSubscriber(I)Ljava/lang/String;
+Lcom/android/internal/telephony/UiccSmsController;->getPreferredSmsSubscription()I
+Lcom/android/internal/telephony/UiccSmsController;->isImsSmsSupportedForSubscriber(I)Z
+Lcom/android/internal/telephony/UiccSmsController;->sendDataForSubscriber(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;I[BLandroid/app/PendingIntent;Landroid/app/PendingIntent;)V
+Lcom/android/internal/telephony/UiccSmsController;->sendErrorInPendingIntent(Landroid/app/PendingIntent;I)V
+Lcom/android/internal/telephony/UiccSmsController;->sendErrorInPendingIntents(Ljava/util/List;I)V
+Lcom/android/internal/telephony/UiccSmsController;->updateMessageOnIccEfForSubscriber(ILjava/lang/String;II[B)Z
+Lcom/android/internal/telephony/UUSInfo;->getDcs()I
+Lcom/android/internal/telephony/UUSInfo;->getType()I
+Lcom/android/internal/telephony/UUSInfo;->getUserData()[B
+Lcom/android/internal/telephony/WakeLockStateMachine;->log(Ljava/lang/String;)V
+Lcom/android/internal/telephony/WakeLockStateMachine;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/WakeLockStateMachine;->mIdleState:Lcom/android/internal/telephony/WakeLockStateMachine$IdleState;
+Lcom/android/internal/telephony/WakeLockStateMachine;->mPhone:Lcom/android/internal/telephony/Phone;
+Lcom/android/internal/telephony/WapPushOverSms;->dispatchWapPdu([BLandroid/content/BroadcastReceiver;Lcom/android/internal/telephony/InboundSmsHandler;)I
+Lcom/android/internal/telephony/WapPushOverSms;->getDeliveryOrReadReportThreadId(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)J
+Lcom/android/internal/telephony/WapPushOverSms;->isDuplicateNotification(Landroid/content/Context;Lcom/google/android/mms/pdu/NotificationInd;)Z
+Lcom/android/internal/telephony/WapPushOverSms;->isWapPushForMms([BLcom/android/internal/telephony/InboundSmsHandler;)Z
+Lcom/android/internal/telephony/WapPushOverSms;->mContext:Landroid/content/Context;
+Lcom/android/internal/telephony/WapPushOverSms;->mDeviceIdleController:Landroid/os/IDeviceIdleController;
+Lcom/android/internal/telephony/WapPushOverSms;->mWapPushManager:Lcom/android/internal/telephony/IWapPushManager;
+Lcom/android/internal/telephony/WspTypeDecoder;-><init>([B)V
+Lcom/android/internal/telephony/WspTypeDecoder;->decodeContentType(I)Z
+Lcom/android/internal/telephony/WspTypeDecoder;->decodeIntegerValue(I)Z
+Lcom/android/internal/telephony/WspTypeDecoder;->decodeShortInteger(I)Z
+Lcom/android/internal/telephony/WspTypeDecoder;->decodeTextString(I)Z
+Lcom/android/internal/telephony/WspTypeDecoder;->decodeUintvarInteger(I)Z
+Lcom/android/internal/telephony/WspTypeDecoder;->decodeValueLength(I)Z
+Lcom/android/internal/telephony/WspTypeDecoder;->decodeXWapApplicationId(I)Z
+Lcom/android/internal/telephony/WspTypeDecoder;->getContentParameters()Ljava/util/HashMap;
+Lcom/android/internal/telephony/WspTypeDecoder;->getDecodedDataLength()I
+Lcom/android/internal/telephony/WspTypeDecoder;->getValue32()J
+Lcom/android/internal/telephony/WspTypeDecoder;->getValueString()Ljava/lang/String;
+Lcom/android/internal/telephony/WspTypeDecoder;->mWspData:[B
+Lcom/android/internal/telephony/WspTypeDecoder;->seekXWapApplicationId(II)Z
 Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Lcom/android/internal/util/ArrayUtils;->appendElement(Ljava/lang/Class;[Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object;
+Lcom/android/internal/util/ArrayUtils;->appendInt([II)[I
+Lcom/android/internal/util/ArrayUtils;->contains([II)Z
+Lcom/android/internal/util/ArrayUtils;->contains([Ljava/lang/Object;Ljava/lang/Object;)Z
+Lcom/android/internal/util/ArrayUtils;->emptyArray(Ljava/lang/Class;)[Ljava/lang/Object;
+Lcom/android/internal/util/ArrayUtils;->indexOf([Ljava/lang/Object;Ljava/lang/Object;)I
+Lcom/android/internal/util/ArrayUtils;->isEmpty([Ljava/lang/Object;)Z
+Lcom/android/internal/util/ArrayUtils;->newUnpaddedArray(Ljava/lang/Class;I)[Ljava/lang/Object;
+Lcom/android/internal/util/ArrayUtils;->newUnpaddedIntArray(I)[I
+Lcom/android/internal/util/ArrayUtils;->removeElement(Ljava/lang/Class;[Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object;
+Lcom/android/internal/util/BitwiseInputStream;-><init>([B)V
+Lcom/android/internal/util/BitwiseInputStream;->available()I
+Lcom/android/internal/util/BitwiseInputStream;->read(I)I
+Lcom/android/internal/util/BitwiseInputStream;->readByteArray(I)[B
+Lcom/android/internal/util/BitwiseInputStream;->skip(I)V
+Lcom/android/internal/util/BitwiseOutputStream;-><init>(I)V
+Lcom/android/internal/util/BitwiseOutputStream;->toByteArray()[B
+Lcom/android/internal/util/BitwiseOutputStream;->write(II)V
+Lcom/android/internal/util/BitwiseOutputStream;->writeByteArray(I[B)V
+Lcom/android/internal/util/CharSequences;->compareToIgnoreCase(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)I
+Lcom/android/internal/util/CharSequences;->equals(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Z
+Lcom/android/internal/util/FastMath;->round(F)I
+Lcom/android/internal/util/FastXmlSerializer;-><init>()V
+Lcom/android/internal/util/GrowingArrayUtils;->append([III)[I
+Lcom/android/internal/util/GrowingArrayUtils;->append([Ljava/lang/Object;ILjava/lang/Object;)[Ljava/lang/Object;
+Lcom/android/internal/util/HexDump;->hexStringToByteArray(Ljava/lang/String;)[B
+Lcom/android/internal/util/HexDump;->toHexString(I)Ljava/lang/String;
+Lcom/android/internal/util/HexDump;->toHexString([B)Ljava/lang/String;
+Lcom/android/internal/util/HexDump;->toHexString([BII)Ljava/lang/String;
 Lcom/android/internal/util/HexDump;->toHexString([BZ)Ljava/lang/String;
+Lcom/android/internal/util/IState;->getName()Ljava/lang/String;
+Lcom/android/internal/util/MemInfoReader;-><init>()V
+Lcom/android/internal/util/MemInfoReader;->getCachedSize()J
+Lcom/android/internal/util/MemInfoReader;->getFreeSize()J
+Lcom/android/internal/util/MemInfoReader;->getRawInfo()[J
+Lcom/android/internal/util/MemInfoReader;->getTotalSize()J
+Lcom/android/internal/util/MemInfoReader;->readMemInfo()V
+Lcom/android/internal/util/Preconditions;->checkArgument(Z)V
+Lcom/android/internal/util/Preconditions;->checkArgument(ZLjava/lang/Object;)V
+Lcom/android/internal/util/Preconditions;->checkArgumentInRange(IIILjava/lang/String;)I
+Lcom/android/internal/util/Preconditions;->checkNotNull(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/android/internal/util/Preconditions;->checkNotNull(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/android/internal/util/Preconditions;->checkState(Z)V
+Lcom/android/internal/util/Preconditions;->checkState(ZLjava/lang/String;)V
+Lcom/android/internal/util/State;-><init>()V
+Lcom/android/internal/util/State;->enter()V
+Lcom/android/internal/util/State;->exit()V
+Lcom/android/internal/util/State;->getName()Ljava/lang/String;
+Lcom/android/internal/util/State;->processMessage(Landroid/os/Message;)Z
+Lcom/android/internal/util/StateMachine;-><init>(Ljava/lang/String;)V
+Lcom/android/internal/util/StateMachine;-><init>(Ljava/lang/String;Landroid/os/Handler;)V
+Lcom/android/internal/util/StateMachine;-><init>(Ljava/lang/String;Landroid/os/Looper;)V
+Lcom/android/internal/util/StateMachine;->dump(Ljava/io/FileDescriptor;Ljava/io/PrintWriter;[Ljava/lang/String;)V
+Lcom/android/internal/util/StateMachine;->obtainMessage(III)Landroid/os/Message;
+Lcom/android/internal/util/StateMachine;->obtainMessage(IIILjava/lang/Object;)Landroid/os/Message;
+Lcom/android/internal/util/StateMachine;->sendMessage(I)V
+Lcom/android/internal/util/StateMachine;->sendMessage(II)V
+Lcom/android/internal/util/StateMachine;->sendMessage(IIILjava/lang/Object;)V
+Lcom/android/internal/util/StateMachine;->sendMessage(ILjava/lang/Object;)V
+Lcom/android/internal/util/StateMachine;->sendMessage(Landroid/os/Message;)V
+Lcom/android/internal/view/ActionBarPolicy;-><init>(Landroid/content/Context;)V
+Lcom/android/internal/view/ActionBarPolicy;->get(Landroid/content/Context;)Lcom/android/internal/view/ActionBarPolicy;
+Lcom/android/internal/view/ActionBarPolicy;->getEmbeddedMenuWidthLimit()I
+Lcom/android/internal/view/ActionBarPolicy;->getMaxActionButtons()I
+Lcom/android/internal/view/ActionBarPolicy;->getStackedTabMaxWidth()I
+Lcom/android/internal/view/ActionBarPolicy;->getTabContainerHeight()I
+Lcom/android/internal/view/ActionBarPolicy;->hasEmbeddedTabs()Z
+Lcom/android/internal/view/ActionBarPolicy;->mContext:Landroid/content/Context;
+Lcom/android/internal/view/ActionBarPolicy;->showsOverflowMenuButton()Z
 Lcom/android/internal/view/BaseIWindow;-><init>()V
 Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/view/IInputMethodManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodManager;
 Lcom/android/internal/view/IInputMethodSession$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodSession;
+Lcom/android/internal/view/InputConnectionWrapper$InputContextCallback;->dispose()V
+Lcom/android/internal/view/InputConnectionWrapper$InputContextCallback;->getInstance()Lcom/android/internal/view/InputConnectionWrapper$InputContextCallback;
+Lcom/android/internal/view/menu/ActionMenu;-><init>(Landroid/content/Context;)V
+Lcom/android/internal/view/menu/ActionMenuItem;-><init>(Landroid/content/Context;IIIILjava/lang/CharSequence;)V
+Lcom/android/internal/view/menu/ContextMenuBuilder;-><init>(Landroid/content/Context;)V
+Lcom/android/internal/view/menu/IconMenuItemView;->getTextAppropriateLayoutParams()Lcom/android/internal/view/menu/IconMenuView$LayoutParams;
+Lcom/android/internal/view/menu/IconMenuItemView;->setIconMenuView(Lcom/android/internal/view/menu/IconMenuView;)V
+Lcom/android/internal/view/menu/IconMenuItemView;->setItemInvoker(Lcom/android/internal/view/menu/MenuBuilder$ItemInvoker;)V
+Lcom/android/internal/view/menu/IconMenuView$SavedState;-><init>(Landroid/os/Parcel;)V
+Lcom/android/internal/view/menu/IconMenuView;->createMoreItemView()Lcom/android/internal/view/menu/IconMenuItemView;
+Lcom/android/internal/view/menu/IconMenuView;->getNumActualItemsShown()I
+Lcom/android/internal/view/menu/IconMenuView;->mItemBackground:Landroid/graphics/drawable/Drawable;
+Lcom/android/internal/view/menu/IconMenuView;->mMaxItems:I
+Lcom/android/internal/view/menu/IconMenuView;->mMenu:Lcom/android/internal/view/menu/MenuBuilder;
+Lcom/android/internal/view/menu/MenuDialogHelper;-><init>(Lcom/android/internal/view/menu/MenuBuilder;)V
+Lcom/android/internal/view/menu/MenuDialogHelper;->dismiss()V
+Lcom/android/internal/view/menu/MenuDialogHelper;->show(Landroid/os/IBinder;)V
+Lcom/android/internal/view/WindowManagerPolicyThread;->getLooper()Landroid/os/Looper;
+Lcom/android/internal/widget/AbsActionBarView;->dismissPopupMenus()V
+Lcom/android/internal/widget/ActionBarContextView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
+Lcom/android/internal/widget/ActionBarOverlayLayout;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
+Lcom/android/internal/widget/ActionBarOverlayLayout;->setWindowCallback(Landroid/view/Window$Callback;)V
+Lcom/android/internal/widget/EditableInputConnection;-><init>(Landroid/widget/TextView;)V
 Lcom/android/internal/widget/ILockSettings$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/widget/ILockSettings;
 Lcom/android/internal/widget/ILockSettings;->getBoolean(Ljava/lang/String;ZI)Z
 Lcom/android/internal/widget/ILockSettings;->getLong(Ljava/lang/String;JI)J
@@ -2090,17 +4092,109 @@
 Lcom/android/internal/widget/IRemoteViewsFactory;->hasStableIds()Z
 Lcom/android/internal/widget/IRemoteViewsFactory;->isCreated()Z
 Lcom/android/internal/widget/IRemoteViewsFactory;->onDataSetChanged()V
+Lcom/android/internal/widget/LinearLayoutWithDefaultTouchRecepient;-><init>(Landroid/content/Context;)V
+Lcom/android/internal/widget/LinearLayoutWithDefaultTouchRecepient;->setDefaultTouchRecepient(Landroid/view/View;)V
+Lcom/android/internal/widget/LockPatternChecker;->checkPassword(Lcom/android/internal/widget/LockPatternUtils;Ljava/lang/String;ILcom/android/internal/widget/LockPatternChecker$OnCheckCallback;)Landroid/os/AsyncTask;
+Lcom/android/internal/widget/LockPatternUtils$RequestThrottledException;-><init>(I)V
+Lcom/android/internal/widget/LockPatternUtils$RequestThrottledException;->getTimeoutMs()I
+Lcom/android/internal/widget/LockPatternUtils;-><init>(Landroid/content/Context;)V
+Lcom/android/internal/widget/LockPatternUtils;->checkPassword(Ljava/lang/String;I)Z
+Lcom/android/internal/widget/LockPatternUtils;->getActivePasswordQuality(I)I
+Lcom/android/internal/widget/LockPatternUtils;->getDevicePolicyManager()Landroid/app/admin/DevicePolicyManager;
+Lcom/android/internal/widget/LockPatternUtils;->getKeyguardStoredPasswordQuality(I)I
+Lcom/android/internal/widget/LockPatternUtils;->getLockSettings()Lcom/android/internal/widget/ILockSettings;
+Lcom/android/internal/widget/LockPatternUtils;->getOwnerInfo(I)Ljava/lang/String;
+Lcom/android/internal/widget/LockPatternUtils;->getPowerButtonInstantlyLocks(I)Z
+Lcom/android/internal/widget/LockPatternUtils;->getString(Ljava/lang/String;I)Ljava/lang/String;
+Lcom/android/internal/widget/LockPatternUtils;->isDeviceEncryptionEnabled()Z
+Lcom/android/internal/widget/LockPatternUtils;->isLockPasswordEnabled(I)Z
+Lcom/android/internal/widget/LockPatternUtils;->isLockPatternEnabled(I)Z
+Lcom/android/internal/widget/LockPatternUtils;->isLockScreenDisabled(I)Z
+Lcom/android/internal/widget/LockPatternUtils;->isSecure(I)Z
+Lcom/android/internal/widget/LockPatternUtils;->isTactileFeedbackEnabled()Z
+Lcom/android/internal/widget/LockPatternUtils;->isVisiblePatternEnabled(I)Z
+Lcom/android/internal/widget/LockPatternUtils;->mContentResolver:Landroid/content/ContentResolver;
+Lcom/android/internal/widget/LockPatternUtils;->mContext:Landroid/content/Context;
+Lcom/android/internal/widget/LockPatternUtils;->patternToHash(Ljava/util/List;)[B
+Lcom/android/internal/widget/LockPatternUtils;->patternToString(Ljava/util/List;)Ljava/lang/String;
+Lcom/android/internal/widget/LockPatternUtils;->reportFailedPasswordAttempt(I)V
+Lcom/android/internal/widget/LockPatternUtils;->reportSuccessfulPasswordAttempt(I)V
+Lcom/android/internal/widget/LockPatternUtils;->saveLockPassword(Ljava/lang/String;Ljava/lang/String;II)V
+Lcom/android/internal/widget/LockPatternUtils;->setLockoutAttemptDeadline(II)J
+Lcom/android/internal/widget/LockPatternUtils;->setLong(Ljava/lang/String;JI)V
+Lcom/android/internal/widget/LockPatternUtils;->setOwnerInfo(Ljava/lang/String;I)V
+Lcom/android/internal/widget/LockPatternUtils;->setOwnerInfoEnabled(ZI)V
+Lcom/android/internal/widget/LockPatternUtils;->setString(Ljava/lang/String;Ljava/lang/String;I)V
+Lcom/android/internal/widget/LockPatternView$Cell;->column:I
+Lcom/android/internal/widget/LockPatternView$Cell;->row:I
+Lcom/android/internal/widget/LockPatternView$DisplayMode;->Animate:Lcom/android/internal/widget/LockPatternView$DisplayMode;
+Lcom/android/internal/widget/LockPatternView$DisplayMode;->Correct:Lcom/android/internal/widget/LockPatternView$DisplayMode;
+Lcom/android/internal/widget/LockPatternView$DisplayMode;->Wrong:Lcom/android/internal/widget/LockPatternView$DisplayMode;
+Lcom/android/internal/widget/LockPatternView$SavedState;-><init>(Landroid/os/Parcel;)V
+Lcom/android/internal/widget/LockPatternView$SavedState;-><init>(Landroid/os/Parcelable;Ljava/lang/String;IZZZ)V
+Lcom/android/internal/widget/LockPatternView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
+Lcom/android/internal/widget/LockPatternView;->clearPattern()V
+Lcom/android/internal/widget/LockPatternView;->disableInput()V
+Lcom/android/internal/widget/LockPatternView;->enableInput()V
+Lcom/android/internal/widget/LockPatternView;->getCellStates()[[Lcom/android/internal/widget/LockPatternView$CellState;
+Lcom/android/internal/widget/LockPatternView;->mInStealthMode:Z
+Lcom/android/internal/widget/LockPatternView;->mPaint:Landroid/graphics/Paint;
+Lcom/android/internal/widget/LockPatternView;->mPathPaint:Landroid/graphics/Paint;
+Lcom/android/internal/widget/LockPatternView;->mPattern:Ljava/util/ArrayList;
+Lcom/android/internal/widget/LockPatternView;->mPatternDisplayMode:Lcom/android/internal/widget/LockPatternView$DisplayMode;
+Lcom/android/internal/widget/LockPatternView;->mPatternInProgress:Z
+Lcom/android/internal/widget/LockPatternView;->mSquareHeight:F
+Lcom/android/internal/widget/LockPatternView;->mSquareWidth:F
+Lcom/android/internal/widget/LockPatternView;->notifyPatternDetected()V
+Lcom/android/internal/widget/LockPatternView;->setDisplayMode(Lcom/android/internal/widget/LockPatternView$DisplayMode;)V
+Lcom/android/internal/widget/LockPatternView;->setInStealthMode(Z)V
+Lcom/android/internal/widget/LockPatternView;->setOnPatternListener(Lcom/android/internal/widget/LockPatternView$OnPatternListener;)V
+Lcom/android/internal/widget/LockPatternView;->setTactileFeedbackEnabled(Z)V
+Lcom/android/internal/widget/PointerLocationView$PointerState;-><init>()V
+Lcom/android/internal/widget/PointerLocationView$PointerState;->mCurDown:Z
+Lcom/android/internal/widget/PointerLocationView;->mCurDown:Z
+Lcom/android/internal/widget/PointerLocationView;->mCurNumPointers:I
+Lcom/android/internal/widget/PointerLocationView;->mMaxNumPointers:I
+Lcom/android/internal/widget/PointerLocationView;->mPointers:Ljava/util/ArrayList;
+Lcom/android/internal/widget/PointerLocationView;->mPrintCoords:Z
+Lcom/android/internal/widget/PreferenceImageView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
+Lcom/android/internal/widget/RecyclerView$RecycledViewPool$ScrapData;->mScrapHeap:Ljava/util/ArrayList;
 Lcom/android/internal/widget/ScrollBarUtils;->getThumbLength(IIII)I
+Lcom/android/internal/widget/SlidingTab$Slider;->tab:Landroid/widget/ImageView;
+Lcom/android/internal/widget/SlidingTab$Slider;->text:Landroid/widget/TextView;
+Lcom/android/internal/widget/SlidingTab;->mAnimationDoneListener:Landroid/view/animation/Animation$AnimationListener;
+Lcom/android/internal/widget/SlidingTab;->mLeftSlider:Lcom/android/internal/widget/SlidingTab$Slider;
+Lcom/android/internal/widget/SlidingTab;->mRightSlider:Lcom/android/internal/widget/SlidingTab$Slider;
+Lcom/android/internal/widget/SlidingTab;->onAnimationDone()V
+Lcom/android/internal/widget/SlidingTab;->resetView()V
+Lcom/android/internal/widget/SlidingTab;->setHoldAfterTrigger(ZZ)V
+Lcom/android/internal/widget/SlidingTab;->setLeftHintText(I)V
+Lcom/android/internal/widget/SlidingTab;->setLeftTabResources(IIII)V
+Lcom/android/internal/widget/SlidingTab;->setOnTriggerListener(Lcom/android/internal/widget/SlidingTab$OnTriggerListener;)V
+Lcom/android/internal/widget/SlidingTab;->setRightHintText(I)V
+Lcom/android/internal/widget/SlidingTab;->setRightTabResources(IIII)V
+Lcom/android/internal/widget/TextViewInputDisabler;-><init>(Landroid/widget/TextView;)V
+Lcom/android/internal/widget/TextViewInputDisabler;->setInputEnabled(Z)V
+Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageScrolled(IFI)V
+Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageScrollStateChanged(I)V
+Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageSelected(I)V
+Lcom/android/internal/widget/ViewPager;->getCurrentItem()I
 Lcom/android/okhttp/Connection;->getSocket()Ljava/net/Socket;
 Lcom/android/okhttp/ConnectionPool;->connections:Ljava/util/Deque;
 Lcom/android/okhttp/ConnectionPool;->keepAliveDurationNs:J
 Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I
 Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool;
+Lcom/android/okhttp/HttpHandler;-><init>()V
+Lcom/android/okhttp/HttpsHandler;-><init>()V
 Lcom/android/okhttp/HttpUrl$Builder;->build()Lcom/android/okhttp/HttpUrl;
 Lcom/android/okhttp/HttpUrl;->encodedPath()Ljava/lang/String;
 Lcom/android/okhttp/HttpUrl;->newBuilder()Lcom/android/okhttp/HttpUrl$Builder;
 Lcom/android/okhttp/HttpUrl;->parse(Ljava/lang/String;)Lcom/android/okhttp/HttpUrl;
 Lcom/android/okhttp/HttpUrl;->query()Ljava/lang/String;
+Lcom/android/okhttp/internal/http/HeaderParser;->skipUntil(Ljava/lang/String;ILjava/lang/String;)I
+Lcom/android/okhttp/internal/http/HeaderParser;->skipWhitespace(Ljava/lang/String;I)I
+Lcom/android/okhttp/internal/http/HttpDate;->format(Ljava/util/Date;)Ljava/lang/String;
+Lcom/android/okhttp/internal/http/HttpDate;->parse(Ljava/lang/String;)Ljava/util/Date;
 Lcom/android/okhttp/internal/http/HttpEngine;->getConnection()Lcom/android/okhttp/Connection;
 Lcom/android/okhttp/internal/http/HttpEngine;->hasResponse()Z
 Lcom/android/okhttp/internal/http/HttpEngine;->httpStream:Lcom/android/okhttp/internal/http/HttpStream;
@@ -2113,6 +4207,29 @@
 Lcom/android/okhttp/internal/http/HttpEngine;->userResponse:Lcom/android/okhttp/Response;
 Lcom/android/okhttp/internal/http/HttpEngine;->writingRequestHeaders()V
 Lcom/android/okhttp/internal/http/RouteSelector;->hasNext()Z
+Lcom/android/okhttp/internal/huc/HttpsURLConnectionImpl;->delegate:Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;
+Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;->client:Lcom/android/okhttp/OkHttpClient;
+Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;->httpEngine:Lcom/android/okhttp/internal/http/HttpEngine;
+Lcom/android/okhttp/internal/Internal;-><init>()V
+Lcom/android/okhttp/internal/Internal;->addLenient(Lcom/android/okhttp/Headers$Builder;Ljava/lang/String;)V
+Lcom/android/okhttp/internal/Internal;->addLenient(Lcom/android/okhttp/Headers$Builder;Ljava/lang/String;Ljava/lang/String;)V
+Lcom/android/okhttp/internal/Internal;->apply(Lcom/android/okhttp/ConnectionSpec;Ljavax/net/ssl/SSLSocket;Z)V
+Lcom/android/okhttp/internal/Internal;->callEngineGetStreamAllocation(Lcom/android/okhttp/Call;)Lcom/android/okhttp/internal/http/StreamAllocation;
+Lcom/android/okhttp/internal/Internal;->callEnqueue(Lcom/android/okhttp/Call;Lcom/android/okhttp/Callback;Z)V
+Lcom/android/okhttp/internal/Internal;->connectionBecameIdle(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/internal/io/RealConnection;)Z
+Lcom/android/okhttp/internal/Internal;->get(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/Address;Lcom/android/okhttp/internal/http/StreamAllocation;)Lcom/android/okhttp/internal/io/RealConnection;
+Lcom/android/okhttp/internal/Internal;->getHttpUrlChecked(Ljava/lang/String;)Lcom/android/okhttp/HttpUrl;
+Lcom/android/okhttp/internal/Internal;->instance:Lcom/android/okhttp/internal/Internal;
+Lcom/android/okhttp/internal/Internal;->internalCache(Lcom/android/okhttp/OkHttpClient;)Lcom/android/okhttp/internal/InternalCache;
+Lcom/android/okhttp/internal/Internal;->put(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/internal/io/RealConnection;)V
+Lcom/android/okhttp/internal/Internal;->routeDatabase(Lcom/android/okhttp/ConnectionPool;)Lcom/android/okhttp/internal/RouteDatabase;
+Lcom/android/okhttp/internal/Internal;->setCache(Lcom/android/okhttp/OkHttpClient;Lcom/android/okhttp/internal/InternalCache;)V
+Lcom/android/okhttp/internal/Platform;->get()Lcom/android/okhttp/internal/Platform;
+Lcom/android/okhttp/internal/Platform;->logW(Ljava/lang/String;)V
+Lcom/android/okhttp/internal/Util;->closeAll(Ljava/io/Closeable;Ljava/io/Closeable;)V
+Lcom/android/okhttp/internal/Util;->closeQuietly(Ljava/io/Closeable;)V
+Lcom/android/okhttp/internal/Util;->EMPTY_BYTE_ARRAY:[B
+Lcom/android/okhttp/internal/Util;->UTF_8:Ljava/nio/charset/Charset;
 Lcom/android/okhttp/OkHttpClient;-><init>()V
 Lcom/android/okhttp/OkHttpClient;->connectionPool:Lcom/android/okhttp/ConnectionPool;
 Lcom/android/okhttp/OkHttpClient;->DEFAULT_PROTOCOLS:Ljava/util/List;
@@ -2133,11 +4250,62 @@
 Lcom/android/okhttp/Response;->message:Ljava/lang/String;
 Lcom/android/okhttp/Response;->networkResponse:Lcom/android/okhttp/Response;
 Lcom/android/okhttp/Response;->protocol:Lcom/android/okhttp/Protocol;
+Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;-><init>()V
+Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;->add(Lcom/android/org/bouncycastle/asn1/ASN1Encodable;)V
+Lcom/android/org/bouncycastle/asn1/ASN1InputStream;-><init>(Ljava/io/InputStream;)V
+Lcom/android/org/bouncycastle/asn1/ASN1InputStream;-><init>([B)V
+Lcom/android/org/bouncycastle/asn1/ASN1InputStream;->readObject()Lcom/android/org/bouncycastle/asn1/ASN1Primitive;
+Lcom/android/org/bouncycastle/asn1/ASN1Integer;-><init>(Ljava/math/BigInteger;)V
+Lcom/android/org/bouncycastle/asn1/DERBitString;-><init>([B)V
+Lcom/android/org/bouncycastle/asn1/DEREncodableVector;-><init>()V
+Lcom/android/org/bouncycastle/asn1/DERInteger;-><init>(J)V
+Lcom/android/org/bouncycastle/asn1/DERInteger;-><init>(Ljava/math/BigInteger;)V
+Lcom/android/org/bouncycastle/asn1/DERNull;->INSTANCE:Lcom/android/org/bouncycastle/asn1/DERNull;
+Lcom/android/org/bouncycastle/asn1/DERObjectIdentifier;-><init>(Ljava/lang/String;)V
+Lcom/android/org/bouncycastle/asn1/DEROctetString;-><init>([B)V
+Lcom/android/org/bouncycastle/asn1/DEROutputStream;-><init>(Ljava/io/OutputStream;)V
+Lcom/android/org/bouncycastle/asn1/DERSequence;-><init>()V
+Lcom/android/org/bouncycastle/asn1/DERSequence;-><init>(Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;)V
+Lcom/android/org/bouncycastle/asn1/DERSet;-><init>(Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;)V
+Lcom/android/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers;->sha256WithRSAEncryption:Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;
+Lcom/android/org/bouncycastle/asn1/x509/AlgorithmIdentifier;-><init>(Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;)V
+Lcom/android/org/bouncycastle/asn1/x509/AlgorithmIdentifier;-><init>(Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;Lcom/android/org/bouncycastle/asn1/ASN1Encodable;)V
+Lcom/android/org/bouncycastle/asn1/x509/Certificate;->getInstance(Ljava/lang/Object;)Lcom/android/org/bouncycastle/asn1/x509/Certificate;
+Lcom/android/org/bouncycastle/asn1/x509/DigestInfo;-><init>(Lcom/android/org/bouncycastle/asn1/x509/AlgorithmIdentifier;[B)V
+Lcom/android/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo;->getInstance(Ljava/lang/Object;)Lcom/android/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo;
+Lcom/android/org/bouncycastle/asn1/x509/Time;-><init>(Ljava/util/Date;)V
+Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;-><init>()V
+Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->generateTBSCertificate()Lcom/android/org/bouncycastle/asn1/x509/TBSCertificate;
+Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setEndDate(Lcom/android/org/bouncycastle/asn1/x509/Time;)V
+Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setIssuer(Lcom/android/org/bouncycastle/asn1/x509/X509Name;)V
+Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setSerialNumber(Lcom/android/org/bouncycastle/asn1/ASN1Integer;)V
+Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setSignature(Lcom/android/org/bouncycastle/asn1/x509/AlgorithmIdentifier;)V
+Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setStartDate(Lcom/android/org/bouncycastle/asn1/x509/Time;)V
+Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setSubject(Lcom/android/org/bouncycastle/asn1/x509/X509Name;)V
+Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setSubjectPublicKeyInfo(Lcom/android/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo;)V
+Lcom/android/org/bouncycastle/asn1/x509/X509Name;-><init>(Lcom/android/org/bouncycastle/asn1/ASN1Sequence;)V
+Lcom/android/org/bouncycastle/asn1/x509/X509Name;-><init>(Ljava/lang/String;)V
+Lcom/android/org/bouncycastle/asn1/x509/X509Name;->CN:Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;
+Lcom/android/org/bouncycastle/asn1/x509/X509Name;->getOIDs()Ljava/util/Vector;
+Lcom/android/org/bouncycastle/asn1/x509/X509Name;->getValues()Ljava/util/Vector;
+Lcom/android/org/bouncycastle/asn1/x9/X9ObjectIdentifiers;->ecdsa_with_SHA256:Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;
+Lcom/android/org/bouncycastle/jce/provider/BouncyCastleProvider;-><init>()V
+Lcom/android/org/bouncycastle/jce/provider/X509CertificateObject;-><init>(Lcom/android/org/bouncycastle/asn1/x509/Certificate;)V
+Lcom/android/org/bouncycastle/jce/X509Principal;-><init>([B)V
+Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;-><init>()V
+Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->generate(Ljava/security/PrivateKey;)Ljava/security/cert/X509Certificate;
+Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setIssuerDN(Lcom/android/org/bouncycastle/asn1/x509/X509Name;)V
+Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setIssuerDN(Ljavax/security/auth/x500/X500Principal;)V
+Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setNotAfter(Ljava/util/Date;)V
+Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setNotBefore(Ljava/util/Date;)V
+Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setPublicKey(Ljava/security/PublicKey;)V
+Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setSerialNumber(Ljava/math/BigInteger;)V
+Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setSignatureAlgorithm(Ljava/lang/String;)V
+Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setSubjectDN(Lcom/android/org/bouncycastle/asn1/x509/X509Name;)V
+Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setSubjectDN(Ljavax/security/auth/x500/X500Principal;)V
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->getAlpnSelectedProtocol()[B
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->getApplicationProtocol()Ljava/lang/String;
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->getApplicationProtocols()[Ljava/lang/String;
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->getChannelId()[B
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->getHandshakeApplicationProtocol()Ljava/lang/String;
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->getHostname()Ljava/lang/String;
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->getHostnameOrIP()Ljava/lang/String;
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->getNpnSelectedProtocol()[B
@@ -2152,14 +4320,73 @@
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->setNpnProtocols([B)V
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->setSoWriteTimeout(I)V
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->setUseSessionTickets(Z)V
+Lcom/android/org/conscrypt/ClientSessionContext;->getSession(Ljava/lang/String;I)Lcom/android/org/conscrypt/NativeSslSession;
+Lcom/android/org/conscrypt/ClientSessionContext;->setPersistentCache(Lcom/android/org/conscrypt/SSLClientSessionCache;)V
 Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setHostname(Ljava/lang/String;)V
 Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setUseSessionTickets(Z)V
+Lcom/android/org/conscrypt/FileClientSessionCache$Impl;->getSessionData(Ljava/lang/String;I)[B
+Lcom/android/org/conscrypt/FileClientSessionCache;->usingDirectory(Ljava/io/File;)Lcom/android/org/conscrypt/SSLClientSessionCache;
+Lcom/android/org/conscrypt/NativeCrypto;->ASN1_seq_pack_X509([J)[B
+Lcom/android/org/conscrypt/NativeCrypto;->ASN1_seq_unpack_X509_bio(J)[J
+Lcom/android/org/conscrypt/NativeCrypto;->ASN1_TIME_to_Calendar(JLjava/util/Calendar;)V
+Lcom/android/org/conscrypt/NativeCrypto;->BIO_free_all(J)V
+Lcom/android/org/conscrypt/NativeCrypto;->create_BIO_InputStream(Lcom/android/org/conscrypt/OpenSSLBIOInputStream;Z)J
+Lcom/android/org/conscrypt/NativeCrypto;->create_BIO_OutputStream(Ljava/io/OutputStream;)J
+Lcom/android/org/conscrypt/NativeCrypto;->d2i_PKCS7_bio(JI)[J
+Lcom/android/org/conscrypt/NativeCrypto;->d2i_SSL_SESSION([B)J
+Lcom/android/org/conscrypt/NativeCrypto;->d2i_X509([B)J
+Lcom/android/org/conscrypt/NativeCrypto;->d2i_X509_bio(J)J
+Lcom/android/org/conscrypt/NativeCrypto;->d2i_X509_CRL_bio(J)J
+Lcom/android/org/conscrypt/NativeCrypto;->EC_GROUP_clear_free(J)V
+Lcom/android/org/conscrypt/NativeCrypto;->EC_GROUP_new_by_curve_name(Ljava/lang/String;)J
+Lcom/android/org/conscrypt/NativeCrypto;->EC_POINT_clear_free(J)V
+Lcom/android/org/conscrypt/NativeCrypto;->EVP_CIPHER_CTX_new()J
+Lcom/android/org/conscrypt/NativeCrypto;->EVP_CIPHER_iv_length(J)I
+Lcom/android/org/conscrypt/NativeCrypto;->EVP_get_cipherbyname(Ljava/lang/String;)J
+Lcom/android/org/conscrypt/NativeCrypto;->EVP_get_digestbyname(Ljava/lang/String;)J
+Lcom/android/org/conscrypt/NativeCrypto;->EVP_MD_CTX_create()J
+Lcom/android/org/conscrypt/NativeCrypto;->EVP_MD_CTX_destroy(J)V
+Lcom/android/org/conscrypt/NativeCrypto;->EVP_MD_size(J)I
+Lcom/android/org/conscrypt/NativeCrypto;->EVP_PKEY_free(J)V
+Lcom/android/org/conscrypt/NativeCrypto;->EVP_PKEY_new_RSA([B[B[B[B[B[B[B[B)J
+Lcom/android/org/conscrypt/NativeCrypto;->get_X509_REVOKED_ext_oids(JI)[Ljava/lang/String;
+Lcom/android/org/conscrypt/NativeCrypto;->get_X509_REVOKED_revocationDate(J)J
+Lcom/android/org/conscrypt/NativeCrypto;->i2d_PKCS7([J)[B
+Lcom/android/org/conscrypt/NativeCrypto;->i2d_SSL_SESSION(J)[B
+Lcom/android/org/conscrypt/NativeCrypto;->i2d_X509_REVOKED(J)[B
+Lcom/android/org/conscrypt/NativeCrypto;->PEM_read_bio_PKCS7(JI)[J
+Lcom/android/org/conscrypt/NativeCrypto;->PEM_read_bio_X509(J)J
+Lcom/android/org/conscrypt/NativeCrypto;->PEM_read_bio_X509_CRL(J)J
+Lcom/android/org/conscrypt/NativeCrypto;->RAND_bytes([B)V
+Lcom/android/org/conscrypt/NativeCrypto;->RSA_generate_key_ex(I[B)J
+Lcom/android/org/conscrypt/NativeCrypto;->SSL_CTX_new()J
+Lcom/android/org/conscrypt/NativeCrypto;->SSL_SESSION_cipher(J)Ljava/lang/String;
+Lcom/android/org/conscrypt/NativeCrypto;->SSL_SESSION_free(J)V
+Lcom/android/org/conscrypt/NativeCrypto;->SSL_SESSION_get_time(J)J
+Lcom/android/org/conscrypt/NativeCrypto;->SSL_SESSION_get_version(J)Ljava/lang/String;
+Lcom/android/org/conscrypt/NativeCrypto;->SSL_SESSION_session_id(J)[B
+Lcom/android/org/conscrypt/NativeCrypto;->X509_REVOKED_dup(J)J
+Lcom/android/org/conscrypt/NativeCrypto;->X509_REVOKED_get_ext(JLjava/lang/String;)J
+Lcom/android/org/conscrypt/NativeCrypto;->X509_REVOKED_get_ext_oid(JLjava/lang/String;)[B
+Lcom/android/org/conscrypt/NativeCrypto;->X509_REVOKED_get_serialNumber(J)[B
+Lcom/android/org/conscrypt/NativeCrypto;->X509_REVOKED_print(JJ)V
+Lcom/android/org/conscrypt/NativeCrypto;->X509_supported_extension(J)I
+Lcom/android/org/conscrypt/OpenSSLBIOInputStream;-><init>(Ljava/io/InputStream;Z)V
+Lcom/android/org/conscrypt/OpenSSLBIOInputStream;->getBioContext()J
+Lcom/android/org/conscrypt/OpenSSLBIOInputStream;->release()V
+Lcom/android/org/conscrypt/OpenSSLContextImpl$TLSv12;-><init>()V
+Lcom/android/org/conscrypt/OpenSSLContextImpl;-><init>()V
+Lcom/android/org/conscrypt/OpenSSLContextImpl;->engineGetClientSessionContext()Lcom/android/org/conscrypt/ClientSessionContext;
+Lcom/android/org/conscrypt/OpenSSLContextImpl;->getPreferred()Lcom/android/org/conscrypt/OpenSSLContextImpl;
 Lcom/android/org/conscrypt/OpenSSLKey;-><init>(J)V
 Lcom/android/org/conscrypt/OpenSSLKey;->fromPrivateKey(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey;
 Lcom/android/org/conscrypt/OpenSSLKey;->getNativeRef()Lcom/android/org/conscrypt/NativeRef$EVP_PKEY;
 Lcom/android/org/conscrypt/OpenSSLKey;->getPublicKey()Ljava/security/PublicKey;
+Lcom/android/org/conscrypt/OpenSSLKeyHolder;->getOpenSSLKey()Lcom/android/org/conscrypt/OpenSSLKey;
 Lcom/android/org/conscrypt/OpenSSLProvider;-><init>()V
 Lcom/android/org/conscrypt/OpenSSLRandom;-><init>()V
+Lcom/android/org/conscrypt/OpenSSLSocketFactoryImpl;-><init>()V
+Lcom/android/org/conscrypt/OpenSSLSocketFactoryImpl;->sslParameters:Lcom/android/org/conscrypt/SSLParametersImpl;
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getChannelId()[B
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostname()Ljava/lang/String;
@@ -2177,32 +4404,83 @@
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setUseSessionTickets(Z)V
 Lcom/android/org/conscrypt/OpenSSLX509Certificate;->fromX509PemInputStream(Ljava/io/InputStream;)Lcom/android/org/conscrypt/OpenSSLX509Certificate;
 Lcom/android/org/conscrypt/OpenSSLX509Certificate;->mContext:J
+Lcom/android/org/conscrypt/SSLParametersImpl;->getDefault()Lcom/android/org/conscrypt/SSLParametersImpl;
+Lcom/android/org/conscrypt/SSLParametersImpl;->getDefaultX509TrustManager()Ljavax/net/ssl/X509TrustManager;
+Lcom/android/org/conscrypt/SSLParametersImpl;->getX509TrustManager()Ljavax/net/ssl/X509TrustManager;
+Lcom/android/org/conscrypt/SSLParametersImpl;->setEnabledProtocols([Ljava/lang/String;)V
+Lcom/android/org/conscrypt/SSLParametersImpl;->x509TrustManager:Ljavax/net/ssl/X509TrustManager;
 Lcom/android/org/conscrypt/TrustedCertificateStore;-><init>()V
 Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateChain(Ljava/security/cert/X509Certificate;)Ljava/util/List;
 Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;)V
 Lcom/android/org/conscrypt/TrustManagerImpl;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;
+Lcom/android/org/conscrypt/X509PublicKey;-><init>(Ljava/lang/String;[B)V
+Lcom/android/server/net/BaseNetworkObserver;-><init>()V
+Lcom/android/server/net/NetlinkTracker;-><init>(Ljava/lang/String;Lcom/android/server/net/NetlinkTracker$Callback;)V
+Lcom/android/server/net/NetlinkTracker;->clearLinkProperties()V
+Lcom/android/server/net/NetlinkTracker;->getLinkProperties()Landroid/net/LinkProperties;
+Lcom/android/server/ResettableTimeout$T;-><init>(Lcom/android/server/ResettableTimeout;)V
+Lcom/android/server/ResettableTimeout;->mLock:Landroid/os/ConditionVariable;
+Lcom/android/server/ResettableTimeout;->mOffAt:J
+Lcom/google/android/collect/Lists;->newArrayList([Ljava/lang/Object;)Ljava/util/ArrayList;
+Lcom/google/android/collect/Sets;->newArraySet()Landroid/util/ArraySet;
+Lcom/google/android/collect/Sets;->newArraySet([Ljava/lang/Object;)Landroid/util/ArraySet;
+Lcom/google/android/collect/Sets;->newHashSet()Ljava/util/HashSet;
+Lcom/google/android/collect/Sets;->newHashSet([Ljava/lang/Object;)Ljava/util/HashSet;
+Lcom/google/android/collect/Sets;->newSortedSet()Ljava/util/SortedSet;
+Lcom/google/android/gles_jni/EGLImpl;-><init>()V
+Lcom/google/android/gles_jni/GLImpl;-><init>()V
 Lcom/google/android/mms/ContentType;->getAudioTypes()Ljava/util/ArrayList;
 Lcom/google/android/mms/ContentType;->getImageTypes()Ljava/util/ArrayList;
 Lcom/google/android/mms/ContentType;->getVideoTypes()Ljava/util/ArrayList;
 Lcom/google/android/mms/ContentType;->isAudioType(Ljava/lang/String;)Z
 Lcom/google/android/mms/ContentType;->isDrmType(Ljava/lang/String;)Z
 Lcom/google/android/mms/ContentType;->isImageType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isSupportedAudioType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isSupportedImageType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isSupportedType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isSupportedVideoType(Ljava/lang/String;)Z
 Lcom/google/android/mms/ContentType;->isTextType(Ljava/lang/String;)Z
 Lcom/google/android/mms/ContentType;->isVideoType(Ljava/lang/String;)Z
+Lcom/google/android/mms/InvalidHeaderValueException;-><init>(Ljava/lang/String;)V
 Lcom/google/android/mms/MmsException;-><init>()V
 Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
 Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/Throwable;)V
 Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(I[B)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;->setReportAllowed(I)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;->setTransactionId([B)V
+Lcom/google/android/mms/pdu/Base64;->decodeBase64([B)[B
+Lcom/google/android/mms/pdu/CharacterSets;->getMibEnumValue(Ljava/lang/String;)I
 Lcom/google/android/mms/pdu/CharacterSets;->getMimeName(I)Ljava/lang/String;
+Lcom/google/android/mms/pdu/DeliveryInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/DeliveryInd;->getDate()J
 Lcom/google/android/mms/pdu/DeliveryInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/DeliveryInd;->getStatus()I
+Lcom/google/android/mms/pdu/DeliveryInd;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
 Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(I[B)V
 Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(Ljava/lang/String;)V
 Lcom/google/android/mms/pdu/EncodedStringValue;-><init>([B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;->appendTextString([B)V
 Lcom/google/android/mms/pdu/EncodedStringValue;->concat([Lcom/google/android/mms/pdu/EncodedStringValue;)Ljava/lang/String;
+Lcom/google/android/mms/pdu/EncodedStringValue;->copy(Lcom/google/android/mms/pdu/EncodedStringValue;)Lcom/google/android/mms/pdu/EncodedStringValue;
 Lcom/google/android/mms/pdu/EncodedStringValue;->encodeStrings([Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;->extract(Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;->getCharacterSet()I
 Lcom/google/android/mms/pdu/EncodedStringValue;->getString()Ljava/lang/String;
+Lcom/google/android/mms/pdu/EncodedStringValue;->getTextString()[B
+Lcom/google/android/mms/pdu/EncodedStringValue;->setCharacterSet(I)V
+Lcom/google/android/mms/pdu/EncodedStringValue;->setTextString([B)V
+Lcom/google/android/mms/pdu/GenericPdu;-><init>()V
+Lcom/google/android/mms/pdu/GenericPdu;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
 Lcom/google/android/mms/pdu/GenericPdu;->getMessageType()I
+Lcom/google/android/mms/pdu/GenericPdu;->getPduHeaders()Lcom/google/android/mms/pdu/PduHeaders;
+Lcom/google/android/mms/pdu/GenericPdu;->mPduHeaders:Lcom/google/android/mms/pdu/PduHeaders;
 Lcom/google/android/mms/pdu/GenericPdu;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/GenericPdu;->setMessageType(I)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>()V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->addTo(Lcom/google/android/mms/pdu/EncodedStringValue;)V
 Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getBody()Lcom/google/android/mms/pdu/PduBody;
 Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getDate()J
 Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getPriority()I
@@ -2212,15 +4490,31 @@
 Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setDate(J)V
 Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setPriority(I)V
 Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;-><init>()V
+Lcom/google/android/mms/pdu/NotificationInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/NotificationInd;->getContentClass()I
 Lcom/google/android/mms/pdu/NotificationInd;->getContentLocation()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getDeliveryReport()I
 Lcom/google/android/mms/pdu/NotificationInd;->getExpiry()J
 Lcom/google/android/mms/pdu/NotificationInd;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
 Lcom/google/android/mms/pdu/NotificationInd;->getMessageClass()[B
 Lcom/google/android/mms/pdu/NotificationInd;->getMessageSize()J
 Lcom/google/android/mms/pdu/NotificationInd;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
 Lcom/google/android/mms/pdu/NotificationInd;->getTransactionId()[B
+Lcom/google/android/mms/pdu/NotificationInd;->setContentClass(I)V
 Lcom/google/android/mms/pdu/NotificationInd;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/NotificationInd;->setDeliveryReport(I)V
+Lcom/google/android/mms/pdu/NotificationInd;->setExpiry(J)V
+Lcom/google/android/mms/pdu/NotificationInd;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;->setMessageClass([B)V
+Lcom/google/android/mms/pdu/NotificationInd;->setMessageSize(J)V
+Lcom/google/android/mms/pdu/NotificationInd;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;->setTransactionId([B)V
 Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(I[BI)V
+Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/NotifyRespInd;->setReportAllowed(I)V
+Lcom/google/android/mms/pdu/NotifyRespInd;->setStatus(I)V
+Lcom/google/android/mms/pdu/NotifyRespInd;->setTransactionId([B)V
 Lcom/google/android/mms/pdu/PduBody;-><init>()V
 Lcom/google/android/mms/pdu/PduBody;->addPart(ILcom/google/android/mms/pdu/PduPart;)V
 Lcom/google/android/mms/pdu/PduBody;->addPart(Lcom/google/android/mms/pdu/PduPart;)Z
@@ -2229,403 +4523,1114 @@
 Lcom/google/android/mms/pdu/PduBody;->getPartByContentLocation(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
 Lcom/google/android/mms/pdu/PduBody;->getPartByFileName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
 Lcom/google/android/mms/pdu/PduBody;->getPartByName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartIndex(Lcom/google/android/mms/pdu/PduPart;)I
 Lcom/google/android/mms/pdu/PduBody;->getPartsNum()I
+Lcom/google/android/mms/pdu/PduBody;->removePart(I)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduComposer$BufferStack;->copy()V
+Lcom/google/android/mms/pdu/PduComposer$BufferStack;->mark()Lcom/google/android/mms/pdu/PduComposer$PositionMarker;
+Lcom/google/android/mms/pdu/PduComposer$BufferStack;->newbuf()V
+Lcom/google/android/mms/pdu/PduComposer$BufferStack;->pop()V
+Lcom/google/android/mms/pdu/PduComposer$PositionMarker;->getLength()I
 Lcom/google/android/mms/pdu/PduComposer;-><init>(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)V
+Lcom/google/android/mms/pdu/PduComposer;->appendEncodedString(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/PduComposer;->appendHeader(I)I
+Lcom/google/android/mms/pdu/PduComposer;->appendLongInteger(J)V
+Lcom/google/android/mms/pdu/PduComposer;->appendOctet(I)V
+Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString([B)V
+Lcom/google/android/mms/pdu/PduComposer;->appendShortInteger(I)V
+Lcom/google/android/mms/pdu/PduComposer;->appendTextString(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/PduComposer;->appendTextString([B)V
+Lcom/google/android/mms/pdu/PduComposer;->appendUintvarInteger(J)V
+Lcom/google/android/mms/pdu/PduComposer;->appendValueLength(J)V
+Lcom/google/android/mms/pdu/PduComposer;->arraycopy([BII)V
 Lcom/google/android/mms/pdu/PduComposer;->make()[B
+Lcom/google/android/mms/pdu/PduComposer;->mContentTypeMap:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduComposer;->mMessage:Ljava/io/ByteArrayOutputStream;
+Lcom/google/android/mms/pdu/PduComposer;->mPdu:Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduComposer;->mPduHeader:Lcom/google/android/mms/pdu/PduHeaders;
+Lcom/google/android/mms/pdu/PduComposer;->mPosition:I
+Lcom/google/android/mms/pdu/PduComposer;->mResolver:Landroid/content/ContentResolver;
+Lcom/google/android/mms/pdu/PduComposer;->mStack:Lcom/google/android/mms/pdu/PduComposer$BufferStack;
+Lcom/google/android/mms/pdu/PduContentTypes;->contentTypes:[Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduHeaders;-><init>()V
+Lcom/google/android/mms/pdu/PduHeaders;->appendEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
+Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValue(I)Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValues(I)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/PduHeaders;->getLongInteger(I)J
+Lcom/google/android/mms/pdu/PduHeaders;->getOctet(I)I
+Lcom/google/android/mms/pdu/PduHeaders;->getTextString(I)[B
+Lcom/google/android/mms/pdu/PduHeaders;->setEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
+Lcom/google/android/mms/pdu/PduHeaders;->setLongInteger(JI)V
+Lcom/google/android/mms/pdu/PduHeaders;->setOctet(II)V
+Lcom/google/android/mms/pdu/PduParser;->$assertionsDisabled:Z
+Lcom/google/android/mms/pdu/PduParser;-><init>([BZ)V
+Lcom/google/android/mms/pdu/PduParser;->checkPartPosition(Lcom/google/android/mms/pdu/PduPart;)I
+Lcom/google/android/mms/pdu/PduParser;->log(Ljava/lang/String;)V
 Lcom/google/android/mms/pdu/PduParser;->parse()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduParser;->parseContentType(Ljava/io/ByteArrayInputStream;Ljava/util/HashMap;)[B
+Lcom/google/android/mms/pdu/PduParser;->parsePartHeaders(Ljava/io/ByteArrayInputStream;Lcom/google/android/mms/pdu/PduPart;I)Z
+Lcom/google/android/mms/pdu/PduParser;->parseShortInteger(Ljava/io/ByteArrayInputStream;)I
+Lcom/google/android/mms/pdu/PduParser;->parseUnsignedInt(Ljava/io/ByteArrayInputStream;)I
+Lcom/google/android/mms/pdu/PduParser;->parseValueLength(Ljava/io/ByteArrayInputStream;)I
+Lcom/google/android/mms/pdu/PduParser;->parseWapString(Ljava/io/ByteArrayInputStream;I)[B
 Lcom/google/android/mms/pdu/PduPart;-><init>()V
 Lcom/google/android/mms/pdu/PduPart;->generateLocation()Ljava/lang/String;
 Lcom/google/android/mms/pdu/PduPart;->getCharset()I
+Lcom/google/android/mms/pdu/PduPart;->getContentDisposition()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentId()[B
 Lcom/google/android/mms/pdu/PduPart;->getContentLocation()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentTransferEncoding()[B
 Lcom/google/android/mms/pdu/PduPart;->getContentType()[B
 Lcom/google/android/mms/pdu/PduPart;->getData()[B
+Lcom/google/android/mms/pdu/PduPart;->getDataLength()I
 Lcom/google/android/mms/pdu/PduPart;->getDataUri()Landroid/net/Uri;
 Lcom/google/android/mms/pdu/PduPart;->getFilename()[B
 Lcom/google/android/mms/pdu/PduPart;->getName()[B
 Lcom/google/android/mms/pdu/PduPart;->setCharset(I)V
+Lcom/google/android/mms/pdu/PduPart;->setContentDisposition([B)V
 Lcom/google/android/mms/pdu/PduPart;->setContentId([B)V
 Lcom/google/android/mms/pdu/PduPart;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentTransferEncoding([B)V
 Lcom/google/android/mms/pdu/PduPart;->setContentType([B)V
 Lcom/google/android/mms/pdu/PduPart;->setData([B)V
 Lcom/google/android/mms/pdu/PduPart;->setDataUri(Landroid/net/Uri;)V
+Lcom/google/android/mms/pdu/PduPart;->setFilename([B)V
+Lcom/google/android/mms/pdu/PduPart;->setName([B)V
+Lcom/google/android/mms/pdu/PduPersister;->ADDRESS_FIELDS:[I
+Lcom/google/android/mms/pdu/PduPersister;->CHARSET_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->ENCODED_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->getByteArrayFromPartColumn(Landroid/database/Cursor;I)[B
 Lcom/google/android/mms/pdu/PduPersister;->getBytes(Ljava/lang/String;)[B
+Lcom/google/android/mms/pdu/PduPersister;->getIntegerFromPartColumn(Landroid/database/Cursor;I)Ljava/lang/Integer;
+Lcom/google/android/mms/pdu/PduPersister;->getPartContentType(Lcom/google/android/mms/pdu/PduPart;)Ljava/lang/String;
 Lcom/google/android/mms/pdu/PduPersister;->getPduPersister(Landroid/content/Context;)Lcom/google/android/mms/pdu/PduPersister;
 Lcom/google/android/mms/pdu/PduPersister;->getPendingMessages(J)Landroid/database/Cursor;
 Lcom/google/android/mms/pdu/PduPersister;->load(Landroid/net/Uri;)Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduPersister;->loadRecipients(ILjava/util/HashSet;Ljava/util/HashMap;Z)V
+Lcom/google/android/mms/pdu/PduPersister;->LONG_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->mContentResolver:Landroid/content/ContentResolver;
+Lcom/google/android/mms/pdu/PduPersister;->mContext:Landroid/content/Context;
+Lcom/google/android/mms/pdu/PduPersister;->MESSAGE_BOX_MAP:Ljava/util/HashMap;
 Lcom/google/android/mms/pdu/PduPersister;->move(Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->mTelephonyManager:Landroid/telephony/TelephonyManager;
+Lcom/google/android/mms/pdu/PduPersister;->OCTET_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->PART_PROJECTION:[Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPersister;->PDU_CACHE_INSTANCE:Lcom/google/android/mms/util/PduCache;
 Lcom/google/android/mms/pdu/PduPersister;->persist(Lcom/google/android/mms/pdu/GenericPdu;Landroid/net/Uri;ZZLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->persistAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
 Lcom/google/android/mms/pdu/PduPersister;->persistPart(Lcom/google/android/mms/pdu/PduPart;JLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->TEXT_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
 Lcom/google/android/mms/pdu/PduPersister;->toIsoString([B)Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPersister;->updateAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
 Lcom/google/android/mms/pdu/PduPersister;->updateHeaders(Landroid/net/Uri;Lcom/google/android/mms/pdu/SendReq;)V
 Lcom/google/android/mms/pdu/PduPersister;->updateParts(Landroid/net/Uri;Lcom/google/android/mms/pdu/PduBody;Ljava/util/HashMap;)V
+Lcom/google/android/mms/pdu/QuotedPrintable;->decodeQuotedPrintable([B)[B
+Lcom/google/android/mms/pdu/ReadOrigInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
 Lcom/google/android/mms/pdu/ReadOrigInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/ReadOrigInd;->getReadStatus()I
 Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/EncodedStringValue;[BII[Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/ReadRecInd;->getMessageId()[B
 Lcom/google/android/mms/pdu/ReadRecInd;->setDate(J)V
+Lcom/google/android/mms/pdu/RetrieveConf;-><init>()V
+Lcom/google/android/mms/pdu/RetrieveConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/RetrieveConf;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/RetrieveConf;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/RetrieveConf;->getContentType()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->getDeliveryReport()I
 Lcom/google/android/mms/pdu/RetrieveConf;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/RetrieveConf;->getMessageClass()[B
 Lcom/google/android/mms/pdu/RetrieveConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->getReadReport()I
+Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveStatus()I
+Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveText()Lcom/google/android/mms/pdu/EncodedStringValue;
 Lcom/google/android/mms/pdu/RetrieveConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->setContentType([B)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setDeliveryReport(I)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setMessageClass([B)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setMessageId([B)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setReadReport(I)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveStatus(I)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveText(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setTransactionId([B)V
+Lcom/google/android/mms/pdu/SendConf;-><init>()V
+Lcom/google/android/mms/pdu/SendConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
 Lcom/google/android/mms/pdu/SendConf;->getMessageId()[B
 Lcom/google/android/mms/pdu/SendConf;->getResponseStatus()I
 Lcom/google/android/mms/pdu/SendConf;->getTransactionId()[B
 Lcom/google/android/mms/pdu/SendReq;-><init>()V
+Lcom/google/android/mms/pdu/SendReq;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/SendReq;->addBcc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
 Lcom/google/android/mms/pdu/SendReq;->getBcc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/SendReq;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/SendReq;->getContentType()[B
+Lcom/google/android/mms/pdu/SendReq;->getDeliveryReport()I
+Lcom/google/android/mms/pdu/SendReq;->getExpiry()J
+Lcom/google/android/mms/pdu/SendReq;->getMessageClass()[B
+Lcom/google/android/mms/pdu/SendReq;->getMessageSize()J
+Lcom/google/android/mms/pdu/SendReq;->getReadReport()I
 Lcom/google/android/mms/pdu/SendReq;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;->setBcc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->setCc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->setContentType([B)V
 Lcom/google/android/mms/pdu/SendReq;->setDeliveryReport(I)V
 Lcom/google/android/mms/pdu/SendReq;->setExpiry(J)V
 Lcom/google/android/mms/pdu/SendReq;->setMessageClass([B)V
 Lcom/google/android/mms/pdu/SendReq;->setMessageSize(J)V
 Lcom/google/android/mms/pdu/SendReq;->setReadReport(I)V
 Lcom/google/android/mms/pdu/SendReq;->setTo([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->setTransactionId([B)V
+Lcom/google/android/mms/util/AbstractCache;-><init>()V
 Lcom/google/android/mms/util/AbstractCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/google/android/mms/util/AbstractCache;->purge(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/google/android/mms/util/AbstractCache;->purgeAll()V
+Lcom/google/android/mms/util/AbstractCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Z
+Lcom/google/android/mms/util/DownloadDrmHelper;->isDrmConvertNeeded(Ljava/lang/String;)Z
+Lcom/google/android/mms/util/DownloadDrmHelper;->modifyDrmFwLockFileExtension(Ljava/lang/String;)Ljava/lang/String;
+Lcom/google/android/mms/util/DrmConvertSession;->close(Ljava/lang/String;)I
+Lcom/google/android/mms/util/DrmConvertSession;->convert([BI)[B
+Lcom/google/android/mms/util/DrmConvertSession;->open(Landroid/content/Context;Ljava/lang/String;)Lcom/google/android/mms/util/DrmConvertSession;
+Lcom/google/android/mms/util/PduCache;-><init>()V
 Lcom/google/android/mms/util/PduCache;->getInstance()Lcom/google/android/mms/util/PduCache;
 Lcom/google/android/mms/util/PduCache;->isUpdating(Landroid/net/Uri;)Z
 Lcom/google/android/mms/util/PduCache;->purge(Landroid/net/Uri;)Lcom/google/android/mms/util/PduCacheEntry;
 Lcom/google/android/mms/util/PduCache;->purgeAll()V
+Lcom/google/android/mms/util/PduCacheEntry;-><init>(Lcom/google/android/mms/pdu/GenericPdu;IJ)V
+Lcom/google/android/mms/util/PduCacheEntry;->getMessageBox()I
 Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/util/PduCacheEntry;->getThreadId()J
+Lcom/google/android/mms/util/SqliteWrapper;->checkSQLiteException(Landroid/content/Context;Landroid/database/sqlite/SQLiteException;)V
+Lcom/google/android/mms/util/SqliteWrapper;->delete(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I
 Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
-Ljava/io/Console;->encoding()Ljava/lang/String;
-Ljava/io/File;->filePath:Ljava/nio/file/Path;
-Ljava/io/File;->fs:Ljava/io/FileSystem;
-Ljava/io/File;->path:Ljava/lang/String;
-Ljava/io/File;->prefixLength:I
-Ljava/io/File;->status:Ljava/io/File$PathStatus;
-Ljava/io/FileDescriptor;->descriptor:I
-Ljava/io/FileDescriptor;->getInt$()I
-Ljava/io/FileDescriptor;->isSocket$()Z
-Ljava/io/FileDescriptor;->setInt$(I)V
-Ljava/io/FileInputStream;->fd:Ljava/io/FileDescriptor;
-Ljava/io/FileOutputStream;->channel:Ljava/nio/channels/FileChannel;
-Ljava/io/FileOutputStream;->fd:Ljava/io/FileDescriptor;
-Ljava/io/FileSystem;->canonicalize(Ljava/lang/String;)Ljava/lang/String;
-Ljava/io/FileSystem;->checkAccess(Ljava/io/File;I)Z
-Ljava/io/FileSystem;->compare(Ljava/io/File;Ljava/io/File;)I
-Ljava/io/FileSystem;->createDirectory(Ljava/io/File;)Z
-Ljava/io/FileSystem;->createFileExclusively(Ljava/lang/String;)Z
-Ljava/io/FileSystem;->delete(Ljava/io/File;)Z
-Ljava/io/FileSystem;->fromURIPath(Ljava/lang/String;)Ljava/lang/String;
-Ljava/io/FileSystem;->getBooleanAttributes(Ljava/io/File;)I
-Ljava/io/FileSystem;->getDefaultParent()Ljava/lang/String;
-Ljava/io/FileSystem;->getLastModifiedTime(Ljava/io/File;)J
-Ljava/io/FileSystem;->getLength(Ljava/io/File;)J
-Ljava/io/FileSystem;->getPathSeparator()C
-Ljava/io/FileSystem;->getSeparator()C
-Ljava/io/FileSystem;->getSpace(Ljava/io/File;I)J
-Ljava/io/FileSystem;->hashCode(Ljava/io/File;)I
-Ljava/io/FileSystem;->isAbsolute(Ljava/io/File;)Z
-Ljava/io/FileSystem;->list(Ljava/io/File;)[Ljava/lang/String;
-Ljava/io/FileSystem;->listRoots()[Ljava/io/File;
-Ljava/io/FileSystem;->normalize(Ljava/lang/String;)Ljava/lang/String;
-Ljava/io/FileSystem;->prefixLength(Ljava/lang/String;)I
-Ljava/io/FileSystem;->rename(Ljava/io/File;Ljava/io/File;)Z
-Ljava/io/FileSystem;->resolve(Ljava/io/File;)Ljava/lang/String;
-Ljava/io/FileSystem;->resolve(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
-Ljava/io/FileSystem;->setLastModifiedTime(Ljava/io/File;J)Z
-Ljava/io/FileSystem;->setPermission(Ljava/io/File;IZZ)Z
-Ljava/io/FileSystem;->setReadOnly(Ljava/io/File;)Z
-Ljava/io/ObjectInputStream;->bin:Ljava/io/ObjectInputStream$BlockDataInputStream;
-Ljava/io/ObjectInputStream;->bytesToDoubles([BI[DII)V
-Ljava/io/ObjectInputStream;->bytesToFloats([BI[FII)V
-Ljava/io/ObjectOutputStream;->protocol:I
-Ljava/io/ObjectStreamClass;->computeDefaultSUID(Ljava/lang/Class;)J
-Ljava/io/ObjectStreamClass;->computeFieldOffsets()V
-Ljava/io/ObjectStreamClass;->fields:[Ljava/io/ObjectStreamField;
-Ljava/io/ObjectStreamClass;->getConstructorId(Ljava/lang/Class;)J
-Ljava/io/ObjectStreamClass;->getLocalDesc()Ljava/io/ObjectStreamClass;
-Ljava/io/ObjectStreamClass;->getNumObjFields()I
-Ljava/io/ObjectStreamClass;->getPrimDataSize()I
-Ljava/io/ObjectStreamClass;->hasReadObjectMethod()Z
-Ljava/io/ObjectStreamClass;->hasReadObjectNoDataMethod()Z
-Ljava/io/ObjectStreamClass;->hasWriteObjectData()Z
-Ljava/io/ObjectStreamClass;->newInstance()Ljava/lang/Object;
-Ljava/io/ObjectStreamClass;->newInstance(Ljava/lang/Class;J)Ljava/lang/Object;
-Ljava/io/ObjectStreamField;->getField()Ljava/lang/reflect/Field;
-Ljava/io/RandomAccessFile;->fd:Ljava/io/FileDescriptor;
-Ljava/lang/AbstractStringBuilder;->value:[C
-Ljava/lang/Boolean;->value:Z
-Ljava/lang/Byte;->toHexString(BZ)Ljava/lang/String;
-Ljava/lang/Byte;->value:B
-Ljava/lang/Character;->value:C
-Ljava/lang/Class;-><init>()V
-Ljava/lang/Class;->accessFlags:I
-Ljava/lang/Class;->classLoader:Ljava/lang/ClassLoader;
-Ljava/lang/Class;->clinitThreadId:I
-Ljava/lang/Class;->dexCache:Ljava/lang/Object;
-Ljava/lang/Class;->dexClassDefIndex:I
-Ljava/lang/Class;->getDeclaredMethodsUnchecked(Z)[Ljava/lang/reflect/Method;
-Ljava/lang/Class;->getMethod(Ljava/lang/String;[Ljava/lang/Class;Z)Ljava/lang/reflect/Method;
-Ljava/lang/Class;->ifTable:[Ljava/lang/Object;
-Ljava/lang/Class;->name:Ljava/lang/String;
-Ljava/lang/Class;->objectSize:I
-Ljava/lang/Class;->status:I
-Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader;
-Ljava/lang/Double;->value:D
-Ljava/lang/Enum;->getSharedConstants(Ljava/lang/Class;)[Ljava/lang/Enum;
-Ljava/lang/Enum;->name:Ljava/lang/String;
-Ljava/lang/Enum;->ordinal:I
-Ljava/lang/Float;->value:F
-Ljava/lang/Integer;->value:I
-Ljava/lang/invoke/MethodHandles$Lookup;-><init>(Ljava/lang/Class;I)V
-Ljava/lang/Long;->value:J
-Ljava/lang/Object;->identityHashCode(Ljava/lang/Object;)I
-Ljava/lang/ref/Reference;->getReferent()Ljava/lang/Object;
-Ljava/lang/ref/Reference;->referent:Ljava/lang/Object;
-Ljava/lang/ref/ReferenceQueue;->add(Ljava/lang/ref/Reference;)V
-Ljava/lang/reflect/AccessibleObject;->override:Z
-Ljava/lang/reflect/Constructor;->serializationCopy(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
-Ljava/lang/reflect/Executable;->artMethod:J
-Ljava/lang/reflect/Field;->accessFlags:I
-Ljava/lang/reflect/Field;->getOffset()I
-Ljava/lang/reflect/Parameter;-><init>(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V
-Ljava/lang/reflect/Proxy;->invoke(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/Runtime;-><init>()V
-Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V
-Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V
-Ljava/lang/Runtime;->loadLibrary0(Ljava/lang/ClassLoader;Ljava/lang/String;)V
-Ljava/lang/Runtime;->mLibPaths:[Ljava/lang/String;
-Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
-Ljava/lang/Short;->value:S
-Ljava/lang/StackTraceElement;->declaringClass:Ljava/lang/String;
-Ljava/lang/StackTraceElement;->fileName:Ljava/lang/String;
-Ljava/lang/StackTraceElement;->lineNumber:I
-Ljava/lang/StackTraceElement;->methodName:Ljava/lang/String;
-Ljava/lang/String;-><init>(II[C)V
-Ljava/lang/String;->count:I
-Ljava/lang/String;->getCharsNoCheck(II[CI)V
-Ljava/lang/String;->hash:I
-Ljava/lang/String;->indexOf([CII[CIII)I
-Ljava/lang/String;->lastIndexOf([CII[CIII)I
-Ljava/lang/System;-><init>()V
-Ljava/lang/System;->arraycopy([CI[CII)V
-Ljava/lang/System;->arraycopy([FI[FII)V
-Ljava/lang/System;->arraycopy([II[III)V
-Ljava/lang/System;->arraycopy([JI[JII)V
-Ljava/lang/System;->arraycopy([SI[SII)V
-Ljava/lang/System;->arraycopy([ZI[ZII)V
-Ljava/lang/System;->log(CLjava/lang/String;Ljava/lang/Throwable;)V
-Ljava/lang/System;->logE(Ljava/lang/String;)V
-Ljava/lang/System;->logE(Ljava/lang/String;Ljava/lang/Throwable;)V
-Ljava/lang/System;->logW(Ljava/lang/String;Ljava/lang/Throwable;)V
-Ljava/lang/Thread;-><init>(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V
-Ljava/lang/Thread;->contextClassLoader:Ljava/lang/ClassLoader;
-Ljava/lang/Thread;->daemon:Z
-Ljava/lang/Thread;->dispatchUncaughtException(Ljava/lang/Throwable;)V
-Ljava/lang/Thread;->getUncaughtExceptionPreHandler()Ljava/lang/Thread$UncaughtExceptionHandler;
-Ljava/lang/Thread;->group:Ljava/lang/ThreadGroup;
-Ljava/lang/Thread;->inheritableThreadLocals:Ljava/lang/ThreadLocal$ThreadLocalMap;
-Ljava/lang/Thread;->inheritedAccessControlContext:Ljava/security/AccessControlContext;
-Ljava/lang/Thread;->lock:Ljava/lang/Object;
-Ljava/lang/Thread;->name:Ljava/lang/String;
-Ljava/lang/Thread;->nativePeer:J
-Ljava/lang/Thread;->parkBlocker:Ljava/lang/Object;
-Ljava/lang/Thread;->priority:I
-Ljava/lang/Thread;->target:Ljava/lang/Runnable;
-Ljava/lang/Thread;->threadLocals:Ljava/lang/ThreadLocal$ThreadLocalMap;
-Ljava/lang/Thread;->threadSeqNumber:J
-Ljava/lang/ThreadGroup;->add(Ljava/lang/Thread;)V
-Ljava/lang/ThreadGroup;->groups:[Ljava/lang/ThreadGroup;
-Ljava/lang/ThreadGroup;->mainThreadGroup:Ljava/lang/ThreadGroup;
-Ljava/lang/ThreadGroup;->name:Ljava/lang/String;
-Ljava/lang/ThreadGroup;->ngroups:I
-Ljava/lang/ThreadGroup;->parent:Ljava/lang/ThreadGroup;
-Ljava/lang/ThreadGroup;->systemThreadGroup:Ljava/lang/ThreadGroup;
-Ljava/lang/ThreadGroup;->threadTerminated(Ljava/lang/Thread;)V
-Ljava/lang/ThreadLocal;->getMap(Ljava/lang/Thread;)Ljava/lang/ThreadLocal$ThreadLocalMap;
-Ljava/lang/Throwable;->backtrace:Ljava/lang/Object;
-Ljava/lang/Throwable;->cause:Ljava/lang/Throwable;
-Ljava/lang/Throwable;->detailMessage:Ljava/lang/String;
-Ljava/lang/Throwable;->getOurStackTrace()[Ljava/lang/StackTraceElement;
-Ljava/lang/Throwable;->nativeFillInStackTrace()Ljava/lang/Object;
-Ljava/lang/Throwable;->printStackTrace(Ljava/lang/Throwable$PrintStreamOrWriter;)V
-Ljava/lang/Throwable;->stackTrace:[Ljava/lang/StackTraceElement;
-Ljava/lang/Throwable;->suppressedExceptions:Ljava/util/List;
-Ljava/lang/Void;-><init>()V
-Ljava/net/Authenticator;->theAuthenticator:Ljava/net/Authenticator;
-Ljava/net/DatagramSocket;->getFileDescriptor$()Ljava/io/FileDescriptor;
-Ljava/net/DatagramSocket;->impl:Ljava/net/DatagramSocketImpl;
-Ljava/net/HttpCookie;->assignors:Ljava/util/Map;
-Ljava/net/HttpCookie;->comment:Ljava/lang/String;
-Ljava/net/HttpCookie;->commentURL:Ljava/lang/String;
-Ljava/net/HttpCookie;->domain:Ljava/lang/String;
-Ljava/net/HttpCookie;->header:Ljava/lang/String;
-Ljava/net/HttpCookie;->httpOnly:Z
-Ljava/net/HttpCookie;->maxAge:J
-Ljava/net/HttpCookie;->name:Ljava/lang/String;
-Ljava/net/HttpCookie;->path:Ljava/lang/String;
-Ljava/net/HttpCookie;->portlist:Ljava/lang/String;
-Ljava/net/HttpCookie;->secure:Z
-Ljava/net/HttpCookie;->toDiscard:Z
-Ljava/net/HttpCookie;->tspecials:Ljava/lang/String;
-Ljava/net/HttpCookie;->value:Ljava/lang/String;
-Ljava/net/HttpCookie;->version:I
-Ljava/net/HttpCookie;->whenCreated:J
-Ljava/net/Inet4Address;-><init>()V
-Ljava/net/Inet6Address$Inet6AddressHolder;->ipaddress:[B
-Ljava/net/Inet6Address$Inet6AddressHolder;->scope_id:I
-Ljava/net/Inet6Address$Inet6AddressHolder;->scope_id_set:Z
-Ljava/net/Inet6Address$Inet6AddressHolder;->scope_ifname:Ljava/net/NetworkInterface;
-Ljava/net/Inet6Address;-><init>()V
-Ljava/net/Inet6Address;->holder6:Ljava/net/Inet6Address$Inet6AddressHolder;
-Ljava/net/InetAddress$InetAddressHolder;->address:I
-Ljava/net/InetAddress$InetAddressHolder;->family:I
-Ljava/net/InetAddress$InetAddressHolder;->hostName:Ljava/lang/String;
-Ljava/net/InetAddress$InetAddressHolder;->originalHostName:Ljava/lang/String;
-Ljava/net/InetAddress;->clearDnsCache()V
-Ljava/net/InetAddress;->getAllByNameOnNet(Ljava/lang/String;I)[Ljava/net/InetAddress;
-Ljava/net/InetAddress;->holder()Ljava/net/InetAddress$InetAddressHolder;
-Ljava/net/InetAddress;->holder:Ljava/net/InetAddress$InetAddressHolder;
-Ljava/net/InetAddress;->isNumeric(Ljava/lang/String;)Z
-Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;
-Ljava/net/InetSocketAddress;->holder:Ljava/net/InetSocketAddress$InetSocketAddressHolder;
-Ljava/net/InterfaceAddress;-><init>()V
-Ljava/net/Proxy;-><init>()V
-Ljava/net/ServerSocket;->factory:Ljava/net/SocketImplFactory;
-Ljava/net/Socket;->factory:Ljava/net/SocketImplFactory;
-Ljava/net/Socket;->getFileDescriptor$()Ljava/io/FileDescriptor;
-Ljava/net/Socket;->impl:Ljava/net/SocketImpl;
-Ljava/net/SocketException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
-Ljava/net/SocketImpl;->serverSocket:Ljava/net/ServerSocket;
-Ljava/net/SocketImpl;->socket:Ljava/net/Socket;
-Ljava/net/SocksSocketImpl;-><init>()V
-Ljava/net/URI;->fragment:Ljava/lang/String;
-Ljava/net/URI;->host:Ljava/lang/String;
-Ljava/net/URI;->port:I
-Ljava/net/URI;->query:Ljava/lang/String;
-Ljava/net/URI;->string:Ljava/lang/String;
-Ljava/net/URL;->factory:Ljava/net/URLStreamHandlerFactory;
-Ljava/net/URL;->handler:Ljava/net/URLStreamHandler;
-Ljava/net/URL;->handlers:Ljava/util/Hashtable;
-Ljava/net/URL;->protocol:Ljava/lang/String;
-Ljava/net/URLClassLoader;->acc:Ljava/security/AccessControlContext;
-Ljava/net/URLClassLoader;->ucp:Lsun/misc/URLClassPath;
-Ljava/nio/Buffer;->address:J
-Ljava/nio/Buffer;->capacity:I
-Ljava/nio/Buffer;->limit:I
-Ljava/nio/Buffer;->position:I
-Ljava/nio/Buffer;->_elementSizeShift:I
-Ljava/nio/ByteBuffer;->hb:[B
-Ljava/nio/ByteBuffer;->isReadOnly:Z
-Ljava/nio/ByteBuffer;->offset:I
-Ljava/nio/CharBuffer;->toString(II)Ljava/lang/String;
-Ljava/nio/charset/Charset;->defaultCharset:Ljava/nio/charset/Charset;
-Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
-Ljava/nio/DirectByteBuffer;-><init>(JI)V
-Ljava/security/KeyPairGenerator;->getInstance(Lsun/security/jca/GetInstance$Instance;Ljava/lang/String;)Ljava/security/KeyPairGenerator;
-Ljava/security/KeyStore;->keyStoreSpi:Ljava/security/KeyStoreSpi;
-Ljava/security/Signature;->getInstance(Lsun/security/jca/GetInstance$Instance;Ljava/lang/String;)Ljava/security/Signature;
-Ljava/security/spec/ECParameterSpec;->getCurveName()Ljava/lang/String;
-Ljava/security/spec/ECParameterSpec;->setCurveName(Ljava/lang/String;)V
-Ljava/text/Collator;->icuColl:Landroid/icu/text/Collator;
-Ljava/text/DateFormat;->is24Hour:Ljava/lang/Boolean;
-Ljava/text/DecimalFormatSymbols;->getPercentString()Ljava/lang/String;
-Ljava/text/NumberFormat;->getInstance(Ljava/util/Locale;I)Ljava/text/NumberFormat;
-Ljava/time/Duration;->toSeconds()Ljava/math/BigDecimal;
-Ljava/time/OffsetDateTime;-><init>(Ljava/time/LocalDateTime;Ljava/time/ZoneOffset;)V
-Ljava/time/ZoneId;->of(Ljava/lang/String;Z)Ljava/time/ZoneId;
-Ljava/util/ArrayDeque;->elements:[Ljava/lang/Object;
-Ljava/util/ArrayDeque;->head:I
-Ljava/util/ArrayDeque;->tail:I
-Ljava/util/ArrayList$SubList;->offset:I
-Ljava/util/ArrayList$SubList;->parent:Ljava/util/AbstractList;
-Ljava/util/ArrayList$SubList;->parentOffset:I
-Ljava/util/ArrayList$SubList;->size:I
-Ljava/util/ArrayList;->elementData:[Ljava/lang/Object;
-Ljava/util/ArrayList;->size:I
-Ljava/util/Arrays$ArrayList;->a:[Ljava/lang/Object;
-Ljava/util/Arrays;->deepToString([Ljava/lang/Object;Ljava/lang/StringBuilder;Ljava/util/Set;)V
-Ljava/util/Calendar;->zone:Ljava/util/TimeZone;
-Ljava/util/Collections$EmptyList;-><init>()V
-Ljava/util/Collections$EmptyMap;-><init>()V
-Ljava/util/Collections$SynchronizedCollection;->c:Ljava/util/Collection;
-Ljava/util/Collections$SynchronizedList;->list:Ljava/util/List;
-Ljava/util/Collections$SynchronizedMap;->m:Ljava/util/Map;
-Ljava/util/Collections$UnmodifiableCollection;->c:Ljava/util/Collection;
-Ljava/util/Collections$UnmodifiableMap;->m:Ljava/util/Map;
-Ljava/util/concurrent/atomic/AtomicInteger;->value:I
-Ljava/util/concurrent/ConcurrentHashMap$BaseIterator;->hasMoreElements()Z
-Ljava/util/concurrent/CopyOnWriteArrayList;->elements:[Ljava/lang/Object;
-Ljava/util/concurrent/CopyOnWriteArraySet;->al:Ljava/util/concurrent/CopyOnWriteArrayList;
-Ljava/util/concurrent/Executors$RunnableAdapter;->task:Ljava/lang/Runnable;
-Ljava/util/concurrent/FutureTask;->callable:Ljava/util/concurrent/Callable;
-Ljava/util/concurrent/FutureTask;->EXCEPTIONAL:I
-Ljava/util/concurrent/FutureTask;->outcome:Ljava/lang/Object;
-Ljava/util/concurrent/FutureTask;->state:I
-Ljava/util/concurrent/LinkedBlockingDeque;->first:Ljava/util/concurrent/LinkedBlockingDeque$Node;
-Ljava/util/concurrent/LinkedBlockingDeque;->lock:Ljava/util/concurrent/locks/ReentrantLock;
-Ljava/util/concurrent/LinkedBlockingQueue;->capacity:I
-Ljava/util/concurrent/LinkedBlockingQueue;->head:Ljava/util/concurrent/LinkedBlockingQueue$Node;
-Ljava/util/concurrent/LinkedBlockingQueue;->putLock:Ljava/util/concurrent/locks/ReentrantLock;
-Ljava/util/concurrent/LinkedBlockingQueue;->takeLock:Ljava/util/concurrent/locks/ReentrantLock;
-Ljava/util/concurrent/locks/ReentrantLock;->sync:Ljava/util/concurrent/locks/ReentrantLock$Sync;
-Ljava/util/concurrent/PriorityBlockingQueue;->dequeue()Ljava/lang/Object;
-Ljava/util/concurrent/PriorityBlockingQueue;->lock:Ljava/util/concurrent/locks/ReentrantLock;
-Ljava/util/concurrent/PriorityBlockingQueue;->notEmpty:Ljava/util/concurrent/locks/Condition;
-Ljava/util/concurrent/ThreadPoolExecutor;->allowCoreThreadTimeOut:Z
-Ljava/util/concurrent/ThreadPoolExecutor;->ctl:Ljava/util/concurrent/atomic/AtomicInteger;
-Ljava/util/concurrent/ThreadPoolExecutor;->defaultHandler:Ljava/util/concurrent/RejectedExecutionHandler;
-Ljava/util/concurrent/ThreadPoolExecutor;->mainLock:Ljava/util/concurrent/locks/ReentrantLock;
-Ljava/util/EnumMap;->keyType:Ljava/lang/Class;
-Ljava/util/EnumSet;->elementType:Ljava/lang/Class;
-Ljava/util/HashMap$HashIterator;->hasNext()Z
-Ljava/util/HashMap$HashIterator;->remove()V
-Ljava/util/HashMap$Node;->key:Ljava/lang/Object;
-Ljava/util/HashMap$Node;->next:Ljava/util/HashMap$Node;
-Ljava/util/HashMap$Node;->value:Ljava/lang/Object;
-Ljava/util/HashMap;->modCount:I
-Ljava/util/HashMap;->table:[Ljava/util/HashMap$Node;
-Ljava/util/HashSet;->map:Ljava/util/HashMap;
-Ljava/util/jar/JarFile;->manifest:Ljava/util/jar/Manifest;
-Ljava/util/LinkedHashMap$LinkedHashIterator;->hasNext()Z
-Ljava/util/LinkedHashMap;->accessOrder:Z
-Ljava/util/LinkedHashMap;->eldest()Ljava/util/Map$Entry;
-Ljava/util/LinkedList$Node;->item:Ljava/lang/Object;
-Ljava/util/LinkedList$Node;->next:Ljava/util/LinkedList$Node;
-Ljava/util/LinkedList;->first:Ljava/util/LinkedList$Node;
-Ljava/util/LinkedList;->size:I
-Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
-Ljava/util/Locale;->getInstance(Lsun/util/locale/BaseLocale;Lsun/util/locale/LocaleExtensions;)Ljava/util/Locale;
-Ljava/util/logging/Handler;->sealed:Z
-Ljava/util/logging/Logger;->treeLock:Ljava/lang/Object;
-Ljava/util/logging/LogManager;->getFormatterProperty(Ljava/lang/String;Ljava/util/logging/Formatter;)Ljava/util/logging/Formatter;
-Ljava/util/PriorityQueue;->modCount:I
-Ljava/util/PriorityQueue;->queue:[Ljava/lang/Object;
-Ljava/util/PriorityQueue;->size:I
-Ljava/util/Properties;->saveConvert(Ljava/lang/String;ZZ)Ljava/lang/String;
-Ljava/util/Random;->seedUniquifier()J
-Ljava/util/regex/Matcher;->appendPos:I
-Ljava/util/TimerTask;->period:J
-Ljava/util/UUID;->leastSigBits:J
-Ljava/util/UUID;->mostSigBits:J
-Ljava/util/Vector;->elementData(I)Ljava/lang/Object;
-Ljava/util/zip/Adler32;->update(II)I
-Ljava/util/zip/CRC32;->update(II)I
-Ljava/util/zip/Deflater;->buf:[B
-Ljava/util/zip/Deflater;->finish:Z
-Ljava/util/zip/Deflater;->finished:Z
-Ljava/util/zip/Deflater;->len:I
-Ljava/util/zip/Deflater;->level:I
-Ljava/util/zip/Deflater;->off:I
-Ljava/util/zip/Deflater;->setParams:Z
-Ljava/util/zip/Deflater;->strategy:I
-Ljava/util/zip/Inflater;->buf:[B
-Ljava/util/zip/Inflater;->finished:Z
-Ljava/util/zip/Inflater;->len:I
-Ljava/util/zip/Inflater;->needDict:Z
-Ljava/util/zip/Inflater;->off:I
-Ljava/util/zip/ZipEntry;-><init>(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V
-Ljava/util/zip/ZipEntry;->method:I
-Ljava/util/zip/ZipFile;->close(J)V
-Ljava/util/zip/ZipFile;->getEntry(J[BZ)J
-Ljava/util/zip/ZipFile;->jzfile:J
-Ljava/util/zip/ZipInputStream;->flag:I
-Ljava/util/zip/ZipInputStream;->tmpbuf:[B
-Ljava/util/zip/ZipOutputStream;->method:I
-Ljava/util/zip/ZipOutputStream;->names:Ljava/util/HashSet;
-Ljava/util/zip/ZipOutputStream;->written:J
-Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
-Ljavax/net/ssl/SSLSocketFactory;->createSocket(Ljava/net/Socket;Ljava/io/InputStream;Z)Ljava/net/Socket;
-Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
+Lcom/google/android/mms/util/SqliteWrapper;->query(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
+Lcom/google/android/mms/util/SqliteWrapper;->requery(Landroid/content/Context;Landroid/database/Cursor;)Z
+Lcom/google/android/mms/util/SqliteWrapper;->update(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I
+Lcom/google/android/util/AbstractMessageParser$Token$Type;->ACRONYM:Lcom/google/android/util/AbstractMessageParser$Token$Type;
+Lcom/google/android/util/AbstractMessageParser$Token$Type;->FLICKR:Lcom/google/android/util/AbstractMessageParser$Token$Type;
+Lcom/google/android/util/AbstractMessageParser$Token$Type;->FORMAT:Lcom/google/android/util/AbstractMessageParser$Token$Type;
+Lcom/google/android/util/AbstractMessageParser$Token$Type;->GOOGLE_VIDEO:Lcom/google/android/util/AbstractMessageParser$Token$Type;
+Lcom/google/android/util/AbstractMessageParser$Token$Type;->HTML:Lcom/google/android/util/AbstractMessageParser$Token$Type;
+Lcom/google/android/util/AbstractMessageParser$Token$Type;->LINK:Lcom/google/android/util/AbstractMessageParser$Token$Type;
+Lcom/google/android/util/AbstractMessageParser$Token$Type;->MUSIC:Lcom/google/android/util/AbstractMessageParser$Token$Type;
+Lcom/google/android/util/AbstractMessageParser$Token$Type;->PHOTO:Lcom/google/android/util/AbstractMessageParser$Token$Type;
+Lcom/google/android/util/AbstractMessageParser$Token$Type;->SMILEY:Lcom/google/android/util/AbstractMessageParser$Token$Type;
+Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
+Lcom/google/android/util/AbstractMessageParser$Token$Type;->YOUTUBE_VIDEO:Lcom/google/android/util/AbstractMessageParser$Token$Type;
+Lcom/sun/nio/file/ExtendedWatchEventModifier;->FILE_TREE:Lcom/sun/nio/file/ExtendedWatchEventModifier;
+Lgov/nist/core/Debug;->printStackTrace(Ljava/lang/Exception;)V
+Lgov/nist/core/GenericObject;-><init>()V
+Lgov/nist/core/GenericObject;->dbgPrint()V
+Lgov/nist/core/GenericObject;->debugDump(I)Ljava/lang/String;
+Lgov/nist/core/GenericObject;->encode()Ljava/lang/String;
+Lgov/nist/core/GenericObject;->getMatcher()Lgov/nist/core/Match;
+Lgov/nist/core/GenericObject;->indentation:I
+Lgov/nist/core/GenericObject;->isMySubclass(Ljava/lang/Class;)Z
+Lgov/nist/core/GenericObject;->match(Ljava/lang/Object;)Z
+Lgov/nist/core/GenericObject;->matchExpression:Lgov/nist/core/Match;
+Lgov/nist/core/GenericObject;->merge(Ljava/lang/Object;)V
+Lgov/nist/core/GenericObject;->sprint(Ljava/lang/String;)V
+Lgov/nist/core/GenericObject;->stringRepresentation:Ljava/lang/String;
+Lgov/nist/core/GenericObjectList;-><init>()V
+Lgov/nist/core/GenericObjectList;-><init>(Ljava/lang/String;)V
+Lgov/nist/core/GenericObjectList;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
+Lgov/nist/core/GenericObjectList;-><init>(Ljava/lang/String;Ljava/lang/String;)V
+Lgov/nist/core/GenericObjectList;->concatenate(Lgov/nist/core/GenericObjectList;)V
+Lgov/nist/core/GenericObjectList;->concatenate(Lgov/nist/core/GenericObjectList;Z)V
+Lgov/nist/core/GenericObjectList;->debugDump(I)Ljava/lang/String;
+Lgov/nist/core/GenericObjectList;->first()Lgov/nist/core/GenericObject;
+Lgov/nist/core/GenericObjectList;->getIndentation()Ljava/lang/String;
+Lgov/nist/core/GenericObjectList;->indentation:I
+Lgov/nist/core/GenericObjectList;->isMySubclass(Ljava/lang/Class;)Z
+Lgov/nist/core/GenericObjectList;->match(Ljava/lang/Object;)Z
+Lgov/nist/core/GenericObjectList;->myClass:Ljava/lang/Class;
+Lgov/nist/core/GenericObjectList;->next()Lgov/nist/core/GenericObject;
+Lgov/nist/core/GenericObjectList;->next(Ljava/util/ListIterator;)Lgov/nist/core/GenericObject;
+Lgov/nist/core/GenericObjectList;->setMyClass(Ljava/lang/Class;)V
+Lgov/nist/core/GenericObjectList;->stringRep:Ljava/lang/String;
+Lgov/nist/core/Host;-><init>()V
+Lgov/nist/core/Host;-><init>(Ljava/lang/String;)V
+Lgov/nist/core/Host;->encode()Ljava/lang/String;
+Lgov/nist/core/Host;->getAddress()Ljava/lang/String;
+Lgov/nist/core/Host;->getHostname()Ljava/lang/String;
+Lgov/nist/core/Host;->isIPv6Reference(Ljava/lang/String;)Z
+Lgov/nist/core/Host;->setAddress(Ljava/lang/String;)V
+Lgov/nist/core/Host;->setHostname(Ljava/lang/String;)V
+Lgov/nist/core/HostNameParser;-><init>(Lgov/nist/core/LexerCore;)V
+Lgov/nist/core/HostNameParser;-><init>(Ljava/lang/String;)V
+Lgov/nist/core/HostNameParser;->host()Lgov/nist/core/Host;
+Lgov/nist/core/HostNameParser;->hostPort(Z)Lgov/nist/core/HostPort;
+Lgov/nist/core/HostPort;-><init>()V
+Lgov/nist/core/HostPort;->encode()Ljava/lang/String;
+Lgov/nist/core/HostPort;->encode(Ljava/lang/StringBuffer;)Ljava/lang/StringBuffer;
+Lgov/nist/core/HostPort;->getHost()Lgov/nist/core/Host;
+Lgov/nist/core/HostPort;->getInetAddress()Ljava/net/InetAddress;
+Lgov/nist/core/HostPort;->getPort()I
+Lgov/nist/core/HostPort;->hasPort()Z
+Lgov/nist/core/HostPort;->removePort()V
+Lgov/nist/core/HostPort;->setHost(Lgov/nist/core/Host;)V
+Lgov/nist/core/HostPort;->setPort(I)V
+Lgov/nist/core/InternalErrorHandler;->handleException(Ljava/lang/Exception;)V
+Lgov/nist/core/InternalErrorHandler;->handleException(Ljava/lang/String;)V
+Lgov/nist/core/LexerCore;-><init>(Ljava/lang/String;Ljava/lang/String;)V
+Lgov/nist/core/LexerCore;->byteStringNoSemicolon()Ljava/lang/String;
+Lgov/nist/core/LexerCore;->byteStringNoSlash()Ljava/lang/String;
+Lgov/nist/core/LexerCore;->charAsString(I)Ljava/lang/String;
+Lgov/nist/core/LexerCore;->comment()Ljava/lang/String;
+Lgov/nist/core/LexerCore;->createParseException()Ljava/text/ParseException;
+Lgov/nist/core/LexerCore;->currentLexer:Ljava/util/Hashtable;
+Lgov/nist/core/LexerCore;->getBuffer()Ljava/lang/String;
+Lgov/nist/core/LexerCore;->getNextId()Ljava/lang/String;
+Lgov/nist/core/LexerCore;->getNextToken()Lgov/nist/core/Token;
+Lgov/nist/core/LexerCore;->getPtr()I
+Lgov/nist/core/LexerCore;->getRest()Ljava/lang/String;
+Lgov/nist/core/LexerCore;->getString(C)Ljava/lang/String;
+Lgov/nist/core/LexerCore;->isTokenChar(C)Z
+Lgov/nist/core/LexerCore;->lexerTables:Ljava/util/Hashtable;
+Lgov/nist/core/LexerCore;->markInputPosition()I
+Lgov/nist/core/LexerCore;->match(I)Lgov/nist/core/Token;
+Lgov/nist/core/LexerCore;->number()Ljava/lang/String;
+Lgov/nist/core/LexerCore;->peekNextToken()Lgov/nist/core/Token;
+Lgov/nist/core/LexerCore;->peekNextToken(I)[Lgov/nist/core/Token;
+Lgov/nist/core/LexerCore;->quotedString()Ljava/lang/String;
+Lgov/nist/core/LexerCore;->rewindInputPosition(I)V
+Lgov/nist/core/LexerCore;->selectLexer(Ljava/lang/String;)V
+Lgov/nist/core/LexerCore;->SPorHT()V
+Lgov/nist/core/LexerCore;->startsId()Z
+Lgov/nist/core/LexerCore;->ttoken()Ljava/lang/String;
+Lgov/nist/core/LexerCore;->ttokenSafe()Ljava/lang/String;
+Lgov/nist/core/Match;->match(Ljava/lang/String;)Z
+Lgov/nist/core/NameValue;-><init>()V
+Lgov/nist/core/NameValue;-><init>(Ljava/lang/String;Ljava/lang/Object;)V
+Lgov/nist/core/NameValue;-><init>(Ljava/lang/String;Ljava/lang/Object;Z)V
+Lgov/nist/core/NameValue;->encode()Ljava/lang/String;
+Lgov/nist/core/NameValue;->encode(Ljava/lang/StringBuffer;)Ljava/lang/StringBuffer;
+Lgov/nist/core/NameValue;->getName()Ljava/lang/String;
+Lgov/nist/core/NameValue;->getValueAsObject()Ljava/lang/Object;
+Lgov/nist/core/NameValue;->setName(Ljava/lang/String;)V
+Lgov/nist/core/NameValue;->setQuotedValue()V
+Lgov/nist/core/NameValue;->setSeparator(Ljava/lang/String;)V
+Lgov/nist/core/NameValue;->setValueAsObject(Ljava/lang/Object;)V
+Lgov/nist/core/NameValueList;-><init>()V
+Lgov/nist/core/NameValueList;-><init>(Z)V
+Lgov/nist/core/NameValueList;->delete(Ljava/lang/String;)Z
+Lgov/nist/core/NameValueList;->encode()Ljava/lang/String;
+Lgov/nist/core/NameValueList;->encode(Ljava/lang/StringBuffer;)Ljava/lang/StringBuffer;
+Lgov/nist/core/NameValueList;->getNames()Ljava/util/Iterator;
+Lgov/nist/core/NameValueList;->getNameValue(Ljava/lang/String;)Lgov/nist/core/NameValue;
+Lgov/nist/core/NameValueList;->getParameter(Ljava/lang/String;)Ljava/lang/String;
+Lgov/nist/core/NameValueList;->getValue(Ljava/lang/String;)Ljava/lang/Object;
+Lgov/nist/core/NameValueList;->hasNameValue(Ljava/lang/String;)Z
+Lgov/nist/core/NameValueList;->iterator()Ljava/util/Iterator;
+Lgov/nist/core/NameValueList;->set(Lgov/nist/core/NameValue;)V
+Lgov/nist/core/NameValueList;->set(Ljava/lang/String;Ljava/lang/Object;)V
+Lgov/nist/core/NameValueList;->setSeparator(Ljava/lang/String;)V
+Lgov/nist/core/net/DefaultNetworkLayer;->SINGLETON:Lgov/nist/core/net/DefaultNetworkLayer;
+Lgov/nist/core/net/NetworkLayer;->createDatagramSocket()Ljava/net/DatagramSocket;
+Lgov/nist/core/net/NetworkLayer;->createDatagramSocket(ILjava/net/InetAddress;)Ljava/net/DatagramSocket;
+Lgov/nist/core/net/NetworkLayer;->createServerSocket(IILjava/net/InetAddress;)Ljava/net/ServerSocket;
+Lgov/nist/core/net/NetworkLayer;->createSocket(Ljava/net/InetAddress;I)Ljava/net/Socket;
+Lgov/nist/core/net/NetworkLayer;->createSSLServerSocket(IILjava/net/InetAddress;)Ljavax/net/ssl/SSLServerSocket;
+Lgov/nist/core/net/NetworkLayer;->createSSLSocket(Ljava/net/InetAddress;I)Ljavax/net/ssl/SSLSocket;
+Lgov/nist/core/ParserCore;-><init>()V
+Lgov/nist/core/ParserCore;->lexer:Lgov/nist/core/LexerCore;
+Lgov/nist/core/StringTokenizer;->ptr:I
+Lgov/nist/core/ThreadAuditor$ThreadHandle;->getPingIntervalInMillisecs()J
+Lgov/nist/core/ThreadAuditor$ThreadHandle;->ping()V
+Lgov/nist/core/ThreadAuditor;-><init>()V
+Lgov/nist/core/ThreadAuditor;->addCurrentThread()Lgov/nist/core/ThreadAuditor$ThreadHandle;
+Lgov/nist/core/ThreadAuditor;->getPingIntervalInMillisecs()J
+Lgov/nist/core/ThreadAuditor;->isEnabled()Z
+Lgov/nist/core/ThreadAuditor;->setPingIntervalInMillisecs(J)V
+Lgov/nist/core/Token;-><init>()V
+Lgov/nist/core/Token;->getTokenType()I
+Lgov/nist/core/Token;->getTokenValue()Ljava/lang/String;
+Lgov/nist/javax/sip/address/GenericURI;-><init>()V
+Lgov/nist/javax/sip/address/GenericURI;->encode()Ljava/lang/String;
+Lgov/nist/javax/sip/address/GenericURI;->getScheme()Ljava/lang/String;
+Lgov/nist/javax/sip/address/SipUri;->getHost()Ljava/lang/String;
+Lgov/nist/javax/sip/address/SipUri;->getParameter(Ljava/lang/String;)Ljava/lang/String;
+Lgov/nist/javax/sip/address/SipUri;->getPort()I
+Lgov/nist/javax/sip/address/SipUri;->getUser()Ljava/lang/String;
+Lgov/nist/javax/sip/address/SipUri;->removeParameter(Ljava/lang/String;)V
+Lgov/nist/javax/sip/address/SipUri;->setParameter(Ljava/lang/String;Ljava/lang/String;)V
+Lgov/nist/javax/sip/address/SipUri;->setUserParam(Ljava/lang/String;)V
+Lgov/nist/javax/sip/parser/URLParser;-><init>(Ljava/lang/String;)V
+Lgov/nist/javax/sip/parser/URLParser;->sipURL(Z)Lgov/nist/javax/sip/address/SipUri;
+Ljava/lang/DexCache;->dexFile:J
+Ljava/lang/invoke/SerializedLambda;-><init>(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Ljava/lang/invoke/SerializedLambda;->getCapturedArg(I)Ljava/lang/Object;
+Ljava/lang/invoke/SerializedLambda;->getCapturedArgCount()I
+Ljava/lang/invoke/SerializedLambda;->getCapturingClass()Ljava/lang/String;
+Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceClass()Ljava/lang/String;
+Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceMethodName()Ljava/lang/String;
+Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceMethodSignature()Ljava/lang/String;
+Ljava/lang/invoke/SerializedLambda;->getImplClass()Ljava/lang/String;
+Ljava/lang/invoke/SerializedLambda;->getImplMethodKind()I
+Ljava/lang/invoke/SerializedLambda;->getImplMethodName()Ljava/lang/String;
+Ljava/lang/invoke/SerializedLambda;->getImplMethodSignature()Ljava/lang/String;
+Ljava/lang/invoke/SerializedLambda;->getInstantiatedMethodType()Ljava/lang/String;
+Ljava/lang/UNIXProcess;->pid:I
+Ljava/net/AddressCache$AddressCacheEntry;-><init>(Ljava/lang/Object;)V
+Ljava/net/AddressCache$AddressCacheEntry;->expiryNanos:J
+Ljava/net/AddressCache$AddressCacheEntry;->value:Ljava/lang/Object;
+Ljava/net/AddressCache$AddressCacheKey;->mHostname:Ljava/lang/String;
+Ljava/net/AddressCache;->cache:Llibcore/util/BasicLruCache;
+Ljava/net/Inet6AddressImpl;->addressCache:Ljava/net/AddressCache;
+Ljava/net/PlainSocketImpl;-><init>()V
+Ljava/nio/DirectByteBuffer;->cleaner()Lsun/misc/Cleaner;
+Ljava/nio/file/FileTreeWalker;->followLinks:Z
+Ljava/nio/file/FileTreeWalker;->linkOptions:[Ljava/nio/file/LinkOption;
+Ljava/nio/file/FileTreeWalker;->maxDepth:I
+Ljava/util/zip/ZipFile$ZipEntryIterator;->nextElement()Ljava/util/zip/ZipEntry;
+Ljunit/framework/TestCase;->fName:Ljava/lang/String;
+Ljunit/framework/TestSuite;->isPublicTestMethod(Ljava/lang/reflect/Method;)Z
+Ljunit/framework/TestSuite;->isTestMethod(Ljava/lang/reflect/Method;)Z
+Llibcore/icu/DateIntervalFormat;->formatDateRange(JJILjava/lang/String;)Ljava/lang/String;
+Llibcore/icu/ICU;->CACHED_PATTERNS:Llibcore/util/BasicLruCache;
+Llibcore/icu/ICU;->getBestDateTimePattern(Ljava/lang/String;Ljava/util/Locale;)Ljava/lang/String;
+Llibcore/icu/ICU;->getBestDateTimePatternNative(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+Llibcore/icu/ICU;->getDateFormatOrder(Ljava/lang/String;)[C
+Llibcore/icu/LocaleData;->firstDayOfWeek:Ljava/lang/Integer;
+Llibcore/icu/LocaleData;->get(Ljava/util/Locale;)Llibcore/icu/LocaleData;
+Llibcore/icu/LocaleData;->longStandAloneWeekdayNames:[Ljava/lang/String;
+Llibcore/icu/LocaleData;->mapInvalidAndNullLocales(Ljava/util/Locale;)Ljava/util/Locale;
+Llibcore/icu/LocaleData;->minimalDaysInFirstWeek:Ljava/lang/Integer;
+Llibcore/icu/LocaleData;->shortMonthNames:[Ljava/lang/String;
+Llibcore/icu/LocaleData;->shortStandAloneMonthNames:[Ljava/lang/String;
+Llibcore/icu/LocaleData;->shortStandAloneWeekdayNames:[Ljava/lang/String;
+Llibcore/icu/LocaleData;->timeFormat_Hm:Ljava/lang/String;
+Llibcore/icu/LocaleData;->timeFormat_hm:Ljava/lang/String;
+Llibcore/icu/LocaleData;->today:Ljava/lang/String;
+Llibcore/icu/LocaleData;->tomorrow:Ljava/lang/String;
+Llibcore/icu/LocaleData;->zeroDigit:C
+Llibcore/icu/TimeZoneNames;->forLocale(Ljava/util/Locale;)[Ljava/lang/String;
+Llibcore/io/AsynchronousCloseMonitor;->signalBlockedThreads(Ljava/io/FileDescriptor;)V
+Llibcore/io/BlockGuardOs;-><init>(Llibcore/io/Os;)V
+Llibcore/io/BlockGuardOs;->chmod(Ljava/lang/String;I)V
+Llibcore/io/BlockGuardOs;->chown(Ljava/lang/String;II)V
+Llibcore/io/BlockGuardOs;->close(Ljava/io/FileDescriptor;)V
+Llibcore/io/BlockGuardOs;->fchmod(Ljava/io/FileDescriptor;I)V
+Llibcore/io/BlockGuardOs;->fchown(Ljava/io/FileDescriptor;II)V
+Llibcore/io/BlockGuardOs;->fdatasync(Ljava/io/FileDescriptor;)V
+Llibcore/io/BlockGuardOs;->fstat(Ljava/io/FileDescriptor;)Landroid/system/StructStat;
+Llibcore/io/BlockGuardOs;->fstatvfs(Ljava/io/FileDescriptor;)Landroid/system/StructStatVfs;
+Llibcore/io/BlockGuardOs;->lchown(Ljava/lang/String;II)V
+Llibcore/io/BlockGuardOs;->link(Ljava/lang/String;Ljava/lang/String;)V
+Llibcore/io/BlockGuardOs;->lseek(Ljava/io/FileDescriptor;JI)J
+Llibcore/io/BlockGuardOs;->lstat(Ljava/lang/String;)Landroid/system/StructStat;
+Llibcore/io/BlockGuardOs;->mkdir(Ljava/lang/String;I)V
+Llibcore/io/BlockGuardOs;->mkfifo(Ljava/lang/String;I)V
+Llibcore/io/BlockGuardOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor;
+Llibcore/io/BlockGuardOs;->posix_fallocate(Ljava/io/FileDescriptor;JJ)V
+Llibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;J)I
+Llibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;[BIIJ)I
+Llibcore/io/BlockGuardOs;->pwrite(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;J)I
+Llibcore/io/BlockGuardOs;->pwrite(Ljava/io/FileDescriptor;[BIIJ)I
+Llibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I
+Llibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;[BII)I
+Llibcore/io/BlockGuardOs;->readlink(Ljava/lang/String;)Ljava/lang/String;
+Llibcore/io/BlockGuardOs;->readv(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I
+Llibcore/io/BlockGuardOs;->realpath(Ljava/lang/String;)Ljava/lang/String;
+Llibcore/io/BlockGuardOs;->remove(Ljava/lang/String;)V
+Llibcore/io/BlockGuardOs;->rename(Ljava/lang/String;Ljava/lang/String;)V
+Llibcore/io/BlockGuardOs;->stat(Ljava/lang/String;)Landroid/system/StructStat;
+Llibcore/io/BlockGuardOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs;
+Llibcore/io/BlockGuardOs;->symlink(Ljava/lang/String;Ljava/lang/String;)V
+Llibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I
+Llibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;[BII)I
+Llibcore/io/BlockGuardOs;->writev(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I
+Llibcore/io/BufferIterator;->readByte()B
+Llibcore/io/BufferIterator;->readByteArray([BII)V
+Llibcore/io/BufferIterator;->readInt()I
+Llibcore/io/BufferIterator;->readIntArray([III)V
+Llibcore/io/BufferIterator;->seek(I)V
+Llibcore/io/BufferIterator;->skip(I)V
+Llibcore/io/DropBox;->addText(Ljava/lang/String;Ljava/lang/String;)V
+Llibcore/io/ForwardingOs;-><init>(Llibcore/io/Os;)V
+Llibcore/io/ForwardingOs;->access(Ljava/lang/String;I)Z
+Llibcore/io/ForwardingOs;->chmod(Ljava/lang/String;I)V
+Llibcore/io/ForwardingOs;->chown(Ljava/lang/String;II)V
+Llibcore/io/ForwardingOs;->getenv(Ljava/lang/String;)Ljava/lang/String;
+Llibcore/io/ForwardingOs;->lchown(Ljava/lang/String;II)V
+Llibcore/io/ForwardingOs;->link(Ljava/lang/String;Ljava/lang/String;)V
+Llibcore/io/ForwardingOs;->lstat(Ljava/lang/String;)Landroid/system/StructStat;
+Llibcore/io/ForwardingOs;->mkdir(Ljava/lang/String;I)V
+Llibcore/io/ForwardingOs;->mkfifo(Ljava/lang/String;I)V
+Llibcore/io/ForwardingOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor;
+Llibcore/io/ForwardingOs;->os:Llibcore/io/Os;
+Llibcore/io/ForwardingOs;->readlink(Ljava/lang/String;)Ljava/lang/String;
+Llibcore/io/ForwardingOs;->remove(Ljava/lang/String;)V
+Llibcore/io/ForwardingOs;->removexattr(Ljava/lang/String;Ljava/lang/String;)V
+Llibcore/io/ForwardingOs;->rename(Ljava/lang/String;Ljava/lang/String;)V
+Llibcore/io/ForwardingOs;->setenv(Ljava/lang/String;Ljava/lang/String;Z)V
+Llibcore/io/ForwardingOs;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
+Llibcore/io/ForwardingOs;->setxattr(Ljava/lang/String;Ljava/lang/String;[BI)V
+Llibcore/io/ForwardingOs;->stat(Ljava/lang/String;)Landroid/system/StructStat;
+Llibcore/io/ForwardingOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs;
+Llibcore/io/ForwardingOs;->symlink(Ljava/lang/String;Ljava/lang/String;)V
+Llibcore/io/ForwardingOs;->sysconf(I)J
+Llibcore/io/ForwardingOs;->unlink(Ljava/lang/String;)V
+Llibcore/io/IoBridge;->isConnected(Ljava/io/FileDescriptor;Ljava/net/InetAddress;III)Z
+Llibcore/io/IoUtils;->closeQuietly(Ljava/io/FileDescriptor;)V
+Llibcore/io/IoUtils;->closeQuietly(Ljava/lang/AutoCloseable;)V
+Llibcore/io/IoUtils;->closeQuietly(Ljava/net/Socket;)V
+Llibcore/io/IoUtils;->readFileAsByteArray(Ljava/lang/String;)[B
+Llibcore/io/IoUtils;->readFileAsString(Ljava/lang/String;)Ljava/lang/String;
+Llibcore/io/IoUtils;->setBlocking(Ljava/io/FileDescriptor;Z)V
+Llibcore/io/MemoryMappedFile;->bigEndianIterator()Llibcore/io/BufferIterator;
+Llibcore/io/MemoryMappedFile;->mmapRO(Ljava/lang/String;)Llibcore/io/MemoryMappedFile;
+Llibcore/io/Os;->chmod(Ljava/lang/String;I)V
+Llibcore/io/Os;->close(Ljava/io/FileDescriptor;)V
+Llibcore/io/Os;->connect(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V
+Llibcore/io/Os;->gai_strerror(I)Ljava/lang/String;
+Llibcore/io/Os;->remove(Ljava/lang/String;)V
+Llibcore/io/Os;->setenv(Ljava/lang/String;Ljava/lang/String;Z)V
+Llibcore/io/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
+Llibcore/io/Os;->stat(Ljava/lang/String;)Landroid/system/StructStat;
+Llibcore/io/Os;->strerror(I)Ljava/lang/String;
+Llibcore/io/Os;->sysconf(I)J
+Llibcore/io/Streams;->readAsciiLine(Ljava/io/InputStream;)Ljava/lang/String;
+Llibcore/io/Streams;->readFully(Ljava/io/InputStream;)[B
+Llibcore/io/Streams;->readFully(Ljava/io/InputStream;[B)V
+Llibcore/io/Streams;->readSingleByte(Ljava/io/InputStream;)I
+Llibcore/io/Streams;->skipAll(Ljava/io/InputStream;)V
+Llibcore/io/Streams;->writeSingleByte(Ljava/io/OutputStream;I)V
+Llibcore/net/event/NetworkEventDispatcher;->addListener(Llibcore/net/event/NetworkEventListener;)V
+Llibcore/net/event/NetworkEventDispatcher;->getInstance()Llibcore/net/event/NetworkEventDispatcher;
+Llibcore/net/event/NetworkEventListener;-><init>()V
+Llibcore/net/http/HttpDate;->format(Ljava/util/Date;)Ljava/lang/String;
+Llibcore/net/http/HttpDate;->parse(Ljava/lang/String;)Ljava/util/Date;
+Llibcore/net/MimeUtils;->guessExtensionFromMimeType(Ljava/lang/String;)Ljava/lang/String;
+Llibcore/net/MimeUtils;->guessMimeTypeFromExtension(Ljava/lang/String;)Ljava/lang/String;
+Llibcore/net/NetworkSecurityPolicy;->isCleartextTrafficPermitted()Z
+Llibcore/util/BasicLruCache;-><init>(I)V
+Llibcore/util/BasicLruCache;->evictAll()V
+Llibcore/util/BasicLruCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
+Llibcore/util/BasicLruCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+Llibcore/util/EmptyArray;->BYTE:[B
+Llibcore/util/EmptyArray;->INT:[I
+Llibcore/util/EmptyArray;->OBJECT:[Ljava/lang/Object;
+Llibcore/util/ZoneInfoDB$TzData;-><init>()V
+Lorg/apache/harmony/dalvik/ddmc/Chunk;-><init>(ILjava/nio/ByteBuffer;)V
+Lorg/apache/harmony/dalvik/ddmc/ChunkHandler;->CHUNK_ORDER:Ljava/nio/ByteOrder;
+Lorg/apache/harmony/dalvik/ddmc/DdmServer;->broadcast(I)V
+Lorg/apache/harmony/dalvik/ddmc/DdmServer;->sendChunk(Lorg/apache/harmony/dalvik/ddmc/Chunk;)V
+Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;->getThreadStats()[B
+Lorg/apache/harmony/xml/dom/ElementImpl;->localName:Ljava/lang/String;
+Lorg/apache/harmony/xml/ExpatAttributes;-><init>()V
+Lorg/apache/harmony/xml/ExpatParser$EntityParser;->depth:I
+Lorg/apache/harmony/xml/ExpatParser;-><init>(Ljava/lang/String;Lorg/apache/harmony/xml/ExpatReader;ZLjava/lang/String;Ljava/lang/String;)V
+Lorg/apache/harmony/xml/ExpatParser;->append([BII)V
+Lorg/apache/harmony/xml/ExpatParser;->append([CII)V
+Lorg/apache/harmony/xml/ExpatParser;->attributes:Lorg/apache/harmony/xml/ExpatAttributes;
+Lorg/apache/harmony/xml/ExpatParser;->cloneAttributes()Lorg/xml/sax/Attributes;
+Lorg/apache/harmony/xml/ExpatParser;->finish()V
+Lorg/apache/harmony/xml/ExpatParser;->xmlReader:Lorg/apache/harmony/xml/ExpatReader;
+Lorg/apache/harmony/xml/ExpatReader;-><init>()V
+Lorg/apache/harmony/xml/ExpatReader;->contentHandler:Lorg/xml/sax/ContentHandler;
+Lorg/apache/xalan/extensions/ExpressionContext;->getContextNode()Lorg/w3c/dom/Node;
+Lorg/apache/xalan/extensions/ExpressionContext;->getErrorListener()Ljavax/xml/transform/ErrorListener;
+Lorg/apache/xalan/extensions/ExpressionContext;->getVariableOrParam(Lorg/apache/xml/utils/QName;)Lorg/apache/xpath/objects/XObject;
+Lorg/apache/xalan/extensions/ExpressionContext;->getXPathContext()Lorg/apache/xpath/XPathContext;
+Lorg/apache/xalan/extensions/ExtensionHandler;-><init>(Ljava/lang/String;Ljava/lang/String;)V
+Lorg/apache/xalan/extensions/ExtensionHandler;->callFunction(Ljava/lang/String;Ljava/util/Vector;Ljava/lang/Object;Lorg/apache/xalan/extensions/ExpressionContext;)Ljava/lang/Object;
+Lorg/apache/xalan/extensions/ExtensionHandler;->getClassForName(Ljava/lang/String;)Ljava/lang/Class;
+Lorg/apache/xalan/extensions/ObjectFactory$ConfigurationError;-><init>(Ljava/lang/String;Ljava/lang/Exception;)V
+Lorg/apache/xalan/extensions/ObjectFactory;->findClassLoader()Ljava/lang/ClassLoader;
+Lorg/apache/xalan/extensions/ObjectFactory;->findProviderClass(Ljava/lang/String;Ljava/lang/ClassLoader;Z)Ljava/lang/Class;
+Lorg/apache/xalan/processor/TransformerFactoryImpl;-><init>()V
+Lorg/apache/xalan/res/XSLMessages;->createMessage(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
+Lorg/apache/xalan/res/XSLTErrorResources;-><init>()V
+Lorg/apache/xalan/serialize/SerializerUtils;->outputResultTreeFragment(Lorg/apache/xml/serializer/SerializationHandler;Lorg/apache/xpath/objects/XObject;Lorg/apache/xpath/XPathContext;)V
+Lorg/apache/xalan/templates/AVT;->evaluate(Lorg/apache/xpath/XPathContext;ILorg/apache/xml/utils/PrefixResolver;)Ljava/lang/String;
+Lorg/apache/xalan/templates/ElemElement;->execute(Lorg/apache/xalan/transformer/TransformerImpl;)V
+Lorg/apache/xalan/templates/ElemExsltFunction;->execute(Lorg/apache/xalan/transformer/TransformerImpl;[Lorg/apache/xpath/objects/XObject;)V
+Lorg/apache/xalan/templates/ElemExtensionCall;->getAttribute(Ljava/lang/String;Lorg/w3c/dom/Node;Lorg/apache/xalan/transformer/TransformerImpl;)Ljava/lang/String;
+Lorg/apache/xalan/templates/ElemLiteralResult;->getLiteralResultAttribute(Ljava/lang/String;)Lorg/apache/xalan/templates/AVT;
+Lorg/apache/xalan/templates/ElemTemplate;->getMatch()Lorg/apache/xpath/XPath;
+Lorg/apache/xalan/templates/ElemTemplate;->getName()Lorg/apache/xml/utils/QName;
+Lorg/apache/xalan/templates/ElemTemplateElement;->getFirstChildElem()Lorg/apache/xalan/templates/ElemTemplateElement;
+Lorg/apache/xalan/templates/ElemTemplateElement;->getNextSiblingElem()Lorg/apache/xalan/templates/ElemTemplateElement;
+Lorg/apache/xalan/templates/ElemTemplateElement;->getParentElem()Lorg/apache/xalan/templates/ElemTemplateElement;
+Lorg/apache/xalan/templates/ElemTemplateElement;->getStylesheetRoot()Lorg/apache/xalan/templates/StylesheetRoot;
+Lorg/apache/xalan/templates/ElemTemplateElement;->getXSLToken()I
+Lorg/apache/xalan/templates/ElemTextLiteral;->getChars()[C
+Lorg/apache/xalan/templates/KeyDeclaration;->getName()Lorg/apache/xml/utils/QName;
+Lorg/apache/xalan/templates/KeyDeclaration;->getUse()Lorg/apache/xpath/XPath;
+Lorg/apache/xalan/templates/StylesheetRoot;->getDefaultRootRule()Lorg/apache/xalan/templates/ElemTemplate;
+Lorg/apache/xalan/templates/StylesheetRoot;->getDefaultRule()Lorg/apache/xalan/templates/ElemTemplate;
+Lorg/apache/xalan/templates/StylesheetRoot;->getDefaultTextRule()Lorg/apache/xalan/templates/ElemTemplate;
+Lorg/apache/xalan/templates/StylesheetRoot;->getTemplateComposed(Lorg/apache/xml/utils/QName;)Lorg/apache/xalan/templates/ElemTemplate;
+Lorg/apache/xalan/transformer/ClonerToResultTree;->cloneToResultTree(IILorg/apache/xml/dtm/DTM;Lorg/apache/xml/serializer/SerializationHandler;Z)V
+Lorg/apache/xalan/transformer/DecimalToRoman;-><init>(JLjava/lang/String;JLjava/lang/String;)V
+Lorg/apache/xalan/transformer/DecimalToRoman;->m_postLetter:Ljava/lang/String;
+Lorg/apache/xalan/transformer/DecimalToRoman;->m_postValue:J
+Lorg/apache/xalan/transformer/DecimalToRoman;->m_preLetter:Ljava/lang/String;
+Lorg/apache/xalan/transformer/DecimalToRoman;->m_preValue:J
+Lorg/apache/xalan/transformer/MsgMgr;->error(Ljavax/xml/transform/SourceLocator;Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;Ljava/lang/String;)V
+Lorg/apache/xalan/transformer/TransformerImpl;->createSerializationHandler(Ljavax/xml/transform/Result;Lorg/apache/xalan/templates/OutputProperties;)Lorg/apache/xml/serializer/SerializationHandler;
+Lorg/apache/xalan/transformer/TransformerImpl;->executeChildTemplates(Lorg/apache/xalan/templates/ElemTemplateElement;Lorg/w3c/dom/Node;Lorg/apache/xml/utils/QName;Lorg/xml/sax/ContentHandler;)V
+Lorg/apache/xalan/transformer/TransformerImpl;->executeChildTemplates(Lorg/apache/xalan/templates/ElemTemplateElement;Z)V
+Lorg/apache/xalan/transformer/TransformerImpl;->getCountersTable()Lorg/apache/xalan/transformer/CountersTable;
+Lorg/apache/xalan/transformer/TransformerImpl;->getCurrentTemplateElements()Lorg/apache/xml/utils/ObjectStack;
+Lorg/apache/xalan/transformer/TransformerImpl;->getCurrentTemplateElementsCount()I
+Lorg/apache/xalan/transformer/TransformerImpl;->getMatchedNode()I
+Lorg/apache/xalan/transformer/TransformerImpl;->getMatchedTemplate()Lorg/apache/xalan/templates/ElemTemplate;
+Lorg/apache/xalan/transformer/TransformerImpl;->getMode()Lorg/apache/xml/utils/QName;
+Lorg/apache/xalan/transformer/TransformerImpl;->getMsgMgr()Lorg/apache/xalan/transformer/MsgMgr;
+Lorg/apache/xalan/transformer/TransformerImpl;->getOutputFormat()Lorg/apache/xalan/templates/OutputProperties;
+Lorg/apache/xalan/transformer/TransformerImpl;->getResultTreeHandler()Lorg/apache/xml/serializer/SerializationHandler;
+Lorg/apache/xalan/transformer/TransformerImpl;->getSerializationHandler()Lorg/apache/xml/serializer/SerializationHandler;
+Lorg/apache/xalan/transformer/TransformerImpl;->getXPathContext()Lorg/apache/xpath/XPathContext;
+Lorg/apache/xalan/transformer/TransformerImpl;->m_attrSetStack:Ljava/util/Stack;
+Lorg/apache/xalan/transformer/TransformerImpl;->m_currentMatchedNodes:Lorg/apache/xml/utils/NodeVector;
+Lorg/apache/xalan/transformer/TransformerImpl;->m_currentMatchTemplates:Ljava/util/Stack;
+Lorg/apache/xalan/transformer/TransformerImpl;->m_currentTemplateElements:Lorg/apache/xml/utils/ObjectStack;
+Lorg/apache/xalan/transformer/TransformerImpl;->m_currentTemplateRuleIsNull:Lorg/apache/xml/utils/BoolStack;
+Lorg/apache/xalan/transformer/TransformerImpl;->m_inputContentHandler:Lorg/xml/sax/ContentHandler;
+Lorg/apache/xalan/transformer/TransformerImpl;->m_outputTarget:Ljavax/xml/transform/Result;
+Lorg/apache/xalan/transformer/TransformerImpl;->m_stringWriterObjectPool:Lorg/apache/xml/utils/ObjectPool;
+Lorg/apache/xalan/transformer/TransformerImpl;->m_urlOfSource:Ljava/lang/String;
+Lorg/apache/xalan/transformer/TransformerImpl;->m_xcontext:Lorg/apache/xpath/XPathContext;
+Lorg/apache/xalan/transformer/TransformerImpl;->popCurrentFuncResult()Ljava/lang/Object;
+Lorg/apache/xalan/transformer/TransformerImpl;->pushCurrentFuncResult(Ljava/lang/Object;)V
+Lorg/apache/xalan/transformer/TransformerImpl;->pushElemTemplateElement(Lorg/apache/xalan/templates/ElemTemplateElement;)V
+Lorg/apache/xalan/Version;->getVersion()Ljava/lang/String;
+Lorg/apache/xalan/xslt/EnvironmentCheck;-><init>()V
+Lorg/apache/xalan/xslt/EnvironmentCheck;->appendEnvironmentReport(Lorg/w3c/dom/Node;Lorg/w3c/dom/Document;Ljava/util/Hashtable;)V
+Lorg/apache/xalan/xslt/EnvironmentCheck;->getEnvironmentHash()Ljava/util/Hashtable;
+Lorg/apache/xalan/xslt/ObjectFactory;->findClassLoader()Ljava/lang/ClassLoader;
+Lorg/apache/xalan/xslt/ObjectFactory;->newInstance(Ljava/lang/String;Ljava/lang/ClassLoader;Z)Ljava/lang/Object;
+Lorg/apache/xml/dtm/Axis;->getNames(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/Axis;->isReverse(I)Z
+Lorg/apache/xml/dtm/DTM;->getDocument()I
+Lorg/apache/xml/dtm/DTM;->getDocumentRoot(I)I
+Lorg/apache/xml/dtm/DTM;->getFirstChild(I)I
+Lorg/apache/xml/dtm/DTM;->getNextSibling(I)I
+Lorg/apache/xml/dtm/DTM;->getNode(I)Lorg/w3c/dom/Node;
+Lorg/apache/xml/dtm/DTM;->getNodeName(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/DTM;->getNodeType(I)S
+Lorg/apache/xml/dtm/DTM;->getParent(I)I
+Lorg/apache/xml/dtm/DTM;->getSourceLocatorFor(I)Ljavax/xml/transform/SourceLocator;
+Lorg/apache/xml/dtm/DTM;->getStringValue(I)Lorg/apache/xml/utils/XMLString;
+Lorg/apache/xml/dtm/DTM;->migrateTo(Lorg/apache/xml/dtm/DTMManager;)V
+Lorg/apache/xml/dtm/DTMAxisIterator;->cloneIterator()Lorg/apache/xml/dtm/DTMAxisIterator;
+Lorg/apache/xml/dtm/DTMAxisIterator;->getLast()I
+Lorg/apache/xml/dtm/DTMAxisIterator;->getNodeByPosition(I)I
+Lorg/apache/xml/dtm/DTMAxisIterator;->getPosition()I
+Lorg/apache/xml/dtm/DTMAxisIterator;->gotoMark()V
+Lorg/apache/xml/dtm/DTMAxisIterator;->isReverse()Z
+Lorg/apache/xml/dtm/DTMAxisIterator;->next()I
+Lorg/apache/xml/dtm/DTMAxisIterator;->reset()Lorg/apache/xml/dtm/DTMAxisIterator;
+Lorg/apache/xml/dtm/DTMAxisIterator;->setMark()V
+Lorg/apache/xml/dtm/DTMAxisIterator;->setRestartable(Z)V
+Lorg/apache/xml/dtm/DTMAxisIterator;->setStartNode(I)Lorg/apache/xml/dtm/DTMAxisIterator;
+Lorg/apache/xml/dtm/DTMException;-><init>(Ljava/lang/String;)V
+Lorg/apache/xml/dtm/DTMFilter;->acceptNode(II)S
+Lorg/apache/xml/dtm/DTMIterator;->cloneWithReset()Lorg/apache/xml/dtm/DTMIterator;
+Lorg/apache/xml/dtm/DTMIterator;->getCurrentPos()I
+Lorg/apache/xml/dtm/DTMIterator;->getDTM(I)Lorg/apache/xml/dtm/DTM;
+Lorg/apache/xml/dtm/DTMIterator;->nextNode()I
+Lorg/apache/xml/dtm/DTMIterator;->runTo(I)V
+Lorg/apache/xml/dtm/DTMIterator;->setCurrentPos(I)V
+Lorg/apache/xml/dtm/DTMIterator;->setRoot(ILjava/lang/Object;)V
+Lorg/apache/xml/dtm/DTMIterator;->setShouldCacheNodes(Z)V
+Lorg/apache/xml/dtm/DTMManager;->getDTM(Ljavax/xml/transform/Source;ZLorg/apache/xml/dtm/DTMWSFilter;ZZ)Lorg/apache/xml/dtm/DTM;
+Lorg/apache/xml/dtm/DTMManager;->getXMLStringFactory()Lorg/apache/xml/utils/XMLStringFactory;
+Lorg/apache/xml/dtm/DTMManager;->release(Lorg/apache/xml/dtm/DTM;Z)Z
+Lorg/apache/xml/dtm/ref/CoroutineManager;-><init>()V
+Lorg/apache/xml/dtm/ref/CoroutineManager;->co_joinCoroutineSet(I)I
+Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;-><init>()V
+Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->includeSelf()Lorg/apache/xml/dtm/DTMAxisIterator;
+Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->reset()Lorg/apache/xml/dtm/DTMAxisIterator;
+Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->resetPosition()Lorg/apache/xml/dtm/DTMAxisIterator;
+Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->returnNode(I)I
+Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->setRestartable(Z)V
+Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->_includeSelf:Z
+Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->_isRestartable:Z
+Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->_last:I
+Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->_markedNode:I
+Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->_position:I
+Lorg/apache/xml/dtm/ref/DTMAxisIteratorBase;->_startNode:I
+Lorg/apache/xml/dtm/ref/DTMAxisIterNodeList;-><init>(Lorg/apache/xml/dtm/DTM;Lorg/apache/xml/dtm/DTMAxisIterator;)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->appendChild(IZZ)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->appendTextChild(Ljava/lang/String;)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->declareNamespaceInContext(II)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->documentRegistration()V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->documentRelease()V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->ensureSizeOfIndex(II)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->error(Ljava/lang/String;)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->findGTE([IIII)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->findInSortedSuballocatedIntVector(Lorg/apache/xml/utils/SuballocatedIntVector;I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->findNamespaceContext(I)Lorg/apache/xml/utils/SuballocatedIntVector;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocument()I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentAllDeclarationsProcessed()Z
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentBaseURI()Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentEncoding(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentRoot(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentStandalone(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentSystemIdentifier(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDocumentVersion(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getDTMIDs()Lorg/apache/xml/utils/SuballocatedIntVector;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getExpandedTypeID(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getExpandedTypeID(Ljava/lang/String;Ljava/lang/String;I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getFirstChild(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getFirstNamespaceNode(IZ)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getLastChild(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getLevel(I)S
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getLocalNameFromExpandedNameID(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getManager()Lorg/apache/xml/dtm/DTMManager;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNamespaceFromExpandedNameID(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNamespaceType(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNextAttribute(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNextNamespaceNode(IIZ)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNextSibling(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNode(I)Lorg/w3c/dom/Node;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNodeHandle(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNodeIdent(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getNodeType(I)S
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getOwnerDocument(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getParent(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getPreviousSibling(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getShouldStripWhitespace()Z
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getStringValueChunk(II[I)[C
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->getStringValueChunkCount(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->hasChildNodes(I)Z
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->indexNode(II)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->isCharacterElementContentWhitespace(I)Z
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->isDocumentAllDeclarationsProcessed(I)Z
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->isNodeAfter(II)Z
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->isSupported(Ljava/lang/String;Ljava/lang/String;)Z
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->makeNodeHandle(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->makeNodeIdentity(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_expandedNameTable:Lorg/apache/xml/dtm/ref/ExpandedNameTable;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_exptype:Lorg/apache/xml/utils/SuballocatedIntVector;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_firstch:Lorg/apache/xml/utils/SuballocatedIntVector;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_nextsib:Lorg/apache/xml/utils/SuballocatedIntVector;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_parent:Lorg/apache/xml/utils/SuballocatedIntVector;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_prevsib:Lorg/apache/xml/utils/SuballocatedIntVector;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_size:I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_wsfilter:Lorg/apache/xml/dtm/DTMWSFilter;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->m_xstrf:Lorg/apache/xml/utils/XMLStringFactory;
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->popShouldStripWhitespace()V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->pushShouldStripWhitespace(Z)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->setDocumentBaseURI(Ljava/lang/String;)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->setFeature(Ljava/lang/String;Z)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->setShouldStripWhitespace(Z)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->supportsPreStripping()Z
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_exptype(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_firstch(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_level(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_nextsib(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_parent(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_prevsib(I)I
+Lorg/apache/xml/dtm/ref/DTMDefaultBase;->_type(I)S
+Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$InternalAxisIteratorBase;-><init>(Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$InternalAxisIteratorBase;->_currentNode:I
+Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$NamespaceIterator;-><init>(Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$NamespaceIterator;->next()I
+Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$NamespaceIterator;->setStartNode(I)Lorg/apache/xml/dtm/DTMAxisIterator;
+Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$NthDescendantIterator;-><init>(Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;I)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$SingletonIterator;-><init>(Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators$SingletonIterator;-><init>(Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;I)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;-><init>(Lorg/apache/xml/dtm/DTMManager;Ljavax/xml/transform/Source;ILorg/apache/xml/dtm/DTMWSFilter;Lorg/apache/xml/utils/XMLStringFactory;Z)V
+Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;->getAxisIterator(I)Lorg/apache/xml/dtm/DTMAxisIterator;
+Lorg/apache/xml/dtm/ref/DTMDefaultBaseIterators;->getTypedAxisIterator(II)Lorg/apache/xml/dtm/DTMAxisIterator;
+Lorg/apache/xml/dtm/ref/DTMDefaultBaseTraversers;->getAxisTraverser(I)Lorg/apache/xml/dtm/DTMAxisTraverser;
+Lorg/apache/xml/dtm/ref/DTMManagerDefault;-><init>()V
+Lorg/apache/xml/dtm/ref/DTMManagerDefault;->addDTM(Lorg/apache/xml/dtm/DTM;I)V
+Lorg/apache/xml/dtm/ref/DTMManagerDefault;->addDTM(Lorg/apache/xml/dtm/DTM;II)V
+Lorg/apache/xml/dtm/ref/DTMManagerDefault;->getFirstFreeDTMID()I
+Lorg/apache/xml/dtm/ref/DTMManagerDefault;->getXMLReader(Ljavax/xml/transform/Source;)Lorg/xml/sax/XMLReader;
+Lorg/apache/xml/dtm/ref/DTMManagerDefault;->releaseXMLReader(Lorg/xml/sax/XMLReader;)V
+Lorg/apache/xml/dtm/ref/DTMNodeIterator;-><init>(Lorg/apache/xml/dtm/DTMIterator;)V
+Lorg/apache/xml/dtm/ref/DTMNodeIterator;->getDTMIterator()Lorg/apache/xml/dtm/DTMIterator;
+Lorg/apache/xml/dtm/ref/DTMNodeIterator;->getRoot()Lorg/w3c/dom/Node;
+Lorg/apache/xml/dtm/ref/DTMNodeList;-><init>(Lorg/apache/xml/dtm/DTMIterator;)V
+Lorg/apache/xml/dtm/ref/DTMNodeProxy;-><init>(Lorg/apache/xml/dtm/DTM;I)V
+Lorg/apache/xml/dtm/ref/DTMNodeProxy;->getDTM()Lorg/apache/xml/dtm/DTM;
+Lorg/apache/xml/dtm/ref/DTMNodeProxy;->getDTMNodeNumber()I
+Lorg/apache/xml/dtm/ref/DTMNodeProxy;->getStringValue()Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/DTMStringPool;-><init>()V
+Lorg/apache/xml/dtm/ref/DTMStringPool;->indexToString(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/DTMStringPool;->m_intToString:Ljava/util/Vector;
+Lorg/apache/xml/dtm/ref/DTMStringPool;->removeAllElements()V
+Lorg/apache/xml/dtm/ref/DTMStringPool;->stringToIndex(Ljava/lang/String;)I
+Lorg/apache/xml/dtm/ref/ExpandedNameTable;->getExpandedTypeID(Ljava/lang/String;Ljava/lang/String;I)I
+Lorg/apache/xml/dtm/ref/ExpandedNameTable;->getExpandedTypeID(Ljava/lang/String;Ljava/lang/String;IZ)I
+Lorg/apache/xml/dtm/ref/ExpandedNameTable;->getLocalName(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/ExpandedNameTable;->getSize()I
+Lorg/apache/xml/dtm/ref/ExpandedNameTable;->getType(I)S
+Lorg/apache/xml/dtm/ref/IncrementalSAXSource;->deliverMoreNodes(Z)Ljava/lang/Object;
+Lorg/apache/xml/dtm/ref/IncrementalSAXSource;->setContentHandler(Lorg/xml/sax/ContentHandler;)V
+Lorg/apache/xml/dtm/ref/IncrementalSAXSource;->setLexicalHandler(Lorg/xml/sax/ext/LexicalHandler;)V
+Lorg/apache/xml/dtm/ref/IncrementalSAXSource;->startParse(Lorg/xml/sax/InputSource;)V
+Lorg/apache/xml/dtm/ref/IncrementalSAXSource_Filter;-><init>()V
+Lorg/apache/xml/dtm/ref/IncrementalSAXSource_Filter;->setXMLReader(Lorg/xml/sax/XMLReader;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$AncestorIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$AncestorIterator;->next()I
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$AncestorIterator;->setStartNode(I)Lorg/apache/xml/dtm/DTMAxisIterator;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$AttributeIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$ChildrenIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$ChildrenIterator;->setStartNode(I)Lorg/apache/xml/dtm/DTMAxisIterator;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$DescendantIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$FollowingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$FollowingSiblingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$ParentIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$ParentIterator;->setNodeType(I)Lorg/apache/xml/dtm/DTMAxisIterator;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$PrecedingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$PrecedingSiblingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedAncestorIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedAttributeIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedChildrenIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedDescendantIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedFollowingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedFollowingSiblingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedPrecedingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedPrecedingSiblingIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2$TypedSingletonIterator;-><init>(Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;I)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;-><init>(Lorg/apache/xml/dtm/DTMManager;Ljavax/xml/transform/Source;ILorg/apache/xml/dtm/DTMWSFilter;Lorg/apache/xml/utils/XMLStringFactory;ZIZZZ)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->copyAttribute(IILorg/apache/xml/serializer/SerializationHandler;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->copyAttributes(ILorg/apache/xml/serializer/SerializationHandler;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->copyElement(IILorg/apache/xml/serializer/SerializationHandler;)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->copyNS(ILorg/apache/xml/serializer/SerializationHandler;Z)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->copyTextNode(ILorg/apache/xml/serializer/SerializationHandler;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->dispatchCharactersEvents(ILorg/xml/sax/ContentHandler;Z)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getFirstAttribute(I)I
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getIdForNamespace(Ljava/lang/String;)I
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getLocalName(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getNodeName(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getNodeNameX(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getNodeValue(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getStringValue()Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getStringValue(I)Lorg/apache/xml/utils/XMLString;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->getStringValueX(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->m_buildIdIndex:Z
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->_exptype2(I)I
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->_exptype2Type(I)I
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->_firstch2(I)I
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM2;->_nextsib2(I)I
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->dispatchToEvents(ILorg/xml/sax/ContentHandler;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getAttributeNode(ILjava/lang/String;Ljava/lang/String;)I
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getContentHandler()Lorg/xml/sax/ContentHandler;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getDeclHandler()Lorg/xml/sax/ext/DeclHandler;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getDocumentTypeDeclarationPublicIdentifier()Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getDocumentTypeDeclarationSystemIdentifier()Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getDTDHandler()Lorg/xml/sax/DTDHandler;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getEntityResolver()Lorg/xml/sax/EntityResolver;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getErrorHandler()Lorg/xml/sax/ErrorHandler;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getLexicalHandler()Lorg/xml/sax/ext/LexicalHandler;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getNamespaceURI(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getNumberOfNodes()I
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getPrefix(I)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getSourceLocatorFor(I)Ljavax/xml/transform/SourceLocator;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->getUnparsedEntityURI(Ljava/lang/String;)Ljava/lang/String;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->isAttributeSpecified(I)Z
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->migrateTo(Lorg/apache/xml/dtm/DTMManager;)V
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->m_idAttributes:Ljava/util/Hashtable;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->m_parents:Lorg/apache/xml/utils/IntStack;
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->m_previous:I
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->needsTwoThreads()Z
+Lorg/apache/xml/dtm/ref/sax2dtm/SAX2DTM;->setProperty(Ljava/lang/String;Ljava/lang/Object;)V
+Lorg/apache/xml/dtm/ref/SecuritySupport;->getContextClassLoader()Ljava/lang/ClassLoader;
+Lorg/apache/xml/dtm/ref/SecuritySupport;->getFileExists(Ljava/io/File;)Z
+Lorg/apache/xml/dtm/ref/SecuritySupport;->getFileInputStream(Ljava/io/File;)Ljava/io/FileInputStream;
+Lorg/apache/xml/dtm/ref/SecuritySupport;->getInstance()Lorg/apache/xml/dtm/ref/SecuritySupport;
+Lorg/apache/xml/dtm/ref/SecuritySupport;->getLastModified(Ljava/io/File;)J
+Lorg/apache/xml/dtm/ref/SecuritySupport;->getParentClassLoader(Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;
+Lorg/apache/xml/dtm/ref/SecuritySupport;->getResourceAsStream(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/io/InputStream;
+Lorg/apache/xml/dtm/ref/SecuritySupport;->getSystemClassLoader()Ljava/lang/ClassLoader;
+Lorg/apache/xml/dtm/ref/SecuritySupport;->getSystemProperty(Ljava/lang/String;)Ljava/lang/String;
+Lorg/apache/xml/res/XMLErrorResources;-><init>()V
+Lorg/apache/xml/res/XMLMessages;->createXMLMessage(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
+Lorg/apache/xml/serializer/CharInfo$CharKey;-><init>(C)V
+Lorg/apache/xml/serializer/CharInfo;-><init>(Ljava/lang/String;Ljava/lang/String;Z)V
+Lorg/apache/xml/serializer/CharInfo;->get(I)Z
+Lorg/apache/xml/serializer/CharInfo;->getCharInfo(Ljava/lang/String;Ljava/lang/String;)Lorg/apache/xml/serializer/CharInfo;
+Lorg/apache/xml/serializer/CharInfo;->set(I)V
+Lorg/apache/xml/serializer/dom3/LSSerializerImpl;-><init>()V
+Lorg/apache/xml/serializer/DOMSerializer;->serialize(Lorg/w3c/dom/Node;)V
+Lorg/apache/xml/serializer/ElemContext;->m_elementName:Ljava/lang/String;
+Lorg/apache/xml/serializer/ElemContext;->m_elementURI:Ljava/lang/String;
+Lorg/apache/xml/serializer/ElemContext;->m_startTagOpen:Z
+Lorg/apache/xml/serializer/ElemContext;->push(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/apache/xml/serializer/ElemContext;
+Lorg/apache/xml/serializer/ElemDesc;->isAttrFlagSet(Ljava/lang/String;I)Z
+Lorg/apache/xml/serializer/Encodings;->convertMime2JavaEncoding(Ljava/lang/String;)Ljava/lang/String;
+Lorg/apache/xml/serializer/Encodings;->getMimeEncoding(Ljava/lang/String;)Ljava/lang/String;
+Lorg/apache/xml/serializer/Encodings;->getWriter(Ljava/io/OutputStream;Ljava/lang/String;)Ljava/io/Writer;
+Lorg/apache/xml/serializer/NamespaceMappings;-><init>()V
+Lorg/apache/xml/serializer/NamespaceMappings;->generateNextPrefix()Ljava/lang/String;
+Lorg/apache/xml/serializer/NamespaceMappings;->lookupNamespace(Ljava/lang/String;)Ljava/lang/String;
+Lorg/apache/xml/serializer/NamespaceMappings;->lookupPrefix(Ljava/lang/String;)Ljava/lang/String;
+Lorg/apache/xml/serializer/OutputPropertiesFactory;->getDefaultMethodProperties(Ljava/lang/String;)Ljava/util/Properties;
+Lorg/apache/xml/serializer/OutputPropertyUtils;->getBooleanProperty(Ljava/lang/String;Ljava/util/Properties;)Z
+Lorg/apache/xml/serializer/OutputPropertyUtils;->getIntProperty(Ljava/lang/String;Ljava/util/Properties;)I
+Lorg/apache/xml/serializer/SerializationHandler;->close()V
+Lorg/apache/xml/serializer/SerializationHandler;->flushPending()V
+Lorg/apache/xml/serializer/SerializationHandler;->setEscaping(Z)Z
+Lorg/apache/xml/serializer/SerializationHandler;->setIndentAmount(I)V
+Lorg/apache/xml/serializer/SerializationHandler;->setNamespaceMappings(Lorg/apache/xml/serializer/NamespaceMappings;)V
+Lorg/apache/xml/serializer/Serializer;->asContentHandler()Lorg/xml/sax/ContentHandler;
+Lorg/apache/xml/serializer/Serializer;->asDOMSerializer()Lorg/apache/xml/serializer/DOMSerializer;
+Lorg/apache/xml/serializer/Serializer;->getOutputFormat()Ljava/util/Properties;
+Lorg/apache/xml/serializer/Serializer;->getOutputStream()Ljava/io/OutputStream;
+Lorg/apache/xml/serializer/Serializer;->getWriter()Ljava/io/Writer;
+Lorg/apache/xml/serializer/Serializer;->reset()Z
+Lorg/apache/xml/serializer/Serializer;->setOutputFormat(Ljava/util/Properties;)V
+Lorg/apache/xml/serializer/Serializer;->setOutputStream(Ljava/io/OutputStream;)V
+Lorg/apache/xml/serializer/Serializer;->setWriter(Ljava/io/Writer;)V
+Lorg/apache/xml/serializer/SerializerBase;->fireCharEvent([CII)V
+Lorg/apache/xml/serializer/SerializerBase;->fireCommentEvent([CII)V
+Lorg/apache/xml/serializer/SerializerBase;->fireEndDoc()V
+Lorg/apache/xml/serializer/SerializerBase;->fireEndElem(Ljava/lang/String;)V
+Lorg/apache/xml/serializer/SerializerBase;->fireEscapingEvent(Ljava/lang/String;Ljava/lang/String;)V
+Lorg/apache/xml/serializer/SerializerBase;->getDoctypePublic()Ljava/lang/String;
+Lorg/apache/xml/serializer/SerializerBase;->getDoctypeSystem()Ljava/lang/String;
+Lorg/apache/xml/serializer/SerializerBase;->getEncoding()Ljava/lang/String;
+Lorg/apache/xml/serializer/SerializerBase;->getPrefixPart(Ljava/lang/String;)Ljava/lang/String;
+Lorg/apache/xml/serializer/SerializerBase;->getVersion()Ljava/lang/String;
+Lorg/apache/xml/serializer/SerializerBase;->m_attributes:Lorg/apache/xml/serializer/AttributesImplSerializer;
+Lorg/apache/xml/serializer/SerializerBase;->m_charsBuff:[C
+Lorg/apache/xml/serializer/SerializerBase;->m_elemContext:Lorg/apache/xml/serializer/ElemContext;
+Lorg/apache/xml/serializer/SerializerBase;->m_needToCallStartDocument:Z
+Lorg/apache/xml/serializer/SerializerBase;->m_tracer:Lorg/apache/xml/serializer/SerializerTrace;
+Lorg/apache/xml/serializer/SerializerBase;->setDoctypePublic(Ljava/lang/String;)V
+Lorg/apache/xml/serializer/SerializerBase;->setDoctypeSystem(Ljava/lang/String;)V
+Lorg/apache/xml/serializer/SerializerBase;->setIndent(Z)V
+Lorg/apache/xml/serializer/SerializerBase;->setMediaType(Ljava/lang/String;)V
+Lorg/apache/xml/serializer/SerializerBase;->setOmitXMLDeclaration(Z)V
+Lorg/apache/xml/serializer/SerializerBase;->setStandalone(Ljava/lang/String;)V
+Lorg/apache/xml/serializer/SerializerBase;->setStandaloneInternal(Ljava/lang/String;)V
+Lorg/apache/xml/serializer/SerializerBase;->setVersion(Ljava/lang/String;)V
+Lorg/apache/xml/serializer/SerializerFactory;->getSerializer(Ljava/util/Properties;)Lorg/apache/xml/serializer/Serializer;
+Lorg/apache/xml/serializer/SerializerTraceWriter;-><init>(Ljava/io/Writer;Lorg/apache/xml/serializer/SerializerTrace;)V
+Lorg/apache/xml/serializer/ToHTMLStream;-><init>()V
+Lorg/apache/xml/serializer/ToHTMLStream;->getElemDesc(Ljava/lang/String;)Lorg/apache/xml/serializer/ElemDesc;
+Lorg/apache/xml/serializer/ToSAXHandler;-><init>(Lorg/xml/sax/ContentHandler;Ljava/lang/String;)V
+Lorg/apache/xml/serializer/ToSAXHandler;-><init>(Lorg/xml/sax/ContentHandler;Lorg/xml/sax/ext/LexicalHandler;Ljava/lang/String;)V
+Lorg/apache/xml/serializer/ToSAXHandler;->m_lexHandler:Lorg/xml/sax/ext/LexicalHandler;
+Lorg/apache/xml/serializer/ToSAXHandler;->m_saxHandler:Lorg/xml/sax/ContentHandler;
+Lorg/apache/xml/serializer/ToSAXHandler;->reset()Z
+Lorg/apache/xml/serializer/ToSAXHandler;->startDocumentInternal()V
+Lorg/apache/xml/serializer/ToSAXHandler;->startElement(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+Lorg/apache/xml/serializer/ToStream;->setCdataSectionElements(Ljava/lang/String;Ljava/util/Properties;)V
+Lorg/apache/xml/serializer/ToStream;->setEncoding(Ljava/lang/String;)V
+Lorg/apache/xml/serializer/ToStream;->setIndentAmount(I)V
+Lorg/apache/xml/serializer/ToTextSAXHandler;-><init>(Lorg/xml/sax/ContentHandler;Ljava/lang/String;)V
+Lorg/apache/xml/serializer/ToTextSAXHandler;-><init>(Lorg/xml/sax/ContentHandler;Lorg/xml/sax/ext/LexicalHandler;Ljava/lang/String;)V
+Lorg/apache/xml/serializer/ToTextStream;-><init>()V
+Lorg/apache/xml/serializer/ToUnknownStream;-><init>()V
+Lorg/apache/xml/serializer/ToXMLSAXHandler;-><init>(Lorg/xml/sax/ContentHandler;Ljava/lang/String;)V
+Lorg/apache/xml/serializer/ToXMLSAXHandler;-><init>(Lorg/xml/sax/ContentHandler;Lorg/xml/sax/ext/LexicalHandler;Ljava/lang/String;)V
+Lorg/apache/xml/serializer/ToXMLStream;-><init>()V
+Lorg/apache/xml/serializer/WriterToASCI;-><init>(Ljava/io/OutputStream;)V
+Lorg/apache/xml/serializer/WriterToUTF8Buffered;-><init>(Ljava/io/OutputStream;)V
+Lorg/apache/xml/utils/DefaultErrorHandler;-><init>()V
+Lorg/apache/xml/utils/DefaultErrorHandler;->printLocation(Ljava/io/PrintWriter;Ljava/lang/Throwable;)V
+Lorg/apache/xml/utils/DOMHelper;->isNodeAfter(Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)Z
+Lorg/apache/xml/utils/DOMHelper;->isNodeTheSame(Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)Z
+Lorg/apache/xml/utils/FastStringBuffer;->append(Ljava/lang/String;)V
+Lorg/apache/xml/utils/FastStringBuffer;->getString(II)Ljava/lang/String;
+Lorg/apache/xml/utils/FastStringBuffer;->length()I
+Lorg/apache/xml/utils/IntStack;->peek()I
+Lorg/apache/xml/utils/ObjectVector;->elementAt(I)Ljava/lang/Object;
+Lorg/apache/xml/utils/ObjectVector;->size()I
+Lorg/apache/xml/utils/PrefixResolverDefault;-><init>(Lorg/w3c/dom/Node;)V
+Lorg/apache/xml/utils/PrefixResolverDefault;->getNamespaceForPrefix(Ljava/lang/String;)Ljava/lang/String;
+Lorg/apache/xml/utils/QName;-><init>(Ljava/lang/String;)V
+Lorg/apache/xml/utils/QName;-><init>(Ljava/lang/String;Ljava/lang/String;)V
+Lorg/apache/xml/utils/QName;->getLocalName()Ljava/lang/String;
+Lorg/apache/xml/utils/SAXSourceLocator;-><init>(Lorg/xml/sax/SAXParseException;)V
+Lorg/apache/xml/utils/StringBufferPool;->free(Lorg/apache/xml/utils/FastStringBuffer;)V
+Lorg/apache/xml/utils/StringBufferPool;->get()Lorg/apache/xml/utils/FastStringBuffer;
+Lorg/apache/xml/utils/StringVector;->elementAt(I)Ljava/lang/String;
+Lorg/apache/xml/utils/StringVector;->size()I
+Lorg/apache/xml/utils/StylesheetPIHandler;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+Lorg/apache/xml/utils/StylesheetPIHandler;->getAssociatedStylesheet()Ljavax/xml/transform/Source;
+Lorg/apache/xml/utils/StylesheetPIHandler;->setBaseId(Ljava/lang/String;)V
+Lorg/apache/xml/utils/StylesheetPIHandler;->setURIResolver(Ljavax/xml/transform/URIResolver;)V
+Lorg/apache/xml/utils/SuballocatedIntVector;-><init>(I)V
+Lorg/apache/xml/utils/SuballocatedIntVector;->elementAt(I)I
+Lorg/apache/xml/utils/SuballocatedIntVector;->setElementAt(II)V
+Lorg/apache/xml/utils/SuballocatedIntVector;->size()I
+Lorg/apache/xml/utils/SystemIDResolver;->getAbsoluteURI(Ljava/lang/String;)Ljava/lang/String;
+Lorg/apache/xml/utils/SystemIDResolver;->getAbsoluteURI(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+Lorg/apache/xml/utils/SystemIDResolver;->getAbsoluteURIFromRelative(Ljava/lang/String;)Ljava/lang/String;
+Lorg/apache/xml/utils/SystemIDResolver;->isAbsoluteURI(Ljava/lang/String;)Z
+Lorg/apache/xml/utils/URI$MalformedURIException;-><init>(Ljava/lang/String;)V
+Lorg/apache/xml/utils/URI;-><init>(Ljava/lang/String;)V
+Lorg/apache/xml/utils/URI;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+Lorg/apache/xml/utils/URI;-><init>(Lorg/apache/xml/utils/URI;)V
+Lorg/apache/xml/utils/URI;-><init>(Lorg/apache/xml/utils/URI;Ljava/lang/String;)V
+Lorg/apache/xml/utils/URI;->getFragment()Ljava/lang/String;
+Lorg/apache/xml/utils/URI;->getHost()Ljava/lang/String;
+Lorg/apache/xml/utils/URI;->getPath()Ljava/lang/String;
+Lorg/apache/xml/utils/URI;->getPort()I
+Lorg/apache/xml/utils/URI;->getQueryString()Ljava/lang/String;
+Lorg/apache/xml/utils/URI;->getScheme()Ljava/lang/String;
+Lorg/apache/xml/utils/URI;->getUserinfo()Ljava/lang/String;
+Lorg/apache/xml/utils/URI;->setFragment(Ljava/lang/String;)V
+Lorg/apache/xml/utils/URI;->setHost(Ljava/lang/String;)V
+Lorg/apache/xml/utils/URI;->setPort(I)V
+Lorg/apache/xml/utils/URI;->setScheme(Ljava/lang/String;)V
+Lorg/apache/xml/utils/URI;->setUserinfo(Ljava/lang/String;)V
+Lorg/apache/xml/utils/WrappedRuntimeException;-><init>(Ljava/lang/Exception;)V
+Lorg/apache/xml/utils/WrappedRuntimeException;->getException()Ljava/lang/Exception;
+Lorg/apache/xml/utils/XML11Char;->isXML11ValidNCName(Ljava/lang/String;)Z
+Lorg/apache/xml/utils/XML11Char;->isXML11ValidQName(Ljava/lang/String;)Z
+Lorg/apache/xml/utils/XMLReaderManager;->getInstance()Lorg/apache/xml/utils/XMLReaderManager;
+Lorg/apache/xml/utils/XMLReaderManager;->getXMLReader()Lorg/xml/sax/XMLReader;
+Lorg/apache/xml/utils/XMLReaderManager;->releaseXMLReader(Lorg/xml/sax/XMLReader;)V
+Lorg/apache/xml/utils/XMLString;->dispatchCharactersEvents(Lorg/xml/sax/ContentHandler;)V
+Lorg/apache/xml/utils/XMLString;->equals(Lorg/apache/xml/utils/XMLString;)Z
+Lorg/apache/xml/utils/XMLString;->fixWhiteSpace(ZZZ)Lorg/apache/xml/utils/XMLString;
+Lorg/apache/xml/utils/XMLStringDefault;-><init>(Ljava/lang/String;)V
+Lorg/apache/xml/utils/XMLStringFactory;-><init>()V
+Lorg/apache/xml/utils/XMLStringFactory;->emptystr()Lorg/apache/xml/utils/XMLString;
+Lorg/apache/xml/utils/XMLStringFactory;->newstr(Ljava/lang/String;)Lorg/apache/xml/utils/XMLString;
+Lorg/apache/xpath/axes/ChildTestIterator;-><init>(Lorg/apache/xml/dtm/DTMAxisTraverser;)V
+Lorg/apache/xpath/axes/DescendantIterator;-><init>()V
+Lorg/apache/xpath/axes/LocPathIterator;->getDTM(I)Lorg/apache/xml/dtm/DTM;
+Lorg/apache/xpath/axes/LocPathIterator;->getPrefixResolver()Lorg/apache/xml/utils/PrefixResolver;
+Lorg/apache/xpath/axes/LocPathIterator;->getXPathContext()Lorg/apache/xpath/XPathContext;
+Lorg/apache/xpath/axes/NodeSequence;->getContainedIter()Lorg/apache/xml/dtm/DTMIterator;
+Lorg/apache/xpath/axes/NodeSequence;->nextNode()I
+Lorg/apache/xpath/axes/OneStepIterator;-><init>(Lorg/apache/xml/dtm/DTMAxisIterator;I)V
+Lorg/apache/xpath/CachedXPathAPI;-><init>()V
+Lorg/apache/xpath/CachedXPathAPI;-><init>(Lorg/apache/xpath/CachedXPathAPI;)V
+Lorg/apache/xpath/CachedXPathAPI;->eval(Lorg/w3c/dom/Node;Ljava/lang/String;)Lorg/apache/xpath/objects/XObject;
+Lorg/apache/xpath/CachedXPathAPI;->getXPathContext()Lorg/apache/xpath/XPathContext;
+Lorg/apache/xpath/CachedXPathAPI;->selectNodeList(Lorg/w3c/dom/Node;Ljava/lang/String;)Lorg/w3c/dom/NodeList;
+Lorg/apache/xpath/CachedXPathAPI;->selectNodeList(Lorg/w3c/dom/Node;Ljava/lang/String;Lorg/w3c/dom/Node;)Lorg/w3c/dom/NodeList;
+Lorg/apache/xpath/CachedXPathAPI;->selectSingleNode(Lorg/w3c/dom/Node;Ljava/lang/String;)Lorg/w3c/dom/Node;
+Lorg/apache/xpath/CachedXPathAPI;->selectSingleNode(Lorg/w3c/dom/Node;Ljava/lang/String;Lorg/w3c/dom/Node;)Lorg/w3c/dom/Node;
+Lorg/apache/xpath/compiler/FunctionTable;-><init>()V
+Lorg/apache/xpath/compiler/FunctionTable;->installFunction(Ljava/lang/String;Ljava/lang/Class;)I
+Lorg/apache/xpath/Expression;->assertion(ZLjava/lang/String;)V
+Lorg/apache/xpath/Expression;->error(Lorg/apache/xpath/XPathContext;Ljava/lang/String;[Ljava/lang/Object;)V
+Lorg/apache/xpath/Expression;->exprGetParent()Lorg/apache/xpath/ExpressionNode;
+Lorg/apache/xpath/ExpressionNode;->exprGetParent()Lorg/apache/xpath/ExpressionNode;
+Lorg/apache/xpath/functions/FuncCurrent;-><init>()V
+Lorg/apache/xpath/functions/FuncExtFunction;->getFunctionName()Ljava/lang/String;
+Lorg/apache/xpath/functions/FuncExtFunction;->getMethodKey()Ljava/lang/Object;
+Lorg/apache/xpath/functions/Function;-><init>()V
+Lorg/apache/xpath/functions/WrongNumberArgsException;-><init>(Ljava/lang/String;)V
+Lorg/apache/xpath/NodeSet;-><init>()V
+Lorg/apache/xpath/NodeSet;-><init>(Lorg/w3c/dom/Node;)V
+Lorg/apache/xpath/NodeSet;-><init>(Lorg/w3c/dom/NodeList;)V
+Lorg/apache/xpath/NodeSet;-><init>(Lorg/w3c/dom/traversal/NodeIterator;)V
+Lorg/apache/xpath/NodeSet;->addElement(Lorg/w3c/dom/Node;)V
+Lorg/apache/xpath/NodeSet;->addNode(Lorg/w3c/dom/Node;)V
+Lorg/apache/xpath/NodeSet;->contains(Lorg/w3c/dom/Node;)Z
+Lorg/apache/xpath/NodeSet;->elementAt(I)Lorg/w3c/dom/Node;
+Lorg/apache/xpath/NodeSet;->setShouldCacheNodes(Z)V
+Lorg/apache/xpath/NodeSetDTM;-><init>(Lorg/w3c/dom/NodeList;Lorg/apache/xpath/XPathContext;)V
+Lorg/apache/xpath/NodeSetDTM;-><init>(Lorg/w3c/dom/traversal/NodeIterator;Lorg/apache/xpath/XPathContext;)V
+Lorg/apache/xpath/NodeSetDTM;->addNode(I)V
+Lorg/apache/xpath/NodeSetDTM;->detach()V
+Lorg/apache/xpath/NodeSetDTM;->getLength()I
+Lorg/apache/xpath/NodeSetDTM;->item(I)I
+Lorg/apache/xpath/objects/XBoolean;-><init>(Z)V
+Lorg/apache/xpath/objects/XBoolean;->bool()Z
+Lorg/apache/xpath/objects/XBoolean;->str()Ljava/lang/String;
+Lorg/apache/xpath/objects/XBooleanStatic;-><init>(Z)V
+Lorg/apache/xpath/objects/XNodeSet;-><init>(ILorg/apache/xml/dtm/DTMManager;)V
+Lorg/apache/xpath/objects/XNodeSet;-><init>(Lorg/apache/xml/dtm/DTMIterator;)V
+Lorg/apache/xpath/objects/XNodeSet;-><init>(Lorg/apache/xml/dtm/DTMManager;)V
+Lorg/apache/xpath/objects/XNodeSet;->iterRaw()Lorg/apache/xml/dtm/DTMIterator;
+Lorg/apache/xpath/objects/XNodeSet;->mutableNodeset()Lorg/apache/xpath/NodeSetDTM;
+Lorg/apache/xpath/objects/XNodeSet;->nodelist()Lorg/w3c/dom/NodeList;
+Lorg/apache/xpath/objects/XNumber;-><init>(D)V
+Lorg/apache/xpath/objects/XNumber;->num()D
+Lorg/apache/xpath/objects/XNumber;->str()Ljava/lang/String;
+Lorg/apache/xpath/objects/XObject;->bool()Z
+Lorg/apache/xpath/objects/XObject;->create(Ljava/lang/Object;)Lorg/apache/xpath/objects/XObject;
+Lorg/apache/xpath/objects/XObject;->getType()I
+Lorg/apache/xpath/objects/XObject;->getTypeString()Ljava/lang/String;
+Lorg/apache/xpath/objects/XObject;->iter()Lorg/apache/xml/dtm/DTMIterator;
+Lorg/apache/xpath/objects/XObject;->nodelist()Lorg/w3c/dom/NodeList;
+Lorg/apache/xpath/objects/XObject;->nodeset()Lorg/w3c/dom/traversal/NodeIterator;
+Lorg/apache/xpath/objects/XObject;->num()D
+Lorg/apache/xpath/objects/XObject;->object()Ljava/lang/Object;
+Lorg/apache/xpath/objects/XObject;->str()Ljava/lang/String;
+Lorg/apache/xpath/objects/XObject;->xstr()Lorg/apache/xml/utils/XMLString;
+Lorg/apache/xpath/objects/XRTreeFrag;-><init>(ILorg/apache/xpath/XPathContext;)V
+Lorg/apache/xpath/objects/XRTreeFrag;->asNodeIterator()Lorg/apache/xml/dtm/DTMIterator;
+Lorg/apache/xpath/objects/XRTreeFrag;->convertToNodeset()Lorg/w3c/dom/NodeList;
+Lorg/apache/xpath/objects/XString;-><init>(Ljava/lang/String;)V
+Lorg/apache/xpath/objects/XString;->num()D
+Lorg/apache/xpath/patterns/NodeTest;->setWhatToShow(I)V
+Lorg/apache/xpath/res/XPATHErrorResources;-><init>()V
+Lorg/apache/xpath/res/XPATHMessages;->createXPATHMessage(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
+Lorg/apache/xpath/XPath;-><init>(Ljava/lang/String;Ljavax/xml/transform/SourceLocator;Lorg/apache/xml/utils/PrefixResolver;I)V
+Lorg/apache/xpath/XPath;-><init>(Ljava/lang/String;Ljavax/xml/transform/SourceLocator;Lorg/apache/xml/utils/PrefixResolver;ILjavax/xml/transform/ErrorListener;)V
+Lorg/apache/xpath/XPath;->execute(Lorg/apache/xpath/XPathContext;ILorg/apache/xml/utils/PrefixResolver;)Lorg/apache/xpath/objects/XObject;
+Lorg/apache/xpath/XPath;->execute(Lorg/apache/xpath/XPathContext;Lorg/w3c/dom/Node;Lorg/apache/xml/utils/PrefixResolver;)Lorg/apache/xpath/objects/XObject;
+Lorg/apache/xpath/XPath;->getPatternString()Ljava/lang/String;
+Lorg/apache/xpath/XPathAPI;->selectNodeList(Lorg/w3c/dom/Node;Ljava/lang/String;)Lorg/w3c/dom/NodeList;
+Lorg/apache/xpath/XPathAPI;->selectNodeList(Lorg/w3c/dom/Node;Ljava/lang/String;Lorg/w3c/dom/Node;)Lorg/w3c/dom/NodeList;
+Lorg/apache/xpath/XPathAPI;->selectSingleNode(Lorg/w3c/dom/Node;Ljava/lang/String;)Lorg/w3c/dom/Node;
+Lorg/apache/xpath/XPathAPI;->selectSingleNode(Lorg/w3c/dom/Node;Ljava/lang/String;Lorg/w3c/dom/Node;)Lorg/w3c/dom/Node;
+Lorg/apache/xpath/XPathContext$XPathExpressionContext;->getDTMManager()Lorg/apache/xml/dtm/DTMManager;
+Lorg/apache/xpath/XPathContext$XPathExpressionContext;->getXPathContext()Lorg/apache/xpath/XPathContext;
+Lorg/apache/xpath/XPathContext;-><init>()V
+Lorg/apache/xpath/XPathContext;-><init>(Ljava/lang/Object;)V
+Lorg/apache/xpath/XPathContext;->getAxesIteratorStackStacks()Ljava/util/Stack;
+Lorg/apache/xpath/XPathContext;->getContextNodeList()Lorg/apache/xml/dtm/DTMIterator;
+Lorg/apache/xpath/XPathContext;->getContextNodeListsStack()Ljava/util/Stack;
+Lorg/apache/xpath/XPathContext;->getCurrentExpressionNodeStack()Lorg/apache/xml/utils/IntStack;
+Lorg/apache/xpath/XPathContext;->getCurrentNode()I
+Lorg/apache/xpath/XPathContext;->getCurrentNodeStack()Lorg/apache/xml/utils/IntStack;
+Lorg/apache/xpath/XPathContext;->getDTM(I)Lorg/apache/xml/dtm/DTM;
+Lorg/apache/xpath/XPathContext;->getDTMHandleFromNode(Lorg/w3c/dom/Node;)I
+Lorg/apache/xpath/XPathContext;->getDTMManager()Lorg/apache/xml/dtm/DTMManager;
+Lorg/apache/xpath/XPathContext;->getExpressionContext()Lorg/apache/xalan/extensions/ExpressionContext;
+Lorg/apache/xpath/XPathContext;->getNamespaceContext()Lorg/apache/xml/utils/PrefixResolver;
+Lorg/apache/xpath/XPathContext;->getOwnerObject()Ljava/lang/Object;
+Lorg/apache/xpath/XPathContext;->getSAXLocator()Ljavax/xml/transform/SourceLocator;
+Lorg/apache/xpath/XPathContext;->getVarStack()Lorg/apache/xpath/VariableStack;
+Lorg/apache/xpath/XPathContext;->m_dtmManager:Lorg/apache/xml/dtm/DTMManager;
+Lorg/apache/xpath/XPathContext;->popContextNodeList()V
+Lorg/apache/xpath/XPathContext;->popCurrentNode()V
+Lorg/apache/xpath/XPathContext;->pushContextNodeList(Lorg/apache/xml/dtm/DTMIterator;)V
+Lorg/apache/xpath/XPathContext;->pushCurrentNode(I)V
+Lorg/apache/xpath/XPathContext;->reset()V
+Lorg/apache/xpath/XPathContext;->setAxesIteratorStackStacks(Ljava/util/Stack;)V
+Lorg/apache/xpath/XPathContext;->setContextNodeListsStack(Ljava/util/Stack;)V
+Lorg/apache/xpath/XPathContext;->setCurrentExpressionNodeStack(Lorg/apache/xml/utils/IntStack;)V
+Lorg/apache/xpath/XPathContext;->setCurrentNodeStack(Lorg/apache/xml/utils/IntStack;)V
+Lorg/apache/xpath/XPathContext;->setSecureProcessing(Z)V
+Lorg/apache/xpath/XPathContext;->setVarStack(Lorg/apache/xpath/VariableStack;)V
+Lorg/ccil/cowan/tagsoup/AttributesImpl;-><init>(Lorg/xml/sax/Attributes;)V
+Lorg/ccil/cowan/tagsoup/AttributesImpl;->addAttribute(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
 Lorg/ccil/cowan/tagsoup/AttributesImpl;->data:[Ljava/lang/String;
 Lorg/ccil/cowan/tagsoup/AttributesImpl;->length:I
+Lorg/ccil/cowan/tagsoup/AttributesImpl;->setAttribute(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+Lorg/ccil/cowan/tagsoup/AttributesImpl;->setValue(ILjava/lang/String;)V
+Lorg/ccil/cowan/tagsoup/AutoDetector;->autoDetectingReader(Ljava/io/InputStream;)Ljava/io/Reader;
+Lorg/ccil/cowan/tagsoup/Element;-><init>(Lorg/ccil/cowan/tagsoup/ElementType;Z)V
+Lorg/ccil/cowan/tagsoup/Element;->anonymize()V
+Lorg/ccil/cowan/tagsoup/Element;->atts()Lorg/ccil/cowan/tagsoup/AttributesImpl;
+Lorg/ccil/cowan/tagsoup/Element;->canContain(Lorg/ccil/cowan/tagsoup/Element;)Z
+Lorg/ccil/cowan/tagsoup/Element;->clean()V
+Lorg/ccil/cowan/tagsoup/Element;->flags()I
+Lorg/ccil/cowan/tagsoup/Element;->localName()Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Element;->name()Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Element;->namespace()Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Element;->next()Lorg/ccil/cowan/tagsoup/Element;
+Lorg/ccil/cowan/tagsoup/Element;->parent()Lorg/ccil/cowan/tagsoup/ElementType;
+Lorg/ccil/cowan/tagsoup/Element;->preclosed:Z
+Lorg/ccil/cowan/tagsoup/Element;->setAttribute(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+Lorg/ccil/cowan/tagsoup/Element;->setNext(Lorg/ccil/cowan/tagsoup/Element;)V
+Lorg/ccil/cowan/tagsoup/Element;->theAtts:Lorg/ccil/cowan/tagsoup/AttributesImpl;
+Lorg/ccil/cowan/tagsoup/Element;->theNext:Lorg/ccil/cowan/tagsoup/Element;
+Lorg/ccil/cowan/tagsoup/Element;->theType:Lorg/ccil/cowan/tagsoup/ElementType;
+Lorg/ccil/cowan/tagsoup/ElementType;-><init>(Ljava/lang/String;IIILorg/ccil/cowan/tagsoup/Schema;)V
+Lorg/ccil/cowan/tagsoup/ElementType;->atts()Lorg/ccil/cowan/tagsoup/AttributesImpl;
+Lorg/ccil/cowan/tagsoup/ElementType;->setAttribute(Lorg/ccil/cowan/tagsoup/AttributesImpl;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
 Lorg/ccil/cowan/tagsoup/ElementType;->theAtts:Lorg/ccil/cowan/tagsoup/AttributesImpl;
 Lorg/ccil/cowan/tagsoup/ElementType;->theFlags:I
 Lorg/ccil/cowan/tagsoup/ElementType;->theLocalName:Ljava/lang/String;
@@ -2635,91 +5640,500 @@
 Lorg/ccil/cowan/tagsoup/ElementType;->theNamespace:Ljava/lang/String;
 Lorg/ccil/cowan/tagsoup/ElementType;->theParent:Lorg/ccil/cowan/tagsoup/ElementType;
 Lorg/ccil/cowan/tagsoup/ElementType;->theSchema:Lorg/ccil/cowan/tagsoup/Schema;
+Lorg/ccil/cowan/tagsoup/HTMLScanner;-><init>()V
 Lorg/ccil/cowan/tagsoup/HTMLSchema;-><init>()V
+Lorg/ccil/cowan/tagsoup/jaxp/SAXFactoryImpl;-><init>()V
+Lorg/ccil/cowan/tagsoup/jaxp/SAXParserImpl;-><init>()V
+Lorg/ccil/cowan/tagsoup/jaxp/SAXParserImpl;->newInstance(Ljava/util/Map;)Lorg/ccil/cowan/tagsoup/jaxp/SAXParserImpl;
 Lorg/ccil/cowan/tagsoup/Parser;-><init>()V
+Lorg/ccil/cowan/tagsoup/Parser;->bogonsEmpty:Z
+Lorg/ccil/cowan/tagsoup/Parser;->CDATAElements:Z
+Lorg/ccil/cowan/tagsoup/Parser;->cleanPublicid(Ljava/lang/String;)Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Parser;->defaultAttributes:Z
+Lorg/ccil/cowan/tagsoup/Parser;->etagchars:[C
+Lorg/ccil/cowan/tagsoup/Parser;->expandEntities(Ljava/lang/String;)Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Parser;->getInputStream(Ljava/lang/String;Ljava/lang/String;)Ljava/io/InputStream;
+Lorg/ccil/cowan/tagsoup/Parser;->ignorableWhitespace:Z
+Lorg/ccil/cowan/tagsoup/Parser;->ignoreBogons:Z
+Lorg/ccil/cowan/tagsoup/Parser;->lookupEntity([CII)I
+Lorg/ccil/cowan/tagsoup/Parser;->makeName([CII)Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Parser;->pop()V
+Lorg/ccil/cowan/tagsoup/Parser;->push(Lorg/ccil/cowan/tagsoup/Element;)V
+Lorg/ccil/cowan/tagsoup/Parser;->rectify(Lorg/ccil/cowan/tagsoup/Element;)V
+Lorg/ccil/cowan/tagsoup/Parser;->restart(Lorg/ccil/cowan/tagsoup/Element;)V
+Lorg/ccil/cowan/tagsoup/Parser;->restartablyPop()V
+Lorg/ccil/cowan/tagsoup/Parser;->rootBogons:Z
+Lorg/ccil/cowan/tagsoup/Parser;->schemaProperty:Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Parser;->split(Ljava/lang/String;)[Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Parser;->theAttributeName:Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Parser;->theAutoDetector:Lorg/ccil/cowan/tagsoup/AutoDetector;
+Lorg/ccil/cowan/tagsoup/Parser;->theContentHandler:Lorg/xml/sax/ContentHandler;
+Lorg/ccil/cowan/tagsoup/Parser;->theDoctypeIsPresent:Z
+Lorg/ccil/cowan/tagsoup/Parser;->theDoctypeSystemId:Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Parser;->theFeatures:Ljava/util/HashMap;
+Lorg/ccil/cowan/tagsoup/Parser;->theLexicalHandler:Lorg/xml/sax/ext/LexicalHandler;
+Lorg/ccil/cowan/tagsoup/Parser;->theNewElement:Lorg/ccil/cowan/tagsoup/Element;
+Lorg/ccil/cowan/tagsoup/Parser;->thePCDATA:Lorg/ccil/cowan/tagsoup/Element;
+Lorg/ccil/cowan/tagsoup/Parser;->thePITarget:Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Parser;->theSaved:Lorg/ccil/cowan/tagsoup/Element;
+Lorg/ccil/cowan/tagsoup/Parser;->theScanner:Lorg/ccil/cowan/tagsoup/Scanner;
+Lorg/ccil/cowan/tagsoup/Parser;->theSchema:Lorg/ccil/cowan/tagsoup/Schema;
+Lorg/ccil/cowan/tagsoup/Parser;->theStack:Lorg/ccil/cowan/tagsoup/Element;
+Lorg/ccil/cowan/tagsoup/Parser;->trimquotes(Ljava/lang/String;)Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Parser;->virginStack:Z
+Lorg/ccil/cowan/tagsoup/PYXScanner;-><init>()V
+Lorg/ccil/cowan/tagsoup/PYXWriter;-><init>(Ljava/io/Writer;)V
+Lorg/ccil/cowan/tagsoup/ScanHandler;->aname([CII)V
+Lorg/ccil/cowan/tagsoup/ScanHandler;->aval([CII)V
+Lorg/ccil/cowan/tagsoup/ScanHandler;->entity([CII)V
+Lorg/ccil/cowan/tagsoup/ScanHandler;->eof([CII)V
+Lorg/ccil/cowan/tagsoup/ScanHandler;->etag([CII)V
+Lorg/ccil/cowan/tagsoup/ScanHandler;->gi([CII)V
+Lorg/ccil/cowan/tagsoup/ScanHandler;->pcdata([CII)V
+Lorg/ccil/cowan/tagsoup/ScanHandler;->pi([CII)V
+Lorg/ccil/cowan/tagsoup/ScanHandler;->stagc([CII)V
+Lorg/ccil/cowan/tagsoup/Scanner;->startCDATA()V
+Lorg/ccil/cowan/tagsoup/Schema;->elementType(Ljava/lang/String;III)V
+Lorg/ccil/cowan/tagsoup/Schema;->getElementType(Ljava/lang/String;)Lorg/ccil/cowan/tagsoup/ElementType;
+Lorg/ccil/cowan/tagsoup/Schema;->getEntity(Ljava/lang/String;)I
+Lorg/ccil/cowan/tagsoup/Schema;->getPrefix()Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Schema;->getURI()Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/Schema;->parent(Ljava/lang/String;Ljava/lang/String;)V
 Lorg/ccil/cowan/tagsoup/Schema;->theElementTypes:Ljava/util/HashMap;
 Lorg/ccil/cowan/tagsoup/Schema;->theEntities:Ljava/util/HashMap;
 Lorg/ccil/cowan/tagsoup/Schema;->thePrefix:Ljava/lang/String;
 Lorg/ccil/cowan/tagsoup/Schema;->theRoot:Lorg/ccil/cowan/tagsoup/ElementType;
 Lorg/ccil/cowan/tagsoup/Schema;->theURI:Ljava/lang/String;
-Lsun/misc/Cleaner;->clean()V
-Lsun/misc/Unsafe;->addressSize()I
-Lsun/misc/Unsafe;->allocateInstance(Ljava/lang/Class;)Ljava/lang/Object;
-Lsun/misc/Unsafe;->allocateMemory(J)J
-Lsun/misc/Unsafe;->arrayBaseOffset(Ljava/lang/Class;)I
-Lsun/misc/Unsafe;->arrayIndexScale(Ljava/lang/Class;)I
-Lsun/misc/Unsafe;->compareAndSwapInt(Ljava/lang/Object;JII)Z
-Lsun/misc/Unsafe;->compareAndSwapLong(Ljava/lang/Object;JJJ)Z
-Lsun/misc/Unsafe;->compareAndSwapObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z
-Lsun/misc/Unsafe;->copyMemory(JJJ)V
-Lsun/misc/Unsafe;->copyMemoryFromPrimitiveArray(Ljava/lang/Object;JJJ)V
-Lsun/misc/Unsafe;->copyMemoryToPrimitiveArray(JLjava/lang/Object;JJ)V
-Lsun/misc/Unsafe;->freeMemory(J)V
-Lsun/misc/Unsafe;->fullFence()V
-Lsun/misc/Unsafe;->getAndAddInt(Ljava/lang/Object;JI)I
-Lsun/misc/Unsafe;->getAndAddLong(Ljava/lang/Object;JJ)J
-Lsun/misc/Unsafe;->getAndSetInt(Ljava/lang/Object;JI)I
-Lsun/misc/Unsafe;->getAndSetLong(Ljava/lang/Object;JJ)J
-Lsun/misc/Unsafe;->getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;
-Lsun/misc/Unsafe;->getArrayBaseOffsetForComponentType(Ljava/lang/Class;)I
-Lsun/misc/Unsafe;->getArrayIndexScaleForComponentType(Ljava/lang/Class;)I
-Lsun/misc/Unsafe;->getBoolean(Ljava/lang/Object;J)Z
-Lsun/misc/Unsafe;->getByte(J)B
-Lsun/misc/Unsafe;->getByte(Ljava/lang/Object;J)B
-Lsun/misc/Unsafe;->getChar(J)C
-Lsun/misc/Unsafe;->getChar(Ljava/lang/Object;J)C
-Lsun/misc/Unsafe;->getDouble(J)D
-Lsun/misc/Unsafe;->getDouble(Ljava/lang/Object;J)D
-Lsun/misc/Unsafe;->getFloat(J)F
-Lsun/misc/Unsafe;->getFloat(Ljava/lang/Object;J)F
-Lsun/misc/Unsafe;->getInt(J)I
-Lsun/misc/Unsafe;->getInt(Ljava/lang/Object;J)I
-Lsun/misc/Unsafe;->getIntVolatile(Ljava/lang/Object;J)I
-Lsun/misc/Unsafe;->getLong(J)J
-Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J
-Lsun/misc/Unsafe;->getLongVolatile(Ljava/lang/Object;J)J
-Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object;
-Lsun/misc/Unsafe;->getObjectVolatile(Ljava/lang/Object;J)Ljava/lang/Object;
-Lsun/misc/Unsafe;->getShort(J)S
-Lsun/misc/Unsafe;->getShort(Ljava/lang/Object;J)S
-Lsun/misc/Unsafe;->getUnsafe()Lsun/misc/Unsafe;
-Lsun/misc/Unsafe;->INVALID_FIELD_OFFSET:I
-Lsun/misc/Unsafe;->loadFence()V
-Lsun/misc/Unsafe;->objectFieldOffset(Ljava/lang/reflect/Field;)J
-Lsun/misc/Unsafe;->pageSize()I
-Lsun/misc/Unsafe;->park(ZJ)V
-Lsun/misc/Unsafe;->putBoolean(Ljava/lang/Object;JZ)V
-Lsun/misc/Unsafe;->putByte(JB)V
-Lsun/misc/Unsafe;->putByte(Ljava/lang/Object;JB)V
-Lsun/misc/Unsafe;->putChar(JC)V
-Lsun/misc/Unsafe;->putChar(Ljava/lang/Object;JC)V
-Lsun/misc/Unsafe;->putDouble(JD)V
-Lsun/misc/Unsafe;->putDouble(Ljava/lang/Object;JD)V
-Lsun/misc/Unsafe;->putFloat(JF)V
-Lsun/misc/Unsafe;->putFloat(Ljava/lang/Object;JF)V
-Lsun/misc/Unsafe;->putInt(JI)V
-Lsun/misc/Unsafe;->putInt(Ljava/lang/Object;JI)V
-Lsun/misc/Unsafe;->putIntVolatile(Ljava/lang/Object;JI)V
-Lsun/misc/Unsafe;->putLong(JJ)V
-Lsun/misc/Unsafe;->putLong(Ljava/lang/Object;JJ)V
-Lsun/misc/Unsafe;->putLongVolatile(Ljava/lang/Object;JJ)V
-Lsun/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V
-Lsun/misc/Unsafe;->putObjectVolatile(Ljava/lang/Object;JLjava/lang/Object;)V
-Lsun/misc/Unsafe;->putOrderedInt(Ljava/lang/Object;JI)V
-Lsun/misc/Unsafe;->putOrderedLong(Ljava/lang/Object;JJ)V
-Lsun/misc/Unsafe;->putOrderedObject(Ljava/lang/Object;JLjava/lang/Object;)V
-Lsun/misc/Unsafe;->putShort(JS)V
-Lsun/misc/Unsafe;->putShort(Ljava/lang/Object;JS)V
-Lsun/misc/Unsafe;->setMemory(JJB)V
-Lsun/misc/Unsafe;->storeFence()V
-Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
-Lsun/misc/Unsafe;->THE_ONE:Lsun/misc/Unsafe;
-Lsun/misc/Unsafe;->unpark(Ljava/lang/Object;)V
-Lsun/misc/URLClassPath$JarLoader;->getJarFile()Ljava/util/jar/JarFile;
-Lsun/misc/URLClassPath;->lmap:Ljava/util/HashMap;
-Lsun/misc/URLClassPath;->loaders:Ljava/util/ArrayList;
-Lsun/misc/URLClassPath;->urls:Ljava/util/Stack;
-Lsun/nio/ch/DirectBuffer;->cleaner()Lsun/misc/Cleaner;
-Lsun/security/x509/AlgorithmId;->get(Ljava/lang/String;)Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/AlgorithmId;->getName()Ljava/lang/String;
-Lsun/security/x509/AVA;->hasRFC2253Keyword()Z
+Lorg/ccil/cowan/tagsoup/XMLWriter;-><init>(Ljava/io/Writer;)V
+Lorg/ccil/cowan/tagsoup/XMLWriter;->htmlMode:Z
+Lorg/ccil/cowan/tagsoup/XMLWriter;->setOutput(Ljava/io/Writer;)V
+Lorg/ccil/cowan/tagsoup/XMLWriter;->setOutputProperty(Ljava/lang/String;Ljava/lang/String;)V
+Lorg/ccil/cowan/tagsoup/XMLWriter;->setPrefix(Ljava/lang/String;Ljava/lang/String;)V
+Lorg/xml/sax/helpers/NamespaceSupport$Context;-><init>(Lorg/xml/sax/helpers/NamespaceSupport;)V
+Lorg/xml/sax/helpers/ParserAdapter$AttributeListAdapter;-><init>(Lorg/xml/sax/helpers/ParserAdapter;)V
+Lsun/misc/ASCIICaseInsensitiveComparator;->CASE_INSENSITIVE_ORDER:Ljava/util/Comparator;
+Lsun/misc/ASCIICaseInsensitiveComparator;->lowerCaseHashCode(Ljava/lang/String;)I
+Lsun/misc/BASE64Decoder;-><init>()V
+Lsun/misc/BASE64Decoder;->pem_convert_array:[B
+Lsun/misc/BASE64Encoder;-><init>()V
+Lsun/misc/BASE64Encoder;->pem_array:[C
+Lsun/misc/CEFormatException;-><init>(Ljava/lang/String;)V
+Lsun/misc/CEStreamExhausted;-><init>()V
+Lsun/misc/CharacterDecoder;-><init>()V
+Lsun/misc/CharacterEncoder;-><init>()V
+Lsun/misc/CharacterEncoder;->encodeBuffer([B)Ljava/lang/String;
+Lsun/misc/CharacterEncoder;->encodeBufferPrefix(Ljava/io/OutputStream;)V
+Lsun/misc/CharacterEncoder;->pStream:Ljava/io/PrintStream;
+Lsun/misc/Cleaner;->create(Ljava/lang/Object;Ljava/lang/Runnable;)Lsun/misc/Cleaner;
+Lsun/misc/FloatingDecimal;->$assertionsDisabled:Z
+Lsun/misc/FloatingDecimal;->getHexDigit(Ljava/lang/String;I)I
+Lsun/misc/FloatingDecimal;->stripLeadingZeros(Ljava/lang/String;)Ljava/lang/String;
+Lsun/misc/FormattedFloatingDecimal$Form;->COMPATIBLE:Lsun/misc/FormattedFloatingDecimal$Form;
+Lsun/misc/FormattedFloatingDecimal$Form;->DECIMAL_FLOAT:Lsun/misc/FormattedFloatingDecimal$Form;
+Lsun/misc/FormattedFloatingDecimal$Form;->SCIENTIFIC:Lsun/misc/FormattedFloatingDecimal$Form;
+Lsun/misc/FormattedFloatingDecimal;->$assertionsDisabled:Z
+Lsun/misc/FpUtils;->$assertionsDisabled:Z
+Lsun/misc/FpUtils;->rawCopySign(DD)D
+Lsun/misc/HexDumpEncoder;-><init>()V
+Lsun/misc/HexDumpEncoder;->currentByte:I
+Lsun/misc/HexDumpEncoder;->offset:I
+Lsun/misc/HexDumpEncoder;->thisLine:[B
+Lsun/misc/HexDumpEncoder;->thisLineLength:I
+Lsun/misc/IOUtils;->readFully(Ljava/io/InputStream;IZ)[B
+Lsun/misc/JarIndex;-><init>([Ljava/lang/String;)V
+Lsun/misc/JarIndex;->write(Ljava/io/OutputStream;)V
+Lsun/misc/MessageUtils;-><init>()V
+Lsun/misc/MetaIndex;->forJar(Ljava/io/File;)Lsun/misc/MetaIndex;
+Lsun/misc/MetaIndex;->registerDirectory(Ljava/io/File;)V
+Lsun/misc/VM;->maxDirectMemory()J
+Lsun/net/ftp/FtpClient;-><init>()V
+Lsun/net/util/IPAddressUtil;->isIPv4LiteralAddress(Ljava/lang/String;)Z
+Lsun/net/util/IPAddressUtil;->isIPv6LiteralAddress(Ljava/lang/String;)Z
+Lsun/net/www/MessageHeader;-><init>()V
+Lsun/net/www/MessageHeader;-><init>(Ljava/io/InputStream;)V
+Lsun/net/www/MessageHeader;->add(Ljava/lang/String;Ljava/lang/String;)V
+Lsun/net/www/MessageHeader;->findValue(Ljava/lang/String;)Ljava/lang/String;
+Lsun/net/www/MessageHeader;->prepend(Ljava/lang/String;Ljava/lang/String;)V
+Lsun/net/www/MessageHeader;->print(Ljava/io/PrintStream;)V
+Lsun/net/www/MessageHeader;->set(Ljava/lang/String;Ljava/lang/String;)V
+Lsun/net/www/ParseUtil;->decode(Ljava/lang/String;)Ljava/lang/String;
+Lsun/net/www/ParseUtil;->encodePath(Ljava/lang/String;Z)Ljava/lang/String;
+Lsun/net/www/ParseUtil;->fileToEncodedURL(Ljava/io/File;)Ljava/net/URL;
+Lsun/net/www/URLConnection;-><init>(Ljava/net/URL;)V
+Lsun/net/www/URLConnection;->setProperties(Lsun/net/www/MessageHeader;)V
+Lsun/nio/ch/DirectBuffer;->address()J
+Lsun/nio/ch/FileChannelImpl;->unmap0(JJ)I
+Lsun/nio/ch/SelectorImpl;->publicSelectedKeys:Ljava/util/Set;
+Lsun/nio/ch/SelectorImpl;->selectedKeys:Ljava/util/Set;
+Lsun/nio/cs/HistoricallyNamedCharset;->historicalName()Ljava/lang/String;
+Lsun/nio/cs/ThreadLocalCoders;->decoderFor(Ljava/lang/Object;)Ljava/nio/charset/CharsetDecoder;
+Lsun/nio/fs/BasicFileAttributesHolder;->get()Ljava/nio/file/attribute/BasicFileAttributes;
+Lsun/reflect/misc/ReflectUtil;->checkPackageAccess(Ljava/lang/Class;)V
+Lsun/reflect/misc/ReflectUtil;->checkPackageAccess(Ljava/lang/String;)V
+Lsun/reflect/misc/ReflectUtil;->isPackageAccessible(Ljava/lang/Class;)Z
+Lsun/reflect/misc/ReflectUtil;->isSubclassOf(Ljava/lang/Class;Ljava/lang/Class;)Z
+Lsun/reflect/Reflection;->ensureMemberAccess(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;I)V
+Lsun/reflect/Reflection;->isSubclassOf(Ljava/lang/Class;Ljava/lang/Class;)Z
+Lsun/security/action/GetBooleanAction;-><init>(Ljava/lang/String;)V
+Lsun/security/action/GetIntegerAction;-><init>(Ljava/lang/String;I)V
+Lsun/security/action/GetPropertyAction;-><init>(Ljava/lang/String;)V
+Lsun/security/action/GetPropertyAction;-><init>(Ljava/lang/String;Ljava/lang/String;)V
+Lsun/security/jca/GetInstance$Instance;->impl:Ljava/lang/Object;
+Lsun/security/jca/GetInstance$Instance;->provider:Ljava/security/Provider;
+Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Lsun/security/jca/GetInstance$Instance;
+Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Lsun/security/jca/GetInstance$Instance;
+Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;Ljava/security/Provider;)Lsun/security/jca/GetInstance$Instance;
+Lsun/security/jca/JCAUtil;->getSecureRandom()Ljava/security/SecureRandom;
+Lsun/security/jca/ProviderConfig;->argument:Ljava/lang/String;
+Lsun/security/jca/ProviderConfig;->CL_STRING:[Ljava/lang/Class;
+Lsun/security/jca/ProviderConfig;->disableLoad()V
+Lsun/security/jca/ProviderConfig;->hasArgument()Z
+Lsun/security/jca/ProviderList;->getService(Ljava/lang/String;Ljava/lang/String;)Ljava/security/Provider$Service;
+Lsun/security/jca/Providers;->getProviderList()Lsun/security/jca/ProviderList;
+Lsun/security/jca/Providers;->startJarVerification()Ljava/lang/Object;
+Lsun/security/jca/Providers;->stopJarVerification(Ljava/lang/Object;)V
+Lsun/security/pkcs/ContentInfo;-><init>(Lsun/security/util/ObjectIdentifier;Lsun/security/util/DerValue;)V
+Lsun/security/pkcs/ContentInfo;-><init>([B)V
+Lsun/security/pkcs/ContentInfo;->DATA_OID:Lsun/security/util/ObjectIdentifier;
+Lsun/security/pkcs/ContentInfo;->encode(Lsun/security/util/DerOutputStream;)V
+Lsun/security/pkcs/ContentInfo;->getData()[B
+Lsun/security/pkcs/ParsingException;-><init>(Ljava/lang/String;)V
+Lsun/security/pkcs/PKCS7;-><init>([B)V
+Lsun/security/pkcs/PKCS7;-><init>([Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/ContentInfo;[Ljava/security/cert/X509Certificate;[Ljava/security/cert/X509CRL;[Lsun/security/pkcs/SignerInfo;)V
+Lsun/security/pkcs/PKCS7;-><init>([Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/ContentInfo;[Ljava/security/cert/X509Certificate;[Lsun/security/pkcs/SignerInfo;)V
+Lsun/security/pkcs/PKCS7;->encodeSignedData(Ljava/io/OutputStream;)V
+Lsun/security/pkcs/PKCS7;->getCertificates()[Ljava/security/cert/X509Certificate;
+Lsun/security/pkcs/PKCS7;->getContentInfo()Lsun/security/pkcs/ContentInfo;
+Lsun/security/pkcs/PKCS7;->getSignerInfos()[Lsun/security/pkcs/SignerInfo;
+Lsun/security/pkcs/PKCS7;->verify(Lsun/security/pkcs/SignerInfo;[B)Lsun/security/pkcs/SignerInfo;
+Lsun/security/pkcs/PKCS7;->verify([B)[Lsun/security/pkcs/SignerInfo;
+Lsun/security/pkcs/PKCS8Key;-><init>()V
+Lsun/security/pkcs/PKCS8Key;->algid:Lsun/security/x509/AlgorithmId;
+Lsun/security/pkcs/PKCS8Key;->encodedKey:[B
+Lsun/security/pkcs/PKCS8Key;->key:[B
+Lsun/security/pkcs/PKCS9Attribute;-><init>(Ljava/lang/String;Ljava/lang/Object;)V
+Lsun/security/pkcs/PKCS9Attribute;-><init>(Lsun/security/util/DerValue;)V
+Lsun/security/pkcs/PKCS9Attribute;-><init>(Lsun/security/util/ObjectIdentifier;Ljava/lang/Object;)V
+Lsun/security/pkcs/PKCS9Attribute;->CONTENT_TYPE_OID:Lsun/security/util/ObjectIdentifier;
+Lsun/security/pkcs/PKCS9Attribute;->derEncode(Ljava/io/OutputStream;)V
+Lsun/security/pkcs/PKCS9Attribute;->EMAIL_ADDRESS_OID:Lsun/security/util/ObjectIdentifier;
+Lsun/security/pkcs/PKCS9Attribute;->getOID()Lsun/security/util/ObjectIdentifier;
+Lsun/security/pkcs/PKCS9Attribute;->getValue()Ljava/lang/Object;
+Lsun/security/pkcs/PKCS9Attribute;->MESSAGE_DIGEST_OID:Lsun/security/util/ObjectIdentifier;
+Lsun/security/pkcs/PKCS9Attribute;->SIGNING_TIME_OID:Lsun/security/util/ObjectIdentifier;
+Lsun/security/pkcs/PKCS9Attributes;-><init>(Lsun/security/util/DerInputStream;)V
+Lsun/security/pkcs/PKCS9Attributes;-><init>(Lsun/security/util/DerInputStream;Z)V
+Lsun/security/pkcs/PKCS9Attributes;-><init>([Lsun/security/pkcs/PKCS9Attribute;)V
+Lsun/security/pkcs/PKCS9Attributes;->encode(BLjava/io/OutputStream;)V
+Lsun/security/pkcs/PKCS9Attributes;->getAttribute(Ljava/lang/String;)Lsun/security/pkcs/PKCS9Attribute;
+Lsun/security/pkcs/PKCS9Attributes;->getAttributeValue(Lsun/security/util/ObjectIdentifier;)Ljava/lang/Object;
+Lsun/security/pkcs/PKCS9Attributes;->getDerEncoding()[B
+Lsun/security/pkcs/SignerInfo;-><init>(Lsun/security/x509/X500Name;Ljava/math/BigInteger;Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/PKCS9Attributes;Lsun/security/x509/AlgorithmId;[BLsun/security/pkcs/PKCS9Attributes;)V
+Lsun/security/pkcs/SignerInfo;-><init>(Lsun/security/x509/X500Name;Ljava/math/BigInteger;Lsun/security/x509/AlgorithmId;Lsun/security/x509/AlgorithmId;[B)V
+Lsun/security/pkcs/SignerInfo;->getCertificate(Lsun/security/pkcs/PKCS7;)Ljava/security/cert/X509Certificate;
+Lsun/security/pkcs/SignerInfo;->getCertificateChain(Lsun/security/pkcs/PKCS7;)Ljava/util/ArrayList;
+Lsun/security/pkcs/SignerInfo;->getDigestAlgorithmId()Lsun/security/x509/AlgorithmId;
+Lsun/security/pkcs/SignerInfo;->getDigestEncryptionAlgorithmId()Lsun/security/x509/AlgorithmId;
+Lsun/security/pkcs/SignerInfo;->getEncryptedDigest()[B
+Lsun/security/provider/certpath/X509CertificatePair;->clearCache()V
+Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/io/InputStream;)V
+Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/io/InputStream;Ljava/lang/String;)V
+Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/util/List;)V
+Lsun/security/provider/certpath/X509CertPath;->certs:Ljava/util/List;
+Lsun/security/provider/certpath/X509CertPath;->getEncodingsStatic()Ljava/util/Iterator;
+Lsun/security/provider/X509Factory;->addToCache(Lsun/security/util/Cache;[BLjava/lang/Object;)V
+Lsun/security/provider/X509Factory;->certCache:Lsun/security/util/Cache;
+Lsun/security/provider/X509Factory;->crlCache:Lsun/security/util/Cache;
+Lsun/security/provider/X509Factory;->getFromCache(Lsun/security/util/Cache;[B)Ljava/lang/Object;
+Lsun/security/provider/X509Factory;->intern(Ljava/security/cert/X509Certificate;)Lsun/security/x509/X509CertImpl;
+Lsun/security/provider/X509Factory;->intern(Ljava/security/cert/X509CRL;)Lsun/security/x509/X509CRLImpl;
+Lsun/security/timestamp/TimestampToken;-><init>([B)V
+Lsun/security/timestamp/TimestampToken;->getDate()Ljava/util/Date;
+Lsun/security/timestamp/TimestampToken;->getHashAlgorithm()Lsun/security/x509/AlgorithmId;
+Lsun/security/timestamp/TimestampToken;->getHashedMessage()[B
+Lsun/security/timestamp/TimestampToken;->getNonce()Ljava/math/BigInteger;
+Lsun/security/util/BitArray;-><init>(I[B)V
+Lsun/security/util/BitArray;->toByteArray()[B
+Lsun/security/util/Cache;-><init>()V
+Lsun/security/util/Cache;->clear()V
+Lsun/security/util/Cache;->get(Ljava/lang/Object;)Ljava/lang/Object;
+Lsun/security/util/Cache;->newHardMemoryCache(I)Lsun/security/util/Cache;
+Lsun/security/util/Cache;->put(Ljava/lang/Object;Ljava/lang/Object;)V
+Lsun/security/util/Debug;->getInstance(Ljava/lang/String;)Lsun/security/util/Debug;
+Lsun/security/util/Debug;->println()V
+Lsun/security/util/Debug;->println(Ljava/lang/String;)V
+Lsun/security/util/Debug;->toHexString(Ljava/math/BigInteger;)Ljava/lang/String;
+Lsun/security/util/DerIndefLenConverter;-><init>()V
+Lsun/security/util/DerIndefLenConverter;->convert([B)[B
+Lsun/security/util/DerIndefLenConverter;->data:[B
+Lsun/security/util/DerIndefLenConverter;->dataPos:I
+Lsun/security/util/DerIndefLenConverter;->dataSize:I
+Lsun/security/util/DerIndefLenConverter;->isIndefinite(I)Z
+Lsun/security/util/DerIndefLenConverter;->newData:[B
+Lsun/security/util/DerIndefLenConverter;->numOfTotalLenBytes:I
+Lsun/security/util/DerIndefLenConverter;->parseLength()I
+Lsun/security/util/DerIndefLenConverter;->parseTag()V
+Lsun/security/util/DerIndefLenConverter;->parseValue(I)V
+Lsun/security/util/DerIndefLenConverter;->writeLengthAndValue()V
+Lsun/security/util/DerIndefLenConverter;->writeTag()V
+Lsun/security/util/DerInputStream;-><init>([B)V
+Lsun/security/util/DerInputStream;->available()I
+Lsun/security/util/DerInputStream;->getBigInteger()Ljava/math/BigInteger;
+Lsun/security/util/DerInputStream;->getBitString()[B
+Lsun/security/util/DerInputStream;->getDerValue()Lsun/security/util/DerValue;
+Lsun/security/util/DerInputStream;->getInteger()I
+Lsun/security/util/DerInputStream;->getOctetString()[B
+Lsun/security/util/DerInputStream;->getOID()Lsun/security/util/ObjectIdentifier;
+Lsun/security/util/DerInputStream;->getSequence(I)[Lsun/security/util/DerValue;
+Lsun/security/util/DerInputStream;->getSet(I)[Lsun/security/util/DerValue;
+Lsun/security/util/DerInputStream;->getSet(IZ)[Lsun/security/util/DerValue;
+Lsun/security/util/DerInputStream;->getUTCTime()Ljava/util/Date;
+Lsun/security/util/DerInputStream;->getUTF8String()Ljava/lang/String;
+Lsun/security/util/DerInputStream;->mark(I)V
+Lsun/security/util/DerInputStream;->peekByte()I
+Lsun/security/util/DerInputStream;->reset()V
+Lsun/security/util/DerInputStream;->subStream(IZ)Lsun/security/util/DerInputStream;
+Lsun/security/util/DerInputStream;->tag:B
+Lsun/security/util/DerOutputStream;-><init>()V
+Lsun/security/util/DerOutputStream;-><init>(I)V
+Lsun/security/util/DerOutputStream;->putBitString([B)V
+Lsun/security/util/DerOutputStream;->putBoolean(Z)V
+Lsun/security/util/DerOutputStream;->putDerValue(Lsun/security/util/DerValue;)V
+Lsun/security/util/DerOutputStream;->putIA5String(Ljava/lang/String;)V
+Lsun/security/util/DerOutputStream;->putInteger(I)V
+Lsun/security/util/DerOutputStream;->putInteger(Ljava/math/BigInteger;)V
+Lsun/security/util/DerOutputStream;->putNull()V
+Lsun/security/util/DerOutputStream;->putOctetString([B)V
+Lsun/security/util/DerOutputStream;->putOID(Lsun/security/util/ObjectIdentifier;)V
+Lsun/security/util/DerOutputStream;->putOrderedSetOf(B[Lsun/security/util/DerEncoder;)V
+Lsun/security/util/DerOutputStream;->putPrintableString(Ljava/lang/String;)V
+Lsun/security/util/DerOutputStream;->putSequence([Lsun/security/util/DerValue;)V
+Lsun/security/util/DerOutputStream;->putUTCTime(Ljava/util/Date;)V
+Lsun/security/util/DerOutputStream;->putUTF8String(Ljava/lang/String;)V
+Lsun/security/util/DerOutputStream;->write(BLsun/security/util/DerOutputStream;)V
+Lsun/security/util/DerOutputStream;->write(B[B)V
+Lsun/security/util/DerValue;-><init>(B[B)V
+Lsun/security/util/DerValue;-><init>(Ljava/io/InputStream;)V
+Lsun/security/util/DerValue;-><init>(Ljava/lang/String;)V
+Lsun/security/util/DerValue;-><init>([B)V
+Lsun/security/util/DerValue;-><init>([BII)V
+Lsun/security/util/DerValue;->buffer:Lsun/security/util/DerInputBuffer;
+Lsun/security/util/DerValue;->createTag(BZB)B
+Lsun/security/util/DerValue;->data:Lsun/security/util/DerInputStream;
+Lsun/security/util/DerValue;->encode(Lsun/security/util/DerOutputStream;)V
+Lsun/security/util/DerValue;->getAsString()Ljava/lang/String;
+Lsun/security/util/DerValue;->getBigInteger()Ljava/math/BigInteger;
+Lsun/security/util/DerValue;->getBitString()[B
+Lsun/security/util/DerValue;->getData()Lsun/security/util/DerInputStream;
+Lsun/security/util/DerValue;->getDataBytes()[B
+Lsun/security/util/DerValue;->getOctetString()[B
+Lsun/security/util/DerValue;->getOID()Lsun/security/util/ObjectIdentifier;
+Lsun/security/util/DerValue;->getPositiveBigInteger()Ljava/math/BigInteger;
+Lsun/security/util/DerValue;->getUnalignedBitString()Lsun/security/util/BitArray;
+Lsun/security/util/DerValue;->isConstructed()Z
+Lsun/security/util/DerValue;->isContextSpecific()Z
+Lsun/security/util/DerValue;->isContextSpecific(B)Z
+Lsun/security/util/DerValue;->isPrintableStringChar(C)Z
+Lsun/security/util/DerValue;->resetTag(B)V
+Lsun/security/util/DerValue;->tag:B
+Lsun/security/util/DerValue;->toByteArray()[B
+Lsun/security/util/DerValue;->toDerInputStream()Lsun/security/util/DerInputStream;
+Lsun/security/util/ManifestDigester$Entry;->digest(Ljava/security/MessageDigest;)[B
+Lsun/security/util/ManifestDigester$Entry;->digestWorkaround(Ljava/security/MessageDigest;)[B
+Lsun/security/util/ManifestDigester;-><init>([B)V
+Lsun/security/util/ManifestDigester;->get(Ljava/lang/String;Z)Lsun/security/util/ManifestDigester$Entry;
+Lsun/security/util/ManifestDigester;->manifestDigest(Ljava/security/MessageDigest;)[B
+Lsun/security/util/MemoryCache$HardCacheEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;J)V
+Lsun/security/util/MemoryCache$SoftCacheEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;JLjava/lang/ref/ReferenceQueue;)V
+Lsun/security/util/ObjectIdentifier;-><init>(Ljava/lang/String;)V
+Lsun/security/util/ObjectIdentifier;-><init>([I)V
+Lsun/security/util/ObjectIdentifier;->equals(Lsun/security/util/ObjectIdentifier;)Z
+Lsun/security/util/ObjectIdentifier;->newInternal([I)Lsun/security/util/ObjectIdentifier;
+Lsun/security/util/PropertyExpander;->expand(Ljava/lang/String;)Ljava/lang/String;
+Lsun/security/util/ResourcesMgr;->getString(Ljava/lang/String;)Ljava/lang/String;
+Lsun/security/util/SecurityConstants;->CREATE_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission;
+Lsun/security/util/SecurityConstants;->GET_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission;
+Lsun/security/util/SecurityConstants;->MODIFY_THREADGROUP_PERMISSION:Ljava/lang/RuntimePermission;
+Lsun/security/util/SecurityConstants;->MODIFY_THREAD_PERMISSION:Ljava/lang/RuntimePermission;
+Lsun/security/util/SignatureFileVerifier;->isBlockOrSF(Ljava/lang/String;)Z
+Lsun/security/x509/AccessDescription;-><init>(Lsun/security/util/DerValue;)V
+Lsun/security/x509/AccessDescription;->getAccessLocation()Lsun/security/x509/GeneralName;
+Lsun/security/x509/AccessDescription;->getAccessMethod()Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AlgorithmId;-><init>()V
+Lsun/security/x509/AlgorithmId;-><init>(Lsun/security/util/ObjectIdentifier;)V
+Lsun/security/x509/AlgorithmId;-><init>(Lsun/security/util/ObjectIdentifier;Ljava/security/AlgorithmParameters;)V
+Lsun/security/x509/AlgorithmId;->derEncode(Ljava/io/OutputStream;)V
+Lsun/security/x509/AlgorithmId;->DSA_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AlgorithmId;->EC_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AlgorithmId;->encode()[B
+Lsun/security/x509/AlgorithmId;->encode(Lsun/security/util/DerOutputStream;)V
+Lsun/security/x509/AlgorithmId;->equals(Lsun/security/x509/AlgorithmId;)Z
+Lsun/security/x509/AlgorithmId;->getAlgorithmId(Ljava/lang/String;)Lsun/security/x509/AlgorithmId;
+Lsun/security/x509/AlgorithmId;->getDigAlgFromSigAlg(Ljava/lang/String;)Ljava/lang/String;
+Lsun/security/x509/AlgorithmId;->getEncAlgFromSigAlg(Ljava/lang/String;)Ljava/lang/String;
+Lsun/security/x509/AlgorithmId;->getEncodedParams()[B
+Lsun/security/x509/AlgorithmId;->getOID()Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AlgorithmId;->getParameters()Ljava/security/AlgorithmParameters;
+Lsun/security/x509/AlgorithmId;->MD2_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AlgorithmId;->MD5_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AlgorithmId;->params:Lsun/security/util/DerValue;
+Lsun/security/x509/AlgorithmId;->parse(Lsun/security/util/DerValue;)Lsun/security/x509/AlgorithmId;
+Lsun/security/x509/AlgorithmId;->RSAEncryption_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AlgorithmId;->sha1WithRSAEncryption_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AlgorithmId;->SHA256_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AlgorithmId;->SHA384_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AlgorithmId;->SHA512_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AlgorithmId;->SHA_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AttributeNameEnumeration;-><init>()V
+Lsun/security/x509/AVA;-><init>(Lsun/security/util/ObjectIdentifier;Lsun/security/util/DerValue;)V
+Lsun/security/x509/AVA;->getDerValue()Lsun/security/util/DerValue;
+Lsun/security/x509/AVA;->getObjectIdentifier()Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AVA;->getValueString()Ljava/lang/String;
+Lsun/security/x509/AVA;->toRFC2253CanonicalString()Ljava/lang/String;
+Lsun/security/x509/AVAComparator;->INSTANCE:Ljava/util/Comparator;
+Lsun/security/x509/AVAKeyword;->getOID(Ljava/lang/String;ILjava/util/Map;)Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AVAKeyword;->isCompliant(I)Z
+Lsun/security/x509/AVAKeyword;->keyword:Ljava/lang/String;
+Lsun/security/x509/AVAKeyword;->keywordMap:Ljava/util/Map;
+Lsun/security/x509/AVAKeyword;->oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/AVAKeyword;->oidMap:Ljava/util/Map;
+Lsun/security/x509/CertificateAlgorithmId;-><init>(Lsun/security/x509/AlgorithmId;)V
+Lsun/security/x509/CertificateExtensions;-><init>()V
+Lsun/security/x509/CertificateExtensions;-><init>(Lsun/security/util/DerInputStream;)V
+Lsun/security/x509/CertificateExtensions;->encode(Ljava/io/OutputStream;Z)V
+Lsun/security/x509/CertificateExtensions;->get(Ljava/lang/String;)Ljava/lang/Object;
+Lsun/security/x509/CertificateExtensions;->set(Ljava/lang/String;Ljava/lang/Object;)V
+Lsun/security/x509/CertificateIssuerName;-><init>(Lsun/security/x509/X500Name;)V
+Lsun/security/x509/CertificateSerialNumber;-><init>(I)V
+Lsun/security/x509/CertificateSerialNumber;-><init>(Ljava/math/BigInteger;)V
+Lsun/security/x509/CertificateSubjectName;-><init>(Lsun/security/x509/X500Name;)V
+Lsun/security/x509/CertificateSubjectName;->get(Ljava/lang/String;)Ljava/lang/Object;
+Lsun/security/x509/CertificateValidity;-><init>(Ljava/util/Date;Ljava/util/Date;)V
+Lsun/security/x509/CertificateVersion;-><init>(I)V
+Lsun/security/x509/CertificateX509Key;-><init>(Ljava/security/PublicKey;)V
+Lsun/security/x509/CRLDistributionPointsExtension;->encodeThis()V
+Lsun/security/x509/CRLNumberExtension;-><init>(Ljava/lang/Boolean;Ljava/lang/Object;)V
+Lsun/security/x509/CRLNumberExtension;->encodeThis()V
+Lsun/security/x509/CRLNumberExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
+Lsun/security/x509/Extension;-><init>(Lsun/security/x509/Extension;)V
+Lsun/security/x509/Extension;->encode(Lsun/security/util/DerOutputStream;)V
+Lsun/security/x509/Extension;->getExtensionId()Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/GeneralName;-><init>(Lsun/security/x509/GeneralNameInterface;)V
+Lsun/security/x509/GeneralName;->getName()Lsun/security/x509/GeneralNameInterface;
+Lsun/security/x509/GeneralName;->getType()I
+Lsun/security/x509/GeneralNames;-><init>()V
+Lsun/security/x509/GeneralNames;-><init>(Lsun/security/util/DerValue;)V
+Lsun/security/x509/GeneralNames;->add(Lsun/security/x509/GeneralName;)Lsun/security/x509/GeneralNames;
+Lsun/security/x509/GeneralNames;->encode(Lsun/security/util/DerOutputStream;)V
+Lsun/security/x509/GeneralNames;->isEmpty()Z
+Lsun/security/x509/KeyIdentifier;-><init>(Ljava/security/PublicKey;)V
+Lsun/security/x509/KeyIdentifier;->getIdentifier()[B
+Lsun/security/x509/KeyIdentifier;->octetString:[B
+Lsun/security/x509/KeyUsageExtension;-><init>([Z)V
+Lsun/security/x509/KeyUsageExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
+Lsun/security/x509/NetscapeCertTypeExtension;-><init>([B)V
+Lsun/security/x509/NetscapeCertTypeExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
+Lsun/security/x509/OIDMap$OIDInfo;->clazz:Ljava/lang/Class;
+Lsun/security/x509/OIDMap;->getClass(Lsun/security/util/ObjectIdentifier;)Ljava/lang/Class;
+Lsun/security/x509/OIDMap;->nameMap:Ljava/util/Map;
+Lsun/security/x509/OIDMap;->oidMap:Ljava/util/Map;
+Lsun/security/x509/PKIXExtensions;->CertificateIssuer_Id:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/SerialNumber;-><init>(Lsun/security/util/DerValue;)V
+Lsun/security/x509/SubjectAlternativeNameExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
+Lsun/security/x509/SubjectKeyIdentifierExtension;-><init>([B)V
+Lsun/security/x509/UniqueIdentity;-><init>(Lsun/security/util/DerInputStream;)V
+Lsun/security/x509/UniqueIdentity;-><init>(Lsun/security/util/DerValue;)V
+Lsun/security/x509/UniqueIdentity;->encode(Lsun/security/util/DerOutputStream;B)V
+Lsun/security/x509/URIName;->getName()Ljava/lang/String;
+Lsun/security/x509/URIName;->getScheme()Ljava/lang/String;
+Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;)V
+Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;)V
+Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+Lsun/security/x509/X500Name;-><init>(Lsun/security/util/DerInputStream;)V
+Lsun/security/x509/X500Name;-><init>(Lsun/security/util/DerValue;)V
+Lsun/security/x509/X500Name;-><init>([B)V
+Lsun/security/x509/X500Name;->allAvas()Ljava/util/List;
+Lsun/security/x509/X500Name;->asX500Name(Ljavax/security/auth/x500/X500Principal;)Lsun/security/x509/X500Name;
+Lsun/security/x509/X500Name;->asX500Principal()Ljavax/security/auth/x500/X500Principal;
+Lsun/security/x509/X500Name;->commonName_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->countryName_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->DNQUALIFIER_OID:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->DOMAIN_COMPONENT_OID:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->encode(Lsun/security/util/DerOutputStream;)V
+Lsun/security/x509/X500Name;->GENERATIONQUALIFIER_OID:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->getCommonName()Ljava/lang/String;
+Lsun/security/x509/X500Name;->GIVENNAME_OID:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->INITIALS_OID:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->ipAddress_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->isEmpty()Z
+Lsun/security/x509/X500Name;->localityName_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->orgName_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->orgUnitName_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->SERIALNUMBER_OID:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->stateName_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->streetAddress_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->SURNAME_OID:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->title_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X500Name;->userid_oid:Lsun/security/util/ObjectIdentifier;
+Lsun/security/x509/X509CertImpl;-><init>(Lsun/security/util/DerValue;)V
+Lsun/security/x509/X509CertImpl;-><init>(Lsun/security/x509/X509CertInfo;)V
+Lsun/security/x509/X509CertImpl;-><init>([B)V
+Lsun/security/x509/X509CertImpl;->algId:Lsun/security/x509/AlgorithmId;
+Lsun/security/x509/X509CertImpl;->get(Ljava/lang/String;)Ljava/lang/Object;
+Lsun/security/x509/X509CertImpl;->getEncodedInternal()[B
+Lsun/security/x509/X509CertImpl;->parse(Lsun/security/util/DerValue;)V
+Lsun/security/x509/X509CertImpl;->readOnly:Z
+Lsun/security/x509/X509CertImpl;->sign(Ljava/security/PrivateKey;Ljava/lang/String;)V
+Lsun/security/x509/X509CertImpl;->signature:[B
+Lsun/security/x509/X509CertImpl;->signedCert:[B
+Lsun/security/x509/X509CertInfo;-><init>()V
+Lsun/security/x509/X509CertInfo;-><init>([B)V
+Lsun/security/x509/X509CertInfo;->get(Ljava/lang/String;)Ljava/lang/Object;
+Lsun/security/x509/X509CertInfo;->set(Ljava/lang/String;Ljava/lang/Object;)V
+Lsun/security/x509/X509CRLEntryImpl;->getExtension(Lsun/security/util/ObjectIdentifier;)Lsun/security/x509/Extension;
+Lsun/security/x509/X509CRLImpl;-><init>(Ljava/io/InputStream;)V
+Lsun/security/x509/X509CRLImpl;-><init>(Lsun/security/util/DerValue;)V
+Lsun/security/x509/X509CRLImpl;-><init>([B)V
+Lsun/security/x509/X509CRLImpl;->getEncodedInternal()[B
+Lsun/security/x509/X509Key;-><init>()V
+Lsun/security/x509/X509Key;->algid:Lsun/security/x509/AlgorithmId;
+Lsun/security/x509/X509Key;->encodedKey:[B
+Lsun/security/x509/X509Key;->key:[B
+Lsun/security/x509/X509Key;->parse(Lsun/security/util/DerValue;)Ljava/security/PublicKey;
+Lsun/security/x509/X509Key;->unusedBits:I
+Lsun/util/calendar/AbstractCalendar;->getDayOfWeekDateOnOrBefore(JI)J
+Lsun/util/calendar/AbstractCalendar;->getTimeOfDayValue(Lsun/util/calendar/CalendarDate;)J
+Lsun/util/calendar/BaseCalendar$Date;->getNormalizedYear()I
+Lsun/util/calendar/BaseCalendar$Date;->setNormalizedYear(I)V
+Lsun/util/calendar/CalendarDate;->getDayOfMonth()I
+Lsun/util/calendar/CalendarDate;->getMonth()I
+Lsun/util/calendar/CalendarDate;->getTimeOfDay()J
+Lsun/util/calendar/CalendarDate;->getYear()I
+Lsun/util/calendar/CalendarDate;->setDate(III)Lsun/util/calendar/CalendarDate;
+Lsun/util/calendar/CalendarDate;->setDayOfMonth(I)Lsun/util/calendar/CalendarDate;
+Lsun/util/calendar/CalendarDate;->setHours(I)Lsun/util/calendar/CalendarDate;
+Lsun/util/calendar/CalendarDate;->setMillis(I)Lsun/util/calendar/CalendarDate;
+Lsun/util/calendar/CalendarDate;->setMinutes(I)Lsun/util/calendar/CalendarDate;
+Lsun/util/calendar/CalendarDate;->setSeconds(I)Lsun/util/calendar/CalendarDate;
+Lsun/util/calendar/CalendarSystem;->forName(Ljava/lang/String;)Lsun/util/calendar/CalendarSystem;
+Lsun/util/calendar/CalendarSystem;->getGregorianCalendar()Lsun/util/calendar/Gregorian;
+Lsun/util/calendar/CalendarSystem;->getTime(Lsun/util/calendar/CalendarDate;)J
+Lsun/util/calendar/CalendarSystem;->newCalendarDate(Ljava/util/TimeZone;)Lsun/util/calendar/CalendarDate;
+Lsun/util/calendar/CalendarSystem;->validate(Lsun/util/calendar/CalendarDate;)Z
+Lsun/util/calendar/CalendarUtils;->floorDivide(II)I
+Lsun/util/calendar/CalendarUtils;->floorDivide(JJ)J
+Lsun/util/calendar/CalendarUtils;->mod(II)I
+Lsun/util/calendar/CalendarUtils;->mod(JJ)J
+Lsun/util/calendar/Era;-><init>(Ljava/lang/String;Ljava/lang/String;JZ)V
+Lsun/util/calendar/Era;->getAbbreviation()Ljava/lang/String;
+Lsun/util/calendar/Era;->getName()Ljava/lang/String;
+Lsun/util/calendar/Era;->getSinceDate()Lsun/util/calendar/CalendarDate;
+Lsun/util/calendar/ImmutableGregorianDate;->unsupported()V
+Lsun/util/calendar/LocalGregorianCalendar$Date;->getNormalizedYear()I
+Lsun/util/calendar/LocalGregorianCalendar$Date;->setEra(Lsun/util/calendar/Era;)Lsun/util/calendar/LocalGregorianCalendar$Date;
+Lsun/util/calendar/LocalGregorianCalendar$Date;->setNormalizedYear(I)V
+Lsun/util/calendar/LocalGregorianCalendar$Date;->setYear(I)Lsun/util/calendar/LocalGregorianCalendar$Date;
+Lsun/util/calendar/LocalGregorianCalendar;->newCalendarDate(Ljava/util/TimeZone;)Lsun/util/calendar/LocalGregorianCalendar$Date;
+Lsun/util/calendar/LocalGregorianCalendar;->normalize(Lsun/util/calendar/CalendarDate;)Z
+Lsun/util/calendar/LocalGregorianCalendar;->validate(Lsun/util/calendar/CalendarDate;)Z
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index e5e64d3..575ba34 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -120,7 +120,6 @@
 Landroid/os/SystemVibrator;-><init>()V
 Landroid/os/UserHandle;->isSameApp(II)Z
 Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
-Landroid/os/UserManager;->isAdminUser()Z
 Landroid/R$styleable;->CheckBoxPreference:[I
 Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
 Landroid/telephony/ims/compat/ImsService;-><init>()V
@@ -246,7 +245,3 @@
 Lcom/android/internal/R$styleable;->NumberPicker:[I
 Lcom/android/internal/R$styleable;->TwoLineListItem:[I
 Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z
-Ljava/lang/System;->arraycopy([BI[BII)V
-Ljava/net/Inet4Address;->ALL:Ljava/net/InetAddress;
-Ljava/net/Inet4Address;->ANY:Ljava/net/InetAddress;
-Ljava/net/Inet6Address;->ANY:Ljava/net/InetAddress;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 95bcf0e..b6bff9c 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -1240,6 +1240,12 @@
 android.graphics.pdf.PdfDocument
 android.graphics.pdf.PdfEditor
 android.graphics.pdf.PdfRenderer
+android.graphics.text.LineBreaker
+android.graphics.text.LineBreaker$Builder
+android.graphics.text.LineBreaker$ParagraphConstraints
+android.graphics.text.LineBreaker$Result
+android.graphics.text.MeasuredText
+android.graphics.text.MeasuredText$Builder
 android.hardware.Camera
 android.hardware.Camera$CameraInfo
 android.hardware.Camera$Face
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index d7cca15..997ed25 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -76,7 +76,8 @@
  * @attr ref android.R.styleable#AccessibilityService_notificationTimeout
  * @attr ref android.R.styleable#AccessibilityService_packageNames
  * @attr ref android.R.styleable#AccessibilityService_settingsActivity
- * @attr ref android.R.styleable#AccessibilityService_minimumUiTimeout
+ * @attr ref android.R.styleable#AccessibilityService_nonInteractiveUiTimeout
+ * @attr ref android.R.styleable#AccessibilityService_interactiveUiTimeout
  * @see AccessibilityService
  * @see android.view.accessibility.AccessibilityEvent
  * @see android.view.accessibility.AccessibilityManager
@@ -434,11 +435,14 @@
     public boolean crashed;
 
     /**
-     * The minimum timeout in milliseconds that UI controls need to remain on the screen.
-     *
-     * @see #setMinimumUiTimeoutMillis
+     * A recommended timeout in milliseconds for non-interactive controls.
      */
-    private int mMinimumUiTimeout;
+    private int mNonInteractiveUiTimeout;
+
+    /**
+     * A recommended timeout in milliseconds for interactive controls.
+     */
+    private int mInteractiveUiTimeout;
 
     /**
      * The component name the accessibility service.
@@ -544,8 +548,11 @@
             notificationTimeout = asAttributes.getInt(
                     com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
                     0);
-            mMinimumUiTimeout = asAttributes.getInt(
-                    com.android.internal.R.styleable.AccessibilityService_minimumUiTimeout,
+            mNonInteractiveUiTimeout = asAttributes.getInt(
+                    com.android.internal.R.styleable.AccessibilityService_nonInteractiveUiTimeout,
+                    0);
+            mInteractiveUiTimeout = asAttributes.getInt(
+                    com.android.internal.R.styleable.AccessibilityService_interactiveUiTimeout,
                     0);
             flags = asAttributes.getInt(
                     com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
@@ -616,7 +623,8 @@
         packageNames = other.packageNames;
         feedbackType = other.feedbackType;
         notificationTimeout = other.notificationTimeout;
-        mMinimumUiTimeout = other.mMinimumUiTimeout;
+        mNonInteractiveUiTimeout = other.mNonInteractiveUiTimeout;
+        mInteractiveUiTimeout = other.mInteractiveUiTimeout;
         flags = other.flags;
     }
 
@@ -775,26 +783,57 @@
     }
 
     /**
-     * Set the minimum time that controls need to remain on the screen to support the user.
+     * Set the recommended time that non-interactive controls need to remain on the screen to
+     * support the user.
      * <p>
-     *    <strong>This value can be dynamically set at runtime by
-     *    {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}.</strong>
+     *     <strong>This value can be dynamically set at runtime by
+     *     {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}.</strong>
      * </p>
      *
      * @param timeout The timeout in milliseconds.
+     *
+     * @see android.R.styleable#AccessibilityService_nonInteractiveUiTimeout
      */
-    public void setMinimumUiTimeoutMillis(int timeout) {
-        mMinimumUiTimeout = timeout;
+    public void setNonInteractiveUiTimeoutMillis(int timeout) {
+        mNonInteractiveUiTimeout = timeout;
     }
 
     /**
-     * Get the minimum ui timeout.
+     * Get the recommended timeout for non-interactive controls.
      *
-     * @see #setMinimumUiTimeoutMillis
      * @return The timeout in milliseconds.
+     *
+     * @see #setNonInteractiveUiTimeoutMillis(int)
      */
-    public int getMinimumUiTimeoutMillis() {
-        return mMinimumUiTimeout;
+    public int getNonInteractiveUiTimeoutMillis() {
+        return mNonInteractiveUiTimeout;
+    }
+
+    /**
+     * Set the recommended time that interactive controls need to remain on the screen to
+     * support the user.
+     * <p>
+     *     <strong>This value can be dynamically set at runtime by
+     *     {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}.</strong>
+     * </p>
+     *
+     * @param timeout The timeout in milliseconds.
+     *
+     * @see android.R.styleable#AccessibilityService_interactiveUiTimeout
+     */
+    public void setInteractiveUiTimeoutMillis(int timeout) {
+        mInteractiveUiTimeout = timeout;
+    }
+
+    /**
+     * Get the recommended timeout for interactive controls.
+     *
+     * @return The timeout in milliseconds.
+     *
+     * @see #setInteractiveUiTimeoutMillis(int)
+     */
+    public int getInteractiveUiTimeoutMillis() {
+        return mInteractiveUiTimeout;
     }
 
     /** {@hide} */
@@ -815,7 +854,8 @@
         parcel.writeStringArray(packageNames);
         parcel.writeInt(feedbackType);
         parcel.writeLong(notificationTimeout);
-        parcel.writeInt(mMinimumUiTimeout);
+        parcel.writeInt(mNonInteractiveUiTimeout);
+        parcel.writeInt(mInteractiveUiTimeout);
         parcel.writeInt(flags);
         parcel.writeInt(crashed ? 1 : 0);
         parcel.writeParcelable(mComponentName, flagz);
@@ -833,7 +873,8 @@
         packageNames = parcel.readStringArray();
         feedbackType = parcel.readInt();
         notificationTimeout = parcel.readLong();
-        mMinimumUiTimeout = parcel.readInt();
+        mNonInteractiveUiTimeout = parcel.readInt();
+        mInteractiveUiTimeout = parcel.readInt();
         flags = parcel.readInt();
         crashed = parcel.readInt() != 0;
         mComponentName = parcel.readParcelable(this.getClass().getClassLoader());
@@ -884,7 +925,9 @@
         stringBuilder.append(", ");
         stringBuilder.append("notificationTimeout: ").append(notificationTimeout);
         stringBuilder.append(", ");
-        stringBuilder.append("minimumUiTimeout: ").append(mMinimumUiTimeout);
+        stringBuilder.append("nonInteractiveUiTimeout: ").append(mNonInteractiveUiTimeout);
+        stringBuilder.append(", ");
+        stringBuilder.append("interactiveUiTimeout: ").append(mInteractiveUiTimeout);
         stringBuilder.append(", ");
         appendFlags(stringBuilder, flags);
         stringBuilder.append(", ");
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 3189d08..579144b 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -16,17 +16,15 @@
 
 package android.accounts;
 
-import static android.Manifest.permission.GET_ACCOUNTS;
-
+import android.annotation.BroadcastBehavior;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.Size;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.BroadcastBehavior;
 import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.content.BroadcastReceiver;
@@ -36,27 +34,24 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.res.Resources;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
 import android.database.SQLException;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcelable;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.R;
+
 import com.google.android.collect.Maps;
 
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.lang.SuppressWarnings;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -510,7 +505,7 @@
      * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}.
      *
      * @param account The account to query for user data
-     * @return The user data, null if the account or key doesn't exist
+     * @return The user data, null if the account, key doesn't exist, or the user is locked
      */
     public String getUserData(final Account account, final String key) {
         if (account == null) throw new IllegalArgumentException("account is null");
@@ -880,7 +875,7 @@
      * @param userdata String values to use for the account's userdata, null for
      *            none
      * @return True if the account was successfully added, false if the account
-     *         already exists, the account is null, or another error occurs.
+     *         already exists, the account is null, the user is locked, or another error occurs.
      */
     public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
         if (account == null) throw new IllegalArgumentException("account is null");
@@ -1349,7 +1344,7 @@
      * @param account The account for which an auth token is to be fetched. Cannot be {@code null}.
      * @param authTokenType The type of auth token to fetch. Cannot be {@code null}.
      * @return The cached auth token for this account and type, or null if
-     *     no auth token is cached or the account does not exist.
+     *     no auth token is cached, the account does not exist, or the user is locked
      * @see #getAuthToken
      */
     public String peekAuthToken(final Account account, final String authTokenType) {
@@ -1420,7 +1415,7 @@
     }
 
     /**
-     * Sets one userdata key for an account.  Intended by use for the
+     * Sets one userdata key for an account. Intended by use for the
      * authenticator to stash state for itself, not directly by applications.
      * The meaning of the keys and values is up to the authenticator.
      *
@@ -3082,7 +3077,7 @@
     }
 
     /**
-     * Asks the user to enter a new password for an account but not updating the
+     * Asks the user to enter a new password for the account but not updating the
      * saved credentials for the account until {@link #finishSession} is called.
      * <p>
      * This method may be called from any thread, but the returned
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3cc5e37..86ed267 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -120,6 +120,8 @@
 import android.view.autofill.AutofillManager.AutofillClient;
 import android.view.autofill.AutofillPopupWindow;
 import android.view.autofill.IAutofillWindowPresenter;
+import android.view.intelligence.ContentCaptureEvent;
+import android.view.intelligence.IntelligenceManager;
 import android.widget.AdapterView;
 import android.widget.Toast;
 import android.widget.Toolbar;
@@ -131,8 +133,6 @@
 import com.android.internal.app.WindowDecorActionBar;
 import com.android.internal.policy.PhoneWindow;
 
-import dalvik.system.VMRuntime;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -823,6 +823,10 @@
     /** The autofill manager. Always access via {@link #getAutofillManager()}. */
     @Nullable private AutofillManager mAutofillManager;
 
+    /** The screen observation manager. Always access via {@link #getIntelligenceManager()}. */
+    @Nullable private IntelligenceManager mIntelligenceManager;
+
+
     static final class NonConfigurationInstances {
         Object activity;
         HashMap<String, Object> children;
@@ -996,7 +1000,7 @@
     }
 
     /**
-     * (Create and) return the autofill manager
+     * (Creates, sets and) returns the autofill manager
      *
      * @return The autofill manager
      */
@@ -1008,6 +1012,43 @@
         return mAutofillManager;
     }
 
+    /**
+     * (Creates, sets, and ) returns the intelligence manager
+     *
+     * @return The intelligence manager
+     */
+    @NonNull private IntelligenceManager getIntelligenceManager() {
+        if (mIntelligenceManager == null) {
+            mIntelligenceManager = getSystemService(IntelligenceManager.class);
+        }
+        return mIntelligenceManager;
+    }
+
+    private void notifyIntelligenceManagerIfNeeded(@ContentCaptureEvent.EventType int event) {
+        final IntelligenceManager im = getIntelligenceManager();
+        if (im == null || !im.isContentCaptureEnabled()) {
+            return;
+        }
+        switch (event) {
+            case ContentCaptureEvent.TYPE_ACTIVITY_CREATED:
+                //TODO(b/111276913): decide whether the InteractionSessionId should be
+                // saved / restored in the activity bundle.
+                im.onActivityCreated(mToken, getComponentName());
+                break;
+            case ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED:
+                im.onActivityDestroyed();
+                break;
+            case ContentCaptureEvent.TYPE_ACTIVITY_STARTED:
+            case ContentCaptureEvent.TYPE_ACTIVITY_RESUMED:
+            case ContentCaptureEvent.TYPE_ACTIVITY_PAUSED:
+            case ContentCaptureEvent.TYPE_ACTIVITY_STOPPED:
+                im.onActivityLifecycleEvent(event);
+                break;
+            default:
+                Log.w(TAG, "notifyIntelligenceManagerIfNeeded(): invalid type " + event);
+        }
+    }
+
     @Override
     protected void attachBaseContext(Context newBase) {
         super.attachBaseContext(newBase);
@@ -1083,6 +1124,8 @@
         }
         mRestoredFromBundle = savedInstanceState != null;
         mCalled = true;
+
+        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_CREATED);
     }
 
     /**
@@ -1316,6 +1359,7 @@
         if (mAutoFillResetNeeded) {
             getAutofillManager().onVisibleForAutofill();
         }
+        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STARTED);
     }
 
     /**
@@ -1398,6 +1442,7 @@
                 }
             }
         }
+        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_RESUMED);
         mCalled = true;
     }
 
@@ -1791,6 +1836,7 @@
                 mAutoFillIgnoreFirstResumePause = false;
             }
         }
+        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_PAUSED);
         mCalled = true;
     }
 
@@ -1979,6 +2025,7 @@
                 getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
                         mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
             }
+            notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STOPPED);
         }
     }
 
@@ -2049,6 +2096,9 @@
         }
 
         getApplication().dispatchActivityDestroyed(this);
+
+        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
+
     }
 
     /**
@@ -6405,9 +6455,16 @@
 
     void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd,
             @NonNull PrintWriter writer, @Nullable String[] args) {
-        if (args != null && args.length > 0 && args[0].equals("--autofill")) {
-            dumpAutofillManager(prefix, writer);
-            return;
+        if (args != null && args.length > 0) {
+            // Handle special cases
+            switch (args[0]) {
+                case "--autofill":
+                    dumpAutofillManager(prefix, writer);
+                    return;
+                case "--intelligence":
+                    dumpIntelligenceManager(prefix, writer);
+                    return;
+            }
         }
         writer.print(prefix); writer.print("Local Activity ");
                 writer.print(Integer.toHexString(System.identityHashCode(this)));
@@ -6437,6 +6494,7 @@
         mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
 
         dumpAutofillManager(prefix, writer);
+        dumpIntelligenceManager(prefix, writer);
 
         ResourcesManager.getInstance().dump(prefix, writer);
     }
@@ -6452,6 +6510,15 @@
         }
     }
 
+    void dumpIntelligenceManager(String prefix, PrintWriter writer) {
+        final IntelligenceManager im = getIntelligenceManager();
+        if (im != null) {
+            im.dump(prefix, writer);
+        } else {
+            writer.print(prefix); writer.println("No IntelligenceManager");
+        }
+    }
+
     /**
      * Bit indicating that this activity is "immersive" and should not be
      * interrupted by notifications if possible.
@@ -7272,31 +7339,6 @@
             }
         }
 
-        // This property is set for all non-user builds except final release
-        boolean isApiWarningEnabled = SystemProperties.getInt("ro.art.hiddenapi.warning", 0) == 1;
-
-        if (isAppDebuggable || isApiWarningEnabled) {
-            if (!mMainThread.mHiddenApiWarningShown && VMRuntime.getRuntime().hasUsedHiddenApi()) {
-                // Only show the warning once per process.
-                mMainThread.mHiddenApiWarningShown = true;
-
-                String appName = getApplicationInfo().loadLabel(getPackageManager())
-                        .toString();
-                String warning = "Detected problems with API compatibility\n"
-                                 + "(visit g.co/dev/appcompat for more info)";
-                if (isAppDebuggable) {
-                    new AlertDialog.Builder(this)
-                        .setTitle(appName)
-                        .setMessage(warning)
-                        .setPositiveButton(android.R.string.ok, null)
-                        .setCancelable(false)
-                        .show();
-                } else {
-                    Toast.makeText(this, appName + "\n" + warning, Toast.LENGTH_LONG).show();
-                }
-            }
-        }
-
         mActivityTransitionState.enterReady(this);
         getApplication().dispatchActivityPostStarted(this);
     }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4756bf4..9079f1a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -23,6 +23,8 @@
 import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
 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.view.Display.INVALID_DISPLAY;
 
 import android.annotation.NonNull;
@@ -45,6 +47,7 @@
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentProvider;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IContentProvider;
 import android.content.IIntentReceiver;
@@ -84,6 +87,7 @@
 import android.os.Debug;
 import android.os.DropBoxManager;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.GraphicsEnvironment;
 import android.os.Handler;
 import android.os.HandlerExecutor;
@@ -114,6 +118,9 @@
 import android.renderscript.RenderScriptCacheDir;
 import android.security.NetworkSecurityPolicy;
 import android.security.net.config.NetworkSecurityConfigProvider;
+import android.system.ErrnoException;
+import android.system.OsConstants;
+import android.system.StructStat;
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
@@ -162,13 +169,16 @@
 
 import libcore.io.DropBox;
 import libcore.io.EventLogger;
+import libcore.io.ForwardingOs;
 import libcore.io.IoUtils;
+import libcore.io.Os;
 import libcore.net.event.NetworkEventDispatcher;
 
 import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
 
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -392,6 +402,9 @@
         = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
 
     final GcIdler mGcIdler = new GcIdler();
+    final PurgeIdler mPurgeIdler = new PurgeIdler();
+
+    boolean mPurgeIdlerScheduled = false;
     boolean mGcIdlerScheduled = false;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -1681,6 +1694,7 @@
         public static final int RUN_ISOLATED_ENTRY_POINT = 158;
         public static final int EXECUTE_TRANSACTION = 159;
         public static final int RELAUNCH_ACTIVITY = 160;
+        public static final int PURGE_RESOURCES = 161;
 
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
@@ -1724,6 +1738,7 @@
                     case RUN_ISOLATED_ENTRY_POINT: return "RUN_ISOLATED_ENTRY_POINT";
                     case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION";
                     case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
+                    case PURGE_RESOURCES: return "PURGE_RESOURCES";
                 }
             }
             return Integer.toString(code);
@@ -1761,6 +1776,7 @@
                 case UNBIND_SERVICE:
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
                     handleUnbindService((BindServiceData)msg.obj);
+                    schedulePurgeIdler();
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case SERVICE_ARGS:
@@ -1771,6 +1787,7 @@
                 case STOP_SERVICE:
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
                     handleStopService((IBinder)msg.obj);
+                    schedulePurgeIdler();
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case CONFIGURATION_CHANGED:
@@ -1904,6 +1921,9 @@
                 case RELAUNCH_ACTIVITY:
                     handleRelaunchActivityLocally((IBinder) msg.obj);
                     break;
+                case PURGE_RESOURCES:
+                    schedulePurgeIdler();
+                    break;
             }
             Object obj = msg.obj;
             if (obj instanceof SomeArgs) {
@@ -1956,6 +1976,17 @@
         @Override
         public final boolean queueIdle() {
             doGcIfNeeded();
+            nPurgePendingResources();
+            return false;
+        }
+    }
+
+    final class PurgeIdler implements MessageQueue.IdleHandler {
+        @Override
+        public boolean queueIdle() {
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "purgePendingResources");
+            nPurgePendingResources();
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             return false;
         }
     }
@@ -2285,6 +2316,22 @@
         mH.removeMessages(H.GC_WHEN_IDLE);
     }
 
+    void schedulePurgeIdler() {
+        if (!mPurgeIdlerScheduled) {
+            mPurgeIdlerScheduled = true;
+            Looper.myQueue().addIdleHandler(mPurgeIdler);
+        }
+        mH.removeMessages(H.PURGE_RESOURCES);
+    }
+
+    void unschedulePurgeIdler() {
+        if (mPurgeIdlerScheduled) {
+            mPurgeIdlerScheduled = false;
+            Looper.myQueue().removeIdleHandler(mPurgeIdler);
+        }
+        mH.removeMessages(H.PURGE_RESOURCES);
+    }
+
     void doGcIfNeeded() {
         mGcIdlerScheduled = false;
         final long now = SystemClock.uptimeMillis();
@@ -4586,6 +4633,7 @@
             }
             r.setState(ON_DESTROY);
         }
+        schedulePurgeIdler();
         mActivities.remove(token);
         StrictMode.decrementExpectedActivityCount(activityClass);
         return r;
@@ -5830,14 +5878,17 @@
             }
         }
 
-        // Allow application-generated systrace messages if we're debuggable.
-        boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-        Trace.setAppTracingAllowed(isAppDebuggable);
-        ThreadedRenderer.setDebuggingEnabled(isAppDebuggable || Build.IS_DEBUGGABLE);
-        if (isAppDebuggable && data.enableBinderTracking) {
+        // Allow binder tracing, and application-generated systrace messages if we're profileable.
+        boolean isAppProfileable = data.appInfo.isProfileableByShell();
+        Trace.setAppTracingAllowed(isAppProfileable);
+        if (isAppProfileable && data.enableBinderTracking) {
             Binder.enableTracing();
         }
 
+        // Allow renderer debugging features if we're debuggable.
+        boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+        ThreadedRenderer.setDebuggingEnabled(isAppDebuggable || Build.IS_DEBUGGABLE);
+
         /**
          * Initialize the default http proxy in this process for the reasons we set the time zone.
          */
@@ -6749,7 +6800,7 @@
         }
     }
 
-    private class DropBoxReporter implements DropBox.Reporter {
+    private static class DropBoxReporter implements DropBox.Reporter {
 
         private DropBoxManager dropBox;
 
@@ -6769,7 +6820,84 @@
 
         private synchronized void ensureInitialized() {
             if (dropBox == null) {
-                dropBox = (DropBoxManager) getSystemContext().getSystemService(Context.DROPBOX_SERVICE);
+                dropBox = currentActivityThread().getApplication()
+                        .getSystemService(DropBoxManager.class);
+            }
+        }
+    }
+
+    private static class AndroidOs extends ForwardingOs {
+        /**
+         * Install selective syscall interception. For example, this is used to
+         * implement special filesystem paths that will be redirected to
+         * {@link ContentResolver#openFileDescriptor(Uri, String)}.
+         */
+        public static void install() {
+            // If feature is disabled, we don't need to install
+            if (!DEPRECATE_DATA_COLUMNS) return;
+
+            // If app is modern enough, we don't need to install
+            if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) return;
+
+            // Install interception and make sure it sticks!
+            Os def = null;
+            do {
+                def = Os.getDefault();
+            } while (!Os.compareAndSetDefault(def, new AndroidOs(def)));
+        }
+
+        private AndroidOs(Os os) {
+            super(os);
+        }
+
+        private FileDescriptor openDeprecatedDataPath(String path, int mode) throws ErrnoException {
+            final Uri uri = ContentResolver.translateDeprecatedDataPath(path);
+            Log.v(TAG, "Redirecting " + path + " to " + uri);
+
+            final ContentResolver cr = currentActivityThread().getApplication()
+                    .getContentResolver();
+            try {
+                final FileDescriptor fd = new FileDescriptor();
+                fd.setInt$(cr.openFileDescriptor(uri,
+                        FileUtils.translateModePosixToString(mode)).detachFd());
+                return fd;
+            } catch (FileNotFoundException e) {
+                throw new ErrnoException(e.getMessage(), OsConstants.ENOENT);
+            }
+        }
+
+        @Override
+        public boolean access(String path, int mode) throws ErrnoException {
+            if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) {
+                // If we opened it okay, then access check succeeded
+                IoUtils.closeQuietly(
+                        openDeprecatedDataPath(path, FileUtils.translateModeAccessToPosix(mode)));
+                return true;
+            } else {
+                return super.access(path, mode);
+            }
+        }
+
+        @Override
+        public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
+            if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) {
+                return openDeprecatedDataPath(path, mode);
+            } else {
+                return super.open(path, flags, mode);
+            }
+        }
+
+        @Override
+        public StructStat stat(String path) throws ErrnoException {
+            if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) {
+                final FileDescriptor fd = openDeprecatedDataPath(path, OsConstants.O_RDONLY);
+                try {
+                    return android.system.Os.fstat(fd);
+                } finally {
+                    IoUtils.closeQuietly(fd);
+                }
+            } else {
+                return super.stat(path);
             }
         }
     }
@@ -6777,6 +6905,9 @@
     public static void main(String[] args) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
 
+        // Install selective syscall interception
+        AndroidOs.install();
+
         // CloseGuard defaults to true and can be quite spammy.  We
         // disable it here, but selectively enable it later (via
         // StrictMode) on debug builds, but using DropBox, not logs.
@@ -6826,6 +6957,6 @@
     }
 
     // ------------------ Regular JNI ------------------------
-
+    private native void nPurgePendingResources();
     private native void nDumpGraphicsInfo(FileDescriptor fd);
 }
diff --git a/core/java/android/app/AppOpsManager.aidl b/core/java/android/app/AppOpsManager.aidl
index 4b97a15..9329fbc 100644
--- a/core/java/android/app/AppOpsManager.aidl
+++ b/core/java/android/app/AppOpsManager.aidl
@@ -18,3 +18,6 @@
 
 parcelable AppOpsManager.PackageOps;
 parcelable AppOpsManager.OpEntry;
+
+parcelable AppOpsManager.HistoricalPackageOps;
+parcelable AppOpsManager.HistoricalOpEntry;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 3b05566..1e2244e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -17,7 +17,9 @@
 package android.app;
 
 import android.Manifest;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -25,6 +27,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
+import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes.AttributeUsage;
 import android.os.Binder;
 import android.os.IBinder;
@@ -40,8 +43,11 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 
@@ -137,22 +143,38 @@
             "foreground",   // MODE_FOREGROUND
     };
 
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "UID_STATE_" }, value = {
+            UID_STATE_PERSISTENT,
+            UID_STATE_TOP,
+            UID_STATE_FOREGROUND_SERVICE,
+            UID_STATE_FOREGROUND,
+            UID_STATE_BACKGROUND,
+            UID_STATE_CACHED
+    })
+    public @interface UidState {}
+
     /**
      * Metrics about an op when its uid is persistent.
      * @hide
      */
+    @SystemApi
     public static final int UID_STATE_PERSISTENT = 0;
 
     /**
      * Metrics about an op when its uid is at the top.
      * @hide
      */
+    @SystemApi
     public static final int UID_STATE_TOP = 1;
 
     /**
      * Metrics about an op when its uid is running a foreground service.
      * @hide
      */
+    @SystemApi
     public static final int UID_STATE_FOREGROUND_SERVICE = 2;
 
     /**
@@ -165,18 +187,21 @@
      * Metrics about an op when its uid is in the foreground for any other reasons.
      * @hide
      */
+    @SystemApi
     public static final int UID_STATE_FOREGROUND = 3;
 
     /**
      * Metrics about an op when its uid is in the background for any reason.
      * @hide
      */
+    @SystemApi
     public static final int UID_STATE_BACKGROUND = 4;
 
     /**
      * Metrics about an op when its uid is cached.
      * @hide
      */
+    @SystemApi
     public static final int UID_STATE_CACHED = 5;
 
     /**
@@ -1285,11 +1310,11 @@
             AppOpsManager.MODE_ALLOWED, // CALL_PHONE
             AppOpsManager.MODE_ALLOWED, // READ_SMS
             AppOpsManager.MODE_IGNORED, // WRITE_SMS
-            AppOpsManager.MODE_DEFAULT, // RECEIVE_SMS
+            AppOpsManager.MODE_ALLOWED, // RECEIVE_SMS
             AppOpsManager.MODE_ALLOWED, // RECEIVE_EMERGENCY_BROADCAST
             AppOpsManager.MODE_ALLOWED, // RECEIVE_MMS
-            AppOpsManager.MODE_DEFAULT, // RECEIVE_WAP_PUSH
-            AppOpsManager.MODE_DEFAULT, // SEND_SMS
+            AppOpsManager.MODE_ALLOWED, // RECEIVE_WAP_PUSH
+            AppOpsManager.MODE_ALLOWED, // SEND_SMS
             AppOpsManager.MODE_ALLOWED, // READ_ICC_SMS
             AppOpsManager.MODE_ALLOWED, // WRITE_ICC_SMS
             AppOpsManager.MODE_DEFAULT, // WRITE_SETTINGS
@@ -1326,7 +1351,7 @@
             AppOpsManager.MODE_ALLOWED, // PROCESS_OUTGOING_CALLS
             AppOpsManager.MODE_ALLOWED, // USE_FINGERPRINT
             AppOpsManager.MODE_ALLOWED, // BODY_SENSORS
-            AppOpsManager.MODE_DEFAULT, // READ_CELL_BROADCASTS
+            AppOpsManager.MODE_ALLOWED, // READ_CELL_BROADCASTS
             AppOpsManager.MODE_ERRORED, // MOCK_LOCATION
             AppOpsManager.MODE_ALLOWED, // READ_EXTERNAL_STORAGE
             AppOpsManager.MODE_ALLOWED, // WRITE_EXTERNAL_STORAGE
@@ -1537,6 +1562,19 @@
     }
 
     /**
+     * Retrieve the permission associated with an operation, or null if there is not one.
+     *
+     * @param op The operation name.
+     *
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public static String opToPermission(@NonNull String op) {
+        return opToPermission(strOpToOp(op));
+    }
+
+    /**
      * Retrieve the user restriction associated with an operation, or null if there is not one.
      * @hide
      */
@@ -1574,6 +1612,20 @@
     }
 
     /**
+     * Retrieve the default mode for the app op.
+     *
+     * @param appOp The app op name
+     *
+     * @return the default mode for the app op
+     *
+     * @hide
+     */
+    @SystemApi
+    public static int opToDefaultMode(@NonNull String appOp) {
+        return opToDefaultMode(strOpToOp(appOp));
+    }
+
+    /**
      * Retrieve the human readable mode.
      * @hide
      */
@@ -1869,6 +1921,377 @@
     }
 
     /**
+     * This class represents historical app op information about a package. The history
+     * is aggregated information about ops for a certain amount of time such
+     * as the times the op was accessed, the times the op was rejected, the total
+     * duration the app op has been accessed.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final class HistoricalPackageOps implements Parcelable {
+        private final int mUid;
+        private final @NonNull String mPackageName;
+        private final @NonNull List<HistoricalOpEntry> mEntries;
+
+        /**
+         * @hide
+         */
+        public HistoricalPackageOps(int uid, @NonNull String packageName) {
+            mUid = uid;
+            mPackageName = packageName;
+            mEntries = new ArrayList<>();
+        }
+
+        HistoricalPackageOps(@NonNull Parcel parcel) {
+            mUid = parcel.readInt();
+            mPackageName = parcel.readString();
+            mEntries = parcel.createTypedArrayList(HistoricalOpEntry.CREATOR);
+        }
+
+        /**
+         * @hide
+         */
+        public void addEntry(@NonNull HistoricalOpEntry entry) {
+            mEntries.add(entry);
+        }
+
+        /**
+         * Gets the package name which the data represents.
+         *
+         * @return The package name which the data represents.
+         */
+        public @NonNull String getPackageName() {
+            return mPackageName;
+        }
+
+        /**
+         *  Gets the UID which the data represents.
+         *
+         * @return The UID which the data represents.
+         */
+        public int getUid() {
+            return mUid;
+        }
+
+        /**
+         * Gets number historical app op entries.
+         *
+         * @return The number historical app op entries.
+         *
+         * @see #getEntryAt(int)
+         */
+        public int getEntryCount() {
+            return mEntries.size();
+        }
+
+        /**
+         * Gets the historical at a given index.
+         *
+         * @param index The index to lookup.
+         *
+         * @return The entry at the given index.
+         *
+         * @see #getEntryCount()
+         */
+        public @NonNull HistoricalOpEntry getEntryAt(int index) {
+            return mEntries.get(index);
+        }
+
+        /**
+         * Gets the historical entry for a given op name.
+         *
+         * @param opName The op name.
+         *
+         * @return The historical entry for that op name.
+         */
+        public @Nullable HistoricalOpEntry getEntry(@NonNull String opName) {
+            final int entryCount = mEntries.size();
+            for (int i = 0; i < entryCount; i++) {
+                final HistoricalOpEntry entry = mEntries.get(i);
+                if (entry.getOp().equals(opName)) {
+                    return entry;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel parcel, int flags) {
+            parcel.writeInt(mUid);
+            parcel.writeString(mPackageName);
+            parcel.writeTypedList(mEntries, flags);
+        }
+
+        public static final Creator<HistoricalPackageOps> CREATOR =
+                new Creator<HistoricalPackageOps>() {
+            @Override
+            public @NonNull HistoricalPackageOps createFromParcel(@NonNull Parcel parcel) {
+                return new HistoricalPackageOps(parcel);
+            }
+
+            @Override
+            public @NonNull HistoricalPackageOps[] newArray(int size) {
+                return new HistoricalPackageOps[size];
+            }
+        };
+    }
+
+    /**
+     * This class represents historical information about an app op. The history
+     * is aggregated information about the op for a certain amount of time such
+     * as the times the op was accessed, the times the op was rejected, the total
+     * duration the app op has been accessed.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final class HistoricalOpEntry implements Parcelable {
+        private final int mOp;
+        private final long[] mAccessCount;
+        private final long[] mRejectCount;
+        private final long[] mAccessDuration;
+
+        /**
+         * @hide
+         */
+        public HistoricalOpEntry(int op) {
+            mOp = op;
+            mAccessCount = new long[_NUM_UID_STATE];
+            mRejectCount = new long[_NUM_UID_STATE];
+            mAccessDuration = new long[_NUM_UID_STATE];
+        }
+
+        HistoricalOpEntry(@NonNull Parcel parcel) {
+            mOp = parcel.readInt();
+            mAccessCount = parcel.createLongArray();
+            mRejectCount = parcel.createLongArray();
+            mAccessDuration = parcel.createLongArray();
+        }
+
+        /**
+         * @hide
+         */
+        public void addEntry(@UidState int uidState, long accessCount,
+                long rejectCount, long accessDuration) {
+            mAccessCount[uidState] = accessCount;
+            mRejectCount[uidState] = rejectCount;
+            mAccessDuration[uidState] = accessDuration;
+        }
+
+        /**
+         * Gets the op name.
+         *
+         * @return The op name.
+         */
+        public @NonNull String getOp() {
+            return sOpToString[mOp];
+        }
+
+        /**
+         * Gets the number times the op was accessed (performed) in the foreground.
+         *
+         * @return The times the op was accessed in the foreground.
+         *
+         * @see #getBackgroundAccessCount()
+         * @see #getAccessCount(int)
+         */
+        public long getForegroundAccessCount() {
+            return sum(mAccessCount, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
+        }
+
+        /**
+         * Gets the number times the op was accessed (performed) in the background.
+         *
+         * @return The times the op was accessed in the background.
+         *
+         * @see #getForegroundAccessCount()
+         * @see #getAccessCount(int)
+         */
+        public long getBackgroundAccessCount() {
+            return sum(mAccessCount, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
+        }
+
+        /**
+         * Gets the number times the op was accessed (performed) for a given uid state.
+         *
+         * @param uidState The UID state for which to query. Could be one of
+         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
+         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
+         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
+         *
+         * @return The times the op was accessed for the given UID state.
+         *
+         * @see #getForegroundAccessCount()
+         * @see #getBackgroundAccessCount()
+         */
+        public long getAccessCount(@UidState int uidState) {
+            return mAccessCount[uidState];
+        }
+
+        /**
+         * Gets the number times the op was rejected in the foreground.
+         *
+         * @return The times the op was rejected in the foreground.
+         *
+         * @see #getBackgroundRejectCount()
+         * @see #getRejectCount(int)
+         */
+        public long getForegroundRejectCount() {
+            return sum(mRejectCount, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
+        }
+
+        /**
+         * Gets the number times the op was rejected in the background.
+         *
+         * @return The times the op was rejected in the background.
+         *
+         * @see #getForegroundRejectCount()
+         * @see #getRejectCount(int)
+         */
+        public long getBackgroundRejectCount() {
+            return sum(mRejectCount, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
+        }
+
+        /**
+         * Gets the number times the op was rejected for a given uid state.
+         *
+         * @param uidState The UID state for which to query. Could be one of
+         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
+         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
+         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
+         *
+         * @return The times the op was rejected for the given UID state.
+         *
+         * @see #getForegroundRejectCount()
+         * @see #getBackgroundRejectCount()
+         */
+        public long getRejectCount(@UidState int uidState) {
+            return mRejectCount[uidState];
+        }
+
+        /**
+         * Gets the total duration the app op was accessed (performed) in the foreground.
+         *
+         * @return The total duration the app op was accessed in the foreground.
+         *
+         * @see #getBackgroundAccessDuration()
+         * @see #getAccessDuration(int)
+         */
+        public long getForegroundAccessDuration() {
+            return sum(mAccessDuration, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
+        }
+
+        /**
+         * Gets the total duration the app op was accessed (performed) in the background.
+         *
+         * @return The total duration the app op was accessed in the background.
+         *
+         * @see #getForegroundAccessDuration()
+         * @see #getAccessDuration(int)
+         */
+        public long getBackgroundAccessDuration() {
+            return sum(mAccessDuration, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
+        }
+
+        /**
+         * Gets the total duration the app op was accessed (performed) for a given UID state.
+         *
+         * @param uidState The UID state for which to query. Could be one of
+         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
+         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
+         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
+         *
+         * @return The total duration the app op was accessed for the given UID state.
+         *
+         * @see #getForegroundAccessDuration()
+         * @see #getBackgroundAccessDuration()
+         */
+        public long getAccessDuration(@UidState int uidState) {
+            return mAccessDuration[uidState];
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeInt(mOp);
+            parcel.writeLongArray(mAccessCount);
+            parcel.writeLongArray(mRejectCount);
+            parcel.writeLongArray(mAccessDuration);
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (other == null || getClass() != other.getClass()) {
+                return false;
+            }
+            final HistoricalOpEntry otherInstance = (HistoricalOpEntry) other;
+            if (mOp != otherInstance.mOp) {
+                return false;
+            }
+            if (!Arrays.equals(mAccessCount, otherInstance.mAccessCount)) {
+                return false;
+            }
+            if (!Arrays.equals(mRejectCount, otherInstance.mRejectCount)) {
+                return false;
+            }
+            return Arrays.equals(mAccessDuration, otherInstance.mAccessDuration);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = mOp;
+            result = 31 * result + Arrays.hashCode(mAccessCount);
+            result = 31 * result + Arrays.hashCode(mRejectCount);
+            result = 31 * result + Arrays.hashCode(mAccessDuration);
+            return result;
+        }
+
+        /**
+         *
+         * Computes the sum given the start and end index.
+         *
+         * @param counts The data array.
+         * @param start The start index (inclusive)
+         * @param end The end index (exclusive)
+         * @return The sum.
+         */
+        private static long sum(@NonNull long[] counts, int start, int end) {
+            long totalCount = 0;
+            for (int i = start; i <= end; i++) {
+                totalCount += counts[i];
+            }
+            return totalCount;
+        }
+
+        public static final Creator<HistoricalOpEntry> CREATOR = new Creator<HistoricalOpEntry>() {
+            @Override
+            public @NonNull HistoricalOpEntry createFromParcel(@NonNull Parcel source) {
+                return new HistoricalOpEntry(source);
+            }
+
+            @Override
+            public @NonNull HistoricalOpEntry[] newArray(int size) {
+                return new HistoricalOpEntry[size];
+            }
+        };
+    }
+
+    /**
      * Callback for notification of changes to operation state.
      */
     public interface OnOpChangedListener {
@@ -1925,6 +2348,30 @@
     }
 
     /**
+     * Retrieve current operation state for all applications.
+     *
+     * @param ops The set of operations you are interested in, or null if you want all of them.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
+    @SystemApi
+    public List<AppOpsManager.PackageOps> getPackagesForOpStrs(String[] ops) {
+        if (ops == null) {
+            return getPackagesForOps(null);
+        }
+        final int[] opCodes = new int[ops.length];
+        for (int i = 0; i < ops.length; ++i) {
+            final Integer opCode = sOpStrToOp.get(ops[i]);
+            if (opCode == null) {
+                opCodes[i] = OP_NONE;
+            } else {
+                opCodes[i] = opCode;
+            }
+        }
+        return getPackagesForOps(opCodes);
+    }
+
+    /**
      * Retrieve current operation state for one application.
      *
      * @param uid The uid of the application of interest.
@@ -1943,6 +2390,74 @@
     }
 
     /**
+     * Retrieve historical app op stats for a package.
+     *
+     * @param uid The UID to query for.
+     * @param packageName The package to query for.
+     * @param beginTimeMillis The beginning of the interval in milliseconds since
+     *     epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be non
+     *     negative.
+     * @param endTimeMillis The end of the interval in milliseconds since
+     *     epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be after
+     *     {@code beginTimeMillis}.
+     * @param opNames The ops to query for. Pass {@code null} for all ops.
+     *
+     * @return The historical ops or {@code null} if there are no ops for this package.
+     *
+     * @throws IllegalArgumentException If any of the argument contracts is violated.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
+    public @Nullable HistoricalPackageOps getHistoricalPackagesOps(
+            int uid, @NonNull String packageName, @Nullable String[] opNames,
+            long beginTimeMillis, long endTimeMillis) {
+        try {
+            return mService.getHistoricalPackagesOps(uid, packageName, opNames,
+                    beginTimeMillis, endTimeMillis);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieve historical app op stats for all packages.
+     *
+     * @param beginTimeMillis The beginning of the interval in milliseconds since
+     *     epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be non
+     *     negative.
+     * @param endTimeMillis The end of the interval in milliseconds since
+     *     epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be after
+     *     {@code beginTimeMillis}.
+     * @param opNames The ops to query for. Pass {@code null} for all ops.
+     *
+     * @return The historical ops or an empty list if there are no ops for any package.
+     *
+     * @throws IllegalArgumentException If any of the argument contracts is violated.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
+    public @NonNull List<HistoricalPackageOps> getAllHistoricPackagesOps(
+            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis) {
+        try {
+            @SuppressWarnings("unchecked")
+            final ParceledListSlice<HistoricalPackageOps> payload =
+                    mService.getAllHistoricalPackagesOps(opNames, beginTimeMillis, endTimeMillis);
+            if (payload != null) {
+                return payload.getList();
+            }
+            return Collections.emptyList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Sets given app op in the specified mode for app ops in the UID.
      * This applies to all apps currently in the UID or installed in
      * this UID in the future.
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index f5d5e6e..7fe21b2 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.NonNull;
 import android.util.SparseIntArray;
 
 import com.android.internal.util.function.QuadFunction;
@@ -73,4 +74,21 @@
      * access to app ops for their user.
      */
     public abstract void setDeviceAndProfileOwners(SparseIntArray owners);
+
+    /**
+     * Sets the app-ops mode for a certain app-op and uid.
+     *
+     * <p>Similar as {@link AppOpsManager#setMode} but does not require the package manager to be
+     * working. Hence this can be used very early during boot.
+     *
+     * <p>Only for internal callers. Does <u>not</u> verify that package name belongs to uid.
+     *
+     * @param code The op code to set.
+     * @param uid The UID for which to set.
+     * @param packageName The package for which to set.
+     * @param mode The new mode to set.
+     * @param isPrivileged If the package is privileged
+     */
+    public abstract void setMode(int code, int uid, @NonNull String packageName, int mode,
+            boolean isPrivileged);
 }
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 62f6bac..e2f2075 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -31,7 +31,10 @@
  * Rule instance information for zen mode.
  */
 public final class AutomaticZenRule implements Parcelable {
-
+    /* @hide */
+    private static final int ENABLED = 1;
+    /* @hide */
+    private static final int DISABLED = 0;
     private boolean enabled = false;
     private String name;
     private @InterruptionFilter int interruptionFilter;
@@ -39,6 +42,7 @@
     private ComponentName owner;
     private long creationTime;
     private ZenPolicy mZenPolicy;
+    private boolean mModified = false;
 
     /**
      * Creates an automatic zen rule.
@@ -101,8 +105,8 @@
     }
 
     public AutomaticZenRule(Parcel source) {
-        enabled = source.readInt() == 1;
-        if (source.readInt() == 1) {
+        enabled = source.readInt() == ENABLED;
+        if (source.readInt() == ENABLED) {
             name = source.readString();
         }
         interruptionFilter = source.readInt();
@@ -110,6 +114,7 @@
         owner = source.readParcelable(null);
         creationTime = source.readLong();
         mZenPolicy = source.readParcelable(null);
+        mModified = source.readInt() == ENABLED;
     }
 
     /**
@@ -148,10 +153,18 @@
     }
 
     /**
+     * Returns whether this rule's name has been modified by the user.
+     * @hide
+     */
+    public boolean isModified() {
+        return mModified;
+    }
+
+    /**
      * Gets the zen policy.
      */
     public ZenPolicy getZenPolicy() {
-        return this.mZenPolicy.copy();
+        return mZenPolicy == null ? null : this.mZenPolicy.copy();
     }
 
     /**
@@ -191,6 +204,14 @@
     }
 
     /**
+     * Sets modified state of this rule.
+     * @hide
+     */
+    public void setModified(boolean modified) {
+        this.mModified = modified;
+    }
+
+    /**
      * Sets the zen policy.
      */
     public void setZenPolicy(ZenPolicy zenPolicy) {
@@ -204,7 +225,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(enabled ? 1 : 0);
+        dest.writeInt(enabled ? ENABLED : DISABLED);
         if (name != null) {
             dest.writeInt(1);
             dest.writeString(name);
@@ -216,6 +237,7 @@
         dest.writeParcelable(owner, 0);
         dest.writeLong(creationTime);
         dest.writeParcelable(mZenPolicy, 0);
+        dest.writeInt(mModified ? ENABLED : DISABLED);
     }
 
     @Override
@@ -237,6 +259,7 @@
         if (o == this) return true;
         final AutomaticZenRule other = (AutomaticZenRule) o;
         return other.enabled == enabled
+                && other.mModified == mModified
                 && Objects.equals(other.name, name)
                 && other.interruptionFilter == interruptionFilter
                 && Objects.equals(other.conditionId, conditionId)
@@ -248,7 +271,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, creationTime,
-                mZenPolicy);
+                mZenPolicy, mModified);
     }
 
     public static final Parcelable.Creator<AutomaticZenRule> CREATOR
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 519a274..e759762 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -265,17 +265,6 @@
     void getMyMemoryState(out ActivityManager.RunningAppProcessInfo outInfo);
     boolean killProcessesBelowForeground(in String reason);
     UserInfo getCurrentUser();
-    /**
-     * Informs ActivityManagerService that the keyguard is showing.
-     *
-     * @param showingKeyguard True if the keyguard is showing, false otherwise.
-     * @param showingAod True if AOD is showing, false otherwise.
-     * @param secondaryDisplayShowing The displayId of the secondary display on which the keyguard
-     *        is showing, or INVALID_DISPLAY if there is no such display. Only meaningful if
-     *        showing is true.
-     */
-    void setLockScreenShown(boolean showingKeyguard, boolean showingAod,
-            int secondaryDisplayShowing);
     // This is not public because you need to be very careful in how you
     // manage your activity to make sure it is always the uid you expect.
     int getLaunchedFromUid(in IBinder activityToken);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index b7b6352..6f11a76 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -177,6 +177,7 @@
     void cancelRecentsAnimation(boolean restoreHomeStackPosition);
     void startLockTaskModeByToken(in IBinder token);
     void stopLockTaskModeByToken(in IBinder token);
+    void updateLockTaskPackages(int userId, in String[] packages);
     boolean isInLockTaskMode();
     int getLockTaskModeState();
     void setTaskDescription(in IBinder token, in ActivityManager.TaskDescription values);
@@ -244,16 +245,16 @@
     ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType);
 
     /**
-     * Informs ActivityManagerService that the keyguard is showing.
+     * Informs ActivityTaskManagerService that the keyguard is showing.
      *
      * @param showingKeyguard True if the keyguard is showing, false otherwise.
      * @param showingAod True if AOD is showing, false otherwise.
-     * @param secondaryDisplayShowing The displayId of the secondary display on which the keyguard
-     *        is showing, or INVALID_DISPLAY if there is no such display. Only meaningful if
-     *        showing is true.
+     * @param secondaryDisplaysShowing The displayId's of the secondary displays on which the
+     * keyguard is showing, or {@code null} if there is no such display. Only meaningful if showing
+     * is {@code true}.
      */
     void setLockScreenShown(boolean showingKeyguard, boolean showingAod,
-            int secondaryDisplayShowing);
+            in int[] secondaryDisplaysShowing);
     Bundle getAssistContextExtras(int requestType);
     boolean launchAssistIntent(in Intent intent, int requestType, in String hint, int userHandle,
             in Bundle args);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 357420b..bd9cf6d 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -146,6 +146,7 @@
 
     int getZenMode();
     ZenModeConfig getZenModeConfig();
+    NotificationManager.Policy getConsolidatedNotificationPolicy();
     oneway void setZenMode(int mode, in Uri conditionId, String reason);
     oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
     boolean isNotificationPolicyAccessGranted(String pkg);
diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java
index 8f83ee3..b1541c6 100644
--- a/core/java/android/app/InstantAppResolverService.java
+++ b/core/java/android/app/InstantAppResolverService.java
@@ -240,7 +240,7 @@
                 args.arg4 = token;
                 args.arg5 = sanitizedIntent;
                 mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER,
-                        callback).sendToTarget();
+                        args).sendToTarget();
             }
         };
     }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4f41da6..6d464fb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1275,6 +1275,8 @@
     private String mShortcutId;
     private CharSequence mSettingsText;
 
+    private PendingIntent mAppOverlayIntent;
+
     /** @hide */
     @IntDef(prefix = { "GROUP_ALERT_" }, value = {
             GROUP_ALERT_ALL, GROUP_ALERT_CHILDREN, GROUP_ALERT_SUMMARY
@@ -2225,6 +2227,9 @@
         }
 
         mGroupAlertBehavior = parcel.readInt();
+        if (parcel.readInt() != 0) {
+            mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel);
+        }
     }
 
     @Override
@@ -2339,6 +2344,7 @@
         that.mBadgeIcon = this.mBadgeIcon;
         that.mSettingsText = this.mSettingsText;
         that.mGroupAlertBehavior = this.mGroupAlertBehavior;
+        that.mAppOverlayIntent = this.mAppOverlayIntent;
 
         if (!heavy) {
             that.lightenPayload(); // will clean out extras
@@ -2660,6 +2666,13 @@
 
         parcel.writeInt(mGroupAlertBehavior);
 
+        if (mAppOverlayIntent != null) {
+            parcel.writeInt(1);
+            mAppOverlayIntent.writeToParcel(parcel, 0);
+        } else {
+            parcel.writeInt(0);
+        }
+
         // mUsesStandardHeader is not written because it should be recomputed in listeners
     }
 
@@ -3073,6 +3086,14 @@
     }
 
     /**
+     * Returns the intent that will be used to display app content in a floating window over the
+     * existing foreground activity.
+     */
+    public PendingIntent getAppOverlayIntent() {
+        return mAppOverlayIntent;
+    }
+
+    /**
      * The small icon representing this notification in the status bar and content view.
      *
      * @return the small icon representing this notification.
@@ -3406,6 +3427,23 @@
             return this;
         }
 
+        /**
+         * Sets the intent that will be used to display app content in a floating window
+         * over the existing foreground activity.
+         *
+         * <p>This intent will be ignored unless this notification is posted to a channel that
+         * allows {@link NotificationChannel#canOverlayApps() app overlays}.</p>
+         *
+         * <p>Notifications with a valid and allowed app overlay intent will be displayed as
+         * floating windows outside of the notification shade on unlocked devices. When a user
+         * interacts with one of these windows, this app overlay intent will be invoked and
+         * displayed.</p>
+         */
+        public Builder setAppOverlayIntent(PendingIntent intent) {
+            mN.mAppOverlayIntent = intent;
+            return this;
+        }
+
         /** @removed */
         @Deprecated
         public Builder setChannel(String channelId) {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 9f93e17..41ceaaf 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,8 @@
  */
 package android.app;
 
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
@@ -41,6 +43,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * A representation of settings that apply to a collection of similarly themed notifications.
@@ -81,6 +84,7 @@
     private static final String ATT_FG_SERVICE_SHOWN = "fgservice";
     private static final String ATT_GROUP = "group";
     private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
+    private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay";
     private static final String DELIMITER = ",";
 
     /**
@@ -116,6 +120,11 @@
     /**
      * @hide
      */
+    public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000100;
+
+    /**
+     * @hide
+     */
     public static final int[] LOCKABLE_FIELDS = new int[] {
             USER_LOCKED_PRIORITY,
             USER_LOCKED_VISIBILITY,
@@ -124,6 +133,7 @@
             USER_LOCKED_VIBRATION,
             USER_LOCKED_SOUND,
             USER_LOCKED_SHOW_BADGE,
+            USER_LOCKED_ALLOW_APP_OVERLAY
     };
 
     private static final int DEFAULT_LIGHT_COLOR = 0;
@@ -133,6 +143,7 @@
             NotificationManager.IMPORTANCE_UNSPECIFIED;
     private static final boolean DEFAULT_DELETED = false;
     private static final boolean DEFAULT_SHOW_BADGE = true;
+    private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
 
     @UnsupportedAppUsage
     private final String mId;
@@ -156,6 +167,7 @@
     private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
     // If this is a blockable system notification channel.
     private boolean mBlockableSystem = false;
+    private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY;
 
     /**
      * Creates a notification channel.
@@ -217,6 +229,7 @@
         mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null;
         mLightColor = in.readInt();
         mBlockableSystem = in.readBoolean();
+        mAllowAppOverlay = in.readBoolean();
     }
 
     @Override
@@ -269,6 +282,7 @@
         }
         dest.writeInt(mLightColor);
         dest.writeBoolean(mBlockableSystem);
+        dest.writeBoolean(mAllowAppOverlay);
     }
 
     /**
@@ -461,6 +475,22 @@
     }
 
     /**
+     * Sets whether notifications posted to this channel can appear outside of the notification
+     * shade, floating over other apps' content.
+     *
+     * <p>This value will be ignored for channels that aren't allowed to pop on screen (that is,
+     * channels whose {@link #getImportance() importance} is <
+     * {@link NotificationManager#IMPORTANCE_HIGH}.</p>
+     *
+     * <p>Only modifiable before the channel is submitted to
+     *      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.</p>
+     * @see Notification#getAppOverlayIntent()
+     */
+    public void setAllowAppOverlay(boolean allowAppOverlay) {
+        mAllowAppOverlay = allowAppOverlay;
+    }
+
+    /**
      * Returns the id of this channel.
      */
     public String getId() {
@@ -573,6 +603,22 @@
     }
 
     /**
+     * Returns whether notifications posted to this channel can display outside of the notification
+     * shade, in a floating window on top of other apps.
+     */
+    public boolean canOverlayApps() {
+        return isAppOverlayAllowed() && getImportance() >= IMPORTANCE_HIGH;
+    }
+
+    /**
+     * Like {@link #canOverlayApps()}, but only checks the permission, not the importance.
+     * @hide
+     */
+    public boolean isAppOverlayAllowed() {
+        return mAllowAppOverlay;
+    }
+
+    /**
      * @hide
      */
     @SystemApi
@@ -605,6 +651,7 @@
     /**
      * Returns whether the user has chosen the importance of this channel, either to affirm the
      * initial selection from the app, or changed it to be higher or lower.
+     * @see #getImportance()
      */
     public boolean hasUserSetImportance() {
         return (mUserLockedFields & USER_LOCKED_IMPORTANCE) != 0;
@@ -652,6 +699,7 @@
         lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
         setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false));
         setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
+        setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
     }
 
     @Nullable
@@ -770,6 +818,9 @@
         if (isBlockableSystem()) {
             out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockableSystem()));
         }
+        if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) {
+            out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps()));
+        }
 
         out.endTag(null, TAG_CHANNEL);
     }
@@ -812,6 +863,7 @@
         record.put(ATT_DELETED, Boolean.toString(isDeleted()));
         record.put(ATT_GROUP, getGroup());
         record.put(ATT_BLOCKABLE_SYSTEM, isBlockableSystem());
+        record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps());
         return record;
     }
 
@@ -899,58 +951,36 @@
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
-
         NotificationChannel that = (NotificationChannel) o;
-
-        if (getImportance() != that.getImportance()) return false;
-        if (mBypassDnd != that.mBypassDnd) return false;
-        if (getLockscreenVisibility() != that.getLockscreenVisibility()) return false;
-        if (mLights != that.mLights) return false;
-        if (getLightColor() != that.getLightColor()) return false;
-        if (getUserLockedFields() != that.getUserLockedFields()) return false;
-        if (mVibrationEnabled != that.mVibrationEnabled) return false;
-        if (mShowBadge != that.mShowBadge) return false;
-        if (isDeleted() != that.isDeleted()) return false;
-        if (isBlockableSystem() != that.isBlockableSystem()) return false;
-        if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
-        if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
-            return false;
-        }
-        if (getDescription() != null ? !getDescription().equals(that.getDescription())
-                : that.getDescription() != null) {
-            return false;
-        }
-        if (getSound() != null ? !getSound().equals(that.getSound()) : that.getSound() != null) {
-            return false;
-        }
-        if (!Arrays.equals(mVibration, that.mVibration)) return false;
-        if (getGroup() != null ? !getGroup().equals(that.getGroup()) : that.getGroup() != null) {
-            return false;
-        }
-        return getAudioAttributes() != null ? getAudioAttributes().equals(that.getAudioAttributes())
-                : that.getAudioAttributes() == null;
-
+        return getImportance() == that.getImportance() &&
+                mBypassDnd == that.mBypassDnd &&
+                getLockscreenVisibility() == that.getLockscreenVisibility() &&
+                mLights == that.mLights &&
+                getLightColor() == that.getLightColor() &&
+                getUserLockedFields() == that.getUserLockedFields() &&
+                isFgServiceShown() == that.isFgServiceShown() &&
+                mVibrationEnabled == that.mVibrationEnabled &&
+                mShowBadge == that.mShowBadge &&
+                isDeleted() == that.isDeleted() &&
+                isBlockableSystem() == that.isBlockableSystem() &&
+                mAllowAppOverlay == that.mAllowAppOverlay &&
+                Objects.equals(getId(), that.getId()) &&
+                Objects.equals(getName(), that.getName()) &&
+                Objects.equals(mDesc, that.mDesc) &&
+                Objects.equals(getSound(), that.getSound()) &&
+                Arrays.equals(mVibration, that.mVibration) &&
+                Objects.equals(getGroup(), that.getGroup()) &&
+                Objects.equals(getAudioAttributes(), that.getAudioAttributes());
     }
 
     @Override
     public int hashCode() {
-        int result = getId() != null ? getId().hashCode() : 0;
-        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
-        result = 31 * result + (getDescription() != null ? getDescription().hashCode() : 0);
-        result = 31 * result + getImportance();
-        result = 31 * result + (mBypassDnd ? 1 : 0);
-        result = 31 * result + getLockscreenVisibility();
-        result = 31 * result + (getSound() != null ? getSound().hashCode() : 0);
-        result = 31 * result + (mLights ? 1 : 0);
-        result = 31 * result + getLightColor();
+        int result = Objects.hash(getId(), getName(), mDesc, getImportance(), mBypassDnd,
+                getLockscreenVisibility(), getSound(), mLights, getLightColor(),
+                getUserLockedFields(),
+                isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
+                getAudioAttributes(), isBlockableSystem(), mAllowAppOverlay);
         result = 31 * result + Arrays.hashCode(mVibration);
-        result = 31 * result + getUserLockedFields();
-        result = 31 * result + (mVibrationEnabled ? 1 : 0);
-        result = 31 * result + (mShowBadge ? 1 : 0);
-        result = 31 * result + (isDeleted() ? 1 : 0);
-        result = 31 * result + (getGroup() != null ? getGroup().hashCode() : 0);
-        result = 31 * result + (getAudioAttributes() != null ? getAudioAttributes().hashCode() : 0);
-        result = 31 * result + (isBlockableSystem() ? 1 : 0);
         return result;
     }
 
@@ -976,6 +1006,7 @@
                 + ", mGroup='" + mGroup + '\''
                 + ", mAudioAttributes=" + mAudioAttributes
                 + ", mBlockableSystem=" + mBlockableSystem
+                + ", mAllowAppOverlay=" + mAllowAppOverlay
                 + '}';
         pw.println(prefix + output);
     }
@@ -1001,6 +1032,7 @@
                 + ", mGroup='" + mGroup + '\''
                 + ", mAudioAttributes=" + mAudioAttributes
                 + ", mBlockableSystem=" + mBlockableSystem
+                + ", mAllowAppOverlay=" + mAllowAppOverlay
                 + '}';
     }
 
@@ -1034,6 +1066,7 @@
             mAudioAttributes.writeToProto(proto, NotificationChannelProto.AUDIO_ATTRIBUTES);
         }
         proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
+        proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowAppOverlay);
 
         proto.end(token);
     }
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 17c5cba..2322a42 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -32,6 +32,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * A grouping of related notification channels. e.g., channels that all belong to a single account.
@@ -49,13 +50,33 @@
     private static final String ATT_DESC = "desc";
     private static final String ATT_ID = "id";
     private static final String ATT_BLOCKED = "blocked";
+    private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay";
+    private static final String ATT_USER_LOCKED = "locked";
 
+    private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
+
+    /**
+     * @hide
+     */
+    public static final int USER_LOCKED_BLOCKED_STATE = 0x00000001;
+    /**
+     * @hide
+     */
+    @TestApi
+    public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000002;
+
+    /**
+     * @see #getId()
+     */
     @UnsupportedAppUsage
     private final String mId;
     private CharSequence mName;
     private String mDescription;
     private boolean mBlocked;
     private List<NotificationChannel> mChannels = new ArrayList<>();
+    private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY;
+    // Bitwise representation of fields that have been changed by the user
+    private int mUserLockedFields;
 
     /**
      * Creates a notification channel group.
@@ -89,6 +110,8 @@
         }
         in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader());
         mBlocked = in.readBoolean();
+        mAllowAppOverlay = in.readBoolean();
+        mUserLockedFields = in.readInt();
     }
 
     private String getTrimmedString(String input) {
@@ -115,6 +138,8 @@
         }
         dest.writeParcelableList(mChannels, flags);
         dest.writeBoolean(mBlocked);
+        dest.writeBoolean(mAllowAppOverlay);
+        dest.writeInt(mUserLockedFields);
     }
 
     /**
@@ -156,6 +181,15 @@
     }
 
     /**
+     * Returns whether notifications posted to this channel group can display outside of the
+     * notification shade, in a floating window on top of other apps. These may additionally be
+     * blocked at the notification channel level, see {@link NotificationChannel#canOverlayApps()}.
+     */
+    public boolean canOverlayApps() {
+        return mAllowAppOverlay;
+    }
+
+    /**
      * Sets the user visible description of this group.
      *
      * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too
@@ -166,6 +200,21 @@
     }
 
     /**
+     * Sets whether notifications posted to this channel group can appear outside of the
+     * notification shade, floating over other apps' content.
+     *
+     * <p>This value will be ignored for notifications that are posted to channels that do not
+     * allow app overlays ({@link NotificationChannel#canOverlayApps()}.
+     *
+     * <p>Only modifiable before the channel is submitted to
+     * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.</p>
+     * @see Notification#getAppOverlayIntent()
+     */
+    public void setAllowAppOverlay(boolean allowAppOverlay) {
+        mAllowAppOverlay = allowAppOverlay;
+    }
+
+    /**
      * @hide
      */
     @TestApi
@@ -190,10 +239,34 @@
     /**
      * @hide
      */
+    @TestApi
+    public void lockFields(int field) {
+        mUserLockedFields |= field;
+    }
+
+    /**
+     * @hide
+     */
+    public void unlockFields(int field) {
+        mUserLockedFields &= ~field;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public int getUserLockedFields() {
+        return mUserLockedFields;
+    }
+
+    /**
+     * @hide
+     */
     public void populateFromXml(XmlPullParser parser) {
         // Name, id, and importance are set in the constructor.
         setDescription(parser.getAttributeValue(null, ATT_DESC));
         setBlocked(safeBool(parser, ATT_BLOCKED, false));
+        setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
     }
 
     private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
@@ -216,6 +289,10 @@
             out.attribute(null, ATT_DESC, getDescription().toString());
         }
         out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked()));
+        if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) {
+            out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps()));
+        }
+        out.attribute(null, ATT_USER_LOCKED, Integer.toString(mUserLockedFields));
 
         out.endTag(null, TAG_GROUP);
     }
@@ -230,6 +307,8 @@
         record.put(ATT_NAME, getName());
         record.put(ATT_DESC, getDescription());
         record.put(ATT_BLOCKED, isBlocked());
+        record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps());
+        record.put(ATT_USER_LOCKED, mUserLockedFields);
         return record;
     }
 
@@ -255,30 +334,20 @@
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
-
         NotificationChannelGroup that = (NotificationChannelGroup) o;
-
-        if (isBlocked() != that.isBlocked()) return false;
-        if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
-        if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
-            return false;
-        }
-        if (getDescription() != null ? !getDescription().equals(that.getDescription())
-                : that.getDescription() != null) {
-            return false;
-        }
-        return getChannels() != null ? getChannels().equals(that.getChannels())
-                : that.getChannels() == null;
+        return isBlocked() == that.isBlocked() &&
+                mAllowAppOverlay == that.mAllowAppOverlay &&
+                mUserLockedFields == that.mUserLockedFields &&
+                Objects.equals(getId(), that.getId()) &&
+                Objects.equals(getName(), that.getName()) &&
+                Objects.equals(getDescription(), that.getDescription()) &&
+                Objects.equals(getChannels(), that.getChannels());
     }
 
     @Override
     public int hashCode() {
-        int result = getId() != null ? getId().hashCode() : 0;
-        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
-        result = 31 * result + (getDescription() != null ? getDescription().hashCode() : 0);
-        result = 31 * result + (isBlocked() ? 1 : 0);
-        result = 31 * result + (getChannels() != null ? getChannels().hashCode() : 0);
-        return result;
+        return Objects.hash(getId(), getName(), getDescription(), isBlocked(), getChannels(),
+                mAllowAppOverlay, mUserLockedFields);
     }
 
     @Override
@@ -287,6 +356,8 @@
         cloned.setDescription(getDescription());
         cloned.setBlocked(isBlocked());
         cloned.setChannels(getChannels());
+        cloned.setAllowAppOverlay(canOverlayApps());
+        cloned.lockFields(mUserLockedFields);
         return cloned;
     }
 
@@ -298,6 +369,8 @@
                 + ", mDescription=" + (!TextUtils.isEmpty(mDescription) ? "hasDescription " : "")
                 + ", mBlocked=" + mBlocked
                 + ", mChannels=" + mChannels
+                + ", mAllowAppOverlay=" + mAllowAppOverlay
+                + ", mUserLockedFields=" + mUserLockedFields
                 + '}';
     }
 
@@ -312,7 +385,7 @@
         for (NotificationChannel channel : mChannels) {
             channel.writeToProto(proto, NotificationChannelGroupProto.CHANNELS);
         }
-
+        proto.write(NotificationChannelGroupProto.ALLOW_APP_OVERLAY, mAllowAppOverlay);
         proto.end(token);
     }
 }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 3f07024..89ec19b 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -825,6 +825,18 @@
     /**
      * @hide
      */
+    public NotificationManager.Policy getConsolidatedNotificationPolicy() {
+        INotificationManager service = getService();
+        try {
+            return service.getConsolidatedNotificationPolicy();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
     public int getRuleInstanceCount(ComponentName owner) {
         INotificationManager service = getService();
         try {
@@ -1062,7 +1074,7 @@
     }
 
     /**
-     * Gets the current notification policy.
+     * Gets the current user-specified default notification policy.
      *
      * <p>
      */
@@ -1621,6 +1633,71 @@
                 return new Policy[size];
             }
         };
+
+        /** @hide **/
+        public boolean allowAlarms() {
+            return (priorityCategories & PRIORITY_CATEGORY_ALARMS) != 0;
+        }
+
+        /** @hide **/
+        public boolean allowMedia() {
+            return (priorityCategories & PRIORITY_CATEGORY_MEDIA) != 0;
+        }
+
+        /** @hide **/
+        public boolean allowSystem() {
+            return (priorityCategories & PRIORITY_CATEGORY_SYSTEM) != 0;
+        }
+
+        /** @hide **/
+        public boolean allowRepeatCallers() {
+            return (priorityCategories & PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
+        }
+
+        /** @hide **/
+        public boolean allowCalls() {
+            return (priorityCategories & PRIORITY_CATEGORY_CALLS) != 0;
+        }
+
+        /** @hide **/
+        public boolean allowMessages() {
+            return (priorityCategories & PRIORITY_CATEGORY_MESSAGES) != 0;
+        }
+
+        /** @hide **/
+        public boolean allowEvents() {
+            return (priorityCategories & PRIORITY_CATEGORY_EVENTS) != 0;
+        }
+
+        /** @hide **/
+        public boolean allowReminders() {
+            return (priorityCategories & PRIORITY_CATEGORY_REMINDERS) != 0;
+        }
+
+        /** @hide **/
+        public int allowCallsFrom() {
+            return priorityCallSenders;
+        }
+
+        /** @hide **/
+        public int allowMessagesFrom() {
+            return priorityMessageSenders;
+        }
+
+        /**
+         * returns a deep copy of this policy
+         * @hide
+         */
+        public Policy copy() {
+            final Parcel parcel = Parcel.obtain();
+            try {
+                writeToParcel(parcel, 0);
+                parcel.setDataPosition(0);
+                return new Policy(parcel);
+            } finally {
+                parcel.recycle();
+            }
+        }
     }
 
     /**
@@ -1708,5 +1785,4 @@
             default: return defValue;
         }
     }
-
 }
diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java
index 9bfdae0..d149243 100644
--- a/core/java/android/app/ProcessMemoryState.java
+++ b/core/java/android/app/ProcessMemoryState.java
@@ -33,10 +33,11 @@
     public final long cacheInBytes;
     public final long swapInBytes;
     public final long rssHighWatermarkInBytes;
+    public final long startTimeNanos;
 
     public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault,
                               long pgmajfault, long rssInBytes, long cacheInBytes,
-                              long swapInBytes, long rssHighWatermarkInBytes) {
+                              long swapInBytes, long rssHighWatermarkInBytes, long startTimeNanos) {
         this.uid = uid;
         this.processName = processName;
         this.oomScore = oomScore;
@@ -46,6 +47,7 @@
         this.cacheInBytes = cacheInBytes;
         this.swapInBytes = swapInBytes;
         this.rssHighWatermarkInBytes = rssHighWatermarkInBytes;
+        this.startTimeNanos = startTimeNanos;
     }
 
     private ProcessMemoryState(Parcel in) {
@@ -58,6 +60,7 @@
         cacheInBytes = in.readLong();
         swapInBytes = in.readLong();
         rssHighWatermarkInBytes = in.readLong();
+        startTimeNanos = in.readLong();
     }
 
     public static final Creator<ProcessMemoryState> CREATOR = new Creator<ProcessMemoryState>() {
@@ -88,5 +91,6 @@
         parcel.writeLong(cacheInBytes);
         parcel.writeLong(swapInBytes);
         parcel.writeLong(rssHighWatermarkInBytes);
+        parcel.writeLong(startTimeNanos);
     }
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 77cebc8..713f752 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -48,6 +48,8 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutManager;
 import android.content.res.Resources;
+import android.debug.AdbManager;
+import android.debug.IAdbManager;
 import android.hardware.ConsumerIrManager;
 import android.hardware.ISerialManager;
 import android.hardware.SensorManager;
@@ -64,6 +66,8 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.IHdmiControlService;
 import android.hardware.input.InputManager;
+import android.hardware.iris.IIrisService;
+import android.hardware.iris.IrisManager;
 import android.hardware.location.ContextHubManager;
 import android.hardware.radio.RadioManager;
 import android.hardware.usb.IUsbManager;
@@ -159,6 +163,8 @@
 import android.view.autofill.AutofillManager;
 import android.view.autofill.IAutoFillManager;
 import android.view.inputmethod.InputMethodManager;
+import android.view.intelligence.IIntelligenceManager;
+import android.view.intelligence.IntelligenceManager;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textservice.TextServicesManager;
 
@@ -583,6 +589,15 @@
                 return new UsbManager(ctx, IUsbManager.Stub.asInterface(b));
             }});
 
+        registerService(Context.ADB_SERVICE, AdbManager.class,
+                new CachedServiceFetcher<AdbManager>() {
+                    @Override
+                    public AdbManager createService(ContextImpl ctx)
+                                throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getServiceOrThrow(Context.ADB_SERVICE);
+                        return new AdbManager(ctx, IAdbManager.Stub.asInterface(b));
+                    }});
+
         registerService(Context.SERIAL_SERVICE, SerialManager.class,
                 new CachedServiceFetcher<SerialManager>() {
             @Override
@@ -825,16 +840,34 @@
                     }
                 });
 
+        registerService(Context.IRIS_SERVICE, IrisManager.class,
+                new CachedServiceFetcher<IrisManager>() {
+                    @Override
+                    public IrisManager createService(ContextImpl ctx)
+                        throws ServiceNotFoundException {
+                        final IBinder binder =
+                                ServiceManager.getServiceOrThrow(Context.IRIS_SERVICE);
+                        IIrisService service = IIrisService.Stub.asInterface(binder);
+                        return new IrisManager(ctx.getOuterContext(), service);
+                    }
+                });
+
         registerService(Context.BIOMETRIC_SERVICE, BiometricManager.class,
                 new CachedServiceFetcher<BiometricManager>() {
                     @Override
                     public BiometricManager createService(ContextImpl ctx)
                             throws ServiceNotFoundException {
-                        final IBinder binder =
-                                ServiceManager.getServiceOrThrow(Context.BIOMETRIC_SERVICE);
-                        final IBiometricService service =
-                                IBiometricService.Stub.asInterface(binder);
-                        return new BiometricManager(ctx.getOuterContext(), service);
+                        if (BiometricManager.hasBiometrics(ctx)) {
+                            final IBinder binder =
+                                    ServiceManager.getServiceOrThrow(Context.BIOMETRIC_SERVICE);
+                            final IBiometricService service =
+                                    IBiometricService.Stub.asInterface(binder);
+                            return new BiometricManager(ctx.getOuterContext(), service);
+                        } else {
+                            // Allow access to the manager when service is null. This saves memory
+                            // on devices without biometric hardware.
+                            return new BiometricManager(ctx.getOuterContext(), null);
+                        }
                     }
                 });
 
@@ -1007,6 +1040,17 @@
                 return new AutofillManager(ctx.getOuterContext(), service);
             }});
 
+        registerService(Context.INTELLIGENCE_MANAGER_SERVICE, IntelligenceManager.class,
+                new CachedServiceFetcher<IntelligenceManager>() {
+            @Override
+            public IntelligenceManager createService(ContextImpl ctx)
+                    throws ServiceNotFoundException {
+                // Get the services without throwing as this is an optional feature
+                IBinder b = ServiceManager.getService(Context.INTELLIGENCE_MANAGER_SERVICE);
+                IIntelligenceManager service = IIntelligenceManager.Stub.asInterface(b);
+                return new IntelligenceManager(ctx.getOuterContext(), service);
+            }});
+
         registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() {
             @Override
             public VrManager createService(ContextImpl ctx) throws ServiceNotFoundException {
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 096c7aa..78b7d48 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfigurationProto.ACTIVITY_TYPE;
 import static android.app.WindowConfigurationProto.APP_BOUNDS;
 import static android.app.WindowConfigurationProto.WINDOWING_MODE;
+import static android.view.Surface.rotationToString;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -60,6 +61,17 @@
      */
     private Rect mAppBounds;
 
+    /**
+     * The current rotation of this window container relative to the default
+     * orientation of the display it is on (regardless of how deep in the hierarchy
+     * it is). It is used by the configuration hierarchy to apply rotation-dependent
+     * policy during bounds calculation.
+     */
+    private int mRotation = ROTATION_UNDEFINED;
+
+    /** Rotation is not defined, use the parent containers rotation. */
+    public static final int ROTATION_UNDEFINED = -1;
+
     /** The current windowing mode of the configuration. */
     private @WindowingMode int mWindowingMode;
 
@@ -161,6 +173,9 @@
     /** Bit that indicates that the {@link #mAlwaysOnTop} changed.
      * @hide */
     public static final int WINDOW_CONFIG_ALWAYS_ON_TOP = 1 << 4;
+    /** Bit that indicates that the {@link #mRotation} changed.
+     * @hide */
+    public static final int WINDOW_CONFIG_ROTATION = 1 << 5;
     /** @hide */
     @IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
             WINDOW_CONFIG_BOUNDS,
@@ -168,6 +183,7 @@
             WINDOW_CONFIG_WINDOWING_MODE,
             WINDOW_CONFIG_ACTIVITY_TYPE,
             WINDOW_CONFIG_ALWAYS_ON_TOP,
+            WINDOW_CONFIG_ROTATION,
     })
     public @interface WindowConfig {}
 
@@ -194,6 +210,7 @@
         dest.writeInt(mWindowingMode);
         dest.writeInt(mActivityType);
         dest.writeInt(mAlwaysOnTop);
+        dest.writeInt(mRotation);
     }
 
     private void readFromParcel(Parcel source) {
@@ -202,6 +219,7 @@
         mWindowingMode = source.readInt();
         mActivityType = source.readInt();
         mAlwaysOnTop = source.readInt();
+        mRotation = source.readInt();
     }
 
     @Override
@@ -287,6 +305,14 @@
         return mBounds;
     }
 
+    public int getRotation() {
+        return mRotation;
+    }
+
+    public void setRotation(int rotation) {
+        mRotation = rotation;
+    }
+
     public void setWindowingMode(@WindowingMode int windowingMode) {
         mWindowingMode = windowingMode;
     }
@@ -324,6 +350,7 @@
         setWindowingMode(other.mWindowingMode);
         setActivityType(other.mActivityType);
         setAlwaysOnTop(other.mAlwaysOnTop);
+        setRotation(other.mRotation);
     }
 
     /** Set this object to completely undefined.
@@ -339,6 +366,7 @@
         setWindowingMode(WINDOWING_MODE_UNDEFINED);
         setActivityType(ACTIVITY_TYPE_UNDEFINED);
         setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED);
+        setRotation(ROTATION_UNDEFINED);
     }
 
     /**
@@ -375,6 +403,10 @@
             changed |= WINDOW_CONFIG_ALWAYS_ON_TOP;
             setAlwaysOnTop(delta.mAlwaysOnTop);
         }
+        if (delta.mRotation != ROTATION_UNDEFINED && delta.mRotation != mRotation) {
+            changed |= WINDOW_CONFIG_ROTATION;
+            setRotation(delta.mRotation);
+        }
         return changed;
     }
 
@@ -418,6 +450,11 @@
             changes |= WINDOW_CONFIG_ALWAYS_ON_TOP;
         }
 
+        if ((compareUndefined || other.mRotation != ROTATION_UNDEFINED)
+                && mRotation != other.mRotation) {
+            changes |= WINDOW_CONFIG_ROTATION;
+        }
+
         return changes;
     }
 
@@ -454,6 +491,8 @@
         if (n != 0) return n;
         n = mAlwaysOnTop - that.mAlwaysOnTop;
         if (n != 0) return n;
+        n = mRotation - that.mRotation;
+        if (n != 0) return n;
 
         // if (n != 0) return n;
         return n;
@@ -482,6 +521,7 @@
         result = 31 * result + mWindowingMode;
         result = 31 * result + mActivityType;
         result = 31 * result + mAlwaysOnTop;
+        result = 31 * result + mRotation;
         return result;
     }
 
@@ -493,6 +533,8 @@
                 + " mWindowingMode=" + windowingModeToString(mWindowingMode)
                 + " mActivityType=" + activityTypeToString(mActivityType)
                 + " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop)
+                + " mRotation=" + (mRotation == ROTATION_UNDEFINED
+                        ? "undefined" : rotationToString(mRotation))
                 + "}";
     }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 92daf08..f129a71 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3845,6 +3845,11 @@
     public static final int KEYGUARD_DISABLE_IRIS = 1 << 8;
 
     /**
+     * NOTE: Please remember to update the DevicePolicyManagerTest's testKeyguardDisabledFeatures
+     * CTS test when adding to the list above.
+     */
+
+    /**
      * Disable all biometric authentication on keyguard secure screens (e.g. PIN/Pattern/Password).
      */
     public static final int KEYGUARD_DISABLE_BIOMETRICS =
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index fefb8d3..43f902a 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -2,6 +2,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
@@ -713,7 +714,11 @@
 
         ViewNode[] mChildren;
 
-        ViewNode() {
+        // TODO(b/111276913): temporarily made public / @hide until we decide what will be used by
+        // ScreenObservation.
+        /** @hide */
+        @SystemApi
+        public ViewNode() {
         }
 
         ViewNode(ParcelTransferReader reader, int nestingLevel) {
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
new file mode 100644
index 0000000..2cf13ec2
--- /dev/null
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 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 android.app.role;
+
+import android.app.role.IRoleManagerCallback;
+
+/**
+ * @hide
+ */
+interface IRoleManager {
+
+    boolean isRoleAvailable(in String roleName);
+
+    boolean isRoleHeld(in String roleName, in String packageName);
+
+    List<String> getRoleHoldersAsUser(in String roleName, int userId);
+
+    void addRoleHolderAsUser(in String roleName, in String packageName, int userId,
+            in IRoleManagerCallback callback);
+
+    void removeRoleHolderAsUser(in String roleName, in String packageName, int userId,
+            in IRoleManagerCallback callback);
+
+    void clearRoleHoldersAsUser(in String roleName, int userId, in IRoleManagerCallback callback);
+
+    boolean addRoleHolderFromController(in String roleName, in String packageName);
+
+    boolean removeRoleHolderFromController(in String roleName, in String packageName);
+}
diff --git a/core/java/android/app/role/IRoleManagerCallback.aidl b/core/java/android/app/role/IRoleManagerCallback.aidl
new file mode 100644
index 0000000..c0f8eea
--- /dev/null
+++ b/core/java/android/app/role/IRoleManagerCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 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 android.app.role;
+
+/**
+ * @hide
+ */
+oneway interface IRoleManagerCallback {
+
+    void onSuccess();
+
+    void onFailure();
+}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
new file mode 100644
index 0000000..ed27d9f
--- /dev/null
+++ b/core/java/android/app/role/RoleManager.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2018 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 android.app.role;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * This class provides information about and manages roles.
+ * <p>
+ * A role is a unique name within the system associated with certain privileges. The list of
+ * available roles might change with a system app update, so apps should not make assumption about
+ * the availability of roles. Instead, they should always query if the role is available using
+ * {@link #isRoleAvailable(String)} before trying to do anything with it. Some predefined role names
+ * are available as constants in this class, and a list of possibly available roles can be found in
+ * the AndroidX Libraries.
+ * <p>
+ * There can be multiple applications qualifying for a role, but only a subset of them can become
+ * role holders. To qualify for a role, an application must meet certain requirements, including
+ * defining certain components in its manifest. These requirements can be found in the AndroidX
+ * Libraries. Then the application will need user consent to become a role holder, which can be
+ * requested using {@link android.app.Activity#startActivityForResult(Intent, int)} with the
+ * {@code Intent} obtained from {@link #createRequestRoleIntent(String)}.
+ * <p>
+ * Upon becoming a role holder, the application may be granted certain privileges that are role
+ * specific. When the application loses its role, these privileges will also be revoked.
+ */
+@SystemService(Context.ROLE_SERVICE)
+public final class RoleManager {
+
+    private static final String LOG_TAG = RoleManager.class.getSimpleName();
+
+    /**
+     * The name of the dialer role.
+     */
+    public static final String ROLE_DIALER = "android.app.role.DIALER";
+
+    /**
+     * The name of the SMS role.
+     */
+    public static final String ROLE_SMS = "android.app.role.SMS";
+
+    /**
+     * The action used to request user approval of a role for an application.
+     *
+     * @hide
+     */
+    public static final String ACTION_REQUEST_ROLE = "android.app.role.action.REQUEST_ROLE";
+
+    /**
+     * The name of the requested role.
+     * <p>
+     * <strong>Type:</strong> String
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
+
+    /**
+     * The permission required to manage records of role holders in {@link RoleManager} directly.
+     *
+     * @hide
+     */
+    public static final String PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER =
+            "com.android.permissioncontroller.permission.MANAGE_ROLE_HOLDERS_FROM_CONTROLLER";
+
+    @NonNull
+    private final Context mContext;
+
+    @NonNull
+    private final IRoleManager mService;
+
+    /**
+     * @hide
+     */
+    public RoleManager(@NonNull Context context) throws ServiceManager.ServiceNotFoundException {
+        mContext = context;
+        mService = IRoleManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
+                Context.ROLE_SERVICE));
+    }
+
+    /**
+     * Returns an {@code Intent} suitable for passing to
+     * {@link android.app.Activity#startActivityForResult(Intent, int)} which prompts the user to
+     * grant a role to this application.
+     * <p>
+     * If the role is granted, the {@code resultCode} will be
+     * {@link android.app.Activity#RESULT_OK}, otherwise it will be
+     * {@link android.app.Activity#RESULT_CANCELED}.
+     *
+     * @param roleName the name of requested role
+     *
+     * @return the {@code Intent} to prompt user to grant the role
+     *
+     * @throws IllegalArgumentException if {@code role} is {@code null} or empty
+     */
+    @NonNull
+    public Intent createRequestRoleIntent(@NonNull String roleName) {
+        Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+        Intent intent = new Intent(ACTION_REQUEST_ROLE);
+        intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
+        intent.putExtra(EXTRA_REQUEST_ROLE_NAME, roleName);
+        return intent;
+    }
+
+    /**
+     * Check whether a role is available in the system.
+     *
+     * @param roleName the name of role to checking for
+     *
+     * @return whether the role is available in the system
+     *
+     * @throws IllegalArgumentException if the role name is {@code null} or empty
+     */
+    public boolean isRoleAvailable(@NonNull String roleName) {
+        Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+        try {
+            return mService.isRoleAvailable(roleName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Check whether the calling application is holding a particular role.
+     *
+     * @param roleName the name of the role to check for
+     *
+     * @return whether the calling application is holding the role
+     *
+     * @throws IllegalArgumentException if the role name is {@code null} or empty.
+     */
+    public boolean isRoleHeld(@NonNull String roleName) {
+        Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+        try {
+            return mService.isRoleHeld(roleName, mContext.getPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get package names of the applications holding the role.
+     * <p>
+     * <strong>Note:</strong> Using this API requires holding
+     * {@code android.permission.MANAGE_ROLE_HOLDERS}.
+     *
+     * @param roleName the name of the role to get the role holder for
+     *
+     * @return a list of package names of the role holders, or an empty list if none.
+     *
+     * @throws IllegalArgumentException if the role name is {@code null} or empty.
+     *
+     * @see #getRoleHoldersAsUser(String, UserHandle)
+     *
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+    @SystemApi
+    public List<String> getRoleHolders(@NonNull String roleName) {
+        return getRoleHoldersAsUser(roleName, UserHandle.of(UserHandle.getCallingUserId()));
+    }
+
+    /**
+     * Get package names of the applications holding the role.
+     * <p>
+     * <strong>Note:</strong> Using this API requires holding
+     * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
+     * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
+     *
+     * @param roleName the name of the role to get the role holder for
+     * @param user the user to get the role holder for
+     *
+     * @return a list of package names of the role holders, or an empty list if none.
+     *
+     * @throws IllegalArgumentException if the role name is {@code null} or empty.
+     *
+     * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
+     * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
+     * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
+     *
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+    @SystemApi
+    public List<String> getRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user) {
+        Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+        Preconditions.checkNotNull(user, "user cannot be null");
+        try {
+            return mService.getRoleHoldersAsUser(roleName, user.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Add a specific application to the holders of a role. If the role is exclusive, the previous
+     * holder will be replaced.
+     * <p>
+     * <strong>Note:</strong> Using this API requires holding
+     * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
+     * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
+     *
+     * @param roleName the name of the role to add the role holder for
+     * @param packageName the package name of the application to add to the role holders
+     * @param user the user to add the role holder for
+     * @param executor the {@code Executor} to run the callback on.
+     * @param callback the callback for whether this call is successful
+     *
+     * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
+     *
+     * @see #getRoleHoldersAsUser(String, UserHandle)
+     * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
+     * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+    @SystemApi
+    public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
+            @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
+            @NonNull RoleManagerCallback callback) {
+        Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+        Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+        Preconditions.checkNotNull(user, "user cannot be null");
+        Preconditions.checkNotNull(executor, "executor cannot be null");
+        Preconditions.checkNotNull(callback, "callback cannot be null");
+        try {
+            mService.addRoleHolderAsUser(roleName, packageName, user.getIdentifier(),
+                    new RoleManagerCallbackDelegate(executor, callback));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove a specific application from the holders of a role.
+     * <p>
+     * <strong>Note:</strong> Using this API requires holding
+     * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
+     * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
+     *
+     * @param roleName the name of the role to remove the role holder for
+     * @param packageName the package name of the application to remove from the role holders
+     * @param user the user to remove the role holder for
+     * @param executor the {@code Executor} to run the callback on.
+     * @param callback the callback for whether this call is successful
+     *
+     * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
+     *
+     * @see #getRoleHoldersAsUser(String, UserHandle)
+     * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
+     * @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+    @SystemApi
+    public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
+            @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
+            @NonNull RoleManagerCallback callback) {
+        Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+        Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+        Preconditions.checkNotNull(user, "user cannot be null");
+        Preconditions.checkNotNull(executor, "executor cannot be null");
+        Preconditions.checkNotNull(callback, "callback cannot be null");
+        try {
+            mService.removeRoleHolderAsUser(roleName, packageName, user.getIdentifier(),
+                    new RoleManagerCallbackDelegate(executor, callback));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove all holders of a role.
+     * <p>
+     * <strong>Note:</strong> Using this API requires holding
+     * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
+     * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
+     *
+     * @param roleName the name of the role to remove role holders for
+     * @param user the user to remove role holders for
+     * @param executor the {@code Executor} to run the callback on.
+     * @param callback the callback for whether this call is successful
+     *
+     * @throws IllegalArgumentException if the role name is {@code null} or empty.
+     *
+     * @see #getRoleHoldersAsUser(String, UserHandle)
+     * @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
+     * @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+    @SystemApi
+    public void clearRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user,
+            @CallbackExecutor @NonNull Executor executor, @NonNull RoleManagerCallback callback) {
+        Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+        Preconditions.checkNotNull(user, "user cannot be null");
+        Preconditions.checkNotNull(executor, "executor cannot be null");
+        Preconditions.checkNotNull(callback, "callback cannot be null");
+        try {
+            mService.clearRoleHoldersAsUser(roleName, user.getIdentifier(),
+                    new RoleManagerCallbackDelegate(executor, callback));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Add a specific application to the holders of a role, only modifying records inside
+     * {@link RoleManager}. Should only be called from
+     * {@link android.rolecontrollerservice.RoleControllerService}.
+     * <p>
+     * <strong>Note:</strong> Using this API requires holding
+     * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
+     *
+     * @param roleName the name of the role to add the role holder for
+     * @param packageName the package name of the application to add to the role holders
+     *
+     * @return whether the operation was successful, and will also be {@code true} if a matching
+     *         role holder is already found.
+     *
+     * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
+     *
+     * @see #getRoleHolders(String)
+     * @see #removeRoleHolderFromController(String, String)
+     *
+     * @hide
+     */
+    @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
+    @SystemApi
+    public boolean addRoleHolderFromController(@NonNull String roleName,
+            @NonNull String packageName) {
+        Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+        Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+        try {
+            return mService.addRoleHolderFromController(roleName, packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove a specific application from the holders of a role, only modifying records inside
+     * {@link RoleManager}. Should only be called from
+     * {@link android.rolecontrollerservice.RoleControllerService}.
+     * <p>
+     * <strong>Note:</strong> Using this API requires holding
+     * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
+     *
+     * @param roleName the name of the role to remove the role holder for
+     * @param packageName the package name of the application to remove from the role holders
+     *
+     * @return whether the operation was successful, and will also be {@code true} if no matching
+     *         role holder was found to remove.
+     *
+     * @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
+     *
+     * @see #getRoleHolders(String)
+     * @see #addRoleHolderFromController(String, String)
+     *
+     * @hide
+     */
+    @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
+    @SystemApi
+    public boolean removeRoleHolderFromController(@NonNull String roleName,
+            @NonNull String packageName) {
+        Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+        Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+        try {
+            return mService.removeRoleHolderFromController(roleName, packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private static class RoleManagerCallbackDelegate extends IRoleManagerCallback.Stub {
+
+        @NonNull
+        private final Executor mExecutor;
+        @NonNull
+        private final RoleManagerCallback mCallback;
+
+        RoleManagerCallbackDelegate(@NonNull Executor executor,
+                @NonNull RoleManagerCallback callback) {
+            mExecutor = executor;
+            mCallback = callback;
+        }
+
+        @Override
+        public void onSuccess() {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(mCallback::onSuccess);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void onFailure() {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(mCallback::onFailure);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/role/RoleManagerCallback.java b/core/java/android/app/role/RoleManagerCallback.java
new file mode 100644
index 0000000..ca68ebc
--- /dev/null
+++ b/core/java/android/app/role/RoleManagerCallback.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 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 android.app.role;
+
+import android.annotation.SystemApi;
+
+/**
+ * Callback for a {@link RoleManager} request.
+ *
+ * @hide
+ */
+@SystemApi
+public interface RoleManagerCallback {
+
+    /**
+     * Signals a success.
+     */
+    void onSuccess();
+
+    /**
+     * Signals a failure.
+     */
+    void onFailure();
+}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 9713527..4d52263 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -51,4 +51,8 @@
     void registerAppUsageObserver(int observerId, in String[] packages, long timeLimitMs,
             in PendingIntent callback, String callingPackage);
     void unregisterAppUsageObserver(int observerId, String callingPackage);
+    void registerUsageSessionObserver(int sessionObserverId, in String[] observed, long timeLimitMs,
+            long sessionThresholdTimeMs, in PendingIntent limitReachedCallbackIntent,
+            in PendingIntent sessionEndCallbackIntent, String callingPackage);
+    void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage);
 }
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index dbb00eb..6d7400e 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -595,7 +596,7 @@
      *                       exceeded by the group of apps. The delivered Intent will also contain
      *                       the extras {@link #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and
      *                       {@link #EXTRA_TIME_USED}. Cannot be null.
-     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission or
+     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and
      *                           is not the profile owner of this user.
      */
     @SystemApi
@@ -616,7 +617,7 @@
      * to any observer registered by this application. Unregistering an observer that was already
      * unregistered or never registered will have no effect.
      * @param observerId The id of the observer that was previously registered.
-     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission or is
+     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and is
      *                           not the profile owner of this user.
      */
     @SystemApi
@@ -629,6 +630,81 @@
         }
     }
 
+    /**
+     * Register a usage session observer that receives a callback on the provided {@code
+     * limitReachedCallbackIntent} when the sum of usages of apps in the packages array exceeds
+     * the {@code timeLimit} specified within a usage session. After the {@code timeLimit} has
+     * been reached, the usage session observer will receive a callback on the provided {@code
+     * sessionEndCallbackIntent} when the usage session ends. Registering another session
+     * observer against a {@code sessionObserverId} that has already been registered will
+     * override the previous session observer.
+     *
+     * @param sessionObserverId A unique id associated with the group of apps to be
+     *                          monitored. There can be multiple groups with common
+     *                          packages and different time limits.
+     * @param packages The list of packages to observe for foreground activity time. Cannot be null
+     *                 and must include at least one package.
+     * @param timeLimit The total time the set of apps can be used continuously before the {@code
+     *                  limitReachedCallbackIntent} is delivered. Must be at least one minute.
+     * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
+     * @param sessionThresholdTime The time that can take place between usage sessions before the
+     *                             next session is considered a new session. Must be non-negative.
+     * @param sessionThresholdTimeUnit The unit for time specified in {@code sessionThreshold}.
+     *                                 Cannot be null.
+     * @param limitReachedCallbackIntent The {@link PendingIntent} that will be dispatched when the
+     *                                   time limit is exceeded by the group of apps. The delivered
+     *                                   Intent will also contain the extras {@link
+     *                                   #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and {@link
+     *                                   #EXTRA_TIME_USED}. Cannot be null.
+     * @param sessionEndCallbackIntent The {@link PendingIntent}  that will be dispatched when the
+     *                                 session has ended after the time limit has been exceeded. The
+     *                                 session is considered at its end after the {@code observed}
+     *                                 usage has stopped and an additional {@code
+     *                                 sessionThresholdTime} has passed. The delivered Intent will
+     *                                 also contain the extras {@link #EXTRA_OBSERVER_ID} and {@link
+     *                                 #EXTRA_TIME_USED}. Can be null.
+     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and
+     *                           is not the profile owner of this user.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
+    public void registerUsageSessionObserver(int sessionObserverId, @NonNull String[] packages,
+            long timeLimit, @NonNull TimeUnit timeUnit, long sessionThresholdTime,
+            @NonNull TimeUnit sessionThresholdTimeUnit,
+            @NonNull PendingIntent limitReachedCallbackIntent,
+            @Nullable PendingIntent sessionEndCallbackIntent) {
+        try {
+            mService.registerUsageSessionObserver(sessionObserverId, packages,
+                    timeUnit.toMillis(timeLimit),
+                    sessionThresholdTimeUnit.toMillis(sessionThresholdTime),
+                    limitReachedCallbackIntent, sessionEndCallbackIntent,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregister the usage session observer specified by the {@code sessionObserverId}. This will
+     * only apply to any app session observer registered by this application. Unregistering an
+     * observer that was already unregistered or never registered will have no effect.
+     *
+     * @param sessionObserverId The id of the observer that was previously registered.
+     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and
+     *                           is not the profile owner of this user.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
+    public void unregisterUsageSessionObserver(int sessionObserverId) {
+        try {
+            mService.unregisterUsageSessionObserver(sessionObserverId, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** @hide */
     public static String reasonToString(int standbyReason) {
         StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index a9187b6..c6e94c7 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -17,8 +17,12 @@
 package android.appwidget;
 
 import android.annotation.UnsupportedAppUsage;
+import android.app.Activity;
+import android.app.ActivityOptions;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
@@ -30,6 +34,7 @@
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -44,6 +49,8 @@
 import android.widget.RemoteViewsAdapter.RemoteAdapterConnectionCallback;
 import android.widget.TextView;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
@@ -651,4 +658,39 @@
         super.onInitializeAccessibilityNodeInfoInternal(info);
         info.setClassName(AppWidgetHostView.class.getName());
     }
+
+    /** @hide */
+    public ActivityOptions createSharedElementActivityOptions(
+            int[] sharedViewIds, String[] sharedViewNames, Intent fillInIntent) {
+        Context parentContext = getContext();
+        while ((parentContext instanceof ContextWrapper)
+                && !(parentContext instanceof Activity)) {
+            parentContext = ((ContextWrapper) parentContext).getBaseContext();
+        }
+        if (!(parentContext instanceof Activity)) {
+            return null;
+        }
+
+        List<Pair<View, String>> sharedElements = new ArrayList<>();
+        Bundle extras = new Bundle();
+
+        for (int i = 0; i < sharedViewIds.length; i++) {
+            View view = findViewById(sharedViewIds[i]);
+            if (view != null) {
+                sharedElements.add(Pair.create(view, sharedViewNames[i]));
+
+                extras.putParcelable(sharedViewNames[i], RemoteViews.getSourceBounds(view));
+            }
+        }
+
+        if (!sharedElements.isEmpty()) {
+            fillInIntent.putExtra(RemoteViews.EXTRA_SHARED_ELEMENT_BOUNDS, extras);
+            final ActivityOptions opts = ActivityOptions.makeSceneTransitionAnimation(
+                    (Activity) parentContext,
+                    sharedElements.toArray(new Pair[sharedElements.size()]));
+            opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            return opts;
+        }
+        return null;
+    }
 }
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index a2a6b9b..4de1dfc 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -52,7 +52,9 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.os.storage.StorageManager;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -88,6 +90,30 @@
  */
 public abstract class ContentResolver {
     /**
+     * Enables logic that supports deprecation of {@code _data} columns,
+     * typically by replacing values with fake paths that the OS then offers to
+     * redirect to {@link #openFileDescriptor(Uri, String)}, which developers
+     * should be using directly.
+     *
+     * @hide
+     */
+    public static final boolean DEPRECATE_DATA_COLUMNS = SystemProperties
+            .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false);
+
+    /**
+     * Special filesystem path prefix which indicates that a path should be
+     * treated as a {@code content://} {@link Uri} when
+     * {@link #DEPRECATE_DATA_COLUMNS} is enabled.
+     * <p>
+     * The remainder of the path after this prefix is a
+     * {@link Uri#getSchemeSpecificPart()} value, which includes authority, path
+     * segments, and query parameters.
+     *
+     * @hide
+     */
+    public static final String DEPRECATE_DATA_PREFIX = "/mnt/content/";
+
+    /**
      * @deprecated instead use
      * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
      */
@@ -3261,4 +3287,16 @@
             e.rethrowFromSystemServer();
         }
     }
+
+    /** {@hide} */
+    public static Uri translateDeprecatedDataPath(String path) {
+        final String ssp = "//" + path.substring(DEPRECATE_DATA_PREFIX.length());
+        return Uri.parse(new Uri.Builder().scheme(SCHEME_CONTENT)
+                .encodedOpaquePart(ssp).build().toString());
+    }
+
+    /** {@hide} */
+    public static String translateDeprecatedDataPath(Uri uri) {
+        return DEPRECATE_DATA_PREFIX + uri.getEncodedSchemeSpecificPart().substring(2);
+    }
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3197352..2aa32c4 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3061,6 +3061,7 @@
             USER_SERVICE,
             RESTRICTIONS_SERVICE,
             APP_OPS_SERVICE,
+            ROLE_SERVICE,
             CAMERA_SERVICE,
             PRINT_SERVICE,
             CONSUMER_IR_SERVICE,
@@ -3705,6 +3706,17 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.iris.IrisManager} for handling management
+     * of iris authentication.
+     *
+     * @hide
+     * @see #getSystemService
+     * @see android.hardware.iris.IrisManager
+     */
+    public static final String IRIS_SERVICE = "iris";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.hardware.biometrics.BiometricManager} for handling management
      * of face authentication.
      *
@@ -3853,6 +3865,14 @@
     public static final String AUTOFILL_MANAGER_SERVICE = "autofill";
 
     /**
+     * Official published name of the intelligence service.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    public static final String INTELLIGENCE_MANAGER_SERVICE = "intelligence";
+
+    /**
      * Use with {@link #getSystemService(String)} to access the
      * {@link com.android.server.voiceinteraction.SoundTriggerService}.
      *
@@ -3966,6 +3986,18 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * Use with {@link #getSystemService} to retrieve a {@link
+     * android.debug.AdbManager} for access to ADB debug functions.
+     *
+     * @see #getSystemService(String)
+     * @see android.debug.AdbManager
+     *
+     * @hide
+     */
+    public static final String ADB_SERVICE = "adb";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
      * android.hardware.SerialManager} for access to serial ports.
      *
      * @see #getSystemService(String)
@@ -4044,6 +4076,15 @@
     public static final String APP_OPS_SERVICE = "appops";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link android.app.role.RoleManager}
+     * for managing roles.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.role.RoleManager
+     */
+    public static final String ROLE_SERVICE = "role";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.hardware.camera2.CameraManager} for interacting with
      * camera devices.
@@ -4186,6 +4227,15 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.ThermalService} for accessing the thermal service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String THERMAL_SERVICE = "thermalservice";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.content.pm.ShortcutManager} for accessing the launcher shortcut service.
      *
      * @see #getSystemService(String)
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index ea1a2fe..6c0fa4c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -23,6 +23,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
@@ -1924,6 +1925,29 @@
     public static final String ACTION_REVIEW_PERMISSION_USAGE =
             "android.intent.action.REVIEW_PERMISSION_USAGE";
 
+    /**
+     * Activity action: Launch UI to review uses of permissions for a single app.
+     * <p>
+     * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose
+     * permissions will be reviewed (mandatory).
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     * <p class="note">
+     * This requires {@link android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS} permission.
+     * </p>
+     *
+     * @see #EXTRA_PACKAGE_NAME
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REVIEW_APP_PERMISSION_USAGE =
+            "android.intent.action.REVIEW_APP_PERMISSION_USAGE";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent broadcast actions (see action variable).
@@ -5285,6 +5309,7 @@
             FLAG_RECEIVER_EXCLUDE_BACKGROUND,
             FLAG_RECEIVER_FROM_SHELL,
             FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS,
+            FLAG_RECEIVER_OFFLOAD,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Flags {}
@@ -5329,6 +5354,7 @@
             FLAG_RECEIVER_EXCLUDE_BACKGROUND,
             FLAG_RECEIVER_FROM_SHELL,
             FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS,
+            FLAG_RECEIVER_OFFLOAD,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface MutableFlags {}
@@ -5755,6 +5781,12 @@
      */
     public static final int FLAG_RECEIVER_FOREGROUND = 0x10000000;
     /**
+     * If set, when sending a broadcast the recipient will be run on the offload queue.
+     *
+     * @hide
+     */
+    public static final int FLAG_RECEIVER_OFFLOAD = 0x80000000;
+    /**
      * If this is an ordered broadcast, don't allow receivers to abort the broadcast.
      * They can still propagate results through to later receivers, but they can not prevent
      * later receivers from seeing the broadcast.
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index d3652e8..877dfee 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -57,6 +57,9 @@
          *
          * <p>This callback will be run on your main thread.
          *
+         * <p><em>Note: This callback will not be triggered when preferences are cleared via
+         * {@link Editor#clear()}.</em>
+         *
          * @param sharedPreferences The {@link SharedPreferences} that received
          *            the change.
          * @param key The key of the preference that was changed, added, or
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index c33f143..6b5c659 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -635,6 +635,13 @@
      */
     public static final int PRIVATE_FLAG_USES_NON_SDK_API = 1 << 22;
 
+    /**
+     * Indicates whether this application can be profiled by the shell user,
+     * even when running on a device that is running in user mode.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_PROFILEABLE_BY_SHELL = 1 << 23;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
             PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -654,6 +661,7 @@
             PRIVATE_FLAG_PRIVILEGED,
             PRIVATE_FLAG_PRODUCT,
             PRIVATE_FLAG_PRODUCT_SERVICES,
+            PRIVATE_FLAG_PROFILEABLE_BY_SHELL,
             PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
             PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY,
             PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
@@ -1929,6 +1937,14 @@
     }
 
     /**
+     * Returns whether or not this application can be profiled by the shell user,
+     * even when running on a device that is running in user mode.
+     */
+    public boolean isProfileableByShell() {
+        return (privateFlags & PRIVATE_FLAG_PROFILEABLE_BY_SHELL) != 0;
+    }
+
+    /**
      * Returns true if the app has declared in its manifest that it wants its split APKs to be
      * loaded into isolated Contexts, with their own ClassLoaders and Resources objects.
      * @hide
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 8fddb99..cef21f6 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -38,4 +38,9 @@
     void commit(in IntentSender statusReceiver, boolean forTransferred);
     void transfer(in String packageName);
     void abandon();
+    boolean isMultiPackage();
+    int[] getChildSessionIds();
+    void addChildSessionId(in int sessionId);
+    void removeChildSessionId(in int sessionId);
+    int getParentSessionId();
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index e9cfa78..8f90199 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -365,12 +365,14 @@
      */
     public @NonNull Session openSession(int sessionId) throws IOException {
         try {
-            return new Session(mInstaller.openSession(sessionId));
+            try {
+                return new Session(mInstaller.openSession(sessionId));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         } catch (RuntimeException e) {
             ExceptionUtils.maybeUnwrapIOException(e);
             throw e;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -769,9 +771,18 @@
      * If an APK included in this session is already defined by the existing
      * installation (for example, the same split name), the APK in this session
      * will replace the existing APK.
+     * <p>
+     * In such a case that multiple packages need to be commited simultaneously,
+     * multiple sessions can be referenced by a single multi-package session.
+     * This session is created with no package name and calling
+     * {@link SessionParams#setMultiPackage()} with {@code true}. The
+     * individual session IDs can be added with {@link #addChildSessionId(int)}
+     * and commit of the multi-package session will result in all child sessions
+     * being committed atomically.
      */
     public static class Session implements Closeable {
-        private IPackageInstallerSession mSession;
+        /** {@hide} */
+        protected final IPackageInstallerSession mSession;
 
         /** {@hide} */
         public Session(IPackageInstallerSession session) {
@@ -1080,6 +1091,71 @@
                 throw e.rethrowFromSystemServer();
             }
         }
+
+        /**
+         * @return {@code true} if this session will commit more than one package when it is
+         * committed.
+         */
+        public boolean isMultiPackage() {
+            try {
+                return mSession.isMultiPackage();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * @return the session ID of the multi-package session that this belongs to or
+         * {@link SessionInfo#INVALID_ID} if it does not belong to a multi-package session.
+         */
+        public int getParentSessionId() {
+            try {
+                return mSession.getParentSessionId();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * @return the set of session IDs that will be committed atomically when this session is
+         * committed if this is a multi-package session or null if none exist.
+         */
+        @NonNull
+        public int[] getChildSessionIds() {
+            try {
+                return mSession.getChildSessionIds();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Adds a session ID to the set of sessions that will be committed atomically
+         * when this session is committed.
+         *
+         * @param sessionId the session ID to add to this multi-package session.
+         */
+        public void addChildSessionId(int sessionId) {
+            try {
+                mSession.addChildSessionId(sessionId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Removes a session ID from the set of sessions that will be committed
+         * atomically when this session is committed.
+         *
+         * @param sessionId the session ID to remove from this multi-package session.
+         */
+        public void removeChildSessionId(int sessionId) {
+            try {
+                mSession.removeChildSessionId(sessionId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
     }
 
     /**
@@ -1149,6 +1225,8 @@
         public String[] grantedRuntimePermissions;
         /** {@hide} */
         public String installerPackageName;
+        /** {@hide} */
+        public boolean isMultiPackage;
 
         /**
          * Construct parameters for a new package install session.
@@ -1178,6 +1256,7 @@
             volumeUuid = source.readString();
             grantedRuntimePermissions = source.readStringArray();
             installerPackageName = source.readString();
+            isMultiPackage = source.readBoolean();
         }
 
         /**
@@ -1392,6 +1471,18 @@
             this.installerPackageName = installerPackageName;
         }
 
+        /**
+         * Set this session to be the parent of a multi-package install.
+         *
+         * A multi-package install session contains no APKs and only references other install
+         * sessions via ID. When a multi-package session is committed, all of its children
+         * are committed to the system in an atomic manner. If any children fail to install,
+         * all of them do, including the multi-package session.
+         */
+        public void setMultiPackage() {
+            this.isMultiPackage = true;
+        }
+
         /** {@hide} */
         public void dump(IndentingPrintWriter pw) {
             pw.printPair("mode", mode);
@@ -1408,6 +1499,7 @@
             pw.printPair("volumeUuid", volumeUuid);
             pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions);
             pw.printPair("installerPackageName", installerPackageName);
+            pw.printPair("isMultiPackage", isMultiPackage);
             pw.println();
         }
 
@@ -1433,6 +1525,7 @@
             dest.writeString(volumeUuid);
             dest.writeStringArray(grantedRuntimePermissions);
             dest.writeString(installerPackageName);
+            dest.writeBoolean(isMultiPackage);
         }
 
         public static final Parcelable.Creator<SessionParams>
@@ -1454,6 +1547,12 @@
      */
     public static class SessionInfo implements Parcelable {
 
+        /**
+         * A session ID that does not exist or is invalid.
+         */
+        public static final int INVALID_ID = -1;
+        /** {@hide} */
+        private static final int[] NO_SESSIONS = {};
         /** {@hide} */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public int sessionId;
@@ -1503,6 +1602,12 @@
         public String[] grantedRuntimePermissions;
         /** {@hide} */
         public int installFlags;
+        /** {@hide} */
+        public boolean isMultiPackage;
+        /** {@hide} */
+        public int parentSessionId = INVALID_ID;
+        /** {@hide} */
+        public int[] childSessionIds = NO_SESSIONS;
 
         /** {@hide} */
         @UnsupportedAppUsage
@@ -1531,6 +1636,12 @@
             referrerUri = source.readParcelable(null);
             grantedRuntimePermissions = source.readStringArray();
             installFlags = source.readInt();
+            isMultiPackage = source.readBoolean();
+            parentSessionId = source.readInt();
+            childSessionIds = source.createIntArray();
+            if (childSessionIds == null) {
+                childSessionIds = NO_SESSIONS;
+            }
         }
 
         /**
@@ -1784,6 +1895,30 @@
             return createDetailsIntent();
         }
 
+        /**
+         * Returns true if this session is a multi-package session containing references to other
+         * sessions.
+         */
+        public boolean isMultiPackage() {
+            return isMultiPackage;
+        }
+
+        /**
+         * Returns the parent multi-package session ID if this session belongs to one,
+         * {@link #INVALID_ID} otherwise.
+         */
+        public int getParentSessionId() {
+            return parentSessionId;
+        }
+
+        /**
+         * Returns the set of session IDs that will be committed when this session is commited if
+         * this session is a multi-package session.
+         */
+        public int[] getChildSessionIds() {
+            return childSessionIds;
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -1811,6 +1946,9 @@
             dest.writeParcelable(referrerUri, flags);
             dest.writeStringArray(grantedRuntimePermissions);
             dest.writeInt(installFlags);
+            dest.writeBoolean(isMultiPackage);
+            dest.writeInt(parentSessionId);
+            dest.writeIntArray(childSessionIds);
         }
 
         public static final Parcelable.Creator<SessionInfo>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index dfb8128..c1ac061 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -855,6 +855,14 @@
      */
     public static final int INSTALL_VIRTUAL_PRELOAD = 0x00010000;
 
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that this package
+     * is an APEX package
+     *
+     * @hide
+     */
+    public static final int INSTALL_APEX = 0x00020000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {
             DONT_KILL_APP
@@ -907,6 +915,11 @@
     public static final int INSTALL_REASON_USER = 4;
 
     /**
+     * @hide
+     */
+    public static final int INSTALL_UNKNOWN = 0;
+
+    /**
      * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
      * on success.
      *
@@ -2935,6 +2948,15 @@
     public static final int FLAG_PERMISSION_REVIEW_REQUIRED =  1 << 6;
 
     /**
+     * Permission flag: The permission has not been explicitly requested by
+     * the app but has been added automatically by the system. Revoke once
+     * the app does explicitly request it.
+     *
+     * @hide
+     */
+    public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED =  1 << 7;
+
+    /**
      * Mask for all permission flags.
      *
      * @hide
@@ -3585,7 +3607,10 @@
             FLAG_PERMISSION_POLICY_FIXED,
             FLAG_PERMISSION_REVOKE_ON_UPGRADE,
             FLAG_PERMISSION_SYSTEM_FIXED,
-            FLAG_PERMISSION_GRANTED_BY_DEFAULT
+            FLAG_PERMISSION_GRANTED_BY_DEFAULT,
+            /*
+            FLAG_PERMISSION_REVOKE_WHEN_REQUESED
+            */
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PermissionFlags {}
@@ -5439,6 +5464,28 @@
             ComponentName[] set, ComponentName activity);
 
     /**
+     * Replaces an existing preferred activity mapping to the system, and if that were not present
+     * adds a new preferred activity.  This will be used to automatically select the given activity
+     * component when {@link Context#startActivity(Intent) Context.startActivity()} finds multiple
+     * matching activities and also matches the given filter.
+     *
+     * @param filter The set of intents under which this activity will be made preferred.
+     * @param match The IntentFilter match category that this preference applies to. Should be a
+     *              combination of {@link IntentFilter#MATCH_CATEGORY_MASK} and
+     *              {@link IntentFilter#MATCH_ADJUSTMENT_MASK}).
+     * @param set The set of activities that the user was picking from when this preference was
+     *            made.
+     * @param activity The component name of the activity that is to be preferred.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void replacePreferredActivity(@NonNull IntentFilter filter, int match,
+            @NonNull List<ComponentName> set, @NonNull ComponentName activity) {
+        replacePreferredActivity(filter, match, set.toArray(new ComponentName[0]), activity);
+    }
+
+    /**
      * @hide
      */
     @Deprecated
@@ -6103,6 +6150,7 @@
             case FLAG_PERMISSION_REVOKE_ON_UPGRADE: return "REVOKE_ON_UPGRADE";
             case FLAG_PERMISSION_USER_FIXED: return "USER_FIXED";
             case FLAG_PERMISSION_REVIEW_REQUIRED: return "REVIEW_REQUIRED";
+            case FLAG_PERMISSION_REVOKE_WHEN_REQUESTED: return "REVOKE_WHEN_REQUESTED";
             default: return Integer.toString(flag);
         }
     }
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 4f58321..6f49cc4 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -703,6 +703,18 @@
     public abstract SparseArray<String> getAppsWithSharedUserIds();
 
     /**
+     * Get the value of attribute android:sharedUserId for the given packageName if specified,
+     * otherwise {@code null}.
+     */
+    public abstract String getSharedUserIdForPackage(@NonNull String packageName);
+
+    /**
+     * Get all packages which specified the given sharedUserId as android:sharedUserId attribute
+     * or an empty array if no package specified it.
+     */
+    public abstract String[] getPackagesForSharedUserId(@NonNull String sharedUserId, int userId);
+
+    /**
      * Return if device is currently in a "core" boot environment, typically
      * used to support full-disk encryption. Only apps marked with
      * {@code coreApp} attribute are available.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 046e9e7..7ef5264 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -228,6 +228,24 @@
         CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT);
     }
 
+    // STOPSHIP(b/112545973): remove once feature enabled by default
+    private static final Set<String> FORCE_AUDIO_PACKAGES;
+    private static final Set<String> FORCE_VIDEO_PACKAGES;
+    private static final Set<String> FORCE_IMAGES_PACKAGES;
+    static {
+        FORCE_AUDIO_PACKAGES = parsePackageList(
+                SystemProperties.get(StorageManager.PROP_FORCE_AUDIO));
+        FORCE_VIDEO_PACKAGES = parsePackageList(
+                SystemProperties.get(StorageManager.PROP_FORCE_VIDEO));
+        FORCE_IMAGES_PACKAGES = parsePackageList(
+                SystemProperties.get(StorageManager.PROP_FORCE_IMAGES));
+    }
+
+    private static Set<String> parsePackageList(String pkgs) {
+        if (TextUtils.isEmpty(pkgs)) return Collections.emptySet();
+        return new ArraySet<String>(Arrays.asList(pkgs.split(",")));
+    }
+
     private static final boolean LOG_UNSAFE_BROADCASTS = false;
 
     /**
@@ -2417,7 +2435,7 @@
         }
 
         final int NP = PackageParser.NEW_PERMISSIONS.length;
-        StringBuilder implicitPerms = null;
+        StringBuilder newPermsMsg = null;
         for (int ip=0; ip<NP; ip++) {
             final PackageParser.NewPermissionInfo npi
                     = PackageParser.NEW_PERMISSIONS[ip];
@@ -2425,19 +2443,20 @@
                 break;
             }
             if (!pkg.requestedPermissions.contains(npi.name)) {
-                if (implicitPerms == null) {
-                    implicitPerms = new StringBuilder(128);
-                    implicitPerms.append(pkg.packageName);
-                    implicitPerms.append(": compat added ");
+                if (newPermsMsg == null) {
+                    newPermsMsg = new StringBuilder(128);
+                    newPermsMsg.append(pkg.packageName);
+                    newPermsMsg.append(": compat added ");
                 } else {
-                    implicitPerms.append(' ');
+                    newPermsMsg.append(' ');
                 }
-                implicitPerms.append(npi.name);
+                newPermsMsg.append(npi.name);
                 pkg.requestedPermissions.add(npi.name);
+                pkg.implicitPermissions.add(npi.name);
             }
         }
-        if (implicitPerms != null) {
-            Slog.i(TAG, implicitPerms.toString());
+        if (newPermsMsg != null) {
+            Slog.i(TAG, newPermsMsg.toString());
         }
 
 
@@ -2454,6 +2473,7 @@
                 final String perm = newPerms.get(in);
                 if (!pkg.requestedPermissions.contains(perm)) {
                     pkg.requestedPermissions.add(perm);
+                    pkg.implicitPermissions.add(perm);
                 }
             }
         }
@@ -2534,6 +2554,34 @@
                     }
                 }
             }
+        } else {
+            if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) {
+                pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
+                pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
+            }
+            if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) {
+                pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
+                pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
+            }
+            if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) {
+                pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
+                pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
+            }
+
+            if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_LEGACY, false)) {
+                if (pkg.requestedPermissions
+                        .contains(android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
+                    pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
+                    pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
+                    pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
+                }
+                if (pkg.requestedPermissions
+                        .contains(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
+                    pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
+                    pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
+                    pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
+                }
+            }
         }
 
         return pkg;
@@ -3501,6 +3549,8 @@
                 com.android.internal.R.styleable.AndroidManifestApplication_debuggable,
                 false)) {
             ai.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
+            // Debuggable implies profileable
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL;
         }
 
         if (sa.getBoolean(
@@ -3867,6 +3917,14 @@
                 // Dependencies for app installers; we don't currently try to
                 // enforce this.
                 XmlUtils.skipCurrentTag(parser);
+            } else if (tagName.equals("profileable")) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestProfileable);
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestProfileable_shell, false)) {
+                    ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL;
+                }
+                XmlUtils.skipCurrentTag(parser);
 
             } else {
                 if (!RIGID_PARSER) {
@@ -3883,10 +3941,13 @@
             }
         }
 
-        // Add a hidden app detail activity which forwards user to App Details page.
-        Activity a = generateAppDetailsHiddenActivity(owner, flags, outError,
-                owner.baseHardwareAccelerated);
-        owner.activities.add(a);
+        if (TextUtils.isEmpty(owner.staticSharedLibName)) {
+            // Add a hidden app detail activity to normal apps which forwards user to App Details
+            // page.
+            Activity a = generateAppDetailsHiddenActivity(owner, flags, outError,
+                    owner.baseHardwareAccelerated);
+            owner.activities.add(a);
+        }
 
         if (hasActivityOrder) {
             Collections.sort(owner.activities, (a1, a2) -> Integer.compare(a2.order, a1.order));
@@ -6335,6 +6396,9 @@
         @UnsupportedAppUsage
         public final ArrayList<String> requestedPermissions = new ArrayList<String>();
 
+        /** Permissions requested but not in the manifest. */
+        public final ArrayList<String> implicitPermissions = new ArrayList<>();
+
         @UnsupportedAppUsage
         public ArrayList<String> protectedBroadcasts;
 
@@ -6353,6 +6417,7 @@
         public ArrayList<String> usesOptionalLibraries = null;
         @UnsupportedAppUsage
         public String[] usesLibraryFiles = null;
+        public ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
 
         public ArrayList<ActivityIntentInfo> preferredActivityFilters = null;
 
@@ -6863,6 +6928,8 @@
 
             dest.readStringList(requestedPermissions);
             internStringArrayList(requestedPermissions);
+            dest.readStringList(implicitPermissions);
+            internStringArrayList(implicitPermissions);
             protectedBroadcasts = dest.createStringArrayList();
             internStringArrayList(protectedBroadcasts);
 
@@ -6887,6 +6954,8 @@
             internStringArrayList(usesOptionalLibraries);
             usesLibraryFiles = dest.readStringArray();
 
+            usesLibraryInfos = dest.createTypedArrayList(SharedLibraryInfo.CREATOR);
+
             final int libCount = dest.readInt();
             if (libCount > 0) {
                 usesStaticLibraries = new ArrayList<>(libCount);
@@ -7025,6 +7094,7 @@
             dest.writeParcelableList(instrumentation, flags);
 
             dest.writeStringList(requestedPermissions);
+            dest.writeStringList(implicitPermissions);
             dest.writeStringList(protectedBroadcasts);
 
             // TODO: This doesn't work: b/64295061
@@ -7037,6 +7107,7 @@
             dest.writeStringList(usesLibraries);
             dest.writeStringList(usesOptionalLibraries);
             dest.writeStringArray(usesLibraryFiles);
+            dest.writeTypedList(usesLibraryInfos);
 
             if (ArrayUtils.isEmpty(usesStaticLibraries)) {
                 dest.writeInt(-1);
@@ -7503,6 +7574,10 @@
                 && p.usesLibraryFiles != null) {
             return true;
         }
+        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0
+                && p.usesLibraryInfos != null) {
+            return true;
+        }
         if (p.staticSharedLibName != null) {
             return true;
         }
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 33bc951..096301c 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -19,11 +19,13 @@
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -69,12 +71,15 @@
      */
     public static final int VERSION_UNDEFINED = -1;
 
+    private final String mPath;
+    private final String mPackageName;
     private final String mName;
 
     private final long mVersion;
     private final @Type int mType;
     private final VersionedPackage mDeclaringPackage;
     private final List<VersionedPackage> mDependentPackages;
+    private List<SharedLibraryInfo> mDependencies;
 
     /**
      * Creates a new instance.
@@ -87,18 +92,23 @@
      *
      * @hide
      */
-    public SharedLibraryInfo(String name, long version, int type,
-            VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages) {
+    public SharedLibraryInfo(String path, String packageName, String name, long version, int type,
+            VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
+            List<SharedLibraryInfo> dependencies) {
+        mPath = path;
+        mPackageName = packageName;
         mName = name;
         mVersion = version;
         mType = type;
         mDeclaringPackage = declaringPackage;
         mDependentPackages = dependentPackages;
+        mDependencies = dependencies;
     }
 
     private SharedLibraryInfo(Parcel parcel) {
-        this(parcel.readString(), parcel.readLong(), parcel.readInt(),
-                parcel.readParcelable(null), parcel.readArrayList(null));
+        this(parcel.readString(), parcel.readString(), parcel.readString(), parcel.readLong(),
+                parcel.readInt(), parcel.readParcelable(null), parcel.readArrayList(null),
+                parcel.createTypedArrayList(SharedLibraryInfo.CREATOR));
     }
 
     /**
@@ -121,6 +131,71 @@
     }
 
     /**
+     * If the shared library is a jar file, returns the path of that jar. Null otherwise.
+     * Only libraries with TYPE_BUILTIN are in jar files.
+     *
+     * @return The path.
+     *
+     * @hide
+     */
+    public @Nullable String getPath() {
+        return mPath;
+    }
+
+    /**
+     * If the shared library is an apk, returns the package name. Null otherwise.
+     * Only libraries with TYPE_DYNAMIC or TYPE_STATIC are in apks.
+     *
+     * @return The package name.
+     *
+     * @hide
+     */
+    public @Nullable String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Add a library dependency to that library. Note that this
+     * should be called under the package manager lock.
+     *
+     * @hide
+     */
+    public void addDependency(@Nullable SharedLibraryInfo info) {
+        if (info == null) {
+            // For convenience of the caller, allow null to be passed.
+            // This can happen when we create the dependencies of builtin
+            // libraries.
+            return;
+        }
+        if (mDependencies == null) {
+            mDependencies = new ArrayList<>();
+        }
+        mDependencies.add(info);
+    }
+
+    /**
+     * Clear all dependencies.
+     *
+     * @hide
+     */
+    public void clearDependencies() {
+        mDependencies = null;
+    }
+
+    /**
+     * Gets the libraries this library directly depends on. Note that
+     * the package manager prevents recursive dependencies when installing
+     * a package.
+     *
+     * @return The dependencies.
+     *
+     * @hide
+     */
+    public @Nullable List<SharedLibraryInfo> getDependencies() {
+        return mDependencies;
+    }
+
+    /**
      * @deprecated Use {@link #getLongVersion()} instead.
      */
     @Deprecated
@@ -196,11 +271,14 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mPath);
+        parcel.writeString(mPackageName);
         parcel.writeString(mName);
         parcel.writeLong(mVersion);
         parcel.writeInt(mType);
         parcel.writeParcelable(mDeclaringPackage, flags);
         parcel.writeList(mDependentPackages);
+        parcel.writeTypedList(mDependencies);
     }
 
     private static String typeToString(int type) {
diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/SharedLibraryNames.java
index 387d29e8..5afc8a9 100644
--- a/core/java/android/content/pm/SharedLibraryNames.java
+++ b/core/java/android/content/pm/SharedLibraryNames.java
@@ -22,15 +22,15 @@
  */
 public class SharedLibraryNames {
 
-    static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java";
+    public static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java";
 
-    static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java";
+    public static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java";
 
-    static final String ANDROID_TEST_BASE = "android.test.base";
+    public static final String ANDROID_TEST_BASE = "android.test.base";
 
-    static final String ANDROID_TEST_MOCK = "android.test.mock";
+    public static final String ANDROID_TEST_MOCK = "android.test.mock";
 
-    static final String ANDROID_TEST_RUNNER = "android.test.runner";
+    public static final String ANDROID_TEST_RUNNER = "android.test.runner";
 
-    static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
+    public static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
 }
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 0350eff..5f23749 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1064,6 +1064,17 @@
         }
     }
 
+    @UnsupportedAppUsage
+    void setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr) {
+        synchronized (this) {
+            ensureValidLocked();
+            synchronized (srcAssetManager) {
+                srcAssetManager.ensureValidLocked();
+                nativeThemeCopy(mObject, dstThemePtr, srcAssetManager.mObject, srcThemePtr);
+            }
+        }
+    }
+
     @Override
     protected void finalize() throws Throwable {
         if (DEBUG_REFS && mNumRefs != 0) {
@@ -1375,7 +1386,8 @@
     private static native void nativeThemeDestroy(long themePtr);
     private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId,
             boolean force);
-    static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr);
+    private static native void nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr,
+            long srcAssetManagerPtr, long srcThemePtr);
     static native void nativeThemeClear(long themePtr);
     private static native int nativeThemeGetAttributeValue(long ptr, long themePtr,
             @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve);
diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java
index 6a4aae6..14eb11a 100644
--- a/core/java/android/content/res/FontResourcesParser.java
+++ b/core/java/android/content/res/FontResourcesParser.java
@@ -76,6 +76,10 @@
 
     // A class represents font element in xml file which points a file in resource.
     public static final class FontFileResourceEntry {
+        public static final int RESOLVE_BY_FONT_TABLE = Typeface.RESOLVE_BY_FONT_TABLE;
+        public static final int UPRIGHT = 0;
+        public static final int ITALIC = 1;
+
         private final @NonNull String mFileName;
         private int mWeight;
         private int mItalic;
@@ -216,7 +220,7 @@
         int weight = array.getInt(R.styleable.FontFamilyFont_fontWeight,
                 Typeface.RESOLVE_BY_FONT_TABLE);
         int italic = array.getInt(R.styleable.FontFamilyFont_fontStyle,
-                Typeface.RESOLVE_BY_FONT_TABLE);
+                FontFileResourceEntry.RESOLVE_BY_FONT_TABLE);
         String variationSettings = array.getString(
                 R.styleable.FontFamilyFont_fontVariationSettings);
         int ttcIndex = array.getInt(R.styleable.FontFamilyFont_ttcIndex, 0);
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index dfa30a2..2ad4f62 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -944,7 +944,8 @@
                 }
                 return Typeface.createFromResources(familyEntry, mAssets, file);
             }
-            return Typeface.createFromResources(mAssets, file, value.assetCookie);
+            return new Typeface.Builder(mAssets, file, false /* isAsset */, value.assetCookie)
+                    .build();
         } catch (XmlPullParserException e) {
             Log.e(TAG, "Failed to parse xml resource " + file, e);
         } catch (IOException e) {
@@ -1335,7 +1336,7 @@
         void setTo(ThemeImpl other) {
             synchronized (mKey) {
                 synchronized (other.mKey) {
-                    AssetManager.nativeThemeCopy(mTheme, other.mTheme);
+                    mAssets.setThemeTo(mTheme, other.mAssets, other.mTheme);
 
                     mThemeResId = other.mThemeResId;
                     mKey.setTo(other.getKey());
diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java
new file mode 100644
index 0000000..ae3d794
--- /dev/null
+++ b/core/java/android/debug/AdbManager.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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 android.debug;
+
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * This class allows the control of ADB-related functions. Currently only ADB over USB is
+ * supported, and none of the API is public.
+ *
+ * @hide
+ */
+@SystemService(Context.ADB_SERVICE)
+public class AdbManager {
+    private static final String TAG = "AdbManager";
+
+    private final Context mContext;
+    private final IAdbManager mService;
+
+    /**
+     * {@hide}
+     */
+    public AdbManager(Context context, IAdbManager service) {
+        mContext = context;
+        mService = service;
+    }
+}
diff --git a/core/java/android/debug/AdbManagerInternal.java b/core/java/android/debug/AdbManagerInternal.java
new file mode 100644
index 0000000..4469f0f
--- /dev/null
+++ b/core/java/android/debug/AdbManagerInternal.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 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 android.debug;
+
+/**
+ * This class allows the control of ADB-related functions that should only be called from the system
+ * server.
+ *
+ * @hide Only should be called from the system server.
+ */
+public abstract class AdbManagerInternal {
+    /**
+     * Registers a ADB transport mechanism.
+     *
+     * @param transport ADB transport interface to register
+     */
+    public abstract void registerTransport(IAdbTransport transport);
+
+    /**
+     * Unregisters a previously registered ADB transport mechanism.
+     *
+     * @param transport previously-added ADB transport interface to be removed
+     */
+    public abstract void unregisterTransport(IAdbTransport transport);
+
+    /**
+     * Returns {@code true} if ADB debugging is enabled.
+     */
+    public abstract boolean isAdbEnabled();
+}
diff --git a/core/java/android/debug/IAdbManager.aidl b/core/java/android/debug/IAdbManager.aidl
new file mode 100644
index 0000000..79e0794
--- /dev/null
+++ b/core/java/android/debug/IAdbManager.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 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 android.debug;
+
+/**
+ * Interface to communicate remotely with the {@code AdbService} in the system server.
+ *
+ * @hide
+ */
+interface IAdbManager {
+    /**
+     * Allow ADB debugging from the attached host. If {@code alwaysAllow} is
+     * {@code true}, add {@code publicKey} to list of host keys that the
+     * user has approved.
+     *
+     * @param alwaysAllow if true, add permanently to list of allowed keys
+     * @param publicKey RSA key in mincrypt format and Base64-encoded
+     */
+    void allowDebugging(boolean alwaysAllow, String publicKey);
+
+    /**
+     * Deny ADB debugging from the attached host.
+     */
+    void denyDebugging();
+
+    /**
+     * Clear all public keys installed for secure ADB debugging.
+     */
+    void clearDebuggingKeys();
+}
diff --git a/core/java/android/debug/IAdbTransport.aidl b/core/java/android/debug/IAdbTransport.aidl
new file mode 100644
index 0000000..77211fc93
--- /dev/null
+++ b/core/java/android/debug/IAdbTransport.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 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 android.debug;
+
+/** @hide */
+interface IAdbTransport {
+    void onAdbEnabled(boolean enabled);
+}
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 2a64c2e..c814b7c 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -38,7 +38,7 @@
      * BiometricManager.
      * @hide
      */
-    int BIOMETRIC_ERROR_NONE = 0;
+    int BIOMETRIC_SUCCESS = 0;
 
     /**
      * The hardware is unavailable. Try again later.
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index c788bc5..1d9330d 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -224,12 +224,25 @@
     public static final int FACE_ACQUIRED_RECALIBRATE = 13;
 
     /**
+     * The face is too different from a previous acquisition. This condition
+     * only applies to enrollment. This can happen if the user passes the
+     * device to someone else in the middle of enrollment.
+     */
+    public static final int FACE_ACQUIRED_TOO_DIFFERENT = 14;
+
+    /**
+     * The face is too similar to a previous acquisition. This condition only
+     * applies to enrollment. The user should change their pose.
+     */
+    public static final int FACE_ACQUIRED_TOO_SIMILAR = 15;
+
+    /**
      * Hardware vendors may extend this list if there are conditions that do not fall under one of
      * the above categories. Vendors are responsible for providing error strings for these errors.
      *
      * @hide
      */
-    public static final int FACE_ACQUIRED_VENDOR = 14;
+    public static final int FACE_ACQUIRED_VENDOR = 16;
 
     /**
      * @hide
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 1d40001..ff58c75 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -22,6 +22,7 @@
 import android.annotation.IntDef;
 import android.annotation.RequiresPermission;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.RemoteException;
 import android.util.Slog;
 
@@ -35,28 +36,48 @@
     /**
      * No error detected.
      */
-    public static final int ERROR_NONE = BiometricConstants.BIOMETRIC_ERROR_NONE;
+    public static final int BIOMETRIC_SUCCESS =
+            BiometricConstants.BIOMETRIC_SUCCESS;
 
     /**
      * The hardware is unavailable. Try again later.
      */
-    public static final int ERROR_UNAVAILABLE = BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+    public static final int BIOMETRIC_ERROR_UNAVAILABLE =
+            BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
 
     /**
      * The user does not have any biometrics enrolled.
      */
-    public static final int ERROR_NO_BIOMETRICS = BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS;
+    public static final int BIOMETRIC_ERROR_NO_BIOMETRICS =
+            BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS;
 
     /**
      * There is no biometric hardware.
      */
-    public static final int ERROR_NO_HARDWARE = BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
+    public static final int BIOMETRIC_ERROR_NO_HARDWARE =
+            BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
 
-    @IntDef({ERROR_NONE, ERROR_UNAVAILABLE, ERROR_NO_BIOMETRICS, ERROR_NO_HARDWARE})
+    @IntDef({BIOMETRIC_SUCCESS,
+            BIOMETRIC_ERROR_UNAVAILABLE,
+            BIOMETRIC_ERROR_NO_BIOMETRICS,
+            BIOMETRIC_ERROR_NO_HARDWARE})
     @interface BiometricError {}
 
     private final Context mContext;
     private final IBiometricService mService;
+    private final boolean mHasHardware;
+
+    /**
+     * @param context
+     * @return
+     * @hide
+     */
+    public static boolean hasBiometrics(Context context) {
+        final PackageManager pm = context.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
+                || pm.hasSystemFeature(PackageManager.FEATURE_IRIS)
+                || pm.hasSystemFeature(PackageManager.FEATURE_FACE);
+    }
 
     /**
      * @hide
@@ -66,15 +87,18 @@
     public BiometricManager(Context context, IBiometricService service) {
         mContext = context;
         mService = service;
+
+        mHasHardware = hasBiometrics(context);
     }
 
     /**
      * Determine if biometrics can be used. In other words, determine if {@link BiometricPrompt}
      * can be expected to be shown (hardware available, templates enrolled, user-enabled).
      *
-     * @return Returns {@link #ERROR_NO_BIOMETRICS} if the user does not have any enrolled, or
-     *     {@link #ERROR_UNAVAILABLE} if none are currently supported/enabled. Returns
-     *     {@link #ERROR_NONE} if a biometric can currently be used (enrolled and available).
+     * @return Returns {@link #BIOMETRIC_ERROR_NO_BIOMETRICS} if the user does not have any
+     *     enrolled, or {@link #BIOMETRIC_ERROR_UNAVAILABLE} if none are currently
+     *     supported/enabled. Returns {@link #BIOMETRIC_SUCCESS} if a biometric can currently be
+     *     used (enrolled and available).
      */
     @RequiresPermission(USE_BIOMETRIC)
     public @BiometricError int canAuthenticate() {
@@ -85,8 +109,12 @@
                 throw e.rethrowFromSystemServer();
             }
         } else {
-            Slog.w(TAG, "hasEnrolledBiometrics(): Service not connected");
-            return ERROR_UNAVAILABLE;
+            if (!mHasHardware) {
+                return BIOMETRIC_ERROR_NO_HARDWARE;
+            } else {
+                Slog.w(TAG, "hasEnrolledBiometrics(): Service not connected");
+                return BIOMETRIC_ERROR_UNAVAILABLE;
+            }
         }
     }
 
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 46e66e0..444ca87 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -22,12 +22,14 @@
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.PublicKey;
 import android.hardware.camera2.impl.SyntheticKey;
+import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
 import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.utils.ArrayUtils;
 import android.hardware.camera2.utils.TypeReference;
 import android.util.Rational;
 
 import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -193,6 +195,7 @@
     private List<CaptureRequest.Key<?>> mAvailableSessionKeys;
     private List<CaptureRequest.Key<?>> mAvailablePhysicalRequestKeys;
     private List<CaptureResult.Key<?>> mAvailableResultKeys;
+    private ArrayList<RecommendedStreamConfigurationMap> mRecommendedConfigurations;
 
     /**
      * Takes ownership of the passed-in properties object
@@ -313,6 +316,103 @@
     }
 
     /**
+     * <p>Retrieve camera device recommended stream configuration map
+     * {@link RecommendedStreamConfigurationMap} for a given use case.</p>
+     *
+     * <p>The stream configurations advertised here are efficient in terms of power and performance
+     * for common use cases like preview, video, snapshot, etc. The recommended maps are usually
+     * only small subsets of the exhaustive list provided in
+     * {@link #SCALER_STREAM_CONFIGURATION_MAP} and suggested for a particular use case by the
+     * camera device implementation. For further information about the expected configurations in
+     * various scenarios please refer to:
+     * <ul>
+     * <li>{@link RecommendedStreamConfigurationMap#USECASE_PREVIEW}</li>
+     * <li>{@link RecommendedStreamConfigurationMap#USECASE_RECORD}</li>
+     * <li>{@link RecommendedStreamConfigurationMap#USECASE_VIDEO_SNAPSHOT}</li>
+     * <li>{@link RecommendedStreamConfigurationMap#USECASE_SNAPSHOT}</li>
+     * <li>{@link RecommendedStreamConfigurationMap#USECASE_RAW}</li>
+     * <li>{@link RecommendedStreamConfigurationMap#USECASE_ZSL}</li>
+     * </ul>
+     * </p>
+     *
+     * <p>For example on how this can be used by camera clients to find out the maximum recommended
+     * preview and snapshot resolution, consider the following pseudo-code:
+     * </p>
+     * <pre><code>
+     * public static Size getMaxSize(Size... sizes) {
+     *     if (sizes == null || sizes.length == 0) {
+     *         throw new IllegalArgumentException("sizes was empty");
+     *     }
+     *
+     *     Size sz = sizes[0];
+     *     for (Size size : sizes) {
+     *         if (size.getWidth() * size.getHeight() &gt; sz.getWidth() * sz.getHeight()) {
+     *             sz = size;
+     *         }
+     *     }
+     *
+     *     return sz;
+     * }
+     *
+     * CameraCharacteristics characteristics =
+     *         cameraManager.getCameraCharacteristics(cameraId);
+     * RecommendedStreamConfigurationMap previewConfig =
+     *         characteristics.getRecommendedStreamConfigurationMap(
+     *                  RecommendedStreamConfigurationMap.USECASE_PREVIEW);
+     * RecommendedStreamConfigurationMap snapshotConfig =
+     *         characteristics.getRecommendedStreamConfigurationMap(
+     *                  RecommendedStreamConfigurationMap.USECASE_SNAPSHOT);
+     *
+     * if ((previewConfig != null) &amp;&amp; (snapshotConfig != null)) {
+     *
+     *      Set<Size> snapshotSizeSet = snapshotConfig.getOutputSizes(
+     *              ImageFormat.JPEG);
+     *      Size[] snapshotSizes = new Size[snapshotSizeSet.size()];
+     *      snapshotSizes = snapshotSizeSet.toArray(snapshotSizes);
+     *      Size suggestedMaxJpegSize = getMaxSize(snapshotSizes);
+     *
+     *      Set<Size> previewSizeSet = snapshotConfig.getOutputSizes(
+     *              ImageFormat.PRIVATE);
+     *      Size[] previewSizes = new Size[previewSizeSet.size()];
+     *      previewSizes = previewSizeSet.toArray(previewSizes);
+     *      Size suggestedMaxPreviewSize = getMaxSize(previewSizes);
+     * }
+     *
+     * </code></pre>
+     *
+     * <p>Similar logic can be used for other use cases as well.</p>
+     *
+     * <p>Support for recommended stream configurations is optional. In case there a no
+     * suggested configurations for the particular use case, please refer to
+     * {@link #SCALER_STREAM_CONFIGURATION_MAP} for the exhaustive available list.</p>
+     *
+     * @param usecase Use case id.
+     *
+     * @throws IllegalArgumentException In case the use case argument is invalid.
+     * @return Valid {@link RecommendedStreamConfigurationMap} or null in case the camera device
+     *         doesn't have any recommendation for this use case or the recommended configurations
+     *         are invalid.
+     */
+    public RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(
+            @RecommendedStreamConfigurationMap.RecommendedUsecase int usecase) {
+        if (((usecase >= RecommendedStreamConfigurationMap.USECASE_PREVIEW) &&
+                (usecase <= RecommendedStreamConfigurationMap.USECASE_RAW)) ||
+                ((usecase >= RecommendedStreamConfigurationMap.USECASE_VENDOR_START) &&
+                (usecase < RecommendedStreamConfigurationMap.MAX_USECASE_COUNT))) {
+            if (mRecommendedConfigurations == null) {
+                mRecommendedConfigurations = mProperties.getRecommendedStreamConfigurations();
+                if (mRecommendedConfigurations == null) {
+                    return null;
+                }
+            }
+
+            return mRecommendedConfigurations.get(usecase);
+        }
+
+        throw new IllegalArgumentException(String.format("Invalid use case: %d", usecase));
+    }
+
+    /**
      * <p>Returns a subset of {@link #getAvailableCaptureRequestKeys} keys that the
      * camera device can pass as part of the capture session initialization.</p>
      *
@@ -329,7 +429,7 @@
      * but clients should be aware and expect delays during their application.
      * An example usage scenario could look like this:</p>
      * <ul>
-     * <li>The camera client starts by quering the session parameter key list via
+     * <li>The camera client starts by querying the session parameter key list via
      *   {@link android.hardware.camera2.CameraCharacteristics#getAvailableSessionKeys }.</li>
      * <li>Before triggering the capture session create sequence, a capture request
      *   must be built via {@link CameraDevice#createCaptureRequest } using an
@@ -1583,7 +1683,7 @@
      *   {@link android.graphics.ImageFormat#RAW12 RAW12}.</li>
      * <li>Processed (but not-stalling): any non-RAW format without a stall duration.  Typically
      *   {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888},
-     *   {@link android.graphics.ImageFormat#NV21 NV21}, or {@link android.graphics.ImageFormat#YV12 YV12}.</li>
+     *   {@link android.graphics.ImageFormat#NV21 NV21}, {@link android.graphics.ImageFormat#YV12 YV12}, or {@link android.graphics.ImageFormat#Y8 Y8} .</li>
      * </ul>
      * <p><b>Range of valid values:</b><br></p>
      * <p>For processed (and stalling) format streams, &gt;= 1.</p>
@@ -1646,6 +1746,7 @@
      * <li>{@link android.graphics.ImageFormat#NV21 NV21}</li>
      * <li>{@link android.graphics.ImageFormat#YV12 YV12}</li>
      * <li>Implementation-defined formats, i.e. {@link android.hardware.camera2.params.StreamConfigurationMap#isOutputSupportedFor(Class) }</li>
+     * <li>{@link android.graphics.ImageFormat#Y8 Y8}</li>
      * </ul>
      * <p>For full guarantees, query {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration } with a
      * processed format -- it will return 0 for a non-stalling stream.</p>
@@ -2122,6 +2223,35 @@
      * or output will never hurt maximum frame rate (i.e.  {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration getOutputStallDuration(ImageFormat.PRIVATE, size)} is always 0),</p>
      * <p>Attempting to configure an input stream with output streams not
      * listed as available in this map is not valid.</p>
+     * <p>Additionally, if the camera device is MONOCHROME with Y8 support, it will also support
+     * the following map of formats if its dependent capability
+     * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}) is supported:</p>
+     * <table>
+     * <thead>
+     * <tr>
+     * <th align="left">Input Format</th>
+     * <th align="left">Output Format</th>
+     * <th align="left">Capability</th>
+     * </tr>
+     * </thead>
+     * <tbody>
+     * <tr>
+     * <td align="left">{@link android.graphics.ImageFormat#PRIVATE }</td>
+     * <td align="left">{@link android.graphics.ImageFormat#Y8 }</td>
+     * <td align="left">PRIVATE_REPROCESSING</td>
+     * </tr>
+     * <tr>
+     * <td align="left">{@link android.graphics.ImageFormat#Y8 }</td>
+     * <td align="left">{@link android.graphics.ImageFormat#JPEG }</td>
+     * <td align="left">YUV_REPROCESSING</td>
+     * </tr>
+     * <tr>
+     * <td align="left">{@link android.graphics.ImageFormat#Y8 }</td>
+     * <td align="left">{@link android.graphics.ImageFormat#Y8 }</td>
+     * <td align="left">YUV_REPROCESSING</td>
+     * </tr>
+     * </tbody>
+     * </table>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
@@ -2297,6 +2427,7 @@
      * <li>{@link android.graphics.ImageFormat#YUV_420_888 }</li>
      * <li>{@link android.graphics.ImageFormat#RAW10 }</li>
      * <li>{@link android.graphics.ImageFormat#RAW12 }</li>
+     * <li>{@link android.graphics.ImageFormat#Y8 }</li>
      * </ul>
      * <p>All other formats may or may not have an allowed stall duration on
      * a per-capability basis; refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
@@ -2447,6 +2578,37 @@
             new Key<Integer>("android.scaler.croppingType", int.class);
 
     /**
+     * <p>Recommended stream configurations for common client use cases.</p>
+     * <p>Optional subset of the android.scaler.availableStreamConfigurations that contains
+     * similar tuples listed as
+     * (i.e. width, height, format, output/input stream, usecase bit field).
+     * Camera devices will be able to suggest particular stream configurations which are
+     * power and performance efficient for specific use cases. For more information about
+     * retrieving the suggestions see
+     * {@link android.hardware.camera2.CameraCharacteristics#getRecommendedStreamConfigurationMap }.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.RecommendedStreamConfiguration[]> SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS =
+            new Key<android.hardware.camera2.params.RecommendedStreamConfiguration[]>("android.scaler.availableRecommendedStreamConfigurations", android.hardware.camera2.params.RecommendedStreamConfiguration[].class);
+
+    /**
+     * <p>Recommended mappings of image formats that are supported by this
+     * camera device for input streams, to their corresponding output formats.</p>
+     * <p>This is a recommended subset of the complete list of mappings found in
+     * android.scaler.availableInputOutputFormatsMap. The same requirements apply here as well.
+     * The list however doesn't need to contain all available and supported mappings. Instead of
+     * this developers must list only recommended and efficient entries.
+     * If set, the information will be available in the ZERO_SHUTTER_LAG recommended stream
+     * configuration see
+     * {@link android.hardware.camera2.CameraCharacteristics#getRecommendedStreamConfigurationMap }.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.ReprocessFormatsMap> SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP =
+            new Key<android.hardware.camera2.params.ReprocessFormatsMap>("android.scaler.availableRecommendedInputOutputFormatsMap", android.hardware.camera2.params.ReprocessFormatsMap.class);
+
+    /**
      * <p>The area of the image sensor which corresponds to active pixels after any geometric
      * distortion correction has been applied.</p>
      * <p>This is the rectangle representing the size of the active region of the sensor (i.e.
@@ -3487,6 +3649,21 @@
             new Key<Boolean>("android.depth.depthIsExclusive", boolean.class);
 
     /**
+     * <p>Recommended depth stream configurations for common client use cases.</p>
+     * <p>Optional subset of the android.depth.availableDepthStreamConfigurations that
+     * contains similar tuples listed as
+     * (i.e. width, height, format, output/input stream, usecase bit field).
+     * Camera devices will be able to suggest particular depth stream configurations which are
+     * power and performance efficient for specific use cases. For more information about
+     * retrieving the suggestions see
+     * {@link android.hardware.camera2.CameraCharacteristics#getRecommendedStreamConfigurationMap }.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.RecommendedStreamConfiguration[]> DEPTH_AVAILABLE_RECOMMENDED_DEPTH_STREAM_CONFIGURATIONS =
+            new Key<android.hardware.camera2.params.RecommendedStreamConfiguration[]>("android.depth.availableRecommendedDepthStreamConfigurations", android.hardware.camera2.params.RecommendedStreamConfiguration[].class);
+
+    /**
      * <p>String containing the ids of the underlying physical cameras.</p>
      * <p>For a logical camera, this is concatenation of all underlying physical camera ids.
      * The null terminator for physical camera id must be preserved so that the whole string
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index ce88697..ac00f14 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -356,6 +356,12 @@
      * </table><br>
      * </p>
      *
+     * <p>MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
+     * includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME})
+     * supporting {@link android.graphics.ImageFormat#Y8 Y8} support substituting {@code YUV}
+     * streams with {@code Y8} in all guaranteed stream combinations for the device's hardware level
+     * and capabilities.</p>
+     *
      * <p>FULL-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
      * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) devices
      * support at least the following stream combinations in addition to those for
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index d4dc181..ffc2264 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -539,6 +539,8 @@
      * <li>{@link android.graphics.ImageFormat#PRIVATE } will be reprocessable into both
      *   {@link android.graphics.ImageFormat#YUV_420_888 } and
      *   {@link android.graphics.ImageFormat#JPEG } formats.</li>
+     * <li>For a MONOCHROME camera supporting Y8 format, {@link android.graphics.ImageFormat#PRIVATE } will be reprocessable into
+     *   {@link android.graphics.ImageFormat#Y8 }.</li>
      * <li>The maximum available resolution for PRIVATE streams
      *   (both input/output) will match the maximum available
      *   resolution of JPEG streams.</li>
@@ -612,7 +614,7 @@
      * then the list of resolutions for YUV_420_888 from {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } contains at
      * least one resolution &gt;= 8 megapixels, with a minimum frame duration of &lt;= 1/20
      * s.</p>
-     * <p>If the device supports the {@link android.graphics.ImageFormat#RAW10 }, {@link android.graphics.ImageFormat#RAW12 }, then those can also be
+     * <p>If the device supports the {@link android.graphics.ImageFormat#RAW10 }, {@link android.graphics.ImageFormat#RAW12 }, {@link android.graphics.ImageFormat#Y8 }, then those can also be
      * captured at the same rate as the maximum-size YUV_420_888 resolution is.</p>
      * <p>If the device supports the PRIVATE_REPROCESSING capability, then the same guarantees
      * as for the YUV_420_888 format also apply to the {@link android.graphics.ImageFormat#PRIVATE } format.</p>
@@ -646,6 +648,8 @@
      *   {@link android.graphics.ImageFormat#YUV_420_888 } and {@link android.graphics.ImageFormat#JPEG } formats.</li>
      * <li>The maximum available resolution for {@link android.graphics.ImageFormat#YUV_420_888 } streams (both input/output) will match the
      *   maximum available resolution of {@link android.graphics.ImageFormat#JPEG } streams.</li>
+     * <li>For a MONOCHROME camera with Y8 format support, all the requirements mentioned
+     *   above for YUV_420_888 apply for Y8 format as well.</li>
      * <li>Static metadata {@link CameraCharacteristics#REPROCESS_MAX_CAPTURE_STALL android.reprocess.maxCaptureStall}.</li>
      * <li>Only the below controls are effective for reprocessing requests and will be present
      *   in capture results. The reprocess requests are from the original capture results
@@ -692,8 +696,8 @@
      * <li>The {@link CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE android.depth.depthIsExclusive} entry is listed by this device.</li>
      * <li>As of Android P, the {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} entry is listed by this device.</li>
      * <li>A LIMITED camera with only the DEPTH_OUTPUT capability does not have to support
-     *   normal YUV_420_888, JPEG, and PRIV-format outputs. It only has to support the DEPTH16
-     *   format.</li>
+     *   normal YUV_420_888, Y8, JPEG, and PRIV-format outputs. It only has to support the
+     *   DEPTH16 format.</li>
      * </ul>
      * <p>Generally, depth output operates at a slower frame rate than standard color capture,
      * so the DEPTH16 and DEPTH_POINT_CLOUD formats will commonly have a stall duration that
@@ -877,6 +881,10 @@
     /**
      * <p>The camera device is a monochrome camera that doesn't contain a color filter array,
      * and the pixel values on U and V planes are all 128.</p>
+     * <p>A MONOCHROME camera must support the guaranteed stream combinations required for
+     * its device level and capabilities. Additionally, if the monochrome camera device
+     * supports Y8 format, all mandatory stream combination requirements related to {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888} apply
+     * to {@link android.graphics.ImageFormat#Y8 Y8} as well.</p>
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME = 12;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 4a20468..2744e91 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -3277,8 +3277,8 @@
      * will not slow down capture rate when applying correction. FAST may be the same as OFF if
      * any correction at all would slow down capture rate.  Every output stream will have a
      * similar amount of enhancement applied.</p>
-     * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
-     * applied to any RAW output.</p>
+     * <p>The correction only applies to processed outputs such as YUV, Y8, JPEG, or DEPTH16; it is
+     * not applied to any RAW output.</p>
      * <p>This control will be on by default on devices that support this control. Applications
      * disabling distortion correction need to pay extra attention with the coordinate system of
      * metering regions, crop region, and face rectangles. When distortion correction is OFF,
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index a7e185c..2b67f3e 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -4620,8 +4620,8 @@
      * will not slow down capture rate when applying correction. FAST may be the same as OFF if
      * any correction at all would slow down capture rate.  Every output stream will have a
      * similar amount of enhancement applied.</p>
-     * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
-     * applied to any RAW output.</p>
+     * <p>The correction only applies to processed outputs such as YUV, Y8, JPEG, or DEPTH16; it is
+     * not applied to any RAW output.</p>
      * <p>This control will be on by default on devices that support this control. Applications
      * disabling distortion correction need to pay extra attention with the coordinate system of
      * metering regions, crop region, and face rectangles. When distortion correction is OFF,
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 0610d7a..f81bd13 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -21,6 +21,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.marshal.MarshalQueryable;
@@ -38,6 +39,7 @@
 import android.hardware.camera2.marshal.impl.MarshalQueryableParcelable;
 import android.hardware.camera2.marshal.impl.MarshalQueryablePrimitive;
 import android.hardware.camera2.marshal.impl.MarshalQueryableRange;
+import android.hardware.camera2.marshal.impl.MarshalQueryableRecommendedStreamConfiguration;
 import android.hardware.camera2.marshal.impl.MarshalQueryableRect;
 import android.hardware.camera2.marshal.impl.MarshalQueryableReprocessFormatsMap;
 import android.hardware.camera2.marshal.impl.MarshalQueryableRggbChannelVector;
@@ -50,6 +52,8 @@
 import android.hardware.camera2.params.HighSpeedVideoConfiguration;
 import android.hardware.camera2.params.LensShadingMap;
 import android.hardware.camera2.params.OisSample;
+import android.hardware.camera2.params.RecommendedStreamConfiguration;
+import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
 import android.hardware.camera2.params.ReprocessFormatsMap;
 import android.hardware.camera2.params.StreamConfiguration;
 import android.hardware.camera2.params.StreamConfigurationDuration;
@@ -911,6 +915,252 @@
         return true;
     }
 
+    private void parseRecommendedConfigurations(RecommendedStreamConfiguration[] configurations,
+            StreamConfigurationMap fullMap, boolean isDepth,
+            ArrayList<ArrayList<StreamConfiguration>> /*out*/streamConfigList,
+            ArrayList<ArrayList<StreamConfigurationDuration>> /*out*/streamDurationList,
+            ArrayList<ArrayList<StreamConfigurationDuration>> /*out*/streamStallList,
+            boolean[] /*out*/supportsPrivate) {
+
+        streamConfigList.ensureCapacity(RecommendedStreamConfigurationMap.MAX_USECASE_COUNT);
+        streamDurationList.ensureCapacity(RecommendedStreamConfigurationMap.MAX_USECASE_COUNT);
+        streamStallList.ensureCapacity(RecommendedStreamConfigurationMap.MAX_USECASE_COUNT);
+        for (int i = 0; i < RecommendedStreamConfigurationMap.MAX_USECASE_COUNT; i++) {
+            streamConfigList.add(new ArrayList<StreamConfiguration> ());
+            streamDurationList.add(new ArrayList<StreamConfigurationDuration> ());
+            streamStallList.add(new ArrayList<StreamConfigurationDuration> ());
+        }
+
+        for (RecommendedStreamConfiguration c : configurations) {
+            int width = c.getWidth();
+            int height = c.getHeight();
+            int internalFormat = c.getFormat();
+            int publicFormat =
+                (isDepth) ? StreamConfigurationMap.depthFormatToPublic(internalFormat) :
+                StreamConfigurationMap.imageFormatToPublic(internalFormat);
+            Size sz = new Size(width, height);
+            int usecaseBitmap = c.getUsecaseBitmap();
+
+            if (!c.isInput()) {
+                StreamConfigurationDuration minDurationConfiguration = null;
+                StreamConfigurationDuration stallDurationConfiguration = null;
+
+                StreamConfiguration streamConfiguration = new StreamConfiguration(internalFormat,
+                        width, height, /*input*/ false);
+
+                long minFrameDuration = fullMap.getOutputMinFrameDuration(publicFormat, sz);
+                if (minFrameDuration > 0) {
+                    minDurationConfiguration = new StreamConfigurationDuration(internalFormat,
+                            width, height, minFrameDuration);
+                }
+
+                long stallDuration = fullMap.getOutputStallDuration(publicFormat, sz);
+                if (stallDuration > 0) {
+                    stallDurationConfiguration = new StreamConfigurationDuration(internalFormat,
+                            width, height, stallDuration);
+                }
+
+                for (int i = 0; i < RecommendedStreamConfigurationMap.MAX_USECASE_COUNT; i++) {
+                    if ((usecaseBitmap & (1 << i)) != 0) {
+                        ArrayList<StreamConfiguration> sc = streamConfigList.get(i);
+                        sc.add(streamConfiguration);
+
+                        if (minFrameDuration > 0) {
+                            ArrayList<StreamConfigurationDuration> scd = streamDurationList.get(i);
+                            scd.add(minDurationConfiguration);
+                        }
+
+                        if (stallDuration > 0) {
+                            ArrayList<StreamConfigurationDuration> scs = streamStallList.get(i);
+                            scs.add(stallDurationConfiguration);
+                        }
+
+                        if ((supportsPrivate != null) && !supportsPrivate[i] &&
+                                (publicFormat == ImageFormat.PRIVATE)) {
+                            supportsPrivate[i] = true;
+                        }
+                    }
+                }
+            } else {
+                if (usecaseBitmap != (1 << RecommendedStreamConfigurationMap.USECASE_ZSL)) {
+                    throw new IllegalArgumentException("Recommended input stream configurations " +
+                            "should only be advertised in the ZSL use case!");
+                }
+
+                ArrayList<StreamConfiguration> sc = streamConfigList.get(
+                        RecommendedStreamConfigurationMap.USECASE_ZSL);
+                sc.add(new StreamConfiguration(internalFormat,
+                        width, height, /*input*/ true));
+            }
+        }
+    }
+
+    private class StreamConfigurationData {
+        StreamConfiguration [] streamConfigurationArray = null;
+        StreamConfigurationDuration [] minDurationArray = null;
+        StreamConfigurationDuration [] stallDurationArray = null;
+    }
+
+    public void initializeStreamConfigurationData(ArrayList<StreamConfiguration> sc,
+            ArrayList<StreamConfigurationDuration> scd, ArrayList<StreamConfigurationDuration> scs,
+            StreamConfigurationData /*out*/scData) {
+        if ((scData == null) || (sc == null)) {
+            return;
+        }
+
+        scData.streamConfigurationArray = new StreamConfiguration[sc.size()];
+        scData.streamConfigurationArray = sc.toArray(scData.streamConfigurationArray);
+
+        if ((scd != null) && !scd.isEmpty()) {
+            scData.minDurationArray = new StreamConfigurationDuration[scd.size()];
+            scData.minDurationArray = scd.toArray(scData.minDurationArray);
+        } else {
+            scData.minDurationArray = new StreamConfigurationDuration[0];
+        }
+
+        if ((scs != null) && !scs.isEmpty()) {
+            scData.stallDurationArray = new StreamConfigurationDuration[scs.size()];
+            scData.stallDurationArray = scs.toArray(scData.stallDurationArray);
+        } else {
+            scData.stallDurationArray = new StreamConfigurationDuration[0];
+        }
+    }
+
+    /**
+     * Retrieve the list of recommended stream configurations.
+     *
+     * @return A list of recommended stream configuration maps for each common use case or null
+     *         in case the recommended stream configurations are invalid or incomplete.
+     * @hide
+     */
+    public ArrayList<RecommendedStreamConfigurationMap> getRecommendedStreamConfigurations() {
+        RecommendedStreamConfiguration[] configurations = getBase(
+                CameraCharacteristics.SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS);
+        RecommendedStreamConfiguration[] depthConfigurations = getBase(
+                CameraCharacteristics.DEPTH_AVAILABLE_RECOMMENDED_DEPTH_STREAM_CONFIGURATIONS);
+        if ((configurations == null) && (depthConfigurations == null)) {
+            return null;
+        }
+
+        StreamConfigurationMap fullMap = getStreamConfigurationMap();
+        ArrayList<RecommendedStreamConfigurationMap> recommendedConfigurations =
+            new ArrayList<RecommendedStreamConfigurationMap> ();
+
+        ArrayList<ArrayList<StreamConfiguration>> streamConfigList =
+            new ArrayList<ArrayList<StreamConfiguration>>();
+        ArrayList<ArrayList<StreamConfigurationDuration>> streamDurationList =
+            new ArrayList<ArrayList<StreamConfigurationDuration>>();
+        ArrayList<ArrayList<StreamConfigurationDuration>> streamStallList =
+            new ArrayList<ArrayList<StreamConfigurationDuration>>();
+        boolean[] supportsPrivate =
+                new boolean[RecommendedStreamConfigurationMap.MAX_USECASE_COUNT];
+        try {
+            if (configurations != null) {
+                parseRecommendedConfigurations(configurations, fullMap, /*isDepth*/ false,
+                        streamConfigList, streamDurationList, streamStallList, supportsPrivate);
+            }
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Failed parsing the recommended stream configurations!");
+            return null;
+        }
+
+        ArrayList<ArrayList<StreamConfiguration>> depthStreamConfigList =
+            new ArrayList<ArrayList<StreamConfiguration>>();
+        ArrayList<ArrayList<StreamConfigurationDuration>> depthStreamDurationList =
+            new ArrayList<ArrayList<StreamConfigurationDuration>>();
+        ArrayList<ArrayList<StreamConfigurationDuration>> depthStreamStallList =
+            new ArrayList<ArrayList<StreamConfigurationDuration>>();
+        if (depthConfigurations != null) {
+            try {
+                parseRecommendedConfigurations(depthConfigurations, fullMap, /*isDepth*/ true,
+                        depthStreamConfigList, depthStreamDurationList, depthStreamStallList,
+                        /*supportsPrivate*/ null);
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "Failed parsing the recommended depth stream configurations!");
+                return null;
+            }
+        }
+
+        ReprocessFormatsMap inputOutputFormatsMap = getBase(
+                CameraCharacteristics.SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP);
+        HighSpeedVideoConfiguration[] highSpeedVideoConfigurations = getBase(
+                CameraCharacteristics.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS);
+        boolean listHighResolution = isBurstSupported();
+        recommendedConfigurations.ensureCapacity(
+                RecommendedStreamConfigurationMap.MAX_USECASE_COUNT);
+        for (int i = 0; i < RecommendedStreamConfigurationMap.MAX_USECASE_COUNT; i++) {
+            StreamConfigurationData scData = new StreamConfigurationData();
+            if (configurations != null) {
+                initializeStreamConfigurationData(streamConfigList.get(i),
+                        streamDurationList.get(i), streamStallList.get(i), scData);
+            }
+
+            StreamConfigurationData depthScData = new StreamConfigurationData();
+            if (depthConfigurations != null) {
+                initializeStreamConfigurationData(depthStreamConfigList.get(i),
+                        depthStreamDurationList.get(i), depthStreamStallList.get(i), depthScData);
+            }
+
+            if ((scData.streamConfigurationArray == null) &&
+                    (depthScData.streamConfigurationArray == null)) {
+                recommendedConfigurations.add(null);
+                continue;
+            }
+
+            StreamConfigurationMap map = null;
+            switch (i) {
+                case RecommendedStreamConfigurationMap.USECASE_PREVIEW:
+                case RecommendedStreamConfigurationMap.USECASE_RAW:
+                case RecommendedStreamConfigurationMap.USECASE_VIDEO_SNAPSHOT:
+                    map = new StreamConfigurationMap(scData.streamConfigurationArray,
+                            scData.minDurationArray, scData.stallDurationArray,
+                            /*depthconfiguration*/ null, /*depthminduration*/ null,
+                            /*depthstallduration*/ null, /*highspeedvideoconfigurations*/ null,
+                            /*inputoutputformatsmap*/ null, listHighResolution, supportsPrivate[i]);
+                    break;
+                case RecommendedStreamConfigurationMap.USECASE_RECORD:
+                    map = new StreamConfigurationMap(scData.streamConfigurationArray,
+                            scData.minDurationArray, scData.stallDurationArray,
+                            /*depthconfiguration*/ null, /*depthminduration*/ null,
+                            /*depthstallduration*/ null, highSpeedVideoConfigurations,
+                            /*inputoutputformatsmap*/ null, listHighResolution, supportsPrivate[i]);
+                    break;
+                case RecommendedStreamConfigurationMap.USECASE_ZSL:
+                    map = new StreamConfigurationMap(scData.streamConfigurationArray,
+                            scData.minDurationArray, scData.stallDurationArray,
+                            depthScData.streamConfigurationArray, depthScData.minDurationArray,
+                            depthScData.stallDurationArray, /*highSpeedVideoConfigurations*/ null,
+                            inputOutputFormatsMap, listHighResolution, supportsPrivate[i]);
+                    break;
+                default:
+                    map = new StreamConfigurationMap(scData.streamConfigurationArray,
+                            scData.minDurationArray, scData.stallDurationArray,
+                            depthScData.streamConfigurationArray, depthScData.minDurationArray,
+                            depthScData.stallDurationArray, /*highSpeedVideoConfigurations*/ null,
+                            /*inputOutputFormatsMap*/ null, listHighResolution, supportsPrivate[i]);
+            }
+
+            recommendedConfigurations.add(new RecommendedStreamConfigurationMap(map, /*usecase*/i,
+                        supportsPrivate[i]));
+        }
+
+        return recommendedConfigurations;
+    }
+
+    private boolean isBurstSupported() {
+        boolean ret = false;
+
+        int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+        for (int capability : capabilities) {
+            if (capability == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE) {
+                ret = true;
+                break;
+            }
+        }
+
+        return ret;
+    }
+
     private StreamConfigurationMap getStreamConfigurationMap() {
         StreamConfiguration[] configurations = getBase(
                 CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
@@ -928,14 +1178,7 @@
                 CameraCharacteristics.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS);
         ReprocessFormatsMap inputOutputFormatsMap = getBase(
                 CameraCharacteristics.SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP);
-        int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
-        boolean listHighResolution = false;
-        for (int capability : capabilities) {
-            if (capability == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE) {
-                listHighResolution = true;
-                break;
-            }
-        }
+        boolean listHighResolution = isBurstSupported();
         return new StreamConfigurationMap(
                 configurations, minFrameDurations, stallDurations,
                 depthConfigurations, depthMinFrameDurations, depthStallDurations,
@@ -1399,6 +1642,7 @@
                 new MarshalQueryableRggbChannelVector(),
                 new MarshalQueryableBlackLevelPattern(),
                 new MarshalQueryableHighSpeedVideoConfiguration(),
+                new MarshalQueryableRecommendedStreamConfiguration(),
 
                 // generic parcelable marshaler (MUST BE LAST since it has lowest priority)
                 new MarshalQueryableParcelable(),
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRecommendedStreamConfiguration.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRecommendedStreamConfiguration.java
new file mode 100644
index 0000000..22a76b7
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableRecommendedStreamConfiguration.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 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 android.hardware.camera2.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.params.RecommendedStreamConfiguration;
+import android.hardware.camera2.utils.TypeReference;
+
+import static android.hardware.camera2.impl.CameraMetadataNative.*;
+import static android.hardware.camera2.marshal.MarshalHelpers.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Marshaler for {@code android.scaler.availableRecommendedStreamConfigurations} custom class
+ * {@link RecommendedStreamConfiguration}
+ *
+ * <p>Data is stored as {@code (width, height, format, input, usecaseBitmap)} tuples (int32).</p>
+ */
+public class MarshalQueryableRecommendedStreamConfiguration
+        implements MarshalQueryable<RecommendedStreamConfiguration> {
+    private static final int SIZE = SIZEOF_INT32 * 5;
+
+    private class MarshalerRecommendedStreamConfiguration
+            extends Marshaler<RecommendedStreamConfiguration> {
+        protected MarshalerRecommendedStreamConfiguration(
+                TypeReference<RecommendedStreamConfiguration> typeReference, int nativeType) {
+            super(MarshalQueryableRecommendedStreamConfiguration.this, typeReference, nativeType);
+        }
+
+        @Override
+        public void marshal(RecommendedStreamConfiguration value, ByteBuffer buffer) {
+            buffer.putInt(value.getWidth());
+            buffer.putInt(value.getHeight());
+            buffer.putInt(value.getFormat());
+            buffer.putInt(value.isInput() ? 1 : 0);
+            buffer.putInt(value.getUsecaseBitmap());
+        }
+
+        @Override
+        public RecommendedStreamConfiguration unmarshal(ByteBuffer buffer) {
+            int width = buffer.getInt();
+            int height = buffer.getInt();
+            int format = buffer.getInt();
+            boolean input = buffer.getInt() != 0;
+            int usecaseBitmap = buffer.getInt();
+
+            return new RecommendedStreamConfiguration(format, width, height, input, usecaseBitmap);
+        }
+
+        @Override
+        public int getNativeSize() {
+            return SIZE;
+        }
+
+    }
+
+    @Override
+    public Marshaler<RecommendedStreamConfiguration> createMarshaler(
+            TypeReference<RecommendedStreamConfiguration> managedType, int nativeType) {
+        return new MarshalerRecommendedStreamConfiguration(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<RecommendedStreamConfiguration> managedType,
+            int nativeType) {
+        return nativeType ==
+            TYPE_INT32 && managedType.getType().equals(RecommendedStreamConfiguration.class);
+    }
+}
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfiguration.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfiguration.java
new file mode 100644
index 0000000..5dd0517
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfiguration.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 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 android.hardware.camera2.params;
+
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.utils.HashCodeHelpers;
+
+/**
+ * Immutable class to store the recommended stream configurations to set up
+ * {@link android.view.Surface Surfaces} for creating a {@link CameraCaptureSession capture session}
+ * with {@link CameraDevice#createCaptureSession}.
+ *
+ * @see CameraCharacteristics#getRecommendedStreamConfigurationMap
+ *
+ * @hide
+ */
+public final class RecommendedStreamConfiguration extends StreamConfiguration{
+
+    /**
+     * Create a new {@link RecommendedStreamConfiguration}.
+     *
+     * @param format image format
+     * @param width image width, in pixels (positive)
+     * @param height image height, in pixels (positive)
+     * @param input true if this is an input configuration, false for output configurations
+     * @param usecaseBitmap Use case bitmap
+     *
+     * @throws IllegalArgumentException
+     *              if width/height were not positive
+     *              or if the format was not user-defined in ImageFormat/PixelFormat
+     *                  (IMPL_DEFINED is ok)
+     *
+     * @hide
+     */
+    public RecommendedStreamConfiguration(
+            final int format, final int width, final int height, final boolean input, final int
+            usecaseBitmap) {
+        super(format, width, height, input);
+        mUsecaseBitmap = usecaseBitmap;
+    }
+
+    /**
+     * Return usecase bitmap.
+     *
+     * @return usecase bitmap
+     */
+    public int getUsecaseBitmap() {
+        return mUsecaseBitmap;
+    }
+
+    /**
+     * Check if this {@link RecommendedStreamConfiguration} is equal to another
+     * {@link RecommendedStreamConfiguration}.
+     *
+     * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p>
+     *
+     * @return {@code true} if the objects were equal, {@code false} otherwise
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof RecommendedStreamConfiguration) {
+            final RecommendedStreamConfiguration other = (RecommendedStreamConfiguration) obj;
+            return mFormat == other.mFormat &&
+                    mWidth == other.mWidth &&
+                    mHeight == other.mHeight &&
+                    mUsecaseBitmap == other.mUsecaseBitmap &&
+                    mInput == other.mInput;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return HashCodeHelpers.hashCode(mFormat, mWidth, mHeight, mInput ? 1 : 0, mUsecaseBitmap);
+    }
+
+    private final int mUsecaseBitmap;
+}
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
new file mode 100644
index 0000000..59e4a33
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2018 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 android.hardware.camera2.params;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.util.ArraySet;
+import android.util.Range;
+import android.util.Size;
+import android.view.Surface;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Immutable class to store the recommended stream configurations to set up
+ * {@link android.view.Surface Surfaces} for creating a
+ * {@link android.hardware.camera2.CameraCaptureSession capture session} with
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession}.
+ *
+ * <p>The recommended list does not replace or deprecate the exhaustive complete list found in
+ * {@link StreamConfigurationMap}. It is a suggestion about available power and performance
+ * efficient stream configurations for a specific use case. Per definition it is only a subset
+ * of {@link StreamConfigurationMap} and can be considered by developers for optimization
+ * purposes.</p>
+ *
+ * <p>This also duplicates the minimum frame durations and stall durations from the
+ * {@link StreamConfigurationMap} for each format/size combination that can be used to calculate
+ * effective frame rate when submitting multiple captures.
+ * </p>
+ *
+ * <p>An instance of this object is available by invoking
+ * {@link CameraCharacteristics#getRecommendedStreamConfigurationMap} and passing a respective
+ * usecase id. For more information about supported use case constants see
+ * {@link #USECASE_PREVIEW}.</p>
+ *
+ * <pre><code>{@code
+ * CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
+ * RecommendedStreamConfigurationMap configs = characteristics.getRecommendedStreamConfigurationMap(
+ *         RecommendedStreamConfigurationMap.USECASE_PREVIEW);
+ * }</code></pre>
+ *
+ * @see CameraCharacteristics#getRecommendedStreamConfigurationMap
+ * @see CameraDevice#createCaptureSession
+ */
+public final class RecommendedStreamConfigurationMap {
+
+    private static final String TAG = "RecommendedStreamConfigurationMap";
+    private int mUsecase;
+    private boolean mSupportsPrivate;
+    private StreamConfigurationMap mRecommendedMap;
+
+    /** @hide */
+    public static final int MAX_USECASE_COUNT = 32;
+
+    /**
+     * The recommended stream configuration map for use case preview must contain a subset of
+     * efficient, non-stalling configurations that must include both
+     * {@link android.graphics.ImageFormat#PRIVATE} and
+     * {@link android.graphics.ImageFormat#YUV_420_888} output formats. Even if available for the
+     * camera device, high speed or input configurations will be absent.
+     */
+    public static final int USECASE_PREVIEW = 0x0;
+
+    /**
+     * The recommended stream configuration map for recording must contain a subset of efficient
+     * video configurations that include {@link android.graphics.ImageFormat#PRIVATE}
+     * output format for at least all supported {@link android.media.CamcorderProfile profiles}.
+     * High speed configurations if supported will be available as well. Even if available for the
+     * camera device, input configurations will be absent.
+     */
+    public static final int USECASE_RECORD = 0x1;
+
+    /**
+     * The recommended stream configuration map for use case video snapshot must only contain a
+     * subset of efficient liveshot configurations that include
+     * {@link android.graphics.ImageFormat#JPEG} output format. The sizes will match at least
+     * the maximum resolution of usecase record and will not cause any preview glitches. Even
+     * if available for the camera device, high speed or input configurations will be absent.
+     */
+    public static final int USECASE_VIDEO_SNAPSHOT = 0x2;
+
+    /**
+     * The recommended stream configuration map for use case snapshot must contain a subset of
+     * efficient still capture configurations that must include
+     * {@link android.graphics.ImageFormat#JPEG} output format and at least one configuration with
+     * size approximately equal to the sensor pixel array size
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
+     * Even if available for the camera device, high speed or input configurations will be absent.
+     */
+    public static final int USECASE_SNAPSHOT = 0x3;
+
+    /**
+     * In case the device supports
+     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING} and/or
+     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING},
+     * the recommended stream configuration map for use case ZSL must contain a subset of efficient
+     * configurations that include the suggested input and output format mappings. Even if
+     * available for the camera device, high speed configurations will be absent.
+     */
+    public static final int USECASE_ZSL = 0x4;
+
+    /**
+     * In case the device supports
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW}, the
+     * recommended stream configuration map for use case RAW must contain a subset of efficient
+     * configurations that include the {@link android.graphics.ImageFormat#RAW_SENSOR} and other
+     * RAW output formats. Even if available for the camera device, high speed and input
+     * configurations will be absent.
+     */
+    public static final int USECASE_RAW = 0x5;
+
+    /**
+     * Device specific use cases.
+     * @hide
+     */
+    public static final int USECASE_VENDOR_START = 0x18;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"USECASE_"}, value =
+        {USECASE_PREVIEW,
+        USECASE_RECORD,
+        USECASE_VIDEO_SNAPSHOT,
+        USECASE_SNAPSHOT,
+        USECASE_ZSL,
+        USECASE_RAW })
+     public @interface RecommendedUsecase {};
+
+    /**
+     * Create a new {@link RecommendedStreamConfigurationMap}.
+     *
+     * @param recommendedMap stream configuration map that contains for the specific use case
+     * @param usecase Recommended use case
+     * @param supportsPrivate Flag indicating private format support.
+     *
+     * @hide
+     */
+    public RecommendedStreamConfigurationMap(StreamConfigurationMap recommendedMap, int usecase,
+            boolean supportsPrivate) {
+        mRecommendedMap = recommendedMap;
+        mUsecase = usecase;
+        mSupportsPrivate = supportsPrivate;
+    }
+
+    /**
+     * Get the use case value for the recommended stream configurations.
+     *
+     * @return Use case id.
+     */
+    public int getRecommendedUseCase() {
+        return mUsecase;
+    }
+
+    private Set<Integer> getUnmodifiableIntegerSet(int[] intArray) {
+        if ((intArray != null) && (intArray.length > 0)) {
+            ArraySet<Integer> integerSet = new ArraySet<Integer>();
+            integerSet.ensureCapacity(intArray.length);
+            for (int intEntry : intArray) {
+                integerSet.add(intEntry);
+            }
+
+            return Collections.unmodifiableSet(integerSet);
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the image {@code format} output formats in this stream configuration.
+     *
+     * <p>
+     * For more information refer to {@link StreamConfigurationMap#getOutputFormats}.
+     * </p>
+     *
+     * @return a non-modifiable set of Integer formats
+     */
+    public @NonNull Set<Integer> getOutputFormats() {
+        return getUnmodifiableIntegerSet(mRecommendedMap.getOutputFormats());
+    }
+
+    /**
+     * Get the image {@code format} output formats for a reprocessing input format.
+     *
+     * <p>
+     * For more information refer to {@link StreamConfigurationMap#getValidOutputFormatsForInput}.
+     * </p>
+     *
+     * @return a non-modifiable set of Integer formats
+     */
+    public @Nullable Set<Integer> getValidOutputFormatsForInput(int inputFormat) {
+        return getUnmodifiableIntegerSet(mRecommendedMap.getValidOutputFormatsForInput(
+                    inputFormat));
+    }
+
+    /**
+     * Get the image {@code format} input formats in this stream configuration.
+     *
+     * <p>All image formats returned by this function will be defined in either {@link ImageFormat}
+     * or in {@link PixelFormat} (and there is no possibility of collision).</p>
+     *
+     * @return a non-modifiable set of Integer formats
+     */
+    public @Nullable Set<Integer> getInputFormats() {
+        return getUnmodifiableIntegerSet(mRecommendedMap.getInputFormats());
+    }
+
+    private Set<Size> getUnmodifiableSizeSet(Size[] sizeArray) {
+        if ((sizeArray != null) && (sizeArray.length > 0)) {
+            ArraySet<Size> sizeSet = new ArraySet<Size>();
+            sizeSet.addAll(Arrays.asList(sizeArray));
+            return Collections.unmodifiableSet(sizeSet);
+        }
+
+        return  null;
+    }
+
+    /**
+     * Get the supported input sizes for this input format.
+     *
+     * <p>The format must have come from {@link #getInputFormats}; otherwise
+     * {@code null} is returned.</p>
+     *
+     * @param format a format from {@link #getInputFormats}
+     * @return a non-modifiable set of sizes, or {@code null} if the format was not available.
+     */
+    public @Nullable Set<Size> getInputSizes(int format) {
+        return getUnmodifiableSizeSet(mRecommendedMap.getInputSizes(format));
+    }
+
+    /**
+     * Determine whether or not output surfaces with a particular user-defined format can be passed
+     * {@link CameraDevice#createCaptureSession createCaptureSession}.
+     *
+     * <p>
+     * For further information refer to {@link StreamConfigurationMap#isOutputSupportedFor}.
+     * </p>
+     *
+     *
+     * @param format an image format from either {@link ImageFormat} or {@link PixelFormat}
+     * @return
+     *          {@code true} if using a {@code surface} with this {@code format} will be
+     *          supported with {@link CameraDevice#createCaptureSession}
+     *
+     * @throws IllegalArgumentException
+     *          if the image format was not a defined named constant
+     *          from either {@link ImageFormat} or {@link PixelFormat}
+     */
+    public boolean isOutputSupportedFor(int format) {
+        return mRecommendedMap.isOutputSupportedFor(format);
+    }
+
+    /**
+     * Get a list of sizes compatible with the requested image {@code format}.
+     *
+     * <p>
+     * For more information refer to {@link StreamConfigurationMap#getOutputSizes}.
+     * </p>
+     *
+     *
+     * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
+     * @return  a non-modifiable set of supported sizes,
+     *          or {@code null} if the {@code format} is not a supported output
+     */
+    public @Nullable Set<Size> getOutputSizes(int format) {
+        return getUnmodifiableSizeSet(mRecommendedMap.getOutputSizes(format));
+    }
+
+    /**
+     * Get a list of supported high speed video recording sizes.
+     * <p>
+     * For more information refer to {@link StreamConfigurationMap#getHighSpeedVideoSizes}.
+     * </p>
+     *
+     * @return a non-modifiable set of supported high speed video recording sizes
+     */
+    public @Nullable Set<Size> getHighSpeedVideoSizes() {
+        return getUnmodifiableSizeSet(mRecommendedMap.getHighSpeedVideoSizes());
+    }
+
+    private Set<Range<Integer>> getUnmodifiableRangeSet(Range<Integer>[] rangeArray) {
+        if ((rangeArray != null) && (rangeArray.length > 0)) {
+            ArraySet<Range<Integer>> rangeSet = new ArraySet<Range<Integer>>();
+            rangeSet.addAll(Arrays.asList(rangeArray));
+            return Collections.unmodifiableSet(rangeSet);
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the frame per second ranges (fpsMin, fpsMax) for input high speed video size.
+     *
+     * <p>
+     * For further information refer to
+     * {@link StreamConfigurationMap#getHighSpeedVideoFpsRangesFor}.
+     * </p>
+     * @param size one of the sizes returned by {@link #getHighSpeedVideoSizes()}
+     * @return a non-modifiable set of supported high speed video recording FPS ranges The upper
+     *         bound of returned ranges is guaranteed to be greater than or equal to 120.
+     * @throws IllegalArgumentException if input size does not exist in the return value of
+     *             getHighSpeedVideoSizes
+     */
+    public @Nullable Set<Range<Integer>> getHighSpeedVideoFpsRangesFor(Size size) {
+        return getUnmodifiableRangeSet(mRecommendedMap.getHighSpeedVideoFpsRangesFor(size));
+    }
+
+    /**
+     * Get a list of supported high speed video recording FPS ranges.
+     * <p>
+     * For further information refer to {@link StreamConfigurationMap#getHighSpeedVideoFpsRanges}.
+     * </p>
+     * @return a non-modifiable set of supported high speed video recording FPS ranges The upper
+     *         bound of returned ranges is guaranteed to be larger or equal to 120.
+     */
+    public @Nullable Set<Range<Integer>> getHighSpeedVideoFpsRanges() {
+        return getUnmodifiableRangeSet(mRecommendedMap.getHighSpeedVideoFpsRanges());
+    }
+
+    /**
+     * Get the supported video sizes for an input high speed FPS range.
+     *
+     * <p>
+     * For further information refer to {@link StreamConfigurationMap#getHighSpeedVideoSizesFor}.
+     * </p>
+     *
+     * @param fpsRange one of the FPS range returned by {@link #getHighSpeedVideoFpsRanges()}
+     * @return A non-modifiable set of video sizes to create high speed capture sessions for high
+     *         speed streaming use cases.
+     *
+     * @throws IllegalArgumentException if input FPS range does not exist in the return value of
+     *         getHighSpeedVideoFpsRanges
+     */
+    public @Nullable Set<Size> getHighSpeedVideoSizesFor(Range<Integer> fpsRange) {
+        return getUnmodifiableSizeSet(mRecommendedMap.getHighSpeedVideoSizesFor(fpsRange));
+    }
+
+    /**
+     * Get a list of supported high resolution sizes, which cannot operate at full BURST_CAPTURE
+     * rate.
+     *
+     * <p>
+     * For further information refer to {@link StreamConfigurationMap#getHighResolutionOutputSizes}.
+     * </p>
+     *
+     * @return a non-modifiable set of supported slower high-resolution sizes, or {@code null} if
+     *         the BURST_CAPTURE capability is not supported
+     */
+    public @Nullable Set<Size> getHighResolutionOutputSizes(int format) {
+        return getUnmodifiableSizeSet(mRecommendedMap.getHighResolutionOutputSizes(format));
+    }
+
+    /**
+     * Get the minimum
+     * {@link android.hardware.camera2.CaptureRequest#SENSOR_FRAME_DURATION frame duration}
+     * for the format/size combination (in nanoseconds).
+     *
+     * <p>
+     * For further information refer to {@link StreamConfigurationMap#getOutputMinFrameDuration}.
+     * </p>
+     *
+     * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
+     * @param size an output-compatible size
+     * @return a minimum frame duration {@code >} 0 in nanoseconds, or
+     *          0 if the minimum frame duration is not available.
+     *
+     * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
+     * @throws NullPointerException if {@code size} was {@code null}
+     *
+     */
+    public long getOutputMinFrameDuration(int format, Size size) {
+        return mRecommendedMap.getOutputMinFrameDuration(format, size);
+    }
+
+    /**
+     * Get the stall duration for the format/size combination (in nanoseconds).
+     *
+     * <p>
+     * For further information refer to {@link StreamConfigurationMap#getOutputStallDuration}.
+     * </p>
+     *
+     * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
+     * @param size an output-compatible size
+     * @return a stall duration {@code >=} 0 in nanoseconds
+     *
+     * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
+     * @throws NullPointerException if {@code size} was {@code null}
+     */
+    public long getOutputStallDuration(int format, Size size) {
+        return mRecommendedMap.getOutputStallDuration(format, size);
+    }
+
+    /**
+     * Get a list of sizes compatible with {@code klass} to use as an output.
+     *
+     * <p>For further information refer to {@link StreamConfigurationMap#getOutputSizes(Class)}.
+     * </p>
+     *
+     * @param klass
+     *          a non-{@code null} {@link Class} object reference
+     * @return
+     *          a non-modifiable set of supported sizes for {@link ImageFormat#PRIVATE} format,
+     *          or {@code null} if the {@code klass} is not a supported output.
+     *
+     *
+     * @throws NullPointerException if {@code klass} was {@code null}
+     *
+     */
+    public <T> @Nullable Set<Size> getOutputSizes(Class<T> klass) {
+        if (mSupportsPrivate) {
+            return getUnmodifiableSizeSet(mRecommendedMap.getOutputSizes(klass));
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration}
+     * for the class/size combination (in nanoseconds).
+     *
+     * <p>For more information refer to
+     * {@link StreamConfigurationMap#getOutputMinFrameDuration(Class, Size)}.</p>
+     *
+     * @param klass
+     *          a class which  has a non-empty array returned by {@link #getOutputSizes(Class)}
+     * @param size an output-compatible size
+     * @return a minimum frame duration {@code >} 0 in nanoseconds, or
+     *          0 if the minimum frame duration is not available.
+     *
+     * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
+     * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
+     *
+     */
+    public <T> long getOutputMinFrameDuration(final Class<T> klass, final Size size) {
+        if (mSupportsPrivate) {
+            return mRecommendedMap.getOutputMinFrameDuration(klass, size);
+        }
+
+        return 0;
+    }
+
+    /**
+     * Get the stall duration for the class/size combination (in nanoseconds).
+     *
+     * <p>For more information refer to
+     * {@link StreamConfigurationMap#getOutputStallDuration(Class, Size)}.
+     *
+     * @param klass
+     *          a class which has a non-empty array returned by {@link #getOutputSizes(Class)}.
+     * @param size an output-compatible size
+     * @return a minimum frame duration {@code >=} 0 in nanoseconds, or 0 if the stall duration is
+     *         not available.
+     *
+     * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
+     * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
+     *
+     */
+    public <T> long getOutputStallDuration(final Class<T> klass, final Size size) {
+        if (mSupportsPrivate) {
+            return mRecommendedMap.getOutputStallDuration(klass, size);
+        }
+
+        return 0;
+    }
+
+    /**
+     * Determine whether or not the {@code surface} in its current state is suitable to be included
+     * in a {@link CameraDevice#createCaptureSession capture session} as an output.
+     *
+     * <p>For more information refer to {@link StreamConfigurationMap#isOutputSupportedFor}.
+     * </p>
+     *
+     * @param surface a non-{@code null} {@link Surface} object reference
+     * @return {@code true} if this is supported, {@code false} otherwise
+     *
+     * @throws NullPointerException if {@code surface} was {@code null}
+     * @throws IllegalArgumentException if the Surface endpoint is no longer valid
+     *
+     */
+    public boolean isOutputSupportedFor(Surface surface) {
+        return mRecommendedMap.isOutputSupportedFor(surface);
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/params/StreamConfiguration.java b/core/java/android/hardware/camera2/params/StreamConfiguration.java
index a6fc10f..eb92291 100644
--- a/core/java/android/hardware/camera2/params/StreamConfiguration.java
+++ b/core/java/android/hardware/camera2/params/StreamConfiguration.java
@@ -40,7 +40,7 @@
  *
  * @hide
  */
-public final class StreamConfiguration {
+public class StreamConfiguration {
 
     /**
      * Create a new {@link StreamConfiguration}.
@@ -164,8 +164,8 @@
         return HashCodeHelpers.hashCode(mFormat, mWidth, mHeight, mInput ? 1 : 0);
     }
 
-    private final int mFormat;
-    private final int mWidth;
-    private final int mHeight;
-    private final boolean mInput;
+    protected int mFormat;
+    protected int mWidth;
+    protected int mHeight;
+    protected boolean mInput;
 }
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 414c463..dd052a8 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -98,6 +98,43 @@
             HighSpeedVideoConfiguration[] highSpeedVideoConfigurations,
             ReprocessFormatsMap inputOutputFormatsMap,
             boolean listHighResolution) {
+        this(configurations, minFrameDurations, stallDurations,
+                    depthConfigurations, depthMinFrameDurations, depthStallDurations,
+                    highSpeedVideoConfigurations, inputOutputFormatsMap, listHighResolution,
+                    /*enforceImplementationDefined*/ true);
+    }
+
+    /**
+     * Create a new {@link StreamConfigurationMap}.
+     *
+     * <p>The array parameters ownership is passed to this object after creation; do not
+     * write to them after this constructor is invoked.</p>
+     *
+     * @param configurations a non-{@code null} array of {@link StreamConfiguration}
+     * @param minFrameDurations a non-{@code null} array of {@link StreamConfigurationDuration}
+     * @param stallDurations a non-{@code null} array of {@link StreamConfigurationDuration}
+     * @param highSpeedVideoConfigurations an array of {@link HighSpeedVideoConfiguration}, null if
+     *        camera device does not support high speed video recording
+     * @param listHighResolution a flag indicating whether the device supports BURST_CAPTURE
+     *        and thus needs a separate list of slow high-resolution output sizes
+     * @param enforceImplementationDefined a flag indicating whether
+     *        IMPLEMENTATION_DEFINED format configuration must be present
+     * @throws NullPointerException if any of the arguments except highSpeedVideoConfigurations
+     *         were {@code null} or any subelements were {@code null}
+     *
+     * @hide
+     */
+    public StreamConfigurationMap(
+            StreamConfiguration[] configurations,
+            StreamConfigurationDuration[] minFrameDurations,
+            StreamConfigurationDuration[] stallDurations,
+            StreamConfiguration[] depthConfigurations,
+            StreamConfigurationDuration[] depthMinFrameDurations,
+            StreamConfigurationDuration[] depthStallDurations,
+            HighSpeedVideoConfiguration[] highSpeedVideoConfigurations,
+            ReprocessFormatsMap inputOutputFormatsMap,
+            boolean listHighResolution,
+            boolean enforceImplementationDefined) {
 
         if (configurations == null) {
             // If no color configurations exist, ensure depth ones do
@@ -169,7 +206,7 @@
                     mDepthOutputFormats.get(config.getFormat()) + 1);
         }
 
-        if (configurations != null &&
+        if (configurations != null && enforceImplementationDefined &&
                 mOutputFormats.indexOfKey(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) < 0) {
             throw new AssertionError(
                     "At least one stream configuration for IMPLEMENTATION_DEFINED must exist");
@@ -208,7 +245,7 @@
      * @see ImageFormat
      * @see PixelFormat
      */
-    public final int[] getOutputFormats() {
+    public int[] getOutputFormats() {
         return getPublicFormats(/*output*/true);
     }
 
@@ -232,7 +269,7 @@
      * @see ImageFormat
      * @see PixelFormat
      */
-    public final int[] getValidOutputFormatsForInput(int inputFormat) {
+    public int[] getValidOutputFormatsForInput(int inputFormat) {
         if (mInputOutputFormatsMap == null) {
             return new int[0];
         }
@@ -250,7 +287,7 @@
      * @see ImageFormat
      * @see PixelFormat
      */
-    public final int[] getInputFormats() {
+    public int[] getInputFormats() {
         return getPublicFormats(/*output*/false);
     }
 
@@ -426,6 +463,34 @@
     }
 
     /**
+     * Determine whether or not the particular stream configuration is suitable to be included
+     * in a {@link CameraDevice#createCaptureSession capture session} as an output.
+     *
+     * @param size stream configuration size
+     * @param format stream configuration format
+     * @return {@code true} if this is supported, {@code false} otherwise
+     *
+     * @see CameraDevice#createCaptureSession
+     * @see #isOutputSupportedFor(Class)
+     * @hide
+     */
+    public boolean isOutputSupportedFor(Size size, int format) {
+        int internalFormat = imageFormatToInternal(format);
+        int dataspace = imageFormatToDataspace(format);
+
+        StreamConfiguration[] configs =
+            dataspace != HAL_DATASPACE_DEPTH ? mConfigurations : mDepthConfigurations;
+        for (StreamConfiguration config : configs) {
+            if ((config.getFormat() == internalFormat) && config.isOutput() &&
+                    config.getSize().equals(size)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
      * Get a list of sizes compatible with {@code klass} to use as an output.
      *
      * <p>Some of the supported classes may support additional formats beyond
@@ -1062,8 +1127,9 @@
      * @see ImageFormat
      * @see PixelFormat
      * @see #checkArgumentFormat
+     * @hide
      */
-    static int imageFormatToPublic(int format) {
+    public static int imageFormatToPublic(int format) {
         switch (format) {
             case HAL_PIXEL_FORMAT_BLOB:
                 return ImageFormat.JPEG;
@@ -1105,8 +1171,9 @@
      * @see ImageFormat
      * @see PixelFormat
      * @see #checkArgumentFormat
+     * @hide
      */
-    static int depthFormatToPublic(int format) {
+    public static int depthFormatToPublic(int format) {
         switch (format) {
             case HAL_PIXEL_FORMAT_BLOB:
                 return ImageFormat.DEPTH_POINT_CLOUD;
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 7840fd0..9db1f92 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -235,7 +235,8 @@
         // If true, enables automatic brightness control.
         public boolean useAutoBrightness;
 
-        // If true, scales the brightness to half of desired.
+        // If true, scales the brightness to a fraction of desired (as defined by
+        // screenLowPowerBrightnessFactor).
         public boolean lowPowerMode;
 
         // The factor to adjust the screen brightness in low power mode in the range
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 536c4a5..322863a 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -311,6 +311,21 @@
     }
 
     /**
+     * Pokes the the driver to have it start looking for faces again.
+     * @hide
+     */
+    @RequiresPermission(MANAGE_BIOMETRIC)
+    public void userActivity() {
+        if (mService != null) {
+            try {
+                mService.userActivity();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * Sets the active user. This is meant to be used to select the current profile for enrollment
      * to allow separate enrolled faces for a work profile
      *
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 6681bd7..47df8e8 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -98,4 +98,6 @@
     int setRequireAttention(boolean requireAttention, in byte [] token);
 
     boolean getRequireAttention(in byte [] token);
+
+    void userActivity();
 }
diff --git a/core/java/android/hardware/iris/IIrisService.aidl b/core/java/android/hardware/iris/IIrisService.aidl
new file mode 100644
index 0000000..8cf3c13
--- /dev/null
+++ b/core/java/android/hardware/iris/IIrisService.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 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 android.hardware.iris;
+
+/**
+ * Communication channel from client to the iris service. These methods are all require the
+ * MANAGE_BIOMETRIC signature permission.
+ * @hide
+ */
+interface IIrisService {
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/iris/IrisManager.java b/core/java/android/hardware/iris/IrisManager.java
new file mode 100644
index 0000000..281ac47
--- /dev/null
+++ b/core/java/android/hardware/iris/IrisManager.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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 android.hardware.iris;
+
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * A class that coordinates access to the iris authentication hardware.
+ * @hide
+ */
+@SystemService(Context.IRIS_SERVICE)
+public class IrisManager {
+
+    /**
+     * @hide
+     */
+    public IrisManager(Context context, IIrisService service) {
+    }
+}
diff --git a/core/java/android/hardware/location/ContextHubBroadcastReceiver.java b/core/java/android/hardware/location/ContextHubBroadcastReceiver.java
deleted file mode 100644
index e0cc8b7..0000000
--- a/core/java/android/hardware/location/ContextHubBroadcastReceiver.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2018 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 android.hardware.location;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
-
-/**
- * A BroadcastReceiver that can be used with the Context Hub Service notifications.
- *
- * @hide
- */
-public class ContextHubBroadcastReceiver extends BroadcastReceiver {
-    // The context at which this receiver operates in
-    private Context mContext;
-
-    // The handler to post callbacks to when receiving Context Hub Service intents
-    private Handler mHandler;
-
-    // The callback to be invoked when receiving Context Hub Service intents
-    private ContextHubClientCallback mCallback;
-
-    // The string to use as the broadcast action for this receiver
-    private String mAction;
-
-    // True when this receiver is registered to receive Intents, false otherwise
-    private boolean mRegistered = false;
-
-    public ContextHubBroadcastReceiver(Context context, Handler handler,
-                                       ContextHubClientCallback callback, String tag) {
-        mContext = context;
-        mHandler = handler;
-        mCallback = callback;
-        mAction = tag;
-    }
-
-    /**
-     * Registers this receiver to receive Intents from the Context Hub Service. This method must
-     * only be invoked when the receiver is not registered.
-     *
-     * @throws IllegalStateException if the receiver is already registered
-     */
-    public void register() throws IllegalStateException {
-        if (mRegistered) {
-            throw new IllegalStateException(
-                "Cannot register ContextHubBroadcastReceiver multiple times");
-        }
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(mAction);
-        mContext.registerReceiver(this, intentFilter, null /* broadcastPermission */, mHandler);
-        mRegistered = true;
-    }
-
-    /**
-     * Unregisters this receiver. This method must only be invoked if {@link #register()} is
-     * previously invoked.
-     *
-     * @throws IllegalStateException if the receiver is not yet registered
-     */
-    public void unregister() throws IllegalStateException {
-        if (!mRegistered) {
-            throw new IllegalStateException(
-                "Cannot unregister ContextHubBroadcastReceiver when not registered");
-        }
-        mContext.unregisterReceiver(this);
-        mRegistered = false;
-    }
-
-    /**
-     * Creates a new PendingIntent associated with this receiver.
-     *
-     * @param flags the flags {@link PendingIntent.Flags} to use for the PendingIntent
-     *
-     * @return a PendingIntent to receive notifications for this receiver
-     */
-    public PendingIntent getPendingIntent(@PendingIntent.Flags int flags) {
-        return PendingIntent.getBroadcast(
-            mContext, 0 /* requestCode */, new Intent(mAction), flags);
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        // TODO: Implement this
-    }
-}
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 56da719..2717c4e 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -19,6 +19,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.app.PendingIntent;
+import android.content.Intent;
 import android.os.RemoteException;
 
 import com.android.internal.util.Preconditions;
@@ -106,14 +107,16 @@
      * This method should be used if the caller wants to receive notifications even after the
      * process exits. The client must have an open connection with the Context Hub Service (i.e. it
      * cannot have been closed through the {@link #close()} method). Only one PendingIntent can be
-     * registered at a time for a single ContextHubClient. If registered successfully, intents will
-     * be delivered regarding events for the specified nanoapp from the attached Context Hub. Any
-     * unicast messages for this client will also be delivered. The intent will have an extra
+     * registered at a time for a single ContextHubClient, and the PendingIntent cannot be
+     * registered if already registered by a ContextHubClient. If registered successfully, intents
+     * will be delivered regarding events for the specified nanoapp from the attached Context Hub.
+     * Any unicast messages for this client will also be delivered. The intent will have an extra
      * {@link ContextHubManager.EXTRA_CONTEXT_HUB_INFO} of type {@link ContextHubInfo}, which
      * describes the Context Hub the intent event was for. The intent will also have an extra
      * {@link ContextHubManager.EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which
      * will contain the type of the event. See {@link ContextHubManager.Event} for description of
-     * each event type, along with event-specific extra fields.
+     * each event type, along with event-specific extra fields. A client can use
+     * {@link ContextHubIntentEvent.fromIntent(Intent)} to parse the Intent generated by the event.
      *
      * When the intent is received, this client can be recreated through
      * {@link ContextHubManager.createClient(PendingIntent, ContextHubInfo,
@@ -126,10 +129,6 @@
      * continued to be maintained at the Context Hub Service until
      * {@link #unregisterIntent(PendingIntent)} is called for registered intents.
      *
-     * See {@link ContextHubBroadcastReceiver} for a helper class to generate the
-     * {@link PendingIntent} through a {@link BroadcastReceiver}, and maps an {@link Intent} to a
-     * {@link ContextHubClientCallback}.
-     *
      * @param pendingIntent the PendingIntent to register for this client
      * @param nanoAppId     the unique ID of the nanoapp to receive events for
      * @return true on success, false otherwise
diff --git a/core/java/android/hardware/location/ContextHubIntentEvent.java b/core/java/android/hardware/location/ContextHubIntentEvent.java
new file mode 100644
index 0000000..96e7496
--- /dev/null
+++ b/core/java/android/hardware/location/ContextHubIntentEvent.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2018 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 android.hardware.location;
+
+import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.content.Intent;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A helper class to retrieve information about a Intent event received for a PendingIntent
+ * registered through {@link ContextHubClient.registerIntent(PendingIntent, long)}. This object
+ * can only be created through the factory method {@link ContextHubIntentEvent.fromIntent(Intent)}.
+ *
+ * @hide
+ */
+public class ContextHubIntentEvent {
+    @ContextHubManager.Event private final int mEventType;
+
+    @NonNull private final ContextHubInfo mContextHubInfo;
+
+    private final long mNanoAppId;
+
+    private final NanoAppMessage mNanoAppMessage;
+
+    private final int mNanoAppAbortCode;
+
+    private ContextHubIntentEvent(
+            @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
+            long nanoAppId, NanoAppMessage nanoAppMessage, int nanoAppAbortCode) {
+        mContextHubInfo = contextHubInfo;
+        mEventType = eventType;
+        mNanoAppId = nanoAppId;
+        mNanoAppMessage = nanoAppMessage;
+        mNanoAppAbortCode = nanoAppAbortCode;
+    }
+
+    private ContextHubIntentEvent(
+            @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType) {
+        this(contextHubInfo, eventType, -1 /* nanoAppId */, null /* nanoAppMessage */,
+                -1 /* nanoAppAbortCode */);
+    }
+
+    private ContextHubIntentEvent(
+            @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
+            long nanoAppId) {
+        this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */,
+                -1 /* nanoAppAbortCode */);
+    }
+
+    private ContextHubIntentEvent(
+            @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
+            long nanoAppId, @NonNull NanoAppMessage nanoAppMessage) {
+        this(contextHubInfo, eventType, nanoAppId, nanoAppMessage, -1 /* nanoAppAbortCode */);
+    }
+
+    private ContextHubIntentEvent(
+            @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
+            long nanoAppId, int nanoAppAbortCode) {
+        this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */, nanoAppAbortCode);
+    }
+
+    /**
+     * Creates a ContextHubIntentEvent object from an Intent received through a PendingIntent
+     * registered through {@link ContextHubClient.registerIntent(PendingIntent, long)}.
+     *
+     * @param intent the Intent object from an Intent event
+     * @return the ContextHubIntentEvent object describing the event
+     *
+     * @throws IllegalArgumentException if the Intent was not a valid intent
+     */
+    @NonNull
+    public static ContextHubIntentEvent fromIntent(@NonNull Intent intent) {
+        Preconditions.checkNotNull(intent, "Intent cannot be null");
+
+        hasExtraOrThrow(intent, ContextHubManager.EXTRA_CONTEXT_HUB_INFO);
+        ContextHubInfo info = intent.getParcelableExtra(ContextHubManager.EXTRA_CONTEXT_HUB_INFO);
+        if (info == null) {
+            throw new IllegalArgumentException("ContextHubInfo extra was null");
+        }
+
+        int eventType = getIntExtraOrThrow(intent, ContextHubManager.EXTRA_EVENT_TYPE);
+        ContextHubIntentEvent event;
+        switch (eventType) {
+            case ContextHubManager.EVENT_NANOAPP_LOADED:
+            case ContextHubManager.EVENT_NANOAPP_UNLOADED:
+            case ContextHubManager.EVENT_NANOAPP_ENABLED:
+            case ContextHubManager.EVENT_NANOAPP_DISABLED:
+            case ContextHubManager.EVENT_NANOAPP_ABORTED:
+            case ContextHubManager.EVENT_NANOAPP_MESSAGE: // fall through
+                long nanoAppId = getLongExtraOrThrow(intent, ContextHubManager.EXTRA_NANOAPP_ID);
+                if (eventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
+                    hasExtraOrThrow(intent, ContextHubManager.EXTRA_MESSAGE);
+                    NanoAppMessage message =
+                            intent.getParcelableExtra(ContextHubManager.EXTRA_MESSAGE);
+                    if (message == null) {
+                        throw new IllegalArgumentException("NanoAppMessage extra was null");
+                    }
+
+                    event = new ContextHubIntentEvent(info, eventType, nanoAppId, message);
+                } else if (eventType == ContextHubManager.EVENT_NANOAPP_ABORTED) {
+                    int nanoAppAbortCode = getIntExtraOrThrow(
+                            intent, ContextHubManager.EXTRA_NANOAPP_ABORT_CODE);
+                    event = new ContextHubIntentEvent(info, eventType, nanoAppId, nanoAppAbortCode);
+                } else {
+                    event = new ContextHubIntentEvent(info, eventType, nanoAppId);
+                }
+                break;
+
+            case ContextHubManager.EVENT_HUB_RESET:
+                event = new ContextHubIntentEvent(info, eventType);
+                break;
+
+            default:
+                throw new IllegalArgumentException("Unknown intent event type " + eventType);
+        }
+
+        return event;
+    }
+
+    /**
+     * @return the event type of this Intent event
+     */
+    @ContextHubManager.Event
+    public int getEventType() {
+        return mEventType;
+    }
+
+    /**
+     * @return the ContextHubInfo object describing the Context Hub this event was for
+     */
+    @NonNull
+    public ContextHubInfo getContextHubInfo() {
+        return mContextHubInfo;
+    }
+
+    /**
+     * @return the ID of the nanoapp this event was for
+     *
+     * @throws UnsupportedOperationException if the event did not have a nanoapp associated
+     */
+    public long getNanoAppId() {
+        if (mEventType == ContextHubManager.EVENT_HUB_RESET) {
+            throw new UnsupportedOperationException(
+                    "Cannot invoke getNanoAppId() on Context Hub reset event");
+        }
+        return mNanoAppId;
+    }
+
+    /**
+     * @return the nanoapp's abort code
+     *
+     * @throws UnsupportedOperationException if this was not a nanoapp abort event
+     */
+    public int getNanoAppAbortCode() {
+        if (mEventType != ContextHubManager.EVENT_NANOAPP_ABORTED) {
+            throw new UnsupportedOperationException(
+                    "Cannot invoke getNanoAppAbortCode() on non-abort event: " + mEventType);
+        }
+        return mNanoAppAbortCode;
+    }
+
+    /**
+     * @return the message from a nanoapp
+     *
+     * @throws UnsupportedOperationException if this was not a nanoapp message event
+     */
+    @NonNull
+    public NanoAppMessage getNanoAppMessage() {
+        if (mEventType != ContextHubManager.EVENT_NANOAPP_MESSAGE) {
+            throw new UnsupportedOperationException(
+                    "Cannot invoke getNanoAppMessage() on non-message event: " + mEventType);
+        }
+        return mNanoAppMessage;
+    }
+
+    @Override
+    public String toString() {
+        String out = "ContextHubIntentEvent[eventType = " + mEventType
+                + ", contextHubId = " + mContextHubInfo.getId();
+
+        if (mEventType != ContextHubManager.EVENT_HUB_RESET) {
+            out += ", nanoAppId = 0x" + Long.toHexString(mNanoAppId);
+        }
+        if (mEventType == ContextHubManager.EVENT_NANOAPP_ABORTED) {
+            out += ", nanoAppAbortCode = " + mNanoAppAbortCode;
+        }
+        if (mEventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
+            out += ", nanoAppMessage = " + mNanoAppMessage;
+        }
+
+        return out + "]";
+    }
+
+    private static void hasExtraOrThrow(Intent intent, String extra) {
+        if (!intent.hasExtra(extra)) {
+            throw new IllegalArgumentException("Intent did not have extra: " + extra);
+        }
+    }
+
+    private static int getIntExtraOrThrow(Intent intent, String extra) {
+        hasExtraOrThrow(intent, extra);
+        return intent.getIntExtra(extra, -1 /* defaultValue */);
+    }
+
+    private static long getLongExtraOrThrow(Intent intent, String extra) {
+        hasExtraOrThrow(intent, extra);
+        return intent.getLongExtra(extra, -1 /* defaultValue */);
+    }
+}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index b0b77f3..9acefa5 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -808,7 +808,7 @@
      *
      * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or pendingIntent
      *                                  was not associated with a client
-     * @throws IllegalStateException    if there were too many registered clients at the service
+     * @throws IllegalStateException    if the client is already registered to a valid callback
      * @throws NullPointerException     if pendingIntent, hubInfo, callback, or executor is null
      *
      * @hide
@@ -818,8 +818,24 @@
             @NonNull PendingIntent pendingIntent, @NonNull ContextHubInfo hubInfo,
             @NonNull ContextHubClientCallback callback,
             @NonNull @CallbackExecutor Executor executor) {
-        // TODO: Implement this
-        throw new UnsupportedOperationException("Not implemented yet");
+        Preconditions.checkNotNull(pendingIntent, "PendingIntent cannot be null");
+        Preconditions.checkNotNull(callback, "Callback cannot be null");
+        Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null");
+        Preconditions.checkNotNull(executor, "Executor cannot be null");
+
+        ContextHubClient client = new ContextHubClient(hubInfo);
+        IContextHubClientCallback clientInterface = createClientCallback(
+                client, callback, executor);
+
+        IContextHubClient clientProxy;
+        try {
+            clientProxy = mService.bindClient(pendingIntent, clientInterface, hubInfo.getId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        client.setClientProxy(clientProxy);
+        return client;
     }
 
     /**
@@ -833,7 +849,7 @@
      *
      * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or pendingIntent
      *                                  was not associated with a client
-     * @throws IllegalStateException    if there were too many registered clients at the service
+     * @throws IllegalStateException    if the client is already registered to a valid callback
      * @throws NullPointerException     if pendingIntent, hubInfo, or callback is null
      *
      * @hide
diff --git a/core/java/android/hardware/location/IContextHubClient.aidl b/core/java/android/hardware/location/IContextHubClient.aidl
index 7559cd5..b539414 100644
--- a/core/java/android/hardware/location/IContextHubClient.aidl
+++ b/core/java/android/hardware/location/IContextHubClient.aidl
@@ -31,8 +31,8 @@
     void close();
 
     // Registers a PendingIntent with the client
-    boolean registerIntent(in PendingIntent intent, long nanoAppId);
+    boolean registerIntent(in PendingIntent pendingIntent, long nanoAppId);
 
     // Unregisters a PendingIntent from the client
-    boolean unregisterIntent(in PendingIntent intent);
+    boolean unregisterIntent(in PendingIntent pendingIntent);
 }
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 233e857..9b0acdf 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -17,6 +17,7 @@
 package android.hardware.location;
 
 // Declare any non-default types here with import statements
+import android.app.PendingIntent;
 import android.hardware.location.ContextHubInfo;
 import android.hardware.location.ContextHubMessage;
 import android.hardware.location.NanoApp;
@@ -60,6 +61,11 @@
     // Creates a client to send and receive messages
     IContextHubClient createClient(in IContextHubClientCallback client, int contextHubId);
 
+    // Binds an existing client to a new callback interface, provided a previously registered
+    // PendingIntent
+    IContextHubClient bindClient(
+            in PendingIntent pendingIntent, in IContextHubClientCallback client, int contextHubId);
+
     // Returns a list of ContextHub objects of available hubs
     List<ContextHubInfo> getContextHubs();
 
diff --git a/core/java/android/hardware/location/NanoApp.java b/core/java/android/hardware/location/NanoApp.java
index ded1bb8..62e7182 100644
--- a/core/java/android/hardware/location/NanoApp.java
+++ b/core/java/android/hardware/location/NanoApp.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.util.Log;
 
+import com.android.internal.util.Preconditions;
+
 /** A class describing nano apps.
  * A nano app is a piece of executable code that can be
  * downloaded onto a specific architecture. These are targtted
@@ -195,10 +197,12 @@
      *               needed Sensors
      */
     public void setNeededSensors(int[] neededSensors) {
+        Preconditions.checkNotNull(neededSensors, "neededSensors must not be null");
         mNeededSensors = neededSensors;
     }
 
     public void setOutputEvents(int[] outputEvents) {
+        Preconditions.checkNotNull(outputEvents, "outputEvents must not be null");
         mOutputEvents = outputEvents;
     }
 
@@ -208,10 +212,10 @@
      * @param appBinary generated events
      */
     public void setAppBinary(byte[] appBinary) {
+        Preconditions.checkNotNull(appBinary, "appBinary must not be null");
         mAppBinary = appBinary;
     }
 
-
     /**
      * get the publisher name
      *
diff --git a/core/java/android/hardware/radio/Utils.java b/core/java/android/hardware/radio/Utils.java
index 9887f78..dd7f5b2 100644
--- a/core/java/android/hardware/radio/Utils.java
+++ b/core/java/android/hardware/radio/Utils.java
@@ -47,7 +47,7 @@
 
     static @NonNull Map<String, String> readStringMap(@NonNull Parcel in) {
         int size = in.readInt();
-        Map<String, String> map = new HashMap<>();
+        Map<String, String> map = new HashMap<>(size);
         while (size-- > 0) {
             String key = in.readString();
             String value = in.readString();
@@ -70,7 +70,7 @@
 
     static @NonNull Map<String, Integer> readStringIntMap(@NonNull Parcel in) {
         int size = in.readInt();
-        Map<String, Integer> map = new HashMap<>();
+        Map<String, Integer> map = new HashMap<>(size);
         while (size-- > 0) {
             String key = in.readString();
             int value = in.readInt();
@@ -90,7 +90,7 @@
 
     static <T> Set<T> createSet(@NonNull Parcel in, Parcelable.Creator<T> c) {
         int size = in.readInt();
-        Set<T> set = new HashSet<>();
+        Set<T> set = new HashSet<>(size);
         while (size-- > 0) {
             set.add(in.readTypedObject(c));
         }
@@ -107,15 +107,12 @@
     }
 
     static Set<Integer> createIntSet(@NonNull Parcel in) {
-        return createSet(in, new Parcelable.Creator<Integer>() {
-            public Integer createFromParcel(Parcel in) {
-                return in.readInt();
-            }
-
-            public Integer[] newArray(int size) {
-                return new Integer[size];
-            }
-        });
+        int size = in.readInt();
+        Set<Integer> set = new HashSet<>(size);
+        while (size-- > 0) {
+            set.add(in.readInt());
+        }
+        return set;
     }
 
     static <T extends Parcelable> void writeTypedCollection(@NonNull Parcel dest,
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 6d9c913..f4e776c 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -111,17 +111,6 @@
      */
     ParcelFileDescriptor getControlFd(long function);
 
-    /* Allow USB debugging from the attached host. If alwaysAllow is true, add the
-     * the public key to list of host keys that the user has approved.
-     */
-    void allowUsbDebugging(boolean alwaysAllow, String publicKey);
-
-    /* Deny USB debugging from the attached host */
-    void denyUsbDebugging();
-
-    /* Clear public keys installed for secure USB debugging */
-    void clearUsbDebuggingKeys();
-
     /* Gets the list of USB ports. */
     UsbPort[] getPorts();
 
diff --git a/core/java/android/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl
index 1e75bf4..4b1a08d 100644
--- a/core/java/android/net/INetdEventCallback.aidl
+++ b/core/java/android/net/INetdEventCallback.aidl
@@ -28,6 +28,11 @@
      * Reports a single DNS lookup function call.
      * This method must not block or perform long-running operations.
      *
+     * @param netId the ID of the network the lookup was performed on.
+     * @param eventType one of the EVENT_* constants in {@link INetdEventListener}.
+     * @param returnCode the return value of the query, may vary based on {@code eventType}. See
+     *        {@code getaddrinfo()}, {@code gethostbyaddr()} and {@code gethostbyname()} section in
+     *        bionic/libc/include/netdb.h.
      * @param hostname the name that was looked up.
      * @param ipAddresses (possibly a subset of) the IP addresses returned.
      *        At most {@link #DNS_REPORTED_IP_ADDRESSES_LIMIT} addresses are logged.
@@ -36,8 +41,8 @@
      * @param timestamp the timestamp at which the query was reported by netd.
      * @param uid the UID of the application that performed the query.
      */
-    void onDnsEvent(String hostname, in String[] ipAddresses, int ipAddressesCount, long timestamp,
-            int uid);
+    void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+            in String[] ipAddresses, int ipAddressesCount, long timestamp, int uid);
 
     /**
      * Represents a private DNS validation success or failure.
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index b274155..62cf7d7 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -19,9 +19,11 @@
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 import com.google.android.collect.Sets;
 
+import java.net.InetAddress;
 import java.util.HashSet;
 
 /**
@@ -34,8 +36,10 @@
     private LinkAddress mAddr;
     private HashSet<String> mFlags = Sets.newHashSet();
 
-    private static final String FLAG_UP = "up";
-    private static final String FLAG_DOWN = "down";
+    private static final String FLAG_UP = INetd.IF_STATE_UP;
+    private static final String FLAG_DOWN = INetd.IF_STATE_DOWN;
+
+    private static final  String[] EMPTY_STRING_ARRAY = new String[0];
 
     @Override
     public String toString() {
@@ -112,6 +116,40 @@
     }
 
     /**
+     * Construct InterfaceConfiguration from InterfaceConfigurationParcel.
+     */
+    public static InterfaceConfiguration fromParcel(InterfaceConfigurationParcel p) {
+        InterfaceConfiguration cfg = new InterfaceConfiguration();
+        cfg.setHardwareAddress(p.hwAddr);
+
+        final InetAddress addr = NetworkUtils.numericToInetAddress(p.ipv4Addr);
+        cfg.setLinkAddress(new LinkAddress(addr, p.prefixLength));
+        for (String flag : p.flags) {
+            cfg.setFlag(flag);
+        }
+
+        return cfg;
+    }
+
+    /**
+     * Convert InterfaceConfiguration to InterfaceConfigurationParcel with given ifname.
+     */
+    public InterfaceConfigurationParcel toParcel(String iface) {
+        InterfaceConfigurationParcel cfgParcel = new InterfaceConfigurationParcel();
+        cfgParcel.ifName = iface;
+        if (!TextUtils.isEmpty(mHwAddr)) {
+            cfgParcel.hwAddr = mHwAddr;
+        } else {
+            cfgParcel.hwAddr = "";
+        }
+        cfgParcel.ipv4Addr = mAddr.getAddress().getHostAddress();
+        cfgParcel.prefixLength = mAddr.getPrefixLength();
+        cfgParcel.flags = mFlags.toArray(EMPTY_STRING_ARRAY);
+
+        return cfgParcel;
+    }
+
+    /**
      * This function determines if the interface is up and has a valid IP
      * configuration (IP address has a non zero octet).
      *
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 98f3567..4cd0001 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -393,4 +393,19 @@
         }
         return out;
     }
+
+    /**
+     * Checks if this MAC Address matches the provided range.
+     *
+     * @param baseAddress MacAddress representing the base address to compare with.
+     * @param mask MacAddress representing the mask to use during comparison.
+     * @return true if this MAC Address matches the given range.
+     *
+     * @hide
+     */
+    public boolean matches(@NonNull MacAddress baseAddress, @NonNull MacAddress mask) {
+        Preconditions.checkNotNull(baseAddress);
+        Preconditions.checkNotNull(mask);
+        return (mAddr & mask.mAddr) == (baseAddress.mAddr & mask.mAddr);
+    }
 }
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index a2da6ea..c0487b5 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -65,6 +65,12 @@
      */
     public String subscriberId;
 
+    /**
+     * Set to skip 464xlat. This means the device will treat the network as IPv6-only and
+     * will not attempt to detect a NAT64 via RFC 7050 DNS lookups.
+     */
+    public boolean skip464xlat;
+
     public NetworkMisc() {
     }
 
@@ -75,6 +81,7 @@
             acceptUnvalidated = nm.acceptUnvalidated;
             subscriberId = nm.subscriberId;
             provisioningNotificationDisabled = nm.provisioningNotificationDisabled;
+            skip464xlat = nm.skip464xlat;
         }
     }
 
@@ -90,6 +97,7 @@
         out.writeInt(acceptUnvalidated ? 1 : 0);
         out.writeString(subscriberId);
         out.writeInt(provisioningNotificationDisabled ? 1 : 0);
+        out.writeInt(skip464xlat ? 1 : 0);
     }
 
     public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -101,6 +109,7 @@
             networkMisc.acceptUnvalidated = in.readInt() != 0;
             networkMisc.subscriberId = in.readString();
             networkMisc.provisioningNotificationDisabled = in.readInt() != 0;
+            networkMisc.skip464xlat = in.readInt() != 0;
             return networkMisc;
         }
 
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index d5fb2e7..299b232 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -91,16 +91,16 @@
     public static final int MASK_ALL_NETWORKS     = 0b11110000;
 
     public static final int FIREWALL_RULE_DEFAULT = 0;
-    public static final int FIREWALL_RULE_ALLOW = 1;
-    public static final int FIREWALL_RULE_DENY = 2;
+    public static final int FIREWALL_RULE_ALLOW = INetd.FIREWALL_RULE_ALLOW;
+    public static final int FIREWALL_RULE_DENY = INetd.FIREWALL_RULE_DENY;
 
-    public static final int FIREWALL_TYPE_WHITELIST = 0;
-    public static final int FIREWALL_TYPE_BLACKLIST = 1;
+    public static final int FIREWALL_TYPE_WHITELIST = INetd.FIREWALL_WHITELIST;
+    public static final int FIREWALL_TYPE_BLACKLIST = INetd.FIREWALL_BLACKLIST;
 
-    public static final int FIREWALL_CHAIN_NONE = 0;
-    public static final int FIREWALL_CHAIN_DOZABLE = 1;
-    public static final int FIREWALL_CHAIN_STANDBY = 2;
-    public static final int FIREWALL_CHAIN_POWERSAVE = 3;
+    public static final int FIREWALL_CHAIN_NONE = INetd.FIREWALL_CHAIN_NONE;
+    public static final int FIREWALL_CHAIN_DOZABLE = INetd.FIREWALL_CHAIN_DOZABLE;
+    public static final int FIREWALL_CHAIN_STANDBY = INetd.FIREWALL_CHAIN_STANDBY;
+    public static final int FIREWALL_CHAIN_POWERSAVE = INetd.FIREWALL_CHAIN_POWERSAVE;
 
     public static final String FIREWALL_CHAIN_NAME_NONE = "none";
     public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable";
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index e270fc2..5447f59 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -811,14 +811,19 @@
      * packet needs to be subtracted from the root UID on the base interface both for tx
      * and rx traffic (http://b/12249687, http:/b/33681750).
      *
+     * As for eBPF, the per uid stats is collected by different hook, the rx packets on base
+     * interface will not be counted. Thus, the adjustment on root uid is only needed in tx
+     * direction.
+     *
      * <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
      * {@code ConcurrentHashMap}
      * @param baseTraffic Traffic on the base interfaces. Will be mutated.
      * @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated.
      * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
+     * @param useBpfStats True if eBPF is in use.
      */
     public static void apply464xlatAdjustments(NetworkStats baseTraffic,
-            NetworkStats stackedTraffic, Map<String, String> stackedIfaces) {
+            NetworkStats stackedTraffic, Map<String, String> stackedIfaces, boolean useBpfStats) {
         // Total 464xlat traffic to subtract from uid 0 on all base interfaces.
         // stackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
         final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size());
@@ -837,15 +842,20 @@
                 continue;
             }
             // Subtract any 464lat traffic seen for the root UID on the current base interface.
+            // However, for eBPF, the per uid stats is collected by different hook, the rx packets
+            // on base interface will not be counted. Thus, the adjustment on root uid is only
+            // needed in tx direction.
             adjust.iface = baseIface;
-            adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
+            if (!useBpfStats) {
+                adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
+                adjust.rxPackets = -entry.rxPackets;
+            }
             adjust.txBytes = -(entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
-            adjust.rxPackets = -entry.rxPackets;
             adjust.txPackets = -entry.txPackets;
             adjustments.combineValues(adjust);
 
-            // For 464xlat traffic, xt_qtaguid only counts the bytes of the native IPv4 packet sent
-            // on the stacked interface with prefix "v4-" and drops the IPv6 header size after
+            // For 464xlat traffic, per uid stats only counts the bytes of the native IPv4 packet
+            // sent on the stacked interface with prefix "v4-" and drops the IPv6 header size after
             // unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
             // difference for all packets (http://b/12249687, http:/b/33681750).
             entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA;
@@ -864,8 +874,8 @@
      * base and stacked traffic.
      * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
      */
-    public void apply464xlatAdjustments(Map<String, String> stackedIfaces) {
-        apply464xlatAdjustments(this, this, stackedIfaces);
+    public void apply464xlatAdjustments(Map<String, String> stackedIfaces, boolean useBpfStats) {
+        apply464xlatAdjustments(this, this, stackedIfaces, useBpfStats);
     }
 
     /**
diff --git a/core/java/android/net/UidRange.aidl b/core/java/android/net/UidRange.aidl
new file mode 100644
index 0000000..f70fc8e
--- /dev/null
+++ b/core/java/android/net/UidRange.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 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 android.net;
+
+/**
+ * An inclusive range of UIDs.
+ *
+ * {@hide}
+ */
+parcelable UidRange;
\ No newline at end of file
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
index 3164929..793c82d 100644
--- a/core/java/android/net/UidRange.java
+++ b/core/java/android/net/UidRange.java
@@ -19,17 +19,14 @@
 import static android.os.UserHandle.PER_USER_RANGE;
 
 import android.os.Parcel;
-import android.os.Parcelable;
 
 /**
  * An inclusive range of UIDs.
  *
  * @hide
  */
-public final class UidRange implements Parcelable {
-    public final int start;
-    public final int stop;
-
+public final class UidRange extends UidRangeParcel {
+    private UidRange() {}
     public UidRange(int startUid, int stopUid) {
         if (startUid < 0) throw new IllegalArgumentException("Invalid start UID.");
         if (stopUid < 0) throw new IllegalArgumentException("Invalid stop UID.");
@@ -89,26 +86,18 @@
         return start + "-" + stop;
     }
 
-    // implement the Parcelable interface
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(start);
-        dest.writeInt(stop);
-    }
+    /**
+     * DO NOT override "writeToParcel" and "readFromParcel" in this class.
+     * The parceling code is autogenerated by the superclass.
+     */
 
     public static final Creator<UidRange> CREATOR =
         new Creator<UidRange>() {
             @Override
             public UidRange createFromParcel(Parcel in) {
-                int start = in.readInt();
-                int stop = in.readInt();
-
-                return new UidRange(start, stop);
+                UidRange obj = new UidRange();
+                obj.readFromParcel(in);
+                return obj;
             }
             @Override
             public UidRange[] newArray(int size) {
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 8b6194c..da4b823 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -249,6 +249,7 @@
      * If the current thread is not currently executing an incoming transaction,
      * then its own pid is returned.
      */
+    @CriticalNative
     public static final native int getCallingPid();
 
     /**
@@ -258,6 +259,7 @@
      * permissions.  If the current thread is not currently executing an
      * incoming transaction, then its own uid is returned.
      */
+    @CriticalNative
     public static final native int getCallingUid();
 
     /**
@@ -288,6 +290,7 @@
      * @see #getCallingUid()
      * @see #restoreCallingIdentity(long)
      */
+    @CriticalNative
     public static final native long clearCallingIdentity();
 
     /**
@@ -364,6 +367,7 @@
      * @see StrictMode
      * @hide
      */
+    @CriticalNative
     public static final native void setThreadStrictModePolicy(int policyMask);
 
     /**
@@ -372,6 +376,7 @@
      * @see #setThreadStrictModePolicy
      * @hide
      */
+    @CriticalNative
     public static final native int getThreadStrictModePolicy();
 
     /**
@@ -555,6 +560,79 @@
     }
 
     /**
+     * Listener to be notified about each proxy-side binder call.
+     *
+     * See {@link setProxyTransactListener}.
+     * @hide
+     */
+    public interface ProxyTransactListener {
+        /**
+         * Called before onTransact.
+         *
+         * @return an object that will be passed back to #onTransactEnded (or null).
+         */
+        Object onTransactStarted(IBinder binder, int transactionCode);
+
+        /**
+         * Called after onTranact (even when an exception is thrown).
+         *
+         * @param session The object return by #onTransactStarted.
+         */
+        void onTransactEnded(@Nullable Object session);
+    }
+
+    /**
+     * Propagates the work source to binder calls executed by the system server.
+     *
+     * <li>By default, this listener will propagate the worksource if the outgoing call happens on
+     * the same thread as the incoming binder call.
+     * <li>Custom attribution can be done by calling {@link ThreadLocalWorkSourceUid#set(int)}.
+     * @hide
+     */
+    public static class PropagateWorkSourceTransactListener implements ProxyTransactListener {
+        @Override
+        public Object onTransactStarted(IBinder binder, int transactionCode) {
+           // Note that {@link Binder#getCallingUid()} is already set to the UID of the current
+           // process when this method is called.
+           //
+           // We use ThreadLocalWorkSourceUid instead. It also allows feature owners to set
+           // {@link ThreadLocalWorkSourceUid#set(int) manually to attribute resources to a UID.
+            int uid = ThreadLocalWorkSourceUid.get();
+            if (uid >= 0) {
+                int originalUid = Binder.setThreadWorkSource(uid);
+                return Integer.valueOf(originalUid);
+            }
+            return null;
+        }
+
+        @Override
+        public void onTransactEnded(Object session) {
+            if (session != null) {
+                int uid = (int) session;
+                Binder.setThreadWorkSource(uid);
+            }
+        }
+    }
+
+    /**
+     * Sets a listener for the transact method on the proxy-side.
+     *
+     * <li>The listener is global. Only fast operations should be done to avoid thread
+     * contentions.
+     * <li>The listener implementation needs to handle synchronization if needed. The methods on the
+     * listener can be called concurrently.
+     * <li>Listener set will be used for new transactions. On-going transaction will still use the
+     * previous listener (if already set).
+     * <li>The listener is called on the critical path of the binder transaction so be careful about
+     * performance.
+     * <li>Never execute another binder transaction inside the listener.
+     * @hide
+     */
+    public static void setProxyTransactListener(@Nullable ProxyTransactListener listener) {
+        BinderProxy.setTransactListener(listener);
+    }
+
+    /**
      * Default implementation is a stub that returns false.  You will want
      * to override this to do the appropriate unmarshalling of transactions.
      *
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 591370f..720c167 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.util.Log;
 import android.util.SparseIntArray;
 
@@ -45,6 +46,15 @@
     // Assume the process-wide default value when created
     volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
 
+    private static volatile Binder.ProxyTransactListener sTransactListener = null;
+
+    /**
+     * @see {@link Binder#setProxyTransactListener(listener)}.
+     */
+    public static void setTransactListener(@Nullable Binder.ProxyTransactListener listener) {
+        sTransactListener = listener;
+    }
+
     /*
      * Map from longs to BinderProxy, retaining only a WeakReference to the BinderProxies.
      * We roll our own only because we need to lazily remove WeakReferences during accesses
@@ -469,9 +479,21 @@
             Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
                     stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
         }
+
+        // Make sure the listener won't change while processing a transaction.
+        final Binder.ProxyTransactListener transactListener = sTransactListener;
+        Object session = null;
+        if (transactListener != null) {
+            session = transactListener.onTransactStarted(this, code);
+        }
+
         try {
             return transactNative(code, data, reply, flags);
         } finally {
+            if (transactListener != null) {
+                transactListener.onTransactEnded(session);
+            }
+
             if (tracingEnabled) {
                 Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
             }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 483b764..0c4a0b3e 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -641,6 +641,13 @@
     public static String DIRECTORY_DOCUMENTS = "Documents";
 
     /**
+     * Standard directory in which to place screenshots that have been taken by
+     * the user. Typically used as a secondary directory under
+     * {@link #DIRECTORY_PICTURES}.
+     */
+    public static String DIRECTORY_SCREENSHOTS = "Screenshots";
+
+    /**
      * List of standard storage directories.
      * <p>
      * Each of its values have its own constant:
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index f71fdd7..0b90f54 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -22,16 +22,19 @@
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
 import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+import static android.system.OsConstants.F_OK;
 import static android.system.OsConstants.O_APPEND;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDONLY;
 import static android.system.OsConstants.O_RDWR;
 import static android.system.OsConstants.O_TRUNC;
 import static android.system.OsConstants.O_WRONLY;
+import static android.system.OsConstants.R_OK;
 import static android.system.OsConstants.SPLICE_F_MORE;
 import static android.system.OsConstants.SPLICE_F_MOVE;
 import static android.system.OsConstants.S_ISFIFO;
 import static android.system.OsConstants.S_ISREG;
+import static android.system.OsConstants.W_OK;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1300,6 +1303,23 @@
     }
 
     /** {@hide} */
+    public static int translateModeAccessToPosix(int mode) {
+        if (mode == F_OK) {
+            // There's not an exact mapping, so we attempt a read-only open to
+            // determine if a file exists
+            return O_RDONLY;
+        } else if ((mode & (R_OK | W_OK)) == (R_OK | W_OK)) {
+            return O_RDWR;
+        } else if ((mode & R_OK) == R_OK) {
+            return O_RDONLY;
+        } else if ((mode & W_OK) == W_OK) {
+            return O_WRONLY;
+        } else {
+            throw new IllegalArgumentException("Bad mode: " + mode);
+        }
+    }
+
+    /** {@hide} */
     @VisibleForTesting
     public static class MemoryPipe extends Thread implements AutoCloseable {
         private final FileDescriptor[] pipe;
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 0c56d48..b25707a 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -32,6 +32,8 @@
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -55,8 +57,8 @@
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
     private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
     private static final String ANGLE_PACKAGE_NAME = "com.android.angle";
-    private static final String GLES_MODE_METADATA_KEY = "com.android.angle.GLES_MODE";
     private static final String ANGLE_RULES_FILE = "a4a_rules.json";
+    private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
 
     private ClassLoader mClassLoader;
     private String mLayerPath;
@@ -170,10 +172,18 @@
 
                     String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS);
 
-                    Log.i(TAG, "Debug layer list: " + layers);
+                    Log.i(TAG, "Vulkan debug layer list: " + layers);
                     if (layers != null && !layers.isEmpty()) {
                         setDebugLayers(layers);
                     }
+
+                    String layersGLES =
+                            coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS_GLES);
+
+                    Log.i(TAG, "GLES debug layer list: " + layersGLES);
+                    if (layersGLES != null && !layersGLES.isEmpty()) {
+                        setDebugLayersGLES(layersGLES);
+                    }
                 }
             }
         }
@@ -199,38 +209,11 @@
                 && (!angleEnabledApp.isEmpty() && !packageName.isEmpty())
                 && angleEnabledApp.equals(packageName)) {
 
-            if (DEBUG) Log.v(TAG, packageName + " opted in for ANGLE via Developer Setting");
+            Log.i(TAG, packageName + " opted in for ANGLE via Developer Setting");
 
             devOptIn = true;
         }
 
-        ApplicationInfo appInfo;
-        try {
-            appInfo = context.getPackageManager().getApplicationInfo(packageName,
-                PackageManager.GET_META_DATA);
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.w(TAG, "Failed to get info about current application: " + packageName);
-            return;
-        }
-
-        String appPref = "dontcare";
-        final BaseBundle metadata = appInfo.metaData;
-        if (metadata != null) {
-            final String glesMode = metadata.getString(GLES_MODE_METADATA_KEY);
-            if (glesMode != null) {
-                if (glesMode.equals("angle")) {
-                    appPref = "angle";
-                    if (DEBUG) Log.v(TAG, packageName + " opted for ANGLE via AndroidManifest");
-                } else if (glesMode.equals("native")) {
-                    appPref = "native";
-                    if (DEBUG) Log.v(TAG, packageName + " opted for NATIVE via AndroidManifest");
-                } else {
-                    Log.w(TAG, "Unrecognized GLES_MODE (\"" + glesMode + "\") for " + packageName
-                               + ". Supported values are \"angle\" or \"native\"");
-                }
-            }
-        }
-
         ApplicationInfo angleInfo;
         try {
             angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME,
@@ -253,39 +236,76 @@
 
         if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
 
-        // Pass the rules file to loader for ANGLE decisions
-        AssetManager angleAssets = null;
-        try {
-            angleAssets =
-                context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.w(TAG, "Failed to get AssetManager for '" + ANGLE_PACKAGE_NAME + "'");
-            return;
-        }
-
-        AssetFileDescriptor assetsFd = null;
-        try {
-            assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
-        } catch (IOException e) {
-            Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE + " from "
-                       + "'" + ANGLE_PACKAGE_NAME + "'");
-            return;
-        }
-
+        // Look up rules file to pass to ANGLE
         FileDescriptor rulesFd = null;
         long rulesOffset = 0;
         long rulesLength = 0;
-        if (assetsFd != null) {
-            rulesFd = assetsFd.getFileDescriptor();
-            rulesOffset = assetsFd.getStartOffset();
-            rulesLength = assetsFd.getLength();
-        } else {
-            Log.w(TAG, "Failed to get file descriptor for " + ANGLE_RULES_FILE);
-            return;
+
+        // Check for temporary rules if debuggable or root
+        if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) {
+            String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
+            if (angleTempRules != null && !angleTempRules.isEmpty()) {
+                Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
+                File tempRulesFile = new File(angleTempRules);
+                if (tempRulesFile.exists()) {
+                    Log.i(TAG, angleTempRules + " exists, loading file.");
+                    FileInputStream stream = null;
+                    try {
+                        stream = new FileInputStream(angleTempRules);
+                    } catch (FileNotFoundException e) {
+                        Log.w(TAG, "Unable to create stream for temp ANGLE rules");
+                    }
+
+                    if (stream != null) {
+                        try {
+                            rulesFd = stream.getFD();
+                            rulesOffset = 0;
+                            rulesLength = stream.getChannel().size();
+                            Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
+                        } catch (IOException e) {
+                            Log.w(TAG, "Failed to get input stream for " + angleTempRules);
+                        }
+                    }
+                }
+            }
+        }
+
+        // If no temp rules, load the real ones from the APK
+        if (rulesFd == null) {
+
+            // Pass the rules file to loader for ANGLE decisions
+            AssetManager angleAssets = null;
+            try {
+                angleAssets =
+                    context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.w(TAG, "Failed to get AssetManager for '" + ANGLE_PACKAGE_NAME + "'");
+                return;
+            }
+
+            AssetFileDescriptor assetsFd = null;
+            try {
+                assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
+            } catch (IOException e) {
+                Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE + " from "
+                           + "'" + ANGLE_PACKAGE_NAME + "'");
+                return;
+            }
+
+            if (assetsFd != null) {
+                rulesFd = assetsFd.getFileDescriptor();
+                rulesOffset = assetsFd.getStartOffset();
+                rulesLength = assetsFd.getLength();
+            } else {
+                Log.w(TAG, "Failed to get file descriptor for " + ANGLE_RULES_FILE);
+                return;
+            }
         }
 
         // Further opt-in logic is handled in native, so pass relevant info down
-        setAngleInfo(paths, packageName, appPref, devOptIn,
+        // TODO: Move the ANGLE selection logic earlier so we don't need to keep these
+        //       file descriptors open.
+        setAngleInfo(paths, packageName, devOptIn,
                      rulesFd, rulesOffset, rulesLength);
     }
 
@@ -424,8 +444,9 @@
     private static native int getCanLoadSystemLibraries();
     private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
     private static native void setDebugLayers(String layers);
+    private static native void setDebugLayersGLES(String layers);
     private static native void setDriverPath(String path);
-    private static native void setAngleInfo(String path, String appPackage, String appPref,
+    private static native void setAngleInfo(String path, String appPackage,
                                             boolean devOptIn, FileDescriptor rulesFd,
                                             long rulesOffset, long rulesLength);
 }
diff --git a/core/java/android/os/IThermalEventListener.aidl b/core/java/android/os/IThermalEventListener.aidl
index 9a6de60..fc93b5c 100644
--- a/core/java/android/os/IThermalEventListener.aidl
+++ b/core/java/android/os/IThermalEventListener.aidl
@@ -27,6 +27,5 @@
      * Called when a thermal throttling start/stop event is received.
      * @param temperature the temperature at which the event was generated.
      */
-    void notifyThrottling(
-        in boolean isThrottling, in Temperature temperature);
+    void notifyThrottling(in Temperature temperature);
 }
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index e388eda..287a5ed 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -19,6 +19,8 @@
 import android.os.IThermalEventListener;
 import android.os.Temperature;
 
+import java.util.List;
+
 /**
  * {@hide}
  */
@@ -30,22 +32,29 @@
       */
     void registerThermalEventListener(in IThermalEventListener listener);
     /**
+      * Register a listener for thermal events on given temperature type.
+      * @param listener the IThermalEventListener to be notified.
+      * @param type the temperature type IThermalEventListener to be notified.
+      * {@hide}
+      */
+    void registerThermalEventListenerWithType(in IThermalEventListener listener, in int type);
+    /**
       * Unregister a previously-registered listener for thermal events.
       * @param listener the IThermalEventListener to no longer be notified.
       * {@hide}
       */
     void unregisterThermalEventListener(in IThermalEventListener listener);
     /**
-      * Send a thermal throttling start/stop notification to all listeners.
-      * @param temperature the temperature at which the event was generated.
+      * Get current temperature with its throttling status.
+      * @return list of android.os.Temperature
       * {@hide}
       */
-    oneway void notifyThrottling(
-        in boolean isThrottling, in Temperature temperature);
+    List<Temperature> getCurrentTemperatures();
     /**
-      * Return whether system performance is currently thermal throttling.
-      * @return true if thermal throttling is currently in effect
+      * Get current temperature with its throttling status on given temperature type.
+      * @param type the temperature type to query.
+      * @return list of android.os.Temperature
       * {@hide}
       */
-    boolean isThrottling();
+    List<Temperature> getCurrentTemperaturesWithType(in int type);
 }
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index a54c589..70688fd 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -291,22 +291,7 @@
     }
 
     private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
-        if ((mode & MODE_READ_WRITE) == 0) {
-            throw new IllegalArgumentException(
-                    "Must specify MODE_READ_ONLY, MODE_WRITE_ONLY, or MODE_READ_WRITE");
-        }
-
-        int flags = 0;
-        switch (mode & MODE_READ_WRITE) {
-            case 0:
-            case MODE_READ_ONLY: flags = O_RDONLY; break;
-            case MODE_WRITE_ONLY: flags = O_WRONLY; break;
-            case MODE_READ_WRITE: flags = O_RDWR; break;
-        }
-
-        if ((mode & MODE_CREATE) != 0) flags |= O_CREAT;
-        if ((mode & MODE_TRUNCATE) != 0) flags |= O_TRUNC;
-        if ((mode & MODE_APPEND) != 0) flags |= O_APPEND;
+        final int flags = FileUtils.translateModePfdToPosix(mode);
 
         int realMode = S_IRWXU | S_IRWXG;
         if ((mode & MODE_WORLD_READABLE) != 0) realMode |= S_IROTH;
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index e0b2c78..27c281d 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -477,6 +477,13 @@
     public static final String SHUTDOWN_BATTERY_THERMAL_STATE = "thermal,battery";
 
     /**
+     * The value to pass as the 'reason' argument to android_reboot() when device temperature
+     * is too high.
+     * @hide
+     */
+    public static final String SHUTDOWN_THERMAL_STATE = "thermal";
+
+    /**
      * The value to pass as the 'reason' argument to android_reboot() when device is running
      * critically low on battery.
      * @hide
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 379d28c..651caec 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -990,6 +990,8 @@
     /** @hide */
     public static final int PROC_TAB_TERM = (int)'\t';
     /** @hide */
+    public static final int PROC_NEWLINE_TERM = (int) '\n';
+    /** @hide */
     public static final int PROC_COMBINE = 0x100;
     /** @hide */
     public static final int PROC_PARENS = 0x200;
@@ -1009,7 +1011,8 @@
      *
      * <p>The format is a list of integers, where every integer describes a variable in the file. It
      * specifies how the variable is syntactically terminated (e.g. {@link Process#PROC_SPACE_TERM},
-     * {@link Process#PROC_TAB_TERM}, {@link Process#PROC_ZERO_TERM}).
+     * {@link Process#PROC_TAB_TERM}, {@link Process#PROC_ZERO_TERM}, {@link
+     * Process#PROC_NEWLINE_TERM}).
      *
      * <p>If the variable should be parsed and returned to the caller, the termination type should
      * be binary OR'd with the type of output (e.g. {@link Process#PROC_OUT_STRING}, {@link
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index bbb8a7b..be8f784 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -419,9 +419,11 @@
 
     /** @hide */
     public void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.print("callbacks: "); pw.println(mCallbacks.size());
-        pw.print(prefix); pw.print("killed: "); pw.println(mKilled);
-        pw.print(prefix); pw.print("broadcasts count: "); pw.println(mBroadcastCount);
+        synchronized (mCallbacks) {
+            pw.print(prefix); pw.print("callbacks: "); pw.println(mCallbacks.size());
+            pw.print(prefix); pw.print("killed: "); pw.println(mKilled);
+            pw.print(prefix); pw.print("broadcasts count: "); pw.println(mBroadcastCount);
+        }
     }
 
     private void logExcessiveCallbacks() {
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index 8767731..37ed52c 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -16,6 +16,13 @@
 
 package android.os;
 
+import android.annotation.IntDef;
+import android.hardware.thermal.V2_0.TemperatureType;
+import android.hardware.thermal.V2_0.ThrottlingSeverity;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Temperature values used by IThermalService.
  */
@@ -24,24 +31,89 @@
  * @hide
  */
 public class Temperature implements Parcelable {
-    /* Temperature value */
+    /** Temperature value */
     private float mValue;
-    /* A temperature type from HardwarePropertiesManager */
+    /** A temperature type from ThermalHAL */
     private int mType;
+    /** Name of this temperature */
+    private String mName;
+    /** The level of the sensor is currently in throttling */
+    private int mStatus;
 
-    public Temperature() {
-        this(HardwarePropertiesManager.UNDEFINED_TEMPERATURE,
-            Integer.MIN_VALUE);
+    /** @hide */
+    @IntDef(prefix = { "THROTTLING_" }, value = {
+            THROTTLING_NONE,
+            THROTTLING_LIGHT,
+            THROTTLING_MODERATE,
+            THROTTLING_SEVERE,
+            THROTTLING_CRITICAL,
+            THROTTLING_WARNING,
+            THROTTLING_SHUTDOWN,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ThrottlingStatus {}
+
+    /** Keep in sync with hardware/interfaces/thermal/2.0/types.hal */
+    public static final int THROTTLING_NONE = ThrottlingSeverity.NONE;
+    public static final int THROTTLING_LIGHT = ThrottlingSeverity.LIGHT;
+    public static final int THROTTLING_MODERATE = ThrottlingSeverity.MODERATE;
+    public static final int THROTTLING_SEVERE = ThrottlingSeverity.SEVERE;
+    public static final int THROTTLING_CRITICAL = ThrottlingSeverity.CRITICAL;
+    public static final int THROTTLING_WARNING = ThrottlingSeverity.WARNING;
+    public static final int THROTTLING_SHUTDOWN = ThrottlingSeverity.SHUTDOWN;
+
+    /** @hide */
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_UNKNOWN,
+            TYPE_CPU,
+            TYPE_GPU,
+            TYPE_BATTERY,
+            TYPE_SKIN,
+            TYPE_USB_PORT,
+            TYPE_POWER_AMPLIFIER,
+            TYPE_BCL_VOLTAGE,
+            TYPE_BCL_CURRENT,
+            TYPE_BCL_PERCENTAGE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    /* Keep in sync with hardware/interfaces/thermal/2.0/types.hal */
+    public static final int TYPE_UNKNOWN = TemperatureType.UNKNOWN;
+    public static final int TYPE_CPU = TemperatureType.CPU;
+    public static final int TYPE_GPU = TemperatureType.GPU;
+    public static final int TYPE_BATTERY = TemperatureType.BATTERY;
+    public static final int TYPE_SKIN = TemperatureType.SKIN;
+    public static final int TYPE_USB_PORT = TemperatureType.USB_PORT;
+    public static final int TYPE_POWER_AMPLIFIER = TemperatureType.POWER_AMPLIFIER;
+    public static final int TYPE_BCL_VOLTAGE = TemperatureType.BCL_VOLTAGE;
+    public static final int TYPE_BCL_CURRENT = TemperatureType.BCL_CURRENT;
+    public static final int TYPE_BCL_PERCENTAGE = TemperatureType.BCL_PERCENTAGE;
+
+    /**
+     * Verify a valid temperature type.
+     *
+     * @return true if a temperature type is valid otherwise false.
+     */
+    public static boolean isValidType(int type) {
+        return type >= TYPE_UNKNOWN && type <= TYPE_BCL_PERCENTAGE;
     }
 
-    public Temperature(float value, int type) {
+    public Temperature() {
+        this(Float.NaN, TYPE_UNKNOWN, "", THROTTLING_NONE);
+    }
+
+    public Temperature(float value, @Type int type, String name, int status) {
         mValue = value;
-        mType = type;
+        mType = isValidType(type) ? type : TYPE_UNKNOWN;
+        mName = name;
+        mStatus = status;
     }
 
     /**
      * Return the temperature value.
-     * @return a temperature value in floating point.
+     *
+     * @return a temperature value in floating point could be NaN.
      */
     public float getValue() {
         return mValue;
@@ -49,18 +121,30 @@
 
     /**
      * Return the temperature type.
-     * @return a temperature type:
-     *         HardwarePropertiesManager.DEVICE_TEMPERATURE_CPU, etc.
+     *
+     * @return a temperature type: TYPE_*
      */
-    public int getType() {
+    public @Type int getType() {
         return mType;
     }
 
-    /*
-     * Parcel read/write code must be kept in sync with
-     * frameworks/native/services/thermalservice/aidl/android/os/
-     * Temperature.cpp
+    /**
+     * Return the temperature name.
+     *
+     * @return a temperature name as String.
      */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Return the temperature throttling status.
+     *
+     * @return a temperature throttling status: THROTTLING_*
+     */
+    public @ThrottlingStatus int getStatus() {
+        return mStatus;
+    }
 
     private Temperature(Parcel p) {
         readFromParcel(p);
@@ -68,31 +152,36 @@
 
     /**
      * Fill in Temperature members from a Parcel.
+     *
      * @param p the parceled Temperature object.
      */
     public void readFromParcel(Parcel p) {
         mValue = p.readFloat();
         mType = p.readInt();
+        mName = p.readString();
+        mStatus = p.readInt();
     }
 
     @Override
     public void writeToParcel(Parcel p, int flags) {
         p.writeFloat(mValue);
         p.writeInt(mType);
+        p.writeString(mName);
+        p.writeInt(mStatus);
     }
 
     public static final Parcelable.Creator<Temperature> CREATOR =
             new Parcelable.Creator<Temperature>() {
-        @Override
-        public Temperature createFromParcel(Parcel p) {
-            return new Temperature(p);
-        }
+                @Override
+                public Temperature createFromParcel(Parcel p) {
+                    return new Temperature(p);
+                }
 
-        @Override
-        public Temperature[] newArray(int size) {
-            return new Temperature[size];
-        }
-    };
+                @Override
+                public Temperature[] newArray(int size) {
+                    return new Temperature[size];
+                }
+            };
 
     @Override
     public int describeContents() {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 00b989e..92b3169 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -936,6 +936,21 @@
     public static final String DISALLOW_AUTOFILL = "no_autofill";
 
     /**
+     * Specifies if the contents of a user's screen is not allowed to be captured for artificial
+     * intelligence purposes.
+     *
+     * <p>Device owner and profile owner can set this restriction. When it is set by device owner,
+     * only the target user will be affected.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_INTELLIGENCE_CAPTURE = "no_intelligence_capture";
+
+    /**
      * Specifies if user switching is blocked on the current user.
      *
      * <p> This restriction can only be set by the device owner, it will be applied to all users.
@@ -1278,6 +1293,8 @@
      * @return whether this process is running under the primary user.
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public boolean isPrimaryUser() {
         UserInfo user = getUserInfo(UserHandle.myUserId());
         return user != null && user.isPrimary();
@@ -1295,10 +1312,15 @@
     }
 
     /**
+     * Used to check if this process is running as an admin user. An admin user is allowed to
+     * modify or configure certain settings that aren't available to non-admin users,
+     * create and delete additional users, etc. There can be more than one admin users.
+     *
+     * @return whether this process is running under an admin user.
      * @hide
-     * Returns whether the caller is running as an admin user. There can be more than one admin
-     * user.
      */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public boolean isAdminUser() {
         return isUserAdmin(UserHandle.myUserId());
     }
@@ -1323,12 +1345,14 @@
     }
 
     /**
-     * Returns whether the caller is running as restricted profile. Restricted profile may have
-     * a reduced number of available apps, app restrictions and account restrictions.
-     * @return whether the user making this call is a linked user
+     * Used to check if this process is running under a restricted profile. Restricted profiles
+     * may have a reduced number of available apps, app restrictions, and account restrictions.
+     *
+     * @return whether this process is running under a restricted profile.
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public boolean isRestrictedProfile() {
         try {
             return mService.isRestricted();
@@ -1374,10 +1398,13 @@
     }
 
     /**
-     * Checks if the calling app is running as a guest user.
-     * @return whether the caller is a guest user.
+     * Used to check if this process is running under a guest user. A guest user may be transient.
+     *
+     * @return whether this process is running under a guest user.
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public boolean isGuestUser() {
         UserInfo user = getUserInfo(UserHandle.myUserId());
         return user != null && user.isGuest();
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index df771df..c91cda6 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -132,6 +132,15 @@
     public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage";
 
     /** {@hide} */
+    public static final String PROP_FORCE_AUDIO = "persist.fw.force_audio";
+    /** {@hide} */
+    public static final String PROP_FORCE_VIDEO = "persist.fw.force_video";
+    /** {@hide} */
+    public static final String PROP_FORCE_IMAGES = "persist.fw.force_images";
+    /** {@hide} */
+    public static final String PROP_FORCE_LEGACY = "persist.fw.force_legacy";
+
+    /** {@hide} */
     public static final String UUID_PRIVATE_INTERNAL = null;
     /** {@hide} */
     public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 63ff7b2..f521c68 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.IVold;
 
 /**
  * Mount service local interface.
@@ -101,12 +102,34 @@
      * Delete storage sandbox for the given package.
      *
      * @param packageName The package for which the sandbox needs to be destroyed.
+     * @param sharedUserId The sharedUserId if specified by the package.
      * @param userId The userId in which the sandbox needs to be destroyed.
      */
-    public abstract void destroySandboxForApp(@NonNull String packageName, int userId);
+    public abstract void destroySandboxForApp(@NonNull String packageName,
+            @Nullable String sharedUserId, int userId);
 
     /**
      * @return Labels of storage volumes that are visible to the given userId.
      */
     public abstract String[] getVisibleVolumesForUser(int userId);
+
+    /**
+     * A listener for reset events in the StorageManagerService.
+     */
+    public interface ResetListener {
+        /**
+         * A method that should be triggered internally by StorageManagerInternal
+         * when StorageManagerService reset happens.
+         *
+         * @param vold The binder object to vold.
+         */
+        void onReset(IVold vold);
+    }
+
+    /**
+     * Add a listener to listen to reset event in StorageManagerService.
+     *
+     * @param listener The listener that will be notified on reset events.
+     */
+    public abstract void addResetListener(ResetListener listener);
 }
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 5bef7ee..8a03e9e 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -331,7 +331,12 @@
      * @return intent to request access, or {@code null} if the requested directory is invalid for
      *         that volume.
      * @see DocumentsContract
+     * @deprecated Callers should migrate to using {@link Intent#ACTION_OPEN_DOCUMENT_TREE} instead.
+     *             Launching this {@link Intent} on devices running
+     *             {@link android.os.Build.VERSION_CODES#Q} or higher, will immediately finish
+     *             with a result code of {@link android.app.Activity#RESULT_CANCELED}.
      */
+    @Deprecated
     public @Nullable Intent createAccessIntent(String directoryName) {
         if ((isPrimary() && directoryName == null) ||
                 (directoryName != null && !Environment.isStandardDirectory(directoryName))) {
@@ -425,32 +430,4 @@
         parcel.writeString(mFsUuid);
         parcel.writeString(mState);
     }
-
-    /** {@hide} */
-    public static final class ScopedAccessProviderContract {
-
-        private ScopedAccessProviderContract() {
-            throw new UnsupportedOperationException("contains constants only");
-        }
-
-        public static final String AUTHORITY = "com.android.documentsui.scopedAccess";
-
-        public static final String TABLE_PACKAGES = "packages";
-        public static final String TABLE_PERMISSIONS = "permissions";
-
-        public static final String COL_PACKAGE = "package_name";
-        public static final String COL_VOLUME_UUID = "volume_uuid";
-        public static final String COL_DIRECTORY = "directory";
-        public static final String COL_GRANTED = "granted";
-
-        public static final String[] TABLE_PACKAGES_COLUMNS = new String[] { COL_PACKAGE };
-        public static final String[] TABLE_PERMISSIONS_COLUMNS =
-                new String[] { COL_PACKAGE, COL_VOLUME_UUID, COL_DIRECTORY, COL_GRANTED };
-
-        public static final int TABLE_PACKAGES_COL_PACKAGE = 0;
-        public static final int TABLE_PERMISSIONS_COL_PACKAGE = 0;
-        public static final int TABLE_PERMISSIONS_COL_VOLUME_UUID = 1;
-        public static final int TABLE_PERMISSIONS_COL_DIRECTORY = 2;
-        public static final int TABLE_PERMISSIONS_COL_GRANTED = 3;
-    }
 }
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 7ffb22f..2ea7066 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -16,16 +16,15 @@
 
 package android.permission;
 
-import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 
 import com.android.internal.annotations.Immutable;
+import com.android.server.SystemConfig;
 
-import java.util.Arrays;
-import java.util.Collections;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
@@ -42,28 +41,8 @@
      *
      * @hide
      */
-    public static final List<SplitPermissionInfo> SPLIT_PERMISSIONS = Arrays.asList(
-            // READ_EXTERNAL_STORAGE is always required when an app requests
-            // WRITE_EXTERNAL_STORAGE, because we can't have an app that has
-            // write access without read access.  The hack here with the target
-            // target SDK version ensures that this grant is always done.
-            new SplitPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
-                    Collections.singletonList(android.Manifest.permission.READ_EXTERNAL_STORAGE),
-                    android.os.Build.VERSION_CODES.CUR_DEVELOPMENT + 1),
-            new SplitPermissionInfo(android.Manifest.permission.READ_CONTACTS,
-                    Collections.singletonList(android.Manifest.permission.READ_CALL_LOG),
-                    android.os.Build.VERSION_CODES.JELLY_BEAN),
-            new SplitPermissionInfo(android.Manifest.permission.WRITE_CONTACTS,
-                    Collections.singletonList(android.Manifest.permission.WRITE_CALL_LOG),
-                    android.os.Build.VERSION_CODES.JELLY_BEAN),
-            new SplitPermissionInfo(Manifest.permission.ACCESS_FINE_LOCATION,
-                    Collections.singletonList(
-                            android.Manifest.permission.ACCESS_BACKGROUND_LOCATION),
-                    android.os.Build.VERSION_CODES.P0),
-            new SplitPermissionInfo(Manifest.permission.ACCESS_COARSE_LOCATION,
-                    Collections.singletonList(
-                            android.Manifest.permission.ACCESS_BACKGROUND_LOCATION),
-                    android.os.Build.VERSION_CODES.P0));
+    public static final ArrayList<SplitPermissionInfo> SPLIT_PERMISSIONS =
+            SystemConfig.getInstance().getSplitPermissions();
 
     private final @NonNull Context mContext;
 
@@ -88,15 +67,14 @@
      * such an old app asks for a location permission (i.e. the
      * {@link SplitPermissionInfo#getSplitPermission()}), then the
      * {@link Manifest.permission#ACCESS_BACKGROUND_LOCATION} permission (inside
-     * {@{@link SplitPermissionInfo#getNewPermissions}) is added.
+     * {@link SplitPermissionInfo#getNewPermissions}) is added.
      *
      * <p>Note: Regular apps do not have to worry about this. The platform and permission controller
      * automatically add the new permissions where needed.
      *
      * @return All permissions that are split.
      */
-    public @NonNull
-    List<SplitPermissionInfo> getSplitPermissions() {
+    public @NonNull List<SplitPermissionInfo> getSplitPermissions() {
         return SPLIT_PERMISSIONS;
     }
 
@@ -145,9 +123,18 @@
             return mTargetSdk;
         }
 
-        private SplitPermissionInfo(@NonNull String rootPerm, @NonNull List<String> newPerms,
+        /**
+         * Constructs a split permission.
+         *
+         * @param splitPerm old permission that will be split
+         * @param newPerms list of new permissions that {@code rootPerm} will be split into
+         * @param targetSdk apps targetting SDK versions below this will have {@code rootPerm}
+         * split into {@code newPerms}
+         * @hide
+         */
+        public SplitPermissionInfo(@NonNull String splitPerm, @NonNull List<String> newPerms,
                 int targetSdk) {
-            mSplitPerm = rootPerm;
+            mSplitPerm = splitPerm;
             mNewPerms = newPerms;
             mTargetSdk = targetSdk;
         }
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index bc72c4e..3d93afd 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -509,6 +509,100 @@
         private static final int MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS = 1000 * 10;
 
         /**
+         * Value for {@link CallLog.Calls#BLOCK_REASON}, set as the default value when a call was
+         * not blocked by a CallScreeningService or any other system call blocking method.
+         */
+        public static final int BLOCK_REASON_NOT_BLOCKED = 0;
+
+        /**
+         * Value for {@link CallLog.Calls#BLOCK_REASON}, set when {@link CallLog.Calls#TYPE} is
+         * {@link CallLog.Calls#BLOCKED_TYPE} to indicate that a call was blocked by a
+         * CallScreeningService. The {@link CallLog.Calls#CALL_SCREENING_COMPONENT_NAME} and
+         * {@link CallLog.Calls#CALL_SCREENING_APP_NAME} columns will indicate which call screening
+         * service was responsible for blocking the call.
+         */
+        public static final int BLOCK_REASON_CALL_SCREENING_SERVICE = 1;
+
+        /**
+         * Value for {@link CallLog.Calls#BLOCK_REASON}, set when {@link CallLog.Calls#TYPE} is
+         * {@link CallLog.Calls#BLOCKED_TYPE} to indicate that a call was blocked because the user
+         * configured a contact to be sent directly to voicemail.
+         */
+        public static final int BLOCK_REASON_DIRECT_TO_VOICEMAIL = 2;
+
+        /**
+         * Value for {@link CallLog.Calls#BLOCK_REASON}, set when {@link CallLog.Calls#TYPE} is
+         * {@link CallLog.Calls#BLOCKED_TYPE} to indicate that a call was blocked because it is
+         * in the BlockedNumbers provider.
+         */
+        public static final int BLOCK_REASON_BLOCKED_NUMBER = 3;
+
+        /**
+         * Value for {@link CallLog.Calls#BLOCK_REASON}, set when {@link CallLog.Calls#TYPE} is
+         * {@link CallLog.Calls#BLOCKED_TYPE} to indicate that a call was blocked because the user
+         * has chosen to block all calls from unknown numbers.
+         */
+        public static final int BLOCK_REASON_UNKNOWN_NUMBER = 4;
+
+        /**
+         * Value for {@link CallLog.Calls#BLOCK_REASON}, set when {@link CallLog.Calls#TYPE} is
+         * {@link CallLog.Calls#BLOCKED_TYPE} to indicate that a call was blocked because the user
+         * has chosen to block all calls from restricted numbers.
+         */
+        public static final int BLOCK_REASON_RESTRICTED_NUMBER = 5;
+
+        /**
+         * Value for {@link CallLog.Calls#BLOCK_REASON}, set when {@link CallLog.Calls#TYPE} is
+         * {@link CallLog.Calls#BLOCKED_TYPE} to indicate that a call was blocked because the user
+         * has chosen to block all calls from pay phones.
+         */
+        public static final int BLOCK_REASON_PAY_PHONE = 6;
+
+        /**
+         * Value for {@link CallLog.Calls#BLOCK_REASON}, set when {@link CallLog.Calls#TYPE} is
+         * {@link CallLog.Calls#BLOCKED_TYPE} to indicate that a call was blocked because the user
+         * has chosen to block all calls from numbers not in their contacts.
+         */
+        public static final int BLOCK_REASON_NOT_IN_CONTACTS = 7;
+
+        /**
+         * The ComponentName of the CallScreeningService which blocked this call. Will be
+         * populated when the {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#BLOCKED_TYPE}.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CALL_SCREENING_COMPONENT_NAME = "call_screening_component_name";
+
+        /**
+         * The name of the app which blocked a call. Will be populated when the
+         * {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#BLOCKED_TYPE}. Provided as a
+         * convenience so that the call log can still indicate which app blocked a call, even if
+         * that app is no longer installed.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CALL_SCREENING_APP_NAME = "call_screening_app_name";
+
+        /**
+         * Where the {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#BLOCKED_TYPE},
+         * indicates the reason why a call is blocked.
+         * <P>Type: INTEGER</P>
+         *
+         * <p>
+         * Allowed values:
+         * <ul>
+         * <li>{@link CallLog.Calls#BLOCK_REASON_NOT_BLOCKED}</li>
+         * <li>{@link CallLog.Calls#BLOCK_REASON_CALL_SCREENING_SERVICE}</li>
+         * <li>{@link CallLog.Calls#BLOCK_REASON_DIRECT_TO_VOICEMAIL}</li>
+         * <li>{@link CallLog.Calls#BLOCK_REASON_BLOCKED_NUMBER}</li>
+         * <li>{@link CallLog.Calls#BLOCK_REASON_UNKNOWN_NUMBER}</li>
+         * <li>{@link CallLog.Calls#BLOCK_REASON_RESTRICTED_NUMBER}</li>
+         * <li>{@link CallLog.Calls#BLOCK_REASON_PAY_PHONE}</li>
+         * <li>{@link CallLog.Calls#BLOCK_REASON_NOT_IN_CONTACTS}</li>
+         * </ul>
+         * </p>
+         */
+        public static final String BLOCK_REASON = "block_reason";
+
+        /**
          * Adds a call to the call log.
          *
          * @param ci the CallerInfo object to get the target contact from.  Can be null
@@ -530,12 +624,14 @@
          * {@hide}
          */
         public static Uri addCall(CallerInfo ci, Context context, String number,
-                int presentation, int callType, int features, PhoneAccountHandle accountHandle,
+                int presentation, int callType, int features,
+                PhoneAccountHandle accountHandle,
                 long start, int duration, Long dataUsage) {
-            return addCall(ci, context, number, /* postDialDigits =*/ "", /* viaNumber =*/ "",
-                    presentation, callType, features, accountHandle, start, duration,
-                    dataUsage, /* addForAllUsers =*/ false, /* userToBeInsertedTo =*/ null,
-                    /* is_read =*/ false);
+            return addCall(ci, context, number, "" /* postDialDigits */, "" /* viaNumber */,
+                presentation, callType, features, accountHandle, start, duration,
+                dataUsage, false /* addForAllUsers */, null /* userToBeInsertedTo */,
+                false /* isRead */, Calls.BLOCK_REASON_NOT_BLOCKED /* callBlockReason */,
+                null /* callScreeningAppName */, null /* callScreeningComponentName */);
         }
 
 
@@ -572,8 +668,10 @@
                 int features, PhoneAccountHandle accountHandle, long start, int duration,
                 Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo) {
             return addCall(ci, context, number, postDialDigits, viaNumber, presentation, callType,
-                    features, accountHandle, start, duration, dataUsage, addForAllUsers,
-                    userToBeInsertedTo, /* is_read =*/ false);
+                features, accountHandle, start, duration, dataUsage, addForAllUsers,
+                userToBeInsertedTo, false /* isRead */ , Calls.BLOCK_REASON_NOT_BLOCKED
+                /* callBlockReason */, null /* callScreeningAppName */,
+                null /* callScreeningComponentName */);
         }
 
         /**
@@ -602,8 +700,11 @@
          * @param userToBeInsertedTo {@link UserHandle} of user that the call is going to be
          *                           inserted to. null if it is inserted to the current user. The
          *                           value is ignored if @{link addForAllUsers} is true.
-         * @param is_read Flag to show if the missed call log has been read by the user or not.
+         * @param isRead Flag to show if the missed call log has been read by the user or not.
          *                Used for call log restore of missed calls.
+         * @param callBlockReason The reason why the call is blocked.
+         * @param callScreeningAppName The call screening application name which block the call.
+         * @param callScreeningComponentName The call screening component name which block the call.
          *
          * @result The URI of the call log entry belonging to the user that made or received this
          *        call.  This could be of the shadow provider.  Do not return it to non-system apps,
@@ -615,7 +716,8 @@
                 String postDialDigits, String viaNumber, int presentation, int callType,
                 int features, PhoneAccountHandle accountHandle, long start, int duration,
                 Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo,
-                boolean is_read) {
+                boolean isRead, int callBlockReason, String callScreeningAppName,
+                String callScreeningComponentName) {
             if (VERBOSE_LOG) {
                 Log.v(LOG_TAG, String.format("Add call: number=%s, user=%s, for all=%s",
                         number, userToBeInsertedTo, addForAllUsers));
@@ -684,12 +786,19 @@
             values.put(PHONE_ACCOUNT_ID, accountId);
             values.put(PHONE_ACCOUNT_ADDRESS, accountAddress);
             values.put(NEW, Integer.valueOf(1));
+            if ((ci != null) && (ci.name != null)) {
+                values.put(CACHED_NAME, ci.name);
+            }
             values.put(ADD_FOR_ALL_USERS, addForAllUsers ? 1 : 0);
 
             if (callType == MISSED_TYPE) {
-                values.put(IS_READ, Integer.valueOf(is_read ? 1 : 0));
+                values.put(IS_READ, Integer.valueOf(isRead ? 1 : 0));
             }
 
+            values.put(BLOCK_REASON, callBlockReason);
+            values.put(CALL_SCREENING_APP_NAME, callScreeningAppName);
+            values.put(CALL_SCREENING_COMPONENT_NAME, callScreeningComponentName);
+
             if ((ci != null) && (ci.contactIdOrZero > 0)) {
                 // Update usage information for the number associated with the contact ID.
                 // We need to use both the number and the ID for obtaining a data ID since other
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 0e782d7..68f8acd 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -461,6 +461,41 @@
     }
 
     /**
+     * Return recently modified documents under the requested root. This will
+     * only be called for roots that advertise
+     * {@link Root#FLAG_SUPPORTS_RECENTS}. The returned documents should be
+     * sorted by {@link Document#COLUMN_LAST_MODIFIED} in descending order of
+     * the most recently modified documents.
+     * <p>
+     * If this method is overriden by the concrete DocumentsProvider and
+     * QUERY_ARGS_LIMIT is specified with a nonnegative int under queryArgs, the
+     * result will be limited by that number and QUERY_ARG_LIMIT will be
+     * specified under EXTRA_HONORED_ARGS. Otherwise, a default 64 limit will
+     * be used and no QUERY_ARG* will be specified under EXTRA_HONORED_ARGS.
+     * <p>
+     * Recent documents do not support change notifications.
+     *
+     * @param projection list of {@link Document} columns to put into the
+     *            cursor. If {@code null} all supported columns should be
+     *            included.
+     * @param queryArgs the extra query arguments.
+     * @param signal used by the caller to signal if the request should be
+     *            cancelled. May be null.
+     * @see DocumentsContract#EXTRA_LOADING
+     */
+    @SuppressWarnings("unused")
+    public Cursor queryRecentDocuments(
+            String rootId, String[] projection, @Nullable Bundle queryArgs,
+            @Nullable CancellationSignal signal)
+            throws FileNotFoundException {
+        Cursor c = queryRecentDocuments(rootId, projection);
+        Bundle extras = new Bundle();
+        c.setExtras(extras);
+        extras.putStringArray(ContentResolver.EXTRA_HONORED_ARGS, new String[0]);
+        return c;
+    }
+
+    /**
      * Return metadata for the single requested document. You should avoid
      * making network requests to keep this request fast.
      *
@@ -774,7 +809,7 @@
      * Implementation is provided by the parent class. Cannot be overridden.
      *
      * @see #queryRoots(String[])
-     * @see #queryRecentDocuments(String, String[])
+     * @see #queryRecentDocuments(String, String[], Bundle, CancellationSignal)
      * @see #queryDocument(String, String[])
      * @see #queryChildDocuments(String, String[], String)
      * @see #querySearchDocuments(String, String, String[])
@@ -787,7 +822,8 @@
                 case MATCH_ROOTS:
                     return queryRoots(projection);
                 case MATCH_RECENT:
-                    return queryRecentDocuments(getRootId(uri), projection);
+                    return queryRecentDocuments(
+                            getRootId(uri), projection, queryArgs, cancellationSignal);
                 case MATCH_SEARCH:
                     return querySearchDocuments(
                             getRootId(uri), getSearchDocumentsQuery(uri), projection);
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index a1d1c57..76607e9 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -15,8 +15,6 @@
  */
 package android.provider;
 
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -25,22 +23,22 @@
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ProviderInfo;
 import android.content.pm.Signature;
 import android.database.Cursor;
 import android.graphics.Typeface;
+import android.graphics.fonts.Font;
+import android.graphics.fonts.FontFamily;
+import android.graphics.fonts.FontStyle;
 import android.graphics.fonts.FontVariationAxis;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
-import android.os.ResultReceiver;
-import android.util.ArraySet;
 import android.util.Log;
 import android.util.LruCache;
 
@@ -49,7 +47,6 @@
 import com.android.internal.util.Preconditions;
 
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -64,11 +61,11 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Utility class to deal with Font ContentProviders.
@@ -636,7 +633,34 @@
         if (uriBuffer.isEmpty()) {
             return null;
         }
-        return new Typeface.Builder(fonts, uriBuffer).build();
+
+        FontFamily.Builder familyBuilder = null;
+        for (FontInfo fontInfo : fonts) {
+            final ByteBuffer buffer = uriBuffer.get(fontInfo.getUri());
+            if (buffer == null) {
+                continue;
+            }
+            try {
+                final Font font = new Font.Builder(buffer)
+                        .setWeight(fontInfo.getWeight())
+                        .setSlant(fontInfo.isItalic()
+                                ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT)
+                        .setTtcIndex(fontInfo.getTtcIndex())
+                        .setFontVariationSettings(fontInfo.getAxes())
+                        .build();
+                if (familyBuilder == null) {
+                    familyBuilder = new FontFamily.Builder(font);
+                } else {
+                    familyBuilder.addFont(font);
+                }
+            } catch (IOException e) {
+                continue;
+            }
+        }
+        if (familyBuilder == null) {
+            return null;
+        }
+        return new Typeface.CustomFallbackBuilder(familyBuilder.build()).build();
     }
 
     /**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 57f33f0..291891e 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -16,11 +16,14 @@
 
 package android.provider;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.UnsupportedAppUsage;
+import android.app.Activity;
+import android.app.AppGlobals;
 import android.content.ClipData;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
@@ -39,19 +42,29 @@
 import android.os.CancellationSignal;
 import android.os.Environment;
 import android.os.OperationCanceledException;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
 import android.service.media.CameraPrewarmService;
+import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.List;
+import java.util.Objects;
+import java.util.Set;
 
 /**
  * The Media provider contains meta data for all available media on both internal
@@ -60,9 +73,13 @@
 public final class MediaStore {
     private final static String TAG = "MediaStore";
 
+    /** The authority for the media provider */
     public static final String AUTHORITY = "media";
+    /** A content:// style uri to the authority for the media provider */
+    public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
 
-    private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
+    private static final String VOLUME_INTERNAL = "internal";
+    private static final String VOLUME_EXTERNAL = "external";
 
     /**
      * The method name used by the media scanner and mtp to tell the media provider to
@@ -96,6 +113,15 @@
      */
     public static final String PARAM_DELETE_DATA = "deletedata";
 
+    /** {@hide} */
+    public static final String PARAM_PRIMARY = "primary";
+    /** {@hide} */
+    public static final String PARAM_SECONDARY = "secondary";
+    /** {@hide} */
+    public static final String PARAM_INCLUDE_PENDING = "includePending";
+    /** {@hide} */
+    public static final String PARAM_PROGRESS = "progress";
+
     /**
      * Activity Action: Launch a music player.
      * The activity should be able to play, browse, or manipulate music files stored on the device.
@@ -263,7 +289,7 @@
      * any personal content like existing photos or videos on the device. The
      * applications should be careful not to share any photo or video with other
      * applications or internet. The activity should use {@link
-     * android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} to display
+     * Activity#setShowWhenLocked} to display
      * on top of the lock screen while secured. There is no activity stack when
      * this flag is used, so launching more than one activity is strongly
      * discouraged.
@@ -308,8 +334,8 @@
      * it when the device is secured (e.g. with a pin, password, pattern, or face unlock).
      * Applications responding to this intent must not expose any personal content like existing
      * photos or videos on the device. The applications should be careful not to share any photo
-     * or video with other applications or internet. The activity should use {@link
-     * android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} to display on top of the
+     * or video with other applications or Internet. The activity should use {@link
+     * Activity#setShowWhenLocked} to display on top of the
      * lock screen while secured. There is no activity stack when this flag is used, so
      * launching more than one activity is strongly discouraged.
      * <p>
@@ -361,6 +387,55 @@
     public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
 
     /**
+     * Standard action that can be sent to review the given media file.
+     * <p>
+     * The launched application is expected to provide a large-scale view of the
+     * given media file, while allowing the user to quickly access other
+     * recently captured media files.
+     * <p>
+     * Input: {@link Intent#getData} is URI of the primary media item to
+     * initially display.
+     *
+     * @see #ACTION_REVIEW_SECURE
+     * @see #EXTRA_BRIGHTNESS
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public final static String ACTION_REVIEW = "android.provider.action.REVIEW";
+
+    /**
+     * Standard action that can be sent to review the given media file when the
+     * device is secured (e.g. with a pin, password, pattern, or face unlock).
+     * The applications should be careful not to share any media with other
+     * applications or Internet. The activity should use
+     * {@link Activity#setShowWhenLocked} to display on top of the lock screen
+     * while secured. There is no activity stack when this flag is used, so
+     * launching more than one activity is strongly discouraged.
+     * <p>
+     * The launched application is expected to provide a large-scale view of the
+     * given primary media file, while only allowing the user to quickly access
+     * other media from an explicit secondary list.
+     * <p>
+     * Input: {@link Intent#getData} is URI of the primary media item to
+     * initially display. {@link Intent#getClipData} is the limited list of
+     * secondary media items that the user is allowed to review. If
+     * {@link Intent#getClipData} is undefined, then no other media access
+     * should be allowed.
+     *
+     * @see #EXTRA_BRIGHTNESS
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public final static String ACTION_REVIEW_SECURE = "android.provider.action.REVIEW_SECURE";
+
+    /**
+     * When defined, the launched application is requested to set the given
+     * brightness value via
+     * {@link android.view.WindowManager.LayoutParams#screenBrightness} to help
+     * ensure a smooth transition when launching {@link #ACTION_REVIEW} or
+     * {@link #ACTION_REVIEW_SECURE} intents.
+     */
+    public final static String EXTRA_BRIGHTNESS = "android.provider.extra.BRIGHTNESS";
+
+    /**
      * The name of the Intent-extra used to control the quality of a recorded video. This is an
      * integer property. Currently value 0 means low quality, suitable for MMS messages, and
      * value 1 means high quality. In the future other quality levels may be added.
@@ -391,9 +466,200 @@
     public static final String UNKNOWN_STRING = "<unknown>";
 
     /**
+     * Update the given {@link Uri} to also include any pending media items from
+     * calls such as
+     * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
+     * By default no pending items are returned.
+     *
+     * @see MediaColumns#IS_PENDING
+     */
+    public static @NonNull Uri setIncludePending(@NonNull Uri uri) {
+        return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_PENDING, "1").build();
+    }
+
+    /**
+     * Create a new pending media item using the given parameters. Pending items
+     * are expected to have a short lifetime, and owners should either
+     * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a
+     * pending item within a few hours after first creating it.
+     *
+     * @return token which can be passed to {@link #openPending(Context, Uri)}
+     *         to work with this pending item.
+     */
+    public static @NonNull Uri createPending(@NonNull Context context,
+            @NonNull PendingParams params) {
+        final Uri.Builder builder = params.insertUri.buildUpon();
+        if (!TextUtils.isEmpty(params.primaryDirectory)) {
+            builder.appendQueryParameter(PARAM_PRIMARY, params.primaryDirectory);
+        }
+        if (!TextUtils.isEmpty(params.secondaryDirectory)) {
+            builder.appendQueryParameter(PARAM_SECONDARY, params.secondaryDirectory);
+        }
+        return context.getContentResolver().insert(builder.build(), params.insertValues);
+    }
+
+    /**
+     * Open a pending media item to make progress on it. You can open a pending
+     * item multiple times before finally calling either
+     * {@link PendingSession#publish()} or {@link PendingSession#abandon()}.
+     *
+     * @param uri token which was previously returned from
+     *            {@link #createPending(Context, PendingParams)}.
+     */
+    public static @NonNull PendingSession openPending(@NonNull Context context, @NonNull Uri uri) {
+        return new PendingSession(context, uri);
+    }
+
+    /**
+     * Parameters that describe a pending media item.
+     */
+    public static class PendingParams {
+        /** {@hide} */
+        public final Uri insertUri;
+        /** {@hide} */
+        public final ContentValues insertValues;
+        /** {@hide} */
+        public String primaryDirectory;
+        /** {@hide} */
+        public String secondaryDirectory;
+
+        /**
+         * Create parameters that describe a pending media item.
+         *
+         * @param insertUri the {@code content://} Uri where this pending item
+         *            should be inserted when finally published. For example, to
+         *            publish an image, use
+         *            {@link MediaStore.Images.Media#getContentUri(String)}.
+         */
+        public PendingParams(@NonNull Uri insertUri, @NonNull String displayName,
+                @NonNull String mimeType) {
+            this.insertUri = Objects.requireNonNull(insertUri);
+            final long now = System.currentTimeMillis() / 1000;
+            this.insertValues = new ContentValues();
+            this.insertValues.put(MediaColumns.DISPLAY_NAME, Objects.requireNonNull(displayName));
+            this.insertValues.put(MediaColumns.MIME_TYPE, Objects.requireNonNull(mimeType));
+            this.insertValues.put(MediaColumns.DATE_ADDED, now);
+            this.insertValues.put(MediaColumns.DATE_MODIFIED, now);
+            this.insertValues.put(MediaColumns.IS_PENDING, 1);
+        }
+
+        /**
+         * Optionally set the primary directory under which this pending item
+         * should be persisted. Only specific well-defined directories from
+         * {@link Environment} are allowed based on the media type being
+         * inserted.
+         * <p>
+         * For example, when creating pending {@link MediaStore.Images.Media}
+         * items, only {@link Environment#DIRECTORY_PICTURES} or
+         * {@link Environment#DIRECTORY_DCIM} are allowed.
+         * <p>
+         * You may leave this value undefined to store the media in a default
+         * location. For example, when this value is left undefined, pending
+         * {@link MediaStore.Audio.Media} items are stored under
+         * {@link Environment#DIRECTORY_MUSIC}.
+         */
+        public void setPrimaryDirectory(@Nullable String primaryDirectory) {
+            this.primaryDirectory = primaryDirectory;
+        }
+
+        /**
+         * Optionally set the secondary directory under which this pending item
+         * should be persisted. Any valid directory name is allowed.
+         * <p>
+         * You may leave this value undefined to store the media as a direct
+         * descendant of the {@link #setPrimaryDirectory(String)} location.
+         */
+        public void setSecondaryDirectory(@Nullable String secondaryDirectory) {
+            this.secondaryDirectory = secondaryDirectory;
+        }
+    }
+
+    /**
+     * Session actively working on a pending media item. Pending items are
+     * expected to have a short lifetime, and owners should either
+     * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a
+     * pending item within a few hours after first creating it.
+     */
+    public static class PendingSession implements AutoCloseable {
+        /** {@hide} */
+        private final Context mContext;
+        /** {@hide} */
+        private final Uri mUri;
+
+        /** {@hide} */
+        public PendingSession(Context context, Uri uri) {
+            mContext = Objects.requireNonNull(context);
+            mUri = Objects.requireNonNull(uri);
+        }
+
+        /**
+         * Open the underlying file representing this media item. When a media
+         * item is successfully completed, you should
+         * {@link ParcelFileDescriptor#close()} and then {@link #publish()} it.
+         *
+         * @see #notifyProgress(int)
+         */
+        public @NonNull ParcelFileDescriptor open() throws FileNotFoundException {
+            return mContext.getContentResolver().openFileDescriptor(mUri, "rw");
+        }
+
+        /**
+         * Open the underlying file representing this media item. When a media
+         * item is successfully completed, you should
+         * {@link OutputStream#close()} and then {@link #publish()} it.
+         *
+         * @see #notifyProgress(int)
+         */
+        public @NonNull OutputStream openOutputStream() throws FileNotFoundException {
+            return mContext.getContentResolver().openOutputStream(mUri);
+        }
+
+        /**
+         * Notify of current progress on this pending media item. Gallery
+         * applications may choose to surface progress information of this
+         * pending item.
+         *
+         * @param progress a percentage between 0 and 100.
+         */
+        public void notifyProgress(@IntRange(from = 0, to = 100) int progress) {
+            final Uri withProgress = mUri.buildUpon()
+                    .appendQueryParameter(PARAM_PROGRESS, Integer.toString(progress)).build();
+            mContext.getContentResolver().notifyChange(withProgress, null, 0);
+        }
+
+        /**
+         * When this media item is successfully completed, call this method to
+         * publish and make the final item visible to the user.
+         *
+         * @return the final {@code content://} Uri representing the newly
+         *         published media.
+         */
+        public @NonNull Uri publish() {
+            final ContentValues values = new ContentValues();
+            values.put(MediaColumns.IS_PENDING, 0);
+            mContext.getContentResolver().update(mUri, values, null, null);
+            return mUri;
+        }
+
+        /**
+         * When this media item has failed to be completed, call this method to
+         * destroy the pending item record and any data related to it.
+         */
+        public void abandon() {
+            mContext.getContentResolver().delete(mUri, null, null);
+        }
+
+        @Override
+        public void close() {
+            // No resources to close, but at least we can inform people that no
+            // progress is being actively made.
+            notifyProgress(-1);
+        }
+    }
+
+    /**
      * Common fields for most MediaProvider tables
      */
-
     public interface MediaColumns extends BaseColumns {
         /**
          * Path to the file on disk.
@@ -417,6 +683,23 @@
         public static final String DATA = "_data";
 
         /**
+         * Hash of the file on disk.
+         * <p>
+         * Contains a 20-byte binary blob which is the SHA-1 hash of the file as
+         * persisted on disk. For performance reasons, the hash may not be
+         * immediately available, in which case a {@code NULL} value will be
+         * returned. If the underlying file is modified, this value will be
+         * cleared and recalculated.
+         * <p>
+         * If you require the hash of a specific item, you can call
+         * {@link ContentResolver#canonicalize(Uri)}, which will block until the
+         * hash is calculated.
+         * <p>
+         * Type: BLOB
+         */
+        public static final String HASH = "_hash";
+
+        /**
          * The size of the file in bytes
          * <P>Type: INTEGER (long)</P>
          */
@@ -474,6 +757,17 @@
         public static final String IS_DRM = "is_drm";
 
         /**
+         * Flag indicating if a media item is pending, and still being inserted
+         * by its owner.
+         * <p>
+         * Type: BOOLEAN
+         *
+         * @see MediaStore#createPending(Context, PendingParams)
+         * @see MediaStore#QUERY_ARG_INCLUDE_PENDING
+         */
+        public static final String IS_PENDING = "is_pending";
+
+        /**
          * The width of the image/video in pixels.
          */
         public static final String WIDTH = "width";
@@ -484,11 +778,13 @@
         public static final String HEIGHT = "height";
 
         /**
-         * Package that contributed this media.
-         * @hide
+         * Package name that contributed this media. The value may be
+         * {@code NULL} if ownership cannot be reliably determined.
+         * <p>
+         * Type: TEXT
          */
         public static final String OWNER_PACKAGE_NAME = "owner_package_name";
-     }
+    }
 
     /**
      * Media provider table containing an index of all files in the media storage,
@@ -506,8 +802,7 @@
          * @return the URI to the files table on the given volume
          */
         public static Uri getContentUri(String volumeName) {
-            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                    "/file");
+            return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("file").build();
         }
 
         /**
@@ -520,8 +815,7 @@
          */
         public static final Uri getContentUri(String volumeName,
                 long rowId) {
-            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
-                    + "/file/" + rowId);
+            return ContentUris.withAppendedId(getContentUri(volumeName), rowId);
         }
 
         /**
@@ -530,8 +824,7 @@
          */
         @UnsupportedAppUsage
         public static Uri getMtpObjectsUri(String volumeName) {
-            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                    "/object");
+            return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("object").build();
         }
 
         /**
@@ -541,8 +834,7 @@
         @UnsupportedAppUsage
         public static final Uri getMtpObjectsUri(String volumeName,
                 long fileId) {
-            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
-                    + "/object/" + fileId);
+            return ContentUris.withAppendedId(getMtpObjectsUri(volumeName), fileId);
         }
 
         /**
@@ -552,8 +844,8 @@
         @UnsupportedAppUsage
         public static final Uri getMtpReferencesUri(String volumeName,
                 long fileId) {
-            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
-                    + "/object/" + fileId + "/references");
+            return getMtpObjectsUri(volumeName, fileId).buildUpon().appendPath("references")
+                    .build();
         }
 
         /**
@@ -561,7 +853,7 @@
          * @hide
          */
         public static final Uri getDirectoryUri(String volumeName) {
-            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + "/dir");
+            return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("dir").build();
         }
 
         /**
@@ -774,7 +1066,7 @@
              * <P>Type: INTEGER</P>
              *
              * @deprecated all thumbnails should be obtained via
-             *             {@link Images.Thumbnails#getThumbnail}, as this
+             *             {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
              *             value is no longer supported.
              */
             @Deprecated
@@ -918,8 +1210,8 @@
              * @return the URI to the image media table on the given volume
              */
             public static Uri getContentUri(String volumeName) {
-                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                        "/images/media");
+                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("images")
+                        .appendPath("media").build();
             }
 
             /**
@@ -1060,8 +1352,8 @@
              * @return the URI to the image media table on the given volume
              */
             public static Uri getContentUri(String volumeName) {
-                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                        "/images/thumbnails");
+                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("images")
+                        .appendPath("thumbnails").build();
             }
 
             /**
@@ -1368,14 +1660,29 @@
              * @return the URI to the audio media table on the given volume
              */
             public static Uri getContentUri(String volumeName) {
-                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                        "/audio/media");
+                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
+                        .appendPath("media").build();
             }
 
-            public static Uri getContentUriForPath(String path) {
-                return (path.startsWith(
-                        Environment.getStorageDirectory().getAbsolutePath() + "/")
-                        ? EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
+            /**
+             * Get the content:// style URI for the given audio media file.
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path.
+             */
+            public static @Nullable Uri getContentUriForPath(@NonNull String path) {
+                final StorageManager sm = AppGlobals.getInitialApplication()
+                        .getSystemService(StorageManager.class);
+                final StorageVolume sv = sm.getStorageVolume(new File(path));
+                if (sv != null) {
+                    if (sv.isPrimary()) {
+                        return EXTERNAL_CONTENT_URI;
+                    } else {
+                        return getContentUri(sv.getUuid());
+                    }
+                } else {
+                    return INTERNAL_CONTENT_URI;
+                }
             }
 
             /**
@@ -1451,8 +1758,8 @@
              * @return the URI to the audio genres table on the given volume
              */
             public static Uri getContentUri(String volumeName) {
-                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                        "/audio/genres");
+                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
+                        .appendPath("genres").build();
             }
 
             /**
@@ -1464,8 +1771,8 @@
              * with the given the volume and audioID
              */
             public static Uri getContentUriForAudioId(String volumeName, int audioId) {
-                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                        "/audio/media/" + audioId + "/genres");
+                return ContentUris.withAppendedId(Audio.Media.getContentUri(volumeName), audioId)
+                        .buildUpon().appendPath("genres").build();
             }
 
             /**
@@ -1501,10 +1808,10 @@
              */
             public static final class Members implements AudioColumns {
 
-                public static final Uri getContentUri(String volumeName,
-                        long genreId) {
-                    return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
-                            + "/audio/genres/" + genreId + "/members");
+                public static final Uri getContentUri(String volumeName, long genreId) {
+                    return ContentUris
+                            .withAppendedId(Audio.Genres.getContentUri(volumeName), genreId)
+                            .buildUpon().appendPath("members").build();
                 }
 
                 /**
@@ -1592,8 +1899,8 @@
              * @return the URI to the audio playlists table on the given volume
              */
             public static Uri getContentUri(String volumeName) {
-                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                        "/audio/playlists");
+                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
+                        .appendPath("playlists").build();
             }
 
             /**
@@ -1628,10 +1935,10 @@
              * Sub-directory of each playlist containing all members.
              */
             public static final class Members implements AudioColumns {
-                public static final Uri getContentUri(String volumeName,
-                        long playlistId) {
-                    return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
-                            + "/audio/playlists/" + playlistId + "/members");
+                public static final Uri getContentUri(String volumeName, long playlistId) {
+                    return ContentUris
+                            .withAppendedId(Audio.Playlists.getContentUri(volumeName), playlistId)
+                            .buildUpon().appendPath("members").build();
                 }
 
                 /**
@@ -1731,8 +2038,8 @@
              * @return the URI to the audio artists table on the given volume
              */
             public static Uri getContentUri(String volumeName) {
-                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                        "/audio/artists");
+                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
+                        .appendPath("artists").build();
             }
 
             /**
@@ -1768,10 +2075,10 @@
              * a song by the artist appears.
              */
             public static final class Albums implements AlbumColumns {
-                public static final Uri getContentUri(String volumeName,
-                        long artistId) {
-                    return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
-                            + "/audio/artists/" + artistId + "/albums");
+                public static final Uri getContentUri(String volumeName,long artistId) {
+                    return ContentUris
+                            .withAppendedId(Audio.Artists.getContentUri(volumeName), artistId)
+                            .buildUpon().appendPath("albums").build();
                 }
             }
         }
@@ -1866,8 +2173,8 @@
              * @return the URI to the audio albums table on the given volume
              */
             public static Uri getContentUri(String volumeName) {
-                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                        "/audio/albums");
+                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
+                        .appendPath("albums").build();
             }
 
             /**
@@ -2001,7 +2308,7 @@
              * <P>Type: INTEGER</P>
              *
              * @deprecated all thumbnails should be obtained via
-             *             {@link Images.Thumbnails#getThumbnail}, as this
+             *             {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
              *             value is no longer supported.
              */
             @Deprecated
@@ -2029,6 +2336,27 @@
              * <P>Type: INTEGER</P>
              */
             public static final String BOOKMARK = "bookmark";
+
+            /**
+             * The standard of color aspects
+             * <P>Type: INTEGER</P>
+             * @hide
+             */
+            public static final String COLOR_STANDARD = "color_standard";
+
+            /**
+             * The transfer of color aspects
+             * <P>Type: INTEGER</P>
+             * @hide
+             */
+            public static final String COLOR_TRANSFER = "color_transfer";
+
+            /**
+             * The range of color aspects
+             * <P>Type: INTEGER</P>
+             * @hide
+             */
+            public static final String COLOR_RANGE = "color_range";
         }
 
         public static final class Media implements VideoColumns {
@@ -2040,8 +2368,8 @@
              * @return the URI to the video media table on the given volume
              */
             public static Uri getContentUri(String volumeName) {
-                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                        "/video/media");
+                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("video")
+                        .appendPath("media").build();
             }
 
             /**
@@ -2164,8 +2492,8 @@
              * @return the URI to the image media table on the given volume
              */
             public static Uri getContentUri(String volumeName) {
-                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
-                        "/video/thumbnails");
+                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("video")
+                        .appendPath("thumbnails").build();
             }
 
             /**
@@ -2233,10 +2561,70 @@
     }
 
     /**
+     * Return list of all volume names currently available. This includes a
+     * unique name for each shared storage device that is currently mounted.
+     * <p>
+     * Each name can be passed to APIs like
+     * {@link MediaStore.Images.Media#getContentUri(String)} to query media at
+     * that location.
+     */
+    public static @NonNull Set<String> getAllVolumeNames(Context context) {
+        final StorageManager sm = context.getSystemService(StorageManager.class);
+        final Set<String> volumeNames = new ArraySet<>();
+        volumeNames.add(VOLUME_INTERNAL);
+        for (VolumeInfo vi : sm.getVolumes()) {
+            if (vi.isMountedReadable()) {
+                if (vi.isPrimary()) {
+                    volumeNames.add(VOLUME_EXTERNAL);
+                } else {
+                    volumeNames.add(vi.getFsUuid());
+                }
+            }
+        }
+        return volumeNames;
+    }
+
+    /**
+     * Return the volume name that the given {@link Uri} references.
+     */
+    public static @NonNull String getVolumeName(@NonNull Uri uri) {
+        final List<String> segments = uri.getPathSegments();
+        if (uri.getAuthority().equals(AUTHORITY) && segments != null && segments.size() > 0) {
+            return segments.get(0);
+        } else {
+            throw new IllegalArgumentException("Not a media Uri: " + uri);
+        }
+    }
+
+    /** {@hide} */
+    public static @NonNull File getVolumePath(@NonNull String volumeName)
+            throws FileNotFoundException {
+        Objects.requireNonNull(volumeName);
+
+        if (VOLUME_INTERNAL.equals(volumeName)) {
+            return Environment.getDataDirectory();
+        } else if (VOLUME_EXTERNAL.equals(volumeName)) {
+            return Environment.getExternalStorageDirectory();
+        }
+
+        final StorageManager sm = AppGlobals.getInitialApplication()
+                .getSystemService(StorageManager.class);
+        for (VolumeInfo vi : sm.getVolumes()) {
+            if (Objects.equals(vi.getFsUuid(), volumeName)) {
+                final File path = vi.getPathForUser(UserHandle.myUserId());
+                if (path == null) {
+                    throw new FileNotFoundException("Failed to find path for " + vi);
+                }
+            }
+        }
+        throw new FileNotFoundException("Failed to find path for " + volumeName);
+    }
+
+    /**
      * Uri for querying the state of the media scanner.
      */
     public static Uri getMediaScannerUri() {
-        return Uri.parse(CONTENT_AUTHORITY_SLASH + "none/media_scanner");
+        return AUTHORITY_URI.buildUpon().appendPath("none").appendPath("media_scanner").build();
     }
 
     /**
@@ -2261,16 +2649,10 @@
      * @return A version string, or null if the version could not be determined.
      */
     public static String getVersion(Context context) {
-        Cursor c = context.getContentResolver().query(
-                Uri.parse(CONTENT_AUTHORITY_SLASH + "none/version"),
-                null, null, null, null);
-        if (c != null) {
-            try {
-                if (c.moveToFirst()) {
-                    return c.getString(0);
-                }
-            } finally {
-                c.close();
+        final Uri uri = AUTHORITY_URI.buildUpon().appendPath("none").appendPath("version").build();
+        try (Cursor c = context.getContentResolver().query(uri, null, null, null, null)) {
+            if (c.moveToFirst()) {
+                return c.getString(0);
             }
         }
         return null;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 80e8f78..8e4de9f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -819,6 +819,15 @@
             "android.settings.action.MANAGE_WRITE_SETTINGS";
 
     /**
+     * Activity Action: Show screen for controlling app usage properties for an app.
+     * Input: Intent's extra EXTRA_PACKAGE_NAME must specify the application package name.
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_APP_USAGE_SETTINGS =
+            "android.settings.action.APP_USAGE_SETTINGS";
+
+    /**
      * Activity Action: Show screen of details about a particular application.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -1591,8 +1600,11 @@
      * Applications typically use this action to ask the user to revert the "Do not ask again"
      * status of directory access requests made by
      * {@link android.os.storage.StorageVolume#createAccessIntent(String)}.
+     * @deprecated use {@link #ACTION_APPLICATION_DETAILS_SETTINGS} to manage storage permissions
+     *             for a specific application
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @Deprecated
     public static final String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS =
             "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS";
 
@@ -1815,27 +1827,39 @@
 
 
     /**
-     * User has not started setup personalization.
+     * Indicates that the user has not started setup personalization.
+     * One of the possible states for {@link Secure#USER_SETUP_PERSONALIZATION_STATE}.
+     *
      * @hide
      */
+    @SystemApi
     public static final int USER_SETUP_PERSONALIZATION_NOT_STARTED = 0;
 
     /**
-     * User has not yet completed setup personalization.
+     * Indicates that the user has not yet completed setup personalization.
+     * One of the possible states for {@link Secure#USER_SETUP_PERSONALIZATION_STATE}.
+     *
      * @hide
      */
+    @SystemApi
     public static final int USER_SETUP_PERSONALIZATION_STARTED = 1;
 
     /**
-     * User has snoozed personalization and will complete it later.
+     * Indicates that the user has snoozed personalization and will complete it later.
+     * One of the possible states for {@link Secure#USER_SETUP_PERSONALIZATION_STATE}.
+     *
      * @hide
      */
+    @SystemApi
     public static final int USER_SETUP_PERSONALIZATION_PAUSED = 2;
 
     /**
-     * User has completed setup personalization.
+     * Indicates that the user has completed setup personalization.
+     * One of the possible states for {@link Secure#USER_SETUP_PERSONALIZATION_STATE}.
+     *
      * @hide
      */
+    @SystemApi
     public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10;
 
     /** @hide */
@@ -4871,6 +4895,7 @@
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_NETWORK_SHOW_RSSI);
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_ON);
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
+            MOVED_TO_GLOBAL.add(Settings.Global.WIFI_P2P_PENDING_FACTORY_RESET);
             MOVED_TO_GLOBAL.add(Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
             MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_ENABLE);
             MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_TIMEOUT);
@@ -5614,18 +5639,23 @@
         public static final String DEVICE_PROVISIONED = Global.DEVICE_PROVISIONED;
 
         /**
-         * Whether the current user has been set up via setup wizard (0 = false, 1 = true)
+         * Indicates whether the current user has completed setup via the setup wizard.
+         * <p>
+         * Type: int (0 for false, 1 for true)
+         *
          * @hide
          */
+        @SystemApi
         @TestApi
         public static final String USER_SETUP_COMPLETE = "user_setup_complete";
 
         /**
-         * The current state of device personalization.
+         * Defines the user's current state of device personalization.
+         * The possible states are defined in {@link UserSetupPersonalization}.
          *
          * @hide
-         * @see UserSetupPersonalization
          */
+        @SystemApi
         public static final String USER_SETUP_PERSONALIZATION_STATE =
                 "user_setup_personalization_state";
 
@@ -5639,10 +5669,14 @@
         public static final String TV_USER_SETUP_COMPLETE = "tv_user_setup_complete";
 
         /**
-         * Prefix for category name that marks whether a suggested action from that category was
-         * completed.
+         * The prefix for a category name that indicates whether a suggested action from that
+         * category was marked as completed.
+         * <p>
+         * Type: int (0 for false, 1 for true)
+         *
          * @hide
          */
+        @SystemApi
         public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
 
         /**
@@ -5914,11 +5948,14 @@
             "lock_screen_owner_info_enabled";
 
         /**
-         * When set by a user, allows notifications to be shown atop a securely locked screen
-         * in their full "private" form (same as when the device is unlocked).
+         * Indicates whether the user has allowed notifications to be shown atop a securely locked
+         * screen in their full "private" form (same as when the device is unlocked).
+         * <p>
+         * Type: int (0 for false, 1 for true)
+         *
          * @hide
          */
-        @UnsupportedAppUsage
+        @SystemApi
         public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS =
                 "lock_screen_allow_private_notifications";
 
@@ -6038,6 +6075,7 @@
          * shortcut. Must be its flattened {@link ComponentName}.
          * @hide
          */
+        @TestApi
         public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE =
                 "accessibility_shortcut_target_service";
 
@@ -6530,23 +6568,22 @@
         public static final String MULTI_PRESS_TIMEOUT = "multi_press_timeout";
 
         /**
-         * Whether the user specifies a minimum ui timeout to override minimum ui timeout of
-         * accessibility service
+         * Setting that specifies recommended timeout in milliseconds for controls
+         * which don't need user's interactions.
          *
-         * Type: int (0 for false, 1 for true)
          * @hide
          */
-        public static final String ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED =
-                "accessibility_minimum_ui_timeout_enabled";
+        public static final String ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS =
+                "accessibility_non_interactive_ui_timeout_ms";
 
         /**
-         * Setting that specifies ui minimum timeout in milliseconds.
+         * Setting that specifies recommended timeout in milliseconds for controls
+         * which need user's interactions.
          *
-         * @see #ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED
          * @hide
          */
-        public static final String ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS =
-                "accessibility_minimum_ui_timeout_ms";
+        public static final String ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS =
+                "accessibility_interactive_ui_timeout_ms";
 
         /**
          * List of the enabled print services.
@@ -6943,10 +6980,12 @@
         public static final String BACKUP_TRANSPORT = "backup_transport";
 
         /**
-         * Version for which the setup wizard was last shown.  Bumped for
-         * each release when there is new setup information to show.
+         * Indicates the version for which the setup wizard was last shown. The version gets
+         * bumped for each release when there is new setup information to show.
+         *
          * @hide
          */
+        @SystemApi
         public static final String LAST_SETUP_SHOWN = "last_setup_shown";
 
         /**
@@ -7259,9 +7298,13 @@
         private static final Validator DOZE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
-         * Whether doze should be always on.
+         * Indicates whether doze should be always on.
+         * <p>
+         * Type: int (0 for false, 1 for true)
+         *
          * @hide
          */
+        @SystemApi
         public static final String DOZE_ALWAYS_ON = "doze_always_on";
 
         private static final Validator DOZE_ALWAYS_ON_VALIDATOR = BOOLEAN_VALIDATOR;
@@ -7387,8 +7430,16 @@
         public static final String DIALER_DEFAULT_APPLICATION = "dialer_default_application";
 
         /**
-         * Specifies the package name currently configured to be the default application to perform
-         * the user-defined call redirection service with Telecom.
+         * Specifies the component name currently configured to be the default call screening
+         * application
+         * @hide
+         */
+        public static final String CALL_SCREENING_DEFAULT_COMPONENT =
+                "call_screening_default_component";
+
+        /**
+         * Specifies the component name currently configured to be the default application to
+         * perform the user-defined call redirection service with Telecom.
          * @hide
          */
         @UnsupportedAppUsage
@@ -7571,10 +7622,13 @@
         public static final String UNSAFE_VOLUME_MUSIC_ACTIVE_MS = "unsafe_volume_music_active_ms";
 
         /**
-         * This preference enables notification display on the lockscreen.
+         * Indicates whether notification display on the lock screen is enabled.
+         * <p>
+         * Type: int (0 for false, 1 for true)
+         *
          * @hide
          */
-        @UnsupportedAppUsage
+        @SystemApi
         public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS =
                 "lock_screen_show_notifications";
 
@@ -7768,10 +7822,13 @@
                 BOOLEAN_VALIDATOR;
 
         /**
-         * Whether Assist Gesture Deferred Setup has been completed
+         * Indicates whether the Assist Gesture Deferred Setup has been completed.
+         * <p>
+         * Type: int (0 for false, 1 for true)
          *
          * @hide
          */
+        @SystemApi
         public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
 
         /**
@@ -8144,6 +8201,12 @@
                 "packages_to_clear_data_before_full_restore";
 
         /**
+         * Setting to determine whether to use the new notification priority handling features.
+         * @hide
+         */
+        public static final String NOTIFICATION_NEW_INTERRUPTION_MODEL = "new_interruption_model";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
@@ -8255,8 +8318,9 @@
             ZEN_SETTINGS_SUGGESTION_VIEWED,
             CHARGING_SOUNDS_ENABLED,
             CHARGING_VIBRATION_ENABLED,
-            ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED,
-            ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS,
+            ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS,
+            ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS,
+            NOTIFICATION_NEW_INTERRUPTION_MODEL,
         };
 
         /**
@@ -8412,8 +8476,12 @@
             VALIDATORS.put(ZEN_SETTINGS_SUGGESTION_VIEWED, BOOLEAN_VALIDATOR);
             VALIDATORS.put(CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
             VALIDATORS.put(CHARGING_VIBRATION_ENABLED, BOOLEAN_VALIDATOR);
-            VALIDATORS.put(ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED, BOOLEAN_VALIDATOR);
-            VALIDATORS.put(ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS, NON_NEGATIVE_INTEGER_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS,
+                    NON_NEGATIVE_INTEGER_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, NON_NEGATIVE_INTEGER_VALIDATOR);
+            VALIDATORS.put(USER_SETUP_COMPLETE, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(ASSIST_GESTURE_SETUP_COMPLETE, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(NOTIFICATION_NEW_INTERRUPTION_MODEL, BOOLEAN_VALIDATOR);
         }
 
         /**
@@ -9119,16 +9187,19 @@
         */
        public static final String DEVICE_PROVISIONED = "device_provisioned";
 
-       /**
-        * Whether mobile data should be allowed while the device is being provisioned.
-        * This allows the provisioning process to turn off mobile data before the user
-        * has an opportunity to set things up, preventing other processes from burning
-        * precious bytes before wifi is setup.
-        * (0 = false, 1 = true)
-        * @hide
-        */
-       public static final String DEVICE_PROVISIONING_MOBILE_DATA_ENABLED =
-               "device_provisioning_mobile_data";
+        /**
+         * Indicates whether mobile data should be allowed while the device is being provisioned.
+         * This allows the provisioning process to turn off mobile data before the user
+         * has an opportunity to set things up, preventing other processes from burning
+         * precious bytes before wifi is setup.
+         * <p>
+         * Type: int (0 for false, 1 for true)
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final String DEVICE_PROVISIONING_MOBILE_DATA_ENABLED =
+                "device_provisioning_mobile_data";
 
        /**
         * The saved value for WindowManagerService.setForcedDisplaySize().
@@ -9925,6 +9996,15 @@
                 "wifi_rtt_background_exec_gap_ms";
 
         /**
+         * Indicate whether factory reset request is pending.
+         *
+         * Type: int (0 for false, 1 for true)
+         * @hide
+         */
+        public static final String WIFI_P2P_PENDING_FACTORY_RESET =
+                "wifi_p2p_pending_factory_reset";
+
+        /**
          * Whether soft AP will shut down after a timeout period when no devices are connected.
          *
          * Type: int (0 for false, 1 for true)
@@ -10226,6 +10306,18 @@
         public static final String WIFI_LINK_SPEED_METRICS_ENABLED =
                 "wifi_link_speed_metrics_enabled";
 
+        /**
+         * Setting to enable the PNO frequency culling optimization.
+         * Disabled by default, and setting it to 1 will enable it.
+         * The value is boolean (0 or 1).
+         * @hide
+         */
+        public static final String WIFI_PNO_FREQUENCY_CULLING_ENABLED =
+                "wifi_pno_frequency_culling_enabled";
+
+        private static final Validator WIFI_PNO_FREQUENCY_CULLING_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
        /**
         * The maximum number of times we will retry a connection to an access
         * point for which we have failed in acquiring an IP address from DHCP.
@@ -11428,6 +11520,24 @@
         public static final String NETWORK_WATCHLIST_ENABLED = "network_watchlist_enabled";
 
         /**
+         * Whether or not show hidden launcher icon apps feature is enabled.
+         * Type: int (0 for false, 1 for true)
+         * Default: 0
+         * @hide
+         */
+        public static final String SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED =
+                "show_hidden_icon_apps_enabled";
+
+        /**
+         * Whether or not show new app installed notification is enabled.
+         * Type: int (0 for false, 1 for true)
+         * Default: 0
+         * @hide
+         */
+        public static final String SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED =
+                "show_new_app_installed_notification_enabled";
+
+        /**
          * Flag to keep background restricted profiles running after exiting. If disabled,
          * the restricted profile can be put into stopped state as soon as the user leaves it.
          * Type: int (0 for false, 1 for true)
@@ -11642,13 +11752,20 @@
         public static final String ANGLE_ENABLED_APP = "angle_enabled_app";
 
         /**
-         * Ordered GPU debug layer list
+         * Ordered GPU debug layer list for Vulkan
          * i.e. <layer1>:<layer2>:...:<layerN>
          * @hide
          */
         public static final String GPU_DEBUG_LAYERS = "gpu_debug_layers";
 
         /**
+         * Ordered GPU debug layer list for GLES
+         * i.e. <layer1>:<layer2>:...:<layerN>
+         * @hide
+         */
+        public static final String GPU_DEBUG_LAYERS_GLES = "gpu_debug_layers_gles";
+
+        /**
          * Addition app for GPU layer discovery
          * @hide
          */
@@ -12103,13 +12220,14 @@
         public static final String NETWORK_SCORING_PROVISIONED = "network_scoring_provisioned";
 
         /**
-         * Whether the user wants to be prompted for password to decrypt the device on boot.
-         * This only matters if the storage is encrypted.
+         * Indicates whether the user wants to be prompted for password to decrypt the device on
+         * boot. This only matters if the storage is encrypted.
          * <p>
          * Type: int (0 for false, 1 for true)
+         *
          * @hide
          */
-        @UnsupportedAppUsage
+        @SystemApi
         public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
 
         /**
@@ -12293,12 +12411,14 @@
         public static final String SAFE_BOOT_DISALLOWED = "safe_boot_disallowed";
 
         /**
-         * Whether this device is currently in retail demo mode. If true, device
+         * Indicates whether this device is currently in retail demo mode. If true, the device
          * usage is severely limited.
          * <p>
          * Type: int (0 for false, 1 for true)
+         *
          * @hide
          */
+        @SystemApi
         public static final String DEVICE_DEMO_MODE = "device_demo_mode";
 
         /**
@@ -12409,6 +12529,17 @@
                 "privileged_device_identifier_target_q_behavior_enabled";
 
         /**
+         * If set to 1, the device identifier check will be relaxed to the previous READ_PHONE_STATE
+         * permission check for 3P apps.
+         *
+         * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
+         *
+         * @hide
+         */
+        public static final String PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED =
+                "privileged_device_identifier_3p_check_relaxed";
+
+        /**
          * If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored
          * and restoring to lower version of platform API will be skipped.
          *
@@ -12620,6 +12751,11 @@
             VALIDATORS.put(APP_AUTO_RESTRICTION_ENABLED, APP_AUTO_RESTRICTION_ENABLED_VALIDATOR);
             VALIDATORS.put(ZEN_DURATION, ZEN_DURATION_VALIDATOR);
             VALIDATORS.put(CHARGING_VIBRATION_ENABLED, CHARGING_VIBRATION_ENABLED_VALIDATOR);
+            VALIDATORS.put(DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(REQUIRE_PASSWORD_TO_DECRYPT, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(DEVICE_DEMO_MODE, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(WIFI_PNO_FREQUENCY_CULLING_ENABLED,
+                    WIFI_PNO_FREQUENCY_CULLING_ENABLED_VALIDATOR);
         }
 
         /**
diff --git a/core/java/android/rolecontrollerservice/IRoleControllerService.aidl b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
new file mode 100644
index 0000000..0000b9f
--- /dev/null
+++ b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 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 android.rolecontrollerservice;
+
+import android.app.role.IRoleManagerCallback;
+
+/**
+ * @hide
+ */
+oneway interface IRoleControllerService {
+
+    void onAddRoleHolder(in String roleName, in String packageName,
+                         in IRoleManagerCallback callback);
+
+    void onRemoveRoleHolder(in String roleName, in String packageName,
+                           in IRoleManagerCallback callback);
+
+    void onClearRoleHolders(in String roleName, in IRoleManagerCallback callback);
+}
diff --git a/core/java/android/rolecontrollerservice/RoleControllerService.java b/core/java/android/rolecontrollerservice/RoleControllerService.java
new file mode 100644
index 0000000..da11bca
--- /dev/null
+++ b/core/java/android/rolecontrollerservice/RoleControllerService.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 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 android.rolecontrollerservice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.role.IRoleManagerCallback;
+import android.app.role.RoleManager;
+import android.app.role.RoleManagerCallback;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Abstract base class for the role controller service.
+ * <p>
+ * Subclass should implement the business logic for role management, including enforcing role
+ * requirements and granting or revoking relevant privileges of roles. This class can only be
+ * implemented by the permission controller app which is registered in {@code PackageManager}.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class RoleControllerService extends Service {
+
+    private static final String LOG_TAG = RoleControllerService.class.getSimpleName();
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service. The service should also
+     * require the {@link android.Manifest.permission#BIND_ROLE_CONTROLLER_SERVICE} permission so
+     * that other applications can not abuse it.
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.rolecontrollerservice.RoleControllerService";
+
+    @Nullable
+    @Override
+    public final IBinder onBind(@Nullable Intent intent) {
+        return new IRoleControllerService.Stub() {
+
+            @Override
+            public void onAddRoleHolder(String roleName, String packageName,
+                    IRoleManagerCallback callback) {
+                Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+                Preconditions.checkStringNotEmpty(packageName,
+                        "packageName cannot be null or empty");
+                Preconditions.checkNotNull(callback, "callback cannot be null");
+                RoleControllerService.this.onAddRoleHolder(roleName, packageName,
+                        new RoleManagerCallbackDelegate(callback));
+            }
+
+            @Override
+            public void onRemoveRoleHolder(String roleName, String packageName,
+                    IRoleManagerCallback callback) {
+                Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+                Preconditions.checkStringNotEmpty(packageName,
+                        "packageName cannot be null or empty");
+                Preconditions.checkNotNull(callback, "callback cannot be null");
+                RoleControllerService.this.onRemoveRoleHolder(roleName, packageName,
+                        new RoleManagerCallbackDelegate(callback));
+            }
+
+            @Override
+            public void onClearRoleHolders(String roleName, IRoleManagerCallback callback) {
+                Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+                Preconditions.checkNotNull(callback, "callback cannot be null");
+                RoleControllerService.this.onClearRoleHolders(roleName,
+                        new RoleManagerCallbackDelegate(callback));
+            }
+        };
+    }
+
+    /**
+     * Add a specific application to the holders of a role. If the role is exclusive, the previous
+     * holder will be replaced.
+     * <p>
+     * Implementation should enforce the role requirements and grant or revoke the relevant
+     * privileges of roles.
+     *
+     * @param roleName the name of the role to add the role holder for
+     * @param packageName the package name of the application to add to the role holders
+     * @param callback the callback for whether this call is successful
+     *
+     * @see RoleManager#addRoleHolderAsUser(String, String, UserHandle, Executor,
+     *      RoleManagerCallback)
+     */
+    public abstract void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
+            @NonNull RoleManagerCallback callback);
+
+    /**
+     * Remove a specific application from the holders of a role.
+     *
+     * @param roleName the name of the role to remove the role holder for
+     * @param packageName the package name of the application to remove from the role holders
+     * @param callback the callback for whether this call is successful
+     *
+     * @see RoleManager#removeRoleHolderAsUser(String, String, UserHandle, Executor,
+     *      RoleManagerCallback)
+     */
+    public abstract void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
+            @NonNull RoleManagerCallback callback);
+
+    /**
+     * Remove all holders of a role.
+     *
+     * @param roleName the name of the role to remove role holders for
+     * @param callback the callback for whether this call is successful
+     *
+     * @see RoleManager#clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
+     */
+    public abstract void onClearRoleHolders(@NonNull String roleName,
+            @NonNull RoleManagerCallback callback);
+
+    private static class RoleManagerCallbackDelegate implements RoleManagerCallback {
+
+        private IRoleManagerCallback mCallback;
+
+        RoleManagerCallbackDelegate(IRoleManagerCallback callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        public void onSuccess() {
+            try {
+                mCallback.onSuccess();
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Error calling onSuccess() callback");
+            }
+        }
+
+        @Override
+        public void onFailure() {
+            try {
+                mCallback.onFailure();
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Error calling onFailure() callback");
+            }
+        }
+    }
+}
diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl
new file mode 100644
index 0000000..bacad8b
--- /dev/null
+++ b/core/java/android/service/intelligence/IIntelligenceService.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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 android.service.intelligence;
+
+import android.service.intelligence.InteractionSessionId;
+import android.service.intelligence.InteractionContext;
+
+import android.view.intelligence.ContentCaptureEvent;
+
+import java.util.List;
+
+
+/**
+ * Interface from the system to an intelligence service.
+ *
+ * @hide
+ */
+oneway interface IIntelligenceService {
+
+    // Called when session is created (context not null) or destroyed (context null)
+    void onSessionLifecycle(in InteractionContext context, in InteractionSessionId sessionId);
+
+    void onContentCaptureEvents(in InteractionSessionId sessionId,
+                                in List<ContentCaptureEvent> events);
+}
diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/IntelligenceService.java
new file mode 100644
index 0000000..a2b60f0
--- /dev/null
+++ b/core/java/android/service/intelligence/IntelligenceService.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 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 android.service.intelligence;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.intelligence.ContentCaptureEvent;
+
+import java.util.List;
+
+/**
+ * A service used to capture the content of the screen.
+ *
+ * <p>The data collected by this service can be analyzed and combined with other sources to provide
+ * contextual data in other areas of the system such as Autofill.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class IntelligenceService extends Service {
+
+    private static final String TAG = "IntelligenceService";
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     * To be supported, the service must also require the
+     * {@link android.Manifest.permission#BIND_INTELLIGENCE_SERVICE} permission so
+     * that other applications can not abuse it.
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.service.intelligence.IntelligenceService";
+
+    private Handler mHandler;
+
+    private final IIntelligenceService mInterface = new IIntelligenceService.Stub() {
+
+        @Override
+        public void onSessionLifecycle(InteractionContext context, InteractionSessionId sessionId)
+                throws RemoteException {
+            if (context != null) {
+                mHandler.sendMessage(
+                        obtainMessage(IntelligenceService::onCreateInteractionSession,
+                                IntelligenceService.this, context, sessionId));
+            } else {
+                mHandler.sendMessage(
+                        obtainMessage(IntelligenceService::onDestroyInteractionSession,
+                                IntelligenceService.this, sessionId));
+            }
+        }
+        @Override
+        public void onContentCaptureEvents(InteractionSessionId sessionId,
+                List<ContentCaptureEvent> events) {
+            mHandler.sendMessage(
+                    obtainMessage(IntelligenceService::onContentCaptureEvent,
+                            IntelligenceService.this, sessionId, events));
+
+        }
+    };
+
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mHandler = new Handler(Looper.getMainLooper(), null, true);
+    }
+
+    /** @hide */
+    @Override
+    public final IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mInterface.asBinder();
+        }
+        Log.w(TAG, "Tried to bind to wrong intent: " + intent);
+        return null;
+    }
+
+    /**
+     * Creates a new interaction session.
+     *
+     * @param context interaction context
+     * @param sessionId the session's Id
+     */
+    public void onCreateInteractionSession(@NonNull InteractionContext context,
+            @NonNull InteractionSessionId sessionId) {}
+
+    /**
+     * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
+     * session.
+     *
+     * @param sessionId the session's Id
+     * @param events the events
+     */
+     // TODO(b/111276913): rename to onContentCaptureEvents
+    public abstract void onContentCaptureEvent(@NonNull InteractionSessionId sessionId,
+            @NonNull List<ContentCaptureEvent> events);
+
+    /**
+     * Destroys the interaction session.
+     *
+     * @param sessionId the id of the session to destroy
+     */
+    public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {}
+}
diff --git a/core/java/android/service/intelligence/InteractionContext.aidl b/core/java/android/service/intelligence/InteractionContext.aidl
new file mode 100644
index 0000000..4ce6aa4
--- /dev/null
+++ b/core/java/android/service/intelligence/InteractionContext.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2018, 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 android.service.intelligence;
+
+parcelable InteractionContext;
diff --git a/core/java/android/service/intelligence/InteractionContext.java b/core/java/android/service/intelligence/InteractionContext.java
new file mode 100644
index 0000000..c1803ad
--- /dev/null
+++ b/core/java/android/service/intelligence/InteractionContext.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 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 android.service.intelligence;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.TaskInfo;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+// TODO(b/111276913): add javadocs / implement Parcelable / implement equals/hashcode/toString
+/** @hide */
+@SystemApi
+public final class InteractionContext implements Parcelable {
+
+    /**
+     * Flag used to indicate that the app explicitly disabled content capture for the activity
+     * (using
+     * {@link android.view.intelligence.IntelligenceManager#disableContentCapture()}),
+     * in which case the service will just receive activity-level events.
+     */
+    public static final int FLAG_DISABLED_BY_APP = 0x1;
+
+    /**
+     * Flag used to indicate that the activity's window is tagged with
+     * {@link android.view.Display#FLAG_SECURE}, in which case the service will just receive
+     * activity-level events.
+     */
+    public static final int FLAG_DISABLED_BY_FLAG_SECURE = 0x2;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_DISABLED_BY_APP,
+            FLAG_DISABLED_BY_FLAG_SECURE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ContextCreationFlags{}
+
+    // TODO(b/111276913): create new object for taskId + componentName / reuse on other places
+    private final @NonNull ComponentName mComponentName;
+    private final int mTaskId;
+    private final int mDisplayId;
+    private final int mFlags;
+
+
+    /** @hide */
+    public InteractionContext(@NonNull ComponentName componentName, int taskId, int displayId,
+            int flags) {
+        mComponentName = Preconditions.checkNotNull(componentName);
+        mTaskId = taskId;
+        mDisplayId = displayId;
+        mFlags = flags;
+    }
+
+    /**
+     * Gets the id of the {@link TaskInfo task} associated with this context.
+     */
+    public int getTaskId() {
+        return mTaskId;
+    }
+
+    /**
+     * Gets the activity associated with this context.
+     */
+    public @NonNull ComponentName getActivityComponent() {
+        return mComponentName;
+    }
+
+    /**
+     * Gets the ID of the display associated with this context, as defined by
+     * {G android.hardware.display.DisplayManager#getDisplay(int) DisplayManager.getDisplay()}.
+     */
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    /**
+     * Gets the flags associated with this context.
+     *
+     * @return any combination of {@link #FLAG_DISABLED_BY_FLAG_SECURE} and
+     * {@link #FLAG_DISABLED_BY_APP}.
+     */
+    public @ContextCreationFlags int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * @hide
+     */
+    // TODO(b/111276913): dump to proto as well
+    public void dump(PrintWriter pw) {
+        pw.print("comp="); pw.print(mComponentName.flattenToShortString());
+        pw.print(", taskId="); pw.print(mTaskId);
+        pw.print(", displayId="); pw.print(mDisplayId);
+        if (mFlags > 0) {
+            pw.print(", flags="); pw.print(mFlags);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Context[act=" + mComponentName.flattenToShortString() + ", taskId=" + mTaskId
+                + ", displayId=" + mDisplayId + ", flags=" + mFlags + "]";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeParcelable(mComponentName, flags);
+        parcel.writeInt(mTaskId);
+        parcel.writeInt(mDisplayId);
+        parcel.writeInt(mFlags);
+    }
+
+    public static final Parcelable.Creator<InteractionContext> CREATOR =
+            new Parcelable.Creator<InteractionContext>() {
+
+        @Override
+        public InteractionContext createFromParcel(Parcel parcel) {
+            final ComponentName componentName = parcel.readParcelable(null);
+            final int taskId = parcel.readInt();
+            final int displayId = parcel.readInt();
+            final int flags = parcel.readInt();
+            return new InteractionContext(componentName, taskId, displayId, flags);
+        }
+
+        @Override
+        public InteractionContext[] newArray(int size) {
+            return new InteractionContext[size];
+        }
+    };
+}
diff --git a/core/java/android/service/intelligence/InteractionSessionId.aidl b/core/java/android/service/intelligence/InteractionSessionId.aidl
new file mode 100644
index 0000000..a5392b6
--- /dev/null
+++ b/core/java/android/service/intelligence/InteractionSessionId.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2018, 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 android.service.intelligence;
+
+parcelable InteractionSessionId;
diff --git a/core/java/android/service/intelligence/InteractionSessionId.java b/core/java/android/service/intelligence/InteractionSessionId.java
new file mode 100644
index 0000000..667193b
--- /dev/null
+++ b/core/java/android/service/intelligence/InteractionSessionId.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 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 android.service.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.PrintWriter;
+import java.util.UUID;
+
+// TODO(b/111276913): add javadocs / implement equals/hashcode/string
+/** @hide */
+@SystemApi
+public final class InteractionSessionId implements Parcelable {
+
+    private final @NonNull String mValue;
+
+    /**
+     * Creates a new instance.
+     *
+     * @hide
+     */
+    public InteractionSessionId() {
+        this(UUID.randomUUID().toString());
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param value The internal value.
+     *
+     * @hide
+     */
+    public InteractionSessionId(@NonNull String value) {
+        mValue = value;
+    }
+
+    /**
+     * @hide
+     */
+    public String getValue() {
+        return mValue;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((mValue == null) ? 0 : mValue.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        final InteractionSessionId other = (InteractionSessionId) obj;
+        if (mValue == null) {
+            if (other.mValue != null) return false;
+        } else if (!mValue.equals(other.mValue)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p><b>NOTE: </b>this method is only useful for debugging purposes and is not guaranteed to
+     * be stable, hence it should not be used to identify the session.
+     */
+    @Override
+    public String toString() {
+        return mValue;
+    }
+
+    /** @hide */
+    // TODO(b/111276913): dump to proto as well
+    public void dump(PrintWriter pw) {
+        pw.print(mValue);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mValue);
+    }
+
+    public static final Parcelable.Creator<InteractionSessionId> CREATOR =
+            new Parcelable.Creator<InteractionSessionId>() {
+
+        @Override
+        public InteractionSessionId createFromParcel(Parcel parcel) {
+            return new InteractionSessionId(parcel.readString());
+        }
+
+        @Override
+        public InteractionSessionId[] newArray(int size) {
+            return new InteractionSessionId[size];
+        }
+    };
+}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 37a9b10..8371c31b 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -69,9 +69,9 @@
 public class ZenModeConfig implements Parcelable {
     private static String TAG = "ZenModeConfig";
 
-    public static final int SOURCE_ANYONE = 0;
-    public static final int SOURCE_CONTACT = 1;
-    public static final int SOURCE_STAR = 2;
+    public static final int SOURCE_ANYONE = Policy.PRIORITY_SENDERS_ANY;
+    public static final int SOURCE_CONTACT = Policy.PRIORITY_SENDERS_CONTACTS;
+    public static final int SOURCE_STAR = Policy.PRIORITY_SENDERS_STARRED;
     public static final int MAX_SOURCE = SOURCE_STAR;
     private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
     private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR;
@@ -777,24 +777,6 @@
     };
 
     /**
-     * @return notification policy based on manual and automatic rules
-     */
-    public Policy getConsolidatedNotificationPolicy() {
-        ZenPolicy policy = new ZenPolicy();
-
-        // assumption: manual rule always uses the default policy
-        for (ZenRule rule : automaticRules.values()) {
-            if (rule.isAutomaticActive()) {
-                if (rule.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
-                    policy.apply(rule.zenPolicy);
-                }
-            }
-        }
-
-        return toNotificationPolicy(policy);
-    }
-
-    /**
      * Converts a zenPolicy to a notificationPolicy using this ZenModeConfig's values as its
      * defaults for all unset values in zenPolicy
      */
@@ -891,7 +873,7 @@
         }
 
         return new NotificationManager.Policy(priorityCategories, callSenders,
-                messageSenders, suppressedVisualEffects);
+                messageSenders, suppressedVisualEffects, defaultPolicy.state);
     }
 
     private boolean isPriorityCategoryEnabled(int categoryType, Policy policy) {
@@ -945,6 +927,7 @@
         }
         priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
         priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
+
         return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
                 suppressedVisualEffects, areChannelsBypassingDnd
                 ? Policy.STATE_CHANNELS_BYPASSING_DND : 0);
@@ -1732,13 +1715,25 @@
     }
 
     /**
+     * Determines whether dnd behavior should mute all sounds controlled by ringer
+     */
+    public static boolean areAllZenBehaviorSoundsMuted(NotificationManager.Policy
+            policy) {
+        boolean allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
+        boolean allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
+        boolean allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
+        return !allowAlarms && !allowMedia && !allowSystem
+                && areAllPriorityOnlyNotificationZenSoundsMuted(policy);
+    }
+
+    /**
      * Determines if DND is currently overriding the ringer
      */
-    public static boolean isZenOverridingRinger(int zen, ZenModeConfig zenConfig) {
+    public static boolean isZenOverridingRinger(int zen, Policy consolidatedPolicy) {
         return zen == Global.ZEN_MODE_NO_INTERRUPTIONS
                 || zen == Global.ZEN_MODE_ALARMS
                 || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
-                && ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(zenConfig));
+                && ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(consolidatedPolicy));
     }
 
     /**
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 1ccf529..43ab8dc 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -818,6 +818,10 @@
      * @hide
      */
     public void apply(ZenPolicy policyToApply) {
+        if (policyToApply == null) {
+            return;
+        }
+
         // apply priority categories
         for (int category = 0; category < mPriorityCategories.size(); category++) {
             if (mPriorityCategories.get(category) == STATE_DISALLOW) {
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 7af9db8..7f1082d 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -17,7 +17,6 @@
 package android.service.textclassifier;
 
 import android.Manifest;
-import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -98,8 +97,7 @@
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
             TextClassifierService.this.onSuggestSelection(
-                    request.getText(), request.getStartIndex(), request.getEndIndex(),
-                    TextSelection.Options.from(sessionId, request), mCancellationSignal,
+                    sessionId, request, mCancellationSignal,
                     new Callback<TextSelection>() {
                         @Override
                         public void onSuccess(TextSelection result) {
@@ -132,8 +130,7 @@
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
             TextClassifierService.this.onClassifyText(
-                    request.getText(), request.getStartIndex(), request.getEndIndex(),
-                    TextClassification.Options.from(sessionId, request), mCancellationSignal,
+                    sessionId, request, mCancellationSignal,
                     new Callback<TextClassification>() {
                         @Override
                         public void onSuccess(TextClassification result) {
@@ -164,7 +161,7 @@
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
             TextClassifierService.this.onGenerateLinks(
-                    request.getText(), TextLinks.Options.from(sessionId, request),
+                    sessionId, request,
                     mCancellationSignal,
                     new Callback<TextLinks>() {
                         @Override
@@ -238,25 +235,6 @@
             @NonNull CancellationSignal cancellationSignal,
             @NonNull Callback<TextSelection> callback);
 
-    // TODO: Remove once apps can build against the latest sdk.
-    /** @hide */
-    public void onSuggestSelection(
-            @NonNull CharSequence text,
-            @IntRange(from = 0) int selectionStartIndex,
-            @IntRange(from = 0) int selectionEndIndex,
-            @Nullable TextSelection.Options options,
-            @NonNull CancellationSignal cancellationSignal,
-            @NonNull Callback<TextSelection> callback) {
-        final TextClassificationSessionId sessionId = options.getSessionId();
-        final TextSelection.Request request = options.getRequest() != null
-                ? options.getRequest()
-                : new TextSelection.Request.Builder(
-                        text, selectionStartIndex, selectionEndIndex)
-                        .setDefaultLocales(options.getDefaultLocales())
-                        .build();
-        onSuggestSelection(sessionId, request, cancellationSignal, callback);
-    }
-
     /**
      * Classifies the specified text and returns a {@link TextClassification} object that can be
      * used to generate a widget for handling the classified text.
@@ -272,26 +250,6 @@
             @NonNull CancellationSignal cancellationSignal,
             @NonNull Callback<TextClassification> callback);
 
-    // TODO: Remove once apps can build against the latest sdk.
-    /** @hide */
-    public void onClassifyText(
-            @NonNull CharSequence text,
-            @IntRange(from = 0) int startIndex,
-            @IntRange(from = 0) int endIndex,
-            @Nullable TextClassification.Options options,
-            @NonNull CancellationSignal cancellationSignal,
-            @NonNull Callback<TextClassification> callback) {
-        final TextClassificationSessionId sessionId = options.getSessionId();
-        final TextClassification.Request request = options.getRequest() != null
-                ? options.getRequest()
-                : new TextClassification.Request.Builder(
-                        text, startIndex, endIndex)
-                        .setDefaultLocales(options.getDefaultLocales())
-                        .setReferenceTime(options.getReferenceTime())
-                        .build();
-        onClassifyText(sessionId, request, cancellationSignal, callback);
-    }
-
     /**
      * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
      * links information.
@@ -307,23 +265,6 @@
             @NonNull CancellationSignal cancellationSignal,
             @NonNull Callback<TextLinks> callback);
 
-    // TODO: Remove once apps can build against the latest sdk.
-    /** @hide */
-    public void onGenerateLinks(
-            @NonNull CharSequence text,
-            @Nullable TextLinks.Options options,
-            @NonNull CancellationSignal cancellationSignal,
-            @NonNull Callback<TextLinks> callback) {
-        final TextClassificationSessionId sessionId = options.getSessionId();
-        final TextLinks.Request request = options.getRequest() != null
-                ? options.getRequest()
-                : new TextLinks.Request.Builder(text)
-                        .setDefaultLocales(options.getDefaultLocales())
-                        .setEntityConfig(options.getEntityConfig())
-                        .build();
-        onGenerateLinks(sessionId, request, cancellationSignal, callback);
-    }
-
     /**
      * Writes the selection event.
      * This is called when a selection event occurs. e.g. user changed selection; or smart selection
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 3ab8a0a..e5fd292 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1269,29 +1269,47 @@
      * scrolling on the specified line.
      */
     public float getLineLeft(int line) {
-        int dir = getParagraphDirection(line);
-        Alignment align = getParagraphAlignment(line);
+        final int dir = getParagraphDirection(line);
+        final Alignment align = getParagraphAlignment(line);
 
-        if (align == Alignment.ALIGN_LEFT) {
-            return 0;
-        } else if (align == Alignment.ALIGN_NORMAL) {
-            if (dir == DIR_RIGHT_TO_LEFT)
-                return getParagraphRight(line) - getLineMax(line);
-            else
-                return 0;
-        } else if (align == Alignment.ALIGN_RIGHT) {
-            return mWidth - getLineMax(line);
-        } else if (align == Alignment.ALIGN_OPPOSITE) {
-            if (dir == DIR_RIGHT_TO_LEFT)
-                return 0;
-            else
+        // First convert combinations of alignment and direction settings to
+        // three basic cases: ALIGN_LEFT, ALIGN_RIGHT and ALIGN_CENTER.
+        // For unexpected cases, it will fallback to ALIGN_LEFT.
+        final Alignment resultAlign;
+        switch(align) {
+            case ALIGN_NORMAL:
+                resultAlign =
+                        dir == DIR_RIGHT_TO_LEFT ? Alignment.ALIGN_RIGHT : Alignment.ALIGN_LEFT;
+                break;
+            case ALIGN_OPPOSITE:
+                resultAlign =
+                        dir == DIR_RIGHT_TO_LEFT ? Alignment.ALIGN_LEFT : Alignment.ALIGN_RIGHT;
+                break;
+            case ALIGN_CENTER:
+                resultAlign = Alignment.ALIGN_CENTER;
+                break;
+            case ALIGN_RIGHT:
+                resultAlign = Alignment.ALIGN_RIGHT;
+                break;
+            default: /* align == Alignment.ALIGN_LEFT */
+                resultAlign = Alignment.ALIGN_LEFT;
+        }
+
+        // Here we must use getLineMax() to do the computation, because it maybe overridden by
+        // derived class. And also note that line max equals the width of the text in that line
+        // plus the leading margin.
+        switch (resultAlign) {
+            case ALIGN_CENTER:
+                final int left = getParagraphLeft(line);
+                final float max = getLineMax(line);
+                // This computation only works when mWidth equals leadingMargin plus
+                // the width of text in this line. If this condition doesn't meet anymore,
+                // please change here too.
+                return (float) Math.floor(left + (mWidth - max) / 2);
+            case ALIGN_RIGHT:
                 return mWidth - getLineMax(line);
-        } else { /* align == Alignment.ALIGN_CENTER */
-            int left = getParagraphLeft(line);
-            int right = getParagraphRight(line);
-            int max = ((int) getLineMax(line)) & ~1;
-
-            return left + ((right - left) - max) / 2;
+            default: /* resultAlign == Alignment.ALIGN_LEFT */
+                return 0;
         }
     }
 
@@ -1300,29 +1318,40 @@
      * scrolling on the specified line.
      */
     public float getLineRight(int line) {
-        int dir = getParagraphDirection(line);
-        Alignment align = getParagraphAlignment(line);
+        final int dir = getParagraphDirection(line);
+        final Alignment align = getParagraphAlignment(line);
 
-        if (align == Alignment.ALIGN_LEFT) {
-            return getParagraphLeft(line) + getLineMax(line);
-        } else if (align == Alignment.ALIGN_NORMAL) {
-            if (dir == DIR_RIGHT_TO_LEFT)
+        final Alignment resultAlign;
+        switch(align) {
+            case ALIGN_NORMAL:
+                resultAlign =
+                        dir == DIR_RIGHT_TO_LEFT ? Alignment.ALIGN_RIGHT : Alignment.ALIGN_LEFT;
+                break;
+            case ALIGN_OPPOSITE:
+                resultAlign =
+                        dir == DIR_RIGHT_TO_LEFT ? Alignment.ALIGN_LEFT : Alignment.ALIGN_RIGHT;
+                break;
+            case ALIGN_CENTER:
+                resultAlign = Alignment.ALIGN_CENTER;
+                break;
+            case ALIGN_RIGHT:
+                resultAlign = Alignment.ALIGN_RIGHT;
+                break;
+            default: /* align == Alignment.ALIGN_LEFT */
+                resultAlign = Alignment.ALIGN_LEFT;
+        }
+
+        switch (resultAlign) {
+            case ALIGN_CENTER:
+                final int right = getParagraphRight(line);
+                final float max = getLineMax(line);
+                // This computation only works when mWidth equals leadingMargin plus width of the
+                // text in this line. If this condition doesn't meet anymore, please change here.
+                return (float) Math.ceil(right - (mWidth - max) / 2);
+            case ALIGN_RIGHT:
                 return mWidth;
-            else
-                return getParagraphLeft(line) + getLineMax(line);
-        } else if (align == Alignment.ALIGN_RIGHT) {
-            return mWidth;
-        } else if (align == Alignment.ALIGN_OPPOSITE) {
-            if (dir == DIR_RIGHT_TO_LEFT)
+            default: /* resultAlign == Alignment.ALIGN_LEFT */
                 return getLineMax(line);
-            else
-                return mWidth;
-        } else { /* align == Alignment.ALIGN_CENTER */
-            int left = getParagraphLeft(line);
-            int right = getParagraphRight(line);
-            int max = ((int) getLineMax(line)) & ~1;
-
-            return right - ((right - left) - max) / 2;
         }
     }
 
@@ -1671,7 +1700,7 @@
      * Return the vertical position of the baseline of the specified line.
      */
     public final int getLineBaseline(int line) {
-        // getLineTop(line+1) == getLineTop(line)
+        // getLineTop(line+1) == getLineBottom(line)
         return getLineTop(line+1) - getLineDescent(line);
     }
 
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 195de07..b970c25 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -84,7 +84,7 @@
 public class TextUtils {
     private static final String TAG = "TextUtils";
 
-    // Zero-width character used to fill ellipsized strings when codepoint lenght must be preserved.
+    // Zero-width character used to fill ellipsized strings when codepoint length must be preserved.
     /* package */ static final char ELLIPSIS_FILLER = '\uFEFF'; // ZERO WIDTH NO-BREAK SPACE
 
     // TODO: Based on CLDR data, these need to be localized for Dzongkha (dz) and perhaps
diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java
index 1bdcff9..7e499f2 100644
--- a/core/java/android/transition/Scene.java
+++ b/core/java/android/transition/Scene.java
@@ -16,6 +16,8 @@
 
 package android.transition;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.util.SparseArray;
@@ -196,22 +198,24 @@
      * information is used by Scene to determine whether there is a previous
      * scene which should be exited before the new scene is entered.
      *
-     * @param view The view on which the current scene is being set
+     * @param sceneRoot The view on which the current scene is being set
      */
     @UnsupportedAppUsage
-    static void setCurrentScene(View view, Scene scene) {
-        view.setTagInternal(com.android.internal.R.id.current_scene, scene);
+    static void setCurrentScene(@NonNull View sceneRoot, @Nullable Scene scene) {
+        sceneRoot.setTagInternal(com.android.internal.R.id.current_scene, scene);
     }
 
     /**
      * Gets the current {@link Scene} set on the given view. A scene is set on a view
      * only if that view is the scene root.
      *
+     * @param sceneRoot The view on which the current scene will be returned
      * @return The current Scene set on this view. A value of null indicates that
      * no Scene is currently set.
      */
-    static Scene getCurrentScene(View view) {
-        return (Scene) view.getTag(com.android.internal.R.id.current_scene);
+    @Nullable
+    public static Scene getCurrentScene(@NonNull View sceneRoot) {
+        return (Scene) sceneRoot.getTag(com.android.internal.R.id.current_scene);
     }
 
     /**
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index b79228d..8819d22 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -531,7 +531,7 @@
             View view = unmatchedStart.keyAt(i);
             if (view != null && isValidTarget(view)) {
                 TransitionValues end = unmatchedEnd.remove(view);
-                if (end != null && end.view != null && isValidTarget(end.view)) {
+                if (end != null && isValidTarget(end.view)) {
                     TransitionValues start = unmatchedStart.removeAt(i);
                     mStartValuesList.add(start);
                     mEndValuesList.add(end);
@@ -738,9 +738,8 @@
                     if (end != null) {
                         view = end.view;
                         String[] properties = getTransitionProperties();
-                        if (view != null && properties != null && properties.length > 0) {
-                            infoValues = new TransitionValues();
-                            infoValues.view = view;
+                        if (properties != null && properties.length > 0) {
+                            infoValues = new TransitionValues(view);
                             TransitionValues newValues = endValues.viewValues.get(view);
                             if (newValues != null) {
                                 for (int j = 0; j < properties.length; ++j) {
@@ -1431,8 +1430,7 @@
                 int id = mTargetIds.get(i);
                 View view = sceneRoot.findViewById(id);
                 if (view != null) {
-                    TransitionValues values = new TransitionValues();
-                    values.view = view;
+                    TransitionValues values = new TransitionValues(view);
                     if (start) {
                         captureStartValues(values);
                     } else {
@@ -1449,8 +1447,7 @@
             }
             for (int i = 0; i < mTargets.size(); ++i) {
                 View view = mTargets.get(i);
-                TransitionValues values = new TransitionValues();
-                values.view = view;
+                TransitionValues values = new TransitionValues(view);
                 if (start) {
                     captureStartValues(values);
                 } else {
@@ -1576,8 +1573,7 @@
             }
         }
         if (view.getParent() instanceof ViewGroup) {
-            TransitionValues values = new TransitionValues();
-            values.view = view;
+            TransitionValues values = new TransitionValues(view);
             if (start) {
                 captureStartValues(values);
             } else {
diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java
index 589ad57..b9b2a70 100644
--- a/core/java/android/transition/TransitionSet.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -163,8 +163,7 @@
      */
     public TransitionSet addTransition(Transition transition) {
         if (transition != null) {
-            mTransitions.add(transition);
-            transition.mParent = this;
+            addTransitionInternal(transition);
             if (mDuration >= 0) {
                 transition.setDuration(mDuration);
             }
@@ -184,6 +183,11 @@
         return this;
     }
 
+    private void addTransitionInternal(Transition transition) {
+        mTransitions.add(transition);
+        transition.mParent = this;
+    }
+
     /**
      * Returns the number of child transitions in the TransitionSet.
      *
@@ -355,8 +359,10 @@
     public void setPathMotion(PathMotion pathMotion) {
         super.setPathMotion(pathMotion);
         mChangeFlags |= FLAG_CHANGE_PATH_MOTION;
-        for (int i = 0; i < mTransitions.size(); i++) {
-            mTransitions.get(i).setPathMotion(pathMotion);
+        if (mTransitions != null) {
+            for (int i = 0; i < mTransitions.size(); i++) {
+                mTransitions.get(i).setPathMotion(pathMotion);
+            }
         }
     }
 
@@ -604,7 +610,7 @@
         clone.mTransitions = new ArrayList<Transition>();
         int numTransitions = mTransitions.size();
         for (int i = 0; i < numTransitions; ++i) {
-            clone.addTransition((Transition) mTransitions.get(i).clone());
+            clone.addTransitionInternal((Transition) mTransitions.get(i).clone());
         }
         return clone;
     }
diff --git a/core/java/android/transition/TransitionValues.java b/core/java/android/transition/TransitionValues.java
index 11f2962..85fa67d 100644
--- a/core/java/android/transition/TransitionValues.java
+++ b/core/java/android/transition/TransitionValues.java
@@ -16,6 +16,7 @@
 
 package android.transition;
 
+import android.annotation.NonNull;
 import android.util.ArrayMap;
 import android.view.View;
 import android.view.ViewGroup;
@@ -43,19 +44,32 @@
  */
 public class TransitionValues {
 
+    /** @deprecated Use {@link #TransitionValues(View)} instead */
+    @Deprecated
+    public TransitionValues() {
+    }
+
+    public TransitionValues(@NonNull View view) {
+        this.view = view;
+    }
+
     /**
      * The View with these values
      */
+    @SuppressWarnings("NullableProblems") // Can't make it final because of deprecated constructor.
+    @NonNull
     public View view;
 
     /**
      * The set of values tracked by transitions for this scene
      */
+    @NonNull
     public final Map<String, Object> values = new ArrayMap<String, Object>();
 
     /**
      * The Transitions that targeted this view.
      */
+    @NonNull
     final ArrayList<Transition> targetedTransitions = new ArrayList<Transition>();
 
     @Override
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index bd2bef4..3c4b8c3 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -360,15 +360,49 @@
             return null;
         }
 
-        View startView = (startValues != null) ? startValues.view : null;
-        View endView = (endValues != null) ? endValues.view : null;
+        if (startValues == null) {
+            // startValues(and startView) will never be null for disappear transition.
+            return null;
+        }
+
+        final View startView = startValues.view;
+        final View endView = (endValues != null) ? endValues.view : null;
         View overlayView = null;
         View viewToKeep = null;
-        if (endView == null || endView.getParent() == null) {
-            if (endView != null) {
-                // endView was removed from its parent - add it to the overlay
-                overlayView = endView;
-            } else if (startView != null) {
+        boolean reusingOverlayView = false;
+
+        View savedOverlayView = (View) startView.getTag(R.id.transition_overlay_view_tag);
+        if (savedOverlayView != null) {
+            // we've already created overlay for the start view.
+            // it means that we are applying two visibility
+            // transitions for the same view
+            overlayView = savedOverlayView;
+            reusingOverlayView = true;
+        } else {
+            boolean needOverlayForStartView = false;
+
+            if (endView == null || endView.getParent() == null) {
+                if (endView != null) {
+                    // endView was removed from its parent - add it to the overlay
+                    overlayView = endView;
+                } else {
+                    needOverlayForStartView = true;
+                }
+            } else {
+                // visibility change
+                if (endVisibility == View.INVISIBLE) {
+                    viewToKeep = endView;
+                } else {
+                    // Becoming GONE
+                    if (startView == endView) {
+                        viewToKeep = endView;
+                    } else {
+                        needOverlayForStartView = true;
+                    }
+                }
+            }
+
+            if (needOverlayForStartView) {
                 // endView does not exist. Use startView only under certain
                 // conditions, because placing a view in an overlay necessitates
                 // it being removed from its current parent
@@ -385,74 +419,69 @@
                     if (!parentVisibilityInfo.visibilityChange) {
                         overlayView = TransitionUtils.copyViewImage(sceneRoot, startView,
                                 startParent);
-                    } else if (startParent.getParent() == null) {
+                    } else {
                         int id = startParent.getId();
-                        if (id != View.NO_ID && sceneRoot.findViewById(id) != null
-                                && mCanRemoveViews) {
+                        if (startParent.getParent() == null && id != View.NO_ID
+                                && sceneRoot.findViewById(id) != null && mCanRemoveViews) {
                             // no parent, but its parent is unparented  but the parent
                             // hierarchy has been replaced by a new hierarchy with the same id
                             // and it is safe to un-parent startView
                             overlayView = startView;
+                        } else {
+                            // TODO: Handle this case as well
                         }
                     }
                 }
             }
-        } else {
-            // visibility change
-            if (endVisibility == View.INVISIBLE) {
-                viewToKeep = endView;
-            } else {
-                // Becoming GONE
-                if (startView == endView) {
-                    viewToKeep = endView;
-                } else if (mCanRemoveViews) {
-                    overlayView = startView;
-                } else {
-                    overlayView = TransitionUtils.copyViewImage(sceneRoot, startView,
-                            (View) startView.getParent());
-                }
-            }
         }
-        final int finalVisibility = endVisibility;
 
         if (overlayView != null) {
             // TODO: Need to do this for general case of adding to overlay
-            int[] screenLoc = (int[]) startValues.values.get(PROPNAME_SCREEN_LOCATION);
-            int screenX = screenLoc[0];
-            int screenY = screenLoc[1];
-            int[] loc = new int[2];
-            sceneRoot.getLocationOnScreen(loc);
-            overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
-            overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
-            final ViewGroupOverlay overlay = sceneRoot.getOverlay();
-            overlay.add(overlayView);
-            Animator animator = onDisappear(sceneRoot, overlayView, startValues, endValues);
-            if (animator == null) {
-                overlay.remove(overlayView);
+            final ViewGroupOverlay overlay;
+            if (!reusingOverlayView) {
+                overlay = sceneRoot.getOverlay();
+                int[] screenLoc = (int[]) startValues.values.get(PROPNAME_SCREEN_LOCATION);
+                int screenX = screenLoc[0];
+                int screenY = screenLoc[1];
+                int[] loc = new int[2];
+                sceneRoot.getLocationOnScreen(loc);
+                overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
+                overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
+                overlay.add(overlayView);
             } else {
-                final View finalOverlayView = overlayView;
-                addListener(new TransitionListenerAdapter() {
+                overlay = null;
+            }
+            Animator animator = onDisappear(sceneRoot, overlayView, startValues, endValues);
+            if (!reusingOverlayView) {
+                if (animator == null) {
+                    overlay.remove(overlayView);
+                } else {
+                    startView.setTagInternal(R.id.transition_overlay_view_tag, overlayView);
+                    final View finalOverlayView = overlayView;
+                    addListener(new TransitionListenerAdapter() {
 
-                    @Override
-                    public void onTransitionPause(Transition transition) {
-                        overlay.remove(finalOverlayView);
-                    }
-
-                    @Override
-                    public void onTransitionResume(Transition transition) {
-                        if (finalOverlayView.getParent() == null) {
-                            overlay.add(finalOverlayView);
-                        } else {
-                            cancel();
+                        @Override
+                        public void onTransitionPause(Transition transition) {
+                            overlay.remove(finalOverlayView);
                         }
-                    }
 
-                    @Override
-                    public void onTransitionEnd(Transition transition) {
-                        overlay.remove(finalOverlayView);
-                        transition.removeListener(this);
-                    }
-                });
+                        @Override
+                        public void onTransitionResume(Transition transition) {
+                            if (finalOverlayView.getParent() == null) {
+                                overlay.add(finalOverlayView);
+                            } else {
+                                cancel();
+                            }
+                        }
+
+                        @Override
+                        public void onTransitionEnd(Transition transition) {
+                            startView.setTagInternal(R.id.transition_overlay_view_tag, null);
+                            overlay.remove(finalOverlayView);
+                            transition.removeListener(this);
+                        }
+                    });
+                }
             }
             return animator;
         }
@@ -463,7 +492,7 @@
             Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
             if (animator != null) {
                 DisappearListener disappearListener = new DisappearListener(viewToKeep,
-                        finalVisibility, mSuppressLayout);
+                        endVisibility, mSuppressLayout);
                 animator.addListener(disappearListener);
                 animator.addPauseListener(disappearListener);
                 addListener(disappearListener);
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index d74a0fe..4bd43d0 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -16,14 +16,17 @@
 
 package android.util;
 
+import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
+
 import libcore.util.EmptyArray;
 
-import android.annotation.UnsupportedAppUsage;
 import java.lang.reflect.Array;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Predicate;
 
 /**
  * ArraySet is a generic set data structure that is designed to be more memory efficient than a
@@ -357,6 +360,22 @@
      * @return Returns the value stored at the given index.
      */
     public E valueAt(int index) {
+        if (index >= mSize) {
+            // The array might be slightly bigger than mSize, in which case, indexing won't fail.
+            throw new ArrayIndexOutOfBoundsException(index);
+        }
+        return valueAtUnchecked(index);
+    }
+
+    /**
+     * Returns the value at the given index in the array without checking that the index is within
+     * bounds. This allows testing values at the end of the internal array, outside of the
+     * [0, mSize) bounds.
+     *
+     * @hide
+     */
+    @TestApi
+    public E valueAtUnchecked(int index) {
         return (E) mArray[index];
     }
 
@@ -491,26 +510,40 @@
         return false;
     }
 
+    /** Returns true if the array size should be decreased. */
+    private boolean shouldShrink() {
+        return mHashes.length > (BASE_SIZE * 2) && mSize < mHashes.length / 3;
+    }
+
+    /**
+     * Returns the new size the array should have. Is only valid if {@link #shouldShrink} returns
+     * true.
+     */
+    private int getNewShrunkenSize() {
+        // We don't allow it to shrink smaller than (BASE_SIZE*2) to avoid flapping between that
+        // and BASE_SIZE.
+        return mSize > (BASE_SIZE * 2) ? (mSize + (mSize >> 1)) : (BASE_SIZE * 2);
+    }
+
     /**
      * Remove the key/value mapping at the given index.
      * @param index The desired index, must be between 0 and {@link #size()}-1.
      * @return Returns the value that was stored at this index.
      */
     public E removeAt(int index) {
+        if (index >= mSize) {
+            // The array might be slightly bigger than mSize, in which case, indexing won't fail.
+            throw new ArrayIndexOutOfBoundsException(index);
+        }
         final Object old = mArray[index];
         if (mSize <= 1) {
             // Now empty.
             if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
-            freeArrays(mHashes, mArray, mSize);
-            mHashes = EmptyArray.INT;
-            mArray = EmptyArray.OBJECT;
-            mSize = 0;
+            clear();
         } else {
-            if (mHashes.length > (BASE_SIZE * 2) && mSize < mHashes.length / 3) {
-                // Shrunk enough to reduce size of arrays.  We don't allow it to
-                // shrink smaller than (BASE_SIZE*2) to avoid flapping between
-                // that and BASE_SIZE.
-                final int n = mSize > (BASE_SIZE * 2) ? (mSize + (mSize >> 1)) : (BASE_SIZE * 2);
+            if (shouldShrink()) {
+                // Shrunk enough to reduce size of arrays.
+                final int n = getNewShrunkenSize();
 
                 if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n);
 
@@ -568,6 +601,62 @@
     }
 
     /**
+     * Removes all values that satisfy the predicate. This implementation avoids using the
+     * {@link #iterator()}.
+     *
+     * @param filter A predicate which returns true for elements to be removed
+     */
+    @Override
+    public boolean removeIf(Predicate<? super E> filter) {
+        if (mSize == 0) {
+            return false;
+        }
+
+        // Intentionally not using removeAt() to avoid unnecessary intermediate resizing.
+
+        int replaceIndex = 0;
+        int numRemoved = 0;
+        for (int i = 0; i < mSize; ++i) {
+            if (filter.test((E) mArray[i])) {
+                numRemoved++;
+            } else {
+                if (replaceIndex != i) {
+                    mArray[replaceIndex] = mArray[i];
+                    mHashes[replaceIndex] = mHashes[i];
+                }
+                replaceIndex++;
+            }
+        }
+
+        if (numRemoved == 0) {
+            return false;
+        } else if (numRemoved == mSize) {
+            clear();
+            return true;
+        }
+
+        mSize -= numRemoved;
+        if (shouldShrink()) {
+            // Shrunk enough to reduce size of arrays.
+            final int n = getNewShrunkenSize();
+            final int[] ohashes = mHashes;
+            final Object[] oarray = mArray;
+            allocArrays(n);
+
+            System.arraycopy(ohashes, 0, mHashes, 0, mSize);
+            System.arraycopy(oarray, 0, mArray, 0, mSize);
+        } else {
+            // Null out values at the end of the array. Not doing it in the loop above to avoid
+            // writing twice to the same index or writing unnecessarily if the array would have been
+            // discarded anyway.
+            for (int i = mSize; i < mArray.length; ++i) {
+                mArray[i] = null;
+            }
+        }
+        return true;
+    }
+
+    /**
      * Return the number of items in this array map.
      */
     @Override
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index db2c190..b047ef7 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -36,11 +36,12 @@
     public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX;
     public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
     public static final String EMERGENCY_DIAL_SHORTCUTS = "settings_emergency_dial_shortcuts";
+    public static final String SAFETY_HUB = "settings_safety_hub";
+    public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
 
     private static final Map<String, String> DEFAULT_FLAGS;
     static {
         DEFAULT_FLAGS = new HashMap<>();
-        DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
         DEFAULT_FLAGS.put("settings_audio_switcher", "true");
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
         DEFAULT_FLAGS.put("settings_dynamic_homepage", "false");
@@ -48,7 +49,10 @@
         DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
         DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
         DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
-        DEFAULT_FLAGS.put(EMERGENCY_DIAL_SHORTCUTS, "false");
+        DEFAULT_FLAGS.put(EMERGENCY_DIAL_SHORTCUTS, "true");
+        DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
+        DEFAULT_FLAGS.put(SAFETY_HUB, "false");
+        DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
     }
 
     /**
diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java
index 37bb597..56558d0 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -17,6 +17,7 @@
 package android.util;
 
 import android.annotation.UnsupportedAppUsage;
+import android.graphics.Rect;
 
 /**
  * A class that contains utility methods related to numbers.
@@ -227,4 +228,18 @@
         }
         throw new IllegalArgumentException("Addition overflow: " + a + " + " + b);
     }
+
+    /**
+     * Resize a {@link Rect} so one size would be {@param largestSide}.
+     *
+     * @param outToResize Rectangle that will be resized.
+     * @param largestSide Size of the largest side.
+     */
+    public static void fitRect(Rect outToResize, int largestSide) {
+        if (outToResize.isEmpty()) {
+            return;
+        }
+        float maxSize = Math.max(outToResize.width(), outToResize.height());
+        outToResize.scale(largestSide / maxSize);
+    }
 }
diff --git a/core/java/android/view/GhostView.java b/core/java/android/view/GhostView.java
index 98ed217..74c801b 100644
--- a/core/java/android/view/GhostView.java
+++ b/core/java/android/view/GhostView.java
@@ -52,7 +52,7 @@
             RecordingCanvas dlCanvas = (RecordingCanvas) canvas;
             mView.mRecreateDisplayList = true;
             RenderNode renderNode = mView.updateDisplayListIfDirty();
-            if (renderNode.isValid()) {
+            if (renderNode.hasDisplayList()) {
                 dlCanvas.insertReorderBarrier(); // enable shadow for this rendernode
                 dlCanvas.drawRenderNode(renderNode);
                 dlCanvas.insertInorderBarrier(); // re-disable reordering/shadows
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 0c3a295..5c07f44 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -88,29 +88,6 @@
     void addWindowToken(IBinder token, int type, int displayId);
     void removeWindowToken(IBinder token, int displayId);
     void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
-    int getPendingAppTransition();
-    void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
-            IRemoteCallback startedCallback);
-    void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
-            int startHeight);
-    void overridePendingAppTransitionClipReveal(int startX, int startY,
-            int startWidth, int startHeight);
-    void overridePendingAppTransitionThumb(in GraphicBuffer srcThumb, int startX, int startY,
-            IRemoteCallback startedCallback, boolean scaleUp);
-    void overridePendingAppTransitionAspectScaledThumb(in GraphicBuffer srcThumb, int startX,
-            int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
-            boolean scaleUp);
-    /**
-     * Overrides animation for app transition that exits from an application to a multi-window
-     * environment and allows specifying transition animation parameters for each window.
-     *
-     * @param specs Array of transition animation descriptions for entering windows.
-     *
-     * @hide
-     */
-    void overridePendingAppTransitionMultiThumb(in AppTransitionAnimationSpec[] specs,
-            IRemoteCallback startedCallback, IRemoteCallback finishedCallback, boolean scaleUp);
-    void overridePendingAppTransitionInPlace(String packageName, int anim);
 
     /**
      * Like overridePendingAppTransitionMultiThumb, but uses a future to supply the specs. This is
@@ -134,9 +111,6 @@
     // caller must call setNewConfiguration() sometime later.
     Configuration updateOrientationFromAppTokens(in Configuration currentConfig,
             IBinder freezeThisOneIfNeeded, int displayId);
-    // Notify window manager of the new display override configuration. Returns an array of stack
-    // ids that were affected by the update, ActivityManager should resize these stacks.
-    int[] setNewDisplayOverrideConfiguration(in Configuration overrideConfig, int displayId);
 
     void startFreezingScreen(int exitAnim, int enterAnim);
     void stopFreezingScreen();
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 0a3403b..7e5c149 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -2523,10 +2523,11 @@
     }
 
     /**
-     * Retrieve the repeat count of the event.  For both key up and key down
-     * events, this is the number of times the key has repeated with the first
-     * down starting at 0 and counting up from there.  For multiple key
-     * events, this is the number of down/up pairs that have occurred.
+     * Retrieve the repeat count of the event.  For key down events,
+     * this is the number of times the key has repeated with the first
+     * down starting at 0 and counting up from there.  For key up events,
+     * this is always equal to zero. For multiple key events,
+     * this is the number of down/up pairs that have occurred.
      *
      * @return The number of times the key has repeated.
      */
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 9aab419..a4d3ce7 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -26,6 +26,7 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.graphics.Canvas;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Trace;
@@ -78,7 +79,7 @@
      * This field should be made private, so it is hidden from the SDK.
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected final Context mContext;
 
     // these are optional, set by the caller
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index c1ab4d4..f0f4c1c 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -694,7 +694,7 @@
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
         updateViewTreeDisplayList(view);
 
-        if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
+        if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) {
             RecordingCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
             try {
                 final int saveCount = canvas.save();
@@ -857,7 +857,7 @@
 
 
     void buildLayer(RenderNode node) {
-        if (node.isValid()) {
+        if (node.hasDisplayList()) {
             nBuildLayer(mNativeProxy, node.mNativeRenderNode);
         }
     }
diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java
index 6fb32e3..bef9f07 100644
--- a/core/java/android/view/TouchDelegate.java
+++ b/core/java/android/view/TouchDelegate.java
@@ -103,13 +103,13 @@
     }
 
     /**
-     * Will forward touch events to the delegate view if the event is within the bounds
+     * Forward touch events to the delegate view if the event is within the bounds
      * specified in the constructor.
      *
      * @param event The touch event to forward
-     * @return True if the event was forwarded to the delegate, false otherwise.
+     * @return True if the event was consumed by the delegate, false otherwise.
      */
-    public boolean onTouchEvent(MotionEvent event) {
+    public boolean onTouchEvent(@NonNull MotionEvent event) {
         int x = (int)event.getX();
         int y = (int)event.getY();
         boolean sendToDelegate = false;
@@ -139,18 +139,65 @@
                 break;
         }
         if (sendToDelegate) {
-            final View delegateView = mDelegateView;
-
             if (hit) {
                 // Offset event coordinates to be inside the target view
-                event.setLocation(delegateView.getWidth() / 2, delegateView.getHeight() / 2);
+                event.setLocation(mDelegateView.getWidth() / 2, mDelegateView.getHeight() / 2);
             } else {
                 // Offset event coordinates to be outside the target view (in case it does
                 // something like tracking pressed state)
                 int slop = mSlop;
                 event.setLocation(-(slop * 2), -(slop * 2));
             }
-            handled = delegateView.dispatchTouchEvent(event);
+            handled = mDelegateView.dispatchTouchEvent(event);
+        }
+        return handled;
+    }
+
+    /**
+     * Forward hover events to the delegate view if the event is within the bounds
+     * specified in the constructor and touch exploration is enabled.
+     *
+     * @param event The hover event to forward
+     * @return True if the event was consumed by the delegate, false otherwise.
+     *
+     * @see android.view.accessibility.AccessibilityManager#isTouchExplorationEnabled
+     */
+    public boolean onTouchExplorationHoverEvent(@NonNull MotionEvent event) {
+        if (mBounds == null) {
+            return false;
+        }
+
+        final int x = (int) event.getX();
+        final int y = (int) event.getY();
+        boolean hit = true;
+        boolean handled = false;
+
+        final boolean isInbound = mBounds.contains(x, y);
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_HOVER_ENTER:
+                mDelegateTargeted = isInbound;
+                break;
+            case MotionEvent.ACTION_HOVER_MOVE:
+                if (isInbound) {
+                    mDelegateTargeted = true;
+                } else {
+                    // delegated previously
+                    if (mDelegateTargeted && !mSlopBounds.contains(x, y)) {
+                        hit = false;
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_HOVER_EXIT:
+                mDelegateTargeted = true;
+                break;
+        }
+        if (mDelegateTargeted) {
+            if (hit) {
+                event.setLocation(mDelegateView.getWidth() / 2, mDelegateView.getHeight() / 2);
+            } else {
+                mDelegateTargeted = false;
+            }
+            handled = mDelegateView.dispatchHoverEvent(event);
         }
         return handled;
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1157b28..453d788 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9852,12 +9852,12 @@
             // We weren't called from within a direct call to fitSystemWindows,
             // call into it as a fallback in case we're in a class that overrides it
             // and has logic to perform.
-            if (fitSystemWindows(insets.getSystemWindowInsets())) {
+            if (fitSystemWindows(insets.getSystemWindowInsetsAsRect())) {
                 return insets.consumeSystemWindowInsets();
             }
         } else {
             // We were called from within a direct call to fitSystemWindows.
-            if (fitSystemWindowsInt(insets.getSystemWindowInsets())) {
+            if (fitSystemWindowsInt(insets.getSystemWindowInsetsAsRect())) {
                 return insets.consumeSystemWindowInsets();
             }
         }
@@ -9960,7 +9960,7 @@
     protected boolean computeFitSystemWindows(Rect inoutInsets, Rect outLocalInsets) {
         WindowInsets innerInsets = computeSystemWindowInsets(new WindowInsets(inoutInsets),
                 outLocalInsets);
-        inoutInsets.set(innerInsets.getSystemWindowInsets());
+        inoutInsets.set(innerInsets.getSystemWindowInsetsAsRect());
         return innerInsets.isSystemWindowInsetsConsumed();
     }
 
@@ -9979,7 +9979,7 @@
                 || mAttachInfo == null
                 || ((mAttachInfo.mSystemUiVisibility & SYSTEM_UI_LAYOUT_FLAGS) == 0
                 && !mAttachInfo.mOverscanRequested)) {
-            outLocalInsets.set(in.getSystemWindowInsets());
+            outLocalInsets.set(in.getSystemWindowInsetsAsRect());
             return in.consumeSystemWindowInsets().inset(outLocalInsets);
         } else {
             // The application wants to take care of fitting system window for
@@ -12830,6 +12830,15 @@
     }
 
     /**
+     * Returns true if the given point, in local coordinates, is inside the hovered child.
+     *
+     * @hide
+     */
+    protected boolean pointInHoveredChild(MotionEvent event) {
+        return false;
+    }
+
+    /**
      * Dispatch a generic motion event to the view under the first pointer.
      * <p>
      * Do not call this method directly.
@@ -13584,6 +13593,17 @@
      * @see #onHoverChanged
      */
     public boolean onHoverEvent(MotionEvent event) {
+        // Explore by touch should dispatch events to children under pointer first if any before
+        // dispatching to TouchDelegate. For children non-hoverable that will not consume events,
+        // it should also not delegate when they got the pointer hovered.
+        if (mTouchDelegate != null && !pointInHoveredChild(event)) {
+            final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
+            if (manager.isEnabled() && manager.isTouchExplorationEnabled()
+                    && mTouchDelegate.onTouchExplorationHoverEvent(event)) {
+                return true;
+            }
+        }
+
         // The root view may receive hover (or touch) events that are outside the bounds of
         // the window.  This code ensures that we only send accessibility events for
         // hovers that are actually within the bounds of the root view.
@@ -13598,7 +13618,7 @@
             }
         } else {
             if (action == MotionEvent.ACTION_HOVER_EXIT
-                    || (action == MotionEvent.ACTION_MOVE
+                    || (action == MotionEvent.ACTION_HOVER_MOVE
                             && !pointInView(event.getX(), event.getY()))) {
                 mSendingHoverAccessibilityEvents = false;
                 sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
@@ -14710,7 +14730,7 @@
      */
     public float getCameraDistance() {
         final float dpi = mResources.getDisplayMetrics().densityDpi;
-        return -(mRenderNode.getCameraDistance() * dpi);
+        return mRenderNode.getCameraDistance() * dpi;
     }
 
     /**
@@ -14756,7 +14776,7 @@
         final float dpi = mResources.getDisplayMetrics().densityDpi;
 
         invalidateViewProperty(true, false);
-        mRenderNode.setCameraDistance(-Math.abs(distance) / dpi);
+        mRenderNode.setCameraDistance(Math.abs(distance) / dpi);
         invalidateViewProperty(false, false);
 
         invalidateParentIfNeededAndWasQuickRejected();
@@ -16658,7 +16678,7 @@
     @UnsupportedAppUsage
     void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
         if (!isHardwareAccelerated()
-                || !mRenderNode.isValid()
+                || !mRenderNode.hasDisplayList()
                 || (mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
             if (invalidateParent) {
                 invalidateParentCaches();
@@ -19047,7 +19067,7 @@
         switch (mLayerType) {
             case LAYER_TYPE_HARDWARE:
                 updateDisplayListIfDirty();
-                if (attachInfo.mThreadedRenderer != null && mRenderNode.isValid()) {
+                if (attachInfo.mThreadedRenderer != null && mRenderNode.hasDisplayList()) {
                     attachInfo.mThreadedRenderer.buildLayer(mRenderNode);
                 }
                 break;
@@ -19214,11 +19234,11 @@
         }
 
         if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
-                || !renderNode.isValid()
+                || !renderNode.hasDisplayList()
                 || (mRecreateDisplayList)) {
             // Don't need to recreate the display list, just need to tell our
             // children to restore/recreate theirs
-            if (renderNode.isValid()
+            if (renderNode.hasDisplayList()
                     && !mRecreateDisplayList) {
                 mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
                 mPrivateFlags &= ~PFLAG_DIRTY_MASK;
@@ -19235,7 +19255,7 @@
             int height = mBottom - mTop;
             int layerType = getLayerType();
 
-            final RecordingCanvas canvas = renderNode.start(width, height);
+            final RecordingCanvas canvas = renderNode.startRecording(width, height);
 
             try {
                 if (layerType == LAYER_TYPE_SOFTWARE) {
@@ -19266,7 +19286,7 @@
                     }
                 }
             } finally {
-                renderNode.end(canvas);
+                renderNode.endRecording();
                 setDisplayListProperties(renderNode);
             }
         } else {
@@ -20118,7 +20138,7 @@
             // Delay getting the display list until animation-driven alpha values are
             // set up and possibly passed on to the view
             renderNode = updateDisplayListIfDirty();
-            if (!renderNode.isValid()) {
+            if (!renderNode.hasDisplayList()) {
                 // Uncommon, but possible. If a view is removed from the hierarchy during the call
                 // to getDisplayList(), the display list will be marked invalid and we should not
                 // try to use it again.
@@ -20581,7 +20601,7 @@
             mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
 
             final RenderNode renderNode = mBackgroundRenderNode;
-            if (renderNode != null && renderNode.isValid()) {
+            if (renderNode != null && renderNode.hasDisplayList()) {
                 setBackgroundRenderNodeProperties(renderNode);
                 ((RecordingCanvas) canvas).drawRenderNode(renderNode);
                 return;
@@ -20635,7 +20655,7 @@
         final Rect bounds = drawable.getBounds();
         final int width = bounds.width();
         final int height = bounds.height();
-        final RecordingCanvas canvas = renderNode.start(width, height);
+        final RecordingCanvas canvas = renderNode.startRecording(width, height);
 
         // Reverse left/top translation done by drawable canvas, which will
         // instead be applied by rendernode's LTRB bounds below. This way, the
@@ -20646,7 +20666,7 @@
         try {
             drawable.draw(canvas);
         } finally {
-            renderNode.end(canvas);
+            renderNode.endRecording();
         }
 
         // Set up drawable properties that are view-independent.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 58febb05..1e91aa8 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2383,6 +2383,16 @@
         return mFirstHoverTarget != null;
     }
 
+    /** @hide */
+    @Override
+    protected boolean pointInHoveredChild(MotionEvent event) {
+        if (mFirstHoverTarget != null) {
+            return isTransformedTouchPointInView(event.getX(), event.getY(),
+                mFirstHoverTarget.child, null);
+        }
+        return false;
+    }
+
     @Override
     public void addChildrenForAccessibility(ArrayList<View> outChildren) {
         if (getAccessibilityNodeProvider() != null) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 170c783..dd1f640 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -243,9 +243,10 @@
     final Context mContext;
     /**
      * TODO(b/116349163): Check if we can merge this into {@link #mContext}.
+     * @hide
      */
     @NonNull
-    private Context mDisplayContext;
+    public Context mDisplayContext;
 
     @UnsupportedAppUsage
     final IWindowSession mWindowSession;
@@ -2322,10 +2323,12 @@
                         mResizeMode = freeformResizing
                                 ? RESIZE_MODE_FREEFORM
                                 : RESIZE_MODE_DOCKED_DIVIDER;
+                        final boolean backdropSizeMatchesFrame =
+                                mWinFrame.width() == mPendingBackDropFrame.width()
+                                        && mWinFrame.height() == mPendingBackDropFrame.height();
                         // TODO: Need cutout?
-                        startDragResizing(mPendingBackDropFrame,
-                                mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
-                                mPendingStableInsets, mResizeMode);
+                        startDragResizing(mPendingBackDropFrame, !backdropSizeMatchesFrame,
+                                mPendingVisibleInsets, mPendingStableInsets, mResizeMode);
                     } else {
                         // We shouldn't come here, but if we come we should end the resize.
                         endDragResizing();
@@ -6874,7 +6877,7 @@
         RenderNode renderNode = view.mRenderNode;
         info[0]++;
         if (renderNode != null) {
-            info[1] += renderNode.getDebugSize();
+            info[1] += renderNode.computeApproximateMemoryUsage();
         }
 
         if (view instanceof ViewGroup) {
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 8628da3..4a7e783 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -17,8 +17,10 @@
 
 package android.view;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
+import android.graphics.Insets;
 import android.graphics.Rect;
 
 import com.android.internal.util.Preconditions;
@@ -43,26 +45,24 @@
  */
 public final class WindowInsets {
 
-    private Rect mSystemWindowInsets;
-    private Rect mWindowDecorInsets;
-    private Rect mStableInsets;
-    private Rect mTempRect;
-    private boolean mIsRound;
-    private DisplayCutout mDisplayCutout;
+    @NonNull private final Insets mSystemWindowInsets;
+    @NonNull private final Insets mWindowDecorInsets;
+    @NonNull private final Insets mStableInsets;
+    @Nullable private Rect mTempRect;
+    private final boolean mIsRound;
+    @Nullable private final DisplayCutout mDisplayCutout;
 
     /**
      * In multi-window we force show the navigation bar. Because we don't want that the surface size
      * changes in this mode, we instead have a flag whether the navigation bar size should always
      * be consumed, so the app is treated like there is no virtual navigation bar at all.
      */
-    private boolean mAlwaysConsumeNavBar;
+    private final boolean mAlwaysConsumeNavBar;
 
-    private boolean mSystemWindowInsetsConsumed = false;
-    private boolean mWindowDecorInsetsConsumed = false;
-    private boolean mStableInsetsConsumed = false;
-    private boolean mDisplayCutoutConsumed = false;
-
-    private static final Rect EMPTY_RECT = new Rect(0, 0, 0, 0);
+    private final boolean mSystemWindowInsetsConsumed;
+    private final boolean mWindowDecorInsetsConsumed;
+    private final boolean mStableInsetsConsumed;
+    private final boolean mDisplayCutoutConsumed;
 
     /**
      * Since new insets may be added in the future that existing apps couldn't
@@ -74,21 +74,27 @@
     public static final WindowInsets CONSUMED;
 
     static {
-        CONSUMED = new WindowInsets(null, null, null, false, false, null);
+        CONSUMED = new WindowInsets((Insets) null, null, null, false, false, null);
     }
 
     /** @hide */
     public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, Rect stableInsets,
             boolean isRound, boolean alwaysConsumeNavBar, DisplayCutout displayCutout) {
+        this(Insets.of(systemWindowInsets), Insets.of(windowDecorInsets), Insets.of(stableInsets),
+                isRound, alwaysConsumeNavBar, displayCutout);
+    }
+
+    private WindowInsets(Insets systemWindowInsets, Insets windowDecorInsets,
+            Insets stableInsets, boolean isRound, boolean alwaysConsumeNavBar,
+            DisplayCutout displayCutout) {
         mSystemWindowInsetsConsumed = systemWindowInsets == null;
-        mSystemWindowInsets = mSystemWindowInsetsConsumed
-                ? EMPTY_RECT : new Rect(systemWindowInsets);
+        mSystemWindowInsets = mSystemWindowInsetsConsumed ? Insets.NONE : systemWindowInsets;
 
         mWindowDecorInsetsConsumed = windowDecorInsets == null;
-        mWindowDecorInsets = mWindowDecorInsetsConsumed ? EMPTY_RECT : new Rect(windowDecorInsets);
+        mWindowDecorInsets = mWindowDecorInsetsConsumed ? Insets.NONE : windowDecorInsets;
 
         mStableInsetsConsumed = stableInsets == null;
-        mStableInsets = mStableInsetsConsumed ? EMPTY_RECT : new Rect(stableInsets);
+        mStableInsets = mStableInsetsConsumed ? Insets.NONE : stableInsets;
 
         mIsRound = isRound;
         mAlwaysConsumeNavBar = alwaysConsumeNavBar;
@@ -104,16 +110,21 @@
      * @param src Source to copy insets from
      */
     public WindowInsets(WindowInsets src) {
-        mSystemWindowInsets = src.mSystemWindowInsets;
-        mWindowDecorInsets = src.mWindowDecorInsets;
-        mStableInsets = src.mStableInsets;
-        mSystemWindowInsetsConsumed = src.mSystemWindowInsetsConsumed;
-        mWindowDecorInsetsConsumed = src.mWindowDecorInsetsConsumed;
-        mStableInsetsConsumed = src.mStableInsetsConsumed;
-        mIsRound = src.mIsRound;
-        mAlwaysConsumeNavBar = src.mAlwaysConsumeNavBar;
-        mDisplayCutout = src.mDisplayCutout;
-        mDisplayCutoutConsumed = src.mDisplayCutoutConsumed;
+        this(src.mSystemWindowInsetsConsumed ? null : src.mSystemWindowInsets,
+                src.mWindowDecorInsetsConsumed ? null : src.mWindowDecorInsets,
+                src.mStableInsetsConsumed ? null : src.mStableInsets,
+                src.mIsRound, src.mAlwaysConsumeNavBar,
+                displayCutoutCopyConstructorArgument(src));
+    }
+
+    private static DisplayCutout displayCutoutCopyConstructorArgument(WindowInsets w) {
+        if (w.mDisplayCutoutConsumed) {
+            return null;
+        } else if (w.mDisplayCutout == null) {
+            return DisplayCutout.NO_CUTOUT;
+        } else {
+            return w.mDisplayCutout;
+        }
     }
 
     /** @hide */
@@ -126,22 +137,35 @@
      * Used to provide a safe copy of the system window insets to pass through
      * to the existing fitSystemWindows method and other similar internals.
      * @hide
+     *
+     * @deprecated use {@link #getSystemWindowInsets()} instead.
      */
-    @UnsupportedAppUsage
-    public Rect getSystemWindowInsets() {
+    @Deprecated
+    @NonNull
+    public Rect getSystemWindowInsetsAsRect() {
         if (mTempRect == null) {
             mTempRect = new Rect();
         }
-        if (mSystemWindowInsets != null) {
-            mTempRect.set(mSystemWindowInsets);
-        } else {
-            // If there were no system window insets, this is just empty.
-            mTempRect.setEmpty();
-        }
+        mTempRect.set(mSystemWindowInsets.left, mSystemWindowInsets.top,
+                mSystemWindowInsets.right, mSystemWindowInsets.bottom);
         return mTempRect;
     }
 
     /**
+     * Returns the system window insets in pixels.
+     *
+     * <p>The system window inset represents the area of a full-screen window that is
+     * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
+     * </p>
+     *
+     * @return The system window insets
+     */
+    @NonNull
+    public Insets getSystemWindowInsets() {
+        return mSystemWindowInsets;
+    }
+
+    /**
      * Returns the left system window inset in pixels.
      *
      * <p>The system window inset represents the area of a full-screen window that is
@@ -304,11 +328,13 @@
      *
      * @return A modified copy of this WindowInsets
      */
+    @NonNull
     public WindowInsets consumeDisplayCutout() {
-        final WindowInsets result = new WindowInsets(this);
-        result.mDisplayCutout = null;
-        result.mDisplayCutoutConsumed = true;
-        return result;
+        return new WindowInsets(mSystemWindowInsetsConsumed ? null : mSystemWindowInsets,
+                mWindowDecorInsetsConsumed ? null : mWindowDecorInsets,
+                mStableInsetsConsumed ? null : mStableInsets,
+                mIsRound, mAlwaysConsumeNavBar,
+                null /* displayCutout */);
     }
 
 
@@ -349,101 +375,95 @@
      *
      * @return A modified copy of this WindowInsets
      */
+    @NonNull
     public WindowInsets consumeSystemWindowInsets() {
-        final WindowInsets result = new WindowInsets(this);
-        result.mSystemWindowInsets = EMPTY_RECT;
-        result.mSystemWindowInsetsConsumed = true;
-        return result;
+        return new WindowInsets(null /* systemWindowInsets */,
+                mWindowDecorInsetsConsumed ? null : mWindowDecorInsets,
+                mStableInsetsConsumed ? null : mStableInsets,
+                mIsRound, mAlwaysConsumeNavBar,
+                displayCutoutCopyConstructorArgument(this));
     }
 
-    /**
-     * Returns a copy of this WindowInsets with selected system window insets fully consumed.
-     *
-     * @param left true to consume the left system window inset
-     * @param top true to consume the top system window inset
-     * @param right true to consume the right system window inset
-     * @param bottom true to consume the bottom system window inset
-     * @return A modified copy of this WindowInsets
-     * @hide pending API
-     */
-    public WindowInsets consumeSystemWindowInsets(boolean left, boolean top,
-            boolean right, boolean bottom) {
-        if (left || top || right || bottom) {
-            final WindowInsets result = new WindowInsets(this);
-            result.mSystemWindowInsets = new Rect(
-                    left ? 0 : mSystemWindowInsets.left,
-                    top ? 0 : mSystemWindowInsets.top,
-                    right ? 0 : mSystemWindowInsets.right,
-                    bottom ? 0 : mSystemWindowInsets.bottom);
-            return result;
-        }
-        return this;
-    }
-
+    // TODO(b/119190588): replace @code with @link below
     /**
      * Returns a copy of this WindowInsets with selected system window insets replaced
      * with new values.
      *
+     * <p>Note: If the system window insets are already consumed, this method will return them
+     * unchanged on {@link android.os.Build.VERSION_CODES#Q Q} and later. Prior to
+     * {@link android.os.Build.VERSION_CODES#Q Q}, the new values were applied regardless of
+     * whether they were consumed, and this method returns invalid non-zero consumed insets.
+     *
      * @param left New left inset in pixels
      * @param top New top inset in pixels
      * @param right New right inset in pixels
      * @param bottom New bottom inset in pixels
      * @return A modified copy of this WindowInsets
+     * @deprecated use {@code Builder#Builder(WindowInsets)} with
+     *             {@link Builder#setSystemWindowInsets(Insets)} instead.
      */
-    public WindowInsets replaceSystemWindowInsets(int left, int top,
-            int right, int bottom) {
-        final WindowInsets result = new WindowInsets(this);
-        result.mSystemWindowInsets = new Rect(left, top, right, bottom);
-        return result;
+    @Deprecated
+    @NonNull
+    public WindowInsets replaceSystemWindowInsets(int left, int top, int right, int bottom) {
+        // Compat edge case: what should this do if the insets have already been consumed?
+        // On platforms prior to Q, the behavior was to override the insets with non-zero values,
+        // but leave them consumed, which is invalid (consumed insets must be zero).
+        // The behavior is now keeping them consumed and discarding the new insets.
+        if (mSystemWindowInsetsConsumed) {
+            return this;
+        }
+        return new Builder(this).setSystemWindowInsets(Insets.of(left, top, right, bottom)).build();
     }
 
+    // TODO(b/119190588): replace @code with @link below
     /**
      * Returns a copy of this WindowInsets with selected system window insets replaced
      * with new values.
      *
+     * <p>Note: If the system window insets are already consumed, this method will return them
+     * unchanged on {@link android.os.Build.VERSION_CODES#Q Q} and later. Prior to
+     * {@link android.os.Build.VERSION_CODES#Q Q}, the new values were applied regardless of
+     * whether they were consumed, and this method returns invalid non-zero consumed insets.
+     *
      * @param systemWindowInsets New system window insets. Each field is the inset in pixels
      *                           for that edge
      * @return A modified copy of this WindowInsets
+     * @deprecated use {@code Builder#Builder(WindowInsets)} with
+     *             {@link Builder#setSystemWindowInsets(Insets)} instead.
      */
+    @Deprecated
+    @NonNull
     public WindowInsets replaceSystemWindowInsets(Rect systemWindowInsets) {
-        final WindowInsets result = new WindowInsets(this);
-        result.mSystemWindowInsets = new Rect(systemWindowInsets);
-        return result;
+        return replaceSystemWindowInsets(systemWindowInsets.left, systemWindowInsets.top,
+                systemWindowInsets.right, systemWindowInsets.bottom);
     }
 
     /**
      * @hide
      */
+    @NonNull
     public WindowInsets consumeWindowDecorInsets() {
-        final WindowInsets result = new WindowInsets(this);
-        result.mWindowDecorInsets.set(0, 0, 0, 0);
-        result.mWindowDecorInsetsConsumed = true;
-        return result;
+        return new WindowInsets(mSystemWindowInsetsConsumed ? null : mSystemWindowInsets,
+                null /* windowDecorInsets */,
+                mStableInsetsConsumed ? null : mStableInsets,
+                mIsRound, mAlwaysConsumeNavBar,
+                displayCutoutCopyConstructorArgument(this));
     }
 
     /**
-     * @hide
+     * Returns the stable insets in pixels.
+     *
+     * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
+     * partially or fully obscured by the system UI elements.  This value does not change
+     * based on the visibility state of those elements; for example, if the status bar is
+     * normally shown, but temporarily hidden, the stable inset will still provide the inset
+     * associated with the status bar being shown.</p>
+     *
+     * @return The stable insets
      */
-    public WindowInsets consumeWindowDecorInsets(boolean left, boolean top,
-            boolean right, boolean bottom) {
-        if (left || top || right || bottom) {
-            final WindowInsets result = new WindowInsets(this);
-            result.mWindowDecorInsets = new Rect(left ? 0 : mWindowDecorInsets.left,
-                    top ? 0 : mWindowDecorInsets.top,
-                    right ? 0 : mWindowDecorInsets.right,
-                    bottom ? 0 : mWindowDecorInsets.bottom);
-            return result;
-        }
-        return this;
-    }
-
-    /**
-     * @hide
-     */
-    public WindowInsets replaceWindowDecorInsets(int left, int top, int right, int bottom) {
-        final WindowInsets result = new WindowInsets(this);
-        result.mWindowDecorInsets = new Rect(left, top, right, bottom);
-        return result;
+    @NonNull
+    public Insets getStableInsets() {
+        return mStableInsets;
     }
 
     /**
@@ -527,11 +547,13 @@
      *
      * @return A modified copy of this WindowInsets
      */
+    @NonNull
     public WindowInsets consumeStableInsets() {
-        final WindowInsets result = new WindowInsets(this);
-        result.mStableInsets = EMPTY_RECT;
-        result.mStableInsetsConsumed = true;
-        return result;
+        return new WindowInsets(mSystemWindowInsetsConsumed ? null : mSystemWindowInsets,
+                mWindowDecorInsetsConsumed ? null : mWindowDecorInsets,
+                null /* stableInsets */,
+                mIsRound, mAlwaysConsumeNavBar,
+                displayCutoutCopyConstructorArgument(this));
     }
 
     /**
@@ -555,8 +577,11 @@
      * Returns a copy of this instance inset in the given directions.
      *
      * @see #inset(int, int, int, int)
+     * @deprecated use {@link #inset(Insets)}
      * @hide
      */
+    @Deprecated
+    @NonNull
     public WindowInsets inset(Rect r) {
         return inset(r.left, r.top, r.right, r.bottom);
     }
@@ -564,6 +589,17 @@
     /**
      * Returns a copy of this instance inset in the given directions.
      *
+     * @see #inset(int, int, int, int)
+     * @hide
+     */
+    @NonNull
+    public WindowInsets inset(Insets insets) {
+        return inset(insets.left, insets.top, insets.right, insets.bottom);
+    }
+
+    /**
+     * Returns a copy of this instance inset in the given directions.
+     *
      * This is intended for dispatching insets to areas of the window that are smaller than the
      * current area.
      *
@@ -579,35 +615,27 @@
      * @param bottom the amount of insets to remove from the bottom. Must be non-negative.
      *
      * @return the inset insets
-     *
-     * @hide pending API
      */
-    @UnsupportedAppUsage
+    @NonNull
     public WindowInsets inset(int left, int top, int right, int bottom) {
         Preconditions.checkArgumentNonnegative(left);
         Preconditions.checkArgumentNonnegative(top);
         Preconditions.checkArgumentNonnegative(right);
         Preconditions.checkArgumentNonnegative(bottom);
 
-        WindowInsets result = new WindowInsets(this);
-        if (!result.mSystemWindowInsetsConsumed) {
-            result.mSystemWindowInsets =
-                    insetInsets(result.mSystemWindowInsets, left, top, right, bottom);
-        }
-        if (!result.mWindowDecorInsetsConsumed) {
-            result.mWindowDecorInsets =
-                    insetInsets(result.mWindowDecorInsets, left, top, right, bottom);
-        }
-        if (!result.mStableInsetsConsumed) {
-            result.mStableInsets = insetInsets(result.mStableInsets, left, top, right, bottom);
-        }
-        if (mDisplayCutout != null) {
-            result.mDisplayCutout = result.mDisplayCutout.inset(left, top, right, bottom);
-            if (result.mDisplayCutout.isEmpty()) {
-                result.mDisplayCutout = null;
-            }
-        }
-        return result;
+        return new WindowInsets(
+                mSystemWindowInsetsConsumed ? null :
+                        insetInsets(mSystemWindowInsets, left, top, right, bottom),
+                mWindowDecorInsetsConsumed ? null :
+                        insetInsets(mWindowDecorInsets, left, top, right, bottom),
+                mStableInsetsConsumed ? null :
+                        insetInsets(mStableInsets, left, top, right, bottom),
+                mIsRound, mAlwaysConsumeNavBar,
+                mDisplayCutoutConsumed
+                        ? null :
+                        mDisplayCutout == null
+                                ? DisplayCutout.NO_CUTOUT
+                                : mDisplayCutout.inset(left, top, right, bottom));
     }
 
     @Override
@@ -634,7 +662,7 @@
                 mWindowDecorInsetsConsumed, mStableInsetsConsumed, mDisplayCutoutConsumed);
     }
 
-    private static Rect insetInsets(Rect insets, int left, int top, int right, int bottom) {
+    private static Insets insetInsets(Insets insets, int left, int top, int right, int bottom) {
         int newLeft = Math.max(0, insets.left - left);
         int newTop = Math.max(0, insets.top - top);
         int newRight = Math.max(0, insets.right - right);
@@ -642,7 +670,7 @@
         if (newLeft == left && newTop == top && newRight == right && newBottom == bottom) {
             return insets;
         }
-        return new Rect(newLeft, newTop, newRight, newBottom);
+        return Insets.of(newLeft, newTop, newRight, newBottom);
     }
 
     /**
@@ -651,4 +679,122 @@
     boolean isSystemWindowInsetsConsumed() {
         return mSystemWindowInsetsConsumed;
     }
+
+    /**
+     * Builder for WindowInsets.
+     */
+    public static class Builder {
+
+        private Insets mSystemWindowInsets;
+        private Insets mStableInsets;
+        private DisplayCutout mDisplayCutout;
+
+        private Insets mWindowDecorInsets;
+        private boolean mIsRound;
+        private boolean mAlwaysConsumeNavBar;
+
+        /**
+         * Creates a builder where all insets are initially consumed.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Creates a builder where all insets are initialized from {@link WindowInsets}.
+         *
+         * @param insets the instance to initialize from.
+         */
+        public Builder(WindowInsets insets) {
+            mSystemWindowInsets = insets.mSystemWindowInsetsConsumed ? null
+                    : insets.mSystemWindowInsets;
+            mStableInsets = insets.mStableInsetsConsumed ? null : insets.mStableInsets;
+            mDisplayCutout = displayCutoutCopyConstructorArgument(insets);
+            mWindowDecorInsets =  insets.mWindowDecorInsetsConsumed ? null
+                    : insets.mWindowDecorInsets;
+            mIsRound = insets.mIsRound;
+            mAlwaysConsumeNavBar = insets.mAlwaysConsumeNavBar;
+        }
+
+        /**
+         * Sets system window insets in pixels.
+         *
+         * <p>The system window inset represents the area of a full-screen window that is
+         * partially or fully obscured by the status bar, navigation bar, IME or other system
+         * windows.</p>
+         *
+         * @see #getSystemWindowInsets()
+         * @return itself
+         */
+        @NonNull
+        public Builder setSystemWindowInsets(@NonNull Insets systemWindowInsets) {
+            Preconditions.checkNotNull(systemWindowInsets);
+            mSystemWindowInsets = systemWindowInsets;
+            return this;
+        }
+
+        /**
+         * Sets the stable insets in pixels.
+         *
+         * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
+         * partially or fully obscured by the system UI elements.  This value does not change
+         * based on the visibility state of those elements; for example, if the status bar is
+         * normally shown, but temporarily hidden, the stable inset will still provide the inset
+         * associated with the status bar being shown.</p>
+         *
+         * @see #getStableInsets()
+         * @return itself
+         */
+        @NonNull
+        public Builder setStableInsets(@NonNull Insets stableInsets) {
+            Preconditions.checkNotNull(stableInsets);
+            mStableInsets = stableInsets;
+            return this;
+        }
+
+        /**
+         * Sets the display cutout.
+         *
+         * @see #getDisplayCutout()
+         * @param displayCutout the display cutout or null if there is none
+         * @return itself
+         */
+        @NonNull
+        public Builder setDisplayCutout(@Nullable DisplayCutout displayCutout) {
+            mDisplayCutout = displayCutout != null ? displayCutout : DisplayCutout.NO_CUTOUT;
+            return this;
+        }
+
+        /** @hide */
+        @NonNull
+        public Builder setWindowDecorInsets(@NonNull Insets windowDecorInsets) {
+            Preconditions.checkNotNull(windowDecorInsets);
+            mWindowDecorInsets = windowDecorInsets;
+            return this;
+        }
+
+        /** @hide */
+        @NonNull
+        public Builder setRound(boolean round) {
+            mIsRound = round;
+            return this;
+        }
+
+        /** @hide */
+        @NonNull
+        public Builder setAlwaysConsumeNavBar(boolean alwaysConsumeNavBar) {
+            mAlwaysConsumeNavBar = alwaysConsumeNavBar;
+            return this;
+        }
+
+        /**
+         * Builds a {@link WindowInsets} instance.
+         *
+         * @return the {@link WindowInsets} instance.
+         */
+        @NonNull
+        public WindowInsets build() {
+            return new WindowInsets(mSystemWindowInsets, mWindowDecorInsets, mStableInsets,
+                    mIsRound, mAlwaysConsumeNavBar, mDisplayCutout);
+        }
+    }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index e88682e..88b9c80 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -21,10 +21,14 @@
 import android.Manifest;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -51,6 +55,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IntPair;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -114,6 +120,39 @@
     public static final String ACTION_CHOOSE_ACCESSIBILITY_BUTTON =
             "com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON";
 
+    /**
+     * Annotations for content flag of UI.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "FLAG_CONTENT_" }, value = {
+            FLAG_CONTENT_ICONS,
+            FLAG_CONTENT_TEXT,
+            FLAG_CONTENT_CONTROLS
+    })
+    public @interface ContentFlag {}
+
+    /**
+     * Use this flag to indicate the content of a UI that times out contains icons.
+     *
+     * @see #getRecommendedTimeoutMillis(int, int)
+     */
+    public static final int FLAG_CONTENT_ICONS = 1;
+
+    /**
+     * Use this flag to indicate the content of a UI that times out contains text.
+     *
+     * @see #getRecommendedTimeoutMillis(int, int)
+     */
+    public static final int FLAG_CONTENT_TEXT = 2;
+
+    /**
+     * Use this flag to indicate the content of a UI that times out contains interactive controls.
+     *
+     * @see #getRecommendedTimeoutMillis(int, int)
+     */
+    public static final int FLAG_CONTENT_CONTROLS = 4;
+
     @UnsupportedAppUsage
     static final Object sInstanceSync = new Object();
 
@@ -139,7 +178,8 @@
 
     int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
 
-    int mMinimumUiTimeout;
+    int mInteractiveUiTimeout;
+    int mNonInteractiveUiTimeout;
 
     boolean mIsTouchExplorationEnabled;
 
@@ -204,6 +244,7 @@
      *
      * @hide
      */
+    @TestApi
     public interface AccessibilityServicesStateChangeListener {
 
         /**
@@ -300,7 +341,9 @@
         }
 
         @Override
-        public void notifyServicesStateChanged() {
+        public void notifyServicesStateChanged(long updatedUiTimeout) {
+            updateUiTimeout(updatedUiTimeout);
+
             final ArrayMap<AccessibilityServicesStateChangeListener, Handler> listeners;
             synchronized (mLock) {
                 if (mServicesStateChangeListeners.isEmpty()) {
@@ -322,11 +365,6 @@
         public void setRelevantEventTypes(int eventTypes) {
             mRelevantEventTypes = eventTypes;
         }
-
-        @Override
-        public void setMinimumUiTimeout(int uiTimeout) {
-            mMinimumUiTimeout = uiTimeout;
-        }
     };
 
     /**
@@ -778,6 +816,7 @@
      *                for a callback on the process's main handler.
      * @hide
      */
+    @TestApi
     public void addAccessibilityServicesStateChangeListener(
             @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
         synchronized (mLock) {
@@ -793,6 +832,7 @@
      *
      * @hide
      */
+    @TestApi
     public void removeAccessibilityServicesStateChangeListener(
             @NonNull AccessibilityServicesStateChangeListener listener) {
         synchronized (mLock) {
@@ -834,16 +874,35 @@
     }
 
     /**
-     * Get the minimum timeout for changes to the UI needed by this user. Controls should remain
+     * Get the recommended timeout for changes to the UI needed by this user. Controls should remain
      * on the screen for at least this long to give users time to react. Some users may need
      * extra time to review the controls, or to reach them, or to activate assistive technology
      * to activate the controls automatically.
+     * <p>
+     * Use the combination of content flags to indicate contents of UI. For example, use
+     * {@code FLAG_CONTENT_ICONS | FLAG_CONTENT_TEXT} for message notification which contains
+     * icons and text, or use {@code FLAG_CONTENT_TEXT | FLAG_CONTENT_CONTROLS} for button dialog
+     * which contains text and button controls.
+     * <p/>
      *
-     * @return The minimum ui timeout for the current user in milliseconds.
-     * {@link Integer#MAX_VALUE} if timeout is infinite.
+     * @param originalTimeout The timeout appropriate for users with no accessibility needs.
+     * @param uiContentFlags The combination of flags {@link #FLAG_CONTENT_ICONS},
+     *                       {@link #FLAG_CONTENT_TEXT} or {@link #FLAG_CONTENT_CONTROLS} to
+     *                       indicate the contents of UI.
+     * @return The recommended UI timeout for the current user in milliseconds.
      */
-    public int getMinimumUiTimeoutMillis() {
-        return mMinimumUiTimeout;
+    public int getRecommendedTimeoutMillis(int originalTimeout, @ContentFlag int uiContentFlags) {
+        boolean hasControls = (uiContentFlags & FLAG_CONTENT_CONTROLS) != 0;
+        boolean hasIconsOrText = (uiContentFlags & FLAG_CONTENT_ICONS) != 0
+                || (uiContentFlags & FLAG_CONTENT_TEXT) != 0;
+        int recommendedTimeout = originalTimeout;
+        if (hasControls) {
+            recommendedTimeout = Math.max(recommendedTimeout, mInteractiveUiTimeout);
+        }
+        if (hasIconsOrText) {
+            recommendedTimeout = Math.max(recommendedTimeout, mNonInteractiveUiTimeout);
+        }
+        return recommendedTimeout;
     }
 
     /**
@@ -1056,6 +1115,9 @@
      *
      * @hide
      */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
     public void performAccessibilityShortcut() {
         final IAccessibilityManager service;
         synchronized (mLock) {
@@ -1139,6 +1201,30 @@
         }
     }
 
+    /**
+     * Get the component name of the service currently assigned to the accessibility shortcut.
+     *
+     * @return The flattened component name
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+    @Nullable
+    public String getAccessibilityShortcutService() {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+        }
+        if (service != null) {
+            try {
+                return service.getAccessibilityShortcutService();
+            } catch (RemoteException re) {
+                re.rethrowFromSystemServer();
+            }
+        }
+        return null;
+    }
+
     private IAccessibilityManager getServiceLocked() {
         if (mService == null) {
             tryConnectToServiceLocked(null);
@@ -1159,7 +1245,7 @@
             final long userStateAndRelevantEvents = service.addClient(mClient, mUserId);
             setStateLocked(IntPair.first(userStateAndRelevantEvents));
             mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents);
-            mMinimumUiTimeout = service.getMinimumUiTimeout();
+            updateUiTimeout(service.getRecommendedTimeoutMillis());
             mService = service;
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
@@ -1233,6 +1319,17 @@
     }
 
     /**
+     * Update interactive and non-interactive UI timeout.
+     *
+     * @param uiTimeout A pair of {@code int}s. First integer for interactive one, and second
+     *                  integer for non-interactive one.
+     */
+    private void updateUiTimeout(long uiTimeout) {
+        mInteractiveUiTimeout = IntPair.first(uiTimeout);
+        mNonInteractiveUiTimeout = IntPair.second(uiTimeout);
+    }
+
+    /**
      * Determines if the accessibility button within the system navigation area is supported.
      *
      * @return {@code true} if the accessibility button is supported on this device,
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 3e2ef18..2767a82 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -67,11 +67,14 @@
 
     void notifyAccessibilityButtonVisibilityChanged(boolean available);
 
-    // Requires WRITE_SECURE_SETTINGS
+    // Requires Manifest.permission.MANAGE_ACCESSIBILITY
     void performAccessibilityShortcut();
 
+    // Requires Manifest.permission.MANAGE_ACCESSIBILITY
+    String getAccessibilityShortcutService();
+
     // System process only
     boolean sendFingerprintGesture(int gestureKeyCode);
 
-    int getMinimumUiTimeout();
+    long getRecommendedTimeoutMillis();
 }
diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
index d2ddca3..94b9ad1 100644
--- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
@@ -26,9 +26,7 @@
 
     void setState(int stateFlags);
 
-    void notifyServicesStateChanged();
+    void notifyServicesStateChanged(long updatedUiTimeout);
 
     void setRelevantEventTypes(int eventTypes);
-
-    void setMinimumUiTimeout(int uiTimeout);
 }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index d24b822..1340955 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -62,6 +62,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
+import com.android.internal.inputmethod.StartInputFlags;
 import com.android.internal.inputmethod.StartInputReason;
 import com.android.internal.inputmethod.UnbindReason;
 import com.android.internal.os.SomeArgs;
@@ -294,30 +295,6 @@
     private static final SparseArray<InputMethodManager> sInstanceMap = new SparseArray<>();
 
     /**
-     * @hide Flag for IInputMethodManager.windowGainedFocus: a view in
-     * the window has input focus.
-     */
-    public static final int CONTROL_WINDOW_VIEW_HAS_FOCUS = 1<<0;
-
-    /**
-     * @hide Flag for IInputMethodManager.windowGainedFocus: the focus
-     * is a text editor.
-     */
-    public static final int CONTROL_WINDOW_IS_TEXT_EDITOR = 1<<1;
-
-    /**
-     * @hide Flag for IInputMethodManager.windowGainedFocus: this is the first
-     * time the window has gotten focus.
-     */
-    public static final int CONTROL_WINDOW_FIRST = 1<<2;
-
-    /**
-     * @hide Flag for IInputMethodManager.startInput: this is the first
-     * time the window has gotten focus.
-     */
-    public static final int CONTROL_START_INITIAL = 1<<8;
-
-    /**
      * Timeout in milliseconds for delivering a key to an IME.
      */
     static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500;
@@ -470,26 +447,53 @@
     }
 
     /**
-     * Checks the consistency between {@link InputMethodManager} state and {@link View} state.
+     * Returns fallback {@link InputMethodManager} if the called one is not likely to be compatible
+     * with the given {@code view}.
      *
-     * @param view {@link View} to be checked
-     * @return {@code true} if {@code view} is not {@code null} and there is a {@link Context}
-     *         mismatch between {@link InputMethodManager} and {@code view}
+     * @param view {@link View} to be checked.
+     * @return {@code null} when it is unnecessary (or impossible) to use fallback
+     *         {@link InputMethodManager} to which IME API calls need to be re-dispatched.
+     *          Non-{@code null} {@link InputMethodManager} if this method believes it'd be safer to
+     *          re-dispatch IME APIs calls on it.
      */
-    private boolean shouldDispatchToViewContext(@Nullable View view) {
+    @Nullable
+    private InputMethodManager getFallbackInputMethodManagerIfNecessary(@Nullable View view) {
         if (view == null) {
-            return false;
+            return null;
         }
-        final int viewDisplayId = view.getContext().getDisplayId();
-        if (viewDisplayId != mDisplayId) {
-            Log.w(TAG, "b/117267690: Context mismatch found. view=" + view + " belongs to"
-                    + " displayId=" + viewDisplayId
-                    + " but InputMethodManager belongs to displayId=" + mDisplayId
-                    + ". Use the right InputMethodManager instance to avoid performance overhead.",
-                    new Throwable());
-            return true;
+        // As evidenced in Bug 118341760, view.getViewRootImpl().getDisplayId() is supposed to be
+        // more reliable to determine with which display the given view is interacting than
+        // view.getContext().getDisplayId() / view.getContext().getSystemService(), which can be
+        // easily messed up by app developers (or library authors) by creating inconsistent
+        // ContextWrapper objects that re-dispatch those methods to other Context such as
+        // ApplicationContext.
+        final ViewRootImpl viewRootImpl = view.getViewRootImpl();
+        if (viewRootImpl == null) {
+            return null;
         }
-        return false;
+        final int viewRootDisplayId = viewRootImpl.getDisplayId();
+        if (viewRootDisplayId == mDisplayId) {
+            // Expected case.  Good to go.
+            return null;
+        }
+        final InputMethodManager fallbackImm =
+                viewRootImpl.mDisplayContext.getSystemService(InputMethodManager.class);
+        if (fallbackImm == null) {
+            Log.e(TAG, "b/117267690: Failed to get non-null fallback IMM. view=" + view);
+            return null;
+        }
+        if (fallbackImm.mDisplayId != viewRootDisplayId) {
+            Log.e(TAG, "b/117267690: Failed to get fallback IMM with expected displayId="
+                    + viewRootDisplayId + " actual IMM#displayId=" + fallbackImm.mDisplayId
+                    + " view=" + view);
+            return null;
+        }
+        Log.w(TAG, "b/117267690: Display ID mismatch found."
+                + " ViewRootImpl displayId=" + viewRootDisplayId
+                + " InputMethodManager displayId=" + mDisplayId
+                + ". Use the right InputMethodManager instance to avoid performance overhead.",
+                new Throwable());
+        return fallbackImm;
     }
 
     private static boolean canStartInput(View servedView) {
@@ -1000,8 +1004,9 @@
      */
     public boolean isActive(View view) {
         // Re-dispatch if there is a context mismatch.
-        if (shouldDispatchToViewContext(view)) {
-            return view.getContext().getSystemService(InputMethodManager.class).isActive(view);
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+        if (fallbackImm != null) {
+            return fallbackImm.isActive(view);
         }
 
         checkFocus();
@@ -1088,9 +1093,9 @@
 
     public void displayCompletions(View view, CompletionInfo[] completions) {
         // Re-dispatch if there is a context mismatch.
-        if (shouldDispatchToViewContext(view)) {
-            view.getContext().getSystemService(InputMethodManager.class)
-                    .displayCompletions(view, completions);
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+        if (fallbackImm != null) {
+            fallbackImm.displayCompletions(view, completions);
             return;
         }
 
@@ -1113,9 +1118,9 @@
 
     public void updateExtractedText(View view, int token, ExtractedText text) {
         // Re-dispatch if there is a context mismatch.
-        if (shouldDispatchToViewContext(view)) {
-            view.getContext().getSystemService(InputMethodManager.class)
-                    .updateExtractedText(view, token, text);
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+        if (fallbackImm != null) {
+            fallbackImm.updateExtractedText(view, token, text);
             return;
         }
 
@@ -1161,9 +1166,9 @@
      */
     public boolean showSoftInput(View view, int flags) {
         // Re-dispatch if there is a context mismatch.
-        if (shouldDispatchToViewContext(view)) {
-            return view.getContext().getSystemService(InputMethodManager.class)
-                    .showSoftInput(view, flags);
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+        if (fallbackImm != null) {
+            return fallbackImm.showSoftInput(view, flags);
         }
 
         return showSoftInput(view, flags, null);
@@ -1229,9 +1234,9 @@
      */
     public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
         // Re-dispatch if there is a context mismatch.
-        if (shouldDispatchToViewContext(view)) {
-            return view.getContext().getSystemService(InputMethodManager.class)
-                    .showSoftInput(view, flags, resultReceiver);
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+        if (fallbackImm != null) {
+            return fallbackImm.showSoftInput(view, flags, resultReceiver);
         }
 
         checkFocus();
@@ -1398,8 +1403,9 @@
      */
     public void restartInput(View view) {
         // Re-dispatch if there is a context mismatch.
-        if (shouldDispatchToViewContext(view)) {
-            view.getContext().getSystemService(InputMethodManager.class).restartInput(view);
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+        if (fallbackImm != null) {
+            fallbackImm.restartInput(view);
             return;
         }
 
@@ -1417,8 +1423,8 @@
     }
 
     boolean startInputInner(@StartInputReason int startInputReason,
-            @Nullable IBinder windowGainingFocus, int controlFlags, int softInputMode,
-            int windowFlags) {
+            @Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags,
+            @SoftInputModeFlags int softInputMode, int windowFlags) {
         final View view;
         synchronized (mH) {
             view = mServedView;
@@ -1440,9 +1446,9 @@
                 Log.e(TAG, "ABORT input: ServedView must be attached to a Window");
                 return false;
             }
-            controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
+            startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS;
             if (view.onCheckIsTextEditor()) {
-                controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
+                startInputFlags |= StartInputFlags.IS_TEXT_EDITOR;
             }
             softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode;
             windowFlags = view.getViewRootImpl().mWindowAttributes.flags;
@@ -1497,7 +1503,7 @@
             // If we already have a text box, then this view is already
             // connected so we want to restart it.
             if (mCurrentTextBoxAttribute == null) {
-                controlFlags |= CONTROL_START_INITIAL;
+                startInputFlags |= StartInputFlags.INITIAL_CONNECTION;
             }
 
             // Hook 'em up and let 'er rip.
@@ -1535,11 +1541,11 @@
 
             try {
                 if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
-                        + ic + " tba=" + tba + " controlFlags=#"
-                        + Integer.toHexString(controlFlags));
+                        + ic + " tba=" + tba + " startInputFlags="
+                        + InputMethodDebug.startInputFlagsToString(startInputFlags));
                 final InputBindResult res = mService.startInputOrWindowGainedFocus(
-                        startInputReason, mClient, windowGainingFocus, controlFlags, softInputMode,
-                        windowFlags, tba, servedContext, missingMethodFlags,
+                        startInputReason, mClient, windowGainingFocus, startInputFlags,
+                        softInputMode, windowFlags, tba, servedContext, missingMethodFlags,
                         view.getContext().getApplicationInfo().targetSdkVersion);
                 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
                 if (res == null) {
@@ -1547,7 +1553,8 @@
                             + " null. startInputReason="
                             + InputMethodDebug.startInputReasonToString(startInputReason)
                             + " editorInfo=" + tba
-                            + " controlFlags=#" + Integer.toHexString(controlFlags));
+                            + " startInputFlags="
+                            + InputMethodDebug.startInputFlagsToString(startInputFlags));
                     return false;
                 }
                 if (res.id != null) {
@@ -1754,15 +1761,15 @@
             focusInLocked(focusedView != null ? focusedView : rootView);
         }
 
-        int controlFlags = 0;
+        int startInputFlags = 0;
         if (focusedView != null) {
-            controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
+            startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS;
             if (focusedView.onCheckIsTextEditor()) {
-                controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
+                startInputFlags |= StartInputFlags.IS_TEXT_EDITOR;
             }
         }
         if (first) {
-            controlFlags |= CONTROL_WINDOW_FIRST;
+            startInputFlags |= StartInputFlags.FIRST_WINDOW_FOCUS_GAIN;
         }
 
         if (checkFocusNoStartInput(forceNewFocus)) {
@@ -1771,7 +1778,7 @@
             // about the window gaining focus, to help make the transition
             // smooth.
             if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(),
-                    controlFlags, softInputMode, windowFlags)) {
+                    startInputFlags, softInputMode, windowFlags)) {
                 return;
             }
         }
@@ -1783,8 +1790,8 @@
                 if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
                 mService.startInputOrWindowGainedFocus(
                         StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
-                        rootView.getWindowToken(), controlFlags, softInputMode, windowFlags, null,
-                        null, 0 /* missingMethodFlags */,
+                        rootView.getWindowToken(), startInputFlags, softInputMode, windowFlags,
+                        null, null, 0 /* missingMethodFlags */,
                         rootView.getContext().getApplicationInfo().targetSdkVersion);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -1827,9 +1834,9 @@
     public void updateSelection(View view, int selStart, int selEnd,
             int candidatesStart, int candidatesEnd) {
         // Re-dispatch if there is a context mismatch.
-        if (shouldDispatchToViewContext(view)) {
-            view.getContext().getSystemService(InputMethodManager.class)
-                    .updateSelection(view, selStart, selEnd, candidatesStart, candidatesEnd);
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+        if (fallbackImm != null) {
+            fallbackImm.updateSelection(view, selStart, selEnd, candidatesStart, candidatesEnd);
             return;
         }
 
@@ -1871,8 +1878,9 @@
      */
     public void viewClicked(View view) {
         // Re-dispatch if there is a context mismatch.
-        if (shouldDispatchToViewContext(view)) {
-            view.getContext().getSystemService(InputMethodManager.class).viewClicked(view);
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+        if (fallbackImm != null) {
+            fallbackImm.viewClicked(view);
             return;
         }
 
@@ -1941,9 +1949,9 @@
     @Deprecated
     public void updateCursor(View view, int left, int top, int right, int bottom) {
         // Re-dispatch if there is a context mismatch.
-        if (shouldDispatchToViewContext(view)) {
-            view.getContext().getSystemService(InputMethodManager.class)
-                    .updateCursor(view, left, top, right, bottom);
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+        if (fallbackImm != null) {
+            fallbackImm.updateCursor(view, left, top, right, bottom);
             return;
         }
 
@@ -1979,9 +1987,9 @@
             return;
         }
         // Re-dispatch if there is a context mismatch.
-        if (shouldDispatchToViewContext(view)) {
-            view.getContext().getSystemService(InputMethodManager.class)
-                    .updateCursorAnchorInfo(view, cursorAnchorInfo);
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+        if (fallbackImm != null) {
+            fallbackImm.updateCursorAnchorInfo(view, cursorAnchorInfo);
             return;
         }
 
@@ -2031,9 +2039,9 @@
      */
     public void sendAppPrivateCommand(View view, String action, Bundle data) {
         // Re-dispatch if there is a context mismatch.
-        if (shouldDispatchToViewContext(view)) {
-            view.getContext().getSystemService(InputMethodManager.class)
-                    .sendAppPrivateCommand(view, action, data);
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+        if (fallbackImm != null) {
+            fallbackImm.sendAppPrivateCommand(view, action, data);
             return;
         }
 
@@ -2209,9 +2217,9 @@
     public void dispatchKeyEventFromInputMethod(@Nullable View targetView,
             @NonNull KeyEvent event) {
         // Re-dispatch if there is a context mismatch.
-        if (shouldDispatchToViewContext(targetView)) {
-            targetView.getContext().getSystemService(InputMethodManager.class)
-                    .dispatchKeyEventFromInputMethod(targetView, event);
+        final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(targetView);
+        if (fallbackImm != null) {
+            fallbackImm.dispatchKeyEventFromInputMethod(targetView, event);
             return;
         }
 
diff --git a/core/java/android/view/inspector/ChildTraverser.java b/core/java/android/view/inspector/ChildTraverser.java
new file mode 100644
index 0000000..b775de5
--- /dev/null
+++ b/core/java/android/view/inspector/ChildTraverser.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 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 android.view.inspector;
+
+import android.annotation.NonNull;
+
+/**
+ * Interface for visiting all the child nodes of an inspectable object.
+ *
+ * Inspectable objects may return a collection of children as an array, an {@link Iterable} or an
+ * {@link java.util.Iterator}. This provides a unified API for traversing across all the children
+ * of an inspectable node.
+ *
+ * This interface is consumed by {@link InspectionHelper#traverseChildren(Object, ChildTraverser)}
+ * and may be implemented as a lambda.
+ *
+ * @see InspectionHelper#traverseChildren(Object, ChildTraverser)
+ * @hide
+ */
+@FunctionalInterface
+public interface ChildTraverser {
+    /**
+     * Visit one child object of a parent inspectable object.
+     *
+     * The iteration interface will filter null values out before passing them to this method, but
+     * some child objects may not be inspectable. It is up to the implementor to determine their
+     * inspectablity and what to do with them.
+     *
+     * @param child A child object, guaranteed not to be null.
+     */
+    void traverseChild(@NonNull Object child);
+}
diff --git a/core/java/android/view/inspector/InspectableChildren.java b/core/java/android/view/inspector/InspectableChildren.java
new file mode 100644
index 0000000..de8fa29
--- /dev/null
+++ b/core/java/android/view/inspector/InspectableChildren.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 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 android.view.inspector;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a getter for an inspectable node's inspectable children.
+ *
+ * This annotation can be applied to any getter that returns a collection of objects, either an
+ * array, an {@link Iterable} or a {@link java.util.Iterator}. The getter may return null, which
+ * will be treated as an empty collection. Additionally, the inspector will discard any null
+ * entries in the collection.
+ *
+ * By default, this annotation is inherited. At runtime, the inspector introspects on the class
+ * hierachy and uses the annotated getter from the bottommost class, if different from any
+ * annoated getters of the parent class. If a class inherits from a parent class with an annotated
+ * getter, but does not include this annotation, the child class will be traversed using the
+ * getter annotated on the parent. This holds true even if the child class overrides the getter.
+ *
+ * @see InspectionHelper#traverseChildren(Object, ChildTraverser)
+ * @see InspectionHelper#hasChildTraversal()
+ * @hide
+ */
+@Target({METHOD})
+@Retention(SOURCE)
+public @interface InspectableChildren {
+}
diff --git a/core/java/android/view/inspector/InspectableNodeName.java b/core/java/android/view/inspector/InspectableNodeName.java
new file mode 100644
index 0000000..716409c
--- /dev/null
+++ b/core/java/android/view/inspector/InspectableNodeName.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018 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 android.view.inspector;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Marks the node name to display to a developer in the inspection tree.
+ *
+ * This annotation is optional to marking a class as inspectable. If it is omitted, the node name
+ * will be inferred using the semantics of {@link Class#getSimpleName()}. The fully qualified class
+ * name is always available in the tree, this is for display purposes only. If a class is inflated
+ * from XML and the tag it inflates from does not match its simple name, this annotation should be
+ * used to inform the inspector to display the XML tag name in the inspection tree view.
+ *
+ * This annotation does not inherit. If a class extends an annotated parent class, but does not
+ * annotate itself, its node name will be inferred from its Java name.
+ *
+ * @see InspectionHelper#getNodeName()
+ * @hide
+ */
+@Target({TYPE})
+@Retention(SOURCE)
+public @interface InspectableNodeName {
+    /**
+     * The display name for nodes of this type.
+     *
+     * @return The name for nodes of this type
+     */
+    String value();
+}
diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java
new file mode 100644
index 0000000..b0fd503
--- /dev/null
+++ b/core/java/android/view/inspector/InspectableProperty.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 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 android.view.inspector;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a getter of a property on an inspectable node.
+ *
+ * This annotation is inherited by default. If a child class doesn't add it to a getter, but a
+ * parent class does, the property will be inspected, even if the child overrides the definition
+ * of the getter. If a child class defines a property of the same name of a property on the parent
+ * but on a different getter, the inspector will use the child's getter when inspecting instances
+ * of the child, and the parent's otherwise.
+ *
+ * @see InspectionHelper#mapProperties(PropertyMapper)
+ * @see InspectionHelper#readProperties(Object, PropertyReader)
+ * @hide
+ */
+@Target({METHOD})
+@Retention(SOURCE)
+public @interface InspectableProperty {
+    /**
+     * The name of the property.
+     *
+     * If left empty (the default), the property name will be inferred from the name of the getter
+     * method.
+     *
+     * @return The name of the property.
+     */
+    String value() default "";
+}
diff --git a/core/java/android/view/inspector/InspectionHelper.java b/core/java/android/view/inspector/InspectionHelper.java
new file mode 100644
index 0000000..27a9704
--- /dev/null
+++ b/core/java/android/view/inspector/InspectionHelper.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2018 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 android.view.inspector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * An interface for companion objects used to inspect views.
+ *
+ * Inspection helpers only need to handle the properties, name and traversal of the specific class
+ * they are defined for, not anything from a parent class. At runtime, the inspector instantiates
+ * one instance of each inspection helper, and handles visiting them in the correct inheritance
+ * order for each type it inspects.
+ *
+ * Properties are read from the top of the type tree to the bottom, so that classes that override
+ * a property in their parent class can overwrite it in the reader. In general, properties will
+ * cleanly inherit through their getters, and the inspector runtime will read the properties of a
+ * parent class via the parent's inspection helper, and the child helper will only read properties
+ * added or changed since the parent was defined.
+ *
+ * Only one child traversal is considered for each class. If a descendant class defines a
+ * different child traversal than its parent, only the bottom traversal is used. If a class does
+ * not define its own child traversal, but one of its ancestors does, the bottom-most ancestor's
+ * traversal will be used.
+ *
+ * @param <T> The type of inspectable this helper operates on
+ * @hide
+ */
+public interface InspectionHelper<T> {
+    /**
+     * Map the string names of the properties this helper knows about to integer IDs.
+     *
+     * Each helper is responsible for storing the integer IDs of all its properties. This is the
+     * only method that is allowed to modify the stored IDs.
+     *
+     * Calling {@link #readProperties(T, PropertyReader)} before calling this results in
+     * undefined behavior.
+     *
+     * @param propertyMapper A {@link PropertyMapper} or lambda which maps string names to IDs.
+     */
+    void mapProperties(@NonNull PropertyMapper propertyMapper);
+
+    /**
+     * Read the values of an instance of this helper's type into a {@link PropertyReader}.
+     *
+     * This method needs to return the property IDs stored by
+     * {@link #mapProperties(PropertyMapper)}. Implementations should track if their properties
+     * have been mapped and throw a {@link UninitializedPropertyMapException} if this method is
+     * called before {mapProperties}.
+     *
+     * @param inspectable A object of type {@link T} to read the properties of.
+     * @param propertyReader An object which receives the property IDs and values.
+     */
+    void readProperties(@NonNull T inspectable, @NonNull PropertyReader propertyReader);
+
+    /**
+     * Query if this inspectable type can potentially have child nodes.
+     *
+     * E.g.: any descendant of {@link android.view.ViewGroup} can have child nodes, but a leaf
+     * view like {@link android.widget.ImageView} may not.
+     *
+     * The default implementation always returns false. If an implementing class overrides this, it
+     * should also define {@link #traverseChildren(T, ChildTraverser)}.
+     *
+     * @return True if this inspectable type can potentially have child nodes, false otherwise.
+     */
+    default boolean hasChildTraversal() {
+        return false;
+    }
+
+    /**
+     * Traverse the child nodes of an instance of this helper's type into a {@link ChildTraverser}.
+     *
+     * This provides the ability to traverse over a variety of collection APIs (e.g.: arrays,
+     * {@link Iterable}, or {@link java.util.Iterator}) in a uniform fashion. The traversal must be
+     * in the order defined by this helper's type. If the getter returns null, the helper must
+     * treat it as an empty collection.
+     *
+     * The default implementation throws a {@link NoChildTraversalException}. If
+     * {@link #hasChildTraversal()} returns is overriden to return true, it is expected that the
+     * implementing class will also override this method and provide a traversal.
+     *
+     * @param inspectable An object of type {@link T} to traverse the child nodes of.
+     * @param childTraverser A {@link ChildTraverser} or lamba to receive the children in order.
+     * @throws NoChildTraversalException If there is no defined child traversal
+     */
+    default void traverseChildren(
+            @NonNull T inspectable,
+            @SuppressWarnings("unused") @NonNull ChildTraverser childTraverser) {
+        throw new NoChildTraversalException(inspectable.getClass());
+    }
+
+    /**
+     * Get an optional name to display to developers for inspection nodes of this helper's type.
+     *
+     * The default implementation returns null, which will cause the runtime to use the class's
+     * simple name as defined by {@link Class#getSimpleName()} as the node name.
+     *
+     * If the type of this helper is inflated from XML, this method should be overridden to return
+     * the string used as the tag name for this type in XML.
+     *
+     * @return A string to use as the node name, or null to use the simple class name fallback.
+     */
+    @Nullable
+    default String getNodeName() {
+        return null;
+    }
+
+    /**
+     * Thrown by {@link #readProperties(Object, PropertyReader)} if called before
+     * {@link #mapProperties(PropertyMapper)}.
+     */
+    class UninitializedPropertyMapException extends RuntimeException {
+        public UninitializedPropertyMapException() {
+            super("Unable to read properties of an inspectable before mapping their IDs.");
+        }
+    }
+
+    /**
+     * Thrown by {@link #traverseChildren(Object, ChildTraverser)} if no child traversal exists.
+     */
+    class NoChildTraversalException extends RuntimeException {
+        public NoChildTraversalException(Class cls) {
+            super(String.format(
+                    "Class %s does not have a defined child traversal. Cannot traverse children.",
+                    cls.getCanonicalName()
+            ));
+        }
+    }
+}
diff --git a/core/java/android/view/inspector/OWNERS b/core/java/android/view/inspector/OWNERS
new file mode 100644
index 0000000..0473f54
--- /dev/null
+++ b/core/java/android/view/inspector/OWNERS
@@ -0,0 +1,3 @@
+alanv@google.com
+ashleyrose@google.com
+aurimas@google.com
\ No newline at end of file
diff --git a/core/java/android/view/inspector/PropertyMapper.java b/core/java/android/view/inspector/PropertyMapper.java
new file mode 100644
index 0000000..35550bd
--- /dev/null
+++ b/core/java/android/view/inspector/PropertyMapper.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2018 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 android.view.inspector;
+
+import android.annotation.NonNull;
+
+/**
+ * An interface for mapping the string names of inspectable properties to integer identifiers.
+ *
+ * This interface is consumed by {@link InspectionHelper#mapProperties(PropertyMapper)}.
+ *
+ * Mapping properties to IDs enables quick comparisons against shadow copies of inspectable
+ * objects without performing a large number of string comparisons.
+ *
+ * @see InspectionHelper#mapProperties(PropertyMapper)
+ * @hide
+ */
+public interface PropertyMapper {
+    /**
+     * Map a string name to an integer ID for a primitive boolean property.
+     *
+     * @param name The name of the property
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     */
+    int mapBoolean(@NonNull String name);
+
+    /**
+     * Map a string name to an integer ID for a primitive byte property.
+     *
+     * @param name The name of the property
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     */
+    int mapByte(@NonNull String name);
+
+    /**
+     * Map a string name to an integer ID for a primitive char property.
+     *
+     * @param name The name of the property
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     */
+    int mapChar(@NonNull String name);
+
+    /**
+     * Map a string name to an integer ID for a primitive double property.
+     *
+     * @param name The name of the property
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     */
+    int mapDouble(@NonNull String name);
+
+    /**
+     * Map a string name to an integer ID for a primitive float property.
+     *
+     * @param name The name of the property
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     */
+    int mapFloat(@NonNull String name);
+
+    /**
+     * Map a string name to an integer ID for a primitive int property.
+     *
+     * @param name The name of the property
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     */
+    int mapInt(@NonNull String name);
+
+    /**
+     * Map a string name to an integer ID for a primitive long property.
+     *
+     * @param name The name of the property
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     */
+    int mapLong(@NonNull String name);
+
+    /**
+     * Map a string name to an integer ID for a primitive short property.
+     *
+     * @param name The name of the property
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     */
+    int mapShort(@NonNull String name);
+
+    /**
+     * Map a string name to an integer ID for an object property.
+     *
+     * @param name The name of the property
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     */
+    int mapObject(@NonNull String name);
+
+    /**
+     * Thrown from a map method if a property name is already mapped as different type.
+     */
+    class PropertyConflictException extends RuntimeException {
+        public PropertyConflictException(
+                @NonNull String name,
+                @NonNull String newPropertyType,
+                @NonNull String existingPropertyType) {
+            super(String.format(
+                    "Attempted to map property \"%s\" as type %s, but it is already mapped as %s.",
+                    name,
+                    newPropertyType,
+                    existingPropertyType
+            ));
+        }
+    }
+}
diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java
new file mode 100644
index 0000000..df81c10
--- /dev/null
+++ b/core/java/android/view/inspector/PropertyReader.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2018 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 android.view.inspector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * An interface for reading the properties of an inspectable object.
+ *
+ * Used as the parameter for {@link InspectionHelper#readProperties(Object, PropertyReader)}.
+ * It has separate methods for all primitive types to avoid autoboxing overhead if a concrete
+ * implementation is able to work with primitives. Implementations should be prepared to accept
+ * {null} as the value of {@link PropertyReader#readObject(int, Object)}.
+ *
+ * @see InspectionHelper#readProperties(Object, PropertyReader)
+ * @hide
+ */
+public interface PropertyReader {
+    /**
+     * Read a primitive boolean property.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {boolean}
+     */
+    void readBoolean(int id, boolean value);
+
+    /**
+     * Read a primitive byte property.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {byte}
+     */
+    void readByte(int id, byte value);
+
+    /**
+     * Read a primitive character property.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {char}
+     */
+    void readChar(int id, char value);
+
+    /**
+     * Read a read a primitive double property.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {double}
+     */
+    void readDouble(int id, double value);
+
+    /**
+     * Read a primitive float property.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {float}
+     */
+    void readFloat(int id, float value);
+
+    /**
+     * Read a primitive integer property.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as an {int}
+     */
+    void readInt(int id, int value);
+
+    /**
+     * Read a primitive long property.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {long}
+     */
+    void readLong(int id, long value);
+
+    /**
+     * Read a primitive short property.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {short}
+     */
+    void readShort(int id, short value);
+
+    /**
+     * Read any object as a property.
+     *
+     * If value is null, the property is marked as empty.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as an object
+     */
+    void readObject(int id, @Nullable Object value);
+
+    /**
+     * Thrown if a client calls a typed read method for a property of a different type.
+     */
+    class PropertyTypeMismatchException extends RuntimeException {
+        public PropertyTypeMismatchException(
+                int id,
+                @NonNull String expectedPropertyType,
+                @NonNull String actualPropertyType,
+                @Nullable String propertyName) {
+            super(formatMessage(id, expectedPropertyType, actualPropertyType, propertyName));
+        }
+
+        public PropertyTypeMismatchException(
+                int id,
+                @NonNull String expectedPropertyType,
+                @NonNull String actualPropertyType) {
+            super(formatMessage(id, expectedPropertyType, actualPropertyType, null));
+        }
+
+        private static @NonNull String formatMessage(
+                int id,
+                @NonNull String expectedPropertyType,
+                @NonNull String actualPropertyType,
+                @Nullable String propertyName) {
+
+            if (propertyName == null) {
+                return String.format(
+                        "Attempted to read property with ID 0x%08X as type %s, "
+                            + "but the ID is of type %s.",
+                        id,
+                        expectedPropertyType,
+                        actualPropertyType
+                );
+            } else {
+                return String.format(
+                        "Attempted to read property \"%s\" with ID 0x%08X as type %s, "
+                            + "but the ID is of type %s.",
+                        propertyName,
+                        id,
+                        expectedPropertyType,
+                        actualPropertyType
+                );
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/core/java/android/view/intelligence/ContentCaptureEvent.aidl
new file mode 100644
index 0000000..c66a6cb
--- /dev/null
+++ b/core/java/android/view/intelligence/ContentCaptureEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 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 android.view.intelligence;
+
+parcelable ContentCaptureEvent;
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.java b/core/java/android/view/intelligence/ContentCaptureEvent.java
new file mode 100644
index 0000000..2530ae3
--- /dev/null
+++ b/core/java/android/view/intelligence/ContentCaptureEvent.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2018 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 android.view.intelligence;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+// TODO(b/111276913): add javadocs / implement Parcelable / implement
+/** @hide */
+@SystemApi
+public final class ContentCaptureEvent implements Parcelable {
+
+    /** @hide */
+    public static final int TYPE_ACTIVITY_DESTROYED = -2;
+    /** @hide */
+    public static final int TYPE_ACTIVITY_CREATED = -1;
+
+    /**
+     * Called when the activity is started.
+     */
+    public static final int TYPE_ACTIVITY_STARTED  = 1;
+
+    /**
+     * Called when the activity is resumed.
+     */
+    public static final int TYPE_ACTIVITY_RESUMED = 2;
+
+    /**
+     * Called when the activity is paused.
+     */
+    public static final int TYPE_ACTIVITY_PAUSED = 3;
+
+    /**
+     * Called when the activity is stopped.
+     */
+    public static final int TYPE_ACTIVITY_STOPPED  = 4;
+
+    /**
+     * Called when a node has been added to the screen and is visible to the user.
+     *
+     * <p>The metadata of the node is available through {@link #getViewNode()}.
+     */
+    public static final int TYPE_VIEW_ADDED = 5;
+
+    /**
+     * Called when a node has been removed from the screen and is not visible to the user anymore.
+     *
+     * <p>The id of the node is available through {@link #getId()}.
+     */
+    public static final int TYPE_VIEW_REMOVED = 6;
+
+    /**
+     * Called when the text of a node has been changed.
+     *
+     * <p>The id of the node is available through {@link #getId()}, and the new text is
+     * available through {@link #getText()}.
+     */
+    public static final int TYPE_VIEW_TEXT_CHANGED = 7;
+
+    // TODO(b/111276913): add event to indicate when FLAG_SECURE was changed?
+
+    /** @hide */
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_ACTIVITY_STARTED,
+            TYPE_ACTIVITY_PAUSED,
+            TYPE_ACTIVITY_RESUMED,
+            TYPE_ACTIVITY_STOPPED,
+            TYPE_VIEW_ADDED,
+            TYPE_VIEW_REMOVED,
+            TYPE_VIEW_TEXT_CHANGED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EventType{}
+
+    private final int mType;
+    private final long mEventTime;
+    private final int mFlags;
+
+
+    /** @hide */
+    public ContentCaptureEvent(int type, long eventTime, int flags) {
+        mType = type;
+        mEventTime = eventTime;
+        mFlags = flags;
+    }
+
+    /**
+     * Gets the type of the event.
+     *
+     * @return one of {@link #TYPE_ACTIVITY_STARTED}, {@link #TYPE_ACTIVITY_RESUMED},
+     * {@link #TYPE_ACTIVITY_PAUSED}, {@link #TYPE_ACTIVITY_STOPPED},
+     * {@link #TYPE_VIEW_ADDED}, {@link #TYPE_VIEW_REMOVED}, or {@link #TYPE_VIEW_TEXT_CHANGED}.
+     */
+    public @EventType int getType() {
+        return mType;
+    }
+
+    /**
+     * Gets when the event was generated, in ms.
+     */
+    public long getEventTime() {
+        return mEventTime;
+    }
+
+    /**
+     * Gets optional flags associated with the event.
+     *
+     * @return either {@code 0} or
+     * {@link android.view.intelligence.IntelligenceManager#FLAG_USER_INPUT}.
+     */
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Gets the whole metadata of the node associated with the event.
+     *
+     * <p>Only set on {@link #TYPE_VIEW_ADDED} events.
+     */
+    @Nullable
+    public ViewNode getViewNode() {
+        return null;
+    }
+
+    /**
+     * Gets the {@link AutofillId} of the node associated with the event.
+     *
+     * <p>Only set on {@link #TYPE_VIEW_REMOVED} and {@link #TYPE_VIEW_TEXT_CHANGED} events.
+     */
+    @Nullable
+    public AutofillId getId() {
+        return null;
+    }
+
+    /**
+     * Gets the current text of the node associated with the event.
+     *
+     * <p>Only set on {@link #TYPE_VIEW_TEXT_CHANGED} events.
+     */
+    @Nullable
+    public CharSequence getText() {
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder string = new StringBuilder("ContentCaptureEvent[type=")
+                .append(getTypeAsString(mType)).append(", time=").append(mEventTime);
+        if (mFlags > 0) {
+            string.append(", flags=").append(mFlags);
+        }
+        return string.append(']').toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(mType);
+        parcel.writeLong(mEventTime);
+        parcel.writeInt(mFlags);
+    }
+
+    public static final Parcelable.Creator<ContentCaptureEvent> CREATOR =
+            new Parcelable.Creator<ContentCaptureEvent>() {
+
+        @Override
+        public ContentCaptureEvent createFromParcel(Parcel parcel) {
+            final int type = parcel.readInt();
+            final long eventTime  = parcel.readLong();
+            final int flags = parcel.readInt();
+            return new ContentCaptureEvent(type, eventTime, flags);
+        }
+
+        @Override
+        public ContentCaptureEvent[] newArray(int size) {
+            return new ContentCaptureEvent[size];
+        }
+    };
+
+
+    /** @hide */
+    public static String getTypeAsString(@EventType int type) {
+        switch (type) {
+            case TYPE_ACTIVITY_STARTED:
+                return "ACTIVITY_STARTED";
+            case TYPE_ACTIVITY_RESUMED:
+                return "ACTIVITY_RESUMED";
+            case TYPE_ACTIVITY_PAUSED:
+                return "ACTIVITY_PAUSED";
+            case TYPE_ACTIVITY_STOPPED:
+                return "ACTIVITY_STOPPED";
+            case TYPE_VIEW_ADDED:
+                return "VIEW_ADDED";
+            case TYPE_VIEW_REMOVED:
+                return "VIEW_REMOVED";
+            case TYPE_VIEW_TEXT_CHANGED:
+                return "VIEW_TEXT_CHANGED";
+            default:
+                return "UKNOWN_TYPE: " + type;
+        }
+    }
+}
diff --git a/core/java/android/view/intelligence/IIntelligenceManager.aidl b/core/java/android/view/intelligence/IIntelligenceManager.aidl
new file mode 100644
index 0000000..2f128de
--- /dev/null
+++ b/core/java/android/view/intelligence/IIntelligenceManager.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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 android.view.intelligence;
+
+import android.content.ComponentName;
+import android.os.IBinder;
+import android.service.intelligence.InteractionSessionId;
+import android.view.intelligence.ContentCaptureEvent;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.util.List;
+
+/**
+ * {@hide}
+ */
+oneway interface IIntelligenceManager {
+    /**
+      * Starts a session, sending the "remote" sessionId to the receiver.
+      */
+    void startSession(int userId, IBinder activityToken, in ComponentName componentName,
+                      in InteractionSessionId sessionId, int flags, in IResultReceiver result);
+
+    /**
+      * Finishes a session.
+      */
+    void finishSession(int userId, in InteractionSessionId sessionId);
+
+    /**
+      * Sends a batch of events
+      */
+    void sendEvents(int userId, in InteractionSessionId sessionId,
+                    in List<ContentCaptureEvent> events);
+}
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
new file mode 100644
index 0000000..9bf6c2c
--- /dev/null
+++ b/core/java/android/view/intelligence/IntelligenceManager.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2018 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 android.view.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.service.intelligence.InteractionSessionId;
+import android.util.Log;
+import android.view.intelligence.ContentCaptureEvent.EventType;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * TODO(b/111276913): add javadocs / implement
+ */
+@SystemService(Context.INTELLIGENCE_MANAGER_SERVICE)
+public final class IntelligenceManager {
+
+    private static final String TAG = "IntelligenceManager";
+
+    // TODO(b/111276913): define a way to dynamically set it (for example, using settings?)
+    private static final boolean VERBOSE = false;
+
+    /**
+     * Used to indicate that a text change was caused by user input (for example, through IME).
+     */
+    //TODO(b/111276913): link to notifyTextChanged() method once available
+    public static final int FLAG_USER_INPUT = 0x1;
+
+
+    /**
+     * Initial state, when there is no session.
+     *
+     * @hide
+     */
+    public static final int STATE_UNKNOWN = 0;
+
+    /**
+     * Service's startSession() was called, but server didn't confirm it was created yet.
+     *
+     * @hide
+     */
+    public static final int STATE_WAITING_FOR_SERVER = 1;
+
+    /**
+     * Session is active.
+     *
+     * @hide
+     */
+    public static final int STATE_ACTIVE = 2;
+
+    private final Context mContext;
+
+    @Nullable
+    private final IIntelligenceManager mService;
+
+    private final Object mLock = new Object();
+
+    @Nullable
+    @GuardedBy("mLock")
+    private InteractionSessionId mId;
+
+    @GuardedBy("mLock")
+    private int mState = STATE_UNKNOWN;
+
+    @GuardedBy("mLock")
+    private IBinder mApplicationToken;
+
+    // TODO(b/111276913): replace by an interface name implemented by Activity, similar to
+    // AutofillClient
+    @GuardedBy("mLock")
+    private ComponentName mComponentName;
+
+    /** @hide */
+    public IntelligenceManager(@NonNull Context context, @Nullable IIntelligenceManager service) {
+        mContext = Preconditions.checkNotNull(context, "context cannot be null");
+        mService = service;
+    }
+
+    /** @hide */
+    public void onActivityCreated(@NonNull IBinder token, @NonNull ComponentName componentName) {
+        if (!isContentCaptureEnabled()) return;
+
+        synchronized (mLock) {
+            if (mState != STATE_UNKNOWN) {
+                Log.w(TAG, "ignoring onActivityStarted(" + token + ") while on state "
+                        + getStateAsStringLocked());
+                return;
+            }
+            mState = STATE_WAITING_FOR_SERVER;
+            mId = new InteractionSessionId();
+            mApplicationToken = token;
+            mComponentName = componentName;
+
+            if (VERBOSE) {
+                Log.v(TAG, "onActivityStarted(): token=" + token + ", act=" + componentName
+                        + ", id=" + mId);
+            }
+            final int flags = 0; // TODO(b/111276913): get proper flags
+
+            try {
+                mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
+                        mId, flags, new IResultReceiver.Stub() {
+                            @Override
+                            public void send(int resultCode, Bundle resultData)
+                                    throws RemoteException {
+                                synchronized (mLock) {
+                                    if (resultCode > 0) {
+                                        mState = STATE_ACTIVE;
+                                    } else {
+                                        // TODO(b/111276913): handle other cases like disabled by
+                                        // service
+                                        mState = STATE_UNKNOWN;
+                                    }
+                                    if (VERBOSE) {
+                                        Log.v(TAG, "onActivityStarted() result: code=" + resultCode
+                                                + ", id=" + mId
+                                                + ", state=" + getStateAsStringLocked());
+                                    }
+                                }
+                            }
+                        });
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Used for intermediate events (i.e, other than created and destroyed).
+     *
+     * @hide
+     */
+    public void onActivityLifecycleEvent(@EventType int type) {
+        if (!isContentCaptureEnabled()) return;
+
+        //TODO(b/111276913): should buffer event (and call service on handler thread), instead of
+        // calling right away
+        final ContentCaptureEvent event = new ContentCaptureEvent(type, SystemClock.uptimeMillis(),
+                0);
+        final List<ContentCaptureEvent> events = Arrays.asList(event);
+
+        synchronized (mLock) {
+            //TODO(b/111276913): check session state; for example, how to handle if it's waiting for
+            // remote id
+
+            if (VERBOSE) {
+                Log.v(TAG, "onActivityLifecycleEvent() for " + mComponentName.flattenToShortString()
+                        + ": " + ContentCaptureEvent.getTypeAsString(type));
+            }
+
+            try {
+                mService.sendEvents(mContext.getUserId(), mId, events);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /** @hide */
+    public void onActivityDestroyed() {
+        if (!isContentCaptureEnabled()) return;
+
+        synchronized (mLock) {
+            //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
+            // id) and send it to the cache of batched commands
+
+            if (VERBOSE) {
+                Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsStringLocked()
+                        + ", mId=" + mId);
+            }
+
+            try {
+                mService.finishSession(mContext.getUserId(), mId);
+                mState = STATE_UNKNOWN;
+                mId = null;
+                mApplicationToken = null;
+                mComponentName = null;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns the component name of the {@code android.service.intelligence.IntelligenceService}
+     * that is enabled for the current user.
+     */
+    @Nullable
+    public ComponentName getIntelligenceServiceComponentName() {
+        //TODO(b/111276913): implement
+        return null;
+    }
+
+    /**
+     * Checks whether content capture is enabled for this activity.
+     */
+    public boolean isContentCaptureEnabled() {
+        //TODO(b/111276913): properly implement by checking if it was explicitly disabled by
+        // service, or if service is not set
+        // (and probably renamign to isEnabledLocked()
+        return mService != null;
+    }
+
+    /**
+     * Called by apps to disable content capture.
+     *
+     * <p><b>Note: </b> this call is not persisted accross reboots, so apps should typically call
+     * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
+     */
+    public void disableContentCapture() {
+        //TODO(b/111276913): implement
+    }
+
+    /**
+     * Called by the the service {@link android.service.intelligence.IntelligenceService}
+     * to define whether content capture should be enabled for activities with such
+     * {@link android.content.ComponentName}.
+     *
+     * <p>Useful to blacklist a particular activity.
+     *
+     * @throws UnsupportedOperationException if not called by the UID that owns the
+     * {@link android.service.intelligence.IntelligenceService} associated with the
+     * current user.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void setActivityContentCaptureEnabled(@NonNull ComponentName activity,
+            boolean enabled) {
+        //TODO(b/111276913): implement
+    }
+
+    /**
+     * Called by the the service {@link android.service.intelligence.IntelligenceService}
+     * to define whether content capture should be enabled for activities of the app with such
+     * {@code packageName}.
+     *
+     * <p>Useful to blacklist any activity from a particular app.
+     *
+     * @throws UnsupportedOperationException if not called by the UID that owns the
+     * {@link android.service.intelligence.IntelligenceService} associated with the
+     * current user.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void setPackageContentCaptureEnabled(@NonNull String packageName, boolean enabled) {
+        //TODO(b/111276913): implement
+    }
+
+    /**
+     * Gets the activities where content capture was disabled by
+     * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}.
+     *
+     * @throws UnsupportedOperationException if not called by the UID that owns the
+     * {@link android.service.intelligence.IntelligenceService} associated with the
+     * current user.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public Set<ComponentName> getContentCaptureDisabledActivities() {
+        //TODO(b/111276913): implement
+        return null;
+    }
+
+    /**
+     * Gets the apps where content capture was disabled by
+     * {@link #setPackageContentCaptureEnabled(String, boolean)}.
+     *
+     * @throws UnsupportedOperationException if not called by the UID that owns the
+     * {@link android.service.intelligence.IntelligenceService} associated with the
+     * current user.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public Set<String> getContentCaptureDisabledPackages() {
+        //TODO(b/111276913): implement
+        return null;
+    }
+
+    /** @hide */
+    public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.println("IntelligenceManager");
+        final String prefix2 = prefix + "  ";
+        synchronized (mLock) {
+            pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
+            pw.print(prefix2); pw.print("mService: "); pw.println(mService);
+            pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
+            pw.print(prefix2); pw.print("enabled: "); pw.println(isContentCaptureEnabled());
+            pw.print(prefix2); pw.print("id: "); pw.println(mId);
+            pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
+            pw.print(getStateAsStringLocked()); pw.println(")");
+            pw.print(prefix2); pw.print("appToken: "); pw.println(mApplicationToken);
+            pw.print(prefix2); pw.print("componentName: "); pw.println(mComponentName);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private String getStateAsStringLocked() {
+        return getStateAsString(mState);
+    }
+
+    @NonNull
+    private static String getStateAsString(int state) {
+        switch (state) {
+            case STATE_UNKNOWN:
+                return "UNKNOWN";
+            case STATE_WAITING_FOR_SERVER:
+                return "WAITING_FOR_SERVER";
+            case STATE_ACTIVE:
+                return "ACTIVE";
+            default:
+                return "INVALID:" + state;
+        }
+    }
+}
diff --git a/core/java/android/view/intelligence/ViewNode.java b/core/java/android/view/intelligence/ViewNode.java
new file mode 100644
index 0000000..357ecf5
--- /dev/null
+++ b/core/java/android/view/intelligence/ViewNode.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 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 android.view.intelligence;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.assist.AssistStructure;
+import android.view.autofill.AutofillId;
+
+//TODO(b/111276913): add javadocs / implement Parcelable / implement
+//TODO(b/111276913): for now it's extending ViewNode directly as it needs most of its properties,
+// but it might be better to create a common, abstract android.view.ViewNode class that both extend
+// instead
+/** @hide */
+@SystemApi
+public final class ViewNode extends AssistStructure.ViewNode {
+
+    /** @hide */
+    public ViewNode() {
+    }
+
+    /**
+     * Returns the {@link AutofillId} of this view's parent, if the parent is also part of the
+     * screen observation tree.
+     */
+    @Nullable
+    public AutofillId getParentAutofillId() {
+        //TODO(b/111276913): implement
+        return null;
+    }
+}
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
new file mode 100644
index 0000000..5fcf227
--- /dev/null
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -0,0 +1,779 @@
+/*
+ * Copyright (C) 2018 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 android.view.textclassifier;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.app.Person;
+import android.app.RemoteAction;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.SpannedString;
+import android.util.ArraySet;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Represents a list of actions suggested by a {@link TextClassifier} on a given conversation.
+ *
+ * @see TextClassifier#suggestConversationActions(Request)
+ */
+public final class ConversationActions implements Parcelable {
+
+    public static final Creator<ConversationActions> CREATOR =
+            new Creator<ConversationActions>() {
+                @Override
+                public ConversationActions createFromParcel(Parcel in) {
+                    return new ConversationActions(in);
+                }
+
+                @Override
+                public ConversationActions[] newArray(int size) {
+                    return new ConversationActions[size];
+                }
+            };
+
+    /** @hide */
+    @Retention(SOURCE)
+    @StringDef(
+            value = {
+                    TYPE_VIEW_CALENDAR,
+                    TYPE_VIEW_MAP,
+                    TYPE_TRACK_FLIGHT,
+                    TYPE_OPEN_URL,
+                    TYPE_SEND_SMS,
+                    TYPE_CALL_PHONE,
+                    TYPE_SEND_EMAIL,
+                    TYPE_TEXT_REPLY,
+                    TYPE_CREATE_REMINDER,
+                    TYPE_SHARE_LOCATION
+            },
+            prefix = "TYPE_")
+    public @interface ActionType {}
+
+    /**
+     * Indicates an action to view a calendar at a specified time.
+     */
+    public static final String TYPE_VIEW_CALENDAR = "view_calendar";
+    /**
+     * Indicates an action to view the map at a specified location.
+     */
+    public static final String TYPE_VIEW_MAP = "view_map";
+    /**
+     * Indicates an action to track a flight.
+     */
+    public static final String TYPE_TRACK_FLIGHT = "track_flight";
+    /**
+     * Indicates an action to open an URL.
+     */
+    public static final String TYPE_OPEN_URL = "open_url";
+    /**
+     * Indicates an action to send a SMS.
+     */
+    public static final String TYPE_SEND_SMS = "send_sms";
+    /**
+     * Indicates an action to call a phone number.
+     */
+    public static final String TYPE_CALL_PHONE = "call_phone";
+    /**
+     * Indicates an action to send an email.
+     */
+    public static final String TYPE_SEND_EMAIL = "send_email";
+    /**
+     * Indicates an action to reply with a text message.
+     */
+    public static final String TYPE_TEXT_REPLY = "text_reply";
+    /**
+     * Indicates an action to create a reminder.
+     */
+    public static final String TYPE_CREATE_REMINDER = "create_reminder";
+    /**
+     * Indicates an action to reply with a location.
+     */
+    public static final String TYPE_SHARE_LOCATION = "share_location";
+
+    /** @hide */
+    @Retention(SOURCE)
+    @StringDef(
+            value = {
+                    HINT_FOR_NOTIFICATION,
+                    HINT_FOR_IN_APP,
+            },
+            prefix = "HINT_")
+    public @interface Hint {}
+    /**
+     * To indicate the generated actions will be used within the app.
+     */
+    public static final String HINT_FOR_IN_APP = "in_app";
+    /**
+     * To indicate the generated actions will be used for notification.
+     */
+    public static final String HINT_FOR_NOTIFICATION = "notification";
+
+    private List<ConversationAction> mConversationActions;
+
+    /** Constructs a {@link ConversationActions} object. */
+    public ConversationActions(@NonNull List<ConversationAction> conversationActions) {
+        mConversationActions =
+                Collections.unmodifiableList(Preconditions.checkNotNull(conversationActions));
+    }
+
+    private ConversationActions(Parcel in) {
+        mConversationActions =
+                Collections.unmodifiableList(in.createTypedArrayList(ConversationAction.CREATOR));
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeTypedList(mConversationActions);
+    }
+
+    /** Returns an immutable list of {@link ConversationAction} objects. */
+    @NonNull
+    public List<ConversationAction> getConversationActions() {
+        return mConversationActions;
+    }
+
+    /** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
+    public static final class ConversationAction implements Parcelable {
+
+        public static final Creator<ConversationAction> CREATOR =
+                new Creator<ConversationAction>() {
+                    @Override
+                    public ConversationAction createFromParcel(Parcel in) {
+                        return new ConversationAction(in);
+                    }
+
+                    @Override
+                    public ConversationAction[] newArray(int size) {
+                        return new ConversationAction[size];
+                    }
+                };
+
+        @NonNull
+        @ActionType
+        private final String mType;
+        @NonNull
+        private final CharSequence mTextReply;
+        @Nullable
+        private final RemoteAction mAction;
+
+        @FloatRange(from = 0, to = 1)
+        private final float mScore;
+
+        @NonNull
+        private final Bundle mExtras;
+
+        private ConversationAction(
+                @NonNull String type,
+                @Nullable RemoteAction action,
+                @Nullable CharSequence textReply,
+                float score,
+                @NonNull Bundle extras) {
+            mType = Preconditions.checkNotNull(type);
+            mAction = action;
+            mTextReply = textReply;
+            mScore = score;
+            mExtras = Preconditions.checkNotNull(extras);
+        }
+
+        private ConversationAction(Parcel in) {
+            mType = in.readString();
+            mAction = in.readParcelable(null);
+            mTextReply = in.readCharSequence();
+            mScore = in.readFloat();
+            mExtras = in.readBundle();
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeString(mType);
+            parcel.writeParcelable(mAction, flags);
+            parcel.writeCharSequence(mTextReply);
+            parcel.writeFloat(mScore);
+            parcel.writeBundle(mExtras);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @NonNull
+        @ActionType
+        /** Returns the type of this action, for example, {@link #TYPE_VIEW_CALENDAR}. */
+        public String getType() {
+            return mType;
+        }
+
+        @Nullable
+        /**
+         * Returns a RemoteAction object, which contains the icon, label and a PendingIntent, for
+         * the specified action type.
+         */
+        public RemoteAction getAction() {
+            return mAction;
+        }
+
+        /**
+         * Returns the confidence score for the specified action. The value ranges from 0 (low
+         * confidence) to 1 (high confidence).
+         */
+        @FloatRange(from = 0, to = 1)
+        public float getConfidenceScore() {
+            return mScore;
+        }
+
+        /**
+         * Returns the text reply that could be sent as a reply to the given conversation.
+         * <p>
+         * This is only available when the type of the action is {@link #TYPE_TEXT_REPLY}.
+         */
+        @Nullable
+        public CharSequence getTextReply() {
+            return mTextReply;
+        }
+
+        /**
+         * Returns the extended data related to this conversation action.
+         *
+         * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+         * prefer to hold a reference to the returned bundle rather than frequently calling this
+         * method.
+         */
+        @NonNull
+        public Bundle getExtras() {
+            return mExtras.deepCopy();
+        }
+
+        /** Builder class to construct {@link ConversationAction}. */
+        public static final class Builder {
+            @Nullable
+            @ActionType
+            private String mType;
+            @Nullable
+            private RemoteAction mAction;
+            @Nullable
+            private CharSequence mTextReply;
+            private float mScore;
+            @Nullable
+            private Bundle mExtras;
+
+            public Builder(@NonNull @ActionType String actionType) {
+                mType = Preconditions.checkNotNull(actionType);
+            }
+
+            /**
+             * Sets an action that may be performed on the given conversation.
+             */
+            @NonNull
+            public Builder setAction(@Nullable RemoteAction action) {
+                mAction = action;
+                return this;
+            }
+
+            /**
+             * Sets a text reply that may be performed on the given conversation.
+             */
+            @NonNull
+            public Builder setTextReply(@Nullable CharSequence textReply) {
+                mTextReply = textReply;
+                return this;
+            }
+
+            /** Sets the confident score. */
+            @NonNull
+            public Builder setConfidenceScore(@FloatRange(from = 0, to = 1) float score) {
+                mScore = score;
+                return this;
+            }
+
+            /**
+             * Sets the extended data for the conversation action object.
+             */
+            @NonNull
+            public Builder setExtras(@Nullable Bundle extras) {
+                mExtras = extras;
+                return this;
+            }
+
+            /** Builds the {@link ConversationAction} object. */
+            @NonNull
+            public ConversationAction build() {
+                return new ConversationAction(
+                        mType,
+                        mAction,
+                        mTextReply,
+                        mScore,
+                        mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
+            }
+        }
+    }
+
+    /** Represents a message in the conversation. */
+    public static final class Message implements Parcelable {
+        @Nullable
+        private final Person mAuthor;
+        @Nullable
+        private final ZonedDateTime mComposeTime;
+        @Nullable
+        private final CharSequence mText;
+        @NonNull
+        private final Bundle mExtras;
+
+        private Message(
+                @Nullable Person author,
+                @Nullable ZonedDateTime composeTime,
+                @Nullable CharSequence text,
+                @NonNull Bundle bundle) {
+            mAuthor = author;
+            mComposeTime = composeTime;
+            mText = text;
+            mExtras = Preconditions.checkNotNull(bundle);
+        }
+
+        private Message(Parcel in) {
+            mAuthor = in.readParcelable(null);
+            mComposeTime =
+                    in.readInt() == 0
+                            ? null
+                            : ZonedDateTime.parse(
+                                    in.readString(), DateTimeFormatter.ISO_ZONED_DATE_TIME);
+            mText = in.readCharSequence();
+            mExtras = in.readBundle();
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeParcelable(mAuthor, flags);
+            parcel.writeInt(mComposeTime != null ? 1 : 0);
+            if (mComposeTime != null) {
+                parcel.writeString(mComposeTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+            }
+            parcel.writeCharSequence(mText);
+            parcel.writeBundle(mExtras);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<Message> CREATOR =
+                new Creator<Message>() {
+                    @Override
+                    public Message createFromParcel(Parcel in) {
+                        return new Message(in);
+                    }
+
+                    @Override
+                    public Message[] newArray(int size) {
+                        return new Message[size];
+                    }
+                };
+
+        /** Returns the person that composed the message. */
+        @Nullable
+        public Person getAuthor() {
+            return mAuthor;
+        }
+
+        /** Returns the compose time of the message. */
+        @Nullable
+        public ZonedDateTime getTime() {
+            return mComposeTime;
+        }
+
+        /** Returns the text of the message. */
+        @Nullable
+        public CharSequence getText() {
+            return mText;
+        }
+
+        /**
+         * Returns the extended data related to this conversation action.
+         *
+         * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+         * prefer to hold a reference to the returned bundle rather than frequently calling this
+         * method.
+         */
+        @NonNull
+        public Bundle getExtras() {
+            return mExtras.deepCopy();
+        }
+
+        /** Builder class to construct a {@link Message} */
+        public static final class Builder {
+            @Nullable
+            private Person mAuthor;
+            @Nullable
+            private ZonedDateTime mComposeTime;
+            @Nullable
+            private CharSequence mText;
+            @Nullable
+            private Bundle mExtras;
+
+            /** Sets the person who composed this message. */
+            @NonNull
+            public Builder setAuthor(@Nullable Person author) {
+                mAuthor = author;
+                return this;
+            }
+
+            /** Sets the text of this message */
+            @NonNull
+            public Builder setText(@Nullable CharSequence text) {
+                mText = text;
+                return this;
+            }
+
+            /** Sets the compose time of this message */
+            @NonNull
+            public Builder setComposeTime(@Nullable ZonedDateTime composeTime) {
+                mComposeTime = composeTime;
+                return this;
+            }
+
+            /** Sets a set of extended data to the message. */
+            @NonNull
+            public Builder setExtras(@Nullable Bundle bundle) {
+                this.mExtras = bundle;
+                return this;
+            }
+
+            /** Builds the {@link Message} object. */
+            @NonNull
+            public Message build() {
+                return new Message(
+                        mAuthor,
+                        mComposeTime,
+                        mText == null ? null : new SpannedString(mText),
+                        mExtras == null ? new Bundle() : mExtras.deepCopy());
+            }
+        }
+    }
+
+    /** Configuration object for specifying what action types to identify. */
+    public static final class TypeConfig implements Parcelable {
+        @NonNull
+        @ActionType
+        private final Set<String> mExcludedTypes;
+        @NonNull
+        @ActionType
+        private final Set<String> mIncludedTypes;
+        private final boolean mIncludeTypesFromTextClassifier;
+
+        private TypeConfig(
+                @NonNull Set<String> includedTypes,
+                @NonNull Set<String> excludedTypes,
+                boolean includeTypesFromTextClassifier) {
+            mIncludedTypes = Preconditions.checkNotNull(includedTypes);
+            mExcludedTypes = Preconditions.checkNotNull(excludedTypes);
+            mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
+        }
+
+        private TypeConfig(Parcel in) {
+            mIncludedTypes = new ArraySet<>(in.createStringArrayList());
+            mExcludedTypes = new ArraySet<>(in.createStringArrayList());
+            mIncludeTypesFromTextClassifier = in.readByte() != 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeStringList(new ArrayList<>(mIncludedTypes));
+            parcel.writeStringList(new ArrayList<>(mExcludedTypes));
+            parcel.writeByte((byte) (mIncludeTypesFromTextClassifier ? 1 : 0));
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<TypeConfig> CREATOR =
+                new Creator<TypeConfig>() {
+                    @Override
+                    public TypeConfig createFromParcel(Parcel in) {
+                        return new TypeConfig(in);
+                    }
+
+                    @Override
+                    public TypeConfig[] newArray(int size) {
+                        return new TypeConfig[size];
+                    }
+                };
+
+        /**
+         * Returns a final list of types that the text classifier should look for.
+         *
+         * <p>NOTE: This method is intended for use by a text classifier.
+         *
+         * @param defaultTypes types the text classifier thinks should be included before factoring
+         *    in the included/excluded types given by the client.
+         */
+        @NonNull
+        public Collection<String> resolveTypes(@Nullable Collection<String> defaultTypes) {
+            Set<String> types = new ArraySet<>();
+            if (mIncludeTypesFromTextClassifier && defaultTypes != null) {
+                types.addAll(defaultTypes);
+            }
+            types.addAll(mIncludedTypes);
+            types.removeAll(mExcludedTypes);
+            return Collections.unmodifiableCollection(types);
+        }
+
+        /**
+         * Return whether the client allows the text classifier to include its own list of default
+         * types. If this function returns {@code true}, the text classifier can consider specifying
+         * a default list of entity types in {@link #resolveTypes(Collection)}.
+         *
+         * <p>NOTE: This method is intended for use by a text classifier.
+         *
+         * @see #resolveTypes(Collection)
+         */
+        public boolean shouldIncludeTypesFromTextClassifier() {
+            return mIncludeTypesFromTextClassifier;
+        }
+
+        /** Builder class to construct the {@link TypeConfig} object. */
+        public static final class Builder {
+            @Nullable
+            private Collection<String> mExcludedTypes;
+            @Nullable
+            private Collection<String> mIncludedTypes;
+            private boolean mIncludeTypesFromTextClassifier = true;
+
+            /**
+             * Sets a collection of types that are explicitly included, for example, {@link
+             * #TYPE_VIEW_CALENDAR}.
+             */
+            @NonNull
+            public Builder setIncludedTypes(
+                    @Nullable @ActionType Collection<String> includedTypes) {
+                mIncludedTypes = includedTypes;
+                return this;
+            }
+
+            /**
+             * Sets a collection of types that are explicitly excluded, for example, {@link
+             * #TYPE_VIEW_CALENDAR}.
+             */
+            @NonNull
+            public Builder setExcludedTypes(
+                    @Nullable @ActionType Collection<String> excludedTypes) {
+                mExcludedTypes = excludedTypes;
+                return this;
+            }
+
+            /**
+             * Specifies whether or not to include the types suggested by the text classifier. By
+             * default, it is included.
+             */
+            @NonNull
+            public Builder includeTypesFromTextClassifier(boolean includeTypesFromTextClassifier) {
+                mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
+                return this;
+            }
+
+            /**
+             * Combines all of the options that have been set and returns a new {@link TypeConfig}
+             * object.
+             */
+            @NonNull
+            public TypeConfig build() {
+                return new TypeConfig(
+                        mIncludedTypes == null
+                                ? Collections.emptySet()
+                                : new ArraySet<>(mIncludedTypes),
+                        mExcludedTypes == null
+                                ? Collections.emptySet()
+                                : new ArraySet<>(mExcludedTypes),
+                        mIncludeTypesFromTextClassifier);
+            }
+        }
+    }
+
+    /**
+     * A request object for generating conversation action suggestions.
+     *
+     * @see TextClassifier#suggestConversationActions(Request)
+     */
+    public static final class Request implements Parcelable {
+        @NonNull
+        private final List<Message> mConversation;
+        @NonNull
+        private final TypeConfig mTypeConfig;
+        private final int mMaxSuggestions;
+        @NonNull
+        @Hint
+        private final List<String> mHints;
+
+        private Request(
+                @NonNull List<Message> conversation,
+                @NonNull TypeConfig typeConfig,
+                int maxSuggestions,
+                @Nullable @Hint List<String> hints) {
+            mConversation = Preconditions.checkNotNull(conversation);
+            mTypeConfig = Preconditions.checkNotNull(typeConfig);
+            mMaxSuggestions = maxSuggestions;
+            mHints = hints;
+        }
+
+        private Request(Parcel in) {
+            List<Message> conversation = new ArrayList<>();
+            in.readParcelableList(conversation, null);
+            mConversation = Collections.unmodifiableList(conversation);
+            mTypeConfig = in.readParcelable(null);
+            mMaxSuggestions = in.readInt();
+            List<String> hints = new ArrayList<>();
+            in.readStringList(hints);
+            mHints = Collections.unmodifiableList(hints);
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeParcelableList(mConversation, flags);
+            parcel.writeParcelable(mTypeConfig, flags);
+            parcel.writeInt(mMaxSuggestions);
+            parcel.writeStringList(mHints);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<Request> CREATOR =
+                new Creator<Request>() {
+                    @Override
+                    public Request createFromParcel(Parcel in) {
+                        return new Request(in);
+                    }
+
+                    @Override
+                    public Request[] newArray(int size) {
+                        return new Request[size];
+                    }
+                };
+
+        /** Returns the type config. */
+        @NonNull
+        public TypeConfig getTypeConfig() {
+            return mTypeConfig;
+        }
+
+        /** Returns an immutable list of messages that make up the conversation. */
+        @NonNull
+        public List<Message> getConversation() {
+            return mConversation;
+        }
+
+        /**
+         * Return the maximal number of suggestions the caller wants, value 0 means no restriction.
+         */
+        @IntRange(from = 0)
+        public int getMaxSuggestions() {
+            return mMaxSuggestions;
+        }
+
+        /** Returns an immutable list of hints */
+        @Nullable
+        @Hint
+        public List<String> getHints() {
+            return mHints;
+        }
+
+        /** Builder object to construct the {@link Request} object. */
+        public static final class Builder {
+            @NonNull
+            private List<Message> mConversation;
+            @Nullable
+            private TypeConfig mTypeConfig;
+            private int mMaxSuggestions;
+            @Nullable
+            @Hint
+            private List<String> mHints;
+
+            /**
+             * Constructs a builder.
+             *
+             * @param conversation the conversation that the text classifier is going to generate
+             *     actions for.
+             */
+            public Builder(@NonNull List<Message> conversation) {
+                mConversation = Preconditions.checkNotNull(conversation);
+            }
+
+            /**
+             * Sets the hints to help text classifier to generate actions. It could be used to help
+             * text classifier to infer what types of actions the caller may be interested in.
+             */
+            public Builder setHints(@Nullable @Hint List<String> hints) {
+                mHints = hints;
+                return this;
+            }
+
+            /** Sets the type config. */
+            @NonNull
+            public Builder setTypeConfig(@Nullable TypeConfig typeConfig) {
+                mTypeConfig = typeConfig;
+                return this;
+            }
+
+            /** Sets the maximum number of suggestions you want.
+             * <p>
+             * Value 0 means no restriction.
+             */
+            @NonNull
+            public Builder setMaxSuggestions(@IntRange(from = 0) int maxSuggestions) {
+                mMaxSuggestions = Preconditions.checkArgumentNonnegative(maxSuggestions);
+                return this;
+            }
+
+            /** Builds the {@link Request} object. */
+            @NonNull
+            public Request build() {
+                return new Request(
+                        Collections.unmodifiableList(mConversation),
+                        mTypeConfig == null ? new TypeConfig.Builder().build() : mTypeConfig,
+                        mMaxSuggestions,
+                        mHints == null
+                                ? Collections.emptyList()
+                                : Collections.unmodifiableList(mHints));
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/ModelFileManager.java b/core/java/android/view/textclassifier/ModelFileManager.java
new file mode 100644
index 0000000..adea125
--- /dev/null
+++ b/core/java/android/view/textclassifier/ModelFileManager.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2018 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 android.view.textclassifier;
+
+import static android.view.textclassifier.TextClassifier.DEFAULT_LOG_TAG;
+
+import android.annotation.Nullable;
+import android.os.LocaleList;
+import android.os.ParcelFileDescriptor;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.StringJoiner;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Manages model files that are listed by the model files supplier.
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class ModelFileManager {
+    private final Object mLock = new Object();
+    private final Supplier<List<ModelFile>> mModelFileSupplier;
+
+    private List<ModelFile> mModelFiles;
+
+    public ModelFileManager(Supplier<List<ModelFile>> modelFileSupplier) {
+        mModelFileSupplier = Preconditions.checkNotNull(modelFileSupplier);
+    }
+
+    /**
+     * Returns an unmodifiable list of model files listed by the given model files supplier.
+     * <p>
+     * The result is cached.
+     */
+    public List<ModelFile> listModelFiles() {
+        synchronized (mLock) {
+            if (mModelFiles == null) {
+                mModelFiles = Collections.unmodifiableList(mModelFileSupplier.get());
+            }
+            return mModelFiles;
+        }
+    }
+
+    /**
+     * Returns the best model file for the given localelist, {@code null} if nothing is found.
+     *
+     * @param localeList the required locales, use {@code null} if there is no preference.
+     */
+    public ModelFile findBestModelFile(@Nullable LocaleList localeList) {
+        // Specified localeList takes priority over the system default, so it is listed first.
+        final String languages = localeList == null || localeList.isEmpty()
+                ? LocaleList.getDefault().toLanguageTags()
+                : localeList.toLanguageTags() + "," + LocaleList.getDefault().toLanguageTags();
+        final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages);
+
+        ModelFile bestModel = null;
+        for (ModelFile model : listModelFiles()) {
+            if (model.isAnyLanguageSupported(languageRangeList)) {
+                if (model.isPreferredTo(bestModel)) {
+                    bestModel = model;
+                }
+            }
+        }
+        return bestModel;
+    }
+
+    /**
+     * Default implementation of the model file supplier.
+     */
+    public static final class ModelFileSupplierImpl implements Supplier<List<ModelFile>> {
+        private final File mUpdatedModelFile;
+        private final File mFactoryModelDir;
+        private final Pattern mModelFilenamePattern;
+        private final Function<Integer, Integer> mVersionSupplier;
+        private final Function<Integer, String> mSupportedLocalesSupplier;
+
+        public ModelFileSupplierImpl(
+                File factoryModelDir,
+                String factoryModelFileNameRegex,
+                File updatedModelFile,
+                Function<Integer, Integer> versionSupplier,
+                Function<Integer, String> supportedLocalesSupplier) {
+            mUpdatedModelFile = Preconditions.checkNotNull(updatedModelFile);
+            mFactoryModelDir = Preconditions.checkNotNull(factoryModelDir);
+            mModelFilenamePattern = Pattern.compile(
+                    Preconditions.checkNotNull(factoryModelFileNameRegex));
+            mVersionSupplier = Preconditions.checkNotNull(versionSupplier);
+            mSupportedLocalesSupplier = Preconditions.checkNotNull(supportedLocalesSupplier);
+        }
+
+        @Override
+        public List<ModelFile> get() {
+            final List<ModelFile> modelFiles = new ArrayList<>();
+            // The update model has the highest precedence.
+            if (mUpdatedModelFile.exists()) {
+                final ModelFile updatedModel = createModelFile(mUpdatedModelFile);
+                if (updatedModel != null) {
+                    modelFiles.add(updatedModel);
+                }
+            }
+            // Factory models should never have overlapping locales, so the order doesn't matter.
+            if (mFactoryModelDir.exists() && mFactoryModelDir.isDirectory()) {
+                final File[] files = mFactoryModelDir.listFiles();
+                for (File file : files) {
+                    final Matcher matcher = mModelFilenamePattern.matcher(file.getName());
+                    if (matcher.matches() && file.isFile()) {
+                        final ModelFile model = createModelFile(file);
+                        if (model != null) {
+                            modelFiles.add(model);
+                        }
+                    }
+                }
+            }
+            return modelFiles;
+        }
+
+        /** Returns null if the path did not point to a compatible model. */
+        @Nullable
+        private ModelFile createModelFile(File file) {
+            if (!file.exists()) {
+                return null;
+            }
+            ParcelFileDescriptor modelFd = null;
+            try {
+                modelFd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+                if (modelFd == null) {
+                    return null;
+                }
+                final int modelFdInt = modelFd.getFd();
+                final int version = mVersionSupplier.apply(modelFdInt);
+                final String supportedLocalesStr = mSupportedLocalesSupplier.apply(modelFdInt);
+                if (supportedLocalesStr.isEmpty()) {
+                    Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
+                    return null;
+                }
+                final List<Locale> supportedLocales = new ArrayList<>();
+                for (String langTag : supportedLocalesStr.split(",")) {
+                    supportedLocales.add(Locale.forLanguageTag(langTag));
+                }
+                return new ModelFile(
+                        file,
+                        version,
+                        supportedLocales,
+                        ModelFile.LANGUAGE_INDEPENDENT.equals(supportedLocalesStr));
+            } catch (FileNotFoundException e) {
+                Log.e(DEFAULT_LOG_TAG, "Failed to find " + file.getAbsolutePath(), e);
+                return null;
+            } finally {
+                maybeCloseAndLogError(modelFd);
+            }
+        }
+
+        /**
+         * Closes the ParcelFileDescriptor, if non-null, and logs any errors that occur.
+         */
+        private static void maybeCloseAndLogError(@Nullable ParcelFileDescriptor fd) {
+            if (fd == null) {
+                return;
+            }
+            try {
+                fd.close();
+            } catch (IOException e) {
+                Log.e(DEFAULT_LOG_TAG, "Error closing file.", e);
+            }
+        }
+
+    }
+
+    /**
+     * Describes TextClassifier model files on disk.
+     */
+    public static final class ModelFile {
+        public static final String LANGUAGE_INDEPENDENT = "*";
+
+        private final File mFile;
+        private final int mVersion;
+        private final List<Locale> mSupportedLocales;
+        private final boolean mLanguageIndependent;
+
+        public ModelFile(File file, int version, List<Locale> supportedLocales,
+                boolean languageIndependent) {
+            mFile = Preconditions.checkNotNull(file);
+            mVersion = version;
+            mSupportedLocales = Preconditions.checkNotNull(supportedLocales);
+            mLanguageIndependent = languageIndependent;
+        }
+
+        /** Returns the absolute path to the model file. */
+        public String getPath() {
+            return mFile.getAbsolutePath();
+        }
+
+        /** Returns a name to use for id generation, effectively the name of the model file. */
+        public String getName() {
+            return mFile.getName();
+        }
+
+        /** Returns the version tag in the model's metadata. */
+        public int getVersion() {
+            return mVersion;
+        }
+
+        /** Returns whether the language supports any language in the given ranges. */
+        public boolean isAnyLanguageSupported(List<Locale.LanguageRange> languageRanges) {
+            Preconditions.checkNotNull(languageRanges);
+            return mLanguageIndependent || Locale.lookup(languageRanges, mSupportedLocales) != null;
+        }
+
+        /** Returns an immutable lists of supported locales. */
+        public List<Locale> getSupportedLocales() {
+            return Collections.unmodifiableList(mSupportedLocales);
+        }
+
+        /**
+         * Returns if this model file is preferred to the given one.
+         */
+        public boolean isPreferredTo(@Nullable ModelFile model) {
+            // A model is preferred to no model.
+            if (model == null) {
+                return true;
+            }
+
+            // A language-specific model is preferred to a language independent
+            // model.
+            if (!mLanguageIndependent && model.mLanguageIndependent) {
+                return true;
+            }
+
+            // A higher-version model is preferred.
+            if (mVersion > model.getVersion()) {
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(getPath());
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (other instanceof ModelFile) {
+                final ModelFile otherModel = (ModelFile) other;
+                return TextUtils.equals(getPath(), otherModel.getPath());
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            final StringJoiner localesJoiner = new StringJoiner(",");
+            for (Locale locale : mSupportedLocales) {
+                localesJoiner.add(locale.toLanguageTag());
+            }
+            return String.format(Locale.US,
+                    "ModelFile { path=%s name=%s version=%d locales=%s }",
+                    getPath(), getName(), mVersion, localesJoiner.toString());
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 9511a9e..f6c3d77 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -33,6 +33,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -126,6 +127,7 @@
     @NonNull private final List<RemoteAction> mActions;
     @NonNull private final EntityConfidence mEntityConfidence;
     @Nullable private final String mId;
+    @NonNull private final Bundle mExtras;
 
     private TextClassification(
             @Nullable String text,
@@ -135,7 +137,8 @@
             @Nullable OnClickListener legacyOnClickListener,
             @NonNull List<RemoteAction> actions,
             @NonNull Map<String, Float> entityConfidence,
-            @Nullable String id) {
+            @Nullable String id,
+            @NonNull Bundle extras) {
         mText = text;
         mLegacyIcon = legacyIcon;
         mLegacyLabel = legacyLabel;
@@ -144,6 +147,7 @@
         mActions = Collections.unmodifiableList(actions);
         mEntityConfidence = new EntityConfidence(entityConfidence);
         mId = id;
+        mExtras = extras;
     }
 
     /**
@@ -255,6 +259,18 @@
         return mId;
     }
 
+    /**
+     * Returns the extended data.
+     *
+     * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+     * prefer to hold a reference to the returned bundle rather than frequently calling this
+     * method.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras.deepCopy();
+    }
+
     @Override
     public String toString() {
         return String.format(Locale.US,
@@ -359,6 +375,7 @@
         @Nullable private Intent mLegacyIntent;
         @Nullable private OnClickListener mLegacyOnClickListener;
         @Nullable private String mId;
+        @Nullable private Bundle mExtras;
 
         /**
          * Sets the classified text.
@@ -471,12 +488,22 @@
         }
 
         /**
+         * Sets the extended data.
+         */
+        @NonNull
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
          * Builds and returns a {@link TextClassification} object.
          */
         @NonNull
         public TextClassification build() {
             return new TextClassification(mText, mLegacyIcon, mLegacyLabel, mLegacyIntent,
-                    mLegacyOnClickListener, mActions, mEntityConfidence, mId);
+                    mLegacyOnClickListener, mActions, mEntityConfidence, mId,
+                    mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
         }
     }
 
@@ -490,18 +517,21 @@
         private final int mEndIndex;
         @Nullable private final LocaleList mDefaultLocales;
         @Nullable private final ZonedDateTime mReferenceTime;
+        @NonNull private final Bundle mExtras;
 
         private Request(
                 CharSequence text,
                 int startIndex,
                 int endIndex,
                 LocaleList defaultLocales,
-                ZonedDateTime referenceTime) {
+                ZonedDateTime referenceTime,
+                Bundle extras) {
             mText = text;
             mStartIndex = startIndex;
             mEndIndex = endIndex;
             mDefaultLocales = defaultLocales;
             mReferenceTime = referenceTime;
+            mExtras = extras;
         }
 
         /**
@@ -548,6 +578,18 @@
         }
 
         /**
+         * Returns the extended data.
+         *
+         * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+         * prefer to hold a reference to the returned bundle rather than frequently calling this
+         * method.
+         */
+        @NonNull
+        public Bundle getExtras() {
+            return mExtras.deepCopy();
+        }
+
+        /**
          * A builder for building TextClassification requests.
          */
         public static final class Builder {
@@ -555,6 +597,7 @@
             private final CharSequence mText;
             private final int mStartIndex;
             private final int mEndIndex;
+            private Bundle mExtras;
 
             @Nullable private LocaleList mDefaultLocales;
             @Nullable private ZonedDateTime mReferenceTime;
@@ -602,11 +645,23 @@
             }
 
             /**
+             * Sets the extended data.
+             *
+             * @return this builder
+             */
+            @NonNull
+            public Builder setExtras(@Nullable Bundle extras) {
+                mExtras = extras;
+                return this;
+            }
+
+            /**
              * Builds and returns the request object.
              */
             @NonNull
             public Request build() {
-                return new Request(mText, mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime);
+                return new Request(mText, mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime,
+                        mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
             }
         }
 
@@ -628,6 +683,7 @@
             if (mReferenceTime != null) {
                 dest.writeString(mReferenceTime.toString());
             }
+            dest.writeBundle(mExtras);
         }
 
         public static final Parcelable.Creator<Request> CREATOR =
@@ -649,6 +705,7 @@
             mEndIndex = in.readInt();
             mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
             mReferenceTime = in.readInt() == 0 ? null : ZonedDateTime.parse(in.readString());
+            mExtras = in.readBundle();
         }
     }
 
@@ -664,6 +721,7 @@
         dest.writeTypedList(mActions);
         mEntityConfidence.writeToParcel(dest, flags);
         dest.writeString(mId);
+        dest.writeBundle(mExtras);
     }
 
     public static final Parcelable.Creator<TextClassification> CREATOR =
@@ -695,6 +753,7 @@
         mLegacyIntent = null; // mLegacyIntent is not parcelled.
         mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
         mId = in.readString();
+        mExtras = in.readBundle();
     }
 
     // Best effort attempt to try to load a drawable from the provided icon.
@@ -717,67 +776,4 @@
         }
         return null;
     }
-
-    // TODO: Remove once apps can build against the latest sdk.
-    /**
-     * Optional input parameters for generating TextClassification.
-     * @hide
-     */
-    public static final class Options {
-
-        @Nullable private final TextClassificationSessionId mSessionId;
-        @Nullable private final Request mRequest;
-        @Nullable private LocaleList mDefaultLocales;
-        @Nullable private ZonedDateTime mReferenceTime;
-
-        public Options() {
-            this(null, null);
-        }
-
-        private Options(
-                @Nullable TextClassificationSessionId sessionId, @Nullable Request request) {
-            mSessionId = sessionId;
-            mRequest = request;
-        }
-
-        /** Helper to create Options from a Request. */
-        public static Options from(TextClassificationSessionId sessionId, Request request) {
-            final Options options = new Options(sessionId, request);
-            options.setDefaultLocales(request.getDefaultLocales());
-            options.setReferenceTime(request.getReferenceTime());
-            return options;
-        }
-
-        /** @param defaultLocales ordered list of locale preferences. */
-        public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
-            mDefaultLocales = defaultLocales;
-            return this;
-        }
-
-        /** @param referenceTime refrence time used for interpreting relatives dates */
-        public Options setReferenceTime(@Nullable ZonedDateTime referenceTime) {
-            mReferenceTime = referenceTime;
-            return this;
-        }
-
-        @Nullable
-        public LocaleList getDefaultLocales() {
-            return mDefaultLocales;
-        }
-
-        @Nullable
-        public ZonedDateTime getReferenceTime() {
-            return mReferenceTime;
-        }
-
-        @Nullable
-        public Request getRequest() {
-            return mRequest;
-        }
-
-        @Nullable
-        public TextClassificationSessionId getSessionId() {
-            return mSessionId;
-        }
-    }
 }
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 2e92f14..e675744 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -325,6 +325,17 @@
     }
 
     /**
+     * Suggests and returns a list of actions according to the given conversation.
+     */
+    @WorkerThread
+    default ConversationActions suggestConversationActions(
+            @NonNull ConversationActions.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
+        return new ConversationActions(Collections.emptyList());
+    }
+
+    /**
      * Reports a selection event.
      *
      * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 7f1e443..159bfaa 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -58,16 +58,12 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
-import java.util.StringJoiner;
 import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * Default implementation of the {@link TextClassifier} interface.
@@ -81,13 +77,18 @@
 public final class TextClassifierImpl implements TextClassifier {
 
     private static final String LOG_TAG = DEFAULT_LOG_TAG;
-    private static final String MODEL_DIR = "/etc/textclassifier/";
-    private static final String MODEL_FILE_REGEX = "textclassifier\\.(.*)\\.model";
-    private static final String UPDATED_MODEL_FILE_PATH =
-            "/data/misc/textclassifier/textclassifier.model";
-    private static final String LANG_ID_MODEL_FILE_PATH = "/etc/textclassifier/lang_id.model";
-    private static final String UPDATED_LANG_ID_MODEL_FILE_PATH =
-            "/data/misc/textclassifier/lang_id.model";
+
+    private static final File FACTORY_MODEL_DIR = new File("/etc/textclassifier/");
+    // Annotator
+    private static final String ANNOTATOR_FACTORY_MODEL_FILENAME_REGEX =
+            "textclassifier\\.(.*)\\.model";
+    private static final File ANNOTATOR_UPDATED_MODEL_FILE =
+            new File("/data/misc/textclassifier/textclassifier.model");
+
+    // LangID
+    private static final String LANG_ID_FACTORY_MODEL_FILENAME_REGEX = "lang_id.model";
+    private static final File UPDATED_LANG_ID_MODEL_FILE =
+            new File("/data/misc/textclassifier/lang_id.model");
 
     private final Context mContext;
     private final TextClassifier mFallback;
@@ -95,9 +96,7 @@
 
     private final Object mLock = new Object();
     @GuardedBy("mLock") // Do not access outside this lock.
-    private List<ModelFile> mAllModelFiles;
-    @GuardedBy("mLock") // Do not access outside this lock.
-    private ModelFile mModel;
+    private ModelFileManager.ModelFile mAnnotatorModelInUse;
     @GuardedBy("mLock") // Do not access outside this lock.
     private AnnotatorModel mAnnotatorImpl;
     @GuardedBy("mLock") // Do not access outside this lock.
@@ -109,12 +108,29 @@
 
     private final TextClassificationConstants mSettings;
 
+    private final ModelFileManager mAnnotatorModelFileManager;
+    private final ModelFileManager mLangIdModelFileManager;
+
     public TextClassifierImpl(
             Context context, TextClassificationConstants settings, TextClassifier fallback) {
         mContext = Preconditions.checkNotNull(context);
         mFallback = Preconditions.checkNotNull(fallback);
         mSettings = Preconditions.checkNotNull(settings);
         mGenerateLinksLogger = new GenerateLinksLogger(mSettings.getGenerateLinksLogSampleRate());
+        mAnnotatorModelFileManager = new ModelFileManager(
+                new ModelFileManager.ModelFileSupplierImpl(
+                        FACTORY_MODEL_DIR,
+                        ANNOTATOR_FACTORY_MODEL_FILENAME_REGEX,
+                        ANNOTATOR_UPDATED_MODEL_FILE,
+                        AnnotatorModel::getVersion,
+                        AnnotatorModel::getLocales));
+        mLangIdModelFileManager = new ModelFileManager(
+                new ModelFileManager.ModelFileSupplierImpl(
+                        FACTORY_MODEL_DIR,
+                        LANG_ID_FACTORY_MODEL_FILENAME_REGEX,
+                        UPDATED_LANG_ID_MODEL_FILE,
+                        fd -> -1, // TODO: Replace this with LangIdModel.getVersion(fd)
+                        fd -> ModelFileManager.ModelFile.LANGUAGE_INDEPENDENT));
     }
 
     public TextClassifierImpl(Context context, TextClassificationConstants settings) {
@@ -334,22 +350,24 @@
             throws FileNotFoundException {
         synchronized (mLock) {
             localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
-            final ModelFile bestModel = findBestModelLocked(localeList);
+            final ModelFileManager.ModelFile bestModel =
+                    mAnnotatorModelFileManager.findBestModelFile(localeList);
             if (bestModel == null) {
-                throw new FileNotFoundException("No model for " + localeList.toLanguageTags());
+                throw new FileNotFoundException(
+                        "No annotator model for " + localeList.toLanguageTags());
             }
-            if (mAnnotatorImpl == null || !Objects.equals(mModel, bestModel)) {
+            if (mAnnotatorImpl == null || !Objects.equals(mAnnotatorModelInUse, bestModel)) {
                 Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
                 destroyAnnotatorImplIfExistsLocked();
-                final ParcelFileDescriptor fd = ParcelFileDescriptor.open(
+                final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
                         new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
                 try {
-                    if (fd != null) {
-                        mAnnotatorImpl = new AnnotatorModel(fd.getFd());
-                        mModel = bestModel;
+                    if (pfd != null) {
+                        mAnnotatorImpl = new AnnotatorModel(pfd.getFd());
+                        mAnnotatorModelInUse = bestModel;
                     }
                 } finally {
-                    maybeCloseAndLogError(fd);
+                    maybeCloseAndLogError(pfd);
                 }
             }
             return mAnnotatorImpl;
@@ -367,40 +385,19 @@
     private LangIdModel getLangIdImpl() throws FileNotFoundException {
         synchronized (mLock) {
             if (mLangIdImpl == null) {
-                ParcelFileDescriptor factoryFd = null;
-                ParcelFileDescriptor updateFd = null;
+                final ModelFileManager.ModelFile bestModel =
+                        mLangIdModelFileManager.findBestModelFile(LocaleList.getEmptyLocaleList());
+                if (bestModel == null) {
+                    throw new FileNotFoundException("No LangID model is found");
+                }
+                final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
+                        new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
                 try {
-                    int factoryVersion = -1;
-                    int updateVersion = factoryVersion;
-                    final File factoryFile = new File(LANG_ID_MODEL_FILE_PATH);
-                    if (factoryFile.exists()) {
-                        factoryFd = ParcelFileDescriptor.open(
-                                factoryFile, ParcelFileDescriptor.MODE_READ_ONLY);
-                        // TODO: Uncomment when method is implemented:
-                        // if (factoryFd != null) {
-                        //     factoryVersion = LangIdModel.getVersion(factoryFd.getFd());
-                        // }
-                    }
-                    final File updateFile = new File(UPDATED_LANG_ID_MODEL_FILE_PATH);
-                    if (updateFile.exists()) {
-                        updateFd = ParcelFileDescriptor.open(
-                                updateFile, ParcelFileDescriptor.MODE_READ_ONLY);
-                        // TODO: Uncomment when method is implemented:
-                        // if (updateFd != null) {
-                        //     updateVersion = LangIdModel.getVersion(updateFd.getFd());
-                        // }
-                    }
-
-                    if (updateVersion > factoryVersion) {
-                        mLangIdImpl = new LangIdModel(updateFd.getFd());
-                    } else if (factoryFd != null) {
-                        mLangIdImpl = new LangIdModel(factoryFd.getFd());
-                    } else {
-                        throw new FileNotFoundException("Language detection model not found");
+                    if (pfd != null) {
+                        mLangIdImpl = new LangIdModel(pfd.getFd());
                     }
                 } finally {
-                    maybeCloseAndLogError(factoryFd);
-                    maybeCloseAndLogError(updateFd);
+                    maybeCloseAndLogError(pfd);
                 }
             }
             return mLangIdImpl;
@@ -409,8 +406,9 @@
 
     private String createId(String text, int start, int end) {
         synchronized (mLock) {
-            return SelectionSessionLogger.createId(text, start, end, mContext, mModel.getVersion(),
-                    mModel.getSupportedLocales());
+            return SelectionSessionLogger.createId(text, start, end, mContext,
+                    mAnnotatorModelInUse.getVersion(),
+                    mAnnotatorModelInUse.getSupportedLocales());
         }
     }
 
@@ -418,66 +416,6 @@
         return (locales == null) ? "" : locales.toLanguageTags();
     }
 
-    /**
-     * Finds the most appropriate model to use for the given target locale list.
-     *
-     * The basic logic is: we ignore all models that don't support any of the target locales. For
-     * the remaining candidates, we take the update model unless its version number is lower than
-     * the factory version. It's assumed that factory models do not have overlapping locale ranges
-     * and conflict resolution between these models hence doesn't matter.
-     */
-    @GuardedBy("mLock") // Do not call outside this lock.
-    @Nullable
-    private ModelFile findBestModelLocked(LocaleList localeList) {
-        // Specified localeList takes priority over the system default, so it is listed first.
-        final String languages = localeList.isEmpty()
-                ? LocaleList.getDefault().toLanguageTags()
-                : localeList.toLanguageTags() + "," + LocaleList.getDefault().toLanguageTags();
-        final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages);
-
-        ModelFile bestModel = null;
-        for (ModelFile model : listAllModelsLocked()) {
-            if (model.isAnyLanguageSupported(languageRangeList)) {
-                if (model.isPreferredTo(bestModel)) {
-                    bestModel = model;
-                }
-            }
-        }
-        return bestModel;
-    }
-
-    /** Returns a list of all model files available, in order of precedence. */
-    @GuardedBy("mLock") // Do not call outside this lock.
-    private List<ModelFile> listAllModelsLocked() {
-        if (mAllModelFiles == null) {
-            final List<ModelFile> allModels = new ArrayList<>();
-            // The update model has the highest precedence.
-            if (new File(UPDATED_MODEL_FILE_PATH).exists()) {
-                final ModelFile updatedModel = ModelFile.fromPath(UPDATED_MODEL_FILE_PATH);
-                if (updatedModel != null) {
-                    allModels.add(updatedModel);
-                }
-            }
-            // Factory models should never have overlapping locales, so the order doesn't matter.
-            final File modelsDir = new File(MODEL_DIR);
-            if (modelsDir.exists() && modelsDir.isDirectory()) {
-                final File[] modelFiles = modelsDir.listFiles();
-                final Pattern modelFilenamePattern = Pattern.compile(MODEL_FILE_REGEX);
-                for (File modelFile : modelFiles) {
-                    final Matcher matcher = modelFilenamePattern.matcher(modelFile.getName());
-                    if (matcher.matches() && modelFile.isFile()) {
-                        final ModelFile model = ModelFile.fromPath(modelFile.getAbsolutePath());
-                        if (model != null) {
-                            allModels.add(model);
-                        }
-                    }
-                }
-            }
-            mAllModelFiles = allModels;
-        }
-        return mAllModelFiles;
-    }
-
     private TextClassification createClassificationResult(
             AnnotatorModel.ClassificationResult[] classifications,
             String text, int start, int end, @Nullable Instant referenceTime) {
@@ -523,12 +461,18 @@
     @Override
     public void dump(@NonNull IndentingPrintWriter printWriter) {
         synchronized (mLock) {
-            listAllModelsLocked();
             printWriter.println("TextClassifierImpl:");
             printWriter.increaseIndent();
-            printWriter.println("Model file(s):");
+            printWriter.println("Annotator model file(s):");
             printWriter.increaseIndent();
-            for (ModelFile modelFile : mAllModelFiles) {
+            for (ModelFileManager.ModelFile modelFile :
+                    mAnnotatorModelFileManager.listModelFiles()) {
+                printWriter.println(modelFile.toString());
+            }
+            printWriter.decreaseIndent();
+            printWriter.println("LangID model file(s):");
+            for (ModelFileManager.ModelFile modelFile :
+                    mLangIdModelFileManager.listModelFiles()) {
                 printWriter.println(modelFile.toString());
             }
             printWriter.decreaseIndent();
@@ -554,126 +498,6 @@
     }
 
     /**
-     * Describes TextClassifier model files on disk.
-     */
-    private static final class ModelFile {
-
-        private final String mPath;
-        private final String mName;
-        private final int mVersion;
-        private final List<Locale> mSupportedLocales;
-        private final boolean mLanguageIndependent;
-
-        /** Returns null if the path did not point to a compatible model. */
-        static @Nullable ModelFile fromPath(String path) {
-            final File file = new File(path);
-            if (!file.exists()) {
-                return null;
-            }
-            ParcelFileDescriptor modelFd = null;
-            try {
-                modelFd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
-                if (modelFd == null) {
-                    return null;
-                }
-                final int version = AnnotatorModel.getVersion(modelFd.getFd());
-                final String supportedLocalesStr = AnnotatorModel.getLocales(modelFd.getFd());
-                if (supportedLocalesStr.isEmpty()) {
-                    Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
-                    return null;
-                }
-                final boolean languageIndependent = supportedLocalesStr.equals("*");
-                final List<Locale> supportedLocales = new ArrayList<>();
-                for (String langTag : supportedLocalesStr.split(",")) {
-                    supportedLocales.add(Locale.forLanguageTag(langTag));
-                }
-                return new ModelFile(path, file.getName(), version, supportedLocales,
-                                     languageIndependent);
-            } catch (FileNotFoundException e) {
-                Log.e(DEFAULT_LOG_TAG, "Failed to peek " + file.getAbsolutePath(), e);
-                return null;
-            } finally {
-                maybeCloseAndLogError(modelFd);
-            }
-        }
-
-        /** The absolute path to the model file. */
-        String getPath() {
-            return mPath;
-        }
-
-        /** A name to use for id generation. Effectively the name of the model file. */
-        String getName() {
-            return mName;
-        }
-
-        /** Returns the version tag in the model's metadata. */
-        int getVersion() {
-            return mVersion;
-        }
-
-        /** Returns whether the language supports any language in the given ranges. */
-        boolean isAnyLanguageSupported(List<Locale.LanguageRange> languageRanges) {
-            return mLanguageIndependent || Locale.lookup(languageRanges, mSupportedLocales) != null;
-        }
-
-        /** All locales supported by the model. */
-        List<Locale> getSupportedLocales() {
-            return Collections.unmodifiableList(mSupportedLocales);
-        }
-
-        public boolean isPreferredTo(ModelFile model) {
-            // A model is preferred to no model.
-            if (model == null) {
-                return true;
-            }
-
-            // A language-specific model is preferred to a language independent
-            // model.
-            if (!mLanguageIndependent && model.mLanguageIndependent) {
-                return true;
-            }
-
-            // A higher-version model is preferred.
-            if (getVersion() > model.getVersion()) {
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            if (this == other) {
-                return true;
-            }
-            if (other instanceof ModelFile) {
-                final ModelFile otherModel = (ModelFile) other;
-                return mPath.equals(otherModel.mPath);
-            }
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            final StringJoiner localesJoiner = new StringJoiner(",");
-            for (Locale locale : mSupportedLocales) {
-                localesJoiner.add(locale.toLanguageTag());
-            }
-            return String.format(Locale.US, "ModelFile { path=%s name=%s version=%d locales=%s }",
-                    mPath, mName, mVersion, localesJoiner.toString());
-        }
-
-        private ModelFile(String path, String name, int version, List<Locale> supportedLocales,
-                          boolean languageIndependent) {
-            mPath = path;
-            mName = name;
-            mVersion = version;
-            mSupportedLocales = supportedLocales;
-            mLanguageIndependent = languageIndependent;
-        }
-    }
-
-    /**
      * Helper class to store the information from which RemoteActions are built.
      */
     private static final class LabeledIntent {
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 1cac3ed..b31438f 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -20,7 +20,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.LocaleList;
 import android.os.Parcel;
@@ -29,8 +28,6 @@
 import android.text.method.MovementMethod;
 import android.text.style.ClickableSpan;
 import android.text.style.URLSpan;
-import android.text.util.Linkify;
-import android.text.util.Linkify.LinkifyMask;
 import android.view.View;
 import android.view.textclassifier.TextClassifier.EntityType;
 import android.widget.TextView;
@@ -634,125 +631,4 @@
             return new TextLinks(mFullText, mLinks);
         }
     }
-
-    // TODO: Remove once apps can build against the latest sdk.
-    /**
-     * Optional input parameters for generating TextLinks.
-     * @hide
-     */
-    public static final class Options {
-
-        @Nullable private final TextClassificationSessionId mSessionId;
-        @Nullable private final Request mRequest;
-        @Nullable private LocaleList mDefaultLocales;
-        @Nullable private TextClassifier.EntityConfig mEntityConfig;
-        private boolean mLegacyFallback;
-
-        private @ApplyStrategy int mApplyStrategy;
-        private Function<TextLink, TextLinkSpan> mSpanFactory;
-
-        private String mCallingPackageName;
-
-        @UnsupportedAppUsage
-        public Options() {
-            this(null, null);
-        }
-
-        private Options(
-                @Nullable TextClassificationSessionId sessionId, @Nullable Request request) {
-            mSessionId = sessionId;
-            mRequest = request;
-        }
-
-        /** Helper to create Options from a Request. */
-        public static Options from(TextClassificationSessionId sessionId, Request request) {
-            final Options options = new Options(sessionId, request);
-            options.setDefaultLocales(request.getDefaultLocales());
-            options.setEntityConfig(request.getEntityConfig());
-            return options;
-        }
-
-        /** Returns a new options object based on the specified link mask. */
-        public static Options fromLinkMask(@LinkifyMask int mask) {
-            final List<String> entitiesToFind = new ArrayList<>();
-
-            if ((mask & Linkify.WEB_URLS) != 0) {
-                entitiesToFind.add(TextClassifier.TYPE_URL);
-            }
-            if ((mask & Linkify.EMAIL_ADDRESSES) != 0) {
-                entitiesToFind.add(TextClassifier.TYPE_EMAIL);
-            }
-            if ((mask & Linkify.PHONE_NUMBERS) != 0) {
-                entitiesToFind.add(TextClassifier.TYPE_PHONE);
-            }
-            if ((mask & Linkify.MAP_ADDRESSES) != 0) {
-                entitiesToFind.add(TextClassifier.TYPE_ADDRESS);
-            }
-
-            return new Options().setEntityConfig(
-                    TextClassifier.EntityConfig.createWithEntityList(entitiesToFind));
-        }
-
-        /** @param defaultLocales ordered list of locale preferences. */
-        public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
-            mDefaultLocales = defaultLocales;
-            return this;
-        }
-
-        /** @param entityConfig definition of which entity types to look for. */
-        public Options setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) {
-            mEntityConfig = entityConfig;
-            return this;
-        }
-
-        /** @param applyStrategy strategy to use when resolving conflicts. */
-        public Options setApplyStrategy(@ApplyStrategy int applyStrategy) {
-            checkValidApplyStrategy(applyStrategy);
-            mApplyStrategy = applyStrategy;
-            return this;
-        }
-
-        /** @param spanFactory factory for converting TextLink to TextLinkSpan. */
-        public Options setSpanFactory(@Nullable Function<TextLink, TextLinkSpan> spanFactory) {
-            mSpanFactory = spanFactory;
-            return this;
-        }
-
-        @Nullable
-        public LocaleList getDefaultLocales() {
-            return mDefaultLocales;
-        }
-
-        @Nullable
-        public TextClassifier.EntityConfig getEntityConfig() {
-            return mEntityConfig;
-        }
-
-        @ApplyStrategy
-        public int getApplyStrategy() {
-            return mApplyStrategy;
-        }
-
-        @Nullable
-        public Function<TextLink, TextLinkSpan> getSpanFactory() {
-            return mSpanFactory;
-        }
-
-        @Nullable
-        public Request getRequest() {
-            return mRequest;
-        }
-
-        @Nullable
-        public TextClassificationSessionId getSessionId() {
-            return mSessionId;
-        }
-
-        private static void checkValidApplyStrategy(int applyStrategy) {
-            if (applyStrategy != APPLY_STRATEGY_IGNORE && applyStrategy != APPLY_STRATEGY_REPLACE) {
-                throw new IllegalArgumentException(
-                        "Invalid apply strategy. See TextLinks.ApplyStrategy for options.");
-            }
-        }
-    }
 }
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index a4550af..52d01ea 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -375,56 +375,4 @@
         mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
         mId = in.readString();
     }
-
-
-    // TODO: Remove once apps can build against the latest sdk.
-    /**
-     * Optional input parameters for generating TextSelection.
-     * @hide
-     */
-    public static final class Options {
-
-        @Nullable private final TextClassificationSessionId mSessionId;
-        @Nullable private final Request mRequest;
-        @Nullable private LocaleList mDefaultLocales;
-        private boolean mDarkLaunchAllowed;
-
-        public Options() {
-            this(null, null);
-        }
-
-        private Options(
-                @Nullable TextClassificationSessionId sessionId, @Nullable Request request) {
-            mSessionId = sessionId;
-            mRequest = request;
-        }
-
-        /** Helper to create Options from a Request. */
-        public static Options from(TextClassificationSessionId sessionId, Request request) {
-            final Options options = new Options(sessionId, request);
-            options.setDefaultLocales(request.getDefaultLocales());
-            return options;
-        }
-
-        /** @param defaultLocales ordered list of locale preferences. */
-        public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
-            mDefaultLocales = defaultLocales;
-            return this;
-        }
-
-        @Nullable
-        public LocaleList getDefaultLocales() {
-            return mDefaultLocales;
-        }
-
-        @Nullable
-        public Request getRequest() {
-            return mRequest;
-        }
-
-        @Nullable
-        public TextClassificationSessionId getSessionId() {
-            return mSessionId;
-        }
-    }
 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 48c164f..5b1544b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -48,6 +48,7 @@
 import android.graphics.RenderNode;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
@@ -257,7 +258,7 @@
             needsToBeShifted = true;
         }
         boolean needsRecord() {
-            return isDirty || !renderNode.isValid();
+            return isDirty || !renderNode.hasDisplayList();
         }
     }
     private TextRenderNode[] mTextRenderNodes;
@@ -310,12 +311,12 @@
 
     Drawable mDrawableForCursor = null;
 
-    @UnsupportedAppUsage
-    private Drawable mSelectHandleLeft;
-    @UnsupportedAppUsage
-    private Drawable mSelectHandleRight;
-    @UnsupportedAppUsage
-    private Drawable mSelectHandleCenter;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    Drawable mSelectHandleLeft;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    Drawable mSelectHandleRight;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    Drawable mSelectHandleCenter;
 
     // Global listener that detects changes in the global position of the TextView
     private PositionListener mPositionListener;
@@ -542,7 +543,7 @@
             for (int i = 0; i < mTextRenderNodes.length; i++) {
                 RenderNode displayList = mTextRenderNodes[i] != null
                         ? mTextRenderNodes[i].renderNode : null;
-                if (displayList != null && displayList.isValid()) {
+                if (displayList != null && displayList.hasDisplayList()) {
                     displayList.discardDisplayList();
                 }
             }
@@ -3927,7 +3928,7 @@
                 SelectionModifierCursorController selectionController = getSelectionController();
                 if (selectionController.mStartHandle == null) {
                     // As these are for initializing selectionController, hide() must be called.
-                    selectionController.initDrawables();
+                    loadHandleDrawables(false /* overwrite */);
                     selectionController.initHandles();
                     selectionController.hide();
                 }
@@ -4495,13 +4496,11 @@
             mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
             mContainer.setContentView(this);
 
-            mDrawableLtr = drawableLtr;
-            mDrawableRtl = drawableRtl;
+            setDrawables(drawableLtr, drawableRtl);
+
             mMinSize = mTextView.getContext().getResources().getDimensionPixelSize(
                     com.android.internal.R.dimen.text_handle_min_size);
 
-            updateDrawable();
-
             final int handleHeight = getPreferredHeight();
             mTouchOffsetY = -0.3f * handleHeight;
             mIdealVerticalOffset = 0.7f * handleHeight;
@@ -4511,9 +4510,14 @@
             return mIdealVerticalOffset;
         }
 
-        protected void updateDrawable() {
-            if (mIsDragging) {
-                // Don't update drawable during dragging.
+        void setDrawables(final Drawable drawableLtr, final Drawable drawableRtl) {
+            mDrawableLtr = drawableLtr;
+            mDrawableRtl = drawableRtl;
+            updateDrawable(true /* updateDrawableWhenDragging */);
+        }
+
+        protected void updateDrawable(final boolean updateDrawableWhenDragging) {
+            if (!updateDrawableWhenDragging && mIsDragging) {
                 return;
             }
             final Layout layout = mTextView.getLayout();
@@ -5030,7 +5034,7 @@
                     // Fall through.
                 case MotionEvent.ACTION_CANCEL:
                     mIsDragging = false;
-                    updateDrawable();
+                    updateDrawable(false /* updateDrawableWhenDragging */);
                     break;
             }
             return true;
@@ -5315,7 +5319,7 @@
                 Selection.setSelection((Spannable) mTextView.getText(),
                         mTextView.getSelectionStart(), offset);
             }
-            updateDrawable();
+            updateDrawable(false /* updateDrawableWhenDragging */);
             if (mTextActionMode != null) {
                 invalidateActionMode();
             }
@@ -5717,16 +5721,22 @@
         }
 
         private InsertionHandleView getHandle() {
-            if (mSelectHandleCenter == null) {
-                mSelectHandleCenter = mTextView.getContext().getDrawable(
-                        mTextView.mTextSelectHandleRes);
-            }
             if (mHandle == null) {
+                loadHandleDrawables(false /* overwrite */);
                 mHandle = new InsertionHandleView(mSelectHandleCenter);
             }
             return mHandle;
         }
 
+        private void reloadHandleDrawable() {
+            if (mHandle == null) {
+                // No need to reload, the potentially new drawable will
+                // be used when the handle is created.
+                return;
+            }
+            mHandle.setDrawables(mSelectHandleCenter, mSelectHandleCenter);
+        }
+
         @Override
         public void onDetached() {
             final ViewTreeObserver observer = mTextView.getViewTreeObserver();
@@ -5790,21 +5800,10 @@
             if (mTextView.isInBatchEditMode()) {
                 return;
             }
-            initDrawables();
+            loadHandleDrawables(false /* overwrite */);
             initHandles();
         }
 
-        private void initDrawables() {
-            if (mSelectHandleLeft == null) {
-                mSelectHandleLeft = mTextView.getContext().getDrawable(
-                        mTextView.mTextSelectHandleLeftRes);
-            }
-            if (mSelectHandleRight == null) {
-                mSelectHandleRight = mTextView.getContext().getDrawable(
-                        mTextView.mTextSelectHandleRightRes);
-            }
-        }
-
         private void initHandles() {
             // Lazy object creation has to be done before updatePosition() is called.
             if (mStartHandle == null) {
@@ -5824,6 +5823,16 @@
             hideInsertionPointCursorController();
         }
 
+        private void reloadHandleDrawables() {
+            if (mStartHandle == null) {
+                // No need to reload, the potentially new drawables will
+                // be used when the handles are created.
+                return;
+            }
+            mStartHandle.setDrawables(mSelectHandleLeft, mSelectHandleRight);
+            mEndHandle.setDrawables(mSelectHandleRight, mSelectHandleLeft);
+        }
+
         public void hide() {
             if (mStartHandle != null) mStartHandle.hide();
             if (mEndHandle != null) mEndHandle.hide();
@@ -6184,6 +6193,32 @@
         }
     }
 
+    /**
+     * Loads the insertion and selection handle Drawables from TextView. If the handle
+     * drawables are already loaded, do not overwrite them unless the method parameter
+     * is set to true. This logic is required to avoid overwriting Drawables assigned
+     * to mSelectHandle[Center/Left/Right] by developers using reflection, unless they
+     * explicitly call the setters in TextView.
+     *
+     * @param overwrite whether to overwrite already existing nonnull Drawables
+     */
+    void loadHandleDrawables(final boolean overwrite) {
+        if (mSelectHandleCenter == null || overwrite) {
+            mSelectHandleCenter = mTextView.getTextSelectHandle();
+            if (hasInsertionController()) {
+                getInsertionController().reloadHandleDrawable();
+            }
+        }
+
+        if (mSelectHandleLeft == null || mSelectHandleRight == null || overwrite) {
+            mSelectHandleLeft = mTextView.getTextSelectHandleLeft();
+            mSelectHandleRight = mTextView.getTextSelectHandleRight();
+            if (hasSelectionController()) {
+                getSelectionController().reloadHandleDrawables();
+            }
+        }
+    }
+
     private class CorrectionHighlighter {
         private final Path mPath = new Path();
         private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 6a3fc0f..9da2a43 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -28,6 +28,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Color;
+import android.graphics.Insets;
 import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -568,7 +569,7 @@
     private Point getCurrentClampedWindowCoordinates() {
         final Rect windowBounds;
         if (mParentSurface.mIsMainWindowSurface) {
-            final Rect systemInsets = mView.getRootWindowInsets().getSystemWindowInsets();
+            final Insets systemInsets = mView.getRootWindowInsets().getSystemWindowInsets();
             windowBounds = new Rect(systemInsets.left, systemInsets.top,
                     mParentSurface.mWidth - systemInsets.right,
                     mParentSurface.mHeight - systemInsets.bottom);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 4d03123..a93604f 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.StyleRes;
 import android.annotation.UnsupportedAppUsage;
+import android.app.Activity;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.Application;
@@ -56,13 +57,14 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.IntArray;
 import android.util.Log;
+import android.util.Pair;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.LayoutInflater.Filter;
 import android.view.RemotableViewMethod;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.AdapterView.OnItemClickListener;
@@ -129,13 +131,19 @@
     static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
 
     /**
+     * The intent extra that contains the bounds for all shared elements.
+     */
+    public static final String EXTRA_SHARED_ELEMENT_BOUNDS =
+            "android.widget.extra.SHARED_ELEMENT_BOUNDS";
+
+    /**
      * Maximum depth of nested views calls from {@link #addView(int, RemoteViews)} and
      * {@link #RemoteViews(RemoteViews, RemoteViews)}.
      */
     private static final int MAX_NESTED_VIEWS = 10;
 
     // The unique identifiers for each custom {@link Action}.
-    private static final int SET_ON_CLICK_PENDING_INTENT_TAG = 1;
+    private static final int SET_ON_CLICK_RESPONSE_TAG = 1;
     private static final int REFLECTION_ACTION_TAG = 2;
     private static final int SET_DRAWABLE_TINT_TAG = 3;
     private static final int VIEW_GROUP_ACTION_ADD_TAG = 4;
@@ -143,7 +151,6 @@
     private static final int SET_EMPTY_VIEW_ACTION_TAG = 6;
     private static final int VIEW_GROUP_ACTION_REMOVE_TAG = 7;
     private static final int SET_PENDING_INTENT_TEMPLATE_TAG = 8;
-    private static final int SET_ON_CLICK_FILL_IN_INTENT_TAG = 9;
     private static final int SET_REMOTE_VIEW_ADAPTER_INTENT_TAG = 10;
     private static final int TEXT_VIEW_DRAWABLE_ACTION_TAG = 11;
     private static final int BITMAP_REFLECTION_ACTION_TAG = 12;
@@ -228,7 +235,8 @@
     /** Class cookies of the Parcel this instance was read from. */
     private final Map<Class, Object> mClassCookies;
 
-    private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
+    private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = (view, pendingIntent, response)
+            -> startPendingIntent(view, pendingIntent, response.getLaunchOptions(view));
 
     private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
 
@@ -362,57 +370,10 @@
     }
 
     /** @hide */
-    public static class OnClickHandler {
-
-        @UnsupportedAppUsage
-        public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) {
-            try {
-                // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
-                Context context = view.getContext();
-                ActivityOptions opts = getActivityOptions(context);
-                // The NEW_TASK flags are applied through the activity options and not as a part of
-                // the call to startIntentSender() to ensure that they are consistently applied to
-                // both mutable and immutable PendingIntents.
-                context.startIntentSender(
-                        pendingIntent.getIntentSender(), fillInIntent,
-                        0, 0, 0, opts.toBundle());
-            } catch (IntentSender.SendIntentException e) {
-                android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
-                return false;
-            } catch (Exception e) {
-                android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " +
-                        "unknown exception: ", e);
-                return false;
-            }
-            return true;
-        }
+    public interface OnClickHandler {
 
         /** @hide */
-        protected ActivityOptions getActivityOptions(Context context) {
-            if (context.getResources().getBoolean(
-                    com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
-                TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
-                        com.android.internal.R.styleable.Window);
-                int windowAnimations = windowStyle.getResourceId(
-                        com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
-                TypedArray windowAnimationStyle = context.obtainStyledAttributes(
-                        windowAnimations, com.android.internal.R.styleable.WindowAnimation);
-                int enterAnimationId = windowAnimationStyle.getResourceId(com.android.internal.R
-                        .styleable.WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0);
-                windowStyle.recycle();
-                windowAnimationStyle.recycle();
-
-                if (enterAnimationId != 0) {
-                    final ActivityOptions opts = ActivityOptions.makeCustomAnimation(context,
-                            enterAnimationId, 0);
-                    opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    return opts;
-                }
-            }
-            final ActivityOptions opts = ActivityOptions.makeBasic();
-            opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            return opts;
-        }
+        boolean onClickHandler(View view, PendingIntent pendingIntent, RemoteResponse response);
     }
 
     /**
@@ -630,86 +591,6 @@
         }
     }
 
-    private class SetOnClickFillInIntent extends Action {
-        public SetOnClickFillInIntent(int id, Intent fillInIntent) {
-            this.viewId = id;
-            this.fillInIntent = fillInIntent;
-        }
-
-        public SetOnClickFillInIntent(Parcel parcel) {
-            viewId = parcel.readInt();
-            fillInIntent = parcel.readTypedObject(Intent.CREATOR);
-        }
-
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(viewId);
-            dest.writeTypedObject(fillInIntent, 0 /* no flags */);
-        }
-
-        @Override
-        public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
-            final View target = root.findViewById(viewId);
-            if (target == null) return;
-
-            if (!mIsWidgetCollectionChild) {
-                Log.e(LOG_TAG, "The method setOnClickFillInIntent is available " +
-                        "only from RemoteViewsFactory (ie. on collection items).");
-                return;
-            }
-            if (target == root) {
-                target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
-            } else if (fillInIntent != null) {
-                OnClickListener listener = new OnClickListener() {
-                    public void onClick(View v) {
-                        // Insure that this view is a child of an AdapterView
-                        View parent = (View) v.getParent();
-                        // Break the for loop on the first encounter of:
-                        //    1) an AdapterView,
-                        //    2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
-                        //    3) a null parent.
-                        // 2) and 3) are unexpected and catch the case where a child is not
-                        // correctly parented in an AdapterView.
-                        while (parent != null && !(parent instanceof AdapterView<?>)
-                                && !((parent instanceof AppWidgetHostView) &&
-                                    !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
-                            parent = (View) parent.getParent();
-                        }
-
-                        if (!(parent instanceof AdapterView<?>)) {
-                            // Somehow they've managed to get this far without having
-                            // and AdapterView as a parent.
-                            Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
-                            return;
-                        }
-
-                        // Insure that a template pending intent has been set on an ancestor
-                        if (!(parent.getTag() instanceof PendingIntent)) {
-                            Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without" +
-                                    " calling setPendingIntentTemplate on parent.");
-                            return;
-                        }
-
-                        PendingIntent pendingIntent = (PendingIntent) parent.getTag();
-
-                        final Rect rect = getSourceBounds(v);
-
-                        fillInIntent.setSourceBounds(rect);
-                        handler.onClickHandler(v, pendingIntent, fillInIntent);
-                    }
-
-                };
-                target.setOnClickListener(listener);
-            }
-        }
-
-        @Override
-        public int getActionTag() {
-            return SET_ON_CLICK_FILL_IN_INTENT_TAG;
-        }
-
-        Intent fillInIntent;
-    }
-
     private class SetPendingIntentTemplate extends Action {
         public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
             this.viewId = id;
@@ -749,22 +630,17 @@
                             }
                             if (vg == null) return;
 
-                            Intent fillInIntent = null;
+                            RemoteResponse response = null;
                             int childCount = vg.getChildCount();
                             for (int i = 0; i < childCount; i++) {
                                 Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
-                                if (tag instanceof Intent) {
-                                    fillInIntent = (Intent) tag;
+                                if (tag instanceof RemoteResponse) {
+                                    response = (RemoteResponse) tag;
                                     break;
                                 }
                             }
-                            if (fillInIntent == null) return;
-
-                            final Rect rect = getSourceBounds(view);
-
-                            final Intent intent = new Intent();
-                            intent.setSourceBounds(rect);
-                            handler.onClickHandler(view, pendingIntentTemplate, fillInIntent);
+                            if (response == null) return;
+                            response.handleViewClick(view, handler);
                         }
                     }
                 };
@@ -922,20 +798,22 @@
      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
      * to launch the provided {@link PendingIntent}.
      */
-    private class SetOnClickPendingIntent extends Action {
-        public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) {
+    private class SetOnClickResponse extends Action {
+
+        SetOnClickResponse(int id, RemoteResponse response) {
             this.viewId = id;
-            this.pendingIntent = pendingIntent;
+            this.mResponse = response;
         }
 
-        public SetOnClickPendingIntent(Parcel parcel) {
+        SetOnClickResponse(Parcel parcel) {
             viewId = parcel.readInt();
-            pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
+            mResponse = new RemoteResponse();
+            mResponse.readFromParcel(parcel);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(viewId);
-            PendingIntent.writePendingIntentOrNullToParcel(pendingIntent, dest);
+            mResponse.writeToParcel(dest, flags);
         }
 
         @Override
@@ -943,50 +821,54 @@
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
-            // If the view is an AdapterView, setting a PendingIntent on click doesn't make much
-            // sense, do they mean to set a PendingIntent template for the AdapterView's children?
-            if (mIsWidgetCollectionChild) {
-                Log.w(LOG_TAG, "Cannot setOnClickPendingIntent for collection item " +
-                        "(id: " + viewId + ")");
-                ApplicationInfo appInfo = root.getContext().getApplicationInfo();
+            if (mResponse.mPendingIntent != null) {
+                // If the view is an AdapterView, setting a PendingIntent on click doesn't make
+                // much sense, do they mean to set a PendingIntent template for the
+                // AdapterView's children?
+                if (mIsWidgetCollectionChild) {
+                    Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item "
+                            + "(id: " + viewId + ")");
+                    ApplicationInfo appInfo = root.getContext().getApplicationInfo();
 
-                // We let this slide for HC and ICS so as to not break compatibility. It should have
-                // been disabled from the outset, but was left open by accident.
-                if (appInfo != null &&
-                        appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
+                    // We let this slide for HC and ICS so as to not break compatibility. It should
+                    // have been disabled from the outset, but was left open by accident.
+                    if (appInfo != null
+                            && appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
+                        return;
+                    }
+                }
+                target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
+            } else if (mResponse.mFillIntent != null) {
+                if (!mIsWidgetCollectionChild) {
+                    Log.e(LOG_TAG, "The method setOnClickFillInIntent is available "
+                            + "only from RemoteViewsFactory (ie. on collection items).");
                     return;
                 }
+                if (target == root) {
+                    // Target is a root node of an AdapterView child. Set the response in the tag.
+                    // Actual click handling is done by OnItemClickListener in
+                    // SetPendingIntentTemplate, which uses this tag information.
+                    target.setTagInternal(com.android.internal.R.id.fillInIntent, mResponse);
+                    return;
+                }
+            } else {
+                // No intent to apply
+                target.setOnClickListener(null);
+                return;
             }
-
-            // If the pendingIntent is null, we clear the onClickListener
-            OnClickListener listener = null;
-            if (pendingIntent != null) {
-                listener = new OnClickListener() {
-                    public void onClick(View v) {
-                        // Find target view location in screen coordinates and
-                        // fill into PendingIntent before sending.
-                        final Rect rect = getSourceBounds(v);
-
-                        final Intent intent = new Intent();
-                        intent.setSourceBounds(rect);
-                        handler.onClickHandler(v, pendingIntent, intent);
-                    }
-                };
-            }
-            target.setTagInternal(R.id.pending_intent_tag, pendingIntent);
-            target.setOnClickListener(listener);
+            target.setOnClickListener(v -> mResponse.handleViewClick(v, handler));
         }
 
         @Override
         public int getActionTag() {
-            return SET_ON_CLICK_PENDING_INTENT_TAG;
+            return SET_ON_CLICK_RESPONSE_TAG;
         }
 
-        @UnsupportedAppUsage
-        PendingIntent pendingIntent;
+        final RemoteResponse mResponse;
     }
 
-    private static Rect getSourceBounds(View v) {
+    /** @hide **/
+    public static Rect getSourceBounds(View v) {
         final float appScale = v.getContext().getResources()
                 .getCompatibilityInfo().applicationScale;
         final int[] pos = new int[2];
@@ -2413,8 +2295,8 @@
     private Action getActionFromParcel(Parcel parcel, int depth) {
         int tag = parcel.readInt();
         switch (tag) {
-            case SET_ON_CLICK_PENDING_INTENT_TAG:
-                return new SetOnClickPendingIntent(parcel);
+            case SET_ON_CLICK_RESPONSE_TAG:
+                return new SetOnClickResponse(parcel);
             case SET_DRAWABLE_TINT_TAG:
                 return new SetDrawableTint(parcel);
             case REFLECTION_ACTION_TAG:
@@ -2430,8 +2312,6 @@
                 return new SetEmptyView(parcel);
             case SET_PENDING_INTENT_TEMPLATE_TAG:
                 return new SetPendingIntentTemplate(parcel);
-            case SET_ON_CLICK_FILL_IN_INTENT_TAG:
-                return new SetOnClickFillInIntent(parcel);
             case SET_REMOTE_VIEW_ADAPTER_INTENT_TAG:
                 return new SetRemoteViewsAdapterIntent(parcel);
             case TEXT_VIEW_DRAWABLE_ACTION_TAG:
@@ -2834,7 +2714,7 @@
      * to launch the provided {@link PendingIntent}. The source bounds
      * ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the clicked
      * view in screen space.
-     * Note that any activity options associated with the pendingIntent may get overridden
+     * Note that any activity options associated with the mPendingIntent may get overridden
      * before starting the intent.
      *
      * When setting the on-click action of items within collections (eg. {@link ListView},
@@ -2846,7 +2726,19 @@
      * @param pendingIntent The {@link PendingIntent} to send when user clicks
      */
     public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {
-        addAction(new SetOnClickPendingIntent(viewId, pendingIntent));
+        setOnClickResponse(viewId, RemoteResponse.fromPendingIntent(pendingIntent));
+    }
+
+    /**
+     * Equivalent of calling
+     * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
+     * to launch the provided {@link RemoteResponse}.
+     *
+     * @param viewId The id of the view that will trigger the {@link RemoteResponse} when clicked
+     * @param response The {@link RemoteResponse} to send when user clicks
+     */
+    public void setOnClickResponse(int viewId, RemoteResponse response) {
+        addAction(new SetOnClickResponse(viewId, response));
     }
 
     /**
@@ -2883,7 +2775,7 @@
      *        in order to determine the on-click behavior of the view specified by viewId
      */
     public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
-        addAction(new SetOnClickFillInIntent(viewId, fillInIntent));
+        setOnClickResponse(viewId, RemoteResponse.fromFillInIntent(fillInIntent));
     }
 
     /**
@@ -3903,4 +3795,213 @@
             }
         }
     }
+
+    /**
+     * Class representing a response to an action performed on any element of a RemoteViews.
+     */
+    public static class RemoteResponse {
+
+        private PendingIntent mPendingIntent;
+        private Intent mFillIntent;
+
+        private IntArray mViewIds;
+        private ArrayList<String> mElementNames;
+
+        /**
+         * Creates a response which sends a pending intent as part of the response. The source
+         * bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
+         * target view in screen space.
+         * Note that any activity options associated with the mPendingIntent may get overridden
+         * before starting the intent.
+         *
+         * @param pendingIntent The {@link PendingIntent} to send as part of the response
+         */
+        public static RemoteResponse fromPendingIntent(PendingIntent pendingIntent) {
+            RemoteResponse response = new RemoteResponse();
+            response.mPendingIntent = pendingIntent;
+            return response;
+        }
+
+        /**
+         * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is
+         * very costly to set PendingIntents on the individual items, and is hence not permitted.
+         * Instead a single PendingIntent template can be set on the collection, see {@link
+         * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
+         * action of a given item can be distinguished by setting a fillInIntent on that item. The
+         * fillInIntent is then combined with the PendingIntent template in order to determine the
+         * final intent which will be executed when the item is clicked. This works as follows: any
+         * fields which are left blank in the PendingIntent template, but are provided by the
+         * fillInIntent will be overwritten, and the resulting PendingIntent will be used. The rest
+         * of the PendingIntent template will then be filled in with the associated fields that are
+         * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
+         * Creates a response which sends a pending intent as part of the response. The source
+         * bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
+         * target view in screen space.
+         * Note that any activity options associated with the mPendingIntent may get overridden
+         * before starting the intent.
+         *
+         * @param fillIntent The intent which will be combined with the parent's PendingIntent in
+         *                  order to determine the behavior of the response
+         *
+         * @see RemoteViews#setPendingIntentTemplate(int, PendingIntent)
+         * @see RemoteViews#setOnClickFillInIntent(int, Intent)
+         * @return
+         */
+        public static RemoteResponse fromFillInIntent(Intent fillIntent) {
+            RemoteResponse response = new RemoteResponse();
+            response.mFillIntent = fillIntent;
+            return response;
+        }
+
+        /**
+         * Adds a shared element to be transferred as part of the transition between Activities
+         * using cross-Activity scene animations. The position of the first element will be used as
+         * the epicenter for the exit Transition. The position of the associated shared element in
+         * the launched Activity will be the epicenter of its entering Transition.
+         *
+         * @param viewId The id of the view to be shared as part of the transition
+         * @param sharedElementName The shared element name for this view
+         *
+         * @see ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[])
+         */
+        public RemoteResponse addSharedElement(int viewId, String sharedElementName) {
+            if (mViewIds == null) {
+                mViewIds = new IntArray();
+                mElementNames = new ArrayList<>();
+            }
+            mViewIds.add(viewId);
+            mElementNames.add(sharedElementName);
+            return this;
+        }
+
+        private void writeToParcel(Parcel dest, int flags) {
+            PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest);
+            if (mPendingIntent == null) {
+                // Only write the intent if pending intent is null
+                dest.writeTypedObject(mFillIntent, flags);
+            }
+            dest.writeIntArray(mViewIds == null ? null : mViewIds.toArray());
+            dest.writeStringList(mElementNames);
+        }
+
+        private void readFromParcel(Parcel parcel) {
+            mPendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
+            if (mPendingIntent == null) {
+                mFillIntent = parcel.readTypedObject(Intent.CREATOR);
+            }
+            int[] viewIds = parcel.createIntArray();
+            mViewIds = viewIds == null ? null : IntArray.wrap(viewIds);
+            mElementNames = parcel.createStringArrayList();
+        }
+
+        private void handleViewClick(View v, OnClickHandler handler) {
+            final PendingIntent pi;
+            if (mPendingIntent != null) {
+                pi = mPendingIntent;
+            } else if (mFillIntent != null) {
+                // Insure that this view is a child of an AdapterView
+                View parent = (View) v.getParent();
+                // Break the for loop on the first encounter of:
+                //    1) an AdapterView,
+                //    2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
+                //    3) a null parent.
+                // 2) and 3) are unexpected and catch the case where a child is not
+                // correctly parented in an AdapterView.
+                while (parent != null && !(parent instanceof AdapterView<?>)
+                        && !((parent instanceof AppWidgetHostView)
+                        && !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
+                    parent = (View) parent.getParent();
+                }
+
+                if (!(parent instanceof AdapterView<?>)) {
+                    // Somehow they've managed to get this far without having
+                    // and AdapterView as a parent.
+                    Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
+                    return;
+                }
+                // Insure that a template pending intent has been set on an ancestor
+                if (!(parent.getTag() instanceof PendingIntent)) {
+                    Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without"
+                            + " calling setPendingIntentTemplate on parent.");
+                    return;
+                }
+
+                pi = (PendingIntent) parent.getTag();
+            } else {
+                Log.e(LOG_TAG, "Response has neither pendingIntent nor fillInIntent");
+                return;
+            }
+
+            handler.onClickHandler(v, pi, this);
+        }
+
+        /** @hide */
+        public Pair<Intent, ActivityOptions> getLaunchOptions(View view) {
+            Intent intent = mPendingIntent != null ? new Intent() : new Intent(mFillIntent);
+            intent.setSourceBounds(getSourceBounds(view));
+
+            ActivityOptions opts = null;
+
+            Context context = view.getContext();
+            if (context.getResources().getBoolean(
+                    com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
+                TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
+                        com.android.internal.R.styleable.Window);
+                int windowAnimations = windowStyle.getResourceId(
+                        com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
+                TypedArray windowAnimationStyle = context.obtainStyledAttributes(
+                        windowAnimations, com.android.internal.R.styleable.WindowAnimation);
+                int enterAnimationId = windowAnimationStyle.getResourceId(com.android.internal.R
+                        .styleable.WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0);
+                windowStyle.recycle();
+                windowAnimationStyle.recycle();
+
+                if (enterAnimationId != 0) {
+                    opts = ActivityOptions.makeCustomAnimation(context,
+                            enterAnimationId, 0);
+                    opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                }
+            }
+
+            if (opts == null && mViewIds != null && mElementNames != null) {
+                View parent = (View) view.getParent();
+                while (parent != null && !(parent instanceof AppWidgetHostView)) {
+                    parent = (View) parent.getParent();
+                }
+                if (parent instanceof AppWidgetHostView) {
+                    opts = ((AppWidgetHostView) parent).createSharedElementActivityOptions(
+                            mViewIds.toArray(),
+                            mElementNames.toArray(new String[mElementNames.size()]), intent);
+                }
+            }
+
+            if (opts == null) {
+                opts = ActivityOptions.makeBasic();
+                opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            }
+            return Pair.create(intent, opts);
+        }
+    }
+
+    /** @hide */
+    public static boolean startPendingIntent(View view, PendingIntent pendingIntent,
+            Pair<Intent, ActivityOptions> options) {
+        try {
+            // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
+            Context context = view.getContext();
+            // The NEW_TASK flags are applied through the activity options and not as a part of
+            // the call to startIntentSender() to ensure that they are consistently applied to
+            // both mutable and immutable PendingIntents.
+            context.startIntentSender(
+                    pendingIntent.getIntentSender(), options.first,
+                    0, 0, 0, options.second.toBundle());
+        } catch (IntentSender.SendIntentException e) {
+            Log.e(LOG_TAG, "Cannot send pending intent: ", e);
+            return false;
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Cannot send pending intent due to unknown exception: ", e);
+            return false;
+        }
+        return true;
+    }
 }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 66809db..572670f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -67,6 +67,7 @@
 import android.graphics.fonts.FontVariationAxis;
 import android.icu.text.DecimalFormatSymbols;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.LocaleList;
@@ -799,17 +800,21 @@
     // they are defined by the TextView's style and are theme-dependent.
     @UnsupportedAppUsage
     int mCursorDrawableRes;
-    // These six fields, could be moved to Editor, since we know their default values and we
-    // could condition the creation of the Editor to a non standard value. This is however
-    // brittle since the hardcoded values here (such as
-    // com.android.internal.R.drawable.text_select_handle_left) would have to be updated if the
-    // default style is modified.
-    @UnsupportedAppUsage
+    // Note: this might be stale if setTextSelectHandleLeft is used. We could simplify the code
+    // by removing it, but we would break apps targeting <= P that use it by reflection.
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     int mTextSelectHandleLeftRes;
-    @UnsupportedAppUsage
+    private Drawable mTextSelectHandleLeft;
+    // Note: this might be stale if setTextSelectHandleRight is used. We could simplify the code
+    // by removing it, but we would break apps targeting <= P that use it by reflection.
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     int mTextSelectHandleRightRes;
-    @UnsupportedAppUsage
+    private Drawable mTextSelectHandleRight;
+    // Note: this might be stale if setTextSelectHandle is used. We could simplify the code
+    // by removing it, but we would break apps targeting <= P that use it by reflection.
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     int mTextSelectHandleRes;
+    private Drawable mTextSelectHandle;
     int mTextEditSuggestionItemLayout;
     int mTextEditSuggestionContainerLayout;
     int mTextEditSuggestionHighlightStyle;
@@ -3477,6 +3482,175 @@
     }
 
     /**
+     * Sets the Drawable corresponding to the selection handle used for
+     * positioning the cursor within text. The Drawable defaults to the value
+     * of the textSelectHandle attribute.
+     * Note that any change applied to the handle Drawable will not be visible
+     * until the handle is hidden and then drawn again.
+     *
+     * @see #setTextSelectHandle(int)
+     * @attr ref android.R.styleable#TextView_textSelectHandle
+     */
+    @android.view.RemotableViewMethod
+    public void setTextSelectHandle(@NonNull Drawable textSelectHandle) {
+        Preconditions.checkNotNull(textSelectHandle,
+                "The text select handle should not be null.");
+        mTextSelectHandle = textSelectHandle;
+        mTextSelectHandleRes = 0;
+        if (mEditor != null) {
+            mEditor.loadHandleDrawables(true /* overwrite */);
+        }
+    }
+
+    /**
+     * Sets the Drawable corresponding to the selection handle used for
+     * positioning the cursor within text. The Drawable defaults to the value
+     * of the textSelectHandle attribute.
+     * Note that any change applied to the handle Drawable will not be visible
+     * until the handle is hidden and then drawn again.
+     *
+     * @see #setTextSelectHandle(Drawable)
+     * @attr ref android.R.styleable#TextView_textSelectHandle
+     */
+    @android.view.RemotableViewMethod
+    public void setTextSelectHandle(@DrawableRes int textSelectHandle) {
+        Preconditions.checkArgumentPositive(textSelectHandle,
+                "The text select handle should be a valid drawable resource id.");
+        setTextSelectHandle(mContext.getDrawable(textSelectHandle));
+    }
+
+    /**
+     * Returns the Drawable corresponding to the selection handle used
+     * for positioning the cursor within text.
+     * Note that any change applied to the handle Drawable will not be visible
+     * until the handle is hidden and then drawn again.
+     *
+     * @return the text select handle drawable
+     *
+     * @see #setTextSelectHandle(Drawable)
+     * @see #setTextSelectHandle(int)
+     * @attr ref android.R.styleable#TextView_textSelectHandle
+     */
+    @Nullable public Drawable getTextSelectHandle() {
+        if (mTextSelectHandle == null && mTextSelectHandleRes != 0) {
+            mTextSelectHandle = mContext.getDrawable(mTextSelectHandleRes);
+        }
+        return mTextSelectHandle;
+    }
+
+    /**
+     * Sets the Drawable corresponding to the left handle used
+     * for selecting text. The Drawable defaults to the value of the
+     * textSelectHandleLeft attribute.
+     * Note that any change applied to the handle Drawable will not be visible
+     * until the handle is hidden and then drawn again.
+     *
+     * @see #setTextSelectHandleLeft(int)
+     * @attr ref android.R.styleable#TextView_textSelectHandleLeft
+     */
+    @android.view.RemotableViewMethod
+    public void setTextSelectHandleLeft(@NonNull Drawable textSelectHandleLeft) {
+        Preconditions.checkNotNull(textSelectHandleLeft,
+                "The left text select handle should not be null.");
+        mTextSelectHandleLeft = textSelectHandleLeft;
+        mTextSelectHandleLeftRes = 0;
+        if (mEditor != null) {
+            mEditor.loadHandleDrawables(true /* overwrite */);
+        }
+    }
+
+    /**
+     * Sets the Drawable corresponding to the left handle used
+     * for selecting text. The Drawable defaults to the value of the
+     * textSelectHandleLeft attribute.
+     * Note that any change applied to the handle Drawable will not be visible
+     * until the handle is hidden and then drawn again.
+     *
+     * @see #setTextSelectHandleLeft(Drawable)
+     * @attr ref android.R.styleable#TextView_textSelectHandleLeft
+     */
+    @android.view.RemotableViewMethod
+    public void setTextSelectHandleLeft(@DrawableRes int textSelectHandleLeft) {
+        Preconditions.checkArgumentPositive(textSelectHandleLeft,
+                "The text select left handle should be a valid drawable resource id.");
+        setTextSelectHandleLeft(mContext.getDrawable(textSelectHandleLeft));
+    }
+
+    /**
+     * Returns the Drawable corresponding to the left handle used
+     * for selecting text.
+     * Note that any change applied to the handle Drawable will not be visible
+     * until the handle is hidden and then drawn again.
+     *
+     * @return the left text selection handle drawable
+     *
+     * @see #setTextSelectHandleLeft(Drawable)
+     * @see #setTextSelectHandleLeft(int)
+     * @attr ref android.R.styleable#TextView_textSelectHandleLeft
+     */
+    @Nullable public Drawable getTextSelectHandleLeft() {
+        if (mTextSelectHandleLeft == null && mTextSelectHandleLeftRes != 0) {
+            mTextSelectHandleLeft = mContext.getDrawable(mTextSelectHandleLeftRes);
+        }
+        return mTextSelectHandleLeft;
+    }
+
+    /**
+     * Sets the Drawable corresponding to the right handle used
+     * for selecting text. The Drawable defaults to the value of the
+     * textSelectHandleRight attribute.
+     * Note that any change applied to the handle Drawable will not be visible
+     * until the handle is hidden and then drawn again.
+     *
+     * @see #setTextSelectHandleRight(int)
+     * @attr ref android.R.styleable#TextView_textSelectHandleRight
+     */
+    @android.view.RemotableViewMethod
+    public void setTextSelectHandleRight(@NonNull Drawable textSelectHandleRight) {
+        Preconditions.checkNotNull(textSelectHandleRight,
+                "The right text select handle should not be null.");
+        mTextSelectHandleRight = textSelectHandleRight;
+        mTextSelectHandleRightRes = 0;
+        if (mEditor != null) {
+            mEditor.loadHandleDrawables(true /* overwrite */);
+        }
+    }
+
+    /**
+     * Sets the Drawable corresponding to the right handle used
+     * for selecting text. The Drawable defaults to the value of the
+     * textSelectHandleRight attribute.
+     * Note that any change applied to the handle Drawable will not be visible
+     * until the handle is hidden and then drawn again.
+     *
+     * @see #setTextSelectHandleRight(Drawable)
+     * @attr ref android.R.styleable#TextView_textSelectHandleRight
+     */
+    @android.view.RemotableViewMethod
+    public void setTextSelectHandleRight(@DrawableRes int textSelectHandleRight) {
+        Preconditions.checkArgumentPositive(textSelectHandleRight,
+                "The text select right handle should be a valid drawable resource id.");
+        setTextSelectHandleRight(mContext.getDrawable(textSelectHandleRight));
+    }
+
+    /**
+     * Returns the Drawable corresponding to the right handle used
+     * for selecting text.
+     *
+     * @return the right text selection handle drawable
+     *
+     * @see #setTextSelectHandleRight(Drawable)
+     * @see #setTextSelectHandleRight(int)
+     * @attr ref android.R.styleable#TextView_textSelectHandleRight
+     */
+    @Nullable public Drawable getTextSelectHandleRight() {
+        if (mTextSelectHandleRight == null && mTextSelectHandleRightRes != 0) {
+            mTextSelectHandleRight = mContext.getDrawable(mTextSelectHandleRightRes);
+        }
+        return mTextSelectHandleRight;
+    }
+
+    /**
      * Sets the text appearance from the specified style resource.
      * <p>
      * Use a framework-defined {@code TextAppearance} style like
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index d7031ea..3462e08 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -191,7 +191,8 @@
 
     public static final AlertController create(Context context, DialogInterface di, Window window) {
         final TypedArray a = context.obtainStyledAttributes(
-                null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
+                null, R.styleable.AlertDialog, R.attr.alertDialogStyle,
+                R.style.Theme_DeviceDefault_Settings);
         int controllerType = a.getInt(R.styleable.AlertDialog_controllerType, 0);
         a.recycle();
 
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 768dddd..049103b 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.app;
 
 import android.app.AppOpsManager;
+import android.content.pm.ParceledListSlice;
 import android.os.Bundle;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsActiveCallback;
@@ -40,6 +41,10 @@
     int checkPackage(int uid, String packageName);
     List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
     List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
+    ParceledListSlice getAllHistoricalPackagesOps(in String[] ops,
+            long beginTimeMillis, long endTimeMillis);
+    AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int uid, String packageName,
+            in String[] ops, long beginTimeMillis, long endTimeMillis);
     List<AppOpsManager.PackageOps> getUidOps(int uid, in int[] ops);
     void setUidMode(int code, int uid, int mode);
     void setMode(int code, int uid, String packageName, int mode);
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index e7ac566..19d8a83 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -1396,6 +1396,11 @@
         return as;
     }
 
+    // See b/118826162 -- to avoid logspaming, we rate limit the WTF.
+    private static final long INVERSE_PROC_STATE_WTF_MIN_INTERVAL_MS = 10_000L;
+    private long mNextInverseProcStateWtfUptime;
+    private int mSkippedInverseProcStateWtfCount;
+
     public void updateTrackingAssociationsLocked(int curSeq, long now) {
         final int NUM = mTrackingAssociations.size();
         for (int i = NUM - 1; i >= 0; i--) {
@@ -1417,12 +1422,24 @@
                     } else {
                         act.stopActive(now);
                         if (act.mProcState < procState) {
-                            Slog.w(TAG, "Tracking association " + act + " whose proc state "
-                                    + act.mProcState + " is better than process " + proc
-                                    + " proc state " + procState);
+                            final long nowUptime = SystemClock.uptimeMillis();
+                            if (mNextInverseProcStateWtfUptime > nowUptime) {
+                                mSkippedInverseProcStateWtfCount++;
+                            } else {
+                                // TODO We still see it during boot related to GMS-core.
+                                // b/118826162
+                                Slog.wtf(TAG, "Tracking association " + act + " whose proc state "
+                                        + act.mProcState + " is better than process " + proc
+                                        + " proc state " + procState
+                                        + " (" +  mSkippedInverseProcStateWtfCount + " skipped)");
+                                mSkippedInverseProcStateWtfCount = 0;
+                                mNextInverseProcStateWtfUptime =
+                                        nowUptime + INVERSE_PROC_STATE_WTF_MIN_INTERVAL_MS;
+                            }
                         }
                     }
                 } else {
+                    // Don't need rate limiting on it.
                     Slog.wtf(TAG, "Tracking association without process: " + act
                             + " in " + act.getAssociationState());
                 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index a5dc3d1..025e27b 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -19,6 +19,8 @@
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 
+import java.util.StringJoiner;
+
 /**
  * Provides useful methods for debugging.
  */
@@ -96,7 +98,7 @@
      * @return {@link String} message corresponds for the given {@code softInputMode}.
      */
     public static String softInputModeToString(@SoftInputModeFlags int softInputMode) {
-        final StringBuilder sb = new StringBuilder();
+        final StringJoiner joiner = new StringJoiner("|");
         final int state = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
         final int adjust = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
         final boolean isForwardNav =
@@ -104,55 +106,75 @@
 
         switch (state) {
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
-                sb.append("STATE_UNSPECIFIED");
+                joiner.add("STATE_UNSPECIFIED");
                 break;
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
-                sb.append("STATE_UNCHANGED");
+                joiner.add("STATE_UNCHANGED");
                 break;
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
-                sb.append("STATE_HIDDEN");
+                joiner.add("STATE_HIDDEN");
                 break;
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
-                sb.append("STATE_ALWAYS_HIDDEN");
+                joiner.add("STATE_ALWAYS_HIDDEN");
                 break;
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
-                sb.append("STATE_VISIBLE");
+                joiner.add("STATE_VISIBLE");
                 break;
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
-                sb.append("STATE_ALWAYS_VISIBLE");
+                joiner.add("STATE_ALWAYS_VISIBLE");
                 break;
             default:
-                sb.append("STATE_UNKNOWN(");
-                sb.append(state);
-                sb.append(")");
+                joiner.add("STATE_UNKNOWN(" + state + ")");
                 break;
         }
 
         switch (adjust) {
             case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED:
-                sb.append("|ADJUST_UNSPECIFIED");
+                joiner.add("ADJUST_UNSPECIFIED");
                 break;
             case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE:
-                sb.append("|ADJUST_RESIZE");
+                joiner.add("ADJUST_RESIZE");
                 break;
             case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN:
-                sb.append("|ADJUST_PAN");
+                joiner.add("ADJUST_PAN");
                 break;
             case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING:
-                sb.append("|ADJUST_NOTHING");
+                joiner.add("ADJUST_NOTHING");
                 break;
             default:
-                sb.append("|ADJUST_UNKNOWN(");
-                sb.append(adjust);
-                sb.append(")");
+                joiner.add("ADJUST_UNKNOWN(" + adjust + ")");
                 break;
         }
 
         if (isForwardNav) {
             // This is a special bit that is set by the system only during the window navigation.
-            sb.append("|IS_FORWARD_NAVIGATION");
+            joiner.add("IS_FORWARD_NAVIGATION");
         }
 
-        return sb.toString();
+        return joiner.setEmptyValue("(none)").toString();
+    }
+
+    /**
+     * Converts {@link StartInputFlags} to {@link String} for debug logging.
+     *
+     * @param startInputFlags integer constant for {@link StartInputFlags}.
+     * @return {@link String} message corresponds for the given {@code startInputFlags}.
+     */
+    public static String startInputFlagsToString(@StartInputFlags int startInputFlags) {
+        final StringJoiner joiner = new StringJoiner("|");
+        if ((startInputFlags & StartInputFlags.VIEW_HAS_FOCUS) != 0) {
+            joiner.add("VIEW_HAS_FOCUS");
+        }
+        if ((startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
+            joiner.add("IS_TEXT_EDITOR");
+        }
+        if ((startInputFlags & StartInputFlags.FIRST_WINDOW_FOCUS_GAIN) != 0) {
+            joiner.add("FIRST_WINDOW_FOCUS_GAIN");
+        }
+        if ((startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0) {
+            joiner.add("INITIAL_CONNECTION");
+        }
+
+        return joiner.setEmptyValue("(none)").toString();
     }
 }
diff --git a/core/java/com/android/internal/inputmethod/StartInputFlags.java b/core/java/com/android/internal/inputmethod/StartInputFlags.java
new file mode 100644
index 0000000..ba26d8d
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/StartInputFlags.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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.internal.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Describes additional info in
+ * {@link com.android.internal.view.IInputMethodManager#startInputOrWindowGainedFocus}.
+ */
+@Retention(SOURCE)
+@IntDef(flag = true, value = {
+        StartInputFlags.VIEW_HAS_FOCUS,
+        StartInputFlags.IS_TEXT_EDITOR,
+        StartInputFlags.FIRST_WINDOW_FOCUS_GAIN,
+        StartInputFlags.INITIAL_CONNECTION})
+public @interface StartInputFlags {
+    /**
+     * There is a focused view in the focused window.
+     */
+    int VIEW_HAS_FOCUS = 1;
+
+    /**
+     * The focused view is a text editor.
+     */
+    int IS_TEXT_EDITOR = 2;
+
+    /**
+     * This is the first time the window has gotten focus.
+     */
+    int FIRST_WINDOW_FOCUS_GAIN = 4;
+
+    /**
+     * An internal concept to distinguish "start" and "restart". This concept doesn't look well
+     * documented hence we probably need to revisit this though.
+     */
+    int INITIAL_CONNECTION = 8;
+}
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index d1c2799..0a7cff6 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -113,11 +113,12 @@
 
     /**
      * Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
-     * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map)
+     * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map, boolean)
      */
     public static void apply464xlatAdjustments(NetworkStats baseTraffic,
-            NetworkStats stackedTraffic) {
-        NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, sStackedIfaces);
+            NetworkStats stackedTraffic, boolean useBpfStats) {
+        NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, sStackedIfaces,
+                useBpfStats);
     }
 
     @VisibleForTesting
@@ -263,7 +264,7 @@
 
         // No locking here: apply464xlatAdjustments behaves fine with an add-only ConcurrentHashMap.
         // TODO: remove this and only apply adjustments in NetworkStatsService.
-        stats.apply464xlatAdjustments(sStackedIfaces);
+        stats.apply464xlatAdjustments(sStackedIfaces, mUseBpfStats);
 
         return stats;
     }
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 856712f..70fc72f 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -140,6 +140,7 @@
             latencyDuration = 0;
         }
         final int callingUid = getCallingUid();
+        final int workSourceUid = getWorkSourceUid();
 
         synchronized (mLock) {
             // This was already checked in #callStart but check again while synchronized.
@@ -147,7 +148,8 @@
                 return;
             }
 
-            final UidEntry uidEntry = getUidEntry(callingUid);
+            final boolean isWorkSourceSet = workSourceUid >= 0;
+            final UidEntry uidEntry = getUidEntry(isWorkSourceSet ? workSourceUid : callingUid);
             uidEntry.callCount++;
 
             if (recordCall) {
@@ -155,7 +157,8 @@
                 uidEntry.recordedCallCount++;
 
                 final CallStat callStat = uidEntry.getOrCreate(
-                        s.binderClass, s.transactionCode, mDeviceState.isScreenInteractive());
+                        callingUid, s.binderClass, s.transactionCode,
+                        mDeviceState.isScreenInteractive());
                 callStat.callCount++;
                 callStat.recordedCallCount++;
                 callStat.cpuTimeMicros += duration;
@@ -174,7 +177,8 @@
                 // Only record the total call count if we already track data for this key.
                 // It helps to keep the memory usage down when sampling is enabled.
                 final CallStat callStat = uidEntry.get(
-                        s.binderClass, s.transactionCode, mDeviceState.isScreenInteractive());
+                        callingUid, s.binderClass, s.transactionCode,
+                        mDeviceState.isScreenInteractive());
                 if (callStat != null) {
                     callStat.callCount++;
                 }
@@ -251,7 +255,8 @@
                 final UidEntry entry = mUidEntries.valueAt(entryIdx);
                 for (CallStat stat : entry.getCallStatsList()) {
                     ExportedCallStat exported = new ExportedCallStat();
-                    exported.uid = entry.uid;
+                    exported.workSourceUid = entry.workSourceUid;
+                    exported.callingUid = stat.callingUid;
                     exported.className = stat.binderClass.getName();
                     exported.binderClass = stat.binderClass;
                     exported.transactionCode = stat.transactionCode;
@@ -338,10 +343,8 @@
         entries.sort(Comparator.<UidEntry>comparingDouble(value -> value.cpuTimeMicros).reversed());
         final String datasetSizeDesc = verbose ? "" : "(top 90% by cpu time) ";
         final StringBuilder sb = new StringBuilder();
-        final List<UidEntry> topEntries = verbose ? entries
-                : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
         pw.println("Per-UID raw data " + datasetSizeDesc
-                + "(package/uid, call_desc, screen_interactive, "
+                + "(package/uid, worksource, call_desc, screen_interactive, "
                 + "cpu_time_micros, max_cpu_time_micros, "
                 + "latency_time_micros, max_latency_time_micros, exception_count, "
                 + "max_request_size_bytes, max_reply_size_bytes, recorded_call_count, "
@@ -351,7 +354,9 @@
         for (ExportedCallStat e : exportedCallStats) {
             sb.setLength(0);
             sb.append("    ")
-                    .append(uidToString(e.uid, appIdToPkgNameMap))
+                    .append(uidToString(e.callingUid, appIdToPkgNameMap))
+                    .append(',')
+                    .append(uidToString(e.workSourceUid, appIdToPkgNameMap))
                     .append(',').append(e.className)
                     .append('#').append(e.methodName)
                     .append(',').append(e.screenInteractive)
@@ -372,7 +377,7 @@
         final List<UidEntry> summaryEntries = verbose ? entries
                 : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
         for (UidEntry entry : summaryEntries) {
-            String uidStr = uidToString(entry.uid, appIdToPkgNameMap);
+            String uidStr = uidToString(entry.workSourceUid, appIdToPkgNameMap);
             pw.println(String.format("  %10d %3.0f%% %8d %8d %s",
                     entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
                     entry.recordedCallCount, entry.callCount, uidStr));
@@ -415,11 +420,15 @@
         return Binder.getCallingUid();
     }
 
+    protected int getWorkSourceUid() {
+        return Binder.getThreadWorkSource();
+    }
+
     protected long getElapsedRealtimeMicro() {
         return SystemClock.elapsedRealtimeNanos() / 1000;
     }
 
-    private boolean shouldRecordDetailedData() {
+    protected boolean shouldRecordDetailedData() {
         return mRandom.nextInt() % mPeriodicSamplingInterval == 0;
     }
 
@@ -462,7 +471,8 @@
      * Aggregated data by uid/class/method to be sent through WestWorld.
      */
     public static class ExportedCallStat {
-        public int uid;
+        public int callingUid;
+        public int workSourceUid;
         public String className;
         public String methodName;
         public boolean screenInteractive;
@@ -483,10 +493,12 @@
 
     @VisibleForTesting
     public static class CallStat {
-        public Class<? extends Binder> binderClass;
-        public int transactionCode;
+        // The UID who executed the transaction (i.e. Binder#getCallingUid).
+        public final int callingUid;
+        public final Class<? extends Binder> binderClass;
+        public final int transactionCode;
         // True if the screen was interactive when the call ended.
-        public boolean screenInteractive;
+        public final boolean screenInteractive;
         // Number of calls for which we collected data for. We do not record data for all the calls
         // when sampling is on.
         public long recordedCallCount;
@@ -508,8 +520,9 @@
         public long maxReplySizeBytes;
         public long exceptionCount;
 
-        CallStat(Class<? extends Binder> binderClass, int transactionCode,
+        CallStat(int callingUid, Class<? extends Binder> binderClass, int transactionCode,
                 boolean screenInteractive) {
+            this.callingUid = callingUid;
             this.binderClass = binderClass;
             this.transactionCode = transactionCode;
             this.screenInteractive = screenInteractive;
@@ -518,6 +531,7 @@
 
     /** Key used to store CallStat object in a Map. */
     public static class CallStatKey {
+        public int callingUid;
         public Class<? extends Binder> binderClass;
         public int transactionCode;
         private boolean screenInteractive;
@@ -529,7 +543,8 @@
             }
 
             final CallStatKey key = (CallStatKey) o;
-            return transactionCode == key.transactionCode
+            return callingUid == key.callingUid
+                    && transactionCode == key.transactionCode
                     && screenInteractive == key.screenInteractive
                     && (binderClass.equals(key.binderClass));
         }
@@ -538,6 +553,7 @@
         public int hashCode() {
             int result = binderClass.hashCode();
             result = 31 * result + transactionCode;
+            result = 31 * result + callingUid;
             result = 31 * result + (screenInteractive ? 1231 : 1237);
             return result;
         }
@@ -546,7 +562,9 @@
 
     @VisibleForTesting
     public static class UidEntry {
-        int uid;
+        // The UID who is responsible for the binder transaction. If the bluetooth process execute a
+        // transaction on behalf of app foo, the workSourceUid will be the uid of app foo.
+        public int workSourceUid;
         // Number of calls for which we collected data for. We do not record data for all the calls
         // when sampling is on.
         public long recordedCallCount;
@@ -558,7 +576,7 @@
         public long cpuTimeMicros;
 
         UidEntry(int uid) {
-            this.uid = uid;
+            this.workSourceUid = uid;
         }
 
         // Aggregate time spent per each call name: call_desc -> cpu_time_micros
@@ -566,22 +584,25 @@
         private CallStatKey mTempKey = new CallStatKey();
 
         @Nullable
-        CallStat get(Class<? extends Binder> binderClass, int transactionCode,
+        CallStat get(int callingUid, Class<? extends Binder> binderClass, int transactionCode,
                 boolean screenInteractive) {
             // Use a global temporary key to avoid creating new objects for every lookup.
+            mTempKey.callingUid = callingUid;
             mTempKey.binderClass = binderClass;
             mTempKey.transactionCode = transactionCode;
             mTempKey.screenInteractive = screenInteractive;
             return mCallStats.get(mTempKey);
         }
 
-        CallStat getOrCreate(Class<? extends Binder> binderClass, int transactionCode,
-                boolean screenInteractive) {
-            CallStat mapCallStat = get(binderClass, transactionCode, screenInteractive);
+        CallStat getOrCreate(int callingUid, Class<? extends Binder> binderClass,
+                int transactionCode, boolean screenInteractive) {
+            CallStat mapCallStat = get(callingUid, binderClass, transactionCode, screenInteractive);
             // Only create CallStat if it's a new entry, otherwise update existing instance
             if (mapCallStat == null) {
-                mapCallStat = new CallStat(binderClass, transactionCode, screenInteractive);
+                mapCallStat = new CallStat(callingUid, binderClass, transactionCode,
+                        screenInteractive);
                 CallStatKey key = new CallStatKey();
+                key.callingUid = callingUid;
                 key.binderClass = binderClass;
                 key.transactionCode = transactionCode;
                 key.screenInteractive = screenInteractive;
@@ -613,12 +634,12 @@
             }
 
             UidEntry uidEntry = (UidEntry) o;
-            return uid == uidEntry.uid;
+            return workSourceUid == uidEntry.workSourceUid;
         }
 
         @Override
         public int hashCode() {
-            return uid;
+            return workSourceUid;
         }
     }
 
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
new file mode 100644
index 0000000..6b277a0
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2018 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.internal.os;
+
+import android.annotation.Nullable;
+import android.os.Process;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+
+/**
+ * Given a process, will iterate over the child threads of the process, and return the CPU usage
+ * statistics for each child thread. The CPU usage statistics contain the amount of time spent in a
+ * frequency band.
+ */
+public class KernelCpuThreadReader {
+
+    private static final String TAG = "KernelCpuThreadReader";
+
+    private static final boolean DEBUG = false;
+
+    /**
+     * The name of the file to read CPU statistics from, must be found in {@code
+     * /proc/$PID/task/$TID}
+     */
+    private static final String CPU_STATISTICS_FILENAME = "time_in_state";
+
+    /**
+     * The name of the file to read process command line invocation from, must be found in
+     * {@code /proc/$PID/}
+     */
+    private static final String PROCESS_NAME_FILENAME = "cmdline";
+
+    /**
+     * The name of the file to read thread name from, must be found in
+     * {@code /proc/$PID/task/$TID}
+     */
+    private static final String THREAD_NAME_FILENAME = "comm";
+
+    /**
+     * Default process name when the name can't be read
+     */
+    private static final String DEFAULT_PROCESS_NAME = "unknown_process";
+
+    /**
+     * Default thread name when the name can't be read
+     */
+    private static final String DEFAULT_THREAD_NAME = "unknown_thread";
+
+    /**
+     * Default mount location of the {@code proc} filesystem
+     */
+    private static final Path DEFAULT_PROC_PATH = Paths.get("/proc");
+
+    /**
+     * The initial {@code time_in_state} file for {@link ProcTimeInStateReader}
+     */
+    private static final Path DEFAULT_INITIAL_TIME_IN_STATE_PATH =
+            DEFAULT_PROC_PATH.resolve("self/time_in_state");
+
+    /**
+     * Where the proc filesystem is mounted
+     */
+    private final Path mProcPath;
+
+    /**
+     * Frequencies read from the {@code time_in_state} file. Read from {@link
+     * #mProcTimeInStateReader#getCpuFrequenciesKhz()} and cast to {@code int[]}
+     */
+    private final int[] mFrequenciesKhz;
+
+    /**
+     * Used to read and parse {@code time_in_state} files
+     */
+    private final ProcTimeInStateReader mProcTimeInStateReader;
+
+    private KernelCpuThreadReader() throws IOException {
+        this(DEFAULT_PROC_PATH, DEFAULT_INITIAL_TIME_IN_STATE_PATH);
+    }
+
+    /**
+     * Create with a path where `proc` is mounted. Used primarily for testing
+     *
+     * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc})
+     * @param initialTimeInStatePath where the initial {@code time_in_state} file exists to define
+     * format
+     */
+    @VisibleForTesting
+    public KernelCpuThreadReader(Path procPath, Path initialTimeInStatePath) throws IOException {
+        mProcPath = procPath;
+        mProcTimeInStateReader = new ProcTimeInStateReader(initialTimeInStatePath);
+
+        // Copy mProcTimeInState's frequencies, casting the longs to ints
+        long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz();
+        mFrequenciesKhz = new int[frequenciesKhz.length];
+        for (int i = 0; i < frequenciesKhz.length; i++) {
+            mFrequenciesKhz[i] = (int) frequenciesKhz[i];
+        }
+    }
+
+    /**
+     * Create the reader and handle exceptions during creation
+     *
+     * @return the reader, null if an exception was thrown during creation
+     */
+    @Nullable
+    public static KernelCpuThreadReader create() {
+        try {
+            return new KernelCpuThreadReader();
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to initialize KernelCpuThreadReader", e);
+            return null;
+        }
+    }
+
+    /**
+     * Read all of the CPU usage statistics for each child thread of the current process
+     *
+     * @return process CPU usage containing usage of all child threads
+     */
+    @Nullable
+    public ProcessCpuUsage getCurrentProcessCpuUsage() {
+        return getProcessCpuUsage(
+                mProcPath.resolve("self"),
+                Process.myPid(),
+                Process.myUid());
+    }
+
+    /**
+     * Read all of the CPU usage statistics for each child thread of a process
+     *
+     * @param processPath the {@code /proc} path of the thread
+     * @param processId the ID of the process
+     * @param uid the ID of the user who owns the process
+     * @return process CPU usage containing usage of all child threads
+     */
+    @Nullable
+    private ProcessCpuUsage getProcessCpuUsage(Path processPath, int processId, int uid) {
+        if (DEBUG) {
+            Slog.d(TAG, "Reading CPU thread usages with directory " + processPath
+                    + " process ID " + processId
+                    + " and user ID " + uid);
+        }
+
+        final Path allThreadsPath = processPath.resolve("task");
+        final ArrayList<ThreadCpuUsage> threadCpuUsages = new ArrayList<>();
+        try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(allThreadsPath)) {
+            for (Path threadDirectory : threadPaths) {
+                ThreadCpuUsage threadCpuUsage = getThreadCpuUsage(threadDirectory);
+                if (threadCpuUsage != null) {
+                    threadCpuUsages.add(threadCpuUsage);
+                }
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to iterate over thread paths", e);
+            return null;
+        }
+
+        // If we found no threads, then the process has exited while we were reading from it
+        if (threadCpuUsages.isEmpty()) {
+            return null;
+        }
+
+        if (DEBUG) {
+            Slog.d(TAG, "Read CPU usage of " + threadCpuUsages.size() + " threads");
+        }
+        return new ProcessCpuUsage(
+                processId,
+                getProcessName(processPath),
+                uid,
+                threadCpuUsages);
+    }
+
+    /**
+     * Get the CPU frequencies that correspond to the times reported in
+     * {@link ThreadCpuUsage#usageTimesMillis}
+     */
+    @Nullable
+    public int[] getCpuFrequenciesKhz() {
+        return mFrequenciesKhz;
+    }
+
+    /**
+     * Get a thread's CPU usage
+     *
+     * @param threadDirectory the {@code /proc} directory of the thread
+     * @return null in the case that the directory read failed
+     */
+    @Nullable
+    private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) {
+        // Get the thread ID from the directory name
+        final int threadId;
+        try {
+            final String directoryName = threadDirectory.getFileName().toString();
+            threadId = Integer.parseInt(directoryName);
+        } catch (NumberFormatException e) {
+            Slog.w(TAG, "Failed to parse thread ID when iterating over /proc/*/task", e);
+            return null;
+        }
+
+        // Get the thread name from the thread directory
+        final String threadName = getThreadName(threadDirectory);
+
+        // Get the CPU statistics from the directory
+        final Path threadCpuStatPath = threadDirectory.resolve(CPU_STATISTICS_FILENAME);
+        final long[] cpuUsagesLong = mProcTimeInStateReader.getUsageTimesMillis(threadCpuStatPath);
+        if (cpuUsagesLong == null) {
+            return null;
+        }
+
+        // Convert long[] to int[]
+        final int[] cpuUsages = new int[cpuUsagesLong.length];
+        for (int i = 0; i < cpuUsagesLong.length; i++) {
+            cpuUsages[i] = (int) cpuUsagesLong[i];
+        }
+
+        return new ThreadCpuUsage(threadId, threadName, cpuUsages);
+    }
+
+    /**
+     * Get the command used to start a process
+     */
+    private String getProcessName(Path processPath) {
+        final Path processNamePath = processPath.resolve(PROCESS_NAME_FILENAME);
+
+        final String processName =
+                ProcStatsUtil.readSingleLineProcFile(processNamePath.toString());
+        if (processName != null) {
+            return processName;
+        }
+        return DEFAULT_PROCESS_NAME;
+    }
+
+    /**
+     * Get the name of a thread, given the {@code /proc} path of the thread
+     */
+    private String getThreadName(Path threadPath) {
+        final Path threadNamePath = threadPath.resolve(THREAD_NAME_FILENAME);
+        final String threadName =
+                ProcStatsUtil.readNullSeparatedFile(threadNamePath.toString());
+        if (threadName == null) {
+            return DEFAULT_THREAD_NAME;
+        }
+        return threadName;
+    }
+
+    /**
+     * CPU usage of a process
+     */
+    public static class ProcessCpuUsage {
+        public final int processId;
+        public final String processName;
+        public final int uid;
+        public final ArrayList<ThreadCpuUsage> threadCpuUsages;
+
+        ProcessCpuUsage(
+                int processId,
+                String processName,
+                int uid,
+                ArrayList<ThreadCpuUsage> threadCpuUsages) {
+            this.processId = processId;
+            this.processName = processName;
+            this.uid = uid;
+            this.threadCpuUsages = threadCpuUsages;
+        }
+    }
+
+    /**
+     * CPU usage of a thread
+     */
+    public static class ThreadCpuUsage {
+        public final int threadId;
+        public final String threadName;
+        public final int[] usageTimesMillis;
+
+        ThreadCpuUsage(
+                int threadId,
+                String threadName,
+                int[] usageTimesMillis) {
+            this.threadId = threadId;
+            this.threadName = threadName;
+            this.usageTimesMillis = usageTimesMillis;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/ProcStatsUtil.java b/core/java/com/android/internal/os/ProcStatsUtil.java
new file mode 100644
index 0000000..3d4df89
--- /dev/null
+++ b/core/java/com/android/internal/os/ProcStatsUtil.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2018 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.internal.os;
+
+import android.annotation.Nullable;
+import android.os.StrictMode;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * Utility functions for reading {@code proc} files
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+public final class ProcStatsUtil {
+
+    private static final String TAG = "ProcStatsUtil";
+
+    /**
+     * How much to read into a buffer when reading a proc file
+     */
+    private static final int READ_SIZE = 1024;
+
+    /**
+     * Class only contains static utility functions, and should not be instantiated
+     */
+    private ProcStatsUtil() {
+    }
+
+    /**
+     * Read a {@code proc} file where the contents are separated by null bytes. Replaces the null
+     * bytes with spaces, and removes any trailing null bytes
+     *
+     * @param path path of the file to read
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+    @Nullable
+    public static String readNullSeparatedFile(String path) {
+        String contents = readSingleLineProcFile(path);
+        if (contents == null) {
+            return null;
+        }
+
+        // Content is either double-null terminated, or terminates at end of line. Remove anything
+        // after the double-null
+        final int endIndex = contents.indexOf("\0\0");
+        if (endIndex != -1) {
+            contents = contents.substring(0, endIndex);
+        }
+
+        // Change the null-separated contents into space-seperated
+        return contents.replace("\0", " ");
+    }
+
+    /**
+     * Read a {@code proc} file that contains a single line (e.g. {@code /proc/$PID/cmdline}, {@code
+     * /proc/$PID/comm})
+     *
+     * @param path path of the file to read
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+    @Nullable
+    public static String readSingleLineProcFile(String path) {
+        return readTerminatedProcFile(path, (byte) '\n');
+    }
+
+    /**
+     * Read a {@code proc} file that terminates with a specific byte
+     *
+     * @param path path of the file to read
+     * @param terminator byte that terminates the file. We stop reading once this character is
+     * seen, or at the end of the file
+     */
+    @Nullable
+    public static String readTerminatedProcFile(String path, byte terminator) {
+        // Permit disk reads here, as /proc isn't really "on disk" and should be fast.
+        // TODO: make BlockGuard ignore /proc/ and /sys/ files perhaps?
+        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
+        try (FileInputStream is = new FileInputStream(path)) {
+            ByteArrayOutputStream byteStream = null;
+            final byte[] buffer = new byte[READ_SIZE];
+            while (true) {
+                // Read file into buffer
+                final int len = is.read(buffer);
+                if (len <= 0) {
+                    // If we've read nothing, we're done
+                    break;
+                }
+
+                // Find the terminating character
+                int terminatingIndex = -1;
+                for (int i = 0; i < len; i++) {
+                    if (buffer[i] == terminator) {
+                        terminatingIndex = i;
+                        break;
+                    }
+                }
+                final boolean foundTerminator = terminatingIndex != -1;
+
+                // If we have found it and the byte stream isn't initialized, we don't need to
+                // initialize it and can return the string here
+                if (foundTerminator && byteStream == null) {
+                    return new String(buffer, 0, terminatingIndex);
+                }
+
+                // Initialize the byte stream
+                if (byteStream == null) {
+                    byteStream = new ByteArrayOutputStream(READ_SIZE);
+                }
+
+                // Write the whole buffer if terminator not found, or up to the terminator if found
+                byteStream.write(buffer, 0, foundTerminator ? terminatingIndex : len);
+
+                // If we've found the terminator, we can finish
+                if (foundTerminator) {
+                    break;
+                }
+            }
+
+            // If the byte stream is null at the end, this means that we have read an empty file
+            if (byteStream == null) {
+                return "";
+            }
+            return byteStream.toString();
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to open proc file", e);
+            return null;
+        } finally {
+            StrictMode.setThreadPolicy(savedPolicy);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/ProcTimeInStateReader.java b/core/java/com/android/internal/os/ProcTimeInStateReader.java
new file mode 100644
index 0000000..3a63498
--- /dev/null
+++ b/core/java/com/android/internal/os/ProcTimeInStateReader.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2018 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.internal.os;
+
+import android.annotation.Nullable;
+import android.os.Process;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * Reads and parses {@code time_in_state} files in the {@code proc} filesystem.
+ *
+ * Every line in a {@code time_in_state} file contains two numbers, separated by a single space
+ * character. The first number is the frequency of the CPU used in kilohertz. The second number is
+ * the time spent in this frequency. In the {@code time_in_state} file, this is given in 10s of
+ * milliseconds, but this class returns in milliseconds. This can be per user, process, or thread
+ * depending on which {@code time_in_state} file is used.
+ *
+ * For example, a {@code time_in_state} file would look like this:
+ * <pre>
+ *   300000 3
+ *   364800 0
+ *   ...
+ *   1824000 0
+ *   1900800 1
+ * </pre>
+ *
+ * This file would indicate that the CPU has spent 30 milliseconds at frequency 300,000KHz (300Mhz)
+ * and 10 milliseconds at frequency 1,900,800KHz (1.9GHz).
+ */
+public class ProcTimeInStateReader {
+    private static final String TAG = "ProcTimeInStateReader";
+
+    /**
+     * The format of a single line of the {@code time_in_state} file that exports the frequency
+     * values
+     */
+    private static final int[] TIME_IN_STATE_LINE_FREQUENCY_FORMAT = {
+            Process.PROC_OUT_LONG | Process.PROC_SPACE_TERM,
+            Process.PROC_NEWLINE_TERM,
+    };
+
+    /**
+     * The format of a single line of the {@code time_in_state} file that exports the time values
+     */
+    private static final int[] TIME_IN_STATE_LINE_TIME_FORMAT = {
+            Process.PROC_SPACE_TERM,
+            Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM,
+    };
+
+    /**
+     * The format of the {@code time_in_state} file, defined using {@link Process}'s {@code
+     * PROC_OUT_LONG} and related variables
+     *
+     * Defined on first successful read of {@code time_in_state} file.
+     */
+    private int[] mTimeInStateTimeFormat;
+
+    /**
+     * The frequencies reported in each {@code time_in_state} file
+     *
+     * Defined on first successful read of {@code time_in_state} file.
+     */
+    private long[] mFrequenciesKhz;
+
+    /**
+     * @param initialTimeInStateFile the file to base the format of the frequency files on, and to
+     * read frequencies from. Expected to be in the same format as all other {@code time_in_state}
+     * files, and contain the same frequencies.
+     * @throws IOException if reading the initial {@code time_in_state} file failed
+     */
+    public ProcTimeInStateReader(Path initialTimeInStateFile) throws IOException {
+        initializeTimeInStateFormat(initialTimeInStateFile);
+    }
+
+    /**
+     * Read the CPU usages from a file
+     *
+     * @param timeInStatePath path where the CPU usages are read from
+     * @return list of CPU usage times from the file. These correspond to the CPU frequencies given
+     * by {@link ProcTimeInStateReader#getFrequenciesKhz}
+     */
+    @Nullable
+    public long[] getUsageTimesMillis(final Path timeInStatePath) {
+        // Read in the time_in_state file
+        final long[] readLongs = new long[mFrequenciesKhz.length];
+        final boolean readSuccess = Process.readProcFile(
+                timeInStatePath.toString(),
+                mTimeInStateTimeFormat,
+                null, readLongs, null);
+        if (!readSuccess) {
+            return null;
+        }
+        // Usage time is given in 10ms, so convert to ms
+        for (int i = 0; i < readLongs.length; i++) {
+            readLongs[i] *= 10;
+        }
+        return readLongs;
+    }
+
+    /**
+     * Get the frequencies found in each {@code time_in_state} file
+     *
+     * @return list of CPU frequencies. These correspond to the CPU times given by {@link
+     * ProcTimeInStateReader#getUsageTimesMillis(Path)}()}.
+     */
+    @Nullable
+    public long[] getFrequenciesKhz() {
+        return mFrequenciesKhz;
+    }
+
+    /**
+     * Set the {@link #mTimeInStateTimeFormat} and {@link #mFrequenciesKhz} variables based on the
+     * an input file. If the file is empty, these variables aren't set
+     *
+     * This needs to be run once on the first invocation of {@link #getUsageTimesMillis(Path)}. This
+     * is because we need to know how many frequencies are available in order to parse time
+     * {@code time_in_state} file using {@link Process#readProcFile}, which only accepts
+     * fixed-length formats. Also, as the frequencies do not change between {@code time_in_state}
+     * files, we read and store them here.
+     *
+     * @param timeInStatePath the input file to base the format off of
+     */
+    private void initializeTimeInStateFormat(final Path timeInStatePath) throws IOException {
+        // Read the bytes of the `time_in_state` file
+        byte[] timeInStateBytes = Files.readAllBytes(timeInStatePath);
+
+        // The number of lines in the `time_in_state` file is the number of frequencies available
+        int numFrequencies = 0;
+        for (int i = 0; i < timeInStateBytes.length; i++) {
+            if (timeInStateBytes[i] == '\n') {
+                numFrequencies++;
+            }
+        }
+        if (numFrequencies == 0) {
+            throw new IOException("Empty time_in_state file");
+        }
+
+        // Set `mTimeInStateTimeFormat` and `timeInStateFrequencyFormat` to the correct length, and
+        // then copy in the `TIME_IN_STATE_{FREQUENCY,TIME}_LINE_FORMAT` until it's full. As we only
+        // use the frequency format in this method, it is not an member variable.
+        final int[] timeInStateTimeFormat =
+                new int[numFrequencies * TIME_IN_STATE_LINE_TIME_FORMAT.length];
+        final int[] timeInStateFrequencyFormat =
+                new int[numFrequencies * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length];
+        for (int i = 0; i < numFrequencies; i++) {
+            System.arraycopy(
+                    TIME_IN_STATE_LINE_FREQUENCY_FORMAT, 0, timeInStateFrequencyFormat,
+                    i * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length,
+                    TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length);
+            System.arraycopy(
+                    TIME_IN_STATE_LINE_TIME_FORMAT, 0, timeInStateTimeFormat,
+                    i * TIME_IN_STATE_LINE_TIME_FORMAT.length,
+                    TIME_IN_STATE_LINE_TIME_FORMAT.length);
+        }
+
+        // Read the frequencies from the `time_in_state` file and store them, as they will be the
+        // same for every `time_in_state` file
+        final long[] readLongs = new long[numFrequencies];
+        final boolean readSuccess = Process.parseProcLine(
+                timeInStateBytes, 0, timeInStateBytes.length, timeInStateFrequencyFormat,
+                null, readLongs, null);
+        if (!readSuccess) {
+            throw new IOException("Failed to parse time_in_state file");
+        }
+
+        mTimeInStateTimeFormat = timeInStateTimeFormat;
+        mFrequenciesKhz = readLongs;
+    }
+}
diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index 1ee4269..4b878c7 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -28,10 +28,7 @@
 
 import com.android.internal.util.FastPrintWriter;
 
-import libcore.io.IoUtils;
-
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.text.SimpleDateFormat;
@@ -40,7 +37,6 @@
 import java.util.Comparator;
 import java.util.Date;
 import java.util.List;
-import java.util.StringTokenizer;
 
 public class ProcessCpuTracker {
     private static final String TAG = "ProcessCpuTracker";
@@ -176,8 +172,6 @@
 
     private boolean mFirst = true;
 
-    private byte[] mBuffer = new byte[4096];
-
     public interface FilterStats {
         /** Which stats to pick when filtering */
         boolean needed(Stats stats);
@@ -863,40 +857,11 @@
         pw.println();
     }
 
-    private String readFile(String file, char endChar) {
-        // Permit disk reads here, as /proc/meminfo isn't really "on
-        // disk" and should be fast.  TODO: make BlockGuard ignore
-        // /proc/ and /sys/ files perhaps?
-        StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
-        FileInputStream is = null;
-        try {
-            is = new FileInputStream(file);
-            int len = is.read(mBuffer);
-            is.close();
-
-            if (len > 0) {
-                int i;
-                for (i=0; i<len; i++) {
-                    if (mBuffer[i] == endChar) {
-                        break;
-                    }
-                }
-                return new String(mBuffer, 0, i);
-            }
-        } catch (java.io.FileNotFoundException e) {
-        } catch (java.io.IOException e) {
-        } finally {
-            IoUtils.closeQuietly(is);
-            StrictMode.setThreadPolicy(savedPolicy);
-        }
-        return null;
-    }
-
     private void getName(Stats st, String cmdlineFile) {
         String newName = st.name;
         if (st.name == null || st.name.equals("app_process")
                 || st.name.equals("<pre-initialized>")) {
-            String cmdName = readFile(cmdlineFile, '\0');
+            String cmdName = ProcStatsUtil.readTerminatedProcFile(cmdlineFile, (byte) '\0');
             if (cmdName != null && cmdName.length() > 1) {
                 newName = cmdName;
                 int i = newName.lastIndexOf("/");
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 98b7b5d..65213c0 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -216,6 +216,11 @@
      */
     native protected static void nativeUnmountStorageOnInit();
 
+    private static void callPostForkSystemServerHooks() {
+        // SystemServer specific post fork hooks run before child post fork hooks.
+        VM_HOOKS.postForkSystemServer();
+    }
+
     private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
             boolean isZygote, String instructionSet) {
         VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index f14007b..cc958f4 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -69,7 +69,6 @@
     private ColorDrawable mNavigationBarColor;
     private boolean mOldFullscreen;
     private boolean mFullscreen;
-    private final int mResizeMode;
     private final Rect mOldSystemInsets = new Rect();
     private final Rect mOldStableInsets = new Rect();
     private final Rect mSystemInsets = new Rect();
@@ -79,7 +78,7 @@
     public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
             Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
             Drawable userCaptionBackgroundDrawable, int statusBarColor, int navigationBarColor,
-            boolean fullscreen, Rect systemInsets, Rect stableInsets, int resizeMode) {
+            boolean fullscreen, Rect systemInsets, Rect stableInsets) {
         setName("ResizeFrame");
 
         mRenderer = renderer;
@@ -100,7 +99,6 @@
         mStableInsets.set(stableInsets);
         mOldSystemInsets.set(systemInsets);
         mOldStableInsets.set(stableInsets);
-        mResizeMode = resizeMode;
 
         // Kick off our draw thread.
         start();
@@ -109,33 +107,35 @@
     void onResourcesLoaded(DecorView decorView, Drawable resizingBackgroundDrawable,
             Drawable captionBackgroundDrawableDrawable, Drawable userCaptionBackgroundDrawable,
             int statusBarColor, int navigationBarColor) {
-        mDecorView = decorView;
-        mResizingBackgroundDrawable = resizingBackgroundDrawable != null
-                        && resizingBackgroundDrawable.getConstantState() != null
-                ? resizingBackgroundDrawable.getConstantState().newDrawable()
-                : null;
-        mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable != null
-                        && captionBackgroundDrawableDrawable.getConstantState() != null
-                ? captionBackgroundDrawableDrawable.getConstantState().newDrawable()
-                : null;
-        mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable != null
-                        && userCaptionBackgroundDrawable.getConstantState() != null
-                ? userCaptionBackgroundDrawable.getConstantState().newDrawable()
-                : null;
-        if (mCaptionBackgroundDrawable == null) {
-            mCaptionBackgroundDrawable = mResizingBackgroundDrawable;
-        }
-        if (statusBarColor != 0) {
-            mStatusBarColor = new ColorDrawable(statusBarColor);
-            addSystemBarNodeIfNeeded();
-        } else {
-            mStatusBarColor = null;
-        }
-        if (navigationBarColor != 0) {
-            mNavigationBarColor = new ColorDrawable(navigationBarColor);
-            addSystemBarNodeIfNeeded();
-        } else {
-            mNavigationBarColor = null;
+        synchronized (this) {
+            mDecorView = decorView;
+            mResizingBackgroundDrawable = resizingBackgroundDrawable != null
+                    && resizingBackgroundDrawable.getConstantState() != null
+                    ? resizingBackgroundDrawable.getConstantState().newDrawable()
+                    : null;
+            mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable != null
+                    && captionBackgroundDrawableDrawable.getConstantState() != null
+                    ? captionBackgroundDrawableDrawable.getConstantState().newDrawable()
+                    : null;
+            mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable != null
+                    && userCaptionBackgroundDrawable.getConstantState() != null
+                    ? userCaptionBackgroundDrawable.getConstantState().newDrawable()
+                    : null;
+            if (mCaptionBackgroundDrawable == null) {
+                mCaptionBackgroundDrawable = mResizingBackgroundDrawable;
+            }
+            if (statusBarColor != 0) {
+                mStatusBarColor = new ColorDrawable(statusBarColor);
+                addSystemBarNodeIfNeeded();
+            } else {
+                mStatusBarColor = null;
+            }
+            if (navigationBarColor != 0) {
+                mNavigationBarColor = new ColorDrawable(navigationBarColor);
+                addSystemBarNodeIfNeeded();
+            } else {
+                mNavigationBarColor = null;
+            }
         }
     }
 
@@ -186,7 +186,7 @@
      * All resources of the renderer will be released. This function can be called from the
      * the UI thread as well as the renderer thread.
      */
-    public void releaseRenderer() {
+    void releaseRenderer() {
         synchronized (this) {
             if (mRenderer != null) {
                 // Invalidate the current content bounds.
@@ -268,7 +268,7 @@
      * @param ySize The height of the content.
      * @return true if a frame should be requested after the content is drawn; false otherwise.
      */
-    public boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) {
+    boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) {
         synchronized (this) {
             final boolean firstCall = mLastContentWidth == 0;
             // The current content buffer is drawn here.
@@ -291,7 +291,7 @@
         }
     }
 
-    public void onRequestDraw(boolean reportNextDraw) {
+    void onRequestDraw(boolean reportNextDraw) {
         synchronized (this) {
             mReportNextDraw = reportNextDraw;
             mOldTargetRect.set(0, 0, 0, 0);
@@ -329,8 +329,8 @@
             return;
         }
 
-        // Since the surface is spanning the entire screen, we have to add the start offset of
-        // the bounds to get to the surface location.
+        // Content may not be drawn at the surface origin, so we want to keep the offset when we're
+        // resizing it.
         final int left = mLastXOffset + newBounds.left;
         final int top = mLastYOffset + newBounds.top;
         final int width = newBounds.width();
@@ -414,6 +414,8 @@
     }
 
     void setUserCaptionBackgroundDrawable(Drawable userCaptionBackgroundDrawable) {
-        mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable;
+        synchronized (this) {
+            mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable;
+        }
     }
 }
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 94140ab..e2c23de 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1001,7 +1001,7 @@
                         insets.getSystemWindowInsetRight(), 0);
             }
         }
-        mFrameOffsets.set(insets.getSystemWindowInsets());
+        mFrameOffsets.set(insets.getSystemWindowInsetsAsRect());
         insets = updateColorViews(insets, true /* animate */);
         insets = updateStatusGuard(insets);
         if (getForeground() != null) {
@@ -2093,7 +2093,7 @@
                     initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                     mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
                     getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
-                    stableInsets, resizeMode);
+                    stableInsets);
 
             // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
             // If we want to get the shadow shown while resizing, we would need to elevate a new
@@ -2229,7 +2229,14 @@
         // or it didn't change.
         if ((wasAdjustedForStack || mElevationAdjustedForStack)
                 && getElevation() != elevation) {
-            mWindow.setElevation(elevation);
+            if (!isResizing()) {
+                mWindow.setElevation(elevation);
+            } else {
+                // Just suppress the shadow when resizing, don't adjust surface insets because it'll
+                // cause a flicker when drag resize for freeform window starts. #onContentDrawn()
+                // will compensate the offset when passing to BackdropFrameRenderer.
+                setElevation(elevation);
+            }
         }
     }
 
diff --git a/core/java/com/android/internal/util/ScreenRecordHelper.java b/core/java/com/android/internal/util/ScreenRecordHelper.java
new file mode 100644
index 0000000..64d0898
--- /dev/null
+++ b/core/java/com/android/internal/util/ScreenRecordHelper.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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.internal.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Helper class to initiate a screen recording
+ */
+public class ScreenRecordHelper {
+    private static final String SYSUI_PACKAGE = "com.android.systemui";
+    private static final String SYSUI_SCREENRECORD_LAUNCHER =
+            "com.android.systemui.screenrecord.ScreenRecordDialog";
+
+    private final Context mContext;
+
+    /**
+     * Create a new ScreenRecordHelper for the given context
+     * @param context
+     */
+    public ScreenRecordHelper(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Show dialog of screen recording options to user.
+     */
+    public void launchRecordPrompt() {
+        final ComponentName launcherComponent = new ComponentName(SYSUI_PACKAGE,
+                SYSUI_SCREENRECORD_LAUNCHER);
+        final Intent intent = new Intent();
+        intent.setComponent(launcherComponent);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent);
+    }
+}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 29c55c2..1e71bd1 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -55,7 +55,8 @@
     // @NonNull
     InputBindResult startInputOrWindowGainedFocus(
             /* @StartInputReason */ int startInputReason,
-            in IInputMethodClient client, in IBinder windowToken, int controlFlags,
+            in IInputMethodClient client, in IBinder windowToken,
+            /* @StartInputFlags */ int startInputFlags,
             /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
             int windowFlags, in EditorInfo attribute, IInputContext inputContext,
             /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags,
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index 4a1c955..ba0ff01 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -313,8 +313,7 @@
         pullChildren();
 
         final int vis = getWindowSystemUiVisibility();
-        final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
-        final Rect systemInsets = insets.getSystemWindowInsets();
+        final Rect systemInsets = insets.getSystemWindowInsetsAsRect();
 
         // The top and bottom action bars are always within the content area.
         boolean changed = applyInsets(mActionBarTop, systemInsets, true, true, false, true);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 80d8063..c96bacd 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -55,21 +55,21 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
+
 import com.google.android.collect.Lists;
 
 import libcore.util.HexEncoding;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.StringJoiner;
+
 /**
  * Utilities for the lock pattern and its settings.
  */
@@ -1527,7 +1527,7 @@
     /**
      * @see StrongAuthTracker#isFingerprintAllowedForUser
      */
-    public boolean isFingerprintAllowedForUser(int userId) {
+    public boolean isBiometricAllowedForUser(int userId) {
         return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_BIOMETRIC) == 0;
     }
 
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index b799728..25a5a07 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -69,7 +69,7 @@
     private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h)
 
     private static final boolean PROFILE_DRAWING = false;
-    private static final float LINE_FADE_ALPHA_MULTIPLIER = 3.5f;
+    private static final float LINE_FADE_ALPHA_MULTIPLIER = 1.5f;
     private final CellState[][] mCellStates;
 
     private final int mDotSize;
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index d68e8f8..15745e9 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -26,6 +26,7 @@
 import android.os.Environment;
 import android.os.Process;
 import android.os.storage.StorageManager;
+import android.permission.PermissionManager.SplitPermissionInfo;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -74,6 +75,8 @@
     // system configuration files.
     final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>();
 
+    final ArrayList<SplitPermissionInfo> mSplitPermissions = new ArrayList<>();
+
     // These are the built-in shared libraries that were read from the
     // system configuration files.  Keys are the library names; strings are the
     // paths to the libraries.
@@ -192,6 +195,10 @@
         return mSystemPermissions;
     }
 
+    public ArrayList<SplitPermissionInfo> getSplitPermissions() {
+        return mSplitPermissions;
+    }
+
     public ArrayMap<String, String> getSharedLibraries() {
         return mSharedLibraries;
     }
@@ -337,16 +344,11 @@
         readPermissions(Environment.buildPath(
                 Environment.getProductDirectory(), "etc", "permissions"), ALLOW_ALL);
 
-        // Allow /product_services to customize system configs around libs, features, permissions
-        // and apps.
-        int productServicesPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS |
-                ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS;
+        // Allow /product_services to customize all system configs
         readPermissions(Environment.buildPath(
-                Environment.getProductServicesDirectory(), "etc", "sysconfig"),
-                productServicesPermissionFlag);
+                Environment.getProductServicesDirectory(), "etc", "sysconfig"), ALLOW_ALL);
         readPermissions(Environment.buildPath(
-                Environment.getProductServicesDirectory(), "etc", "permissions"),
-                productServicesPermissionFlag);
+                Environment.getProductServicesDirectory(), "etc", "permissions"), ALLOW_ALL);
     }
 
     void readPermissions(File libraryDir, int permissionFlag) {
@@ -489,6 +491,8 @@
                     perms.add(perm);
                     XmlUtils.skipCurrentTag(parser);
 
+                } else if ("split-permission".equals(name) && allowPermissions) {
+                    readSplitPermission(parser, permFile);
                 } else if ("library".equals(name) && allowLibs) {
                     String lname = parser.getAttributeValue(null, "name");
                     String lfile = parser.getAttributeValue(null, "file");
@@ -888,4 +892,46 @@
         }
         mOemPermissions.put(packageName, permissions);
     }
+
+    private void readSplitPermission(XmlPullParser parser, File permFile)
+            throws IOException, XmlPullParserException {
+        String splitPerm = parser.getAttributeValue(null, "name");
+        if (splitPerm == null) {
+            Slog.w(TAG, "<split-permission> without name in " + permFile + " at "
+                    + parser.getPositionDescription());
+            XmlUtils.skipCurrentTag(parser);
+            return;
+        }
+        String targetSdkStr = parser.getAttributeValue(null, "targetSdk");
+        int targetSdk = Build.VERSION_CODES.CUR_DEVELOPMENT + 1;
+        if (!TextUtils.isEmpty(targetSdkStr)) {
+            try {
+                targetSdk = Integer.parseInt(targetSdkStr);
+            } catch (NumberFormatException e) {
+                Slog.w(TAG, "<split-permission> targetSdk not an integer in " + permFile + " at "
+                        + parser.getPositionDescription());
+                XmlUtils.skipCurrentTag(parser);
+                return;
+            }
+        }
+        final int depth = parser.getDepth();
+        List<String> newPermissions = new ArrayList<>();
+        while (XmlUtils.nextElementWithin(parser, depth)) {
+            String name = parser.getName();
+            if ("new-permission".equals(name)) {
+                final String newName = parser.getAttributeValue(null, "name");
+                if (TextUtils.isEmpty(newName)) {
+                    Slog.w(TAG, "name is required for <new-permission> in "
+                            + parser.getPositionDescription());
+                    continue;
+                }
+                newPermissions.add(newName);
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+        if (!newPermissions.isEmpty()) {
+            mSplitPermissions.add(new SplitPermissionInfo(splitPerm, newPermissions, targetSdk));
+        }
+    }
 }
diff --git a/core/java/com/android/server/net/BaseNetdEventCallback.java b/core/java/com/android/server/net/BaseNetdEventCallback.java
index fdba2f3..97247aa 100644
--- a/core/java/com/android/server/net/BaseNetdEventCallback.java
+++ b/core/java/com/android/server/net/BaseNetdEventCallback.java
@@ -26,8 +26,8 @@
  */
 public class BaseNetdEventCallback extends INetdEventCallback.Stub {
     @Override
-    public void onDnsEvent(String hostname, String[] ipAddresses,
-            int ipAddressesCount, long timestamp, int uid) {
+    public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+            String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
         // default no-op
     }
 
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 59c29e2..c96aaba 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -42,6 +42,7 @@
         "com_google_android_gles_jni_EGLImpl.cpp",
         "com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm
         "android_app_Activity.cpp",
+	"android_app_ActivityThread.cpp",
         "android_app_NativeActivity.cpp",
         "android_app_admin_SecurityLog.cpp",
         "android_opengl_EGL14.cpp",
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index fb571df..2e7501f 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -83,7 +83,8 @@
         return 0;
     }
     std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
-            builder->langId, builder->variant, std::move(builder->fonts));
+            builder->langId, builder->variant, std::move(builder->fonts),
+            true /* isCustomFallback */);
     if (family->getCoverage().length() == 0) {
         return 0;
     }
@@ -129,7 +130,7 @@
         return false;
     }
     std::shared_ptr<minikin::MinikinFont> minikinFont =
-            std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex,
+            std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, "", ttcIndex,
                     builder->axes);
     minikin::Font::Builder fontBuilder(minikinFont);
 
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index d391de7..a8b0640 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -28,6 +28,7 @@
 
 #include "SkBlurDrawLooper.h"
 #include "SkColorFilter.h"
+#include "SkFontTypes.h"
 #include "SkMaskFilter.h"
 #include "SkPath.h"
 #include "SkPathEffect.h"
@@ -684,13 +685,13 @@
     }
 
     static jint getHinting(jlong paintHandle) {
-        return reinterpret_cast<Paint*>(paintHandle)->getHinting()
-                == Paint::kNo_Hinting ? 0 : 1;
+        return (SkFontHinting)reinterpret_cast<Paint*>(paintHandle)->getHinting()
+                == kNo_SkFontHinting ? 0 : 1;
     }
 
     static void setHinting(jlong paintHandle, jint mode) {
         reinterpret_cast<Paint*>(paintHandle)->setHinting(
-                mode == 0 ? Paint::kNo_Hinting : Paint::kNormal_Hinting);
+                mode == 0 ? kNo_SkFontHinting : kNormal_SkFontHinting);
     }
 
     static void setAntiAlias(jlong paintHandle, jboolean aa) {
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 34ec365..30d6ff4 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -20,11 +20,13 @@
 #include "FontUtils.h"
 #include "GraphicsJNI.h"
 #include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
 #include "SkTypeface.h"
 #include <android_runtime/android_util_AssetManager.h>
 #include <androidfw/AssetManager.h>
 #include <hwui/Typeface.h>
 #include <minikin/FontFamily.h>
+#include <minikin/SystemFonts.h>
 
 using namespace android;
 
@@ -108,6 +110,7 @@
 // CriticalNative
 static void Typeface_setDefault(jlong faceHandle) {
     Typeface::setDefault(toTypeface(faceHandle));
+    minikin::SystemFonts::registerDefault(toTypeface(faceHandle)->fFontCollection);
 }
 
 static jobject Typeface_getSupportedAxes(JNIEnv *env, jobject, jlong faceHandle) {
@@ -128,6 +131,12 @@
     return result;
 }
 
+static void Typeface_registerGenericFamily(JNIEnv *env, jobject, jstring familyName, jlong ptr) {
+    ScopedUtfChars familyNameChars(env, familyName);
+    minikin::SystemFonts::registerFallback(familyNameChars.c_str(),
+                                           toTypeface(ptr)->fFontCollection);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gTypefaceMethods[] = {
@@ -144,6 +153,8 @@
                                            (void*)Typeface_createFromArray },
     { "nativeSetDefault",         "(J)V",   (void*)Typeface_setDefault },
     { "nativeGetSupportedAxes",   "(J)[I",  (void*)Typeface_getSupportedAxes },
+    { "nativeRegisterGenericFamily", "(Ljava/lang/String;J)V",
+          (void*)Typeface_registerGenericFamily },
 };
 
 int register_android_graphics_Typeface(JNIEnv* env)
diff --git a/core/jni/android/graphics/fonts/Font.cpp b/core/jni/android/graphics/fonts/Font.cpp
index 2d1d7a0..8178318 100644
--- a/core/jni/android/graphics/fonts/Font.cpp
+++ b/core/jni/android/graphics/fonts/Font.cpp
@@ -134,7 +134,7 @@
 
 // Regular JNI
 static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jobject buffer,
-        jint weight, jboolean italic, jint ttcIndex) {
+        jstring filePath, jint weight, jboolean italic, jint ttcIndex) {
     NPE_CHECK_RETURN_ZERO(env, buffer);
     std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr));
     const void* fontPtr = env->GetDirectBufferAddress(buffer);
@@ -148,6 +148,7 @@
                           "buffer size must not be zero or negative");
         return 0;
     }
+    ScopedUtfChars fontPath(env, filePath);
     jobject fontRef = MakeGlobalRefOrDie(env, buffer);
     sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
             release_global_ref, reinterpret_cast<void*>(fontRef)));
@@ -171,8 +172,9 @@
         return 0;
     }
     std::shared_ptr<minikin::MinikinFont> minikinFont =
-            std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex,
-                    builder->axes);
+            std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize,
+                                              std::string_view(fontPath.c_str(), fontPath.size()),
+                                              ttcIndex, builder->axes);
     minikin::Font font = minikin::Font::Builder(minikinFont).setWeight(weight)
                     .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build();
     return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
@@ -188,7 +190,7 @@
 static const JNINativeMethod gFontBuilderMethods[] = {
     { "nInitBuilder", "()J", (void*) Font_Builder_initBuilder },
     { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
-    { "nBuild", "(JLjava/nio/ByteBuffer;IZI)J", (void*) Font_Builder_build },
+    { "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build },
     { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },
 
     { "nGetNativeAsset", "(Landroid/content/res/AssetManager;Ljava/lang/String;ZI)J",
diff --git a/core/jni/android/graphics/fonts/FontFamily.cpp b/core/jni/android/graphics/fonts/FontFamily.cpp
index 767e068..249e4f3 100644
--- a/core/jni/android/graphics/fonts/FontFamily.cpp
+++ b/core/jni/android/graphics/fonts/FontFamily.cpp
@@ -57,7 +57,7 @@
 
 // Regular JNI
 static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr,
-            jstring langTags, jint variant) {
+            jstring langTags, jint variant, jboolean isCustomFallback) {
     std::unique_ptr<NativeFamilyBuilder> builder(toBuilder(builderPtr));
     uint32_t localeId;
     if (langTags == nullptr) {
@@ -67,7 +67,8 @@
         localeId = minikin::registerLocaleList(str.c_str());
     }
     std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
-            localeId, static_cast<minikin::FamilyVariant>(variant), std::move(builder->fonts));
+            localeId, static_cast<minikin::FamilyVariant>(variant), std::move(builder->fonts),
+            isCustomFallback);
     if (family->getCoverage().length() == 0) {
         // No coverage means minikin rejected given font for some reasons.
         jniThrowException(env, "java/lang/IllegalArgumentException",
@@ -87,7 +88,7 @@
 static const JNINativeMethod gFontFamilyBuilderMethods[] = {
     { "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder },
     { "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont },
-    { "nBuild", "(JLjava/lang/String;I)J", (void*) FontFamily_Builder_build },
+    { "nBuild", "(JLjava/lang/String;IZ)J", (void*) FontFamily_Builder_build },
 
     { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
 };
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index a45b493..d50e60c 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -657,6 +657,7 @@
     switch (internalFormat) {
         // For sized internal format.
         case GL_RGBA16F:
+        case GL_SRGB8_ALPHA8:
             return GL_RGBA;
         // Base internal formats and pixel formats are still the same, see Table 1 in
         // https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
diff --git a/core/jni/android_app_ActivityThread.cpp b/core/jni/android_app_ActivityThread.cpp
new file mode 100644
index 0000000..d56e4c5
--- /dev/null
+++ b/core/jni/android_app_ActivityThread.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/JNIHelp.h>
+
+#include <minikin/Layout.h>
+#include <renderthread/RenderProxy.h>
+
+#include "core_jni_helpers.h"
+#include <unistd.h>
+
+namespace android {
+
+static void android_app_ActivityThread_purgePendingResources(JNIEnv* env, jobject clazz) {
+    // Don't care about return values.
+    mallopt(M_PURGE, 0);
+}
+
+static void
+android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) {
+    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
+    android::uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
+    minikin::Layout::dumpMinikinStats(fd);
+}
+
+
+static JNINativeMethod gActivityThreadMethods[] = {
+    // ------------ Regular JNI ------------------
+    { "nPurgePendingResources",        "()V",
+      (void*) android_app_ActivityThread_purgePendingResources },
+    { "nDumpGraphicsInfo",        "(Ljava/io/FileDescriptor;)V",
+      (void*) android_app_ActivityThread_dumpGraphics }
+};
+
+int register_android_app_ActivityThread(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/app/ActivityThread",
+            gActivityThreadMethods, NELEM(gActivityThreadMethods));
+}
+
+};
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index dca2da3..84f53468 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -523,10 +523,11 @@
     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
     const Typeface* typeface = paint->getAndroidTypeface();
     ScopedCharArrayRO text(env, charArray);
+    // drawTextString and drawTextChars doesn't use context info
     get_canvas(canvasHandle)->drawText(
-            text.get(), text.size(),  // text buffer
-            index, count,  // draw range
-            0, text.size(),  // context range
+            text.get() + index, count,  // text buffer
+            0, count,  // draw range
+            0, count,  // context range
             x, y,  // draw position
             static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr /* measured text */);
 }
@@ -537,10 +538,12 @@
     ScopedStringChars text(env, strObj);
     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
     const Typeface* typeface = paint->getAndroidTypeface();
+    const int count = end - start;
+    // drawTextString and drawTextChars doesn't use context info
     get_canvas(canvasHandle)->drawText(
-            text.get(), text.size(),  // text buffer
-            start, end - start,  // draw range
-            0, text.size(),  // context range
+            text.get() + start, count,  // text buffer
+            0, count,  // draw range
+            0, count,  // context range
             x, y,  // draw position
             static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr /* measured text */);
 }
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 83a8c2e..99b5f85 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -37,6 +37,7 @@
 #define ENCODING_AAC_XHE        16
 #define ENCODING_AC4            17
 #define ENCODING_E_AC3_JOC      18
+#define ENCODING_DOLBY_MAT      19
 
 #define ENCODING_INVALID    0
 #define ENCODING_DEFAULT    1
@@ -71,10 +72,10 @@
         return AUDIO_FORMAT_AAC_HE_V1;
     case ENCODING_AAC_HE_V2:
         return AUDIO_FORMAT_AAC_HE_V2;
-    case ENCODING_DOLBY_TRUEHD:
-        return AUDIO_FORMAT_DOLBY_TRUEHD;
     case ENCODING_IEC61937:
         return AUDIO_FORMAT_IEC61937;
+    case ENCODING_DOLBY_TRUEHD:
+        return AUDIO_FORMAT_DOLBY_TRUEHD;
     case ENCODING_AAC_ELD:
         return AUDIO_FORMAT_AAC_ELD;
     case ENCODING_AAC_XHE:
@@ -85,6 +86,8 @@
         return AUDIO_FORMAT_E_AC3_JOC;
     case ENCODING_DEFAULT:
         return AUDIO_FORMAT_DEFAULT;
+    case ENCODING_DOLBY_MAT:
+        return AUDIO_FORMAT_MAT;
     default:
         return AUDIO_FORMAT_INVALID;
     }
@@ -134,6 +137,11 @@
         return ENCODING_AC4;
     case AUDIO_FORMAT_E_AC3_JOC:
         return ENCODING_E_AC3_JOC;
+    case AUDIO_FORMAT_MAT:
+    case AUDIO_FORMAT_MAT_1_0:
+    case AUDIO_FORMAT_MAT_2_0:
+    case AUDIO_FORMAT_MAT_2_1:
+        return ENCODING_DOLBY_MAT;
     case AUDIO_FORMAT_DEFAULT:
         return ENCODING_DEFAULT;
     default:
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index d4a84c1..7410b52 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1016,6 +1016,7 @@
     jintArray jFormats = NULL;
     jobjectArray jGains = NULL;
     jobject jHandle = NULL;
+    jobject jAudioPortConfig = NULL;
     jstring jDeviceName = NULL;
     bool useInMask;
     size_t numPositionMasks = 0;
@@ -1194,7 +1195,6 @@
         goto exit;
     }
 
-    jobject jAudioPortConfig;
     jStatus = convertAudioPortConfigFromNative(env,
                                                        *jAudioPort,
                                                        &jAudioPortConfig,
@@ -1230,6 +1230,9 @@
     if (jHandle != NULL) {
         env->DeleteLocalRef(jHandle);
     }
+    if (jAudioPortConfig != NULL) {
+        env->DeleteLocalRef(jAudioPortConfig);
+    }
 
     return jStatus;
 }
@@ -1300,12 +1303,15 @@
     }
 
     for (size_t i = 0; i < numPorts; i++) {
-        jobject jAudioPort;
+        jobject jAudioPort = NULL;
         jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]);
         if (jStatus != AUDIO_JAVA_SUCCESS) {
             goto exit;
         }
         env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort);
+        if (jAudioPort != NULL) {
+            env->DeleteLocalRef(jAudioPort);
+        }
     }
 
 exit:
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index e64da5c..b1a9866 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -32,16 +32,15 @@
     android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
 }
 
-void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring appPref, jboolean devOptIn,
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jboolean devOptIn,
                          jobject rulesFd, jlong rulesOffset, jlong rulesLength) {
     ScopedUtfChars pathChars(env, path);
     ScopedUtfChars appNameChars(env, appName);
-    ScopedUtfChars appPrefChars(env, appPref);
 
     int rulesFd_native = jniGetFDFromFileDescriptor(env, rulesFd);
 
     android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
-            appPrefChars.c_str(), devOptIn, rulesFd_native, rulesOffset, rulesLength);
+            devOptIn, rulesFd_native, rulesOffset, rulesLength);
 }
 
 void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
@@ -58,12 +57,20 @@
     }
 }
 
+void setDebugLayersGLES_native(JNIEnv* env, jobject clazz, jstring layers) {
+    if (layers != nullptr) {
+        ScopedUtfChars layersChars(env, layers);
+        android::GraphicsEnv::getInstance().setDebugLayersGLES(layersChars.c_str());
+    }
+}
+
 const JNINativeMethod g_methods[] = {
     { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) },
     { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
-    { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
+    { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;ZLjava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
     { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
     { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
+    { "setDebugLayersGLES", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayersGLES_native) },
 };
 
 const char* const kGraphicsEnvironmentName = "android/os/GraphicsEnvironment";
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 830ca83..b2d44e7 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -1208,13 +1208,23 @@
   // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
 }
 
-static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr,
-                            jlong src_theme_ptr) {
+static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_asset_manager_ptr,
+                            jlong dst_theme_ptr, jlong src_asset_manager_ptr, jlong src_theme_ptr) {
   Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
   Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
-  if (!dst_theme->SetTo(*src_theme)) {
-    jniThrowException(env, "java/lang/IllegalArgumentException",
-                      "Themes are from different AssetManagers");
+
+  if (dst_asset_manager_ptr != src_asset_manager_ptr) {
+    ScopedLock<AssetManager2> dst_assetmanager(AssetManagerFromLong(dst_asset_manager_ptr));
+    CHECK(dst_theme->GetAssetManager() == &(*dst_assetmanager));
+    (void) dst_assetmanager;
+
+    ScopedLock <AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr));
+    CHECK(src_theme->GetAssetManager() == &(*src_assetmanager));
+    (void) src_assetmanager;
+
+    dst_theme->SetTo(*src_theme);
+  } else {
+    dst_theme->SetTo(*src_theme);
   }
 }
 
@@ -1255,10 +1265,11 @@
   Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
   CHECK(theme->GetAssetManager() == &(*assetmanager));
   (void) assetmanager;
-  (void) theme;
   (void) priority;
   (void) tag;
   (void) prefix;
+
+  theme->Dump();
 }
 
 static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/,
@@ -1377,7 +1388,7 @@
     {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
     {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy},
     {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
-    {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy},
+    {"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
     {"nativeThemeClear", "(J)V", (void*)NativeThemeClear},
     {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
      (void*)NativeThemeGetAttributeValue},
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index ec98080..fd042b3 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -865,17 +865,17 @@
 
 // ----------------------------------------------------------------------------
 
-static jint android_os_Binder_getCallingPid(JNIEnv* env, jobject clazz)
+static jint android_os_Binder_getCallingPid()
 {
     return IPCThreadState::self()->getCallingPid();
 }
 
-static jint android_os_Binder_getCallingUid(JNIEnv* env, jobject clazz)
+static jint android_os_Binder_getCallingUid()
 {
     return IPCThreadState::self()->getCallingUid();
 }
 
-static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
+static jlong android_os_Binder_clearCallingIdentity()
 {
     return IPCThreadState::self()->clearCallingIdentity();
 }
@@ -894,12 +894,12 @@
     IPCThreadState::self()->restoreCallingIdentity(token);
 }
 
-static void android_os_Binder_setThreadStrictModePolicy(JNIEnv* env, jobject clazz, jint policyMask)
+static void android_os_Binder_setThreadStrictModePolicy(jint policyMask)
 {
     IPCThreadState::self()->setStrictModePolicy(policyMask);
 }
 
-static jint android_os_Binder_getThreadStrictModePolicy(JNIEnv* env, jobject clazz)
+static jint android_os_Binder_getThreadStrictModePolicy()
 {
     return IPCThreadState::self()->getStrictModePolicy();
 }
@@ -950,11 +950,16 @@
 
 static const JNINativeMethod gBinderMethods[] = {
      /* name, signature, funcPtr */
+    // @CriticalNative
     { "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid },
+    // @CriticalNative
     { "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
+    // @CriticalNative
     { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
     { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
+    // @CriticalNative
     { "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
+    // @CriticalNative
     { "getThreadStrictModePolicy", "()I", (void*)android_os_Binder_getThreadStrictModePolicy },
     // @CriticalNative
     { "setThreadWorkSource", "(I)I", (void*)android_os_Binder_setThreadWorkSource },
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 8998cd7..e0fd1a6 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -88,17 +88,6 @@
     sp<InvokeRunnableMessage> mMessage;
 };
 
-
-// ---------------- Regular JNI -----------------------------
-
-static void
-android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) {
-    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
-    android::uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
-    minikin::Layout::dumpMinikinStats(fd);
-}
-
-
 // ---------------- @FastNative -----------------------------
 
 static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
@@ -205,12 +194,6 @@
     { "nDrawRoundRect",           "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
 };
 
-static JNINativeMethod gActivityThreadMethods[] = {
-        // ------------ Regular JNI ------------------
-    { "nDumpGraphicsInfo",        "(Ljava/io/FileDescriptor;)V",
-                                               (void*) android_app_ActivityThread_dumpGraphics }
-};
-
 int register_android_view_DisplayListCanvas(JNIEnv* env) {
     jclass runnableClass = FindClassOrDie(env, "java/lang/Runnable");
     gRunnableMethodId = GetMethodIDOrDie(env, runnableClass, "run", "()V");
@@ -218,9 +201,4 @@
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
-int register_android_app_ActivityThread(JNIEnv* env) {
-    return RegisterMethodsOrDie(env, "android/app/ActivityThread",
-            gActivityThreadMethods, NELEM(gActivityThreadMethods));
-}
-
 };
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index bb71a5d..e89b593 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -295,6 +295,22 @@
     return SET_AND_DIRTY(setBottom, bottom, RenderNode::Y);
 }
 
+static jint android_view_RenderNode_getLeft(jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getLeft();
+}
+
+static jint android_view_RenderNode_getTop(jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getTop();
+}
+
+static jint android_view_RenderNode_getRight(jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getRight();
+}
+
+static jint android_view_RenderNode_getBottom(jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getBottom();
+}
+
 static jboolean android_view_RenderNode_setLeftTopRightBottom(jlong renderNodePtr,
         int left, int top, int right, int bottom) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
@@ -645,6 +661,10 @@
     { "nSetTop",               "(JI)Z",  (void*) android_view_RenderNode_setTop },
     { "nSetRight",             "(JI)Z",  (void*) android_view_RenderNode_setRight },
     { "nSetBottom",            "(JI)Z",  (void*) android_view_RenderNode_setBottom },
+    { "nGetLeft",              "(J)I",  (void*) android_view_RenderNode_getLeft },
+    { "nGetTop",               "(J)I",  (void*) android_view_RenderNode_getTop },
+    { "nGetRight",             "(J)I",  (void*) android_view_RenderNode_getRight },
+    { "nGetBottom",            "(J)I",  (void*) android_view_RenderNode_getBottom },
     { "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom },
     { "nOffsetLeftAndRight",   "(JI)Z",  (void*) android_view_RenderNode_offsetLeftAndRight },
     { "nOffsetTopAndBottom",   "(JI)Z",  (void*) android_view_RenderNode_offsetTopAndBottom },
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3e04bb3..8e9a0bf 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -20,7 +20,9 @@
 #include <sys/mount.h>
 #include <linux/fs.h>
 
+#include <functional>
 #include <list>
+#include <optional>
 #include <sstream>
 #include <string>
 
@@ -70,6 +72,8 @@
 
 namespace {
 
+using namespace std::placeholders;
+
 using android::String8;
 using android::base::StringAppendF;
 using android::base::StringPrintf;
@@ -84,6 +88,7 @@
 static const char kIsolatedStorage[] = "persist.sys.isolated_storage";
 static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
 static jclass gZygoteClass;
+static jmethodID gCallPostForkSystemServerHooks;
 static jmethodID gCallPostForkChildHooks;
 
 static bool g_is_security_enforced = true;
@@ -650,12 +655,12 @@
 static FileDescriptorTable* gOpenFdTable = NULL;
 
 static bool FillFileDescriptorVector(JNIEnv* env,
-                                     jintArray java_fds,
+                                     jintArray managed_fds,
                                      std::vector<int>* fds,
                                      std::string* error_msg) {
   CHECK(fds != nullptr);
-  if (java_fds != nullptr) {
-    ScopedIntArrayRO ar(env, java_fds);
+  if (managed_fds != nullptr) {
+    ScopedIntArrayRO ar(env, managed_fds);
     if (ar.get() == nullptr) {
       *error_msg = "Bad fd array";
       return false;
@@ -668,234 +673,46 @@
   return true;
 }
 
-// Utility routine to specialize a zygote child process.
-static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
-                             jint runtime_flags, jobjectArray javaRlimits,
-                             jlong permittedCapabilities, jlong effectiveCapabilities,
-                             jint mount_external, jstring java_se_info, jstring java_se_name,
-                             bool is_system_server, bool is_child_zygote, jstring instructionSet,
-                             jstring dataDir, jstring packageName, jobjectArray packagesForUid,
-                             jobjectArray visibleVolIds) {
-  std::string error_msg;
-
-  auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
-      __attribute__ ((noreturn)) {
-    const char* se_name_c_str = nullptr;
-    std::unique_ptr<ScopedUtfChars> se_name;
-    if (java_se_name != nullptr) {
-      se_name.reset(new ScopedUtfChars(env, java_se_name));
-      se_name_c_str = se_name->c_str();
-    }
-    if (se_name_c_str == nullptr && is_system_server) {
-      se_name_c_str = "system_server";
-    }
-    const std::string& error_msg = (se_name_c_str == nullptr)
-        ? msg
-        : StringPrintf("(%s) %s", se_name_c_str, msg.c_str());
-    env->FatalError(error_msg.c_str());
-    __builtin_unreachable();
-  };
-
-  // Keep capabilities across UID change, unless we're staying root.
-  if (uid != 0) {
-    if (!EnableKeepCapabilities(&error_msg)) {
-      fail_fn(error_msg);
+[[noreturn]]
+static void ZygoteFailure(JNIEnv* env,
+                          const char* process_name,
+                          jstring managed_process_name,
+                          const std::string& msg) {
+  std::unique_ptr<ScopedUtfChars> scoped_managed_process_name_ptr = nullptr;
+  if (managed_process_name != nullptr) {
+    scoped_managed_process_name_ptr.reset(new ScopedUtfChars(env, managed_process_name));
+    if (scoped_managed_process_name_ptr->c_str() != nullptr) {
+      process_name = scoped_managed_process_name_ptr->c_str();
     }
   }
 
-  if (!SetInheritable(permittedCapabilities, &error_msg)) {
-    fail_fn(error_msg);
-  }
-  if (!DropCapabilitiesBoundingSet(&error_msg)) {
-    fail_fn(error_msg);
-  }
+  const std::string& error_msg =
+      (process_name == nullptr) ? msg : StringPrintf("(%s) %s", process_name, msg.c_str());
 
-  bool use_native_bridge = !is_system_server && (instructionSet != NULL)
-      && android::NativeBridgeAvailable();
-  if (use_native_bridge) {
-    ScopedUtfChars isa_string(env, instructionSet);
-    use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
-  }
-  if (use_native_bridge && dataDir == NULL) {
-    // dataDir should never be null if we need to use a native bridge.
-    // In general, dataDir will never be null for normal applications. It can only happen in
-    // special cases (for isolated processes which are not associated with any app). These are
-    // launched by the framework and should not be emulated anyway.
-    use_native_bridge = false;
-    ALOGW("Native bridge will not be used because dataDir == NULL.");
-  }
+  env->FatalError(error_msg.c_str());
+  __builtin_unreachable();
+}
 
-  std::string package_name_str("");
-  if (packageName != nullptr) {
-    ScopedUtfChars package(env, packageName);
-    package_name_str = package.c_str();
-  } else if (is_system_server) {
-    package_name_str = "android";
-  }
-  std::vector<std::string> packages_for_uid;
-  if (packagesForUid != nullptr) {
-    jsize count = env->GetArrayLength(packagesForUid);
-    for (jsize i = 0; i < count; ++i) {
-      jstring package_for_uid = (jstring) env->GetObjectArrayElement(packagesForUid, i);
-      ScopedUtfChars package(env, package_for_uid);
-      packages_for_uid.push_back(package.c_str());
-    }
-  }
-  std::vector<std::string> visible_vol_ids;
-  if (visibleVolIds != nullptr) {
-    jsize count = env->GetArrayLength(visibleVolIds);
-    for (jsize i = 0; i < count; ++i) {
-      jstring visible_vol_id = (jstring) env->GetObjectArrayElement(visibleVolIds, i);
-      ScopedUtfChars vol(env, visible_vol_id);
-      visible_vol_ids.push_back(vol.c_str());
-    }
-  }
-  bool success = MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg,
-      package_name_str, packages_for_uid, visible_vol_ids);
-  if (!success) {
-    ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
-    if (errno == ENOTCONN || errno == EROFS) {
-      // When device is actively encrypting, we get ENOTCONN here
-      // since FUSE was mounted before the framework restarted.
-      // When encrypted device is booting, we get EROFS since
-      // FUSE hasn't been created yet by init.
-      // In either case, continue without external storage.
+static std::optional<std::string> ExtractJString(JNIEnv* env,
+                                                 const char* process_name,
+                                                 jstring managed_process_name,
+                                                 jstring managed_string) {
+  if (managed_string == nullptr) {
+    return std::optional<std::string>();
+  } else {
+    ScopedUtfChars scoped_string_chars(env, managed_string);
+
+    if (scoped_string_chars.c_str() != nullptr) {
+      return std::optional<std::string>(scoped_string_chars.c_str());
     } else {
-      fail_fn(error_msg);
+      ZygoteFailure(env, process_name, managed_process_name, "Failed to extract JString.");
     }
   }
-
-  // If this zygote isn't root, it won't be able to create a process group,
-  // since the directory is owned by root.
-  if (!is_system_server && getuid() == 0) {
-      int rc = createProcessGroup(uid, getpid());
-      if (rc != 0) {
-          if (rc == -EROFS) {
-              ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
-          } else {
-              ALOGE("createProcessGroup(%d, %d) failed: %s", uid, 0/*pid*/, strerror(-rc));
-          }
-      }
-  }
-
-  if (!SetGids(env, javaGids, &error_msg)) {
-    fail_fn(error_msg);
-  }
-
-  if (!SetRLimits(env, javaRlimits, &error_msg)) {
-    fail_fn(error_msg);
-  }
-
-  if (use_native_bridge) {
-    ScopedUtfChars isa_string(env, instructionSet);
-    ScopedUtfChars data_dir(env, dataDir);
-    android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
-  }
-
-  int rc = setresgid(gid, gid, gid);
-  if (rc == -1) {
-    fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
-  }
-
-  // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
-  // uid from 0, which clears capabilities.  The other alternative is to call
-  // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
-  // b/71859146).  As the result, privileged syscalls used below still need to be accessible in
-  // app process.
-  SetUpSeccompFilter(uid);
-
-  rc = setresuid(uid, uid, uid);
-  if (rc == -1) {
-    fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
-  }
-
-  // The "dumpable" flag of a process, which controls core dump generation, is
-  // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
-  // user or group ID changes. See proc(5) for possible values. In most cases,
-  // the value is 0, so core dumps are disabled for zygote children. However,
-  // when running in a Chrome OS container, the value is already set to 2,
-  // which allows the external crash reporter to collect all core dumps. Since
-  // only system crashes are interested, core dump is disabled for app
-  // processes. This also ensures compliance with CTS.
-  int dumpable = prctl(PR_GET_DUMPABLE);
-  if (dumpable == -1) {
-      ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
-      RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
-  }
-  if (dumpable == 2 && uid >= AID_APP) {
-    if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
-      ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
-      RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
-    }
-  }
-
-  if (NeedsNoRandomizeWorkaround()) {
-      // Work around ARM kernel ASLR lossage (http://b/5817320).
-      int old_personality = personality(0xffffffff);
-      int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
-      if (new_personality == -1) {
-          ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
-      }
-  }
-
-  if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
-                       &error_msg)) {
-    fail_fn(error_msg);
-  }
-
-  if (!SetSchedulerPolicy(&error_msg)) {
-    fail_fn(error_msg);
-  }
-
-  const char* se_info_c_str = NULL;
-  ScopedUtfChars* se_info = NULL;
-  if (java_se_info != NULL) {
-      se_info = new ScopedUtfChars(env, java_se_info);
-      se_info_c_str = se_info->c_str();
-      if (se_info_c_str == NULL) {
-        fail_fn("se_info_c_str == NULL");
-      }
-  }
-  const char* se_name_c_str = NULL;
-  ScopedUtfChars* se_name = NULL;
-  if (java_se_name != NULL) {
-      se_name = new ScopedUtfChars(env, java_se_name);
-      se_name_c_str = se_name->c_str();
-      if (se_name_c_str == NULL) {
-        fail_fn("se_name_c_str == NULL");
-      }
-  }
-  rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
-  if (rc == -1) {
-    fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
-          is_system_server, se_info_c_str, se_name_c_str));
-  }
-
-  // Make it easier to debug audit logs by setting the main thread's name to the
-  // nice name rather than "app_process".
-  if (se_name_c_str == NULL && is_system_server) {
-    se_name_c_str = "system_server";
-  }
-  if (se_name_c_str != NULL) {
-    SetThreadName(se_name_c_str);
-  }
-
-  delete se_info;
-  delete se_name;
-
-  // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
-  UnsetChldSignalHandler();
-
-  env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
-                            is_system_server, is_child_zygote, instructionSet);
-  if (env->ExceptionCheck()) {
-    fail_fn("Error calling post fork hooks.");
-  }
 }
 
-// Utility routine to fork zygote and specialize the child process.
-static pid_t ForkCommon(JNIEnv* env, jstring java_se_name, bool is_system_server,
-                        jintArray fdsToClose, jintArray fdsToIgnore) {
+// Utility routine to fork a zygote.
+static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
+                        jintArray managed_fds_to_close, jintArray managed_fds_to_ignore) {
   SetSignalHandlers();
 
   // Block SIGCHLD prior to fork.
@@ -903,23 +720,9 @@
   sigemptyset(&sigchld);
   sigaddset(&sigchld, SIGCHLD);
 
-  auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
-      __attribute__ ((noreturn)) {
-    const char* se_name_c_str = nullptr;
-    std::unique_ptr<ScopedUtfChars> se_name;
-    if (java_se_name != nullptr) {
-      se_name.reset(new ScopedUtfChars(env, java_se_name));
-      se_name_c_str = se_name->c_str();
-    }
-    if (se_name_c_str == nullptr && is_system_server) {
-      se_name_c_str = "system_server";
-    }
-    const std::string& error_msg = (se_name_c_str == nullptr)
-        ? msg
-        : StringPrintf("(%s) %s", se_name_c_str, msg.c_str());
-    env->FatalError(error_msg.c_str());
-    __builtin_unreachable();
-  };
+  // Curry a failure function.
+  auto fail_fn = std::bind(ZygoteFailure, env, is_system_server ? "system_server" : "zygote",
+                           nullptr, _1);
 
   // Temporarily block SIGCHLD during forks. The SIGCHLD handler might
   // log, which would result in the logging FDs we close being reopened.
@@ -935,18 +738,18 @@
   __android_log_close();
   stats_log_close();
 
+  // If this is the first fork for this zygote, create the open FD table.  If
+  // it isn't, we just need to check whether the list of open files has changed
+  // (and it shouldn't in the normal case).
   std::string error_msg;
-
-  // If this is the first fork for this zygote, create the open FD table.
-  // If it isn't, we just need to check whether the list of open files has
-  // changed (and it shouldn't in the normal case).
   std::vector<int> fds_to_ignore;
-  if (!FillFileDescriptorVector(env, fdsToIgnore, &fds_to_ignore, &error_msg)) {
+  if (!FillFileDescriptorVector(env, managed_fds_to_ignore, &fds_to_ignore, &error_msg)) {
     fail_fn(error_msg);
   }
-  if (gOpenFdTable == NULL) {
+
+  if (gOpenFdTable == nullptr) {
     gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, &error_msg);
-    if (gOpenFdTable == NULL) {
+    if (gOpenFdTable == nullptr) {
       fail_fn(error_msg);
     }
   } else if (!gOpenFdTable->Restat(fds_to_ignore, &error_msg)) {
@@ -962,7 +765,7 @@
     PreApplicationInit();
 
     // Clean up any descriptors which must be closed immediately
-    if (!DetachDescriptors(env, fdsToClose, &error_msg)) {
+    if (!DetachDescriptors(env, managed_fds_to_close, &error_msg)) {
       fail_fn(error_msg);
     }
 
@@ -980,9 +783,236 @@
   if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
     fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
   }
+
   return pid;
 }
 
+// Utility routine to specialize a zygote child process.
+static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
+                             jint runtime_flags, jobjectArray rlimits,
+                             jlong permitted_capabilities, jlong effective_capabilities,
+                             jint mount_external, jstring managed_se_info,
+                             jstring managed_nice_name, bool is_system_server,
+                             bool is_child_zygote, jstring managed_instruction_set,
+                             jstring managed_app_data_dir, jstring managed_package_name,
+                             jobjectArray managed_pacakges_for_uid,
+                             jobjectArray managed_visible_vol_ids) {
+  auto fail_fn = std::bind(ZygoteFailure, env, is_system_server ? "system_server" : "zygote",
+                           managed_nice_name, _1);
+  auto extract_fn = std::bind(ExtractJString, env, is_system_server ? "system_server" : "zygote",
+                              managed_nice_name, _1);
+
+  auto se_info = extract_fn(managed_se_info);
+  auto nice_name = extract_fn(managed_nice_name);
+  auto instruction_set = extract_fn(managed_instruction_set);
+  auto app_data_dir = extract_fn(managed_app_data_dir);
+  auto package_name = extract_fn(managed_package_name);
+
+  std::string error_msg;
+
+  // Keep capabilities across UID change, unless we're staying root.
+  if (uid != 0) {
+    if (!EnableKeepCapabilities(&error_msg)) {
+      fail_fn(error_msg);
+    }
+  }
+
+  if (!SetInheritable(permitted_capabilities, &error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  if (!DropCapabilitiesBoundingSet(&error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  bool use_native_bridge = !is_system_server &&
+                           instruction_set.has_value() &&
+                           android::NativeBridgeAvailable() &&
+                           android::NeedsNativeBridge(instruction_set.value().c_str());
+
+  if (use_native_bridge && !app_data_dir.has_value()) {
+    // The app_data_dir variable should never be empty if we need to use a
+    // native bridge.  In general, app_data_dir will never be empty for normal
+    // applications.  It can only happen in special cases (for isolated
+    // processes which are not associated with any app).  These are launched by
+    // the framework and should not be emulated anyway.
+    use_native_bridge = false;
+    ALOGW("Native bridge will not be used because managed_app_data_dir == nullptr.");
+  }
+
+  if (!package_name.has_value()) {
+    if (is_system_server) {
+      package_name.emplace("android");
+    } else {
+      package_name.emplace("");
+    }
+  }
+
+  std::vector<std::string> packages_for_uid;
+  if (managed_pacakges_for_uid != nullptr) {
+    jsize count = env->GetArrayLength(managed_pacakges_for_uid);
+    for (jsize package_index = 0; package_index < count; ++package_index) {
+      jstring managed_package_for_uid =
+          (jstring) env->GetObjectArrayElement(managed_pacakges_for_uid, package_index);
+
+      auto package_for_uid = extract_fn(managed_package_for_uid);
+      if (LIKELY(package_for_uid.has_value())) {
+        packages_for_uid.emplace_back(std::move(package_for_uid.value()));
+      } else {
+        fail_fn("Null string found in managed packages_for_uid.");
+      }
+    }
+  }
+
+  std::vector<std::string> visible_vol_ids;
+  if (managed_visible_vol_ids != nullptr) {
+    jsize count = env->GetArrayLength(managed_visible_vol_ids);
+    for (jsize vol_id_index = 0; vol_id_index < count; ++vol_id_index) {
+      jstring managed_visible_vol_id =
+          (jstring) env->GetObjectArrayElement(managed_visible_vol_ids, vol_id_index);
+
+      auto visible_vol_id = extract_fn(managed_visible_vol_id);
+      if (LIKELY(visible_vol_id.has_value())) {
+        visible_vol_ids.emplace_back(std::move(visible_vol_id.value()));
+      } else {
+        fail_fn("Null string found in managed visible_vol_ids.");
+      }
+    }
+  }
+
+  if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg,
+                            package_name.value(), packages_for_uid, visible_vol_ids)) {
+    ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
+    if (errno == ENOTCONN || errno == EROFS) {
+      // When device is actively encrypting, we get ENOTCONN here
+      // since FUSE was mounted before the framework restarted.
+      // When encrypted device is booting, we get EROFS since
+      // FUSE hasn't been created yet by init.
+      // In either case, continue without external storage.
+    } else {
+      fail_fn(error_msg);
+    }
+  }
+
+  // If this zygote isn't root, it won't be able to create a process group,
+  // since the directory is owned by root.
+  if (!is_system_server && getuid() == 0) {
+    int rc = createProcessGroup(uid, getpid());
+    if (rc == -EROFS) {
+      ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
+    } else if (rc != 0) {
+      ALOGE("createProcessGroup(%d, %d) failed: %s", uid, /* pid= */ 0, strerror(-rc));
+    }
+  }
+
+  if (!SetGids(env, gids, &error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  if (!SetRLimits(env, rlimits, &error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  if (use_native_bridge) {
+    // Due to the logic behind use_native_bridge we know that both app_data_dir
+    // and instruction_set contain values.
+    android::PreInitializeNativeBridge(app_data_dir.value().c_str(),
+                                       instruction_set.value().c_str());
+  }
+
+  if (setresgid(gid, gid, gid) == -1) {
+    fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
+  }
+
+  // Must be called when the new process still has CAP_SYS_ADMIN, in this case,
+  // before changing uid from 0, which clears capabilities.  The other
+  // alternative is to call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that
+  // breaks SELinux domain transition (see b/71859146).  As the result,
+  // privileged syscalls used below still need to be accessible in app process.
+  SetUpSeccompFilter(uid);
+
+  if (setresuid(uid, uid, uid) == -1) {
+    fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
+  }
+
+  // The "dumpable" flag of a process, which controls core dump generation, is
+  // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
+  // user or group ID changes. See proc(5) for possible values. In most cases,
+  // the value is 0, so core dumps are disabled for zygote children. However,
+  // when running in a Chrome OS container, the value is already set to 2,
+  // which allows the external crash reporter to collect all core dumps. Since
+  // only system crashes are interested, core dump is disabled for app
+  // processes. This also ensures compliance with CTS.
+  int dumpable = prctl(PR_GET_DUMPABLE);
+  if (dumpable == -1) {
+    ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
+    RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
+  }
+
+  if (dumpable == 2 && uid >= AID_APP) {
+    if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
+      ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
+      RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
+    }
+  }
+
+  if (NeedsNoRandomizeWorkaround()) {
+    // Work around ARM kernel ASLR lossage (http://b/5817320).
+    int old_personality = personality(0xffffffff);
+    int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
+    if (new_personality == -1) {
+      ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
+    }
+  }
+
+  if (!SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities,
+                       &error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  if (!SetSchedulerPolicy(&error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
+  const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
+
+  if (selinux_android_setcontext(uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
+    fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed",
+                         uid, is_system_server, se_info_ptr, nice_name_ptr));
+  }
+
+  // Make it easier to debug audit logs by setting the main thread's name to the
+  // nice name rather than "app_process".
+  if (nice_name.has_value()) {
+    SetThreadName(nice_name.value().c_str());
+  } else if (is_system_server) {
+    SetThreadName("system_server");
+  }
+
+  // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
+  UnsetChldSignalHandler();
+
+  if (is_system_server) {
+    env->CallStaticVoidMethod(gZygoteClass, gCallPostForkSystemServerHooks);
+    if (env->ExceptionCheck()) {
+      fail_fn("Error calling post fork system server hooks.");
+    }
+    // TODO(oth): Remove hardcoded label here (b/117874058).
+    static const char* kSystemServerLabel = "u:r:system_server:s0";
+    if (selinux_android_setcon(kSystemServerLabel) != 0) {
+      fail_fn(CREATE_ERROR("selinux_android_setcon(%s)", kSystemServerLabel));
+    }
+  }
+
+  env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
+                            is_system_server, is_child_zygote, managed_instruction_set);
+
+  if (env->ExceptionCheck()) {
+    fail_fn("Error calling post fork hooks.");
+  }
+}
+
 static uint64_t GetEffectiveCapabilityMask(JNIEnv* env) {
     __user_cap_header_struct capheader;
     memset(&capheader, 0, sizeof(capheader));
@@ -995,16 +1025,82 @@
         RuntimeAbort(env, __LINE__, "capget failed");
     }
 
-    return capdata[0].effective |
-           (static_cast<uint64_t>(capdata[1].effective) << 32);
+    return capdata[0].effective | (static_cast<uint64_t>(capdata[1].effective) << 32);
+}
+
+static jlong CalculateCapabilities(JNIEnv* env, jint uid, jint gid, jintArray gids,
+                                   bool is_child_zygote) {
+  jlong capabilities = 0;
+
+  /*
+   *  Grant the following capabilities to the Bluetooth user:
+   *    - CAP_WAKE_ALARM
+   *    - CAP_NET_RAW
+   *    - CAP_NET_BIND_SERVICE (for DHCP client functionality)
+   *    - CAP_SYS_NICE (for setting RT priority for audio-related threads)
+   */
+
+  if (multiuser_get_app_id(uid) == AID_BLUETOOTH) {
+    capabilities |= (1LL << CAP_WAKE_ALARM);
+    capabilities |= (1LL << CAP_NET_RAW);
+    capabilities |= (1LL << CAP_NET_BIND_SERVICE);
+    capabilities |= (1LL << CAP_SYS_NICE);
+  }
+
+  /*
+   * Grant CAP_BLOCK_SUSPEND to processes that belong to GID "wakelock"
+   */
+
+  bool gid_wakelock_found = false;
+  if (gid == AID_WAKELOCK) {
+    gid_wakelock_found = true;
+  } else if (gids != nullptr) {
+    jsize gids_num = env->GetArrayLength(gids);
+    ScopedIntArrayRO native_gid_proxy(env, gids);
+
+    if (native_gid_proxy.get() == nullptr) {
+      RuntimeAbort(env, __LINE__, "Bad gids array");
+    }
+
+    for (int gid_index = gids_num; --gids_num >= 0;) {
+      if (native_gid_proxy[gid_index] == AID_WAKELOCK) {
+        gid_wakelock_found = true;
+        break;
+      }
+    }
+  }
+
+  if (gid_wakelock_found) {
+    capabilities |= (1LL << CAP_BLOCK_SUSPEND);
+  }
+
+  /*
+   * Grant child Zygote processes the following capabilities:
+   *   - CAP_SETUID (change UID of child processes)
+   *   - CAP_SETGID (change GID of child processes)
+   *   - CAP_SETPCAP (change capabilities of child processes)
+   */
+
+  if (is_child_zygote) {
+    capabilities |= (1LL << CAP_SETUID);
+    capabilities |= (1LL << CAP_SETGID);
+    capabilities |= (1LL << CAP_SETPCAP);
+  }
+
+  /*
+   * Containers run without some capabilities, so drop any caps that are not
+   * available.
+   */
+
+  return capabilities & GetEffectiveCapabilityMask(env);
 }
 }  // anonymous namespace
 
 namespace android {
 
 static void com_android_internal_os_Zygote_nativeSecurityInit(JNIEnv*, jclass) {
-  // security_getenforce is not allowed on app process. Initialize and cache the value before
-  // zygote forks.
+  // security_getenforce is not allowed on app process. Initialize and cache
+  // the value before zygote forks.
   g_is_security_enforced = security_getenforce();
 }
 
@@ -1015,78 +1111,35 @@
 static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
         JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
         jint runtime_flags, jobjectArray rlimits,
-        jint mount_external, jstring se_info, jstring se_name,
-        jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote,
-        jstring instructionSet, jstring appDataDir, jstring packageName,
-        jobjectArray packagesForUid, jobjectArray visibleVolIds) {
-    jlong capabilities = 0;
+        jint mount_external, jstring se_info, jstring nice_name,
+        jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote,
+        jstring instruction_set, jstring app_data_dir, jstring package_name,
+        jobjectArray packages_for_uid, jobjectArray visible_vol_ids) {
+    jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
 
-    // Grant CAP_WAKE_ALARM to the Bluetooth process.
-    // Additionally, allow bluetooth to open packet sockets so it can start the DHCP client.
-    // Grant CAP_SYS_NICE to allow Bluetooth to set RT priority for
-    // audio-related threads.
-    // TODO: consider making such functionality an RPC to netd.
-    if (multiuser_get_app_id(uid) == AID_BLUETOOTH) {
-      capabilities |= (1LL << CAP_WAKE_ALARM);
-      capabilities |= (1LL << CAP_NET_RAW);
-      capabilities |= (1LL << CAP_NET_BIND_SERVICE);
-      capabilities |= (1LL << CAP_SYS_NICE);
-    }
-
-    // Grant CAP_BLOCK_SUSPEND to processes that belong to GID "wakelock"
-    bool gid_wakelock_found = false;
-    if (gid == AID_WAKELOCK) {
-      gid_wakelock_found = true;
-    } else if (gids != NULL) {
-      jsize gids_num = env->GetArrayLength(gids);
-      ScopedIntArrayRO ar(env, gids);
-      if (ar.get() == NULL) {
-        RuntimeAbort(env, __LINE__, "Bad gids array");
-      }
-      for (int i = 0; i < gids_num; i++) {
-        if (ar[i] == AID_WAKELOCK) {
-          gid_wakelock_found = true;
-          break;
-        }
-      }
-    }
-    if (gid_wakelock_found) {
-      capabilities |= (1LL << CAP_BLOCK_SUSPEND);
-    }
-
-    // If forking a child zygote process, that zygote will need to be able to change
-    // the UID and GID of processes it forks, as well as drop those capabilities.
-    if (is_child_zygote) {
-      capabilities |= (1LL << CAP_SETUID);
-      capabilities |= (1LL << CAP_SETGID);
-      capabilities |= (1LL << CAP_SETPCAP);
-    }
-
-    // Containers run without some capabilities, so drop any caps that are not
-    // available.
-    capabilities &= GetEffectiveCapabilityMask(env);
-
-    pid_t pid = ForkCommon(env, se_name, false, fdsToClose, fdsToIgnore);
+    pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore);
     if (pid == 0) {
       SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
                        capabilities, capabilities,
-                       mount_external, se_info, se_name, false,
-                       is_child_zygote == JNI_TRUE, instructionSet, appDataDir, packageName,
-                       packagesForUid, visibleVolIds);
+                       mount_external, se_info, nice_name, false,
+                       is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
+                       package_name, packages_for_uid, visible_vol_ids);
     }
     return pid;
 }
 
 static jint com_android_internal_os_Zygote_nativeForkSystemServer(
         JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
-        jint runtime_flags, jobjectArray rlimits, jlong permittedCapabilities,
-        jlong effectiveCapabilities) {
-  pid_t pid = ForkCommon(env, NULL, true, NULL, NULL);
+        jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
+        jlong effective_capabilities) {
+  pid_t pid = ForkCommon(env, true,
+                         /* managed_fds_to_close= */ nullptr,
+                         /* managed_fds_to_ignore= */ nullptr);
   if (pid == 0) {
       SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
-                       permittedCapabilities, effectiveCapabilities,
-                       MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true,
-                       false, NULL, NULL, nullptr, nullptr, nullptr);
+                       permitted_capabilities, effective_capabilities,
+                       MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
+                       false, nullptr, nullptr, nullptr, nullptr, nullptr);
   } else if (pid > 0) {
       // The zygote process checks whether the child process has died or not.
       ALOGI("System server process %d has been created", pid);
@@ -1120,7 +1173,7 @@
     ScopedUtfChars path_native(env, path);
     const char* path_cstr = path_native.c_str();
     if (!path_cstr) {
-        RuntimeAbort(env, __LINE__, "path_cstr == NULL");
+        RuntimeAbort(env, __LINE__, "path_cstr == nullptr");
     }
     FileDescriptorWhitelist::Get()->Allow(path_cstr);
 }
@@ -1181,6 +1234,9 @@
 
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
   gZygoteClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteClassName));
+  gCallPostForkSystemServerHooks = GetStaticMethodIDOrDie(env, gZygoteClass,
+                                                          "callPostForkSystemServerHooks",
+                                                          "()V");
   gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks",
                                                    "(IZZLjava/lang/String;)V");
 
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index f56f7ec..2465759 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -37,6 +37,7 @@
   "/dev/socket/zygote",
   "/dev/socket/zygote_secondary",
   "/dev/socket/webview_zygote",
+  "/dev/socket/heapprofd",
   "/sys/kernel/debug/tracing/trace_marker",
   "/system/framework/framework-res.apk",
   "/dev/urandom",
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 30a9a01..480b1ea 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -9,7 +9,14 @@
 singhtejinder@google.com
 yanglu@google.com
 yaochen@google.com
+yro@google.com
+
+# Settings UI
+per-file settings_enums.proto=zhfan@google.com
 
 # Frameworks
 ogunwale@google.com
 jjaggi@google.com
+
+# Launcher
+hyunyoungs@google.com
diff --git a/core/proto/android/app/notification_channel.proto b/core/proto/android/app/notification_channel.proto
index 75cc18b..435d32f 100644
--- a/core/proto/android/app/notification_channel.proto
+++ b/core/proto/android/app/notification_channel.proto
@@ -57,4 +57,7 @@
     // If this is a blockable system notification channel.
     optional bool is_blockable_system = 17;
     optional bool fg_service_shown = 18;
+    // Default is true.
+    // Allows the notifications to appear outside of the shade in floating windows
+    optional bool allow_app_overlay = 19;
 }
diff --git a/core/proto/android/app/notification_channel_group.proto b/core/proto/android/app/notification_channel_group.proto
index 4fb27b0..6d6ceb2 100644
--- a/core/proto/android/app/notification_channel_group.proto
+++ b/core/proto/android/app/notification_channel_group.proto
@@ -36,4 +36,7 @@
     optional string description = 3;
     optional bool is_blocked = 4;
     repeated NotificationChannelProto channels = 5;
+    // Default is true.
+    // Allows the notifications to appear outside of the shade in floating windows
+    optional bool allow_app_overlay = 6;
 }
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 2797550..3a908dc 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -26,35 +26,46 @@
     ACTION_UNKNOWN = 0;
     PAGE_VISIBLE = 1;
     PAGE_HIDE = 2;
-    PREF_CHANGE = 3;
+
+    // ACTION: Settings > Any preference is changed
+    ACTION_SETTINGS_PREFERENCE_CHANGE = 853;
 }
 
 /**
  * Id for Settings pages. Each page must have its own unique Id.
  */
 enum PageId {
-  // Unknown page. Should not be used in production code.
-  PAGE_UNKNOWN = 0;
+    // Unknown page. Should not be used in production code.
+    PAGE_UNKNOWN = 0;
 
-  // OPEN: Settings homepage
-  SETTINGS_HOMEPAGE = 1502;
+    // OPEN: Settings homepage
+    SETTINGS_HOMEPAGE = 1502;
 
-  // OPEN: Settings > System > Input & Gesture > Wake screen
-  SETTINGS_GESTURE_WAKE_SCREEN = 1570;
+    // OPEN: Settings > System > Input & Gesture > Wake screen
+    SETTINGS_GESTURE_WAKE_SCREEN = 1570;
 
-  // OPEN: Settings > Network & internet > Mobile network
-  MOBILE_NETWORK = 1571;
+    // OPEN: Settings > Network & internet > Mobile network
+    MOBILE_NETWORK = 1571;
 
-  // OPEN: Settings > Network & internet > Mobile network > Choose network
-  MOBILE_NETWORK_SELECT = 1581;
+    // OPEN: Settings > Network & internet > Mobile network > Choose network
+    MOBILE_NETWORK_SELECT = 1581;
 
-  // OPEN: Settings > Network & internet > Mobile network > Mobile Data > Dialog
-  MOBILE_DATA_DIALOG = 1582;
+    // OPEN: Settings > Network & internet > Mobile network > Mobile Data > Dialog
+    MOBILE_DATA_DIALOG = 1582;
 
-  // OPEN: Settings > Network & internet > Mobile network > Data roaming > Dialog
-  MOBILE_ROAMING_DIALOG = 1583;
+    // OPEN: Settings > Network & internet > Mobile network > Data roaming > Dialog
+    MOBILE_ROAMING_DIALOG = 1583;
 
-  // Settings > Display > Lock screen display > On lock screen
-  LOCK_SCREEN_NOTIFICATION_CONTENT = 1584;
+    // Settings > Display > Lock screen display > On lock screen
+    LOCK_SCREEN_NOTIFICATION_CONTENT = 1584;
+
+    // ConfirmDeviceCredentials > BiometricPrompt
+    BIOMETRIC_FRAGMENT = 1585;
+
+    // OPEN: Biometric Enrollment (android.settings.BIOMETRIC_ENROLL action intent)
+    BIOMETRIC_ENROLL_ACTIVITY = 1586;
+
+    // OPEN: Settings > Privacy
+    TOP_LEVEL_PRIVACY = 1587;
 }
 
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 8f28970..0b9e347 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -26,6 +26,7 @@
 import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
 import "frameworks/base/core/proto/android/os/procrank.proto";
 import "frameworks/base/core/proto/android/os/ps.proto";
+import "frameworks/base/core/proto/android/os/statsdata.proto";
 import "frameworks/base/core/proto/android/os/system_properties.proto";
 import "frameworks/base/core/proto/android/providers/settings.proto";
 import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
@@ -301,6 +302,12 @@
         (section).userdebug_and_eng_only = true
     ];
 
+    optional android.os.StatsDataDumpProto stats_data = 3023 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "stats --proto",
+        (section).userdebug_and_eng_only = true
+    ];
+
     // Reserved for OEMs.
     extensions 50000 to 100000;
 }
diff --git a/core/proto/android/os/statsdata.proto b/core/proto/android/os/statsdata.proto
new file mode 100644
index 0000000..25d76b8
--- /dev/null
+++ b/core/proto/android/os/statsdata.proto
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package android.os;
+
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+// Dump of statsd report data (dumpsys stats --proto).
+message StatsDataDumpProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // The following is an android.os.statsd.ConfigMetricsReportList, which is defined
+    // in frameworks/base/cmds/statsd/src/stats_log.proto. It should not be imported (even weakly)
+    // in AOSP because it would drag with it atoms.proto, which is enormous and awkward.
+    repeated bytes config_metrics_report_list = 1;
+
+}
\ No newline at end of file
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 47dbc07..69ebb59 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -394,13 +394,16 @@
 
         // App allowed to load GPU debug layers.
         optional SettingProto debug_app = 1;
-        // Ordered GPU debug layer list
+        // Ordered GPU debug layer list for Vulkan
         // i.e. <layer1>:<layer2>:...:<layerN>
         optional SettingProto debug_layers = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
         // App will load ANGLE instead of native GLES drivers.
         optional SettingProto angle_enabled_app = 3;
         // App that can provide layer libraries.
         optional SettingProto debug_layer_app = 4;
+        // Ordered GPU debug layer list for GLES
+        // i.e. <layer1>:<layer2>:...:<layerN>
+        optional SettingProto debug_layers_gles = 5;
     }
     optional Gpu gpu = 59;
 
@@ -722,8 +725,10 @@
     optional SettingProto set_install_location = 103 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto shortcut_manager_constants = 104;
     optional SettingProto show_first_crash_dialog = 105 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto show_hidden_launcher_icon_apps_enabled = 141 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto show_restart_in_crash_dialog = 106 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto show_mute_in_crash_dialog = 107 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto show_new_app_installed_notification_enabled = 142 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     message SmartSelection {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -963,5 +968,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 141;
+    // Next tag = 143;
 }
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 3ead633..f8acc33 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -72,8 +72,10 @@
         // List of the accessibility services to which the user has granted
         // permission to put the device into touch exploration mode.
         optional SettingProto touch_exploration_granted_accessibility_services = 31;
-        optional SettingProto minimum_ui_timeout_enabled = 32 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        optional SettingProto minimum_ui_timeout_ms = 33 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        reserved 32; // minimum_ui_timeout_enabled
+        reserved 33; // minimum_ui_timeout_ms
+        optional SettingProto non_interactive_ui_timeout_ms = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto interactive_ui_timeout_ms = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     }
     optional Accessibility accessibility = 2;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c1c86f0..b0dbaa0 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -45,7 +45,6 @@
     optional bool display_frozen = 6;
     optional int32 rotation = 7;
     optional int32 last_orientation = 8;
-    optional AppTransitionProto app_transition = 9;
 }
 
 /* represents RootWindowContainer object */
@@ -159,6 +158,7 @@
     optional DisplayFramesProto display_frames = 13;
     optional int32 surface_size = 14;
     optional string focused_app = 15;
+    optional AppTransitionProto app_transition = 16;
 }
 
 /* represents DisplayFrames */
diff --git a/core/proto/android/service/adb.proto b/core/proto/android/service/adb.proto
new file mode 100644
index 0000000..0060813
--- /dev/null
+++ b/core/proto/android/service/adb.proto
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+syntax = "proto2";
+package android.service.adb;
+
+option java_multiple_files = true;
+option java_outer_classname = "AdbServiceProto";
+
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+message AdbServiceDumpProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional AdbDebuggingManagerProto debugging_manager = 1;
+}
+
+message AdbDebuggingManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional bool connected_to_adb = 1;
+    optional string last_key_recevied = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
+    optional string user_keys = 3 [ (android.privacy).dest = DEST_LOCAL ];
+    optional string system_keys = 4 [ (android.privacy).dest = DEST_LOCAL ];
+}
diff --git a/core/proto/android/stats/launcher/Android.bp b/core/proto/android/stats/launcher/Android.bp
new file mode 100644
index 0000000..b8fb6ff
--- /dev/null
+++ b/core/proto/android/stats/launcher/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2018 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.
+
+java_library {
+    name: "launcherprotosnano",
+    proto: {
+        type: "nano",
+        output_params: ["store_unknown_fields=true"],
+        include_dirs: ["external/protobuf/src"],
+    },
+
+    sdk_version: "current",
+    srcs: [
+        "*.proto",
+    ],
+}
diff --git a/core/proto/android/stats/launcher/launcher.proto b/core/proto/android/stats/launcher/launcher.proto
new file mode 100644
index 0000000..dbd0e03
--- /dev/null
+++ b/core/proto/android/stats/launcher/launcher.proto
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+syntax = "proto2";
+package android.stats.launcher;
+option java_multiple_files = true;
+
+enum LauncherAction {
+    DEFAULT_ACTION = 0;
+    LAUNCH_APP = 1;
+    LAUNCH_TASK = 2;
+    DISMISS_TASK = 3;
+    LONGPRESS = 4;
+    DRAGDROP = 5;
+    SWIPE_UP = 6;
+    SWIPE_DOWN = 7;
+    SWIPE_LEFT = 8;
+    SWIPE_RIGHT = 9;
+}
+
+enum LauncherState {
+    BACKGROUND = 0;
+    HOME = 1;
+    OVERVIEW = 2;
+    ALLAPPS = 3;
+}
+
+message LauncherTarget {
+    enum Type {
+        NONE = 0;
+        ITEM_TYPE = 1;
+        CONTROL_TYPE = 2;
+        CONTAINER_TYPE = 3;
+    }
+    enum Item {
+        DEFAULT_ITEM = 0;
+        APP_ICON = 1;
+        SHORTCUT = 2;
+        WIDGET = 3;
+        FOLDER_ICON = 4;
+        DEEPSHORTCUT = 5;
+        SEARCHBOX = 6;
+        EDITTEXT = 7;
+        NOTIFICATION = 8;
+        TASK = 9;
+    }
+    enum Container {
+        DEFAULT_CONTAINER = 0;
+        HOTSEAT = 1;
+        FOLDER = 2;
+        PREDICTION = 3;
+        SEARCHRESULT = 4;
+    }
+    enum Control {
+        DEFAULT_CONTROL = 0;
+        MENU = 1;
+        UNINSTALL = 2;
+        REMOVE = 3;
+    }
+    optional Type type = 1;
+    optional Item item = 2;
+    optional Container container = 3;
+    optional Control control = 4;
+    optional string launch_component = 5;
+    optional int32 page_id = 6;
+    optional int32 grid_x = 7;
+    optional int32 grid_y = 8;
+}
+
+message LauncherExtension {
+    repeated LauncherTarget src_target = 1;
+    repeated LauncherTarget dst_target = 2;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 176fedb..093a860 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -401,6 +401,7 @@
     <protected-broadcast android:name="android.telecom.action.DEFAULT_DIALER_CHANGED" />
     <protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED" />
     <protected-broadcast android:name="android.provider.action.SMS_MMS_DB_CREATED" />
+    <protected-broadcast android:name="android.provider.action.SMS_MMS_DB_LOST" />
     <protected-broadcast android:name="android.intent.action.CONTENT_CHANGED" />
     <protected-broadcast android:name="android.provider.Telephony.MMS_DOWNLOADED" />
 
@@ -488,6 +489,7 @@
     <protected-broadcast android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" />
     <protected-broadcast android:name="android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED" />
     <protected-broadcast android:name="android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED" />
+    <protected-broadcast android:name="android.telephony.action.SECRET_CODE" />
     <protected-broadcast android:name="android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION" />
     <protected-broadcast android:name="android.telephony.action.SUBSCRIPTION_PLANS_CHANGED" />
 
@@ -1791,6 +1793,12 @@
     <permission android:name="android.permission.MANAGE_USB"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to manage Android Debug Bridge settings.
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.MANAGE_DEBUGGING"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows an application to access the MTP USB kernel driver.
          For use only by the device side MTP implementation.
          @hide -->
@@ -2940,6 +2948,12 @@
     <permission android:name="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE"
         android:protectionLevel="signature" />
 
+    <!-- Must be required by the RoleControllerService to ensure that only the system can bind to
+         it.
+         @hide -->
+    <permission android:name="android.permission.BIND_ROLE_CONTROLLER_SERVICE"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Must be required by the RuntimePermissionPresenterService to ensure
          that only the system can bind to it.
          @hide -->
@@ -3005,6 +3019,14 @@
     <permission android:name="android.permission.BIND_TEXTCLASSIFIER_SERVICE"
                 android:protectionLevel="signature" />
 
+    <!-- Must be required by a android.service.intelligence.IntelligenceService,
+         to ensure that only the system can bind to it.
+         @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_INTELLIGENCE_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- Must be required by hotword enrollment application,
          to ensure that only the system can interact with it.
          @hide <p>Not for use by third-party applications.</p> -->
@@ -3274,6 +3296,11 @@
     <permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to manage the holders of a role.
+         @hide -->
+    <permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
+                android:protectionLevel="signature|installer" />
+
     <!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
          <p>Not for use by third-party applications.
          @hide
@@ -4188,6 +4215,11 @@
     <permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows modifying accessibility state.
+         @hide -->
+    <permission android:name="android.permission.MANAGE_ACCESSIBILITY"
+                android:protectionLevel="signature|setup" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/TEST_MAPPING b/core/res/TEST_MAPPING
new file mode 100644
index 0000000..1d22d782
--- /dev/null
+++ b/core/res/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+    "presubmit": [
+        {
+            "name": "CtsPermission2TestCases",
+            "options": [
+                {
+                    "include-filter": "android.permission2.cts.PermissionPolicyTest#testPlatformPermissionPolicyUnaltered"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 5028150..9ba991b 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofoon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"oudio op te neem"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om oudio op te neem?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Aktiwiteitherkenning"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"herken aktiwiteit"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om jou fisieke aktiwiteit te herken?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"foto\'s en video te neem"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om foto\'s te neem en video\'s op te neem?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Hierdie program kan jou ligging kry op grond van jou netwerkhulpbronne soos selfoontorings en Wi-Fi-netwerke. Hierdie liggingdienste moet aangeskakel en op jou tablet beskikbaar wees sodat die program hulle kan gebruik."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Hierdie program kan jou ligging kry op grond van jou netwerkhulpbronne soos selfoontorings en Wi-Fi-netwerke. Hierdie liggingdienste moet aangeskakel en op jou TV beskikbaar wees sodat die program hulle kan gebruik."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Hierdie program kan jou ligging kry op grond van jou netwerkhulpbronne soos selfoontorings en Wi-Fi-netwerke. Hierdie liggingdienste moet aangeskakel en op jou foon beskikbaar wees sodat die program hulle kan gebruik."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"kry presiese ligging op die agtergrond"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Hierdie program kan enige tyd terwyl dit op die agtergrond is jou presiese ligging kry. Hierdie liggingdienste moet aangeskakel wees en op jou foon beskikbaar wees sodat die program hulle kan gebruik. Dit kan veroorsaak dat meer batterykrag gebruik word."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"kry ligging op die agtergrond"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"As dit bo en behalwe toegang tot die benaderde of presiese ligging verleen word, kan die program die ligging kry terwyl dit op die agtergrond werk."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"verander jou klankinstellings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Laat die program toe om globale klankinstellings soos volume en watter luidspreker vir uitvoer gebruik word, te verander."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"neem klank op"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Hierdie program kan enige tyd oudio met die mikrofoon opneem."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"stuur bevele na die SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Laat die program toe om bevele na die SIM te stuur. Dit is baie gevaarlik."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"herken fisieke aktiwiteit"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Hierdie program kan jou fisieke aktiwiteit herken."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"neem foto\'s en video\'s"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Hierdie program kan enige tyd met die kamera foto\'s neem en video\'s opneem."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"beheer vibrasie"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Laat die program toe om jou fotoversameling te wysig."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"lees liggings in jou mediaversameling"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Laat die program toe om liggings in jou mediaversameling te lees."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Die program <xliff:g id="APP">%s</xliff:g> wil staaf."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometriese hardeware is nie beskikbaar nie"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Gedeeltelike vingerafdruk is bespeur. Probeer asseblief weer."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kon nie vingerafdruk verwerk nie. Probeer asseblief weer."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> outovul-voorstelle</item>
       <item quantity="one">Een outovul-voorstel</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Stoor in &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Stoor <xliff:g id="TYPE">%1$s</xliff:g> in &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Stoor <xliff:g id="TYPE_0">%1$s</xliff:g> en <xliff:g id="TYPE_1">%2$s</xliff:g> in &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Stoor <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> en <xliff:g id="TYPE_2">%3$s</xliff:g> in &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Dateer op na &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Dateer <xliff:g id="TYPE">%1$s</xliff:g> op na &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Dateer <xliff:g id="TYPE_0">%1$s</xliff:g> en <xliff:g id="TYPE_1">%2$s</xliff:g> op na &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Dateer <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> en <xliff:g id="TYPE_2">%3$s</xliff:g> op na &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Stoor in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Stoor <xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Stoor <xliff:g id="TYPE_0">%1$s</xliff:g> en <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Stoor <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> en <xliff:g id="TYPE_2">%3$s</xliff:g> in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Dateer op in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Dateer <xliff:g id="TYPE">%1$s</xliff:g> op in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Dateer <xliff:g id="TYPE_0">%1$s</xliff:g> en <xliff:g id="TYPE_1">%2$s</xliff:g> op in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Dateer hierdie items op in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> en <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Stoor"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Nee, dankie"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Dateer op"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 217d631..5b2286b 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"ማይክሮፎን"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ኦዲዮ ይቅዱ"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ኦዲዮን እንዲቀዳ ይፈቀድለት?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"የእንቅስቃሴ ለይቶ ማወቅ"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"እንቅስቃሴን ለይቶ ማወቅ"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; አካላዊ እንቅስቃሴዎን ለይቶ እንዲያውቅ ይፈቀድለት?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"ካሜራ"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ስዕሎች ያንሱ እና ቪዲዮ ይቅረጹ"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ስዕሎችን እንዲያነሳ እና ቪዲዮን እንዲቀርጽ ይፈቀድለት?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ይህ መተግበሪያ እንደ የሕዋስ ማማዎች እና የWi-Fi አውታረ መረቦች ከመሳሰሉ የአውታረ መረብ ምንጮች ላይ በመመርኮዝ የእርስዎን መገኛ አካባቢ ማግኘት ይችላል። እነዚህ የመገኛ አካባቢ አገልግሎቶች መተግበሪያው መጠቀም እንዲችል ሊበሩ እና በእርስዎ ጡባዊ ላይ ሊገኙ የሚችሉ መሆን አለባቸው።"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ይህ መተግበሪያ እንደ የሕዋስ ማማዎች እና የWi-Fi አውታረ መረቦች ከመሳሰሉ የአውታረ መረብ ምንጮች ላይ በመመርኮዝ የእርስዎን መገኛ አካባቢ ማግኘት ይችላል። እነዚህ የመገኛ አካባቢ አገልግሎቶች እርስዎ መጠቀም እንዲችሉ ሊበሩ እና በእርስዎ ቴሌቪዥን ላይ ሊገኙ የሚችሉ መሆን አለባቸው።"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ይህ መተግበሪያ እንደ የሕዋስ ማማዎች እና የWi-Fi አውታረ መረቦች ከመሳሰሉ የአውታረ መረብ ምንጮች ላይ በመመርኮዝ የእርስዎን መገኛ አካባቢ ማግኘት ይችላል። እነዚህ የመገኛ አካባቢ አገልግሎቶች መተግበሪያው መጠቀም እንዲችል ሊበሩ እና በእርስዎ ስልክ ላይ ሊገኙ የሚችሉ መሆን አለባቸው።"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"በበስተጀርባ ትክክለኛ መገኛ አካባቢ ላይ ድረስ"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ይህ መተግበሪያ በማናቸውም ጊዜ በበስተጀርባ ሲሆን ብቻ ትክክለኛውን መገኛ አካባቢ ማግኘት ይችላል። እነዚህ የመገኛ አካባቢ አገልግሎቶች መተግበሪያው መጠቀም እንዲችል ሊበሩ እና በእርስዎ ስልክ ላይ ሊገኙ የሚችሉ መሆን አለባቸው።"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"አካባቢን በበስተጀርባ ድረስ"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"ይህ ከግምታዊ ወይም ትክክለኛ አካባቢ በተጨማሪ ከተሰጠ መተግበሪያው በበስተጀርባ እያሄደ ሳለ አካባቢውን መድረስ ይችላል።"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"የድምፅ ቅንብሮችን ለውጥ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"መተግበሪያው አንደ የድምጽ መጠን እና ለውጽአት የትኛውን የድምጽ ማጉያ ጥቅም ላይ እንደዋለ የመሳሰሉ ሁለንተናዊ የድምጽ ቅንብሮችን እንዲያስተካክል ይፈቅድለታል።"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ኦዲዮ ይቅዱ"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"ይህ መተግበሪያ በማናቸውም ጊዜ ማይክራፎኑን በመጠቀም ኦዲዮን መቅዳት ይችላል።"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"ወደ ሲሙ ትዕዛዞችን መላክ"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"መተግበሪያው ትዕዛዞችን ወደ ሲሙ እንዲልክ ያስችለዋል። ይሄ በጣማ አደገኛ ነው።"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"አካላዊ እንቅስቃሴን ለይቶ ማወቅ"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"ይህ መተግበሪያ አካላዊ እንቅስቃሴዎን ለይቶ ሊያውቅ ይችላል።"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ፎቶዎች እና ቪዲዮዎች ያንሱ"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"ይህ መተግበሪያ በማናቸውም ጊዜ ካሜራውን በመጠቀም ፎቶ ሊያነሳ እና ቪዲዮዎችን ሊቀርጽ ይችላል።"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ነዛሪ ተቆጣጠር"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"መተግበሪያው የፎቶ ስብስብዎን እንዲቀይረው ያስችለዋል።"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"አካባቢዎችን ከሚዲያ ስብስብዎ ማንበብ"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"መተግበሪያው አካባቢዎችን ከሚዲያ ስብስብዎ እንዲያነብብ ያስችለዋል።"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"መተግበሪያ <xliff:g id="APP">%s</xliff:g> ማረጋገጥ ይፈልጋል"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ባዮሜትራዊ ሃርድዌር አይገኝም"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ከፊል የጣት አሻራ ተገኝቷል። እባክዎ እንደገና ይሞክሩ።"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ጣት አሻራን መስራት አልተቻለም። እባክዎ እንደገና ይሞክሩ።"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> ራስ-ሙላ ጥቆማዎች</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> ራስ-ሙላ ጥቆማዎች</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"ወደ &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ይቀመጥ?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> ወደ &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ይቀመጥ?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> እና <xliff:g id="TYPE_1">%2$s</xliff:g> ወደ &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ይቀመጡ?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>፣ <xliff:g id="TYPE_1">%2$s</xliff:g> እና <xliff:g id="TYPE_2">%3$s</xliff:g> ወደ &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ይቀመጡ?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"ወደ &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ይዘመን?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> ወደ &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ይዘመን?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> እና <xliff:g id="TYPE_1">%2$s</xliff:g> ወደ &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ይዘመኑ?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>፣ <xliff:g id="TYPE_1">%2$s</xliff:g> እና <xliff:g id="TYPE_2">%3$s</xliff:g> ወደ &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ይዘመኑ?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"ወደ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ይቀመጥ?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> ወደ "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ይቀመጡ?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> እና <xliff:g id="TYPE_1">%2$s</xliff:g> ወደ "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ይቀመጡ?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>፣ <xliff:g id="TYPE_1">%2$s</xliff:g> እና <xliff:g id="TYPE_2">%3$s</xliff:g> ወደ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ይቀመጡ?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"በ"<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ውስጥ ይዘመን?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g> በ"<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ውስጥ ይዘመን?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> እና <xliff:g id="TYPE_1">%2$s</xliff:g> በ"<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ውስጥ ይዘመኑ?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"እነዚህ ንጥሎች በ"<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ውስጥ ይዘመኑ፦ <xliff:g id="TYPE_0">%1$s</xliff:g>፣ <xliff:g id="TYPE_1">%2$s</xliff:g> እና <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"አስቀምጥ"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"አይ፣ አመሰግናለሁ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"አዘምን"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index c11279a..9a48e14 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -171,10 +171,10 @@
     <string name="contentServiceSync" msgid="8353523060269335667">"مزامنة"</string>
     <string name="contentServiceSyncNotificationTitle" msgid="7036196943673524858">"تتعذّر المزامنة."</string>
     <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4884451152168188763">"تمت محاولة حذف مقدار كبير من محتوى <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
-    <string name="low_memory" product="tablet" msgid="6494019234102154896">"سعة تخزين الجهاز اللوحي ممتلئة! احذف بعض الملفات لإخلاء مساحة."</string>
-    <string name="low_memory" product="watch" msgid="4415914910770005166">"سعة تخزين المشاهدة ممتلئة! احذف بعض الملفات لتحرير مساحة."</string>
-    <string name="low_memory" product="tv" msgid="516619861191025923">"سعة تخزين التلفزيون ممتلئة. يمكنك حذف بعض الملفات لتوفير مساحة فارغة."</string>
-    <string name="low_memory" product="default" msgid="3475999286680000541">"سعة تخزين الهاتف ممتلئة. احذف بعض الملفات لإخلاء مساحة."</string>
+    <string name="low_memory" product="tablet" msgid="6494019234102154896">"مساحة تخزين الجهاز اللوحي ممتلئة! احذف بعض الملفات لإخلاء مساحة."</string>
+    <string name="low_memory" product="watch" msgid="4415914910770005166">"مساحة تخزين المشاهدة ممتلئة! احذف بعض الملفات لتحرير مساحة."</string>
+    <string name="low_memory" product="tv" msgid="516619861191025923">"مساحة تخزين التلفزيون ممتلئة. يمكنك حذف بعض الملفات لتوفير مساحة فارغة."</string>
+    <string name="low_memory" product="default" msgid="3475999286680000541">"مساحة تخزين الهاتف ممتلئة. احذف بعض الملفات لإخلاء مساحة."</string>
     <plurals name="ssl_ca_cert_warning" formatted="false" msgid="5106721205300213569">
       <item quantity="zero">تم تثبيت شهادة المرجع المصدق</item>
       <item quantity="two">تم تثبيت شهادتي المرجع المصدق</item>
@@ -304,6 +304,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"الميكروفون"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"تسجيل الصوت"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بتسجيل الصوت؟"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"التعرّف على النشاط"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"التعرّف على النشاط"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"‏هل تريد السماح للتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالتعرّف على نشاطك البدني؟"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"الكاميرا"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"التقاط صور وتسجيل فيديو"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالتقاط الصور وتسجيل الفيديو؟"</string>
@@ -430,14 +433,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"‏يمكن لهذا التطبيق معرفة موقعك من خلال مصادر الشبكات مثل أبراج الجوّال وشبكات Wi-Fi. ويجب تشغيل خدمات المواقع هذه وأن تكون متاحة على الجهاز اللوحي حتى يتمكن التطبيق من استخدامها."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"‏يمكن لهذا التطبيق معرفة موقعك من خلال مصادر الشبكات مثل أبراج الجوّال وشبكات Wi-Fi. ويجب تشغيل خدمات المواقع هذه وأن تكون متاحة على جهاز التلفزيون حتى يتمكن التطبيق من استخدامها."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"‏يمكن لهذا التطبيق معرفة موقعك من خلال مصادر الشبكات مثل أبراج الجوّال وشبكات Wi-Fi. ويجب تشغيل خدمات المواقع هذه وأن تكون متاحة على الهاتف حتى يتمكن التطبيق من استخدامها."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"الوصول إلى الموقع الجغرافي الدقيق في الخلفية"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"يمكن لهذا التطبيق معرفة موقعك الجغرافي بالضبط في أي وقت يعمل به في الخلفية. ويجب تفعيل خدمات الموقع الجغرافي هذه وأن تكون متاحة على الهاتف حتى يتمكن التطبيق من استخدامها. وقد يؤدي هذا إلى زيادة استهلاك طاقة البطارية."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"الوصول إلى الموقع الجغرافي في الخلفية"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"إذا تمّ منح إذن التطبيق هذا بالإضافة إلى الموقع الجغرافي التقريبي أو الدقيق، يمكن للتطبيق الوصول إلى الموقع الجغرافي أثناء تشغيله في الخلفية."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"تغيير إعداداتك الصوتية"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"للسماح للتطبيق بتعديل إعدادات الصوت العامة مثل مستوى الصوت وأي السماعات يتم استخدامها للاستماع."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"تسجيل الصوت"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"يمكن لهذا التطبيق تسجيل الصوت باستخدام الميكروفون في أي وقت."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"‏إرسال أوامر إلى شريحة SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"‏السماح للتطبيق بإرسال أوامر إلى شريحة SIM. وهذا أمر بالغ الخطورة."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"التعرّف على النشاط البدني"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"يمكن لهذا التطبيق التعرّف على نشاطك البدني."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"التقاط صور وفيديوهات"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"يمكن لهذا التطبيق التقاط صور وتسجيل فيديوهات باستخدام الكاميرا في أي وقت."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"التحكم في الاهتزاز"</string>
@@ -530,6 +535,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"للسماح للتطبيق بتعديل مجموعة صورك."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"قراءة المواقع من مجموعة الوسائط التابعة لك"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"للسماح للتطبيق بقراءة المواقع من مجموعة الوسائط التابعة لك."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"يتطلَّب التطبيق <xliff:g id="APP">%s</xliff:g> المصادقة."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"معدّات المقاييس الحيوية غير متاحة."</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"تم اكتشاف بصمة الإصبع بشكل جزئي؛ يرجى إعادة المحاولة."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"تعذرت معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string>
@@ -676,7 +682,7 @@
     <string name="policylab_encryptedStorage" msgid="8901326199909132915">"تعيين تشفير التخزين"</string>
     <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"يمكنك طلب تشفير بيانات التطبيق المخزنة."</string>
     <string name="policylab_disableCamera" msgid="6395301023152297826">"إيقاف الكاميرات"</string>
-    <string name="policydesc_disableCamera" msgid="2306349042834754597">"يمكنك منح استخدام جميع كاميرات الجهاز."</string>
+    <string name="policydesc_disableCamera" msgid="2306349042834754597">"يمكنك منع استخدام جميع كاميرات الجهاز."</string>
     <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"إيقاف بعض ميزات قفل الشاشة"</string>
     <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"منع استخدام بعض ميزات قفل الشاشة."</string>
   <string-array name="phoneTypes">
@@ -1173,7 +1179,7 @@
     <string name="view_flight_desc" msgid="3876322502674253506">"تتبُّع الرحلة الجوية المختارة"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"مساحة التخزين منخفضة"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"قد لا تعمل بعض وظائف النظام"</string>
-    <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ليست هناك سعة تخزينية كافية للنظام. تأكد من أنه لديك مساحة خالية تبلغ ٢٥٠ ميغابايت وأعد التشغيل."</string>
+    <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ليست هناك مساحة تخزين كافية للنظام. تأكد من أنه لديك مساحة خالية تبلغ ٢٥٠ ميغابايت وأعد التشغيل."</string>
     <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> قيد التشغيل"</string>
     <string name="app_running_notification_text" msgid="1197581823314971177">"انقر للحصول على مزيد من المعلومات أو لإيقاف التطبيق."</string>
     <string name="ok" msgid="5970060430562524910">"حسنًا"</string>
@@ -1249,7 +1255,7 @@
     <string name="android_start_title" product="default" msgid="4536778526365907780">"جارٍ بدء تشغيل الهاتف…"</string>
     <string name="android_start_title" product="tablet" msgid="4929837533850340472">"جارٍ بدء تشغيل الجهاز اللوحي…"</string>
     <string name="android_start_title" product="device" msgid="7467484093260449437">"جارٍ بدء تشغيل الجهاز…"</string>
-    <string name="android_upgrading_fstrim" msgid="8036718871534640010">"جارٍ تحسين سعة التخزين."</string>
+    <string name="android_upgrading_fstrim" msgid="8036718871534640010">"جارٍ تحسين مساحة التخزين."</string>
     <string name="android_upgrading_notification_title" product="default" msgid="1511552415039349062">"جارٍ إنهاء تحديث النظام…"</string>
     <string name="app_upgrading_toast" msgid="3008139776215597053">"جارٍ ترقية <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"جارٍ تحسين التطبيق <xliff:g id="NUMBER_0">%1$d</xliff:g> من <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
@@ -1594,7 +1600,7 @@
     <string name="action_menu_overflow_description" msgid="2295659037509008453">"المزيد من الخيارات"</string>
     <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s، %2$s"</string>
     <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s، %2$s، %3$s"</string>
-    <string name="storage_internal" msgid="3570990907910199483">"سعة التخزين المشتركة الداخلية"</string>
+    <string name="storage_internal" msgid="3570990907910199483">"مساحة التخزين المشتركة الداخلية"</string>
     <string name="storage_sd_card" msgid="3282948861378286745">"‏بطاقة SD"</string>
     <string name="storage_sd_card_label" msgid="6347111320774379257">"‏بطاقة SD من <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb_drive" msgid="6261899683292244209">"‏محرك أقراص USB"</string>
@@ -2016,7 +2022,7 @@
     <string name="app_category_news" msgid="7496506240743986873">"الأخبار والمجلات"</string>
     <string name="app_category_maps" msgid="5878491404538024367">"الخرائط والتنقل"</string>
     <string name="app_category_productivity" msgid="3742083261781538852">"الإنتاجية"</string>
-    <string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"سعة التخزين للجهاز"</string>
+    <string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"مساحة التخزين للجهاز"</string>
     <string name="adb_debugging_notification_channel_tv" msgid="5537766997350092316">"‏تصحيح أخطاء USB"</string>
     <string name="time_picker_hour_label" msgid="2979075098868106450">"ساعة"</string>
     <string name="time_picker_minute_label" msgid="5168864173796598399">"دقيقة"</string>
@@ -2037,14 +2043,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> اقتراح للملء التلقائي</item>
       <item quantity="one">اقتراح واحد للملء التلقائي</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"‏هل تريد الحفظ في &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;؟"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"‏هل تريد حفظ <xliff:g id="TYPE">%1$s</xliff:g> في &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;؟"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"‏هل تريد حفظ <xliff:g id="TYPE_0">%1$s</xliff:g> و<xliff:g id="TYPE_1">%2$s</xliff:g> في &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;؟"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"‏هل تريد حفظ <xliff:g id="TYPE_0">%1$s</xliff:g> و<xliff:g id="TYPE_1">%2$s</xliff:g> و<xliff:g id="TYPE_2">%3$s</xliff:g> في &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;؟"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"‏هل تريد التعديل إلى &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;؟"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"‏هل تريد تعديل <xliff:g id="TYPE">%1$s</xliff:g> إلى &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;؟"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"‏هل تريد تعديل <xliff:g id="TYPE_0">%1$s</xliff:g> و<xliff:g id="TYPE_1">%2$s</xliff:g> إلى &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;؟"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"‏هل تريد تعديل <xliff:g id="TYPE_0">%1$s</xliff:g> و<xliff:g id="TYPE_1">%2$s</xliff:g> و<xliff:g id="TYPE_2">%3$s</xliff:g> إلى &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;؟"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"هل تريد الحفظ في "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"؟"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"هل تريد حفظ <xliff:g id="TYPE">%1$s</xliff:g> في "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"؟"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"هل تريد حفظ <xliff:g id="TYPE_0">%1$s</xliff:g> و<xliff:g id="TYPE_1">%2$s</xliff:g> في "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"؟"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"هل تريد حفظ <xliff:g id="TYPE_0">%1$s</xliff:g> و<xliff:g id="TYPE_1">%2$s</xliff:g> و<xliff:g id="TYPE_2">%3$s</xliff:g> في "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"؟"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"هل تريد التحديث في "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"؟"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"هل تريد تحديث <xliff:g id="TYPE">%1$s</xliff:g> في "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"؟"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"هل تريد تحديث <xliff:g id="TYPE_0">%1$s</xliff:g> و<xliff:g id="TYPE_1">%2$s</xliff:g> في "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"؟"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"هل تريد تحديث هذه العناصر في "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g> و<xliff:g id="TYPE_1">%2$s</xliff:g> و<xliff:g id="TYPE_2">%3$s</xliff:g>؟"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"حفظ"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"لا، شكرًا"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"تعديل"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 5a0a7ec..4df7643 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"মাইক্ৰ\'ফ\'ন"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"অডিঅ\' ৰেকর্ড কৰিব পাৰে"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক অডিঅ\' ৰেকৰ্ড কৰিবলৈ অনুমতি দিবনে?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"কার্যকলাপ চিনাক্তকৰণ"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"কাৰ্যকলাপ চিনাক্ত কৰক"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক নিজৰ শাৰীৰিক কাৰ্যকলাপ চিনাক্ত কৰাৰ অনুমতি দিবনে?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"কেমেৰা"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ফট\' তুলিব আৰু ভিডিঅ\' ৰেকৰ্ড কৰিব পাৰে"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক ছবি তুলিবলৈ আৰু ভিডিঅ\' ৰেকৰ্ড কৰিবলৈ অনুমতি দিবনে?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"চেল টাৱাৰ আৰু ৱাই-ফাই নেটৱর্কৰ দৰে নেটৱর্কৰ উৎসসমূহক ভিত্তি কৰি এই এপটোৱে আপোনাৰ অৱস্থান নিৰ্ণয় কৰিব পাৰে। এই অৱস্থানৰ সেৱাসমূহ অন হৈ থাকিলে আৰু আপোনাৰ টেবলেটটোত উপলব্ধ হ\'লেহে এপটোৱে সেইবোৰ ব্যৱহাৰ কৰিবলৈ সক্ষম হ\'ব।"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"চেল টাৱাৰ আৰু ৱাই-ফাই নেটৱর্কৰ দৰে নেটৱর্কৰ উৎসসমূহক ভিত্তি কৰি এই এপটোৱে আপোনাৰ অৱস্থান নিৰ্ণয় কৰিব পাৰে। এই অৱস্থানৰ সেৱাসমূহ অন হৈ থাকিলে আৰু আপোনাৰ টিভিত উপলব্ধ হ\'লেহে এপটোৱে সেইবোৰ ব্যৱহাৰ কৰিবলৈ সক্ষম হ\'ব।"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"এই এপটোৱে ম\'বাইলৰ টাৱাৰ আৰু ৱাই-ফাইৰ নেটৱৰ্ক আদিৰ দৰে নেটৱৰ্কৰ উৎসসমূহক ভিত্তি কৰি আপোনাৰ অৱস্থান চিনাক্ত কৰিব পাৰে। সেই অৱস্থান সেৱাসমূহ আপোনাৰ ফ\'নত সক্ষম অৱস্থাত থাকিলেহে এপটোৱে সেইবোৰ ব্যৱহাৰ কৰিব পাৰিব।"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"নেপথ্যত সঠিক অৱস্থান পাব পাৰে"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"এই এপটোৱে যেতিয়া ই নেপথ্যত চলি থাকে তেতিয়া আপোনাৰ সঠিক অৱস্থান নিৰ্ণয় কৰিব পাৰে। এপটোৱে ব্যৱহাৰ কৰিব পৰাকৈ এই অৱস্থান সেৱাসমূহ অন হৈ থাকিবই লাগিব আৰু আপোনাৰ ফ\'নত উপলব্ধ হ\'ব লাগিব। ইয়াৰ ফলত বেটাৰিৰ খৰচ বাঢ়িব পাৰে।"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"নেপথ্যত চলি থকা সময়ত অৱস্থানৰ এক্সেছ"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"ইয়াৰ উপৰিও যদি ইয়াক আনুমানিক বা সঠিক অৱস্থানৰ এক্সেছ দিয়া হয়, তেন্তে উক্ত এপে নেপথ্যত চলি থকাৰ সময়ত অৱস্থানৰ এক্সেছ লাভ কৰিব পাৰে।"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"আপোনাৰ অডিঅ\' ছেটিংসমূহ সলনি কৰক"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"এপটোক ভলিউমৰ দৰে গ্ল\'বেল অডিঅ\' ছেটিংসমূহ যাৰ স্পীকাৰক আউটপুটৰ বাবে ব্যৱহাৰ হয় তাক সলনি কৰিবলৈ অনুমতি দিয়ে৷"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"অডিঅ\' ৰেকর্ড কৰক"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"এই এপটোৱে যিকোনো সময়তে মাইক্ৰ\'ফ\'ন ব্যৱহাৰ কৰি অডিঅ\' ৰেকৰ্ড কৰিব পাৰে।"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"ছিমলৈ নিৰ্দেশ পঠিয়াব পাৰে"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"ছিমলৈ নিৰ্দেশসমূহ প্ৰেৰণ কৰিবলৈ এপক অনুমতি দিয়ে। ই অতি ক্ষতিকাৰক।"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"শাৰীৰিক কাৰ্যকলাপ চিনাক্ত কৰক"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"এই এপটোৱে আপোনাৰ শাৰীৰিক কাৰ্যকলাপ চিনাক্ত কৰিব পাৰে।"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ফট\' তোলা আৰু ভিডিঅ\' ৰেকৰ্ড কৰা"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"এই এপে যিকোনো সময়তে কেমেৰা ব্যৱহাৰ কৰি ফট\' তুলিব আৰু ভিডিঅ\' ৰেকর্ড কৰিব পাৰে।"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"কম্পন নিয়ন্ত্ৰণ কৰক"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"এপক আপোনাৰ ফট’ সংগ্ৰহ সালসলনি কৰিবলৈ দিয়ে।"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"আপোনাৰ মিডিয়া সংগ্ৰহৰ অৱস্থান পঢ়িবলৈ"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"এপক আপোনাৰ মিডিয়া সংগ্ৰহৰ অৱস্থান পঢ়িবলৈ দিয়ে।"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> এপে বিশ্বাসযোগ্যতাৰ প্ৰমাণ কৰিব বিচাৰে।"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"বায়োমেট্ৰিক হাৰ্ডৱেৰ উপলব্ধ নহয়"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ফিংগাৰপ্ৰিণ্ট আংশিকভাৱে চিনাক্ত কৰা হৈছে। অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক৷"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ফিগাৰপ্ৰিণ্টৰ প্ৰক্ৰিয়া সম্পাদন কৰিবপৰা নগ\'ল। অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক৷"</string>
@@ -1898,14 +1904,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g>টা স্বয়ংপূৰ্তি পৰামৰ্শ</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g>টা স্বয়ংপূৰ্তি পৰামৰ্শ</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;ত ছেভ কৰিবনে?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g>ক &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;ত ছেভ কৰিবনে?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> আৰু <xliff:g id="TYPE_1">%2$s</xliff:g>ক &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;ত ছেভ কৰিবনে?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, আৰু <xliff:g id="TYPE_2">%3$s</xliff:g>ক &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;ত ছেভ কৰিবনে?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"আপডে’ট কৰি &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; বনাবনে?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g>ক আপডে’ট কৰি &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; বনাবনে?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> আৰু <xliff:g id="TYPE_1">%2$s</xliff:g>ক আপডে’ট কৰি &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; বনাবনে?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> আৰু <xliff:g id="TYPE_2">%3$s</xliff:g>ক আপডে’ট কৰি to &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; বনাবনে?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"ত ছেভ কৰিবনে?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"ত <xliff:g id="TYPE">%1$s</xliff:g>ক ছেভ কৰিবনে?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"ত <xliff:g id="TYPE_0">%1$s</xliff:g> আৰু <xliff:g id="TYPE_1">%2$s</xliff:g>ক ছেভ কৰিবনে?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"ত <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> আৰু <xliff:g id="TYPE_2">%3$s</xliff:g>ক ছেভ কৰিবনে?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"ত আপডে\'ট কৰিবনে?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"ত <xliff:g id="TYPE">%1$s</xliff:g> আপডে\'ট কৰিবনে?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"ত <xliff:g id="TYPE_0">%1$s</xliff:g> আৰু <xliff:g id="TYPE_1">%2$s</xliff:g> আপডে\'ট কৰিবনে?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"এই তথ্যবোৰ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> আৰু <xliff:g id="TYPE_2">%3$s</xliff:g>ত আপডে\'ট কৰিবনে ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"ছেভ কৰক"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"নালাগে, ধন্যবাদ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"আপডে’ট কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index af65bbb..4bb6c1f 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"səsi qeydə alın"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə səs yazmaq icazəsi verilsin?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Fəaliyyətin tanınması"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"fəaliyyəti tanıyın"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin fiziki fəaliyyətinizi tanımasına icazə verilsin?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"şəkil çəkin və video yazın"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə şəkil və video çəkmək icazəsi verilsin?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Bu tətbiq mobil qüllələr və Wi-Fi şəbəkələri kimi şəbəkə mənbələrinin əassında əkanınızı əldə edə bilər. Bu məkan xidmətləri aktiv edilməlidir və planşetdə tətbiq tərəfindən istifadə üçün əlçatan olmalıdır."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Bu tətbiq mobil qüllələr və Wi-Fi şəbəkələri kimi şəbəkə mənbələrinin əassında əkanınızı əldə edə bilər. Bu məkan xidmətləri aktiv edilməlidir və TV\'də tətbiq tərəfindən istifadə üçün əlçatan olmalıdır."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Bu tətbiq mobil qüllələr və Wi-Fi şəbəkələri kimi şəbəkə mənbələrinin əassında əkanınızı əldə edə bilər. Bu məkan xidmətləri aktiv edilməlidir və telefonda tətbiq tərəfindən istifadə üçün əlçatan olmalıdır."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"arxa fonda dəqiq məkana daxil olun"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Bu tətbiq istənilən zaman arxa fonda olduqda dəqiq məkanınızı əldə edə bilər. Tətbiqin bunlardan istifadə etməsi üçün bu məkan xidmətləri aktiv edilməlidir və telefonda əlçatan olmalıdır. Bu, batareya sərfiyyatını artıra bilər."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"arxa fonda məkan girişi"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Bu, təqribi və ya dəqiq məkan girişinə əlavə olaraq verilərsə, tətbiq arxa fonda işləyərkən məkana daxil ola bilər."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"audio ayarlarınızı dəyişir"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tətbiqə səs və hansı spikerin çıxış üçün istifadə olunduğu kimi qlobal səs ayarlarını dəyişdirməyə imkan verir."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"səs yaz"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Bu tətbiq istədiyiniz zaman mikrofonu istifadə edərək audio qeyd edə bilər."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"əmrləri SIM\'ə göndərin"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Tətbiqə SIM-ə əmrlər göndərməyə imkan verir. Bu, çox təhlükəlidir."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"fiziki fəaliyyəti tanıyın"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Bu tətbiq fiziki fəaliyyətinizi tanıya bilər."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"şəkil və video çəkmək"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Bu tətbiq istədiyiniz zaman kameranı istifadə edərək şəkil çəkə və video qeydə ala bilər."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"vibrasiyaya nəzarət edir"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Tətbiqin foto kolleksiyanıza düzəliş etməsinə icazə verir."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"media kolleksiyanızdan məkanları oxuyun"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Tətbiqin media kolleksiyanızdan məkanları oxumasına icazə verin."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> doğrulamaq istəyir."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrik proqram əlçatan deyil"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Barmaq qismən müəyyən olundu. Lütfən, yenidən cəhd edin."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Barmaq izi tanınmadı. Lütfən, yenidən cəhd edin."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> avtomatik doldurma təklifi</item>
       <item quantity="one">Bir avtomatik doldurma təklifi</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; etiketində yadda saxlansın?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;b&gt; etiketində yadda saxlansın?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> və <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; etiketində yadda saxlansın?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> və <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; etiketində yadda saxlansın?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; etiketində yenilənsin?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;b&gt; etiketində yenilənsin?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> və <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; etiketində yenilənsin?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> və <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; etiketində yenilənsin?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ünvanında yadda saxlansın?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ünvanında yadda saxlansın?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> və <xliff:g id="TYPE_1">%2$s</xliff:g> "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ünvanında yadda saxlansın?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> və <xliff:g id="TYPE_2">%3$s</xliff:g> "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ünvanında yadda saxlansın?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ünvanında yenilənsin?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g> "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ünvanında yenilənsin?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> və <xliff:g id="TYPE_1">%2$s</xliff:g> "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ünvanında yenilənsin?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Bu elementlər "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ünvanında yenilənsin: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> və <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Yadda saxlayın"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Xeyr, çox sağ olun"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Yeniləyin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 805082a..e650f2d 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -295,6 +295,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"snima zvuk"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Želite li da omogućite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima zvuk?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Prepoznavanje aktivnosti"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"prepoznavanje aktivnosti"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Želite li da omogućite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prepoznaje fizičke aktivnosti?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"snima slike i video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Želite li da omogućite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima slike i video snimke?"</string>
@@ -421,14 +424,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ova aplikacija može da pristupi vašoj lokaciji pomoću izvora mreže, kao što su mobilni predajnici i Wi-Fi mreže. Ove usluge lokacije moraju da budu uključene i dostupne na tabletu da bi aplikacija mogla da ih koristi."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ova aplikacija može da pristupi vašoj lokaciji pomoću izvora mreže, kao što su mobilni predajnici i Wi-Fi mreže. Ove usluge lokacije moraju da budu uključene i dostupne na TV-u da bi aplikacija mogla da ih koristi."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ova aplikacija može da pristupi vašoj lokaciji pomoću izvora mreže, kao što su mobilni predajnici i Wi-Fi mreže. Ove usluge lokacije moraju da budu uključene i dostupne na telefonu da bi aplikacija mogla da ih koristi."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"pristup preciznoj lokaciji u pozadini"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ova aplikacija može da odredi vašu tačnu lokaciju svaki put kada radi u pozadini. Ove usluge lokacije moraju da budu uključene i dostupne na telefonu da bi aplikacija mogla da ih koristi. To može da poveća potrošnju baterije."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"pristup lokaciji u pozadini"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Ako se pored približnog ili preciznog pristupa lokacija odobri i ovaj, aplikacija može da pristupa lokaciji dok je pokrenuta u pozadini."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"promena audio podešavanja"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Dozvoljava aplikaciji da menja globalna audio podešavanja kao što su jačina zvuka i izbor zvučnika koji se koristi kao izlaz."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"snimanje audio zapisa"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Ova aplikacija može da snima zvuk pomoću mikrofona u bilo kom trenutku."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"slanje komandi na SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Omogućava aplikaciji da šalje komande SIM kartici. To je veoma opasno."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"prepoznavanje fizičkih aktivnosti"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Ova aplikacija može da prepozna fizičke aktivnosti."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"snimanje fotografija i video snimaka"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Ova aplikacija može da snima fotografije i video snimke pomoću kamere u bilo kom trenutku."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrola vibracije"</string>
@@ -521,6 +526,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Dozvoljava aplikaciji da menja kolekciju slika."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"čitanje lokacija iz medijske kolekcije"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Dozvoljava aplikaciji da čita lokacije iz medijske kolekcije."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi da potvrdi vaš identitet."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrijski hardver nije dostupan"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Otkriven je delimični otisak prsta. Probajte ponovo."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nije uspela obrada otiska prsta. Probajte ponovo."</string>
@@ -1932,14 +1938,14 @@
       <item quantity="few"><xliff:g id="COUNT">%1$s</xliff:g> automatski popunjena predloga</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> automatski popunjenih predloga</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Želite li da sačuvate u: &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Želite li da sačuvate stavku <xliff:g id="TYPE">%1$s</xliff:g> u: &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Želite li da sačuvate stavke <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> u: &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Želite li da sačuvate stavke <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> u: &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Želite li da ažurirate na &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Želite li da ažurirate stavku <xliff:g id="TYPE">%1$s</xliff:g> na &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Želite li da ažurirate stavke <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> na &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Želite li da ažurirate stavke <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> na &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Želite li da sačuvate u usluzi "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Želite li da sačuvate stavku <xliff:g id="TYPE">%1$s</xliff:g> u usluzi "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Želite li da sačuvate stavke <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> u usluzi "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Želite li da sačuvate stavke <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> u usluzi "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Želite li da ažurirate u usluzi "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Želite li da ažurirate stavku <xliff:g id="TYPE">%1$s</xliff:g> u usluzi "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Želite li da ažurirate stavke <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> u usluzi "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Želite li da ažurirate ove stavke u usluzi "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Sačuvaj"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Ne, hvala"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Ažuriraj"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 7692220..db75d1d 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -298,6 +298,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Мікрафон"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"запісваць аўдыя"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; запісваць аўдыя?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Распазнаванне актыўнасці"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"распазнаваць актыўнасць"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; распазнаваць фізічную актыўнасць?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"рабіць фатаздымкі і запісваць відэа"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; рабіць фота і запісваць відэа?"</string>
@@ -424,14 +427,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Гэта праграма можа атрымліваць звесткі пра ваша месцазнаходжанне на падставе даных сеткавых крыніц, такіх як вышкі сотавай сувязі і сеткі Wi-Fi. Гэтыя сэрвісы вызначэння месцазнаходжання павінны быць уключаны i даступныя на вашым планшэце, каб праграма магла іх выкарыстоўваць."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Гэта праграма можа атрымліваць звесткі пра ваша месцазнаходжанне на падставе даных сеткавых крыніц, такіх як вышкі сотавай сувязі і сеткі Wi-Fi. Гэтыя сэрвісы вызначэння месцазнаходжання павінны быць уключаны i даступныя на вашым тэлевізары, каб праграма магла іх выкарыстоўваць."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Гэта праграма можа атрымліваць звесткі пра ваша месцазнаходжанне на падставе даных сеткавых крыніц, такіх як вышкі сотавай сувязі і сеткі Wi-Fi. Гэтыя сэрвісы вызначэння месцазнаходжання павінны быць уключаны i даступныя на вашым тэлефоне, каб праграма магла іх выкарыстоўваць."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"доступ да дакладнага месцазнаходжання ў фонавым рэжыме"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Гэта праграма можа атрымліваць звесткі пра ваша дакладнае месцазнаходжанне ў любы час, калі яна працуе ў фонавым рэжыме. Службы геалакацыі павінны быць уключаны і даступныя на вашым тэлефоне, каб праграма магла імі карыстацца. Гэта можа павялічыць спажыванне зараду акумулятара."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"доступ да вызначэння месцазнаходжання ў фонавым рэжыме"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Акрамя доступу да прыкладнага ці дакладнага месцазнаходжання праграма можа мець доступ да вызначэння геалакацыі ў фонавым рэжыме працы. На гэта патрабуецца дазвол."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"змяняць налады аудыё"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Дазваляе прыкладанням змяняць глабальныя налады гуку, такія як моц і тое, што дынамік выкарыстоўваецца для выхаду."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"запісваць аўдыё"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Гэта праграма можа у любы час запісваць аўдыя, выкарыстоўваючы мікрафон."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"адпраўляць каманды на SIM-карту"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Дазваляе праграме адпраўляць каманды SIM-карце. Гэта вельмі небяспечна."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"распазнаваць фізічную актыўнасць"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Гэта праграма можа распазнаваць фізічную актыўнасць."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"рабіць фатаграфіі і відэа"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Гэта праграма можа рабіць фота і запісваць відэа з дапамогай камеры ў любы час."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"кіраванне вібрацыяй"</string>
@@ -524,6 +529,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Праграма зможа змяняць фотакалекцыю."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"паказваць месцазнаходжанне ў калекцыі мультымедыя"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Праграма зможа паказваць месцазнаходжанне ў калекцыі мультымедыя."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Праграма \"<xliff:g id="APP">%s</xliff:g>\" патрабуе аўтэнтыфікацыі."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Біяметрычнае абсталяванне недаступнае"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Выяўлена частка адбіткаў пальцаў. Паспрабуйце яшчэ раз."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не атрымалася апрацаваць адбітак пальца. Паспрабуйце яшчэ раз."</string>
@@ -1967,14 +1973,14 @@
       <item quantity="many"><xliff:g id="COUNT">%1$s</xliff:g> прапаноў аўтазапаўнення</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> прапановы аўтазапаўнення</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Захаваць у &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Захаваць <xliff:g id="TYPE">%1$s</xliff:g> у &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Захаваць <xliff:g id="TYPE_0">%1$s</xliff:g> і <xliff:g id="TYPE_1">%2$s</xliff:g> у &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Захаваць <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> і <xliff:g id="TYPE_2">%3$s</xliff:g> у &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Абнавіць у &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Абнавіць <xliff:g id="TYPE">%1$s</xliff:g> у &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Абнавіць <xliff:g id="TYPE_0">%1$s</xliff:g> і <xliff:g id="TYPE_1">%2$s</xliff:g> у &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Абнавіць <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> і <xliff:g id="TYPE_2">%3$s</xliff:g> у &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Захаваць у сэрвісе "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Захаваць даныя \"<xliff:g id="TYPE">%1$s</xliff:g>\" у сэрвісе "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Захаваць даныя \"<xliff:g id="TYPE_0">%1$s</xliff:g>\" і \"<xliff:g id="TYPE_1">%2$s</xliff:g>\" у сэрвісе "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Захаваць даныя \"<xliff:g id="TYPE_0">%1$s</xliff:g>\", \"<xliff:g id="TYPE_1">%2$s</xliff:g>\" і \"<xliff:g id="TYPE_2">%3$s</xliff:g>\" у сэрвісе "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Абнавіць у сэрвісе "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Абнавіць даныя \"<xliff:g id="TYPE">%1$s</xliff:g>\" у сэрвісе "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Абнавіць даныя \"<xliff:g id="TYPE_0">%1$s</xliff:g>\" і \"<xliff:g id="TYPE_1">%2$s</xliff:g>\" у сэрвісе "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Абнавіць наступныя элементы ў сэрвісе "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> і <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Захаваць"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Не, дзякуй"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Абнавіць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 33c4898..e2e5bd4 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"записва звук"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да записва аудио?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Разпознаване на активността"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"разпознаване на активността"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да разпознава физическата ви активност?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"да прави снимки и записва видеоклипове"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да прави снимки и да записва видеоклипове?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Приложението може да получава данни за местоположението ви въз основа на мрежови източници, като клетъчни кули и Wi-Fi. Тези услуги за местоположение трябва да са включени и налице на таблета ви, за да могат да се използват от приложението."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Приложението може да получава данни за местоположението ви въз основа на мрежови източници, като клетъчни кули и Wi-Fi. Тези услуги за местоположение трябва да са включени и налице на телевизора ви, за да могат да се използват от приложението."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Приложението може да получава данни за местоположението ви въз основа на мрежови източници, като клетъчни кули и Wi-Fi. Тези услуги за местоположение трябва да са включени и налице на телефона ви, за да могат да се използват от приложението."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"достъп до точното местоположение на заден план"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Приложението може да получава данни за точното ви местоположение по всяко време, когато работи на заден план. Тези услуги за местоположение трябва да са включени и налице на телефона ви, за да могат да се използват от приложението. Това може да увеличи потреблението на батерията."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"достъп до местоположението на заден план"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Ако разрешението бъде предоставено в допълнение към достъпа до приблизителното или точното местоположение, приложението може да осъществява достъп до местоположението, докато се изпълнява на заден план."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"промяна на настройките ви за звука"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Разрешава на приложението да променя глобалните настройки за звука, като например силата и това, кой високоговорител се използва за изход."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"записва звук"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Това приложение може по всяко време да записва звук посредством микрофона."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"изпращане на команди до SIM картата"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Разрешава на приложението да изпраща команди до SIM картата. Това е много опасно."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"разпознаване на физическата активност"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Това приложение може да разпознава физическата ви активност."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"правене на снимки и видеоклипове"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Това приложение може по всяко време да прави снимки и да записва видеоклипове посредством камерата."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"контролиране на вибрирането"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Разрешава на приложението да променя колекцията ви от снимки."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"да чете местоположенията от мултимедийната ви колекция"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Разрешава на приложението да чете местоположенията от мултимедийната ви колекция."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Приложението <xliff:g id="APP">%s</xliff:g> изисква удостоверяване."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометричният хардуер не е налице"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Открит е частичен отпечатък. Моля, опитайте отново."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Отпечатъкът не можа да се обработи. Моля, опитайте отново."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> предложения за автоматично попълване</item>
       <item quantity="one">1 предложение за автоматично попълване</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Да се запази ли в/ъв &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> да се запази ли в/ъв &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> да се запазят ли в/ъв &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g> да се запазят ли в/ъв &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Да се актуализира ли в(ъв) &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> да се актуализира ли в(ъв) &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> да се актуализират ли в(ъв) &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g> да се актуализират ли в(ъв) &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Искате ли да запазите в(ъв) "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Искате ли да запазите <xliff:g id="TYPE">%1$s</xliff:g> в(ъв) "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Искате ли да запазите <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> в(ъв) "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Искате ли да запазите <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g> в(ъв) "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Искате ли да актуализирате в(ъв) "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Искате ли да актуализирате <xliff:g id="TYPE">%1$s</xliff:g> в(ъв) "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Искате ли да актуализирате <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> в(ъв) "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Искате ли да актуализирате тези елементи в(ъв) "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Запазване"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Не, благодаря"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Актуализиране"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index f0787b4..0122c6c 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"মাইক্রোফোন"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"অডিও রেকর্ড"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে অডিও রেকর্ড করতে দেবেন?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"অ্যাক্টিভিটি শনাক্তকরণ"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"অ্যাক্টিভিটি শনাক্ত করুন"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; কে আপনার শারীরিক অ্যাক্টিভিটি শনাক্ত করার অনুমতি দেবেন?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"ক্যামেরা"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ছবি তোলা এবং ভিডিও রেকর্ড"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে ফটো তুলতে এবং ভিডিও রেকর্ড করতে দেবেন?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"মোবাইল টাওয়ার এবং ওয়াই-ফাই নেটওয়ার্কগুলির মত নেটওয়ার্ক উৎসগুলির উপর ভিত্তি করে এই অ্যাপটি আপনার লোকেশন শনাক্ত করতে পারে৷ এই লোকেশন পরিষেবাগুলি অবশ্যই চালু রাখতে হবে এবং অ্যাপটি যাতে সেগুলি ব্যবহার করতে পারে সেইজন্য সেগুলিকে আপনার ট্যাবলেটে উপলব্ধ করে রাখতে হবে৷"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"মোবাইল টাওয়ার এবং ওয়াই-ফাই নেটওয়ার্কগুলির মত নেটওয়ার্কের উৎসগুলির উপর ভিত্তি করে এই অ্যাপটি আপনার লোকেশন শনাক্ত করতে পারে৷ এই লোকেশন পরিষেবাগুলি অবশ্যই চালু রাখতে হবে এবং অ্যাপটি যাতে সেগুলি ব্যবহার করতে পারে সেজন্য সেগুলিকে আপনার টিভিতে উপলব্ধ করে রাখতে হবে৷"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"মোবাইল টাওয়ার এবং ওয়াই-ফাই নেটওয়ার্কগুলির মত নেটওয়ার্ক উৎসগুলির উপর ভিত্তি করে এই অ্যাপটি আপনার লোকেশন শনাক্ত করতে পারে৷ এই লোকেশন পরিষেবাগুলি অবশ্যই চালু রাখতে হবে এবং অ্যাপটি যাতে সেগুলি ব্যবহার করতে পারে সেজন্য সেগুলিকে আপনার ফোনে উপলব্ধ করে রাখতে হবে৷"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ব্যাকগ্রাউন্ডে চলতে থাকলে আপনার যথাযথ লোকেশন অ্যাক্সেস করা"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"এই অ্যাপটি ব্যাকগ্রাউন্ডে চলতে থাকলে যেকোনও সময়ে আপনার যথাযথ লোকেশন জানতে পারবে। এই লোকেশন পরিষেবাগুলি অবশ্যই চালু রাখতে হবে এবং আপনার ফোনে সেগুলি উপলভ্য থাকতে হবে যাতে অ্যাপটি সেগুলি ব্যবহার করতে পারে। এর জন্য অতিরিক্ত ব্যাটারি খরচ হতে পারে।"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"ব্যাকগ্রাউন্ডে লোকেশন অ্যাক্সেস করা"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"আনুমানিক বা একদম যথাযথ লোকেশন অ্যাক্সেস করার অনুমতি দেওয়া হলে এই অ্যাপটি ব্যাকগ্রাউন্ডে চালু থাকাকালীন আপনার লোকেশন অ্যাক্সেস করতে পারবে।"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"আপনার অডিও সেটিংস পরিবর্তন করে"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ভলিউম এবং যেখানে স্পিকার আউটপুট হিসাবে ব্যবহৃত হয় সেই সব ক্ষেত্রে গ্লোবাল অডিও সেটিংসের সংশোধন করতে অ্যাপ্লিকেশনটিকে মঞ্জুর করে৷"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"অডিও রেকর্ড"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"এই অ্যাপটি মাইক্রোফোন ব্যবহার করে যে কোনো সময় অডিও রেকর্ড করতে পারে৷"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"সিম এ আদেশগুলি পাঠান"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"অ্যাপ্লিকেশানটিকে সিম কার্ডে কমান্ডগুলি পাঠানোর অনুমতি দেয়৷ এটি খুবই বিপজ্জনক৷"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"শারীরিক অ্যাক্টিভিটি শনাক্ত করুন"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"এই অ্যাপ আপনার শারীরিক অ্যাক্টিভিটি শনাক্ত করতে পারবে।"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ছবি এবং ভিডিও তোলে"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"এই অ্যাপটি যে কোনো সময় ক্যামেরা ব্যবহার করে ছবি তুলতে বা ভিডিও রেকর্ড করতে পারে৷"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ভাইব্রেশন নিয়ন্ত্রণ করুন"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"অ্যাপকে আপনার ফটো সংগ্রহ পরিবর্তন করার অনুমতি দিন।"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"ডিয়া সংগ্রহ থেকে লোকেশন দেখতে দিন"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"আপনার মিডিয়া সংগ্রহ থেকে লোকেশন দেখতে অ্যাপকে অনুমতি দিন।"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> অ্যাপ্লিকেশন যাচাই করতে চাইছে।"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"বায়োমেট্রিক হার্ডওয়্যার পাওয়া যাবে না"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"আঙ্গুলের ছাপ আংশিক শনাক্ত করা হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"আঙ্গুলের ছাপ প্রক্রিয়া করা যায়নি৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
@@ -648,7 +654,7 @@
     <string name="policylab_resetPassword" msgid="4934707632423915395">"স্ক্রিন লক পরিবর্তন করুন"</string>
     <string name="policydesc_resetPassword" msgid="1278323891710619128">"স্ক্রিন লক পরিবর্তন করুন৷"</string>
     <string name="policylab_forceLock" msgid="2274085384704248431">"স্ক্রিনটি লক করে"</string>
-    <string name="policydesc_forceLock" msgid="1141797588403827138">"স্ক্রিন কখন কিভাবে লক হবে তা নিয়ন্ত্রণ করে৷"</string>
+    <string name="policydesc_forceLock" msgid="1141797588403827138">"স্ক্রিন কখন কীভাবে লক হবে তা নিয়ন্ত্রণ করে৷"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"সমস্ত ডেটা মুছে দেয়"</string>
     <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"ফ্যাক্টরি ডেটা আবার সেট কার্য সম্পাদনার দ্বারা কোনো রকম সতর্কতা ছাড়াই ট্যাবলেটের ডেটা মোছে৷"</string>
     <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"সতর্কীকরণ ছাড়াই একটি ফ্যাক্টরি ডেটা আবার সেট করার দ্বারা টিভির ডেটা মুছে ফেলে৷"</string>
@@ -1873,7 +1879,7 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"অক্ষম করা <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"কনফারেন্স কল"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"টুলটিপ"</string>
-    <string name="app_category_game" msgid="5431836943981492993">"গেম্স"</string>
+    <string name="app_category_game" msgid="5431836943981492993">"গেম"</string>
     <string name="app_category_audio" msgid="1659853108734301647">"মিউজিক ও অডিও"</string>
     <string name="app_category_video" msgid="2728726078629384196">"চলচ্চিত্র ও ভিডিওগুলি"</string>
     <string name="app_category_image" msgid="4867854544519846048">"ফটো ও ছবিগুলি"</string>
@@ -1898,14 +1904,14 @@
       <item quantity="one">স্বতঃপূর্ণ করার <xliff:g id="COUNT">%1$s</xliff:g>টি প্রস্তাবনা</item>
       <item quantity="other">স্বতঃপূর্ণ করার <xliff:g id="COUNT">%1$s</xliff:g>টি প্রস্তাবনা</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; এ সংরক্ষণ করবেন?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> কে &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;এ সংরক্ষণ করবেন?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> এবং <xliff:g id="TYPE_1">%2$s</xliff:g> কে &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; এ সংরক্ষণ করবেন?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, এবং <xliff:g id="TYPE_2">%3$s</xliff:g> কে &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; এ সংরক্ষণ করবেন?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;-তে আপডেট করতে চান?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> থেকে &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;-এ আপডেট করতে চান?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> এবং <xliff:g id="TYPE_1">%2$s</xliff:g>o &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;-এ আপডেট করতে চান?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>,এবং <xliff:g id="TYPE_2">%3$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; আপডেট করতে চান?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"-এ সেভ করতে চান?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"-এ <xliff:g id="TYPE">%1$s</xliff:g> সেভ করতে চান?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"-এ <xliff:g id="TYPE_0">%1$s</xliff:g> এবং <xliff:g id="TYPE_1">%2$s</xliff:g> সেভ করতে চান?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"-এ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> এবং <xliff:g id="TYPE_2">%3$s</xliff:g> সেভ করতে চান?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"-এ আপডেট করতে চান?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"-এ <xliff:g id="TYPE">%1$s</xliff:g> আপডেট করতে চান?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"-এ <xliff:g id="TYPE_0">%1$s</xliff:g> এবং <xliff:g id="TYPE_1">%2$s</xliff:g> আপডেট করতে চান?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"-এ এই আইটেমগুলি আপডেট করতে চান: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> এবং <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"সেভ করুন"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"না থাক"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"আপডেট করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index e895ce0..453bedf 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -262,7 +262,7 @@
     <string name="notification_channel_vpn" msgid="8330103431055860618">"Status VPN-a"</string>
     <string name="notification_channel_device_admin" msgid="1568154104368069249">"Administracija uređaja"</string>
     <string name="notification_channel_alerts" msgid="4496839309318519037">"Upozorenja"</string>
-    <string name="notification_channel_retail_mode" msgid="6088920674914038779">"Promotivna demonstracija u maloprodaji"</string>
+    <string name="notification_channel_retail_mode" msgid="6088920674914038779">"Prodajna demonstracija"</string>
     <string name="notification_channel_usb" msgid="9006850475328924681">"USB veza"</string>
     <string name="notification_channel_heavy_weight_app" msgid="6218742927792852607">"Pokrenuta je aplikacija"</string>
     <string name="notification_channel_foreground_service" msgid="3931987440602669158">"Aplikacije koje troše bateriju"</string>
@@ -295,6 +295,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"snima zvuk"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snimanje zvuka?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Prepoznavanje aktivnosti"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"prepoznavanje aktivnosti"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Dozvoliti aplikaciji<xliff:g id="APP_NAME">%1$s</xliff:g> prepoznavanje vaše fizičke aktivnosti?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"slika i snima videozapise"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snimanje slika i videozapisa?"</string>
@@ -421,14 +424,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ova aplikacija može odrediti vašu lokaciju na osnovu izvora mreže kao što su predajnici za mobilnu mrežu i WiFi mreže. Ove usluge za određivanje lokacije moraju biti uključene i omogućene na vašem tabletu kako bi ih aplikacija mogla koristiti."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ova aplikacija može odrediti vašu lokaciju na osnovu izvora mreže kao što su predajnici za mobilnu mrežu i WiFi mreže. Ove usluge za određivanje lokacije moraju biti uključene i omogućene na vašem TV-u kako bi ih aplikacija mogla koristiti."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ova aplikacija može odrediti vašu lokaciju na osnovu izvora mreže kao što su predajnici za mobilnu mrežu i WiFi mreže. Ove usluge za određivanje lokacije moraju biti uključene i omogućene na vašem telefonu kako bi ih aplikacija mogla koristiti."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"pristupi tačnoj lokaciji u pozadini"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ova aplikacija može odrediti vašu tačnu lokaciju u svakom trenutku kada je u pozadini. Ove usluge za određivanje lokacije moraju biti uključene i dostupne na vašem telefonu da ih aplikacija može koristiti. To može izazvati povećanje potrošnje baterije."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"pristup lokaciji u pozadini"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Ako je ovo odobreno, pored pristupa približnoj ili tačnoj lokaciji, aplikacija može pristupiti lokaciji dok radi u pozadini."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"izmjene postavki zvuka"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Omogućava aplikaciji izmjenu općih postavki zvuka, kao što su jačina zvuka i izbor izlaznog zvučnika."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"snimanje audiozapisa"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Ova aplikacija može u svakom trenutku snimati zvuk koristeći mikrofon."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"slanje komandi SIM kartici"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Omogućava aplikaciji slanje naredbi na SIM. Ovo je vrlo opasno."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"prepoznavanje fizičke aktivnosti"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Ova aplikacija može prepoznati vašu fizičku aktivnost."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"snimanje slika i videozapisa"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Ova aplikacija može slikati fotografije i snimati videozapise koristeći kameru bilo kada."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrola vibracije"</string>
@@ -521,6 +526,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Omogućava aplikaciji da mijenja vašu kolekciju fotografija."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"čitanje lokacija iz kolekcije medija"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Omogućava aplikaciji da čita lokacije iz vaše kolekcije medija."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikacija <xliff:g id="APP">%s</xliff:g> traži autentifikaciju."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrijski hardver nije dostupan"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Otkriven je djelomičan otisak prsta. Pokušajte ponovo."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nije uspjela obrada otiska prsta. Pokušajte ponovo."</string>
@@ -831,7 +837,7 @@
     <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Pogrešno ste unijeli svoju lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nBroj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
     <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Pogrešno ste unijeli svoj PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nBroj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Pogrešno ste unijeli uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Možete pokušati još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta. Nakon toga ćete morati otključati tablet prijavom na svoj Google račun.\n\n Broj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_2">%3$d</xliff:g>"</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Uzorak za otključavanje ste neispravno nacrtali <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, bit će zatraženo da TV otključate pomoću Google prijave.\n\n Pokušajte opet za <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Uzorak za otključavanje ste neispravno nacrtali <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, bit će zatraženo da TV otključate pomoću Google prijave.\n\n Pokušajte opet za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Pogrešno ste unijeli uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Možete pokušati još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta. Nakon toga ćete morati otključati telefon prijavom na svoj Google račun.\n\n Broj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_2">%3$d</xliff:g>"</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Pogrešno ste pokušali otključati tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Možete pokušati još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta. Ukoliko ni tada ne uspijete otključati tablet, tablet će se vratiti na fabričke postavke i svi korisnički podaci bit će izgubljeni."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Pokušali ste <xliff:g id="NUMBER_0">%1$d</xliff:g> puta neispravno otključati TV. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, TV će biti vraćen na fabričke postavke i svi podaci korisnika bit će izgubljeni."</string>
@@ -1635,9 +1641,9 @@
     <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Pokušali ste otključati tablet na pogrešan način <xliff:g id="NUMBER">%d</xliff:g> puta. Tablet će sada biti vraćen na fabričke postavke."</string>
     <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Pokušali ste <xliff:g id="NUMBER">%d</xliff:g> puta neispravno otključati TV. TV će sada biti vraćen na fabričke postavke."</string>
     <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Pokušali ste otključati telefon na pogrešan način <xliff:g id="NUMBER">%d</xliff:g> puta. Telefon će sada biti vraćen na fabričke postavke."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Pogrešno ste nacrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako napravite još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, od vas će se tražiti da otključate tablet pomoću e-pošte. \n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Uzorak za otključavanje ste pogrešno nacrtali <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, bit će zatraženo da TV otključate pomoću računa e-pošte.\n\n Pokušajte opet za <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Pogrešno ste nacrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako napravite još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, od vas će se tražiti da otključate telefon pomoću e-pošte. \n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Pogrešno ste nacrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako napravite još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, od vas će se tražiti da otključate tablet pomoću e-pošte. \n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Uzorak za otključavanje ste pogrešno nacrtali <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, bit će zatraženo da TV otključate pomoću računa e-pošte.\n\n Pokušajte opet za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Pogrešno ste nacrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako napravite još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, od vas će se tražiti da otključate telefon pomoću e-pošte. \n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ukloni"</string>
     <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Želite li pojačati zvuk iznad preporučenog nivoa?\n\nDužim slušanjem glasnog zvuka možete oštetiti sluh."</string>
@@ -1934,14 +1940,14 @@
       <item quantity="few"><xliff:g id="COUNT">%1$s</xliff:g> prijedloga za automatsko popunjavanje</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> prijedloga za automatsko popunjavanje</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Želite li sačuvati u &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Želite li da se <xliff:g id="TYPE">%1$s</xliff:g> sačuva u &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Želite li da se <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> sačuvaju u &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Želite li da se <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, i <xliff:g id="TYPE_2">%3$s</xliff:g> sačuvaju &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Želite li ažurirati na &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Želite li da se <xliff:g id="TYPE">%1$s</xliff:g> ažurira na &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Želite li da se <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> ažuriraju na &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Želite li da se <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, i <xliff:g id="TYPE_2">%3$s</xliff:g> ažuriraju na &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Sačuvati u "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Sačuvati <xliff:g id="TYPE">%1$s</xliff:g> u "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Sačuvati <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> u "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Sačuvati <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> u "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Ažurirati u "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Ažurirati <xliff:g id="TYPE">%1$s</xliff:g> u "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Ažurirati <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> u "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Ažurirati ove stavke u "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Sačuvaj"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Ne, hvala"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Ažuriraj"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index b1d4994..a212014 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Micròfon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"gravar àudio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gravi àudio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Reconeixement de l\'activitat"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"reconèixer l\'activitat"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; reconegui la teva activitat física?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Càmera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"fer fotos i vídeos"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; faci fotos i vídeos?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Aquesta aplicació pot obtenir la teva ubicació a partir de fonts de xarxa, com ara torres de telefonia mòbil i xarxes Wi-Fi. Aquests serveis d\'ubicació han d\'estar activats i disponibles a la tauleta perquè l\'aplicació els pugui utilitzar."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Aquesta aplicació pot obtenir la teva ubicació a partir de fonts de xarxa, com ara torres de telefonia mòbil i xarxes Wi-Fi. Aquests serveis d\'ubicació han d\'estar activats i disponibles al televisor perquè l\'aplicació els pugui utilitzar."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Aquesta aplicació pot obtenir la teva ubicació a partir de fonts de xarxa, com ara torres de telefonia mòbil i xarxes Wi-Fi. Aquests serveis d\'ubicació han d\'estar activats i disponibles al telèfon perquè l\'aplicació els pugui utilitzar."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"accedeix a la ubicació exacta en segon pla"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Aquesta aplicació pot obtenir la teva ubicació exacta sempre que estigui en segon pla. Aquests serveis d\'ubicació han d\'estar activats i disponibles al telèfon perquè l\'aplicació els pugui utilitzar, i això pot fer que el consum de bateria augmenti."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"accedir a la ubicació en segon pla"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Si es concedeix aquest permís, a més de l\'accés a la ubicació aproximada o exacta, l\'aplicació pot accedir a la ubicació mentre s\'executa en segon pla."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"canviar la configuració d\'àudio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permet que l\'aplicació modifiqui la configuració d\'àudio general, com ara el volum i l\'altaveu de sortida que es fa servir."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"gravar àudio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Aquesta aplicació pot gravar àudio amb el micròfon en qualsevol moment."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar ordres a la SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permet que l\'aplicació enviï ordres a la SIM. Això és molt perillós."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"reconèixer l\'activitat física"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Aquesta aplicació pot reconèixer la teva activitat física."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"fer fotos i vídeos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Aquesta aplicació pot fer fotos i gravar vídeos amb la càmera en qualsevol moment."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar la vibració"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permet que l\'aplicació modifiqui la teva col·lecció de fotos."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"llegir les ubicacions de les teves col·leccions multimèdia"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permet que l\'aplicació llegeixi les ubicacions de les teves col·leccions multimèdia."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"L\'aplicació <xliff:g id="APP">%s</xliff:g> vol que t\'autentiquis."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Maquinari biomètric no disponible"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"S\'ha detectat una empremta digital parcial. Torna-ho a provar."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No s\'ha pogut processar l\'empremta digital. Torna-ho a provar."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggeriments d\'emplenament automàtic</item>
       <item quantity="one">Un suggeriment d\'emplenament automàtic</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Vols desar-ho a <xliff:g id="LABEL">%1$s</xliff:g>?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Vols desar <xliff:g id="TYPE">%1$s</xliff:g> a <xliff:g id="LABEL">%2$s</xliff:g>?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Vols desar <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> a <xliff:g id="LABEL">%3$s</xliff:g>?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Vols desar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> a <xliff:g id="LABEL">%4$s</xliff:g>?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Vols actualitzar les dades a &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Vols actualitzar <xliff:g id="TYPE">%1$s</xliff:g> a &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Vols actualitzar <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> a &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Vols actualitzar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> a &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Vols desar-ho a "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Vols desar <xliff:g id="TYPE">%1$s</xliff:g> a "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Vols desar <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> a "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Vols desar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> a "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Vols actualitzar-ho a "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Vols actualitzar <xliff:g id="TYPE">%1$s</xliff:g> a "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Vols actualitzar <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> a "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Vols actualitzar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> a "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Desa"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"No, gràcies"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Actualitza"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index a537d6d..d71f72c 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -298,6 +298,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"nahrávání zvuku"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávat zvuk?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Rozpoznávání aktivity"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"rozpoznávání aktivity"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; rozpoznávat vaši fyzickou aktivitu?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparát"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"pořizování fotografií a nahrávání videa"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotit a nahrávat video?"</string>
@@ -424,14 +427,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Tato aplikace může zjistit vaši polohu podle zdrojů sítě, jako jsou vysílací věže nebo sítě Wi-Fi. Aby tyto služby určování polohy mohla aplikace používat, musí být v tabletu dostupné a musí být zapnuté."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Tato aplikace může zjistit vaši polohu podle zdrojů sítě, jako jsou vysílací věže nebo sítě Wi-Fi. Aby tyto služby určování polohy mohla aplikace používat, musí být v televizi dostupné a musí být zapnuté."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Tato aplikace může zjistit vaši polohu podle zdrojů sítě, jako jsou vysílací věže nebo sítě Wi-Fi. Aby tyto služby určování polohy mohla aplikace používat, musí být v telefonu dostupné a musí být zapnuté."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"přístup k přesné poloze na pozadí"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Tato aplikace může zjistit vaši přesnou polohu vždy, když běží na pozadí. Aby tyto služby určování polohy mohla aplikace používat, musí být v telefonu dostupné a musí být zapnuté. Tyto služby mohou způsobit rychlejší vybíjení baterie."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"přístup k poloze na pozadí"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Bude-li oprávnění uděleno dodatečně k přístupu k přibližné nebo přesné poloze, aplikace bude moci používat polohu při spuštění na pozadí."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"změna nastavení zvuku"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Umožňuje aplikaci změnit globální nastavení zvuku, například hlasitost či reproduktor pro výstup zvuku."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"nahrávání zvuku"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Tato aplikace může pomocí mikrofonu kdykoli zaznamenat zvuk."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"odesílání příkazů do SIM karty"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Umožňuje aplikaci odesílat příkazy na kartu SIM. Toto oprávnění je velmi nebezpečné."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"rozpoznávání fyzické aktivity"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Tyto aplikace dokážou rozpoznat vaši fyzickou aktivitu."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"pořizování fotografií a videí"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Tato aplikace může pomocí fotoaparátu kdykoli pořídit snímek nebo nahrát video."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ovládání vibrací"</string>
@@ -524,6 +529,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Umožňuje aplikaci upravit vaši sbírku fotek."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"čtení míst ze sbírky médií"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Umožňuje aplikaci číst místa z vaší sbírky médií."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikace <xliff:g id="APP">%s</xliff:g> potřebuje provést ověření."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrický hardware není k dispozici"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Byla zjištěna jen část otisku prstu. Zkuste to znovu."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Zpracování otisku prstu se nezdařilo. Zkuste to znovu."</string>
@@ -1967,14 +1973,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> návrhů automatického vyplňování</item>
       <item quantity="one">1 návrh automatického vyplňování</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Uložit do služby &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Uložit položku <xliff:g id="TYPE">%1$s</xliff:g> do služby &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Uložit položky <xliff:g id="TYPE_0">%1$s</xliff:g> a <xliff:g id="TYPE_1">%2$s</xliff:g> do služby &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Uložit položky <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> a <xliff:g id="TYPE_2">%3$s</xliff:g> do služby &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Aktualizovat službu &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Aktualizovat položku <xliff:g id="TYPE">%1$s</xliff:g> ve službě &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Aktualizovat položky <xliff:g id="TYPE_0">%1$s</xliff:g> a <xliff:g id="TYPE_1">%2$s</xliff:g> ve službě &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Aktualizovat položky <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> a <xliff:g id="TYPE_2">%3$s</xliff:g> ve službě &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Uložit do služby "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Uložit údaj <xliff:g id="TYPE">%1$s</xliff:g> do služby "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Uložit údaje <xliff:g id="TYPE_0">%1$s</xliff:g> a <xliff:g id="TYPE_1">%2$s</xliff:g> do služby "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Uložit údaje <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> a <xliff:g id="TYPE_2">%3$s</xliff:g> do služby "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Aktualizovat ve službě "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Aktualizovat údaj <xliff:g id="TYPE">%1$s</xliff:g> ve službě "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Aktualizovat údaje <xliff:g id="TYPE_0">%1$s</xliff:g> a <xliff:g id="TYPE_1">%2$s</xliff:g> ve službě "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Aktualizovat tyto položky ve službě "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> a <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Uložit"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Ne, děkuji"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Aktualizovat"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 28c08b1..6e9ae6b 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"optage lyd"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at optage lyd?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Aktivitetsgenkendelse"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"genkend aktivitet"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Vil du tillade, at &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; genkender din fysiske aktivitet?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"tage billeder og optage video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at tage billeder og optage video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Denne app kan bestemme din placering ved hjælp af netværkskilder, som f.eks. mobilmaster og Wi-Fi-netværk. Disse placeringstjenester skal være aktiverede og tilgængelige på din tablet, før appen kan bruge dem."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Denne app kan bestemme din placering ved hjælp af netværkskilder, som f.eks. mobilmaster og Wi-Fi-netværk. Disse placeringstjenester skal være aktiverede og tilgængelige på dit fjernsyn, før appen kan bruge dem."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Denne app kan bestemme din placering ved hjælp af netværkskilder, som f.eks. mobilmaster og Wi-Fi-netværk. Disse placeringstjenester skal være aktiverede og tilgængelige på din telefon, før appen kan bruge dem."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"få adgang til nøjagtig placering i baggrunden"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Denne app kan få din nøjagtige placering, når den er i baggrunden. Disse placeringstjenester skal være aktiverede og tilgængelige på din telefon, før appen kan bruge dem. Dette kan øge batteriforbruget."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"adgang til placering i baggrunden"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Hvis appen godkendes, og der gives adgang til den omtrentlige eller nøjagtige placering, kan appen registrere placeringen, mens den kører i baggrunden."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skifte dine lydindstillinger"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tillader, at appen kan ændre globale lydindstillinger, som f.eks. lydstyrke og hvilken højttaler der bruges til output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"optage lyd"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Denne app kan til enhver tid optage lyd via mikrofonen."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"send kommandoer til SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Tillader, at appen sender kommandoer til SIM-kortet. Dette er meget farligt."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"genkend fysisk aktivitet"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Denne app kan genkende din fysiske aktivitet."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"tage billeder og optage video"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Med denne app kan du tage billeder og optage video med kameraet når som helst."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"administrere vibration"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Tillader, at appen kan ændre din billedsamling."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"læse placeringer fra din mediesamling"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Tillader, at appen kan læse placeringer fra din mediesamling."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g>-appen kræver din godkendelse."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrisk hardware er ikke tilgængelig"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Der blev registreret et delvist fingeraftryk. Prøv igen."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingeraftrykket kunne ikke behandles. Prøv igen."</string>
@@ -944,7 +950,7 @@
     <string name="menu_sym_shortcut_label" msgid="4019695553731017933">"Sym+"</string>
     <string name="menu_function_shortcut_label" msgid="1984053777418162618">"Fn+"</string>
     <string name="menu_space_shortcut_label" msgid="2410328639272162537">"plads"</string>
-    <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"indtast"</string>
+    <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"angiv"</string>
     <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"slet"</string>
     <string name="search_go" msgid="8298016669822141719">"Søg"</string>
     <string name="search_hint" msgid="1733947260773056054">"Søg…"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> forslag fra autofyld</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> forslag fra autofyld</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Vil du gemme i &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Vil du gemme <xliff:g id="TYPE">%1$s</xliff:g> i &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Vil du gemme <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> i &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Vil du gemme <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g> i &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Vil du opdatere til &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Vil du opdatere <xliff:g id="TYPE">%1$s</xliff:g> til &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Vil du opdatere <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> til &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Vil du opdatere <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g> til &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Vil du gemme i "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Vil du gemme <xliff:g id="TYPE">%1$s</xliff:g> i "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Vil du gemme <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> i "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Vil du gemme <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g> i "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Vil du opdatere i "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Vil du opdatere <xliff:g id="TYPE">%1$s</xliff:g> i "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Vil du opdatere <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> i "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Vil du opdatere disse elementer i "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Gem"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Nej tak"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Opdater"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 4a2efbd..e0f8201 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"Audio aufnehmen"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, Audioaufnahmen zu machen?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Aktivitätserkennung"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"Aktivitäten erkennen"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, deine körperlichen Aktivitäten zu erkennen?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"Bilder und Videos aufnehmen"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, Bilder und Videos aufzunehmen?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Diese App kann deinen Standort mithilfe von Netzwerkquellen wie Mobilfunkmasten und WLAN ermitteln. Die App kann diese Standortdienste nur verwenden, wenn sie auf deinem Tablet aktiviert und verfügbar sind."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Diese App kann deinen Standort mithilfe von Netzwerkquellen wie Mobilfunkmasten und WLAN ermitteln. Die App kann diese Standortdienste nur verwenden, wenn sie auf deinem Fernseher aktiviert und verfügbar sind."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Diese App kann deinen Standort mithilfe von Netzwerkquellen wie Mobilfunkmasten und WLAN ermitteln. Die App kann diese Standortdienste nur verwenden, wenn sie auf deinem Smartphone aktiviert und verfügbar sind."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"im Hintergrund auf den genauen Standort zugreifen"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Diese App kann deinen genauen Standort jederzeit im Hintergrund ermitteln. Die App kann diese Standortdienste nur verwenden, wenn sie auf deinem Smartphone aktiviert und verfügbar sind. Hierdurch kann sich der Akkuverbrauch erhöhen."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"Im Hintergrund auf den Standort zugreifen"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Wenn zusätzlich zum Zugriff auf den ungefähren oder genauen Standort diese Erlaubnis erteilt wird, kann die App bei Ausführung im Hintergrund auf den Standort zugreifen."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"Audio-Einstellungen ändern"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ermöglicht der App, globale Audio-Einstellungen zu ändern, etwa die Lautstärke und den Lautsprecher für die Ausgabe."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"Audio aufnehmen"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Diese App kann jederzeit Audio über das Mikrofon aufnehmen."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"Befehle an die SIM senden"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Ermöglicht der App das Senden von Befehlen an die SIM-Karte. Dies ist äußerst risikoreich."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"körperlichen Aktivitäten erkennen"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Diese App kann deine körperlichen Aktivitäten erkennen."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"Bilder und Videos aufnehmen"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Diese App kann mit der Kamera jederzeit Bilder und Videos aufnehmen."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"Vibrationsalarm steuern"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Ermöglicht der App, deine Fotosammlung zu ändern."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"Standorte aus meiner Mediensammlung abrufen"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ermöglicht der App, Standorte aus deiner Mediensammlung abzurufen."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> möchte, dass du dich authentifizierst."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrische Hardware nicht verfügbar"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Fingerabdruck teilweise erkannt. Bitte versuche es noch einmal."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingerabdruck konnte nicht verarbeitet werden. Bitte versuche es noch einmal."</string>
@@ -545,7 +551,7 @@
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerabdruck-Symbol"</string>
     <string name="permlab_manageFace" msgid="2137540986007309781">"Gesichtserkennungshardware verwalten"</string>
-    <string name="permdesc_manageFace" msgid="8919637120670185330">"Ermöglicht der App, Methoden zum Hinzufügen oder Entfernen von Gesichtsvorlagen anzuwenden."</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Ermöglicht der App,  Gesichtsvorlagen hinzuzufügen oder zu entfernen."</string>
     <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"Gesichtserkennungshardware verwenden"</string>
     <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Ermöglicht der App, für die Authentifizierung Gesichtserkennungshardware zu verwenden"</string>
     <string name="face_acquired_insufficient" msgid="5901287247766106330">"Kann Gesicht nicht verarbeiten. Versuch es erneut."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> AutoFill-Vorschläge</item>
       <item quantity="one">1 AutoFill-Vorschlag</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"In &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; speichern?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> in &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; speichern?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> und <xliff:g id="TYPE_1">%2$s</xliff:g> in &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; speichern?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> und <xliff:g id="TYPE_2">%3$s</xliff:g> in &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; speichern?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Über &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; aktualisieren?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> über &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; aktualisieren?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> und <xliff:g id="TYPE_1">%2$s</xliff:g> über &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; aktualisieren?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> und <xliff:g id="TYPE_2">%3$s</xliff:g> über &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; aktualisieren?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"In "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" speichern?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" speichern?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> und <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" speichern?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> und <xliff:g id="TYPE_2">%3$s</xliff:g> in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" speichern?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"In "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" aktualisieren?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" aktualisieren?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> und <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" aktualisieren?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> und <xliff:g id="TYPE_2">%3$s</xliff:g> in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" aktualisieren?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Speichern"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Nein danke"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Aktualisieren"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 04b9749..6090465 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Μικρόφωνο"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ηχογραφεί"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η εγγραφή ήχου;"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Αναγνώριση δραστηριότητας"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"αναγνώριση δραστηριότητας"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; να αναγνωρίζει τη σωματική σας δραστηριότητα;"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Κάμερα"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"γίνεται λήψη φωτογραφιών και εγγραφή βίντεο"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η λήψη φωτογραφιών και η εγγραφή βίντεο;"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Αυτή η εφαρμογή μπορεί να ανιχνεύσει την τοποθεσία σας βάσει πηγών δικτύου, όπως κεραίες κινητής τηλεφωνίας και δίκτυα Wi-Fi. Αυτές οι υπηρεσίες τοποθεσίας θα πρέπει να είναι ενεργοποιημένες και διαθέσιμες στο tablet που χρησιμοποιείτε, προκειμένου να μπορεί να τις χρησιμοποιήσει η εφαρμογή."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Αυτή η εφαρμογή μπορεί να ανιχνεύσει την τοποθεσία σας βάσει πηγών δικτύου, όπως κεραίες κινητής τηλεφωνίας και δίκτυα Wi-Fi. Αυτές οι υπηρεσίες τοποθεσίας θα πρέπει να είναι ενεργοποιημένες και διαθέσιμες στην τηλεόρασή σας, προκειμένου να μπορεί να τις χρησιμοποιήσει η εφαρμογή."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Αυτή η εφαρμογή μπορεί να ανιχνεύσει την τοποθεσία σας βάσει πηγών δικτύου, όπως κεραίες κινητής τηλεφωνίας και δίκτυα Wi-Fi. Αυτές οι υπηρεσίες τοποθεσίας θα πρέπει να είναι ενεργοποιημένες και διαθέσιμες στο τηλέφωνό σας, προκειμένου να μπορεί να τις χρησιμοποιήσει η εφαρμογή."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"πρόσβαση στην ακριβή τοποθεσία στο παρασκήνιο"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Αυτή η εφαρμογή μπορεί να ανιχνεύσει την ακριβή τοποθεσία σας οποτεδήποτε βρίσκεται στο παρασκήνιο. Αυτές οι υπηρεσίες τοποθεσίας θα πρέπει να είναι ενεργοποιημένες και διαθέσιμες στο τηλέφωνό σας, προκειμένου να μπορεί να τις χρησιμοποιήσει η εφαρμογή. Με την ενεργοποίηση αυτής της ρύθμισης, μπορεί να αυξηθεί η κατανάλωση μπαταρίας."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"πρόσβαση στην τοποθεσία στο παρασκήνιο"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Εάν εκχωρηθεί επιπρόσθετα σε μια καταπροσέγγιση ή ακριβή πρόσβαση τοποθεσίας, η εφαρμογή μπορεί να έχει πρόσβαση στην τοποθεσία κατά την εκτέλεση στο παρασκήνιο."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"αλλάζει τις ρυθμίσεις ήχου"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Επιτρέπει στην εφαρμογή την τροποποίηση καθολικών ρυθμίσεων ήχου, όπως η ένταση και ποιο ηχείο χρησιμοποιείται για έξοδο."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"εγγράφει ήχο"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Αυτή η εφαρμογή μπορεί να κάνει εγγραφή ήχου χρησιμοποιώντας το μικρόφωνο, ανά πάσα στιγμή."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"στέλνει εντολές στην κάρτα SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Επιτρέπει στην εφαρμογή την αποστολή εντολών στην κάρτα SIM. Αυτό είναι εξαιρετικά επικίνδυνο."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"αναγνώριση σωματικής δραστηριότητας"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Αυτή η εφαρμογή μπορεί να αναγνωρίσει τη σωματική σας δραστηριότητα."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"κάνει λήψη φωτογραφιών και βίντεο"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Αυτή η εφαρμογή μπορεί να τραβήξει φωτογραφίες και βίντεο χρησιμοποιώντας την κάμερα, ανά πάσα στιγμή."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ελέγχει τη δόνηση"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Επιτρέπει στην εφαρμογή να τροποποιήσει τη συλλογή φωτογραφιών σας."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"ανάγνωση τοποθεσιών από τη συλλογή πολυμέσων σας"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Επιτρέπει στην εφαρμογή να διαβάσει τοποθεσίες από τη συλλογή πολυμέσων σας."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> επιθυμεί έλεγχο ταυτότητας."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Δεν υπάρχει διαθέσιμος βιομετρικός εξοπλισμός"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Εντοπίστηκε μερικό μοναδικό χαρακτηριστικό. Δοκιμάστε ξανά."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Δεν ήταν δυνατή η επεξεργασία του μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> προτάσεις αυτόματης συμπλήρωσης</item>
       <item quantity="one">Μία πρόταση αυτόματης συμπλήρωσης</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Αποθήκευση σε &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;;"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Αποθήκευση <xliff:g id="TYPE">%1$s</xliff:g> σε &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;;"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Αποθήκευση <xliff:g id="TYPE_0">%1$s</xliff:g> και <xliff:g id="TYPE_1">%2$s</xliff:g> σε &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;;"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Αποθήκευση <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> και <xliff:g id="TYPE_2">%3$s</xliff:g> σε &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;;"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Να γίνει ενημέρωση στο &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;;"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Να γίνει ενημέρωση των δεδομένων <xliff:g id="TYPE">%1$s</xliff:g> στην υπηρεσία &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;;"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Να γίνει ενημέρωση των δεδομένων <xliff:g id="TYPE_0">%1$s</xliff:g> και <xliff:g id="TYPE_1">%2$s</xliff:g> στην υπηρεσία &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;;"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Να γίνει ενημέρωση των δεδομένων <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> και <xliff:g id="TYPE_2">%3$s</xliff:g> στην υπηρεσία &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;;"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Αποθήκευση σε "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>";"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Αποθήκευση <xliff:g id="TYPE">%1$s</xliff:g> σε "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>";"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Αποθήκευση <xliff:g id="TYPE_0">%1$s</xliff:g> και <xliff:g id="TYPE_1">%2$s</xliff:g> σε "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>";"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Αποθήκευση <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> και <xliff:g id="TYPE_2">%3$s</xliff:g> σε "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>";"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Ενημέρωση σε "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>";"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Ενημέρωση <xliff:g id="TYPE">%1$s</xliff:g> σε "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>";"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Ενημέρωση <xliff:g id="TYPE_0">%1$s</xliff:g> και <xliff:g id="TYPE_1">%2$s</xliff:g> σε "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>";"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Ενημέρωση αυτών των στοιχείων "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> και <xliff:g id="TYPE_2">%3$s</xliff:g>;"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Αποθήκευση"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Όχι, ευχαριστώ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Ενημέρωση"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 8592355..e102e5b 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microphone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"record audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Activity recognition"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"recognise activity"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to recognise your physical activity?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"take pictures and record video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can pick up your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"This app can pick up your location based on network sources such as mobile towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"This app can get your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"access precise location in the background"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"This app can get your exact location any time it is in the background. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"access location in the background"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"If this is granted additionally to the approximate or precise location access, the app can access the location while running in the background."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"change your audio settings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"record audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"This app can record audio using the microphone at any time."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"send commands to the SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Allows the app to send commands to the SIM. This is very dangerous."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"recognise physical activity"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"This app can recognise your physical activity."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"take pictures and videos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"This app can take pictures and record videos using the camera at any time."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"control vibration"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Allows the app to modify your photo collection."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"read locations from your media collection"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Application <xliff:g id="APP">%s</xliff:g> wants to authenticate."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> auto-fill suggestions</item>
       <item quantity="one">One auto-fill suggestion</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Save to &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Save <xliff:g id="TYPE">%1$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Save <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Save <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, and <xliff:g id="TYPE_2">%3$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Update to &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Update <xliff:g id="TYPE">%1$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Update <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Save to "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Save <xliff:g id="TYPE">%1$s</xliff:g> to "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Save <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> to "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Save <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g> to "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Update in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Update <xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Update these items in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Save"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"No, thanks"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Update"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 1e075f8..518efc9 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microphone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"record audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Activity recognition"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"recognise activity"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to recognise your physical activity?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"take pictures and record video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can pick up your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"This app can pick up your location based on network sources such as mobile towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"This app can get your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"access precise location in the background"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"This app can get your exact location any time it is in the background. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"access location in the background"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"If this is granted additionally to the approximate or precise location access, the app can access the location while running in the background."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"change your audio settings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"record audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"This app can record audio using the microphone at any time."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"send commands to the SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Allows the app to send commands to the SIM. This is very dangerous."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"recognise physical activity"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"This app can recognise your physical activity."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"take pictures and videos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"This app can take pictures and record videos using the camera at any time."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"control vibration"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Allows the app to modify your photo collection."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"read locations from your media collection"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Application <xliff:g id="APP">%s</xliff:g> wants to authenticate."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> auto-fill suggestions</item>
       <item quantity="one">One auto-fill suggestion</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Save to &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Save <xliff:g id="TYPE">%1$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Save <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Save <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, and <xliff:g id="TYPE_2">%3$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Update to &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Update <xliff:g id="TYPE">%1$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Update <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Save to "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Save <xliff:g id="TYPE">%1$s</xliff:g> to "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Save <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> to "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Save <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g> to "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Update in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Update <xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Update these items in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Save"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"No, thanks"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Update"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 8592355..e102e5b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microphone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"record audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Activity recognition"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"recognise activity"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to recognise your physical activity?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"take pictures and record video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can pick up your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"This app can pick up your location based on network sources such as mobile towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"This app can get your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"access precise location in the background"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"This app can get your exact location any time it is in the background. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"access location in the background"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"If this is granted additionally to the approximate or precise location access, the app can access the location while running in the background."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"change your audio settings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"record audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"This app can record audio using the microphone at any time."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"send commands to the SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Allows the app to send commands to the SIM. This is very dangerous."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"recognise physical activity"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"This app can recognise your physical activity."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"take pictures and videos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"This app can take pictures and record videos using the camera at any time."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"control vibration"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Allows the app to modify your photo collection."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"read locations from your media collection"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Application <xliff:g id="APP">%s</xliff:g> wants to authenticate."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> auto-fill suggestions</item>
       <item quantity="one">One auto-fill suggestion</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Save to &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Save <xliff:g id="TYPE">%1$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Save <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Save <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, and <xliff:g id="TYPE_2">%3$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Update to &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Update <xliff:g id="TYPE">%1$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Update <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Save to "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Save <xliff:g id="TYPE">%1$s</xliff:g> to "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Save <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> to "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Save <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g> to "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Update in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Update <xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Update these items in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Save"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"No, thanks"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Update"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 8592355..e102e5b 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microphone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"record audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Activity recognition"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"recognise activity"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to recognise your physical activity?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"take pictures and record video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can pick up your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"This app can pick up your location based on network sources such as mobile towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"This app can get your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"access precise location in the background"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"This app can get your exact location any time it is in the background. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"access location in the background"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"If this is granted additionally to the approximate or precise location access, the app can access the location while running in the background."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"change your audio settings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"record audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"This app can record audio using the microphone at any time."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"send commands to the SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Allows the app to send commands to the SIM. This is very dangerous."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"recognise physical activity"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"This app can recognise your physical activity."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"take pictures and videos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"This app can take pictures and record videos using the camera at any time."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"control vibration"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Allows the app to modify your photo collection."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"read locations from your media collection"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Application <xliff:g id="APP">%s</xliff:g> wants to authenticate."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> auto-fill suggestions</item>
       <item quantity="one">One auto-fill suggestion</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Save to &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Save <xliff:g id="TYPE">%1$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Save <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Save <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, and <xliff:g id="TYPE_2">%3$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Update to &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Update <xliff:g id="TYPE">%1$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Update <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g> to &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Save to "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Save <xliff:g id="TYPE">%1$s</xliff:g> to "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Save <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> to "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Save <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g> to "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Update in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Update <xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Update these items in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Save"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"No, thanks"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Update"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 9138098..29a797b 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‎‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‎‎Microphone‎‏‎‎‏‎"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‎‎‎‎‏‏‎‎‎‎record audio‎‏‎‎‏‎"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‏‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to record audio?‎‏‎‎‏‎"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎Activity recognition‎‏‎‎‏‎"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‏‎‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‏‎‏‎‎recognize activity‎‏‎‎‏‎"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to recognize your physical activity?‎‏‎‎‏‎"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎Camera‎‏‎‎‏‎"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‎‏‎‎‏‏‏‏‏‎‎‏‎‎‎‎take pictures and record video‎‏‎‎‏‎"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎‏‎‎‏‏‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to take pictures and record video?‎‏‎‎‏‎"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them.‎‏‎‎‏‎"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎‎‏‏‏‏‏‎This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them.‎‏‎‎‏‎"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‏‏‎‏‎‎‎‎‎‎‎‎‏‏‎This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them.‎‏‎‎‏‎"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‎access precise location in the background‎‏‎‎‏‎"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‏‎This app can get your exact location any time it is in the background. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption.‎‏‎‎‏‎"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‎‏‏‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‎access location in the background‎‏‎‎‏‎"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‎‏‏‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎If this is granted additionally to the approximate or precise location access the app can access the location while running in the background.‎‏‎‎‏‎"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎change your audio settings‎‏‎‎‏‎"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎Allows the app to modify global audio settings such as volume and which speaker is used for output.‎‏‎‎‏‎"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‏‎‎‏‏‎record audio‎‏‎‎‏‎"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎‎‎‏‏‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‏‎‏‎‏‏‎‎‏‎This app can record audio using the microphone at any time.‎‏‎‎‏‎"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‎‏‎send commands to the SIM‎‏‎‎‏‎"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎Allows the app to send commands to the SIM. This is very dangerous.‎‏‎‎‏‎"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎recognize physical activity‎‏‎‎‏‎"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‎This app can recognize your physical activity.‎‏‎‎‏‎"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎take pictures and videos‎‏‎‎‏‎"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‏‎‏‎‎‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‎This app can take pictures and record videos using the camera at any time.‎‏‎‎‏‎"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‎‎‏‎‏‏‎‏‎‏‎‎control vibration‎‏‎‎‏‎"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎Allows the app to modify your photo collection.‎‏‎‎‏‎"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎read locations from your media collection‎‏‎‎‏‎"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‎‎‏‎‏‏‏‏‎‏‎‏‎‎Allows the app to read locations from your media collection.‎‏‎‎‏‎"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‏‏‎‎‎‎‎‎Application ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ wants to authenticate.‎‏‎‎‏‎"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎Biometric hardware unavailable‎‏‎‎‏‎"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎Partial fingerprint detected. Please try again.‎‏‎‎‏‎"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‎‎Couldn\'t process fingerprint. Please try again.‎‏‎‎‏‎"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="COUNT">%1$s</xliff:g>‎‏‎‎‏‏‏‎ autofill suggestions‎‏‎‎‏‎</item>
       <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎One autofill suggestion‎‏‎‎‏‎</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎Save to &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="LABEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‎Save ‎‏‎‎‏‏‎<xliff:g id="TYPE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="LABEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎Save ‎‏‎‎‏‏‎<xliff:g id="TYPE_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and ‎‏‎‎‏‏‎<xliff:g id="TYPE_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="LABEL">%3$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‏‏‎‎‎‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‏‎Save ‎‏‎‎‏‏‎<xliff:g id="TYPE_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎, ‎‏‎‎‏‏‎<xliff:g id="TYPE_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎, and ‎‏‎‎‏‏‎<xliff:g id="TYPE_2">%3$s</xliff:g>‎‏‎‎‏‏‏‎ to &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="LABEL">%4$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‎‏‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎Update to &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="LABEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‏‎Update ‎‏‎‎‏‏‎<xliff:g id="TYPE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="LABEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‎Update ‎‏‎‎‏‏‎<xliff:g id="TYPE_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and ‎‏‎‎‏‏‎<xliff:g id="TYPE_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="LABEL">%3$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‎‏‎‏‎‏‎‏‎‎‏‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‏‎Update ‎‏‎‎‏‏‎<xliff:g id="TYPE_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎, ‎‏‎‎‏‏‎<xliff:g id="TYPE_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎, and ‎‏‎‎‏‏‎<xliff:g id="TYPE_2">%3$s</xliff:g>‎‏‎‎‏‏‏‎ to &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="LABEL">%4$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‏‏‎‎‎‏‏‎‏‏‎‎‎‏‏‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‎Save to ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LABEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‎‎‎‏‏‏‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‎‏‎‏‎‎Save ‎‏‎‎‏‏‎<xliff:g id="TYPE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LABEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎Save ‎‏‎‎‏‏‎<xliff:g id="TYPE_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and ‎‏‎‎‏‏‎<xliff:g id="TYPE_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LABEL">%3$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‏‎Save ‎‏‎‎‏‏‎<xliff:g id="TYPE_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎, ‎‏‎‎‏‏‎<xliff:g id="TYPE_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎, and ‎‏‎‎‏‏‎<xliff:g id="TYPE_2">%3$s</xliff:g>‎‏‎‎‏‏‏‎ to ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LABEL">%4$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‏‎‎‎‎‎‎‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‏‏‎Update in ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LABEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‎‎‏‎Update ‎‏‎‎‏‏‎<xliff:g id="TYPE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ in ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LABEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‏‎‎‎Update ‎‏‎‎‏‏‎<xliff:g id="TYPE_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and ‎‏‎‎‏‏‎<xliff:g id="TYPE_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎ in ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LABEL">%3$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‎‏‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‎‏‎‎‏‎‎Update these items in ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LABEL">%4$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎: ‎‏‎‎‏‏‎<xliff:g id="TYPE_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎, ‎‏‎‎‏‏‎<xliff:g id="TYPE_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎, and ‎‏‎‎‏‏‎<xliff:g id="TYPE_2">%3$s</xliff:g>‎‏‎‎‏‏‏‎ ?‎‏‎‎‏‎"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‎‏‎Save‎‏‎‎‏‎"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎No thanks‎‏‎‎‏‎"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‎‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‎‎Update‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 6176b7b..254d6bb 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Micrófono"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"grabar audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Reconocimiento de actividad"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"reconocer actividad"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; reconozca tu actividad física?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Cámara"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"tomar fotografías y grabar videos"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tome fotos y grabe videos?"</string>
@@ -299,7 +302,7 @@
     <string name="permgroupdesc_calllog" msgid="3006237336748283775">"leer y escribir el registro de llamadas telefónicas"</string>
     <string name="permgrouprequest_calllog" msgid="8487355309583773267">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda al registro de las llamadas telefónicas?"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Teléfono"</string>
-    <string name="permgroupdesc_phone" msgid="6234224354060641055">"realizar y administrar llamadas telefónicas"</string>
+    <string name="permgroupdesc_phone" msgid="6234224354060641055">"hacer y administrar llamadas telefónicas"</string>
     <string name="permgrouprequest_phone" msgid="9166979577750581037">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; haga y administre las llamadas telefónicas?"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Sensores corporales"</string>
     <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acceder a los datos del sensor acerca de tus signos vitales"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Esta app puede obtener tu ubicación a través de fuentes de red, como torres de telefonía móvil y redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu tablet para que la app pueda usarlos."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Esta app puede obtener tu ubicación a través de fuentes de red, telefonía móvil y redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu TV para que la app pueda usarlos."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Esta app puede obtener tu ubicación a través de fuentes de red, como torres de celulares y redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu teléfono para que la app pueda usarlos."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"acceder a la ubicación exacta en segundo plano"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Esta app puede obtener tu ubicación exacta en cualquier momento mientras esté en segundo plano. Los servicios de ubicación deben estar activados y disponibles en el teléfono para que la app pueda usarlos. Es posible que aumente el consumo de batería."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"acceder a la ubicación en segundo plano"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Si este permiso se otorga de manera adicional para aproximar o precisar el acceso a la ubicación, la app podrá acceder a la ubicación mientras se ejecuta en segundo plano."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"cambiar tu configuración de audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que la aplicación modifique la configuración de audio global, por ejemplo, el volumen y el altavoz de salida."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"grabar audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Esta app puede grabar audio con el micrófono en cualquier momento."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos a la tarjeta SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que la aplicación envíe comandos a la tarjeta SIM. Usar este permiso es peligroso."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"reconocer actividad física"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Esta app puede reconocer tu actividad física."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"tomar fotografías y grabar videos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Esta app puede tomar fotos y grabar videos con la cámara en cualquier momento."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar la vibración"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite que la app modifique tu colección de fotos."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"leer ubicaciones de tu colección de contenido multimedia"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que la app lea las ubicaciones de tu colección de contenido multimedia."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"La aplicación de <xliff:g id="APP">%s</xliff:g> quiere autenticarte"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"No hay hardware biométrico disponible"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"La huella digital se detectó parcialmente. Vuelve a intentarlo."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No se pudo procesar la huella digital. Vuelve a intentarlo."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> sugerencias de Autocompletar</item>
       <item quantity="one">Una sugerencia de Autocompletar</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"¿Quieres guardar el contenido en &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"¿Quieres guardar tu <xliff:g id="TYPE">%1$s</xliff:g> en &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"¿Quieres guardar tu <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"¿Quieres guardar tu <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> y <xliff:g id="TYPE_2">%3$s</xliff:g> en &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"¿Quieres actualizar los datos en &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"¿Quieres actualizar tu <xliff:g id="TYPE">%1$s</xliff:g> en &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"¿Quieres actualizar tu <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"¿Quieres actualizar tu <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, y <xliff:g id="TYPE_2">%3$s</xliff:g> en &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"¿Quieres guardar en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"¿Quieres guardar <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"¿Quieres guardar <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"¿Quieres guardar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> y <xliff:g id="TYPE_2">%3$s</xliff:g> en "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"¿Quieres actualizar en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"¿Quieres actualizar <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"¿Quieres actualizar <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"¿Quieres actualizar estos elementos en "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> y <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Guardar"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"No, gracias"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Actualizar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index dd766f7..2472e2b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Micrófono"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"grabar audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"reconocimiento de actividad"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"reconocer actividad"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; reconozca tu actividad física?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Cámara"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"hacer fotos y grabar vídeos"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; haga fotos y grabe vídeos?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Esta aplicación puede obtener tu ubicación a partir de fuentes de red como las antenas de telefonía móvil y las redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu tablet para que la aplicación pueda usarlos."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Esta aplicación puede obtener tu ubicación a partir de fuentes de red como las antenas de telefonía móvil y las redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu TV para que la aplicación pueda usarlos."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Esta aplicación puede obtener tu ubicación a partir de fuentes de red como las antenas de telefonía móvil y las redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu teléfono para que la aplicación pueda usarlos."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"acceder a la ubicación exacta en segundo plano"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Esta aplicación puede obtener tu ubicación exacta en cualquier momento cuando está en segundo plano. Estos servicios de ubicación deben estar activados y disponibles en tu teléfono para que la aplicación pueda utilizarlos. Es posible que aumente el consumo de batería."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"acceder a la ubicación en segundo plano"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Si se concede este permiso además del acceso a la ubicación exacta o aproximada, la aplicación podrá acceder a la ubicación mientras se ejecuta en segundo plano."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"cambiar la configuración de audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que la aplicación modifique la configuración de audio global (por ejemplo, el volumen y el altavoz de salida)."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"grabar sonido"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Esta aplicación puede grabar audio con el micrófono en cualquier momento."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos a la tarjeta SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que la aplicación envíe comandos a la tarjeta SIM. Este permiso es muy peligroso."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"reconocer actividad física"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Esta aplicación puede reconocer tu actividad física."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"realizar fotografías y vídeos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Esta aplicación puede hacer fotografías y grabar vídeos con la cámara en cualquier momento."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar la vibración"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite que la aplicación modifique tu colección de fotos."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"leer las ubicaciones de tu colección de contenido multimedia"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que la aplicación lea las ubicaciones de tu colección de contenido multimedia."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> solicita tu autenticación."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biométrico no disponible"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Se ha detectado una huella digital parcial. Vuelve a intentarlo."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No se ha podido procesar la huella digital. Vuelve a intentarlo."</string>
@@ -645,14 +651,14 @@
     <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Controla el número de contraseñas incorrectas introducidas para desbloquear la pantalla y bloquea el tablet o borra todos los datos del usuario si se introducen demasiadas contraseñas incorrectas."</string>
     <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Controla el número de contraseñas incorrectas introducidas para desbloquear la pantalla y bloquea la TV o borra todos los datos del usuario si se introducen demasiadas contraseñas incorrectas."</string>
     <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Controla el número de contraseñas incorrectas introducidas para desbloquear la pantalla y bloquea el teléfono o borra todos los datos del usuario si se introducen demasiadas contraseñas incorrectas."</string>
-    <string name="policylab_resetPassword" msgid="4934707632423915395">"Cambiar el bloqueo de pantalla"</string>
-    <string name="policydesc_resetPassword" msgid="1278323891710619128">"Cambiar el bloqueo de pantalla"</string>
+    <string name="policylab_resetPassword" msgid="4934707632423915395">"Cambia el bloqueo de pantalla"</string>
+    <string name="policydesc_resetPassword" msgid="1278323891710619128">"Cambia el bloqueo de pantalla"</string>
     <string name="policylab_forceLock" msgid="2274085384704248431">"Bloquear la pantalla"</string>
-    <string name="policydesc_forceLock" msgid="1141797588403827138">"Controlar cómo y cuándo se bloquea la pantalla"</string>
+    <string name="policydesc_forceLock" msgid="1141797588403827138">"Controla cómo y cuándo se bloquea la pantalla"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Borrar todos los datos"</string>
     <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Borrar los datos del tablet sin avisar restableciendo el estado de fábrica"</string>
     <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Borra los datos de la TV sin advertencia previa restableciendo la TV a los valores predeterminados de fábrica."</string>
-    <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Borrar los datos del teléfono sin avisar restableciendo el estado de fábrica"</string>
+    <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Borra los datos del teléfono sin avisar restableciendo el estado de fábrica"</string>
     <string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Borrar datos del usuario"</string>
     <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Borra los datos del usuario en este tablet sin avisar."</string>
     <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Borra los datos del usuario en esta TV sin avisar."</string>
@@ -664,7 +670,7 @@
     <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Cifrado del almacenamiento"</string>
     <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Exige que se cifren los datos de la aplicación almacenados."</string>
     <string name="policylab_disableCamera" msgid="6395301023152297826">"Inhabilitar cámaras"</string>
-    <string name="policydesc_disableCamera" msgid="2306349042834754597">"Evitar el uso de las cámaras del dispositivo"</string>
+    <string name="policydesc_disableCamera" msgid="2306349042834754597">"Evita el uso de las cámaras del dispositivo"</string>
     <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Inhabilitar algunas funciones del bloqueo de pantalla"</string>
     <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Evitar el uso de algunas funciones del bloqueo de pantalla"</string>
   <string-array name="phoneTypes">
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> sugerencias de Autocompletar</item>
       <item quantity="one">1 sugerencia de Autocompletar</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"¿Guardar en &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"¿Guardar <xliff:g id="TYPE">%1$s</xliff:g> en &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"¿Guardar <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"¿Guardar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> y <xliff:g id="TYPE_2">%3$s</xliff:g> en &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"¿Quieres actualizar a &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"¿Quieres actualizar <xliff:g id="TYPE">%1$s</xliff:g> a &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"¿Quieres actualizar <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> a &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"¿Quieres actualizar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> y <xliff:g id="TYPE_2">%3$s</xliff:g> a &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"¿Guardar en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"¿Guardar <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"¿Guardar <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"¿Guardar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> y <xliff:g id="TYPE_2">%3$s</xliff:g> en "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"¿Actualizar en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"¿Actualizar <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"¿Actualizar <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"¿Actualizar estos elementos en "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> y <xliff:g id="TYPE_2">%3$s</xliff:g>)?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Guardar"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"No, gracias"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Actualizar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 650fcb6..b77b772 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"heli salvestamine"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; salvestada heli?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Tegevuste tuvastamine"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"tegevuste tuvastamine"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; teie füüsilised tegevused tuvastada?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kaamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"pildistamine ja video salvestamine"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; jäädvustada pilte ja salvestada videoid?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"See rakendus näeb võrguallikate (nt mobiilimastid ja WiFi-võrgud) abil teie asukohta. Need asukohateenused peavad olema sisse lülitatud ja teie tahvelarvutis saadaval, et rakendus neid kasutada saaks."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"See rakendus näeb võrguallikate (nt mobiilimastid ja WiFi-võrgud) abil teie asukohta. Need asukohateenused peavad olema sisse lülitatud ja teie teleris saadaval, et rakendus neid kasutada saaks."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"See rakendus näeb võrguallikate (nt mobiilimastid ja WiFi-võrgud) abil teie asukohta. Need asukohateenused peavad olema sisse lülitatud ja teie telefonis saadaval, et rakendus neid kasutada saaks."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"juurdepääs täpsele asukohale taustal"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"See rakendus hangib teie täpse asukoha alati, kui see töötab taustal. Need asukohateenused peavad olema sisse lülitatud ja teie telefonis saadaval, et rakendus saaks neid kasutada. See võib suurendada akutoite tarbimist."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"juurdepääs asukohale taustal"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Kui see on antud ka umbkaudsele või täpsele asukohale juurdepääsu puhul, saab rakendus taustal käitamisel juurdepääsu asukohale."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"muuda heliseadeid"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Võimaldab rakendusel muuta üldiseid heliseadeid, näiteks helitugevust ja seda, millist kõlarit kasutatakse väljundiks."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"salvesta heli"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"See rakendus saab mikrofoni kasutades mis tahes ajal heli salvestada."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM-kaardile käskluste saatmine"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Lubab rakendusel saata käske SIM-kaardile. See on väga ohtlik."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"füüsiliste tegevuste tuvastamine"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"See rakendus saab tuvastada teie füüsilised tegevused."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"piltide ja videote tegemine"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"See rakendus saab mis tahes ajal kaameraga pildistada ja videoid salvestada."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"juhtige vibreerimist"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Võimaldab rakendusel muuta teie fotokogu."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"Lugeda teie meediakogus olevaid asukohti"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Võimaldab rakendusel lugeda teie meediakogus olevaid asukohti."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Rakendus <xliff:g id="APP">%s</xliff:g> soovib autentimist."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biomeetriline riistvara ei ole saadaval"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Tuvastati osaline sõrmejälg. Proovige uuesti."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Sõrmejälge ei õnnestunud töödelda. Proovige uuesti."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> automaatse täitmise soovitust</item>
       <item quantity="one">Üks automaatse täitmise soovitus</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Kas salvestada teenusesse &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Kas salvestada üksus <xliff:g id="TYPE">%1$s</xliff:g> teenusesse &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Kas salvestada üksused <xliff:g id="TYPE_0">%1$s</xliff:g> ja <xliff:g id="TYPE_1">%2$s</xliff:g> teenusesse &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Kas salvestada üksused <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ja <xliff:g id="TYPE_2">%3$s</xliff:g> teenusesse &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Kas värskendada valikule &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Kas värskendada üksus <xliff:g id="TYPE">%1$s</xliff:g> valikule &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Kas värskendada üksused <xliff:g id="TYPE_0">%1$s</xliff:g> ja <xliff:g id="TYPE_1">%2$s</xliff:g> valikule &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Kas värskendada üksused <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ja <xliff:g id="TYPE_2">%3$s</xliff:g> valikule &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Kas salvestada teenuses "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Kas salvestada <xliff:g id="TYPE">%1$s</xliff:g> teenuses "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Kas salvestada <xliff:g id="TYPE_0">%1$s</xliff:g> ja <xliff:g id="TYPE_1">%2$s</xliff:g> teenuses "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Kas salvestada <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ja <xliff:g id="TYPE_2">%3$s</xliff:g> teenuses "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Kas värskendada üksust teenuses "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Kas värskendada üksust <xliff:g id="TYPE">%1$s</xliff:g> teenuses "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Kas värskendada üksusi <xliff:g id="TYPE_0">%1$s</xliff:g> ja <xliff:g id="TYPE_1">%2$s</xliff:g> teenuses "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Kas värskendada teenuses "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" neid üksusi: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ja <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Salvesta"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Tänan, ei"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Värskenda"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 6a6fdbf..26d8b31 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofonoa"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"grabatu audioa"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari audioa grabatzea baimendu nahi diozu?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Ariketa hautematea"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"hauteman ariketa"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Ariketa fisikoa hautematea baimendu nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"atera argazkiak eta grabatu bideoak"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari argazkiak ateratzea eta bideoak grabatzea baimendu nahi diozu?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Aplikazioak zure kokapenaren berri izan dezake sareen iturburuak (adibidez, telefonia mugikorreko dorreak eta Wi-Fi sareak) erabilita. Kokapen-zerbitzu horiek aktibatuta eta erabilgarri izan behar dituzu tabletan, aplikazioak erabil ditzan."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Aplikazioak zure kokapenaren berri izan dezake sareen iturburuak (adibidez, telefonia mugikorreko dorreak eta Wi-Fi sareak) erabilita. Kokapen-zerbitzu horiek aktibatuta eta erabilgarri izan behar dituzu telebistan, aplikazioak erabil ditzan."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Aplikazioak zure kokapenaren berri izan dezake sareen iturburuak (adibidez, telefonia mugikorreko dorreak eta Wi-Fi sareak) erabilita. Kokapen-zerbitzu horiek aktibatuta eta erabilgarri izan behar dituzu telefonoan, aplikazioak erabil ditzan."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"lortu kokapen zehatza atzeko planoan"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Aplikazioak zure kokapen zehatza lor dezake atzeko planoan funtzionatzen duenean. Kokapen-zerbitzu horiek aktibatuta eta erabilgarri izan behar dituzu telefonoan, aplikazioak erabil ditzan. Baliteke bateria gehiago erabiltzea."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"Atzitu kokapena atzeko planoan"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Baimen hau ematen bada kokapen zehatz edo gutxi gorabeherakorako sarbideaz gain, atzeko planoan exekutatu bitartean atzitu ahalko du aplikazioak kokapena."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"aldatu audio-ezarpenak"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Audio-ezarpen orokorrak aldatzeko baimena ematen dio; besteak beste, bolumena eta irteerarako zer bozgorailu erabiltzen den."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"grabatu audioa"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Aplikazioak edonoiz erabil dezake mikrofonoa audioa grabatzeko."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"bidali aginduak SIM txartelera"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"SIM txartelera aginduak bidaltzeko aukera ematen die aplikazioei. Oso arriskutsua da."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"hauteman ariketa fisikoa"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Aplikazioak ariketa fisikoa hauteman dezake."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"atera argazkiak eta grabatu bideoak"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Aplikazioak edonoiz erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrolatu dardara"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Argazki-bilduma aldatzea baimentzen die aplikazioei."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"multimedia-edukien bildumako kokapena irakurri"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Multimedia-edukien bildumako kokapena irakurtzea baimentzen die aplikazioei."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> aplikazioak autentifikatu egin nahi du."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biometrikoa ez dago erabilgarri"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hatz-marka digitala ez da osorik hauteman. Saiatu berriro."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ezin izan da prozesatu hatz-marka. Saiatu berriro."</string>
@@ -1898,14 +1904,14 @@
       <item quantity="other">Automatikoki betetzeko <xliff:g id="COUNT">%1$s</xliff:g> iradokizun</item>
       <item quantity="one">Automatikoki betetzeko 1 iradokizun</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; zerbitzuan gorde nahi duzu?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; zerbitzuan gorde nahi duzu?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> eta <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; zerbitzuan gorde nahi dituzu?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> eta <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; zerbitzuan gorde nahi dituzu?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; zerbitzuan eguneratu nahi duzu?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; zerbitzuan eguneratu nahi duzu?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> eta <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; zerbitzuan eguneratu nahi dituzu?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> eta <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; zerbitzuan eguneratu nahi dituzu?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" zerbitzuan gorde nahi duzu?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" zerbitzuan gorde nahi duzu <xliff:g id="TYPE">%1$s</xliff:g>?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" zerbitzuan gorde nahi dituzu <xliff:g id="TYPE_0">%1$s</xliff:g> eta <xliff:g id="TYPE_1">%2$s</xliff:g>?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" zerbitzuan gorde nahi dituzu <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> eta <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" zerbitzuan eguneratu nahi duzu?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" zerbitzuan eguneratu nahi duzu <xliff:g id="TYPE">%1$s</xliff:g>?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" zerbitzuan eguneratu nahi dituzu <xliff:g id="TYPE_0">%1$s</xliff:g> eta <xliff:g id="TYPE_1">%2$s</xliff:g>?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" zerbitzuan eguneratu nahi dituzu <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> eta <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Gorde"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Ez, eskerrik asko"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Eguneratu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 2310f9e..2d6c2ef 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"میکروفن"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ضبط صدا"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"‏به &lt;/b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود صدا ضبط کند؟"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"تشخیص فعالیت"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"تشخیص فعالیت"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید فعالیت فیزیکی‌تان را تشخیص دهد؟"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"دوربین"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"عکس گرفتن و فیلم‌برداری"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود عکس بگیرد و ویدیو ضبط کند؟"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"‏این برنامه می‌تواند براساس منابع شبکه مانند دکل‌های مخابراتی و شبکه‌های Wi-Fi، مکان شما را تشخیص دهد. این خدمات مکان باید روشن و در رایانه لوحی شما دردسترس باشند تا برنامه بتواند از آن‌ها استفاده کند."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"‏این برنامه می‌تواند براساس منابع شبکه مانند دکل‌های مخابراتی و شبکه‌های Wi-Fi، مکان شما را تشخیص دهد. این خدمات مکان باید روشن و در تلویزیون شما دردسترس باشند تا برنامه بتواند از آن‌ها استفاده کند."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"‏این برنامه می‌تواند براساس منابع شبکه مانند دکل‌های مخابراتی و شبکه‌های Wi-Fi، مکان شما را تشخیص دهد. این خدمات مکان باید روشن و در تلفن شما دردسترس باشند تا برنامه بتواند از آن‌ها استفاده کند."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"دسترسی به موقعیت مکانی دقیق در پس‌زمینه"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"هرزمان این برنامه در پس‌زمینه باشد می‌تواند موقعیت مکانی دقیق شما را دریافت کند. برای اینکه برنامه بتواند از خدمات مکان استفاده کند، این خدمات باید در تلفنتان روشن و دردسترس باشد. ممکن است با این کار مصرف باتری افزایش یابد."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"دسترسی به مکان در پس‌زمینه"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"اگر این مجوز نیز برای دسترسی دقیق یا تقریبی به مکان داده شود، برنامه می‌تواند درحین اجرا در پس‌زمینه به مکان دسترسی پیدا کند."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"تغییر تنظیمات صوتی"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"به برنامه امکان می‌دهد تنظیمات صوتی کلی مانند میزان صدا و بلندگوی مورد استفاده برای پخش صدا را تغییر دهد."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ضبط صدا"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"این برنامه می‌تواند در هرزمانی با استفاده از میکروفون صدا ضبط کند."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"ارسال فرمان به سیم کارت"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"به برنامه اجازه ارسال دستورات به سیم کارت را می‌دهد. این بسیار خطرناک است."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"تشخیص فعالیت فیزیکی"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"این برنامه نمی‌تواند فعالیت فیزیکی‌تان را تشخیص دهد."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"عکسبرداری و فیلمبرداری"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"این برنامه می‌تواند در هرزمانی با استفاده از دوربین عکس و فیلم بگیرد."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"کنترل لرزش"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"به برنامه اجازه می‌دهد مجموعه عکستان را تغییر دهد."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"خواندن مکان‌ها از مجموعه رسانه شما"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"به برنامه اجازه می‌دهد مکان‌ها را از مجموعه رسانه‌تان بخواند."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"برنامه <xliff:g id="APP">%s</xliff:g> می‌خواهد احراز هویت کند."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"سخت‌افزار زیست‌سنجی دردسترس نیست"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"بخشی از اثر انگشت شناسایی شد. لطفاً دوباره امتحان کنید."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"اثرانگشت پردازش نشد. لطفاً دوباره امتحان کنید."</string>
@@ -1192,7 +1198,7 @@
     <string name="volume_music" msgid="5421651157138628171">"میزان صدای رسانه"</string>
     <string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"پخش از طریق بلوتوث"</string>
     <string name="volume_music_hint_silent_ringtone_selected" msgid="8310739960973156272">"آهنگ زنگ روی بی‌صدا تنظیم شد"</string>
-    <string name="volume_call" msgid="3941680041282788711">"میزان صدای هنگام تماس"</string>
+    <string name="volume_call" msgid="3941680041282788711">"صدا حینِ تماس"</string>
     <string name="volume_bluetooth_call" msgid="2002891926351151534">"میزان صدای تماس بلوتوث"</string>
     <string name="volume_alarm" msgid="1985191616042689100">"میزان صدای هشدار"</string>
     <string name="volume_notification" msgid="2422265656744276715">"میزان صدای اعلان"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> پیشنهاد تکمیل خودکار</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> پیشنهاد تکمیل خودکار</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"‏در &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ذخیره شود؟"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"‏<xliff:g id="TYPE">%1$s</xliff:g> در &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ذخیره شود؟"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"‏<xliff:g id="TYPE_0">%1$s</xliff:g> و <xliff:g id="TYPE_1">%2$s</xliff:g> در &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ذخیره شوند؟"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"‏<xliff:g id="TYPE_0">%1$s</xliff:g>، <xliff:g id="TYPE_1">%2$s</xliff:g> و <xliff:g id="TYPE_2">%3$s</xliff:g> در &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ذخیره شوند؟"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"‏به &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; به‌روزرسانی شود؟"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"‏<xliff:g id="TYPE">%1$s</xliff:g> به &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; به‌روزرسانی شود؟"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"‏<xliff:g id="TYPE_0">%1$s</xliff:g> و <xliff:g id="TYPE_1">%2$s</xliff:g> به &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; به‌روزرسانی شود؟"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"‏<xliff:g id="TYPE_0">%1$s</xliff:g>، <xliff:g id="TYPE_1">%2$s</xliff:g>، و <xliff:g id="TYPE_2">%3$s</xliff:g> به &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; به‌روزرسانی شود؟"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"در "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ذخیره شود؟"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> در "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ذخیره شود؟"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> و <xliff:g id="TYPE_1">%2$s</xliff:g> در "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ذخیره شود؟"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>، <xliff:g id="TYPE_1">%2$s</xliff:g> و <xliff:g id="TYPE_2">%3$s</xliff:g> در "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ذخیره شود؟"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"در "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" به‌روزرسانی شود؟"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g> در "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" به‌روزرسانی شود؟"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> و <xliff:g id="TYPE_1">%2$s</xliff:g> در "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" به‌روزرسانی شود؟"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"این موارد در "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>، <xliff:g id="TYPE_1">%2$s</xliff:g> و <xliff:g id="TYPE_2">%3$s</xliff:g> به‌روزرسانی شود؟"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"ذخیره"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"نه سپاسگزارم"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"به‌روزرسانی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 4f2531c..39c4912 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofoni"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"tallentaa ääntä"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nauhoittaa ääntä?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Liikunnan tunnistaminen"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"tunnistaa liikunnan"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tunnistaa liikkumisesi?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ottaa kuvia ja videoita"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ottaa kuvia ja nauhoittaa videoita?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Tämä sovellus voi määrittää sijaintisi matkapuhelinverkon tukiasemien, Wi-Fi-verkkojen ja muiden verkkolähteiden perusteella. Näiden sijaintipalveluiden tulee olla käytössä ja käytettävissä tabletillasi, jotta sovellus voi käyttää niitä."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Tämä sovellus voi määrittää sijaintisi matkapuhelinverkon tukiasemien, Wi-Fi-verkkojen ja muiden verkkolähteiden perusteella. Näiden sijaintipalveluiden tulee olla käytössä ja käytettävissä TV:lläsi, jotta sovellus voi käyttää niitä."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Tämä sovellus voi määrittää sijaintisi matkapuhelinverkon tukiasemien, Wi-Fi-verkkojen ja muiden verkkolähteiden perusteella. Näiden sijaintipalveluiden tulee olla käytössä ja käytettävissä puhelimellasi, jotta sovellus voi käyttää niitä."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"käyttää tarkkaa sijaintia taustalla"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Tämä sovellus saa tarkat sijaintitietosi käyttöönsä taustalla. Näiden sijaintipalveluiden tulee olla käytössä ja käytettävissä puhelimellasi, jotta sovellus voi käyttää niitä. Tämä voi lisätä akun kulutusta."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"käytä sijaintia taustalla"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Jos tämä myönnetään karkean tai tarkan sijainnin käyttöoikeuden lisäksi, sovellus voi käyttää sijaintia toimiessaan taustalla."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"muuta ääniasetuksia"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Antaa sovelluksen muokata yleisiä ääniasetuksia, kuten äänenvoimakkuutta ja käytettävää kaiutinta."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"tallentaa ääntä"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Tämä sovellus voi tallentaa mikrofonilla ääntä koska tahansa."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"lähettää komentoja SIM-kortille"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Antaa sovelluksen lähettää komentoja SIM-kortille. Tämä ei ole turvallista."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"tunnistaa liikkumisen"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Sovellus voi tunnistaa liikkumisesi."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ota kuvia ja videoita"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Tämä sovellus voi ottaa kameralla kuvia ja videoita koska tahansa."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"hallitse värinää"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Antaa sovelluksen muokata kuvakokoelmaasi."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"lukea mediakokoelmasi sijainteja"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Antaa sovelluksen lukea mediakokoelmasi sijainteja."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> pyytää todentamista"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrinen laitteisto ei käytettävissä"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Sormenjälki havaittiin vain osittain. Yritä uudelleen."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Sormenjäljen käsittely epäonnistui. Yritä uudelleen."</string>
@@ -636,9 +642,9 @@
     <string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"Antaa sovelluksen luoda sidoksen operaattorin palveluun. Ei tavallisten sovelluksien käyttöön."</string>
     <string name="permlab_access_notification_policy" msgid="4247510821662059671">"Älä häiritse -tilan käyttöoikeus"</string>
     <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Sallii sovelluksen lukea ja muokata Älä häiritse -tilan asetuksia."</string>
-    <string name="policylab_limitPassword" msgid="4497420728857585791">"Aseta salasanasäännöt"</string>
-    <string name="policydesc_limitPassword" msgid="2502021457917874968">"Hallinnoi ruudun ruudun lukituksen salasanoissa ja PIN-koodeissa sallittuja merkkejä ja niiden pituutta."</string>
-    <string name="policylab_watchLogin" msgid="5091404125971980158">"Tarkkaile näytön avaamisyrityksiä"</string>
+    <string name="policylab_limitPassword" msgid="4497420728857585791">"Asentaa salasanasäännöt"</string>
+    <string name="policydesc_limitPassword" msgid="2502021457917874968">"Hallinnoida ruudun lukituksen salasanoissa ja PIN-koodeissa sallittuja merkkejä ja niiden pituutta."</string>
+    <string name="policylab_watchLogin" msgid="5091404125971980158">"Tarkkailla näytön avaamisyrityksiä"</string>
     <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Valvoo väärien salasanojen lukumäärää näytön lukituksen poistossa sekä lukitsee tablet-laitteen tai poistaa sen tiedot, jos salasana syötetään väärin liian monta kertaa."</string>
     <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Valvo väärien salasanojen määrää poistettaessa näytön lukitusta ja lukitse televisio tai poista television kaikki tiedot, jos salasana kirjoitetaan väärin liian monta kertaa."</string>
     <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Valvoo väärien salasanojen lukumäärää näytön lukituksen poistossa ja lukitsee puhelimen tai poistaa sen kaikki tiedot, jos väärä salasana syötetään liian monta kertaa."</string>
@@ -659,14 +665,14 @@
     <string name="policydesc_wipeData_secondaryUser" product="default" msgid="6787904546711590238">"Pyyhi tämän käyttäjän tiedot puhelimesta ilman varoitusta."</string>
     <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Aseta laitteen yleinen välityspalvelin"</string>
     <string name="policydesc_setGlobalProxy" msgid="8459859731153370499">"Aseta laitteen yleinen välityspalvelin käyttöön, kun käytäntö on käytössä. Vain laitteen omistaja voi asettaa yleisen välityspalvelimen."</string>
-    <string name="policylab_expirePassword" msgid="5610055012328825874">"Määritä ruudun lukituksen salasanan viimeinen voimassaolopäivä"</string>
-    <string name="policydesc_expirePassword" msgid="5367525762204416046">"Muuta sitä, miten usein ruudun lukituksen salasana, PIN-koodi tai kuvio tulee vaihtaa."</string>
-    <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Aseta tallennustilan salaus"</string>
+    <string name="policylab_expirePassword" msgid="5610055012328825874">"Määrittää ruudun lukituksen salasanan viimeinen voimassaolopäivä"</string>
+    <string name="policydesc_expirePassword" msgid="5367525762204416046">"Muuttaa sitä, miten usein ruudun lukituksen salasana, PIN-koodi tai kuvio tulee vaihtaa."</string>
+    <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Asettaa tallennustilan salaus"</string>
     <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Pakota tallennettujen sovellustietojen salaus."</string>
-    <string name="policylab_disableCamera" msgid="6395301023152297826">"Poista kamerat käytöstä"</string>
-    <string name="policydesc_disableCamera" msgid="2306349042834754597">"Estä laitteen kaikkien kameroiden käyttö."</string>
+    <string name="policylab_disableCamera" msgid="6395301023152297826">"Poistaa kamerat käytöstä"</string>
+    <string name="policydesc_disableCamera" msgid="2306349042834754597">"Estää laitteen kaikkien kameroiden käytön."</string>
     <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Estää lukitun näytön toimintoja"</string>
-    <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Estä joidenkin lukitun näytön toimintojen käyttö."</string>
+    <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Estää joidenkin lukitun näytön toimintojen käytön."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Koti"</item>
     <item msgid="869923650527136615">"Mobiili"</item>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> automaattisen täytön ehdotusta</item>
       <item quantity="one">Yksi automaattisen täytön ehdotus</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Tallennetaanko tämä palveluun &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Tallennetaanko <xliff:g id="TYPE">%1$s</xliff:g> palveluun &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Tallennetaanko <xliff:g id="TYPE_0">%1$s</xliff:g> ja <xliff:g id="TYPE_1">%2$s</xliff:g> palveluun &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Tallennetaanko <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ja <xliff:g id="TYPE_2">%3$s</xliff:g> palveluun &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Muutetaanko tämä palvelussa &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Muutetaanko <xliff:g id="TYPE">%1$s</xliff:g>, jonka &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; on tallentanut?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Muutetaanko <xliff:g id="TYPE_0">%1$s</xliff:g> ja <xliff:g id="TYPE_1">%2$s</xliff:g>, jotka &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; on tallentanut?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Muutetaanko <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ja <xliff:g id="TYPE_2">%3$s</xliff:g>, jotka &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; on tallentanut?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Saako "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" tallentaa tämän?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Tallennetaanko <xliff:g id="TYPE">%1$s</xliff:g> ("<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>")?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Tallennetaanko <xliff:g id="TYPE_0">%1$s</xliff:g> ja <xliff:g id="TYPE_1">%2$s</xliff:g> ("<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>")?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Tallennetaanko <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ja <xliff:g id="TYPE_2">%3$s</xliff:g> ("<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>")?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Päivitetäänkö "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Päivitetäänkö <xliff:g id="TYPE">%1$s</xliff:g> ("<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>")?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Päivitetäänkö <xliff:g id="TYPE_0">%1$s</xliff:g> ja <xliff:g id="TYPE_1">%2$s</xliff:g> ("<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>")?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Päivitetäänkö nämä ("<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"): <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ja <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Tallenna"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Ei kiitos"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Muuta"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 39889ec..a03f25f 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microphone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"enregistrer des fichiers audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&amp;gt à enregistrer l\'audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Reconnaissance des activités"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"reconnaître les activités"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à reconnaître vos activités physiques?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Appareil photo"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"prendre des photos et filmer des vidéos"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à prendre des photos et à filmer des vidéos?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Cette application peut déterminer votre position à l\'aide de différentes sources de localisation sur le réseau, comme les tours de téléphonie cellulaire et les réseaux Wi-Fi. Ces services de localisation doivent être activés et accessibles sur votre tablette pour que l\'application puisse les utiliser."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Cette application peut déterminer votre position à l\'aide de différentes sources de localisation sur le réseau, comme les tours de téléphonie cellulaire et les réseaux Wi-Fi. Ces services de localisation doivent être activés et accessibles sur votre téléviseur pour que l\'application puisse les utiliser."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Cette application peut déterminer votre position à l\'aide de différentes sources de localisation sur le réseau, comme les tours de téléphonie cellulaire et les réseaux Wi-Fi. Ces services de localisation doivent être activés et accessibles sur votre téléphone pour que l\'application puisse les utiliser."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"accéder à votre position précise en arrière-plan"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Cette application peut obtenir votre position exacte à tout moment en arrière-plan. Ces services de localisation doivent être activés et accessibles sur votre téléviseur pour que l\'application puisse les utiliser. Cela peut entraîner une utilisation accrue de la pile."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"accès à la localisation en arrière-plan"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Si cette autorisation est accordée en plus de l\'accès approximatif ou précis à la localisation, alors l\'application peut accéder à la localisation lorsqu\'elle fonctionne en arrière-plan."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifier vos paramètres audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permet à l\'application de modifier les paramètres audio généraux, tels que le volume et la sortie audio utilisée."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"enregistrer des fichiers audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Cette application peut enregistrer de l\'audio à l\'aide du microphone en tout temps."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"envoyer des commandes à la carte SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permet à l\'application d\'envoyer des commandes à la carte SIM. Cette fonctionnalité est très dangereuse."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"reconnaître les activités physiques"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Cette application peut reconnaître vos activités physiques."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"prendre des photos et filmer des vidéos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Cette application peut prendre des photos et enregistrer des vidéos à l\'aide de l\'appareil photo en tout temps."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"gérer le vibreur"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Autorise l\'application à modifier votre collection de photos."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"lire les positions issues de votre collection multimédia"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Autorise l\'application à lire les positions indiquées dans votre collection multimédia."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> vous demande de vous authentifier."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Matériel biométrique indisponible"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte digitale partielle détectée. Veuillez essayer de nouveau."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de traiter les empreintes digitales. Veuillez essayer de nouveau."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> suggestion de remplissage automatique</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggestions de remplissage automatique</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Enregistrer dans &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Enregistrer <xliff:g id="TYPE">%1$s</xliff:g> dans &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Enregistrer <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> dans &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Enregistrer <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> et <xliff:g id="TYPE_2">%3$s</xliff:g> dans &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Mettre à jour vers &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Mettre à jour <xliff:g id="TYPE">%1$s</xliff:g> vers &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Mettre à jour <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> vers &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Mettre à jour <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> et <xliff:g id="TYPE_2">%3$s</xliff:g> vers &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Enregistrer sous "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Enregistrer <xliff:g id="TYPE">%1$s</xliff:g> sous "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Enregistrer <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> sous "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Enregistrer <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> et <xliff:g id="TYPE_2">%3$s</xliff:g> sous "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Mettre à jour sous "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Mettre à jour <xliff:g id="TYPE">%1$s</xliff:g> sous "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Mettre à jour <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> sous "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Mettre à jour ces éléments sous "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" : <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> et <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Enregistrer"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Non, merci"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Mettre à jour"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 9759415..5e9d5c1 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -138,7 +138,7 @@
     <string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"VoWiFi <xliff:g id="SPN">%s</xliff:g>"</string>
     <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Désactivé"</string>
     <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi de préférence"</string>
-    <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Priorité au mobile"</string>
+    <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Données mobiles de préférence"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Wi-Fi uniquement"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microphone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"enregistrer des fichiers audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Permettre à &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; d\'enregistrer des contenus audio ?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Reconnaissance de l\'activité"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"reconnaître l\'activité"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à reconnaître votre activité physique ?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Appareil photo"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"prendre des photos et enregistrer des vidéos"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Permettre à &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; de prendre des photos et de filmer des vidéos ?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Cette application peut obtenir votre position via des sources de réseau telles que les antennes-relais et les réseaux Wi-Fi. Ces services de localisation doivent être activés et disponibles sur votre tablette pour que l\'application puisse les utiliser."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Cette application peut obtenir votre position via des sources de réseau telles que les antennes-relais et les réseaux Wi-Fi. Ces services de localisation doivent être activés et disponibles sur votre téléviseur pour que l\'application puisse les utiliser."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Cette application peut obtenir votre position via des sources de réseau telles que les antennes-relais et les réseaux Wi-Fi. Ces services de localisation doivent être activés et disponibles sur votre téléphone pour que l\'application puisse les utiliser."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"accéder à la position exacte en arrière-plan"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Cette application peut obtenir votre position exacte à tout moment lorsqu\'elle s\'exécute en arrière-plan. Ces services de localisation doivent être activés et disponibles sur votre téléphone pour que l\'application puisse les utiliser. Ceci peut réduire l\'autonomie de la batterie."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"accéder à la position en arrière-plan"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Si vous lui accordez cette autorisation en plus de l\'accès à la position approximative ou précise, l\'application peut accéder à votre position lorsqu\'elle est s\'exécute en arrière-plan."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifier vos paramètres audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permet à l\'application de modifier les paramètres audio généraux, tels que le volume et la sortie audio utilisée."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"enregistrer des fichiers audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Cette application peut utiliser le micro pour enregistrer du contenu audio à tout moment."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"envoyer des commandes à la carte SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Autoriser l\'envoi de commandes à la carte SIM via l\'application. Cette fonctionnalité est très risquée."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"reconnaître l\'activité physique"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Cette application peut reconnaître votre activité physique."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"prendre des photos et enregistrer des vidéos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Cette application peut utiliser l\'appareil photo pour prendre des photos et enregistrer des vidéos à tout moment."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"contrôler le vibreur"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Autorise l\'application à modifier votre bibliothèque photo."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"consulter des positions issues de votre bibliothèque multimédia"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Autorise l\'application à consulter des positions issues de votre bibliothèque multimédia."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"L\'application <xliff:g id="APP">%s</xliff:g> veut vous authentifier."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Matériel biométrique indisponible"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte numérique partiellement détectée. Veuillez réessayer."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de reconnaître l\'empreinte numérique. Veuillez réessayer."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> suggestion de saisie automatique</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggestions de saisie automatique</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Enregistrer dans &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Enregistrer <xliff:g id="TYPE">%1$s</xliff:g> dans &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Enregistrer <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> dans &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Enregistrer <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> et <xliff:g id="TYPE_2">%3$s</xliff:g> dans &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Faire passer à &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Faire passer <xliff:g id="TYPE">%1$s</xliff:g> à &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Faire passer <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> à &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Faire passer <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> et <xliff:g id="TYPE_2">%3$s</xliff:g> à &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Enregistrer dans "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Enregistrer <xliff:g id="TYPE">%1$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Enregistrer <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Enregistrer <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> et <xliff:g id="TYPE_2">%3$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Mettre à jour cet élément dans "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Mettre à jour <xliff:g id="TYPE">%1$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Mettre à jour <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Mettre à jour les éléments <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> et <xliff:g id="TYPE_2">%3$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Enregistrer"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Non, merci"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Mettre à jour"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 4d78073..d522b53 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Micrófono"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"gravar audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Recoñecemento da actividade"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"recoñecer actividade"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; recoñeza a túa actividade física?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Cámara"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"tirar fotos e gravar vídeos"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; realice fotos e grave vídeos?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Esta aplicación pode obter a túa localización a partir de fontes de rede como torres de telecomunicacións e redes wifi. Para que a aplicación poida utilizar os servizos de localización, deben estar activados e dispoñibles na túa tableta."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Esta aplicación pode obter a túa localización a partir de fontes de rede como torres de telecomunicacións e redes wifi. Para que a aplicación poida utilizar os servizos de localización, deben estar activados e dispoñibles na túa televisión."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Esta aplicación pode obter a túa localización a partir de fontes de rede como torres de telecomunicacións e redes wifi. Para que a aplicación poida utilizar os servizos de localización, deben estar activados e dispoñibles no teu teléfono."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"acceder á localización exacta en segundo plano"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Esta aplicación pode obter a túa localización exacta cando estea en segundo plano. É necesario activar estes servizos de localización e deben estar dispoñibles no teléfono para que a aplicación poida utilizalos. Ademais, poden supoñer un aumento do consumo de batería."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"acceder á localización en segundo plano"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Se a aplicación ten acceso á localización aproximada ou exacta e lle concedes este permiso, poderá consultar onde te atopas cando estea en segundo plano."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"cambiar a configuración de son"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite á aplicación modificar a configuración de audio global, como o volume e que altofalante se utiliza para a saída."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"gravar audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Esta aplicación pode utilizar o micrófono en calquera momento para gravar audio."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos á SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite á aplicación enviar comandos á SIM. Isto é moi perigoso."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"recoñecer actividade física"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Esta aplicación pode recoñecer a túa actividade física."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"facer fotos e vídeos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Esta aplicación pode utilizar a cámara en calquera momento para sacar fotos e gravar vídeos."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar a vibración"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite que a aplicación modifique a túa colección de fotos."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"ler localizacións da túa colección multimedia"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que a aplicación lea as localizacións da túa colección multimedia."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"A aplicación <xliff:g id="APP">%s</xliff:g> quere que te autentiques."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"O hardware biométrico non está dispoñible"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Detectouse unha impresión dixital parcial. Téntao de novo."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Non se puido procesar a impresión dixital. Téntao de novo."</string>
@@ -1898,14 +1904,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suxestións de autocompletar</item>
       <item quantity="one">Unha suxestión de autocompletar</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Queres gardar o contido en: &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Queres gardar <xliff:g id="TYPE">%1$s</xliff:g> en: &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Queres gardar estes datos (<xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g>) en &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Queres gardar estes datos (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>) en &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Queres actualizar &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; con estes datos?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Queres actualizar &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; con estes datos (<xliff:g id="TYPE">%1$s</xliff:g>)?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Queres actualizar &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; con estes datos (<xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g>)?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Queres actualizar &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; con estes datos (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>)?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Queres gardar o contido en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Queres gardar <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Queres gardar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Queres gardar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> en "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Queres actualizar o contido en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Queres actualizar <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Queres actualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Queres actualizar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> en "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Gardar"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Non, grazas"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Actualizar"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 13090c6..9e9c21e 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"માઇક્રોફોન"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ઑડિઓ રેકોર્ડ કરવાની"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને ઑડિઓ રેકૉર્ડ કરવાની મંજૂરી આપીએ?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"પ્રવૃત્તિની ઓળખ"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"પ્રવૃત્તિને ઓળખો"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને તમારી શારીરિક પ્રવૃત્તિને ઓળખવાની મંજૂરી આપવી છે?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"કૅમેરો"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ચિત્રો લેવાની અને વીડિઓ રેકોર્ડ કરવાની"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને ચિત્રો લેવાની અને વીડિઓ રેકૉર્ડ કરવાની મંજૂરી આપીએ?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"આ ઍપ્લિકેશન, સેલ ટાવર્સ અને વાઇ-ફાઇ નેટવર્ક્સ જેવા નેટવર્ક સ્રોતોના આધારે તમારું સ્થાન મેળવી શકે છે. ઍપ્લિકેશન દ્વારા આ સ્થાન સેવાઓનો ઉપયોગ કરવામાં સમર્થ થવા માટે તમારા ટેબ્લેટ પર આ ઉપલબ્ધ અને ચાલુ હોવી આવશ્યક છે."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"આ ઍપ્લિકેશન, સેલ ટાવર્સ અને વાઇ-ફાઇ નેટવર્ક્સ જેવા નેટવર્ક સ્રોતોના આધારે તમારું સ્થાન મેળવી શકે છે. ઍપ્લિકેશન દ્વારા આ સ્થાન સેવાઓનો ઉપયોગ કરવામાં સમર્થ થવા માટે તમારા ટીવી પર આ ઉપલબ્ધ અને ચાલુ હોવી આવશ્યક છે."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"આ ઍપ્લિકેશન, સેલ ટાવર્સ અને વાઇ-ફાઇ નેટવર્ક્સ જેવા નેટવર્ક સ્રોતોના આધારે તમારું સ્થાન મેળવી શકે છે. ઍપ્લિકેશન દ્વારા આ સ્થાન સેવાઓનો ઉપયોગ કરવામાં સમર્થ થવા માટે તમારા ફોન પર આ ઉપલબ્ધ અને ચાલુ હોવી આવશ્યક છે."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"બૅકગ્રાઉન્ડમાં ચોક્કસ સ્થાન ઍક્સેસ કરો"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"આ ઍપ બૅકગ્રાઉન્ડમાં હોય ત્યારે કોઈપણ સમયે તમારું ચોક્કસ સ્થાન મેળવી શકે છે. ઍપ આ સ્થાન સેવાઓનો ઉપયોગ કરી શકે તે માટે તમારા ફોન પર આ સેવાઓ ઉપલબ્ધ અને ચાલુ હોવી આવશ્યક છે. આ બૅટરી વપરાશ વધારી શકે છે."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"બૅકગ્રાઉન્ડમાં સ્થાન ઍક્સેસ કરો"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"જો અંદાજિત અથવા ચોક્કસ સ્થાનના ઍક્સેસ ઉપરાંત આને પરવાનગી આપવામાં આવી હશે, તો ઍપ બૅકગ્રાઉન્ડમાં ચાલતી હોય ત્યારે સ્થાનને ઍક્સેસ કરી શકશે."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"તમારી ઑડિઓ સેટિંગ્સ બદલો"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"એપ્લિકેશનને વૈશ્વિક ઑડિઓ સેટિંગ્સને સંશોધિત કરવાની મંજૂરી આપે છે, જેમ કે વૉલ્યૂમ અને આઉટપુટ માટે કયા સ્પીકરનો ઉપયોગ કરવો."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ઑડિઓ રેકોર્ડ કરવાની"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"આ ઍપ્લિકેશન, માઇક્રોફોનનો ઉપયોગ કરીને કોઈપણ સમયે ઑડિઓ રેકોર્ડ કરી શકે છે."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"સિમ ને આદેશો મોકલો"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"એપ્લિકેશનને સિમ પરા આદેશો મોકલવાની મંજૂરી આપે છે. આ ખૂબ જ ખતરનાક છે."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"શારીરિક પ્રવૃત્તિને ઓળખો"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"આ ઍપ તમારી શારીરિક પ્રવૃત્તિને ઓળખી શકે છે."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ચિત્રો અને વિડિઓઝ લો"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"આ ઍપ્લિકેશન, કૅમેરાનો ઉપયોગ કરીને કોઈપણ સમયે ચિત્રો લઈ અને વિડિઓઝ રેકોર્ડ કરી શકે છે."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"વાઇબ્રેશન નિયંત્રિત કરો"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"એપને તમારો ફોટો સંગ્રહ સંશોધિત કરવાની મંજૂરી આપે છે."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"આપના મીડિયા સંગ્રહમાંથી સ્થાનો વાંચવા"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"એપને તમારા મીડિયા સંગ્રહમાંથી સ્થાનો વાંચવાની મંજૂરી આપે છે."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> ઍપ પ્રમાણીકરણ કરવા માગે છે."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"બાયોમેટ્રિક હાર્ડવેર ઉપલબ્ધ નથી"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"આંશિક ફિંગરપ્રિન્ટ મળી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ફિંગરપ્રિન્ટ પ્રક્રિયા કરી શકાઈ નથી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
@@ -1898,14 +1904,14 @@
       <item quantity="one">સ્વતઃભરણ વિશે <xliff:g id="COUNT">%1$s</xliff:g> સૂચન</item>
       <item quantity="other">સ્વતઃભરણ વિશે <xliff:g id="COUNT">%1$s</xliff:g> સૂચન</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;માં સાચવીએ?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g>ને &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;માં સાચવીએ?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> અને <xliff:g id="TYPE_1">%2$s</xliff:g>ને &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;માં સાચવીએ?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> અને <xliff:g id="TYPE_2">%3$s</xliff:g>ને &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;માં સાચવીએ?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; પર અપડેટ કરવું છે?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g>ને &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; પર અપડેટ કરવું છે?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> અને <xliff:g id="TYPE_1">%2$s</xliff:g> ને &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; પર અપડેટ કરવું છે?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> અને <xliff:g id="TYPE_2">%3$s</xliff:g>ને &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;પર અપડેટ કરવું છે?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" પર સાચવીએ?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g>ને "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" પર સાચવીએ?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> અને <xliff:g id="TYPE_1">%2$s</xliff:g>ને "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" પર સાચવીએ?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> અને <xliff:g id="TYPE_2">%3$s</xliff:g>ને "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" પર સાચવીએ?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"માં અપડેટ કરીએ?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g>ને "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"માં અપડેટ કરીએ?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> અને <xliff:g id="TYPE_1">%2$s</xliff:g>ને "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"માં અપડેટ કરીએ?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"માંની: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> અને <xliff:g id="TYPE_2">%3$s</xliff:g> બાબતોને અપડેટ કરીએ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"સાચવો"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"ના, આભાર"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"અપડેટ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 05ec806..87b35c9 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"माइक्रोफ़ोन"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ऑडियो रिकॉर्ड करें"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को ऑडियो रिकॉर्ड करने की अनुमति देना चाहते हैं?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"गतिविधि की पहचान"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"गतिविधि को पहचानें"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपने शरीर की गतिविधि को पहचानने की मंज़ूरी देना चाहते हैं?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"कैमरा"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"चित्र लेने और वीडियो रिकॉर्ड करने"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को फ़ोटो खींचने और वीडियो रिकॉर्ड करने की अनुमति देना चाहते हैं?"</string>
@@ -300,7 +303,7 @@
     <string name="permgrouprequest_calllog" msgid="8487355309583773267">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपने काॅल लाॅग एक्सेस करने की मंज़ूरी देना चाहते हैं?"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"फ़ोन"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"फ़ोन कॉल करें और प्रबंधित करें"</string>
-    <string name="permgrouprequest_phone" msgid="9166979577750581037">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को फ़ोन कॉल करने और उन्हें प्रबंधित करने की अनुमति देना चाहते हैं?"</string>
+    <string name="permgrouprequest_phone" msgid="9166979577750581037">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को फ़ोन कॉल करने और उन्हें प्रबंधित करने की अनुमति दें?"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"शरीर संवेदक"</string>
     <string name="permgroupdesc_sensors" msgid="7147968539346634043">"अपने महत्वपूर्ण संकेतों के बारे में सेंसर डेटा को ऐक्सेस करें"</string>
     <string name="permgrouprequest_sensors" msgid="6349806962814556786">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपने स्वास्थ्य से जुड़ी ज़रूरी जानकारी इस्तेमाल करने की अनुमति देना चाहते हैं?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"यह ऐप सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क के स्रोतों के आधार पर आपकी जगह का पता कर सकता है. ये जगह की जानकारी आपके टैबलेट पर चालू और मौजूद होनी चाहिए ताकि ऐप उनका इस्तेमाल कर सके."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"यह ऐप सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क के स्रोतों के आधार पर आपकी जगह का पता कर सकता है. ये जगह की जानकारी आपके टीवी पर चालू और मौजूद होनी चाहिए ताकि ऐप उनका इस्तेमाल कर सके."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"यह ऐप सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क के स्रोतों के आधार पर आपकी जगह का पता कर सकता है. ये जगह की जानकारी आपके फ़ोन पर चालू और मौजूद होनी चाहिए ताकि ऐप उनका इस्तेमाल कर सके."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"बैकग्राउंड में होने पर \'जगह की सटीक जानकारी\' एक्सेस करें"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"यह ऐप्लिकेशन बैकग्राउंड में चलते हुए, किसी भी समय आपकी \'जगह की सटीक जानकारी\' का इस्तेमाल कर सकता है. यह ज़रूरी है कि \'जगह की जानकारी\' वाली ये सेवाएं आपके फ़ोन में मौजूद हों और चालू की गई हों ताकि ऐप्लिकेशन उनका इस्तेमाल कर पाए. ऐसा करने से ज़्यादा बैटरी खर्च हो सकती है."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"बैकग्राउंड में जगह की जानकारी एक्सेस करना"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"अनुमानित या बिल्कुल सही जगह की जानकारी का एक्सेस करने की अनुमति अलग से दिए जाने पर, बैकग्राउंड में चलने के दौरान ऐप्लिकेशन आपकी जगह की जानकारी एक्सेस कर सकता है."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"अपनी ऑडियो सेटिंग बदलें"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ऐप्स  को वैश्विक ऑडियो सेटिंग, जैसे वॉल्‍यूम और कौन-सा स्पीकर आउटपुट के लिए उपयोग किया गया, संशोधित करने देता है."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ऑडियो रिकॉर्ड करने"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"यह ऐप्लिकेशन किसी भी समय माइक्रोफ़ोन का उपयोग करके ऑडियो रिकॉर्ड कर सकता है."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"सिम पर निर्देश भेजें"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"ऐप को सिम पर निर्देश भेजने देती है. यह बहुत ही खतरनाक है."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"शरीर की गतिविधि को पहचानना"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"यह ऐप्लिकेशन आपके शरीर की गतिविधि को पहचान सकता है."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"चित्र और वीडियो लें"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"यह ऐप्लिकेशन किसी भी समय कैमरे का उपयोग करके चित्र ले सकता है और वीडियो रिकॉर्ड कर सकता है."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"कंपन (वाइब्रेशन) को नियंत्रित करें"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"इससे ऐप्लिकेशन को आपके फ़ोटो संग्रह में बदलाव करने की मंज़ूरी दी जाती है."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"अपने मीडिया संग्रह से जगह की जानकारी एक्सेस करने की अनुमति दें"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"इससे ऐप्लिकेशन को आपके मीडिया संग्रह से जगह की जानकारी एक्सेस करने की अनुमति दी जाती है."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"ऐप्लिकेशन <xliff:g id="APP">%s</xliff:g> पुष्टि करना चाहता है."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"बायोमेट्रिक हार्डवेयर उपलब्ध नहीं है"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फ़िंगरप्रिंट की पहचान की गई. कृपया पुनः प्रयास करें."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फ़िंगरप्रिंट संसाधित नहीं हो सका. कृपया पुन: प्रयास करें."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="one">ऑटोमैटिक भरने के <xliff:g id="COUNT">%1$s</xliff:g> सुझाव</item>
       <item quantity="other">ऑटोमैटिक भरने के <xliff:g id="COUNT">%1$s</xliff:g> सुझाव</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; में सेव करें?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> को &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; में सेव करें?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> और <xliff:g id="TYPE_1">%2$s</xliff:g> को &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; में सेव करें?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> और <xliff:g id="TYPE_2">%3$s</xliff:g> को &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; में सेव करें?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; में अपडेट करें?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> को &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; में अपडेट करें?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> और <xliff:g id="TYPE_1">%2$s</xliff:g> को &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; में अपडेट करें?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> और <xliff:g id="TYPE_2">%3$s</xliff:g> को &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; में अपडेट करें?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"क्या आप "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" में सेव करना चाहते हैं?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"क्या आप "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" में <xliff:g id="TYPE">%1$s</xliff:g> सेव करना चाहते हैं?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"क्या आप "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" में <xliff:g id="TYPE_0">%1$s</xliff:g> और <xliff:g id="TYPE_1">%2$s</xliff:g> सेव करना चाहते हैं?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"क्या आप "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" में <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, और <xliff:g id="TYPE_2">%3$s</xliff:g> सेव करना चाहते हैं?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"क्या आप "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" में अपडेट करना चाहते हैं?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"क्या आप "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" में <xliff:g id="TYPE">%1$s</xliff:g> अपडेट करना चाहते हैं?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"क्या आप "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" में <xliff:g id="TYPE_0">%1$s</xliff:g> और <xliff:g id="TYPE_1">%2$s</xliff:g> अपडेट करना चाहते हैं?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"क्या आप "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" में इन आइटम को अपडेट करना चाहते हैं: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, और <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"सेव करें"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"नहीं, धन्यवाद"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"अपडेट करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 839d423..7b24bba 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -124,7 +124,7 @@
     <string name="roamingTextSearching" msgid="8360141885972279963">"Pretraživanje usluge"</string>
     <string name="wfcRegErrorTitle" msgid="3855061241207182194">"Postavljanje Wi‑Fi poziva nije uspjelo"</string>
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="3910386316304772394">"Da biste telefonirali i slali poruke putem Wi-Fi-ja, od mobilnog operatera morate tražiti da postavi tu uslugu. Zatim ponovo uključite Wi-Fi pozive u postavkama. (Kôd pogreške: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+    <item msgid="3910386316304772394">"Da biste telefonirali i slali poruke putem Wi-Fija, od mobilnog operatera morate tražiti da postavi tu uslugu. Zatim ponovo uključite Wi-Fi pozive u postavkama. (Kôd pogreške: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="7372514042696663278">"Poteškoća s registracijom Wi‑Fi poziva kod mobilnog operatera: <xliff:g id="CODE">%1$s</xliff:g>"</item>
@@ -295,6 +295,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"snimati zvuk"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da snima audiozapise?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Prepoznavanje aktivnosti"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"prepoznati aktivnost"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Želite li dopustiti da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prepoznaje vašu tjelesnu aktivnost?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparat"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"snimati fotografije i videozapise"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da snima fotografije i videozapise?"</string>
@@ -421,14 +424,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Aplikacija može dohvatiti vašu lokaciju putem mrežnih izvora poput baznih stanica i Wi-Fi mreža. Te usluge lokacije moraju biti uključene i dostupne na tabletu da bi ih aplikacija mogla upotrebljavati."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Aplikacija može dohvatiti vašu lokaciju putem mrežnih izvora poput baznih stanica i Wi-Fi mreža. Te usluge lokacije moraju biti uključene i dostupne na televizoru da bi ih aplikacija mogla upotrebljavati."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Aplikacija može dohvatiti vašu lokaciju putem mrežnih izvora poput baznih stanica i Wi-Fi mreža. Te usluge lokacije moraju biti uključene i dostupne na telefonu da bi ih aplikacija mogla upotrebljavati."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"pristupiti preciznoj lokaciji u pozadini"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Aplikacija može dobiti vašu točnu lokaciju svaki put kada je u pozadini. Te usluge lokacije moraju biti uključene i dostupne na telefonu da bi ih aplikacija mogla upotrebljavati. To može pojačati potrošnju baterije."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"pristup lokaciji u pozadini"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Ako se ovo odobrava dodatno za pristup približnoj ili preciznoj lokaciji, aplikacija može pristupiti lokaciji tijekom rada u pozadini."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"promjena postavki zvuka"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Aplikaciji omogućuje izmjenu globalnih postavki zvuka, primjerice glasnoće i zvučnika koji se upotrebljava za izlaz."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"snimanje zvuka"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Aplikacija u svakom trenutku može snimati zvuk mikrofonom."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"slati naredbe SIM-u"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Omogućuje aplikaciji slanje naredbi SIM-u. To je vrlo opasno."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"prepoznati tjelesnu aktivnost"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Ova aplikacija može prepoznati vašu tjelesnu aktivnost."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"snimi fotografije i videozapise"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Aplikacija u svakom trenutku može snimati fotografije i videozapise fotoaparatom."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"upravljanje vibracijom"</string>
@@ -521,6 +526,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Omogućuje aplikaciji izmjenu vaše zbirke fotografija."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"čitanje lokacija iz vaše medijske zbirke"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Omogućuje aplikaciji čitanje lokacija iz vaše medijske zbirke."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikacija <xliff:g id="APP">%s</xliff:g> traži autentifikaciju."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrijski hardver nije dostupan"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Otkriven je djelomični otisak prsta. Pokušajte ponovo."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
@@ -1538,7 +1544,7 @@
     <string name="data_usage_wifi_limit_title" msgid="5803363779034792676">"Dost. ogr. Wi-Fi prijenosa pod."</string>
     <string name="data_usage_limit_body" msgid="2908179506560812973">"Podatkovni je promet pauziran za ostatak ciklusa"</string>
     <string name="data_usage_mobile_limit_snoozed_title" msgid="3171402244827034372">"Premašen limit mobilnih podataka"</string>
-    <string name="data_usage_wifi_limit_snoozed_title" msgid="3547771791046344188">"Premašen limit podataka Wi-Fi-ja"</string>
+    <string name="data_usage_wifi_limit_snoozed_title" msgid="3547771791046344188">"Premašen limit podataka Wi-Fija"</string>
     <string name="data_usage_limit_snoozed_body" msgid="1671222777207603301">"Premašili ste postavljeno ograničenje za <xliff:g id="SIZE">%s</xliff:g>"</string>
     <string name="data_usage_restricted_title" msgid="5965157361036321914">"Pozadinski podaci ograničeni"</string>
     <string name="data_usage_restricted_body" msgid="469866376337242726">"Dodirnite i uklonite ograničenje"</string>
@@ -1932,14 +1938,14 @@
       <item quantity="few"><xliff:g id="COUNT">%1$s</xliff:g> prijedloga za automatsko popunjavanje</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> prijedloga za automatsko popunjavanje</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Želite li spremiti na uslugu &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Želite li da se podatak (<xliff:g id="TYPE">%1$s</xliff:g>) spremi na uslugu &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Želite li da se <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> spreme na uslugu &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Želite li da se <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> spreme na uslugu &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Želite li ažurirati na &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Želite li polje <xliff:g id="TYPE">%1$s</xliff:g> ažurirati na &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Želite li polja <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> ažurirati na &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Želite li polja <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> ažurirati na &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Želite li spremiti u oznaku "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Želite li spremiti podatke <xliff:g id="TYPE">%1$s</xliff:g> u oznaku "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Želite li spremiti podatke <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> u oznaku "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Spremite podatke <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> u oznaku "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Želite li ažurirati u oznaku "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Želite li ažurirati podatke <xliff:g id="TYPE">%1$s</xliff:g> u oznaci "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Želite li ažurirati podatke <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> u oznaci "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Želite li ažurirati ove stavke u oznaci "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Spremi"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Ne, hvala"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Ažuriraj"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index bc54bfa..b02b68b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"hanganyag rögzítése"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hangfelvételt készíthessen?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Tevékenység felismerése"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"tevékenység felismerése"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; alkalmazásnak a testmozgás felismerését?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fényképezőgép"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotók és videók készítése"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy képeket és videókat készíthessen?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Az alkalmazás hozzáférhet hálózati forrásokon (például az adótornyokon és a Wi-Fi-hálózatokon) alapuló helyadataihoz. A helyszolgáltatásoknak bekapcsolt és elérhető állapotban kell lenniük a táblagépen ahhoz, hogy az alkalmazás használhassa őket."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Az alkalmazás hozzáférhet hálózati forrásokon (például az adótornyokon és a Wi-Fi-hálózatokon) alapuló helyadataihoz. A helyszolgáltatásoknak bekapcsolt és elérhető állapotban kell lenniük a TV-n ahhoz, hogy az alkalmazás használhassa őket."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Az alkalmazás hozzáférhet hálózati forrásokon (például az adótornyokon és a Wi-Fi-hálózatokon) alapuló helyadataihoz. A helyszolgáltatásoknak bekapcsolt és elérhető állapotban kell lenniük a telefonon ahhoz, hogy az alkalmazás használhassa őket."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"pontos helyadatokhoz való hozzáférés a háttérben"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ez az alkalmazás bármikor hozzáférhet az eszköz pontos helyadataihoz a háttérben. A helyszolgáltatásoknak bekapcsolt és hozzáférhető állapotban kell lenniük a telefonon ahhoz, hogy az alkalmazás használhassa őket. Ez növelheti az akkumulátorhasználatot."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"Hozzáférés a helyadatokhoz a háttérben"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Ha engedélyezi ezt a hozzávetőleges vagy pontos helyadatokhoz való hozzáférés mellett, akkor az alkalmazás hozzáférhet a helyadatokhoz, miközben a háttérben fut."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"hangbeállítások módosítása"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Lehetővé teszi az alkalmazás számára az általános hangbeállítások, például a hangerő és a használni kívánt kimeneti hangszóró módosítását."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"hanganyag rögzítése"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Az alkalmazás a mikrofon használatával bármikor készíthet hangfelvételt."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"parancsok küldése a SIM-kártyára"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Engedélyezi, hogy az alkalmazás parancsokat küldjön a SIM kártyára. Ez rendkívül veszélyes lehet."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"testmozgás felismerése"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Az alkalmazás képes felismerni a testmozgást."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"fotók és videók készítése"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Az alkalmazás a kamera használatával bármikor készíthet fényképeket és rögzíthet videókat."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"rezgés szabályozása"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Engedélyezi az alkalmazásnak a fényképgyűjtemény módosítását."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"helyek olvasása a médiagyűjteményből"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Engedélyezi az alkalmazásnak a helyek médiagyűjteményből való olvasását."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"A(z) <xliff:g id="APP">%s</xliff:g> alkalmazás hitelesítést kér."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrikus hardver nem áll rendelkezésre"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"A rendszer az ujjlenyomatnak csak egy részletét érzékelte. Próbálkozzon újra."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nem sikerült feldolgozni az ujjlenyomatot. Próbálkozzon újra."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> automatikus kitöltési javaslat</item>
       <item quantity="one">Egy automatikus kitöltési javaslat</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Menti ide: &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Menti a következőt: <xliff:g id="TYPE">%1$s</xliff:g> ide: &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Menti a következőket: <xliff:g id="TYPE_0">%1$s</xliff:g> és <xliff:g id="TYPE_1">%2$s</xliff:g> ide: &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Menti a következőket: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> és <xliff:g id="TYPE_2">%3$s</xliff:g> ide: &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Frissít a következőre: &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Frissíti a következőt: <xliff:g id="TYPE">%1$s</xliff:g> erre: &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Frissíti a következőket: <xliff:g id="TYPE_0">%1$s</xliff:g> és <xliff:g id="TYPE_1">%2$s</xliff:g> erre: &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Frissíti a következőket: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> és <xliff:g id="TYPE_2">%3$s</xliff:g> erre: &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Menti a(z) "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" szolgáltatásba?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Menti a(z) "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" szolgáltatásba a következőt: <xliff:g id="TYPE">%1$s</xliff:g>?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Menti a(z) "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" szolgáltatásba a következőket: <xliff:g id="TYPE_0">%1$s</xliff:g> és <xliff:g id="TYPE_1">%2$s</xliff:g>?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Menti a(z) "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" szolgáltatásba a következőket: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> és <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Frissíti a(z) "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" szolgáltatásban?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Frissíti a(z) "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" szolgáltatásban a következőt: <xliff:g id="TYPE">%1$s</xliff:g>?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Frissíti a(z) "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" szolgáltatásban a következőket: <xliff:g id="TYPE_0">%1$s</xliff:g> és <xliff:g id="TYPE_1">%2$s</xliff:g>?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Frissíti a(z) "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" szolgáltatásban a következőket: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> és <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Mentés"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Nem, köszönöm"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Frissítés"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 4a828fa..90ab955 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Խոսափող"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ձայնագրել"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին կատարել ձայնագրություն:"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Ակտիվության ճանաչում"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"ճանաչել ակտիվությունը"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ճանաչել ձեր ֆիզիկական ակտիվությունը:"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Տեսախցիկ"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"լուսանկարել և տեսագրել"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին լուսանկարել և տեսանկարել:"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Այս հավելվածը կարող է ստանալ ձեր տեղադրության տվյալները ցանցային տարբեր աղբյուրներից, օրինակ՝ բջջային աշտարակներից և Wi-Fi ցանցերից: Այս տեղորոշման ծառայությունները պետք է միացված և հասանելի լինեն ձեր պլանշետում, որպեսզի հավելվածը կարողանա օգտագործել դրանք:"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Այս հավելվածը կարող է ստանալ ձեր տեղադրության տվյալները ցանցային տարբեր աղբյուրներից, օրինակ՝ բջջային աշտարակներից և Wi-Fi ցանցերից: Այս տեղորոշման ծառայությունները պետք է միացված և հասանելի լինեն ձեր հեռուստացույցում, որպեսզի հավելվածը կարողանա օգտագործել դրանք:"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Այս հավելվածը կարող է ստանալ ձեր տեղադրության տվյալները ցանցային տարբեր աղբյուրներից, օրինակ՝ բջջային աշտարակներից և Wi-Fi ցանցերից: Այս տեղորոշման ծառայությունները պետք է միացված և հասանելի լինեն ձեր հեռախոսում, որպեսզի հավելվածը կարողանա օգտագործել դրանք:"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"Ֆոնային ռեժիմում տեղադրության տվյալների հասանելիություն"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Հավելվածը կարող է ստանալ ձեր տեղադրության տվյալները ֆոնային ռեժիմում։ Այս տեղորոշման ծառայությունները պետք է միացված և հասանելի լինեն ձեր հեռախոսում, որպեսզի հավելվածը կարողանա օգտագործել դրանք: Սա կարող է արագացնել մարտկոցի լիցքի սպառումը:"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"տեղադրության մասին տվյալների հասանելիություն ֆոնային ռեժիմում"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Եթե բացի մոտավոր կամ ճշգրիտ տեղորոշման տվյալների հասանելիությունից հավելվածին տրամադրեք այս թույլտվությունը, հավելվածին տեղադրության մասին տվյալները հասանելի կլինեն ֆոնային ռեժիմում։"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"փոխել ձեր աուդիո կարգավորումները"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Թույլ է տալիս հավելվածին փոփոխել ձայնանյութի գլոբալ կարգավորումները, ինչպես օրինակ` ձայնը և թե որ խոսափողն է օգտագործված արտածման համար:"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ձայնագրել աուդիո ֆայլ"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Այս հավելվածը ցանկացած պահի կարող է ձայնագրել խոսափողի օգնությամբ:"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"ուղարկել հրամաններ SIM քարտին"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Թույլ է տալիս հավելվածին հրամաններ ուղարկել SIM-ին: Սա շատ վտանգավոր է:"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"ֆիզիկական ակտիվության ճանաչում"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Հավելվածը կարող է ճանաչել ձեր ֆիզիկական ակտիվությունը:"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"լուսանկարել և տեսանկարել"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Այս հավելվածը կարող է ցանկացած պահի լուսանկարել և տեսագրել՝ օգտագործելով տեսախցիկը:"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"կառավարել թրթռումը"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Թույլ է տալիս հավելվածին փոփոխել ձեր լուսանկարների հավաքածուն:"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"ճանաչել տեղադրության մասին տվյալները մեդիա բովանդակության հավաքածուից"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Թույլ է տալիս հավելվածին ճանաչել տեղադրության մասին տվյալները ձեր մեդիա բովանդակության հավաքածուից:"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> հավելվածը նույնականացում է հարցում"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Կենսաչափական սարքը հասանելի չէ"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Մատնահետքը հայտնաբերվել է մասամբ: Փորձեք նորից:"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Չհաջողվեց մշակել մատնահետքը: Նորից փորձեք:"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> ինքնալցման առաջարկ</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> ինքնալցման առաջարկ</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Պահե՞լ &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ծառայությունում"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Պահե՞լ <xliff:g id="TYPE">%1$s</xliff:g>ը &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ծառայությունում"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Պահե՞լ <xliff:g id="TYPE_0">%1$s</xliff:g>ն ու <xliff:g id="TYPE_1">%2$s</xliff:g>ը &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ծառայությունում"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Պահե՞լ <xliff:g id="TYPE_0">%1$s</xliff:g>ը, <xliff:g id="TYPE_1">%2$s</xliff:g>ն ու <xliff:g id="TYPE_2">%3$s</xliff:g>ը &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ծառայությունում"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Թարմացնե՞լ &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ծառայության տվյալները"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Թարմացնե՞լ &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ծառայության տվյալները (<xliff:g id="TYPE">%1$s</xliff:g>)"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Թարմացնե՞լ &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ծառայության տվյալները (<xliff:g id="TYPE_0">%1$s</xliff:g> և <xliff:g id="TYPE_1">%2$s</xliff:g>)"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Թարմացնե՞լ &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ծառայության տվյալները (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> և <xliff:g id="TYPE_2">%3$s</xliff:g>)"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Պահե՞լ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ծառայությունում"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Պահե՞լ տվյալները (<xliff:g id="TYPE">%1$s</xliff:g>) "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ծառայությունում"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Պահե՞լ տվյալները (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>) "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ծառայությունում"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Պահե՞լ տվյալները (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g>) "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ծառայությունում"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Թարմացնե՞լ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ծառայությունում"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Թարմացնե՞լ տվյալները (<xliff:g id="TYPE">%1$s</xliff:g>) "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ծառայությունում"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Թարմացնե՞լ տվյալները (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>) "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ծառայությունում"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Թարմացնե՞լ տվյալները (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g>) "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ծառայությունում"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Պահել"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Ոչ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Թարմացնել"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 167082d..924b264 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"merekam audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merekam audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Pengenalan aktivitas"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"kenali aktivitas"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengenali aktivitas fisik Anda?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"mengambil gambar dan merekam video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengambil gambar dan merekam video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Aplikasi ini dapat memperoleh lokasi Anda berdasarkan sumber jaringan seperti menara seluler dan jaringan Wi-Fi. Layanan lokasi ini harus diaktifkan dan tersedia di tablet agar aplikasi dapat menggunakannya."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Aplikasi ini dapat memperoleh lokasi Anda berdasarkan sumber jaringan seperti menara seluler dan jaringan Wi-Fi. Layanan lokasi ini harus diaktifkan dan tersedia di TV agar aplikasi dapat menggunakannya."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Aplikasi ini dapat memperoleh lokasi Anda berdasarkan sumber jaringan seperti menara seluler dan jaringan Wi-Fi. Layanan lokasi ini harus diaktifkan dan tersedia di ponsel agar aplikasi dapat menggunakannya."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"akses lokasi pasti saat di latar belakang"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Aplikasi ini bisa mendapatkan lokasi pasti Anda kapan pun aplikasi ada di latar belakang. Fitur layanan lokasi ini harus diaktifkan dan tersedia di ponsel agar dapat digunakan oleh aplikasi. Fitur ini dapat meningkatkan konsumsi baterai."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"akses lokasi di latar belakang"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Jika aplikasi diberi izin tambahan ke akses lokasi perkiraan atau akurat, aplikasi dapat mengakses lokasi saat bekerja di latar belakang."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ubah setelan audio Anda"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Memungkinkan aplikasi mengubah setelan audio global, misalnya volume dan pengeras suara mana yang digunakan untuk keluaran."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"rekam audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Aplikasi ini dapat merekam audio menggunakan mikrofon kapan saja."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"kirimkan perintah ke SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Mengizinkan aplikasi mengirim perintah ke SIM. Ini sangat berbahaya."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"kenali aktivitas fisik"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Aplikasi ini dapat mengenali aktivitas fisik Anda."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ambil gambar dan video"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Aplikasi ini dapat mengambil foto dan merekam video menggunakan kamera kapan saja."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrol getaran"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Mengizinkan aplikasi untuk memodifikasi koleksi foto Anda."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"membaca lokasi dari koleksi media Anda"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Mengizinkan aplikasi untuk membaca lokasi dari koleksi media Anda."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikasi <xliff:g id="APP">%s</xliff:g> meminta autentikasi."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biometrik tidak tersedia"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Sebagian sidik jari terdeteksi. Coba lagi."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Tidak dapat memproses sidik jari. Coba lagi."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> saran IsiOtomatis</item>
       <item quantity="one">1 saran IsiOtomatis</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Simpan ke &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Simpan <xliff:g id="TYPE">%1$s</xliff:g> ke &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Simpan <xliff:g id="TYPE_0">%1$s</xliff:g> dan <xliff:g id="TYPE_1">%2$s</xliff:g> ke &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Simpan <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, dan <xliff:g id="TYPE_2">%3$s</xliff:g> ke &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Update ke &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Update <xliff:g id="TYPE">%1$s</xliff:g> ke &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> dan <xliff:g id="TYPE_1">%2$s</xliff:g> ke &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Update <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, dan <xliff:g id="TYPE_2">%3$s</xliff:g> ke &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Simpan ke "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Simpan <xliff:g id="TYPE">%1$s</xliff:g> ke "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Simpan <xliff:g id="TYPE_0">%1$s</xliff:g> dan <xliff:g id="TYPE_1">%2$s</xliff:g> ke "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Simpan <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, dan <xliff:g id="TYPE_2">%3$s</xliff:g> ke "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Perbarui di "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Perbarui <xliff:g id="TYPE">%1$s</xliff:g> di "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Perbarui <xliff:g id="TYPE_0">%1$s</xliff:g> dan <xliff:g id="TYPE_1">%2$s</xliff:g> di "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Perbarui item-item berikut di "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, dan <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Simpan"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Lain kali"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Update"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 0c7e6b8..1be394a 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Hljóðnemi"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"taka upp hljóð"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Viltu leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að taka upp hljóð?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Greining á hreyfingu"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"greina hreyfingu"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Viltu leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að greina hreyfingu þína?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Myndavél"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"taka myndir og taka upp myndskeið"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Viltu leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að taka myndir og myndskeið?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Þetta forrit getur séð staðsetningu þína út frá netkerfum á borð við farsímasenda og Wi-Fi net. Það verður að vera kveikt á slíkri staðsetningarþjónustu og hún þarf að vera aðgengileg spjaldtölvunni til að forritið geti notað hana."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Þetta forrit getur séð staðsetningu þína út frá netkerfum á borð við farsímasenda og Wi-Fi net. Það verður að vera kveikt á slíkri staðsetningarþjónustu og hún þarf að vera aðgengileg sjónvarpinu til að forritið geti notað hana."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Þetta forrit getur séð staðsetningu þína út frá netkerfum á borð við farsímasenda og Wi-Fi net. Það verður að vera kveikt á slíkri staðsetningarþjónustu og hún þarf að vera aðgengileg símanum til að forritið geti notað hana."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"aðgangur að nákvæmri staðsetningu í bakgrunni"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Þetta forrit getur séð nákvæma staðsetningu þína hvenær sem það er í bakgrunninum. Það verður að vera kveikt á þessari staðsetningarþjónustu og hún þarf að vera aðgengileg í símanum til að forritið geti notað hana. Þetta getur aukið rafhlöðunotkun."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"aðgangur að staðsetningu í bakgrunni"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Ef þetta er veitt til viðbótar við aðgang að áætlaðri eða nákvæmri staðsetningu getur forritið fengið aðgang að staðsetningu á meðan það keyrir í bakgrunni."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"breyta hljóðstillingum"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Leyfir forriti að breyta altækum hljóðstillingum, s.s. hljóðstyrk og hvaða hátalari er notaður sem úttak."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"taka upp hljóð"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Þetta forrit getur tekið upp hljóð með hljóðnemanum hvenær sem er."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"senda skipanir til SIM-kortsins"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Leyfir forriti að senda SIM-kortinu skipanir. Þetta er mjög hættulegt."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"greina hreyfingu"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Þetta forrit getur greint hreyfingu þína."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"taka myndir og myndskeið"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Þetta forrit getur tekið myndir og tekið upp myndskeið með myndavélinni hvenær sem er."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"stjórna titringi"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Leyfir forritinu að breyta myndasafninu þínu."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"lesa staðsetningar úr efnissafninu þínu"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Leyfir forritinu að lesa staðsetningar úr efnissafninu þínu."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> forritið vill staðfestingu."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Lífkennavélbúnaður ekki tiltækur"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hluti fingrafars greindist. Reyndu aftur."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ekki var hægt að vinna úr fingrafarinu. Reyndu aftur."</string>
@@ -1898,14 +1904,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> tillaga fyrir sjálfvirka útfyllingu</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> tillögur fyrir sjálfvirka útfyllingu</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Vista á &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Vista <xliff:g id="TYPE">%1$s</xliff:g> á &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Vista <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> á &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Vista <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g> á &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Viltu uppfæra í &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Viltu uppfæra <xliff:g id="TYPE">%1$s</xliff:g> í &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Viltu uppfæra <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> í &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Viltu uppfæra <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g> í &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Vista í "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Vista <xliff:g id="TYPE">%1$s</xliff:g> í "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Vista <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> í "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Vista <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g> í "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Uppfæra í "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Uppfæra <xliff:g id="TYPE">%1$s</xliff:g> í "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Uppfæra <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> í "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Uppfæra þessi atriði í "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Vista"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Nei, takk"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Uppfæra"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index a6d5d23..a3b9835 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microfono"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"registrare audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di registrare audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Riconoscimento di attività"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"riconoscere l\'attività"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Vuoi consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di riconoscere la tua attività fisica?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotocamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"scattare foto e registrare video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di scattare foto e registrare video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Questa app può accedere alla tua posizione tramite fonti di rete come antenne di telefonia mobile e reti Wi-Fi. Tali servizi di geolocalizzazione devono essere attivati e disponibili sul tablet per consentire all\'app di utilizzarli."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Questa app può accedere alla tua posizione tramite fonti di rete come antenne di telefonia mobile e reti Wi-Fi. Tali servizi di geolocalizzazione devono essere attivati e disponibili sulla TV per consentire all\'app di utilizzarli."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Questa app può accedere alla tua posizione tramite fonti di rete come antenne di telefonia mobile e reti Wi-Fi. Tali servizi di geolocalizzazione devono essere attivati e disponibili sul telefono per consentire all\'app di utilizzarli."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"Accesso alla posizione esatta in background"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Questa app può recuperare la tua posizione esatta quando è in background. Questi servizi di geolocalizzazione devono essere attivi e disponibili sul telefono affinché l\'app possa usarli. Potrebbe aumentare il consumo della batteria."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"accesso alla posizione in background"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Se concedi l\'autorizzazione insieme all\'accesso alla posizione precisa o approssimativa, l\'app potrà accedere alla posizione mentre viene eseguita in background."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifica impostazioni audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Consente all\'applicazione di modificare le impostazioni audio globali, come il volume e quale altoparlante viene utilizzato per l\'uscita."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"registrare audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Questa app può registrare audio tramite il microfono in qualsiasi momento."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"invio di comandi alla SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Consente all\'app di inviare comandi alla SIM. Questo è molto pericoloso."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"riconoscimento dell\'attività fisica"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Questa app può riconoscere la tua attività fisica."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"acquisizione di foto e video"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Questa app può scattare foto e registrare video tramite la fotocamera in qualsiasi momento."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controllo vibrazione"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Consente all\'app di modificare la tua raccolta di foto."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"Lettura delle posizioni dalla tua raccolta multimediale"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Consente all\'app di leggere le posizioni dalla tua raccolta multimediale."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"L\'app <xliff:g id="APP">%s</xliff:g> richiede l\'autenticazione."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biometrico non disponibile"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Rilevata impronta digitale parziale. Riprova."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossibile elaborare l\'impronta digitale. Riprova."</string>
@@ -1174,7 +1180,7 @@
     <string name="app_upgrading_toast" msgid="3008139776215597053">"Upgrade dell\'app <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"Ottimizzazione applicazione <xliff:g id="NUMBER_0">%1$d</xliff:g> di <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
     <string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g> in preparazione."</string>
-    <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Avvio applicazioni."</string>
+    <string name="android_upgrading_starting_apps" msgid="451464516346926713">"Avvio app."</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"Conclusione dell\'avvio."</string>
     <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> in esecuzione"</string>
     <string name="heavy_weight_notification_detail" msgid="2304833848484424985">"Tocca per tornare al gioco"</string>
@@ -1802,7 +1808,7 @@
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Comprimi"</string>
     <string name="zen_mode_feature_name" msgid="5254089399895895004">"Non disturbare"</string>
-    <string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"Tempo di inattività"</string>
+    <string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"Tempo di riposo"</string>
     <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Notte di un giorno feriale"</string>
     <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fine settimana"</string>
     <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evento"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggerimenti di Compilazione automatica</item>
       <item quantity="one">Un suggerimento di Compilazione automatica</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Salvare in &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Salvare <xliff:g id="TYPE">%1$s</xliff:g> in &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Salvare <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> in &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Salvare <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> in &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Vuoi eseguire l\'aggiornamento a &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Vuoi eseguire l\'aggiornamento di <xliff:g id="TYPE">%1$s</xliff:g> a &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Vuoi eseguire l\'aggiornamento di <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> a &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Vuoi eseguire l\'aggiornamento di <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> a &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Vuoi salvare su "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Vuoi salvare <xliff:g id="TYPE">%1$s</xliff:g> su "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Vuoi salvare <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> su "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Vuoi salvare <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> su "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Vuoi aggiornare su "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Vuoi aggiornare <xliff:g id="TYPE">%1$s</xliff:g> su "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Vuoi aggiornare <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> su "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Vuoi aggiornare questi elementi su "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Salva"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"No, grazie"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Aggiorna"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 48933f9..5bf5704 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -251,7 +251,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
     <string name="global_action_lockdown" msgid="1099326950891078929">"נעילה"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
-    <string name="notification_hidden_text" msgid="6351207030447943784">"הודעה חדשה"</string>
+    <string name="notification_hidden_text" msgid="6351207030447943784">"התראה חדשה"</string>
     <string name="notification_channel_virtual_keyboard" msgid="6969925135507955575">"מקלדת וירטואלית"</string>
     <string name="notification_channel_physical_keyboard" msgid="7297661826966861459">"מקלדת פיזית"</string>
     <string name="notification_channel_security" msgid="7345516133431326347">"אבטחה"</string>
@@ -298,6 +298,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"מיקרופון"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"הקלטת אודיו"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאה להקליט אודיו?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"זיהוי פעילות"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"זיהוי פעילות"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"‏האם לאפשר ל-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; לזהות את הפעילות הגופנית שלך?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"מצלמה"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"צילום תמונות והקלטת וידאו"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאה לצלם תמונות וסרטונים?"</string>
@@ -424,14 +427,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"‏אפליקציה זו יכולה לזהות את המיקום שלך על סמך מקורות מיקום ברשת, כגון אנטנות סלולריות ורשתות Wi-Fi. שירותי מיקום אלה חייבים להיות מופעלים וזמינים בטאבלט שלך כדי שהאפליקציה תוכל להשתמש בהם."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"‏אפליקציה זו יכולה לזהות את המיקום שלך על סמך מקורות מיקום ברשת, כגון אנטנות סלולריות ורשתות Wi-Fi. שירותי מיקום אלה חייבים להיות מופעלים וזמינים בטלוויזיה שלך כדי שהאפליקציה תוכל להשתמש בהם."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"‏אפליקציה זו יכולה לזהות את המיקום שלך על סמך מקורות מיקום ברשת, כגון אנטנות סלולריות ורשתות Wi-Fi. שירותי מיקום אלה חייבים להיות מופעלים וזמינים בטלפון שלך כדי שהאפליקציה תוכל להשתמש בהם."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"קבלת גישה למיקום מדויק ברקע"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"אפליקציה זו יכולה לזהות את המיקום המדויק שלך בכל זמן שהיא פועלת ברקע. כדי שהאפליקציה תוכל להשתמש בשירותי המיקום, עליהם להיות מופעלים וזמינים בטלפון. ייתכן שפעולה זו תגביר את צריכת הסוללה."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"גישה למיקום ברקע"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"אם מתקבל אישור, בנוסף לגישה למיקום משוער או מדויק, תהיה לאפליקציה גישה למיקום גם כשהיא פועלת ברקע."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"שנה את הגדרות האודיו שלך"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"מאפשר לאפליקציה לשנות הגדרות אודיו גלובליות כמו עוצמת קול ובחירת הרמקול המשמש לפלט."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"הקלט אודיו"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"אפליקציה זו יכולה להשתמש במיקרופון כדי להקליט אודיו בכל עת."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"‏שליחת פקודות אל ה-SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"‏מאפשרת ליישום לשלוח פקודות ל-SIM. זוהי הרשאה מסוכנת מאוד."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"זיהוי הפעילות הגופנית"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"האפליקציה מזהה את הפעילות הגופנית שלך."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"צלם תמונות וסרטונים"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"אפליקציה זו יכולה להשתמש במצלמה כדי לצלם תמונות ולהקליט סרטונים בכל עת."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"שליטה ברטט"</string>
@@ -524,6 +529,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"מאפשרת לאפליקציה לשנות את אוסף התמונות שלך."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"לקרוא מיקומים מאוסף המדיה שלך"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"מאפשרת לאפליקציה לקרוא מיקומים מאוסף המדיה שלך."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"האפליקציה <xliff:g id="APP">%s</xliff:g> רוצה לבצע אימות."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"חומרה ביומטרית לא זמינה"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"זוהתה טביעת אצבע חלקית. נסה שוב."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"לא ניתן היה לעבד את טביעת האצבע. נסה שוב."</string>
@@ -654,7 +660,7 @@
     <string name="policylab_resetPassword" msgid="4934707632423915395">"שינוי נעילת המסך"</string>
     <string name="policydesc_resetPassword" msgid="1278323891710619128">"שינוי של נעילת המסך."</string>
     <string name="policylab_forceLock" msgid="2274085384704248431">"נעילת המסך"</string>
-    <string name="policydesc_forceLock" msgid="1141797588403827138">"שליטה באופן ובתזמון של נעילת המסך"</string>
+    <string name="policydesc_forceLock" msgid="1141797588403827138">"שליטה באופן ובתזמון של נעילת המסך."</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"מחיקת כל הנתונים"</string>
     <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"מחק את נתוני הטאבלט ללא אזהרה על ידי ביצוע איפוס נתוני יצרן."</string>
     <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"מחיקה של נתוני הטלוויזיה ללא אזהרה, על ידי ביצוע איפוס לנתוני היצרן."</string>
@@ -1247,7 +1253,7 @@
     <string name="ringtone_silent" msgid="7937634392408977062">"ללא"</string>
     <string name="ringtone_picker_title" msgid="3515143939175119094">"רינגטונים"</string>
     <string name="ringtone_picker_title_alarm" msgid="6473325356070549702">"צלילי התראה"</string>
-    <string name="ringtone_picker_title_notification" msgid="4837740874822788802">"צלילי הודעה"</string>
+    <string name="ringtone_picker_title_notification" msgid="4837740874822788802">"צלילי התראה"</string>
     <string name="ringtone_unknown" msgid="3914515995813061520">"לא ידוע"</string>
     <plurals name="wifi_available" formatted="false" msgid="7900333017752027322">
       <item quantity="two">‏יש רשתות Wi-Fi זמינות</item>
@@ -1460,7 +1466,7 @@
     <string name="notification_listener_binding_label" msgid="2014162835481906429">"מאזין להתראות"</string>
     <string name="vr_listener_binding_label" msgid="4316591939343607306">"‏VR ליסנר"</string>
     <string name="condition_provider_service_binding_label" msgid="1321343352906524564">"ספק תנאי"</string>
-    <string name="notification_ranker_binding_label" msgid="774540592299064747">"שירות של דירוג הודעות"</string>
+    <string name="notification_ranker_binding_label" msgid="774540592299064747">"שירות של דירוג התראות"</string>
     <string name="vpn_title" msgid="19615213552042827">"‏VPN מופעל"</string>
     <string name="vpn_title_long" msgid="6400714798049252294">"‏VPN מופעל על ידי <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="vpn_text" msgid="1610714069627824309">"הקש כדי לנהל את הרשת."</string>
@@ -1903,7 +1909,7 @@
       <item quantity="one">בחרת <xliff:g id="COUNT_0">%1$d</xliff:g></item>
     </plurals>
     <string name="default_notification_channel_label" msgid="5929663562028088222">"ללא שיוך לקטגוריה"</string>
-    <string name="importance_from_user" msgid="7318955817386549931">"אתה מגדיר את החשיבות של ההודעות האלה."</string>
+    <string name="importance_from_user" msgid="7318955817386549931">"עליך להגדיר את החשיבות של ההתראות האלה."</string>
     <string name="importance_from_person" msgid="9160133597262938296">"ההודעה חשובה בשל האנשים המעורבים."</string>
     <string name="user_creation_account_exists" msgid="1942606193570143289">"האם לאפשר ל-<xliff:g id="APP">%1$s</xliff:g> ליצור משתמש חדש לחשבון <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
     <string name="user_creation_adding" msgid="4482658054622099197">"האם לאפשר ל-<xliff:g id="APP">%1$s</xliff:g> ליצור משתמש חדש לחשבון <xliff:g id="ACCOUNT">%2$s</xliff:g> (כבר קיים משתמש לחשבון הזה) ?"</string>
@@ -1967,14 +1973,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> הצעות של מילוי אוטומטי</item>
       <item quantity="one">הצעה אחת של מילוי אוטומטי</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"‏לשמור בשירות &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"‏האם לשמור <xliff:g id="TYPE">%1$s</xliff:g> בשירות &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"‏האם לשמור <xliff:g id="TYPE_0">%1$s</xliff:g> ו<xliff:g id="TYPE_1">%2$s</xliff:g> בשירות &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"‏האם לשמור <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ו<xliff:g id="TYPE_2">%3$s</xliff:g> בשירות &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"‏האם לעדכן בשירות &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"‏האם לעדכן את <xliff:g id="TYPE">%1$s</xliff:g> בשירות &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"‏האם לעדכן את <xliff:g id="TYPE_0">%1$s</xliff:g> ואת <xliff:g id="TYPE_1">%2$s</xliff:g> בשירות &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"‏האם לעדכן את <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ו<xliff:g id="TYPE_2">%3$s</xliff:g> בשירות &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"לשמור ב-"<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"האם לשמור את <xliff:g id="TYPE">%1$s</xliff:g> ב-"<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"האם לשמור את <xliff:g id="TYPE_0">%1$s</xliff:g> ואת <xliff:g id="TYPE_1">%2$s</xliff:g> ב-"<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"האם לשמור את <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ו-<xliff:g id="TYPE_2">%3$s</xliff:g> ב-"<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"האם לעדכן ב-"<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"האם לעדכן את <xliff:g id="TYPE">%1$s</xliff:g> ב-"<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"האם לעדכן את <xliff:g id="TYPE_0">%1$s</xliff:g> ואת <xliff:g id="TYPE_1">%2$s</xliff:g> ב-"<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"האם לעדכן פריטים אלה ב-"<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ו-<xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"שמירה"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"לא, תודה"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"עדכון"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 994039c..a9f94c9 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"マイク"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"音声の録音"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"音声の録音を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"運動の認識"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"運動を認識します"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"運動の認識を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"カメラ"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"写真と動画の撮影"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"写真と動画の撮影を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"このアプリは、ネットワーク位置情報源(携帯基地局や Wi-Fi ネットワークなど)に基づいて、ユーザーの位置情報を取得します。これらの位置情報サービスは ON の状態にして、タブレットでアプリがサービスを利用できるようにする必要があります。"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"このアプリは、ネットワーク位置情報源(携帯基地局や Wi-Fi ネットワークなど)に基づいて、ユーザーの位置情報を取得します。これらの位置情報サービスは ON の状態にして、テレビでアプリがサービスを利用できるようにする必要があります。"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"このアプリは、ネットワーク位置情報源(携帯基地局や Wi-Fi ネットワークなど)に基づいて、ユーザーの位置情報を取得します。これらの位置情報サービスは ON の状態にして、スマートフォンでアプリがサービスを利用できるようにする必要があります。"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"バックグラウンドで正確な位置情報にアクセス"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"このアプリは、バックグラウンド状態でも常にユーザーの正確な位置情報を取得できます。この位置情報サービスは ON の状態にして、スマートフォンでアプリがサービスを利用できるようにする必要があります。これにより、電池の消費量が増える可能性があります。"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"バックグラウンドでの位置情報へのアクセス"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"これが、おおよその位置情報または正確な位置情報へのアクセスの追加権限の場合、アプリはバックグラウンドでの実行中も位置情報にアクセスできます。"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"音声設定の変更"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"音声全般の設定(音量、出力に使用するスピーカーなど)の変更をアプリに許可します。"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"録音"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"このアプリは、いつでもマイクを使用して録音できます。"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIMへのコマンド送信"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"SIMにコマンドを送信することをアプリに許可します。この許可は非常に危険です。"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"運動の認識"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"このアプリで運動が認識されるようにします。"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"写真と動画の撮影"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"このアプリは、いつでもカメラを使用して写真や動画を撮影できます。"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"バイブレーションの制御"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"写真コレクションの変更をアプリに許可します。"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"メディア コレクションの位置情報の読み取り"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"メディア コレクションの位置情報の読み取りをアプリに許可します。"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"アプリ <xliff:g id="APP">%s</xliff:g> が認証を求めています。"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"生体認証ハードウェアが利用できません"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"指紋を一部しか検出できませんでした。もう一度お試しください。"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"指紋を処理できませんでした。もう一度お試しください。"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other">自動入力の候補 <xliff:g id="COUNT">%1$s</xliff:g> 件</item>
       <item quantity="one">自動入力の候補 1 件</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; に保存しますか?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g>を &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; に保存しますか?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>を &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; に保存しますか?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>、<xliff:g id="TYPE_2">%3$s</xliff:g>を &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; に保存しますか?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; に更新しますか?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g>を &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; に更新しますか?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g>と<xliff:g id="TYPE_1">%2$s</xliff:g>を &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; に更新しますか?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>、<xliff:g id="TYPE_2">%3$s</xliff:g>を &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; に更新しますか?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" に保存しますか?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g>を "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" に保存しますか?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>を "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" に保存しますか?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>、<xliff:g id="TYPE_2">%3$s</xliff:g>を "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" に保存しますか?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" で更新しますか?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g>を "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" で更新しますか?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>を "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" で更新しますか?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>、<xliff:g id="TYPE_2">%3$s</xliff:g>を "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" で更新しますか?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"はい"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"いいえ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"更新"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index a9ce748..e776289 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"მიკროფონი"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"აუდიოს ჩაწერა"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"გსურთ, მიანიჭოთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/b&gt; აუდიოს ჩაწერის ნებართვა?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"აქტივობის ამოცნობა"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"აქტივობის ამოცნობა"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"გსურთ, მიანიჭოთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-ს თქვენი ფიზიკური აქტივობის ამოცნობის ნებართვა?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"კამერა"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ფოტოებისა და ვიდეოების გადაღება"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"გსურთ, მიანიჭოთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/b&gt; სურათების გადაღების და ვიდეოების ჩაწერის ნებართვა?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ამ აპს თქვენი მდებარეობის შესახებ ინფორმაციის მიღება ისეთი წყაროების მეშვეობით შეუძლია, როგორიცაა მობილური კავშირგაბმულობის ანძები და Wi-Fi ქსელები. მდებარეობის აღნიშნული სერვისები თქვენს ტაბლეტზე ჩართული და ხელმისაწვდომი უნდა იყოს, რათა აპმა მათი გამოყენება შეძლოს."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ამ აპს თქვენი მდებარეობის შესახებ ინფორმაციის მიღება ისეთი წყაროების მეშვეობით შეუძლია, როგორიცაა მობილური კავშირგაბმულობის ანძები და Wi-Fi ქსელები. მდებარეობის აღნიშნული სერვისები თქვენს ტელევიზორზე ჩართული და ხელმისაწვდომი უნდა იყოს, რათა აპმა მათი გამოყენება შეძლოს."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ამ აპს თქვენი მდებარეობის შესახებ ინფორმაციის მიღება ისეთი წყაროების მეშვეობით შეუძლია, როგორიცაა მობილური კავშირგაბმულობის ანძები და Wi-Fi ქსელები. მდებარეობის აღნიშნული სერვისები თქვენს ტელეფონზე ჩართული და ხელმისაწვდომი უნდა იყოს, რათა აპმა მათი გამოყენება შეძლოს."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ზუსტ მდებარეობაზე წვდომა ფონურ რეჟიმში"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ამ აპს შეუძლია თქვენი ზუსტი მდებარეობის შესახებ ინფორმაციის მიღება ყოველთვის, როცა გაშვებულია ფონურ რეჟიმში. თქვენს ტელეფონზე ჩართული და ხელმისაწვდომი უნდა იყოს მდებარეობის სერვისები, აპმა მათი გამოყენება რომ შეძლოს. ამან შეიძლება გაზარდოს ბატარეის მოხმარება."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"მდებარეობაზე წვდომა ფონურ რეჟიმში"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"ამ ნებართვის მიახლოებით ან ზუსტ მდებარეობაზე წვდომის ნებართვასთან ერთად მინიჭების შემთხვევაში, აპს შეეძლება მდებარეობაზე წვდომა ფონურ რეჟიმში."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"თქვენი აუდიო პარამეტრების შეცვლა"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"აპს შეეძლება აუდიოს გლობალური პარამეტრების შეცვლა. მაგ.: ხმის სიმაღლე და რომელი დინამიკი გამოიყენება სიგნალის გამოსტანად."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"აუდიოს ჩაწერა"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"ამ აპს ნებისმიერ დროს შეუძლია მიკროფონით აუდიოს ჩაწერა."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"ბრძანებების SIM-ზე გაგზავნა"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"აპისთვის ნების დართვა გაუგზავნოს ბრძანებები SIM-ბარათს. ეს ძალიან საშიშია."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"ფიზიკური აქტივობის ამოცნობა"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"ამ აპს შეუძლია თქვენი ფიზიკური აქტივობის ამოცნობა."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"სურათებისა და ვიდეოების გადაღება"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"ამ აპს ნებისმიერ დროს შეუძლია კამერით სურათების გადაღება და ვიდეოების ჩაწერა."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ვიბრაციის კონტროლი"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"აპი შეძლებს თქვენი ფოტოკოლექციის შეცვლას."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"მდებარეობების გაცნობა თქვენი მედიაკოლექციიდან"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"აპი შეძლებს მდებარეობების გაცნობას თქვენი მედიაკოლექციიდან."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"აპლიკაცია <xliff:g id="APP">%s</xliff:g> ითხოვს ავტორიზაციას."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ბიომეტრიული აპარატურა მიუწვდომელია"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"აღმოჩენილია თითის ნაწილობრივი ანაბეჭდი. გთხოვთ, სცადოთ ხელახლა."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"თითის ანაბეჭდი ვერ მუშავდება. გთხოვთ, სცადოთ ხელახლა."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other">ავტომატური შევსების <xliff:g id="COUNT">%1$s</xliff:g> შემოთავაზება</item>
       <item quantity="one">ავტომატური შევსების ერთი შემოთავაზება</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"გსურთ, შეინახოთ &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>-ში&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"გსურთ, შეინახოთ <xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>-ში&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"გსურთ, შეინახოთ <xliff:g id="TYPE_0">%1$s</xliff:g> და <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>-ში&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"გსურთ, შეინახოთ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, და <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>-ში&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"გსურთ, განაახლოთ &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;-ზე?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"გსურთ, განაახლოთ <xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;-ზე?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"გსურთ, განაახლოთ <xliff:g id="TYPE_0">%1$s</xliff:g> და <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;-ზე?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"გსურთ, განაახლოთ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> და <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;-ზე?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"გსურთ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"-ში შენახვა?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"გსურთ, "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"-ში შეინახოთ <xliff:g id="TYPE">%1$s</xliff:g>?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"გსურთ, "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"-ში შეინახოთ <xliff:g id="TYPE_0">%1$s</xliff:g> და <xliff:g id="TYPE_1">%2$s</xliff:g>?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"გსურთ, "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"-ში შეინახოთ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> და <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"გსურთ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"-ში განახლება?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"გსურთ, "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"-ში განაახლოთ <xliff:g id="TYPE">%1$s</xliff:g>?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"გსურთ, "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"-ში განაახლოთ <xliff:g id="TYPE_0">%1$s</xliff:g> და <xliff:g id="TYPE_1">%2$s</xliff:g>?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"გსურთ, "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"-ში განაახლოთ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> და <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"შენახვა"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"არა, გმადლობთ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"განახლება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index a8b527b..375aa82 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"аудио жазу"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына аудиомазмұн жазуға рұқсат берілсін бе?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Әрекетті тану"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"әрекетті тану"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына физикалық әрекетті тануға рұқсат етілсін бе?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"суретке түсіріп, бейне жазу"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына суретке түсіруге және бейне жазуға рұқсат берілсін бе?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Бұл қолданба орналасқан жеріңізді ұялы байланыс мұнаралары мен Wi-Fi желілері сияқты желі көздерінің негізінде анықтайды. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін олар қосулы әрі планшетте қолжетімді болуы керек."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Бұл қолданба орналасқан жеріңізді ұялы байланыс мұнаралары мен Wi-Fi желілері сияқты желі көздерінің негізінде анықтайды. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін олар қосулы әрі теледидарда қолжетімді болуы керек."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Бұл қолданба орналасқан жеріңізді ұялы байланыс мұнаралары мен Wi-Fi желілері сияқты желі көздерінің негізінде анықтайды. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін олар қосулы әрі телефонда қолжетімді болуы керек."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"геодеректерге фондық режимде кіру"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Бұл қолданба нақты орналасқан жеріңіз туралы ақпаратты фондық режимде де анықтай алады. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін, олар қосулы әрі телефонда қолжетімді болуы керек. Батарея көбірек тұтынылуы мүмкін."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"геодеректерді фондық режимде пайдалану"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Егер ол шамамен есептегендегі немесе нақты орынды пайдалануға рұқсат алса, қолданба фондық режимде жұмыс істеп тұрып-ақ геодеректерді пайдалана алады."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"аудио параметрлерін өзгерту"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Қолданбаға дыбыс қаттылығы және аудио шығыс үндеткішін таңдау сияқты жаһандық аудио параметрлерін өзгерту мүмкіндігін береді."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"аудио жазу"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Бұл қолданба кез келген уақытта микрофон арқылы аудиомазмұн жаза алады."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM картасына пәрмендер жіберу"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Қолданбаға SIM картасына пәрмен жіберу мүмкіндігін береді. Бұл өте қауіпті."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"физикалық әрекетті тану"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Бұл қолданба физикалық әрекетті тани алады."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"фотосурет жасау және бейне жазу"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Бұл қолданба кез келген уақытта камерамен суретке түсіруі және бейнелерді жазуы мүмкін."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"тербелісті басқару"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Қолданбаға суреттер жинағын өзгертуге мүмкіндік береді."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"медиамазмұн жинағынан геодеректерді оқу"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Қолданбаға медиамазмұн жинағынан геодеректерді оқуға мүмкіндік береді."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> қолданбасына аутентификация қажет."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрикалық жабдық жоқ"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Саусақ ізі ішінара анықталды. Әрекетті қайталаңыз."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Саусақ ізін өңдеу мүмкін емес. Әрекетті қайталаңыз."</string>
@@ -1898,14 +1904,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> автотолтыру ұсынысы</item>
       <item quantity="one">Бір автотолтыру ұсынысы</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"\"<xliff:g id="LABEL">%1$s</xliff:g>\" қызметінде сақталсын ба?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> \"<xliff:g id="LABEL">%2$s</xliff:g>\" қызметінде сақталсын ба?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> және <xliff:g id="TYPE_1">%2$s</xliff:g> \"<xliff:g id="LABEL">%3$s</xliff:g>\" қызметінде сақталсын ба?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> және <xliff:g id="TYPE_2">%3$s</xliff:g> \"<xliff:g id="LABEL">%4$s</xliff:g>\" қызметінде сақталсын ба?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; қызметінде жаңартылсын ба?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> мәліметі &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; қызметінде жаңартылсын ба?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> және <xliff:g id="TYPE_1">%2$s</xliff:g> мәліметі &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; қызметінде жаңартылсын ба?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> және <xliff:g id="TYPE_2">%3$s</xliff:g> мәліметі &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; қызметінде жаңартылсын ба?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" қызметіне сақталсын ба?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> деректері "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" қызметіне сақталсын ба?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> және <xliff:g id="TYPE_1">%2$s</xliff:g> деректері "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" қызметіне сақталсын ба?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> және <xliff:g id="TYPE_2">%3$s</xliff:g> деректері "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" қызметіне сақталсын ба?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" қызметінде жаңартылсын ба?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g> деректері "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" қызметінде жаңартылсын ба?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> және <xliff:g id="TYPE_1">%2$s</xliff:g> деректері "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" қызметінде жаңартылсын ба?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" қызметіндегі <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> және <xliff:g id="TYPE_2">%3$s</xliff:g> деректері жаңартылсын ба?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Сақтау"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Жоқ, рақмет"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Жаңарту"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 2620ce4..b9cf1eb 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"មីក្រូ​ហ្វូន"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ថតសំឡេង"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ថតសំឡេង?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"ការសម្គាល់​សកម្មភាព"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"ស្គាល់​សកម្មភាព"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ស្គាល់​សកម្មភាព​រាងកាយ​របស់អ្នក?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"កាមេរ៉ា"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ថតរូប និងថតវីដេអូ"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ថតរូប និងថត​វីដេអូ?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"កម្មវិធី​នេះ​អាច​ទទួល​បាន​ទីតាំង​របស់​អ្នក​ ដោយ​ផ្អែក​លើ​ប្រភព​បណ្តាញ​ ដូចជា​៖​ អង់តែន​ និង​បណ្តាញ​ Wi-Fi​ ។​ សេវាកម្ម​ទីតាំង​ទាំងនេះ​ត្រូវតែ​បើក​ និង​ត្រូវតែ​មាន​នៅ​លើ​ថេប្លេត​របស់​អ្នក ដើម្បី​ឲ្យ​កម្មវិធី​នេះ​អាច​ប្រើ​វា​បាន​។"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"កម្មវិធី​នេះ​អាច​ទទួល​បាន​ទីតាំង​របស់​អ្នក​ ដោយ​ផ្អែក​លើ​ប្រភព​បណ្តាញ​ ដូចជា​៖​ អង់តែន​ និង​បណ្តាញ​ Wi-Fi​ ។​ សេវាកម្ម​ទីតាំង​ទាំងនេះ​ត្រូវតែ​បើក​ និង​ត្រូវតែ​មាន​នៅ​លើ​ទូរទស្សន៍​របស់​អ្នក ដើម្បី​ឲ្យ​កម្មវិធី​នេះ​អាច​ប្រើ​វា​បាន​។"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"កម្មវិធី​នេះ​អាច​ទទួល​បាន​ទីតាំង​របស់​អ្នក​ ដោយ​ផ្អែក​លើ​ប្រភព​បណ្តាញ​ ដូចជា​៖​ អង់តែន​ និង​បណ្តាញ​ Wi-Fi​ ។​ សេវាកម្ម​ទីតាំង​ទាំងនេះ​ត្រូវតែ​បើក​ និង​ត្រូវតែ​មាន​នៅ​លើ​ទូរសព្ទ​របស់​អ្នក ដើម្បី​ឲ្យ​កម្មវិធី​នេះ​អាច​ប្រើ​វា​បាន​។"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ចូលប្រើ​ទីតាំង​ជាក់លាក់​នៅផ្ទៃ​ខាងក្រោយ"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"កម្មវិធីនេះ​អាចទទួល​បានទីតាំង​ពិតប្រាកដ​របស់អ្នក​គ្រប់ពេល​ដែលវា​ស្ថិតនៅ​ផ្ទៃខាងក្រោយ។ សេវាកម្ម​ទីតាំង​ទាំងនេះ​ត្រូវតែ​បើក និងមាន​នៅលើ​ទូរសព្ទ​របស់អ្នក ដើម្បីឱ្យ​កម្មវិធី​នេះអាចប្រើ​ពួកវាបាន។ វាអាចប្រើ​ថាមពល​ច្រើន​ជាងមុន។"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"ចូល​ប្រើ​ទីតាំង​នៅ​ផ្ទៃខាងក្រោយ"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"ប្រសិនបើ​ផ្ដល់​ការអនុញ្ញាត​នេះ​បន្ថែម​ពីលើ​ការចូលប្រើទីតាំងជាក់លាក់ ឬប្រហាក់ប្រហែល កម្មវិធី​នឹងអាចចូលប្រើទីតាំងនោះ ខណៈពេលដំណើរការនៅផ្ទៃខាងក្រោយ។"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ប្ដូរ​ការ​កំណត់​អូឌីយូ​របស់​អ្នក"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ឲ្យ​កម្មវិធី​កែ​ការ​កំណត់​សំឡេង​សកល ដូច​ជា​កម្រិត​សំឡេង និង​អូប៉ាល័រ​ដែល​បាន​ប្រើ​សម្រាប់​លទ្ធផល។"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ថត​សំឡេង"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"កម្មវិធី​នេះ​អាច​ថត​សំឡេង​ដោយ​ប្រើ​មីក្រូហ្វូន​បាន​គ្រប់​ពេល។"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"ផ្ញើពាក្យបញ្ជាទៅស៊ីមកាត"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"ឲ្យ​កម្មវិធី​ផ្ញើ​ពាក្យ​បញ្ជា​ទៅ​ស៊ីម​កាត។ វា​គ្រោះ​ថ្នាក់​ណាស់។"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"ស្គាល់​សកម្មភាព​រាងកាយ"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"កម្មវិធីនេះ​អាចស្គាល់សកម្មភាព​រាងកាយ​របស់អ្នក។"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ថត​រូប និងវីដេអូ"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"កម្មវិធី​នេះ​អាច​ថត​រូប​ និង​ថត​វីដេអូ​ ដោយ​ប្រើ​កាមេរ៉ា​បាន​គ្រប់​ពេល​។"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ពិនិត្យ​ការ​ញ័រ"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"អនុញ្ញាតឱ្យ​កម្មវិធី​កែប្រែ​បណ្ដុំ​រូបថត​របស់​អ្នក។"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"អាន​ទីតាំង​ពី​បណ្ដុំ​មេឌៀ​របស់​អ្នក"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"អនុញ្ញាតឱ្យ​កម្មវិធី​អាន​ទីតាំង​ពីបណ្ដុំ​មេឌៀ​របស់​អ្នក។"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"កម្មវិធី <xliff:g id="APP">%s</xliff:g> ចង់​ធ្វើ​កា​រផ្ទៀងផ្ទាត់។"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"មិនអាច​ប្រើឧបករណ៍​ស្កេន​ស្នាមម្រាមដៃ​បានទេ"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"បានផ្តិតយកស្នាមម្រាមដៃមិនពេញលក្ខណៈ។ សូមព្យាយាមម្តងទៀត។"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
@@ -1899,14 +1905,14 @@
       <item quantity="other">ការណែនាំអំពីការបំពេញដោយស្វ័យប្រវត្តិ <xliff:g id="COUNT">%1$s</xliff:g></item>
       <item quantity="one">ការណែនាំអំពីការបំពេញដោយស្វ័យប្រវត្តិមួយ</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"រក្សាទុក​ទៅក្នុង &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"រក្សាទុក <xliff:g id="TYPE">%1$s</xliff:g> ទៅក្នុង &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"រក្សាទុក <xliff:g id="TYPE_0">%1$s</xliff:g> និង <xliff:g id="TYPE_1">%2$s</xliff:g> ទៅក្នុង &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"រក្សាទុក <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, និង <xliff:g id="TYPE_2">%3$s</xliff:g> ទៅក្នុង &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"ធ្វើ​បច្ចុប្បន្នភាព​ទៅ &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"ធ្វើ​បច្ចុប្បន្នភាព <xliff:g id="TYPE">%1$s</xliff:g> ទៅ &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"ធ្វើ​បច្ចុប្បន្នភាព <xliff:g id="TYPE_0">%1$s</xliff:g> និង <xliff:g id="TYPE_1">%2$s</xliff:g> ទៅ &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"ធ្វើ​បច្ចុប្បន្នភាព <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> និង <xliff:g id="TYPE_2">%3$s</xliff:g> ទៅ &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"រក្សាទុក​ទៅក្នុង "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"រក្សាទុក <xliff:g id="TYPE">%1$s</xliff:g> ទៅក្នុង "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"រក្សាទុក​ <xliff:g id="TYPE_0">%1$s</xliff:g> និង <xliff:g id="TYPE_1">%2$s</xliff:g> ទៅ​ក្នុង "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"រក្សាទុក​ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> និង <xliff:g id="TYPE_2">%3$s</xliff:g> ទៅក្នុង "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"ធ្វើ​បច្ចុប្បន្នភាព​នៅក្នុង "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"ធ្វើ​បច្ចុប្បន្នភាព <xliff:g id="TYPE">%1$s</xliff:g> នៅក្នុង "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"ធ្វើ​បច្ចុប្បន្នភាព​ <xliff:g id="TYPE_0">%1$s</xliff:g> និង <xliff:g id="TYPE_1">%2$s</xliff:g> នៅក្នុង "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"ធ្វើ​បច្ចុប្បន្នភាព​ធាតុ​ទាំងនេះ​នៅក្នុង "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> និង <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"រក្សាទុក"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"ទេ អរគុណ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"ធ្វើ​បច្ចុប្បន្នភាព"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 1b83ec2..2a480cd 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"ಮೈಕ್ರೋಫೋನ್‌"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"ಆಡಿಯೋ ರೆಕಾರ್ಡ್‌ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"ಚಟುವಟಿಕೆಯನ್ನು ಗುರುತಿಸುವಿಕೆ"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"ಚಟುವಟಿಕೆಯನ್ನು ಗುರುತಿಸಿ"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"ನಿಮ್ಮ ದೈಹಿಕ ಚಟುವಟಿಕೆಯನ್ನು ಗುರುತಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸುವುದೇ?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"ಕ್ಯಾಮರಾ"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ಚಿತ್ರಗಳನ್ನು ತೆಗೆಯಲು, ವೀಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಲು"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"ಚಿತ್ರಗಳನ್ನು ಸೆರೆಹಿಡಿಯಲು ಮತ್ತು ವೀಡಿಯೊ ರೆಕಾರ್ಡ್‌ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಸೆಲ್ ಟವರ್‌ಗಳು ಮತ್ತು ವೈ-ಫೈ ನೆಟ್‍‍ವರ್ಕ್‌ನಂತಹ ನೆಟ್‍‍ವರ್ಕ್ ಮೂಲಗಳ ಆಧಾರದ ಮೇಲೆ ಪಡೆಯಬಹುದು. ಈ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಸಾಧ್ಯವಾಗುವಂತೆ ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್‌ನಲ್ಲಿ ಅವುಗಳು ಲಭ್ಯವಿರಬೇಕು."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಸೆಲ್ ಟವರ್‌ಗಳು ಮತ್ತು ವೈ-ಫೈ ನೆಟ್‍‍ವರ್ಕ್‌ನಂತಹ ನೆಟ್‍‍ವರ್ಕ್ ಮೂಲಗಳ ಆಧಾರದ ಮೇಲೆ ಪಡೆಯಬಹುದು. ಈ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಸಾಧ್ಯವಾಗುವಂತೆ ನಿಮ್ಮ ಟಿವಿಯಲ್ಲಿ ಅವುಗಳು ಲಭ್ಯವಿರಬೇಕು."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಸೆಲ್ ಟವರ್‌ಗಳು ಮತ್ತು ವೈ-ಫೈ ನೆಟ್‍‍ವರ್ಕ್‌ನಂತಹ ನೆಟ್‍‍ವರ್ಕ್ ಮೂಲಗಳ ಆಧಾರದ ಮೇಲೆ ಪಡೆಯಬಹುದು. ಈ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಸಾಧ್ಯವಾಗುವಂತೆ ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿ ಅವುಗಳು ಲಭ್ಯವಿರಬೇಕು."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ಹಿನ್ನೆಲೆಯಲ್ಲಿ ನಿಖರ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಹಿನ್ನೆಲೆಯಲ್ಲಿದೆಯಾದರೂ ನಿಮ್ಮ ಸರಿಯಾದ ಸ್ಥಳವನ್ನು ಪಡೆಯಬಹುದು. ಈ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಸಾಧ್ಯವಾಗುವಂತೆ ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿ ಅವುಗಳು ಲಭ್ಯವಿರಬೇಕು."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"ಹಿನ್ನೆಲೆಯಲ್ಲಿ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"ಇದನ್ನು ಅಂದಾಜು ಅಥವಾ ನಿಖರವಾದ ಸ್ಥಳ ಪ್ರವೇಶಕ್ಕೆ ಹೆಚ್ಚುವರಿಯಾಗಿ ಅನುಮತಿಸಿದರೆ, ಆ್ಯಪ್ ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗುತ್ತಿರುವಾಗ ಅದು ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಬಹುದು."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ನಿಮ್ಮ ಆಡಿಯೊ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ವಾಲ್ಯೂಮ್ ರೀತಿಯ ಮತ್ತು ಔಟ್‍‍ಪುಟ್‍‍ಗಾಗಿ ಯಾವ ಸ್ಪೀಕರ್ ಬಳಸಬೇಕು ಎಂಬ ರೀತಿಯ ಜಾಗತಿಕ ಆಡಿಯೊ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಮೈಕ್ರೋಫೋನ್ ಬಳಸುವ ಮೂಲಕ ಯಾವುದೇ ಸಮಯದಲ್ಲಾದರೂ ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಬಹುದು."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"ಸಿಮ್‌ಗೆ ಆಜ್ಞೆಗಳನ್ನು ಕಳುಹಿಸಿ"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"ಸಿಮ್‌ ಗೆ ಆದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಇದು ತುಂಬಾ ಅಪಾಯಕಾರಿ."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"ದೈಹಿಕ ಚಟುವಟಿಕೆಯನ್ನು ಗುರುತಿಸಿ"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"ಈ ಆ್ಯಪ್‌ ನಿಮ್ಮ ದೈಹಿಕ ಚಟುವಟಿಕೆಯನ್ನು ಗುರುತಿಸಬಹುದು."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ಚಿತ್ರಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ಸೆರೆಹಿಡಿಯಿರಿ"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಯಾವ ಸಮಯದಲ್ಲಾದರೂ ಕ್ಯಾಮರಾ ಬಳಸಿಕೊಂಡು ಚಿತ್ರಗಳು ಮತ್ತು ವಿಡಿಯೋಗಳನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಬಹುದು."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ವೈಬ್ರೇಷನ್‌‌ ನಿಯಂತ್ರಿಸಿ"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"ನಿಮ್ಮ ಫೋಟೋ ಸಂಗ್ರಹಣೆಯನ್ನು ಮಾರ್ಪಡಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"ನಿಮ್ಮ ಮೀಡಿಯಾ ಸಂಗ್ರಹಣೆಯಿಂದ ಸ್ಥಳಗಳನ್ನು ಓದಿ"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"ನಿಮ್ಮ ಮೀಡಿಯಾ ಸಂಗ್ರಹಣೆಯಿಂದ ಸ್ಥಳಗಳನ್ನು ಓದಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> ಆ್ಯಪ್‌ಗೆ ದೃಢೀಕರಣದ ಅಗತ್ಯವಿದೆ."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್‌ವೇರ್‌ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ಭಾಗಶಃ ಬೆರಳಚ್ಚು ಪತ್ತೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ಬೆರಳಚ್ಚು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
@@ -1898,14 +1904,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> ಸ್ವಯಂಭರ್ತಿ ಸಲಹೆಗಳು</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> ಸ್ವಯಂಭರ್ತಿ ಸಲಹೆಗಳು</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;ನಲ್ಲಿ ಉಳಿಸುವುದೇ?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> ಅನ್ನು &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;ನಲ್ಲಿ ಉಳಿಸುವುದೇ?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> ಹಾಗೂ <xliff:g id="TYPE_1">%2$s</xliff:g> ಅನ್ನು &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;ನಲ್ಲಿ ಉಳಿಸುವುದೇ?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, ಹಾಗೂ <xliff:g id="TYPE_2">%3$s</xliff:g> ಅನ್ನು &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;ನಲ್ಲಿ ಉಳಿಸುವುದೇ?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;ಗೆ ಅಪ್‌ಡೇಟ್‌ ಮಾಡುವುದೇ?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> ಅನ್ನು &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;ಗೆ ಅಪ್‌ಡೇಟ್‌ ಮಾಡುವುದೇ?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> ಹಾಗೂ <xliff:g id="TYPE_1">%2$s</xliff:g> ಅನ್ನು &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;ಗೆ ಅಪ್‌ಡೇಟ್‌ ಮಾಡುವುದೇ?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, ಹಾಗೂ <xliff:g id="TYPE_2">%3$s</xliff:g> ಅನ್ನು &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;ಗೆ ಅಪ್‌ಡೇಟ್‌ ಮಾಡುವುದೇ?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ನಲ್ಲಿ ಉಳಿಸುವುದೇ?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> ಅನ್ನು "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ನಲ್ಲಿ ಉಳಿಸುವುದೇ?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> ಮತ್ತು <xliff:g id="TYPE_1">%2$s</xliff:g> ಅನ್ನು "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ನಲ್ಲಿ ಉಳಿಸುವುದೇ?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ಮತ್ತು <xliff:g id="TYPE_2">%3$s</xliff:g> ಅನ್ನು "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ನಲ್ಲಿ ಉಳಿಸುವುದೇ?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ನಲ್ಲಿ ಅಪ್‌ಡೇಟ್ ಮಾಡುವುದೇ?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g> ಅನ್ನು "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ನಲ್ಲಿ ಅಪ್‌ಡೇಟ್ ಮಾಡುವುದೇ?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> ಮತ್ತು <xliff:g id="TYPE_1">%2$s</xliff:g> ಅನ್ನು "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ನಲ್ಲಿ ಅಪ್‌ಡೇಟ್ ಮಾಡುವುದೇ?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"ಈ ಮುಂದಿನ ಐಟಂಗಳನ್ನು "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ನಲ್ಲಿ ಅಪ್‌ಡೇಟ್ ಮಾಡುವುದೇ: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ಮತ್ತು <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"ಉಳಿಸಿ"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"ಬೇಡ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"ಅಪ್‌ಡೇಟ್"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 6223dab..b66cbf4 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"마이크"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"오디오 녹음"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 오디오를 녹음하도록 허용하시겠습니까?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"활동 감지"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"활동 확인"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 내 신체 활동을 확인하도록 허용하시겠습니까?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"카메라"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"사진 및 동영상 촬영"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 사진을 촬영하고 동영상을 녹화하도록 허용하시겠습니까?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"이 앱은 기지국 및 Wi-Fi 네트워크와 같은 네트워크 소스를 통해 내 위치를 알 수 있습니다. 앱에서 위치 서비스를 사용하려면 태블릿에서 위치 서비스가 사용 설정되어 있으며 사용 가능해야 합니다."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"이 앱은 기지국 및 Wi-Fi 네트워크와 같은 네트워크 소스를 통해 내 위치를 알 수 있습니다. 앱에서 위치 서비스를 사용하려면 TV에서 위치 서비스가 사용 설정되어 있으며 사용 가능해야 합니다."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"이 앱은 기지국 및 Wi-Fi 네트워크와 같은 네트워크 소스를 통해 내 위치를 알 수 있습니다. 앱에서 위치 서비스를 사용하려면 휴대전화에서 위치 서비스가 사용 설정되어 있으며 사용 가능해야 합니다."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"백그라운드에서 정확한 위치에 액세스"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"이 앱은 백그라운드에서 언제든지 나의 정확한 위치를 알 수 있습니다. 앱에서 위치 서비스를 사용하려면 휴대전화에서 위치 서비스가 사용 설정되어 있으며 사용할 수 있어야 합니다. 배터리 사용량이 늘어날 수 있습니다."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"백그라운드에서 위치 정보 액세스"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"이 권한이 대략적인 위치 정보 또는 정확한 위치 정보 액세스 권한에 추가적으로 부여되면 앱이 백그라운드에서 실행되는 동안 위치에 액세스할 수 있습니다."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"오디오 설정 변경"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"앱이 음량이나 출력을 위해 사용하는 스피커 등 전체 오디오 설정을 변경할 수 있도록 허용합니다."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"오디오 녹음"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"이 앱은 언제든지 마이크를 사용하여 오디오를 녹음할 수 있습니다."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM 카드로 명령 전송"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"앱이 SIM에 명령어를 전송할 수 있도록 허용합니다. 이 기능은 매우 신중히 허용해야 합니다."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"신체 활동 확인"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"이 앱에서 내 신체 활동을 확인할 수 있습니다."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"사진과 동영상 찍기"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"이 앱은 언제든지 카메라를 사용하여 사진을 촬영하고 동영상을 녹화할 수 있습니다."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"진동 제어"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"앱에서 사진 컬렉션을 수정하도록 허용합니다."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"미디어 컬렉션에서 위치 읽기"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"앱에서 미디어 컬렉션의 위치를 읽도록 허용합니다."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> 애플리케이션에서 인증을 요청합니다"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"생체 인식 하드웨어를 사용할 수 없음"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"지문이 일부만 인식되었습니다. 다시 시도해 주세요."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"지문을 인식할 수 없습니다. 다시 시도해 주세요."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other">자동완성 추천 <xliff:g id="COUNT">%1$s</xliff:g>개</item>
       <item quantity="one">자동완성 추천 1개</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;에 저장하시겠습니까?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g>을(를) &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;에 저장하시겠습니까?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> 및 <xliff:g id="TYPE_1">%2$s</xliff:g>을(를) &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;에 저장하시겠습니까?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g>을(를) &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;에 저장하시겠습니까?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;(으)로 업데이트하시겠습니까?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g>을(를) &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;(으)로 업데이트하시겠습니까?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> 및 <xliff:g id="TYPE_1">%2$s</xliff:g>을(를) &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;(으)로 업데이트하시겠습니까?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g>을(를) &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;(으)로 업데이트하시겠습니까?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"에 저장하시겠습니까?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g>을(를) "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"에 저장하시겠습니까?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> 및 <xliff:g id="TYPE_1">%2$s</xliff:g>을(를) "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"에 저장하시겠습니까?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> 및 <xliff:g id="TYPE_2">%3$s</xliff:g>을(를) "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"에 저장하시겠습니까?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"에서 업데이트하시겠습니까?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"에서 <xliff:g id="TYPE">%1$s</xliff:g>을(를) 업데이트하시겠습니까?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"에서 <xliff:g id="TYPE_0">%1$s</xliff:g> 및 <xliff:g id="TYPE_1">%2$s</xliff:g>을(를) 업데이트하시겠습니까?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"에서 <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> 및 <xliff:g id="TYPE_2">%3$s</xliff:g> 업데이트"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"저장"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"사용 안함"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"업데이트"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index f49746f..de3bcd2 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"аудио жаздыруу"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна аудио файлдарды жаздырууга уруксат берилсинби?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Кыймыл-аракетти аныктоо"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"кыймыл-аракетти аныктоо"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна кыймыл-аракетиңизди аныктоого уруксат бересизби?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"сүрөт жана видео тартууга"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна сүрөттөрдү тартып, видеолорду жаздырууга уруксат берилсинби?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Бул колдонмо байланыш мунаралары жана Wi-Fi сыяктуу тармактык булактар аркылуу жайгашкан жериңизди аныктай алат. Колдонмо бул кызматтарды пайдаланышы үчүн, аларды күйгүзүп, планшетиңизге туташтырып коюшуңуз керек."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Бул колдонмо байланыш мунаралары жана Wi-Fi сыяктуу тармактык булактар аркылуу жайгашкан жериңизди аныктай алат. Колдонмо бул кызматтарды пайдаланышы үчүн, аларды күйгүзүп, сыналгыңызга туташтырып коюшуңуз керек."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Бул колдонмо байланыш мунаралары жана Wi-Fi сыяктуу тармактык булактар аркылуу жайгашкан жериңизди аныктай алат. Колдонмо бул кызматтарды пайдаланышы үчүн, аларды күйгүзүп, телефонуңузга туташтырып коюшуңуз керек."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"так аныкталган жайгашкан жерге фондук режимде кирүүгө уруксат берүү"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Бул колдонмо фондук режимде сиздин кайда жүргөнүңүздү көрүп турат. Ал үчүн жайгашкан жерди аныктоо функциясын иштетишиңиз керек. Эскерте кетүүчү нерсе, батареяңыз тез отуруп калышы мүмкүн."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"жайгашкан жерди фондо аныктоо"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Эгер колдонмого жайгашкан жерди болжолдуу же так аныктоого уруксат берилсе, ал фондо иштеп жатып эле жайгашкан жерди аныктап турат."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"аудио жөндөөлөрүңүздү өзгөртүңүз"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Колдонмого үн деңгээли жана кайсы динамик аркылуу үн чыгарылышы керек сыяктуу түзмөктүн аудио тууралоолорун өзгөртүүгө уруксат берет."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"аудио жаздыруу"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Бул колдонмо каалаган убакта микрофон менен аудио файлдарды жаздыра алат."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM-картага буйруктарды жөнөтүү"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Колдонмого SIM-картага буйруктарды жөнөтүү мүмкүнчүлүгүн берет. Бул абдан кооптуу."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"кыймыл-аракетти аныктоо"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Бул колдонмо кыймыл-аракетиңизди аныктап турат."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"сүрөт жана видео тартуу"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Бул колдонмо каалаган убакта камера менен сүрөт же видеолорду тарта алат."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"титирөөнү башкаруу"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Колдонмого сүрөт жыйнагыңызды өзгөртүүгө мүмкүнчүлүк берет."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"медиа жыйнагыңыз сакталган жерлерди окуу"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Колдонмого медиа жыйнагыңыз сакталган жерлерди окууга мүмкүнчүлүк берет."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> колдонмосунда аутентификациядан өтүңүз."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрикалык аппарат жеткиликсиз"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Манжа изи жарым-жартылай аныкталды. Кайра аракет кылыңыз."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Манжа изи иштелбей койду. Кайра аракет кылыңыз."</string>
@@ -1899,14 +1905,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> автотолтуруу сунушу бар</item>
       <item quantity="one">Бир автотолтуруу сунушу бар</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; кызматында сакталсынбы?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; кызматында сакталсынбы?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> жана <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; кызматында сакталсынбы?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> жана <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; кызматында сакталсынбы?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; кызматына жаңыртылсынбы?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; кызматына жаңыртылсынбы?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> жана <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; кызматына жаңыртылсынбы?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> жана <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; кызматына жаңыртылсынбы?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" кызматына сакталсынбы?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" кызматына сакталсынбы?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> жана <xliff:g id="TYPE_1">%2$s</xliff:g> "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" кызматына сакталсынбы?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> жана <xliff:g id="TYPE_2">%3$s</xliff:g> "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" кызматына сакталсынбы?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" кызматында жаңыртылсынбы?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" кызматындагы <xliff:g id="TYPE">%1$s</xliff:g> жаңыртылсын?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" кызматындагы <xliff:g id="TYPE_0">%1$s</xliff:g> жана <xliff:g id="TYPE_1">%2$s</xliff:g> жаңыртылсынбы?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" кызматындагы төмөнкүлөр жаңыртылсынбы: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> жана <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Сактоо"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Жок, рахмат"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Жаңыртуу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 9fc6033..9fb0adb 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"ໄມໂຄຣໂຟນ"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ບັນທຶກສຽງ"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"ອະນຸຍາດ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ໃຫ້ບັນທຶກສຽງບໍ?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"ການຈຳແນກການເຄື່ອນໄຫວ"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"ຈຳແນກການເຄື່ອນໄຫວ"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ຈຳແນກກິດຈະກຳທາງກາຍຂອງທ່ານບໍ?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"ກ້ອງ"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ຖ່າຍ​ຮູບ ແລະ​ບັນ​ທຶກວິ​ດີ​ໂອ"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"ອະນຸຍາດ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ໃຫ້ຖ່າຍຮູບ ແລະ ບັນທຶກວິດີໂອບໍ?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ເຂົ້າເຖິງສະຖານທີ່ແນ່ນອນໃນພື້ນຫຼັງ"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ແອັບນີ້ສາມາດຮັບເອົາສະຖານທີ່ແນ່ນອນຂອງທ່ານໄດ້ທຸກເວລາທີ່ມັນຢູ່ໃນພື້ນຫຼັງ. ການບໍລິການສະຖານທີ່ເຫຼົ່ານີ້ຕ້ອງເປີດຢູ່ ແລະ ສາມາດໃຊ້ໄດ້ໃນໂທລະສັບຂອງທ່ານເພື່ອໃຫ້ແອັບສາມາດໃຊ້ພວກມັນໄດ້. ນີ້ອາດຈະເຮັດໃຫ້ການໃຊ້ແບັດເຕີຣີເພີ່ມຂຶ້ນ."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"ເຂົ້າເຖິງສະຖານທີ່ໃນພື້ນຫຼັງ"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"ຖ້າອະນຸຍາດສິ່ງນີ້ນອກຈາກການເຂົ້າເຖິງສະຖານທີ່ໂດຍປະມານ ຫຼື ທີ່ແນ່ນອນ ແອັບສາມາດເຂົ້າເຖິງສະຖານທີ່ໄດ້ໃນຂະນະທີ່ເປີດໃຊ້ໃນພື້ນຫຼັງ."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ປ່ຽນການຕັ້ງຄ່າສຽງຂອງທ່ານ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ອະນຸຍາດໃຫ້ແອັບຯແກ້ໄຂການຕັ້ງຄ່າສຽງສ່ວນກາງ ເຊັ່ນ: ລະດັບສຽງ ແລະລຳໂພງໃດທີ່ຖືກໃຊ້ສົ່ງສຽງອອກ."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ບັນທຶກສຽງ"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"This app can record audio using the microphone at any time."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"ສົ່ງ​ຄຳ​ສັ່ງ​ຫາ SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"ອະນຸຍາດໃຫ້ແອັບຯສົ່ງຄຳສັ່ງຫາ SIM. ສິ່ງນີ້ອັນຕະລາຍຫຼາຍ."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"ຈຳແນກກິດຈະກຳທາງກາຍ"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"ແອັບນີ້ສາມາດຈັດລະບຽບການເຄື່ອນໄຫວທາງກາຍຂອງທ່ານໄດ້."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ຖ່າຍຮູບ ແລະວິດີໂອ"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"This app can take pictures and record videos using the camera at any time."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ຄວບຄຸມການສັ່ນ"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"ອະນຸຍາດໃຫ້ແອັບແກ້ໄຂຄໍເລັກຊັນຮູບຂອງທ່ານ."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"ອ່ານສະຖານທີ່ຈາກຄໍເລັກຊັນມີເດຍຂອງທ່ານ"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"ອະນຸຍາດໃຫ້ແອັບອ່ານສະຖານທີ່ຈາກຄໍເລັກຊັນມີເດຍຂອງທ່ານ."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"ແອັບພລິເຄຊັນ <xliff:g id="APP">%s</xliff:g> ຕ້ອງການກວດຮັບຮອງຄວາມຖືກຕ້ອງ."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ຮາດແວຊີວະມິຕິບໍ່ສາມາດໃຊ້ໄດ້"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ກວດ​ພົບ​ລາຍ​ນີ້ວ​ມື​ບາງ​ສ່ວນ​ແລ້ວ. ກະ​ລຸ​ນາ​ລອງ​ໃໝ່​ອີກ."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ບໍ່​ສາ​ມາດ​ດຳ​ເນີນ​ການ​ລາຍ​ນີ້ວ​ມື​ໄດ້. ກະ​ລຸ​ນາ​ລອງ​ໃໝ່​ອີກ."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other">ການແນະນຳແບບຕື່ມຂໍ້ມູນແບບອັນຕະໂນມັດ <xliff:g id="COUNT">%1$s</xliff:g> ອັນ</item>
       <item quantity="one">ການແນະນຳແບບຕື່ມຂໍ້ມູນແບບອັນຕະໂນມັດອັນດຽວ</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"ບັນທຶກໃສ່ &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ບໍ?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"ບັນທຶກ <xliff:g id="TYPE">%1$s</xliff:g> ໃສ່ &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ບໍ?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"ບັນທຶກ <xliff:g id="TYPE_0">%1$s</xliff:g> ແລະ <xliff:g id="TYPE_1">%2$s</xliff:g> ໃສ່ &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ບໍ?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"ບັນທຶກ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ແລະ <xliff:g id="TYPE_2">%3$s</xliff:g> ໃສ່ &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ບໍ?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"ອັບເດດ &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ບໍ?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"ອັບເດດ <xliff:g id="TYPE">%1$s</xliff:g> ໃສ່ &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ບໍ?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"ອັບເດດ <xliff:g id="TYPE_0">%1$s</xliff:g> ແລະ <xliff:g id="TYPE_1">%2$s</xliff:g> ໃສ່ &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ບໍ?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"ອັບເດດ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ແລະ <xliff:g id="TYPE_2">%3$s</xliff:g> ໃສ່ &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ບໍ?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"ບັນທຶກໄວ້ໃນ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ບໍ?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"ບັນທຶກ <xliff:g id="TYPE">%1$s</xliff:g> ໄວ້ໃນ "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ບໍ?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"ບັນທຶກ <xliff:g id="TYPE_0">%1$s</xliff:g> ແລະ <xliff:g id="TYPE_1">%2$s</xliff:g> ໄວ້ໃນ "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ບໍ?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"ບັນທຶກ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ແລະ <xliff:g id="TYPE_2">%3$s</xliff:g> ໄວ້ໃນ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ບໍ?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"ອັບເດດໃນ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ບໍ?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"ອັບເດດ <xliff:g id="TYPE">%1$s</xliff:g> ໃນ "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ບໍ?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"ອັບເດດ <xliff:g id="TYPE_0">%1$s</xliff:g> ແລະ <xliff:g id="TYPE_1">%2$s</xliff:g> ໃນ "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ບໍ?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"ອັບເດດລາຍການເຫຼົ່ານີ້ໃນ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ແລະ <xliff:g id="TYPE_2">%3$s</xliff:g> ບໍ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"ບັນທຶກ"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"ບໍ່, ຂອບໃຈ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"ອັບເດດ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index f1dc8d1..f5225c5 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -298,6 +298,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofonas"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"įrašyti garso įrašą"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; įrašyti garsą?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Veiklos atpažinimas"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"atpažinti veiklą"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; atpažinti jūsų fizinę veiklą?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparatas"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotografuoti ir filmuoti"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotografuoti ir įrašyti vaizdo įrašus?"</string>
@@ -424,14 +427,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ši programa gali gauti jūsų vietos informaciją naudodamasi tinklo šaltinių, pvz., mobiliojo ryšio bokštų ir „Wi-Fi“ tinklų, duomenimis. Šios vietovės paslaugos turi būti įjungtos ir pasiekiamos planšetiniame kompiuteryje, kad programa galėtų jas naudoti."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ši programa gali gauti jūsų vietos informaciją naudodamasi tinklo šaltinių, pvz., mobiliojo ryšio bokštų ir „Wi-Fi“ tinklų, duomenimis. Šios vietovės paslaugos turi būti įjungtos ir pasiekiamos TV, kad programa galėtų jas naudoti."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ši programa gali gauti jūsų vietos informaciją naudodamasi tinklo šaltinių, pvz., mobiliojo ryšio bokštų ir „Wi-Fi“ tinklų, duomenimis. Šios vietovės paslaugos turi būti įjungtos ir pasiekiamos telefone, kad programa galėtų jas naudoti."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"pasiekti tikslią vietovę, kai programa veikia fone"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ši programa gali gauti tikslius jūsų vietovės duomenis bet kuriuo metu, kai veikia fone. Šios vietovės paslaugos turi būti įjungtos ir pasiekiamos telefone, kad programa galėtų jas naudoti. Tai gali padidinti akumuliatoriaus energijos suvartojimą."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"prieiga prie vietovės fone"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Jei papildomai suteikiama prieiga prie apytikslės arba tikslios vietovės, programa gali pasiekti vietovės duomenis veikdama fone."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"keisti garso nustatymus"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Leidžiama programai keisti visuotinius garso nustatymus, pvz., garsumą ir tai, kuris garsiakalbis naudojamas išvesčiai."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"įrašyti garsą"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Ši programa gali bet kada įrašyti garsą naudodama mikrofoną."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"siųsti komandas į SIM kortelę"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Programai leidžiama siųsti komandas į SIM kortelę. Tai labai pavojinga."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"atpažinti fizinę veiklą"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Ši programa gali atpažinti jūsų fizinę veiklą."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"fotografuoti ir filmuoti"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Ši programa gali bet kada fotografuoti ir įrašyti vaizdo įrašų naudodama fotoaparatą."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"valdyti vibraciją"</string>
@@ -524,6 +529,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Programai leidžiama keisti nuotraukų kolekciją."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"skaityti vietoves iš medijos kolekcijos"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Programai leidžiama skaityti vietoves iš medijos kolekcijos."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Programa „<xliff:g id="APP">%s</xliff:g>“ nori jus autentifikuoti."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrinė aparatinė įranga nepasiekiama"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Aptiktas dalinis piršto antspaudas. Bandykite dar kartą."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nepavyko apdoroti piršto antspaudo. Bandykite dar kartą."</string>
@@ -1967,14 +1973,14 @@
       <item quantity="many"><xliff:g id="COUNT">%1$s</xliff:g> automatinio pildymo pasiūlymo</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> automatinio pildymo pasiūlymų</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Išsaugoti sistemoje &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Išsaugoti <xliff:g id="TYPE">%1$s</xliff:g> sistemoje &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Išsaugoti <xliff:g id="TYPE_0">%1$s</xliff:g> ir <xliff:g id="TYPE_1">%2$s</xliff:g> sistemoje &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Išsaugoti <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ir <xliff:g id="TYPE_2">%3$s</xliff:g> sistemoje &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Atnaujinti paslaugoje &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Atnaujinti <xliff:g id="TYPE">%1$s</xliff:g> paslaugoje &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Atnaujinti <xliff:g id="TYPE_0">%1$s</xliff:g> ir <xliff:g id="TYPE_1">%2$s</xliff:g> paslaugoje &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Atnaujinti <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ir <xliff:g id="TYPE_2">%3$s</xliff:g> paslaugoje &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Išsaugoti paslaugoje "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Išsaugoti <xliff:g id="TYPE">%1$s</xliff:g> paslaugoje "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Išsaugoti <xliff:g id="TYPE_0">%1$s</xliff:g> ir <xliff:g id="TYPE_1">%2$s</xliff:g> paslaugoje "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Išsaugoti <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ir <xliff:g id="TYPE_2">%3$s</xliff:g> paslaugoje "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Atnaujinti paslaugoje "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Atnaujinti <xliff:g id="TYPE">%1$s</xliff:g> paslaugoje "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Atnaujinti <xliff:g id="TYPE_0">%1$s</xliff:g> ir <xliff:g id="TYPE_1">%2$s</xliff:g> paslaugoje "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Atnaujinti šiuos elementus paslaugoje "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ir <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Išsaugoti"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Ne, ačiū"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Atnaujinti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 156a1a9..d0c16d8 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -222,7 +222,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Tālruņa opcijas"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekrāna bloķētājs"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Strāvas padeve ir izslēgta."</string>
-    <string name="global_action_emergency" msgid="7112311161137421166">"Ārkārtas"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Ārkārtas situācija"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Kļūdu ziņojums"</string>
     <string name="global_action_logout" msgid="935179188218826050">"Beigt sesiju"</string>
     <string name="global_action_screenshot" msgid="8329831278085426283">"Ekrānuzņēmums"</string>
@@ -295,6 +295,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofons"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ierakstīt audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ierakstīt audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Aktivitātes noteikšana"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"noteikt aktivitātes"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; noteikt jūsu fiziskās aktivitātes?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"uzņemt attēlus un ierakstīt videoklipus"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uzņemt fotoattēlus un ierakstīt videoklipus?"</string>
@@ -421,14 +424,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Šī lietotne var iegūt jūsu atrašanās vietu, pamatojoties uz tīkla avotiem, piemēram, mobilo sakaru torņiem un Wi-Fi tīkliem. Šiem atrašanās vietu pakalpojumiem ir jābūt ieslēgtiem un pieejamiem jūsu planšetdatorā, lai lietotne tos varētu izmantot."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Šī lietotne var iegūt jūsu atrašanās vietu, pamatojoties uz tīkla avotiem, piemēram, mobilo sakaru torņiem un Wi-Fi tīkliem. Šiem atrašanās vietu pakalpojumiem ir jābūt ieslēgtiem un pieejamiem jūsu televizorā, lai lietotne tos varētu izmantot."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Šī lietotne var iegūt jūsu atrašanās vietu, pamatojoties uz tīkla avotiem, piemēram, mobilo sakaru torņiem un Wi-Fi tīkliem. Šiem atrašanās vietu pakalpojumiem ir jābūt ieslēgtiem un pieejamiem jūsu tālrunī, lai lietotne tos varētu izmantot."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"Piekļuve precīzai atrašanās vietai, darbojoties fonā"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Šī lietotne var iegūt precīzu jūsu atrašanās vietu jebkurā laikā, darbojoties fonā. Šiem atrašanās vietu pakalpojumiem ir jābūt ieslēgtiem un pieejamiem jūsu tālrunī, lai lietotne varētu tos izmantot. Tādējādi var palielināties akumulatora jaudas patēriņš."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"piekļūt atrašanās vietai fonā"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Ja šī atļauja tiek piešķirta līdz ar piekļuvi aptuvenai vai precīzai atrašanās vietai, lietotne var piekļūt atrašanās vietai, darbojoties fonā."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"mainīt audio iestatījumus"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ļauj lietotnei mainīt globālos audio iestatījumus, piemēram, skaļumu un izejai izmantoto skaļruni."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ierakstīt audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Šī lietotne jebkurā brīdī var ierakstīt audio, izmantojot mikrofonu."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"Sūtīt komandas SIM kartei"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Ļauj lietotnei sūtīt komandas uz SIM karti. Tas ir ļoti bīstami!"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"noteikt fiziskās aktivitātes"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Šī lietotne var noteikt jūsu fiziskās aktivitātes."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"uzņemt attēlus un videoklipus"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Šī lietotne jebkurā brīdī var uzņemt attēlus un ierakstīt videoklipus, izmantojot kameru."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrolēt vibrosignālu"</string>
@@ -521,6 +526,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Ļauj lietotnei pārveidot jūsu fotoattēlu kolekciju."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"Lasīt atrašanās vietas no jūsu multivides kolekcijas"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ļauj lietotnei lasīt atrašanās vietas no jūsu multivides kolekcijas."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Lietotne <xliff:g id="APP">%s</xliff:g> pieprasa autentificēt."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrisko datu aparatūra nav pieejama"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Noteikts daļējs pirksta nospiedums. Lūdzu, mēģiniet vēlreiz."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nevarēja apstrādāt pirksta nospiedumu. Lūdzu, mēģiniet vēlreiz."</string>
@@ -799,7 +805,7 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Nospiediet Izvēlne, lai atbloķētu, vai veiciet ārkārtas zvanu."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Lai atbloķētu, nospiediet vienumu Izvēlne."</string>
     <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Zīmējiet kombināciju, lai atbloķētu."</string>
-    <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Ārkārtas"</string>
+    <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Ārkārtas situācija"</string>
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Atpakaļ pie zvana"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Pareizi!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Mēģināt vēlreiz"</string>
@@ -1932,14 +1938,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> automātiskās aizpildes ieteikums</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> automātiskās aizpildes ieteikumi</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Vai saglabāt pakalpojumā &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Vai saglabāt vienumu “<xliff:g id="TYPE">%1$s</xliff:g>” pakalpojumā &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Vai saglabāt vienumus “<xliff:g id="TYPE_0">%1$s</xliff:g>” un “<xliff:g id="TYPE_1">%2$s</xliff:g>” pakalpojumā &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Vai saglabāt vienumus “<xliff:g id="TYPE_0">%1$s</xliff:g>”, “<xliff:g id="TYPE_1">%2$s</xliff:g>” un “<xliff:g id="TYPE_2">%3$s</xliff:g>” pakalpojumā &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Vai atjaunināt pakalpojumā &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Vai atjaunināt vienumu “<xliff:g id="TYPE">%1$s</xliff:g>” pakalpojumā &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Vai atjaunināt vienumus “<xliff:g id="TYPE_0">%1$s</xliff:g>” un “<xliff:g id="TYPE_1">%2$s</xliff:g>” pakalpojumā &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Vai atjaunināt vienumus “<xliff:g id="TYPE_0">%1$s</xliff:g>”, “<xliff:g id="TYPE_1">%2$s</xliff:g>” un “<xliff:g id="TYPE_2">%3$s</xliff:g>” pakalpojumā &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Vai saglabāt pakalpojumā "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Vai saglabāt informāciju <xliff:g id="TYPE">%1$s</xliff:g> pakalpojumā "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Vai saglabāt informāciju <xliff:g id="TYPE_0">%1$s</xliff:g> un <xliff:g id="TYPE_1">%2$s</xliff:g> pakalpojumā "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Vai saglabāt informāciju <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> un <xliff:g id="TYPE_2">%3$s</xliff:g> pakalpojumā "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Vai atjaunināt informāciju pakalpojumā "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Vai atjaunināt informāciju <xliff:g id="TYPE">%1$s</xliff:g> pakalpojumā "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Vai atjaunināt informāciju <xliff:g id="TYPE_0">%1$s</xliff:g> un <xliff:g id="TYPE_1">%2$s</xliff:g> pakalpojumā "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Vai atjaunināt šos vienumus pakalpojumā "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> un <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Saglabāt"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Nē, paldies"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Atjaunināt"</string>
diff --git a/core/res/res/values-mcc208-mnc01/config.xml b/core/res/res/values-mcc208-mnc01/config.xml
deleted file mode 100644
index 5930e3a..0000000
--- a/core/res/res/values-mcc208-mnc01/config.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds.  Do not translate. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>Orange Internet,orange.fr,,,orange,orange,,,,,208,01,1,DUN</item>
-    </string-array>
-
-</resources>
diff --git a/core/res/res/values-mcc208-mnc10/config.xml b/core/res/res/values-mcc208-mnc10/config.xml
deleted file mode 100644
index 3ed7818..0000000
--- a/core/res/res/values-mcc208-mnc10/config.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>SFR option modem,websfr,,,,,,,,,208,10,,DUN</item>
-        <item>[ApnSettingV3]INTERNET NRJ,internetnrj,,,,,,,,,208,10,,DUN,,,true,0,,,,,,,gid,4E</item>
-    </string-array>
-
-</resources>
diff --git a/core/res/res/values-mcc214-mnc01/config.xml b/core/res/res/values-mcc214-mnc01/config.xml
index 41e24d7..b34696b 100644
--- a/core/res/res/values-mcc214-mnc01/config.xml
+++ b/core/res/res/values-mcc214-mnc01/config.xml
@@ -31,15 +31,6 @@
       <item>9</item>
     </integer-array>
 
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-      <item>INTERNET,airtelnet.es,,,vodafone,vodafone,,,,,214,01,1,DUN</item>
-    </string-array>
-
     <!-- Whether safe headphone volume warning dialog is disabled on Vol+ (operator specific). -->
     <bool name="config_safe_media_disable_on_volume_up">false</bool>
 
diff --git a/core/res/res/values-mcc214-mnc07/config.xml b/core/res/res/values-mcc214-mnc07/config.xml
deleted file mode 100644
index 4b7cc7c..0000000
--- a/core/res/res/values-mcc214-mnc07/config.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>Conexión Compartida,movistar.es,,,MOVISTAR,MOVISTAR,,,,,214,07,1,DUN</item>
-    </string-array>
-
-</resources>
diff --git a/core/res/res/values-mcc222-mnc10/config.xml b/core/res/res/values-mcc222-mnc10/config.xml
index 0085a1b..abacef0 100644
--- a/core/res/res/values-mcc222-mnc10/config.xml
+++ b/core/res/res/values-mcc222-mnc10/config.xml
@@ -20,15 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds.  Do not translate. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-      <item>Tethering Internet,web.omnitel.it,,,,,,,,,222,10,,DUN</item>
-    </string-array>
-
     <!-- Whether safe headphone volume warning dialog is disabled on Vol+ (operator specific). -->
     <bool name="config_safe_media_disable_on_volume_up">false</bool>
 
diff --git a/core/res/res/values-mcc234-mnc20/config.xml b/core/res/res/values-mcc234-mnc20/config.xml
index 1e4bb0b..224dc31 100644
--- a/core/res/res/values-mcc234-mnc20/config.xml
+++ b/core/res/res/values-mcc234-mnc20/config.xml
@@ -20,15 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>3hotspot,3hotspot,,,,,,,,,234,20,0,DUN</item>
-    </string-array>
-
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1440</integer>
diff --git a/core/res/res/values-mcc235-mnc94/config.xml b/core/res/res/values-mcc235-mnc94/config.xml
index d527304..38ae2a0 100644
--- a/core/res/res/values-mcc235-mnc94/config.xml
+++ b/core/res/res/values-mcc235-mnc94/config.xml
@@ -31,14 +31,6 @@
         <item>9</item>
     </integer-array>
 
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>3hotspot,3hotspot,,,,,,,,,235,94,0,DUN</item>
-    </string-array>
-
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1440</integer>
diff --git a/core/res/res/values-mcc268-mnc03/config.xml b/core/res/res/values-mcc268-mnc03/config.xml
index 2f5171b..876c26e 100644
--- a/core/res/res/values-mcc268-mnc03/config.xml
+++ b/core/res/res/values-mcc268-mnc03/config.xml
@@ -30,13 +30,4 @@
       <item>7</item>
       <item>9</item>
     </integer-array>
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,270,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>Optimus HotSpot,modem,,,,,,,,,268,03,,DUN</item>
-    </string-array>
 </resources>
diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml
index 13f6bce..36efd0a 100644
--- a/core/res/res/values-mcc302-mnc220/config.xml
+++ b/core/res/res/values-mcc302-mnc220/config.xml
@@ -23,17 +23,6 @@
 
     <integer name="config_mobile_mtu">1410</integer>
 
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>[ApnSettingV3]TELUS ISP,isp.telus.com,,,,,,,,,302,220,,DUN,IP,IP,true,0,,,,,,,gid,5455</item>
-        <item>[ApnSettingV3]Tethered Mobile Internet,isp.mb.com,,,,,,,,,302,220,,DUN,,,true,0,,,,,,,gid,5043</item>
-        <item>[ApnSettingV3]Tethered Public Mobile,isp.mb.com,,,,,,,,,302,220,,DUN,,,true,0,,,,,,,gid,4D4F</item>
-    </string-array>
-
     <!-- Values for GPS configuration (Telus) -->
     <string-array translatable="false" name="config_gpsParameters">
         <item>SUPL_HOST=supl.google.com</item>
diff --git a/core/res/res/values-mcc302-mnc221/config.xml b/core/res/res/values-mcc302-mnc221/config.xml
index d45b91a..a11dd95 100644
--- a/core/res/res/values-mcc302-mnc221/config.xml
+++ b/core/res/res/values-mcc302-mnc221/config.xml
@@ -20,18 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>[ApnSettingV3]TELUS ISP,isp.telus.com,,,,,,,,,302,221,,DUN,,,true,0,,,,,,,gid,5455</item>
-        <item>[ApnSettingV3]Tethered PC Mobile,isp.mb.com,,,,,,,,,302,221,,DUN,,,true,0,,,,,,,gid,5043</item>
-        <item>[ApnSettingV3]Koodo,sp.koodo.com,,,,,,,,,302,221,,DUN,,,true,0,,,,,,,gid,4B4F</item>
-    </string-array>
-
     <!-- Values for GPS configuration (Telus) -->
     <string-array translatable="false" name="config_gpsParameters">
         <item>SUPL_HOST=supl.google.com</item>
diff --git a/core/res/res/values-mcc302-mnc370/config.xml b/core/res/res/values-mcc302-mnc370/config.xml
index b520d5d..8d29ec1 100644
--- a/core/res/res/values-mcc302-mnc370/config.xml
+++ b/core/res/res/values-mcc302-mnc370/config.xml
@@ -20,17 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds.  Do not translate. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>Fido LTE Tethering,ltedata.apn,,,,,,,,,302,370,,DUN</item>
-        <item>[ApnSettingV3]MTS Tethering,internet.mts,,,,,,,,,302,370,,DUN,,,true,0,,,,,,,gid,2C</item>
-    </string-array>
-
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1410</integer>
diff --git a/core/res/res/values-mcc302-mnc660/config.xml b/core/res/res/values-mcc302-mnc660/config.xml
index 8c2e702..beb2336 100644
--- a/core/res/res/values-mcc302-mnc660/config.xml
+++ b/core/res/res/values-mcc302-mnc660/config.xml
@@ -29,16 +29,6 @@
         <item>7</item>
         <item>9</item>
     </integer-array>
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-      <item>MTS -Tethering,internet.mts,,,,,,,,,302,660,,DUN</item>
-    </string-array>
-
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1430</integer>
diff --git a/core/res/res/values-mcc302-mnc720/config.xml b/core/res/res/values-mcc302-mnc720/config.xml
index 11bfa05..735a8c8 100644
--- a/core/res/res/values-mcc302-mnc720/config.xml
+++ b/core/res/res/values-mcc302-mnc720/config.xml
@@ -20,19 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds.  Do not translate. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>Rogers LTE Tethering,ltedata.apn,,,,,,,,,302,720,,DUN</item>
-        <item>[ApnSettingV3]chatr Tethering,chatrisp.apn,,,,,,,,,302,720,,DUN,,,true,0,,,,,,,imsi,302720x94</item>
-        <item>[ApnSettingV3]Tbaytel Tethering,ltedata.apn,,,,,,,,,302,720,,DUN,IPV4V6,IP,true,0,,,,,,,gid,BA</item>
-        <item>[ApnSettingV3]Cityfone Tethering,ltedata.apn,,,,,,,,,302,720,,DUN,IPV4V6,IP,true,0,,,,,,,spn,CITYFONE</item>
-    </string-array>
-
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1430</integer>
diff --git a/core/res/res/values-mcc311-mnc190/config.xml b/core/res/res/values-mcc311-mnc190/config.xml
index c17a07c..876c26e 100644
--- a/core/res/res/values-mcc311-mnc190/config.xml
+++ b/core/res/res/values-mcc311-mnc190/config.xml
@@ -30,13 +30,4 @@
       <item>7</item>
       <item>9</item>
     </integer-array>
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>Tether,broadband.cellular1.net,,,,,,,,,311,190,,DUN</item>
-    </string-array>
-
 </resources>
diff --git a/core/res/res/values-mcc334-mnc050/config.xml b/core/res/res/values-mcc334-mnc050/config.xml
index 616a8e8..23678f1 100644
--- a/core/res/res/values-mcc334-mnc050/config.xml
+++ b/core/res/res/values-mcc334-mnc050/config.xml
@@ -31,15 +31,6 @@
       <item>9</item>
     </integer-array>
 
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-      <item>Modem,modem.iusacellgsm.mx,,,iusacellgsm,iusacellgsm,,,,,334,050,1,DUN</item>
-    </string-array>
-
     <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
     <string-array translatable="false" name="config_twoDigitNumberPattern">
         <item>"#9"</item>
diff --git a/core/res/res/values-mcc340-mnc01/config.xml b/core/res/res/values-mcc340-mnc01/config.xml
index 1ca8963..3b23b85 100644
--- a/core/res/res/values-mcc340-mnc01/config.xml
+++ b/core/res/res/values-mcc340-mnc01/config.xml
@@ -29,13 +29,4 @@
         <item>7</item>
         <item>9</item>
     </integer-array>
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>Orangeweb,orangeweb,,,orange,orange,,,,,340,01,1,DUN</item>
-    </string-array>
 </resources>
diff --git a/core/res/res/values-mcc425-mnc07/config.xml b/core/res/res/values-mcc425-mnc07/config.xml
index 770cebd..876c26e 100644
--- a/core/res/res/values-mcc425-mnc07/config.xml
+++ b/core/res/res/values-mcc425-mnc07/config.xml
@@ -30,13 +30,4 @@
       <item>7</item>
       <item>9</item>
     </integer-array>
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-      <item>PC HOT mobile,pc.hotm,,,,,,,,,425,07,,DUN</item>
-    </string-array>
 </resources>
diff --git a/core/res/res/values-mcc454-mnc10/config.xml b/core/res/res/values-mcc454-mnc10/config.xml
deleted file mode 100644
index 79a9a7e..0000000
--- a/core/res/res/values-mcc454-mnc10/config.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-      <item>one2free Tethering,internet,,,,,,,,,454,10,0,DUN</item>
-    </string-array>
-
-</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 8bca75e..4b3a53e 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снима аудио"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Дали да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да снима аудио?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Препознавање активност"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"препознавај ја активноста"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Дозволувате ли &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да ја препознава вашата физичка активност?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"фотографира и снима видео"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Дали да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да фотографира и да снима видео?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Апликацијава може да ја добие вашата локација од мрежните извори како што се репетиторите за мобилни мрежи и Wi-Fi мрежите. Овие услуги за локација мора да се вклучени и достапни на таблетот за да може да ги користи апликацијата."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Апликацијава може да ја добие вашата локација од мрежните извори како што се репетиторите за мобилни мрежи и Wi-Fi мрежите. Овие услуги за локација мора да се вклучени и достапни на телевизорот за да може да ги користи апликацијата."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Апликацијава може да ја добие вашата локација од мрежните извори како што се репетиторите за мобилни мрежи и Wi-Fi мрежите. Овие услуги за локација мора да се вклучени и достапни на телефонот за да може да ги користи апликацијата."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"пристап до прецизната локација во заднина"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Апликацијава може да ја добие вашата точна локација секогаш кога е во заднина. Услугиве според локација мора да се вклучени и достапни на телефонот за да може да ги користи апликацијата. Ова може да го зголеми трошењето на батеријата."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"пристап до локацијата во заднина"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Ако покрај дозволата за пристап до приближната или прецизната локација е доделена и оваа дозвола, тогаш апликацијата ќе може да пристапува до локацијата додека се извршува во заднина."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"менува аудио поставки"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Овозможува апликацијата да ги менува глобалните аудио поставки, како што се јачината на звукот и кој звучник се користи за излез."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"снимај аудио"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Апликацијава може да снима аудио со микрофонот во секое време."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"испраќање наредби до SIM-картичката"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Овозможува апликацијата да испраќа наредби до SIM картичката. Ова е многу опасно."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"препознавајте ја физичката активност"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Апликацијава може да ја препознава вашата физичка активност."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"снимај слики и видеа"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Апликацијава може да фотографира и да снима видеа со камерата во секое време."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"контролирај вибрации"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Дозволува апликацијата да ја менува вашата збирка на фотографии."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"да чита локации од вашата збирка на аудиовизуелни содржини"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Дозволува апликацијата да чита локации од вашата збирка на аудиовизуелни содржини."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Апликацијата <xliff:g id="APP">%s</xliff:g> сака да ве провери."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрискиот хардвер е недостапен"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Откриен е делумен отпечаток. Обидете се повторно."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Отпечатокот не можеше да се обработи. Обидете се повторно."</string>
@@ -1900,14 +1906,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> предлог за автоматско пополнување</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> предлози за автоматско пополнување</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Да се зачува во &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Да се зачува <xliff:g id="TYPE">%1$s</xliff:g> во &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Да се зачуваат <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> во &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Да се зачуваат <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g> во &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Дали да се ажурира на &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Дали да се ажурира <xliff:g id="TYPE">%1$s</xliff:g> на &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Дали да се ажурираат <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> на &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Дали да се ажурираат <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g> на &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Да се зачува во "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Да се зачува <xliff:g id="TYPE">%1$s</xliff:g> во "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Да се зачуваат <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> во "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Да се зачуваат <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g> во "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Да се ажурира во "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Да се ажурира <xliff:g id="TYPE">%1$s</xliff:g> во "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Да се ажурираат <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> во "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Да се ажурираат овие ставки во "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Зачувај"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Не, фала"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Ажурирај"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 1efdc72..2184af0 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"മൈക്രോഫോണ്‍"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ഓഡിയോ റെക്കോർഡ് ചെയ്യുക"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"ഓഡിയോ റെക്കോർഡ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കണോ?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"പ്രവർത്തനം തിരിച്ചറിയൽ"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"പ്രവർത്തനം തിരിച്ചറിയുക"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-&lt;/b&gt;നെ നിങ്ങളുടെ ശാരീരിക പ്രവർത്തനം തിരിച്ചറിയാൻ അനുവദിക്കണോ?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"ക്യാമറ"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ചിത്രങ്ങളെടുത്ത് വീഡിയോ റെക്കോർഡുചെയ്യുക"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"ചിത്രം എടുക്കാനും വീഡിയോ റെക്കോർഡ് ചെയ്യാനും &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കണോ?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"സെൽ ടവറുകളും വൈഫൈ നെറ്റ്‌വർക്കുകളും പോലുള്ള നെറ്റ്‌വർക്ക് ഉറവിടങ്ങളെ അടിസ്ഥാനമാക്കിക്കൊണ്ട് ഈ ആപ്പിന് നിങ്ങളുടെ ലൊക്കേഷൻ അനുമാനിക്കാൻ കഴിയും. നിങ്ങളുടെ ടാബ്‌ലെറ്റിൽ ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓൺ ചെയ്തിട്ടുണ്ടെങ്കിൽ മാത്രമാണ് ആപ്പിന് അവ ഉപയോഗിക്കാൻ കഴിയുക."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"സെൽ ടവറുകളും വൈഫൈ നെറ്റ്‌വർക്കുകളും പോലുള്ള നെറ്റ്‌വർക്ക് ഉറവിടങ്ങളെ അടിസ്ഥാനമാക്കിക്കൊണ്ട് ഈ ആപ്പിന് നിങ്ങളുടെ ലൊക്കേഷൻ അനുമാനിക്കാൻ കഴിയും. നിങ്ങളുടെ ടിവിയിൽ ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓൺ ചെയ്തിട്ടുണ്ടെങ്കിൽ മാത്രമാണ് ആപ്പിന് അവ ഉപയോഗിക്കാൻ കഴിയുക."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"സെൽ ടവറുകളും വൈഫൈ നെറ്റ്‌വർക്കുകളും പോലുള്ള നെറ്റ്‌വർക്ക് ഉറവിടങ്ങളെ അടിസ്ഥാനമാക്കിക്കൊണ്ട് ഈ ആപ്പിന് നിങ്ങളുടെ ലൊക്കേഷൻ അനുമാനിക്കാൻ കഴിയും. നിങ്ങളുടെ ഫോണിൽ ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓൺ ചെയ്തിട്ടുണ്ടെങ്കിൽ മാത്രമാണ് ആപ്പിന് അവ ഉപയോഗിക്കാൻ കഴിയുക."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"പശ്ചാത്തലത്തിൽ കൃത്യമായ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യുക"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ഈ ആപ്പ് പശ്ചാത്തലത്തിൽ ഉള്ളപ്പോൾ എപ്പോൾ വേണമെങ്കിലും ഇതിന് നിങ്ങളുടെ കൃത്യമായ ലൊക്കേഷൻ നേടാനാവും. ആപ്പിന് ഉപയോഗിക്കാനായി, ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓണായിരിക്കുകയും നിങ്ങളുടെ ഫോണിൽ ലഭ്യമാവുകയും വേണം. ഇതിലൂടെ ബാറ്ററി ഉപഭോഗം വർദ്ധിച്ചേക്കാം."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"പശ്ചാത്തലത്തിൽ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യുക"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"ഏകദേശം അല്ലെങ്കിൽ കൃത്യമായ ലൊക്കേഷൻ ആക്‌സസിന് ഇത് അധികമായി അനുവദിച്ചതാണെങ്കിൽ, പശ്ചാത്തലത്തിൽ റൺ ചെയ്യുമ്പോഴും ആപ്പിന് ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാനാവും."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"നിങ്ങളുടെ ഓഡിയോ ക്രമീകരണങ്ങൾ മാറ്റുക"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"വോളിയവും ഔട്ട്പുട്ടിനായി ഉപയോഗിച്ച സ്‌പീക്കറും പോലുള്ള ആഗോള ഓഡിയോ ക്രമീകരണങ്ങൾ പരിഷ്‌ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ഓഡിയോ റെക്കോർഡ് ചെയ്യുക"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"ഈ ആപ്പിന് ഏത് സമയത്തും മൈക്രോഫോൺ ഉപയോഗിച്ചുകൊണ്ട് ഓഡിയോ റെക്കോർഡുചെയ്യാൻ കഴിയും."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM-ലേക്ക് കമാൻഡുകൾ അയയ്ക്കുക"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"സിമ്മിലേക്ക് കമാൻഡുകൾ അയയ്‌ക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് വളരെ അപകടകരമാണ്."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"ശാരീരിക പ്രവർത്തനം തിരിച്ചറിയുക"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"നിങ്ങളുടെ ശാരീരിക പ്രവർത്തനം ഈ ആപ്പിന് തിരിച്ചറിയാനാവും."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ചിത്രങ്ങളും വീഡിയോകളും എടുക്കുക"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"ഏതുസമയത്തും ക്യാമറ ഉപയോഗിച്ചുകൊണ്ട് ചിത്രങ്ങൾ എടുക്കാനും വീഡിയോകൾ റെക്കോർഡുചെയ്യാനും ഈ ആപ്പിന് കഴിയും."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"വൈബ്രേറ്റുചെയ്യൽ നിയന്ത്രിക്കുക"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"നിങ്ങളുടെ ഫോട്ടോ ശേഖരം പരിഷ്‌ക്കരിക്കുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"നിങ്ങളുടെ മീഡിയ ശേഖരത്തിൽ നിന്നും ലൊക്കേഷനുകൾ റീഡ് ചെയ്യുക"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"നിങ്ങളുടെ മീഡിയ ശേഖരത്തിൽ നിന്നും ലൊക്കേഷനുകൾ റീഡ് ചെയ്യുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> ആപ്പിന് നിങ്ങളെ പരിശോധിച്ചുറപ്പിക്കണം."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ബയോമെട്രിക് ഹാർ‌ഡ്‌വെയർ ലഭ്യമല്ല"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"വിരലടയാളം ഭാഗികമായി തിരിച്ചറിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"വിരലടയാളം പ്രോസസ്സ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
@@ -1898,14 +1904,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> സ്വയമേവ പൂരിപ്പിക്കൽ നിർദ്ദേശങ്ങൾ</item>
       <item quantity="one">ഒരു സ്വയമേവ പൂരിപ്പിക്കൽ നിർദ്ദേശം</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;എന്നതിലേക്ക് സംരക്ഷിക്കണോ?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> എന്നതിനെ &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;എന്നതിലേക്ക് സംരക്ഷിക്കണോ?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> എന്നിവ&lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;എന്നതിലേക്ക് സംരക്ഷിക്കണോ?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g> എന്നിവ&lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;എന്നതിലേക്ക് സംരക്ഷിക്കണോ?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; എന്നതിലേക്ക് അപ്ഡേറ്റ് ചെയ്യണോ?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> എന്നതിനെ &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; എന്നതിലേക്ക് അപ്ഡേറ്റ് ചെയ്യണോ?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"&lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; എന്നതിലേക്ക് <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> അപ്ഡേറ്റ് ചെയ്യുക?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g> എന്നിവയെ &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; എന്നതിലേക്ക് അപ്ഡേറ്റ് ചെയ്യണോ?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" എന്നതിൽ സംരക്ഷിക്കണോ?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g>, "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" എന്നതിൽ സംരക്ഷിക്കണോ?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> എന്നിവ "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" എന്നതിൽ സംരക്ഷിക്കണോ?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g> എന്നിവ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" എന്നതിൽ സംരക്ഷിക്കണോ?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" എന്നതിൽ അപ്‌ഡേറ്റ് ചെയ്യണോ?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g>, "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" എന്നതിൽ അപ്‌ഡേറ്റ് ചെയ്യണോ?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> എന്നിവ "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" എന്നതിൽ അപ്‌ഡേറ്റ് ചെയ്യണോ?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"ഇനിപ്പറയുന്ന ഇനങ്ങൾ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" എന്നതിൽ അപ്‌ഡേറ്റ് ചെയ്യണോ: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"സംരക്ഷിക്കുക"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"വേണ്ട, നന്ദി"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"അപ്ഡേറ്റ് ചെയ്യുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 86ae178..45e118f 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"дуу хураах"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д аудио бичихийг зөвшөөрөх үү?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Үйл ажиллагааны зөвшөөрөл"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"хөдөлгөөн таних"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-д&lt;/b&gt; таны биеийн дасгал хөдөлгөөн танихыг зөвшөөрөх үү?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камер"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"зураг авах, бичлэг хийх"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д зураг авах, видео хийхийг зөвшөөрөх үү?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Энэ апп үүрэн цамхаг, Wi-Fi сүлжээ зэрэг сүлжээний байршлын эх сурвалжийг ашиглан таны байршлыг мэдэх боломжтой. Эдгээр байршлын үйлчилгээ нь таны таблетад асаалттай байх ёстой ба апп ашиглах боломжтой байх ёстой."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Энэ апп үүрэн цамхаг, Wi-Fi сүлжээ зэрэг сүлжээний байршлын эх сурвалжийг ашиглан таны байршлыг мэдэх боломжтой. Эдгээр байршлын үйлчилгээ нь таны ТВ-д асаалттай байх ёстой ба апп ашиглах боломжтой байх ёстой."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Энэ апп үүрэн цамхаг, Wi-Fi сүлжээ зэрэг сүлжээний байршлын эх сурвалжийг ашиглан таны байршлыг мэдэх боломжтой. Эдгээр байршлын үйлчилгээ нь таны утсанд асаалттай байх ёстой ба апп ашиглах боломжтой байх ёстой."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"нарийвчилсан байршилд арын дэвсгэрт хандах"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Энэ апп нь ард ажиллах үедээ таны байршлыг ямар ч үед нарийн тогтоох боломжтой. Апп эдгээр байршлын үйлчилгээг ашиглахын тулд эдгээрийг таны утсан дээр асааж идэвхтэй байлгах шаардлагатай. Энэ нь батарейны хэрэглээг ихэсгэж болзошгүй."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"байршилд ард хандах"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Хэрэв үүнийг ойролцоо эсвэл нарийвчилсан байршлын хандалтад нэмэлтээр зөвшөөрсөн бол энэ апп ард ажиллах явцдаа байршилд хандаж болно."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"Аудио тохиргоо солих"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Апп нь дууны хэмжээ, спикерын гаралтад ашиглагдах глобал аудио тохиргоог өөрчлөх боломжтой."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"аудио бичих"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Энэ апп ямар ч үед микрофон ашиглан аудио бичих боломжтой."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM картад тушаал илгээх"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Апп-д SIM рүү комманд илгээхийг зөвшөөрнө. Энэ маш аюултай."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"биеийн дасгал хөдөлгөөн таних"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Энэ апп таны биеийн дасгал хөдөлгөөнийг таних боломжтой."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"зураг авах болон видео бичих"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Энэ апп ямар ч үед камер ашиглан зураг авж, видео хийх боломжтой."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"чичиргээг удирдах"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Таны зургийн цуглуулгыг тохируулах зөвшөөрлийг аппад олгодог."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"медиа цуглуулгаасаа байршлыг унших"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Таны медиа цуглуулгаас байршлыг унших зөвшөөрлийг аппад олгодог."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> апп баталгаажуулахыг хүсэж байна."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрийн техник хангамж боломжгүй байна"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Хурууны хээг дутуу уншуулсан байна. Дахин оролдоно уу."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Хурууны хээ боловсруулж чадахгүй байна. Дахин оролдоно уу."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other">автоматаар бөглөх хэсгийн <xliff:g id="COUNT">%1$s</xliff:g> зөвлөмж</item>
       <item quantity="one">Автоматаар бөглөх хэсгийн 1 зөвлөмж</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;-д хадгалах уу?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g>-г &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;-д хадгалах уу?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>-г &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;-д хадгалах уу?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g>-г &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;-д хадгалах уу?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;-д шинэчлэх үү?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g>-г &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;-д шинэчлэх үү?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>-г &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;-д шинэчлэх үү?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g>-г &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;-д шинэчлэх үү?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"-д хадгалах уу?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g>-г "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"-д хадгалах уу?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> болон <xliff:g id="TYPE_1">%2$s</xliff:g>-г "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"-д хадгалах уу?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> болон <xliff:g id="TYPE_2">%3$s</xliff:g>-г "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"-д хадгалах уу?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"-д шинэчлэх үү?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g>-г "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"-д шинэчлэх үү?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> болон <xliff:g id="TYPE_1">%2$s</xliff:g>-г "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"-д шинэчлэх үү?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Эдгээр зүйлийг буюу <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> болон <xliff:g id="TYPE_2">%3$s</xliff:g>-г "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"-д шинэчлэх үү?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Хадгалах"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Үгүй, баярлалаа"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Шинэчлэх"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 70ed012..fb4c408 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -206,10 +206,10 @@
     <string name="reboot_to_reset_title" msgid="4142355915340627490">"फॅक्‍टरी डेटा रीसेट"</string>
     <string name="reboot_to_reset_message" msgid="2432077491101416345">"रीस्टार्ट करत आहे..."</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"बंद होत आहे…"</string>
-    <string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"आपला टॅबलेट बंद होईल."</string>
-    <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"आपला टीव्ही बंद होईल."</string>
+    <string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"तुमचा टॅबलेट बंद होईल."</string>
+    <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"तुमचा टीव्ही बंद होईल."</string>
     <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"तुमचे घड्याळ बंद होईल."</string>
-    <string name="shutdown_confirm" product="default" msgid="649792175242821353">"आपला फोन बंद होईल."</string>
+    <string name="shutdown_confirm" product="default" msgid="649792175242821353">"तुमचा फोन बंद होईल."</string>
     <string name="shutdown_confirm_question" msgid="2906544768881136183">"तुम्ही बंद करू इच्छिता?"</string>
     <string name="reboot_safemode_title" msgid="7054509914500140361">"सुरक्षित मोडमध्ये रीबूट करा"</string>
     <string name="reboot_safemode_confirm" msgid="55293944502784668">"तुम्ही सुरक्षित मोडमध्ये रीबूट करू इच्छिता? हे तुम्ही इंस्टॉल केलेले सर्व तृतीय पक्ष अॅप्लिकेशन अक्षम करेल. तुम्ही पुन्हा रीबूट करता तेव्हा ते पुनर्संचयित केले जातील."</string>
@@ -227,7 +227,7 @@
     <string name="bugreport_title" msgid="2667494803742548533">"बग रीपोर्ट घ्या"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ई-मेल मेसेज म्हणून पाठविण्यासाठी, हे तुमच्या सद्य डिव्हाइस स्थितीविषयी माहिती संकलित करेल. बग रीपोर्ट सुरू करण्यापासून तो पाठविण्यापर्यंत थोडा वेळ लागेल; कृपया धीर धरा."</string>
     <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"परस्परसंवादी अहवाल"</string>
-    <string name="bugreport_option_interactive_summary" msgid="229299488536107968">"बहुतांश प्रसंगांमध्ये याचा वापर करा. ते आपल्याला अहवालाच्या प्रगतीचा मागोवा घेण्याची, समस्येविषयी आणखी तपाशील एंटर करण्याची आणि स्क्रीनशॉट घेण्याची अनुमती देते. ते कदाचित अहवाल देण्यासाठी बराच वेळ घेणारे कमी-वापरलेले विभाग वगळू शकते."</string>
+    <string name="bugreport_option_interactive_summary" msgid="229299488536107968">"बहुतांश प्रसंगांमध्ये याचा वापर करा. ते तुम्हाला अहवालाच्या प्रगतीचा मागोवा घेण्याची, समस्येविषयी आणखी तपाशील एंटर करण्याची आणि स्क्रीनशॉट घेण्याची अनुमती देते. ते कदाचित अहवाल देण्यासाठी बराच वेळ घेणारे कमी-वापरलेले विभाग वगळू शकते."</string>
     <string name="bugreport_option_full_title" msgid="6354382025840076439">"संपूर्ण अहवाल"</string>
     <string name="bugreport_option_full_summary" msgid="7210859858969115745">"तुमचे डिव्हाइस प्रतिसाद देत नाही किंवा खूप धीमे असते किंवा तुम्हाला सर्व अहवाल विभागांची आवश्यकता असते तेव्हा कमीतकमी सिस्टम हस्तक्षेपासाठी या पर्यायाचा वापर करा. तुम्हाला आणखी तपशील एंटर करण्याची किंवा अतिरिक्त स्क्रीनशॉट घेण्याची अनुमती देत नाही."</string>
     <plurals name="bugreport_countdown" formatted="false" msgid="6878900193900090368">
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"मायक्रोफोन"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ऑडिओ रेकॉर्ड"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला ऑडिओ रेकॉर्ड करू द्यायचे?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"अॅक्टिव्हिटी ओळख"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"अॅक्टिव्हिटी ओळखा"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला तुमची शारीरिक अॅक्टिव्हिटी ओळखण्याची अनुमती द्यायची का?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"कॅमेरा"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"चित्रे घेण्याची आणि व्हिडिओ रेकॉर्ड"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला फोटो घेऊ आणि व्हिडिओ रेकॉर्ड करू द्यायचे?"</string>
@@ -339,9 +342,9 @@
     <string name="permlab_receiveSms" msgid="8673471768947895082">"मजकूर मेसेज मिळवा (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS मेसेज प्राप्त करण्याची आणि त्यावर प्रक्रिया करण्याची अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डीव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"मजकूर मेसेज मिळवा (MMS)"</string>
-    <string name="permdesc_receiveMms" msgid="533019437263212260">"MMS मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यास अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डिव्हाइसवर पाठविलेले मेसेज आपल्याला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
+    <string name="permdesc_receiveMms" msgid="533019437263212260">"MMS मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यास अॅप ला अनुमती देते. म्हणजेच अॅप आपल्या डिव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
     <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"सेल प्रसारण मेसेज वाचा"</string>
-    <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"आपल्या डिव्हाइसद्वारे प्राप्त केलेले सेल प्रसारण मेसेज वाचण्यासाठी अॅप ला अनुमती देते. काही स्थानांमध्ये आपल्याला आणीबाणीच्या परिस्थितीची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरीत केल्या जातात. आणीबाणी सेल प्रसारण प्राप्त होते तेव्हा आपल्या डिव्हाइसच्या कार्यप्रदर्शनात किंवा कार्यात दुर्भावनापूर्ण अॅप्स व्यत्यय आणू शकतात."</string>
+    <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"आपल्या डिव्हाइसद्वारे प्राप्त केलेले सेल प्रसारण मेसेज वाचण्यासाठी अॅप ला अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थितीची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरीत केल्या जातात. आणीबाणी सेल प्रसारण प्राप्त होते तेव्हा आपल्या डिव्हाइसच्या कार्यप्रदर्शनात किंवा कार्यात दुर्भावनापूर्ण अॅप्स व्यत्यय आणू शकतात."</string>
     <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"सदस्यता घेतलेली फीड वाचा"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"सध्या संकालित केलेल्या फीडविषयी तपशील मिळविण्यासाठी अॅप ला अनुमती देते."</string>
     <string name="permlab_sendSms" msgid="7544599214260982981">"SMS मेसेज पाठवणे आणि पाहणे"</string>
@@ -351,7 +354,7 @@
     <string name="permdesc_readSms" product="tv" msgid="5796670395641116592">"हा अॅप तुमच्या टीव्हीवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
     <string name="permdesc_readSms" product="default" msgid="6826832415656437652">"हा अॅप तुमच्या फोनवर स्टोअर केलेले सर्व SMS (मजकूर) मेसेज वाचू शकतो."</string>
     <string name="permlab_receiveWapPush" msgid="5991398711936590410">"मजकूर मेसेज मिळवा (WAP)"</string>
-    <string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यासाठी अॅप ला अनुमती देते. ही परवानगी आपल्याला पाठविलेले मेसेज आपल्याला न दर्शविता त्यांचे परीक्षण करण्याची आणि ते हटविण्याची क्षमता समाविष्ट करते."</string>
+    <string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुम्हाला पाठविलेले मेसेज तुम्हाला न दर्शविता त्यांचे परीक्षण करण्याची आणि ते हटविण्याची क्षमता समाविष्ट करते."</string>
     <string name="permlab_getTasks" msgid="6466095396623933906">"चालणारे अॅप्स पुनर्प्राप्त करा"</string>
     <string name="permdesc_getTasks" msgid="7454215995847658102">"सध्या आणि अलीकडे चालणार्‍या कार्यांविषयी माहिती पुनर्प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे डिव्हाइसवर कोणते अॅप्लिकेशन वापरले जात आहेत त्याविषयी माहिती शोधण्यासाठी अॅप ला अनुमती देऊ शकतात."</string>
     <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"प्रोफाईल आणि डिव्हाइस मालक व्यवस्थापित करा"</string>
@@ -387,25 +390,25 @@
     <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"रोचक प्रसारणे पाठविण्यास अॅपला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर तसेच रहाते. अतिरिक्त वापर टीव्ही धीमा किंवा यासाठी बरीच मेमरी वापरली जात असल्यामुळे तो अस्थिर करू शकतो."</string>
     <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो फोनला धीमा किंवा अस्थिर करू शकतो."</string>
     <string name="permlab_readContacts" msgid="8348481131899886131">"तुमचे संपर्क वाचा"</string>
-    <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी आपला संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा सामायिक करू शकतात."</string>
-    <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"तुम्ही विशिष्ट लोकांना इतर मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संप्रेषित केलेल्या फ्रिक्वेन्सीसह, आपल्या टीव्हीवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप्सला अनुमती देतात. ही परवागनी अॅप्सला आपला संपर्क डेटा सेव्ह करण्यासाठी अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्याला न कळविता संपर्क डेटा सामायिक करू शकतात."</string>
-    <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी आपला संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा सामायिक करू शकतात."</string>
+    <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
+    <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"तुम्ही विशिष्ट लोकांना इतर मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संप्रेषित केलेल्या फ्रिक्वेन्सीसह, आपल्या टीव्हीवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप्सला अनुमती देतात. ही परवागनी अॅप्सला तुमचा संपर्क डेटा सेव्ह करण्यासाठी अनुमती देते आणि दुर्भावनापूर्ण अॅप्स तुम्हाला न कळविता संपर्क डेटा शेअर करू शकतात."</string>
+    <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"तुम्ही कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी तुमचा संपर्क डेटा सेव्ह करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा शेअर करू शकतात."</string>
     <string name="permlab_writeContacts" msgid="5107492086416793544">"तुमचे संपर्क सुधारित करा"</string>
     <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
     <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टीव्हीवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅपला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅपला अनुमती देते."</string>
     <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"तुम्ही विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या फोनवर स्टोअर केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
     <string name="permlab_readCallLog" msgid="3478133184624102739">"कॉल लॉग वाचा"</string>
-    <string name="permdesc_readCallLog" msgid="3204122446463552146">"हा अॅप आपला कॉल इतिहास वाचू शकता."</string>
+    <string name="permdesc_readCallLog" msgid="3204122446463552146">"हा अॅप तुमचा कॉल इतिहास वाचू शकता."</string>
     <string name="permlab_writeCallLog" msgid="8552045664743499354">"कॉल लॉग लिहा"</string>
-    <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपला कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
-    <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या टीव्हीचा कॉल लॉग सुधारित करण्यासाठी अॅपला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपला कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
-    <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपला कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
+    <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
+    <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या टीव्हीचा कॉल लॉग सुधारित करण्यासाठी अॅपला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
+    <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"येणार्‍या आणि केल्या जाणार्‍या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string>
     <string name="permlab_bodySensors" msgid="4683341291818520277">"शरीर सेंसर (हृदय गती मॉनिटरसारखे) अॅक्सेस करा"</string>
     <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"हृदय गती सारख्या, आपल्या शारीरिक स्थितीचे नियंत्रण करणार्‍या सेन्सरवरून डेटामध्ये प्रवेश करण्यासाठी अॅपला अनुमती देते."</string>
     <string name="permlab_readCalendar" msgid="6716116972752441641">"कॅलेंडर इव्हेंट आणि तपशील वाचा"</string>
-    <string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"हा अॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि सामायिक करू शकतो किंवा आपला कॅलेंडर डेटा सेव्ह करू शकतो."</string>
-    <string name="permdesc_readCalendar" product="tv" msgid="8837931557573064315">"हा अॅप आपल्या टीव्हीवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि सामायिक करू शकतो किंवा आपला कॅलेंडर डेटा सेव्ह करू शकतो."</string>
-    <string name="permdesc_readCalendar" product="default" msgid="4373978642145196715">"हा अॅप आपल्या फोनवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि सामायिक करू शकतो किंवा आपला कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+    <string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"हा अॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+    <string name="permdesc_readCalendar" product="tv" msgid="8837931557573064315">"हा अॅप आपल्या टीव्हीवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+    <string name="permdesc_readCalendar" product="default" msgid="4373978642145196715">"हा अॅप आपल्या फोनवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
     <string name="permlab_writeCalendar" msgid="8438874755193825647">"कॅलेंडर इव्हेंट जोडा किंवा बदला आणि मालकाला न कळवता अतिथींना ईमेल पाठवा"</string>
     <string name="permdesc_writeCalendar" product="tablet" msgid="1675270619903625982">"हा अॅप आपल्या टॅब्लेटवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
     <string name="permdesc_writeCalendar" product="tv" msgid="9017809326268135866">"हा अॅप आपल्या टीव्हीवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"हा अॅप सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतांच्या आधारावर तुमचे स्थान मिळवू शकतो. अॅपला या स्थान सेवा वापरण्यास सक्षम असण्यासाठी तुमच्या टॅब्लेटवर त्या चालू केलेल्या आणि उपलब्ध असणे आवश्यक आहे."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"हा अॅप सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतांच्या आधारावर तुमचे स्थान मिळवू शकतो. अॅपला या स्थान सेवा वापरण्यास सक्षम असण्यासाठी तुमच्या टीव्हीवर त्या चालू केलेल्या आणि उपलब्ध असणे आवश्यक आहे."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"हा अॅप सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतांच्या आधारावर तुमचे स्थान मिळवू शकतो. अॅपला या स्थान सेवा वापरण्यास सक्षम असण्यासाठी तुमच्या फोनवर त्या चालू केलेल्या आणि उपलब्ध असणे आवश्यक आहे."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"बॅकग्राउंडमध्ये अचूक स्थान अ‍ॅक्सेस करा"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"हे अ‍ॅप बॅकग्राउंडमध्ये असताना अचूक स्थान कधीही मिळवू शकते. या स्थान सेवा सुरू करणे आणि त्या वापरण्यासाठी अ‍ॅपसाठी तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे, यामुळे बॅटरी वापर वाढू शकतो."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"बॅकग्राउंडमध्ये स्थान अॅक्सेस करू शकतो"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"याला अंदाजे किंवा अचूक स्थान अॅक्सेस करण्यास अतिरिक्त मंजूरी दिल्यास, बॅकग्राउंडमध्ये चालतांना अॅप स्थान अॅक्सेस करू शकतो."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"आपल्या ऑडिओ सेटिंग्ज बदला"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"व्हॉल्यूम आणि आउटपुटसाठी कोणता स्पीकर वापरला आहे यासारख्या समग्र ऑडिओ सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ऑडिओ रेकॉर्ड"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"हा अॅप कोणत्याही वेळी मायक्रोफोन वापरून ऑडिओ रेकॉर्ड करू शकता."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"सिम वर कमांड पाठवा"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"अ‍ॅप ला सिम वर कमांड पाठविण्‍याची अनुमती देते. हे खूप धोकादायक असते."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"शारीरिक अॅक्टिव्हिटी ओळखा"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"हे अॅप तुमच्या शारीरिक अॅक्टिव्हिटी ओळखू शकते."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"चित्रे आणि व्हिडिओ घ्या"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"हा अॅप कोणत्याही वेळी कॅमेरा वापरून चित्रेे घेऊ आणि व्ह‍िडिअो रेकॉर्ड करू शकतो."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"व्हायब्रेट नियंत्रित करा"</string>
@@ -454,7 +459,7 @@
     <string name="permdesc_transmitIr" product="default" msgid="7957763745020300725">"अ‍ॅप ला फोनच्‍या इन्‍फ्रारेड ट्रान्‍समीटरचा वापर करण्‍याची अनुमती देते."</string>
     <string name="permlab_setWallpaper" msgid="6627192333373465143">"वॉलपेपर सेट करा"</string>
     <string name="permdesc_setWallpaper" msgid="7373447920977624745">"सिस्टम वॉलपेपर सेट करण्यासाठी अॅप ला अनुमती देते."</string>
-    <string name="permlab_setWallpaperHints" msgid="3278608165977736538">"आपला वॉलपेपर आकार समायोजित करा"</string>
+    <string name="permlab_setWallpaperHints" msgid="3278608165977736538">"तुमचा वॉलपेपर आकार समायोजित करा"</string>
     <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"सिस्टम वॉलपेपर आकार सूचना सेट करण्यासाठी अॅप ला अनुमती देते."</string>
     <string name="permlab_setTimeZone" msgid="2945079801013077340">"टाइम झोन सेट करा"</string>
     <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"टॅब्लेटचा टाइम झोन बदलण्यासाठी अॅप ला अनुमती देते."</string>
@@ -478,7 +483,7 @@
     <string name="permdesc_changeWifiState" msgid="7137950297386127533">"वाय-फाय अॅक्सेस बिंदूंवर कनेक्ट करण्यासाठी आणि त्यावरून डिस्कनेक्ट करण्यासाठी आणि वाय-फाय नेटवर्कसाठी डिव्हाइस कॉंफिगरेशनमध्ये बदल करण्यासाठी अॅपला अनुमती देते."</string>
     <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"वाय-फाय मल्‍टिकास्‍ट रिसेप्‍शनला अनुमती द्या"</string>
     <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या टॅब्लेटवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
-    <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"केवळ आपला टीव्ही न वापरता, एकाधिक पत्ते वापरून एका वाय-फाय नेटवकवरील सर्व डीव्हाइसवर पाठविलेली पॅकेट प्राप्त करण्यासाठी अॅपला अनुमती देते."</string>
+    <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"केवळ तुमचा टीव्ही न वापरता, एकाधिक पत्ते वापरून एका वाय-फाय नेटवकवरील सर्व डीव्हाइसवर पाठविलेली पॅकेट प्राप्त करण्यासाठी अॅपला अनुमती देते."</string>
     <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या फोनवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
     <string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"ब्लूटूथ सेटिंग्ज अॅक्सेस करा"</string>
     <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"स्थानिक ब्लूटूथ टॅबलेट कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अॅप ला अनुमती देते."</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"अॅपला तुमच्या फोटो संग्रहामध्ये सुधारणा करण्याची अनुमती देते."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"तुमच्या मीडिया संग्रहातून स्थाने वाचा"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"अॅपला तुमच्या मीडिया संग्रहामध्येील स्थाने वाचण्यासाठी अनुमती देते."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"अॅप्लिकेशन <xliff:g id="APP">%s</xliff:g>ला ऑथेंटिकेट करायचे आहे."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"बायोमेट्रिक हार्डवेअर उपलब्ध नाही"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फिंगरप्रिंट आढळली. कृपया पुन्हा प्रयत्न करा."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फिंगरप्रिंटवर प्रक्रिया करणे शक्य झाले नाही. कृपया पुन्हा प्रयत्न करा."</string>
@@ -824,12 +830,12 @@
     <string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"वापरकर्ता मार्गदर्शक पहा किंवा कस्टमर केअरशी संपर्क साधा."</string>
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"सिम कार्ड लॉक केलेले आहे."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"सिम कार्ड अनलॉक करत आहे…"</string>
-    <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"तुम्ही आपला अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने काढला. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
-    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"तुम्ही आपला पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
-    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"तुम्ही आपला पिन <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"तुम्ही आपला अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीचा रेखांकित केला आहे. <xliff:g id="NUMBER_1">%2$d</xliff:g> अधिक अयशस्वी प्रयत्नांनंतर, तुम्हाला तुमचे Google साइन इन वापरून आपला टॅब्लेट अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"तुम्ही <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा आपला अनलॉक पॅटर्न अयोग्यरीत्या काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला तुमचे Google साइन इन वापरून आपला टीव्ही अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांनी पुन्हा प्रयत्न करा."</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"तुम्ही आपला अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे रेखांकित केला आहे. <xliff:g id="NUMBER_1">%2$d</xliff:g> अधिक अयशस्वी प्रयत्नांनंतर, तुम्हाला तुमचे Google साइन इन वापरून आपला फोन अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"तुम्ही तुमचा अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने काढला. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"तुम्ही तुमचा पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"तुम्ही तुमचा पिन <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"तुम्ही तुमचा अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीचा रेखांकित केला आहे. <xliff:g id="NUMBER_1">%2$d</xliff:g> अधिक अयशस्वी प्रयत्नांनंतर, तुम्हाला तुमचे Google साइन इन वापरून तुमचा टॅब्लेट अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"तुम्ही <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा तुमचा अनलॉक पॅटर्न अयोग्यरीत्या काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला तुमचे Google साइन इन वापरून तुमचा टीव्ही अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांनी पुन्हा प्रयत्न करा."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"तुम्ही तुमचा अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे रेखांकित केला आहे. <xliff:g id="NUMBER_1">%2$d</xliff:g> अधिक अयशस्वी प्रयत्नांनंतर, तुम्हाला तुमचे Google साइन इन वापरून तुमचा फोन अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"तुम्ही <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा टॅबलेट अनलॉक करण्याचे चुकीचे प्रयत्न केले. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, टॅबलेट फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि सर्व वापरकर्ता डेटा गमावला जाईल."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"तुम्ही <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा टीव्ही अनलॉक करण्याचा अयोग्यरित्या प्रयत्न केला. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, टीव्ही फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि सर्व वापरकर्ता डेटा गमावेल."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"तुम्ही <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा फोन अनलॉक करण्याचे चुकीचे प्रयत्न केले. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, फोन फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि सर्व वापरकर्ता डेटा गमावला जाईल."</string>
@@ -894,7 +900,7 @@
     <string name="js_dialog_title_default" msgid="6961903213729667573">"Javascript"</string>
     <string name="js_dialog_before_unload_title" msgid="2619376555525116593">"नेव्हिगेशनची पुष्टी करा"</string>
     <string name="js_dialog_before_unload_positive_button" msgid="3112752010600484130">"हे पृष्ठ सोडा"</string>
-    <string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"या पृष्ठावर रहा"</string>
+    <string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"या पेजवर रहा"</string>
     <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nआपल्‍याला खात्री आहे की तुम्ही या पृष्‍ठावरून नेव्‍हिगेट करू इच्‍छिता?"</string>
     <string name="save_password_label" msgid="6860261758665825069">"पुष्टी करा"</string>
     <string name="double_tap_toast" msgid="4595046515400268881">"टीप: झूम कमी करण्यासाठी आणि वाढवण्यासाठी दोनदा-टॅप करा."</string>
@@ -933,7 +939,7 @@
     <string name="save_password_notnow" msgid="6389675316706699758">"आत्ता नाही"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"लक्षात ठेवा"</string>
     <string name="save_password_never" msgid="8274330296785855105">"कधीही नाही"</string>
-    <string name="open_permission_deny" msgid="7374036708316629800">"आपल्याला हे पृष्ठ उघडण्याची परवानगी नाही."</string>
+    <string name="open_permission_deny" msgid="7374036708316629800">"तुम्हाला हे पृष्ठ उघडण्याची परवानगी नाही."</string>
     <string name="text_copied" msgid="4985729524670131385">"मजकूर क्लिपबोर्डवर कॉपी केला."</string>
     <string name="more_item_label" msgid="4650918923083320495">"अधिक"</string>
     <string name="prepend_shortcut_label" msgid="2572214461676015642">"मेनू+"</string>
@@ -1185,8 +1191,8 @@
     <string name="new_app_description" msgid="5894852887817332322">"<xliff:g id="OLD_APP">%1$s</xliff:g> सेव्ह न करता बंद होईल"</string>
     <string name="dump_heap_notification" msgid="2618183274836056542">"<xliff:g id="PROC">%1$s</xliff:g> ने मेमेरी मर्यादा वाढविली"</string>
     <string name="dump_heap_notification_detail" msgid="3993078784053054141">"हीप डंप गोळा केले. शेअर करण्यासाठी टॅप करा."</string>
-    <string name="dump_heap_title" msgid="5864292264307651673">"हीप डंप सामायिक करायचे?"</string>
-    <string name="dump_heap_text" msgid="4809417337240334941">"<xliff:g id="PROC">%1$s</xliff:g> प्रक्रियेने त्याची <xliff:g id="SIZE">%2$s</xliff:g> ची प्रक्रिया मेमरी मर्यादा ओलांडली आहे. त्याच्या विकासकासह सामायिक करण्यासाठी तुमच्यासाठी हीप डंप उपलब्ध आहे. सावधगिरी बाळगा: या हीप डंपमध्ये तुमची कोणतीही वैयक्तिक माहिती असू शकते ज्यात अॅप्लिकेशन प्रवेश करू शकतो."</string>
+    <string name="dump_heap_title" msgid="5864292264307651673">"हीप डंप शेअर करायचे?"</string>
+    <string name="dump_heap_text" msgid="4809417337240334941">"<xliff:g id="PROC">%1$s</xliff:g> प्रक्रियेने त्याची <xliff:g id="SIZE">%2$s</xliff:g> ची प्रक्रिया मेमरी मर्यादा ओलांडली आहे. त्याच्या विकासकासह शेअर करण्यासाठी तुमच्यासाठी हीप डंप उपलब्ध आहे. सावधगिरी बाळगा: या हीप डंपमध्ये तुमची कोणतीही वैयक्तिक माहिती असू शकते ज्यात अॅप्लिकेशन प्रवेश करू शकतो."</string>
     <string name="sendText" msgid="5209874571959469142">"मजकुरासाठी क्रिया निवडा"</string>
     <string name="volume_ringtone" msgid="6885421406845734650">"रिंगर व्हॉल्यूम"</string>
     <string name="volume_music" msgid="5421651157138628171">"मीडिया व्हॉल्यूम"</string>
@@ -1322,7 +1328,7 @@
     <string name="adb_active_notification_message" msgid="7463062450474107752">"USB डीबगिंग बंद करण्यासाठी टॅप करा"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB डीबगिंग बंद करण्यासाठी निवडा."</string>
     <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"बग रीपोर्ट घेत आहे..."</string>
-    <string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"बग अहवाल सामायिक करायचा?"</string>
+    <string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"बग अहवाल शेअर करायचा?"</string>
     <string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"बग रीपोर्ट शेअर करत आहे..."</string>
     <string name="share_remote_bugreport_notification_message_finished" msgid="6029609949340992866">"आपल्या प्रशासकाने या डिव्हाइसचे समस्या निवारण करण्यात मदत करण्यासाठी दोष अहवालाची विनंती केली. अॅप्स आणि डेटा शेअर केले जाऊ शकतात."</string>
     <string name="share_remote_bugreport_action" msgid="6249476773913384948">"शेअर करा"</string>
@@ -1441,7 +1447,7 @@
     <string name="next_button_label" msgid="1080555104677992408">"पुढील"</string>
     <string name="skip_button_label" msgid="1275362299471631819">"वगळा"</string>
     <string name="no_matches" msgid="8129421908915840737">"कोणत्याही जुळण्या नाहीत"</string>
-    <string name="find_on_page" msgid="1946799233822820384">"पृष्ठावर शोधा"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"पेजवर शोधा"</string>
     <plurals name="matches_found" formatted="false" msgid="1210884353962081884">
       <item quantity="one"><xliff:g id="TOTAL">%d</xliff:g> पैकी <xliff:g id="INDEX">%d</xliff:g></item>
       <item quantity="other"><xliff:g id="TOTAL">%d</xliff:g> पैकी <xliff:g id="INDEX">%d</xliff:g></item>
@@ -1503,7 +1509,7 @@
     <string name="action_menu_overflow_description" msgid="2295659037509008453">"अधिक पर्याय"</string>
     <string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
     <string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
-    <string name="storage_internal" msgid="3570990907910199483">"अंतर्गत सामायिक केलेला स्टोरेज"</string>
+    <string name="storage_internal" msgid="3570990907910199483">"अंतर्गत शेअर केलेला स्टोरेज"</string>
     <string name="storage_sd_card" msgid="3282948861378286745">"SD कार्ड"</string>
     <string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD कार्ड"</string>
     <string name="storage_usb_drive" msgid="6261899683292244209">"USB ड्राइव्‍ह"</string>
@@ -1601,18 +1607,18 @@
     <string name="kg_login_invalid_input" msgid="5754664119319872197">"अवैध वापरकर्तानाव किंवा पासवर्ड."</string>
     <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"तुमचे वापरकर्तानाव किंवा पासवर्ड विसरलात?\n "<b>"google.com/accounts/recovery"</b>" ला भेट द्या."</string>
     <string name="kg_login_checking_password" msgid="1052685197710252395">"खाते तपासत आहे…"</string>
-    <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"तुम्ही आपला पिन <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
-    <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"तुम्ही आपला पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
-    <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"तुम्ही आपला अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने काढला. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"तुम्ही तुमचा पिन <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"तुम्ही तुमचा पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"तुम्ही तुमचा अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने काढला. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
     <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"तुम्ही <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा टॅबलेट अनलॉक करण्याचा अयोग्यपणे प्रयत्न केला. <xliff:g id="NUMBER_1">%2$d</xliff:g> आणखी अयशस्वी प्रयत्नांनंतर, टॅबलेट फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि वापरकर्ता डेटा गमावेल."</string>
     <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"तुम्ही <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा टीव्ही अनलॉक करण्याचा अयोग्यरित्या प्रयत्न केला. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, टीव्ही फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि सर्व वापरकर्ता डेटा गमावेल."</string>
     <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"तुम्ही <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा फोन अनलॉक करण्याचा अयोग्यपणे प्रयत्न केला. <xliff:g id="NUMBER_1">%2$d</xliff:g> आणखी अयशस्वी प्रयत्नांनंतर, फोन फॅक्टरी डीफॉल्टवर रीसेट केला जाईल आणि वापरकर्ता डेटा गमावेल."</string>
     <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"तुम्ही <xliff:g id="NUMBER">%d</xliff:g> वेळा टॅबलेट अनलॉक करण्याचा अयोग्यपणे प्रयत्न केला. टॅबलेट आता फॅक्टरी डीफॉल्ट वर रीसेट केला जाईल."</string>
     <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"तुम्ही <xliff:g id="NUMBER">%d</xliff:g> वेळा टीव्ही अनलॉक करण्याचा अयोग्यरित्या प्रयत्न केला. टीव्ही आता फॅक्टरी डीफॉल्टवर रीसेट केला जाईल."</string>
     <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"तुम्ही <xliff:g id="NUMBER">%d</xliff:g> वेळा फोन अनलॉक करण्याचा अयोग्यपणे प्रयत्न केला. फोन आता फॅक्टरी डीफॉल्ट वर रीसेट केला जाईल."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"तुम्ही आपला अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला ईमेल खाते वापरून आपला टॅब्लेट अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"तुम्ही <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा आपला अनलॉक पॅटर्न अयोग्यरीत्या काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला ईमेल खाते वापरून आपला टीव्ही अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांनी पुन्हा प्रयत्न करा."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"तुम्ही आपला अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला ईमेल खाते वापरून आपला फोन अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"तुम्ही तुमचा अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला ईमेल खाते वापरून तुमचा टॅब्लेट अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"तुम्ही <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा तुमचा अनलॉक पॅटर्न अयोग्यरीत्या काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला ईमेल खाते वापरून तुमचा टीव्ही अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांनी पुन्हा प्रयत्न करा."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"तुम्ही तुमचा अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला ईमेल खाते वापरून तुमचा फोन अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"काढा"</string>
     <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"शिफारस केलेल्‍या पातळीच्या वर आवाज वाढवायचा?\n\nउच्च आवाजात दीर्घ काळ ऐकण्‍याने आपल्‍या श्रवणशक्तीची हानी होऊ शकते."</string>
@@ -1809,7 +1815,7 @@
     <string name="zen_mode_default_events_name" msgid="8158334939013085363">"इव्‍हेंट"</string>
     <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"निष्क्रिय आहे"</string>
     <string name="muted_by" msgid="5942954724562097128">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> काही ध्‍वनी म्‍यूट करत आहे"</string>
-    <string name="system_error_wipe_data" msgid="6608165524785354962">"आपल्‍या डिव्‍हाइसमध्‍ये अंतर्गत समस्‍या आहे आणि आपला फॅक्‍टरी डेटा रीसेट होईपर्यंत ती अस्‍थिर असू शकते."</string>
+    <string name="system_error_wipe_data" msgid="6608165524785354962">"आपल्‍या डिव्‍हाइसमध्‍ये अंतर्गत समस्‍या आहे आणि तुमचा फॅक्‍टरी डेटा रीसेट होईपर्यंत ती अस्‍थिर असू शकते."</string>
     <string name="system_error_manufacturer" msgid="8086872414744210668">"आपल्‍या डिव्‍हाइसमध्‍ये अंतर्गत समस्‍या आहे. तपशीलांसाठी आपल्‍या निर्मात्याशी संपर्क साधा."</string>
     <string name="stk_cc_ussd_to_dial" msgid="5214333646366591205">"USSD विनंती नियमित कॉलवर बदलली"</string>
     <string name="stk_cc_ussd_to_ss" msgid="4884994189414782605">"USSD विनंती SS विनंतीवर बदलली"</string>
@@ -1898,14 +1904,14 @@
       <item quantity="one">आपोआप भरण्याची <xliff:g id="COUNT">%1$s</xliff:g> सूचना</item>
       <item quantity="other">आपोआप भरण्याच्या <xliff:g id="COUNT">%1$s</xliff:g> सूचना</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; मध्ये सेव्ह करायचे का?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"&lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;मध्ये <xliff:g id="TYPE">%1$s</xliff:g> सेव्ह करायची?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"&lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;मध्ये <xliff:g id="TYPE_0">%1$s</xliff:g> आणि <xliff:g id="TYPE_1">%2$s</xliff:g> सेव्ह करायची?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"&lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;मध्ये <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> आणि <xliff:g id="TYPE_2">%3$s</xliff:g> सेव्ह करायची?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; वर अपडेट करायचे आहे का?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g>&lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g> &lt;/b&gt; वर अपडेट करायचे आहे का?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> आणि <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; वर अपडेट करायचा आहे का?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> आणि <xliff:g id="TYPE_2">%3$s</xliff:g>&lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;वर अपडेट करायचा आहे का?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" मध्ये सेव्ह करायचे का?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g>, "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" मध्ये सेव्ह करायचे का?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> आणि <xliff:g id="TYPE_1">%2$s</xliff:g>, "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" मध्ये सेव्ह करायचे का?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> आणि <xliff:g id="TYPE_2">%3$s</xliff:g>, "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" मध्ये सेव्ह करायचे का?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" मध्ये अपडेट करायचे का?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g>, "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" मध्ये अपडेट करायचे का?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> आणि <xliff:g id="TYPE_1">%2$s</xliff:g>, "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" मध्ये अपडेट करायचे का?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"हे आयटम "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> आणि <xliff:g id="TYPE_2">%3$s</xliff:g> मध्ये अपडेट करायचे का?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"सेव्ह करा"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"नाही, नको"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"अपडेट करा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 208fed6..2896923 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"rakam audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merakam audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Pengecaman aktiviti"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"camkan aktiviti"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengecam aktiviti fizikal anda?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ambil gambar dan rakam video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengambil gambar dan merakam video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Apl ini boleh mendapatkan lokasi anda berdasarkan sumber rangkaian seperti menara selular dan rangkaian Wi-Fi. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada tablet anda untuk membolehkan apl menggunakan perkhidmatan tersebut."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Apl ini boleh mendapatkan lokasi anda berdasarkan sumber rangkaian seperti menara selular dan rangkaian Wi-Fi. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada TV anda untuk membolehkan apl menggunakan perkhidmatan tersebut."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Apl ini boleh mendapatkan lokasi anda berdasarkan sumber rangkaian seperti menara selular dan rangkaian Wi-Fi. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada telefon anda untuk membolehkan apl menggunakan perkhidmatan tersebut."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"akses lokasi tepat di latar belakang"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Apl ini boleh mendapatkan lokasi tepat anda setiap kali apl tersebut berada di latar belakang. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada telefon anda untuk membolehkan apl menggunakan perkhidmatan tersebut. Tindakan ini mungkin meningkatkan penggunaan bateri."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"akses lokasi di latar belakang"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Jika tindakan ini dibenarkan bagi akses lokasi anggaran atau lokasi tepat, apl tersebut boleh mengakses lokasi itu semasa berjalan di latar belakang."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"tukar tetapan audio anda"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Membenarkan apl untuk mengubah suai tetapan audio global seperti kelantangan dan pembesar suara mana digunakan untuk output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"rakam audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Apl ini boleh merakam audio menggunakan mikrofon pada bila-bila masa."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"hantar perintah ke SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Membenarkan apl menghantar arahan kepada SIM. Ini amat berbahaya."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"camkan aktiviti fizikal"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Apl ini dapat mengecam aktiviti fizikal anda."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ambil gambar dan video"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Apl ini boleh mengambil gambar dan merakam video menggunakan kamera pada bila-bila masa."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kawal getaran"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Membenarkan apl mengubah suai koleksi foto anda."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"baca lokasi daripada koleksi media anda"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Membenarkan apl membaca lokasi daripada koleksi media anda."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikasi <xliff:g id="APP">%s</xliff:g> mahu membuat pengesahan."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Perkakasan biometrik tidak tersedia"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Cap jari separa dikesan. Sila cuba lagi."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Tidak dapat memproses cap jari. Sila cuba lagi."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> cadangan autolengkap</item>
       <item quantity="one">Satu cadangan autolengkap</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Simpan ke &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Simpan <xliff:g id="TYPE">%1$s</xliff:g> ke &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Simpan <xliff:g id="TYPE_0">%1$s</xliff:g> dan <xliff:g id="TYPE_1">%2$s</xliff:g> ke &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Simpan <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> dan <xliff:g id="TYPE_2">%3$s</xliff:g> ke &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Kemas kini ke &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Kemas kini <xliff:g id="TYPE">%1$s</xliff:g> ke &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Kemas kini <xliff:g id="TYPE_0">%1$s</xliff:g> dan <xliff:g id="TYPE_1">%2$s</xliff:g> ke &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Kemas kini <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> dan <xliff:g id="TYPE_2">%3$s</xliff:g> ke &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Simpan pada "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Simpan <xliff:g id="TYPE">%1$s</xliff:g> pada "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Simpan <xliff:g id="TYPE_0">%1$s</xliff:g> dan <xliff:g id="TYPE_1">%2$s</xliff:g> pada "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Simpan <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> dan <xliff:g id="TYPE_2">%3$s</xliff:g> pada "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Kemas kini dalam "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Kemas kini <xliff:g id="TYPE">%1$s</xliff:g> dalam "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Kemas kini <xliff:g id="TYPE_0">%1$s</xliff:g> dan <xliff:g id="TYPE_1">%2$s</xliff:g> dalam "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Kemas kini item ini dalam "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> dan <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Simpan"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Tidak, terima kasih"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Kemas kini"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 71dd4b6..e509577 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"မိုက်ခရိုဖုန်း"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"အသံဖမ်းခြင်း"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား အသံဖမ်းယူခွင့် ပေးလိုပါသလား။"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"လှုပ်ရှားမှုကို မှတ်သားပါ"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"လှုပ်ရှားမှုကို မှတ်သားခြင်း"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သင်၏ကိုယ်လက်လှုပ်ရှားမှုကို မှတ်သားခွင့်ပေးလိုပါသလား။"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"ကင်မရာ"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ဓာတ်ပုံ ရိုက်ပြီးနောက် ဗွီဒီယို မှတ်တမ်းတင်ရန်"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား ဓာတ်ပုံနှင့် ဗီဒီယိုရိုက်ကူးခွင့် ပေးလိုပါသလား။"</string>
@@ -387,9 +390,9 @@
     <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"ထုတ်လွှင့်ခြင်းများ ပြီးဆုံးသည့်နောက် ဆက်လက်ရှိနေသည့်၊ တည်မြဲ ထုတ်လွှင့်မှုများပို့ရန် အက်ပ်အား ခွင့်ပြုပါ။ အလွန်အကျွံ လုပ်ဆောင်ပါက တီဗွီ နှေးသွားခြင်း သို့မဟုတ် မှတ်ဉာဏ်အသုံးများမှုကြောင့် မတည်မငြိမ်ဖြစ်ခြင်းများ ဖြစ်တတ်၏။"</string>
     <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"အက်ပ်အား ကြာရှည်ခံ ထုတ်လွှင့်မှု ပြုပါ။ ဤထုတ်လွှင့်မှုများဟာ ထုတ်လွှင့်မှု ပြီးဆုံးပြီးသွားတည့်တိုင် ကျန်နေမည် ဖြစ်ပါသည်။ အလွန်အကျွံသုံးခြင်းကြောင့် မှတ်ဉာဏ်အသုံးများပြီး ဖုန်းနှေးခြင်း၊ မတည်ငြိမ်ခြင်း ဖြစ်နိုင်ပါသည်"</string>
     <string name="permlab_readContacts" msgid="8348481131899886131">"အဆက်အသွယ်များအား ဖတ်ခြင်း"</string>
-    <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"အပလီကေးရှင်းအား ခေါ်ဆိုသော အကြိမ်ရေ၊ အီးမေးလ်အကြိမ်ရေ၊ တခြားဆက်သွယ်မှုများစသည်ကဲ့သို့ သင့်တက်ဘလက်မှာ သိမ်းဆည်းထားသော အဆက်အသွယ်များရဲ့ အချက်အလက်ကို ဖတ်ခွင့်ပြုပါ။ ဤသို့ခွင့်ပြုခြင်းအားဖြင့် အပလီကေးရှင်းများကို သင့် အဆက်အသွယ်၏ အချက်မလက်များကို သိမ်းဆည်းရန် ခွင့်ပြုပြီး အန္တရာယ်ရှိသော အပလီကေးရှင်းများမှ ထိုအချက်အလက်များ ကို သင် မသိစေပဲ ဖြန့်ဝေနိုင််မည် ဖြစ်ပါသည်။"</string>
-    <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"တစ်ဦးတစ်​ယောက်ထံ သင်ခေါ်ထားသော၊ အီးမေးိပု့ထားသော၊ သို့မဟုတ် တစ်ခြားနည်းဖြင့် အဆက်အသွယ်ပြုထားသော အကြိမ်အရေအတွက် အပါအဝင်၊ သင့်တီဗွီတွင် သိမ်းထားသည့် အဆက်အသွယ်ဆိုင်ရာ အချက်အလက်များ ဖတ်ရန် အက်ပ်အား ခွင့်ပြုပါ။ ဤနည်းဖြင့် သင့် အဆက်အသွယ် အချက်အလက်များအား သိမ်းဆည်းရန် အက်ပ်အား ခွင့်ပြုထားခြင်းဖြစ်ပြီး၊  အဆက်အသွယ် အချက်အလက်များအား အန္တရာယ်ရှိသော အက်ပ်များက သင်မသိဘဲ ဝေမျှနိုင်သည်။"</string>
-    <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"အပလီကေးရှင်းအား ခေါ်ဆိုသော အကြိမ်ရေ၊ အီးမေးလ်အကြိမ်ရေ၊ တခြားဆက်သွယ်မှုများစသည်ကဲ့သို့ သင့်ဖုန်းမှာ သိမ်းဆည်းထားသော အဆက်အသွယ်များရဲ့ အချက်အလက်ကို ဖတ်ခွင့်ပြုပါ။ ဤသို့ခွင့်ပြုခြင်းအားဖြင့် အပလီကေးရှင်းများကို သင့် အဆက်အသွယ်၏ အချက်မလက်များကို သိမ်းဆည်းရန် ခွင့်ပြုပြီး အန္တရာယ်ရှိသော အပလီကေးရှင်းများမှ ထိုအချက်အလက်များ ကို သင် မသိစေပဲ ဖြန့်ဝေနိုင််မည် ဖြစ်ပါသည်။"</string>
+    <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"အပလီကေးရှင်းအား ခေါ်ဆိုသော အကြိမ်ရေ၊ အီးမေးလ်အကြိမ်ရေ၊ တခြားဆက်သွယ်မှုများစသည်ကဲ့သို့ သင့်တက်ဘလက်မှာ သိမ်းဆည်းထားသော အဆက်အသွယ်များရဲ့ အချက်အလက်ကို ဖတ်ခွင့်ပြုပါ။ ဤသို့ခွင့်ပြုခြင်းအားဖြင့် အပလီကေးရှင်းများကို သင့် အဆက်အသွယ်၏ အချက်မလက်များကို သိမ်းရန် ခွင့်ပြုပြီး အန္တရာယ်ရှိသော အပလီကေးရှင်းများမှ ထိုအချက်အလက်များ ကို သင် မသိစေပဲ ဖြန့်ဝေနိုင််မည် ဖြစ်ပါသည်။"</string>
+    <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"တစ်ဦးတစ်​ယောက်ထံ သင်ခေါ်ထားသော၊ အီးမေးိပု့ထားသော၊ သို့မဟုတ် တစ်ခြားနည်းဖြင့် အဆက်အသွယ်ပြုထားသော အကြိမ်အရေအတွက် အပါအဝင်၊ သင့်တီဗွီတွင် သိမ်းထားသည့် အဆက်အသွယ်ဆိုင်ရာ အချက်အလက်များ ဖတ်ရန် အက်ပ်အား ခွင့်ပြုပါ။ ဤနည်းဖြင့် သင့် အဆက်အသွယ် အချက်အလက်များအား သိမ်းရန် အက်ပ်အား ခွင့်ပြုထားခြင်းဖြစ်ပြီး၊  အဆက်အသွယ် အချက်အလက်များအား အန္တရာယ်ရှိသော အက်ပ်များက သင်မသိဘဲ ဝေမျှနိုင်သည်။"</string>
+    <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"အပလီကေးရှင်းအား ခေါ်ဆိုသော အကြိမ်ရေ၊ အီးမေးလ်အကြိမ်ရေ၊ တခြားဆက်သွယ်မှုများစသည်ကဲ့သို့ သင့်ဖုန်းမှာ သိမ်းဆည်းထားသော အဆက်အသွယ်များရဲ့ အချက်အလက်ကို ဖတ်ခွင့်ပြုပါ။ ဤသို့ခွင့်ပြုခြင်းအားဖြင့် အပလီကေးရှင်းများကို သင့် အဆက်အသွယ်၏ အချက်မလက်များကို သိမ်းရန် ခွင့်ပြုပြီး အန္တရာယ်ရှိသော အပလီကေးရှင်းများမှ ထိုအချက်အလက်များ ကို သင် မသိစေပဲ ဖြန့်ဝေနိုင််မည် ဖြစ်ပါသည်။"</string>
     <string name="permlab_writeContacts" msgid="5107492086416793544">"အဆက်အသွယ်များအား ပြင်ဆင်ခြင်း"</string>
     <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"အပလီကေးရှင်းအား သင့်တက်ဘလက်မှာ သိမ်းဆည်းထားသော အဆက်အသွယ်များရဲ့ အချက်အလက် (အထူးအဆက်အသွယ်များအား ခေါ်ဆိုသော အကြိမ်ရေ၊ အီးမေးလ်ပို့သောအကြိမ်ရေ သို့ အခြားနည်းလမ်းဖြင့်ဆက်သွယ်မှုများ) ကို ပြင်ဆင်ခွင့်ပြုခြင်း။ ဒီခွင့်ပြုချက်က အပလီကေးရှင်းများအား အဆက်အသွယ် အချက်အလက်များ ဖျက်စီးခြင်း လုပ်ဆောင်စေနိုင်မှာ ဖြစ်ပါသည်။"</string>
     <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"အထူးအဆက်အသွယ်များအား ခေါ်ဆိုသော အကြိမ်ရေ၊ အီးမေးလ်ပို့သောအကြိမ်ရေ သို့ အခြားနည်းလမ်းဖြင့်ဆက်သွယ်မှုများ အပါအဝင်၊ သင့်တီဗွီတွင် သိမ်းဆည်းထားသော အဆက်အသွယ်များ၏ အချက်အလက်အား ပြင်ဆင်ရန် အပလီကေးရှင်းအား ခွင့်ပြုပါ။ ဤသို့ ခွင့်ပြုခြင်းသည် အဆက်အသွယ် အချက်အလက်များ ဖျက်ဆီးရန် အပလီကေးရှင်းများအား  ခွင့်ပြုခြင်းဖြစ်၏။"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ဤအက်ပ်သည် ဆဲလ်တာဝါများနှင့် Wi-Fi ကွန်ရက်များကဲ့သို့ ကွန်ရက်အရင်းအမြစ်ပေါ် အခြေခံပြီး သင့်တည်နေရာအချက်အလက်ကို ရယူနိုင်ပါသည်။ အက်ပ်က အသုံးပြုနိုင်ရန်အတွက် ဤတည်နေရာ ဝန်ဆောင်မှုများကို ဖွင့်ထားရမည် ဖြစ်ပြီး သင့်တက်ဘလက်ပေါ်တွင် ရရှိနိုင်ရပါမည်။"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ဤအက်ပ်သည် ဆဲလ်တာဝါများနှင့် Wi-Fi ကွန်ရက်များကဲ့သို့ ကွန်ရက်အရင်းအမြစ်ပေါ် အခြေခံပြီး သင့်တည်နေရာအချက်အလက်ကို ရယူနိုင်ပါသည်။ အက်ပ်က အသုံးပြုနိုင်ရန်အတွက် ဤတည်နေရာ ဝန်ဆောင်မှုများကို ဖွင့်ထားရမည် ဖြစ်ပြီး သင့်တီဗီပေါ်တွင် ရရှိနိုင်ရပါမည်။"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ဤအက်ပ်သည် ဆဲလ်တာဝါများနှင့် Wi-Fi ကွန်ရက်များကဲ့သို့ ကွန်ရက်အရင်းအမြစ်ပေါ် အခြေခံပြီး သင့်တည်နေရာအချက်အလက်ကို ရယူနိုင်ပါသည်။ အက်ပ်က အသုံးပြုနိုင်ရန်အတွက် ဤတည်နေရာ ဝန်ဆောင်မှုများကို ဖွင့်ထားရမည် ဖြစ်ပြီး သင့်ဖုန်းပေါ်တွင် ရရှိနိုင်ရပါမည်။"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"နောက်ခံတွင် တည်နေရာအတိအကျ အသုံးပြုခြင်း"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"နောက်ခံတွင် ဖွင့်ထားသည့်အခါတိုင်း ဤအက်ပ်က သင်၏တည်နေရာအတိအကျကို ရယူနိုင်ပါသည်။ သင်၏ဖုန်းတွင် အက်ပ်ကအသုံးပြုရန်အတွက် ဤတည်နေရာဝန်ဆောင်မှုများကို ဖွင့်ထားပြီး အသုံးပြု၍ ရပါမည်။ ၎င်းက ဘက်ထရီ ပိုကုန်နိုင်ပါသည်။"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"နောက်ခံတွင် တည်နေရာကို အသုံးပြုရန်"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"ခန့်မှန်းခြေ သို့မဟုတ် တိကျသော တည်နေရာ ဝင်သုံးခွင့်အတွက် ၎င်းကို နောက်ဆက်တွဲ ခွင့်ပြုထားပါက နောက်ခံတွင် လုပ်ဆောင်နေစဉ် အက်ပ်က တည်နေရာကို ရယူအသုံးပြုနိုင်ပါသည်။"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"သင့်အသံအပြင်အဆင်အားပြောင်းခြင်း"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"အပလီကေးရှင်းအား အသံအတိုးအကျယ်နှင့် အထွက်ကို မည်သည့်စပီကာကို သုံးရန်စသည်ဖြင့် စက်တစ်ခုလုံးနှင့်ဆိုင်သော အသံဆိုင်ရာ ဆက်တင်များ ပြင်ဆင်ခွင့် ပြုရန်"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"အသံဖမ်းခြင်း"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"ဤအက်ပ်သည် မိုက်ခရိုဖုန်းကို အသုံးပြုပြီး အချိန်မရွေး အသံသွင်းနိုင်ပါသည်။"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM ထံသို့ ညွှန်ကြားချက်များကို ပို့ပါ"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"အက်ပ်အား ဆင်းမ်ကဒ်ဆီသို့ အမိန့်များ ပေးပို့ခွင့် ပြုခြင်း။ ဤခွင့်ပြုမှုမှာ အန္တရာယ်အလွန် ရှိပါသည်။"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"ကိုယ်လက်လှုပ်ရှားမှုကို မှတ်သားပါ"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"ဤအက်ပ်က သင်၏ကိုယ်လက်လှုပ်ရှားမှုကို မှတ်သားနိုင်ပါသည်။"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ဓါတ်ပုံနှင့်ဗွီဒီယိုရိုက်ခြင်း"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"ဤအက်ပ်သည် ကင်မရာကို အသုံးပြု၍ ဓာတ်ပုံနှင့် ဗီဒီယိုများကို အချိန်မရွေး ရိုက်ကူးနိုင်ပါသည်။"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"တုန်ခုန်မှုအား ထိန်းချုပ်ခြင်း"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"အက်ပ်အား သင့်ဓာတ်ပုံစုစည်းမှုကို ပြုပြင်ခွင့်ပေးသည်။"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"သင့်မီဒီယာစုစည်းမှုမှ တည်နေရာများကို ဖတ်ခြင်း"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"အက်ပ်အား သင့်မီဒီယာစုစည်းမှုမှ တည်နေရာများကို ဖတ်ခွင့်ပေးသည်။"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"အပလီကေးရှင်း <xliff:g id="APP">%s</xliff:g> က အထောက်အထားစိစစ်လိုသည်။"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ဇီဝအချက်အလက်သုံး ကွန်ပျူတာစက်ပစ္စည်း မရရှိနိုင်ပါ"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"လက်ဗွေရဦ တစ်ပိုင်းတစ်စ တွေ့ရှိသည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"လက်ဗွေရာယူခြင်း မဆောင်ရွက်နိုင်ပါ။ ထပ်မံကြိုးစားပါ။"</string>
@@ -1898,14 +1904,14 @@
       <item quantity="other">အော်တိုဖြည့်အကြံပြုချက် <xliff:g id="COUNT">%1$s</xliff:g> ခု</item>
       <item quantity="one">အော်တိုဖြည့်အကြံပြုချက် တစ်ခု</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; တွင် သိမ်းဆည်းလိုပါသလား။"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> ကို &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; တွင် သိမ်းဆည်းလိုပါသလား။"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> နှင့် <xliff:g id="TYPE_1">%2$s</xliff:g> ကို &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; တွင် သိမ်းဆည်းလိုပါသလား။"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>၊ <xliff:g id="TYPE_1">%2$s</xliff:g>  နှင့် <xliff:g id="TYPE_2">%3$s</xliff:g> ကို &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; တွင် သိမ်းဆည်းလိုပါသလား။"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; သို့ အပ်ဒိတ်လုပ်လိုပါသလား။"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> ကို &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; သို့ အပ်ဒိတ်လုပ်လိုပါသလား။"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> နှင့် <xliff:g id="TYPE_1">%2$s</xliff:g> ကို &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; သို့ အပ်ဒိတ်လုပ်လိုပါသလား။"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>၊ <xliff:g id="TYPE_1">%2$s</xliff:g> နှင့် <xliff:g id="TYPE_2">%3$s</xliff:g> ကို &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; သို့ အပ်ဒိတ်လုပ်လိုပါသလား။"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"တွင် သိမ်းလိုပါသလား။"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> ကို "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"တွင် သိမ်းဆည်းလိုပါသလား။"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> နှင့် <xliff:g id="TYPE_1">%2$s</xliff:g> ကို "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"တွင် သိမ်းလိုပါသလား။"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>၊ <xliff:g id="TYPE_1">%2$s</xliff:g> နှင့် <xliff:g id="TYPE_2">%3$s</xliff:g> ကို "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"တွင် သိမ်းဆည်းလိုပါသလား။"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"တွင် အပ်ဒိတ်လုပ်လိုပါသလား။"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g> ကို "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" တွင် အပ်ဒိတ်လုပ်လိုပါသလား။"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> နှင့် <xliff:g id="TYPE_1">%2$s</xliff:g> ကို "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"တွင် အပ်ဒိတ်လုပ်လိုပါသလား။"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"ဤအချက်အလက်များကို "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"တွင် အပ်ဒိတ်လုပ်လိုပါသလား- <xliff:g id="TYPE_0">%1$s</xliff:g>၊ <xliff:g id="TYPE_1">%2$s</xliff:g> နှင့် <xliff:g id="TYPE_2">%3$s</xliff:g>။"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"သိမ်းရန်"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"မလိုပါ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"အပ်ဒိတ်လုပ်ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 28e7d33..b47c965 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ta opp lyd"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ta opp lyd?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Aktivitetsgjenkjenning"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"gjenkjenn aktivitet"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gjenkjenne den fysiske aktiviteten din?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ta bilder og ta opp video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ta bilder og spille inn video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Denne appen kan få posisjonen din fra nettverkskilder som mobilmaster og Wi-Fi-nettverk. Disse posisjonstjenestene må være slått på og tilgjengelige på nettbrettet ditt for at appen skal kunne bruke dem."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Denne appen kan få posisjonen din fra nettverkskilder som mobilmaster og Wi-Fi-nettverk. Disse posisjonstjenestene må være slått på og tilgjengelige på TV-en din for at appen skal kunne bruke dem."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Denne appen kan få posisjonen din fra nettverkskilder som mobilmaster og Wi-Fi-nettverk. Disse posisjonstjenestene må være slått på og tilgjengelige på telefonen din for at appen skal kunne bruke dem."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"tilgang til nøyaktig posisjon i bakgrunnen"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Denne appen kan få den nøyaktige posisjonen din når den er på i bakgrunnen. Disse posisjonstjenestene må være slått på og tilgjengelige på telefonen din for at appen skal kunne bruke dem. Dette kan øke batteriforbruket."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"tilgang til posisjon i bakgrunnen"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Hvis du gir denne tillatelsen, får appen tilgang til posisjonen mens den kjører i bakgrunnen, i tillegg til tilgang til omtrentlig eller presis posisjon."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"endre lydinnstillinger"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Lar appen endre globale lydinnstillinger slik som volum og hvilken høyttaler som brukes for lydavspilling."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ta opp lyd"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Denne appen kan når som helst ta opp lyd med mikrofonen."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"sende kommandoer til SIM-kortet"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Lar appen sende kommandoer til SIM-kortet. Dette er veldig farlig."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"gjenkjenn fysisk aktivitet"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Denne appen kan gjenkjenne den fysiske aktiviteten din."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ta bilder og videoer"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Denne appen kan når som helst ta bilder og spille inn videoer ved hjelp av kameraet."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrollere vibreringen"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Lar appen gjøre endringer i bildesamlingen din."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"lese posisjoner fra mediesamlingen din"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Lar appen lese posisjoner fra mediesamlingen din."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Appen <xliff:g id="APP">%s</xliff:g> vil autentisere."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrisk maskinvare er utilgjengelig"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Deler av fingeravtrykket er registrert. Prøv på nytt."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kunne ikke registrere fingeravtrykket. Prøv på nytt."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> forslag til autofyll</item>
       <item quantity="one">Ett forslag til autofyll</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Vil du lagre i &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Vil du lagre <xliff:g id="TYPE">%1$s</xliff:g> i &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Vil du lagre <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> i &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Vil du lagre <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g> i &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Vil du oppdatere til &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Vil du oppdatere <xliff:g id="TYPE">%1$s</xliff:g> til &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Vil du oppdatere <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> til &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Vil du oppdatere <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g> til &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Vil du lagre i "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Vil du lagre <xliff:g id="TYPE">%1$s</xliff:g> i "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Vil du lagre <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> i "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Vil du lagre <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g> i "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Vil du oppdatere i "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Vil du oppdatere <xliff:g id="TYPE">%1$s</xliff:g> i "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Vil du oppdatere <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> i "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Vil du oppdatere disse elementene i "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Lagre"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Nei takk"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Oppdater"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 875f87d..47d4d6a 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"माइक्रोफोन"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"अडियो रेकर्ड गर्नुहोस्"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई अडियो रेकर्ड गर्न दिने हो?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"गतिविधिको पहिचान"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"गतिविधि पहिचान गर्नुहोस्‌"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई तपाईंको गतिविधि पहिचान गर्न अनुमति दिने हो?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"क्यामेरा"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"तस्बिर खिच्नुका साथै भिडियो रेकर्ड गर्नुहोस्"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई तस्बिरहरू खिच्न र भिडियो रेकर्ड गर्न दिने हो?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"यस अनुप्रयोगले सेलका टावर र Wi-Fi नेटवर्कहरू जस्ता नेटवर्कका स्रोतहरूको आधारमा तपाईंको स्थान बारे जानकारी प्राप्त गर्न सक्छ। यो अनुप्रयोग ती स्रोतहरूको प्रयोग गर्न सक्षम होस् भन्नका खातिर यी स्थान सम्बन्धी सेवाहरूलाई अनिवार्य रूपमा सक्रिय पार्नुपर्छ र यी तपाईंको ट्याब्लेटमा उपलब्ध हुनु पर्छ।"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"यस अनुप्रयोगले सेलका टावर र Wi-Fi नेटवर्कहरू जस्ता नेटवर्कका स्रोतहरूको आधारमा तपाईंको स्थान बारे जानकारी प्राप्त गर्न सक्छ। यो अनुप्रयोग ती स्रोतहरूको प्रयोग गर्न सक्षम होस् भन्नका खातिर यी स्थान सम्बन्धी सेवाहरूलाई अनिवार्य रूपमा सक्रिय पार्नुपर्छ र यी तपाईंको TV मा उपलब्ध हुनु पर्छ।"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"यस अनुप्रयोगले सेलका टावर र Wi-Fi नेटवर्कहरू जस्ता नेटवर्कका स्रोतहरूको आधारमा तपाईंको स्थान बारे जानकारी प्राप्त गर्न सक्छ। यो अनुप्रयोग ती स्रोतहरूको प्रयोग गर्न सक्षम होस् भन्नका खातिर यी स्थान सम्बन्धी सेवाहरूलाई अनिवार्य रूपमा सक्रिय पार्नुपर्छ र यी तपाईंको फोनमा उपलब्ध हुनु पर्छ।"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"पृष्ठभूमिमा सटीक स्थानमाथि पहुँच राख्नुहोस्"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"यो अनुप्रयोगले पृष्ठभूमिमा चलिरहेको अवस्थामा तपाईंलाई जुनसुकै बेला स्थानको सटिक जानकारी दिन सक्छ। यी स्थानसम्बन्धी सेवाहरू अनिवार्य रूपमा सक्रिय गरिएका हुनु पर्छ र अनुप्रयोगले यिनीहरूको प्रयोग गर्न सकोस् भन्नाका खातिर तपाईंको फोनमै उपलब्ध हुन्छन्। यस कार्यले गर्दा ब्याट्री बढी खर्च हुन सक्छ।"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"पृष्ठभूमिमा स्थानसम्बन्धी पहुँच"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"यसका अतिरिक्त यसलाई अनुमानित वा सटिक स्थानमाथि पहुँच राख्ने अनुमति दिइएको छ भने उक्त अनुप्रयोगले पृष्ठभूमिमा चलिरहेको बेला स्थानमाथि पहुँच राख्न सक्छ।"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"तपाईँका अडियो सेटिङहरू परिवर्तन गर्नुहोस्"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"अनुप्रयोगलाई ग्लोबल अडियो सेटिङहरू परिमार्जन गर्न अनुमति दिन्छ, जस्तै आवाजको मात्रा र आउटपुटको लागि कुन स्पिकर प्रयोग गर्ने।"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"अडियो रेकर्ड गर्नुहोस्"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"यस अनुप्रयोगले जुनसुकै समय माइक्रोफोनको प्रयोग गरी अडियो रेकर्ड गर्न सक्छ।"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM मा आदेशहरू पठाउन दिनुहोस्"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"SIM लाई आदेश पठाउन अनुप्रयोगलाई अनुमति दिन्छ। यो निकै खतरनाक हुन्छ।"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"शारीरिक गतिविधि पहिचान गर्नुहोस्‌"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"यो अनुप्रयोगले तपाईंको शारीरिक गतिविधिको पहिचान गर्न सक्छ।"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"तस्बिरहरू र भिडियोहरू लिनुहोस्।"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"यस अनुप्रयोगले जुनसुकै समय क्यामेराको प्रयोग गरी तस्बिर खिच्न र भिडियो रेकर्ड गर्न सक्छ।"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"कम्पन नियन्त्रण गर्नुहोस्"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"यसले अनुप्रयोगलाई तपाईंको तस्बिरको सङ्ग्रह परिमार्जन गर्न दिन्छ।"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"आफ्नो मिडियाको सङ्ग्रहका स्थानहरू पढ्नुहोस्‌"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"यसले अनुप्रयोगलाई तपाईंको मिडिया सङ्ग्रहका स्थानहरू पढ्न दिन्छ।"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"अनुप्रयोग <xliff:g id="APP">%s</xliff:g> ले प्रमाणीकरण गर्न चाहन्छ।"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"बायोमेट्रिक हार्डवेयर उपलब्ध छैन"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक औठाछाप पत्ता लाग्यो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"औठाछाप प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"</string>
@@ -1903,14 +1909,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> स्वत: भरण सुझावहरू</item>
       <item quantity="one">एउटा स्वत: भरण सुझाव</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; मा सुरक्षित गर्ने हो?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> लाई &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; मा सुरक्षित गर्ने हो?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> र <xliff:g id="TYPE_1">%2$s</xliff:g> लाई  &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; मा सुरक्षित गर्ने हो?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> र <xliff:g id="TYPE_2">%3$s</xliff:g> लाई to &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; मा सुरक्षित गर्ने हो?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; मा अद्यावधिक गर्ने हो?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> लाई &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; मा अद्यावधिक गर्ने हो?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> र <xliff:g id="TYPE_1">%2$s</xliff:g> लाई &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; मा अद्यावधिक गर्ने हो?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> र <xliff:g id="TYPE_2">%3$s</xliff:g> लाई to &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; मा अद्यावधिक गर्ने हो?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" मा सुरक्षित गर्ने हो?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> लाई "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" मा सुरक्षित गर्ने हो?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> र <xliff:g id="TYPE_1">%2$s</xliff:g> लाई "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" मा सुरक्षित गर्ने हो?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> र <xliff:g id="TYPE_2">%3$s</xliff:g> लाई "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" मा सुरक्षित गर्ने हो?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" मा अद्यावधिक गर्ने हो?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g> लाई "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" मा अद्यावधिक गर्ने हो?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> र <xliff:g id="TYPE_1">%2$s</xliff:g> लाई "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" मा अद्यावधिक गर्ने हो?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"यी वस्तुहरू "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" मा अद्यावधिक गर्नुहोस्‌: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> र <xliff:g id="TYPE_2">%3$s</xliff:g> हो?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"सुरक्षित गर्नुहोस्"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"पर्दैन, धन्यवाद"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"अद्यावधिक गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index b95ba19..ed79f7b 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microfoon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"audio opnemen"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om audio op te nemen?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Activiteitsherkenning"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"activiteit herkennen"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; je fysieke activiteit herkent?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"foto\'s maken en video opnemen"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om foto\'s te maken en video op te nemen?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Deze app kan je locatie ophalen op basis van netwerkbronnen zoals zendmasten en wifi-netwerken. De app kan alleen gebruikmaken van deze locatieservices als ze zijn ingeschakeld en beschikbaar zijn op je tablet."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Deze app kan je locatie ophalen op basis van netwerkbronnen zoals zendmasten en wifi-netwerken. De app kan alleen gebruikmaken van deze locatieservices als ze zijn ingeschakeld en beschikbaar zijn op je tv."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Deze app kan je locatie ophalen op basis van netwerkbronnen zoals zendmasten en wifi-netwerken. De app kan alleen gebruikmaken van deze locatieservices als ze zijn ingeschakeld en beschikbaar zijn op je telefoon."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"toegang tot precieze locatie op de achtergrond"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Deze app kan je exacte locatie ophalen wanneer de app op de achtergrond wordt uitgevoerd. De app kan alleen gebruikmaken van deze locatieservices als ze zijn ingeschakeld en beschikbaar zijn op je telefoon. Hierdoor kan het batterijverbruik toenemen."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"toegang tot locatie op de achtergrond"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Als dit wordt verleend als aanvulling op toegang tot de geschatte of precieze locatie, kan de app toegang tot de locatie krijgen terwijl de app actief is op de achtergrond."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"je audio-instellingen wijzigen"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Hiermee kan de app algemene audio-instellingen wijzigen zoals het volume en welke luidspreker wordt gebruikt voor de uitvoer."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"audio opnemen"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Deze app kan op elk moment audio opnemen met de microfoon."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"opdrachten verzenden naar de simkaart"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Hiermee kan de app opdrachten verzenden naar de simkaart. Dit is erg gevaarlijk."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"fysieke activiteit herkennen"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Deze app kan je fysieke activiteit herkennen."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"foto\'s en video\'s maken"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Deze app kan op elk moment foto\'s maken en video\'s opnemen met de camera."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"trilling beheren"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Hiermee sta je de app toe je fotocollectie aan te passen."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"locaties van je mediacollecties bekijken"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Hiermee sta je de app toe locaties van je mediacollectie te bekijken."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"App <xliff:g id="APP">%s</xliff:g> wil een verificatie uitvoeren."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrische hardware niet beschikbaar"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Gedeeltelijke vingerafdruk gedetecteerd. Probeer het opnieuw."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kan vingerafdruk niet verwerken. Probeer het opnieuw."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggesties van Automatisch aanvullen</item>
       <item quantity="one">Eén suggestie van Automatisch aanvullen</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Opslaan in &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> opslaan in &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> en <xliff:g id="TYPE_1">%2$s</xliff:g> opslaan in &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> en <xliff:g id="TYPE_2">%3$s</xliff:g> opslaan in &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Updaten naar &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> updaten naar &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> en <xliff:g id="TYPE_1">%2$s</xliff:g> updaten naar &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> en <xliff:g id="TYPE_2">%3$s</xliff:g> updaten naar &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Opslaan in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> opslaan in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> en <xliff:g id="TYPE_1">%2$s</xliff:g> opslaan in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> en <xliff:g id="TYPE_2">%3$s</xliff:g> opslaan in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Updaten in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g> updaten in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> en <xliff:g id="TYPE_1">%2$s</xliff:g> updaten in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Deze items updaten in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> en <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Opslaan"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Nee, bedankt"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Updaten"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 9d3fa8e..031cacb 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"ମାଇକ୍ରୋଫୋନ୍"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ଅଡିଓ ରେକର୍ଡ କରେ"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅଡିଓ ରେକର୍ଡ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"ଗତିବିଧି ଚିହ୍ନଟକରଣ"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"ଗତିବିଧିକୁ ଚିହ୍ନଟ କରିପାରେ"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"ଆପଣଙ୍କ ଶାରୀରିକ ଗତିବିଧି ଚିହ୍ନଟ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ଅନୁମତି କରିବେ?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"କ୍ୟାମେରା"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ଫଟୋ ନିଏ ଓ ଭିଡିଓ ରେକର୍ଡ କରେ"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଫଟୋ ଉଠାଇବାକୁ ଏବଂ ଭିଡିଓ ରେକର୍ଡ କରିବାକୁ ଅନୁମତି ଦେବେ କି?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ଏହି ଆପ୍‍, ନେଟ୍‌ୱର୍କ ସୋର୍ସ ଉପରେ ଆଧାର କରି ଆପଣଙ୍କ ଲୋକେଶନ୍‍ ପ୍ରାପ୍ତ କରିପାରେ, ଯେପରିକି ସେଲ୍‍ ଟାୱାର୍‍ ଓ ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ। ଏହି ଲୋକେଶନ୍‌ ସେବା, ଆପଣଙ୍କ ଟାବଲେଟ୍‌ରେ ଅନ୍‍ ରହିଥିବା ଓ ଉପଲବ୍ଧ ଥିବା ଦରକାର, ଯେଉଁଥିରୁ ଆପ୍‌ ସେଗୁଡ଼ିକର ବ୍ୟବହାର କରିପାରିବ।"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ଏହି ଆପ୍‍, ନେଟ୍‌ୱର୍କ ସୋର୍ସ ଉପରେ ଆଧାର କରି ଆପଣଙ୍କ ଲୋକେଶନ୍‍ ପ୍ରାପ୍ତ କରିପାରେ, ଯେପରିକି ସେଲ୍‍ ଟାୱାର୍‍ ଓ ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ। ଏହି ଲୋକେଶନ୍‌ ସେବା, ଆପଣଙ୍କ ଟିଭିରେ ଅନ୍‍ ରହିଥିବା ଓ ଉପଲବ୍ଧ ଥିବା ଦରକାର, ଯେଉଁଥିରୁ ଆପ୍‌ ସେଗୁଡ଼ିକର ବ୍ୟବହାର କରିପାରିବ।"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ସେଲ୍‍ ଟାୱାର ଓ ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ ପରି ସୋର୍ସକୁ ଆଧାର କରି ଏହି ଆପ୍‍ ଆପଣଙ୍କ ଲୋକେଶନ୍‍ ପ୍ରାପ୍ତ କରିପାରିବ। ଏହି ଲୋକେଶନ୍‍ ସେବାଗୁଡ଼ିକର ବ୍ୟବହାର କରିବାକୁ ସେଗୁଡ଼ିକ ଅନ୍‍ କରାଯିବା ଏବଂ ଆପଣଙ୍କ ଫୋନ୍‌ରେ ଉପଲବ୍ଧ ଥିବା ଜରୁରୀ"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ପୃଷ୍ଠପଟରେ ସଠିକ୍‍ ଲୋକେଶନ୍‍ର ଆକ୍ସେସ୍‍"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ଏହି ଆପ୍‍ ପୃଷ୍ଠପଟରେ ରହିଥିବାବେଳେ ଯେକୌଣସି ସମୟରେ ଆପଣଙ୍କର ଲୋକେଶନ୍‍ ପ୍ରାପ୍ତ କରିପାରିବ। ଏହି ଲୋକେଶନ୍‍ ସେବାଗୁଡ଼ିକ ନିଶ୍ଚିତରୂପେ ଅନ୍‍ ରହିବା ଦରକାର ଏବଂ ଆପ୍‍ର ବ୍ୟବହାର ପାଇଁ ଫୋନ୍‍ରେ ଉପଲବ୍ଧ ଥିବା ଦରକାର। ଏହା ବ୍ୟାଟେରୀ ଅଧିକା ଖର୍ଚ୍ଚ କରିପାରେ।"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"ବ୍ୟାକ୍‌ଗ୍ରାଉଣ୍ଡ୍‌ରେ ଲୋକେସନ୍ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"ଅନୁମାନିତ କିମ୍ବା ବିଲ୍‌କୁଲ୍ ସଠିକ୍ ସ୍ଥାନ ଆକ୍ସେସ୍ କରିବାର ଅନୁମତି ଅତିରିକ୍ତ ଭାବରେ ଦିଆଗଲେ, ବ୍ୟାକ୍‌ଗ୍ରାଉଣ୍ଡରେ ଚାଲୁଥିବା ସମୟରେ ଆପ୍ ଆପଣଙ୍କର ସ୍ଥାନର ଆକ୍ସେସ୍ କରିପାରିବ।"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ଆପଣଙ୍କ ଅଡିଓ ସେଟିଙ୍ଗକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ଆପ୍‌କୁ ଗ୍ଲୋବାଲ୍ ଅଡିଓ ସେଟିଙ୍ଗ, ଯେପରିକି ଭଲ୍ୟୁମ୍‌କୁ ସଂଶୋଧିତ କରିବାକୁ ଏବଂ ଆଉଟପୁଟ୍ ପାଇଁ ସ୍ପିକର୍‌ ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ଅଡିଓ ରେକର୍ଡ କରନ୍ତୁ"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"ଏହି ଆପ୍‍ ଯେକୌଣସି ସମୟରେ ମାଇକ୍ରୋଫୋନ୍‍ ବ୍ୟବହାର କରି ଅଡିଓ ରେକର୍ଡ କରିପାରିବ।"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIMକୁ କମାଣ୍ଡ ପଠାନ୍ତୁ"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"SIMକୁ କମାଣ୍ଡ ପଠାଇବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ। ଏହା ବହୁତ ବିପଦପୂର୍ଣ୍ଣ।"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"ଶାରୀରିକ ଗତିବିଧି ଚିହ୍ନଟକରେ"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"ଏହି ଆପ୍‍ଣ ଆପଣଙ୍କ ଶାରୀରିକ ଗତିବିଧିକୁ ଚିହ୍ନଟ କରିପାରେ"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ଫଟୋ ଓ ଭିଡିଓଗୁଡ଼ିକୁ ନିଅନ୍ତୁ"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"ଏହି ଆପ୍‍ ଯେକୌଣସି ସମୟରେ କ୍ୟାମେରା ବ୍ୟବହାର କରି ଫଟୋ ଉଠାଇପାରେ ଏବଂ ଭିଡିଓ ରେକର୍ଡ କରିପାରେ।"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"କମ୍ପନ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"ଆପଣଙ୍କ ଫଟୋ ସଂଗ୍ରହ ପରିବର୍ତ୍ତନ କରିବାକୁ ଆପ୍‍ ଅନୁମତି ଦେଇଥାଏ।"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"ଆପଣଙ୍କ ମିଡିଆ ସଂଗ୍ରହ ଠାରୁ ଲୋକେସନ୍‍ଗୁଡିକୁ ପଢନ୍ତୁ"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"ଆପଣଙ୍କ ମିଡିଆ ସଂଗ୍ରହ ଠାରୁ ଅବସ୍ଥାନଗୁଡିକୁ ପଢିବାକୁ ଆପ୍‍ ଅନୁମତି ଦେଇଥାଏ।"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"ଆପ୍ଲିକେସନ୍ <xliff:g id="APP">%s</xliff:g> ପ୍ରମାଣିତକୃତ କରିବାକୁ ଚାହୁଁଛି।"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ବାୟୋମେଟ୍ରିକ୍‌ ହାର୍ଡୱେର୍‌ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଂଶିକ ଚିହ୍ନଟ ହେଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ପ୍ରୋସେସ୍‍ କରାଯାଇପାରିଲା ନାହିଁ। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
@@ -1898,14 +1904,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g>ଟି ଅଟୋଫିଲ୍‍ ପରାମର୍ଶ</item>
       <item quantity="one">ଗୋଟିଏ ଅଟୋଫିଲ୍‍ ପରାମର୍ଶ</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;ରେ ସେଭ୍‍ କରିବେ?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;ରେ ସେଭ୍‍ କରିବେ?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> ଏବଂ <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;ରେ ସେଭ୍‍ କରିବେ?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, ଏବଂ <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;ରେ ସେଭ୍‍ କରିବେ?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;କୁ ଅପ୍‍ଡେଟ୍‍ କରିବେ?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;କୁ ଅପ୍‍ଡେଟ୍‍ କରିବେ?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> ଏବଂ <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;କୁ ଅପ୍‍ଡେଟ୍‍ କରିବେ?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, ଏବଂ <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;କୁ ଅପ୍‍ଡେଟ୍‍ କରିବେ?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"କୁ ସେଭ୍ କରିବେ?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"କୁ <xliff:g id="TYPE">%1$s</xliff:g> ସେଭ୍ କରିବେ?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"କୁ <xliff:g id="TYPE_0">%1$s</xliff:g> ଏବଂ <xliff:g id="TYPE_1">%2$s</xliff:g> ସେଭ୍ କରିବେ?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"କୁ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ଏବଂ <xliff:g id="TYPE_2">%3$s</xliff:g> ସେଭ୍ କରିବେ?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"ରେ ଅପ୍‌ଡେଟ୍ କରିବେ?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"ରେ <xliff:g id="TYPE">%1$s</xliff:g>କୁ ଅପ୍‌ଡେଟ୍ କରିବେ।"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"ରେ <xliff:g id="TYPE_0">%1$s</xliff:g> ଏବଂ <xliff:g id="TYPE_1">%2$s</xliff:g> ଅପ୍‌ଡେଟ୍ କରିବେ?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"ରେ: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ଏବଂ <xliff:g id="TYPE_2">%3$s</xliff:g> ଆଇଟମ୍‌ଗୁଡ଼ିକ ଅପ୍‌ଡେଟ୍ କରିବେ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"ସେଭ୍‌ କରନ୍ତୁ"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"ନାଁ, ପଚାରିଥିବାରୁ ଧନ୍ୟବାଦ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"ଅପ୍‍ଡେଟ୍‌ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 6e615a8..2b30459 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"ਮਾਈਕ੍ਰੋਫੋਨ"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">" ਆਡੀਓ  ਰਿਕਾਰਡ ਕਰਨ"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"ਸਰਗਰਮੀ ਦੀ ਪਛਾਣ"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"ਸਰਗਰਮੀ ਨੂੰ ਪਛਾਣੋ"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਆਪਣੇ ਸਰੀਰਕ ਸਰਗਰਮੀ ਦੀ ਪਛਾਣ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"ਕੈਮਰਾ"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ਤਸਵੀਰਾਂ ਲੈਣ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰਨ"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਤਸਵੀਰਾਂ ਖਿੱਚਣ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੇਣਾ ਹੈ?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ਇਹ ਐਪ ਨੈੱਟਵਰਕ ਸਰੋਤਾਂ ਜਿਵੇਂ ਕਿ ਸੈੱਲ ਟਾਵਰਾਂ ਅਤੇ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕਾਂ \'ਤੇ ਆਧਾਰਿਤ ਤੁਹਾਡਾ ਟਿਕਾਣਾ ਪਤਾ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਦੁਆਰਾ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕੀਤੇ ਜਾਣ ਦੇ ਯੋਗ ਹੋਣ ਲਈ ਇਹ ਸੇਵਾਵਾਂ ਤੁਹਾਡੇ ਟੈਬਲੈੱਟ \'ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਅਤੇ ਚਾਲੂ ਕੀਤੀਆਂ ਹੋਣੀਆਂ ਲਾਜ਼ਮੀ ਹਨ।"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ਇਹ ਐਪ ਨੈੱਟਵਰਕ ਸਰੋਤਾਂ ਜਿਵੇਂ ਕਿ ਸੈੱਲ ਟਾਵਰਾਂ ਅਤੇ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕਾਂ \'ਤੇ ਆਧਾਰਿਤ ਤੁਹਾਡਾ ਟਿਕਾਣਾ ਪਤਾ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਵੱਲੋਂ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕੀਤੇ ਜਾਣ ਦੇ ਯੋਗ ਹੋਣ ਲਈ ਇਹ ਸੇਵਾਵਾਂ ਤੁਹਾਡੇ ਟੀਵੀ \'ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਅਤੇ ਚਾਲੂ ਕੀਤੀਆਂ ਹੋਣੀਆਂ ਲਾਜ਼ਮੀ ਹਨ।"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ਇਹ ਐਪ ਨੈੱਟਵਰਕ ਸਰੋਤਾਂ ਜਿਵੇਂ ਕਿ ਸੈੱਲ ਟਾਵਰਾਂ ਅਤੇ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕਾਂ \'ਤੇ ਆਧਾਰਿਤ ਤੁਹਾਡਾ ਟਿਕਾਣਾ ਪਤਾ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਵੱਲੋਂ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕੀਤੇ ਜਾਣ ਦੇ ਯੋਗ ਹੋਣ ਲਈ ਇਹ ਸੇਵਾਵਾਂ ਤੁਹਾਡੇ ਫ਼ੋਨ \'ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਅਤੇ ਚਾਲੂ ਕੀਤੀਆਂ ਹੋਣੀਆਂ ਲਾਜ਼ਮੀ ਹਨ।"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ਬੈਕਗ੍ਰਾਉਂਡ ਵਿੱਚ ਸਟੀਕ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰੋ"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ਇਹ ਐਪ ਬੈਕਗ੍ਰਾਉਂਡ ਵਿੱਚ ਹੋਣ \'ਤੇ ਕਿਸੇ ਵੇਲੇ ਵੀ ਤੁਹਾਡਾ ਸਟੀਕ ਟਿਕਾਣਾ ਪਤਾ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਵੱਲੋਂ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਇਹ ਸੇਵਾਵਾਂ ਤੁਹਾਡੇ ਫ਼ੋਨ \'ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਅਤੇ ਚਾਲੂ ਕੀਤੀਆਂ ਹੋਣੀਆਂ ਲਾਜ਼ਮੀ ਹਨ। ਇਸ ਨਾਲ ਬੈਟਰੀ ਦੀ ਖਪਤ ਵਧ ਸਕਦੀ ਹੈ।"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"ਐਪ ਨੂੰ ਵਧੀਕ ਤੌਰ \'ਤੇ ਅਨੁਮਾਨਿਤ ਜਾਂ ਸਟੀਕ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਦੇਣ \'ਤੇ ਇਹ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲਣ ਵੇਲੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ਆਪਣੀਆਂ ਆਡੀਓ ਸੈਟਿੰਗਾਂ ਬਦਲੋ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ਐਪ ਨੂੰ ਗਲੋਬਲ ਆਡੀਓ ਸੈਟਿੰਗਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਅਵਾਜ਼ ਅਤੇ ਆਊਟਪੁਟ ਲਈ ਕਿਹੜਾ ਸਪੀਕਰ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ।"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">" ਆਡੀਓ  ਰਿਕਾਰਡ ਕਰਨ"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"ਇਹ ਐਪ ਕਿਸੇ ਵੀ ਸਮੇਂ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਦੀ ਵਰਤੋਂ ਕਰਕੇ  ਆਡੀਓ  ਫ਼ਾਈਲ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜੋ"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"ਐਪ ਨੂੰ SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਬਹੁਤ ਘਾਤਕ ਹੈ।"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"ਸਰੀਰਕ ਸਰਗਰਮੀ ਨੂੰ ਪਛਾਣਨਾ"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"ਇਹ ਐਪ ਤੁਹਾਡੀ ਸਰੀਰਕ ਸਰਗਰਮੀ ਨੂੰ ਪਛਾਣ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ਤਸਵੀਰਾਂ ਅਤੇ ਵੀਡੀਓ ਬਣਾਓ"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"ਇਹ ਐਪ ਕਿਸੇ ਵੀ ਸਮੇਂ ਕੈਮਰੇ ਨੂੰ ਵਰਤ ਕੇ ਤਸਵੀਰਾਂ ਖਿੱਚ ਸਕਦੀ ਹੈ ਅਤੇ ਵੀਡੀਓ ਫ਼ਾਈਲਾਂ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ਵਾਈਬ੍ਰੇਸ਼ਨ ਤੇ ਨਿਯੰਤਰਣ ਪਾਓ"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਟੋ ਸੰਗ੍ਰਹਿ ਨੂੰ ਸੋਧਣ ਦਿੰਦੀ ਹੈ।"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"ਤੁਹਾਡੇ ਮੀਡੀਆ ਸੰਗ੍ਰਹਿ ਦੇ ਟਿਕਾਣਿਆਂ ਨੂੰ ਪੜ੍ਹਨਾ"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਮੀਡੀਆ ਸੰਗ੍ਰਹਿ ਦੇ ਟਿਕਾਣਿਆਂ ਨੂੰ ਪੜ੍ਹਨ ਦਿੰਦੀ ਹੈ।"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"ਐਪਲੀਕੇਸ਼ਨ <xliff:g id="APP">%s</xliff:g> ਪ੍ਰਮਾਣੀਕਰਨ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ।"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ਬਾਇਓਮੈਟ੍ਰਿਕ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ਅਧੂਰਾ ਫਿੰਗਰਪ੍ਰਿਟ ਮਿਲਿਆ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪ੍ਰਕਿਰਿਆ ਨਹੀਂ ਕਰ ਸਕਿਆ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
@@ -1874,7 +1880,7 @@
     <string name="conference_call" msgid="3751093130790472426">"ਕਾਨਫਰੰਸ ਕਾਲ"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"ਟੂਲ-ਟਿੱਪ"</string>
     <string name="app_category_game" msgid="5431836943981492993">"ਗੇਮਾਂ"</string>
-    <string name="app_category_audio" msgid="1659853108734301647">"ਸੰਗੀਤ ਅਤੇ  ਆਡੀਓ"</string>
+    <string name="app_category_audio" msgid="1659853108734301647">"ਸੰਗੀਤ ਅਤੇ ਆਡੀਓ"</string>
     <string name="app_category_video" msgid="2728726078629384196">"ਮੂਵੀਆਂ ਅਤੇ ਵੀਡੀਓ"</string>
     <string name="app_category_image" msgid="4867854544519846048">"ਫ਼ੋਟੋਆਂ ਅਤੇ ਚਿੱਤਰ"</string>
     <string name="app_category_social" msgid="5842783057834965912">"ਸਮਾਜਕ ਅਤੇ ਸੰਚਾਰ"</string>
@@ -1898,14 +1904,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> ਆਟੋਫਿਲ ਸੁਝਾਅ</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> ਆਟੋਫਿਲ ਸੁਝਾਅ</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"ਕੀ &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ਵਿੱਚ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"ਕੀ <xliff:g id="TYPE">%1$s</xliff:g> ਨੂੰ &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ਵਿੱਚ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"ਕੀ <xliff:g id="TYPE_0">%1$s</xliff:g> ਅਤੇ <xliff:g id="TYPE_1">%2$s</xliff:g> ਨੂੰ &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ਵਿੱਚ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"ਕੀ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, ਅਤੇ <xliff:g id="TYPE_2">%3$s</xliff:g> ਨੂੰ &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ਵਿੱਚ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"ਕੀ &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ਵਿੱਚ ਅੱਪਡੇਟ ਕਰਨਾ ਹੈ?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"ਕੀ <xliff:g id="TYPE">%1$s</xliff:g> ਨੂੰ &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ਵਿੱਚ ਅੱਪਡੇਟ ਕਰਨਾ ਹੈ?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"ਕੀ <xliff:g id="TYPE_0">%1$s</xliff:g> ਅਤੇ <xliff:g id="TYPE_1">%2$s</xliff:g> ਨੂੰ &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ਵਿੱਚ ਅੱਪਡੇਟ ਕਰਨਾ ਹੈ?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"ਕੀ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ਅਤੇ <xliff:g id="TYPE_2">%3$s</xliff:g> ਨੂੰ &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ਵਿੱਚ ਅੱਪਡੇਟ ਕਰਨਾ ਹੈ?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"ਕੀ ਤੁਸੀਂ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ਵਿੱਚ ਰੱਖਿਅਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"ਕੀ ਤੁਸੀਂ "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ਵਿੱਚ <xliff:g id="TYPE">%1$s</xliff:g> ਨੂੰ ਰੱਖਿਅਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"ਕੀ ਤੁਸੀਂ "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ਵਿੱਚ <xliff:g id="TYPE_0">%1$s</xliff:g> ਅਤੇ <xliff:g id="TYPE_1">%2$s</xliff:g> ਨੂੰ ਰੱਖਿਅਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"ਕੀ ਤੁਸੀਂ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ਵਿੱਚ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ਅਤੇ <xliff:g id="TYPE_2">%3$s</xliff:g> ਨੂੰ ਰੱਖਿਅਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"ਕੀ ਤੁਸੀਂ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ਵਿੱਚ ਅੱਪਡੇਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"ਕੀ ਤੁਸੀਂ "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ਵਿੱਚ <xliff:g id="TYPE">%1$s</xliff:g> ਨੂੰ ਅੱਪਡੇਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"ਕੀ ਤੁਸੀਂ "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ਵਿੱਚ <xliff:g id="TYPE_0">%1$s</xliff:g> ਅਤੇ <xliff:g id="TYPE_1">%2$s</xliff:g> ਨੂੰ ਅੱਪਡੇਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"ਕੀ ਤੁਸੀਂ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ਵਿੱਚ ਇਹਨਾਂ ਆਈਟਮਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, ਅਤੇ <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"ਰੱਖਿਅਤ ਕਰੋ"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"ਅੱਪਡੇਟ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 81a869a..7ef4907 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -298,6 +298,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"nagrywanie dźwięku"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na nagrywanie dźwięku?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Rozpoznawanie aktywności"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"rozpoznawanie aktywności"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na rozpoznawanie Twojej aktywności fizycznej?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Aparat"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"robienie zdjęć i nagrywanie filmów"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na robienie zdjęć i nagrywanie filmów?"</string>
@@ -424,14 +427,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ta aplikacja może określać Twoją lokalizację na podstawie źródeł sieciowych, takich jak stacje bazowe i sieci Wi-Fi. Te usługi lokalizacyjne muszą być włączone i dostępne na tablecie, by aplikacja mogła z nich korzystać."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ta aplikacja może określać Twoją lokalizację na podstawie źródeł sieciowych, takich jak stacje bazowe i sieci Wi-Fi. Te usługi lokalizacyjne muszą być włączone i dostępne na telewizorze, by aplikacja mogła z nich korzystać."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ta aplikacja może określać Twoją lokalizację na podstawie źródeł sieciowych, takich jak stacje bazowe i sieci Wi-Fi. Te usługi lokalizacyjne muszą być włączone i dostępne na telefonie, by aplikacja mogła z nich korzystać."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"dostęp do dokładnej lokalizacji w tle"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ta aplikacja może określić Twoją dokładną lokalizację w dowolnym momencie, działając w tle. Te usługi lokalizacyjne muszą być włączone i dostępne na telefonie, by aplikacja mogła z nich korzystać. Może to zwiększyć zużycie baterii."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"dostęp do lokalizacji w tle"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Jeśli te uprawnienia zostaną przyznane wraz z dostępem do dokładnej lub przybliżonej lokalizacji, aplikacja będzie mogła uzyskiwać dostęp do lokalizacji podczas działania w tle."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"zmienianie ustawień audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Pozwala aplikacji na modyfikowanie globalnych ustawień dźwięku, takich jak głośność oraz urządzenie wyjściowe."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"nagrywanie dźwięku"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Ta aplikacja może w dowolnym momencie nagrać dźwięk przez mikrofon."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"wysyłanie poleceń do karty SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Pozwala aplikacji na wysyłanie poleceń do karty SIM. To bardzo niebezpieczne."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"rozpoznawanie aktywności fizycznej"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Ta aplikacja może rozpoznawać Twoją aktywność fizyczną."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"wykonywanie zdjęć i filmów wideo"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Ta aplikacja może w dowolnym momencie robić zdjęcia i nagrywać filmy przy użyciu aparatu."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"sterowanie wibracjami"</string>
@@ -524,6 +529,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Zezwala aplikacji na modyfikowanie kolekcji zdjęć."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"odczytywanie lokalizacji z kolekcji multimediów"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Zezwala aplikacji na odczytywanie lokalizacji z kolekcji multimediów."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikacja <xliff:g id="APP">%s</xliff:g> wymaga uwierzytelnienia."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Sprzęt biometryczny niedostępny"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Odcisk palca został odczytany tylko częściowo. Spróbuj ponownie."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nie udało się przetworzyć odcisku palca. Spróbuj ponownie."</string>
@@ -1967,14 +1973,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> sugestii autouzupełniania</item>
       <item quantity="one">Jedna sugestia autouzupełniania</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Zapisać w: &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> – zapisać w: &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> – zapisać w: &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> – zapisać w: &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Zaktualizować do &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Zaktualizować <xliff:g id="TYPE">%1$s</xliff:g> do &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Zaktualizować <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> do &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Zaktualizować <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, i <xliff:g id="TYPE_2">%3$s</xliff:g> do &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Zapisać w: "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Zapisać: <xliff:g id="TYPE">%1$s</xliff:g> w "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Zapisać: <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> w: "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Zapisać: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> w: "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Zaktualizować w: "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Zaktualizować: <xliff:g id="TYPE">%1$s</xliff:g> w: "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Zaktualizować: <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> w: "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Zaktualizować w: "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" te elementy: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Zapisz"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Nie, dziękuję"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Zaktualizuj"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 5ac7c2e..5ab4fce 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microfone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"grave áudio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Reconhecimento de atividade"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"reconhecer atividade"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; reconheça sua atividade física?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Câmera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"tire fotos e grave vídeos"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeos?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis no seu tablet para que o app possa usá-los."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis na sua TV para que o app possa usá-los."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"acessar localização precisa em segundo plano"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Este app pode ver sua localização exata a qualquer momento quando está em segundo plano. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los. Isso pode aumentar o consumo de bateria."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"acessar a localização em segundo plano"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Se essa permissão for concedida, além do acesso à localização precisa ou aproximada, o app poderá acessar a localização durante a execução em segundo plano."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"alterar as suas configurações de áudio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que o app modifique configurações de áudio globais como volume e alto-falantes de saída."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Este app pode gravar áudio usando o microfone a qualquer momento."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos para o chip"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que o app envie comandos ao chip. Muito perigoso."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"reconhecer atividade física"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Este app pode reconhecer sua atividade física."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"tirar fotos e gravar vídeos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Este app pode tirar fotos e gravar vídeos usando a câmera a qualquer momento."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar vibração"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite que o app modifique sua coleção de fotos."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"ler locais na sua coleção de mídias"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que o app leia os locais na sua coleção de mídias."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"O app <xliff:g id="APP">%s</xliff:g> está solicitando autenticação."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biométrico indisponível"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital parcial detectada. Tente novamente."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
@@ -1826,7 +1832,7 @@
     <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
     <string name="usb_midi_peripheral_product_name" msgid="4971827859165280403">"Porta USB periférica"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="4797287862999444631">"Mais opções"</string>
-    <string name="floating_toolbar_close_overflow_description" msgid="559796923090723804">"Fechar barra flutuante"</string>
+    <string name="floating_toolbar_close_overflow_description" msgid="559796923090723804">"Fechar menu flutuante"</string>
     <string name="maximize_button_text" msgid="7543285286182446254">"Maximizar"</string>
     <string name="close_button_text" msgid="3937902162644062866">"Fechar"</string>
     <string name="notification_messaging_title_template" msgid="3452480118762691020">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> sugestão de preenchimento automático</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> sugestões de preenchimento automático</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Salvar em &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Salvar <xliff:g id="TYPE">%1$s</xliff:g> em &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Salvar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Salvar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> em &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Atualizar no &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Atualizar <xliff:g id="TYPE">%1$s</xliff:g> no &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> no &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Atualizar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> no &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Salvar em "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Salvar <xliff:g id="TYPE">%1$s</xliff:g> em "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Salvar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Salvar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Atualizar em "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Atualizar <xliff:g id="TYPE">%1$s</xliff:g> em "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Atualizar estes itens em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Salvar"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Não, obrigado"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Atualizar"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 52f067f..ec853bb 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microfone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"gravar áudio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Pretende permitir que a aplicação &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Reconhecimento da atividade"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"reconhecer a atividade"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Pretende permitir que a aplicação &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; reconheça a sua atividade física?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Câmara"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"tirar fotos e gravar vídeos"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Pretende permitir que a aplicação &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeo?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Esta aplicação pode obter a sua localização com base em fontes de rede, tais como torres de redes móveis e redes Wi-Fi. É necessário que estes serviços de localização estejam ativados e disponíveis no seu tablet para que a aplicação os possa utilizar."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Esta aplicação pode obter a sua localização com base em fontes de rede, tais como torres de redes móveis e redes Wi-Fi. É necessário que estes serviços de localização estejam ativados e disponíveis na sua TV para que a aplicação os possa utilizar."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Esta aplicação pode obter a sua localização com base em fontes de rede, tais como torres de redes móveis e redes Wi-Fi. É necessário que estes serviços de localização estejam ativados e disponíveis no seu telemóvel para que a aplicação os possa utilizar."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"aceder à localização exata em segundo plano"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Esta aplicação pode obter a sua localização exata sempre que estiver em segundo plano. É necessário que estes Serviços de localização estejam ativados e disponíveis no seu telemóvel para que a aplicação os possa utilizar. Esta ação pode aumentar o consumo da bateria."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"aceder à localização em segundo plano"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Se tal for concedido em conjunto com o acesso à localização aproximada ou exata, a aplicação pode aceder à localização mesmo estando em segundo plano."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"alterar as suas definições de áudio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que a aplicação modifique definições de áudio globais, tais como o volume e qual o altifalante utilizado para a saída de som."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Esta aplicação pode gravar áudio através do microfone em qualquer altura."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos para o SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que a aplicação envie comandos para o SIM. Esta ação é muito perigosa."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"reconhecer a atividade física"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Esta aplicação consegue reconhecer a sua atividade física."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"tirar fotos e vídeos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Esta aplicação pode tirar fotos e gravar vídeos através da câmara em qualquer altura."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar vibração"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite que a aplicação modifique a sua coleção de fotos."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"ler as localizações a partir da sua coleção de multimédia"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que a aplicação leia as localizações a partir da sua coleção de multimédia."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"A aplicação <xliff:g id="APP">%s</xliff:g> pretende uma autenticação"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biométrico indisponível."</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital detetada. Tente novamente."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> sugestões do preenchimento automático</item>
       <item quantity="one">Uma sugestão do preenchimento automático</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Pretende guardar no &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Pretende guardar <xliff:g id="TYPE">%1$s</xliff:g> no &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Pretende guardar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> no &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Pretende guardar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> no &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Pretende atualizar para o &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Pretende atualizar <xliff:g id="TYPE">%1$s</xliff:g> para o &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Pretende atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> para o &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Pretende atualizar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> para o &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Pretende guardar em "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Pretende guardar <xliff:g id="TYPE">%1$s</xliff:g> em "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Pretende guardar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Pretende guardar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Pretende atualizar em "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Pretende atualizar <xliff:g id="TYPE">%1$s</xliff:g> em "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Pretende atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Pretende atualizar estes itens em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Guardar"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Não, obrigado"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Atualizar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 5ac7c2e..5ab4fce 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microfone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"grave áudio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Reconhecimento de atividade"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"reconhecer atividade"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; reconheça sua atividade física?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Câmera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"tire fotos e grave vídeos"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeos?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis no seu tablet para que o app possa usá-los."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis na sua TV para que o app possa usá-los."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"acessar localização precisa em segundo plano"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Este app pode ver sua localização exata a qualquer momento quando está em segundo plano. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los. Isso pode aumentar o consumo de bateria."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"acessar a localização em segundo plano"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Se essa permissão for concedida, além do acesso à localização precisa ou aproximada, o app poderá acessar a localização durante a execução em segundo plano."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"alterar as suas configurações de áudio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que o app modifique configurações de áudio globais como volume e alto-falantes de saída."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Este app pode gravar áudio usando o microfone a qualquer momento."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos para o chip"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que o app envie comandos ao chip. Muito perigoso."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"reconhecer atividade física"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Este app pode reconhecer sua atividade física."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"tirar fotos e gravar vídeos"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Este app pode tirar fotos e gravar vídeos usando a câmera a qualquer momento."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar vibração"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite que o app modifique sua coleção de fotos."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"ler locais na sua coleção de mídias"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que o app leia os locais na sua coleção de mídias."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"O app <xliff:g id="APP">%s</xliff:g> está solicitando autenticação."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biométrico indisponível"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital parcial detectada. Tente novamente."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
@@ -1826,7 +1832,7 @@
     <string name="usb_midi_peripheral_manufacturer_name" msgid="7176526170008970168">"Android"</string>
     <string name="usb_midi_peripheral_product_name" msgid="4971827859165280403">"Porta USB periférica"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="4797287862999444631">"Mais opções"</string>
-    <string name="floating_toolbar_close_overflow_description" msgid="559796923090723804">"Fechar barra flutuante"</string>
+    <string name="floating_toolbar_close_overflow_description" msgid="559796923090723804">"Fechar menu flutuante"</string>
     <string name="maximize_button_text" msgid="7543285286182446254">"Maximizar"</string>
     <string name="close_button_text" msgid="3937902162644062866">"Fechar"</string>
     <string name="notification_messaging_title_template" msgid="3452480118762691020">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> sugestão de preenchimento automático</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> sugestões de preenchimento automático</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Salvar em &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Salvar <xliff:g id="TYPE">%1$s</xliff:g> em &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Salvar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Salvar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> em &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Atualizar no &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Atualizar <xliff:g id="TYPE">%1$s</xliff:g> no &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> no &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Atualizar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> no &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Salvar em "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Salvar <xliff:g id="TYPE">%1$s</xliff:g> em "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Salvar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Salvar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Atualizar em "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Atualizar <xliff:g id="TYPE">%1$s</xliff:g> em "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Atualizar estes itens em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Salvar"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Não, obrigado"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Atualizar"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 0c945f7..7c4a571 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -295,6 +295,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microfon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"înregistreze sunet"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Pemiteți &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să înregistreze conținut audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Recunoașterea activității"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"recunoașterea activității"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Permiteți aplicației &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să vă recunoască activitatea fizică?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera foto"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotografieze și să înregistreze videoclipuri"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Permiteți &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să facă fotografii și să înregistreze videoclipuri?"</string>
@@ -421,14 +424,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Această aplicație vă poate obține locația cu ajutorul surselor rețelei, cum ar fi turnurile de telefonie mobilă și rețelele Wi-Fi. Aceste servicii de localizare trebuie să fie activate și disponibile pe tabletă pentru ca aplicația să le poată folosi."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Această aplicație vă poate obține locația cu ajutorul surselor rețelei, cum ar fi turnurile de telefonie mobilă și rețelele Wi-Fi. Aceste servicii de localizare trebuie să fie activate și disponibile pe televizor pentru ca aplicația să le poată folosi."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Această aplicație vă poate obține locația cu ajutorul surselor rețelei, cum ar fi turnurile de telefonie mobilă și rețelele Wi-Fi. Aceste servicii de localizare trebuie să fie activate și disponibile pe telefon pentru ca aplicația să le poată folosi."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"să acceseze locația exactă în fundal"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Aplicația vă poate obține locația exactă ori de câte ori rulează în fundal. Serviciile de localizare trebuie să fie activate și disponibile pe telefon pentru ca aplicația să le poată folosi. Acest lucru poate accelera descărcarea bateriei."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"accesați locația în fundal"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Dacă se acordă în plus față de accesul la locație aproximativă sau exactă, aplicația poate accesa locația în timp ce rulează în fundal."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modificare setări audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite aplicației să modifice setările audio globale, cum ar fi volumul și difuzorul care este utilizat pentru ieșire."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"înregistreze sunet"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Această aplicație poate înregistra conținut audio folosind microfonul în orice moment."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"să trimită comenzi către SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite aplicației să trimită comenzi pe cardul SIM. Această permisiune este foarte periculoasă."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"recunoașterea activității fizice"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Această aplicație vă poate recunoaște activitatea fizică."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"realizarea de fotografii și videoclipuri"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Această aplicație poate să facă fotografii și să înregistreze videoclipuri folosind camera foto în orice moment."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlează vibrarea"</string>
@@ -521,6 +526,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite aplicației să vă modifice colecția de fotografii."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"citiți locațiile din colecția media"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite aplicației să citească locațiile din colecția dvs. media."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplicația <xliff:g id="APP">%s</xliff:g> dorește să se autentifice."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biometric indisponibil"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"S-a detectat parțial amprenta. Încercați din nou."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Amprenta nu a putut fi procesată. Încercați din nou."</string>
@@ -639,9 +645,9 @@
     <string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"Permite aplicației să se conecteze la serviciile operatorului. Nu ar trebui să fie niciodată necesară pentru aplicațiile obișnuite."</string>
     <string name="permlab_access_notification_policy" msgid="4247510821662059671">"accesează Nu deranja"</string>
     <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Permite aplicației să citească și să scrie configurația Nu deranja."</string>
-    <string name="policylab_limitPassword" msgid="4497420728857585791">"Setați reguli pentru parolă"</string>
+    <string name="policylab_limitPassword" msgid="4497420728857585791">"Să seteze reguli pentru parolă"</string>
     <string name="policydesc_limitPassword" msgid="2502021457917874968">"Stabiliți lungimea și tipul de caractere permise pentru parolele și codurile PIN de blocare a ecranului."</string>
-    <string name="policylab_watchLogin" msgid="5091404125971980158">"Monitorizați încercările de deblocare a ecranului"</string>
+    <string name="policylab_watchLogin" msgid="5091404125971980158">"Să monitorizeze încercările de deblocare a ecranului"</string>
     <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați tableta sau ștergeți datele acesteia dacă sunt introduse prea multe parole incorecte."</string>
     <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați televizorul sau ștergeți toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string>
     <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați telefonul sau ștergeți toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string>
@@ -664,9 +670,9 @@
     <string name="policydesc_setGlobalProxy" msgid="8459859731153370499">"Setați serverul proxy global pentru dispozitiv, care să fie utilizat cât timp politica este activă. Numai proprietarul dispozitivului poate seta serverul proxy global."</string>
     <string name="policylab_expirePassword" msgid="5610055012328825874">"Setați expirarea parolei pentru blocarea ecranului"</string>
     <string name="policydesc_expirePassword" msgid="5367525762204416046">"Modificați frecvența cu care trebuie să se schimbe parola, codul PIN sau modelul pentru blocarea ecranului."</string>
-    <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Setați criptarea stocării"</string>
+    <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Să seteze criptarea stocării"</string>
     <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Necesită ca datele aplicației stocate să fie criptate."</string>
-    <string name="policylab_disableCamera" msgid="6395301023152297826">"Dezactivați camerele foto"</string>
+    <string name="policylab_disableCamera" msgid="6395301023152297826">"Să dezactiveze camerele foto"</string>
     <string name="policydesc_disableCamera" msgid="2306349042834754597">"Împiedicați utilizarea camerelor foto de pe dispozitiv."</string>
     <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Să oprească funcții de blocare ecran"</string>
     <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Împiedicați folosirea unor funcții de blocare a ecranului."</string>
@@ -1932,14 +1938,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> de sugestii de completare automată</item>
       <item quantity="one">O sugestie de completare automată</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Salvați în &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Salvați <xliff:g id="TYPE">%1$s</xliff:g> în &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Salvați <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> în &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Salvați <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g> în &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Actualizați la &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Actualizați <xliff:g id="TYPE">%1$s</xliff:g> la &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Actualizați <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> la &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Actualizați <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g> la &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Salvați în "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Salvați <xliff:g id="TYPE">%1$s</xliff:g> în "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Salvați <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> în "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Salvați <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g> în "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Actualizați în "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Actualizați <xliff:g id="TYPE">%1$s</xliff:g> în "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Actualizați <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> în "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Actualizați aceste articole în "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Salvați"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Nu, mulțumesc"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Actualizați"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index fd70e9c..ece22b1 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -298,6 +298,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"записывать аудио"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; записывать аудио?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Распознавание активности"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"распознавать активность"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; распознавать физическую активность?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"снимать фото и видео"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Разрешить приложению &lt;b&gt;\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"&lt;/b&gt; снимать фото и видео?"</string>
@@ -424,14 +427,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Приложение может получать сведения о вашем местоположении от сетевых источников, таких как вышки сотовой связи и точки доступа Wi-Fi. Необходимо включить соответствующие параметры на планшете и разрешить приложению использовать геоданные."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Приложение может получать сведения о вашем местоположении от сетевых источников, таких как вышки сотовой связи и точки доступа Wi-Fi. Необходимо включить соответствующие параметры на телевизоре и разрешить приложению использовать геоданные."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Приложение может получать сведения о вашем местоположении от сетевых источников, таких как вышки сотовой связи и точки доступа Wi-Fi. Необходимо включить соответствующие параметры на телефоне и разрешить приложению использовать геоданные."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"Доступ к геоданным в фоновом режиме"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Приложение может получать сведения о вашем точном местоположении в фоновом режиме. Для этого необходимо включить соответствующие параметры на телефоне и разрешить использовать геоданные. Может увеличиться расход заряда батареи."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"доступ к геоданным в фоновом режиме"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Если помимо доступа к данным о точном или приблизительном местоположении вы предоставите это разрешение, приложение сможет получать доступ к геоданным в фоновом режиме."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"Изменение настроек аудио"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Приложение сможет изменять системные настройки звука, например уровень громкости и активный динамик."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"Запись аудио"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Приложение может в любое время записывать аудио с помощью микрофона."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"Отправка команд SIM-карте"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Приложение сможет отправлять команды SIM-карте (данное разрешение представляет большую угрозу)."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"распознавать физическую активность"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Приложение может распознавать физическую активность."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"Фото- и видеосъемка"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Приложение может в любое время делать фотографии и снимать видео с помощью камеры."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"Управление функцией вибросигнала"</string>
@@ -524,6 +529,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Приложение сможет вносить изменения в вашу фотоколлекцию."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"доступ к геоданным в медиаколлекции"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Приложение получит доступ к геоданным в вашей медиаколлекции."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" запрашивает аутентификацию"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрическое оборудование недоступно"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Отсканирована только часть пальца. Повторите попытку."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не удалось распознать отпечаток. Повторите попытку."</string>
@@ -1967,14 +1973,14 @@
       <item quantity="many"><xliff:g id="COUNT">%1$s</xliff:g> вариантов автозаполнения</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> варианта автозаполнения</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Сохранить в &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Сохранить <xliff:g id="TYPE">%1$s</xliff:g> в &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Сохранить <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> в &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Сохранить <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g> в &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Обновить данные в &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Обновить данные (<xliff:g id="TYPE">%1$s</xliff:g>) в &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Обновить данные (<xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g>) в &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Обновить данные (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g>) в &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Сохранить в сервисе "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Сохранить данные (<xliff:g id="TYPE">%1$s</xliff:g>) в сервисе "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Сохранить данные (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>) в сервисе "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Сохранить данные (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g>) в сервисе "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Обновить в сервисе "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Обновить данные (<xliff:g id="TYPE">%1$s</xliff:g>) в сервисе "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Обновить данные (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>) в сервисе "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Обновить данные (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g>) в сервисе "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Сохранить"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Нет, спасибо"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Обновить"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 3bd8ceb..ed3d0f5 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"මයික්‍රොෆෝනය"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ශ්‍රව්‍ය පටිගත කරන්න"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; වෙත ශබ්දය පටි ගත කිරීමට ඉඩ දෙන්නද?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"ක්‍රියාකාරකම් හැඳුනුම"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"ක්‍රියාකාරකම හඳුනා ගන්න"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"ඔබේ ශාරීරික ක්‍රියාකාරකම හඳුනා ගැනීමට &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ඉඩ දෙන්නද?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"කැමරාව"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"පින්තූර ගැනීම සහ වීඩියෝ පටිගත කිරීම"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; වෙත පින්තූර සහ වීඩියෝ ගැනීමට ඉඩ දෙන්නද?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"මෙම යෙදුමට ජංගම දුරකථන කුළුණු සහ Wi-Fi ජාල වැනි ජාල මූලාශ්‍ර පදනම්ව ඔබගේ ස්ථානය ලබා ගත හැකිය. යෙදුමට ඒවා භාවිත කිරීමට හැකි වීමට මෙම ස්ථාන සේවා ක්‍රියාත්මක කර සහ ඔබේ ටැබ්ලට් පරිගණකය මත ලබා ගත හැකිව තිබිය යුතුය."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"මෙම යෙදුමට ජංගම දුරකථන කුළුණු සහ Wi-Fi ජාල වැනි ජාල මූලාශ්‍ර පදනම්ව ඔබගේ ස්ථානය ලබා ගත හැකිය. යෙදුමට ඒවා භාවිත කිරීමට හැකි වීමට මෙම ස්ථාන සේවා ක්‍රියාත්මක කර සහ ඔබේ TV මත ලබා ගත හැකිව තිබිය යුතුය."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"මෙම යෙදුමට ජංගම දුරකථන කුළුණු සහ Wi-Fi ජාල වැනි ජාල මූලාශ්‍ර පදනම්ව ඔබගේ ස්ථානය ලබා ගත හැකිය. යෙදුමට ඒවා භාවිත කිරීමට හැකි වීමට මෙම ස්ථාන සේවා ක්‍රියාත්මක කර සහ ඔබේ දුරකථනය මත ලබා ගත හැකිව තිබිය යුතුය."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"පසුබිම තුළ නිශ්චිත ස්ථානය වෙත පිවිසෙන්න"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"මෙම යෙදුම පසුබිම තුළ ඇති ඕනෑම අවස්ථාවක එයට ඔබේ නිශ්චිත ස්ථානය ලබා ගත හැකිය. යෙදුමට ඒවා භාවිත කිරීමට හැකි වීමට මෙම ස්ථාන සේවා ක්‍රියාත්මක කර සහ ඔබේ දුරකථනය මත ලබා ගත හැකිව තිබිය යුතුය. මෙය බැටරි පරිභෝජනය වැඩි කළ හැකිය."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"පසුබිමේ ස්ථානය ප්‍රවේශ කිරීම"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"මෙය ආසන්න වශයෙන් හෝ නිශ්චිත ස්ථානයක ප්‍රවේශය ලබා දෙන්නේ නම් පසුබිම් ධාවන අතරතුරදී යෙදුමට ස්ථානය වෙත ප්‍රවේශය විය හැක."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ඔබගේ ශ්‍රව්‍ය සැකසීම් වෙනස් කරන්න"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ශබ්දය ආදී ගෝලීය ශබ්ද සැකසීම් වෙනස් කිරීමට සහ ප්‍රතිදානය සඳහා භාවිත කරන්නේ කුමන නාදකය දැයි තේරීමට යෙදුමට අවසර දෙන්න."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ශබ්ද පටිගත කරන්න"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"මෙම යෙදුමට ඕනෑම වේලාවක මයික්‍රෆෝනය භාවිතයෙන් ශ්‍රව්‍ය පටිගත කිරීමට හැකිය."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM වෙත විධාන යැවීම"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"SIM වෙත විධාන ගෙන යාමට යෙදුමට අවසර දෙයි. මෙය ඉතා භයානක වේ."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"ශාරීරික ක්‍රියාකාරකම හඳුනා ගන්න"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"මෙම යෙදුමට ඔබේ ශාරීරික ක්‍රියාකාරකම හඳුනා ගැනීමට නොහැකිය"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"පින්තූර සහ වීඩියෝ ගන්න"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"මෙම යෙදුමට ඕනෑම වේලාවක කැමරාව භාවිත කර පින්තූර ගැනීමට සහ වීඩියෝ පටිගත කිරීමට හැකිය."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"කම්පනය පාලනය කිරීම"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"ඔබගේ ඡායාරූප එකතුව වෙනස් කිරීමට යෙදුමට ඉඩ දෙයි."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"ඔබගේ මාධ්‍ය එකතුවෙන් ස්ථාන කියවන්න"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"ඔබගේ මාධ්‍ය එකතුවෙන් ස්ථාන කියවීමට යෙදුමට ඉඩ දෙයි."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> යෙදුම වෙත සත්‍යාපනය කිරීමට අවශ්‍යයි."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ජීවමිතික දෘඪාංග ලබා ගත නොහැකිය"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ඇඟිලි සලකුණ අඩ වශයෙන් අනාවරණය කර ගැනිණි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ඇඟිලි සලකුණ පිරිසැකසීමට නොහැකි විය. කරුණාකර නැවත උත්සාහ කරන්න."</string>
@@ -1899,14 +1905,14 @@
       <item quantity="one">ස්වයං පිරවුම් යෝජනා <xliff:g id="COUNT">%1$s</xliff:g></item>
       <item quantity="other">ස්වයං පිරවුම් යෝජනා <xliff:g id="COUNT">%1$s</xliff:g></item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; වෙත සුරකින්නද?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; වෙත සුරකින්නද?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> සහ <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; වෙත සුරකින්නද?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, සහ <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; වෙත සුරකින්නද?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; වෙත යාවත්කාලීන කරන්නද?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; වෙත යාවත්කාලීන කරන්නද?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> සහ <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; වෙත යාවත්කාලීන කරන්නද?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, සහ <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; වෙත යාවත්කාලීන කරන්නද?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" වෙත සුරකින්නද?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" වෙත <xliff:g id="TYPE">%1$s</xliff:g> සුරකින්නද?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" වෙත <xliff:g id="TYPE_0">%1$s</xliff:g> සහ <xliff:g id="TYPE_1">%2$s</xliff:g> සුරකින්නද?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" වෙත <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, සහ <xliff:g id="TYPE_2">%3$s</xliff:g> සුරකින්න?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" තුළ යාවත්කාලීන කරන්නද?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" තුළ <xliff:g id="TYPE">%1$s</xliff:g> යාවත්කාලීන කරන්නද?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" තුළ <xliff:g id="TYPE_0">%1$s</xliff:g> සහ <xliff:g id="TYPE_1">%2$s</xliff:g> යාවත්කාලීන කරන්නද?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" තුළ යාවත්කාලීන කරන්නද?: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, සහ <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"සුරකින්න"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"එපා ස්තූතියි"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"යාවත්කාලීන කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index c0981ce..525fcde 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -298,6 +298,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofón"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"nahrávanie zvuku"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; zaznamenávať zvuk?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"rozpoznávanie aktivity"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"rozpoznanie aktivity"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; rozpoznávať vašu fyzickú aktivitu?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparát"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotenie a natáčanie videí"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snímať fotky a zaznamenávať video?"</string>
@@ -424,14 +427,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Táto aplikácia môže získať údaje o vašej polohe na základe sieťových zdrojov, ako sú mobilné veže a siete Wi‑Fi. Na to, aby aplikácia mohla používať služby určovania polohy, musia byť tieto služby zapnuté a k dispozícii na tablete."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Táto aplikácia môže získať údaje o vašej polohe na základe sieťových zdrojov, ako sú mobilné veže a siete Wi‑Fi. Na to, aby aplikácia mohla používať služby určovania polohy, musia byť tieto služby zapnuté a k dispozícii na televízore."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Táto aplikácia môže získať údaje o vašej polohe na základe sieťových zdrojov, ako sú mobilné veže a siete Wi‑Fi. Na to, aby aplikácia mohla používať služby určovania polohy, musia byť tieto služby zapnuté a k dispozícii na telefóne."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"prístup k presnej polohe na pozadí"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Táto aplikácia dokáže kedykoľvek získať vašu presnú polohu, keď je spustená na pozadí. Na to, aby mohla používať služby určovania polohy, musia byť tieto služby zapnuté a k dispozícii v telefóne. Môže to zvýšiť spotebu batérie."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"prístup k polohe na pozadí"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Ak okrem prístupu k približnej alebo presnej polohe udelíte aj toto povolenie, aplikácia bude môcť používať polohu, keď bude spustená na pozadí."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"meniť nastavenia zvuku"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Umožňuje aplikácii upraviť globálne nastavenia zvuku, ako je hlasitosť, alebo určiť, z ktorého reproduktora bude zvuk vychádzať."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"nahrávať zvuk"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Táto aplikácia môže kedykoľvek zaznamenávať zvuk pomocou mikrofónu."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"posielanie príkazov do SIM karty"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Umožňuje aplikácii odosielať príkazy na SIM kartu. Toto je veľmi nebezpečné povolenie."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"rozpoznávanie fyzickej aktivity"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Táto aplikácia dokáže rozpoznať vašu fyzickú aktivitu."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"fotiť a nakrúcať videá"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Táto aplikácia môže kedykoľvek fotografovať a zaznamenávať videá pomocou fotoaparátu."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ovládať vibrovanie"</string>
@@ -524,6 +529,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Umožňuje aplikácii upravovať zbierku fotiek."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"čítať polohy zo zbierky médií"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Umožňuje aplikácii čítať polohy zo zbierky médií."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikácia <xliff:g id="APP">%s</xliff:g> chce overiť totožnosť"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrický hardvér nie je k dispozícii"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Podarilo sa rozpoznať iba časť odtlačku prsta. Skúste to znova."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Odtlačok prsta sa nepodarilo spracovať. Skúste to znova."</string>
@@ -1967,14 +1973,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> návrhov automatického dopĺňania</item>
       <item quantity="one">Jeden návrh automatického dopĺňania</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Uložiť do služby &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Uložiť <xliff:g id="TYPE">%1$s</xliff:g> do služby &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Uložiť <xliff:g id="TYPE_0">%1$s</xliff:g> a <xliff:g id="TYPE_1">%2$s</xliff:g> do služby &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Uložiť <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> a <xliff:g id="TYPE_2">%3$s</xliff:g> do služby &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Nahrať do služby &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Chcete nahrať <xliff:g id="TYPE">%1$s</xliff:g> do služby &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Chcete nahrať <xliff:g id="TYPE_0">%1$s</xliff:g> a <xliff:g id="TYPE_1">%2$s</xliff:g> do služby &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Chcete nahrať <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> a <xliff:g id="TYPE_2">%3$s</xliff:g> do služby &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Uložiť do služby "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Chcete položku <xliff:g id="TYPE">%1$s</xliff:g> uložiť do služby "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Chcete položky <xliff:g id="TYPE_0">%1$s</xliff:g> a <xliff:g id="TYPE_1">%2$s</xliff:g> uložiť do služby "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Chcete položky <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> a <xliff:g id="TYPE_2">%3$s</xliff:g> uložiť do služby "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Aktualizovať v službe "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Chcete údaje <xliff:g id="TYPE">%1$s</xliff:g> aktualizovať v službe "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Chcete položky <xliff:g id="TYPE_0">%1$s</xliff:g> a <xliff:g id="TYPE_1">%2$s</xliff:g> aktualizovať v službe "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Chcete tieto položky aktualizovať v službe "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> a <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Uložiť"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Nie, vďaka"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Aktualizovať"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 261e6ed..252246a 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -298,6 +298,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"snemanje zvoka"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; omogočiti snemanje zvoka?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Prepoznavanje dejavnosti"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"prepoznavanje dejavnosti"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; omogočiti prepoznavanje svoje telesne dejavnosti?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparat"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotografiranje in snemanje videoposnetkov"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; omogočiti fotografiranje in snemanje videoposnetkov?"</string>
@@ -424,14 +427,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ta aplikacija lahko pridobi vašo lokacijo na podlagi omrežnih virov, kot so bazne postaje in omrežja Wi-Fi. Če želite aplikaciji omogočiti uporabo teh lokacijskih storitev, morajo biti te vklopljene in na voljo v tabličnem računalniku."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ta aplikacija lahko pridobi vašo lokacijo na podlagi omrežnih virov, kot so bazne postaje in omrežja Wi-Fi. Če želite aplikaciji omogočiti uporabo teh lokacijskih storitev, morajo biti te vklopljene in na voljo v televizorju."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ta aplikacija lahko pridobi vašo lokacijo na podlagi omrežnih virov, kot so bazne postaje in omrežja Wi-Fi. Če želite aplikaciji omogočiti uporabo teh lokacijskih storitev, morajo biti te vklopljene in na voljo v telefonu."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"dostop do točne lokacije, ko deluje v ozadju"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ta aplikacija lahko pridobi vašo točno lokacijo, ko deluje v ozadju. Če želite aplikaciji omogočiti uporabo teh lokacijskih storitev, morajo biti te vklopljene in na voljo v telefonu. Poraba energije akumulatorja bo morda večja."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"dostop do lokacije med izvajanjem v ozadju"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Če to dovolite poleg dostopa do približne ali natančne lokacije, lahko aplikacija dostopa do lokacije, medtem ko se izvaja v ozadju."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"spreminjanje nastavitev zvoka"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Aplikaciji omogoča spreminjanje splošnih zvočnih nastavitev, na primer glasnost in kateri zvočnik se uporablja."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"snemanje zvoka"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Ta aplikacija lahko poljubno uporablja mikrofon za snemanje zvoka."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"pošiljanje ukazov na kartico SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Aplikaciji dovoli pošiljanje ukazov kartici SIM. To je lahko zelo nevarno."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"prepoznavanje telesne dejavnosti"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Ta aplikacija lahko prepoznava vašo telesno dejavnost."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"fotografiranje in snemanje videoposnetkov"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Ta aplikacija lahko poljubno uporablja fotoaparat za snemanje fotografij ali videoposnetkov."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"nadzor vibriranja"</string>
@@ -524,6 +529,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Aplikaciji omogoča spreminjanje zbirke fotografij."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"branje lokacij v predstavnostni zbirki"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Aplikaciji omogoča branje lokacij v predstavnostni zbirki."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi preveriti pristnost."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Strojna oprema za biometrične podatke ni na voljo"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Zaznan delni prstni odtis. Poskusite znova."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Prstnega odtisa ni bilo mogoče obdelati. Poskusite znova."</string>
@@ -669,7 +675,7 @@
     <string name="policydesc_expirePassword" msgid="5367525762204416046">"Spreminjanje tega, kako pogosto je treba spremeniti geslo, kodo PIN ali vzorec za zaklepanje zaslona."</string>
     <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Nastavitev šifriranja shrambe"</string>
     <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Shranjeni podatki aplikacije morajo biti šifrirani."</string>
-    <string name="policylab_disableCamera" msgid="6395301023152297826">"Onemogoči fotoaparate"</string>
+    <string name="policylab_disableCamera" msgid="6395301023152297826">"Onemogočanje fotoaparatov"</string>
     <string name="policydesc_disableCamera" msgid="2306349042834754597">"Prepreči uporabo vseh fotoaparatov v napravi."</string>
     <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Onemogočanje nekaterih funkcij zaklepanja zaslona"</string>
     <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Prepreči uporabo nekaterih funkcij zaklepanja zaslona."</string>
@@ -1967,14 +1973,14 @@
       <item quantity="few"><xliff:g id="COUNT">%1$s</xliff:g> predlogi za samodejno izpolnjevanje</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> predlogov za samodejno izpolnjevanje</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Želite shraniti pod oznako &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Želite <xliff:g id="TYPE">%1$s</xliff:g> shraniti pod oznako &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Želite <xliff:g id="TYPE_0">%1$s</xliff:g> in <xliff:g id="TYPE_1">%2$s</xliff:g> shraniti pod oznako &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Želite <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> in <xliff:g id="TYPE_2">%3$s</xliff:g> shraniti pod oznako &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Želite posodobiti na oznako &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Želite <xliff:g id="TYPE">%1$s</xliff:g> posodobiti na oznako &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Želite <xliff:g id="TYPE_0">%1$s</xliff:g> in <xliff:g id="TYPE_1">%2$s</xliff:g> posodobiti na oznako &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Želite <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> in <xliff:g id="TYPE_2">%3$s</xliff:g> posodobiti na oznako &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Želite shraniti v aplikacijo "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Želite shraniti element <xliff:g id="TYPE">%1$s</xliff:g> v aplikacijo "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Želite shraniti elementa <xliff:g id="TYPE_0">%1$s</xliff:g> in <xliff:g id="TYPE_1">%2$s</xliff:g> v aplikacijo "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Želite shraniti elemente <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> in <xliff:g id="TYPE_2">%3$s</xliff:g> v aplikacijo "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Želite posodobiti v aplikaciji "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Želite posodobiti element <xliff:g id="TYPE">%1$s</xliff:g> v aplikaciji "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Želite posodobiti elementa <xliff:g id="TYPE_0">%1$s</xliff:g> in <xliff:g id="TYPE_1">%2$s</xliff:g> v aplikaciji "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Želite posodobiti te elemente v aplikaciji "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> in <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Shrani"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Ne, hvala"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Posodobi"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 4b6cb09..9062f29 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofoni"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"regjistro audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të regjistrojë audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Njohja e aktivitetit"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"njih aktivitetin"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të njohë aktivitetin tënd fizik?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"bëj fotografi dhe regjistro video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të nxjerrë fotografi dhe të regjistrojë video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ky aplikacion mund të marrë vendndodhjen tënde bazuar në burimet e rrjetit si antenat e operatorëve celulare dhe rrjetet Wi-Fi. Këto shërbime të vendndodhjes duhet të jenë të aktivizuara dhe në dispozicion në tabletin tënd që aplikacioni të mund t\'i përdorë."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ky aplikacion mund të marrë vendndodhjen tënde bazuar në burimet e rrjetit si antenat e operatorëve celulare dhe rrjetet Wi-Fi. Këto shërbime të vendndodhjes duhet të jenë të aktivizuara dhe në dispozicion në televizorin tënd që aplikacioni të mund t\'i përdorë."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ky aplikacion mund të marrë vendndodhjen tënde bazuar në burimet e rrjetit si antenat e operatorëve celulare dhe rrjetet Wi-Fi. Këto shërbime të vendndodhjes duhet të jenë të aktivizuara dhe në dispozicion në telefonin tënd që aplikacioni të mund t\'i përdorë."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"qasu në vendndodhjen e saktë në sfond"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ky aplikacion mund të marrë vendndodhjen tënde të saktë në çdo kohë kur është në sfond. Këto shërbime të vendndodhjes duhet të jenë të aktivizuara dhe në dispozicion në telefonin tënd që aplikacioni të mund t\'i përdorë. Kjo gjë mund të rritë konsumin e baterisë."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"qasje te vendndodhja në sfond"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Nëse kjo jepet përveç qasjes te vendndodhja e përafërt ose të saktë, aplikacioni mund të qaset te vendndodhja ndërkohë që ekzekutohet në sfond."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ndrysho cilësimet e audios"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Lejon aplikacionin të modifikojë cilësimet globale të audios siç është volumi dhe se cili altoparlant përdoret për daljen."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"regjistro audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Ky aplikacion mund të regjistrojë audio me mikrofonin në çdo kohë."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"dërgo komanda te karta SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Lejon aplikacionin t\'i dërgojë komanda kartës SIM. Kjo është shumë e rrezikshme."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"njih aktivitetin fizik"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Ky aplikacion mund të njohë aktivitetin tënd fizik."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"bëj fotografi dhe video"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Ky aplikacion mund të nxjerrë fotografi dhe të regjistrojë video me kamerën tënde në çdo kohë."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrollo dridhjen"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Lejon aplikacionin të modifikojë koleksionin tënd të fotografive."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"lexo vendndodhjet nga koleksioni yt i medias"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Lejon aplikacionin të lexojë vendndodhjet nga koleksioni yt i medias."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Aplikacioni <xliff:g id="APP">%s</xliff:g> dëshiron të vërtetojë."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Nuk ofrohet harduer biometrik"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"U zbulua një gjurmë gishti e pjesshme. Provo përsëri."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Gjurma e gishtit nuk mund të përpunohej. Provo përsëri."</string>
@@ -1898,14 +1904,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> sugjerime për plotësim automatik</item>
       <item quantity="one">Një sugjerim për plotësim automatik</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Të ruhet te &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Të ruhet <xliff:g id="TYPE">%1$s</xliff:g> te &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Të ruhen <xliff:g id="TYPE_0">%1$s</xliff:g> dhe <xliff:g id="TYPE_1">%2$s</xliff:g> te &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Të ruhen <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> dhe <xliff:g id="TYPE_2">%3$s</xliff:g> te &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Të përditësohet te &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Të përditësohet <xliff:g id="TYPE">%1$s</xliff:g> te &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Të përditësohen <xliff:g id="TYPE_0">%1$s</xliff:g> dhe <xliff:g id="TYPE_1">%2$s</xliff:g> te &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Të përditësohen <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> dhe <xliff:g id="TYPE_2">%3$s</xliff:g> te &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Të ruhet te "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Të ruhet <xliff:g id="TYPE">%1$s</xliff:g> te "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Të ruhet <xliff:g id="TYPE_0">%1$s</xliff:g> dhe <xliff:g id="TYPE_1">%2$s</xliff:g> te "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Të ruhet <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> dhe <xliff:g id="TYPE_2">%3$s</xliff:g> te "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Të përditësohet në "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Të përditësohet <xliff:g id="TYPE">%1$s</xliff:g> në "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Të përditësohet <xliff:g id="TYPE_0">%1$s</xliff:g> dhe <xliff:g id="TYPE_1">%2$s</xliff:g> në "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Të përditësohen këta artikuj në "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> dhe <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Ruaj"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Jo, faleminderit"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Përditëso"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 449fdfc..642e12e 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -295,6 +295,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"снима звук"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Желите ли да омогућите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима звук?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Препознавање активности"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"препознавање активности"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Желите ли да омогућите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; препознаје физичке активности?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"снима слике и видео"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Желите ли да омогућите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима слике и видео снимке?"</string>
@@ -421,14 +424,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ова апликација може да приступи вашој локацији помоћу извора мреже, као што су мобилни предајници и Wi-Fi мреже. Ове услуге локације морају да буду укључене и доступне на таблету да би апликација могла да их користи."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ова апликација може да приступи вашој локацији помоћу извора мреже, као што су мобилни предајници и Wi-Fi мреже. Ове услуге локације морају да буду укључене и доступне на ТВ-у да би апликација могла да их користи."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ова апликација може да приступи вашој локацији помоћу извора мреже, као што су мобилни предајници и Wi-Fi мреже. Ове услуге локације морају да буду укључене и доступне на телефону да би апликација могла да их користи."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"приступ прецизној локацији у позадини"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ова апликација може да одреди вашу тачну локацију сваки пут када ради у позадини. Ове услуге локације морају да буду укључене и доступне на телефону да би апликација могла да их користи. То може да повећа потрошњу батерије."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"приступ локацији у позадини"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Ако се поред приближног или прецизног приступа локација одобри и овај, апликација може да приступа локацији док је покренута у позадини."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"промена аудио подешавања"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Дозвољава апликацији да мења глобална аудио подешавања као што су јачина звука и избор звучника који се користи као излаз."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"снимање аудио записа"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Ова апликација може да снима звук помоћу микрофона у било ком тренутку."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"слање команди на SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Омогућава апликацији да шаље команде SIM картици. То је веома опасно."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"препознавање физичких активности"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Ова апликација може да препозна физичке активности."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"снимање фотографија и видео снимака"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Ова апликација може да снима фотографије и видео снимке помоћу камере у било ком тренутку."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"контрола вибрације"</string>
@@ -521,6 +526,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Дозвољава апликацији да мења колекцију слика."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"читање локација из медијске колекције"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Дозвољава апликацији да чита локације из медијске колекције."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Апликација <xliff:g id="APP">%s</xliff:g> жели да потврди ваш идентитет."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометријски хардвер није доступан"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Откривен је делимични отисак прста. Пробајте поново."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Није успела обрада отиска прста. Пробајте поново."</string>
@@ -1932,14 +1938,14 @@
       <item quantity="few"><xliff:g id="COUNT">%1$s</xliff:g> аутоматски попуњена предлога</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> аутоматски попуњених предлога</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Желите ли да сачувате у: &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Желите ли да сачувате ставку <xliff:g id="TYPE">%1$s</xliff:g> у: &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Желите ли да сачувате ставке <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> у: &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Желите ли да сачувате ставке <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g> у: &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Желите ли да ажурирате на &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Желите ли да ажурирате ставку <xliff:g id="TYPE">%1$s</xliff:g> на &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Желите ли да ажурирате ставке <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> на &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Желите ли да ажурирате ставке <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g> на &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Желите ли да сачувате у услузи "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Желите ли да сачувате ставку <xliff:g id="TYPE">%1$s</xliff:g> у услузи "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Желите ли да сачувате ставке <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> у услузи "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Желите ли да сачувате ставке <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g> у услузи "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Желите ли да ажурирате у услузи "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Желите ли да ажурирате ставку <xliff:g id="TYPE">%1$s</xliff:g> у услузи "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Желите ли да ажурирате ставке <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> у услузи "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Желите ли да ажурирате ове ставке у услузи "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Сачувај"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Не, хвала"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Ажурирај"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 9f46514..2ace15c 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"spela in ljud"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att spela in ljud?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Aktivitetsigenkänning"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"känn igen aktivitet"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Vill du tillåta att &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; känner igen din fysiska aktivitet?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ta bilder och spela in video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att ta bilder och spela in video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Appen kan få information om din plats från källor i nätverket, som mobilmaster och Wi-Fi-nätverk. De platstjänsterna måste ha aktiverats och finnas på surfplattan om appen ska kunna använda dem."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Appen kan få information om din plats från källor i nätverket, som mobilmaster och Wi-Fi-nätverk. De platstjänsterna måste ha aktiverats och fungera på TV:n om appen ska kunna använda dem."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Appen kan få information om din plats från källor i nätverket, som mobilmaster och Wi-Fi-nätverk. De platstjänsterna måste ha aktiverats och finnas på mobilen om appen ska kunna använda dem."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"åtkomst till exakt plats i bakgrunden"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Den här appen kan när som helst få information om din exakta plats när den körs i bakgrunden. Platstjänsterna måste ha aktiverats och finnas på mobilen om appen ska kunna använda dem. Detta kan leda till ökad batteriförbrukning."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"få åtkomst till platsinformation i bakgrunden"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Om denna behörighet ges utöver ungefärlig eller exakt platsåtkomst får appen åtkomst till platsinformation när den körs i bakgrunden."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ändra dina ljudinställningar"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tillåter att appen ändrar globala ljudinställningar som volym och vilken högtalarutgång som används."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"spela in ljud"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Appen kan spela in ljud med mikrofonen när som helst."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"skicka kommandon till SIM-kortet"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Tillåter att appen skickar kommandon till SIM-kortet. Detta är mycket farligt."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"känn igen fysisk aktivitet"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Den här appen kan känna igen fysisk aktivitet."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ta bilder och spela in videoklipp"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Appen kan ta kort och spela in video med kameran när som helst."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"styra vibration"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Tillåter att appen gör ändringar i din fotosamling."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"läsa av platser i din mediesamling"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Tillåter att appen läser av platser i din mediesamling."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Appen <xliff:g id="APP">%s</xliff:g> vill autentisera"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrisk maskinvara är inte tillgänglig"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Ofullständigt fingeravtryck. Försök igen."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> förslag från autofyll</item>
       <item quantity="one">Ett förslag från autofyll</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Vill du spara innehållet i &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Vill du spara <xliff:g id="TYPE">%1$s</xliff:g> i &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Vill du spara <xliff:g id="TYPE_0">%1$s</xliff:g> och <xliff:g id="TYPE_1">%2$s</xliff:g> i &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Vill du spara <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> och <xliff:g id="TYPE_2">%3$s</xliff:g> i &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Vill du uppdatera till &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Vill du uppdatera <xliff:g id="TYPE">%1$s</xliff:g> till &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Vill du uppdatera <xliff:g id="TYPE_0">%1$s</xliff:g> och <xliff:g id="TYPE_1">%2$s</xliff:g> till &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Vill du uppdatera <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> och <xliff:g id="TYPE_2">%3$s</xliff:g> till &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Vill du spara i "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Vill du spara <xliff:g id="TYPE">%1$s</xliff:g> i "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Vill du spara <xliff:g id="TYPE_0">%1$s</xliff:g> och <xliff:g id="TYPE_1">%2$s</xliff:g> i "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Vill du spara <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> och <xliff:g id="TYPE_2">%3$s</xliff:g> i "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Vill du uppdatera detta i "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Vill du uppdatera <xliff:g id="TYPE">%1$s</xliff:g> i "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Vill du uppdatera <xliff:g id="TYPE_0">%1$s</xliff:g> och <xliff:g id="TYPE_1">%2$s</xliff:g> i "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Vill du uppdatera dessa objekt i "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> och <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Spara"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Nej tack"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Uppdatera"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index b0858dc..dcfa08d 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Kipokea sauti"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"irekodi sauti"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; irekodi sauti?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Kutambua shughuli"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"itambue shughuli"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; itambue shughuli unazofanya?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ipige picha na kurekodi video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ipige picha na kurekodi video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Programu hii inaweza kupata eneo lako kulingana na vyanzo vya mtandao kama vile minara ya simu na mitandao ya Wi-Fi. Huduma hizi za mahali lazima ziwashwe na zipatikane kwenye kompyuta yako kibao ili programu iweze kuzitumia."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Programu hii inaweza kupata eneo lako kulingana na vyanzo vya mtandao kama vile minara ya simu na mitandao ya Wi-Fi. Huduma hizi za mahali lazima ziwashwe na zipatikane kwenye runinga yako ili programu iweze kuzitumia."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Programu hii inaweza kupata eneo lako kulingana na vyanzo vya mtandao kama vile minara ya simu na mitandao ya Wi-Fi. Huduma hizi za mahali lazima ziwashwe na zipatikane kwenye simu yako ili programu iweze kuzitumia."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"kufikia mahali halisi ikiwa inatumika chinichini"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Programu hii inaweza kupata mahali halisi ulipo wakati wowote ikiwa inatumika chinichini. Ni lazima uwashe huduma hizi za mahali na zipatikane kwenye simu yako ili programu iweze kuzitumia. Hatua hii inaweza kuongeza utumiaji wa betri."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"ifikie mahali inapotumika chinichini"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Kama ruhusa hii itatolewa, mbali na idhini ya kufikia mahali panapokadiriwa au mahali mahususi, programu inaweza kufikia mahali wakati inatumika chinichini."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"badilisha mipangilio yako ya sauti"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Inaruhusu programu kurekebisha mipangilio ya sauti kila mahali kama vile sauti na ni kipaza sauti kipi ambacho kinatumika kwa kutoa."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"kurekodi sauti"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Programu hii inaweza kurekodi sauti kwa kutumia maikrofoni wakati wowote."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"tuma amri kwenye SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Huruhusu programu kutuma amri kwa SIM. Hii ni hatari sana."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"itambue shughuli unazofanya"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Programu hii inaweza kutambua shughuli unazofanya."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"Kupiga picha na kurekodi video"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Programu hii inaweza kupiga picha na kurekodi video kwa kutumia kamera wakati wowote."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"Kudhibiti mtetemo"</string>
@@ -465,7 +470,7 @@
     <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Huruhusu programu kupata orodha ya akaunti zinazojulikana na runinga. Hii inaweza kujumuisha akaunti zozote zilizofunguliwa na programu ambazo umesakinisha."</string>
     <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Inaruhusu programu kupata orodha ya akaunti zinazojulikana kwa simu. Hii inaweza kujumuisha akaunti zozote zilizoundwa na programu ambazo umesakinisha."</string>
     <string name="permlab_accessNetworkState" msgid="4951027964348974773">"kuona mitandao"</string>
-    <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Inaruhusu programu kuona taarifa kuhusu miunganisho ya mtandao kama vile mitandao ipi iliyopo na imeunganishwa."</string>
+    <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Huruhusu programu kuona taarifa kuhusu miunganisho ya mtandao kama vile mitandao iliyopo na iliyounganishwa."</string>
     <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"pata ufikiaji kamili wa mtandao"</string>
     <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Inaruhusu programu kuunda soketi za mtandao na kutumia itifaki za mtandao maalum. Kivinajri na programu nyingine zilizotolewa zinamaanisha kutuma data kwenye mtandao, kwa hivyo kibali hiki hakihitajiki kutuma data kwenye mtandao."</string>
     <string name="permlab_changeNetworkState" msgid="958884291454327309">"kubadilisha muunganisho wa mtandao"</string>
@@ -473,7 +478,7 @@
     <string name="permlab_changeTetherState" msgid="5952584964373017960">"Badilisha muunganisho uliofunganishwa"</string>
     <string name="permdesc_changeTetherState" msgid="1524441344412319780">"Inaruhusu programu kubadilisha hali ya muunganisho wa mtandao uliofungwa."</string>
     <string name="permlab_accessWifiState" msgid="5202012949247040011">"Kuona miunganisho ya Wi-Fi"</string>
-    <string name="permdesc_accessWifiState" msgid="5002798077387803726">"Inaruhusu programu kuona taarifa kuhusu mtandao wa Wi-Fi, kama vile ikiwa Wi-Fi imewezeshwa mna jina la vifaa vya Wi-Fi vilivyounganishwa."</string>
+    <string name="permdesc_accessWifiState" msgid="5002798077387803726">"Huruhusu programu kuona taarifa kuhusu mtandao wa Wi-Fi, kama vile ikiwa Wi-Fi imewashwa na majina ya vifaa vya Wi-Fi vilivyounganishwa."</string>
     <string name="permlab_changeWifiState" msgid="6550641188749128035">"unganisha na utenganishe kutoka kwa Wi-Fi"</string>
     <string name="permdesc_changeWifiState" msgid="7137950297386127533">"Inaruhusu programu kuunganisha kwenye au kukata kutoka pointi za ufikivu wa Wi-Fi na kufanya mabadiliko kwenye usanidi wa kifaa cha mitandao ya Wi-Fi."</string>
     <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"ruhusu upokeaji wa Wi-Fi Multicast"</string>
@@ -491,9 +496,9 @@
     <string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Huruhusu programu kuunganisha runinga kwenye na kuondoa runinga kutoka mitandao ya WiMAX."</string>
     <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Inaruhusu programu kuunganisha simu kwenye, na kukata simu kutoka mitandao ya WiMAX."</string>
     <string name="permlab_bluetooth" msgid="6127769336339276828">"oanisha na vifaa vya Bluetooth"</string>
-    <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Inaruhusu programu kuona usanidi wa Bluetooth kwenye kompyuta kibao, na kuunda na kukubali miunganisho kwa vifaa vilivyooanishwa."</string>
+    <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Huruhusu programu kuona usanidi wa Bluetooth kwenye kompyuta kibao, na kutuma na kukubali miunganisho kwa vifaa vilivyooanishwa."</string>
     <string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"Huruhusu programu kuona usanidi wa Bluetooth kwenye runinga, na kuomba na kukubali miunganisho kwa vifaa vilivyooanishwa."</string>
-    <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Inaruhusu programu kuona usanidi wa Bluetooth kwenye simu, na kuunda na kukubali miunganisho kwa vifaa vilivyooanishwa."</string>
+    <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Huruhusu programu kuona usanidi wa Bluetooth kwenye simu, na kutuma na kukubali miunganisho kwa vifaa vilivyooanishwa."</string>
     <string name="permlab_nfc" msgid="4423351274757876953">"kudhibiti Mawasiliano ya Vifaa Vilivyokaribu (NFC)"</string>
     <string name="permdesc_nfc" msgid="7120611819401789907">"Inaruhusu programu kuwasiliana na lebo, kadi na wasomaji wa Near Field Communication (NFC)."</string>
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"zima kufuli la skrini yako"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Inaruhusu programu kubadilisha mkusanyiko wa picha zako."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"kusoma maeneo kwenye mkusanyiko wa vipengee vyako"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Inaruhusu programu kusoma maeneo kwenye mkusanyiko wa vipengee vyako."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Programu ya <xliff:g id="APP">%s</xliff:g> inataka kuthibitishwa."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Maunzi ya bayometriki hayapatikani"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Kitambuzi kimegundua sehemu ya kitambulisho. Tafadhali jaribu tena."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Haikuweza kuchakata kitambulisho. Tafadhali jaribu tena."</string>
@@ -1581,7 +1587,7 @@
     </plurals>
     <string name="kg_pattern_instructions" msgid="398978611683075868">"Chora ruwaza yako"</string>
     <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ingiza PIN ya SIM"</string>
-    <string name="kg_pin_instructions" msgid="2377242233495111557">"Ingiza PIN"</string>
+    <string name="kg_pin_instructions" msgid="2377242233495111557">"Weka PIN"</string>
     <string name="kg_password_instructions" msgid="5753646556186936819">"Weka Nenosiri"</string>
     <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM sasa imelemazwa. Ingiza msimbo wa PUK ili kuendelea. Wasiliana na mtoa huduma kwa maelezo."</string>
     <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Weka nambari yako ya PIN"</string>
@@ -1725,7 +1731,7 @@
     <string name="print_service_installed_title" msgid="2246317169444081628">"Huduma ya <xliff:g id="NAME">%s</xliff:g> imesakinisha"</string>
     <string name="print_service_installed_message" msgid="5897362931070459152">"Gusa ili uwashe"</string>
     <string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"Weka PIN ya msimamizi"</string>
-    <string name="restr_pin_enter_pin" msgid="3395953421368476103">"Ingiza PIN"</string>
+    <string name="restr_pin_enter_pin" msgid="3395953421368476103">"Weka PIN"</string>
     <string name="restr_pin_incorrect" msgid="8571512003955077924">"Sio sahihi"</string>
     <string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"PIN ya sasa"</string>
     <string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"PIN mpya"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other">Mapendekezo <xliff:g id="COUNT">%1$s</xliff:g> ya kujaza kiotomatiki</item>
       <item quantity="one">Pendekezo moja la kujaza kiotomatiki</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Ungependa kuhifadhi kwenye &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Ungependa kuhifadhi <xliff:g id="TYPE">%1$s</xliff:g> kwenye &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Ungependa kuhifadhi <xliff:g id="TYPE_0">%1$s</xliff:g> na <xliff:g id="TYPE_1">%2$s</xliff:g> kwenye &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Ungependa kuhifadhi <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, na <xliff:g id="TYPE_2">%3$s</xliff:g> kwenye &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Ungependa kusasisha kuwa &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Ungependa kusasisha <xliff:g id="TYPE">%1$s</xliff:g> kuwa &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Ungependa kusasisha <xliff:g id="TYPE_0">%1$s</xliff:g> na <xliff:g id="TYPE_1">%2$s</xliff:g> kuwa &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Ungependa kusasisha <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> na <xliff:g id="TYPE_2">%3$s</xliff:g> kuwa &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Ungependa kuhifadhi kwenye "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Ungependa kuhifadhi <xliff:g id="TYPE">%1$s</xliff:g> kwenye "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Ungependa kuhifadhi <xliff:g id="TYPE_0">%1$s</xliff:g> na <xliff:g id="TYPE_1">%2$s</xliff:g> kwenye "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Ungependa kuhifadhi <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> na <xliff:g id="TYPE_2">%3$s</xliff:g> kwenye "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Ungependa kusasisha katika "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Ungependa kusasisha <xliff:g id="TYPE">%1$s</xliff:g> katika "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Ungependa kusasisha <xliff:g id="TYPE_0">%1$s</xliff:g> na <xliff:g id="TYPE_1">%2$s</xliff:g> katika "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Ungependa kusasisha vipengee hivi katika "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> na <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Hifadhi"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Hapana, asante"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Sasisha"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 3bf8b78..8ed9b7d 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"மைக்ரோஃபோன்"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ஒலிப் பதிவு செய்யலாம்"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"ஆடியோவைப் பதிவு செய்ய &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; பயன்பாட்டை அனுமதிக்கவா?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"செயல்பாட்டைக் கண்டறிதல்"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"செயல்பாட்டைக் கண்டறியும்"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"உங்கள் உடல் செயல்பாட்டைக் கண்டறிய &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"கேமரா"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"படங்கள் மற்றும் வீடியோக்கள் எடுக்கலாம்"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"படங்கள் எடுக்கவும், வீடியோ பதிவு செய்யவும் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; பயன்பாட்டை அனுமதிக்கவா?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"இந்தப் பயன்பாடு நெட்வொர்க் மூலங்களின் (செல் கோபுரங்கள், வைஃபை நெட்வொர்க்குகள் போன்றவை) அடிப்படையில் உங்கள் இருப்பிடத்தைப் பெறலாம். பயன்பாடு பயன்படுத்தும் வகையில், உங்கள் டேப்லெட்டில் இந்த இருப்பிடச் சேவைகள் இயக்கப்பட்டு, கிடைக்கும்படி இருக்க வேண்டும்."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"இந்தப் பயன்பாடு நெட்வொர்க் மூலங்களின் (செல் கோபுரங்கள், வைஃபை நெட்வொர்க்குகள் போன்றவை) அடிப்படையில் உங்கள் இருப்பிடத்தைப் பெறலாம். பயன்பாடு பயன்படுத்தும் வகையில், உங்கள் டிவியில் இந்த இருப்பிடச் சேவைகள் இயக்கப்பட்டு, கிடைக்கும்படி இருக்க வேண்டும்."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"இந்தப் பயன்பாடு நெட்வொர்க் மூலங்களின் (செல் கோபுரங்கள், வைஃபை நெட்வொர்க்குகள் போன்றவை) அடிப்படையில் உங்கள் இருப்பிடத்தைப் பெறலாம். பயன்பாடு பயன்படுத்தும் வகையில், உங்கள் மொபைலில் இந்த இருப்பிடச் சேவைகள் இயக்கப்பட்டு, கிடைக்கும்படி இருக்க வேண்டும்."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"பின்புலத்தில் இயங்கும்போது துல்லியமான இடத்தைக் கண்டறிதல்"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"இந்த ஆப்ஸ் பின்புலத்தில் இயங்கும்போது நீங்கள் இருக்கும் இடத்தைத் துல்லியமாகக் கண்டறியும். உங்கள் மொபைலில், இருப்பிடச் சேவைகளை ஆப்ஸ் பயன்படுத்துவதற்கு வசதியாக, அவை ஆன் செய்யப்பட்டிருக்க வேண்டும். இதனால் பேட்டரி அதிகம் பயன்படுத்தப்படலாம்."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"பின்புலத்தில் இருப்பிடத்தை அணுகுதல்"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"தோராயமான அல்லது துல்லியமான \'இருப்பிட அணுகலுடன்\' சேர்ந்து இதற்கும் அனுமதி வழங்கப்பட்டால், ஆப்ஸ் பின்புலத்தில் இயங்கினாலும் இருப்பிடத்தை அணுக இயலும்."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"எனது ஆடியோ அமைப்புகளை மாற்றுதல்"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ஒலியளவு மற்றும் வெளியீட்டிற்கு ஸ்பீக்கர்கள் பயன்படுத்தப்படுவது போன்ற ஒட்டுமொத்த ஆடியோ அமைப்புகளைக் கட்டுப்படுத்தப் பயன்பாட்டை அனுமதிக்கிறது."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ஆடியோவைப் பதிவுசெய்தல்"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"இந்தப் பயன்பாடு எப்போது வேண்டுமானாலும் மைக்ரோஃபோனைப் பயன்படுத்தி ஆடியோவை ரெக்கார்டு செய்யலாம்."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"கட்டளைகளை சிம்மிற்கு அனுப்புதல்"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"சிம் க்குக் கட்டளைகளை அனுப்ப பயன்பாட்டை அனுமதிக்கிறது. இது மிகவும் ஆபத்தானதாகும்."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"உடல் செயல்பாட்டைக் கண்டறிதல்"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"உங்கள் உடல் செயல்பாட்டை இந்த ஆப்ஸால் கண்டறிய முடியும்."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"படங்கள் மற்றும் வீடியோக்களை எடுத்தல்"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"இந்தப் பயன்பாடு எப்போது வேண்டுமானாலும் கேமராவைப் பயன்படுத்தி படங்களை எடுக்கலாம், வீடியோக்களை ரெக்கார்டு செய்யலாம்."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"அதிர்வைக் கட்டுப்படுத்துதல்"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"உங்களின் படத் தொகுப்பை மாற்ற ஆப்ஸை அனுமதிக்கும்."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"மீடியா தொகுப்பிலிருந்து இடங்களை அறிதல்"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"உங்களின் மீடியா தொகுப்பிலிருந்து இடங்களை அறிந்துகொள்ள ஆப்ஸை அனுமதிக்கும்."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g>ஐப் பயன்படுத்த அங்கீகாரத்தை உறுதிசெய்க."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"பயோமெட்ரிக் வன்பொருள் இல்லை"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"கைரேகையை ஓரளவுதான் கண்டறிய முடிந்தது. மீண்டும் முயலவும்."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"கைரேகையைச் செயலாக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
@@ -1898,14 +1904,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> தன்னிரப்பிப் பரிந்துரைகள்</item>
       <item quantity="one">ஒரு தன்னிரப்பிப் பரிந்துரை</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; இல் சேமிக்கவா?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g>ஐ &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; இல் சேமிக்கவா?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> மற்றும் <xliff:g id="TYPE_1">%2$s</xliff:g>ஐ &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; இல் சேமிக்கவா?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g> ஆகியவற்றை &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; இல் சேமிக்கவா?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; என்பதற்குப் புதுப்பிக்கவா?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g>ஐ &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; என்பதற்குப் புதுப்பிக்கவா?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> மற்றும் <xliff:g id="TYPE_1">%2$s</xliff:g>ஐ &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; என்பதற்குப் புதுப்பிக்கவா?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> மற்றும் <xliff:g id="TYPE_2">%3$s</xliff:g>ஐ &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; என்பதற்குப் புதுப்பிக்கவா?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" இல் சேமிக்கவா?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g>ஐ "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" இல் சேமிக்கவா?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> மற்றும் <xliff:g id="TYPE_1">%2$s</xliff:g>ஐ "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" இல் சேமிக்கவா?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> மற்றும் <xliff:g id="TYPE_2">%3$s</xliff:g>ஐ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" இல் சேமிக்கவா?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" இல் மாற்றவா?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" இல் <xliff:g id="TYPE">%1$s</xliff:g>ஐ மாற்றவா?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" இல் <xliff:g id="TYPE_0">%1$s</xliff:g> மற்றும் <xliff:g id="TYPE_1">%2$s</xliff:g>ஐ மாற்றவா?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" இல் <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> மற்றும் <xliff:g id="TYPE_2">%3$s</xliff:g>ஐ மாற்றவா?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"சேமி"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"வேண்டாம்"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"புதுப்பி"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index a5430b6..cce816d 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"మైక్రోఫోన్"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ఆడియోను రికార్డ్ చేయడానికి"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"ఆడియోని రికార్డ్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"కార్యకలాప గుర్తింపు"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"కార్యాకలాపాన్ని గుర్తించండి"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"భౌతిక కార్యాకలాపాన్ని గుర్తించడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"కెమెరా"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"చిత్రాలను తీయడానికి మరియు వీడియోను రికార్డ్ చేయడానికి"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"చిత్రాలు తీయడానికి మరియు వీడియో రికార్డ్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ఈ యాప్‌ సెల్ టవర్‌లు మరియు Wi-Fi నెట్‌వర్క్‌ల వంటి నెట్‌వర్క్ మూలాధారాల ఆధారంగా మీ స్థానాన్ని తెలుసుకోగలదు. యాప్‌ ఉపయోగించడానికి మీ టాబ్లెట్‌లో ఈ స్థాన సేవలను తప్పనిసరిగా ఆన్ చేయాలి మరియు అందుబాటులో ఉండాలి."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ఈ యాప్‌ సెల్ టవర్‌లు మరియు Wi-Fi నెట్‌వర్క్‌ల వంటి నెట్‌వర్క్ మూలాధారాల ఆధారంగా మీ స్థానాన్ని తెలుసుకోగలదు. యాప్‌ ఉపయోగించడానికి మీ టీవీలో ఈ స్థాన సేవలను తప్పనిసరిగా ఆన్ చేయాలి మరియు అందుబాటులో ఉండాలి."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ఈ యాప్‌ సెల్ టవర్‌లు మరియు Wi-Fi నెట్‌వర్క్‌ల వంటి నెట్‌వర్క్ మూలాధారాల ఆధారంగా మీ స్థానాన్ని తెలుసుకోగలదు. యాప్‌ ఉపయోగించడానికి మీ ఫోన్‌లో ఈ స్థాన సేవలను తప్పనిసరిగా ఆన్ చేయాలి మరియు అందుబాటులో ఉండాలి."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"నేపథ్యంలో ఖచ్చితమైన స్థానాన్ని యాక్సెస్ చేయి"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ఈ యాప్ నేపథ్యంలో ఉన్నప్పుడు ఎప్పుడైనా అది మీ ఖచ్చితమైన స్థానాన్ని తెలుసుకోగలదు. యాప్‌ ఉపయోగించడానికి మీ ఫోన్‌లో ఈ స్థాన సేవలను తప్పనిసరిగా ఆన్ చేయాలి మరియు అందుబాటులో ఉండాలి. ఇది బ్యాటరీ వినియోగాన్ని పెంచవచ్చు."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"నేపథ్యంలో స్థానాన్ని యాక్సెస్ చేయి"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"సుమారుగా లేదా ఖచ్చితమైన స్థాన యాక్సెస్‌తో పాటు అదనందా ఇది మంజూరు చేయబడితే, యాప్ నేపథ్యంలో నడుస్తున్నప్పుడు స్థానాన్ని యాక్సెస్ చేయగలదు."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"మీ ఆడియో సెట్టింగ్‌లను మార్చడం"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"వాల్యూమ్ మరియు అవుట్‌పుట్ కోసం ఉపయోగించాల్సిన స్పీకర్ వంటి సార్వజనీన ఆడియో సెట్టింగ్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ఆడియోను రికార్డ్ చేయడం"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"ఈ యాప్ మైక్రోఫోన్‌ని ఉపయోగించి ఎప్పుడైనా ఆడియోను రికార్డ్ చేయగలదు."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIMకి ఆదేశాలను పంపడం"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"సిమ్‌కు ఆదేశాలను పంపడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఇది చాలా ప్రమాదకరం."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"భౌతిక కార్యాకలాపాన్ని గుర్తించండి"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"ఈ యాప్ మీ భౌతిక కార్యాకలాపాన్ని గుర్తించగలదు."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"చిత్రాలు మరియు వీడియోలు తీయడం"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"ఈ యాప్‌ కెమెరాను ఉపయోగించి ఎప్పుడైనా చిత్రాలను తీయగలదు మరియు వీడియోలను రికార్డ్ చేయగలదు."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"వైబ్రేషన్‌ను నియంత్రించడం"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"మీ ఫోటో సేకరణను సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవండి"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"అప్లికేషన్ <xliff:g id="APP">%s</xliff:g>కు ప్రమాణీకరణ అవసరం."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"బయోమెట్రిక్ హార్డ్‌వేర్‌ అందుబాటులో లేదు"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"పాక్షిక వేలిముద్ర గుర్తించబడింది. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"వేలిముద్రను ప్రాసెస్ చేయడం సాధ్యపడలేదు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
@@ -1898,14 +1904,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> స్వీయ పూరింపు సూచనలు</item>
       <item quantity="one">ఒక స్వీయ పూరింపు సూచన</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;కు సేవ్ చేయాలా?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g>ని &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;కు సేవ్ చేయాలా?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> మరియు <xliff:g id="TYPE_1">%2$s</xliff:g>లను &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;కు సేవ్ చేయాలా?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> మరియు <xliff:g id="TYPE_2">%3$s</xliff:g>లను &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;కు సేవ్ చేయాలా?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;కు అప్‌డేట్ చేయాలా?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g>ని &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;కు అప్‌డేట్ చేయాలా?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> మరియు <xliff:g id="TYPE_1">%2$s</xliff:g>లను &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;కు అప్‌డేట్ చేయాలా?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> మరియు <xliff:g id="TYPE_2">%3$s</xliff:g>లను &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;కు అప్‌డేట్ చేయాలా?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"లో సేవ్ చేయాలా?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g>ని "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"లో సేవ్ చేయాలా?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> మరియు <xliff:g id="TYPE_1">%2$s</xliff:g>ని "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"లో సేవ్ చేయాలా?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>,మరియు<xliff:g id="TYPE_2">%3$s</xliff:g>ని "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"లో సేవ్ చేయాలా?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"లో అప్‌డేట్ చేయాలా?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g>ని "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"లో అప్‌డేట్ చేయాలా?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> మరియు <xliff:g id="TYPE_1">%2$s</xliff:g>ని "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"లో అప్‌డేట్ చేయాలా?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"ఈ అంశాలను "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"లో అప్‌డేట్ చేయాలా: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> మరియు <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"సేవ్ చేయి"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"వద్దు, ధన్యవాదాలు"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"అప్‌డేట్ చేయి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 0b7e031..ebbf155 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"ไมโครโฟน"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"บันทึกเสียง"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; บันทึกเสียงไหม"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"การจดจำกิจกรรม"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"จดจำกิจกรรม"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; จดจำกิจกรรมการเคลื่อนไหวร่างกายของคุณไหม"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"กล้องถ่ายรูป"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"ถ่ายภาพและบันทึกวิดีโอ"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ถ่ายรูปและบันทึกวิดีโอไหม"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"แอปนี้สามารถรับตำแหน่งของคุณโดยอิงจากแหล่งข้อมูลเครือข่าย เช่น เสาสัญญาณมือถือและเครือข่าย Wi-Fi แอปจะใช้บริการตำแหน่งเหล่านี้ได้ต่อเมื่อคุณเปิดบริการและบริการพร้อมใช้งานในแท็บเล็ตของคุณ"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"แอปนี้สามารถรับตำแหน่งของคุณโดยอิงจากแหล่งข้อมูลเครือข่าย เช่น เสาสัญญาณมือถือและเครือข่าย Wi-Fi แอปจะใช้บริการตำแหน่งเหล่านี้ได้ต่อเมื่อคุณเปิดบริการและบริการพร้อมใช้งานในทีวีของคุณ"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"แอปนี้สามารถรับตำแหน่งของคุณโดยอิงจากแหล่งข้อมูลเครือข่าย เช่น เสาสัญญาณมือถือและเครือข่าย Wi-Fi แอปจะใช้บริการตำแหน่งเหล่านี้ได้ต่อเมื่อคุณเปิดบริการและบริการพร้อมใช้งานในโทรศัพท์ของคุณ"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"เข้าถึงตำแหน่งที่แม่นยำเมื่ออยู่เบื้องหลัง"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"แอปนี้รับตำแหน่งที่แม่นยำของคุณได้ทุกเมื่อที่ทำงานอยู่เบื้องหลัง แอปจะใช้บริการตำแหน่งเหล่านี้ได้ต่อเมื่อคุณเปิดบริการและบริการพร้อมใช้งานในโทรศัพท์ของคุณ ซึ่งอาจทำให้มีการใช้แบตเตอรี่มากขึ้น"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"เข้าถึงตำแหน่งในเบื้องหลัง"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"หากได้รับสิทธิ์นี้เพิ่มจากการเข้าถึงตำแหน่งโดยประมาณหรือตำแหน่งที่แม่นยำ แอปจะมีสิทธิ์เข้าถึงตำแหน่งระหว่างที่ทำงานในเบื้องหลังได้"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"เปลี่ยนการตั้งค่าเสียงของคุณ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"อนุญาตให้แอปพลิเคชันปรับเปลี่ยนการตั้งค่าเสียงทั้งหมดได้ เช่น ระดับเสียงและลำโพงที่จะใช้งาน"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"บันทึกเสียง"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"แอปนี้สามารถบันทึกเสียงด้วยไมโครโฟนได้ทุกเมื่อ"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"ส่งคำสั่งไปยังซิม"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"อนุญาตให้แอปส่งคำสั่งไปยัง SIM ซึ่งอันตรายมาก"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"จดจำกิจกรรมการเคลื่อนไหวร่างกาย"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"แอปนี้จดจำกิจกรรมการเคลื่อนไหวร่างกายของคุณได้"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"ถ่ายภาพและวิดีโอ"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"แอปนี้สามารถถ่ายภาพและวิดีโอด้วยกล้องได้ทุกเมื่อ"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ควบคุมการสั่นเตือน"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"อนุญาตให้แอปแก้ไขคอลเล็กชันรูปภาพของคุณ"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"อ่านตำแหน่งจากคอลเล็กชันสื่อของคุณ"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"อนุญาตให้แอปอ่านตำแหน่งจากคอลเล็กชันสื่อของคุณ"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"แอปพลิเคชัน <xliff:g id="APP">%s</xliff:g> ต้องการตรวจสอบสิทธิ์"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ฮาร์ดแวร์ไบโอเมตริกไม่พร้อมใช้งาน"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ตรวจพบลายนิ้วมือเพียงบางส่วน โปรดลองอีกครั้ง"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ไม่สามารถประมวลผลลายนิ้วมือได้ โปรดลองอีกครั้ง"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other">คำแนะนำสำหรับการป้อนอัตโนมัติ <xliff:g id="COUNT">%1$s</xliff:g> รายการ</item>
       <item quantity="one">คำแนะนำสำหรับการป้อนอัตโนมัติ 1 รายการ</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"บันทึกไปยัง &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ใช่ไหม"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"บันทึก <xliff:g id="TYPE">%1$s</xliff:g> ไปยัง &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ใช่ไหม"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"บันทึก <xliff:g id="TYPE_0">%1$s</xliff:g> และ <xliff:g id="TYPE_1">%2$s</xliff:g> ไปยัง &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ใช่ไหม"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"บันทึก <xliff:g id="TYPE_0">%1$s</xliff:g> <xliff:g id="TYPE_1">%2$s</xliff:g> และ <xliff:g id="TYPE_2">%3$s</xliff:g> ไปยัง &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ใช่ไหม"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"อัปเดตเป็น &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; ไหม"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"อัปเดต<xliff:g id="TYPE">%1$s</xliff:g>เป็น &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; ไหม"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"อัปเดต<xliff:g id="TYPE_0">%1$s</xliff:g>และ<xliff:g id="TYPE_1">%2$s</xliff:g>เป็น &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; ไหม"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"อัปเดต<xliff:g id="TYPE_0">%1$s</xliff:g> <xliff:g id="TYPE_1">%2$s</xliff:g> และ<xliff:g id="TYPE_2">%3$s</xliff:g>เป็น &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; ไหม"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"บันทึกลงใน "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ไหม"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"บันทึก<xliff:g id="TYPE">%1$s</xliff:g>ลงใน "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ไหม"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"บันทึก<xliff:g id="TYPE_0">%1$s</xliff:g>และ<xliff:g id="TYPE_1">%2$s</xliff:g>ลงใน "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ไหม"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"บันทึก<xliff:g id="TYPE_0">%1$s</xliff:g> <xliff:g id="TYPE_1">%2$s</xliff:g> และ<xliff:g id="TYPE_2">%3$s</xliff:g>ลงใน "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ไหม"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"อัปเดตใน "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ไหม"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"อัปเดต<xliff:g id="TYPE">%1$s</xliff:g>ใน "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ไหม"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"อัปเดต<xliff:g id="TYPE_0">%1$s</xliff:g>และ<xliff:g id="TYPE_1">%2$s</xliff:g>ใน "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ไหม"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"อัปเดตข้อมูล<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> และ<xliff:g id="TYPE_2">%3$s</xliff:g> ใน "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ไหม"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"บันทึก"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"ไม่เป็นไร"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"อัปเดต"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index e21f59c..e5bde1d 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikropono"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"mag-record ng audio"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na mag-record ng audio?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Pagtukoy ng aktibidad"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"tukuyin ang aktibidad"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na tukuyin ang iyong pisikal na aktibidad?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"kumuha ng mga larawan at mag-record ng video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na kumuha ng mga larawan at mag-record ng video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Makukuha ng app na ito ang iyong lokasyon batay sa mga pinagmulan ng network gaya ng mga cell tower at Wi-Fi network. Ang mga serbisyo ng lokasyon na ito ay dapat naka-on at available sa iyong tablet para sa app upang magamit ang mga ito."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Makukuha ng app na ito ang iyong lokasyon batay sa mga pinagmulan ng network gaya ng mga cell tower at Wi-Fi network. Ang mga serbisyo ng lokasyon na ito ay dapat naka-on at available sa iyong TV para sa app upang magamit ang mga ito."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Makukuha ng app na ito ang iyong lokasyon batay sa mga pinagmulan ng network gaya ng mga cell tower at Wi-Fi network. Ang mga serbisyo ng lokasyon na ito ay dapat naka-on at available sa iyong telepono para sa app upang magamit ang mga ito."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"i-access ang tumpak na lokasyon sa background"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Makukuha ng app na ito ang iyong eksaktong lokasyon anumang oras na nasa background ito. Ang mga serbisyo ng lokasyon na ito ay dapat naka-on at available sa iyong telepono para magamit ng app ang mga ito. Maaaring lumakas ang pagkonsumo ng baterya dahil dito."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"i-access ang lokasyon sa background"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Kung papahintulutan ito bukod pa sa pag-access sa tinataya o tumpak na lokasyon, maaaring i-access ng app ang lokasyon habang tumatakbo sa background."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"baguhin ang mga setting ng iyong audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Pinapayagan ang app na baguhin ang mga pandaigdigang setting ng audio gaya ng volume at kung aling speaker ang ginagamit para sa output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"mag-record ng audio"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Makakapag-record ng audio ang app na ito gamit ang mikropono anumang oras."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"magpadala ng mga command sa SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Pinapahintulutang magpadala ang app ng mga command sa SIM. Napakapanganib nito."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"tukuyin ang pisikal na aktibidad"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Matutukoy ng app na ito ang iyong pisikal na aktibidad."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"kumuha ng mga larawan at video"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Makakakuha ng mga larawan at makakapag-record ng mga video ang app na ito gamit ang camera anumang oras."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrolin ang pag-vibrate"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Pinapayagan ang app na baguhin ang iyong koleksyon ng larawan."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"basahin ang mga lokasyon mula sa iyong koleksyon ng media"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Pinapayagan ang app na basahin ang mga lokasyon mula sa iyong koleksyon ng media."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Gustong mag-authenticate ng app na <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Walang biometric hardware"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hindi buo ang natukoy na fingerprint. Pakisubukang muli."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Hindi maproseso ang fingerprint. Pakisubukang muli."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> suhestyon sa autofill</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> na suhestyon sa autofill</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"I-save sa &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"I-save ang <xliff:g id="TYPE">%1$s</xliff:g> sa &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"I-save ang <xliff:g id="TYPE_0">%1$s</xliff:g> at <xliff:g id="TYPE_1">%2$s</xliff:g> sa &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"I-save ang <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, at <xliff:g id="TYPE_2">%3$s</xliff:g> sa &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"I-update sa &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"I-update ang <xliff:g id="TYPE">%1$s</xliff:g> sa &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"I-update ang <xliff:g id="TYPE_0">%1$s</xliff:g> at <xliff:g id="TYPE_1">%2$s</xliff:g> sa &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"I-update ang <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, at <xliff:g id="TYPE_2">%3$s</xliff:g> sa &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"I-save sa "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"I-save ang <xliff:g id="TYPE">%1$s</xliff:g> sa "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"I-save ang <xliff:g id="TYPE_0">%1$s</xliff:g> at <xliff:g id="TYPE_1">%2$s</xliff:g> sa "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"I-save ang <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, at <xliff:g id="TYPE_2">%3$s</xliff:g> sa "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"I-update sa "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"I-update ang <xliff:g id="TYPE">%1$s</xliff:g> sa "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"I-update ang <xliff:g id="TYPE_0">%1$s</xliff:g> at <xliff:g id="TYPE_1">%2$s</xliff:g> sa "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"I-update ang mga item na ito sa "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, at <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"I-save"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Hindi, salamat na lang"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"I-update"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 912db20..18e1911 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ses kaydetme"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının ses kaydetmesine izin verilsin mi?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Aktivite algılama"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"aktiviteyi algıla"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasına fiziksel aktivitenizi algılama izni verilsin mi?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotoğraf çekme ve video kaydetme"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının resim çekmesine ve video kaydı yapmasına izin verilsin mi?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Bu uygulama baz istasyonu ve kablosuz ağ gibi ağ kaynaklarını kullanarak konumunuzu belirleyebilir. Uygulamanın bu hizmetleri kullanabilmesi için tabletinizde bu konum hizmetleri açık ve kullanılabilir olmalıdır."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Bu uygulama baz istasyonu ve kablosuz ağ gibi ağ kaynaklarını kullanarak konumunuzu belirleyebilir. Uygulamanın bu hizmetleri kullanabilmesi için TV\'nizde bu konum hizmetleri açık ve kullanılabilir olmalıdır."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Bu uygulama baz istasyonu ve kablosuz ağ gibi ağ kaynaklarını kullanarak konumunuzu belirleyebilir. Uygulamanın bu hizmetleri kullanabilmesi için telefonunuzda bu konum hizmetleri açık ve kullanılabilir olmalıdır."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"arka planda kesin konuma erişme"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Bu uygulama arka plandayken istediği zaman kesin konumunuzu alabilir. Uygulamanın bu hizmetleri kullanabilmesi için telefonunuzda bu konum hizmetleri açık ve kullanılabilir olmalıdır. Bu, pil tüketimini artırabilir."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"konum bilgisine arka planda eriş"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Bu izin, yaklaşık veya tam konum erişimine ek olarak verilirse uygulama, konum bilgisine arka planda çalışırken erişebilir."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ses ayarlarınızı değiştirin"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Uygulamaya ses düzeyi ve ses çıkışı için kullanılan hoparlör gibi genel ses ayarlarını değiştirme izni verir."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ses kaydet"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Bu uygulama, istediği zaman mikrofonu kullanarak ses kaydedebilir."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM karta komut gönderme"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Uygulamanın SIM karta komut göndermesine izin verir. Bu izin çok tehlikelidir."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"fiziksel aktiviteyi algıla"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Bu uygulama fiziksel aktivitenizi algılayabilir."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"resim çekme ve görüntü kaydetme"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Bu uygulama, herhangi bir zamanda kamerayı kullanarak fotoğraf ve video çekebilir."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"titreşimi denetleme"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Uygulamanın fotoğraf koleksiyonunuzu değiştirmesine izin verir."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"medya koleksiyonunuzdaki konumları okuma"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Uygulamanın medya koleksiyonunuzdaki konumları okumasına izin verir."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> uygulaması kimlik doğrulamak istiyor."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biyometrik donanım kullanılamıyor"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Parmak izinin tümü algılanamadı. Lütfen tekrar deneyin."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Parmak izi işlenemedi. Lütfen tekrar deneyin."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> otomatik doldurma önerisi</item>
       <item quantity="one">Bir otomatik doldurma önerisi</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; hizmetine kaydedilsin mi?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g>, &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; hizmetine kaydedilsin mi?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> ve <xliff:g id="TYPE_1">%2$s</xliff:g>, &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; hizmetine kaydedilsin mi?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ve <xliff:g id="TYPE_2">%3$s</xliff:g>, &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; hizmetine kaydedilsin mi?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; hizmetine güncellensin mi?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g>, &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; hizmetine güncellensin mi?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g>  ve <xliff:g id="TYPE_1">%2$s</xliff:g>, &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; hizmetine güncellensin mi?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ve <xliff:g id="TYPE_2">%3$s</xliff:g>, &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;hizmetine güncellensin mi?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" hizmetine kaydedilsin mi?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" hizmetine kaydedilsin mi?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> ve <xliff:g id="TYPE_1">%2$s</xliff:g> "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" hizmetine kaydedilsin mi?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ve <xliff:g id="TYPE_2">%3$s</xliff:g> "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" hizmetine kaydedilsin mi?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" hizmetinde güncellensin mi?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g> "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" hizmetinde güncellensin mi?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> ve <xliff:g id="TYPE_1">%2$s</xliff:g> "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" hizmetinde güncellensin mi?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Şu öğeler "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" hizmetinde güncellensin mi: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ve <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Kaydet"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Hayır, teşekkürler"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Güncelle"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index f84e3e2..e9f44bf 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -298,6 +298,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Мікрофон"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"записувати аудіо"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; записувати аудіо?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Розпізнавання активності"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"розпізнавати активність"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; розпізнавати вашу фізичну активність?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"фотографувати та записувати відео"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; робити знімки та записувати відео?"</string>
@@ -424,14 +427,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Цей додаток може отримувати дані про ваше місцезнаходження на основі джерел мережі, як-от антен мобільного зв’язку та мереж Wi-Fi. Щоб додаток міг користуватися цими службами локації, вони мають бути доступними й увімкненими на вашому планшеті."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Цей додаток може отримувати дані про ваше місцезнаходження на основі джерел мережі, як-от антен мобільного зв’язку та мереж Wi-Fi. Щоб додаток міг користуватися цими службами локації, вони мають бути доступними й увімкненими на вашому телевізорі."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Цей додаток може отримувати дані про ваше місцезнаходження на основі джерел мережі, як-от антен мобільного зв’язку та мереж Wi-Fi. Щоб додаток міг користуватися цими службами локації, вони мають бути доступними й увімкненими на вашому телефоні."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"отримувати доступ до даних про точне місцезнаходження у фоновому режимі"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Цей додаток може будь-коли отримувати дані про ваше точне місцезнаходження у фоновому режимі. Щоб додаток користувався службами локації, вони мають бути наявні й увімкнені на вашому телефоні. Через це може швидше розряджатись акумулятор."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"доступ до геоданих у фоновому режимі"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Якщо ви надасте цей дозвіл і доступ до приблизного або точного місцезнаходження, додаток зможе отримувати геодані у фоновому режимі."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"змінювати налаштув-ня звуку"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Дозволяє програмі змінювати загальні налаштування звуку, як-от гучність і динамік, який використовується для виводу сигналу."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"запис-ти аудіо"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Цей додаток може будь-коли записувати звук за допомогою мікрофона."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"надсилати команди на SIM-карту"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Дозволяє програмі надсилати команди на SIM-карту. Це дуже небезпечно."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"розпізнавати фізичну активність"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Цей додаток може розпізнавати фізичну активність."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"фотограф. та знімати відео"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Цей додаток може будь-коли робити фотографії та записувати відео за допомогою камери."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"контролювати вібросигнал"</string>
@@ -524,6 +529,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Додаток зможе змінювати вашу колекцію фотографій."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"розпізнавати геодані з колекції медіа-вмісту"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Додаток зможе розпізнавати геодані з вашої колекції медіа-вмісту."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Для додатка <xliff:g id="APP">%s</xliff:g> потрібна автентифікація."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Біометричне апаратне забезпечення недоступне"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Відбиток розпізнано частково. Повторіть спробу."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не вдалось обробити відбиток. Повторіть спробу."</string>
@@ -1967,14 +1973,14 @@
       <item quantity="many"><xliff:g id="COUNT">%1$s</xliff:g> пропозицій автозаповнення</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> пропозиції автозаповнення</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Зберегти дані в службі &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Зберегти дані (<xliff:g id="TYPE">%1$s</xliff:g>) у службі &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Зберегти дані (<xliff:g id="TYPE_0">%1$s</xliff:g> і <xliff:g id="TYPE_1">%2$s</xliff:g>) у службі &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Зберегти дані (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> і <xliff:g id="TYPE_2">%3$s</xliff:g>) у службі &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Оновити дані в сервісі &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Оновити дані (<xliff:g id="TYPE">%1$s</xliff:g>) у сервісі &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Оновити дані (<xliff:g id="TYPE_0">%1$s</xliff:g> і <xliff:g id="TYPE_1">%2$s</xliff:g>) у сервісі &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Оновити дані (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> і <xliff:g id="TYPE_2">%3$s</xliff:g>) у сервісі &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Зберегти в сервісі "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Зберегти дані (<xliff:g id="TYPE">%1$s</xliff:g>) у сервісі "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Зберегти дані (<xliff:g id="TYPE_0">%1$s</xliff:g> і <xliff:g id="TYPE_1">%2$s</xliff:g>) у сервісі "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Зберегти дані (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> і <xliff:g id="TYPE_2">%3$s</xliff:g>) у сервісі "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Оновити в сервісі "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Оновити дані (<xliff:g id="TYPE">%1$s</xliff:g>) у сервісі "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Оновити дані (<xliff:g id="TYPE_0">%1$s</xliff:g> і <xliff:g id="TYPE_1">%2$s</xliff:g>) у сервісі "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Оновити в сервісі "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" такі дані: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> і <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Зберегти"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Ні, дякую"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Оновити"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index dc801cd..96bd9a3 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"مائکروفون"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"آڈیو ریکارڈ کریں"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"سرگرمی کی شناخت"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"سرگرمی کی شناخت کریں"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آپ کی جسمانی سرگرمی کی شناخت کرنے کی اجازت دیں؟"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"کیمرا"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"تصاویر لیں اور ویڈیو ریکارڈ کریں"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو تصاویر لینے اور ویڈیو ریکارڈ کرنے کی اجازت دیں؟"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"‏نیٹ ورک مآخذات جیسے کہ سیل ٹاورز اور Wi-Fi نیٹ ورکس کی بنیاد پر یہ ایپ آپ کا مقام حاصل کر سکتی ہے۔ ایپ کو ان مقام کی سروسز کو استعمال کرنے کیلئے ان کا آن ہونا اور آپ کے ٹیبلیٹ پر دستیاب ہونا ضروری ہے۔"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"‏نیٹ ورک مآخذات جیسے کہ سیل ٹاورز اور Wi-Fi نیٹ ورکس کی بنیاد پر یہ ایپ آپ کا مقام حاصل کر سکتی ہے۔ ایپ کو ان مقام کی سروسز کو استعمال کرنے کیلئے ان کا آن ہونا اور آپ کے TV پر دستیاب ہونا ضروری ہے۔"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"‏نیٹ ورک مآخذات جیسے کہ سیل ٹاورز اور Wi-Fi نیٹ ورکس کی بنیاد پر یہ ایپ آپ کا مقام حاصل کر سکتی ہے۔ ایپ کو ان مقام کی سروسز کو استعمال کرنے کیلئے ان کا آن ہونا اور آپ کے فون پر دستیاب ہونا ضروری ہے۔"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"پس منظر میں درست مقام تک رسائی حاصل کریں"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"یہ ایپ جب بھی پس منظر میں ہو آپ کا صحیح مقام حاصل کر سکتی ہے۔ ایپ کو ان مقام کی سروسز کو استعمال کر سکنے کیلئے ان کا آن ہونا اور آپ کے فون پر دستیاب ہونا ضروری ہے۔"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"پس منظر میں مقام کی رسائی حاصل کریں"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"اگر اضافی طور پر اسے تخمینی یا درست مقام تک رسائی کی منظوری دی جاتی ہے تو پس منظر میں چلنے کے دوران ایپ اس مقام تک رسائی حاصل کر سکتی ہے۔"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"اپنے آڈیو کی ترتیبات کو تبدیل کریں"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ایپ کو مجموعی آڈیو ترتیبات جیسے والیوم اور آؤٹ پٹ کیلئے جو اسپیکر استعمال ہوتا ہے اس میں ترمیم کرنے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"آڈیو ریکارڈ کریں"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"یہ ایپ کسی بھی وقت مائیکروفون استعمال کرتے ہوئے آڈیو ریکارڈ کر سکتی ہے۔"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"‏SIM کو ہدایات بھیجیں"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"‏ایپ کو SIM کو کمانڈز بھیجنے کی اجازت دیتا ہے۔ یہ بہت خطرناک ہے۔"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"جسمانی سرگرمی کی شناخت کریں"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"یہ ایپ آپ کی جسمانی سرگرمی کی شناخت کر سکتی ہے۔"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"تصاویر لیں اور ویڈیوز بنائیں"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"یہ ایپ کسی بھی وقت کیمرا استعمال کرتے ہوئے تصاویر لے سکتی ہے اور ویڈیوز ریکارڈ کر سکتی ہے۔"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ارتعاش کو کنٹرول کریں"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"ایپ کو آپ کی تصویر کے مجموعے میں ترمیم کی اجازت دیتا ہے۔"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"اپنی میڈيا کے مجموعے سے مقامات پڑھیں"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"ایپ کو آپ کی میڈيا کے مجموعے سے مقامات پڑھنے کی اجازت دیتا ہے۔"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"ایپلیکیشن <xliff:g id="APP">%s</xliff:g> تصدیق کرنا چاہتی ہے۔"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"بایومیٹرک ہارڈ ویئر دستیاب نہیں ہے"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"جزوی فنگر پرنٹ کی شناخت ہوئی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"فنگر پرنٹ پر کارروائی نہیں کی جا سکی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
@@ -1898,14 +1904,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> آٹو فل تجاویز</item>
       <item quantity="one">ایک آٹو فل تجویز</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"‏&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; میں محفوظ کریں؟"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"‏<xliff:g id="TYPE">%1$s</xliff:g> کو &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; میں محفوظ کریں؟"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"‏<xliff:g id="TYPE_0">%1$s</xliff:g> اور <xliff:g id="TYPE_1">%2$s</xliff:g> کو &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; میں محفوظ کریں؟"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"‏<xliff:g id="TYPE_0">%1$s</xliff:g>،<xliff:g id="TYPE_1">%2$s</xliff:g>، اور <xliff:g id="TYPE_2">%3$s</xliff:g> کو &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; میں محفوظ کریں؟"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"‏&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; میں اپ ڈیٹ کریں؟"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"‏<xliff:g id="TYPE">%1$s</xliff:g> کو &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; میں اپ ڈیٹ کریں؟"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"‏<xliff:g id="TYPE_0">%1$s</xliff:g> اور <xliff:g id="TYPE_1">%2$s</xliff:g> کو &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; میں اپ ڈیٹ کریں؟"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"‏<xliff:g id="TYPE_0">%1$s</xliff:g>،<xliff:g id="TYPE_1">%2$s</xliff:g>، اور <xliff:g id="TYPE_2">%3$s</xliff:g> کو &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; میں اپ ڈیٹ کریں؟"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" میں محفوظ کریں؟"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> کو "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" میں محفوظ کریں؟"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> اور <xliff:g id="TYPE_1">%2$s</xliff:g> کو "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" میں محفوظ کریں؟"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>، <xliff:g id="TYPE_1">%2$s</xliff:g> اور <xliff:g id="TYPE_2">%3$s</xliff:g> کو "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" میں محفوظ کریں؟"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" میں اپ ڈیٹ کریں؟"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" میں <xliff:g id="TYPE">%1$s</xliff:g> اپ ڈیٹ کریں؟"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> اور <xliff:g id="TYPE_1">%2$s</xliff:g> کو "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" میں اپ ڈیٹ کریں؟"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"ان آئٹمز کو "<b>"<xliff:g id="LABEL">%4$s</xliff:g> "</b>" میں اپ ڈیٹ کریں: <xliff:g id="TYPE_0">%1$s</xliff:g>، <xliff:g id="TYPE_1">%2$s</xliff:g> اور <xliff:g id="TYPE_2">%3$s</xliff:g> ؟"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"محفوظ کریں"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"نہیں، شکریہ"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"اپ ڈیٹ کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index e51b810..ccb9e15 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -100,7 +100,7 @@
     <string name="peerTtyModeVco" msgid="1742404978686538049">"Teng huquqli ishtirokchi teletayp rejimini VCO (gapiradi, eshitolmaydi) qilib o‘zgartirdi"</string>
     <string name="peerTtyModeOff" msgid="3280819717850602205">"Teng huquqli ishtirokchi teletayp rejimini OFF (o‘chirilgan) qilib o‘zgartirdi"</string>
     <string name="serviceClassVoice" msgid="1258393812335258019">"Ovozli aloqa"</string>
-    <string name="serviceClassData" msgid="872456782077937893">"Ma’lumot"</string>
+    <string name="serviceClassData" msgid="872456782077937893">"Internet"</string>
     <string name="serviceClassFAX" msgid="5566624998840486475">"FAKS"</string>
     <string name="serviceClassSMS" msgid="2015460373701527489">"SMS"</string>
     <string name="serviceClassDataAsync" msgid="4523454783498551468">"Asinx"</string>
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ovoz yozib olish"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun audio yozib olishga ruxsat berilsinmi?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Harakatni aniqlash"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"harakatni aniqlash"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun jismoniy harakatlaringizni aniqlashga ruxsat berilsinmi?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"suratga olish va video yozib olish"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun surat va videoga olishga ruxsat berilsinmi?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Bu ilova Wi-Fi va uyali tarmoq antennalari kabi tarmoq manbalari asosida joylashuvingiz axborotini olishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular planshetda yoniq bo‘lishi va ishlashi kerak."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Bu ilova Wi-Fi va uyali tarmoq antennalari kabi tarmoq manbalari asosida joylashuvingiz axborotini olishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular televizorda yoniq bo‘lishi va ishlashi kerak."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Bu ilova Wi-Fi va uyali tarmoq antennalari kabi tarmoq manbalari asosida joylashuvingiz axborotini olishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular telefoningizda yoniq bo‘lishi va ishlashi kerak."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"fonda joylashuv axborotini olishga ruxsat"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Bu ilova fon rejimida aniq joylashuv axborotingizdan foydalanishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular telefoningizda yoniq turishi va ishlashi kerak. Bunda batareya sarfi oshishi mumkin."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"fonda joylashuv axborotidan foydalanish"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Agar taxminiy yoki aniq joylashuv axborotiga qo‘shimcha tarzda ruxsat berilgan bo‘lsa, ilova ishlayotganda joylashuv axborotidan fonda foydala oladi."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"audio sozlamalaringizni o‘zgartirish"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ilovalarga tovush va ovoz chiqarish uchun foydalaniladigan karnay kabi global audio sozlamalarini o‘zgartirish uchun ruxsat beradi."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ovoz yozib olish"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Bu ilova xohlagan vaqtda mikrofon yordami audio yozib olishi mumkin."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM kartaga buyruqlar yuborish"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Dasturga SIM kartaga buyruqlar jo‘natishga ruxsat beradi. Bu juda ham xavfli."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"jismoniy harakatni aniqlash"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Bu ilova jismoniy harakatlaringizni aniqlay oladi."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"rasm va videoga olish"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Bu ilova xohlagan vaqtda kamera orqali suratga olishi va video yozib olishi mumkin."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"tebranishni boshqarish"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Ilovaga suratlar to‘plamingizni o‘zgartirishga ruxsat beradi."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"multimedia to‘plamidan joylashuv axborotini o‘qish"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ilovaga multimedia to‘plamingizdan joylashuv axborotini o‘qishga ruxsat beradi."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g> ilovasi qurilma qulfini ochmoqchi."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrik sensor ishlamayapti"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Barmoq izi qisman aniqlandi. Qayta urinib ko‘ring."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Barmoq izi aniqlanmadi. Qayta urinib ko‘ring."</string>
@@ -1898,14 +1904,14 @@
       <item quantity="other">Avtomatik to‘ldirishga oid <xliff:g id="COUNT">%1$s</xliff:g> ta taklif</item>
       <item quantity="one">Avtomatik to‘ldirishga oid bitta taklif</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; xizmatiga saqlansinmi?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; xizmatiga saqlansinmi?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> va <xliff:g id="TYPE_1">%2$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; xizmatiga saqlansinmi?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> va <xliff:g id="TYPE_2">%3$s</xliff:g> &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; xizmatiga saqlansinmi?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; xizmatidagi ma’lumot yangilansinmi?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"&lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; xizmatida <xliff:g id="TYPE">%1$s</xliff:g> ma’lumotlari yangilansinmi?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"&lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; xizmatidagi <xliff:g id="TYPE_0">%1$s</xliff:g> va <xliff:g id="TYPE_1">%2$s</xliff:g> ma’lumotlari yangilansinmi?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"&lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; xizmatidagi <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> va <xliff:g id="TYPE_2">%3$s</xliff:g> ma’lumotlari yangilansinmi?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" xizmatiga saqlansinmi?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"<xliff:g id="TYPE">%1$s</xliff:g> "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" xizmatiga saqlansinmi?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"<xliff:g id="TYPE_0">%1$s</xliff:g> va <xliff:g id="TYPE_1">%2$s</xliff:g> "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" xizmatiga saqlansinmi?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> va <xliff:g id="TYPE_2">%3$s</xliff:g> "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" xizmatiga saqlansinmi?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" xizmatida yangilansinmi?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"<xliff:g id="TYPE">%1$s</xliff:g> "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" xizmatida yangilansinmi?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"<xliff:g id="TYPE_0">%1$s</xliff:g> va <xliff:g id="TYPE_1">%2$s</xliff:g> "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" xizmatida yangilansinmi?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" xizmatidagi <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> va <xliff:g id="TYPE_2">%3$s</xliff:g> yangilansinmi?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Saqlash"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Kerak emas"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Yangilash"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 4d8a20b..02b9ed9 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Micrô"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ghi âm"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ghi âm?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Nhận dạng hoạt động"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"nhận dạng hoạt động"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Bạn có muốn cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nhận dạng hoạt động thể chất của mình không?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Máy ảnh"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"chụp ảnh và quay video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; chụp ảnh và quay video?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ứng dụng này có thể nhận thông tin vị trí của bạn dựa trên các nguồn mạng như tháp phát sóng di động và mạng Wi-Fi. Các dịch vụ vị trí này phải được bật và khả dụng trên máy tính bảng của bạn để ứng dụng có thể sử dụng chúng."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ứng dụng này có thể nhận thông tin vị trí của bạn dựa trên các nguồn mạng như tháp phát sóng di động và mạng Wi-Fi. Các dịch vụ vị trí này phải được bật và khả dụng trên TV của bạn để ứng dụng có thể sử dụng chúng."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ứng dụng này có thể nhận thông tin vị trí của bạn dựa trên các nguồn mạng như tháp phát sóng di động và mạng Wi-Fi. Các dịch vụ vị trí này phải được bật và khả dụng trên điện thoại của bạn để ứng dụng có thể sử dụng chúng."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"truy cập vị trí chính xác trong nền"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Bất cứ khi nào chạy trong nền, ứng dụng này có thể nhận thông tin vị trí chính xác của bạn. Để ứng dụng có thể sử dụng các dịch vụ vị trí, điện thoại của bạn phải có các dịch vụ này và dịch vụ ở trạng thái bật. Hoạt động này có thể tăng mức tiêu thụ pin."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"truy cập vào vị trí trong nền"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Nếu bạn cấp cho ứng dụng quyền truy cập bổ sung vào vị trị gần đúng hoặc chính xác, thì ứng dụng có thể truy cập vào vị trí đó khi chạy trong nền."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"thay đổi cài đặt âm thanh của bạn"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Cho phép ứng dụng sửa đổi cài đặt âm thanh chung chẳng hạn như âm lượng và loa nào được sử dụng cho thiết bị ra."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ghi âm"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Ứng dụng này có thể ghi âm bằng micrô bất kỳ lúc nào."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"gửi lệnh đến SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Cho phép ứng dụng gửi lệnh đến SIM. Việc này rất nguy hiểm."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"nhận dạng hoạt động thể chất"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Ứng dụng này có thể nhận dạng hoạt động thể chất của bạn."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"chụp ảnh và quay video"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Ứng dụng này có thể chụp ảnh và quay video bằng máy ảnh bất cứ lúc nào."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kiểm soát rung"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Cho phép ứng dụng này sửa đổi bộ sưu tập ảnh của bạn."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"đọc vị trí từ bộ sưu tập phương tiện"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Cho phép ứng dụng này đọc vị trí từ bộ sưu tập phương tiện của bạn."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Ứng dụng <xliff:g id="APP">%s</xliff:g> muốn xác thực."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Không có phần cứng sinh trắc học"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Đã phát hiện được một phần vân tay. Vui lòng thử lại."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Không thể xử lý vân tay. Vui lòng thử lại."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> đề xuất tự động điền</item>
       <item quantity="one">Một đề xuất tự động điền</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Lưu vào &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Lưu <xliff:g id="TYPE">%1$s</xliff:g> vào &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Lưu <xliff:g id="TYPE_0">%1$s</xliff:g> và <xliff:g id="TYPE_1">%2$s</xliff:g> vào &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Lưu <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> và <xliff:g id="TYPE_2">%3$s</xliff:g> vào &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Bạn có muốn cập nhật cho &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; không?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Bạn có muốn cập nhật <xliff:g id="TYPE">%1$s</xliff:g> cho &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; không?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Bạn có muốn cập nhật <xliff:g id="TYPE_0">%1$s</xliff:g> và <xliff:g id="TYPE_1">%2$s</xliff:g> cho &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; không?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Bạn có muốn cập nhật <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> và <xliff:g id="TYPE_2">%3$s</xliff:g> cho &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; không?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Lưu vào "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Lưu <xliff:g id="TYPE">%1$s</xliff:g> vào "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Lưu <xliff:g id="TYPE_0">%1$s</xliff:g> và <xliff:g id="TYPE_1">%2$s</xliff:g> vào "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Lưu <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> và <xliff:g id="TYPE_2">%3$s</xliff:g> vào "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Cập nhật trong "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Cập nhật <xliff:g id="TYPE">%1$s</xliff:g> trong "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Cập nhật <xliff:g id="TYPE_0">%1$s</xliff:g> và <xliff:g id="TYPE_1">%2$s</xliff:g> trong "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Cập nhật các mục này: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> và <xliff:g id="TYPE_2">%3$s</xliff:g> trong "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Lưu"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Không, cảm ơn"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Cập nhật"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 609f9bcf..076d6f9 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"麦克风"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"录制音频"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;录音吗?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"运动识别"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"识别运动"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;识别您的健身运动吗?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"相机"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"拍摄照片和录制视频"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;拍摄照片和录制视频吗?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"此应用可根据网络来源(例如基站和 WLAN 网络)获取您的位置信息。您的平板电脑必须支持并开启这些位置信息服务,此应用才能使用这些服务。"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"此应用可根据网络来源(例如基站和 WLAN 网络)获取您的位置信息。您的电视必须支持并开启这些位置信息服务,此应用才能使用这些服务。"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"此应用可根据网络来源(例如基站和 WLAN 网络)获取您的位置信息。您的手机必须支持并开启这些位置信息服务,此应用才能使用这些服务。"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"在后台获取精确的位置信息"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"此应用在后台运行时可随时获取您的精确位置信息。您的手机必须支持并开启这些位置信息服务,此应用才能使用这些服务。这可能会增加耗电量。"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"在后台使用位置信息"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"如果另外授予大致位置信息或精确位置信息访问权限,该应用便可在后台运行时使用位置信息。"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"更改您的音频设置"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"允许该应用修改全局音频设置,例如音量和用于输出的扬声器。"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"录音"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"此应用可随时使用麦克风进行录音。"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"向 SIM 卡发送命令"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"允许应用向SIM卡发送命令(此权限具有很高的危险性)。"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"识别健身运动"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"此应用可以识别您的健身运动。"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"拍摄照片和视频"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"此应用可随时使用相机拍摄照片和录制视频。"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"控制振动"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"允许该应用修改您的照片收藏。"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"从您的媒体收藏中读取位置信息"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"允许该应用从您的媒体收藏中读取位置信息。"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"<xliff:g id="APP">%s</xliff:g>应用需要进行身份验证。"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"生物识别硬件无法使用"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"仅检测到部分指纹,请重试。"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"无法处理指纹,请重试。"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> 条自动填充建议</item>
       <item quantity="one">1 条自动填充建议</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"要保存到&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;吗?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"要将<xliff:g id="TYPE">%1$s</xliff:g>保存到&lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;吗?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"要将<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>保存到&lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;吗?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"要将<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>保存到&lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;吗?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"要更新到&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;吗?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"要将<xliff:g id="TYPE">%1$s</xliff:g>更新到&lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;吗?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"要将<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>更新到&lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;吗?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"要将<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>更新到&lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;吗?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"要保存到"<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"吗?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"要将<xliff:g id="TYPE">%1$s</xliff:g>保存到"<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"吗?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"要将<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>保存到"<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"吗?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"要将<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>保存到"<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"吗?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"要在"<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"中更新吗?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"要在"<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"中更新<xliff:g id="TYPE">%1$s</xliff:g>吗?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"要在"<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"中更新<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>吗?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"要在"<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"中更新<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>这些内容吗?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"保存"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"不用了"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"更新"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 2e3c7d7..1484006 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"麥克風"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"錄音"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;錄音嗎?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"活動識別"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"識別活動"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;識別您的運動嗎?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"相機"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"拍照和錄製影片"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;拍照和錄製影片嗎?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"此應用程式可以根據您的網絡來源 (例如手機訊號塔和 Wi-Fi 網絡) 取得您的位置。您的平板電腦必須支援並啟用這些位置資訊服務,應用程式方可使用這項功能。"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"此應用程式可以根據您的網絡來源 (例如手機訊號塔和 Wi-Fi 網絡) 取得您的位置。您的電視必須支援並啟用這些位置資訊服務,應用程式方可使用這項功能。"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"此應用程式可以根據您的網絡來源 (例如手機訊號塔和 Wi-Fi 網絡) 取得您的位置。您的手機必須支援並啟用這些位置資訊服務,應用程式方可使用這項功能。"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"在背景中存取精確位置"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"此應用程式可在背景運行時隨時獲取您的確實位置資訊。您的手機必須支援並啟用這些位置資訊服務,應用程式方可使用這項功能,但這樣做可能會增加耗電量。"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"在背景存取位置資訊"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"如果您另外授予概略位置或精確位置的存取權,這個應用程式在背景運行時將可存取位置資訊。"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"更改音效設定"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"允許應用程式修改全域音頻設定,例如音量和用於輸出的喇叭。"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"錄製音效"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"此應用程式可以隨時使用麥克風錄音。"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"發送指令至 SIM 卡"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"允許應用程式傳送指令到 SIM 卡。這項操作具有高危險性。"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"識別運動"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"此應用程式可識別您的運動。"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"拍照和拍攝影片"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"此應用程式可以隨時使用相機拍照和攝錄。"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"控制震動"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"允許應用程式修改您的相片集。"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"讀取媒體集的位置"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"允許應用程式讀取媒體集的位置。"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"「<xliff:g id="APP">%s</xliff:g>」應用程式需要驗證。"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"無法使用生物識別硬件"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"只偵測到部分指紋。請再試一次。"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"無法處理指紋。請再試一次。"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> 個自動填入建議</item>
       <item quantity="one">一個自動填入建議</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"要儲存至「&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;」嗎?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"要將<xliff:g id="TYPE">%1$s</xliff:g>儲存至「&lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;」嗎?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"要將<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>儲存至「&lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;」嗎?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"要將<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>儲存至「&lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;」嗎?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"要更新至 &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt; 嗎?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"要將<xliff:g id="TYPE">%1$s</xliff:g>更新至 &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt; 嗎?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"要將<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>更新至 &lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt; 嗎?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"要將<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>更新至 &lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt; 嗎?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"要儲存至 "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" 嗎?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"要將<xliff:g id="TYPE">%1$s</xliff:g>儲存至 "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" 嗎?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"要將<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>儲存至 "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" 嗎?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"要將<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>儲存至 "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" 嗎?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"要在 "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" 中更新嗎?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"要在 "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" 中更新<xliff:g id="TYPE">%1$s</xliff:g>嗎?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"要在 "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" 中更新<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>嗎?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"要在 "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" 中更新<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>嗎?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"儲存"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"不用了,謝謝"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"更新"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 9149f47..608a758 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"麥克風"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"錄音"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」錄音嗎?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"活動識別"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"辨識活動"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;辨識你從事的體能活動嗎?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"相機"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"拍照及錄製影片"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」拍攝相片及錄製影片嗎?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"這個應用程式可根據網路來源 (例如基地台和 Wi-Fi 網路) 取得你的位置資訊。你必須在平板電腦上開啟這類定位服務,才能讓這個應用程式取得位置資訊。"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"這個應用程式可根據網路來源 (例如基地台和 Wi-Fi 網路) 取得你的位置資訊。你必須在電視上開啟這類定位服務,才能讓這個應用程式取得位置資訊。"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"這個應用程式可根據網路來源 (例如基地台和 Wi-Fi 網路) 取得你的位置資訊。你必須在手機上開啟這類定位服務,才能讓這個應用程式取得位置資訊。"</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"在背景中取得精確位置"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"這個應用程式可在背景中隨時取得你的確切位置。你必須在手機上開啟這些定位服務,才能讓這個應用程式取得確切位置。請注意,這麼做可能會增加耗電量。"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"在背景存取位置資訊"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"除了概略位置或精確位置的存取權外,若您另外授予這項存取權,這個應用程式就能在背景執行時存取位置資訊。"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"變更音訊設定"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"允許應用程式修改全域音訊設定,例如音量和用來輸出的喇叭。"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"錄製音訊"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"這個應用程式隨時可使用麥克風錄音。"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"傳送指令到 SIM 卡"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"允許應用程式傳送指令到 SIM 卡。這麼做非常危險。"</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"辨識體能活動"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"這個應用程式可以辨識你從事的體能活動。"</string>
     <string name="permlab_camera" msgid="3616391919559751192">"拍攝相片和影片"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"這個應用程式隨時可使用相機拍照及錄影。"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"控制震動"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"允許應用程式修改你的相片收藏。"</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"讀取你的媒體收藏的位置資訊"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"允許應用程式讀取你的媒體收藏的位置資訊。"</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"「<xliff:g id="APP">%s</xliff:g>」應用程式需要驗證使用者身分。"</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"無法使用生物特徵辨識硬體"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"僅偵測到部分指紋,請再試一次。"</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"無法處理指紋,請再試一次。"</string>
@@ -1897,14 +1903,14 @@
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> 項自動填入建議</item>
       <item quantity="one">1 項自動填入建議</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"要儲存到「<xliff:g id="LABEL">%1$s</xliff:g>」嗎?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"要將<xliff:g id="TYPE">%1$s</xliff:g>儲存到「<xliff:g id="LABEL">%2$s</xliff:g>」嗎?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"要將<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>儲存到「<xliff:g id="LABEL">%3$s</xliff:g>」嗎?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"要將<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>儲存到「<xliff:g id="LABEL">%4$s</xliff:g>」嗎?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"要更新「<xliff:g id="LABEL">%1$s</xliff:g>」中的資料嗎?&lt;b&gt;&lt;/b&gt;"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"要更新「<xliff:g id="LABEL">%2$s</xliff:g>」中的<xliff:g id="TYPE">%1$s</xliff:g>嗎?&lt;b&gt;&lt;/b&gt;"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"要更新「<xliff:g id="LABEL">%3$s</xliff:g>」中的<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>嗎?&lt;b&gt;&lt;/b&gt;"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"要更新「<xliff:g id="LABEL">%4$s</xliff:g>」中的<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>嗎?&lt;b&gt;&lt;/b&gt;"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"要儲存到 "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" 嗎?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"要將<xliff:g id="TYPE">%1$s</xliff:g>儲存到 "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" 嗎?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"要將<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>儲存到 "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" 嗎?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"要將<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>儲存到 "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" 嗎?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"要更新 "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" 中的內容嗎?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"要更新 "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" 中的<xliff:g id="TYPE">%1$s</xliff:g>嗎?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"要更新 "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" 中的<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>嗎?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"要更新 "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" 中的<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>嗎?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"儲存"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"不用了,謝謝"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"更新"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 75709ca..42a1ad0 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -292,6 +292,9 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"I-Microphone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"rekhoda ividiyo"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi irekhode umsindo?"</string>
+    <string name="permgrouplab_activityRecognition" msgid="2838596644535616493">"Ukubonwa komsebenzi"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="7672248027571522602">"bona umsebenzi"</string>
+    <string name="permgrouprequest_activityRecognition" msgid="8121253142311250055">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukubona umsebenzi wakho?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Ikhamela"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"thatha izithombe uphinde urekhode ividiyo"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthatha izithombe iphinde irekhode ividiyo?"</string>
@@ -418,14 +421,16 @@
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Lolu hlelo lokusebenza lungathola indawo yakho ngokususelwe kumithombo yenethiwekhi njengamathawa eseli namanethiwekhi e-Wi-Fi. Lawa masevisi endawo kufanele avulwe futhi atholakale kuthebhulethi yakho ukuze uhlelo lokusebenza lukwazi ukuwasebenzisa."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Lolu hlelo lokusebenza lingathola indawo yakho ngokususelwe kumithombo yenethiwekhi njengamathawa eseli namanethiwekhi e-Wi-Fi. Lawa masevisi endawo kufanele avulwe futhi atholakale ku-TV yakho ukuze uhlelo lokusebenza lukwazi ukuwasebenzisa."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Lolu hlelo lokusebenza lungathola indawo yakho ngokususelwe kumithombo yenethiwekhi njengamathawa eseli kanye namanethiwekhi e-Wi-Fi. Lawa masevisi endawo kufanele avulwe futhi atholakale efonini yakho ukuze uhlelo lokusebenza likwazi ukuwasebenzisa."</string>
-    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"finyelela indawo eqondile emuva"</string>
-    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Lolu hlelo lokusebenza lungakutholela indawo eqondile noma kunini lusemuva. Lawa masevisi endawo kufanele avulwe futhi atholakale efonini yakho ukuze uhlelo lokusebenza lukwazi ukuwasebenzisa. Lokhu kungakhulisa ukusebenza kwebhethri."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="3965397804300661062">"finyelela kundawo ngemuva"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="1096394429579210251">"Uma lokhu kunikezwa ngokungeziwe ekufinyeleleni okulinganiselwe noma okunembile kwendawo uhlelo lokusebenza lungafinyelela kundawo ngenkathi lusebenza ngemuva."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"shintsha izilungiselelo zakho zomsindo"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ivumela uhlelo lokusebenza ukushintsha izilungiselelo zomsindo we-global njengevolomu nokuthi isiphi isipika esisetshenziselwa okukhiphayo."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"qopha umsindo"</string>
     <string name="permdesc_recordAudio" msgid="4245930455135321433">"Lolu hlelo lokusebenza lungafunda umsindo lisebenzisa imakrofoni noma kunini."</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"thumela imilayezo ku-SIM"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"Ivumela uhlelo lokusebenza ukuthumela imiyalo ku-SIM. Lokhu kuyingozi kakhulu."</string>
+    <string name="permlab_activityRecognition" msgid="3634590230567608356">"bona umsebenzi"</string>
+    <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Lolu hlelo lokusebenza lingabona umsebenzi wakho."</string>
     <string name="permlab_camera" msgid="3616391919559751192">"thatha izithombe namavidiyo"</string>
     <string name="permdesc_camera" msgid="5392231870049240670">"Lolu hlelo lokusebenza lungathatha izithombe futhi lirekhode amavidiyo lusebenzisa ikhamera noma kunini."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"lawula ukudlidliza"</string>
@@ -518,6 +523,7 @@
     <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Ivumela uhlelo lwakho lokusebenza ukuthi lilungise iqoqo lakho lesithombe."</string>
     <string name="permlab_mediaLocation" msgid="8675148183726247864">"funda izindawo kusukela kuqoqo lakho lemidiya"</string>
     <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ivumela uhlelo lokusebenza ukuthi lifunde izindawo kusukela kuqoqo lakho lemidiya."</string>
+    <string name="biometric_dialog_default_title" msgid="4229778503907743328">"Uhlelo lokusebenza lwe-<xliff:g id="APP">%s</xliff:g> lifuna ukufakazela ubuqiniso."</string>
     <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"I-Biometric hardware ayitholakali"</string>
     <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Izigxivizo zeminwe ezincane zitholiwe. Sicela uzame futhi."</string>
     <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ayikwazanga ukucubungula izigxivizo zeminwe. Sicela uzame futhi."</string>
@@ -1897,14 +1903,14 @@
       <item quantity="one"><xliff:g id="COUNT">%1$s</xliff:g> iziphakamiso zokugcwalisa ngokuzenzakalelayo</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> iziphakamiso zokugcwalisa ngokuzenzakalelayo</item>
     </plurals>
-    <string name="autofill_save_title" msgid="3345527308992082601">"Londoloza ku-&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Londoloza i-<xliff:g id="TYPE">%1$s</xliff:g> ku-&lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Londoloza i-<xliff:g id="TYPE_0">%1$s</xliff:g> ne-<xliff:g id="TYPE_1">%2$s</xliff:g> ku-&lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Londoloza i-<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, ne-<xliff:g id="TYPE_2">%3$s</xliff:g> ku-&lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title" msgid="4879673117448810818">"Buyekezela ku-&lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_type" msgid="339733442087186755">"Buyekeza i-<xliff:g id="TYPE">%1$s</xliff:g> ukuya ku-&lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Buyekeza i-<xliff:g id="TYPE_0">%1$s</xliff:g> ne-<xliff:g id="TYPE_1">%2$s</xliff:g> ukuya ku-&lt;b&gt;<xliff:g id="LABEL">%3$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"Buyekeza i-<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, ne-<xliff:g id="TYPE_2">%3$s</xliff:g> ukuya ku-&lt;b&gt;<xliff:g id="LABEL">%4$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="autofill_save_title" msgid="327541108460384555">"Londoloza ku-"<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_type" msgid="2339135393607143594">"Londoloza i-<xliff:g id="TYPE">%1$s</xliff:g> ku-"<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_2types" msgid="87616102361154432">"Londoloza i-<xliff:g id="TYPE_0">%1$s</xliff:g> ne-<xliff:g id="TYPE_1">%2$s</xliff:g> ku-"<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_save_title_with_3types" msgid="4108978552969604555">"Londoloza i-<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, ne-<xliff:g id="TYPE_2">%3$s</xliff:g> ku-"<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title" msgid="5305781141104585279">"Buyekeza ku-"<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_type" msgid="4624181147422762233">"Buyekeza i-<xliff:g id="TYPE">%1$s</xliff:g> ku-"<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_2types" msgid="2300113827053626484">"Buyekeza i-<xliff:g id="TYPE_0">%1$s</xliff:g> ne-<xliff:g id="TYPE_1">%2$s</xliff:g> ku-"<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+    <string name="autofill_update_title_with_3types" msgid="9089824354296211922">"Buyekeza lezi zinto ku-"<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, ne-<xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
     <string name="autofill_save_yes" msgid="6398026094049005921">"Londoloza"</string>
     <string name="autofill_save_no" msgid="2625132258725581787">"Cha ngiyabonga"</string>
     <string name="autofill_update_yes" msgid="310358413273276958">"Buyekeza"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a25c998..68ec342 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3587,11 +3587,22 @@
              {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
              android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
         <attr name="notificationTimeout" format="integer" />
-        <!-- The minimum timeout in milliseconds that UI controls need to remain on the screen.
+        <!-- A recommended timeout in milliseconds used in
+             {@link android.view.accessibility.AccessibilityManager#getRecommendedTimeoutMillis(int, int)
+             android.view.accessibility.AccessibilityManager.getRecommendedTimeoutMillis(int, int)}
+             to return a suitable value for UIs that do not include interactive controls.
              This setting can be changed at runtime by calling
              {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
              android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
-        <attr name="minimumUiTimeout" format="integer" />
+        <attr name="nonInteractiveUiTimeout" format="integer" />
+        <!-- A recommended timeout in milliseconds used in
+             {@link android.view.accessibility.AccessibilityManager#getRecommendedTimeoutMillis(int, int)
+             android.view.accessibility.AccessibilityManager.getRecommendedTimeoutMillis(int, int)}
+             to return a suitable value for interactive controls.
+             This setting can be changed at runtime by calling
+             {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
+             android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
+        <attr name="interactiveUiTimeout" format="integer" />
         <!-- Additional flags as specified in
              {@link android.accessibilityservice.AccessibilityServiceInfo}.
              This setting can be changed at runtime by calling
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 99af0de..6fc0f5b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2735,4 +2735,10 @@
         <attr name="name" format="string" />
     </declare-styleable>
 
+
+    <declare-styleable name="AndroidManifestProfileable" parent="AndroidManifestApplication">
+        <!-- Flag indicating whether the application can be profiled by the shell user,
+             even when running on a device that is running in user mode. -->
+        <attr name="shell" format="boolean" />
+    </declare-styleable>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d6450ac..bca72f4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -513,17 +513,6 @@
     <string-array translatable="false" name="config_cdma_dun_supported_types">
     </string-array>
 
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3 or higher.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN"
-         Multiple entries are separated by using string-array:
-         "<item>[ApnSettingV3]Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14,,,,,,,spn,testspn</item>
-          <item>[ApnSettingV5]Name1,apn2,,,,,,,,,123,46,,mms|*,IPV6,IP,true,12,,,,,,,,,,</item>" -->
-    <string-array translatable="false" name="config_tether_apndata">
-    </string-array>
-
     <!-- Boolean indicating whether the wifi chipset has dual frequency band support -->
     <bool translatable="false" name="config_wifi_dual_band_support">false</bool>
 
diff --git a/core/res/res/values/dimens_car.xml b/core/res/res/values/dimens_car.xml
index a0c02ed..c1ca33e 100644
--- a/core/res/res/values/dimens_car.xml
+++ b/core/res/res/values/dimens_car.xml
@@ -44,9 +44,10 @@
     <dimen name="car_headline3_size">24sp</dimen>
     <dimen name="car_headline4_size">20sp</dimen>
     <dimen name="car_body1_size">32sp</dimen>
-    <dimen name="car_body2_size">26sp</dimen>
-    <dimen name="car_body3_size">16sp</dimen>
-    <dimen name="car_body4_size">14sp</dimen>
+    <dimen name="car_body2_size">28sp</dimen>
+    <dimen name="car_body3_size">26sp</dimen>
+    <dimen name="car_body4_size">24sp</dimen>
+    <!-- car_body5_size is deprecated -->
     <dimen name="car_body5_size">18sp</dimen>
     <dimen name="car_label1_size">26sp</dimen>
     <dimen name="car_label2_size">64sp</dimen>
@@ -85,6 +86,7 @@
     <dimen name="car_borderless_button_horizontal_padding">0dp</dimen>
     <dimen name="car_button_radius">@dimen/car_radius_1</dimen>
     <dimen name="car_pill_button_size">56dp</dimen>
+    <dimen name="car_touch_target_size">76dp</dimen>
 
     <!-- Seekbar -->
     <dimen name="car_seekbar_height">6dp</dimen>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 8bca211..3183169 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -175,4 +175,7 @@
 
   <!-- Accessibility action to notify a window there is an outside touch. -->
   <item type="id" name="accessibilityActionOutsideTouch" />
+
+  <!-- A tag used to save the view added to a transition overlay -->
+  <item type="id" name="transition_overlay_view_tag" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b790829..15f29ce 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2912,11 +2912,13 @@
         <public name="supportsAmbientMode" />
         <!-- @hide For use by platform and tools only. Developers should not specify this value. -->
         <public name="usesNonSdkApi" />
-        <public name="minimumUiTimeout" />
+        <public name="nonInteractiveUiTimeout" />
         <public name="isLightTheme" />
         <public name="isSplitRequired" />
         <public name="textLocale" />
         <public name="settingsSliceUri" />
+        <public name="shell" />
+        <public name="interactiveUiTimeout" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9ea82a9..8b7cafb 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1130,14 +1130,13 @@
     <string name="permdesc_accessFineLocation">This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_accessCoarseLocation">access approximate location
-      (network-based)</string>
+    <string name="permlab_accessCoarseLocation">access approximate location (network-based) only in the foreground</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessCoarseLocation" product="tablet">This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them.</string>
+    <string name="permdesc_accessCoarseLocation" product="tablet">This app can get your location based on network sources such as cell towers and Wi-Fi networks, but only when when the app is in the foreground. These location services must be turned on and available on your tablet for the app to be able to use them.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessCoarseLocation" product="tv">This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them.</string>
+    <string name="permdesc_accessCoarseLocation" product="tv">This app can get your location based on network sources such as cell towers and Wi-Fi networks, but only when when the app is in the foreground. These location services must be turned on and available on your TV for the app to be able to use them.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessCoarseLocation" product="default">This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them.</string>
+    <string name="permdesc_accessCoarseLocation" product="default">This app can get your location based on network sources such as cell towers and Wi-Fi networks, but only when the app is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessBackgroundLocation">access location in the background</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0e787cd..670a4a2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1831,7 +1831,6 @@
   <java-symbol type="array" name="config_tether_dhcp_range" />
   <java-symbol type="array" name="config_tether_upstream_types" />
   <java-symbol type="bool" name="config_tether_upstream_automatic" />
-  <java-symbol type="array" name="config_tether_apndata" />
   <java-symbol type="array" name="config_tether_usb_regexs" />
   <java-symbol type="array" name="config_tether_wifi_regexs" />
   <java-symbol type="array" name="config_usbHostBlacklist" />
@@ -3485,4 +3484,6 @@
   <java-symbol type="bool" name="config_useSmsAppService" />
 
   <java-symbol type="string" name="config_defaultAssistantComponentName" />
+
+  <java-symbol type="id" name="transition_overlay_view_tag" />
 </resources>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 7b3d940..7163769 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -187,7 +187,7 @@
     <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288" />
 
     <!-- The Netherlands, 4 digits, known premium codes listed, plus EU -->
-    <shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223" />
+    <shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" />
 
     <!-- Nigeria -->
     <shortcode country="ng" pattern="\\d{1,5}" free="2441" />
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 0036186..9940bf7 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -158,4 +158,19 @@
 
         assertImageAspectAndContents(res);
     }
+
+    @Test
+    public void testTranslateDeprecatedDataPath() throws Exception {
+        assertTranslate(Uri.parse("content://com.example/path/?foo=bar&baz=meow"));
+        assertTranslate(Uri.parse("content://com.example/path/subpath/12/"));
+        assertTranslate(Uri.parse("content://com.example/path/subpath/12"));
+        assertTranslate(Uri.parse("content://com.example/path/12"));
+        assertTranslate(Uri.parse("content://com.example/"));
+        assertTranslate(Uri.parse("content://com.example"));
+    }
+
+    private static void assertTranslate(Uri uri) {
+        assertEquals(uri, ContentResolver
+                .translateDeprecatedDataPath(ContentResolver.translateDeprecatedDataPath(uri)));
+    }
 }
diff --git a/core/tests/coretests/src/android/os/BinderProxyTest.java b/core/tests/coretests/src/android/os/BinderProxyTest.java
new file mode 100644
index 0000000..4c36b5c
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderProxyTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 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 android.os;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+public class BinderProxyTest extends AndroidTestCase {
+    private static class CountingListener implements Binder.ProxyTransactListener {
+        int mStartedCount;
+        int mEndedCount;
+
+        public Object onTransactStarted(IBinder binder, int transactionCode) {
+            mStartedCount++;
+            return null;
+        }
+
+        public void onTransactEnded(@Nullable Object session) {
+            mEndedCount++;
+        }
+    };
+
+    private PowerManager mPowerManager;
+
+    /**
+     * Setup any common data for the upcoming tests.
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+    }
+
+    @MediumTest
+    public void testNoListener() throws Exception {
+        CountingListener listener = new CountingListener();
+        Binder.setProxyTransactListener(listener);
+        Binder.setProxyTransactListener(null);
+
+        mPowerManager.isInteractive();
+
+        assertEquals(0, listener.mStartedCount);
+        assertEquals(0, listener.mEndedCount);
+    }
+
+    @MediumTest
+    public void testListener() throws Exception {
+        CountingListener listener = new CountingListener();
+        Binder.setProxyTransactListener(listener);
+
+        mPowerManager.isInteractive();
+
+        assertEquals(1, listener.mStartedCount);
+        assertEquals(1, listener.mEndedCount);
+    }
+
+    @MediumTest
+    public void testSessionPropagated() throws Exception {
+        Binder.setProxyTransactListener(new Binder.ProxyTransactListener() {
+            public Object onTransactStarted(IBinder binder, int transactionCode) {
+                return "foo";
+            }
+
+            public void onTransactEnded(@Nullable Object session) {
+                assertEquals("foo", session);
+            }
+        });
+
+        // Check it does not throw..
+        mPowerManager.isInteractive();
+    }
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 6966448..55e21a7 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import static android.os.FileUtils.roundStorageSize;
+import static android.os.FileUtils.translateModeAccessToPosix;
 import static android.os.FileUtils.translateModePfdToPosix;
 import static android.os.FileUtils.translateModePosixToPfd;
 import static android.os.FileUtils.translateModePosixToString;
@@ -27,12 +28,16 @@
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
 import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+import static android.system.OsConstants.F_OK;
 import static android.system.OsConstants.O_APPEND;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDONLY;
 import static android.system.OsConstants.O_RDWR;
 import static android.system.OsConstants.O_TRUNC;
 import static android.system.OsConstants.O_WRONLY;
+import static android.system.OsConstants.R_OK;
+import static android.system.OsConstants.W_OK;
+import static android.system.OsConstants.X_OK;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
@@ -525,6 +530,15 @@
         }
     }
 
+    @Test
+    public void testTranslateMode_Access() throws Exception {
+        assertEquals(O_RDONLY, translateModeAccessToPosix(F_OK));
+        assertEquals(O_RDONLY, translateModeAccessToPosix(R_OK));
+        assertEquals(O_WRONLY, translateModeAccessToPosix(W_OK));
+        assertEquals(O_RDWR, translateModeAccessToPosix(R_OK | W_OK));
+        assertEquals(O_RDWR, translateModeAccessToPosix(R_OK | W_OK | X_OK));
+    }
+
     private static void assertTranslate(String string, int posix, int pfd) {
         assertEquals(posix, translateModeStringToPosix(string));
         assertEquals(string, translateModePosixToString(posix));
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 9778acb..3342266 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -370,6 +370,7 @@
                     Settings.Global.PRIVATE_DNS_DEFAULT_MODE,
                     Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED,
                     Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED,
+                    Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED,
                     Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
                     Settings.Global.RADIO_BLUETOOTH,
                     Settings.Global.RADIO_CELL,
@@ -393,7 +394,9 @@
                     Settings.Global.SETTINGS_USE_PSD_API,
                     Settings.Global.SHORTCUT_MANAGER_CONSTANTS,
                     Settings.Global.SHOW_FIRST_CRASH_DIALOG,
+                    Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED,
                     Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG,
+                    Settings.Global.SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED,
                     Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
                     Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
                     Settings.Global.SHOW_TEMPERATURE_WARNING,
@@ -451,6 +454,7 @@
                     Settings.Global.ENABLE_GPU_DEBUG_LAYERS,
                     Settings.Global.GPU_DEBUG_APP,
                     Settings.Global.GPU_DEBUG_LAYERS,
+                    Settings.Global.GPU_DEBUG_LAYERS_GLES,
                     Settings.Global.ANGLE_ENABLED_APP,
                     Settings.Global.GPU_DEBUG_LAYER_APP,
                     Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
@@ -485,6 +489,7 @@
                     Settings.Global.WIFI_IDLE_MS,
                     Settings.Global.WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED,
                     Settings.Global.WIFI_LINK_SPEED_METRICS_ENABLED,
+                    Settings.Global.WIFI_PNO_FREQUENCY_CULLING_ENABLED,
                     Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
                     Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
                     Settings.Global.WIFI_NETWORK_SHOW_RSSI,
@@ -492,6 +497,7 @@
                     Settings.Global.WIFI_NUM_OPEN_NETWORKS_KEPT,
                     Settings.Global.WIFI_ON,
                     Settings.Global.WIFI_P2P_DEVICE_NAME,
+                    Settings.Global.WIFI_P2P_PENDING_FACTORY_RESET,
                     Settings.Global.WIFI_REENABLE_DELAY_MS,
                     Settings.Global.WIFI_RTT_BACKGROUND_EXEC_GAP_MS,
                     Settings.Global.WIFI_SAVED_STATE,
@@ -547,6 +553,7 @@
                  Settings.Secure.BACKUP_PROVISIONED,
                  Settings.Secure.BACKUP_TRANSPORT,
                  Settings.Secure.CALL_REDIRECTION_DEFAULT_APPLICATION,
+                 Settings.Secure.CALL_SCREENING_DEFAULT_COMPONENT,
                  Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED, // Candidate for backup?
                  Settings.Secure.CARRIER_APPS_HANDLED,
                  Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
index c02d97c..82e4bff 100644
--- a/core/tests/coretests/src/android/text/format/FormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -162,7 +162,7 @@
 
         // Make sure it works on different locales.
         setLocale(Locale.FRANCE);
-        assertEquals("2 j", Formatter.formatShortElapsedTime(mContext, 2 * DAY));
+        assertEquals("2\u202fj", Formatter.formatShortElapsedTime(mContext, 2 * DAY));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index c8e46fc..ca6d6cf 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertThat;
 
 import android.content.Context;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.InstrumentationRegistry;
@@ -57,9 +58,8 @@
         mViewRootImpl.getAttachInfo().getStableInsets().set(-10, -20, -30 , -40);
         final WindowInsets insets = mViewRootImpl.getWindowInsets(true /* forceConstruct */);
 
-        assertThat(insets.getSystemWindowInsets(), equalTo(new Rect()));
-        assertThat(new Rect(insets.getStableInsetLeft(), insets.getStableInsetTop(),
-                insets.getStableInsetRight(), insets.getStableInsetBottom()), equalTo(new Rect()));
+        assertThat(insets.getSystemWindowInsets(), equalTo(Insets.NONE));
+        assertThat(insets.getStableInsets(), equalTo(Insets.NONE));
     }
 
     @Test
@@ -68,10 +68,8 @@
         mViewRootImpl.getAttachInfo().getStableInsets().set(10, -20, 30 , -40);
         final WindowInsets insets = mViewRootImpl.getWindowInsets(true /* forceConstruct */);
 
-        assertThat(insets.getSystemWindowInsets(), equalTo(new Rect(0, 20, 0, 40)));
-        assertThat(new Rect(insets.getStableInsetLeft(), insets.getStableInsetTop(),
-                insets.getStableInsetRight(), insets.getStableInsetBottom()),
-                equalTo(new Rect(10, 0, 30, 0)));
+        assertThat(insets.getSystemWindowInsets(), equalTo(Insets.of(0, 20, 0, 40)));
+        assertThat(insets.getStableInsets(), equalTo(Insets.of(10, 0, 30, 0)));
     }
 
     @Test
@@ -80,10 +78,8 @@
         mViewRootImpl.getAttachInfo().getStableInsets().set(10, 20, 30 , 40);
         final WindowInsets insets = mViewRootImpl.getWindowInsets(true /* forceConstruct */);
 
-        assertThat(insets.getSystemWindowInsets(), equalTo(new Rect(10, 20, 30, 40)));
-        assertThat(new Rect(insets.getStableInsetLeft(), insets.getStableInsetTop(),
-                insets.getStableInsetRight(), insets.getStableInsetBottom()),
-                equalTo(new Rect(10, 20, 30, 40)));
+        assertThat(insets.getSystemWindowInsets(), equalTo(Insets.of(10, 20, 30, 40)));
+        assertThat(insets.getStableInsets(), equalTo(Insets.of(10, 20, 30, 40)));
     }
 
     private static class ViewRootImplAccessor {
diff --git a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
new file mode 100644
index 0000000..51e5aec
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2018 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 android.view.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.os.LocaleList;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ModelFileManagerTest {
+
+    @Mock
+    private Supplier<List<ModelFileManager.ModelFile>> mModelFileSupplier;
+    private ModelFileManager.ModelFileSupplierImpl mModelFileSupplierImpl;
+    private ModelFileManager mModelFileManager;
+    private File mRootTestDir;
+    private File mFactoryModelDir;
+    private File mUpdatedModelFile;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mModelFileManager = new ModelFileManager(mModelFileSupplier);
+        mRootTestDir = InstrumentationRegistry.getContext().getCacheDir();
+        mFactoryModelDir = new File(mRootTestDir, "factory");
+        mUpdatedModelFile = new File(mRootTestDir, "updated.model");
+
+        mModelFileSupplierImpl =
+                new ModelFileManager.ModelFileSupplierImpl(
+                        mFactoryModelDir,
+                        "test\\d.model",
+                        mUpdatedModelFile,
+                        fd -> 1,
+                        fd -> ModelFileManager.ModelFile.LANGUAGE_INDEPENDENT
+                );
+
+        mRootTestDir.mkdirs();
+        mFactoryModelDir.mkdirs();
+    }
+
+    @After
+    public void removeTestDir() {
+        recursiveDelete(mRootTestDir);
+    }
+
+    @Test
+    public void get() {
+        ModelFileManager.ModelFile modelFile =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 1, Collections.emptyList(), true);
+        when(mModelFileSupplier.get()).thenReturn(Collections.singletonList(modelFile));
+
+        List<ModelFileManager.ModelFile> modelFiles = mModelFileManager.listModelFiles();
+
+        assertThat(modelFiles).hasSize(1);
+        assertThat(modelFiles.get(0)).isEqualTo(modelFile);
+    }
+
+    @Test
+    public void findBestModel_versionCode() {
+        ModelFileManager.ModelFile olderModelFile =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 1,
+                        Collections.emptyList(), true);
+
+        ModelFileManager.ModelFile newerModelFile =
+                new ModelFileManager.ModelFile(
+                        new File("/path/b"), 2,
+                        Collections.emptyList(), true);
+        when(mModelFileSupplier.get())
+                .thenReturn(Arrays.asList(olderModelFile, newerModelFile));
+
+        ModelFileManager.ModelFile bestModelFile =
+                mModelFileManager.findBestModelFile(LocaleList.getEmptyLocaleList());
+
+        assertThat(bestModelFile).isEqualTo(newerModelFile);
+    }
+
+    @Test
+    public void findBestModel_languageDependentModelIsPreferred() {
+        Locale locale = Locale.forLanguageTag("ja");
+        ModelFileManager.ModelFile languageIndependentModelFile =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 1,
+                        Collections.emptyList(), true);
+
+        ModelFileManager.ModelFile languageDependentModelFile =
+                new ModelFileManager.ModelFile(
+                        new File("/path/b"), 1,
+                        Collections.singletonList(locale), false);
+        when(mModelFileSupplier.get())
+                .thenReturn(
+                        Arrays.asList(languageIndependentModelFile, languageDependentModelFile));
+
+        ModelFileManager.ModelFile bestModelFile =
+                mModelFileManager.findBestModelFile(
+                        LocaleList.forLanguageTags(locale.toLanguageTag()));
+        assertThat(bestModelFile).isEqualTo(languageDependentModelFile);
+    }
+
+    @Test
+    public void findBestModel_useIndependentWhenNoLanguageModelMatch() {
+        Locale locale = Locale.forLanguageTag("ja");
+        ModelFileManager.ModelFile languageIndependentModelFile =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 1,
+                        Collections.emptyList(), true);
+
+        ModelFileManager.ModelFile languageDependentModelFile =
+                new ModelFileManager.ModelFile(
+                        new File("/path/b"), 1,
+                        Collections.singletonList(locale), false);
+
+        when(mModelFileSupplier.get())
+                .thenReturn(
+                        Arrays.asList(languageIndependentModelFile, languageDependentModelFile));
+
+        ModelFileManager.ModelFile bestModelFile =
+                mModelFileManager.findBestModelFile(
+                        LocaleList.forLanguageTags("zh-hk"));
+        assertThat(bestModelFile).isEqualTo(languageIndependentModelFile);
+    }
+
+    @Test
+    public void findBestModel_languageIsMoreImportantThanVersion() {
+        ModelFileManager.ModelFile matchButOlderModel =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 1,
+                        Collections.singletonList(Locale.forLanguageTag("fr")), false);
+
+        ModelFileManager.ModelFile mismatchButNewerModel =
+                new ModelFileManager.ModelFile(
+                        new File("/path/b"), 2,
+                        Collections.singletonList(Locale.forLanguageTag("ja")), false);
+
+        when(mModelFileSupplier.get())
+                .thenReturn(
+                        Arrays.asList(matchButOlderModel, mismatchButNewerModel));
+
+        ModelFileManager.ModelFile bestModelFile =
+                mModelFileManager.findBestModelFile(
+                        LocaleList.forLanguageTags("fr"));
+        assertThat(bestModelFile).isEqualTo(matchButOlderModel);
+    }
+
+    @Test
+    public void modelFileEquals() {
+        ModelFileManager.ModelFile modelA =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 1,
+                        Collections.singletonList(Locale.forLanguageTag("ja")), false);
+
+        ModelFileManager.ModelFile modelB =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 1,
+                        Collections.singletonList(Locale.forLanguageTag("ja")), false);
+
+        assertThat(modelA).isEqualTo(modelB);
+    }
+
+    @Test
+    public void modelFile_different() {
+        ModelFileManager.ModelFile modelA =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 1,
+                        Collections.singletonList(Locale.forLanguageTag("ja")), false);
+
+        ModelFileManager.ModelFile modelB =
+                new ModelFileManager.ModelFile(
+                        new File("/path/b"), 1,
+                        Collections.singletonList(Locale.forLanguageTag("ja")), false);
+
+        assertThat(modelA).isNotEqualTo(modelB);
+    }
+
+
+    @Test
+    public void modelFile_getPath() {
+        ModelFileManager.ModelFile modelA =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 1,
+                        Collections.singletonList(Locale.forLanguageTag("ja")), false);
+
+        assertThat(modelA.getPath()).isEqualTo("/path/a");
+    }
+
+    @Test
+    public void modelFile_getName() {
+        ModelFileManager.ModelFile modelA =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 1,
+                        Collections.singletonList(Locale.forLanguageTag("ja")), false);
+
+        assertThat(modelA.getName()).isEqualTo("a");
+    }
+
+    @Test
+    public void modelFile_isPreferredTo_languageDependentIsBetter() {
+        ModelFileManager.ModelFile modelA =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 1,
+                        Collections.singletonList(Locale.forLanguageTag("ja")), false);
+
+        ModelFileManager.ModelFile modelB =
+                new ModelFileManager.ModelFile(
+                        new File("/path/b"), 2,
+                        Collections.emptyList(), true);
+
+        assertThat(modelA.isPreferredTo(modelB)).isTrue();
+    }
+
+    @Test
+    public void modelFile_isPreferredTo_version() {
+        ModelFileManager.ModelFile modelA =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 2,
+                        Collections.singletonList(Locale.forLanguageTag("ja")), false);
+
+        ModelFileManager.ModelFile modelB =
+                new ModelFileManager.ModelFile(
+                        new File("/path/b"), 1,
+                        Collections.emptyList(), false);
+
+        assertThat(modelA.isPreferredTo(modelB)).isTrue();
+    }
+
+    @Test
+    public void testFileSupplierImpl_updatedFileOnly() throws IOException {
+        mUpdatedModelFile.createNewFile();
+        File model1 = new File(mFactoryModelDir, "test1.model");
+        model1.createNewFile();
+        File model2 = new File(mFactoryModelDir, "test2.model");
+        model2.createNewFile();
+        new File(mFactoryModelDir, "not_match_regex.model").createNewFile();
+
+        List<ModelFileManager.ModelFile> modelFiles = mModelFileSupplierImpl.get();
+        List<String> modelFilePaths =
+                modelFiles
+                        .stream()
+                        .map(modelFile -> modelFile.getPath())
+                        .collect(Collectors.toList());
+
+        assertThat(modelFiles).hasSize(3);
+        assertThat(modelFilePaths).containsExactly(
+                mUpdatedModelFile.getAbsolutePath(),
+                model1.getAbsolutePath(),
+                model2.getAbsolutePath());
+    }
+
+    @Test
+    public void testFileSupplierImpl_empty() {
+        mFactoryModelDir.delete();
+        List<ModelFileManager.ModelFile> modelFiles = mModelFileSupplierImpl.get();
+
+        assertThat(modelFiles).hasSize(0);
+    }
+
+    private static void recursiveDelete(File f) {
+        if (f.isDirectory()) {
+            for (File innerFile : f.listFiles()) {
+                recursiveDelete(innerFile);
+            }
+        }
+        f.delete();
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index de863d7..91a5440 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -29,6 +29,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.support.test.InstrumentationRegistry;
@@ -48,6 +49,13 @@
 @RunWith(AndroidJUnit4.class)
 public class TextClassificationTest {
 
+    private static final String BUNDLE_KEY = "key";
+    private static final String BUNDLE_VALUE = "value";
+    private static final Bundle BUNDLE = new Bundle();
+    static {
+        BUNDLE.putString(BUNDLE_KEY, BUNDLE_VALUE);
+    }
+
     public Icon generateTestIcon(int width, int height, int colorValue) {
         final int numPixels = width * height;
         final int[] colors = new int[numPixels];
@@ -89,6 +97,7 @@
                 .setEntityType(TextClassifier.TYPE_ADDRESS, 0.3f)
                 .setEntityType(TextClassifier.TYPE_PHONE, 0.7f)
                 .setId(id)
+                .setExtras(BUNDLE)
                 .build();
 
         // Parcel and unparcel
@@ -119,6 +128,9 @@
         assertEquals(TextClassifier.TYPE_ADDRESS, result.getEntity(1));
         assertEquals(0.7f, result.getConfidenceScore(TextClassifier.TYPE_PHONE), 1e-7f);
         assertEquals(0.3f, result.getConfidenceScore(TextClassifier.TYPE_ADDRESS), 1e-7f);
+
+        // Extras
+        assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
     }
 
     @Test
@@ -182,6 +194,7 @@
                 new TextClassification.Request.Builder(text, 0, text.length())
                         .setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
                         .setReferenceTime(referenceTime)
+                        .setExtras(BUNDLE)
                         .build();
 
         // Parcel and unparcel.
@@ -197,5 +210,6 @@
         assertEquals(referenceTime, result.getReferenceTime());
         assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
         assertEquals(referenceTime, result.getReferenceTime());
+        assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
     }
 }
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 70cf097..4456122 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -16,11 +16,14 @@
 
 package android.widget;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import android.app.ActivityOptions;
 import android.app.PendingIntent;
+import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -444,4 +447,40 @@
         }
         return found[0];
     }
+
+    @Test
+    public void sharedElement_pendingIntent_notifyParent() throws Exception {
+        RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
+        PendingIntent pi = PendingIntent.getBroadcast(mContext, 0,
+                new Intent("android.widget.RemoteViewsTest_shared_element"),
+                PendingIntent.FLAG_ONE_SHOT);
+        views.setOnClickResponse(R.id.image, RemoteViews.RemoteResponse.fromPendingIntent(pi)
+                .addSharedElement(0, "e0")
+                .addSharedElement(1, "e1")
+                .addSharedElement(2, "e2"));
+
+        WidgetContainer container = new WidgetContainer(mContext);
+        container.addView(new RemoteViews(views).apply(mContext, container));
+        container.findViewById(R.id.image).performClick();
+
+        assertArrayEquals(container.mSharedViewIds, new int[] {0, 1, 2});
+        assertArrayEquals(container.mSharedViewNames, new String[] {"e0", "e1", "e2"});
+    }
+
+    private class WidgetContainer extends AppWidgetHostView {
+        int[] mSharedViewIds;
+        String[] mSharedViewNames;
+
+        WidgetContainer(Context context) {
+            super(context);
+        }
+
+        @Override
+        public ActivityOptions createSharedElementActivityOptions(
+                int[] sharedViewIds, String[] sharedViewNames, Intent fillInIntent) {
+            mSharedViewIds = sharedViewIds;
+            mSharedViewNames = sharedViewNames;
+            return null;
+        }
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java
new file mode 100644
index 0000000..629f7b6
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 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.internal.inputmethod;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowManager.LayoutParams;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputMethodDebugTest {
+    @Test
+    public void testStartInputReasonToString() {
+        // TODO: Use reflection to make sure that all the constants defined in StartInputReason are
+        // covered.
+        assertEquals("UNSPECIFIED",
+                InputMethodDebug.startInputReasonToString(StartInputReason.UNSPECIFIED));
+    }
+
+    @Test
+    public void testUnbindReasonToString() {
+        // TODO: Use reflection to make sure that all the constants defined in UnbindReason are
+        // covered.
+        assertEquals("UNSPECIFIED",
+                InputMethodDebug.startInputReasonToString(UnbindReason.UNSPECIFIED));
+    }
+
+    @Test
+    public void testSoftInputModeToString() {
+        // TODO: add more tests
+        assertEquals("STATE_UNCHANGED|ADJUST_RESIZE|IS_FORWARD_NAVIGATION",
+                InputMethodDebug.softInputModeToString(
+                        LayoutParams.SOFT_INPUT_STATE_UNCHANGED
+                                | LayoutParams.SOFT_INPUT_ADJUST_RESIZE
+                                | LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION));
+    }
+
+    @Test
+    public void testStartInputFlagsToString() {
+        // TODO: add more tests
+        assertEquals("(none)", InputMethodDebug.startInputFlagsToString(0));
+        assertEquals("IS_TEXT_EDITOR",
+                InputMethodDebug.startInputFlagsToString(StartInputFlags.IS_TEXT_EDITOR));
+        assertEquals("VIEW_HAS_FOCUS|INITIAL_CONNECTION",
+                InputMethodDebug.startInputFlagsToString(
+                        StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.INITIAL_CONNECTION));
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 364dcfd..30309cf 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -43,7 +43,8 @@
 @RunWith(AndroidJUnit4.class)
 @Presubmit
 public class BinderCallsStatsTest {
-    private static final int TEST_UID = 1;
+    private static final int WORKSOURCE_UID = 1;
+    private static final int CALLING_UID = 2;
     private static final int REQUEST_SIZE = 2;
     private static final int REPLY_SIZE = 3;
     private final CachedDeviceState mDeviceState = new CachedDeviceState(false, true);
@@ -61,7 +62,7 @@
 
         SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
         assertEquals(1, uidEntries.size());
-        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+        BinderCallsStats.UidEntry uidEntry = uidEntries.get(WORKSOURCE_UID);
         Assert.assertNotNull(uidEntry);
         List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
         assertEquals(1, uidEntry.callCount);
@@ -82,7 +83,7 @@
         callSession = bcs.callStarted(binder, 2);
         bcs.time += 50;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
-        uidEntry = bcs.getUidEntries().get(TEST_UID);
+        uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
         assertEquals(3, uidEntry.callCount);
         assertEquals(1, uidEntry.recordedCallCount);
         // Still sampled even for another API.
@@ -102,7 +103,7 @@
 
         SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
         assertEquals(1, uidEntries.size());
-        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+        BinderCallsStats.UidEntry uidEntry = uidEntries.get(WORKSOURCE_UID);
         Assert.assertNotNull(uidEntry);
         assertEquals(1, uidEntry.callCount);
         assertEquals(10, uidEntry.cpuTimeMicros);
@@ -118,7 +119,7 @@
         bcs.time += 20;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
-        uidEntry = bcs.getUidEntries().get(TEST_UID);
+        uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
         assertEquals(2, uidEntry.callCount);
         assertEquals(30, uidEntry.cpuTimeMicros);
         callStatsList = new ArrayList(uidEntry.getCallStatsList());
@@ -127,7 +128,7 @@
         callSession = bcs.callStarted(binder, 2);
         bcs.time += 50;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
-        uidEntry = bcs.getUidEntries().get(TEST_UID);
+        uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
         assertEquals(3, uidEntry.callCount);
 
         // This is the first transaction of a new type, so the real CPU time will be measured
@@ -178,7 +179,7 @@
 
         SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
         assertEquals(1, uidEntries.size());
-        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+        BinderCallsStats.UidEntry uidEntry = uidEntries.get(WORKSOURCE_UID);
         Assert.assertNotNull(uidEntry);
         assertEquals(3, uidEntry.callCount);
         assertEquals(60 /* 10 + 50 */, uidEntry.cpuTimeMicros);
@@ -212,7 +213,7 @@
 
         SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
         assertEquals(1, uidEntries.size());
-        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+        BinderCallsStats.UidEntry uidEntry = uidEntries.get(WORKSOURCE_UID);
         assertEquals(2, uidEntry.callCount);
         assertEquals(1, uidEntry.recordedCallCount);
         assertEquals(10, uidEntry.cpuTimeMicros);
@@ -309,7 +310,7 @@
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
         List<BinderCallsStats.CallStat> callStatsList =
-                new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
+                new ArrayList(bcs.getUidEntries().get(WORKSOURCE_UID).getCallStatsList());
 
         assertEquals(REQUEST_SIZE, callStatsList.get(0).maxRequestSizeBytes);
         assertEquals(REPLY_SIZE, callStatsList.get(0).maxReplySizeBytes);
@@ -329,7 +330,7 @@
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
         List<BinderCallsStats.CallStat> callStatsList =
-                new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
+                new ArrayList(bcs.getUidEntries().get(WORKSOURCE_UID).getCallStatsList());
 
         assertEquals(50, callStatsList.get(0).maxCpuTimeMicros);
     }
@@ -348,7 +349,7 @@
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
         List<BinderCallsStats.CallStat> callStatsList =
-                new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
+                new ArrayList(bcs.getUidEntries().get(WORKSOURCE_UID).getCallStatsList());
 
         assertEquals(5, callStatsList.get(0).maxLatencyMicros);
     }
@@ -424,7 +425,7 @@
 
         SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
         assertEquals(1, uidEntries.size());
-        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+        BinderCallsStats.UidEntry uidEntry = uidEntries.get(WORKSOURCE_UID);
         Assert.assertNotNull(uidEntry);
         List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
         assertEquals(false, callStatsList.get(0).screenInteractive);
@@ -441,7 +442,7 @@
 
         SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
         assertEquals(1, uidEntries.size());
-        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+        BinderCallsStats.UidEntry uidEntry = uidEntries.get(WORKSOURCE_UID);
         Assert.assertNotNull(uidEntry);
         List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
         assertEquals(true, callStatsList.get(0).screenInteractive);
@@ -510,7 +511,8 @@
 
         assertEquals(1, bcs.getExportedCallStats().size());
         BinderCallsStats.ExportedCallStat stat = bcs.getExportedCallStats().get(0);
-        assertEquals(TEST_UID, stat.uid);
+        assertEquals(WORKSOURCE_UID, stat.workSourceUid);
+        assertEquals(CALLING_UID, stat.callingUid);
         assertEquals("android.os.Binder", stat.className);
         assertEquals("1", stat.methodName);
         assertEquals(true, stat.screenInteractive);
@@ -526,6 +528,22 @@
     }
 
     @Test
+    public void testCallingUidUsedWhenWorkSourceNotSet() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        bcs.workSourceUid = -1;
+
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        assertEquals(1, bcs.getExportedCallStats().size());
+        BinderCallsStats.ExportedCallStat stat = bcs.getExportedCallStats().get(0);
+        assertEquals(CALLING_UID, stat.workSourceUid);
+        assertEquals(CALLING_UID, stat.callingUid);
+    }
+
+    @Test
     public void testGetExportedStatsWithoutCalls() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         Binder binder = new Binder();
@@ -540,9 +558,10 @@
     }
 
     class TestBinderCallsStats extends BinderCallsStats {
-        int callingUid = TEST_UID;
-        long time = 1234;
-        long elapsedTime = 0;
+        public int callingUid = CALLING_UID;
+        public int workSourceUid = WORKSOURCE_UID;
+        public long time = 1234;
+        public long elapsedTime = 0;
 
         TestBinderCallsStats() {
             // Make random generator not random.
@@ -575,6 +594,11 @@
         protected int getCallingUid() {
             return callingUid;
         }
+
+        @Override
+        protected int getWorkSourceUid() {
+            return workSourceUid;
+        }
     }
 
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
new file mode 100644
index 0000000..b9ef434
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 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.internal.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Comparator;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuThreadReaderTest {
+
+    private static final String PROCESS_NAME = "test_process";
+    private static final int[] THREAD_IDS = {0, 1000, 1235, 4321};
+    private static final String[] THREAD_NAMES = {
+            "test_thread_1", "test_thread_2", "test_thread_3", "test_thread_4"
+    };
+    private static final int[] THREAD_CPU_FREQUENCIES = {
+            1000, 2000, 3000, 4000,
+    };
+    private static final int[][] THREAD_CPU_TIMES = {
+            {1, 0, 0, 1},
+            {0, 0, 0, 0},
+            {1000, 1000, 1000, 1000},
+            {0, 1, 2, 3},
+    };
+
+    private File mProcDirectory;
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getContext();
+        mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mProcDirectory);
+    }
+
+    @Test
+    public void testSimple() throws IOException {
+        // Make /proc/self
+        final Path selfPath = mProcDirectory.toPath().resolve("self");
+        assertTrue(selfPath.toFile().mkdirs());
+
+        // Make /proc/self/task
+        final Path selfThreadsPath = selfPath.resolve("task");
+        assertTrue(selfThreadsPath.toFile().mkdirs());
+
+        // Make /proc/self/cmdline
+        Files.write(selfPath.resolve("cmdline"), PROCESS_NAME.getBytes());
+
+        // Make thread directories in reverse order, as they are read in order of creation by
+        // CpuThreadProcReader
+        for (int i = 0; i < THREAD_IDS.length; i++) {
+            // Make /proc/self/task/$TID
+            final Path threadPath = selfThreadsPath.resolve(String.valueOf(THREAD_IDS[i]));
+            assertTrue(threadPath.toFile().mkdirs());
+
+            // Make /proc/self/task/$TID/comm
+            Files.write(threadPath.resolve("comm"), THREAD_NAMES[i].getBytes());
+
+            // Make /proc/self/task/$TID/time_in_state
+            final OutputStream timeInStateStream =
+                    Files.newOutputStream(threadPath.resolve("time_in_state"));
+            for (int j = 0; j < THREAD_CPU_FREQUENCIES.length; j++) {
+                final String line = String.valueOf(THREAD_CPU_FREQUENCIES[j]) + " "
+                        + String.valueOf(THREAD_CPU_TIMES[i][j]) + "\n";
+                timeInStateStream.write(line.getBytes());
+            }
+            timeInStateStream.close();
+        }
+
+        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+                mProcDirectory.toPath(),
+                mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"));
+        final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+                kernelCpuThreadReader.getCurrentProcessCpuUsage();
+
+        assertNotNull(processCpuUsage);
+        assertEquals(android.os.Process.myPid(), processCpuUsage.processId);
+        assertEquals(android.os.Process.myUid(), processCpuUsage.uid);
+        assertEquals(PROCESS_NAME, processCpuUsage.processName);
+
+        // Sort the thread CPU usages to compare with test case
+        final ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
+                new ArrayList<>(processCpuUsage.threadCpuUsages);
+        threadCpuUsages.sort(Comparator.comparingInt(a -> a.threadId));
+
+        int threadCount = 0;
+        for (KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage : threadCpuUsages) {
+            assertEquals(THREAD_IDS[threadCount], threadCpuUsage.threadId);
+            assertEquals(THREAD_NAMES[threadCount], threadCpuUsage.threadName);
+
+            for (int i = 0; i < threadCpuUsage.usageTimesMillis.length; i++) {
+                assertEquals(
+                        THREAD_CPU_TIMES[threadCount][i] * 10,
+                        threadCpuUsage.usageTimesMillis[i]);
+                assertEquals(
+                        THREAD_CPU_FREQUENCIES[i],
+                        kernelCpuThreadReader.getCpuFrequenciesKhz()[i]);
+            }
+            threadCount++;
+        }
+
+        assertEquals(threadCount, THREAD_IDS.length);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java b/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
new file mode 100644
index 0000000..489e164
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2018 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.internal.os;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ProcStatsUtilTest {
+
+    private File mProcDirectory;
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getContext();
+        mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mProcDirectory);
+    }
+
+    @Test
+    public void testReadNullSeparatedFile_empty() throws IOException {
+        assertEquals(
+                "",
+                runReadNullSeparatedFile(""));
+    }
+
+    @Test
+    public void testReadNullSeparatedFile_simple() throws IOException {
+        assertEquals(
+                "abc def ghi",
+                runReadNullSeparatedFile("abc\0def\0ghi"));
+    }
+
+    @Test
+    public void testReadNullSeparatedFile_trailingNulls() throws IOException {
+        assertEquals(
+                "abc",
+                runReadNullSeparatedFile("abc\0\0\0\0"));
+    }
+
+    @Test
+    public void testReadNullSeparatedFile_doubleNullEnds() throws IOException {
+        assertEquals(
+                "abc",
+                runReadNullSeparatedFile("abc\0\0def"));
+    }
+
+    @Test
+    public void testReadSingleLineProcFile_simple() throws IOException {
+        assertEquals(
+                "abc",
+                runReadSingleLineProcFile("abc"));
+    }
+
+    @Test
+    public void testReadSingleLineProcFile_empty() throws IOException {
+        assertEquals(
+                "",
+                runReadSingleLineProcFile(""));
+    }
+
+    @Test
+    public void testReadSingleLineProcFile_newLine() throws IOException {
+        assertEquals(
+                "abc",
+                runReadSingleLineProcFile("abc\ndef"));
+    }
+
+    @Test
+    public void testReadSingleLineProcFile_doubleNewLine() throws IOException {
+        assertEquals(
+                "abc",
+                runReadSingleLineProcFile("abc\n\ndef"));
+    }
+
+    @Test
+    public void testReadTerminatedProcFile_simple() throws IOException {
+        assertEquals(
+                "abc",
+                runReadTerminatedProcFile("abc\0", (byte) '\0'));
+    }
+
+    @Test
+    public void testReadTerminatedProcFile_withExtra() throws IOException {
+        assertEquals(
+                "123",
+                runReadTerminatedProcFile("123\0extra", (byte) '\0'));
+    }
+
+    @Test
+    public void testReadTerminatedProcFile_noTerminator() throws IOException {
+        assertEquals(
+                "noterm",
+                runReadTerminatedProcFile("noterm", (byte) '\0'));
+    }
+
+    @Test
+    public void testReadTerminatedProcFile_newLineTerm() throws IOException {
+        assertEquals(
+                "123",
+                runReadTerminatedProcFile("123\n456", (byte) '\n'));
+    }
+
+    @Test
+    public void testReadTerminatedProcFile_normalCharTerm() throws IOException {
+        assertEquals(
+                "abc",
+                runReadTerminatedProcFile("abcdef", (byte) 'd'));
+    }
+
+    @Test
+    public void testReadTerminatedProcFile_largeUnterminated() throws IOException {
+        String longString = new String(new char[10000]).replace('\0', 'a');
+        assertEquals(
+                longString,
+                runReadTerminatedProcFile(longString, (byte) '\0'));
+    }
+
+    @Test
+    public void testReadTerminatedProcFile_largeTerminated() throws IOException {
+        String longString = new String(new char[10000]).replace('\0', 'a');
+        assertEquals(
+                longString,
+                runReadTerminatedProcFile(longString + "\0", (byte) '\0'));
+    }
+
+    @Test
+    public void testReadTerminatedProcFile_largeExtra() throws IOException {
+        String longString = new String(new char[10000]).replace('\0', 'a');
+        assertEquals(
+                longString,
+                runReadTerminatedProcFile(longString + "\0abc", (byte) '\0'));
+    }
+
+    private String runReadNullSeparatedFile(String fileContents) throws IOException {
+        File tempFile = File.createTempFile("null-separated-file", null, mProcDirectory);
+        Files.write(tempFile.toPath(), fileContents.getBytes());
+        String result = ProcStatsUtil.readNullSeparatedFile(tempFile.toString());
+        Files.delete(tempFile.toPath());
+        return result;
+    }
+
+    private String runReadSingleLineProcFile(String fileContents) throws IOException {
+        File tempFile = File.createTempFile("single-line-proc-file", null, mProcDirectory);
+        Files.write(tempFile.toPath(), fileContents.getBytes());
+        String result = ProcStatsUtil.readSingleLineProcFile(tempFile.toString());
+        Files.delete(tempFile.toPath());
+        return result;
+    }
+
+    private String runReadTerminatedProcFile(
+            String fileContents, byte terminator) throws IOException {
+        File tempFile = File.createTempFile("terminated-proc-file", null, mProcDirectory);
+        Files.write(tempFile.toPath(), fileContents.getBytes());
+        String result = ProcStatsUtil.readTerminatedProcFile(tempFile.toString(), terminator);
+        Files.delete(tempFile.toPath());
+        return result;
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
new file mode 100644
index 0000000..f2a531f
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 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.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ProcTimeInStateReaderTest {
+
+    private File mProcDirectory;
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getContext();
+        mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mProcDirectory);
+    }
+
+    @Test
+    public void testSimple() throws IOException {
+        Path initialTimeInStateFile = mProcDirectory.toPath().resolve("initial-time-in-state");
+        Files.write(initialTimeInStateFile, "1 2\n3 4\n5 6\n7 8\n".getBytes());
+        ProcTimeInStateReader reader = new ProcTimeInStateReader(initialTimeInStateFile);
+
+        assertArrayEquals(
+                "Reported frequencies are correct",
+                new long[]{1, 3, 5, 7},
+                reader.getFrequenciesKhz());
+        assertArrayEquals(
+                "Reported usage times are correct",
+                new long[]{20, 40, 60, 80},
+                reader.getUsageTimesMillis(initialTimeInStateFile));
+    }
+
+    @Test
+    public void testDifferentFile() throws IOException {
+        Path initialTimeInStateFile = mProcDirectory.toPath().resolve("initial-time-in-state");
+        Files.write(initialTimeInStateFile, "1 2\n3 4\n5 6\n7 8\n".getBytes());
+        ProcTimeInStateReader reader = new ProcTimeInStateReader(initialTimeInStateFile);
+
+        Path timeInStateFile = mProcDirectory.toPath().resolve("time-in-state");
+        Files.write(timeInStateFile, "1 20\n3 40\n5 60\n7 80\n".getBytes());
+        assertArrayEquals(
+                "Reported usage times are correct",
+                new long[]{200, 400, 600, 800},
+                reader.getUsageTimesMillis(timeInStateFile));
+    }
+
+    @Test
+    public void testWrongLength() throws IOException {
+        Path initialTimeInStateFile = mProcDirectory.toPath().resolve("initial-time-in-state");
+        Files.write(initialTimeInStateFile, "1 2\n3 4\n5 6\n7 8\n".getBytes());
+        ProcTimeInStateReader reader = new ProcTimeInStateReader(initialTimeInStateFile);
+
+        Path timeInStateFile = mProcDirectory.toPath().resolve("time-in-state");
+        Files.write(timeInStateFile, "1 2\n3 4\n5 6\n".getBytes());
+        assertNull(reader.getUsageTimesMillis(timeInStateFile));
+    }
+
+    @Test
+    public void testEmptyInitialFails() throws IOException {
+        Path initialTimeInStateFile = mProcDirectory.toPath().resolve("initial-time-in-state");
+        Files.write(initialTimeInStateFile, "".getBytes());
+        try {
+            new ProcTimeInStateReader(initialTimeInStateFile);
+            fail("Instantiation didn't fail with empty initial time_in_state file");
+        } catch (IOException ignored) {
+        }
+    }
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 73c10d2..68f24fb 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -178,6 +178,28 @@
     <assign-permission name="android.permission.STATSCOMPANION" uid="statsd" />
     <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="statsd" />
 
+    <split-permission name="android.permission.WRITE_EXTERNAL_STORAGE">
+        <new-permission name="android.permission.READ_EXTERNAL_STORAGE" />
+    </split-permission>
+    <split-permission name="android.permission.READ_CONTACTS"
+                      targetSdk="16">
+        <new-permission name="android.permission.READ_CALL_LOG" />
+    </split-permission>
+    <split-permission name="android.permission.WRITE_CONTACTS"
+                      targetSdk="16">
+        <new-permission name="android.permission.WRITE_CALL_LOG" />
+    </split-permission>
+    <!-- STOPSHIP(b/118882117): change targetSdk to Q when SDK version finalised -->
+    <split-permission name="android.permission.ACCESS_FINE_LOCATION"
+                      targetSdk="10000">
+        <new-permission name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+    </split-permission>
+    <!-- STOPSHIP(b/118882117): change targetSdk to Q when SDK version finalised -->
+    <split-permission name="android.permission.ACCESS_COARSE_LOCATION"
+                      targetSdk="10000">
+        <new-permission name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+    </split-permission>
+
     <!-- This is a list of all the libraries available for application
          code to link against. -->
 
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 28e92db..2604365 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -256,6 +256,7 @@
         <permission name="android.permission.DELETE_PACKAGES"/>
         <permission name="android.permission.FORCE_STOP_PACKAGES"/>
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+        <permission name="android.permission.MANAGE_DEBUGGING"/>
         <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
         <permission name="android.permission.MANAGE_FINGERPRINT"/>
         <permission name="android.permission.MANAGE_USB"/>
@@ -313,6 +314,7 @@
         <permission name="android.permission.INSTALL_PACKAGES"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+        <permission name="android.permission.MANAGE_ACCESSIBILITY"/>
         <permission name="android.permission.MANAGE_ACTIVITY_STACKS"/>
         <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
         <permission name="android.permission.MANAGE_USB"/>
@@ -375,6 +377,7 @@
         <permission name="android.permission.GET_APP_OPS_STATS"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+        <permission name="android.permission.MANAGE_DEBUGGING"/>
         <permission name="android.permission.MANAGE_USB"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.MASTER_CLEAR"/>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index e7a50d7..790b37e 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1301,13 +1301,13 @@
             node.setLeftTopRightBottom(0, 0, width, height);
             node.setClipToBounds(false);
             node.setForceDarkAllowed(false);
-            final RecordingCanvas canvas = node.start(width, height);
+            final RecordingCanvas canvas = node.startRecording(width, height);
             if (source.getWidth() != width || source.getHeight() != height) {
                 canvas.scale(width / (float) source.getWidth(),
                         height / (float) source.getHeight());
             }
             canvas.drawPicture(source);
-            node.end(canvas);
+            node.endRecording();
             Bitmap bitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
             if (config != Config.HARDWARE) {
                 bitmap = bitmap.copy(config, false);
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index e35a3be..3b0dc9d 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -205,11 +205,70 @@
         mBitmap = bitmap;
     }
 
-    /** @hide */
-    public void insertReorderBarrier() {}
+    /**
+     * @deprecated use {@link #enableZ()} instead
+     * @hide */
+    @Deprecated
+    public void insertReorderBarrier() {
+        enableZ();
+    }
 
-    /** @hide */
-    public void insertInorderBarrier() {}
+    /**
+     * @deprecated use {@link #disableZ()} instead
+     * @hide */
+    @Deprecated
+    public void insertInorderBarrier() {
+        disableZ();
+    }
+
+    /**
+     * <p>Enables Z support which defaults to disabled. This allows for RenderNodes drawn with
+     * {@link #drawRenderNode(RenderNode)} to be re-arranged based off of their
+     * {@link RenderNode#getElevation()} and {@link RenderNode#getTranslationZ()}
+     * values. It also enables rendering of shadows for RenderNodes with an elevation or
+     * translationZ.</p>
+     *
+     * <p>Any draw reordering will not be moved before this call. A typical usage of this might
+     * look something like:
+     *
+     * <pre class="prettyprint">
+     *     void draw(Canvas canvas) {
+     *         // Draw any background content
+     *         canvas.drawColor(backgroundColor);
+     *
+     *         // Begin drawing that may be reordered based off of Z
+     *         canvas.enableZ();
+     *         for (RenderNode child : children) {
+     *             canvas.drawRenderNode(child);
+     *         }
+     *         // End drawing that may be reordered based off of Z
+     *         canvas.disableZ();
+     *
+     *         // Draw any overlays
+     *         canvas.drawText("I'm on top of everything!", 0, 0, paint);
+     *     }
+     * </pre>
+     * </p>
+     *
+     * Note: This is not impacted by any {@link #save()} or {@link #restore()} calls as it is not
+     * considered to be part of the current matrix or clip.
+     *
+     * See {@link #disableZ()}
+     */
+    public void enableZ() {
+    }
+
+    /**
+     * Disables Z support, preventing any RenderNodes drawn after this point from being
+     * visually reordered or having shadows rendered.
+     *
+     * Note: This is not impacted by any {@link #save()} or {@link #restore()} calls as it is not
+     * considered to be part of the current matrix or clip.
+     *
+     * See {@link #enableZ()}
+     */
+    public void disableZ() {
+    }
 
     /**
      * Return true if the device that the current layer draws into is opaque
@@ -2110,4 +2169,17 @@
         super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset,
                 colors, colorOffset, indices, indexOffset, indexCount, paint);
     }
+
+    /**
+     * Draws the given RenderNode. This is only supported in hardware rendering, which can be
+     * verified by asserting that {@link #isHardwareAccelerated()} is true. If
+     * {@link #isHardwareAccelerated()} is false then this throws an exception.
+     *
+     * See {@link RenderNode} for more information on what a RenderNode is and how to use it.
+     *
+     * @param renderNode The RenderNode to draw, must be non-null.
+     */
+    public void drawRenderNode(@NonNull RenderNode renderNode) {
+        throw new IllegalArgumentException("Software rendering doesn't support drawRenderNode");
+    }
 }
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 9546a4a..c580c46 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -16,8 +16,6 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
-
 public class ImageFormat {
     /*
      * these constants are chosen to be binary compatible with their previous
@@ -92,20 +90,21 @@
      * </ul>
      * </p>
      *
-     * <pre> y_size = stride * height </pre>
+     * <pre> size = stride * height </pre>
      *
      * <p>For example, the {@link android.media.Image} object can provide data
-     * in this format from a {@link android.hardware.camera2.CameraDevice}
-     * through a {@link android.media.ImageReader} object if this format is
-     * supported by {@link android.hardware.camera2.CameraDevice}.</p>
+     * in this format from a {@link android.hardware.camera2.CameraDevice} (if
+     * supported) through a {@link android.media.ImageReader} object. The
+     * {@link android.media.Image#getPlanes() Image#getPlanes()} will return a
+     * single plane containing the pixel data. The pixel stride is always 1 in
+     * {@link android.media.Image.Plane#getPixelStride()}, and the
+     * {@link android.media.Image.Plane#getRowStride()} describes the vertical
+     * neighboring pixel distance (in bytes) between adjacent rows.</p>
      *
      * @see android.media.Image
      * @see android.media.ImageReader
      * @see android.hardware.camera2.CameraDevice
-     *
-     * @hide
      */
-    @UnsupportedAppUsage
     public static final int Y8 = 0x20203859;
 
     /**
@@ -787,6 +786,7 @@
             case DEPTH_POINT_CLOUD:
             case PRIVATE:
             case RAW_DEPTH:
+            case Y8:
                 return true;
         }
 
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 7af006b..fd5d624 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.util.Pools.SynchronizedPool;
 import android.view.TextureLayer;
 
@@ -27,17 +26,20 @@
 
 /**
  * A Canvas implementation that records view system drawing operations for deferred rendering.
- * This is intended for use with RenderNode. This class keeps a list of all the Paint and
- * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while
+ * This is used in combination with RenderNode. This class keeps a list of all the Paint and
+ * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being released while
  * the RecordingCanvas is still holding a native reference to the memory.
  *
- * @hide
+ * This is obtained by calling {@link RenderNode#startRecording()} and is valid until the matching
+ * {@link RenderNode#endRecording()} is called. It must not be retained beyond that as it is
+ * internally reused.
  */
 public final class RecordingCanvas extends BaseRecordingCanvas {
     // The recording canvas pool should be large enough to handle a deeply nested
     // view hierarchy because display lists are generated recursively.
     private static final int POOL_LIMIT = 25;
 
+    /** @hide */
     public static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
 
     private static final SynchronizedPool<RecordingCanvas> sPool =
@@ -50,6 +52,7 @@
     private int mWidth;
     private int mHeight;
 
+    /** @hide */
     static RecordingCanvas obtain(@NonNull RenderNode node, int width, int height) {
         if (node == null) throw new IllegalArgumentException("node cannot be null");
         RecordingCanvas canvas = sPool.acquire();
@@ -65,15 +68,18 @@
         return canvas;
     }
 
+    /** @hide */
     void recycle() {
         mNode = null;
         sPool.release(this);
     }
 
+    /** @hide */
     long finishRecording() {
         return nFinishRecording(mNativeCanvasWrapper);
     }
 
+    /** @hide */
     @Override
     public boolean isRecordingFor(Object o) {
         return o == mNode;
@@ -138,12 +144,12 @@
     ///////////////////////////////////////////////////////////////////////////
 
     @Override
-    public void insertReorderBarrier() {
+    public void enableZ() {
         nInsertReorderBarrier(mNativeCanvasWrapper, true);
     }
 
     @Override
-    public void insertInorderBarrier() {
+    public void disableZ() {
         nInsertReorderBarrier(mNativeCanvasWrapper, false);
     }
 
@@ -159,7 +165,6 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
     public void callDrawGLFunction2(long drawGLFunction) {
         nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunction, null);
     }
@@ -178,7 +183,6 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
     public void drawGLFunctor2(long drawGLFunctor, @Nullable Runnable releasedCallback) {
         nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback);
     }
@@ -192,8 +196,8 @@
      *
      * @param renderNode The RenderNode to draw.
      */
-    @UnsupportedAppUsage
-    public void drawRenderNode(RenderNode renderNode) {
+    @Override
+    public void drawRenderNode(@NonNull RenderNode renderNode) {
         nDrawRenderNode(mNativeCanvasWrapper, renderNode.mNativeRenderNode);
     }
 
@@ -225,7 +229,6 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
     public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
             CanvasProperty<Float> radius, CanvasProperty<Paint> paint) {
         nDrawCircle(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(),
@@ -254,6 +257,7 @@
                 paint.getNativeContainer());
     }
 
+    /** @hide */
     @Override
     protected void throwIfCannotDraw(Bitmap bitmap) {
         super.throwIfCannotDraw(bitmap);
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index b61488c..12128b3 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -19,9 +19,9 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.view.NativeVectorDrawableAnimator;
 import android.view.RenderNodeAnimator;
+import android.view.Surface;
 import android.view.View;
 
 import dalvik.annotation.optimization.CriticalNative;
@@ -33,89 +33,92 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * <p>A display list records a series of graphics related operations and can replay
- * them later. Display lists are usually built by recording operations on a
- * {@link RecordingCanvas}. Replaying the operations from a display list avoids
- * executing application code on every frame, and is thus much more efficient.</p>
+ * <p>RenderNode is used to build hardware accelerated rendering hierarchies. Each RenderNode
+ * contains both a display list as well as a set of properties that affect the rendering of the
+ * display list. RenderNodes are used internally for all Views by default and are not typically
+ * used directly.</p>
  *
- * <p>Display lists are used internally for all views by default, and are not
- * typically used directly. One reason to consider using a display is a custom
- * {@link View} implementation that needs to issue a large number of drawing commands.
- * When the view invalidates, all the drawing commands must be reissued, even if
- * large portions of the drawing command stream stay the same frame to frame, which
- * can become a performance bottleneck. To solve this issue, a custom View might split
- * its content into several display lists. A display list is updated only when its
- * content, and only its content, needs to be updated.</p>
+ * <p>RenderNodes are used to divide up the rendering content of a complex scene into smaller
+ * pieces that can then be updated individually more cheaply. Updating part of the scene only needs
+ * to update the display list or properties of a small number of RenderNode instead of redrawing
+ * everything from scratch. A RenderNode only needs its display list re-recorded when its content
+ * alone should be changed. RenderNodes can also be transformed without re-recording the display
+ * list through the transform properties.</p>
  *
- * <p>A text editor might for instance store each paragraph into its own display list.
+ * <p>A text editor might for instance store each paragraph into its own RenderNode.
  * Thus when the user inserts or removes characters, only the display list of the
  * affected paragraph needs to be recorded again.</p>
  *
  * <h3>Hardware acceleration</h3>
- * <p>Display lists can only be replayed using a {@link RecordingCanvas}. They are not
+ * <p>RenderNodes can be drawn using a {@link RecordingCanvas}. They are not
  * supported in software. Always make sure that the {@link android.graphics.Canvas}
  * you are using to render a display list is hardware accelerated using
  * {@link android.graphics.Canvas#isHardwareAccelerated()}.</p>
  *
- * <h3>Creating a display list</h3>
+ * <h3>Creating a RenderNode</h3>
  * <pre class="prettyprint">
- *     ThreadedRenderer renderer = myView.getThreadedRenderer();
- *     if (renderer != null) {
- *         DisplayList displayList = renderer.createDisplayList();
- *         RecordingCanvas canvas = displayList.start(width, height);
- *         try {
- *             // Draw onto the canvas
- *             // For instance: canvas.drawBitmap(...);
- *         } finally {
- *             displayList.end();
- *         }
+ *     RenderNode renderNode = RenderNode.create("myRenderNode");
+ *     renderNode.setLeftTopRightBottom(0, 0, 50, 50); // Set the size to 50x50
+ *     RecordingCanvas canvas = renderNode.startRecording();
+ *     try {
+ *         // Draw with the canvas
+ *         canvas.drawRect(...);
+ *     } finally {
+ *         renderNode.endRecording();
  *     }
  * </pre>
  *
- * <h3>Rendering a display list on a View</h3>
+ * <h3>Drawing a RenderNode in a View</h3>
  * <pre class="prettyprint">
  *     protected void onDraw(Canvas canvas) {
- *         if (canvas.isHardwareAccelerated()) {
- *             RecordingCanvas displayListCanvas = (RecordingCanvas) canvas;
- *             displayListCanvas.drawDisplayList(mDisplayList);
+ *         if (canvas instanceof RecordingCanvas) {
+ *             RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+ *             // Check that the RenderNode has a display list, re-recording it if it does not.
+ *             if (!myRenderNode.hasDisplayList()) {
+ *                 updateDisplayList(myRenderNode);
+ *             }
+ *             // Draw the RenderNode into this canvas.
+ *             recordingCanvas.drawRenderNode(myRenderNode);
  *         }
  *     }
  * </pre>
  *
  * <h3>Releasing resources</h3>
  * <p>This step is not mandatory but recommended if you want to release resources
- * held by a display list as soon as possible.</p>
+ * held by a display list as soon as possible. Most significantly any bitmaps it may contain.</p>
  * <pre class="prettyprint">
- *     // Mark this display list invalid, it cannot be used for drawing anymore,
- *     // and release resources held by this display list
- *     displayList.clear();
+ *     // Discards the display list content allowing for any held resources to be released.
+ *     // After calling this
+ *     renderNode.discardDisplayList();
  * </pre>
  *
+ *
  * <h3>Properties</h3>
- * <p>In addition, a display list offers several properties, such as
+ * <p>In addition, a RenderNode offers several properties, such as
  * {@link #setScaleX(float)} or {@link #setLeft(int)}, that can be used to affect all
  * the drawing commands recorded within. For instance, these properties can be used
  * to move around a large number of images without re-issuing all the individual
- * <code>drawBitmap()</code> calls.</p>
+ * <code>canvas.drawBitmap()</code> calls.</p>
  *
  * <pre class="prettyprint">
  *     private void createDisplayList() {
- *         mDisplayList = DisplayList.create("MyDisplayList");
- *         RecordingCanvas canvas = mDisplayList.start(width, height);
+ *         mRenderNode = RenderNode.create("MyRenderNode");
+ *         mRenderNode.setLeftTopRightBottom(0, 0, width, height);
+ *         RecordingCanvas canvas = mRenderNode.startRecording();
  *         try {
  *             for (Bitmap b : mBitmaps) {
  *                 canvas.drawBitmap(b, 0.0f, 0.0f, null);
  *                 canvas.translate(0.0f, b.getHeight());
  *             }
  *         } finally {
- *             displayList.end();
+ *             mRenderNode.endRecording();
  *         }
  *     }
  *
  *     protected void onDraw(Canvas canvas) {
- *         if (canvas.isHardwareAccelerated()) {
- *             RecordingCanvas displayListCanvas = (RecordingCanvas) canvas;
- *             displayListCanvas.drawDisplayList(mDisplayList);
+ *         if (canvas instanceof RecordingCanvas) {
+ *             RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+ *             recordingCanvas.drawRenderNode(mRenderNode);
  *         }
  *     }
  *
@@ -124,15 +127,39 @@
  *          // by x pixels to the right and redraw this view. All the commands
  *          // recorded in createDisplayList() won't be re-issued, only onDraw()
  *          // will be invoked and will execute very quickly
- *          mDisplayList.offsetLeftAndRight(x);
+ *          mRenderNode.offsetLeftAndRight(x);
  *          invalidate();
  *     }
  * </pre>
  *
  * <h3>Threading</h3>
- * <p>Display lists must be created on and manipulated from the UI thread only.</p>
+ * <p>RenderNode may be created and used on any thread but they are not thread-safe. Only
+ * a single thread may interact with a RenderNode at any given time. It is critical
+ * that the RenderNode is only used on the same thread it is drawn with. For example when using
+ * RenderNode with a custom View, then that RenderNode must only be used from the UI thread.</p>
  *
- * @hide
+ * <h3>When to re-render</h3>
+ * <p>Many of the RenderNode mutation methods, such as {@link #setTranslationX(float)}, return
+ * a boolean indicating if the value actually changed or not. This is useful in detecting
+ * if a new frame should be rendered or not. A typical usage would look like:
+ * <pre class="prettyprint">
+ *     public void translateTo(int x, int y) {
+ *         boolean needsUpdate = myRenderNode.setTranslationX(x);
+ *         needsUpdate |= myRenderNode.setTranslationY(y);
+ *         if (needsUpdate) {
+ *             myOwningView.invalidate();
+ *         }
+ *     }
+ * </pre>
+ * This is marginally faster than doing a more explicit up-front check if the value changed by
+ * comparing the desired value against {@link #getTranslationX()} as it minimizes JNI transitions.
+ * The actual mechanism of requesting a new frame to be rendered will depend on how this
+ * RenderNode is being drawn. If it's drawn to a containing View, as in the above snippet,
+ * then simply invalidating that View works. If instead the RenderNode is being drawn to a Canvas
+ * directly such as with {@link Surface#lockHardwareCanvas()} then a new frame needs to be drawn
+ * by calling {@link Surface#lockHardwareCanvas()}, re-drawing the root RenderNode or whatever
+ * top-level content is desired, and finally calling {@link Surface#unlockCanvasAndPost(Canvas)}.
+ * </p>
  */
 public class RenderNode {
 
@@ -142,11 +169,14 @@
                 RenderNode.class.getClassLoader(), nGetNativeFinalizer(), 1024);
     }
 
-    /** Not for general use; use only if you are ThreadedRenderer or RecordingCanvas.
+    /**
+     * Not for general use; use only if you are ThreadedRenderer or RecordingCanvas.
+     *
      * @hide
      */
     public final long mNativeRenderNode;
     private final AnimationHost mAnimationHost;
+    private RecordingCanvas mCurrentRecordingCanvas;
 
     private RenderNode(String name, AnimationHost animationHost) {
         mNativeRenderNode = nCreate(name);
@@ -168,10 +198,13 @@
      * drawing operations, and store / apply render properties when drawn.
      *
      * @param name The name of the RenderNode, used for debugging purpose. May be null.
-     *
      * @return A new RenderNode.
      */
-    @UnsupportedAppUsage
+    public static @NonNull RenderNode create(@Nullable String name) {
+        return new RenderNode(name, null);
+    }
+
+    /** @hide */
     public static RenderNode create(String name, @Nullable AnimationHost animationHost) {
         return new RenderNode(name, animationHost);
     }
@@ -181,6 +214,8 @@
      *
      * Note: This will *NOT* incRef() on the native object, however it will
      * decRef() when it is destroyed. The caller should have already incRef'd it
+     *
+     * @hide
      */
     public static RenderNode adopt(long nativePtr) {
         return new RenderNode(nativePtr);
@@ -215,6 +250,8 @@
 
     /**
      * Enable callbacks for position changes.
+     *
+     * @hide
      */
     public void requestPositionUpdates(PositionUpdateListener listener) {
         nRequestPositionUpdates(mNativeRenderNode, listener);
@@ -226,64 +263,104 @@
      * operations performed on the returned canvas are recorded and
      * stored in this display list.
      *
-     * Calling this method will mark the render node invalid until
-     * {@link #end(RecordingCanvas)} is called.
-     * Only valid render nodes can be replayed.
+     * {@link #endRecording()} must be called when the recording is finished in order to apply
+     * the updated display list.
      *
-     * @param width The width of the recording viewport
-     * @param height The height of the recording viewport
-     *
+     * @param width  The width of the recording viewport. This will not alter the width of the
+     *               RenderNode itself, that must be set with {@link #setLeft(int)} and
+     *               {@link #setRight(int)}
+     * @param height The height of the recording viewport. This will not alter the height of the
+     *               RenderNode itself, that must be set with {@link #setTop(int)} and
+     *               {@link #setBottom(int)}.
      * @return A canvas to record drawing operations.
-     *
-     * @see #end(RecordingCanvas)
-     * @see #isValid()
+     * @see #endRecording()
+     * @see #hasDisplayList()
      */
-    @UnsupportedAppUsage
+    public RecordingCanvas startRecording(int width, int height) {
+        if (mCurrentRecordingCanvas != null) {
+            throw new IllegalStateException(
+                    "Recording currently in progress - missing #endRecording() call?");
+        }
+        mCurrentRecordingCanvas = RecordingCanvas.obtain(this, width, height);
+        return mCurrentRecordingCanvas;
+    }
+
+    /**
+     * Same as {@link #startRecording(int, int)} with the width & height set
+     * to the RenderNode's own width & height. The RenderNode's width & height may be set
+     * with {@link #setLeftTopRightBottom(int, int, int, int)}.
+     */
+    public RecordingCanvas startRecording() {
+        return startRecording(nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode));
+    }
+
+    /**
+     * @hide
+     * @deprecated use {@link #startRecording(int, int)} instead
+     */
+    @Deprecated
     public RecordingCanvas start(int width, int height) {
-        return RecordingCanvas.obtain(this, width, height);
+        return startRecording(width, height);
     }
 
     /**
-     * Same as {@link #start(int, int)} but with the RenderNode's width & height
-     */
-    public RecordingCanvas start() {
-        return RecordingCanvas.obtain(this,
-                nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode));
-    }
-
-    /**
-     * Ends the recording for this display list. A display list cannot be
-     * replayed if recording is not finished. Calling this method marks
-     * the display list valid and {@link #isValid()} will return true.
+     * `
+     * Ends the recording for this display list. Calling this method marks
+     * the display list valid and {@link #hasDisplayList()} will return true.
      *
-     * @see #start(int, int)
-     * @see #isValid()
+     * @see #startRecording(int, int)
+     * @see #hasDisplayList()
      */
-    @UnsupportedAppUsage
-    public void end(RecordingCanvas canvas) {
+    public void endRecording() {
+        if (mCurrentRecordingCanvas == null) {
+            throw new IllegalStateException(
+                    "No recording in progress, forgot to call #startRecording()?");
+        }
+        RecordingCanvas canvas = mCurrentRecordingCanvas;
+        mCurrentRecordingCanvas = null;
         long displayList = canvas.finishRecording();
         nSetDisplayList(mNativeRenderNode, displayList);
         canvas.recycle();
     }
 
     /**
+     * @hide
+     * @deprecated use {@link #endRecording()} instead
+     */
+    @Deprecated
+    public void end(RecordingCanvas canvas) {
+        if (mCurrentRecordingCanvas != canvas) {
+            throw new IllegalArgumentException(
+                    "Canvas given isn't the one that was returned from #startRecording");
+        }
+        endRecording();
+    }
+
+    /**
      * Reset native resources. This is called when cleaning up the state of display lists
      * during destruction of hardware resources, to ensure that we do not hold onto
      * obsolete resources after related resources are gone.
      */
-    @UnsupportedAppUsage
     public void discardDisplayList() {
         nSetDisplayList(mNativeRenderNode, 0);
     }
 
     /**
-     * Returns whether the RenderNode's display list content is currently usable.
-     * If this returns false, the display list should be re-recorded prior to replaying it.
+     * Returns whether the RenderNode has a display list. If this returns false, the RenderNode
+     * should be re-recorded with {@link #startRecording()} and {@link #endRecording()}.
      *
-     * @return boolean true if the display list is able to be replayed, false otherwise.
+     * A RenderNode without a display list may still be drawn, however it will have no impact
+     * on the rendering content until its display list is updated.
+     *
+     * When a RenderNode is no longer drawn by anything the system may automatically
+     * invoke {@link #discardDisplayList()}. It is therefore important to ensure that
+     * {@link #hasDisplayList()} is true on a RenderNode prior to drawing it.
+     *
+     * See {@link #discardDisplayList()}
+     *
+     * @return boolean true if this RenderNode has a display list, false otherwise.
      */
-    @UnsupportedAppUsage
-    public boolean isValid() {
+    public boolean hasDisplayList() {
         return nIsValid(mNativeRenderNode);
     }
 
@@ -326,21 +403,52 @@
     ///////////////////////////////////////////////////////////////////////////
 
     /**
-     * TODO
+     * @hide
+     * @deprecated use {@link #setUseCompositingLayer(boolean, Paint)} instead
      */
+    @Deprecated
     public boolean setLayerType(int layerType) {
         return nSetLayerType(mNativeRenderNode, layerType);
     }
 
     /**
-     * TODO
+     * @hide
+     * @deprecated use {@link #setUseCompositingLayer(boolean, Paint)} instead
      */
+    @Deprecated
     public boolean setLayerPaint(@Nullable Paint paint) {
         return nSetLayerPaint(mNativeRenderNode, paint != null ? paint.getNativeInstance() : 0);
     }
 
     /**
+     * Controls whether or not to force this RenderNode to render to an intermediate buffer.
+     * Internally RenderNode will already promote itself to a composition layer if it's useful
+     * for performance or required for the current combination of {@link #setAlpha(float)} and
+     * {@link #setHasOverlappingRendering(boolean)}.
+     *
+     * The usage of this is instead to allow for either overriding of the internal behavior
+     * if it's measured to be necessary for the particular rendering content in question or, more
+     * usefully, to add a composition effect to the RenderNode via the optional paint parameter.
+     *
+     * Note: When a RenderNode is using a compositing layer it will also result in
+     * clipToBounds=true behavior.
+     *
+     * @param forceToLayer if true this forces the RenderNode to use an intermediate buffer.
+     *                     Default & generally recommended value is false.
+     * @param paint        The blend mode, alpha, and ColorFilter to apply to the compositing layer.
+     *                     Only applies if forceToLayer is true.
+     * @return true if anything changed, false otherwise
+     */
+    public boolean setUseCompositingLayer(boolean forceToLayer, @Nullable Paint paint) {
+        boolean didChange = nSetLayerType(mNativeRenderNode, forceToLayer ? 2 : 0);
+        didChange |= nSetLayerPaint(mNativeRenderNode,
+                paint != null ? paint.getNativeInstance() : 0);
+        return didChange;
+    }
+
+    /**
      * Sets the clip bounds of the RenderNode.
+     *
      * @param rect the bounds to clip to. If null, the clip bounds are reset
      * @return True if the clip bounds changed, false otherwise
      */
@@ -358,26 +466,24 @@
      *
      * @param clipToBounds true if the display list should clip to its bounds
      */
-    @UnsupportedAppUsage
     public boolean setClipToBounds(boolean clipToBounds) {
         return nSetClipToBounds(mNativeRenderNode, clipToBounds);
     }
 
     /**
-     * Sets whether the display list should be drawn immediately after the
-     * closest ancestor display list containing a projection receiver.
+     * Sets whether the RenderNode should be drawn immediately after the
+     * closest ancestor RenderNode containing a projection receiver.
      *
      * @param shouldProject true if the display list should be projected onto a
-     *            containing volume.
+     *                      containing volume.
      */
-    @UnsupportedAppUsage
     public boolean setProjectBackwards(boolean shouldProject) {
         return nSetProjectBackwards(mNativeRenderNode, shouldProject);
     }
 
     /**
-     * Sets whether the display list is a projection receiver - that its parent
-     * DisplayList should draw any descendent DisplayLists with
+     * Sets whether the RenderNode is a projection receiver - that its parent
+     * RenderNode should draw any descendent RenderNodes with
      * ProjectBackwards=true directly on top of it. Default value is false.
      */
     public boolean setProjectionReceiver(boolean shouldRecieve) {
@@ -388,14 +494,18 @@
      * Sets the outline, defining the shape that casts a shadow, and the path to
      * be clipped if setClipToOutline is set.
      *
-     * Deep copies the data into native to simplify reference ownership.
+     * This will make a copy of the provided {@link Outline}, so any future modifications
+     * to the outline will need to call {@link #setOutline(Outline)} with the modified
+     * outline for those changes to be applied.
+     *
+     * @param outline The outline to use for this RenderNode.
      */
     public boolean setOutline(@Nullable Outline outline) {
         if (outline == null) {
             return nSetOutlineNone(mNativeRenderNode);
         }
 
-        switch(outline.mMode) {
+        switch (outline.mMode) {
             case Outline.MODE_EMPTY:
                 return nSetOutlineEmpty(mNativeRenderNode);
             case Outline.MODE_ROUND_RECT:
@@ -412,28 +522,63 @@
     }
 
     /**
+     * Checks if the RenderNode has a shadow. That is, if the combination of {@link #getElevation()}
+     * and {@link #getTranslationZ()} is greater than zero, there is an {@link Outline} set with
+     * a valid shadow caster path, and the provided outline has a non-zero
+     * {@link Outline#getAlpha()}.
+     *
      * @return True if this RenderNode has a shadow, false otherwise
      */
     public boolean hasShadow() {
         return nHasShadow(mNativeRenderNode);
     }
 
-    /** setSpotShadowColor */
+    /**
+     * Sets the color of the spot shadow that is drawn when the RenderNode has a positive Z or
+     * elevation value and is drawn inside of a {@link Canvas#enableZ()} section.
+     * <p>
+     * By default the shadow color is black. Generally, this color will be opaque so the intensity
+     * of the shadow is consistent between different RenderNodes with different colors.
+     * <p>
+     * The opacity of the final spot shadow is a function of the shadow caster height, the
+     * alpha channel of the outlineSpotShadowColor (typically opaque), and the
+     * {@link android.R.attr#spotShadowAlpha} theme attribute
+     *
+     * @param color The color this RenderNode will cast for its elevation spot shadow.
+     */
     public boolean setSpotShadowColor(int color) {
         return nSetSpotShadowColor(mNativeRenderNode, color);
     }
 
-    /** setAmbientShadowColor */
-    public boolean setAmbientShadowColor(int color) {
-        return nSetAmbientShadowColor(mNativeRenderNode, color);
-    }
-
-    /** getSpotShadowColor */
+    /**
+     * @return The shadow color set by {@link #setSpotShadowColor(int)}, or black if nothing
+     * was set
+     */
     public int getSpotShadowColor() {
         return nGetSpotShadowColor(mNativeRenderNode);
     }
 
-    /** getAmbientShadowColor */
+    /**
+     * Sets the color of the ambient shadow that is drawn when the RenderNode has a positive Z or
+     * elevation value and is drawn inside of a {@link Canvas#enableZ()} section.
+     * <p>
+     * By default the shadow color is black. Generally, this color will be opaque so the intensity
+     * of the shadow is consistent between different RenderNodes with different colors.
+     * <p>
+     * The opacity of the final ambient shadow is a function of the shadow caster height, the
+     * alpha channel of the outlineAmbientShadowColor (typically opaque), and the
+     * {@link android.R.attr#ambientShadowAlpha} theme attribute.
+     *
+     * @param color The color this RenderNode will cast for its elevation shadow.
+     */
+    public boolean setAmbientShadowColor(int color) {
+        return nSetAmbientShadowColor(mNativeRenderNode, color);
+    }
+
+    /**
+     * @return The shadow color set by {@link #setAmbientShadowColor(int)}, or black if
+     * nothing was set
+     */
     public int getAmbientShadowColor() {
         return nGetAmbientShadowColor(mNativeRenderNode);
     }
@@ -458,6 +603,8 @@
 
     /**
      * Controls the RenderNode's circular reveal clip.
+     *
+     * @hide
      */
     public boolean setRevealClip(boolean shouldClip,
             float x, float y, float radius) {
@@ -469,6 +616,7 @@
      * transforms (such as {@link #setScaleX(float)}, {@link #setRotation(float)}, etc.)
      *
      * @param matrix A transform matrix to apply to this display list
+     * @hide TODO Do we want this?
      */
     public boolean setStaticMatrix(Matrix matrix) {
         return nSetStaticMatrix(mNativeRenderNode, matrix.native_instance);
@@ -481,6 +629,7 @@
      * for the matrix parameter.
      *
      * @param matrix The matrix, null indicates that the matrix should be cleared.
+     * @hide TODO Do we want this?
      */
     public boolean setAnimationMatrix(Matrix matrix) {
         return nSetAnimationMatrix(mNativeRenderNode,
@@ -491,7 +640,6 @@
      * Sets the translucency level for the display list.
      *
      * @param alpha The translucency of the display list, must be a value between 0.0f and 1.0f
-     *
      * @see View#setAlpha(float)
      * @see #getAlpha()
      */
@@ -503,7 +651,6 @@
      * Returns the translucency level of this display list.
      *
      * @return A value between 0.0f and 1.0f
-     *
      * @see #setAlpha(float)
      */
     public float getAlpha() {
@@ -517,11 +664,9 @@
      *
      * @param hasOverlappingRendering False if the content is guaranteed to be non-overlapping,
      *                                true otherwise.
-     *
      * @see android.view.View#hasOverlappingRendering()
      * @see #hasOverlappingRendering()
      */
-    @UnsupportedAppUsage
     public boolean setHasOverlappingRendering(boolean hasOverlappingRendering) {
         return nSetHasOverlappingRendering(mNativeRenderNode, hasOverlappingRendering);
     }
@@ -529,17 +674,28 @@
     /** @hide */
     @IntDef({USAGE_BACKGROUND})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface UsageHint {}
+    public @interface UsageHint {
+    }
 
-    /** The default usage hint */
+    /**
+     * The default usage hint
+     *
+     * @hide
+     */
     public static final int USAGE_UNKNOWN = 0;
 
-    /** Usage is background content */
+    /**
+     * Usage is background content
+     *
+     * @hide
+     */
     public static final int USAGE_BACKGROUND = 1;
 
     /**
      * Provides a hint on what this RenderNode's display list content contains. This hint is used
      * for automatic content transforms to improve accessibility or similar.
+     *
+     * @hide
      */
     public void setUsageHint(@UsageHint int usageHint) {
         nSetUsageHint(mNativeRenderNode, usageHint);
@@ -549,7 +705,6 @@
      * Indicates whether the content of this display list overlaps.
      *
      * @return True if this display list renders content which overlaps, false otherwise.
-     *
      * @see #setHasOverlappingRendering(boolean)
      */
     public boolean hasOverlappingRendering() {
@@ -579,7 +734,6 @@
      * Sets the translation value for the display list on the X axis.
      *
      * @param translationX The X axis translation value of the display list, in pixels
-     *
      * @see View#setTranslationX(float)
      * @see #getTranslationX()
      */
@@ -600,7 +754,6 @@
      * Sets the translation value for the display list on the Y axis.
      *
      * @param translationY The Y axis translation value of the display list, in pixels
-     *
      * @see View#setTranslationY(float)
      * @see #getTranslationY()
      */
@@ -640,7 +793,6 @@
      * Sets the rotation value for the display list around the Z axis.
      *
      * @param rotation The rotation value of the display list, in degrees
-     *
      * @see View#setRotation(float)
      * @see #getRotation()
      */
@@ -661,7 +813,6 @@
      * Sets the rotation value for the display list around the X axis.
      *
      * @param rotationX The rotation value of the display list, in degrees
-     *
      * @see View#setRotationX(float)
      * @see #getRotationX()
      */
@@ -682,7 +833,6 @@
      * Sets the rotation value for the display list around the Y axis.
      *
      * @param rotationY The rotation value of the display list, in degrees
-     *
      * @see View#setRotationY(float)
      * @see #getRotationY()
      */
@@ -703,7 +853,6 @@
      * Sets the scale value for the display list on the X axis.
      *
      * @param scaleX The scale value of the display list
-     *
      * @see View#setScaleX(float)
      * @see #getScaleX()
      */
@@ -724,7 +873,6 @@
      * Sets the scale value for the display list on the Y axis.
      *
      * @param scaleY The scale value of the display list
-     *
      * @see View#setScaleY(float)
      * @see #getScaleY()
      */
@@ -745,7 +893,6 @@
      * Sets the pivot value for the display list on the X axis
      *
      * @param pivotX The pivot value of the display list on the X axis, in pixels
-     *
      * @see View#setPivotX(float)
      * @see #getPivotX()
      */
@@ -766,7 +913,6 @@
      * Sets the pivot value for the display list on the Y axis
      *
      * @param pivotY The pivot value of the display list on the Y axis, in pixels
-     *
      * @see View#setPivotY(float)
      * @see #getPivotY()
      */
@@ -783,138 +929,222 @@
         return nGetPivotY(mNativeRenderNode);
     }
 
+    /**
+     * @return Whether or not a pivot was explicitly set with {@link #setPivotX(float)} or
+     * {@link #setPivotY(float)}. If no pivot has been set then the pivot will be the center
+     * of the RenderNode.
+     */
     public boolean isPivotExplicitlySet() {
         return nIsPivotExplicitlySet(mNativeRenderNode);
     }
 
-    /** lint */
+    /**
+     * Clears any pivot previously set by a call to  {@link #setPivotX(float)} or
+     * {@link #setPivotY(float)}. After calling this {@link #isPivotExplicitlySet()} will be false
+     * and the pivot used for rotation will return to default of being centered on the view.
+     */
     public boolean resetPivot() {
         return nResetPivot(mNativeRenderNode);
     }
 
     /**
-     * Sets the camera distance for the display list. Refer to
-     * {@link View#setCameraDistance(float)} for more information on how to
-     * use this property.
+     * <p>Sets the distance along the Z axis (orthogonal to the X/Y plane on which
+     * RenderNodes are drawn) from the camera to this RenderNode. The camera's distance
+     * affects 3D transformations, for instance rotations around the X and Y
+     * axis. If the rotationX or rotationY properties are changed and this view is
+     * large (more than half the size of the screen), it is recommended to always
+     * use a camera distance that's greater than the height (X axis rotation) or
+     * the width (Y axis rotation) of this view.</p>
      *
-     * @param distance The distance in Z of the camera of the display list
+     * <p>The distance of the camera from the drawing plane can have an affect on the
+     * perspective distortion of the RenderNode when it is rotated around the x or y axis.
+     * For example, a large distance will result in a large viewing angle, and there
+     * will not be much perspective distortion of the view as it rotates. A short
+     * distance may cause much more perspective distortion upon rotation, and can
+     * also result in some drawing artifacts if the rotated view ends up partially
+     * behind the camera (which is why the recommendation is to use a distance at
+     * least as far as the size of the view, if the view is to be rotated.)</p>
      *
-     * @see View#setCameraDistance(float)
-     * @see #getCameraDistance()
+     * <p>The distance is expressed in pixels and must always be positive</p>
+     *
+     * @param distance The distance in pixels, must always be positive
+     * @see #setRotationX(float)
+     * @see #setRotationY(float)
      */
     public boolean setCameraDistance(float distance) {
-        return nSetCameraDistance(mNativeRenderNode, distance);
+        if (!Float.isFinite(distance) || distance < 0.0f) {
+            throw new IllegalArgumentException("distance must be finite & positive, given="
+                    + distance);
+        }
+        // Native actually wants this to be negative not positive, so we flip it.
+        return nSetCameraDistance(mNativeRenderNode, -distance);
     }
 
     /**
-     * Returns the distance in Z of the camera of the display list.
+     * Returns the distance in Z of the camera for this RenderNode
      *
+     * @return the distance along the Z axis in pixels.
      * @see #setCameraDistance(float)
      */
     public float getCameraDistance() {
-        return nGetCameraDistance(mNativeRenderNode);
+        return -nGetCameraDistance(mNativeRenderNode);
     }
 
     /**
-     * Sets the left position for the display list.
+     * Sets the left position for the RenderNode.
      *
-     * @param left The left position, in pixels, of the display list
-     *
-     * @see View#setLeft(int)
+     * @param left The left position, in pixels, of the RenderNode
+     * @return true if the value changed, false otherwise
      */
     public boolean setLeft(int left) {
         return nSetLeft(mNativeRenderNode, left);
     }
 
     /**
-     * Sets the top position for the display list.
+     * Sets the top position for the RenderNode.
      *
-     * @param top The top position, in pixels, of the display list
-     *
-     * @see View#setTop(int)
+     * @param top The top position, in pixels, of the RenderNode
+     * @return true if the value changed, false otherwise.
      */
     public boolean setTop(int top) {
         return nSetTop(mNativeRenderNode, top);
     }
 
     /**
-     * Sets the right position for the display list.
+     * Sets the right position for the RenderNode.
      *
-     * @param right The right position, in pixels, of the display list
-     *
-     * @see View#setRight(int)
+     * @param right The right position, in pixels, of the RenderNode
+     * @return true if the value changed, false otherwise.
      */
     public boolean setRight(int right) {
         return nSetRight(mNativeRenderNode, right);
     }
 
     /**
-     * Sets the bottom position for the display list.
+     * Sets the bottom position for the RenderNode.
      *
-     * @param bottom The bottom position, in pixels, of the display list
-     *
-     * @see View#setBottom(int)
+     * @param bottom The bottom position, in pixels, of the RenderNode
+     * @return true if the value changed, false otherwise.
      */
     public boolean setBottom(int bottom) {
         return nSetBottom(mNativeRenderNode, bottom);
     }
 
     /**
-     * Sets the left and top positions for the display list
+     * Gets the left position for the RenderNode.
      *
-     * @param left The left position of the display list, in pixels
-     * @param top The top position of the display list, in pixels
-     * @param right The right position of the display list, in pixels
-     * @param bottom The bottom position of the display list, in pixels
+     * See {@link #setLeft(int)}
      *
-     * @see View#setLeft(int)
-     * @see View#setTop(int)
-     * @see View#setRight(int)
-     * @see View#setBottom(int)
+     * @return the left position in pixels
      */
-    @UnsupportedAppUsage
+    public int getLeft() {
+        return nGetLeft(mNativeRenderNode);
+    }
+
+    /**
+     * Gets the top position for the RenderNode.
+     *
+     * See {@link #setTop(int)}
+     *
+     * @return the top position in pixels
+     */
+    public int getTop() {
+        return nGetTop(mNativeRenderNode);
+    }
+
+    /**
+     * Gets the right position for the RenderNode.
+     *
+     * See {@link #setRight(int)}
+     *
+     * @return the right position in pixels
+     */
+    public int getRight() {
+        return nGetRight(mNativeRenderNode);
+    }
+
+    /**
+     * Gets the bottom position for the RenderNode.
+     *
+     * See {@link #setBottom(int)}
+     *
+     * @return the bottom position in pixels
+     */
+    public int getBottom() {
+        return nGetBottom(mNativeRenderNode);
+    }
+
+    /**
+     * Gets the width of the RenderNode, which is the right - left.
+     *
+     * @return the width of the RenderNode
+     */
+    public int getWidth() {
+        return nGetWidth(mNativeRenderNode);
+    }
+
+    /**
+     * Gets the height of the RenderNode, which is the bottom - top.
+     *
+     * @return the height of the RenderNode
+     */
+    public int getHeight() {
+        return nGetHeight(mNativeRenderNode);
+    }
+
+    /**
+     * Sets the left, top, right, and bottom of the RenderNode.
+     *
+     * @param left   The left position of the RenderNode, in pixels
+     * @param top    The top position of the RenderNode, in pixels
+     * @param right  The right position of the RenderNode, in pixels
+     * @param bottom The bottom position of the RenderNode, in pixels
+     * @return true if any values changed, false otherwise.
+     * @see #setLeft(int)
+     * @see #setTop(int)
+     * @see #setRight(int)
+     * @see #setBottom(int)
+     */
     public boolean setLeftTopRightBottom(int left, int top, int right, int bottom) {
         return nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom);
     }
 
     /**
-     * Offsets the left and right positions for the display list
+     * Offsets the left and right positions for the RenderNode
      *
-     * @param offset The amount that the left and right positions of the display
-     *               list are offset, in pixels
-     *
-     * @see View#offsetLeftAndRight(int)
+     * @param offset The amount that the left and right positions are offset in pixels
+     * @return true if any values changed, false otherwise.
      */
-    @UnsupportedAppUsage
     public boolean offsetLeftAndRight(int offset) {
         return nOffsetLeftAndRight(mNativeRenderNode, offset);
     }
 
     /**
-     * Offsets the top and bottom values for the display list
+     * Offsets the top and bottom values for the RenderNode
      *
-     * @param offset The amount that the top and bottom positions of the display
-     *               list are offset, in pixels
-     *
-     * @see View#offsetTopAndBottom(int)
+     * @param offset The amount that the left and right positions are offset in pixels
+     * @return true if any values changed, false otherwise.
      */
     public boolean offsetTopAndBottom(int offset) {
         return nOffsetTopAndBottom(mNativeRenderNode, offset);
     }
 
     /**
-     * Outputs the display list to the log. This method exists for use by
+     * Outputs the RenderNode to the log. This method exists for use by
      * tools to output display lists for selected nodes to the log.
+     *
+     * @hide TODO: Expose? Should the shape of this be different than forced dump to logcat?
      */
-    @UnsupportedAppUsage
     public void output() {
         nOutput(mNativeRenderNode);
     }
 
     /**
-     * Gets the size of the DisplayList for debug purposes.
+     * Gets the approximate memory usage of the RenderNode for debug purposes. Does not include
+     * the memory usage of any child RenderNodes nor any bitmaps, only the memory usage of
+     * this RenderNode and any data it owns.
      */
-    public int getDebugSize() {
+    public int computeApproximateMemoryUsage() {
         return nGetDebugSize(mNativeRenderNode);
     }
 
@@ -954,13 +1184,16 @@
      * For now this interface exists to de-couple RenderNode from anything View-specific in a
      * bit of a kludge.
      *
-     * @hide */
+     * @hide
+     */
     public interface AnimationHost {
-        /** checkstyle */
+        /** @hide */
         void registerAnimatingRenderNode(RenderNode animator);
-        /** checkstyle */
+
+        /** @hide */
         void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator);
-        /** checkstyle */
+
+        /** @hide */
         boolean isAttached();
     }
 
@@ -998,14 +1231,18 @@
     private static native long nCreate(String name);
 
     private static native long nGetNativeFinalizer();
+
     private static native void nOutput(long renderNode);
+
     private static native int nGetDebugSize(long renderNode);
+
     private static native void nRequestPositionUpdates(long renderNode,
             PositionUpdateListener callback);
 
     // Animations
 
     private static native void nAddAnimator(long renderNode, long animatorPtr);
+
     private static native void nEndAllAnimators(long renderNode);
 
 
@@ -1028,8 +1265,10 @@
 
     @CriticalNative
     private static native void nGetTransformMatrix(long renderNode, long nativeMatrix);
+
     @CriticalNative
     private static native void nGetInverseTransformMatrix(long renderNode, long nativeMatrix);
+
     @CriticalNative
     private static native boolean nHasIdentityMatrix(long renderNode);
 
@@ -1037,135 +1276,208 @@
 
     @CriticalNative
     private static native boolean nOffsetTopAndBottom(long renderNode, int offset);
+
     @CriticalNative
     private static native boolean nOffsetLeftAndRight(long renderNode, int offset);
+
     @CriticalNative
     private static native boolean nSetLeftTopRightBottom(long renderNode, int left, int top,
             int right, int bottom);
-    @CriticalNative
-    private static native boolean nSetBottom(long renderNode, int bottom);
-    @CriticalNative
-    private static native boolean nSetRight(long renderNode, int right);
-    @CriticalNative
-    private static native boolean nSetTop(long renderNode, int top);
+
     @CriticalNative
     private static native boolean nSetLeft(long renderNode, int left);
+
+    @CriticalNative
+    private static native boolean nSetTop(long renderNode, int top);
+
+    @CriticalNative
+    private static native boolean nSetRight(long renderNode, int right);
+
+    @CriticalNative
+    private static native boolean nSetBottom(long renderNode, int bottom);
+
+    @CriticalNative
+    private static native int nGetLeft(long renderNode);
+
+    @CriticalNative
+    private static native int nGetTop(long renderNode);
+
+    @CriticalNative
+    private static native int nGetRight(long renderNode);
+
+    @CriticalNative
+    private static native int nGetBottom(long renderNode);
+
     @CriticalNative
     private static native boolean nSetCameraDistance(long renderNode, float distance);
+
     @CriticalNative
     private static native boolean nSetPivotY(long renderNode, float pivotY);
+
     @CriticalNative
     private static native boolean nSetPivotX(long renderNode, float pivotX);
+
     @CriticalNative
     private static native boolean nResetPivot(long renderNode);
+
     @CriticalNative
     private static native boolean nSetLayerType(long renderNode, int layerType);
+
     @CriticalNative
     private static native boolean nSetLayerPaint(long renderNode, long paint);
+
     @CriticalNative
     private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds);
+
     @CriticalNative
     private static native boolean nSetClipBounds(long renderNode, int left, int top,
             int right, int bottom);
+
     @CriticalNative
     private static native boolean nSetClipBoundsEmpty(long renderNode);
+
     @CriticalNative
     private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject);
+
     @CriticalNative
     private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldRecieve);
+
     @CriticalNative
     private static native boolean nSetOutlineRoundRect(long renderNode, int left, int top,
             int right, int bottom, float radius, float alpha);
+
     @CriticalNative
     private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath,
             float alpha);
+
     @CriticalNative
     private static native boolean nSetOutlineEmpty(long renderNode);
+
     @CriticalNative
     private static native boolean nSetOutlineNone(long renderNode);
+
     @CriticalNative
     private static native boolean nHasShadow(long renderNode);
+
     @CriticalNative
     private static native boolean nSetSpotShadowColor(long renderNode, int color);
+
     @CriticalNative
     private static native boolean nSetAmbientShadowColor(long renderNode, int color);
+
     @CriticalNative
     private static native int nGetSpotShadowColor(long renderNode);
+
     @CriticalNative
     private static native int nGetAmbientShadowColor(long renderNode);
+
     @CriticalNative
     private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
+
     @CriticalNative
     private static native boolean nSetRevealClip(long renderNode,
             boolean shouldClip, float x, float y, float radius);
+
     @CriticalNative
     private static native boolean nSetAlpha(long renderNode, float alpha);
+
     @CriticalNative
     private static native boolean nSetHasOverlappingRendering(long renderNode,
             boolean hasOverlappingRendering);
+
     @CriticalNative
     private static native void nSetUsageHint(long renderNode, int usageHint);
+
     @CriticalNative
     private static native boolean nSetElevation(long renderNode, float lift);
+
     @CriticalNative
     private static native boolean nSetTranslationX(long renderNode, float translationX);
+
     @CriticalNative
     private static native boolean nSetTranslationY(long renderNode, float translationY);
+
     @CriticalNative
     private static native boolean nSetTranslationZ(long renderNode, float translationZ);
+
     @CriticalNative
     private static native boolean nSetRotation(long renderNode, float rotation);
+
     @CriticalNative
     private static native boolean nSetRotationX(long renderNode, float rotationX);
+
     @CriticalNative
     private static native boolean nSetRotationY(long renderNode, float rotationY);
+
     @CriticalNative
     private static native boolean nSetScaleX(long renderNode, float scaleX);
+
     @CriticalNative
     private static native boolean nSetScaleY(long renderNode, float scaleY);
+
     @CriticalNative
     private static native boolean nSetStaticMatrix(long renderNode, long nativeMatrix);
+
     @CriticalNative
     private static native boolean nSetAnimationMatrix(long renderNode, long animationMatrix);
 
     @CriticalNative
     private static native boolean nHasOverlappingRendering(long renderNode);
+
     @CriticalNative
     private static native boolean nGetClipToOutline(long renderNode);
+
     @CriticalNative
     private static native float nGetAlpha(long renderNode);
+
     @CriticalNative
     private static native float nGetCameraDistance(long renderNode);
+
     @CriticalNative
     private static native float nGetScaleX(long renderNode);
+
     @CriticalNative
     private static native float nGetScaleY(long renderNode);
+
     @CriticalNative
     private static native float nGetElevation(long renderNode);
+
     @CriticalNative
     private static native float nGetTranslationX(long renderNode);
+
     @CriticalNative
     private static native float nGetTranslationY(long renderNode);
+
     @CriticalNative
     private static native float nGetTranslationZ(long renderNode);
+
     @CriticalNative
     private static native float nGetRotation(long renderNode);
+
     @CriticalNative
     private static native float nGetRotationX(long renderNode);
+
     @CriticalNative
     private static native float nGetRotationY(long renderNode);
+
     @CriticalNative
     private static native boolean nIsPivotExplicitlySet(long renderNode);
+
     @CriticalNative
     private static native float nGetPivotX(long renderNode);
+
     @CriticalNative
     private static native float nGetPivotY(long renderNode);
+
     @CriticalNative
     private static native int nGetWidth(long renderNode);
+
     @CriticalNative
     private static native int nGetHeight(long renderNode);
+
     @CriticalNative
     private static native boolean nSetAllowForceDark(long renderNode, boolean allowForceDark);
+
     @CriticalNative
     private static native boolean nGetAllowForceDark(long renderNode);
 }
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index ba47300..7e8bfb3 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -27,10 +27,11 @@
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
+import android.graphics.fonts.Font;
+import android.graphics.fonts.FontFamily;
 import android.graphics.fonts.FontStyle;
 import android.graphics.fonts.FontVariationAxis;
 import android.graphics.fonts.SystemFonts;
-import android.net.Uri;
 import android.os.Build;
 import android.provider.FontRequest;
 import android.provider.FontsContract;
@@ -50,13 +51,10 @@
 
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -126,7 +124,8 @@
 
     // We cannot support sSystemFallbackMap since we will migrate to public FontFamily API.
     @UnsupportedAppUsage
-    static final Map<String, FontFamily[]> sSystemFallbackMap = Collections.emptyMap();
+    static final Map<String, android.graphics.FontFamily[]> sSystemFallbackMap =
+            Collections.emptyMap();
 
     /**
      * @hide
@@ -164,6 +163,9 @@
     private int[] mSupportedAxes;
     private static final int[] EMPTY_AXES = {};
 
+    // The underlying font families.
+    private final FontFamily[] mFamilies;
+
     @UnsupportedAppUsage
     private static void setDefault(Typeface t) {
         sDefaultTypeface = t;
@@ -192,38 +194,6 @@
 
     /**
      * @hide
-     * Used by Resources to load a font resource of type font file.
-     */
-    @Nullable
-    public static Typeface createFromResources(AssetManager mgr, String path, int cookie) {
-        synchronized (sDynamicCacheLock) {
-            final String key = Builder.createAssetUid(
-                    mgr, path, 0 /* ttcIndex */, null /* axes */,
-                    RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */,
-                    DEFAULT_FAMILY);
-            Typeface typeface = sDynamicTypefaceCache.get(key);
-            if (typeface != null) return typeface;
-
-            FontFamily fontFamily = new FontFamily();
-            // TODO: introduce ttc index and variation settings to resource type font.
-            if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */,
-                    0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
-                    RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
-                if (!fontFamily.freeze()) {
-                    return null;
-                }
-                FontFamily[] families = {fontFamily};
-                typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
-                        RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
-                sDynamicTypefaceCache.put(key, typeface);
-                return typeface;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @hide
      * Used by Resources to load a font resource of type xml.
      */
     @Nullable
@@ -258,21 +228,34 @@
         // family is FontFamilyFilesResourceEntry
         final FontFamilyFilesResourceEntry filesEntry = (FontFamilyFilesResourceEntry) entry;
 
-        FontFamily fontFamily = new FontFamily();
-        for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
-            if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
-                    0 /* resourceCookie */, false /* isAsset */, fontFile.getTtcIndex(),
-                    fontFile.getWeight(), fontFile.getItalic(),
-                    FontVariationAxis.fromFontVariationSettings(fontFile.getVariationSettings()))) {
-                return null;
+        try {
+            FontFamily.Builder familyBuilder = null;
+            for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
+                final Font.Builder fontBuilder = new Font.Builder(mgr, fontFile.getFileName(),
+                        false /* isAsset */, 0 /* cookie */)
+                        .setTtcIndex(fontFile.getTtcIndex())
+                        .setFontVariationSettings(fontFile.getVariationSettings());
+                if (fontFile.getWeight() != Typeface.RESOLVE_BY_FONT_TABLE) {
+                    fontBuilder.setWeight(fontFile.getWeight());
+                }
+                if (fontFile.getItalic() != Typeface.RESOLVE_BY_FONT_TABLE) {
+                    fontBuilder.setSlant(fontFile.getItalic() == FontFileResourceEntry.ITALIC
+                            ?  FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);
+                }
+
+                if (familyBuilder == null) {
+                    familyBuilder = new FontFamily.Builder(fontBuilder.build());
+                } else {
+                    familyBuilder.addFont(fontBuilder.build());
+                }
             }
+            if (familyBuilder == null) {
+                return Typeface.DEFAULT;
+            }
+            typeface = new Typeface.CustomFallbackBuilder(familyBuilder.build()).build();
+        } catch (IOException e) {
+            typeface = Typeface.DEFAULT;
         }
-        if (!fontFamily.freeze()) {
-            return null;
-        }
-        FontFamily[] familyChain = { fontFamily };
-        typeface = createFromFamiliesWithDefault(familyChain, DEFAULT_FAMILY,
-                RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
         synchronized (sDynamicCacheLock) {
             final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
                     null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */,
@@ -339,15 +322,11 @@
         /** @hide */
         public static final int BOLD_WEIGHT = 700;
 
-        private int mTtcIndex;
-        private FontVariationAxis[] mAxes;
+        // Kept for generating asset cache key.
+        private final AssetManager mAssetManager;
+        private final String mPath;
 
-        private AssetManager mAssetManager;
-        private String mPath;
-        private FileDescriptor mFd;
-
-        private FontsContract.FontInfo[] mFonts;
-        private Map<Uri, ByteBuffer> mFontBuffers;
+        private final Font.Builder mFontBuilder;
 
         private String mFallbackFamilyName;
 
@@ -360,7 +339,9 @@
          * @param path The file object refers to the font file.
          */
         public Builder(@NonNull File path) {
-            mPath = path.getAbsolutePath();
+            mFontBuilder = new Font.Builder(path);
+            mAssetManager = null;
+            mPath = null;
         }
 
         /**
@@ -372,7 +353,9 @@
          * @param fd The file descriptor. The passed fd must be mmap-able.
          */
         public Builder(@NonNull FileDescriptor fd) {
-            mFd = fd;
+            mFontBuilder = new Font.Builder(fd);
+            mAssetManager = null;
+            mPath = null;
         }
 
         /**
@@ -381,7 +364,9 @@
          * @param path The full path to the font file.
          */
         public Builder(@NonNull String path) {
-            mPath = path;
+            mFontBuilder = new Font.Builder(new File(path));
+            mAssetManager = null;
+            mPath = null;
         }
 
         /**
@@ -391,27 +376,22 @@
          * @param path The file name of the font data in the asset directory
          */
         public Builder(@NonNull AssetManager assetManager, @NonNull String path) {
-            mAssetManager = Preconditions.checkNotNull(assetManager);
-            mPath = Preconditions.checkStringNotEmpty(path);
+            this(assetManager, path, true /* is asset */, 0 /* cookie */);
         }
 
         /**
-         * Constracts a builder from an array of FontsContract.FontInfo.
+         * Constructs a builder from an asset manager and a file path in an asset directory.
          *
-         * Since {@link FontsContract.FontInfo} holds information about TTC indices and
-         * variation settings, there is no need to call {@link #setTtcIndex} or
-         * {@link #setFontVariationSettings}. Similary, {@link FontsContract.FontInfo} holds
-         * weight and italic information, so {@link #setWeight} and {@link #setItalic} are used
-         * for style matching during font selection.
-         *
-         * @param fonts The array of {@link FontsContract.FontInfo}
-         * @param buffers The mapping from URI to buffers to be used during building.
+         * @param assetManager The application's asset manager
+         * @param path The file name of the font data in the asset directory
+         * @param cookie a cookie for the asset
          * @hide
          */
-        public Builder(@NonNull FontsContract.FontInfo[] fonts,
-                @NonNull Map<Uri, ByteBuffer> buffers) {
-            mFonts = fonts;
-            mFontBuffers = buffers;
+        public Builder(@NonNull AssetManager assetManager, @NonNull String path, boolean isAsset,
+                int cookie) {
+            mFontBuilder = new Font.Builder(assetManager, path, isAsset, cookie);
+            mAssetManager = assetManager;
+            mPath = path;
         }
 
         /**
@@ -423,6 +403,7 @@
          */
         public Builder setWeight(@IntRange(from = 1, to = 1000) int weight) {
             mWeight = weight;
+            mFontBuilder.setWeight(weight);
             return this;
         }
 
@@ -434,7 +415,8 @@
          * @param italic {@code true} if the font is italic. Otherwise {@code false}.
          */
         public Builder setItalic(boolean italic) {
-            mItalic = italic ? STYLE_ITALIC : STYLE_NORMAL;
+            mItalic = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
+            mFontBuilder.setSlant(mItalic);
             return this;
         }
 
@@ -446,11 +428,7 @@
          *                 collection, do not call this method or specify 0.
          */
         public Builder setTtcIndex(@IntRange(from = 0) int ttcIndex) {
-            if (mFonts != null) {
-                throw new IllegalArgumentException(
-                        "TTC index can not be specified for FontResult source.");
-            }
-            mTtcIndex = ttcIndex;
+            mFontBuilder.setTtcIndex(ttcIndex);
             return this;
         }
 
@@ -462,14 +440,7 @@
          *                                  format.
          */
         public Builder setFontVariationSettings(@Nullable String variationSettings) {
-            if (mFonts != null) {
-                throw new IllegalArgumentException(
-                        "Font variation settings can not be specified for FontResult source.");
-            }
-            if (mAxes != null) {
-                throw new IllegalStateException("Font variation settings are already set.");
-            }
-            mAxes = FontVariationAxis.fromFontVariationSettings(variationSettings);
+            mFontBuilder.setFontVariationSettings(variationSettings);
             return this;
         }
 
@@ -479,14 +450,7 @@
          * @param axes An array of font variation axis tag-value pairs.
          */
         public Builder setFontVariationSettings(@Nullable FontVariationAxis[] axes) {
-            if (mFonts != null) {
-                throw new IllegalArgumentException(
-                        "Font variation settings can not be specified for FontResult source.");
-            }
-            if (mAxes != null) {
-                throw new IllegalStateException("Font variation settings are already set.");
-            }
-            mAxes = axes;
+            mFontBuilder.setFontVariationSettings(axes);
             return this;
         }
 
@@ -579,97 +543,55 @@
          * @return Newly created Typeface. May return null if some parameters are invalid.
          */
         public Typeface build() {
-            if (mFd != null) {  // Builder is created with file descriptor.
-                try (FileInputStream fis = new FileInputStream(mFd)) {
-                    FileChannel channel = fis.getChannel();
-                    long size = channel.size();
-                    ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
-
-                    final FontFamily fontFamily = new FontFamily();
-                    if (!fontFamily.addFontFromBuffer(buffer, mTtcIndex, mAxes, mWeight, mItalic)) {
-                        fontFamily.abortCreation();
-                        return resolveFallbackTypeface();
-                    }
-                    if (!fontFamily.freeze()) {
-                        return resolveFallbackTypeface();
-                    }
-                    FontFamily[] families = { fontFamily };
-                    return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
-                            mItalic);
-                } catch (IOException e) {
-                    return resolveFallbackTypeface();
-                }
-            } else if (mAssetManager != null) {  // Builder is created with asset manager.
-                final String key = createAssetUid(
-                        mAssetManager, mPath, mTtcIndex, mAxes, mWeight, mItalic,
+            try {
+                final Font font = mFontBuilder.build();
+                final String key = mAssetManager == null ? null : createAssetUid(
+                        mAssetManager, mPath, font.getTtcIndex(), font.getAxes(),
+                        font.getStyle().getWeight(), font.getStyle().getSlant(),
                         mFallbackFamilyName);
-                synchronized (sDynamicCacheLock) {
-                    Typeface typeface = sDynamicTypefaceCache.get(key);
-                    if (typeface != null) return typeface;
-                    final FontFamily fontFamily = new FontFamily();
-                    if (!fontFamily.addFontFromAssetManager(mAssetManager, mPath, mTtcIndex,
-                            true /* isAsset */, mTtcIndex, mWeight, mItalic, mAxes)) {
-                        fontFamily.abortCreation();
-                        return resolveFallbackTypeface();
+                if (key != null) {
+                    // Dynamic cache lookup is only for assets.
+                    synchronized (sDynamicCacheLock) {
+                        final Typeface typeface = sDynamicTypefaceCache.get(key);
+                        if (typeface != null) {
+                            return typeface;
+                        }
                     }
-                    if (!fontFamily.freeze()) {
-                        return resolveFallbackTypeface();
+                }
+                final FontFamily family = new FontFamily.Builder(font).build();
+                final int weight = mWeight == RESOLVE_BY_FONT_TABLE
+                        ? font.getStyle().getWeight() : mWeight;
+                final int slant = mItalic == RESOLVE_BY_FONT_TABLE
+                        ? font.getStyle().getSlant() : mItalic;
+                final CustomFallbackBuilder builder = new CustomFallbackBuilder(family)
+                        .setStyle(new FontStyle(weight, slant));
+                if (mFallbackFamilyName != null) {
+                    builder.setSystemFallback(mFallbackFamilyName);
+                }
+                final Typeface typeface = builder.build();
+                if (key != null) {
+                    synchronized (sDynamicCacheLock) {
+                        sDynamicTypefaceCache.put(key, typeface);
                     }
-                    FontFamily[] families = { fontFamily };
-                    typeface = createFromFamiliesWithDefault(families, mFallbackFamilyName,
-                            mWeight, mItalic);
-                    sDynamicTypefaceCache.put(key, typeface);
-                    return typeface;
                 }
-            } else if (mPath != null) {  // Builder is created with file path.
-                final FontFamily fontFamily = new FontFamily();
-                if (!fontFamily.addFont(mPath, mTtcIndex, mAxes, mWeight, mItalic)) {
-                    fontFamily.abortCreation();
-                    return resolveFallbackTypeface();
-                }
-                if (!fontFamily.freeze()) {
-                    return resolveFallbackTypeface();
-                }
-                FontFamily[] families = { fontFamily };
-                return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
-                        mItalic);
-            } else if (mFonts != null) {
-                final FontFamily fontFamily = new FontFamily();
-                boolean atLeastOneFont = false;
-                for (FontsContract.FontInfo font : mFonts) {
-                    final ByteBuffer fontBuffer = mFontBuffers.get(font.getUri());
-                    if (fontBuffer == null) {
-                        continue;  // skip
-                    }
-                    final boolean success = fontFamily.addFontFromBuffer(fontBuffer,
-                            font.getTtcIndex(), font.getAxes(), font.getWeight(),
-                            font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL);
-                    if (!success) {
-                        fontFamily.abortCreation();
-                        return null;
-                    }
-                    atLeastOneFont = true;
-                }
-                if (!atLeastOneFont) {
-                    // No fonts are avaialble. No need to create new Typeface and returns fallback
-                    // Typeface instead.
-                    fontFamily.abortCreation();
-                    return null;
-                }
-                fontFamily.freeze();
-                FontFamily[] families = { fontFamily };
-                return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
-                        mItalic);
+                return typeface;
+            } catch (IOException | IllegalArgumentException e) {
+                return resolveFallbackTypeface();
             }
-
-            // Must not reach here.
-            throw new IllegalArgumentException("No source was set.");
         }
     }
 
     /**
      * A builder class for creating new Typeface instance.
      *
+     * There are two font fallback mechanisms, custom font fallback and system font fallback.
+     * The custom font fallback is a simple ordered list. The text renderer tries to see if it can
+     * render a character with the first font and if that font does not support the character, try
+     * next one and so on. It will keep trying until end of the custom fallback chain. The maximum
+     * length of the custom fallback chain is 64.
+     * The system font fallback is a system pre-defined fallback chain. The system fallback is
+     * processed only when no matching font is found in the custom font fallback.
+     *
      * <p>
      * Examples,
      * 1) Create Typeface from single ttf file.
@@ -703,72 +625,85 @@
      * Font font = new Font.Builder("your_font_file.ttf").build();
      * FontFamily family = new FontFamily.Builder(font).build();
      * Typeface typeface = new Typeface.CustomFallbackBuilder(family)
-     *     .setFallback("serif")  // Set serif font family as the fallback.
+     *     .setSystemFallback("serif")  // Set serif font family as the fallback.
+     *     .build();
+     * </code>
+     * </pre>
+     * 4) Create Typeface from single ttf file and set another ttf file for the fallback.
+     * <pre>
+     * <code>
+     * Font font = new Font.Builder("English.ttf").build();
+     * FontFamily family = new FontFamily.Builder(font).build();
+     *
+     * Font fallbackFont = new Font.Builder("Arabic.ttf").build();
+     * FontFamily fallbackFamily = new FontFamily.Builder(fallbackFont).build();
+     * Typeface typeface = new Typeface.CustomFallbackBuilder(family)
+     *     .addCustomFallback(fallbackFamily)  // Specify fallback family.
+     *     .setSystemFallback("serif")  // Set serif font family as the fallback.
      *     .build();
      * </code>
      * </pre>
      * </p>
      */
     public static class CustomFallbackBuilder {
-        // TODO: Remove package modifier once android.graphics.FontFamily is deprecated.
-        private final android.graphics.fonts.FontFamily mFamily;
+        private static final int MAX_CUSTOM_FALLBACK = 64;
+        private final ArrayList<FontFamily> mFamilies = new ArrayList<>();
         private String mFallbackName = null;
-        private @IntRange(from = 0, to = 1000) int mWeight = 400;
-        private boolean mItalic = false;
+        private @Nullable FontStyle mStyle;
 
         /**
          * Constructs a builder with a font family.
          *
          * @param family a family object
          */
-        // TODO: Remove package modifier once android.graphics.FontFamily is deprecated.
-        public CustomFallbackBuilder(@NonNull android.graphics.fonts.FontFamily family) {
+        public CustomFallbackBuilder(@NonNull FontFamily family) {
             Preconditions.checkNotNull(family);
-            mFamily = family;
+            mFamilies.add(family);
         }
 
         /**
          * Sets a system fallback by name.
          *
+         * For more information about fallback, see class description.
+         *
          * @param familyName a family name to be used for fallback if the provided fonts can not be
          *                   used
          */
-        public CustomFallbackBuilder setFallback(@NonNull String familyName) {
+        public CustomFallbackBuilder setSystemFallback(@NonNull String familyName) {
             Preconditions.checkNotNull(familyName);
             mFallbackName = familyName;
             return this;
         }
 
         /**
-         * Sets a weight of the Typeface.
+         * Sets a font style of the Typeface.
          *
-         * If the font family doesn't have a font of given weight, system will select the closest
+         * If the font family doesn't have a font of given style, system will select the closest
          * font from font family. For example, if a font family has fonts of 300 weight and 700
          * weight then setWeight(400) is called, system will select the font of 300 weight.
          *
-         * @see Font#FONT_WEIGHT_THIN
-         * @see Font#FONT_WEIGHT_EXTRA_LIGHT
-         * @see Font#FONT_WEIGHT_LIGHT
-         * @see Font#FONT_WEIGHT_NORMAL
-         * @see Font#FONT_WEIGHT_MEDIUM
-         * @see Font#FONT_WEIGHT_SEMI_BOLD
-         * @see Font#FONT_WEIGHT_BOLD
-         * @see Font#FONT_WEIGHT_EXTRA_BOLD
-         * @see Font#FONT_WEIGHT_BLACK
-         * @param weight a weight value
+         * @param style a font style
          */
-        public CustomFallbackBuilder setWeight(@IntRange(from = 0, to = 1000) int weight) {
-            mWeight = weight;
+        public CustomFallbackBuilder setStyle(@NonNull FontStyle style) {
+            mStyle = style;
             return this;
         }
 
         /**
-         * Sets a italic style of the Typeface.
+         * Append a font family to the end of the custom font fallback.
          *
-         * @param italic true if italic, otherwise false
+         * You can set up to 64 custom fallback families including the first font family you passed
+         * to the constructor.
+         * For more information about fallback, see class description.
+         *
+         * @param family a fallback family
+         * @throws IllegalArgumentException if you give more than 64 custom fallback families
          */
-        public CustomFallbackBuilder setItalic(boolean italic) {
-            mItalic = italic;
+        public CustomFallbackBuilder addCustomFallback(@NonNull FontFamily family) {
+            Preconditions.checkNotNull(family);
+            Preconditions.checkArgument(mFamilies.size() < MAX_CUSTOM_FALLBACK,
+                    "Custom fallback limit exceeded(" + MAX_CUSTOM_FALLBACK + ")");
+            mFamilies.add(family);
             return this;
         }
 
@@ -778,14 +713,23 @@
          * @return the Typeface object
          */
         public Typeface build() {
-            final android.graphics.fonts.FontFamily[] fallback =
-                    SystemFonts.getSystemFallback(mFallbackName);
-            final long[] ptrArray = new long[fallback.length + 1];
-            ptrArray[0] = mFamily.getNativePtr();
-            for (int i = 0; i < fallback.length; ++i) {
-                ptrArray[i + 1] = fallback[i].getNativePtr();
+            final int userFallbackSize = mFamilies.size();
+            final FontFamily[] fallback = SystemFonts.getSystemFallback(mFallbackName);
+            final FontFamily[] fullFamilies = new FontFamily[fallback.length + userFallbackSize];
+            final long[] ptrArray = new long[fallback.length + userFallbackSize];
+            for (int i = 0; i < userFallbackSize; ++i) {
+                ptrArray[i] = mFamilies.get(i).getNativePtr();
+                fullFamilies[i] = mFamilies.get(i);
             }
-            return new Typeface(nativeCreateFromArray(ptrArray, mWeight, mItalic ? 1 : 0));
+            for (int i = 0; i < fallback.length; ++i) {
+                ptrArray[i + userFallbackSize] = fallback[i].getNativePtr();
+                fullFamilies[i + userFallbackSize] = fallback[i];
+            }
+            final int weight = mStyle == null ? 400 : mStyle.getWeight();
+            final int italic =
+                    (mStyle == null || mStyle.getSlant() == FontStyle.FONT_SLANT_UPRIGHT) ?  0 : 1;
+
+            return new Typeface(nativeCreateFromArray(ptrArray, weight, italic), fullFamilies);
         }
     }
 
@@ -850,7 +794,7 @@
                 }
             }
 
-            typeface = new Typeface(nativeCreateFromTypeface(ni, style));
+            typeface = new Typeface(nativeCreateFromTypeface(ni, style), family.mFamilies);
             styles.put(style, typeface);
         }
         return typeface;
@@ -919,7 +863,7 @@
 
             typeface = new Typeface(
                     nativeCreateFromTypefaceWithExactStyle(
-                            base.native_instance, weight, italic));
+                            base.native_instance, weight, italic), base.mFamilies);
             innerCache.put(key, typeface);
         }
         return typeface;
@@ -928,8 +872,9 @@
     /** @hide */
     public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
             @NonNull List<FontVariationAxis> axes) {
-        final long ni = family == null ? 0 : family.native_instance;
-        return new Typeface(nativeCreateFromTypefaceWithVariation(ni, axes));
+        final Typeface base = family == null ? Typeface.DEFAULT : family;
+        return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
+                base.mFamilies);
     }
 
     /**
@@ -1015,7 +960,7 @@
      */
     @Deprecated
     @UnsupportedAppUsage
-    private static Typeface createFromFamilies(FontFamily[] families) {
+    private static Typeface createFromFamilies(android.graphics.FontFamily[] families) {
         long[] ptrArray = new long[families.length];
         for (int i = 0; i < families.length; i++) {
             ptrArray[i] = families[i].mNativePtr;
@@ -1029,14 +974,13 @@
      *
      * @param families array of font families
      */
-    private static Typeface createFromFamilies(
-            @Nullable android.graphics.fonts.FontFamily[] families) {
+    private static Typeface createFromFamilies(@Nullable FontFamily[] families) {
         final long[] ptrArray = new long[families.length];
         for (int i = 0; i < families.length; ++i) {
             ptrArray[i] = families[i].getNativePtr();
         }
         return new Typeface(nativeCreateFromArray(ptrArray,
-                  RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+                  RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE), families);
     }
 
     /**
@@ -1044,8 +988,8 @@
      * TODO: Remove private API use in supportlib: http://b/72665240
      */
     @UnsupportedAppUsage
-    private static Typeface createFromFamiliesWithDefault(FontFamily[] families, int weight,
-                int italic) {
+    private static Typeface createFromFamiliesWithDefault(
+            android.graphics.FontFamily[] families, int weight, int italic) {
         return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, weight, italic);
     }
 
@@ -1063,7 +1007,7 @@
      * @param families array of font families
      */
     @UnsupportedAppUsage
-    private static Typeface createFromFamiliesWithDefault(FontFamily[] families,
+    private static Typeface createFromFamiliesWithDefault(android.graphics.FontFamily[] families,
                 String fallbackName, int weight, int italic) {
         android.graphics.fonts.FontFamily[] fallback = SystemFonts.getSystemFallback(fallbackName);
         long[] ptrArray = new long[families.length + fallback.length];
@@ -1084,6 +1028,19 @@
         }
 
         native_instance = ni;
+        mFamilies = new FontFamily[0];
+        sRegistry.registerNativeAllocation(this, native_instance);
+        mStyle = nativeGetStyle(ni);
+        mWeight = nativeGetWeight(ni);
+    }
+
+    private Typeface(long ni, @NonNull FontFamily[] families) {
+        if (ni == 0) {
+            throw new IllegalStateException("native typeface cannot be made");
+        }
+
+        native_instance = ni;
+        mFamilies = families;
         sRegistry.registerNativeAllocation(this, native_instance);
         mStyle = nativeGetStyle(ni);
         mWeight = nativeGetWeight(ni);
@@ -1097,9 +1054,9 @@
     /** @hide */
     @VisibleForTesting
     public static void initSystemDefaultTypefaces(Map<String, Typeface> systemFontMap,
-            Map<String, android.graphics.fonts.FontFamily[]> fallbacks,
+            Map<String, FontFamily[]> fallbacks,
             FontConfig.Alias[] aliases) {
-        for (Map.Entry<String, android.graphics.fonts.FontFamily[]> entry : fallbacks.entrySet()) {
+        for (Map.Entry<String, FontFamily[]> entry : fallbacks.entrySet()) {
             systemFontMap.put(entry.getKey(), createFromFamilies(entry.getValue()));
         }
 
@@ -1110,11 +1067,19 @@
             final Typeface base = systemFontMap.get(alias.getToName());
             final int weight = alias.getWeight();
             final Typeface newFace = weight == 400 ? base :
-                    new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
+                    new Typeface(nativeCreateWeightAlias(base.native_instance, weight),
+                            base.mFamilies);
             systemFontMap.put(alias.getName(), newFace);
         }
     }
 
+    private static void registerGenericFamilyNative(@NonNull String familyName,
+            @Nullable Typeface typeface) {
+        if (typeface != null) {
+            nativeRegisterGenericFamily(familyName, typeface.native_instance);
+        }
+    }
+
     static {
         final HashMap<String, Typeface> systemFontMap = new HashMap<>();
         initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(),
@@ -1140,6 +1105,15 @@
             create((String) null, Typeface.BOLD_ITALIC),
         };
 
+        // A list of generic families to be registered in native.
+        // https://www.w3.org/TR/css-fonts-4/#generic-font-families
+        String[] genericFamilies = {
+            "serif", "sans-serif", "cursive", "fantasy", "monospace", "system-ui"
+        };
+
+        for (String genericFamily : genericFamilies) {
+            registerGenericFamilyNative(genericFamily, systemFontMap.get(genericFamily));
+        }
     }
 
     @Override
@@ -1202,4 +1176,6 @@
 
     @CriticalNative
     private static native long nativeGetReleaseFunc();
+
+    private static native void nativeRegisterGenericFamily(String str, long nativePtr);
 }
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index 18b41fa..3c44916 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -149,9 +149,13 @@
     }
 
     /**
-     * Returns the alpha value of this drawable's color.
+     * Returns the alpha value of this drawable's color. Note this may not be the same alpha value
+     * provided in {@link Drawable#setAlpha(int)}. Instead this will return the alpha of the color
+     * combined with the alpha provided by setAlpha
      *
      * @return A value between 0 and 255.
+     *
+     * @see ColorDrawable#setAlpha(int)
      */
     @Override
     public int getAlpha() {
@@ -159,7 +163,9 @@
     }
 
     /**
-     * Sets the color's alpha value.
+     * Applies the given alpha to the underlying color. Note if the color already has
+     * an alpha applied to it, this will apply this alpha to the existing value instead of
+     * overwriting it.
      *
      * @param alpha The alpha value to set, between 0 and 255.
      */
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index e1f7263..caf610b 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -934,11 +934,13 @@
      * do account for the value of {@link #setAlpha}, but the general behavior is dependent
      * upon the implementation of the subclass.
      *
+     * @deprecated This method is no longer used in graphics optimizations
+     *
      * @return int The opacity class of the Drawable.
      *
      * @see android.graphics.PixelFormat
      */
-    public abstract @PixelFormat.Opacity int getOpacity();
+    @Deprecated public abstract @PixelFormat.Opacity int getOpacity();
 
     /**
      * Return the appropriate opacity value for two source opacities.  If
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index f426b2d..7f165bf 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -151,7 +151,21 @@
          * @param path the file name of the font data in the asset directory
          */
         public Builder(@NonNull AssetManager am, @NonNull String path) {
-            final long nativeAsset = nGetNativeAsset(am, path, true /* is asset */, 0 /* cookie */);
+            this(am, path, true /* is asset */, 0 /* cookie */);
+        }
+
+        /**
+         * Constructs a builder from an asset manager and a file path in an asset directory.
+         *
+         * @param am the application's asset manager
+         * @param path the file name of the font data in the asset directory
+         * @param isAsset true if the undelying data is in asset
+         * @param cookie set asset cookie
+         * @hide
+         */
+        public Builder(@NonNull AssetManager am, @NonNull String path, boolean isAsset,
+                int cookie) {
+            final long nativeAsset = nGetNativeAsset(am, path, isAsset, cookie);
             if (nativeAsset == 0) {
                 mException = new FileNotFoundException("Unable to open " + path);
                 return;
@@ -293,7 +307,7 @@
          * will resolve the style by reading font tables.
          *
          * For example, if you want to use italic font as upright font, call {@code
-         * setSlant(false)} explicitly.
+         * setSlant(FontStyle.FONT_SLANT_UPRIGHT)} explicitly.
          *
          * @return this builder
          */
@@ -372,7 +386,9 @@
                 }
             }
             final ByteBuffer readonlyBuffer = mBuffer.asReadOnlyBuffer();
-            final long ptr = nBuild(builderPtr, readonlyBuffer, mWeight, italic, mTtcIndex);
+            final String filePath = mFile == null ? "" : mFile.getAbsolutePath();
+            final long ptr = nBuild(builderPtr, readonlyBuffer, filePath, mWeight, italic,
+                    mTtcIndex);
             final Font font = new Font(ptr, readonlyBuffer, mFile,
                     new FontStyle(mWeight, slant), mTtcIndex, mAxes, mLocaleList);
             sFontRegistory.registerNativeAllocation(font, ptr);
@@ -395,7 +411,8 @@
         @CriticalNative
         private static native void nAddAxis(long builderPtr, int tag, float value);
         private static native long nBuild(
-                long builderPtr, ByteBuffer buffer, int weight, boolean italic, int ttcIndex);
+                long builderPtr, @NonNull ByteBuffer buffer, @NonNull String filePath, int weight,
+                boolean italic, int ttcIndex);
         @CriticalNative
         private static native long nGetReleaseNativeFont();
     }
@@ -444,23 +461,14 @@
     }
 
     /**
-     * Get a weight value associated with this font.
+     * Get a style associated with this font.
      *
      * @see Builder#setWeight(int)
-     * @return a weight value
+     * @see Builder#setSlant(int)
+     * @return a font style
      */
-    public @IntRange(from = 0, to = 1000)int getWeight() {
-        return mFontStyle.getWeight();
-    }
-
-    /**
-     * Get a slant value associated with this font.
-     *
-     * @see Builder#setSlant(boolean)
-     * @return a slant value
-     */
-    public @FontStyle.FontSlant int getSlant() {
-        return mFontStyle.getSlant();
+    public FontStyle getStyle() {
+        return mFontStyle;
     }
 
     /**
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index 52a37da..c0f1b16 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -108,29 +108,31 @@
          * @return a font family
          */
         public @NonNull FontFamily build() {
-            return build("", FontConfig.Family.VARIANT_DEFAULT);
+            return build("", FontConfig.Family.VARIANT_DEFAULT, true /* isCustomFallback */);
         }
 
         /** @hide */
-        public @NonNull FontFamily build(@NonNull String langTags, int variant) {
+        public @NonNull FontFamily build(@NonNull String langTags, int variant,
+                boolean isCustomFallback) {
             final long builderPtr = nInitBuilder();
             for (int i = 0; i < mFonts.size(); ++i) {
                 nAddFont(builderPtr, mFonts.get(i).getNativePtr());
             }
-            final long ptr = nBuild(builderPtr, langTags, variant);
+            final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback);
             final FontFamily family = new FontFamily(mFonts, ptr);
             sFamilyRegistory.registerNativeAllocation(family, ptr);
             return family;
         }
 
         private static int makeStyleIdentifier(@NonNull Font font) {
-            return font.getWeight() | (font.getSlant()  << 16);
+            return font.getStyle().getWeight() | (font.getStyle().getSlant()  << 16);
         }
 
         private static native long nInitBuilder();
         @CriticalNative
         private static native void nAddFont(long builderPtr, long fontPtr);
-        private static native long nBuild(long builderPtr, String langTags, int variant);
+        private static native long nBuild(long builderPtr, String langTags, int variant,
+                boolean isCustomFallback);
         @CriticalNative
         private static native long nGetReleaseNativeFamily();
     }
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 750adb2..4a9cf14 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -208,7 +208,7 @@
                 b.addFont(font);
             }
         }
-        return b == null ? null : b.build(languageTags, variant);
+        return b == null ? null : b.build(languageTags, variant, false /* isCustomFallback */);
     }
 
     private static void appendNamedFamily(@NonNull FontConfig.Family xmlFamily,
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 3efe655..6f78651 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -48,9 +48,6 @@
  * </p>
  */
 public class MeasuredText {
-    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
-            MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024);
-
     private long mNativePtr;
     private @NonNull char[] mChars;
 
@@ -166,6 +163,9 @@
      * Note: The appendStyle and appendReplacementRun should be called to cover the text length.
      */
     public static class Builder {
+        private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+                MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024);
+
         private long mNativePtr;
 
         private final @NonNull char[] mText;
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 04cc5bb..9e69488 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <iterator>
 #include <set>
+#include <map>
 
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
@@ -161,6 +162,13 @@
     LOG(INFO) << base::StringPrintf("PG (%02x): ",
                                     package_group.dynamic_ref_table.mAssignedPackageId)
               << list;
+
+    for (size_t i = 0; i < 256; i++) {
+      if (package_group.dynamic_ref_table.mLookupTable[i] != 0) {
+        LOG(INFO) << base::StringPrintf("    e[0x%02x] -> 0x%02x", (uint8_t) i,
+                                        package_group.dynamic_ref_table.mLookupTable[i]);
+      }
+    }
   }
 }
 
@@ -869,6 +877,17 @@
   }
 }
 
+uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) {
+  for (auto& package_group : package_groups_) {
+    for (auto& package2 : package_group.packages_) {
+      if (package2.loaded_package_ == package) {
+        return package_group.dynamic_ref_table.mAssignedPackageId;
+      }
+    }
+  }
+  return 0;
+}
+
 std::unique_ptr<Theme> AssetManager2::NewTheme() {
   return std::unique_ptr<Theme>(new Theme(this));
 }
@@ -1054,44 +1073,231 @@
   }
 }
 
-bool Theme::SetTo(const Theme& o) {
+void Theme::SetTo(const Theme& o) {
   if (this == &o) {
-    return true;
+    return;
   }
 
   type_spec_flags_ = o.type_spec_flags_;
 
-  const bool copy_only_system = asset_manager_ != o.asset_manager_;
-
-  for (size_t p = 0; p < packages_.size(); p++) {
-    const Package* package = o.packages_[p].get();
-    if (package == nullptr || (copy_only_system && p != 0x01)) {
-      // The other theme doesn't have this package, clear ours.
-      packages_[p].reset();
-      continue;
-    }
-
-    if (packages_[p] == nullptr) {
-      // The other theme has this package, but we don't. Make one.
-      packages_[p].reset(new Package());
-    }
-
-    for (size_t t = 0; t < package->types.size(); t++) {
-      const ThemeType* type = package->types[t].get();
-      if (type == nullptr) {
-        // The other theme doesn't have this type, clear ours.
-        packages_[p]->types[t].reset();
+  if (asset_manager_ == o.asset_manager_) {
+    // The theme comes from the same asset manager so all theme data can be copied exactly
+    for (size_t p = 0; p < packages_.size(); p++) {
+      const Package *package = o.packages_[p].get();
+      if (package == nullptr) {
+        // The other theme doesn't have this package, clear ours.
+        packages_[p].reset();
         continue;
       }
 
-      // Create a new type and update it to theirs.
-      const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry));
-      void* copied_data = malloc(type_alloc_size);
-      memcpy(copied_data, type, type_alloc_size);
-      packages_[p]->types[t].reset(reinterpret_cast<ThemeType*>(copied_data));
+      if (packages_[p] == nullptr) {
+        // The other theme has this package, but we don't. Make one.
+        packages_[p].reset(new Package());
+      }
+
+      for (size_t t = 0; t < package->types.size(); t++) {
+        const ThemeType *type = package->types[t].get();
+        if (type == nullptr) {
+          // The other theme doesn't have this type, clear ours.
+          packages_[p]->types[t].reset();
+          continue;
+        }
+
+        // Create a new type and update it to theirs.
+        const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry));
+        void *copied_data = malloc(type_alloc_size);
+        memcpy(copied_data, type, type_alloc_size);
+        packages_[p]->types[t].reset(reinterpret_cast<ThemeType *>(copied_data));
+      }
+    }
+  } else {
+    std::map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies;
+    typedef std::map<int, int> SourceToDestinationRuntimePackageMap;
+    std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
+
+    // Determine which ApkAssets are loaded in both theme AssetManagers
+    std::vector<const ApkAssets*> src_assets = o.asset_manager_->GetApkAssets();
+    for (size_t i = 0; i < src_assets.size(); i++) {
+      const ApkAssets* src_asset = src_assets[i];
+
+      std::vector<const ApkAssets*> dest_assets = asset_manager_->GetApkAssets();
+      for (size_t j = 0; j < dest_assets.size(); j++) {
+        const ApkAssets* dest_asset = dest_assets[j];
+
+        // Map the runtime package of the source apk asset to the destination apk asset
+        if (src_asset->GetPath() == dest_asset->GetPath()) {
+          const std::vector<std::unique_ptr<const LoadedPackage>>& src_packages =
+              src_asset->GetLoadedArsc()->GetPackages();
+          const std::vector<std::unique_ptr<const LoadedPackage>>& dest_packages =
+              dest_asset->GetLoadedArsc()->GetPackages();
+
+          SourceToDestinationRuntimePackageMap package_map;
+
+          // The source and destination package should have the same number of packages loaded in
+          // the same order.
+          const size_t N = src_packages.size();
+          CHECK(N == dest_packages.size())
+              << " LoadedArsc " << src_asset->GetPath() << " differs number of packages.";
+          for (size_t p = 0; p < N; p++) {
+            auto& src_package = src_packages[p];
+            auto& dest_package = dest_packages[p];
+            CHECK(src_package->GetPackageName() == dest_package->GetPackageName())
+                << " Package " << src_package->GetPackageName() << " differs in load order.";
+
+            int src_package_id = o.asset_manager_->GetAssignedPackageId(src_package.get());
+            int dest_package_id = asset_manager_->GetAssignedPackageId(dest_package.get());
+            package_map[src_package_id] = dest_package_id;
+          }
+
+          src_to_dest_asset_cookies.insert(std::pair<ApkAssetsCookie, ApkAssetsCookie>(i, j));
+          src_asset_cookie_id_map.insert(
+              std::pair<ApkAssetsCookie, SourceToDestinationRuntimePackageMap>(i, package_map));
+          break;
+        }
+      }
+    }
+
+    // Reset the data in the destination theme
+    for (size_t p = 0; p < packages_.size(); p++) {
+      if (packages_[p] != nullptr) {
+        packages_[p].reset();
+      }
+    }
+
+    for (size_t p = 0; p < packages_.size(); p++) {
+      const Package *package = o.packages_[p].get();
+      if (package == nullptr) {
+        continue;
+      }
+
+      for (size_t t = 0; t < package->types.size(); t++) {
+        const ThemeType *type = package->types[t].get();
+        if (type == nullptr) {
+          continue;
+        }
+
+        for (size_t e = 0; e < type->entry_count; e++) {
+          const ThemeEntry &entry = type->entries[e];
+          if (entry.value.dataType == Res_value::TYPE_NULL &&
+              entry.value.data != Res_value::DATA_NULL_EMPTY) {
+            continue;
+          }
+
+          // The package id of the attribute needs to be rewritten to the package id of the value in
+          // the destination
+          int attribute_dest_package_id = p;
+          if (attribute_dest_package_id != 0x01) {
+            // Find the cookie of the attribute resource id
+            FindEntryResult attribute_entry_result;
+            ApkAssetsCookie attribute_cookie =
+                o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , false,
+                                            &attribute_entry_result);
+
+            // Determine the package id of the attribute in the destination AssetManager
+            auto attribute_package_map = src_asset_cookie_id_map.find(attribute_cookie);
+            if (attribute_package_map == src_asset_cookie_id_map.end()) {
+              continue;
+            }
+            auto attribute_dest_package = attribute_package_map->second.find(
+                attribute_dest_package_id);
+            if (attribute_dest_package == attribute_package_map->second.end()) {
+              continue;
+            }
+            attribute_dest_package_id = attribute_dest_package->second;
+          }
+
+          // If the attribute value represents an attribute or reference, the package id of the
+          // value needs to be rewritten to the package id of the value in the destination
+          uint32_t attribue_data = entry.value.data;
+          if (entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE
+              || entry.value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE
+              || entry.value.dataType == Res_value::TYPE_ATTRIBUTE
+              || entry.value.dataType == Res_value::TYPE_REFERENCE) {
+
+            // Determine the package id of the reference in the destination AssetManager
+            auto value_package_map = src_asset_cookie_id_map.find(entry.cookie);
+            if (value_package_map == src_asset_cookie_id_map.end()) {
+              continue;
+            }
+
+            auto value_dest_package = value_package_map->second.find(
+                get_package_id(entry.value.data));
+            if (value_dest_package == value_package_map->second.end()) {
+              continue;
+            }
+
+            attribue_data = fix_package_id(entry.value.data, value_dest_package->second);
+          }
+
+          // Lazily instantiate the destination package
+          std::unique_ptr<Package>& dest_package = packages_[attribute_dest_package_id];
+          if (dest_package == nullptr) {
+            dest_package.reset(new Package());
+          }
+
+          // Lazily instantiate and resize the destination type
+          util::unique_cptr<ThemeType>& dest_type = dest_package->types[t];
+          if (dest_type == nullptr || dest_type->entry_count < type->entry_count) {
+            const size_t type_alloc_size = sizeof(ThemeType)
+                + (type->entry_count * sizeof(ThemeEntry));
+            void* dest_data = malloc(type_alloc_size);
+            memset(dest_data, 0, type->entry_count * sizeof(ThemeEntry));
+
+            // Copy the existing destination type values if the type is resized
+            if (dest_type != nullptr) {
+              memcpy(dest_data, type, sizeof(ThemeType)
+                                      + (dest_type->entry_count * sizeof(ThemeEntry)));
+            }
+
+            dest_type.reset(reinterpret_cast<ThemeType *>(dest_data));
+            dest_type->entry_count = type->entry_count;
+          }
+
+          // Find the cookie of the value in the destination
+          auto value_dest_cookie = src_to_dest_asset_cookies.find(entry.cookie);
+          if (value_dest_cookie == src_to_dest_asset_cookies.end()) {
+            continue;
+          }
+
+          dest_type->entries[e].cookie = value_dest_cookie->second;
+          dest_type->entries[e].value.dataType = entry.value.dataType;
+          dest_type->entries[e].value.data = attribue_data;
+          dest_type->entries[e].type_spec_flags = entry.type_spec_flags;
+        }
+      }
     }
   }
-  return true;
+}
+
+void Theme::Dump() const {
+  base::ScopedLogSeverity _log(base::INFO);
+  LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_);
+
+  for (int p = 0; p < packages_.size(); p++) {
+    auto& package = packages_[p];
+    if (package == nullptr) {
+      continue;
+    }
+
+    for (int t = 0; t < package->types.size(); t++) {
+      auto& type = package->types[t];
+      if (type == nullptr) {
+        continue;
+      }
+
+      for (int e = 0; e < type->entry_count; e++) {
+        auto& entry = type->entries[e];
+        if (entry.value.dataType == Res_value::TYPE_NULL &&
+            entry.value.data != Res_value::DATA_NULL_EMPTY) {
+          continue;
+        }
+
+        LOG(INFO) << base::StringPrintf("  entry(0x%08x)=(0x%08x) type=(0x%02x), cookie(%d)",
+                                        make_resid(p, t, e), entry.value.data,
+                                        entry.value.dataType, entry.cookie);
+      }
+    }
+  }
 }
 
 }  // namespace android
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index dc4a0a7..388548b 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -6998,18 +6998,28 @@
 }
 
 status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
-    uint8_t resolvedType;
+    uint8_t resolvedType = Res_value::TYPE_REFERENCE;
+    switch (value->dataType) {
+        case Res_value::TYPE_ATTRIBUTE:
+            resolvedType = Res_value::TYPE_ATTRIBUTE;
+        // fallthrough
+        case Res_value::TYPE_REFERENCE:
+            // Only resolve non-dynamic references and attributes if the package is loaded as a
+            // library or if a shared library is attempting to retrieve its own resource
+            if (!(mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) {
+                return NO_ERROR;
+            }
 
-    if (value->dataType == Res_value::TYPE_ATTRIBUTE
-        || value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
-        resolvedType = Res_value::TYPE_ATTRIBUTE;
-
-    } else if (value->dataType == Res_value::TYPE_REFERENCE
-               || value->dataType == Res_value::TYPE_DYNAMIC_REFERENCE) {
-        resolvedType = Res_value::TYPE_REFERENCE;
-
-    } else {
-        return NO_ERROR;
+        // If the package is loaded as shared library, the resource reference
+        // also need to be fixed.
+        break;
+        case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
+            resolvedType = Res_value::TYPE_ATTRIBUTE;
+        // fallthrough
+        case Res_value::TYPE_DYNAMIC_REFERENCE:
+            break;
+        default:
+            return NO_ERROR;
     }
 
     status_t err = lookupResourceId(&value->data);
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 2f0ee01..5312b06 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -74,6 +74,8 @@
 // AssetManager2 is the main entry point for accessing assets and resources.
 // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets.
 class AssetManager2 {
+  friend Theme;
+
  public:
   struct ResourceName {
     const char* package = nullptr;
@@ -285,6 +287,9 @@
   // been seen while traversing bag parents.
   const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
 
+  // Retrieve the assigned package id of the package if loaded into this AssetManager
+  uint8_t GetAssignedPackageId(const LoadedPackage* package);
+
   // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
   // have a longer lifetime.
   std::vector<const ApkAssets*> apk_assets_;
@@ -355,11 +360,14 @@
   bool ApplyStyle(uint32_t resid, bool force = false);
 
   // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
-  // Returns false if the AssetManagers of the Themes were not compatible.
-  bool SetTo(const Theme& o);
+  // If `o` does not have the same AssetManager as this theme, only attributes from ApkAssets loaded
+  // into both AssetManagers will be copied to this theme.
+  void SetTo(const Theme& o);
 
   void Clear();
 
+  void Dump() const;
+
   inline const AssetManager2* GetAssetManager() const {
     return asset_manager_;
   }
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index a028515..59abad4 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1709,13 +1709,13 @@
 
     struct resource_name
     {
-        const char16_t* package;
+        const char16_t* package = NULL;
         size_t packageLen;
-        const char16_t* type;
-        const char* type8;
+        const char16_t* type = NULL;
+        const char* type8 = NULL;
         size_t typeLen;
-        const char16_t* name;
-        const char* name8;
+        const char16_t* name = NULL;
+        const char* name8 = NULL;
         size_t nameLen;
     };
 
diff --git a/libs/androidfw/tests/DynamicRefTable_test.cpp b/libs/androidfw/tests/DynamicRefTable_test.cpp
index df44e34..5acc46a 100644
--- a/libs/androidfw/tests/DynamicRefTable_test.cpp
+++ b/libs/androidfw/tests/DynamicRefTable_test.cpp
@@ -40,6 +40,26 @@
   EXPECT_EQ(value2.data, 0x02010000);
 };
 
+TEST(DynamicRefTableTest, LookupSharedLibSelfAttributes) {
+  // Shared library
+  DynamicRefTable shared_table(0x03, /* appAsLib */ false);
+  shared_table.addMapping(0x00, 0x03);
+  Res_value value;
+  value.dataType = Res_value::TYPE_ATTRIBUTE;
+  value.data = 0x00010000;
+  ASSERT_EQ(shared_table.lookupResourceValue(&value), NO_ERROR);
+  EXPECT_EQ(value.data, 0x03010000);
+
+  // App loaded as a shared library
+  DynamicRefTable shared_app_table(0x04, /* appAsLib */ true);
+  shared_app_table.addMapping(0x7f, 0x04);
+  Res_value value2;
+  value2.dataType = Res_value::TYPE_ATTRIBUTE;
+  value2.data = 0x7f010000;
+  ASSERT_EQ(shared_app_table.lookupResourceValue(&value2), NO_ERROR);
+  EXPECT_EQ(value2.data, 0x04010000);
+};
+
 TEST(DynamicRefTableTest, LookupDynamicReferences) {
   // Shared library
   DynamicRefTable shared_table(0x2, /* appAsLib */ false);
@@ -51,24 +71,46 @@
   ASSERT_EQ(shared_table.lookupResourceValue(&value), NO_ERROR);
   EXPECT_EQ(value.data, 0x05010000);
 
-  // App loaded as a shared library
-  DynamicRefTable shared_app_table(0x2, /* appAsLib */ true);
-  shared_app_table.addMapping(0x03, 0x05);
-  shared_app_table.addMapping(0x7f, 0x2);
-  Res_value value2;
-  value2.dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
-  value2.data = 0x03010000;
-  ASSERT_EQ(shared_app_table.lookupResourceValue(&value2), NO_ERROR);
-  EXPECT_EQ(value2.data, 0x05010000);
-
   // Regular application
   DynamicRefTable app_table(0x7f, /* appAsLib */ false);
   app_table.addMapping(0x03, 0x05);
   Res_value value3;
-  value3.dataType = Res_value::TYPE_REFERENCE;
+  value3.dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
   value3.data = 0x03010000;
   ASSERT_EQ(app_table.lookupResourceValue(&value3), NO_ERROR);
   EXPECT_EQ(value3.data, 0x05010000);
 };
 
+TEST(DynamicRefTableTest, LookupDynamicAttributes) {
+// App loaded as a shared library
+  DynamicRefTable shared_app_table(0x2, /* appAsLib */ true);
+  shared_app_table.addMapping(0x03, 0x05);
+  shared_app_table.addMapping(0x7f, 0x2);
+  Res_value value2;
+  value2.dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE;
+  value2.data = 0x03010000;
+  ASSERT_EQ(shared_app_table.lookupResourceValue(&value2), NO_ERROR);
+  EXPECT_EQ(value2.data, 0x05010000);
+}
+
+TEST(DynamicRefTableTest, DoNotLookupNonDynamicReferences) {
+  // Regular application
+  DynamicRefTable app_table(0x7f, /* appAsLib */ false);
+  Res_value value;
+  value.dataType = Res_value::TYPE_REFERENCE;
+  value.data = 0x03010000;
+  ASSERT_EQ(app_table.lookupResourceValue(&value), NO_ERROR);
+  EXPECT_EQ(value.data, 0x03010000);
+};
+
+TEST(DynamicRefTableTest, DoNotLookupNonDynamicAttributes) {
+  // App with custom package id
+  DynamicRefTable custom_app_table(0x8f, /* appAsLib */ false);
+  Res_value value2;
+  value2.dataType = Res_value::TYPE_ATTRIBUTE;
+  value2.data = 0x03010000;
+  ASSERT_EQ(custom_app_table.lookupResourceValue(&value2), NO_ERROR);
+  EXPECT_EQ(value2.data, 0x03010000);
+};
+
 } // namespace android
\ No newline at end of file
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 9eb4a13..26d2896 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -94,15 +94,15 @@
             target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
 
   ResTable::resource_name res_name;
-  ASSERT_TRUE(target_table_.getResourceName(R::array::integerArray1, false, &res_name));
+  ASSERT_TRUE(target_table_.getResourceName(R::array::integerArray1, true, &res_name));
 
   ASSERT_TRUE(res_name.package != NULL);
   ASSERT_TRUE(res_name.type != NULL);
-  ASSERT_TRUE(res_name.name != NULL);
+  ASSERT_TRUE(res_name.name8 != NULL);
 
   EXPECT_EQ(String16("com.android.basic"), String16(res_name.package, res_name.packageLen));
   EXPECT_EQ(String16("array"), String16(res_name.type, res_name.typeLen));
-  EXPECT_EQ(String16("integerArray1"), String16(res_name.name, res_name.nameLen));
+  EXPECT_EQ(String8("integerArray1"), String8(res_name.name8, res_name.nameLen));
 }
 
 constexpr const uint32_t kNonOverlaidResourceId = 0x7fff0000u;
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 55d53ed..2c39cee 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -21,12 +21,14 @@
 #include "TestHelpers.h"
 #include "androidfw/ResourceUtils.h"
 #include "data/lib_one/R.h"
+#include "data/lib_two/R.h"
 #include "data/libclient/R.h"
 #include "data/styles/R.h"
 #include "data/system/R.h"
 
 namespace app = com::android::app;
 namespace lib_one = com::android::lib_one;
+namespace lib_two = com::android::lib_two;
 namespace libclient = com::android::libclient;
 
 namespace android {
@@ -263,7 +265,7 @@
   ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree));
 
   // Copy the theme to theme_one.
-  ASSERT_TRUE(theme_one->SetTo(*theme_two));
+  theme_one->SetTo(*theme_two);
 
   // Clear theme_two to make sure we test that there WAS a copy.
   theme_two->Clear();
@@ -279,12 +281,14 @@
   EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
 }
 
-TEST_F(ThemeTest, OnlyCopySystemThemeWhenAssetManagersDiffer) {
+TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) {
   AssetManager2 assetmanager_one;
-  assetmanager_one.SetApkAssets({system_assets_.get(), style_assets_.get()});
+  assetmanager_one.SetApkAssets({system_assets_.get(), lib_one_assets_.get(), style_assets_.get(),
+                                 libclient_assets_.get()});
 
   AssetManager2 assetmanager_two;
-  assetmanager_two.SetApkAssets({system_assets_.get(), style_assets_.get()});
+  assetmanager_two.SetApkAssets({system_assets_.get(), lib_two_assets_.get(), lib_one_assets_.get(),
+                                 style_assets_.get()});
 
   auto theme_one = assetmanager_one.NewTheme();
   ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
@@ -292,17 +296,34 @@
   auto theme_two = assetmanager_two.NewTheme();
   ASSERT_TRUE(theme_two->ApplyStyle(R::style::Theme_One));
   ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleTwo));
+  ASSERT_TRUE(theme_two->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03),
+                                    false /*force*/));
+  ASSERT_TRUE(theme_two->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02),
+                                    false /*force*/));
 
-  EXPECT_TRUE(theme_one->SetTo(*theme_two));
+  theme_one->SetTo(*theme_two);
 
   Res_value value;
   uint32_t flags;
 
-  // No app resources.
-  EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+  // System resources (present in destination asset manager)
+  EXPECT_EQ(0, theme_one->GetAttribute(R::attr::foreground, &value, &flags));
 
-  // Only system.
-  EXPECT_NE(kInvalidCookie, theme_one->GetAttribute(R::attr::foreground, &value, &flags));
+  // The cookie of the style asset is 3 in the source and 2 in the destination.
+  // Check that the cookie has been rewritten to the destination values
+  EXPECT_EQ(2, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+
+  // The cookie of the lib_one asset is 2 in the source and 1 in the destination.
+  // The package id of the lib_one package is 0x03 in the source and 0x02 in the destination
+  // Check that the cookie and packages have been rewritten to the destination values
+  EXPECT_EQ(1, theme_one->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value,
+                                       &flags));
+  EXPECT_EQ(1, theme_one->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value,
+                                       &flags));
+
+  // attr2 references an attribute in lib_one. Check that the resolution of the attribute value is
+  // correct after the value of attr2 had its package id rewritten to the destination package id
+  EXPECT_EQ(700, value.data);
 }
 
 }  // namespace android
diff --git a/libs/androidfw/tests/data/lib_two/R.h b/libs/androidfw/tests/data/lib_two/R.h
index c04a9d3..92b9cc1 100644
--- a/libs/androidfw/tests/data/lib_two/R.h
+++ b/libs/androidfw/tests/data/lib_two/R.h
@@ -24,12 +24,24 @@
 namespace lib_two {
 
 struct R {
+  struct attr {
+    enum : uint32_t {
+      attr3 = 0x02010000, // default
+    };
+  };
+
   struct string {
     enum : uint32_t {
       LibraryString = 0x02020000,  // default
       foo = 0x02020001, // default
     };
   };
+
+  struct style {
+    enum : uint32_t {
+      Theme = 0x02030000, // default
+    };
+  };
 };
 
 }  // namespace lib_two
diff --git a/libs/androidfw/tests/data/lib_two/lib_two.apk b/libs/androidfw/tests/data/lib_two/lib_two.apk
index ad44f9c..486c230 100644
--- a/libs/androidfw/tests/data/lib_two/lib_two.apk
+++ b/libs/androidfw/tests/data/lib_two/lib_two.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/lib_two/res/values/values.xml b/libs/androidfw/tests/data/lib_two/res/values/values.xml
index f4eea26..340d14c 100644
--- a/libs/androidfw/tests/data/lib_two/res/values/values.xml
+++ b/libs/androidfw/tests/data/lib_two/res/values/values.xml
@@ -15,9 +15,17 @@
 -->
 
 <resources>
+    <public type="attr" name="attr3" id="0x00010000" />
+    <attr name="attr3" format="integer" />
+
     <public type="string" name="LibraryString" id="0x00020000" />
     <string name="LibraryString">Hi from library two</string>
 
     <public type="string" name="foo" id="0x00020001" />
     <string name="foo">Foo from lib_two</string>
+
+    <public type="style" name="Theme" id="0x02030000" />
+    <style name="Theme">
+        <item name="com.android.lib_two:attr3">800</item>
+    </style>
 </resources>
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index f0053a4..17d2db71 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -176,7 +176,7 @@
         "pipeline/skia/SkiaRecordingCanvas.cpp",
         "pipeline/skia/SkiaVulkanPipeline.cpp",
         "pipeline/skia/VectorDrawableAtlas.cpp",
-        "pipeline/skia/VkFunctorDrawable.cpp",
+        "pipeline/skia/VkInteropFunctorDrawable.cpp",
         "renderstate/RenderState.cpp",
         "renderthread/CacheManager.cpp",
         "renderthread/CanvasContext.cpp",
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 2e5aef5..9c707ba 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -732,14 +732,10 @@
                             float y, float boundsLeft, float boundsTop, float boundsRight,
                             float boundsBottom, float totalAdvance) {
     if (count <= 0 || paint.nothingToDraw()) return;
-    // Set align to left for drawing, as we don't want individual
-    // glyphs centered or right-aligned; the offset above takes
-    // care of all alignment.
     SkPaint paintCopy(paint);
     if (mPaintFilter) {
         mPaintFilter->filter(&paintCopy);
     }
-    paintCopy.setTextAlign(SkPaint::kLeft_Align);
     SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
     // Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
     // older.
@@ -763,14 +759,10 @@
 void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
                                   const SkPaint& paint, const SkPath& path, size_t start,
                                   size_t end) {
-    // Set align to left for drawing, as we don't want individual
-    // glyphs centered or right-aligned; the offsets take care of
-    // that portion of the alignment.
     SkPaint paintCopy(paint);
     if (mPaintFilter) {
         mPaintFilter->filter(&paintCopy);
     }
-    paintCopy.setTextAlign(SkPaint::kLeft_Align);
     SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
 
     const int N = end - start;
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 753557c..75a6e72 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -286,7 +286,6 @@
 }
 
 void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
-    outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
     if (isHardware()) {
         outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
                                                  info().colorType(), info().alphaType(), nullptr));
@@ -321,7 +320,6 @@
         SkBitmap skiaBitmap;
         skiaBitmap.setInfo(info(), rowBytes());
         skiaBitmap.setPixelRef(sk_ref_sp(this), 0, 0);
-        skiaBitmap.setHasHardwareMipMap(mHasHardwareMipMap);
         // Note we don't cache in this case, because the raster image holds a pointer to this Bitmap
         // internally and ~Bitmap won't be invoked.
         // TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here.
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index c6e4c15..05dc340 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -30,13 +30,15 @@
 namespace android {
 
 MinikinFontSkia::MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize,
-                                 int ttcIndex, const std::vector<minikin::FontVariation>& axes)
+                                 std::string_view filePath, int ttcIndex,
+                                 const std::vector<minikin::FontVariation>& axes)
         : minikin::MinikinFont(typeface->uniqueID())
         , mTypeface(std::move(typeface))
         , mFontData(fontData)
         , mFontSize(fontSize)
         , mTtcIndex(ttcIndex)
-        , mAxes(axes) {}
+        , mAxes(axes)
+        , mFilePath(filePath) {}
 
 static void MinikinFontSkia_SetSkiaPaint(const minikin::MinikinFont* font, SkPaint* skPaint,
                                          const minikin::MinikinPaint& paint,
@@ -131,13 +133,13 @@
     sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
     sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), params));
 
-    return std::make_shared<MinikinFontSkia>(std::move(face), mFontData, mFontSize, ttcIndex,
-                                             variations);
+    return std::make_shared<MinikinFontSkia>(std::move(face), mFontData, mFontSize, mFilePath,
+                                             ttcIndex, variations);
 }
 
 uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) {
     uint32_t flags = paint->getFlags();
-    SkPaint::Hinting hinting = paint->getHinting();
+    SkFontHinting hinting = (SkFontHinting)paint->getHinting();
     // select only flags that might affect text layout
     flags &= (SkPaint::kAntiAlias_Flag | SkPaint::kFakeBoldText_Flag | SkPaint::kLinearText_Flag |
               SkPaint::kSubpixelText_Flag | SkPaint::kEmbeddedBitmapText_Flag |
@@ -148,7 +150,7 @@
 
 void MinikinFontSkia::unpackPaintFlags(SkPaint* paint, uint32_t paintFlags) {
     paint->setFlags(paintFlags & SkPaint::kAllFlags);
-    paint->setHinting(static_cast<SkPaint::Hinting>(paintFlags >> 16));
+    paint->setHinting(static_cast<SkFontHinting>(paintFlags >> 16));
 }
 
 void MinikinFontSkia::populateSkPaint(SkPaint* paint, const MinikinFont* font,
diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
index d156598..55576b7 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -28,8 +28,9 @@
 
 class ANDROID_API MinikinFontSkia : public minikin::MinikinFont {
 public:
-    explicit MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize,
-                             int ttcIndex, const std::vector<minikin::FontVariation>& axes);
+    MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize,
+                    std::string_view filePath, int ttcIndex,
+                    const std::vector<minikin::FontVariation>& axes);
 
     float GetHorizontalAdvance(uint32_t glyph_id, const minikin::MinikinPaint& paint,
                                const minikin::FontFakery& fakery) const override;
@@ -48,6 +49,7 @@
     const void* GetFontData() const;
     size_t GetFontSize() const;
     int GetFontIndex() const;
+    const std::string& getFilePath() const { return mFilePath; }
     const std::vector<minikin::FontVariation>& GetAxes() const;
     std::shared_ptr<minikin::MinikinFont> createFontWithVariation(
             const std::vector<minikin::FontVariation>&) const;
@@ -68,6 +70,7 @@
     size_t mFontSize;
     int mTtcIndex;
     std::vector<minikin::FontVariation> mAxes;
+    std::string mFilePath;
 };
 
 }  // namespace android
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 31d3c0d..c1a3b6d 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -86,6 +86,14 @@
 
     const Typeface* getAndroidTypeface() const { return mTypeface; }
 
+    enum Align {
+        kLeft_Align,
+        kCenter_Align,
+        kRight_Align,
+    };
+    Align getTextAlign() const { return mAlign; }
+    void setTextAlign(Align align) { mAlign = align; }
+
 private:
     float mLetterSpacing = 0;
     float mWordSpacing = 0;
@@ -98,6 +106,7 @@
     // object. Thus, following pointer can never be a dangling pointer. Note that
     // nullptr is valid: it means the default typeface.
     const Typeface* mTypeface = nullptr;
+    Align mAlign = kLeft_Align;
 };
 
 }  // namespace android
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index 29cc890..bdbf5ca 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -34,7 +34,8 @@
         , mMinikinLocaleListId(paint.mMinikinLocaleListId)
         , mFamilyVariant(paint.mFamilyVariant)
         , mHyphenEdit(paint.mHyphenEdit)
-        , mTypeface(paint.mTypeface) {}
+        , mTypeface(paint.mTypeface)
+        , mAlign(paint.mAlign) {}
 
 Paint::Paint(const SkPaint& paint)
         : SkPaint(paint)
@@ -55,6 +56,7 @@
     mFamilyVariant = other.mFamilyVariant;
     mHyphenEdit = other.mHyphenEdit;
     mTypeface = other.mTypeface;
+    mAlign = other.mAlign;
     return *this;
 }
 
@@ -64,6 +66,6 @@
            a.mFontFeatureSettings == b.mFontFeatureSettings &&
            a.mMinikinLocaleListId == b.mMinikinLocaleListId &&
            a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
-           a.mTypeface == b.mTypeface;
+           a.mTypeface == b.mTypeface && a.mAlign == b.mAlign;
 }
 }  // namespace android
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index e8332a8..c4d8aa6 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -183,7 +183,8 @@
     LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont);
 
     std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
-            std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
+            std::move(typeface), data, st.st_size, kRobotoFont, 0,
+            std::vector<minikin::FontVariation>());
     std::vector<minikin::Font> fonts;
     fonts.push_back(minikin::Font::Builder(font).build());
 
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 3fa73a4..45022e7 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -24,7 +24,7 @@
 #include "RenderNode.h"
 #include "pipeline/skia/AnimatedDrawables.h"
 #include "pipeline/skia/GLFunctorDrawable.h"
-#include "pipeline/skia/VkFunctorDrawable.h"
+#include "pipeline/skia/VkInteropFunctorDrawable.h"
 
 namespace android {
 namespace uirenderer {
@@ -81,6 +81,11 @@
 }
 
 void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
+    if (mCurrentBarrier && enableReorder) {
+        // Already in a re-order section, nothing to do
+        return;
+    }
+
     if (nullptr != mCurrentBarrier) {
         // finish off the existing chunk
         SkDrawable* drawable =
@@ -89,9 +94,8 @@
         drawDrawable(drawable);
     }
     if (enableReorder) {
-        mCurrentBarrier = (StartReorderBarrierDrawable*)
-                                  mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
-                                          mDisplayList.get());
+        mCurrentBarrier = mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
+                mDisplayList.get());
         drawDrawable(mCurrentBarrier);
     }
 }
@@ -120,8 +124,8 @@
                                              uirenderer::GlFunctorLifecycleListener* listener) {
     FunctorDrawable* functorDrawable;
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        functorDrawable = mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, listener,
-                asSkCanvas());
+        functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor,
+                listener, asSkCanvas());
     } else {
         functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener,
                 asSkCanvas());
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 2ca110f..7fc41ac 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -22,7 +22,7 @@
 #include "SkiaProfileRenderer.h"
 #include "renderstate/RenderState.h"
 #include "renderthread/Frame.h"
-#include "VkFunctorDrawable.h"
+#include "VkInteropFunctorDrawable.h"
 
 #include <SkSurface.h>
 #include <SkTypes.h>
@@ -144,7 +144,7 @@
 }
 
 void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
-    VkFunctorDrawable::vkInvokeFunctor(functor);
+    VkInteropFunctorDrawable::vkInvokeFunctor(functor);
 }
 
 sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
deleted file mode 100644
index 6486ddb..0000000
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#include "VkFunctorDrawable.h"
-#include <private/hwui/DrawGlInfo.h>
-
-#include "renderthread/EglManager.h"
-#include "thread/ThreadBase.h"
-#include "utils/TimeUtils.h"
-#include <thread>
-#include <utils/Color.h>
-#include <utils/Trace.h>
-#include <utils/TraceUtils.h>
-
-#include <EGL/eglext.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <GLES3/gl3.h>
-
-#include <utils/GLUtils.h>
-
-namespace android {
-namespace uirenderer {
-namespace skiapipeline {
-
-static std::mutex sLock{};
-static ThreadBase* sGLDrawThread = nullptr;
-static renderthread::EglManager sEglManager;
-
-// ScopedDrawRequest makes sure a GL thread is started and EGL context is initialized on it.
-class ScopedDrawRequest {
-public:
-    ScopedDrawRequest() { beginDraw(); }
-private:
-    void beginDraw() {
-        std::lock_guard{sLock};
-
-        if (!sGLDrawThread) {
-            sGLDrawThread = new ThreadBase{};
-        }
-
-        if (!sGLDrawThread->isRunning()) {
-            sGLDrawThread->start("GLFunctorThread");
-        }
-
-        if (!sEglManager.hasEglContext()) {
-            sGLDrawThread->queue().runSync([]() {
-                sEglManager.initialize();
-            });
-        }
-    }
-};
-
-void VkFunctorDrawable::vkInvokeFunctor(Functor* functor) {
-    ScopedDrawRequest _drawRequest{};
-    sGLDrawThread->queue().runSync([&]() {
-        EGLDisplay display = sEglManager.eglDisplay();
-        DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
-        if (display != EGL_NO_DISPLAY) {
-            mode = DrawGlInfo::kModeProcess;
-        }
-        (*functor)(mode, nullptr);
-    });
-}
-
-#define FENCE_TIMEOUT 2000000000
-
-void VkFunctorDrawable::onDraw(SkCanvas* canvas) {
-    ATRACE_CALL();
-
-    if (canvas->getGrContext() == nullptr) {
-        SkDEBUGF(("Attempting to draw VkFunctor into an unsupported surface"));
-        return;
-    }
-
-    ScopedDrawRequest _drawRequest{};
-
-    SkImageInfo surfaceInfo = canvas->imageInfo();
-
-    if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) {
-        // Buffer will be used as an OpenGL ES render target.
-        mFrameBuffer = new GraphicBuffer(
-                 //TODO: try to reduce the size of the buffer: possibly by using clip bounds.
-                 static_cast<uint32_t>(surfaceInfo.width()),
-                 static_cast<uint32_t>(surfaceInfo.height()),
-                 ColorTypeToPixelFormat(surfaceInfo.colorType()),
-                 GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
-                         GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
-                 std::string("VkFunctorDrawable::onDraw pid [") + std::to_string(getpid()) + "]");
-        status_t error = mFrameBuffer->initCheck();
-        if (error < 0) {
-            ALOGW("VkFunctorDrawable::onDraw() failed in GraphicBuffer.create()");
-            return;
-        }
-
-        mFBInfo = surfaceInfo;
-    }
-
-    //TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
-    //TODO: draw command has completed.
-    //TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
-    //TODO: GrVkGpu::destroyResources() for example.
-    bool success = sGLDrawThread->queue().runSync([&]() -> bool {
-        ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
-        EGLDisplay display = sEglManager.eglDisplay();
-        LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
-                "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
-                uirenderer::renderthread::EglManager::eglErrorString());
-        // We use an EGLImage to access the content of the GraphicBuffer
-        // The EGL image is later bound to a 2D texture
-        EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer();
-        AutoEglImage autoImage(display, clientBuffer);
-        if (autoImage.image == EGL_NO_IMAGE_KHR) {
-            ALOGW("Could not create EGL image, err =%s",
-                  uirenderer::renderthread::EglManager::eglErrorString());
-            return false;
-        }
-
-        AutoSkiaGlTexture glTexture;
-        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
-        GL_CHECKPOINT(MODERATE);
-
-        glBindTexture(GL_TEXTURE_2D, 0);
-
-        DrawGlInfo info;
-        SkMatrix44 mat4(canvas->getTotalMatrix());
-        SkIRect clipBounds = canvas->getDeviceClipBounds();
-
-        info.clipLeft = clipBounds.fLeft;
-        info.clipTop = clipBounds.fTop;
-        info.clipRight = clipBounds.fRight;
-        info.clipBottom = clipBounds.fBottom;
-        info.isLayer = true;
-        info.width = mFBInfo.width();
-        info.height = mFBInfo.height();
-        mat4.asColMajorf(&info.transform[0]);
-
-        glViewport(0, 0, info.width, info.height);
-
-        AutoGLFramebuffer glFb;
-        // Bind texture to the frame buffer.
-        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                             glTexture.mTexture, 0);
-        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
-            ALOGE("Failed framebuffer check for created target buffer: %s",
-                    GLUtils::getGLFramebufferError());
-            return false;
-        }
-
-        glDisable(GL_STENCIL_TEST);
-        glDisable(GL_SCISSOR_TEST);
-        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-        glClear(GL_COLOR_BUFFER_BIT);
-
-        (*mFunctor)(DrawGlInfo::kModeDraw, &info);
-
-        EGLSyncKHR glDrawFinishedFence =
-                eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
-        LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR,
-                "Could not create sync fence %#x", eglGetError());
-        glFlush();
-        // TODO: export EGLSyncKHR in file descr
-        // TODO: import file desc in Vulkan Semaphore
-        // TODO: instead block the GPU: probably by using external Vulkan semaphore.
-        // Block the CPU until the glFlush finish.
-        EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0,
-                FENCE_TIMEOUT);
-        LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
-                            "Failed to wait for the fence %#x", eglGetError());
-        eglDestroySyncKHR(display, glDrawFinishedFence);
-        return true;
-    });
-
-    if (!success) {
-        return;
-    }
-
-    SkPaint paint;
-    paint.setBlendMode(SkBlendMode::kSrcOver);
-    canvas->save();
-    // The size of the image matches the size of the canvas. We've used the matrix already, while
-    // drawing into the offscreen surface, so we need to reset it here.
-    canvas->resetMatrix();
-
-    auto functorImage = SkImage::MakeFromAHardwareBuffer(
-        reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType,
-        nullptr, kBottomLeft_GrSurfaceOrigin);
-    canvas->drawImage(functorImage, 0, 0, &paint);
-    canvas->restore();
-}
-
-VkFunctorDrawable::~VkFunctorDrawable() {
-    if (mListener.get() != nullptr) {
-        ScopedDrawRequest _drawRequest{};
-        sGLDrawThread->queue().runSync([&]() {
-             mListener->onGlFunctorReleased(mFunctor);
-        });
-    }
-}
-
-void VkFunctorDrawable::syncFunctor() const {
-    ScopedDrawRequest _drawRequest{};
-    sGLDrawThread->queue().runSync([&]() {
-         (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
-    });
-}
-
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
deleted file mode 100644
index e37f6fb..0000000
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#pragma once
-
-#include "FunctorDrawable.h"
-
-#include <utils/RefBase.h>
-#include <ui/GraphicBuffer.h>
-
-namespace android {
-namespace uirenderer {
-
-namespace skiapipeline {
-
-/**
- * This drawable wraps a Vulkan functor enabling it to be recorded into a list
- * of Skia drawing commands.
- */
-class VkFunctorDrawable : public FunctorDrawable {
-public:
-    VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
-            : FunctorDrawable(functor, listener, canvas) {}
-    virtual ~VkFunctorDrawable();
-
-    void syncFunctor() const override;
-
-    static void vkInvokeFunctor(Functor* functor);
-
-protected:
-    virtual void onDraw(SkCanvas* canvas) override;
-
-private:
-
-    // Variables below describe/store temporary offscreen buffer used for Vulkan pipeline.
-    sp<GraphicBuffer> mFrameBuffer;
-    SkImageInfo mFBInfo;
-};
-
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
new file mode 100644
index 0000000..a594206
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "VkInteropFunctorDrawable.h"
+#include <private/hwui/DrawGlInfo.h>
+
+#include "renderthread/EglManager.h"
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
+#include <thread>
+#include <utils/Color.h>
+#include <utils/Trace.h>
+#include <utils/TraceUtils.h>
+
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include <utils/GLUtils.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+static std::mutex sLock{};
+static ThreadBase* sGLDrawThread = nullptr;
+static renderthread::EglManager sEglManager;
+
+// ScopedDrawRequest makes sure a GL thread is started and EGL context is initialized on it.
+class ScopedDrawRequest {
+public:
+    ScopedDrawRequest() { beginDraw(); }
+private:
+    void beginDraw() {
+        std::lock_guard{sLock};
+
+        if (!sGLDrawThread) {
+            sGLDrawThread = new ThreadBase{};
+        }
+
+        if (!sGLDrawThread->isRunning()) {
+            sGLDrawThread->start("GLFunctorThread");
+        }
+
+        if (!sEglManager.hasEglContext()) {
+            sGLDrawThread->queue().runSync([]() {
+                sEglManager.initialize();
+            });
+        }
+    }
+};
+
+void VkInteropFunctorDrawable::vkInvokeFunctor(Functor* functor) {
+    ScopedDrawRequest _drawRequest{};
+    sGLDrawThread->queue().runSync([&]() {
+        EGLDisplay display = sEglManager.eglDisplay();
+        DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+        if (display != EGL_NO_DISPLAY) {
+            mode = DrawGlInfo::kModeProcess;
+        }
+        (*functor)(mode, nullptr);
+    });
+}
+
+#define FENCE_TIMEOUT 2000000000
+
+void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
+    ATRACE_CALL();
+
+    if (canvas->getGrContext() == nullptr) {
+        SkDEBUGF(("Attempting to draw VkInteropFunctor into an unsupported surface"));
+        return;
+    }
+
+    ScopedDrawRequest _drawRequest{};
+
+    SkImageInfo surfaceInfo = canvas->imageInfo();
+
+    if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) {
+        // Buffer will be used as an OpenGL ES render target.
+        mFrameBuffer = new GraphicBuffer(
+                 //TODO: try to reduce the size of the buffer: possibly by using clip bounds.
+                 static_cast<uint32_t>(surfaceInfo.width()),
+                 static_cast<uint32_t>(surfaceInfo.height()),
+                 ColorTypeToPixelFormat(surfaceInfo.colorType()),
+                 GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+                         GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
+                 std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) +
+                         "]");
+        status_t error = mFrameBuffer->initCheck();
+        if (error < 0) {
+            ALOGW("VkInteropFunctorDrawable::onDraw() failed in GraphicBuffer.create()");
+            return;
+        }
+
+        mFBInfo = surfaceInfo;
+    }
+
+    //TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
+    //TODO: draw command has completed.
+    //TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
+    //TODO: GrVkGpu::destroyResources() for example.
+    bool success = sGLDrawThread->queue().runSync([&]() -> bool {
+        ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
+        EGLDisplay display = sEglManager.eglDisplay();
+        LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
+                "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+                uirenderer::renderthread::EglManager::eglErrorString());
+        // We use an EGLImage to access the content of the GraphicBuffer
+        // The EGL image is later bound to a 2D texture
+        EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer();
+        AutoEglImage autoImage(display, clientBuffer);
+        if (autoImage.image == EGL_NO_IMAGE_KHR) {
+            ALOGW("Could not create EGL image, err =%s",
+                  uirenderer::renderthread::EglManager::eglErrorString());
+            return false;
+        }
+
+        AutoSkiaGlTexture glTexture;
+        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
+        GL_CHECKPOINT(MODERATE);
+
+        glBindTexture(GL_TEXTURE_2D, 0);
+
+        DrawGlInfo info;
+        SkMatrix44 mat4(canvas->getTotalMatrix());
+        SkIRect clipBounds = canvas->getDeviceClipBounds();
+
+        info.clipLeft = clipBounds.fLeft;
+        info.clipTop = clipBounds.fTop;
+        info.clipRight = clipBounds.fRight;
+        info.clipBottom = clipBounds.fBottom;
+        info.isLayer = true;
+        info.width = mFBInfo.width();
+        info.height = mFBInfo.height();
+        mat4.asColMajorf(&info.transform[0]);
+
+        glViewport(0, 0, info.width, info.height);
+
+        AutoGLFramebuffer glFb;
+        // Bind texture to the frame buffer.
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                             glTexture.mTexture, 0);
+        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+            ALOGE("Failed framebuffer check for created target buffer: %s",
+                    GLUtils::getGLFramebufferError());
+            return false;
+        }
+
+        glDisable(GL_STENCIL_TEST);
+        glDisable(GL_SCISSOR_TEST);
+        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+
+        EGLSyncKHR glDrawFinishedFence =
+                eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
+        LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR,
+                "Could not create sync fence %#x", eglGetError());
+        glFlush();
+        // TODO: export EGLSyncKHR in file descr
+        // TODO: import file desc in Vulkan Semaphore
+        // TODO: instead block the GPU: probably by using external Vulkan semaphore.
+        // Block the CPU until the glFlush finish.
+        EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0,
+                FENCE_TIMEOUT);
+        LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
+                            "Failed to wait for the fence %#x", eglGetError());
+        eglDestroySyncKHR(display, glDrawFinishedFence);
+        return true;
+    });
+
+    if (!success) {
+        return;
+    }
+
+    SkPaint paint;
+    paint.setBlendMode(SkBlendMode::kSrcOver);
+    canvas->save();
+    // The size of the image matches the size of the canvas. We've used the matrix already, while
+    // drawing into the offscreen surface, so we need to reset it here.
+    canvas->resetMatrix();
+
+    auto functorImage = SkImage::MakeFromAHardwareBuffer(
+        reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType,
+        nullptr, kBottomLeft_GrSurfaceOrigin);
+    canvas->drawImage(functorImage, 0, 0, &paint);
+    canvas->restore();
+}
+
+VkInteropFunctorDrawable::~VkInteropFunctorDrawable() {
+    if (mListener.get() != nullptr) {
+        ScopedDrawRequest _drawRequest{};
+        sGLDrawThread->queue().runSync([&]() {
+             mListener->onGlFunctorReleased(mFunctor);
+        });
+    }
+}
+
+void VkInteropFunctorDrawable::syncFunctor() const {
+    ScopedDrawRequest _drawRequest{};
+    sGLDrawThread->queue().runSync([&]() {
+         (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
+    });
+}
+
+};  // namespace skiapipeline
+};  // namespace uirenderer
+};  // namespace android
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
new file mode 100644
index 0000000..3269cfb
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include "FunctorDrawable.h"
+
+#include <utils/RefBase.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace uirenderer {
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a Vulkan functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class VkInteropFunctorDrawable : public FunctorDrawable {
+public:
+    VkInteropFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
+            SkCanvas* canvas)
+            : FunctorDrawable(functor, listener, canvas) {}
+    virtual ~VkInteropFunctorDrawable();
+
+    void syncFunctor() const override;
+
+    static void vkInvokeFunctor(Functor* functor);
+
+protected:
+    virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+
+    // Variables below describe/store temporary offscreen buffer used for Vulkan pipeline.
+    sp<GraphicBuffer> mFrameBuffer;
+    SkImageInfo mFBInfo;
+};
+
+};  // namespace skiapipeline
+};  // namespace uirenderer
+};  // namespace android
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 9e435a5..f96b1f8 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -16,8 +16,6 @@
 
 #include "VulkanManager.h"
 
-#include <private/gui/SyncFeatures.h>
-
 #include "Properties.h"
 #include "RenderThread.h"
 #include "renderstate/RenderState.h"
@@ -1033,69 +1031,59 @@
         return INVALID_OPERATION;
     }
 
-    if (SyncFeatures::getInstance().useWaitSync() &&
-        SyncFeatures::getInstance().useNativeFenceSync()) {
-        // Block GPU on the fence.
-        int fenceFd = fence->dup();
-        if (fenceFd == -1) {
-            ALOGE("VulkanManager::fenceWait: error dup'ing fence fd: %d", errno);
-            return -errno;
-        }
-
-        VkSemaphoreCreateInfo semaphoreInfo;
-        semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
-        semaphoreInfo.pNext = nullptr;
-        semaphoreInfo.flags = 0;
-        VkSemaphore semaphore;
-        VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
-        if (VK_SUCCESS != err) {
-            ALOGE("Failed to create import semaphore, err: %d", err);
-            return UNKNOWN_ERROR;
-        }
-        VkImportSemaphoreFdInfoKHR importInfo;
-        importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
-        importInfo.pNext = nullptr;
-        importInfo.semaphore = semaphore;
-        importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
-        importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
-        importInfo.fd = fenceFd;
-
-        err = mImportSemaphoreFdKHR(mDevice, &importInfo);
-        if (VK_SUCCESS != err) {
-            ALOGE("Failed to import semaphore, err: %d", err);
-            return UNKNOWN_ERROR;
-        }
-
-        LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
-
-        VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
-
-        VkSubmitInfo submitInfo;
-        memset(&submitInfo, 0, sizeof(VkSubmitInfo));
-        submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
-        submitInfo.waitSemaphoreCount = 1;
-        // Wait to make sure aquire semaphore set above has signaled.
-        submitInfo.pWaitSemaphores = &semaphore;
-        submitInfo.pWaitDstStageMask = &waitDstStageFlags;
-        submitInfo.commandBufferCount = 1;
-        submitInfo.pCommandBuffers = &mDummyCB;
-        submitInfo.signalSemaphoreCount = 0;
-
-        mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
-
-        // On Android when we import a semaphore, it is imported using temporary permanence. That
-        // means as soon as we queue the semaphore for a wait it reverts to its previous permanent
-        // state before importing. This means it will now be in an idle state with no pending
-        // signal or wait operations, so it is safe to immediately delete it.
-        mDestroySemaphore(mDevice, semaphore, nullptr);
-    } else {
-        // Block CPU on the fence.
-        status_t err = fence->waitForever("VulkanManager::fenceWait");
-        if (err != NO_ERROR) {
-            ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err);
-            return err;
-        }
+    // Block GPU on the fence.
+    int fenceFd = fence->dup();
+    if (fenceFd == -1) {
+        ALOGE("VulkanManager::fenceWait: error dup'ing fence fd: %d", errno);
+        return -errno;
     }
+
+    VkSemaphoreCreateInfo semaphoreInfo;
+    semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+    semaphoreInfo.pNext = nullptr;
+    semaphoreInfo.flags = 0;
+    VkSemaphore semaphore;
+    VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+    if (VK_SUCCESS != err) {
+        ALOGE("Failed to create import semaphore, err: %d", err);
+        return UNKNOWN_ERROR;
+    }
+    VkImportSemaphoreFdInfoKHR importInfo;
+    importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
+    importInfo.pNext = nullptr;
+    importInfo.semaphore = semaphore;
+    importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
+    importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+    importInfo.fd = fenceFd;
+
+    err = mImportSemaphoreFdKHR(mDevice, &importInfo);
+    if (VK_SUCCESS != err) {
+        ALOGE("Failed to import semaphore, err: %d", err);
+        return UNKNOWN_ERROR;
+    }
+
+    LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
+
+    VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+    VkSubmitInfo submitInfo;
+    memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submitInfo.waitSemaphoreCount = 1;
+    // Wait to make sure aquire semaphore set above has signaled.
+    submitInfo.pWaitSemaphores = &semaphore;
+    submitInfo.pWaitDstStageMask = &waitDstStageFlags;
+    submitInfo.commandBufferCount = 1;
+    submitInfo.pCommandBuffers = &mDummyCB;
+    submitInfo.signalSemaphoreCount = 0;
+
+    mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
+
+    // On Android when we import a semaphore, it is imported using temporary permanence. That
+    // means as soon as we queue the semaphore for a wait it reverts to its previous permanent
+    // state before importing. This means it will now be in an idle state with no pending
+    // signal or wait operations, so it is safe to immediately delete it.
+    mDestroySemaphore(mDevice, semaphore, nullptr);
     return OK;
 }
 
@@ -1105,15 +1093,6 @@
         return INVALID_OPERATION;
     }
 
-    if (SyncFeatures::getInstance().useFenceSync()) {
-        ALOGE("VulkanManager::createReleaseFence: Vk backend doesn't support non-native fences");
-        return INVALID_OPERATION;
-    }
-
-    if (!SyncFeatures::getInstance().useNativeFenceSync()) {
-        return OK;
-    }
-
     VkExportSemaphoreCreateInfo exportInfo;
     exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
     exportInfo.pNext = nullptr;
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index fd8c252..9a1ee54 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -46,7 +46,6 @@
                 SkColorGetR(randomColor) + SkColorGetG(randomColor) + SkColorGetB(randomColor) <
                 128 * 3;
         paint.setColor(bgDark ? Color::White : Color::Grey_700);
-        paint.setTextAlign(SkPaint::kCenter_Align);
         paint.setTextSize(size / 2);
         char charToShow = 'A' + (rand() % 26);
         const SkPoint pos[] = {{SkIntToScalar(size / 2),
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 9428f53..5f5a92e 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -81,11 +81,11 @@
     // mean and stddev which doesn't make sense for our usage
     std::vector<BenchmarkReporter::Run> reports;
     BenchmarkReporter::Run report;
-    report.benchmark_name = info.name;
+    report.run_name = info.name;
     report.iterations = static_cast<int64_t>(opts.count);
     report.real_accumulated_time = durationInS;
     report.cpu_accumulated_time = durationInS;
-    report.items_per_second = opts.count / durationInS;
+    report.counters["items_per_second"] = opts.count / durationInS;
     reports.push_back(report);
     reporter->ReportRuns(reports);
 
@@ -94,13 +94,13 @@
     // in that test case than percentiles.
     if (!opts.renderOffscreen) {
         for (auto& ri : REPORTS) {
-            reports[0].benchmark_name = info.name;
-            reports[0].benchmark_name += ri.suffix;
+            reports[0].run_name = info.name;
+            reports[0].run_name += ri.suffix;
             durationInS = proxy->frameTimePercentile(ri.percentile) / 1000.0;
             reports[0].real_accumulated_time = durationInS;
             reports[0].cpu_accumulated_time = durationInS;
             reports[0].iterations = 1;
-            reports[0].items_per_second = 0;
+            reports[0].counters["items_per_second"] = 0;
             reporter->ReportRuns(reports);
         }
     }
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index e424a26..b645aeb 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -55,7 +55,8 @@
     sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
     LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
     std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
-            std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
+            std::move(typeface), data, st.st_size, fileName, 0,
+            std::vector<minikin::FontVariation>());
     std::vector<minikin::Font> fonts;
     fonts.push_back(minikin::Font::Builder(font).build());
     return std::make_shared<minikin::FontFamily>(std::move(fonts));
diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp
new file mode 100644
index 0000000..0619a9c
--- /dev/null
+++ b/libs/incident/Android.bp
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 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.
+
+cc_library_shared {
+    name: "libincident",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "liblog",
+        "libutils",
+    ],
+
+    aidl: {
+        include_dirs: ["frameworks/base/core/java"],
+        export_aidl_headers: true,
+    },
+
+    srcs: [
+        ":libincident_aidl",
+        "proto/android/os/header.proto",
+        "proto/android/os/metadata.proto",
+        "src/IncidentReportArgs.cpp",
+    ],
+
+    proto: {
+        type: "lite",
+        export_proto_headers: true,
+    },
+
+    export_include_dirs: ["include"],
+}
diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk
deleted file mode 100644
index 08c8346..0000000
--- a/libs/incident/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2016 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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libincident
-
-LOCAL_CFLAGS := \
-        -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-LOCAL_SHARED_LIBRARIES := \
-        libbinder \
-        liblog \
-        libutils
-
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/../../core/java
-LOCAL_C_INCLUDES := \
-        $(LOCAL_PATH)/include
-
-LOCAL_SRC_FILES := \
-        ../../core/java/android/os/IIncidentManager.aidl \
-        ../../core/java/android/os/IIncidentReportStatusListener.aidl \
-        proto/android/os/header.proto \
-        proto/android/os/metadata.proto \
-        src/IncidentReportArgs.cpp
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_PROTO_OPTIMIZE_TYPE := lite
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index a02b6af..3e3e651 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -273,6 +273,11 @@
      * supports {@link #ENCODING_E_AC3} but not {@link #ENCODING_E_AC3_JOC}.
      **/
     public static final int ENCODING_E_AC3_JOC = 18;
+    /** Audio data format: Dolby MAT (Metadata-enhanced Audio Transmission)
+     * Dolby MAT bitstreams are used to transmit Dolby TrueHD, channel-based PCM, or PCM with
+     * metadata (object audio) over HDMI (e.g. Dolby Atmos content).
+     **/
+    public static final int ENCODING_DOLBY_MAT = 19;
 
     /** @hide */
     public static String toLogFriendlyEncoding(int enc) {
@@ -311,6 +316,10 @@
                 return "ENCODING_AAC_XHE";
             case ENCODING_AC4:
                 return "ENCODING_AC4";
+            case ENCODING_E_AC3_JOC:
+                return "ENCODING_E_AC3_JOC";
+            case ENCODING_DOLBY_MAT:
+                return "ENCODING_DOLBY_MAT";
             default :
                 return "invalid encoding " + enc;
         }
@@ -518,25 +527,27 @@
     public static boolean isValidEncoding(int audioFormat)
     {
         switch (audioFormat) {
-        case ENCODING_PCM_8BIT:
-        case ENCODING_PCM_16BIT:
-        case ENCODING_PCM_FLOAT:
-        case ENCODING_AC3:
-        case ENCODING_E_AC3:
-        case ENCODING_E_AC3_JOC:
-        case ENCODING_DTS:
-        case ENCODING_DTS_HD:
-        case ENCODING_MP3:
-        case ENCODING_AAC_LC:
-        case ENCODING_AAC_HE_V1:
-        case ENCODING_AAC_HE_V2:
-        case ENCODING_IEC61937:
-        case ENCODING_AAC_ELD:
-        case ENCODING_AAC_XHE:
-        case ENCODING_AC4:
-            return true;
-        default:
-            return false;
+            case ENCODING_PCM_16BIT:
+            case ENCODING_PCM_8BIT:
+            case ENCODING_PCM_FLOAT:
+            case ENCODING_AC3:
+            case ENCODING_E_AC3:
+            case ENCODING_DTS:
+            case ENCODING_DTS_HD:
+            case ENCODING_MP3:
+            case ENCODING_AAC_LC:
+            case ENCODING_AAC_HE_V1:
+            case ENCODING_AAC_HE_V2:
+            case ENCODING_IEC61937:
+            case ENCODING_DOLBY_TRUEHD:
+            case ENCODING_AAC_ELD:
+            case ENCODING_AAC_XHE:
+            case ENCODING_AC4:
+            case ENCODING_E_AC3_JOC:
+            case ENCODING_DOLBY_MAT:
+                return true;
+            default:
+                return false;
         }
     }
 
@@ -544,25 +555,27 @@
     public static boolean isPublicEncoding(int audioFormat)
     {
         switch (audioFormat) {
-        case ENCODING_PCM_8BIT:
-        case ENCODING_PCM_16BIT:
-        case ENCODING_PCM_FLOAT:
-        case ENCODING_AC3:
-        case ENCODING_E_AC3:
-        case ENCODING_E_AC3_JOC:
-        case ENCODING_DTS:
-        case ENCODING_DTS_HD:
-        case ENCODING_IEC61937:
-        case ENCODING_MP3:
-        case ENCODING_AAC_LC:
-        case ENCODING_AAC_HE_V1:
-        case ENCODING_AAC_HE_V2:
-        case ENCODING_AAC_ELD:
-        case ENCODING_AAC_XHE:
-        case ENCODING_AC4:
-            return true;
-        default:
-            return false;
+            case ENCODING_PCM_16BIT:
+            case ENCODING_PCM_8BIT:
+            case ENCODING_PCM_FLOAT:
+            case ENCODING_AC3:
+            case ENCODING_E_AC3:
+            case ENCODING_DTS:
+            case ENCODING_DTS_HD:
+            case ENCODING_MP3:
+            case ENCODING_AAC_LC:
+            case ENCODING_AAC_HE_V1:
+            case ENCODING_AAC_HE_V2:
+            case ENCODING_IEC61937:
+            case ENCODING_DOLBY_TRUEHD:
+            case ENCODING_AAC_ELD:
+            case ENCODING_AAC_XHE:
+            case ENCODING_AC4:
+            case ENCODING_E_AC3_JOC:
+            case ENCODING_DOLBY_MAT:
+                return true;
+            default:
+                return false;
         }
     }
 
@@ -571,28 +584,30 @@
     public static boolean isEncodingLinearPcm(int audioFormat)
     {
         switch (audioFormat) {
-        case ENCODING_PCM_8BIT:
-        case ENCODING_PCM_16BIT:
-        case ENCODING_PCM_FLOAT:
-        case ENCODING_DEFAULT:
-            return true;
-        case ENCODING_AC3:
-        case ENCODING_E_AC3:
-        case ENCODING_E_AC3_JOC:
-        case ENCODING_DTS:
-        case ENCODING_DTS_HD:
-        case ENCODING_MP3:
-        case ENCODING_AAC_LC:
-        case ENCODING_AAC_HE_V1:
-        case ENCODING_AAC_HE_V2:
-        case ENCODING_IEC61937: // wrapped in PCM but compressed
-        case ENCODING_AAC_ELD:
-        case ENCODING_AAC_XHE:
-        case ENCODING_AC4:
-            return false;
-        case ENCODING_INVALID:
-        default:
-            throw new IllegalArgumentException("Bad audio format " + audioFormat);
+            case ENCODING_PCM_16BIT:
+            case ENCODING_PCM_8BIT:
+            case ENCODING_PCM_FLOAT:
+            case ENCODING_DEFAULT:
+                return true;
+            case ENCODING_AC3:
+            case ENCODING_E_AC3:
+            case ENCODING_DTS:
+            case ENCODING_DTS_HD:
+            case ENCODING_MP3:
+            case ENCODING_AAC_LC:
+            case ENCODING_AAC_HE_V1:
+            case ENCODING_AAC_HE_V2:
+            case ENCODING_IEC61937: // wrapped in PCM but compressed
+            case ENCODING_DOLBY_TRUEHD:
+            case ENCODING_AAC_ELD:
+            case ENCODING_AAC_XHE:
+            case ENCODING_AC4:
+            case ENCODING_E_AC3_JOC:
+            case ENCODING_DOLBY_MAT:
+                return false;
+            case ENCODING_INVALID:
+            default:
+                throw new IllegalArgumentException("Bad audio format " + audioFormat);
         }
     }
 
@@ -600,28 +615,30 @@
     public static boolean isEncodingLinearFrames(int audioFormat)
     {
         switch (audioFormat) {
-        case ENCODING_PCM_8BIT:
-        case ENCODING_PCM_16BIT:
-        case ENCODING_PCM_FLOAT:
-        case ENCODING_IEC61937: // same size as stereo PCM
-        case ENCODING_DEFAULT:
-            return true;
-        case ENCODING_AC3:
-        case ENCODING_E_AC3:
-        case ENCODING_E_AC3_JOC:
-        case ENCODING_DTS:
-        case ENCODING_DTS_HD:
-        case ENCODING_MP3:
-        case ENCODING_AAC_LC:
-        case ENCODING_AAC_HE_V1:
-        case ENCODING_AAC_HE_V2:
-        case ENCODING_AAC_ELD:
-        case ENCODING_AAC_XHE:
-        case ENCODING_AC4:
-            return false;
-        case ENCODING_INVALID:
-        default:
-            throw new IllegalArgumentException("Bad audio format " + audioFormat);
+            case ENCODING_PCM_16BIT:
+            case ENCODING_PCM_8BIT:
+            case ENCODING_PCM_FLOAT:
+            case ENCODING_IEC61937: // same size as stereo PCM
+            case ENCODING_DEFAULT:
+                return true;
+            case ENCODING_AC3:
+            case ENCODING_E_AC3:
+            case ENCODING_DTS:
+            case ENCODING_DTS_HD:
+            case ENCODING_MP3:
+            case ENCODING_AAC_LC:
+            case ENCODING_AAC_HE_V1:
+            case ENCODING_AAC_HE_V2:
+            case ENCODING_DOLBY_TRUEHD:
+            case ENCODING_AAC_ELD:
+            case ENCODING_AAC_XHE:
+            case ENCODING_AC4:
+            case ENCODING_E_AC3_JOC:
+            case ENCODING_DOLBY_MAT:
+                return false;
+            case ENCODING_INVALID:
+            default:
+                throw new IllegalArgumentException("Bad audio format " + audioFormat);
         }
     }
     /**
@@ -844,22 +861,24 @@
                 case ENCODING_DEFAULT:
                     mEncoding = ENCODING_PCM_16BIT;
                     break;
-                case ENCODING_PCM_8BIT:
                 case ENCODING_PCM_16BIT:
+                case ENCODING_PCM_8BIT:
                 case ENCODING_PCM_FLOAT:
                 case ENCODING_AC3:
                 case ENCODING_E_AC3:
-                case ENCODING_E_AC3_JOC:
                 case ENCODING_DTS:
                 case ENCODING_DTS_HD:
-                case ENCODING_IEC61937:
                 case ENCODING_MP3:
                 case ENCODING_AAC_LC:
                 case ENCODING_AAC_HE_V1:
                 case ENCODING_AAC_HE_V2:
+                case ENCODING_IEC61937:
+                case ENCODING_DOLBY_TRUEHD:
                 case ENCODING_AAC_ELD:
                 case ENCODING_AAC_XHE:
                 case ENCODING_AC4:
+                case ENCODING_E_AC3_JOC:
+                case ENCODING_DOLBY_MAT:
                     mEncoding = encoding;
                     break;
                 case ENCODING_INVALID:
@@ -1060,22 +1079,24 @@
     /** @hide */
     @IntDef(flag = false, prefix = "ENCODING", value = {
         ENCODING_DEFAULT,
-        ENCODING_PCM_8BIT,
         ENCODING_PCM_16BIT,
+        ENCODING_PCM_8BIT,
         ENCODING_PCM_FLOAT,
         ENCODING_AC3,
         ENCODING_E_AC3,
-        ENCODING_E_AC3_JOC,
         ENCODING_DTS,
         ENCODING_DTS_HD,
         ENCODING_MP3,
-        ENCODING_IEC61937,
+        ENCODING_AAC_LC,
         ENCODING_AAC_HE_V1,
         ENCODING_AAC_HE_V2,
-        ENCODING_AAC_LC,
+        ENCODING_IEC61937,
+        ENCODING_DOLBY_TRUEHD,
         ENCODING_AAC_ELD,
         ENCODING_AAC_XHE,
-        ENCODING_AC4 }
+        ENCODING_AC4,
+        ENCODING_E_AC3_JOC,
+        ENCODING_DOLBY_MAT }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface Encoding {}
@@ -1088,8 +1109,9 @@
             ENCODING_DTS_HD,
             ENCODING_AAC_LC,
             ENCODING_DOLBY_TRUEHD,
-            ENCODING_E_AC3_JOC,
             ENCODING_AC4,
+            ENCODING_E_AC3_JOC,
+            ENCODING_DOLBY_MAT,
     };
 
     /** @hide */
@@ -1100,8 +1122,9 @@
             ENCODING_DTS_HD,
             ENCODING_AAC_LC,
             ENCODING_DOLBY_TRUEHD,
+            ENCODING_AC4,
             ENCODING_E_AC3_JOC,
-            ENCODING_AC4 }
+            ENCODING_DOLBY_MAT }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface SurroundSoundEncoding {}
@@ -1129,10 +1152,12 @@
                 return "AAC";
             case ENCODING_DOLBY_TRUEHD:
                 return "Dolby TrueHD";
-            case ENCODING_E_AC3_JOC:
-                return "Dolby Atmos in Dolby Digital Plus";
             case ENCODING_AC4:
                 return "Dolby AC-4";
+            case ENCODING_E_AC3_JOC:
+                return "Dolby Atmos in Dolby Digital Plus";
+            case ENCODING_DOLBY_MAT:
+                return "Dolby MAT";
             default:
                 return "Unknown surround sound format";
         }
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
index ce71436..1cc650b 100644
--- a/media/java/android/media/AudioPresentation.java
+++ b/media/java/android/media/AudioPresentation.java
@@ -19,13 +19,14 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.TestApi;
+import android.icu.util.ULocale;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 
 
 /**
@@ -34,7 +35,7 @@
  *
  * Used by {@link MediaExtractor} {@link MediaExtractor#getAudioPresentations(int)} and
  * {@link AudioTrack} {@link AudioTrack#setPresentation(AudioPresentation)} to query available
- * presentations and to select one.
+ * presentations and to select one, respectively.
  *
  * A list of available audio presentations in a media source can be queried using
  * {@link MediaExtractor#getAudioPresentations(int)}. This list can be presented to a user for
@@ -49,8 +50,7 @@
 public final class AudioPresentation {
     private final int mPresentationId;
     private final int mProgramId;
-    private final Map<String, String> mLabels;
-    private final String mLanguage;
+    private final ULocale mLanguage;
 
     /** @hide */
     @IntDef(
@@ -63,72 +63,98 @@
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface MasteringIndicationType {}
-
     private final @MasteringIndicationType int mMasteringIndication;
     private final boolean mAudioDescriptionAvailable;
     private final boolean mSpokenSubtitlesAvailable;
     private final boolean mDialogueEnhancementAvailable;
+    private final Map<ULocale, String> mLabels;
 
     /**
      * No preferred reproduction channel layout.
+     *
+     * @see Builder#setMasteringIndication(int)
      */
     public static final int MASTERING_NOT_INDICATED         = 0;
     /**
      * Stereo speaker layout.
+     *
+     * @see Builder#setMasteringIndication(int)
      */
     public static final int MASTERED_FOR_STEREO             = 1;
     /**
      * Two-dimensional (e.g. 5.1) speaker layout.
+     *
+     * @see Builder#setMasteringIndication(int)
      */
     public static final int MASTERED_FOR_SURROUND           = 2;
     /**
      * Three-dimensional (e.g. 5.1.2) speaker layout.
+     *
+     * @see Builder#setMasteringIndication(int)
      */
     public static final int MASTERED_FOR_3D                 = 3;
     /**
      * Prerendered for headphone playback.
+     *
+     * @see Builder#setMasteringIndication(int)
      */
     public static final int MASTERED_FOR_HEADPHONE          = 4;
 
     /**
-     * @hide
+     * This ID is reserved. No items can be explicitly assigned this ID.
      */
-    @TestApi
-    public AudioPresentation(int presentationId,
-                        int programId,
-                        @NonNull Map<String, String> labels,
-                        @NonNull String language,
-                        @MasteringIndicationType int masteringIndication,
-                        boolean audioDescriptionAvailable,
-                        boolean spokenSubtitlesAvailable,
-                        boolean dialogueEnhancementAvailable) {
-        this.mPresentationId = presentationId;
-        this.mProgramId = programId;
-        this.mLanguage = language;
-        this.mMasteringIndication = masteringIndication;
-        this.mAudioDescriptionAvailable = audioDescriptionAvailable;
-        this.mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
-        this.mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
+    private static final int UNKNOWN_ID = -1;
 
-        this.mLabels = new HashMap<String, String>(labels);
+    /**
+     * This allows an application developer to construct an AudioPresentation object with all the
+     * parameters.
+     * The IDs are all that is required for an
+     * {@link AudioTrack#setPresentation(AudioPresentation)} to be successful.
+     * The rest of the metadata is informative only so as to distinguish features
+     * of different presentations.
+     * @param presentationId Presentation ID to be decoded by a next generation audio decoder.
+     * @param programId Program ID to be decoded by a next generation audio decoder.
+     * @param language Locale corresponding to ISO 639-1/639-2 language code.
+     * @param masteringIndication One of {@link AudioPresentation#MASTERING_NOT_INDICATED},
+     *     {@link AudioPresentation#MASTERED_FOR_STEREO},
+     *     {@link AudioPresentation#MASTERED_FOR_SURROUND},
+     *     {@link AudioPresentation#MASTERED_FOR_3D},
+     *     {@link AudioPresentation#MASTERED_FOR_HEADPHONE}.
+     * @param audioDescriptionAvailable Audio description for the visually impaired.
+     * @param spokenSubtitlesAvailable Spoken subtitles for the visually impaired.
+     * @param dialogueEnhancementAvailable Dialogue enhancement.
+     * @param labels Text label indexed by its locale corresponding to the language code.
+     */
+    private AudioPresentation(int presentationId,
+                             int programId,
+                             @NonNull ULocale language,
+                             @MasteringIndicationType int masteringIndication,
+                             boolean audioDescriptionAvailable,
+                             boolean spokenSubtitlesAvailable,
+                             boolean dialogueEnhancementAvailable,
+                             @NonNull Map<ULocale, String> labels) {
+        mPresentationId = presentationId;
+        mProgramId = programId;
+        mLanguage = language;
+        mMasteringIndication = masteringIndication;
+        mAudioDescriptionAvailable = audioDescriptionAvailable;
+        mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
+        mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
+        mLabels = new HashMap<ULocale, String>(labels);
     }
 
     /**
-     * The framework uses this presentation id to select an audio presentation rendered by a
-     * decoder. Presentation id is typically sequential, but does not have to be.
-     * @hide
+     * Returns presentation ID used by the framework to select an audio presentation rendered by a
+     * decoder. Presentation ID is typically sequential, but does not have to be.
      */
-    @TestApi
     public int getPresentationId() {
         return mPresentationId;
     }
 
     /**
-     * The framework uses this program id to select an audio presentation rendered by a decoder.
-     * Program id can be used to further uniquely identify the presentation to a decoder.
-     * @hide
+     * Returns program ID used by the framework to select an audio presentation rendered by a
+     * decoder. Program ID can be used to further uniquely identify the presentation to a decoder.
      */
-    @TestApi
     public int getProgramId() {
         return mProgramId;
     }
@@ -139,9 +165,9 @@
      * or ISO 639-2/T could be used.
      */
     public Map<Locale, String> getLabels() {
-        Map<Locale, String> localeLabels = new HashMap<>();
-        for (Map.Entry<String, String> entry : mLabels.entrySet()) {
-            localeLabels.put(new Locale(entry.getKey()), entry.getValue());
+        Map<Locale, String> localeLabels = new HashMap<Locale, String>();
+        for (Map.Entry<ULocale, String> entry : mLabels.entrySet()) {
+            localeLabels.put(entry.getKey().toLocale(), entry.getValue());
         }
         return localeLabels;
     }
@@ -150,13 +176,20 @@
      * @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code.
      */
     public Locale getLocale() {
-        return new Locale(mLanguage);
+        return mLanguage.toLocale();
+    }
+
+    private ULocale getULocale() {
+        return mLanguage;
     }
 
     /**
      * @return the mastering indication of the audio presentation.
-     * See {@link #MASTERING_NOT_INDICATED}, {@link #MASTERED_FOR_STEREO},
-     * {@link #MASTERED_FOR_SURROUND}, {@link #MASTERED_FOR_3D}, {@link #MASTERED_FOR_HEADPHONE}
+     * See {@link AudioPresentation#MASTERING_NOT_INDICATED},
+     *     {@link AudioPresentation#MASTERED_FOR_STEREO},
+     *     {@link AudioPresentation#MASTERED_FOR_SURROUND},
+     *     {@link AudioPresentation#MASTERED_FOR_3D},
+     *     {@link AudioPresentation#MASTERED_FOR_HEADPHONE}
      */
     @MasteringIndicationType
     public int getMasteringIndication() {
@@ -186,4 +219,147 @@
     public boolean hasDialogueEnhancement() {
         return mDialogueEnhancementAvailable;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof AudioPresentation)) {
+            return false;
+        }
+        AudioPresentation obj = (AudioPresentation) o;
+        return mPresentationId == obj.getPresentationId()
+                && mProgramId == obj.getProgramId()
+                && mLanguage == obj.getULocale()
+                && mMasteringIndication == obj.getMasteringIndication()
+                && mAudioDescriptionAvailable == obj.hasAudioDescription()
+                && mSpokenSubtitlesAvailable == obj.hasSpokenSubtitles()
+                && mDialogueEnhancementAvailable == obj.hasDialogueEnhancement()
+                && mLabels.equals(obj.getLabels());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mPresentationId);
+    }
+
+    /**
+     * A builder class for creating {@link AudioPresentation} objects.
+     */
+    public static class Builder {
+        private final int mPresentationId;
+        private int mProgramId = UNKNOWN_ID;
+        private ULocale mLanguage = new ULocale("");
+        private int mMasteringIndication = MASTERING_NOT_INDICATED;
+        private boolean mAudioDescriptionAvailable = false;
+        private boolean mSpokenSubtitlesAvailable = false;
+        private boolean mDialogueEnhancementAvailable = false;
+        private Map<ULocale, String> mLabels = new HashMap<ULocale, String>();
+
+        /**
+         * Create a {@link Builder}. Any field that should be included in the
+         * {@link AudioPresentation} must be added.
+         *
+         * @param presentationId the presentation ID of this audio presentation
+         */
+        public Builder(int presentationId) {
+            mPresentationId = presentationId;
+        }
+        /**
+         * Sets the ProgramId to which this audio presentation refers.
+         *
+         * @param programId
+         */
+        public @NonNull Builder setProgramId(int programId) {
+            mProgramId = programId;
+            return this;
+        }
+        /**
+         * Sets the language information of the audio presentation.
+         *
+         * @param language code
+         */
+        public @NonNull Builder setLocale(ULocale language) {
+            mLanguage = language;
+            return this;
+        }
+
+        /**
+         * Sets the mastering indication.
+         *
+         * @param masteringIndication Input to set mastering indication.
+         * @throws IllegalArgumentException if the mastering indication is not any of
+         * {@link AudioPresentation#MASTERING_NOT_INDICATED},
+         * {@link AudioPresentation#MASTERED_FOR_STEREO},
+         * {@link AudioPresentation#MASTERED_FOR_SURROUND},
+         * {@link AudioPresentation#MASTERED_FOR_3D},
+         * and {@link AudioPresentation#MASTERED_FOR_HEADPHONE}
+         */
+        public @NonNull Builder setMasteringIndication(
+                @MasteringIndicationType int masteringIndication) {
+            if (masteringIndication != MASTERING_NOT_INDICATED
+                    && masteringIndication != MASTERED_FOR_STEREO
+                    && masteringIndication != MASTERED_FOR_SURROUND
+                    && masteringIndication != MASTERED_FOR_3D
+                    && masteringIndication != MASTERED_FOR_HEADPHONE) {
+                throw new IllegalArgumentException("Unknown mastering indication: "
+                                                        + masteringIndication);
+            }
+            mMasteringIndication = masteringIndication;
+            return this;
+        }
+
+        /**
+         * Sets locale / text label pairs describing the presentation.
+         *
+         * @param labels
+         */
+        public @NonNull Builder setLabels(@NonNull Map<ULocale, String> labels) {
+            mLabels = new HashMap<ULocale, String>(labels);
+            return this;
+        }
+
+        /**
+         * Indicate whether the presentation contains audio description for the visually impaired.
+         *
+         * @param audioDescriptionAvailable
+         */
+        public @NonNull Builder setHasAudioDescription(boolean audioDescriptionAvailable) {
+            mAudioDescriptionAvailable = audioDescriptionAvailable;
+            return this;
+        }
+
+        /**
+         * Indicate whether the presentation contains spoken subtitles for the visually impaired.
+         *
+         * @param spokenSubtitlesAvailable
+         */
+        public @NonNull Builder setHasSpokenSubtitles(boolean spokenSubtitlesAvailable) {
+            mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
+            return this;
+        }
+
+        /**
+         * Indicate whether the presentation supports dialogue enhancement.
+         *
+         * @param dialogueEnhancementAvailable
+         */
+        public @NonNull Builder setHasDialogueEnhancement(boolean dialogueEnhancementAvailable) {
+            mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
+            return this;
+        }
+
+        /**
+         * Creates a {@link AudioPresentation} instance with the specified fields.
+         *
+         * @return The new {@link AudioPresentation} instance
+         */
+        public @NonNull AudioPresentation build() {
+            return new AudioPresentation(mPresentationId, mProgramId,
+                                           mLanguage, mMasteringIndication,
+                                           mAudioDescriptionAvailable, mSpokenSubtitlesAvailable,
+                                           mDialogueEnhancementAvailable, mLabels);
+        }
+    }
 }
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 57b648e..00a393a 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -923,5 +923,51 @@
      * @hide
      */
     public static final int METADATA_KEY_EXIF_LENGTH = 34;
+
+    /**
+     * This key retrieves the color standard, if available.
+     *
+     * @see MediaFormat#COLOR_STANDARD_BT709
+     * @see MediaFormat#COLOR_STANDARD_BT601_PAL
+     * @see MediaFormat#COLOR_STANDARD_BT601_NTSC
+     * @see MediaFormat#COLOR_STANDARD_BT2020
+     *
+     * @hide
+     */
+    public static final int METADATA_KEY_COLOR_STANDARD = 35;
+
+    /**
+     * This key retrieves the color transfer, if available.
+     *
+     * @see MediaFormat#COLOR_TRANSFER_LINEAR
+     * @see MediaFormat#COLOR_TRANSFER_SDR_VIDEO
+     * @see MediaFormat#COLOR_TRANSFER_ST2084
+     * @see MediaFormat#COLOR_TRANSFER_HLG
+     *
+     * @hide
+     */
+    public static final int METADATA_KEY_COLOR_TRANSFER = 36;
+
+    /**
+     * This key retrieves the color range, if available.
+     *
+     * @see MediaFormat#COLOR_RANGE_LIMITED
+     * @see MediaFormat#COLOR_RANGE_FULL
+     *
+     * @hide
+     */
+    public static final int METADATA_KEY_COLOR_RANGE    = 37;
     // Add more here...
+
+    /**
+     * This key retrieves the sample rate, if available.
+     * @hide
+     */
+    public static final int METADATA_KEY_SAMPLERATE      = 38;
+
+    /**
+     * This key retrieves the bits per sample, if available.
+     * @hide
+     */
+    public static final int METADATA_KEY_BITS_PER_SAMPLE = 39;
 }
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index e94413c..0b3c973 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -23,9 +23,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.SurfaceTexture;
-import android.net.Uri;
 import android.os.Handler;
-import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.view.Surface;
 import android.view.SurfaceHolder;
@@ -34,7 +32,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -241,47 +238,6 @@
         return new MediaPlayer2Impl(context);
     }
 
-    private static final String[] decodeMediaPlayer2Uri(String location) {
-        Uri uri = Uri.parse(location);
-        if (!"mediaplayer2".equals(uri.getScheme())) {
-            return new String[] {location};
-        }
-
-        List<String> uris = uri.getQueryParameters("uri");
-        if (uris.isEmpty()) {
-            return new String[] {location};
-        }
-
-        List<String> keys = uri.getQueryParameters("key");
-        List<String> values = uri.getQueryParameters("value");
-        if (keys.size() != values.size()) {
-            return new String[] {uris.get(0)};
-        }
-
-        List<String> ls = new ArrayList();
-        ls.add(uris.get(0));
-        for (int i = 0; i < keys.size() ; i++) {
-            ls.add(keys.get(i));
-            ls.add(values.get(i));
-        }
-
-        return ls.toArray(new String[ls.size()]);
-    }
-
-    private static final String encodeMediaPlayer2Uri(String uri, String[] keys, String[] values) {
-        Uri.Builder builder = new Uri.Builder();
-        builder.scheme("mediaplayer2").path("/").appendQueryParameter("uri", uri);
-        if (keys == null || values == null || keys.length != values.length) {
-            return builder.build().toString();
-        }
-        for (int i = 0; i < keys.length ; i++) {
-            builder
-                .appendQueryParameter("key", keys[i])
-                .appendQueryParameter("value", values[i]);
-        }
-        return builder.build().toString();
-    }
-
     /**
      * @hide
      */
@@ -291,12 +247,6 @@
     }
 
     /**
-     * Returns a {@link MediaPlayerBase} implementation which runs based on
-     * this MediaPlayer2 instance.
-     */
-    public abstract MediaPlayerBase getMediaPlayerBase();
-
-    /**
      * Releases the resources held by this {@code MediaPlayer2} object.
      *
      * It is considered good practice to call this method when you're
@@ -342,12 +292,12 @@
      * Starts or resumes playback. If playback had previously been paused,
      * playback will continue from where it was paused. If playback had
      * reached end of stream and been paused, or never started before,
-     * playback will start at the beginning. If the source had not been
-     * prepared, the player will prepare the source and play.
+     * playback will start at the beginning.
      *
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void play();
+    public abstract Object play();
 
     /**
      * Prepares the player for playback, asynchronously.
@@ -355,32 +305,24 @@
      * After setting the datasource and the display surface, you need to
      * call prepare().
      *
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void prepare();
+    public abstract Object prepare();
 
     /**
      * Pauses playback. Call play() to resume.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void pause();
+    public abstract Object pause();
 
     /**
      * Tries to play next data source if applicable.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void skipToNext();
-
-    /**
-     * Moves the media to specified time position.
-     * Same as {@link #seekTo(long, int)} with {@code mode = SEEK_PREVIOUS_SYNC}.
-     *
-     * @param msec the offset in milliseconds from the start to seek to
-     */
-    // This is an asynchronous call.
-    public void seekTo(long msec) {
-        seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */);
-    }
+    public abstract Object skipToNext();
 
     /**
      * Gets the current playback position.
@@ -399,9 +341,8 @@
 
     /**
      * Gets the current buffered media source position received through progressive downloading.
-     * The received buffering percentage indicates how much of the content has been buffered
-     * or played. For example a buffering update of 80 percent when half the content
-     * has already been played indicates that the next 30 percent of the
+     * For example a buffering update of 8000 milliseconds when 5000 milliseconds of the content
+     * has already been played indicates that the next 3000 milliseconds of the
      * content to play has been buffered.
      *
      * @return the current buffered media source position in milliseconds
@@ -411,7 +352,6 @@
     /**
      * MediaPlayer2 has not been prepared or just has been reset.
      * In this state, MediaPlayer2 doesn't fetch data.
-     * @hide
      */
     public static final int PLAYER_STATE_IDLE = 1001;
 
@@ -419,26 +359,23 @@
      * MediaPlayer2 has been just prepared.
      * In this state, MediaPlayer2 just fetches data from media source,
      * but doesn't actively render data.
-     * @hide
      */
     public static final int PLAYER_STATE_PREPARED = 1002;
 
     /**
      * MediaPlayer2 is paused.
-     * In this state, MediaPlayer2 doesn't actively render data.
-     * @hide
+     * In this state, MediaPlayer2 has allocated resources to construct playback
+     * pipeline, but it doesn't actively render data.
      */
     public static final int PLAYER_STATE_PAUSED = 1003;
 
     /**
      * MediaPlayer2 is actively playing back data.
-     * @hide
      */
     public static final int PLAYER_STATE_PLAYING = 1004;
 
     /**
      * MediaPlayer2 has hit some fatal error and cannot continue playback.
-     * @hide
      */
     public static final int PLAYER_STATE_ERROR = 1005;
 
@@ -464,12 +401,13 @@
     /**
      * Sets the audio attributes for this MediaPlayer2.
      * See {@link AudioAttributes} for how to build and configure an instance of this class.
-     * You must call this method before {@link #prepare()} in order
+     * You must call this method before {@link #play()} and {@link #pause()} in order
      * for the audio attributes to become effective thereafter.
      * @param attributes a non-null set of audio attributes
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
+    public abstract Object setAudioAttributes(@NonNull AudioAttributes attributes);
 
     /**
      * Gets the audio attributes for this MediaPlayer2.
@@ -481,32 +419,36 @@
      * Sets the data source as described by a DataSourceDesc.
      *
      * @param dsd the descriptor of data source you want to play
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void setDataSource(@NonNull DataSourceDesc dsd);
+    public abstract Object setDataSource(@NonNull DataSourceDesc dsd);
 
     /**
      * Sets a single data source as described by a DataSourceDesc which will be played
      * after current data source is finished.
      *
      * @param dsd the descriptor of data source you want to play after current one
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void setNextDataSource(@NonNull DataSourceDesc dsd);
+    public abstract Object setNextDataSource(@NonNull DataSourceDesc dsd);
 
     /**
      * Sets a list of data sources to be played sequentially after current data source is done.
      *
      * @param dsds the list of data sources you want to play after current one
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds);
+    public abstract Object setNextDataSources(@NonNull List<DataSourceDesc> dsds);
 
     /**
      * Removes all data sources pending to be played.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void clearNextDataSources();
+    public abstract Object clearNextDataSources();
 
     /**
      * Gets the current data source as described by a DataSourceDesc.
@@ -518,9 +460,10 @@
     /**
      * Configures the player to loop on the current data source.
      * @param loop true if the current data source is meant to loop.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void loopCurrent(boolean loop);
+    public abstract Object loopCurrent(boolean loop);
 
     /**
      * Sets the volume of the audio of the media to play, expressed as a linear multiplier
@@ -530,12 +473,13 @@
      * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
      * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
      * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void setPlayerVolume(float volume);
+    public abstract Object setPlayerVolume(float volume);
 
     /**
-     * Returns the current volume of this player to this player.
+     * Returns the current volume of this player.
      * Note that it does not take into account the associated stream volume.
      * @return the player volume.
      */
@@ -549,50 +493,34 @@
     }
 
     /**
-     * Create a request parcel which can be routed to the native media
-     * player using {@link #invoke(Parcel, Parcel)}. The Parcel
-     * returned has the proper InterfaceToken set. The caller should
-     * not overwrite that token, i.e it can only append data to the
-     * Parcel.
-     *
-     * @return A parcel suitable to hold a request for the native
-     * player.
-     * {@hide}
-     */
-    public Parcel newRequest() {
-        return null;
-    }
-
-    /**
      * Insert a task in the command queue to help the client to identify whether a batch
      * of commands has been finished. When this command is processed, a notification
-     * {@code EventCallback.onCommandLabelReached} will be fired with the
+     * {@link EventCallback#onCommandLabelReached onCommandLabelReached} will be fired with the
      * given {@code label}.
      *
      * @see EventCallback#onCommandLabelReached
      *
      * @param label An application specific Object used to help to identify the completeness
      * of a batch of commands.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public void notifyWhenCommandLabelReached(@NonNull Object label) { }
+    public abstract Object notifyWhenCommandLabelReached(@NonNull Object label);
 
     /**
      * Sets the {@link SurfaceHolder} to use for displaying the video
      * portion of the media.
      *
      * Either a surface holder or surface must be set if a display or video sink
-     * is needed.  Not calling this method or {@link #setSurface(Surface)}
+     * is needed. Not calling this method or {@link #setSurface(Surface)}
      * when playing back a video will result in only the audio track being played.
      * A null surface holder or surface will result in only the audio track being
      * played.
      *
      * @param sh the SurfaceHolder to use for video display
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released.
-     * @hide
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
-    public abstract void setDisplay(SurfaceHolder sh);
+    public abstract Object setDisplay(SurfaceHolder sh);
 
     /**
      * Sets the {@link Surface} to be used as the sink for the video portion of
@@ -610,54 +538,53 @@
      *
      * @param surface The {@link Surface} to be used for the video portion of
      * the media.
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void setSurface(Surface surface);
-
-    /* Do not change these video scaling mode values below without updating
-     * their counterparts in system/window.h! Please do not forget to update
-     * {@link #isVideoScalingModeSupported} when new video scaling modes
-     * are added.
-     */
-    /**
-     * Specifies a video scaling mode. The content is stretched to the
-     * surface rendering area. When the surface has the same aspect ratio
-     * as the content, the aspect ratio of the content is maintained;
-     * otherwise, the aspect ratio of the content is not maintained when video
-     * is being rendered.
-     * There is no content cropping with this video scaling mode.
-     */
-    public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1;
+    public abstract Object setSurface(Surface surface);
 
     /**
-     * Specifies a video scaling mode. The content is scaled, maintaining
-     * its aspect ratio. The whole surface area is always used. When the
-     * aspect ratio of the content is the same as the surface, no content
-     * is cropped; otherwise, content is cropped to fit the surface.
-     * @hide
+     * Set the low-level power management behavior for this MediaPlayer2. This
+     * can be used when the MediaPlayer2 is not playing through a SurfaceHolder
+     * set with {@link #setDisplay(SurfaceHolder)} and thus can use the
+     * high-level {@link #setScreenOnWhilePlaying(boolean)} feature.
+     *
+     * <p>This function has the MediaPlayer2 access the low-level power manager
+     * service to control the device's power usage while playing is occurring.
+     * The parameter is a combination of {@link android.os.PowerManager} wake flags.
+     * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
+     * permission.
+     * By default, no attempt is made to keep the device awake during playback.
+     *
+     * @param context the Context to use
+     * @param mode    the power/wake mode to set
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @see android.os.PowerManager
      */
-    public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2;
+    // This is an asynchronous call.
+    public abstract Object setWakeMode(Context context, int mode);
 
     /**
-     * Sets video scaling mode. To make the target video scaling mode
-     * effective during playback, this method must be called after
-     * data source is set. If not called, the default video
-     * scaling mode is {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}.
+     * Control whether we should use the attached SurfaceHolder to keep the
+     * screen on while video playback is occurring.  This is the preferred
+     * method over {@link #setWakeMode} where possible, since it doesn't
+     * require that the application have permission for low-level wake lock
+     * access.
      *
-     * <p> The supported video scaling modes are:
-     * <ul>
-     * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}
-     * </ul>
-     *
-     * @param mode target video scaling mode. Must be one of the supported
-     * video scaling modes; otherwise, IllegalArgumentException will be thrown.
-     *
-     * @see MediaPlayer2#VIDEO_SCALING_MODE_SCALE_TO_FIT
-     * @hide
+     * @param screenOn Supply true to keep the screen on, false to allow it to turn off.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
-    public void setVideoScalingMode(int mode) { }
+    // This is an asynchronous call.
+    public abstract Object setScreenOnWhilePlaying(boolean screenOn);
+
+    /**
+     * Cancels a pending command.
+     *
+     * @param token the command to be canceled. This is the returned Object when command is issued.
+     * @return {@code false} if the task could not be cancelled; {@code true} otherwise.
+     */
+    // This is a synchronous call.
+    public abstract boolean cancelCommand(Object token);
 
     /**
      * Discards all pending commands.
@@ -677,7 +604,7 @@
      * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
      * does not correspond to a valid audio device.
      */
-    // This is an asynchronous call.
+    // This is a synchronous call.
     @Override
     public abstract boolean setPreferredDevice(AudioDeviceInfo deviceInfo);
 
@@ -722,58 +649,16 @@
             AudioRouting.OnRoutingChangedListener listener);
 
     /**
-     * Set the low-level power management behavior for this MediaPlayer2.
+     * Returns the size of the video.
      *
-     * <p>This function has the MediaPlayer2 access the low-level power manager
-     * service to control the device's power usage while playing is occurring.
-     * The parameter is a combination of {@link android.os.PowerManager} wake flags.
-     * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
-     * permission.
-     * By default, no attempt is made to keep the device awake during playback.
-     *
-     * @param context the Context to use
-     * @param mode    the power/wake mode to set
-     * @see android.os.PowerManager
-     * @hide
-     */
-    public abstract void setWakeMode(Context context, int mode);
-
-    /**
-     * Control whether we should use the attached SurfaceHolder to keep the
-     * screen on while video playback is occurring.  This is the preferred
-     * method over {@link #setWakeMode} where possible, since it doesn't
-     * require that the application have permission for low-level wake lock
-     * access.
-     *
-     * @param screenOn Supply true to keep the screen on, false to allow it
-     * to turn off.
-     * @hide
-     */
-    public abstract void setScreenOnWhilePlaying(boolean screenOn);
-
-    /**
-     * Returns the width of the video.
-     *
-     * @return the width of the video, or 0 if there is no video,
-     * no display surface was set, or the width has not been determined
-     * yet. The {@code EventCallback} can be registered via
+     * @return the size of the video. The width and height of size could be 0 if there is no video,
+     * no display surface was set, or the size has not been determined yet.
+     * The {@code EventCallback} can be registered via
      * {@link #setEventCallback(Executor, EventCallback)} to provide a
-     * notification {@code EventCallback.onVideoSizeChanged} when the width
+     * notification {@code EventCallback.onVideoSizeChanged} when the size
      * is available.
      */
-    public abstract int getVideoWidth();
-
-    /**
-     * Returns the height of the video.
-     *
-     * @return the height of the video, or 0 if there is no video,
-     * no display surface was set, or the height has not been determined
-     * yet. The {@code EventCallback} can be registered via
-     * {@link #setEventCallback(Executor, EventCallback)} to provide a
-     * notification {@code EventCallback.onVideoSizeChanged} when the height is
-     * available.
-     */
-    public abstract int getVideoHeight();
+    public abstract VideoSize getVideoSize();
 
     /**
      * Return Metrics data about the current player.
@@ -820,14 +705,12 @@
      * The input is a hint to MediaPlayer2.
      *
      * @param params the buffering management params.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released, or {@code setDataSource} has not been called.
-     * @throws IllegalArgumentException if params is invalid or not supported.
      * @hide
      */
     // This is an asynchronous call.
-    public void setBufferingParams(@NonNull BufferingParams params) { }
+    public abstract Object setBufferingParams(@NonNull BufferingParams params);
 
     /**
      * Change playback speed of audio by resampling the audio.
@@ -904,9 +787,10 @@
      * the object state.
      *
      * @param params the playback params.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void setPlaybackParams(@NonNull PlaybackParams params);
+    public abstract Object setPlaybackParams(@NonNull PlaybackParams params);
 
     /**
      * Gets the playback params, containing the current playback rate.
@@ -920,9 +804,10 @@
      * Sets A/V sync mode.
      *
      * @param params the A/V sync params to apply
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void setSyncParams(@NonNull SyncParams params);
+    public abstract Object setSyncParams(@NonNull SyncParams params);
 
     /**
      * Gets the A/V sync mode.
@@ -933,6 +818,18 @@
     public abstract SyncParams getSyncParams();
 
     /**
+     * Moves the media to specified time position.
+     * Same as {@link #seekTo(long, int)} with {@code mode = SEEK_PREVIOUS_SYNC}.
+     *
+     * @param msec the offset in milliseconds from the start to seek to
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     */
+    // This is an asynchronous call.
+    public Object seekTo(long msec) {
+        return seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */);
+    }
+
+    /**
      * Seek modes used in method seekTo(long, int) to move media position
      * to a specified location.
      *
@@ -998,9 +895,10 @@
      * If msec is negative, time position zero will be used.
      * If msec is larger than duration, duration will be used.
      * @param mode the mode indicating where exactly to seek to.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void seekTo(long msec, @SeekMode int mode);
+    public abstract Object seekTo(long msec, @SeekMode int mode);
 
     /**
      * Get current playback position as a {@link MediaTimestamp}.
@@ -1055,9 +953,10 @@
      * However, it is possible to force this player to be part of an already existing audio session
      * by calling this method.
      * This method must be called before one of the overloaded <code> setDataSource </code> methods.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void setAudioSessionId(int sessionId);
+    public abstract Object setAudioSessionId(int sessionId);
 
     /**
      * Returns the audio session ID.
@@ -1080,9 +979,10 @@
      * <p>This method must be called after one of the overloaded <code> setDataSource </code>
      * methods.
      * @param effectId system wide unique id of the effect to attach
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void attachAuxEffect(int effectId);
+    public abstract Object attachAuxEffect(int effectId);
 
 
     /**
@@ -1096,9 +996,10 @@
      * x == 0 -> level = 0
      * 0 < x <= R -> level = 10^(72*(x-R)/20/R)
      * @param level send level scalar
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
-    public abstract void setAuxEffectSendLevel(float level);
+    public abstract Object setAuxEffectSendLevel(float level);
 
     /**
      * Class for MediaPlayer2 to return each audio/video/subtitle track's metadata.
@@ -1221,12 +1122,12 @@
      * @param index the index of the track to be selected. The valid range of the index
      * is 0..total number of track - 1. The total number of tracks as well as the type of
      * each individual track can be found by calling {@link #getTrackInfo()} method.
-     * @throws IllegalStateException if called in an invalid state.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      *
      * @see MediaPlayer2#getTrackInfo
      */
     // This is an asynchronous call.
-    public abstract void selectTrack(int index);
+    public abstract Object selectTrack(int index);
 
     /**
      * Deselect a track.
@@ -1238,12 +1139,12 @@
      * @param index the index of the track to be deselected. The valid range of the index
      * is 0..total number of tracks - 1. The total number of tracks as well as the type of
      * each individual track can be found by calling {@link #getTrackInfo()} method.
-     * @throws IllegalStateException if called in an invalid state.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      *
      * @see MediaPlayer2#getTrackInfo
      */
     // This is an asynchronous call.
-    public abstract void deselectTrack(int index);
+    public abstract Object deselectTrack(int index);
 
     /**
      * Interface definition for callbacks to be invoked when the player has the corresponding
@@ -1258,11 +1159,10 @@
          *
          * @param mp the MediaPlayer2 associated with this callback
          * @param dsd the DataSourceDesc of this data source
-         * @param width the width of the video
-         * @param height the height of the video
+         * @param size the size of the video
          */
         public void onVideoSizeChanged(
-                MediaPlayer2 mp, DataSourceDesc dsd, int width, int height) { }
+                MediaPlayer2 mp, DataSourceDesc dsd, VideoSize size) { }
 
         /**
          * Called to indicate an avaliable timed text
@@ -1361,7 +1261,7 @@
     }
 
     /**
-     * Sets the callback to be invoked when the media source is ready for playback.
+     * Registers the callback to be invoked for various events covered by {@link EventCallback}.
      *
      * @param executor the executor through which the callback should be invoked
      * @param eventCallback the callback that will be run
@@ -1378,7 +1278,6 @@
     // This is a synchronous call.
     public abstract void unregisterEventCallback(EventCallback eventCallback);
 
-
     /* Do not change these values without updating their counterparts
      * in include/media/mediaplayer2.h!
      */
@@ -1387,7 +1286,8 @@
      */
     public static final int MEDIA_ERROR_UNKNOWN = 1;
 
-    /** The video is streamed and its container is not valid for progressive
+    /**
+     * The video is streamed and its container is not valid for progressive
      * playback i.e the video's index (e.g moov atom) is not at the start of the
      * file.
      * @see EventCallback#onError
@@ -1614,16 +1514,6 @@
      */
     public static final int CALL_COMPLETED_PREPARE = 6;
 
-    /** The player just completed a call {@link #releaseDrm}.
-     * @see EventCallback#onCallCompleted
-     */
-    public static final int CALL_COMPLETED_RELEASE_DRM = 12;
-
-    /** The player just completed a call {@link #restoreDrmKeys}.
-     * @see EventCallback#onCallCompleted
-     */
-    public static final int CALL_COMPLETED_RESTORE_DRM_KEYS = 13;
-
     /** The player just completed a call {@link #seekTo}.
      * @see EventCallback#onCallCompleted
      */
@@ -1698,19 +1588,42 @@
      * @see EventCallback#onCallCompleted
      * @hide
      */
-    public static final int CALL_COMPLETED_SET_BUFFERING_PARAMS = 1001;
+    public static final int CALL_COMPLETED_SET_BUFFERING_PARAMS = 31;
 
-    /** The player just completed a call {@code setVideoScalingMode}.
+    /** The player just completed a call {@link #setDisplay}.
      * @see EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_DISPLAY = 33;
+
+    /** The player just completed a call {@link #setWakeMode}.
+     * @see EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_WAKE_MODE = 34;
+
+    /** The player just completed a call {@link #setScreenOnWhilePlaying}.
+     * @see EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING = 35;
+
+    /**
+     * The start of the methods which have separate call complete callback.
      * @hide
      */
-    public static final int CALL_COMPLETED_SET_VIDEO_SCALING_MODE = 1002;
+    public static final int SEPARATE_CALL_COMPLETED_CALLBACK_START = 1000;
 
-    /** The player just completed a call {@code notifyWhenCommandLabelReached}.
+    /** The player just completed a call {@link #notifyWhenCommandLabelReached}.
      * @see EventCallback#onCommandLabelReached
      * @hide
      */
-    public static final int CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED = 1003;
+    public static final int CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED =
+            SEPARATE_CALL_COMPLETED_CALLBACK_START;
+
+    /** The player just completed a call {@link #prepareDrm}.
+     * @see DrmEventCallback#onDrmPrepared
+     * @hide
+     */
+    public static final int CALL_COMPLETED_PREPARE_DRM =
+            SEPARATE_CALL_COMPLETED_CALLBACK_START + 1;
 
     /**
      * @hide
@@ -1722,8 +1635,6 @@
             CALL_COMPLETED_PAUSE,
             CALL_COMPLETED_PLAY,
             CALL_COMPLETED_PREPARE,
-            CALL_COMPLETED_RELEASE_DRM,
-            CALL_COMPLETED_RESTORE_DRM_KEYS,
             CALL_COMPLETED_SEEK_TO,
             CALL_COMPLETED_SELECT_TRACK,
             CALL_COMPLETED_SET_AUDIO_ATTRIBUTES,
@@ -1739,8 +1650,11 @@
             CALL_COMPLETED_SKIP_TO_NEXT,
             CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES,
             CALL_COMPLETED_SET_BUFFERING_PARAMS,
-            CALL_COMPLETED_SET_VIDEO_SCALING_MODE,
-            CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED
+            CALL_COMPLETED_SET_DISPLAY,
+            CALL_COMPLETED_SET_WAKE_MODE,
+            CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING,
+            CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED,
+            CALL_COMPLETED_PREPARE_DRM,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CallCompleted {}
@@ -1864,20 +1778,22 @@
     }
 
     /**
-     * Sets the callback to be invoked when the media source is ready for playback.
+     * Registers the callback to be invoked for various DRM events.
      *
      * @param eventCallback the callback that will be run
      * @param executor the executor through which the callback should be invoked
      */
     // This is a synchronous call.
-    public abstract void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
+    public abstract void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull DrmEventCallback eventCallback);
 
     /**
-     * Clears the {@link DrmEventCallback}.
+     * Unregisters the {@link DrmEventCallback}.
+     *
+     * @param eventCallback the callback to be unregistered
      */
     // This is a synchronous call.
-    public abstract void clearDrmEventCallback();
+    public abstract void unregisterDrmEventCallback(DrmEventCallback eventCallback);
 
     /**
      * The status codes for {@link DrmEventCallback#onDrmPrepared} listener.
@@ -1902,6 +1818,15 @@
      */
     public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3;
 
+    /**
+     * The crypto scheme UUID is not supported by the device.
+     */
+    public static final int PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME = 4;
+
+    /**
+     * The hardware resources are not available, due to being in use.
+     */
+    public static final int PREPARE_DRM_STATUS_RESOURCE_BUSY = 5;
 
     /** @hide */
     @IntDef(flag = false, prefix = "PREPARE_DRM_STATUS", value = {
@@ -1909,6 +1834,8 @@
         PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR,
         PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR,
         PREPARE_DRM_STATUS_PREPARATION_ERROR,
+        PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME,
+        PREPARE_DRM_STATUS_RESOURCE_BUSY,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PrepareDrmStatusCode {}
@@ -1925,41 +1852,30 @@
      * <p>
      * If {@link OnDrmConfigHelper} is registered, it will be called during
      * preparation to allow configuration of the DRM properties before opening the
-     * DRM session. Note that the callback is called synchronously in the thread that called
-     * {@link #prepareDrm}. It should be used only for a series of {@code getDrmPropertyString}
-     * and {@code setDrmPropertyString} calls and refrain from any lengthy operation.
+     * DRM session. It should be used only for a series of {@link #getDrmPropertyString}
+     * and {@link #setDrmPropertyString} calls and refrain from any lengthy operation.
      * <p>
      * If the device has not been provisioned before, this call also provisions the device
      * which involves accessing the provisioning server and can take a variable time to
      * complete depending on the network connectivity.
-     * If {@code OnDrmPreparedListener} is registered, prepareDrm() runs in non-blocking
-     * mode by launching the provisioning in the background and returning. The listener
-     * will be called when provisioning and preparation has finished. If a
-     * {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning
-     * and preparation has finished, i.e., runs in blocking mode.
+     * When needed, the provisioning will be launched  in the background.
+     * The listener {@link DrmEventCallback#onDrmPrepared}
+     * will be called when provisioning and preparation are finished. The application should
+     * check the status code returned with {@link DrmEventCallback#onDrmPrepared} to proceed.
      * <p>
-     * If {@code OnDrmPreparedListener} is registered, it is called to indicate the DRM
+     * The registered {@link DrmEventCallback#onDrmPrepared} is called to indicate the DRM
      * session being ready. The application should not make any assumption about its call
-     * sequence (e.g., before or after prepareDrm returns), or the thread context that will
-     * execute the listener (unless the listener is registered with a handler thread).
+     * sequence (e.g., before or after prepareDrm returns).
      * <p>
      *
      * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved
-     * from the source through {@code getDrmInfo} or registering a {@code onDrmInfoListener}.
+     * from the source through {@code getDrmInfo} or registering a
+     * {@link DrmEventCallback#onDrmInfo}.
      *
-     * @throws IllegalStateException              if called before being prepared or the DRM was
-     *                                            prepared already
-     * @throws UnsupportedSchemeException         if the crypto scheme is not supported
-     * @throws ResourceBusyException              if required DRM resources are in use
-     * @throws ProvisioningNetworkErrorException  if provisioning is required but failed due to a
-     *                                            network error
-     * @throws ProvisioningServerErrorException   if provisioning is required but failed due to
-     *                                            the request denied by the provisioning server
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
-    // This is a synchronous call.
-    public abstract void prepareDrm(@NonNull UUID uuid)
-            throws UnsupportedSchemeException, ResourceBusyException,
-                   ProvisioningNetworkErrorException, ProvisioningServerErrorException;
+    // This is an asynchronous call.
+    public abstract Object prepareDrm(@NonNull UUID uuid);
 
     /**
      * Releases the DRM session
@@ -1970,8 +1886,9 @@
      *
      * @throws NoDrmSchemeException if there is no active DRM session to release
      */
-    // This is an asynchronous call.
-    public abstract void releaseDrm() throws NoDrmSchemeException;
+    // This is a synchronous call.
+    public abstract void releaseDrm()
+            throws NoDrmSchemeException;
 
     /**
      * A key request/response exchange occurs between the app and a license server
@@ -2022,7 +1939,7 @@
      * provided to the DRM engine plugin using provideDrmKeyResponse. When the
      * response is for an offline key request, a key-set identifier is returned that
      * can be used to later restore the keys to a new session with the method
-     * {@ link # restoreDrmKeys}.
+     * {@link # restoreDrmKeys}.
      * When the response is for a streaming or release request, null is returned.
      *
      * @param keySetId When the response is for a release request, keySetId identifies
@@ -2046,8 +1963,10 @@
      * keys to load, obtained from a prior call to {@link #provideDrmKeyResponse}.
      *
      * @param keySetId identifies the saved key set to restore
+     *
+     * @throws NoDrmSchemeException if there is no active DRM session
      */
-    // This is an asynchronous call.
+    // This is a synchronous call.
     public abstract void restoreDrmKeys(@NonNull byte[] keySetId)
             throws NoDrmSchemeException;
 
@@ -2107,28 +2026,6 @@
           }
     }
 
-    /**
-     * Thrown when the device requires DRM provisioning but the provisioning attempt has
-     * failed due to a network error (Internet reachability, timeout, etc.).
-     * Extends MediaDrm.MediaDrmException
-     */
-    public abstract static class ProvisioningNetworkErrorException extends MediaDrmException {
-          protected ProvisioningNetworkErrorException(String detailMessage) {
-              super(detailMessage);
-          }
-    }
-
-    /**
-     * Thrown when the device requires DRM provisioning but the provisioning attempt has
-     * failed due to the provisioning server denying the request.
-     * Extends MediaDrm.MediaDrmException
-     */
-    public abstract static class ProvisioningServerErrorException extends MediaDrmException {
-          protected ProvisioningServerErrorException(String detailMessage) {
-              super(detailMessage);
-          }
-    }
-
     public static final class MetricsConstants {
         private MetricsConstants() {}
 
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 6263e5d..47ab7fa 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -19,11 +19,11 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ContentProvider;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.graphics.SurfaceTexture;
 import android.graphics.Rect;
 import android.media.MediaPlayer2Proto.PlayerMessage;
 import android.media.MediaPlayer2Proto.Value;
@@ -34,9 +34,6 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
 import android.view.Surface;
@@ -78,18 +75,22 @@
         native_init();
     }
 
+    private static final int NEXT_SOURCE_STATE_ERROR = -1;
+    private static final int NEXT_SOURCE_STATE_INIT = 0;
+    private static final int NEXT_SOURCE_STATE_PREPARING = 1;
+    private static final int NEXT_SOURCE_STATE_PREPARED = 2;
+
     private final static String TAG = "MediaPlayer2Impl";
 
     private Context mContext;
 
-    private long mNativeContext; // accessed by native methods
+    private long mNativeContext;  // accessed by native methods
     private long mNativeSurfaceTexture;  // accessed by native methods
-    private int mListenerContext; // accessed by native methods
+    private int mListenerContext;  // accessed by native methods
     private SurfaceHolder mSurfaceHolder;
     private PowerManager.WakeLock mWakeLock = null;
     private boolean mScreenOnWhilePlaying;
     private boolean mStayAwake;
-    private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
 
     private final Object mSrcLock = new Object();
     //--- guarded by |mSrcLock| start
@@ -105,6 +106,7 @@
     private AtomicInteger mBufferedPercentageCurrent = new AtomicInteger(0);
     private AtomicInteger mBufferedPercentageNext = new AtomicInteger(0);
     private volatile float mVolume = 1.0f;
+    private VideoSize mVideoSize = new VideoSize(0, 0);
 
     // Modular DRM
     private final Object mDrmLock = new Object();
@@ -134,7 +136,7 @@
 
     /**
      * Default constructor.
-     * <p>When done with the MediaPlayer2Impl, you should call  {@link #close()},
+     * <p>When done with the MediaPlayer2Impl, you should call {@link #close()},
      * to free the resources. If not released, too many MediaPlayer2Impl instances may
      * result in an exception.</p>
      */
@@ -152,48 +154,14 @@
     }
 
     @Override
-    public MediaPlayerBase getMediaPlayerBase() {
-        return null;
-    }
-
-    /**
-     * Releases the resources held by this {@code MediaPlayer2} object.
-     *
-     * It is considered good practice to call this method when you're
-     * done using the MediaPlayer2. In particular, whenever an Activity
-     * of an application is paused (its onPause() method is called),
-     * or stopped (its onStop() method is called), this method should be
-     * invoked to release the MediaPlayer2 object, unless the application
-     * has a special need to keep the object around. In addition to
-     * unnecessary resources (such as memory and instances of codecs)
-     * being held, failure to call this method immediately if a
-     * MediaPlayer2 object is no longer needed may also lead to
-     * continuous battery consumption for mobile devices, and playback
-     * failure for other applications if no multiple instances of the
-     * same codec are supported on a device. Even if multiple instances
-     * of the same codec are supported, some performance degradation
-     * may be expected when unnecessary multiple instances are used
-     * at the same time.
-     *
-     * {@code close()} may be safely called after a prior {@code close()}.
-     * This class implements the Java {@code AutoCloseable} interface and
-     * may be used with try-with-resources.
-     */
-    @Override
     public void close() {
         super.close();
         release();
     }
 
-    /**
-     * Starts or resumes playback. If playback had previously been paused,
-     * playback will continue from where it was paused. If playback had
-     * been stopped, or never started before, playback will start at the
-     * beginning.
-     */
     @Override
-    public void play() {
-        addTask(new Task(CALL_COMPLETED_PLAY, false) {
+    public Object play() {
+        return addTask(new Task(CALL_COMPLETED_PLAY, false) {
             @Override
             void process() {
                 stayAwake(true);
@@ -204,17 +172,9 @@
 
     private native void _start() throws IllegalStateException;
 
-    /**
-     * Prepares the player for playback, asynchronously.
-     *
-     * After setting the datasource and the display surface, you need to either
-     * call prepare(). For streams, you should call prepare(),
-     * which returns immediately, rather than blocking until enough data has been
-     * buffered.
-     */
     @Override
-    public void prepare() {
-        addTask(new Task(CALL_COMPLETED_PREPARE, true) {
+    public Object prepare() {
+        return addTask(new Task(CALL_COMPLETED_PREPARE, true) {
             @Override
             void process() {
                 _prepare();
@@ -224,12 +184,9 @@
 
     public native void _prepare();
 
-    /**
-     * Pauses playback. Call play() to resume.
-     */
     @Override
-    public void pause() {
-        addTask(new Task(CALL_COMPLETED_PAUSE, false) {
+    public Object pause() {
+        return addTask(new Task(CALL_COMPLETED_PAUSE, false) {
             @Override
             void process() {
                 stayAwake(false);
@@ -241,14 +198,9 @@
 
     private native void _pause() throws IllegalStateException;
 
-    /**
-     * Tries to play next data source if applicable.
-     *
-     * @throws IllegalStateException if it is called in an invalid state
-     */
     @Override
-    public void skipToNext() {
-        addTask(new Task(CALL_COMPLETED_SKIP_TO_NEXT, false) {
+    public Object skipToNext() {
+        return addTask(new Task(CALL_COMPLETED_SKIP_TO_NEXT, false) {
             @Override
             void process() {
                 if (getState() == PLAYER_STATE_PLAYING) {
@@ -259,32 +211,12 @@
         });
     }
 
-    /**
-     * Gets the current playback position.
-     *
-     * @return the current position in milliseconds
-     */
     @Override
     public native long getCurrentPosition();
 
-    /**
-     * Gets the duration of the file.
-     *
-     * @return the duration in milliseconds, if no duration is available
-     *         (for example, if streaming live content), -1 is returned.
-     */
     @Override
     public native long getDuration();
 
-    /**
-     * Gets the current buffered media source position received through progressive downloading.
-     * The received buffering percentage indicates how much of the content has been buffered
-     * or played. For example a buffering update of 80 percent when half the content
-     * has already been played indicates that the next 30 percent of the
-     * content to play has been buffered.
-     *
-     * @return the current buffered media source position in milliseconds
-     */
     @Override
     public long getBufferedPosition() {
         // Use cached buffered percent for now.
@@ -298,17 +230,9 @@
 
     private native int native_getState();
 
-    /**
-     * Sets the audio attributes for this MediaPlayer2.
-     * See {@link AudioAttributes} for how to build and configure an instance of this class.
-     * You must call this method before {@link #prepare()} in order
-     * for the audio attributes to become effective thereafter.
-     * @param attributes a non-null set of audio attributes
-     * @throws IllegalArgumentException if the attributes are null or invalid.
-     */
     @Override
-    public void setAudioAttributes(@NonNull AudioAttributes attributes) {
-        addTask(new Task(CALL_COMPLETED_SET_AUDIO_ATTRIBUTES, false) {
+    public Object setAudioAttributes(@NonNull AudioAttributes attributes) {
+        return addTask(new Task(CALL_COMPLETED_SET_AUDIO_ATTRIBUTES, false) {
             @Override
             void process() {
                 if (attributes == null) {
@@ -326,14 +250,9 @@
         return attributes;
     }
 
-    /**
-     * Sets the data source as described by a DataSourceDesc.
-     *
-     * @param dsd the descriptor of data source you want to play
-     */
     @Override
-    public void setDataSource(@NonNull DataSourceDesc dsd) {
-        addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
+    public Object setDataSource(@NonNull DataSourceDesc dsd) {
+        return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
             @Override
             void process() throws IOException {
                 checkArgument(dsd != null, "the DataSourceDesc cannot be null");
@@ -351,15 +270,9 @@
         });
     }
 
-    /**
-     * Sets a single data source as described by a DataSourceDesc which will be played
-     * after current data source is finished.
-     *
-     * @param dsd the descriptor of data source you want to play after current one
-     */
     @Override
-    public void setNextDataSource(@NonNull DataSourceDesc dsd) {
-        addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
+    public Object setNextDataSource(@NonNull DataSourceDesc dsd) {
+        return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
             @Override
             void process() {
                 checkArgument(dsd != null, "the DataSourceDesc cannot be null");
@@ -374,14 +287,9 @@
         });
     }
 
-    /**
-     * Sets a list of data sources to be played sequentially after current data source is done.
-     *
-     * @param dsds the list of data sources you want to play after current one
-     */
     @Override
-    public void setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
-        addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCES, false) {
+    public Object setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
+        return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCES, false) {
             @Override
             void process() {
                 if (dsds == null || dsds.size() == 0) {
@@ -405,8 +313,8 @@
     }
 
     @Override
-    public void clearNextDataSources() {
-        addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
+    public Object clearNextDataSources() {
+        return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
             @Override
             void process() {
                 synchronized (mSrcLock) {
@@ -428,16 +336,11 @@
         }
     }
 
-    /**
-     * Configures the player to loop on the current data source.
-     * @param loop true if the current data source is meant to loop.
-     */
     @Override
-    public void loopCurrent(boolean loop) {
-        addTask(new Task(CALL_COMPLETED_LOOP_CURRENT, false) {
+    public Object loopCurrent(boolean loop) {
+        return addTask(new Task(CALL_COMPLETED_LOOP_CURRENT, false) {
             @Override
             void process() {
-                // TODO: set the looping mode, send notification
                 setLooping(loop);
             }
         });
@@ -445,57 +348,29 @@
 
     private native void setLooping(boolean looping);
 
-    /**
-     * Sets the volume of the audio of the media to play, expressed as a linear multiplier
-     * on the audio samples.
-     * Note that this volume is specific to the player, and is separate from stream volume
-     * used across the platform.<br>
-     * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
-     * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
-     * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
-     */
     @Override
-    public void setPlayerVolume(float volume) {
-        addTask(new Task(CALL_COMPLETED_SET_PLAYER_VOLUME, false) {
+    public Object setPlayerVolume(float volume) {
+        return addTask(new Task(CALL_COMPLETED_SET_PLAYER_VOLUME, false) {
             @Override
             void process() {
                 mVolume = volume;
-                _setVolume(volume);
+                native_setVolume(volume);
             }
         });
     }
 
-    private native void _setVolume(float volume);
+    private native void native_setVolume(float volume);
 
-    /**
-     * Returns the current volume of this player to this player.
-     * Note that it does not take into account the associated stream volume.
-     * @return the player volume.
-     */
     @Override
     public float getPlayerVolume() {
         return mVolume;
     }
 
-    /**
-     * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
-     */
     @Override
     public float getMaxPlayerVolume() {
         return 1.0f;
     }
 
-    private static final int NEXT_SOURCE_STATE_ERROR = -1;
-    private static final int NEXT_SOURCE_STATE_INIT = 0;
-    private static final int NEXT_SOURCE_STATE_PREPARING = 1;
-    private static final int NEXT_SOURCE_STATE_PREPARED = 2;
-
-    /*
-     * Update the MediaPlayer2Impl SurfaceTexture.
-     * Call after setting a new display surface.
-     */
-    private native void _setVideoSurface(Surface surface);
-
     /* Do not change these values (starting with INVOKE_ID) without updating
      * their counterparts in include/media/mediaplayer2.h!
      */
@@ -504,7 +379,6 @@
     private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3;
     private static final int INVOKE_ID_SELECT_TRACK = 4;
     private static final int INVOKE_ID_DESELECT_TRACK = 5;
-    private static final int INVOKE_ID_SET_VIDEO_SCALE_MODE = 6;
     private static final int INVOKE_ID_GET_SELECTED_TRACK = 7;
 
     /**
@@ -518,7 +392,7 @@
      * native player.
      */
     private PlayerMessage invoke(PlayerMessage msg) {
-        byte[] ret = _invoke(msg.toByteArray());
+        byte[] ret = native_invoke(msg.toByteArray());
         if (ret == null) {
             return null;
         }
@@ -529,11 +403,11 @@
         }
     }
 
-    private native byte[] _invoke(byte[] request);
+    private native byte[] native_invoke(byte[] request);
 
     @Override
-    public void notifyWhenCommandLabelReached(Object label) {
-        addTask(new Task(CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED, false) {
+    public Object notifyWhenCommandLabelReached(Object label) {
+        return addTask(new Task(CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED, false) {
             @Override
             void process() {
                 sendEvent(new EventNotifier() {
@@ -547,130 +421,52 @@
         });
     }
 
-    /**
-     * Sets the {@link SurfaceHolder} to use for displaying the video
-     * portion of the media.
-     *
-     * Either a surface holder or surface must be set if a display or video sink
-     * is needed.  Not calling this method or {@link #setSurface(Surface)}
-     * when playing back a video will result in only the audio track being played.
-     * A null surface holder or surface will result in only the audio track being
-     * played.
-     *
-     * @param sh the SurfaceHolder to use for video display
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released.
-     * @hide
-     */
     @Override
-    public void setDisplay(SurfaceHolder sh) {
-        mSurfaceHolder = sh;
-        Surface surface;
-        if (sh != null) {
-            surface = sh.getSurface();
-        } else {
-            surface = null;
-        }
-        _setVideoSurface(surface);
-        updateSurfaceScreenOn();
+    public Object setDisplay(SurfaceHolder sh) {
+        return addTask(new Task(CALL_COMPLETED_SET_DISPLAY, false) {
+            @Override
+            void process() {
+                mSurfaceHolder = sh;
+                Surface surface;
+                if (sh != null) {
+                    surface = sh.getSurface();
+                } else {
+                    surface = null;
+                }
+                native_setVideoSurface(surface);
+                updateSurfaceScreenOn();
+            }
+        });
     }
 
-    /**
-     * Sets the {@link Surface} to be used as the sink for the video portion of
-     * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but
-     * does not support {@link #setScreenOnWhilePlaying(boolean)}.  Setting a
-     * Surface will un-set any Surface or SurfaceHolder that was previously set.
-     * A null surface will result in only the audio track being played.
-     *
-     * If the Surface sends frames to a {@link SurfaceTexture}, the timestamps
-     * returned from {@link SurfaceTexture#getTimestamp()} will have an
-     * unspecified zero point.  These timestamps cannot be directly compared
-     * between different media sources, different instances of the same media
-     * source, or multiple runs of the same program.  The timestamp is normally
-     * monotonically increasing and is unaffected by time-of-day adjustments,
-     * but it is reset when the position is set.
-     *
-     * @param surface The {@link Surface} to be used for the video portion of
-     * the media.
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released.
-     */
     @Override
-    public void setSurface(Surface surface) {
-        addTask(new Task(CALL_COMPLETED_SET_SURFACE, false) {
+    public Object setSurface(Surface surface) {
+        return addTask(new Task(CALL_COMPLETED_SET_SURFACE, false) {
             @Override
             void process() {
                 if (mScreenOnWhilePlaying && surface != null) {
                     Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
                 }
                 mSurfaceHolder = null;
-                _setVideoSurface(surface);
+                native_setVideoSurface(surface);
                 updateSurfaceScreenOn();
             }
         });
     }
 
-    /**
-     * Sets video scaling mode. To make the target video scaling mode
-     * effective during playback, this method must be called after
-     * data source is set. If not called, the default video
-     * scaling mode is {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}.
-     *
-     * <p> The supported video scaling modes are:
-     * <ul>
-     * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}
-     * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING}
-     * </ul>
-     *
-     * @param mode target video scaling mode. Must be one of the supported
-     * video scaling modes; otherwise, IllegalArgumentException will be thrown.
-     *
-     * @see MediaPlayer2#VIDEO_SCALING_MODE_SCALE_TO_FIT
-     * @see MediaPlayer2#VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
-     * @hide
-     */
+    private native void native_setVideoSurface(Surface surface);
+
     @Override
-    public void setVideoScalingMode(int mode) {
-        addTask(new Task(CALL_COMPLETED_SET_VIDEO_SCALING_MODE, false) {
-            @Override
-            void process() {
-                if (!isVideoScalingModeSupported(mode)) {
-                    final String msg = "Scaling mode " + mode + " is not supported";
-                    throw new IllegalArgumentException(msg);
-                }
-                PlayerMessage request = PlayerMessage.newBuilder()
-                        .addValues(Value.newBuilder()
-                                .setInt32Value(INVOKE_ID_SET_VIDEO_SCALE_MODE))
-                        .addValues(Value.newBuilder().setInt32Value(mode))
-                        .build();
-                invoke(request);
-            }
-        });
+    public boolean cancelCommand(Object token) {
+        synchronized (mTaskLock) {
+            return mPendingTasks.remove(token);
+        }
     }
 
-    /**
-     * Discards all pending commands.
-     */
     @Override
     public void clearPendingCommands() {
-    }
-
-    private void addTask(Task task) {
         synchronized (mTaskLock) {
-            mPendingTasks.add(task);
-            processPendingTask_l();
-        }
-    }
-
-    @GuardedBy("mTaskLock")
-    private void processPendingTask_l() {
-        if (mCurrentTask != null) {
-            return;
-        }
-        if (!mPendingTasks.isEmpty()) {
-            Task task = mPendingTasks.remove(0);
-            mCurrentTask = task;
-            mTaskHandler.post(task);
+            mPendingTasks.clear();
         }
     }
 
@@ -897,9 +693,7 @@
             boolean isCurrent, long srcId, Media2DataSource dataSource,
             long startPos, long endPos);
 
-    /**
-     * @return true if there is a next data source, false otherwise.
-     */
+    // return true if there is a next data source, false otherwise.
     // This function should be always called on |mHandlerThread|.
     private boolean prepareNextDataSource() {
         if (Looper.myLooper() != mHandlerThread.getLooper()) {
@@ -997,30 +791,11 @@
 
     private native void nativePlayNextDataSource(long srcId);
 
-
-    private int getAudioStreamType() {
-        if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
-            mStreamType = _getAudioStreamType();
-        }
-        return mStreamType;
-    }
-
-    private native int _getAudioStreamType() throws IllegalStateException;
-
-
     //--------------------------------------------------------------------------
     // Explicit Routing
     //--------------------
     private AudioDeviceInfo mPreferredDevice = null;
 
-    /**
-     * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
-     * the output from this MediaPlayer2.
-     * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink or source.
-     *  If deviceInfo is null, default routing is restored.
-     * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
-     * does not correspond to a valid audio device.
-     */
     @Override
     public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) {
         if (deviceInfo != null && !deviceInfo.isSink()) {
@@ -1036,10 +811,6 @@
         return status;
     }
 
-    /**
-     * Returns the selected output specified by {@link #setPreferredDevice}. Note that this
-     * is not guaranteed to correspond to the actual device being used for playback.
-     */
     @Override
     public AudioDeviceInfo getPreferredDevice() {
         synchronized (this) {
@@ -1047,12 +818,6 @@
         }
     }
 
-    /**
-     * Returns an {@link AudioDeviceInfo} identifying the current routing of this MediaPlayer2
-     * Note: The query is only valid if the MediaPlayer2 is currently playing.
-     * If the player is not playing, the returned device can be null or correspond to previously
-     * selected device when the player was last active.
-     */
     @Override
     public AudioDeviceInfo getRoutedDevice() {
         int deviceId = native_getRoutedDeviceId();
@@ -1069,130 +834,83 @@
         return null;
     }
 
-    /*
-     * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
-     */
-    @GuardedBy("mRoutingChangeListeners")
-    private void enableNativeRoutingCallbacksLocked(boolean enabled) {
-        if (mRoutingChangeListeners.size() == 0) {
-            native_enableDeviceCallback(enabled);
-        }
-    }
-
-    /**
-     * The list of AudioRouting.OnRoutingChangedListener interfaces added (with
-     * {@link #addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, Handler)}
-     * by an app to receive (re)routing notifications.
-     */
-    @GuardedBy("mRoutingChangeListeners")
-    private ArrayMap<AudioRouting.OnRoutingChangedListener,
-            NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();
-
-    /**
-     * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
-     * changes on this MediaPlayer2.
-     * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
-     * notifications of rerouting events.
-     * @param handler  Specifies the {@link Handler} object for the thread on which to execute
-     * the callback. If <code>null</code>, the handler on the main looper will be used.
-     */
     @Override
     public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener,
             Handler handler) {
-        synchronized (mRoutingChangeListeners) {
-            if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
-                enableNativeRoutingCallbacksLocked(true);
-                mRoutingChangeListeners.put(
-                        listener, new NativeRoutingEventHandlerDelegate(this, listener,
-                                handler != null ? handler : mTaskHandler));
-            }
+        if (listener == null) {
+            throw new IllegalArgumentException("addOnRoutingChangedListener: listener is NULL");
         }
+        RoutingDelegate routingDelegate = new RoutingDelegate(this, listener, handler);
+        native_addDeviceCallback(routingDelegate);
     }
 
-    /**
-     * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added
-     * to receive rerouting notifications.
-     * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
-     * to remove.
-     */
     @Override
     public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) {
-        synchronized (mRoutingChangeListeners) {
-            if (mRoutingChangeListeners.containsKey(listener)) {
-                mRoutingChangeListeners.remove(listener);
-                enableNativeRoutingCallbacksLocked(false);
-            }
+        if (listener == null) {
+            throw new IllegalArgumentException("removeOnRoutingChangedListener: listener is NULL");
         }
+        native_removeDeviceCallback(listener);
     }
 
     private native final boolean native_setOutputDevice(int deviceId);
     private native final int native_getRoutedDeviceId();
-    private native final void native_enableDeviceCallback(boolean enabled);
+    private native void native_addDeviceCallback(RoutingDelegate rd);
+    private native void native_removeDeviceCallback(
+            AudioRouting.OnRoutingChangedListener listener);
 
-    /**
-     * Set the low-level power management behavior for this MediaPlayer2.  This
-     * can be used when the MediaPlayer2 is not playing through a SurfaceHolder
-     * set with {@link #setDisplay(SurfaceHolder)} and thus can use the
-     * high-level {@link #setScreenOnWhilePlaying(boolean)} feature.
-     *
-     * <p>This function has the MediaPlayer2 access the low-level power manager
-     * service to control the device's power usage while playing is occurring.
-     * The parameter is a combination of {@link android.os.PowerManager} wake flags.
-     * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
-     * permission.
-     * By default, no attempt is made to keep the device awake during playback.
-     *
-     * @param context the Context to use
-     * @param mode    the power/wake mode to set
-     * @see android.os.PowerManager
-     * @hide
-     */
     @Override
-    public void setWakeMode(Context context, int mode) {
-        boolean washeld = false;
+    public Object setWakeMode(Context context, int mode) {
+        return addTask(new Task(CALL_COMPLETED_SET_WAKE_MODE, false) {
+            @Override
+            void process() {
+                boolean washeld = false;
 
-        /* Disable persistant wakelocks in media player based on property */
-        if (SystemProperties.getBoolean("audio.offload.ignore_setawake", false) == true) {
-            Log.w(TAG, "IGNORING setWakeMode " + mode);
-            return;
-        }
+                if (mWakeLock != null) {
+                    if (mWakeLock.isHeld()) {
+                        washeld = true;
+                        mWakeLock.release();
+                    }
+                    mWakeLock = null;
+                }
 
-        if (mWakeLock != null) {
-            if (mWakeLock.isHeld()) {
-                washeld = true;
-                mWakeLock.release();
+                PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+                ActivityManager am =
+                        (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+                List<RunningAppProcessInfo> runningAppsProcInfo = am.getRunningAppProcesses();
+                int pid = android.os.Process.myPid();
+                String name = "pid " + String.valueOf(pid);
+                if (runningAppsProcInfo != null) {
+                    for (RunningAppProcessInfo procInfo : runningAppsProcInfo) {
+                        if (procInfo.pid == pid) {
+                            name = procInfo.processName;
+                            break;
+                        }
+                    }
+                }
+                mWakeLock = pm.newWakeLock(mode | PowerManager.ON_AFTER_RELEASE, name);
+                mWakeLock.setReferenceCounted(false);
+                if (washeld) {
+                    mWakeLock.acquire();
+                }
             }
-            mWakeLock = null;
-        }
-
-        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = pm.newWakeLock(mode|PowerManager.ON_AFTER_RELEASE, MediaPlayer2Impl.class.getName());
-        mWakeLock.setReferenceCounted(false);
-        if (washeld) {
-            mWakeLock.acquire();
-        }
+        });
     }
 
-    /**
-     * Control whether we should use the attached SurfaceHolder to keep the
-     * screen on while video playback is occurring.  This is the preferred
-     * method over {@link #setWakeMode} where possible, since it doesn't
-     * require that the application have permission for low-level wake lock
-     * access.
-     *
-     * @param screenOn Supply true to keep the screen on, false to allow it
-     * to turn off.
-     * @hide
-     */
     @Override
-    public void setScreenOnWhilePlaying(boolean screenOn) {
-        if (mScreenOnWhilePlaying != screenOn) {
-            if (screenOn && mSurfaceHolder == null) {
-                Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective without a SurfaceHolder");
+    public Object setScreenOnWhilePlaying(boolean screenOn) {
+        return addTask(new Task(CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING, false) {
+            @Override
+            void process() {
+                if (mScreenOnWhilePlaying != screenOn) {
+                    if (screenOn && mSurfaceHolder == null) {
+                        Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective"
+                                + " without a SurfaceHolder");
+                    }
+                    mScreenOnWhilePlaying = screenOn;
+                    updateSurfaceScreenOn();
+                }
             }
-            mScreenOnWhilePlaying = screenOn;
-            updateSurfaceScreenOn();
-        }
+        });
     }
 
     private void stayAwake(boolean awake) {
@@ -1213,42 +931,11 @@
         }
     }
 
-    /**
-     * Returns the width of the video.
-     *
-     * @return the width of the video, or 0 if there is no video,
-     * no display surface was set, or the width has not been determined
-     * yet. The {@code EventCallback} can be registered via
-     * {@link #setEventCallback(Executor, EventCallback)} to provide a
-     * notification {@code EventCallback.onVideoSizeChanged} when the width
-     * is available.
-     */
     @Override
-    public native int getVideoWidth();
+    public VideoSize getVideoSize() {
+        return mVideoSize;
+    }
 
-    /**
-     * Returns the height of the video.
-     *
-     * @return the height of the video, or 0 if there is no video,
-     * no display surface was set, or the height has not been determined
-     * yet. The {@code EventCallback} can be registered via
-     * {@link #setEventCallback(Executor, EventCallback)} to provide a
-     * notification {@code EventCallback.onVideoSizeChanged} when the height
-     * is available.
-     */
-    @Override
-    public native int getVideoHeight();
-
-    /**
-     * Return Metrics data about the current player.
-     *
-     * @return a {@link PersistableBundle} containing the set of attributes and values
-     * available for the media being handled by this instance of MediaPlayer2
-     * The attributes are descibed in {@link MetricsConstants}.
-     *
-     *  Additional vendor-specific fields may also be present in
-     *  the return value.
-     */
     @Override
     public PersistableBundle getMetrics() {
         PersistableBundle bundle = native_getMetrics();
@@ -1257,48 +944,16 @@
 
     private native PersistableBundle native_getMetrics();
 
-    /**
-     * Checks whether the MediaPlayer2 is playing.
-     *
-     * @return true if currently playing, false otherwise
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released.
-     * @hide
-     */
     @Override
     public native boolean isPlaying();
 
-    /**
-     * Gets the current buffering management params used by the source component.
-     * Calling it only after {@code setDataSource} has been called.
-     * Each type of data source might have different set of default params.
-     *
-     * @return the current buffering management params used by the source component.
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized, or {@code setDataSource} has not been called.
-     * @hide
-     */
     @Override
     @NonNull
     public native BufferingParams getBufferingParams();
 
-    /**
-     * Sets buffering management params.
-     * The object sets its internal BufferingParams to the input, except that the input is
-     * invalid or not supported.
-     * Call it only after {@code setDataSource} has been called.
-     * The input is a hint to MediaPlayer2.
-     *
-     * @param params the buffering management params.
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released, or {@code setDataSource} has not been called.
-     * @throws IllegalArgumentException if params is invalid or not supported.
-     * @hide
-     */
     @Override
-    public void setBufferingParams(@NonNull BufferingParams params) {
-        addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
+    public Object setBufferingParams(@NonNull BufferingParams params) {
+        return addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
             @Override
             void process() {
                 checkArgument(params != null, "the BufferingParams cannot be null");
@@ -1345,24 +1000,9 @@
         return params;
     }
 
-    /**
-     * Sets playback rate using {@link PlaybackParams}. The object sets its internal
-     * PlaybackParams to the input, except that the object remembers previous speed
-     * when input speed is zero. This allows the object to resume at previous speed
-     * when play() is called. Calling it before the object is prepared does not change
-     * the object state. After the object is prepared, calling it with zero speed is
-     * equivalent to calling pause(). After the object is prepared, calling it with
-     * non-zero speed is equivalent to calling play().
-     *
-     * @param params the playback params.
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released.
-     * @throws IllegalArgumentException if params is not supported.
-     */
     @Override
-    public void setPlaybackParams(@NonNull PlaybackParams params) {
-        addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
+    public Object setPlaybackParams(@NonNull PlaybackParams params) {
+        return addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
             @Override
             void process() {
                 checkArgument(params != null, "the PlaybackParams cannot be null");
@@ -1394,8 +1034,8 @@
      * @throws IllegalArgumentException if params are not supported.
      */
     @Override
-    public void setSyncParams(@NonNull SyncParams params) {
-        addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
+    public Object setSyncParams(@NonNull SyncParams params) {
+        return addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
             @Override
             void process() {
                 checkArgument(params != null, "the SyncParams cannot be null");
@@ -1449,8 +1089,8 @@
      * @throws IllegalArgumentException if the mode is invalid.
      */
     @Override
-    public void seekTo(final long msec, @SeekMode int mode) {
-        addTask(new Task(CALL_COMPLETED_SEEK_TO, true) {
+    public Object seekTo(final long msec, @SeekMode int mode) {
+        return addTask(new Task(CALL_COMPLETED_SEEK_TO, true) {
             @Override
             void process() {
                 if (mode < SEEK_PREVIOUS_SYNC || mode > SEEK_CLOSEST) {
@@ -1582,8 +1222,8 @@
      * @throws IllegalArgumentException if the sessionId is invalid.
      */
     @Override
-    public void setAudioSessionId(int sessionId) {
-        addTask(new Task(CALL_COMPLETED_SET_AUDIO_SESSION_ID, false) {
+    public Object setAudioSessionId(int sessionId) {
+        return addTask(new Task(CALL_COMPLETED_SET_AUDIO_SESSION_ID, false) {
             @Override
             void process() {
                 _setAudioSessionId(sessionId);
@@ -1617,8 +1257,8 @@
      * @param effectId system wide unique id of the effect to attach
      */
     @Override
-    public void attachAuxEffect(int effectId) {
-        addTask(new Task(CALL_COMPLETED_ATTACH_AUX_EFFECT, false) {
+    public Object attachAuxEffect(int effectId) {
+        return addTask(new Task(CALL_COMPLETED_ATTACH_AUX_EFFECT, false) {
             @Override
             void process() {
                 _attachAuxEffect(effectId);
@@ -1641,8 +1281,8 @@
      * @param level send level scalar
      */
     @Override
-    public void setAuxEffectSendLevel(float level) {
-        addTask(new Task(CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL, false) {
+    public Object setAuxEffectSendLevel(float level) {
+        return addTask(new Task(CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL, false) {
             @Override
             void process() {
                 _setAuxEffectSendLevel(level);
@@ -1858,8 +1498,8 @@
      * @see android.media.MediaPlayer2#getTrackInfo
      */
     @Override
-    public void selectTrack(int index) {
-        addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
+    public Object selectTrack(int index) {
+        return addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
             @Override
             void process() {
                 selectOrDeselectTrack(index, true /* select */);
@@ -1882,8 +1522,8 @@
      * @see android.media.MediaPlayer2#getTrackInfo
      */
     @Override
-    public void deselectTrack(int index) {
-        addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
+    public Object deselectTrack(int index) {
+        return addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
             @Override
             void process() {
                 selectOrDeselectTrack(index, false /* select */);
@@ -1956,7 +1596,6 @@
     private static final int MEDIA_SUBTITLE_DATA = 201;
     private static final int MEDIA_META_DATA = 202;
     private static final int MEDIA_DRM_INFO = 210;
-    private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000;
 
     private class TaskHandler extends Handler {
         private MediaPlayer2Impl mMediaPlayer;
@@ -1995,80 +1634,175 @@
             }
 
             switch(msg.what) {
-            case MEDIA_PREPARED:
-            {
-                if (dsd != null) {
+                case MEDIA_PREPARED:
+                {
+                    if (dsd != null) {
+                        sendEvent(new EventNotifier() {
+                            @Override
+                            public void notify(EventCallback callback) {
+                                callback.onInfo(
+                                        mMediaPlayer, dsd, MEDIA_INFO_PREPARED, 0);
+                            }
+                        });
+                    }
+
+                    synchronized (mSrcLock) {
+                        Log.i(TAG, "MEDIA_PREPARED: srcId=" + srcId
+                                + ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
+
+                        if (isCurrentSrcId) {
+                            prepareNextDataSource();
+                        } else if (isNextSrcId) {
+                            mNextSourceState = NEXT_SOURCE_STATE_PREPARED;
+                            if (mNextSourcePlayPending) {
+                                playNextDataSource();
+                            }
+                        }
+                    }
+
+                    synchronized (mTaskLock) {
+                        if (mCurrentTask != null
+                                && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE
+                                && mCurrentTask.mDSD == dsd
+                                && mCurrentTask.mNeedToWaitForEventToComplete) {
+                            mCurrentTask.sendCompleteNotification(CALL_STATUS_NO_ERROR);
+                            mCurrentTask = null;
+                            processPendingTask_l();
+                        }
+                    }
+                    return;
+                }
+
+                case MEDIA_DRM_INFO:
+                {
+                    if (msg.obj == null) {
+                        Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
+                    } else if (msg.obj instanceof byte[]) {
+                        // The PlayerMessage was parsed already in postEventFromNative
+                        final DrmInfoImpl drmInfo;
+
+                        synchronized (mDrmLock) {
+                            if (mDrmInfoImpl != null) {
+                                drmInfo = mDrmInfoImpl.makeCopy();
+                            } else {
+                                drmInfo = null;
+                            }
+                        }
+
+                        // notifying the client outside the lock
+                        if (drmInfo != null) {
+                            sendDrmEvent(new DrmEventNotifier() {
+                                @Override
+                                public void notify(DrmEventCallback callback) {
+                                    callback.onDrmInfo(
+                                            mMediaPlayer, dsd, drmInfo);
+                                }
+                            });
+                        }
+                    } else {
+                        Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + msg.obj);
+                    }
+                    return;
+                }
+
+                case MEDIA_PLAYBACK_COMPLETE:
+                {
+                    if (isCurrentSrcId) {
+                        sendEvent(new EventNotifier() {
+                            @Override
+                            public void notify(EventCallback callback) {
+                                callback.onInfo(
+                                        mMediaPlayer, dsd, MEDIA_INFO_DATA_SOURCE_END, 0);
+                            }
+                        });
+                        stayAwake(false);
+
+                        synchronized (mSrcLock) {
+                            mNextSourcePlayPending = true;
+
+                            Log.i(TAG, "MEDIA_PLAYBACK_COMPLETE: srcId=" + srcId
+                                    + ", currentSrcId=" + mCurrentSrcId
+                                    + ", nextSrcId=" + mNextSrcId);
+                        }
+
+                        playNextDataSource();
+                    }
+
+                    return;
+                }
+
+                case MEDIA_STOPPED:
+                case MEDIA_STARTED:
+                case MEDIA_PAUSED:
+                case MEDIA_SKIPPED:
+                case MEDIA_NOTIFY_TIME:
+                {
+                    // Do nothing. The client should have enough information with
+                    // {@link EventCallback#onMediaTimeDiscontinuity}.
+                    break;
+                }
+
+                case MEDIA_BUFFERING_UPDATE:
+                {
+                    final int percent = msg.arg1;
                     sendEvent(new EventNotifier() {
                         @Override
                         public void notify(EventCallback callback) {
                             callback.onInfo(
-                                    mMediaPlayer, dsd, MEDIA_INFO_PREPARED, 0);
+                                    mMediaPlayer, dsd, MEDIA_INFO_BUFFERING_UPDATE, percent);
                         }
                     });
-                }
 
-                synchronized (mSrcLock) {
-                    Log.i(TAG, "MEDIA_PREPARED: srcId=" + srcId
-                            + ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
-
-                    if (isCurrentSrcId) {
-                        prepareNextDataSource();
-                    } else if (isNextSrcId) {
-                        mNextSourceState = NEXT_SOURCE_STATE_PREPARED;
-                        if (mNextSourcePlayPending) {
-                            playNextDataSource();
+                    synchronized (mSrcLock) {
+                        if (isCurrentSrcId) {
+                            mBufferedPercentageCurrent.set(percent);
+                        } else if (isNextSrcId) {
+                            mBufferedPercentageNext.set(percent);
                         }
                     }
+                    return;
                 }
 
-                synchronized (mTaskLock) {
-                    if (mCurrentTask != null
-                            && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE
-                            && mCurrentTask.mDSD == dsd
-                            && mCurrentTask.mNeedToWaitForEventToComplete) {
-                        mCurrentTask.sendCompleteNotification(CALL_STATUS_NO_ERROR);
-                        mCurrentTask = null;
-                        processPendingTask_l();
-                    }
-                }
-                return;
-            }
-
-            case MEDIA_DRM_INFO:
-            {
-                if (msg.obj == null) {
-                    Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
-                } else if (msg.obj instanceof byte[]) {
-                    // The PlayerMessage was parsed already in postEventFromNative
-                    final DrmInfoImpl drmInfo;
-
-                    synchronized (mDrmLock) {
-                        if (mDrmInfoImpl != null) {
-                            drmInfo = mDrmInfoImpl.makeCopy();
-                        } else {
-                            drmInfo = null;
+                case MEDIA_SEEK_COMPLETE:
+                {
+                    synchronized (mTaskLock) {
+                        if (mCurrentTask != null
+                                && mCurrentTask.mMediaCallType == CALL_COMPLETED_SEEK_TO
+                                && mCurrentTask.mNeedToWaitForEventToComplete) {
+                            mCurrentTask.sendCompleteNotification(CALL_STATUS_NO_ERROR);
+                            mCurrentTask = null;
+                            processPendingTask_l();
                         }
                     }
-
-                    // notifying the client outside the lock
-                    if (drmInfo != null) {
-                        sendDrmEvent(new DrmEventNotifier() {
-                            @Override
-                            public void notify(DrmEventCallback callback) {
-                                callback.onDrmInfo(
-                                        mMediaPlayer, dsd, drmInfo);
-                            }
-                        });
-                    }
-                } else {
-                    Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + msg.obj);
+                    return;
                 }
-                return;
-            }
 
-            case MEDIA_PLAYBACK_COMPLETE:
-            {
-                if (isCurrentSrcId) {
+                case MEDIA_SET_VIDEO_SIZE:
+                {
+                    final int width = msg.arg1;
+                    final int height = msg.arg2;
+
+                    mVideoSize = new VideoSize(width, height);
+                    sendEvent(new EventNotifier() {
+                        @Override
+                        public void notify(EventCallback callback) {
+                            callback.onVideoSizeChanged(
+                                    mMediaPlayer, dsd, mVideoSize);
+                        }
+                    });
+                    return;
+                }
+
+                case MEDIA_ERROR:
+                {
+                    Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
+                    sendEvent(new EventNotifier() {
+                        @Override
+                        public void notify(EventCallback callback) {
+                            callback.onError(
+                                    mMediaPlayer, dsd, what, extra);
+                        }
+                    });
                     sendEvent(new EventNotifier() {
                         @Override
                         public void notify(EventCallback callback) {
@@ -2077,231 +1811,127 @@
                         }
                     });
                     stayAwake(false);
-
-                    synchronized (mSrcLock) {
-                        mNextSourcePlayPending = true;
-
-                        Log.i(TAG, "MEDIA_PLAYBACK_COMPLETE: srcId=" + srcId
-                                + ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
-                    }
-
-                    playNextDataSource();
+                    return;
                 }
 
-                return;
-            }
-
-            case MEDIA_STOPPED:
-            case MEDIA_STARTED:
-            case MEDIA_PAUSED:
-            case MEDIA_SKIPPED:
-            case MEDIA_NOTIFY_TIME:
-            {
-                // Do nothing. The client should have enough information with
-                // {@link EventCallback#onMediaTimeDiscontinuity}.
-                break;
-            }
-
-            case MEDIA_BUFFERING_UPDATE:
-            {
-                final int percent = msg.arg1;
-                sendEvent(new EventNotifier() {
-                    @Override
-                    public void notify(EventCallback callback) {
-                        callback.onInfo(
-                                mMediaPlayer, dsd, MEDIA_INFO_BUFFERING_UPDATE, percent);
+                case MEDIA_INFO:
+                {
+                    switch (msg.arg1) {
+                        case MEDIA_INFO_VIDEO_TRACK_LAGGING:
+                            Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
+                            break;
                     }
-                });
 
-                synchronized (mSrcLock) {
-                    if (isCurrentSrcId) {
-                        mBufferedPercentageCurrent.set(percent);
-                    } else if (isNextSrcId) {
-                        mBufferedPercentageNext.set(percent);
-                    }
-                }
-                return;
-            }
-
-            case MEDIA_SEEK_COMPLETE:
-            {
-                synchronized (mTaskLock) {
-                    if (mCurrentTask != null
-                            && mCurrentTask.mMediaCallType == CALL_COMPLETED_SEEK_TO
-                            && mCurrentTask.mNeedToWaitForEventToComplete) {
-                        mCurrentTask.sendCompleteNotification(CALL_STATUS_NO_ERROR);
-                        mCurrentTask = null;
-                        processPendingTask_l();
-                    }
-                }
-                return;
-            }
-
-            case MEDIA_SET_VIDEO_SIZE:
-            {
-                final int width = msg.arg1;
-                final int height = msg.arg2;
-                sendEvent(new EventNotifier() {
-                    @Override
-                    public void notify(EventCallback callback) {
-                        callback.onVideoSizeChanged(
-                                mMediaPlayer, dsd, width, height);
-                    }
-                });
-                return;
-            }
-
-            case MEDIA_ERROR:
-            {
-                Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
-                sendEvent(new EventNotifier() {
-                    @Override
-                    public void notify(EventCallback callback) {
-                        callback.onError(
-                                mMediaPlayer, dsd, what, extra);
-                    }
-                });
-                sendEvent(new EventNotifier() {
-                    @Override
-                    public void notify(EventCallback callback) {
-                        callback.onInfo(
-                                mMediaPlayer, dsd, MEDIA_INFO_DATA_SOURCE_END, 0);
-                    }
-                });
-                stayAwake(false);
-                return;
-            }
-
-            case MEDIA_INFO:
-            {
-                switch (msg.arg1) {
-                    case MEDIA_INFO_VIDEO_TRACK_LAGGING:
-                        Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
-                        break;
-                }
-
-                sendEvent(new EventNotifier() {
-                    @Override
-                    public void notify(EventCallback callback) {
-                        callback.onInfo(
-                                mMediaPlayer, dsd, what, extra);
-                    }
-                });
-
-                if (msg.arg1 == MEDIA_INFO_DATA_SOURCE_START) {
-                    if (isCurrentSrcId) {
-                        prepareNextDataSource();
-                    }
-                }
-
-                // No real default action so far.
-                return;
-            }
-
-            case MEDIA_TIMED_TEXT:
-            {
-                final TimedText text;
-                if (msg.obj instanceof byte[]) {
-                    PlayerMessage playerMsg;
-                    try {
-                        playerMsg = PlayerMessage.parseFrom((byte[]) msg.obj);
-                    } catch (InvalidProtocolBufferException e) {
-                        Log.w(TAG, "Failed to parse timed text.", e);
-                        return;
-                    }
-                    text = TimedTextUtil.parsePlayerMessage(playerMsg);
-                } else {
-                    text = null;
-                }
-
-                sendEvent(new EventNotifier() {
-                    @Override
-                    public void notify(EventCallback callback) {
-                        callback.onTimedText(
-                                mMediaPlayer, dsd, text);
-                    }
-                });
-                return;
-            }
-
-            case MEDIA_SUBTITLE_DATA:
-            {
-                if (msg.obj instanceof byte[]) {
-                    PlayerMessage playerMsg;
-                    try {
-                        playerMsg = PlayerMessage.parseFrom((byte[]) msg.obj);
-                    } catch (InvalidProtocolBufferException e) {
-                        Log.w(TAG, "Failed to parse subtitle data.", e);
-                        return;
-                    }
-                    Iterator<Value> in = playerMsg.getValuesList().iterator();
-                    SubtitleData data = new SubtitleData(
-                            in.next().getInt32Value(),  // trackIndex
-                            in.next().getInt64Value(),  // startTimeUs
-                            in.next().getInt64Value(),  // durationUs
-                            in.next().getBytesValue().toByteArray());  // data
                     sendEvent(new EventNotifier() {
                         @Override
                         public void notify(EventCallback callback) {
-                            callback.onSubtitleData(
+                            callback.onInfo(
+                                    mMediaPlayer, dsd, what, extra);
+                        }
+                    });
+
+                    if (msg.arg1 == MEDIA_INFO_DATA_SOURCE_START) {
+                        if (isCurrentSrcId) {
+                            prepareNextDataSource();
+                        }
+                    }
+
+                    // No real default action so far.
+                    return;
+                }
+
+                case MEDIA_TIMED_TEXT:
+                {
+                    final TimedText text;
+                    if (msg.obj instanceof byte[]) {
+                        PlayerMessage playerMsg;
+                        try {
+                            playerMsg = PlayerMessage.parseFrom((byte[]) msg.obj);
+                        } catch (InvalidProtocolBufferException e) {
+                            Log.w(TAG, "Failed to parse timed text.", e);
+                            return;
+                        }
+                        text = TimedTextUtil.parsePlayerMessage(playerMsg);
+                    } else {
+                        text = null;
+                    }
+
+                    sendEvent(new EventNotifier() {
+                        @Override
+                        public void notify(EventCallback callback) {
+                            callback.onTimedText(
+                                    mMediaPlayer, dsd, text);
+                        }
+                    });
+                    return;
+                }
+
+                case MEDIA_SUBTITLE_DATA:
+                {
+                    if (msg.obj instanceof byte[]) {
+                        PlayerMessage playerMsg;
+                        try {
+                            playerMsg = PlayerMessage.parseFrom((byte[]) msg.obj);
+                        } catch (InvalidProtocolBufferException e) {
+                            Log.w(TAG, "Failed to parse subtitle data.", e);
+                            return;
+                        }
+                        Iterator<Value> in = playerMsg.getValuesList().iterator();
+                        SubtitleData data = new SubtitleData(
+                                in.next().getInt32Value(),  // trackIndex
+                                in.next().getInt64Value(),  // startTimeUs
+                                in.next().getInt64Value(),  // durationUs
+                                in.next().getBytesValue().toByteArray());  // data
+                        sendEvent(new EventNotifier() {
+                            @Override
+                            public void notify(EventCallback callback) {
+                                callback.onSubtitleData(
+                                        mMediaPlayer, dsd, data);
+                            }
+                        });
+                    }
+                    return;
+                }
+
+                case MEDIA_META_DATA:
+                {
+                    final TimedMetaData data;
+                    if (msg.obj instanceof byte[]) {
+                        PlayerMessage playerMsg;
+                        try {
+                            playerMsg = PlayerMessage.parseFrom((byte[]) msg.obj);
+                        } catch (InvalidProtocolBufferException e) {
+                            Log.w(TAG, "Failed to parse timed meta data.", e);
+                            return;
+                        }
+                        Iterator<Value> in = playerMsg.getValuesList().iterator();
+                        data = new TimedMetaData(
+                                in.next().getInt64Value(),  // timestampUs
+                                in.next().getBytesValue().toByteArray());  // metaData
+                    } else {
+                        data = null;
+                    }
+
+                    sendEvent(new EventNotifier() {
+                        @Override
+                        public void notify(EventCallback callback) {
+                            callback.onTimedMetaDataAvailable(
                                     mMediaPlayer, dsd, data);
                         }
                     });
-                }
-                return;
-            }
-
-            case MEDIA_META_DATA:
-            {
-                final TimedMetaData data;
-                if (msg.obj instanceof byte[]) {
-                    PlayerMessage playerMsg;
-                    try {
-                        playerMsg = PlayerMessage.parseFrom((byte[]) msg.obj);
-                    } catch (InvalidProtocolBufferException e) {
-                        Log.w(TAG, "Failed to parse timed meta data.", e);
-                        return;
-                    }
-                    Iterator<Value> in = playerMsg.getValuesList().iterator();
-                    data = new TimedMetaData(
-                            in.next().getInt64Value(),  // timestampUs
-                            in.next().getBytesValue().toByteArray());  // metaData
-                } else {
-                    data = null;
+                    return;
                 }
 
-                sendEvent(new EventNotifier() {
-                    @Override
-                    public void notify(EventCallback callback) {
-                        callback.onTimedMetaDataAvailable(
-                                mMediaPlayer, dsd, data);
-                    }
-                });
-                return;
-            }
-
-            case MEDIA_NOP: // interface test message - ignore
-            {
-                break;
-            }
-
-            case MEDIA_AUDIO_ROUTING_CHANGED:
-            {
-                AudioManager.resetAudioPortGeneration();
-                synchronized (mRoutingChangeListeners) {
-                    for (NativeRoutingEventHandlerDelegate delegate
-                            : mRoutingChangeListeners.values()) {
-                        delegate.notifyClient();
-                    }
+                case MEDIA_NOP: // interface test message - ignore
+                {
+                    break;
                 }
-                return;
-            }
 
-            default:
-            {
-                Log.e(TAG, "Unknown message type " + msg.what);
-                return;
-            }
+                default:
+                {
+                    Log.e(TAG, "Unknown message type " + msg.what);
+                    return;
+                }
             }
         }
     }
@@ -2409,7 +2039,7 @@
         }
     }
 
-    public static void checkArgument(boolean expression, String errorMessage) {
+    private static void checkArgument(boolean expression, String errorMessage) {
         if (!expression) {
             throw new IllegalArgumentException(errorMessage);
         }
@@ -2473,15 +2103,8 @@
     private ArrayList<Pair<Executor, DrmEventCallback> > mDrmEventCallbackRecords
         = new ArrayList<Pair<Executor, DrmEventCallback> >();
 
-    /**
-     * Register a callback to be invoked when the media source is ready
-     * for playback.
-     *
-     * @param eventCallback the callback that will be run
-     * @param executor the executor through which the callback should be invoked
-     */
     @Override
-    public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
+    public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull DrmEventCallback eventCallback) {
         if (eventCallback == null) {
             throw new IllegalArgumentException("Illegal null EventCallback");
@@ -2495,17 +2118,17 @@
         }
     }
 
-    /**
-     * Clears the {@link DrmEventCallback}.
-     */
     @Override
-    public void clearDrmEventCallback() {
+    public void unregisterDrmEventCallback(DrmEventCallback eventCallback) {
         synchronized (mDrmEventCbLock) {
-            mDrmEventCallbackRecords.clear();
+            for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+                if (cb.second == eventCallback) {
+                    mDrmEventCallbackRecords.remove(cb);
+                }
+            }
         }
     }
 
-
     /**
      * Retrieves the DRM Info associated with the current source
      *
@@ -2532,54 +2155,80 @@
         return drmInfo;
     }
 
-
-    /**
-     * Prepares the DRM for the current source
-     * <p>
-     * If {@code OnDrmConfigHelper} is registered, it will be called during
-     * preparation to allow configuration of the DRM properties before opening the
-     * DRM session. Note that the callback is called synchronously in the thread that called
-     * {@code prepareDrm}. It should be used only for a series of {@code getDrmPropertyString}
-     * and {@code setDrmPropertyString} calls and refrain from any lengthy operation.
-     * <p>
-     * If the device has not been provisioned before, this call also provisions the device
-     * which involves accessing the provisioning server and can take a variable time to
-     * complete depending on the network connectivity.
-     * If {@code OnDrmPreparedListener} is registered, prepareDrm() runs in non-blocking
-     * mode by launching the provisioning in the background and returning. The listener
-     * will be called when provisioning and preparation has finished. If a
-     * {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning
-     * and preparation has finished, i.e., runs in blocking mode.
-     * <p>
-     * If {@code OnDrmPreparedListener} is registered, it is called to indicate the DRM
-     * session being ready. The application should not make any assumption about its call
-     * sequence (e.g., before or after prepareDrm returns), or the thread context that will
-     * execute the listener (unless the listener is registered with a handler thread).
-     * <p>
-     *
-     * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved
-     * from the source through {@code getDrmInfo} or registering a {@code onDrmInfoListener}.
-     *
-     * @throws IllegalStateException              if called before prepare(), or the DRM was
-     *                                            prepared already
-     * @throws UnsupportedSchemeException         if the crypto scheme is not supported
-     * @throws ResourceBusyException              if required DRM resources are in use
-     * @throws ProvisioningNetworkErrorException  if provisioning is required but failed due to a
-     *                                            network error
-     * @throws ProvisioningServerErrorException   if provisioning is required but failed due to
-     *                                            the request denied by the provisioning server
-     */
     @Override
-    public void prepareDrm(@NonNull UUID uuid)
+    public Object prepareDrm(@NonNull UUID uuid) {
+        return addTask(new Task(CALL_COMPLETED_PREPARE_DRM, true) {
+            @Override
+            void process() {
+                int status = PREPARE_DRM_STATUS_SUCCESS;
+                boolean sendEvent = true;
+
+                try {
+                    doPrepareDrm(uuid);
+                } catch (ResourceBusyException e) {
+                    status = PREPARE_DRM_STATUS_RESOURCE_BUSY;
+                } catch (UnsupportedSchemeException e) {
+                    status = PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME;
+                } catch (NotProvisionedException e) {
+                    Log.w(TAG, "prepareDrm: NotProvisionedException");
+
+                    // handle provisioning internally; it'll reset mPrepareDrmInProgress
+                    status = HandleProvisioninig(uuid);
+
+                    if (status == PREPARE_DRM_STATUS_SUCCESS) {
+                        // DrmEventCallback will be fired in provisioning
+                        sendEvent = false;
+                    } else {
+                        synchronized (mDrmLock) {
+                            cleanDrmObj();
+                        }
+
+                        switch (status) {
+                        case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR:
+                            Log.e(TAG, "prepareDrm: Provisioning was required but failed " +
+                                    "due to a network error.");
+                            break;
+
+                        case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR:
+                            Log.e(TAG, "prepareDrm: Provisioning was required but the request " +
+                                    "was denied by the server.");
+                            break;
+
+                        case PREPARE_DRM_STATUS_PREPARATION_ERROR:
+                        default:
+                            Log.e(TAG, "prepareDrm: Post-provisioning preparation failed.");
+                            break;
+                        }
+                    }
+                } catch (Exception e) {
+                    status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
+                }
+
+                if (sendEvent) {
+                    final int prepareDrmStatus = status;
+                    sendDrmEvent(new DrmEventNotifier() {
+                        @Override
+                        public void notify(DrmEventCallback callback) {
+                            callback.onDrmPrepared(
+                                    MediaPlayer2Impl.this, mCurrentDSD, prepareDrmStatus);
+                        }
+                    });
+
+                    synchronized (mTaskLock) {
+                        mCurrentTask = null;
+                        processPendingTask_l();
+                    }
+                }
+            }
+        });
+    }
+
+    private void doPrepareDrm(@NonNull UUID uuid)
             throws UnsupportedSchemeException, ResourceBusyException,
-                   ProvisioningNetworkErrorException, ProvisioningServerErrorException
-    {
+                   NotProvisionedException {
         Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper);
 
-        boolean allDoneWithoutProvisioning = false;
-
         synchronized (mDrmLock) {
-
             // only allowing if tied to a protected source; might relax for releasing offline keys
             if (mDrmInfoImpl == null) {
                 final String msg = "prepareDrm(): Wrong usage: The player must be prepared and " +
@@ -2623,8 +2272,7 @@
             }
 
             mDrmConfigAllowed = true;
-        }   // synchronized
-
+        }  // synchronized
 
         // call the callback outside the lock
         if (mOnDrmConfigHelper != null)  {
@@ -2640,127 +2288,64 @@
 
                 mDrmUUID = uuid;
                 mActiveDrmScheme = true;
-
-                allDoneWithoutProvisioning = true;
+                mPrepareDrmInProgress = false;
             } catch (IllegalStateException e) {
                 final String msg = "prepareDrm(): Wrong usage: The player must be " +
                         "in the prepared state to call prepareDrm().";
                 Log.e(TAG, msg);
                 earlyExit = true;
+                mPrepareDrmInProgress = false;
                 throw new IllegalStateException(msg);
             } catch (NotProvisionedException e) {
-                Log.w(TAG, "prepareDrm: NotProvisionedException");
-
-                // handle provisioning internally; it'll reset mPrepareDrmInProgress
-                int result = HandleProvisioninig(uuid);
-
-                // if blocking mode, we're already done;
-                // if non-blocking mode, we attempted to launch background provisioning
-                if (result != PREPARE_DRM_STATUS_SUCCESS) {
-                    earlyExit = true;
-                    String msg;
-
-                    switch (result) {
-                    case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR:
-                        msg = "prepareDrm: Provisioning was required but failed " +
-                                "due to a network error.";
-                        Log.e(TAG, msg);
-                        throw new ProvisioningNetworkErrorExceptionImpl(msg);
-
-                    case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR:
-                        msg = "prepareDrm: Provisioning was required but the request " +
-                                "was denied by the server.";
-                        Log.e(TAG, msg);
-                        throw new ProvisioningServerErrorExceptionImpl(msg);
-
-                    case PREPARE_DRM_STATUS_PREPARATION_ERROR:
-                    default: // default for safeguard
-                        msg = "prepareDrm: Post-provisioning preparation failed.";
-                        Log.e(TAG, msg);
-                        throw new IllegalStateException(msg);
-                    }
-                }
-                // nothing else to do;
-                // if blocking or non-blocking, HandleProvisioninig does the re-attempt & cleanup
+                Log.w(TAG, "prepareDrm: NotProvisionedException", e);
+                throw e;
             } catch (Exception e) {
                 Log.e(TAG, "prepareDrm: Exception " + e);
                 earlyExit = true;
+                mPrepareDrmInProgress = false;
                 throw e;
             } finally {
-                if (!mDrmProvisioningInProgress) {// if early exit other than provisioning exception
-                    mPrepareDrmInProgress = false;
-                }
-                if (earlyExit) {    // cleaning up object if didn't succeed
+                if (earlyExit) {  // clean up object if didn't succeed
                     cleanDrmObj();
                 }
-            } // finally
-        }   // synchronized
-
-
-        // if finished successfully without provisioning, call the callback outside the lock
-        if (allDoneWithoutProvisioning) {
-            sendDrmEvent(new DrmEventNotifier() {
-                @Override
-                public void notify(DrmEventCallback callback) {
-                    callback.onDrmPrepared(
-                            MediaPlayer2Impl.this, mCurrentDSD, PREPARE_DRM_STATUS_SUCCESS);
-                }
-            });
-        }
-
+            }  // finally
+        }  // synchronized
     }
 
-
-    private native void _releaseDrm();
-
-    /**
-     * Releases the DRM session
-     * <p>
-     * The player has to have an active DRM session and be in stopped, or prepared
-     * state before this call is made.
-     * A {@code reset()} call will release the DRM session implicitly.
-     *
-     * @throws NoDrmSchemeException if there is no active DRM session to release
-     */
     @Override
     public void releaseDrm()
-            throws NoDrmSchemeException
-    {
-        addTask(new Task(CALL_COMPLETED_RELEASE_DRM, false) {
-            @Override
-            void process() throws NoDrmSchemeException {
-                synchronized (mDrmLock) {
-                    Log.v(TAG, "releaseDrm:");
+            throws NoDrmSchemeException {
+        synchronized (mDrmLock) {
+            Log.v(TAG, "releaseDrm:");
 
-                    if (!mActiveDrmScheme) {
-                        Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
-                        throw new NoDrmSchemeExceptionImpl(
-                                "releaseDrm: No active DRM scheme to release.");
-                    }
-
-                    try {
-                        // we don't have the player's state in this layer. The below call raises
-                        // exception if we're in a non-stopped/prepared state.
-
-                        // for cleaning native/mediaserver crypto object
-                        _releaseDrm();
-
-                        // for cleaning client-side MediaDrm object; only called if above has succeeded
-                        cleanDrmObj();
-
-                        mActiveDrmScheme = false;
-                    } catch (IllegalStateException e) {
-                        Log.w(TAG, "releaseDrm: Exception ", e);
-                        throw new IllegalStateException(
-                                "releaseDrm: The player is not in a valid state.");
-                    } catch (Exception e) {
-                        Log.e(TAG, "releaseDrm: Exception ", e);
-                    }
-                }   // synchronized
+            if (!mActiveDrmScheme) {
+                Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
+                throw new NoDrmSchemeExceptionImpl(
+                        "releaseDrm: No active DRM scheme to release.");
             }
-        });
+
+            try {
+                // we don't have the player's state in this layer. The below call raises
+                // exception if we're in a non-stopped/prepared state.
+
+                // for cleaning native/mediaserver crypto object
+                _releaseDrm();
+
+                // for cleaning client-side MediaDrm object; only called if above has succeeded
+                cleanDrmObj();
+
+                mActiveDrmScheme = false;
+            } catch (IllegalStateException e) {
+                Log.w(TAG, "releaseDrm: Exception ", e);
+                throw new IllegalStateException(
+                        "releaseDrm: The player is not in a valid state.");
+            } catch (Exception e) {
+                Log.e(TAG, "releaseDrm: Exception ", e);
+            }
+        }  // synchronized
     }
 
+    private native void _releaseDrm();
 
     /**
      * A key request/response exchange occurs between the app and a license server
@@ -2851,7 +2436,7 @@
      * provided to the DRM engine plugin using provideDrmKeyResponse. When the
      * response is for an offline key request, a key-set identifier is returned that
      * can be used to later restore the keys to a new session with the method
-     * {@ link # restoreDrmKeys}.
+     * {@link # restoreDrmKeys}.
      * When the response is for a streaming or release request, null is returned.
      *
      * @param keySetId When the response is for a release request, keySetId identifies
@@ -2904,42 +2489,26 @@
         }   // synchronized
     }
 
-
-    /**
-     * Restore persisted offline keys into a new session.  keySetId identifies the
-     * keys to load, obtained from a prior call to {@link #provideDrmKeyResponse}.
-     *
-     * @param keySetId identifies the saved key set to restore
-     */
     @Override
     public void restoreDrmKeys(@NonNull byte[] keySetId)
-            throws NoDrmSchemeException
-    {
-        addTask(new Task(CALL_COMPLETED_RESTORE_DRM_KEYS, false) {
-            @Override
-            void process() throws NoDrmSchemeException {
-                Log.v(TAG, "restoreDrmKeys: keySetId: " + keySetId);
+            throws NoDrmSchemeException {
+        Log.v(TAG, "restoreDrmKeys: keySetId: " + keySetId);
 
-                synchronized (mDrmLock) {
-
-                    if (!mActiveDrmScheme) {
-                        Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
-                        throw new NoDrmSchemeExceptionImpl(
-                                "restoreDrmKeys: Has to set a DRM scheme first.");
-                    }
-
-                    try {
-                        mDrmObj.restoreKeys(mDrmSessionId, keySetId);
-                    } catch (Exception e) {
-                        Log.w(TAG, "restoreKeys Exception " + e);
-                        throw e;
-                    }
-
-                }   // synchronized
+        synchronized (mDrmLock) {
+            if (!mActiveDrmScheme) {
+                Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
+                throw new NoDrmSchemeExceptionImpl(
+                        "restoreDrmKeys: Has to set a DRM scheme first.");
             }
-        });
-    }
 
+            try {
+                mDrmObj.restoreKeys(mDrmSessionId, keySetId);
+            } catch (Exception e) {
+                Log.w(TAG, "restoreKeys Exception " + e);
+                throw e;
+            }
+        }  // synchronized
+    }
 
     /**
      * Read a DRM engine plugin String property value, given the property name string.
@@ -3164,31 +2733,6 @@
         }
     }
 
-    /**
-     * Thrown when the device requires DRM provisioning but the provisioning attempt has
-     * failed due to a network error (Internet reachability, timeout, etc.).
-     * Extends MediaDrm.MediaDrmException
-     */
-    public static final class ProvisioningNetworkErrorExceptionImpl
-            extends ProvisioningNetworkErrorException {
-        public ProvisioningNetworkErrorExceptionImpl(String detailMessage) {
-            super(detailMessage);
-        }
-    }
-
-    /**
-     * Thrown when the device requires DRM provisioning but the provisioning attempt has
-     * failed due to the provisioning server denying the request.
-     * Extends MediaDrm.MediaDrmException
-     */
-    public static final class ProvisioningServerErrorExceptionImpl
-            extends ProvisioningServerErrorException {
-        public ProvisioningServerErrorExceptionImpl(String detailMessage) {
-            super(detailMessage);
-        }
-    }
-
-
     private native void _prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
 
     // Modular DRM helpers
@@ -3296,7 +2840,6 @@
         private Object drmLock;
         private MediaPlayer2Impl mediaPlayer;
         private int status;
-        private boolean finished;
         public  int status() {
             return status;
         }
@@ -3362,38 +2905,7 @@
 
             boolean succeeded = false;
 
-            boolean hasCallback = false;
-            synchronized (mDrmEventCbLock) {
-                hasCallback = !mDrmEventCallbackRecords.isEmpty();
-            }
-            // non-blocking mode needs the lock
-            if (hasCallback) {
-
-                synchronized (drmLock) {
-                    // continuing with prepareDrm
-                    if (provisioningSucceeded) {
-                        succeeded = mediaPlayer.resumePrepareDrm(uuid);
-                        status = (succeeded) ?
-                                PREPARE_DRM_STATUS_SUCCESS :
-                                PREPARE_DRM_STATUS_PREPARATION_ERROR;
-                    }
-                    mediaPlayer.mDrmProvisioningInProgress = false;
-                    mediaPlayer.mPrepareDrmInProgress = false;
-                    if (!succeeded) {
-                        cleanDrmObj();  // cleaning up if it hasn't gone through while in the lock
-                    }
-                } // synchronized
-
-                // calling the callback outside the lock
-                sendDrmEvent(new DrmEventNotifier() {
-                    @Override
-                    public void notify(DrmEventCallback callback) {
-                        callback.onDrmPrepared(
-                                mediaPlayer, mCurrentDSD, status);
-                    }
-                });
-            } else {   // blocking mode already has the lock
-
+            synchronized (drmLock) {
                 // continuing with prepareDrm
                 if (provisioningSucceeded) {
                     succeeded = mediaPlayer.resumePrepareDrm(uuid);
@@ -3404,12 +2916,28 @@
                 mediaPlayer.mDrmProvisioningInProgress = false;
                 mediaPlayer.mPrepareDrmInProgress = false;
                 if (!succeeded) {
-                    cleanDrmObj();  // cleaning up if it hasn't gone through
+                    cleanDrmObj();  // cleaning up if it hasn't gone through while in the lock
+                }
+            }  // synchronized
+
+            // calling the callback outside the lock
+            sendDrmEvent(new DrmEventNotifier() {
+                @Override
+                public void notify(DrmEventCallback callback) {
+                    callback.onDrmPrepared(
+                            mediaPlayer, mCurrentDSD, status);
+                }
+            });
+
+            synchronized (mTaskLock) {
+                if (mCurrentTask != null
+                        && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM
+                        && mCurrentTask.mNeedToWaitForEventToComplete) {
+                    mCurrentTask = null;
+                    processPendingTask_l();
                 }
             }
-
-            finished = true;
-        }   // run()
+        }
 
         /**
          * Returns a byte[] containing the remainder of 'in', closing it when done.
@@ -3437,50 +2965,29 @@
     }   // ProvisioningThread
 
     private int HandleProvisioninig(UUID uuid) {
-        // the lock is already held by the caller
-
-        if (mDrmProvisioningInProgress) {
-            Log.e(TAG, "HandleProvisioninig: Unexpected mDrmProvisioningInProgress");
-            return PREPARE_DRM_STATUS_PREPARATION_ERROR;
-        }
-
-        MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
-        if (provReq == null) {
-            Log.e(TAG, "HandleProvisioninig: getProvisionRequest returned null.");
-            return PREPARE_DRM_STATUS_PREPARATION_ERROR;
-        }
-
-        Log.v(TAG, "HandleProvisioninig provReq " +
-                " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
-
-        // networking in a background thread
-        mDrmProvisioningInProgress = true;
-
-        mDrmProvisioningThread = new ProvisioningThread().initialize(provReq, uuid, this);
-        mDrmProvisioningThread.start();
-
-        int result;
-
-        // non-blocking: this is not the final result
-        boolean hasCallback = false;
-        synchronized (mDrmEventCbLock) {
-            hasCallback = !mDrmEventCallbackRecords.isEmpty();
-        }
-        if (hasCallback) {
-            result = PREPARE_DRM_STATUS_SUCCESS;
-        } else {
-            // if blocking mode, wait till provisioning is done
-            try {
-                mDrmProvisioningThread.join();
-            } catch (Exception e) {
-                Log.w(TAG, "HandleProvisioninig: Thread.join Exception " + e);
+        synchronized (mDrmLock) {
+            if (mDrmProvisioningInProgress) {
+                Log.e(TAG, "HandleProvisioninig: Unexpected mDrmProvisioningInProgress");
+                return PREPARE_DRM_STATUS_PREPARATION_ERROR;
             }
-            result = mDrmProvisioningThread.status();
-            // no longer need the thread
-            mDrmProvisioningThread = null;
-        }
 
-        return result;
+            MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
+            if (provReq == null) {
+                Log.e(TAG, "HandleProvisioninig: getProvisionRequest returned null.");
+                return PREPARE_DRM_STATUS_PREPARATION_ERROR;
+            }
+
+            Log.v(TAG, "HandleProvisioninig provReq " +
+                    " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
+
+            // networking in a background thread
+            mDrmProvisioningInProgress = true;
+
+            mDrmProvisioningThread = new ProvisioningThread().initialize(provReq, uuid, this);
+            mDrmProvisioningThread.start();
+
+            return PREPARE_DRM_STATUS_SUCCESS;
+        }
     }
 
     private boolean resumePrepareDrm(UUID uuid) {
@@ -3562,13 +3069,6 @@
 
     // Modular DRM end
 
-    /*
-     * Test whether a given video scaling mode is supported.
-     */
-    private boolean isVideoScalingModeSupported(int mode) {
-        return (mode == VIDEO_SCALING_MODE_SCALE_TO_FIT ||
-                mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
-    }
 
     private static class TimedTextUtil {
         // These keys must be in sync with the keys in TextDescription2.h
@@ -3624,6 +3124,26 @@
         }
     }
 
+    private Object addTask(Task task) {
+        synchronized (mTaskLock) {
+            mPendingTasks.add(task);
+            processPendingTask_l();
+        }
+        return task;
+    }
+
+    @GuardedBy("mTaskLock")
+    private void processPendingTask_l() {
+        if (mCurrentTask != null) {
+            return;
+        }
+        if (!mPendingTasks.isEmpty()) {
+            Task task = mPendingTasks.remove(0);
+            mCurrentTask = task;
+            mTaskHandler.post(task);
+        }
+    }
+
     private abstract class Task implements Runnable {
         private final int mMediaCallType;
         private final boolean mNeedToWaitForEventToComplete;
@@ -3691,7 +3211,9 @@
         private void sendCompleteNotification(int status) {
             // In {@link #notifyWhenCommandLabelReached} case, a separate callback
             // {@link #onCommandLabelReached} is already called in {@code process()}.
-            if (mMediaCallType == CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED) {
+            // CALL_COMPLETED_PREPARE_DRM is sent via DrmEventCallback#onDrmPrepared
+            if (mMediaCallType == CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED
+                    || mMediaCallType == CALL_COMPLETED_PREPARE_DRM) {
                 return;
             }
             sendEvent(new EventNotifier() {
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index c537945..0cde01e 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -524,6 +524,9 @@
         private boolean mScanSuccess;
         private int mWidth;
         private int mHeight;
+        private int mColorStandard;
+        private int mColorTransfer;
+        private int mColorRange;
 
         public MyMediaScannerClient() {
             mDateFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
@@ -592,6 +595,9 @@
             mCompilation = 0;
             mWidth = 0;
             mHeight = 0;
+            mColorStandard = -1;
+            mColorTransfer = -1;
+            mColorRange = -1;
 
             return entry;
         }
@@ -760,6 +766,12 @@
                 mWidth = parseSubstring(value, 0, 0);
             } else if (name.equalsIgnoreCase("height")) {
                 mHeight = parseSubstring(value, 0, 0);
+            } else if (name.equalsIgnoreCase("colorstandard")) {
+                mColorStandard = parseSubstring(value, 0, -1);
+            } else if (name.equalsIgnoreCase("colortransfer")) {
+                mColorTransfer = parseSubstring(value, 0, -1);
+            } else if (name.equalsIgnoreCase("colorrange")) {
+                mColorRange = parseSubstring(value, 0, -1);
             } else {
                 //Log.v(TAG, "unknown tag: " + name + " (" + mProcessGenres + ")");
             }
@@ -905,6 +917,15 @@
                     if (resolution != null) {
                         map.put(Video.Media.RESOLUTION, resolution);
                     }
+                    if (mColorStandard >= 0) {
+                        map.put(Video.Media.COLOR_STANDARD, mColorStandard);
+                    }
+                    if (mColorTransfer >= 0) {
+                        map.put(Video.Media.COLOR_TRANSFER, mColorTransfer);
+                    }
+                    if (mColorRange >= 0) {
+                        map.put(Video.Media.COLOR_RANGE, mColorRange);
+                    }
                     if (mDate > 0) {
                         map.put(Video.Media.DATE_TAKEN, mDate);
                     }
@@ -986,7 +1007,7 @@
                 ExifInterface exif = null;
                 try {
                     exif = new ExifInterface(entry.mPath);
-                } catch (IOException ex) {
+                } catch (Exception ex) {
                     // exif is null
                 }
                 if (exif != null) {
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 32aba7f..84dfcb1 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -23,13 +23,12 @@
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources.NotFoundException;
 import android.database.Cursor;
-import android.media.MediaPlayer.OnCompletionListener;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.RemoteException;
 import android.provider.MediaStore;
-import android.provider.Settings;
 import android.provider.MediaStore.MediaColumns;
+import android.provider.Settings;
 import android.util.Log;
 
 import java.io.IOException;
@@ -50,7 +49,6 @@
 
     private static final String[] MEDIA_COLUMNS = new String[] {
         MediaStore.Audio.Media._ID,
-        MediaStore.Audio.Media.DATA,
         MediaStore.Audio.Media.TITLE
     };
     /** Selection that limits query results to just audio files */
@@ -254,7 +252,7 @@
                         cursor = res.query(uri, MEDIA_COLUMNS, mediaSelection, null, null);
                         if (cursor != null && cursor.getCount() == 1) {
                             cursor.moveToFirst();
-                            return cursor.getString(2);
+                            return cursor.getString(1);
                         }
                         // missing cursor is handled below
                     }
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 8664aa1..436897f 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -497,34 +497,6 @@
         return getUriFromCursor(mCursor);
     }
 
-    /**
-     * Queries the database for the Uri to a ringtone in a specific path (the ringtone has to have
-     * been scanned before)
-     *
-     * @param context Context used to query the database
-     * @param path Path to the ringtone file
-     * @return Uri of the ringtone, null if something fails in the query or the ringtone doesn't
-     *            exist
-     *
-     * @hide
-     */
-    private static Uri getExistingRingtoneUriFromPath(Context context, String path) {
-        final String[] proj = {MediaStore.Audio.Media._ID};
-        final String[] selectionArgs = {path};
-        try (final Cursor cursor = context.getContentResolver().query(
-                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, proj,
-                MediaStore.Audio.Media.DATA + "=? ", selectionArgs, /* sortOrder */ null)) {
-            if (cursor == null || !cursor.moveToFirst()) {
-                return null;
-            }
-            final int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
-            if (id == -1) {
-                return null;
-            }
-            return Uri.withAppendedPath(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, "" + id);
-        }
-    }
-
     private static Uri getUriFromCursor(Cursor cursor) {
         return ContentUris.withAppendedId(Uri.parse(cursor.getString(URI_COLUMN_INDEX)), cursor
                 .getLong(ID_COLUMN_INDEX));
@@ -750,28 +722,6 @@
     }
 
     /**
-     * Look up the path for a given {@link Uri} referring to a ringtone sound (TYPE_RINGTONE,
-     * TYPE_NOTIFICATION, or TYPE_ALARM). This is saved in {@link MediaStore.Audio.Media#DATA}.
-     *
-     * @return a {@link File} pointing at the location of the {@param uri} on disk, or {@code null}
-     * if there is no such file.
-     */
-    private File getRingtonePathFromUri(Uri uri) {
-        // Query cursor to get ringtone path
-        final String[] projection = {MediaStore.Audio.Media.DATA};
-        setFilterColumnsList(TYPE_RINGTONE | TYPE_NOTIFICATION | TYPE_ALARM);
-
-        String path = null;
-        try (Cursor cursor = query(uri, projection, constructBooleanTrueWhereClause(mFilterColumns),
-                null, null)) {
-            if (cursor != null && cursor.moveToFirst()) {
-                path = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
-            }
-        }
-        return path != null ? new File(path) : null;
-    }
-
-    /**
      * Disables Settings.System.SYNC_PARENT_SOUNDS.
      *
      * @hide
@@ -879,32 +829,6 @@
                 : uriWithoutUserId.toString().startsWith(storage.toString());
     }
 
-    /** @hide */
-    public boolean isCustomRingtone(Uri uri) {
-        if(!isExternalRingtoneUri(uri)) {
-            // A custom ringtone would be in the external storage
-            return false;
-        }
-
-        final File ringtoneFile = (uri == null ? null : getRingtonePathFromUri(uri));
-        final File parent = (ringtoneFile == null ? null : ringtoneFile.getParentFile());
-        if (parent == null) {
-            return false;
-        }
-
-        final String[] directories = {
-            Environment.DIRECTORY_RINGTONES,
-            Environment.DIRECTORY_NOTIFICATIONS,
-            Environment.DIRECTORY_ALARMS
-        };
-        for (final String directory : directories) {
-            if (parent.equals(Environment.getExternalStoragePublicDirectory(directory))) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     /**
      * Adds an audio file to the list of ringtones.
      *
@@ -989,39 +913,6 @@
     }
 
     /**
-     * Deletes the actual file in the Uri and its ringtone database entry if the Uri's actual path
-     * is in one of the following directories: {@link android.is.Environment#DIRECTORY_RINGTONES},
-     * {@link android.is.Environment#DIRECTORY_NOTIFICATIONS} or
-     * {@link android.is.Environment#DIRECTORY_ALARMS}.
-     *
-     * The given Uri must be a ringtone Content Uri.
-     *
-     * Keep in mind that if the ringtone deleted is a default ringtone, it will still live in the
-     * ringtone cache file so it will be playable from there. However, if an app uses the ringtone
-     * as its own ringtone, it won't be played, which is the same behavior observed for 3rd party
-     * custom ringtones.
-     *
-     * @hide
-     */
-    public boolean deleteExternalRingtone(Uri uri) {
-        if(!isCustomRingtone(uri)) {
-            // We can only delete custom ringtones in the default ringtone storages
-            return false;
-        }
-
-        // Save the path of the ringtone before deleting from our content resolver.
-        final File ringtoneFile = getRingtonePathFromUri(uri);
-        try {
-            if (ringtoneFile != null && mContext.getContentResolver().delete(uri, null, null) > 0) {
-                return ringtoneFile.delete();
-            }
-        } catch (SecurityException e) {
-            Log.d(TAG, "Unable to delete custom ringtone", e);
-        }
-        return false;
-    }
-
-    /**
      * Try opening the given ringtone locally first, but failover to
      * {@link IRingtonePlayer} if we can't access it directly. Typically happens
      * when process doesn't hold
diff --git a/media/java/android/media/RoutingDelegate.java b/media/java/android/media/RoutingDelegate.java
new file mode 100644
index 0000000..2359813
--- /dev/null
+++ b/media/java/android/media/RoutingDelegate.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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 android.media;
+
+import android.os.Handler;
+
+class RoutingDelegate implements AudioRouting.OnRoutingChangedListener {
+    private AudioRouting mAudioRouting;
+    private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener;
+    private Handler mHandler;
+
+    RoutingDelegate(final AudioRouting audioRouting,
+                    final AudioRouting.OnRoutingChangedListener listener,
+                    Handler handler) {
+        mAudioRouting = audioRouting;
+        mOnRoutingChangedListener = listener;
+        mHandler = handler;
+    }
+
+    public AudioRouting.OnRoutingChangedListener getListener() {
+        return mOnRoutingChangedListener;
+    }
+
+    public Handler getHandler() {
+        return mHandler;
+    }
+
+    @Override
+    public void onRoutingChanged(AudioRouting router) {
+        if (mOnRoutingChangedListener != null) {
+            mOnRoutingChangedListener.onRoutingChanged(mAudioRouting);
+        }
+    }
+}
diff --git a/media/java/android/media/VideoSize.java b/media/java/android/media/VideoSize.java
new file mode 100644
index 0000000..7e5cb1f
--- /dev/null
+++ b/media/java/android/media/VideoSize.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2018 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 android.media;
+
+/**
+ * Immutable class for describing width and height dimensions.
+ *
+ * @hide
+ */
+public final class VideoSize {
+    /**
+     * Create a new immutable VideoSize instance.
+     *
+     * @param width The width of the video size
+     * @param height The height of the video size
+     */
+    public VideoSize(int width, int height) {
+        mWidth = width;
+        mHeight = height;
+    }
+
+    /**
+     * Get the width of the video size
+     * @return width
+     */
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * Get the height of the video size
+     * @return height
+     */
+    public int getHeight() {
+        return mHeight;
+    }
+
+    /**
+     * Check if this video size is equal to another video size.
+     * <p>
+     * Two video sizes are equal if and only if both their widths and heights are
+     * equal.
+     * </p>
+     * <p>
+     * A video size object is never equal to any other type of object.
+     * </p>
+     *
+     * @return {@code true} if the objects were equal, {@code false} otherwise
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof VideoSize) {
+            VideoSize other = (VideoSize) obj;
+            return mWidth == other.mWidth && mHeight == other.mHeight;
+        }
+        return false;
+    }
+
+    /**
+     * Return the video size represented as a string with the format {@code "WxH"}
+     *
+     * @return string representation of the video size
+     */
+    @Override
+    public String toString() {
+        return mWidth + "x" + mHeight;
+    }
+
+    private final int mWidth;
+    private final int mHeight;
+}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index e841567..0f531c9 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -81,9 +81,6 @@
         "-Wno-error=deprecated-declarations",
         "-Wunused",
         "-Wunreachable-code",
-        // Allow implicit fallthroughs in android_media_MediaScanner.cpp and
-        // android_mtp_MtpDatabase.cpp until they are fixed.
-        "-Wno-error=implicit-fallthrough",
     ],
 }
 
@@ -126,14 +123,12 @@
         "libcutils",
         "libmedia_helper",
         "libmedia_player2_util",
-        "libmediadrm",
         "libmediaextractor",
         "libmediametrics",
         "libmediaplayer2",
         "libmediaplayer2-protos",
         "libmediandk_utils",
         "libmediautils",
-        "libnetd_client",  // for setNetworkForUser
         "libprotobuf-cpp-lite",
         "libstagefright_esds",
         "libstagefright_foundation",
@@ -142,7 +137,7 @@
         "libstagefright_mpeg2support",
         "libstagefright_nuplayer2",
         "libstagefright_player2",
-        "libstagefright_rtsp",
+        "libstagefright_rtsp_player2",
         "libstagefright_timedtext2",
         "libmedia2_jni_core",
     ],
diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h
index 71b8dac..5306de6 100644
--- a/media/jni/android_media_AudioPresentation.h
+++ b/media/jni/android_media_AudioPresentation.h
@@ -49,7 +49,7 @@
             }
 
             constructID = env->GetMethodID(clazz, "<init>",
-                                "(IILjava/util/Map;Ljava/lang/String;IZZZ)V");
+                                "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
             env->DeleteLocalRef(lclazz);
 
             // list objects
@@ -104,21 +104,26 @@
                 // don't expose private keys (starting with android._)
                 continue;
             }
-
             jobject valueObj = NULL;
-
             AString val;
             CHECK(msg->findString(key, &val));
-
             valueObj = env->NewStringUTF(val.c_str());
-
             if (valueObj != NULL) {
-                jstring keyObj = env->NewStringUTF(key);
-
-                env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
-
-                env->DeleteLocalRef(keyObj); keyObj = NULL;
+                ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
+                if (localeClazz.get() == NULL) {
+                    return -EINVAL;
+                }
+                jmethodID localeConstructID =
+                        env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
+                if (localeConstructID == NULL) {
+                    return -EINVAL;
+                }
+                jstring jLanguage = env->NewStringUTF(key);
+                jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
+                env->CallObjectMethod(hashMap, hashMapPutID, jLocale, valueObj);
+                env->DeleteLocalRef(jLocale); jLocale = NULL;
                 env->DeleteLocalRef(valueObj); valueObj = NULL;
+                env->DeleteLocalRef(jLanguage); jLanguage = NULL;
             }
         }
 
@@ -142,26 +147,36 @@
             if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) {
                 return NULL;
             }
-            jstring jLanguage = env->NewStringUTF(ap->mLanguage.string());
-
+            ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
+            if (localeClazz.get() == NULL) {
+                return NULL;
+            }
+            jmethodID localeConstructID =
+                    env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
+            if (localeConstructID == NULL) {
+                return NULL;
+            }
+            jstring jLanguage = env->NewStringUTF(ap->mLanguage.c_str());
+            jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
             jobject jValueObj = env->NewObject(fields.clazz, fields.constructID,
                                 static_cast<jint>(ap->mPresentationId),
                                 static_cast<jint>(ap->mProgramId),
-                                jLabelObject,
-                                jLanguage,
+                                jLocale,
                                 static_cast<jint>(ap->mMasteringIndication),
                                 static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ?
                                     1 : 0),
                                 static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ?
                                     1 : 0),
                                 static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ?
-                                    1 : 0));
+                                    1 : 0),
+                                jLabelObject);
             if (jValueObj == NULL) {
                 env->DeleteLocalRef(jLanguage); jLanguage = NULL;
                 return NULL;
             }
 
             env->CallBooleanMethod(list, fields.listAddId, jValueObj);
+            env->DeleteLocalRef(jLocale); jLocale = NULL;
             env->DeleteLocalRef(jValueObj); jValueObj = NULL;
             env->DeleteLocalRef(jLanguage); jLanguage = NULL;
         }
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 7cf8828..8637ada 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -543,7 +543,7 @@
     import jav.util.Iterator;
 
     HashMap<k, v> hm;
-    Set<Entry<k, v> > s = hm.entrySet();
+    Set<Entry<k, v>> s = hm.entrySet();
     Iterator i = s.iterator();
     Entry e = s.next();
 */
@@ -613,10 +613,10 @@
 }
 
 static jobject ListOfVectorsToArrayListOfByteArray(JNIEnv *env,
-                                                   List<Vector<uint8_t> > list) {
+                                                   List<Vector<uint8_t>> list) {
     jclass clazz = gFields.arraylistClassId;
     jobject arrayList = env->NewObject(clazz, gFields.arraylist.init);
-    List<Vector<uint8_t> >::iterator iter = list.begin();
+    List<Vector<uint8_t>>::iterator iter = list.begin();
     while (iter != list.end()) {
         jbyteArray byteArray = VectorToJByteArray(env, *iter);
         env->CallBooleanMethod(arrayList, gFields.arraylist.add, byteArray);
@@ -1216,7 +1216,7 @@
         return NULL;
     }
 
-    List<Vector<uint8_t> > secureStops;
+    List<Vector<uint8_t>> secureStops;
 
     status_t err = drm->getSecureStops(secureStops);
 
@@ -1237,7 +1237,7 @@
         return NULL;
     }
 
-    List<Vector<uint8_t> > secureStopIds;
+    List<Vector<uint8_t>> secureStopIds;
 
     status_t err = drm->getSecureStopIds(secureStopIds);
 
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 61c28ed..67005be 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -790,40 +790,6 @@
     return (jint)mp->getState();
 }
 
-static jint
-android_media_MediaPlayer2_getVideoWidth(JNIEnv *env, jobject thiz)
-{
-    sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL ) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
-        return 0;
-    }
-    int w;
-    if (0 != mp->getVideoWidth(&w)) {
-        ALOGE("getVideoWidth failed");
-        w = 0;
-    }
-    ALOGV("getVideoWidth: %d", w);
-    return (jint) w;
-}
-
-static jint
-android_media_MediaPlayer2_getVideoHeight(JNIEnv *env, jobject thiz)
-{
-    sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL ) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
-        return 0;
-    }
-    int h;
-    if (0 != mp->getVideoHeight(&h)) {
-        ALOGE("getVideoHeight failed");
-        h = 0;
-    }
-    ALOGV("getVideoHeight: %d", h);
-    return (jint) h;
-}
-
 static jobject
 android_media_MediaPlayer2_native_getMetrics(JNIEnv *env, jobject thiz)
 {
@@ -893,20 +859,6 @@
     process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
 }
 
-static jint
-android_media_MediaPlayer2_getAudioStreamType(JNIEnv *env, jobject thiz)
-{
-    sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL ) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
-        return 0;
-    }
-    audio_stream_type_t streamtype;
-    process_media_player_call( env, thiz, mp->getAudioStreamType(&streamtype), NULL, NULL );
-    ALOGV("getAudioStreamType: %d (streamtype)", streamtype);
-    return (jint) streamtype;
-}
-
 static jboolean
 android_media_MediaPlayer2_setParameter(JNIEnv *env, jobject thiz, jint key, jobject)
 {
@@ -1372,15 +1324,30 @@
     return mp->getRoutedDeviceId();
 }
 
-static void android_media_MediaPlayer2_enableDeviceCallback(
-        JNIEnv* env, jobject thiz, jboolean enabled)
+static void android_media_MediaPlayer2_addDeviceCallback(
+        JNIEnv* env, jobject thiz, jobject routingDelegate)
 {
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL) {
         return;
     }
 
-    status_t status = mp->enableAudioDeviceCallback(enabled);
+    status_t status = mp->addAudioDeviceCallback(routingDelegate);
+    if (status != NO_ERROR) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        ALOGE("enable device callback failed: %d", status);
+    }
+}
+
+static void android_media_MediaPlayer2_removeDeviceCallback(
+        JNIEnv* env, jobject thiz, jobject listener)
+{
+    sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL) {
+        return;
+    }
+
+    status_t status = mp->removeAudioDeviceCallback(listener);
     if (status != NO_ERROR) {
         jniThrowException(env, "java/lang/IllegalStateException", NULL);
         ALOGE("enable device callback failed: %d", status);
@@ -1467,14 +1434,12 @@
         (void *)android_media_MediaPlayer2_handleDataSourceCallback
     },
     {"nativePlayNextDataSource", "(J)V",                        (void *)android_media_MediaPlayer2_playNextDataSource},
-    {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer2_setVideoSurface},
+    {"native_setVideoSurface", "(Landroid/view/Surface;)V",     (void *)android_media_MediaPlayer2_setVideoSurface},
     {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer2_getBufferingParams},
     {"_setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer2_setBufferingParams},
     {"_prepare",            "()V",                              (void *)android_media_MediaPlayer2_prepare},
     {"_start",              "()V",                              (void *)android_media_MediaPlayer2_start},
     {"native_getState",     "()I",                              (void *)android_media_MediaPlayer2_getState},
-    {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer2_getVideoWidth},
-    {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer2_getVideoHeight},
     {"native_getMetrics",   "()Landroid/os/PersistableBundle;", (void *)android_media_MediaPlayer2_native_getMetrics},
     {"_setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer2_setPlaybackParams},
     {"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer2_getPlaybackParams},
@@ -1487,13 +1452,12 @@
     {"getDuration",         "()J",                              (void *)android_media_MediaPlayer2_getDuration},
     {"_release",            "()V",                              (void *)android_media_MediaPlayer2_release},
     {"_reset",              "()V",                              (void *)android_media_MediaPlayer2_reset},
-    {"_getAudioStreamType", "()I",                              (void *)android_media_MediaPlayer2_getAudioStreamType},
     {"setParameter",        "(ILjava/lang/Object;)Z",          (void *)android_media_MediaPlayer2_setParameter},
     {"getParameter",        "(I)Ljava/lang/Object;",           (void *)android_media_MediaPlayer2_getParameter},
     {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer2_setLooping},
     {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer2_isLooping},
-    {"_setVolume",          "(F)V",                             (void *)android_media_MediaPlayer2_setVolume},
-    {"_invoke",             "([B)[B",                           (void *)android_media_MediaPlayer2_invoke},
+    {"native_setVolume",    "(F)V",                             (void *)android_media_MediaPlayer2_setVolume},
+    {"native_invoke",       "([B)[B",                           (void *)android_media_MediaPlayer2_invoke},
     {"native_init",         "()V",                              (void *)android_media_MediaPlayer2_native_init},
     {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer2_native_setup},
     {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer2_native_finalize},
@@ -1508,7 +1472,9 @@
     // AudioRouting
     {"native_setOutputDevice", "(I)Z",                          (void *)android_media_MediaPlayer2_setOutputDevice},
     {"native_getRoutedDeviceId", "()I",                         (void *)android_media_MediaPlayer2_getRoutedDeviceId},
-    {"native_enableDeviceCallback", "(Z)V",                     (void *)android_media_MediaPlayer2_enableDeviceCallback},
+    {"native_addDeviceCallback", "(Landroid/media/RoutingDelegate;)V", (void *)android_media_MediaPlayer2_addDeviceCallback},
+    {"native_removeDeviceCallback", "(Landroid/media/AudioRouting$OnRoutingChangedListener;)V",
+            (void *)android_media_MediaPlayer2_removeDeviceCallback},
 
     // StreamEventCallback for JAudioTrack
     {"native_stream_event_onTearDown",                "(JJ)V",  (void *)android_media_MediaPlayer2_native_on_tear_down},
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index c0ceb01..58044c0 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -27,6 +27,7 @@
 #include <nativehelper/JNIHelp.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
+#include <android-base/macros.h>                // for FALLTHROUGH_INTENDED
 
 using namespace android;
 
@@ -92,6 +93,7 @@
                 return false;
             }
             // Fall through to take care of the final byte.
+            FALLTHROUGH_INTENDED;
         case 0x0c:
         case 0x0d:
             // Bit pattern 110x, so there is one additional byte.
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 4d8c96f..56b85b5 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -1272,6 +1272,7 @@
             case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
                 writable = true;
                 // fall through
+                FALLTHROUGH_INTENDED;
             case MTP_DEVICE_PROPERTY_IMAGE_SIZE:
             {
                 result = new MtpProperty(property, MTP_TYPE_STR, writable);
diff --git a/media/native/midi/midi.cpp b/media/native/midi/midi.cpp
index 78ca0ab..a5bdba8 100644
--- a/media/native/midi/midi.cpp
+++ b/media/native/midi/midi.cpp
@@ -338,7 +338,8 @@
             numMessageBytes = std::min(maxBytes, numMessageBytes);
             memcpy(buffer, readBuffer + 1, numMessageBytes);
             if (timestampPtr != nullptr) {
-                *timestampPtr = *(uint64_t*)(readBuffer + readCount - sizeof(uint64_t));
+                memcpy(timestampPtr, readBuffer + readCount - sizeof(uint64_t),
+                        sizeof(*timestampPtr));
             }
         }
         *numBytesReceivedPtr = numMessageBytes;
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 24d003b..a4306fe 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -62,8 +62,10 @@
         "libbinder",
         "libui",
         "libgui",
+        "libharfbuzz_ng",  // Only for including hb.h via minikin
         "libsensor",
         "libandroid_runtime",
+        "libminikin",
         "libnetd_client",
         "libhwui",
         "libxml2",
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 9f48bc9..e7e8384 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -224,6 +224,7 @@
     ASystemFont_getAxisCount; # introduced=29
     ASystemFont_getAxisTag; # introduced=29
     ASystemFont_getAxisValue; # introduced=29
+    ASystemFont_matchFamilyStyleCharacter; # introduced=29
     ATrace_beginSection; # introduced=23
     ATrace_endSection; # introduced=23
     ATrace_isEnabled; # introduced=23
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 761a475..4d3d1d6 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -29,6 +29,11 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <hwui/MinikinSkia.h>
+#include <minikin/FontCollection.h>
+#include <minikin/LocaleList.h>
+#include <minikin/SystemFonts.h>
+
 struct XmlCharDeleter {
     void operator()(xmlChar* b) { xmlFree(b); }
 };
@@ -192,6 +197,41 @@
     delete ite;
 }
 
+ASystemFont* ASystemFont_matchFamilyStyleCharacter(
+        const char* _Nonnull familyName,
+        uint16_t weight,
+        bool italic,
+        const char* _Nonnull languageTags,
+        const uint16_t* _Nonnull text,
+        uint32_t textLength,
+        uint32_t* _Nullable runLength) {
+    std::shared_ptr<minikin::FontCollection> fc =
+            minikin::SystemFonts::findFontCollection(familyName);
+    std::vector<minikin::FontCollection::Run> runs =
+            fc->itemize(minikin::U16StringPiece(text, textLength),
+                        minikin::FontStyle(weight, static_cast<minikin::FontStyle::Slant>(italic)),
+                        minikin::registerLocaleList(languageTags),
+                        minikin::FamilyVariant::DEFAULT);
+
+    const minikin::Font* font = runs[0].fakedFont.font;
+    std::unique_ptr<ASystemFont> result = std::make_unique<ASystemFont>();
+    const android::MinikinFontSkia* minikinFontSkia =
+            reinterpret_cast<android::MinikinFontSkia*>(font->typeface().get());
+    result->mFilePath = minikinFontSkia->getFilePath();
+    result->mWeight = font->style().weight();
+    result->mItalic = font->style().slant() == minikin::FontStyle::Slant::ITALIC;
+    result->mCollectionIndex = minikinFontSkia->GetFontIndex();
+    const std::vector<minikin::FontVariation>& axes = minikinFontSkia->GetAxes();
+    result->mAxes.reserve(axes.size());
+    for (auto axis : axes) {
+        result->mAxes.push_back(std::make_pair(axis.axisTag, axis.value));
+    }
+    if (runLength != nullptr) {
+        *runLength = runs[0].end;
+    }
+    return result.release();
+}
+
 xmlNode* findNextFontNode(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode) {
     if (fontNode == nullptr) {
         if (!xmlDoc) {
diff --git a/opengl/java/android/opengl/EGL15.java b/opengl/java/android/opengl/EGL15.java
index f855fe2..bd845e7 100644
--- a/opengl/java/android/opengl/EGL15.java
+++ b/opengl/java/android/opengl/EGL15.java
@@ -20,7 +20,9 @@
  * EGL 1.5
  *
  */
-public class EGL15 {
+public final class EGL15 {
+
+    private EGL15() {};
 
     public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT            = 0x00000001;
     public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT   = 0x00000002;
diff --git a/packages/BackupRestoreConfirmation/res/values-mr/strings.xml b/packages/BackupRestoreConfirmation/res/values-mr/strings.xml
index 4be28db..b354171 100644
--- a/packages/BackupRestoreConfirmation/res/values-mr/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-mr/strings.xml
@@ -24,10 +24,10 @@
     <string name="restore_confirm_text" msgid="7499866728030461776">"कनेक्‍ट केलेल्‍या डेस्‍कटॉप काँप्युटरवरील सर्व डेटाच्या पूर्ण पुनर्संचयनाची विनंती केली गेली आहे. तुम्ही असे होण्यासाठी अनुमती देऊ इच्‍छिता?\n\nतुम्ही स्‍वत: पुनर्संचयनाची विनंती केली नसल्‍यास, कार्य पुढे सुरु राहण्‍यास अनुमती देऊ नका. हे आपल्‍या डिव्‍हाइसवरील कोणत्याही वर्तमान डेटास पुनर्स्‍थित करेल!"</string>
     <string name="allow_restore_button_label" msgid="3081286752277127827">"माझा डेटा पुनर्संचयित करा"</string>
     <string name="deny_restore_button_label" msgid="1724367334453104378">"पुनर्संचयित करू नका"</string>
-    <string name="current_password_text" msgid="8268189555578298067">"कृपया आपला वर्तमान बॅकअप संकेतशब्‍द खाली प्रविष्‍ट करा:"</string>
+    <string name="current_password_text" msgid="8268189555578298067">"कृपया तुमचा वर्तमान बॅकअप संकेतशब्‍द खाली प्रविष्‍ट करा:"</string>
     <string name="device_encryption_restore_text" msgid="1570864916855208992">"कृपया तुमचे डिव्हाइस एंक्रिप्शन पासवर्ड खाली एंटर करा."</string>
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"कृपया तुमचे डिव्हाइस एंक्रिप्शन पासवर्ड खाली एंटर करा. हा बॅकअप संग्रह एंक्रिप्ट करण्‍यासाठी देखील वापरला जाईल."</string>
-    <string name="backup_enc_password_text" msgid="4981585714795233099">"कृपया पूर्ण बॅकअप डेटा एंक्रिप्ट करण्‍यासाठी वापरण्याकरिता पासवर्ड एंटर करा. हे रिक्त सोडल्‍यास, आपला वर्तमान बॅकअप पासवर्ड वापरला जाईल:"</string>
+    <string name="backup_enc_password_text" msgid="4981585714795233099">"कृपया पूर्ण बॅकअप डेटा एंक्रिप्ट करण्‍यासाठी वापरण्याकरिता पासवर्ड एंटर करा. हे रिक्त सोडल्‍यास, तुमचा वर्तमान बॅकअप पासवर्ड वापरला जाईल:"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"तुम्ही पूर्ण बॅकअप डेटा एंक्रिप्ट करू इच्‍छित असल्‍यास, खालील पासवर्ड एंटर करा:"</string>
     <string name="backup_enc_password_required" msgid="7889652203371654149">"तुमचे डिव्हाइस एंक्रिप्ट केले असल्यामुळे, तुम्हाला तुमचा बॅक अप एंक्रिप्ट करणे आवश्यक आहे. कृपया खाली एक पासवर्ड एंटर करा:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"पुनर्स्टोअर केलेला डेटा एंक्रिप्ट केला असल्‍यास, कृपया पासवर्ड खाली एंटर करा:"</string>
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 1b1bf8d..8f13497 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -30,7 +30,7 @@
         "SystemUIPluginLib",
         "SystemUISharedLib",
         "SettingsLib",
-        "android.car.user",
+        "android.car.userlib",
         "androidx.car_car",
         "androidx.legacy_legacy-support-v4",
         "androidx.recyclerview_recyclerview",
diff --git a/packages/CarrierDefaultApp/res/values-mr/strings.xml b/packages/CarrierDefaultApp/res/values-mr/strings.xml
index e1442c2..79cc4aa 100644
--- a/packages/CarrierDefaultApp/res/values-mr/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-mr/strings.xml
@@ -4,7 +4,7 @@
     <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
     <string name="android_system_label" msgid="2797790869522345065">"मोबाइल वाहक"</string>
     <string name="portal_notification_id" msgid="5155057562457079297">"मोबाइल डेटा संपला आहे"</string>
-    <string name="no_data_notification_id" msgid="668400731803969521">"आपला मोबाइल डेटा निष्क्रिय केला गेला"</string>
+    <string name="no_data_notification_id" msgid="668400731803969521">"तुमचा मोबाइल डेटा निष्क्रिय केला गेला"</string>
     <string name="portal_notification_detail" msgid="2295729385924660881">"%s वेबसाइटला भेट देण्‍यासाठी टॅप करा"</string>
     <string name="no_data_notification_detail" msgid="3112125343857014825">"कृपया आपल्या %s सेवा प्रदात्याशी संपर्क साधा"</string>
     <string name="no_mobile_data_connection_title" msgid="7449525772416200578">"मोबाइल डेटा कनेक्‍शन नाही"</string>
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 8f33a70..1136684 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -38,6 +38,7 @@
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
@@ -77,6 +78,10 @@
 public class Assistant extends NotificationAssistantService {
     private static final String TAG = "ExtAssistant";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    public static final boolean AUTO_DEMOTE_NOTIFICATIONS = SystemProperties.getBoolean(
+            "debug.demote_notifs", false);
+    public static final boolean AGE_NOTIFICATIONS = SystemProperties.getBoolean(
+            "debug.age_notifs", false);
 
     private static final String TAG_ASSISTANT = "assistant";
     private static final String TAG_IMPRESSION = "impression-set";
@@ -230,16 +235,19 @@
             @NonNull ArrayList<Notification.Action> smartActions,
             @NonNull ArrayList<CharSequence> smartReplies) {
         Bundle signals = new Bundle();
+
         if (!smartActions.isEmpty()) {
             signals.putParcelableArrayList(Adjustment.KEY_SMART_ACTIONS, smartActions);
         }
         if (!smartReplies.isEmpty()) {
             signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
         }
-        if (mNotificationCategorizer.shouldSilence(entry)) {
-            final int importance = entry.getImportance() < IMPORTANCE_LOW ? entry.getImportance()
-                    : IMPORTANCE_LOW;
-            signals.putInt(KEY_IMPORTANCE, importance);
+        if (AUTO_DEMOTE_NOTIFICATIONS) {
+            if (mNotificationCategorizer.shouldSilence(entry)) {
+                final int importance = entry.getImportance() < IMPORTANCE_LOW
+                        ? entry.getImportance() : IMPORTANCE_LOW;
+                signals.putInt(KEY_IMPORTANCE, importance);
+            }
         }
 
         return new Adjustment(
@@ -445,13 +453,15 @@
     protected final class AgingCallback implements Callback {
         @Override
         public void sendAdjustment(String key, int newImportance) {
-            NotificationEntry entry = mLiveNotifications.get(key);
-            if (entry != null) {
-                Bundle bundle = new Bundle();
-                bundle.putInt(KEY_IMPORTANCE, newImportance);
-                Adjustment adjustment = new Adjustment(entry.getSbn().getPackageName(), key, bundle,
-                        "aging", entry.getSbn().getUserId());
-                adjustNotification(adjustment);
+            if (AGE_NOTIFICATIONS) {
+                NotificationEntry entry = mLiveNotifications.get(key);
+                if (entry != null) {
+                    Bundle bundle = new Bundle();
+                    bundle.putInt(KEY_IMPORTANCE, newImportance);
+                    Adjustment adjustment = new Adjustment(entry.getSbn().getPackageName(), key,
+                            bundle, "aging", entry.getSbn().getUserId());
+                    adjustNotification(adjustment);
+                }
             }
         }
     }
diff --git a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
index 29ee920..ca3daad 100644
--- a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
+++ b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
@@ -37,6 +37,8 @@
     static final String ATT_DISMISSALS = "dismisses";
     static final String ATT_VIEWS = "views";
     static final String ATT_STREAK = "streak";
+    static final String ATT_SENT = "sent";
+    static final String ATT_INTERRUPTIVE = "interruptive";
 
     private int mDismissals = 0;
     private int mViews = 0;
diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
index 8fee822..6f437bd5 100644
--- a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
+++ b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
@@ -15,6 +15,7 @@
  */
 package android.ext.services.notification;
 
+import static android.app.Notification.CATEGORY_MESSAGE;
 import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -148,6 +149,18 @@
         return Objects.equals(getNotification().category, category);
     }
 
+    /**
+     * Similar to {@link #isCategory(String)}, but checking the public version of the notification,
+     * if available.
+     */
+    public boolean isPublicVersionCategory(String category) {
+        Notification publicVersion = getNotification().publicVersion;
+        if (publicVersion == null) {
+            return false;
+        }
+        return Objects.equals(publicVersion.category, category);
+    }
+
     public boolean isAudioAttributesUsage(int usage) {
         return mAttributes != null && mAttributes.getUsage() == usage;
     }
@@ -175,9 +188,9 @@
     }
 
     protected boolean isMessaging() {
-        return isCategory(Notification.CATEGORY_MESSAGE)
-                || hasStyle(Notification.MessagingStyle.class)
-                || hasInlineReply();
+        return isCategory(CATEGORY_MESSAGE)
+                || isPublicVersionCategory(CATEGORY_MESSAGE)
+                || hasStyle(Notification.MessagingStyle.class);
     }
 
     public boolean hasInlineReply() {
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 37a98fd..b2fc417 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -19,23 +19,22 @@
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.RemoteAction;
-import android.app.RemoteInput;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.Process;
-import android.os.SystemProperties;
-import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLinks;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
 
 public class SmartActionsHelper {
     private static final ArrayList<Notification.Action> EMPTY_ACTION_LIST = new ArrayList<>();
@@ -50,12 +49,18 @@
     private static final int MAX_ACTION_EXTRACTION_TEXT_LENGTH = 400;
     private static final int MAX_ACTIONS_PER_LINK = 1;
     private static final int MAX_SMART_ACTIONS = Notification.MAX_ACTION_BUTTONS;
-    // Allow us to test out smart reply with dumb suggestions, it is disabled by default.
-    // TODO: Removed this once we have the model.
-    private static final String SYS_PROP_SMART_REPLIES_EXPERIMENT =
-            "persist.sys.smart_replies_experiment";
+    private static final int MAX_SUGGESTED_REPLIES = 3;
 
-    SmartActionsHelper() {}
+    private static final ConversationActions.TypeConfig TYPE_CONFIG =
+            new ConversationActions.TypeConfig.Builder().setIncludedTypes(
+                    Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+                    .includeTypesFromTextClassifier(false)
+                    .build();
+    private static final List<String> HINTS =
+            Collections.singletonList(ConversationActions.HINT_FOR_NOTIFICATION);
+
+    SmartActionsHelper() {
+    }
 
     /**
      * Adds action adjustments based on the notification contents.
@@ -92,8 +97,31 @@
         if (context == null) {
             return EMPTY_REPLY_LIST;
         }
-        // TODO: replaced this with our model when it is ready.
-        return new ArrayList<>(Arrays.asList("Yes, please", "No, thanks"));
+        TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class);
+        if (tcm == null) {
+            return EMPTY_REPLY_LIST;
+        }
+        CharSequence text = getMostSalientActionText(entry.getNotification());
+        ConversationActions.Message message =
+                new ConversationActions.Message.Builder()
+                        .setText(text)
+                        .build();
+
+        ConversationActions.Request request =
+                new ConversationActions.Request.Builder(Collections.singletonList(message))
+                        .setMaxSuggestions(MAX_SUGGESTED_REPLIES)
+                        .setHints(HINTS)
+                        .setTypeConfig(TYPE_CONFIG)
+                        .build();
+
+        TextClassifier textClassifier = tcm.getTextClassifier();
+        List<ConversationActions.ConversationAction> conversationActions =
+                textClassifier.suggestConversationActions(request).getConversationActions();
+
+        return conversationActions.stream()
+                .map(conversationAction -> conversationAction.getTextReply())
+                .filter(textReply -> !TextUtils.isEmpty(textReply))
+                .collect(Collectors.toCollection(ArrayList::new));
     }
 
     /**
@@ -124,20 +152,30 @@
     }
 
     private boolean isEligibleForReplyAdjustment(@NonNull NotificationEntry entry) {
-        if (!SystemProperties.getBoolean(SYS_PROP_SMART_REPLIES_EXPERIMENT, false)) {
+        if (!Process.myUserHandle().equals(entry.getSbn().getUser())) {
             return false;
         }
-        Notification notification = entry.getNotification();
-        if (notification.actions == null) {
+        String pkg = entry.getSbn().getPackageName();
+        if (TextUtils.isEmpty(pkg) || pkg.equals("android")) {
             return false;
         }
-        return entry.hasInlineReply();
+        // For now, we are only interested in messages.
+        if (!entry.isMessaging()) {
+            return false;
+        }
+        // Does not make sense to provide suggested replies if it is not something that can be
+        // replied.
+        if (!entry.hasInlineReply()) {
+            return false;
+        }
+        return true;
     }
 
     /** Returns the text most salient for action extraction in a notification. */
     @Nullable
     private CharSequence getMostSalientActionText(@NonNull Notification notification) {
         /* If it's messaging style, use the most recent message. */
+        // TODO: Use the last few X messages instead and take the Person object into consideration.
         Parcelable[] messages = notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
         if (messages != null && messages.length != 0) {
             Bundle lastMessage = (Bundle) messages[messages.length - 1];
diff --git a/packages/OsuLogin/Android.mk b/packages/OsuLogin/Android.mk
index f04227b..2c07615 100644
--- a/packages/OsuLogin/Android.mk
+++ b/packages/OsuLogin/Android.mk
@@ -3,6 +3,7 @@
 
 LOCAL_MODULE_TAGS := optional
 LOCAL_USE_AAPT2 := true
+LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/OsuLogin/AndroidManifest.xml b/packages/OsuLogin/AndroidManifest.xml
index bc32d10..123559a 100644
--- a/packages/OsuLogin/AndroidManifest.xml
+++ b/packages/OsuLogin/AndroidManifest.xml
@@ -17,10 +17,10 @@
  */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.hotspot2">
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
-    <uses-permission android:name="android.permission.INTERNET" />
+          package="com.android.hotspot2">
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
 
     <application
         android:networkSecurityConfig="@xml/network_security_config"
@@ -28,7 +28,10 @@
         android:label="@string/app_name"
         android:configChanges="keyboardHidden|orientation|screenSize"
         android:supportsRtl="true">
-        <activity android:name="com.android.hotspot2.osu.OsuLoginActivity">
+        <activity android:name="com.android.hotspot2.osu.OsuLoginActivity"
+                  android:label="@string/action_bar_label"
+                  android:theme="@style/AppTheme"
+                  android:configChanges="keyboardHidden|orientation|screenSize">
             <intent-filter>
                 <action android:name="android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW"/>
                 <category android:name="android.intent.category.DEFAULT"/>
diff --git a/packages/OsuLogin/res/layout/osu_web_view.xml b/packages/OsuLogin/res/layout/osu_web_view.xml
index 4eafb39..fb3c065 100644
--- a/packages/OsuLogin/res/layout/osu_web_view.xml
+++ b/packages/OsuLogin/res/layout/osu_web_view.xml
@@ -1,13 +1,38 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/container"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <WebView
-        android:id="@+id/webview"
+             xmlns:tools="http://schemas.android.com/tools"
+             android:id="@+id/container"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             tools:context="com.android.hotspot2.osu.OsuLoginActivity">
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentRight="true" />
+        android:orientation="vertical">
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="4dp">
 
+            <!-- Eliminates ProgressBar padding by boxing it into a 4dp high container -->
+            <ProgressBar
+                android:id="@+id/progress_bar"
+                style="@android:style/Widget.Material.Light.ProgressBar.Horizontal"
+                android:indeterminate="false"
+                android:max="100"
+                android:progress="0"
+                android:layout_gravity="center"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+        </FrameLayout>
+        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+            android:id="@+id/swipe_refresh"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+        <WebView
+            android:id="@+id/webview"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_alignParentBottom="false"
+            android:layout_alignParentRight="false"/>
+        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
+    </LinearLayout>
 </FrameLayout>
diff --git a/packages/OsuLogin/res/values/strings.xml b/packages/OsuLogin/res/values/strings.xml
index 9fafeef..06fa8c7 100644
--- a/packages/OsuLogin/res/values/strings.xml
+++ b/packages/OsuLogin/res/values/strings.xml
@@ -1,4 +1,6 @@
 <resources>
     <!-- application name [CHAR LIMIT=32] -->
     <string name="app_name">OsuLogin</string>
+    <!-- action bar label [CHAR LIMIT=32] -->
+    <string name="action_bar_label">Online Sign Up</string>
 </resources>
diff --git a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
index 6a48904..0312b81 100644
--- a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
+++ b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
@@ -16,9 +16,12 @@
 
 package com.android.hotspot2.osu;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
+
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.net.ConnectivityManager;
 import android.net.Network;
 import android.net.NetworkCapabilities;
@@ -27,17 +30,22 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.view.KeyEvent;
+import android.view.View;
+import android.webkit.WebChromeClient;
 import android.webkit.WebResourceError;
 import android.webkit.WebResourceRequest;
 import android.webkit.WebSettings;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
+import android.widget.ProgressBar;
 
 import com.android.hotspot2.R;
 
 import java.net.MalformedURLException;
 import java.net.URL;
 
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
 /**
  * Online Sign Up Login Web View launched during Provision Process of Hotspot 2.0 rel2.
  */
@@ -52,6 +60,8 @@
     private ConnectivityManager.NetworkCallback mNetworkCallback;
     private WifiManager mWifiManager;
     private WebView mWebView;
+    private SwipeRefreshLayout mSwipeRefreshLayout;
+    private ProgressBar mProgressBar;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -117,6 +127,9 @@
         }
 
         getActionBar().setDisplayShowHomeEnabled(false);
+        getActionBar().setElevation(0); // remove shadow
+        getActionBar().setTitle(getString(R.string.action_bar_label));
+        getActionBar().setSubtitle("");
         setContentView(R.layout.osu_web_view);
 
         // Exit this app if network disappeared.
@@ -134,7 +147,8 @@
 
         mCm.registerNetworkCallback(
                 new NetworkRequest.Builder().addTransportType(
-                        NetworkCapabilities.TRANSPORT_WIFI).build(),
+                        NetworkCapabilities.TRANSPORT_WIFI).removeCapability(
+                        NET_CAPABILITY_TRUSTED).build(),
                 mNetworkCallback);
 
         mWebView = findViewById(R.id.webview);
@@ -147,12 +161,25 @@
         webSettings.setSupportZoom(true);
         webSettings.setBuiltInZoomControls(true);
         webSettings.setDisplayZoomControls(false);
-
+        mProgressBar = findViewById(R.id.progress_bar);
         mWebView.setWebViewClient(new OsuWebViewClient());
+        mWebView.setWebChromeClient(new WebChromeClient() {
+            @Override
+            public void onProgressChanged(WebView view, int newProgress) {
+                mProgressBar.setProgress(newProgress);
+            }
+        });
+
         if (DBG) {
             Log.d(TAG, "OSU Web View to " + mUrl);
         }
+
         mWebView.loadUrl(mUrl);
+        mSwipeRefreshLayout = findViewById(R.id.swipe_refresh);
+        mSwipeRefreshLayout.setOnRefreshListener(() -> {
+            mWebView.reload();
+            mSwipeRefreshLayout.setRefreshing(true);
+        });
     }
 
     @Override
@@ -191,11 +218,31 @@
         return null;
     }
 
+    private String getHeaderSubtitle(String urlString) {
+        try {
+            URL url = new URL(urlString);
+            return url.getProtocol() + "://" +  url.getHost();
+        } catch (MalformedURLException e) {
+            Log.e(TAG, "Invalid URL " + urlString);
+        }
+        return "";
+    }
+
     private class OsuWebViewClient extends WebViewClient {
         boolean mPageError = false;
 
         @Override
+        public void onPageStarted(WebView view, String urlString, Bitmap favicon) {
+            String subtitle = getHeaderSubtitle(urlString);
+            getActionBar().setSubtitle(subtitle);
+            mProgressBar.setVisibility(View.VISIBLE);
+        }
+
+        @Override
         public void onPageFinished(WebView view, String url) {
+            mProgressBar.setVisibility(View.INVISIBLE);
+            mSwipeRefreshLayout.setRefreshing(false);
+
             // Do not show the page error on UI.
             if (mPageError) {
                 finishAndRemoveTask();
@@ -213,4 +260,5 @@
             }
          }
     }
+
 }
diff --git a/packages/PackageInstaller/res/drawable/app_icon_foreground.xml b/packages/PackageInstaller/res/drawable/app_icon_foreground.xml
index b1f40c1..2dcd49c 100644
--- a/packages/PackageInstaller/res/drawable/app_icon_foreground.xml
+++ b/packages/PackageInstaller/res/drawable/app_icon_foreground.xml
@@ -16,10 +16,10 @@
   -->
 <inset
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:insetTop="12dp"
-    android:insetRight="12dp"
-    android:insetBottom="12dp"
-    android:insetLeft="12dp">
+    android:insetTop="25%"
+    android:insetRight="25%"
+    android:insetBottom="25%"
+    android:insetLeft="25%">
 
     <vector
         android:width="24dp"
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
index 2e7c4aa..dfee4e8 100644
--- a/packages/PackageInstaller/res/values-af/strings.xml
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Gaan voort"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Instellings"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installeer/deïnstalleer Wear-programme"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Kennisgewing dat program geïnstalleer is"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Suksesvol geïnstalleer"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” is suksesvol geïnstalleer"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-am/strings.xml b/packages/PackageInstaller/res/values-am/strings.xml
index 4c3d60d..cfdeb59 100644
--- a/packages/PackageInstaller/res/values-am/strings.xml
+++ b/packages/PackageInstaller/res/values-am/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ቀጥል"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ቅንብሮች"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"የWear መተግበሪያዎችን መጫን/ማራገፍ"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"የመተግበሪያ ተጭኗል ማሳወቂያ"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"በተሳካ ሁኔታ ተጭኗል"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"«<xliff:g id="APPNAME">%1$s</xliff:g>» በተሳካ ሁኔታ ተጭኗል"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml
index 1721bf5..da91983 100644
--- a/packages/PackageInstaller/res/values-ar/strings.xml
+++ b/packages/PackageInstaller/res/values-ar/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"متابعة"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"الإعدادات"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"‏تثبيت / إلغاء تثبيت تطبيقات Android Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"إشعار \"تم تثبيت التطبيق\""</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"تم التثبيت بنجاح."</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"تم تثبيت \"<xliff:g id="APPNAME">%1$s</xliff:g>\" بنجاح."</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-as/strings.xml b/packages/PackageInstaller/res/values-as/strings.xml
new file mode 100644
index 0000000..172e031
--- /dev/null
+++ b/packages/PackageInstaller/res/values-as/strings.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for app_name (7488448184431507488) -->
+    <skip />
+    <!-- no translation found for install (711829760615509273) -->
+    <skip />
+    <!-- no translation found for done (6632441120016885253) -->
+    <skip />
+    <!-- no translation found for cancel (1018267193425558088) -->
+    <skip />
+    <!-- no translation found for installing (4921993079741206516) -->
+    <skip />
+    <!-- no translation found for installing_app (1165095864863849422) -->
+    <skip />
+    <!-- no translation found for install_done (5987363587661783896) -->
+    <skip />
+    <!-- no translation found for install_confirm_question (8176284075816604590) -->
+    <skip />
+    <!-- no translation found for install_confirm_question_update (7942235418781274635) -->
+    <skip />
+    <!-- no translation found for install_confirm_question_update_system (4713001702777910263) -->
+    <skip />
+    <!-- no translation found for install_failed (5777824004474125469) -->
+    <skip />
+    <!-- no translation found for install_failed_blocked (8512284352994752094) -->
+    <skip />
+    <!-- no translation found for install_failed_conflict (3493184212162521426) -->
+    <skip />
+    <!-- no translation found for install_failed_incompatible (6019021440094927928) -->
+    <skip />
+    <!-- no translation found for install_failed_incompatible (2890001324362291683) -->
+    <skip />
+    <!-- no translation found for install_failed_incompatible (7254630419511645826) -->
+    <skip />
+    <!-- no translation found for install_failed_invalid_apk (8581007676422623930) -->
+    <skip />
+    <!-- no translation found for install_failed_msg (6298387264270562442) -->
+    <skip />
+    <!-- no translation found for install_failed_msg (1920009940048975221) -->
+    <skip />
+    <!-- no translation found for install_failed_msg (6484461562647915707) -->
+    <skip />
+    <!-- no translation found for launch (3952550563999890101) -->
+    <skip />
+    <!-- no translation found for unknown_apps_admin_dlg_text (4456572224020176095) -->
+    <skip />
+    <!-- no translation found for unknown_apps_user_restriction_dlg_text (151020786933988344) -->
+    <skip />
+    <!-- no translation found for install_apps_user_restriction_dlg_text (2154119597001074022) -->
+    <skip />
+    <!-- no translation found for ok (7871959885003339302) -->
+    <skip />
+    <!-- no translation found for manage_applications (5400164782453975580) -->
+    <skip />
+    <!-- no translation found for out_of_space_dlg_title (4156690013884649502) -->
+    <skip />
+    <!-- no translation found for out_of_space_dlg_text (8727714096031856231) -->
+    <skip />
+    <!-- no translation found for app_not_found_dlg_title (5107924008597470285) -->
+    <skip />
+    <!-- no translation found for app_not_found_dlg_text (5219983779377811611) -->
+    <skip />
+    <!-- no translation found for user_is_not_allowed_dlg_title (6915293433252210232) -->
+    <skip />
+    <!-- no translation found for user_is_not_allowed_dlg_text (3468447791330611681) -->
+    <skip />
+    <!-- no translation found for generic_error_dlg_title (5863195085927067752) -->
+    <skip />
+    <!-- no translation found for generic_error_dlg_text (5287861443265795232) -->
+    <skip />
+    <!-- no translation found for uninstall_application_title (4045420072401428123) -->
+    <skip />
+    <!-- no translation found for uninstall_update_title (824411791011583031) -->
+    <skip />
+    <!-- no translation found for uninstall_activity_text (1928194674397770771) -->
+    <skip />
+    <!-- no translation found for uninstall_application_text (3816830743706143980) -->
+    <skip />
+    <!-- no translation found for uninstall_application_text_all_users (575491774380227119) -->
+    <skip />
+    <!-- no translation found for uninstall_application_text_user (498072714173920526) -->
+    <skip />
+    <!-- no translation found for uninstall_update_text (863648314632448705) -->
+    <skip />
+    <!-- no translation found for uninstall_update_text_multiuser (8992883151333057227) -->
+    <skip />
+    <!-- no translation found for uninstalling_notification_channel (840153394325714653) -->
+    <skip />
+    <!-- no translation found for uninstall_failure_notification_channel (1136405866767576588) -->
+    <skip />
+    <!-- no translation found for uninstalling (8709566347688966845) -->
+    <skip />
+    <!-- no translation found for uninstalling_app (8866082646836981397) -->
+    <skip />
+    <!-- no translation found for uninstall_done (439354138387969269) -->
+    <skip />
+    <!-- no translation found for uninstall_done_app (4588850984473605768) -->
+    <skip />
+    <!-- no translation found for uninstall_failed (1847750968168364332) -->
+    <skip />
+    <!-- no translation found for uninstall_failed_app (5506028705017601412) -->
+    <skip />
+    <!-- no translation found for uninstall_failed_device_policy_manager (785293813665540305) -->
+    <skip />
+    <!-- no translation found for uninstall_failed_device_policy_manager_of_user (4813104025494168064) -->
+    <skip />
+    <!-- no translation found for uninstall_all_blocked_profile_owner (2009393666026751501) -->
+    <skip />
+    <!-- no translation found for uninstall_blocked_profile_owner (6373897407002404848) -->
+    <skip />
+    <!-- no translation found for uninstall_blocked_device_owner (6724602931761073901) -->
+    <skip />
+    <!-- no translation found for manage_device_administrators (3092696419363842816) -->
+    <skip />
+    <!-- no translation found for manage_users (1243995386982560813) -->
+    <skip />
+    <!-- no translation found for uninstall_failed_msg (2176744834786696012) -->
+    <skip />
+    <!-- no translation found for Parse_error_dlg_text (1661404001063076789) -->
+    <skip />
+    <!-- no translation found for wear_not_allowed_dlg_title (8664785993465117517) -->
+    <skip />
+    <!-- no translation found for wear_not_allowed_dlg_text (704615521550939237) -->
+    <skip />
+    <!-- no translation found for message_staging (8032722385658438567) -->
+    <skip />
+    <!-- no translation found for app_name_unknown (6881210203354323926) -->
+    <skip />
+    <!-- no translation found for untrusted_external_source_warning (6539403649459942547) -->
+    <skip />
+    <!-- no translation found for untrusted_external_source_warning (1206648674551321364) -->
+    <skip />
+    <!-- no translation found for untrusted_external_source_warning (7279739265754475165) -->
+    <skip />
+    <!-- no translation found for anonymous_source_warning (2784902545920822500) -->
+    <skip />
+    <!-- no translation found for anonymous_source_warning (3939101621438855516) -->
+    <skip />
+    <!-- no translation found for anonymous_source_warning (5599483539528168566) -->
+    <skip />
+    <!-- no translation found for anonymous_source_continue (4375745439457209366) -->
+    <skip />
+    <!-- no translation found for external_sources_settings (4046964413071713807) -->
+    <skip />
+    <!-- no translation found for wear_app_channel (1960809674709107850) -->
+    <skip />
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"এপ্ ইনষ্টল কৰাৰ জাননী"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"সফলতাৰে ইনষ্টল কৰা হ’ল"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” সফলতাৰে ইনষ্টল কৰা হ’ল"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-az/strings.xml b/packages/PackageInstaller/res/values-az/strings.xml
index 2248c6d..478322a 100644
--- a/packages/PackageInstaller/res/values-az/strings.xml
+++ b/packages/PackageInstaller/res/values-az/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Davam edin"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ayarlar"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear tətbiqləri quraşdırılır/sistemdən silinir"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Tətbiq quraşdırma bildirişi"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Quraşdırıldı"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" quraşdırıldı"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
index 6cd1f40..cab6be5 100644
--- a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
+++ b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Nastavi"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Podešavanja"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instaliranje/deinstaliranje Wear aplikac."</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Obaveštenje o instaliranju aplikacije"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Instalirana je"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Aplikacija „<xliff:g id="APPNAME">%1$s</xliff:g>“ je instalirana"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-be/strings.xml b/packages/PackageInstaller/res/values-be/strings.xml
index d432eb7..4433b73 100644
--- a/packages/PackageInstaller/res/values-be/strings.xml
+++ b/packages/PackageInstaller/res/values-be/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Далей"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Налады"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Усталяванне і выдаленне праграм Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Апавяшчэнне пра завяршэнне ўсталявання"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Усталявана"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Усталявана праграма \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-bg/strings.xml b/packages/PackageInstaller/res/values-bg/strings.xml
index a1ae85c..2b76bdd 100644
--- a/packages/PackageInstaller/res/values-bg/strings.xml
+++ b/packages/PackageInstaller/res/values-bg/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Напред"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Настройки"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Инсталир./деинсталир. на прилож. за Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Известие, че приложението е инсталирано"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Инсталирането бе успешно"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Приложението <xliff:g id="APPNAME">%1$s</xliff:g> бе инсталирано успешно"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-bn/strings.xml b/packages/PackageInstaller/res/values-bn/strings.xml
index 0afcb81..ee264d9 100644
--- a/packages/PackageInstaller/res/values-bn/strings.xml
+++ b/packages/PackageInstaller/res/values-bn/strings.xml
@@ -24,11 +24,11 @@
     <string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ইনস্টল করা হচ্ছে…"</string>
     <string name="install_done" msgid="5987363587661783896">"অ্যাপটি ইনস্টল করা হয়ে গেছে।"</string>
     <string name="install_confirm_question" msgid="8176284075816604590">"আপনি কি এই অ্যাপ্লিকেশনটি ইনস্টল করতে চান?"</string>
-    <string name="install_confirm_question_update" msgid="7942235418781274635">"আগে থেকেই আছে এই অ্যাপ্লিকেশনটির একটি আপডেট কি আপনি ইনস্টল করতে চান? আপনার আগে থেকেই আছে এমন ডেটা মুছে যাবে না।"</string>
-    <string name="install_confirm_question_update_system" msgid="4713001702777910263">"এই বিল্ট-ইন অ্যাপ্লিকেশনটির একটি আপডেট কি আপনি ইনস্টল করতে চান? আপনার আগে থেকেই আছে এমন ডেটা মুছে যাবে না।"</string>
+    <string name="install_confirm_question_update" msgid="7942235418781274635">"আগে থেকেই থাকা এই অ্যাপ্লিকেশনটির একটি আপডেট কি আপনি ইনস্টল করতে চান? আপনার আগে থেকে থাকা ডেটা মুছে যাবে না।"</string>
+    <string name="install_confirm_question_update_system" msgid="4713001702777910263">"এই বিল্ট-ইন অ্যাপ্লিকেশনটির একটি আপডেট কি আপনি ইনস্টল করতে চান? আপনার আগে থেকে থাকা ডেটা মুছে যাবে না।"</string>
     <string name="install_failed" msgid="5777824004474125469">"অ্যাপটি ইনস্টল করা হয়নি।"</string>
     <string name="install_failed_blocked" msgid="8512284352994752094">"ইনস্টল হওয়া থেকে প্যাকেজটিকে ব্লক করা হয়েছে।"</string>
-    <string name="install_failed_conflict" msgid="3493184212162521426">"আগে থেকেই আছে এমন একটি প্যাকেজের সাথে প্যাকেজটির সমস্যা সৃষ্টি হওয়ায় অ্যাপটি ইনস্টল করা যায়নি।"</string>
+    <string name="install_failed_conflict" msgid="3493184212162521426">"আগে থেকেই থাকা একটি প্যাকেজের সাথে প্যাকেজটির সমস্যা সৃষ্টি হওয়ায় অ্যাপটি ইনস্টল করা যায়নি।"</string>
     <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"আপনার ট্যাবলেটের সাথে মানানসই না হওয়ায় অ্যাপটি ইনস্টল করা যায়নি।"</string>
     <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"এই অ্যাপটি আপনার টিভির সাথে মানানসই নয়।"</string>
     <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"আপনার ফোনের সাথে মানানসই না হওয়ায় অ্যাপটি ইনস্টল করা যায়নি।"</string>
@@ -70,7 +70,7 @@
     <string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"<xliff:g id="USERNAME">%1$s</xliff:g>-এর চালু থাকা ডিভাইস অ্যাডমিন অ্যাপটি আনইনস্টল করা যাচ্ছে না"</string>
     <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"কিছু ব্যবহারকারী বা প্রোফাইলের জন্য এই অ্যাপটি প্রয়োজন কিন্তু অন্যদের জন্য এটি আনইনস্টল করা হয়ে গেছে"</string>
     <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"এই অ্যাপটি আপনার প্রোফাইলের জন্য প্রয়োজন বলে এটি আনইনস্টল করা যাবে না।"</string>
-    <string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"এই অ্যাপটি আপনার ডিভাইস অ্যাডমিনিস্ট্রেটরের জন্য প্রয়োজন বলে এটি আনইনস্টল করা যাবে না।"</string>
+    <string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"অ্যাপটি আপনার ডিভাইস অ্যাডমিনিস্ট্রেটরের প্রয়োজন বলে এটি আনইনস্টল করা যাবে না।"</string>
     <string name="manage_device_administrators" msgid="3092696419363842816">"ডিভাইস অ্যাডমিন অ্যাপ পরিচালনা করুন"</string>
     <string name="manage_users" msgid="1243995386982560813">"ব্যবহারকারীদের পরিচালনা করুন"</string>
     <string name="uninstall_failed_msg" msgid="2176744834786696012">"<xliff:g id="APP_NAME">%1$s</xliff:g> আনইনস্টল করা যায়নি।"</string>
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"চালিয়ে যান"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"সেটিংস"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear অ্যাপ ইনস্টল/আনইনস্টল করা হচ্ছে"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"\'অ্যাপ ইনস্টল করা হয়েছে\' বিজ্ঞপ্তি"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"ইনস্টল করা হয়েছে"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" ইনস্টল করা হয়েছে"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-bs/strings.xml b/packages/PackageInstaller/res/values-bs/strings.xml
index 1cb8b37..97ba693 100644
--- a/packages/PackageInstaller/res/values-bs/strings.xml
+++ b/packages/PackageInstaller/res/values-bs/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Nastavi"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Postavke"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instaliranje/deinstaliranje Wear aplik."</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Obavještenje o instaliranoj aplikaciji"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Uspješno instalirano"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Aplikacija \"<xliff:g id="APPNAME">%1$s</xliff:g>\" je uspješno instalirana"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ca/strings.xml b/packages/PackageInstaller/res/values-ca/strings.xml
index f9ca139..a090808 100644
--- a/packages/PackageInstaller/res/values-ca/strings.xml
+++ b/packages/PackageInstaller/res/values-ca/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continua"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Configuració"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instal·lant o desinstal·lant apps de Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notificació d\'aplicació instal·lada"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"S\'ha instal·lat correctament"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"S\'ha instal·lat <xliff:g id="APPNAME">%1$s</xliff:g> correctament"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-cs/strings.xml b/packages/PackageInstaller/res/values-cs/strings.xml
index 6f68133..1e46214 100644
--- a/packages/PackageInstaller/res/values-cs/strings.xml
+++ b/packages/PackageInstaller/res/values-cs/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Pokračovat"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Nastavení"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalace/odinstalace aplikací pro Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Oznámení o nainstalované aplikaci"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Úspěšně nainstalováno"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Aplikace <xliff:g id="APPNAME">%1$s</xliff:g> byla úspěšně nainstalována"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-da/strings.xml b/packages/PackageInstaller/res/values-da/strings.xml
index 4b40341..d35fb9c 100644
--- a/packages/PackageInstaller/res/values-da/strings.xml
+++ b/packages/PackageInstaller/res/values-da/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Fortsæt"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Indstillinger"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installerer/afinstallerer Wear-apps"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Underretning om appinstallation"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Appen er installeret"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" er installeret"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-de/strings.xml b/packages/PackageInstaller/res/values-de/strings.xml
index 27c19b8..367eb24 100644
--- a/packages/PackageInstaller/res/values-de/strings.xml
+++ b/packages/PackageInstaller/res/values-de/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Weiter"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Einstellungen"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear-Apps installieren/deinstallieren"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Installationsbenachrichtigung für App"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Erfolgreich installiert"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" wurde erfolgreich installiert"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml
index fea3556..d179acb 100644
--- a/packages/PackageInstaller/res/values-el/strings.xml
+++ b/packages/PackageInstaller/res/values-el/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Συνέχεια"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ρυθμίσεις"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Εγκατάσταση/απεγκατάσταση εφαρμογών Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Ειδοποίηση εγκατάστασης εφαρμογής"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Εγκαταστάθηκε επιτυχώς"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Η εφαρμογή \"<xliff:g id="APPNAME">%1$s</xliff:g>\" εγκαταστάθηκε επιτυχώς"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-en-rAU/strings.xml b/packages/PackageInstaller/res/values-en-rAU/strings.xml
index ff926ac7..4016288 100644
--- a/packages/PackageInstaller/res/values-en-rAU/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rAU/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continue"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Settings"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installing/uninstalling Wear apps"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"App installed notification"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Successfully installed"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Successfully installed \'<xliff:g id="APPNAME">%1$s</xliff:g>\'"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-en-rCA/strings.xml b/packages/PackageInstaller/res/values-en-rCA/strings.xml
index ff926ac7..4016288 100644
--- a/packages/PackageInstaller/res/values-en-rCA/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rCA/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continue"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Settings"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installing/uninstalling Wear apps"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"App installed notification"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Successfully installed"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Successfully installed \'<xliff:g id="APPNAME">%1$s</xliff:g>\'"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-en-rGB/strings.xml b/packages/PackageInstaller/res/values-en-rGB/strings.xml
index ff926ac7..4016288 100644
--- a/packages/PackageInstaller/res/values-en-rGB/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rGB/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continue"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Settings"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installing/uninstalling Wear apps"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"App installed notification"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Successfully installed"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Successfully installed \'<xliff:g id="APPNAME">%1$s</xliff:g>\'"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-en-rIN/strings.xml b/packages/PackageInstaller/res/values-en-rIN/strings.xml
index ff926ac7..4016288 100644
--- a/packages/PackageInstaller/res/values-en-rIN/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rIN/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continue"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Settings"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installing/uninstalling Wear apps"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"App installed notification"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Successfully installed"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Successfully installed \'<xliff:g id="APPNAME">%1$s</xliff:g>\'"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-en-rXC/strings.xml b/packages/PackageInstaller/res/values-en-rXC/strings.xml
index 1dc8cee..6833263 100644
--- a/packages/PackageInstaller/res/values-en-rXC/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rXC/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‎‏‎‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‎‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎Continue‎‏‎‎‏‎"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‏‎‎‏‎‎‎‏‏‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎Settings‎‏‎‎‏‎"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‏‎‏‎‎Installing/uninstalling wear apps‎‏‎‎‏‎"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎App installed notification‎‏‎‎‏‎"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‏‎‎Successfully installed‎‏‎‎‏‎"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‎‎‏‎Successfully installed “‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎”‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-es-rUS/strings.xml b/packages/PackageInstaller/res/values-es-rUS/strings.xml
index 887f380..dd45a79 100644
--- a/packages/PackageInstaller/res/values-es-rUS/strings.xml
+++ b/packages/PackageInstaller/res/values-es-rUS/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Configuración"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps para Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notificación de app instalada"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Se instaló correctamente"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Se instaló correctamente \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-es/strings.xml b/packages/PackageInstaller/res/values-es/strings.xml
index 26203b0..7aef333 100644
--- a/packages/PackageInstaller/res/values-es/strings.xml
+++ b/packages/PackageInstaller/res/values-es/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ajustes"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps para Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notificación de aplicación instalada"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Se ha instalado correctamente"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"<xliff:g id="APPNAME">%1$s</xliff:g> se ha instalado correctamente"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-et/strings.xml b/packages/PackageInstaller/res/values-et/strings.xml
index cf9dd56..873ac48 100644
--- a/packages/PackageInstaller/res/values-et/strings.xml
+++ b/packages/PackageInstaller/res/values-et/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Jätka"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Seaded"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Weari rak. installimine/desinstallimine"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Rakenduse installimise märguanne"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Installimine õnnestus"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Rakenduse „<xliff:g id="APPNAME">%1$s</xliff:g>” installimine õnnestus"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
index a73a99c..6e79ac8 100644
--- a/packages/PackageInstaller/res/values-eu/strings.xml
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -54,10 +54,10 @@
     <string name="uninstall_update_title" msgid="824411791011583031">"Desinstalatu eguneratzea"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> aplikazio honen zati da:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Aplikazioa desinstalatu nahi duzu?"</string>
-    <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Erabiltzaile "<b>"guztiei"</b>" desinstalatu nahi diezu aplikazioa? Aplikazioa eta bere datu guztiak ezabatuko zaizkie gailuko erabiltzaile "<b>"guztiei"</b>"."</string>
+    <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Erabiltzaile "<b>"guztiei"</b>" desinstalatu nahi diezu aplikazioa? Aplikazioa eta haren datu guztiak ezabatuko zaizkie gailuko erabiltzaile "<b>"guztiei"</b>"."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> erabiltzaileari desinstalatu nahi diozu aplikazioa?"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"Aplikazio hau jatorrizko bertsioarekin ordeztu nahi duzu? Datu guztiak ezabatuko dira."</string>
-    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Aplikazio hau jatorrizko bertsioarekin ordeztu nahi duzu? Datu guztiak ezabatuko dira. Gailuaren erabiltzaile guztiengan izango du eragina, laneko profilak dituztenengan barne."</string>
+    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Aplikazio hau jatorrizko bertsioarekin ordeztu nahi duzu? Datu guztiak ezabatuko dira. Gailuaren erabiltzaile guztiengan izango du eragina, laneko profilak dituztenak barne."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Abian diren desinstalatze-eragiketak"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Desinstalatu ezin izan direnak"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Desinstalatzen…"</string>
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Egin aurrera"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ezarpenak"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear aplikazioak instalatzea/desinstalatzea"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Aplikazioa instalatu izanaren jakinarazpena"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Instalatu da"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Instalatu da \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-fa/strings.xml b/packages/PackageInstaller/res/values-fa/strings.xml
index be685a9..7a36387 100644
--- a/packages/PackageInstaller/res/values-fa/strings.xml
+++ b/packages/PackageInstaller/res/values-fa/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ادامه"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"تنظیمات"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"نصب/حذف نصب برنامه‌های پوشیدنی"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"اعلان نصب برنامه"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"نصب موفقیت‌آمیز بود"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"<xliff:g id="APPNAME">%1$s</xliff:g> باموفقیت نصب شد"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-fi/strings.xml b/packages/PackageInstaller/res/values-fi/strings.xml
index df73ed7..b73c50b 100644
--- a/packages/PackageInstaller/res/values-fi/strings.xml
+++ b/packages/PackageInstaller/res/values-fi/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Jatka"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Asetukset"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear-sovellusten asennus/poistaminen"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Sovellus asennettu ‑ilmoitus"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Asennus onnistui"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"<xliff:g id="APPNAME">%1$s</xliff:g>: asennus onnistui"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-fr-rCA/strings.xml b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
index 809d20c..a6cd289 100644
--- a/packages/PackageInstaller/res/values-fr-rCA/strings.xml
+++ b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuer"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Paramètres"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installer/désinstaller applis Google Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notification d\'application installée"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Installation réussie"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Installation de « <xliff:g id="APPNAME">%1$s</xliff:g> » réussie"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml
index 6bbd7e8..d36322a 100644
--- a/packages/PackageInstaller/res/values-fr/strings.xml
+++ b/packages/PackageInstaller/res/values-fr/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuer"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Paramètres"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installer/Désinstaller les applis Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notification d\'application installée"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"L\'application a été installée"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"L\'application <xliff:g id="APPNAME">%1$s</xliff:g> a bien été installée"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-gl/strings.xml b/packages/PackageInstaller/res/values-gl/strings.xml
index 4f28411..a7b4278 100644
--- a/packages/PackageInstaller/res/values-gl/strings.xml
+++ b/packages/PackageInstaller/res/values-gl/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Configuración"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps para Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notificación da aplicación instalada"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Instalouse correctamente"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Instalouse correctamente a aplicación <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml
index 4aa04d3..66e6fd1 100644
--- a/packages/PackageInstaller/res/values-gu/strings.xml
+++ b/packages/PackageInstaller/res/values-gu/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"આગળ વધો"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"સેટિંગ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"એમ્બેડ ઍપ્લિકેશનો ઇન્સ્ટૉલ/અનઇન્સ્ટૉલ"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"ઍપ ઇન્સ્ટૉલ થવાનું નોટિફિકેશન"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"સફળતાપૂર્વક ઇન્સ્ટૉલ કરેલ"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” સફળતાપૂર્વક ઇન્સ્ટૉલ કરેલ"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-hi/strings.xml b/packages/PackageInstaller/res/values-hi/strings.xml
index 0a51a00..3c6031c 100644
--- a/packages/PackageInstaller/res/values-hi/strings.xml
+++ b/packages/PackageInstaller/res/values-hi/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"जारी रखें"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"सेटिंग"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear ऐप्लिकेशन इंस्टॉल/अनइंस्टॉल हो रहे हैं"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"ऐप्लिकेशन इंस्टॉल होने की सूचना"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"पूरी तरह से इंस्टॉल हो गया है"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” पूरी तरह से इंस्टॉल हो गया है"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-hr/strings.xml b/packages/PackageInstaller/res/values-hr/strings.xml
index 3169f3a..380df4a 100644
--- a/packages/PackageInstaller/res/values-hr/strings.xml
+++ b/packages/PackageInstaller/res/values-hr/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Nastavi"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Postavke"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instaliranje/deinstaliranje Wear apl."</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Obavijest o instaliranoj aplikaciji"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Uspješno instalirano"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Aplikacija \"<xliff:g id="APPNAME">%1$s</xliff:g>\" uspješno je instalirana"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-hu/strings.xml b/packages/PackageInstaller/res/values-hu/strings.xml
index 1971ea7..9cbff8b 100644
--- a/packages/PackageInstaller/res/values-hu/strings.xml
+++ b/packages/PackageInstaller/res/values-hu/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Tovább"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Beállítások"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear-alkalmazások telepítése/törlése"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Alkalmazás telepítve értesítés"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Sikeresen telepítve"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> alkalmazás sikeresen telepítve"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml
index 4892ddd..2e2c855 100644
--- a/packages/PackageInstaller/res/values-hy/strings.xml
+++ b/packages/PackageInstaller/res/values-hy/strings.xml
@@ -51,7 +51,7 @@
     <string name="generic_error_dlg_title" msgid="5863195085927067752">"Սխալ"</string>
     <string name="generic_error_dlg_text" msgid="5287861443265795232">"Չհաջողվեց ապատեղադրել հավելվածը:"</string>
     <string name="uninstall_application_title" msgid="4045420072401428123">"Հավելվածի ապատեղադրում"</string>
-    <string name="uninstall_update_title" msgid="824411791011583031">"Թարմացման ապատեղադրում"</string>
+    <string name="uninstall_update_title" msgid="824411791011583031">"Ապատեղադրել թարմացումը"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> գործողությունը հետևյալ հավելվածի մասն է`"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Ուզո՞ւմ եք ապատեղադրել այս հավելվածը։"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ապատեղադրե՞լ այս հավելվածը "<b>"բոլոր"</b>" օգտատերերի համար: Հավելվածը և դրա տվյալները կհեռացվեն սարքի "<b>"բոլոր"</b>" օգտատերերից:"</string>
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Շարունակել"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Կարգավորումներ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear հավելվածների տեղադրում/ապատեղադրում"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Ծանուցում հավելվածի տեղադրման մասին"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Տեղադրվեց"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածը տեղադրվեց"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-in/strings.xml b/packages/PackageInstaller/res/values-in/strings.xml
index 212eeb1..fadff61 100644
--- a/packages/PackageInstaller/res/values-in/strings.xml
+++ b/packages/PackageInstaller/res/values-in/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Lanjutkan"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Setelan"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Melakukan instal/uninstal aplikasi Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notifikasi penginstalan aplikasi"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Berhasil diinstal"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Berhasil menginstal “<xliff:g id="APPNAME">%1$s</xliff:g>”"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-is/strings.xml b/packages/PackageInstaller/res/values-is/strings.xml
index 6921b7b..a50714f 100644
--- a/packages/PackageInstaller/res/values-is/strings.xml
+++ b/packages/PackageInstaller/res/values-is/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Áfram"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Stillingar"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Uppsetning/fjarlæging Wear forrita"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Tilkynning um uppsett forrit"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Uppsetning tókst"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ sett upp"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml
index b44d6ea..5ca7eda 100644
--- a/packages/PackageInstaller/res/values-it/strings.xml
+++ b/packages/PackageInstaller/res/values-it/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continua"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Impostazioni"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installazione/disinstallazione app Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notifica relativa alle app installate"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Installata"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"App \"<xliff:g id="APPNAME">%1$s</xliff:g>\" installata"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-iw/strings.xml b/packages/PackageInstaller/res/values-iw/strings.xml
index 573f12a..cd94cda 100644
--- a/packages/PackageInstaller/res/values-iw/strings.xml
+++ b/packages/PackageInstaller/res/values-iw/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"המשך"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"הגדרות"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"‏מתקין/מסיר התקנה של אפליקציות Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"התראה על התקנת האפליקציה"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"הותקנה בהצלחה"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"האפליקציה \"<xliff:g id="APPNAME">%1$s</xliff:g>\" הותקנה בהצלחה"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ja/strings.xml b/packages/PackageInstaller/res/values-ja/strings.xml
index 42f1b3f..7496016 100644
--- a/packages/PackageInstaller/res/values-ja/strings.xml
+++ b/packages/PackageInstaller/res/values-ja/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"次へ"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"設定"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wearアプリ インストール/アンインストール"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"アプリのインストールに関する通知"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"インストールしました"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"「<xliff:g id="APPNAME">%1$s</xliff:g>」をインストールしました"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ka/strings.xml b/packages/PackageInstaller/res/values-ka/strings.xml
index 4b2ae75..aac14dc 100644
--- a/packages/PackageInstaller/res/values-ka/strings.xml
+++ b/packages/PackageInstaller/res/values-ka/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"გაგრძელება"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"პარამეტრები"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear აპების ინსტალაცია/დეინსტალაცია"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"შეტყობინება აპის ინსტალაციის შესახებ"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"წარმატებით დაინსტალირდა"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ წარმატებით დაინსტალირდა"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-kk/strings.xml b/packages/PackageInstaller/res/values-kk/strings.xml
index 708411c..a105a00 100644
--- a/packages/PackageInstaller/res/values-kk/strings.xml
+++ b/packages/PackageInstaller/res/values-kk/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Жалғастыру"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Параметрлер"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear қолданбасын орнату/жою"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Қолданба орнатылғаны туралы хабарландыру"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Орнатылды"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" орнатылды"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-km/strings.xml b/packages/PackageInstaller/res/values-km/strings.xml
index 78b04a0..ce0db91 100644
--- a/packages/PackageInstaller/res/values-km/strings.xml
+++ b/packages/PackageInstaller/res/values-km/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"បន្ត"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ការកំណត់"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"ការដំឡើង/ការលុបកម្មវិធីឧបករណ៍​ពាក់​"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"ការជូនដំណឹង​អំពីកម្មវិធីដែល​បានដំឡើង"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"បាន​ដំឡើង​ដោយ​ជោគជ័យ​ហើយ"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"បាន​ដំឡើង \"<xliff:g id="APPNAME">%1$s</xliff:g>\" ដោយ​ជោគជ័យ​ហើយ"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-kn/strings.xml b/packages/PackageInstaller/res/values-kn/strings.xml
index 5b9698a..4c5373e 100644
--- a/packages/PackageInstaller/res/values-kn/strings.xml
+++ b/packages/PackageInstaller/res/values-kn/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ಮುಂದುವರಿಸಿ"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"wear ಆ್ಯಪ್‌ಗಳನ್ನು ಇನ್‌ಸ್ಟಾಲ್‌/ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"ಆ್ಯಪ್ ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿರುವ ಕುರಿತು ಅಧಿಸೂಚನೆ"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"ಯಶಸ್ವಿಯಾಗಿ ಇನ್‌ಸ್ಟಾಲ್ ಆಗಿದೆ"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" ಆ್ಯಪ್ ಅನ್ನು ಯಶಸ್ವಿಯಾಗಿ ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿದೆ"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ko/strings.xml b/packages/PackageInstaller/res/values-ko/strings.xml
index 50f8157..1741b6e 100644
--- a/packages/PackageInstaller/res/values-ko/strings.xml
+++ b/packages/PackageInstaller/res/values-ko/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"계속"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"설정"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear 앱 설치/제거"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"앱 설치 완료 알림"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"설치 완료"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\'<xliff:g id="APPNAME">%1$s</xliff:g>\' 설치 완료"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml
index 94da72f..6b4abb9 100644
--- a/packages/PackageInstaller/res/values-ky/strings.xml
+++ b/packages/PackageInstaller/res/values-ky/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Улантуу"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Жөндөөлөр"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Тагынма колдонмолорду орнотуу/чыгаруу"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Колдонмолорду орноткучтун билдирмелери"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Ийгиликтүү орнотулду"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" орнотулду"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-lo/strings.xml b/packages/PackageInstaller/res/values-lo/strings.xml
index dba0dcc..aa55e3c 100644
--- a/packages/PackageInstaller/res/values-lo/strings.xml
+++ b/packages/PackageInstaller/res/values-lo/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ສືບຕໍ່"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ການຕັ້ງຄ່າ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"ການຕິດຕັ້ງ/ຖອນການຕິດຕັ້ງແອັບ Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"ການແຈ້ງເຕືອນແອັບທີ່ຕິດຕັ້ງແລ້ວ"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"ຕິດຕັ້ງສຳເລັດແລ້ວ"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"ຕິດຕັ້ງ \"<xliff:g id="APPNAME">%1$s</xliff:g>\" ສຳເລັດແລ້ວ"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-lt/strings.xml b/packages/PackageInstaller/res/values-lt/strings.xml
index 73b0bff..aec94c4 100644
--- a/packages/PackageInstaller/res/values-lt/strings.xml
+++ b/packages/PackageInstaller/res/values-lt/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Tęsti"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Nustatymai"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Įdiegiamos / pašalinamos „Wear“ program."</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Pranešimas apie įdiegtą programą"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Sėkmingai įdiegta"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ sėkmingai įdiegta"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-lv/strings.xml b/packages/PackageInstaller/res/values-lv/strings.xml
index d51a0aa..76840cb 100644
--- a/packages/PackageInstaller/res/values-lv/strings.xml
+++ b/packages/PackageInstaller/res/values-lv/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Tālāk"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Iestatījumi"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear lietotņu instalēšana/atinstalēšana"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Paziņojums par instalētu lietotni"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Sekmīgi instalēta"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Lietotne “<xliff:g id="APPNAME">%1$s</xliff:g>” sekmīgi instalēta"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-mk/strings.xml b/packages/PackageInstaller/res/values-mk/strings.xml
index 4dd948b..998850e 100644
--- a/packages/PackageInstaller/res/values-mk/strings.xml
+++ b/packages/PackageInstaller/res/values-mk/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Продолжи"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Поставки"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Се инсталираат/деинсталираат аплик. Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Известување за инсталирана апликација"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Успешно инсталирана"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"<xliff:g id="APPNAME">%1$s</xliff:g> е успешно инсталирана"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ml/strings.xml b/packages/PackageInstaller/res/values-ml/strings.xml
index bb0d39c..d2dbdcf 100644
--- a/packages/PackageInstaller/res/values-ml/strings.xml
+++ b/packages/PackageInstaller/res/values-ml/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"തുടരുക"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ക്രമീകരണം"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear ആപ്പ് ഇൻസ്‌റ്റാൾ/അൺ ഇൻസ്‌റ്റാൾ ചെയ്യുന്നു"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"ആപ്പ് ഇൻസ്‌റ്റാൾ ചെയ്‌ത അറിയിപ്പ്"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"ഇൻസ്‌റ്റാൾ ചെയ്‌തു"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" ഇൻസ്‌റ്റാൾ ചെയ്‌തു"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-mn/strings.xml b/packages/PackageInstaller/res/values-mn/strings.xml
index 5b93736..56a2128 100644
--- a/packages/PackageInstaller/res/values-mn/strings.xml
+++ b/packages/PackageInstaller/res/values-mn/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Үргэлжлүүлэх"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Тохиргоо"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear аппуудыг суулгаж/устгаж байна"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Апп суулгасны мэдэгдэл"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Амжилттай суулгасан"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>”-г амжилттай суулгасан"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
index 5bbf7b9c..d751c42 100644
--- a/packages/PackageInstaller/res/values-mr/strings.xml
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"सुरू ठेवा"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"सेटिंग्ज"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"wear अ‍ॅप्स इंस्टॉल/अनइंस्टॉल करत आहे"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"अॅप इंस्टॉल सूचना"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"यशस्वीरित्या इंस्टॉल केले"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" यशस्वीरित्या इंस्टॉल झाले"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ms/strings.xml b/packages/PackageInstaller/res/values-ms/strings.xml
index 620dc3f..674f55b 100644
--- a/packages/PackageInstaller/res/values-ms/strings.xml
+++ b/packages/PackageInstaller/res/values-ms/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Teruskan"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Tetapan"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Memasang/menyahpasang apl wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Pemberitahuan apl dipasang"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Berjaya dipasang"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Berjaya memasang \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-my/strings.xml b/packages/PackageInstaller/res/values-my/strings.xml
index c115cad..9c92957 100644
--- a/packages/PackageInstaller/res/values-my/strings.xml
+++ b/packages/PackageInstaller/res/values-my/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ရှေ့ဆက်ရန်"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ဆက်တင်များ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"wear အက်ပ်ကိုထည့်သွင်းခြင်း/ဖယ်ရှားခြင်း"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"အက်ပ်ထည့်သွင်းခြင်း အကြောင်းကြားချက်"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"ထည့်သွင်းပြီးပြီ"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" ကို ထည့်သွင်းပြီးပြီ"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-nb/strings.xml b/packages/PackageInstaller/res/values-nb/strings.xml
index f39bf2d..0d3384e 100644
--- a/packages/PackageInstaller/res/values-nb/strings.xml
+++ b/packages/PackageInstaller/res/values-nb/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Fortsett"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Innstillinger"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Installerer/avinstallerer Wear-apper"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Varsel om at appen er installert"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Installert"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"«<xliff:g id="APPNAME">%1$s</xliff:g>» er installert"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml
index 4d17a3e..a7abc17 100644
--- a/packages/PackageInstaller/res/values-ne/strings.xml
+++ b/packages/PackageInstaller/res/values-ne/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"जारी राख्नुहोस्"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"सेटिङहरू"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"वेयर एपहरूको स्थापना/स्थापना रद्द गर्दै"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"अनुप्रयोगको स्थापना गरिएको सूचना"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"सफलतापूर्वक स्थापना गरियो"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” सफलतापूर्वक स्थापना गरियो"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml
index 39fb8f0..a7a5a34 100644
--- a/packages/PackageInstaller/res/values-nl/strings.xml
+++ b/packages/PackageInstaller/res/values-nl/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Doorgaan"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Instellingen"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear-apps installeren/verwijderen"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Melding dat app is geïnstalleerd"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Installatie voltooid"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\'<xliff:g id="APPNAME">%1$s</xliff:g>\' is geïnstalleerd"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-or/strings.xml b/packages/PackageInstaller/res/values-or/strings.xml
new file mode 100644
index 0000000..348cc61
--- /dev/null
+++ b/packages/PackageInstaller/res/values-or/strings.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="7488448184431507488">"ପ୍ୟାକେଜ୍‌ ଇନଷ୍ଟଲର୍‍"</string>
+    <string name="install" msgid="711829760615509273">"ଇନଷ୍ଟଲ୍‍ କରନ୍ତୁ"</string>
+    <string name="done" msgid="6632441120016885253">"ହୋଇଗଲା"</string>
+    <string name="cancel" msgid="1018267193425558088">"କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"</string>
+    <string name="installing" msgid="4921993079741206516">"ଇନଷ୍ଟଲ୍‌ କରାଯାଉଛି…"</string>
+    <string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ଇନଷ୍ଟଲ୍‌ କରାଯାଉଛି…"</string>
+    <string name="install_done" msgid="5987363587661783896">"ଆପ୍‍ ଇନଷ୍ଟଲ୍‌ ହୋଇଗଲା।"</string>
+    <string name="install_confirm_question" msgid="8176284075816604590">"ଆପଣ ଏହି ଆପ୍ଲିକେଶନ୍ ଇନ୍‌ଷ୍ଟଲ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି?"</string>
+    <string name="install_confirm_question_update" msgid="7942235418781274635">"ପୂର୍ବରୁ ରହିଥିବା ଏହି ଆପ୍ଲିକେଶନ୍‌ରେ ଆପଣ ଅପ୍‌ଡେଟ୍ ଇନ୍‌ଷ୍ଟଲ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି? ନିଜର ବିଦ୍ୟମାନ ଡାଟାକୁ ଆପଣ ହରାଇବେ ନାହିଁ।"</string>
+    <string name="install_confirm_question_update_system" msgid="4713001702777910263">"ଏହି ବିଲ୍ଟ-ଇନ୍ ଆପ୍ଲିକେଶନ୍‌ରେ ଆପଣ ଏକ ଅପ୍‌ଡେଟ୍ ଇନ୍‌ଷ୍ଟଲ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି? ନିଜର ବିଦ୍ୟମାନ ଡାଟାକୁ ଆପଣ ହରାଇବେ ନାହିଁ।"</string>
+    <string name="install_failed" msgid="5777824004474125469">"ଆପ୍‍ ଇନଷ୍ଟଲ୍‌ ହୋଇନାହିଁ।"</string>
+    <string name="install_failed_blocked" msgid="8512284352994752094">"ଏହି ପ୍ୟାକେଜ୍‌କୁ ଇନଷ୍ଟଲ୍‍ କରାଯିବାରୁ ଅବରୋଧ କରାଯାଇଥିଲା।"</string>
+    <string name="install_failed_conflict" msgid="3493184212162521426">"ପୂର୍ବରୁ ଥିବା ପ୍ୟାକେଜ୍‍ ସହ ଏହି ପ୍ୟାକେଜ୍‌ର ସମସ୍ୟା ଉପୁଯିବାରୁ ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ ହୋଇପାରିଲା ନାହିଁ।"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"ଆପ୍‍ ଆପଣଙ୍କ ଟାବଲେଟ୍‌ ସହ କମ୍ପାଟିବଲ୍‍ ନଥିବା ହେତୁ ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ ହୋଇପାରିଲା ନାହିଁ।"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"ଏହି ଆପ୍‍ ଆପଣଙ୍କ ଟିଭି ସହ କମ୍ପାଟିବଲ୍‍ ନୁହେଁ।"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"ଆପ୍‍ ଆପଣଙ୍କ ଫୋନ୍‌ ସହ କମ୍ପାଟିବଲ୍‍ ନଥିବା ହେତୁ ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ ହୋଇପାରିଲା ନାହିଁ।"</string>
+    <string name="install_failed_invalid_apk" msgid="8581007676422623930">"ପ୍ୟାକେଜ୍‍ ଅମାନ୍ୟ ଥିବା ପରି ଜଣାପଡ଼ିବାରୁ ଆପ୍‍ ଇନ୍‌ଷ୍ଟଲ୍‍ ହୋଇପାରିଲା ନାହିଁ।"</string>
+    <string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"<xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ଆପଣଙ୍କ ଟାବଲେଟ୍‍ରେ ଇନ୍‌ଷ୍ଟଲ୍‌ କରିହେଲା ନାହିଁ।"</string>
+    <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"<xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ଆପଣଙ୍କ ଟିଭିରେ ଇନ୍‌ଷ୍ଟଲ୍‍ କରିହେଲା ନାହିଁ।"</string>
+    <string name="install_failed_msg" product="default" msgid="6484461562647915707">"<xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ଆପଣଙ୍କ ଫୋନ୍‌ରେ ଇନ୍‌ଷ୍ଟଲ୍‌ କରିହେଲା ନାହିଁ।"</string>
+    <string name="launch" msgid="3952550563999890101">"ଖୋଲନ୍ତୁ"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"ଅଜଣା ସୋର୍ସରୁ ଆସିଥିବା ଆପଗୁଡ଼ିକ ଇନଷ୍ଟଲ୍‌ କରିବାକୁ ଆପଣଙ୍କ ଆଡମିନ୍‍ ଅନୁମତି ଦିଅନ୍ତି ନାହିଁ"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ଏହି ୟୁଜରଙ୍କ ଦ୍ୱାରା ଅଜଣା ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରାଯାଇପାରିବ ନାହିଁ"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ଏହି ୟୁଜର୍‌ ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରିପାରିବେ ନାହିଁ"</string>
+    <string name="ok" msgid="7871959885003339302">"ଠିକ୍ ଅଛି"</string>
+    <string name="manage_applications" msgid="5400164782453975580">"ଆପ୍‌ଗୁଡ଼ିକର ପରିଚାଳନା କରନ୍ତୁ"</string>
+    <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ଆଉ ସ୍ଥାନ ନାହିଁ"</string>
+    <string name="out_of_space_dlg_text" msgid="8727714096031856231">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଇନଷ୍ଟଲ୍‌ କରାଯାଇପାରିଲା ନାହିଁ। କିଛି ସ୍ଥାନ ଖାଲିକରି ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+    <string name="app_not_found_dlg_title" msgid="5107924008597470285">"ଆପ୍‍ ମିଳିଲା ନାହିଁ"</string>
+    <string name="app_not_found_dlg_text" msgid="5219983779377811611">"ଇନଷ୍ଟଲ୍‌ କରାଯାଇଥିବା ଆପ୍‍ ତାଲିକାରେ ଆପ୍‍ଟି ମିଳିଲା ନାହିଁ।"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"ଅନୁମତି ନାହିଁ"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"ଏହି ଅନଇନଷ୍ଟଲେଶନ୍‍ କରିବାକୁ ବର୍ତ୍ତମାନର ୟୁଜରଙ୍କୁ ଅନୁମତି ଦିଆଯାଇନାହିଁ।"</string>
+    <string name="generic_error_dlg_title" msgid="5863195085927067752">"ତ୍ରୁଟି"</string>
+    <string name="generic_error_dlg_text" msgid="5287861443265795232">"ଆପ୍‌କୁ ଅନଇନଷ୍ଟଲ୍‍ କରାହେବ ନାହିଁ"</string>
+    <string name="uninstall_application_title" msgid="4045420072401428123">"ଆପ୍‌କୁ ଅନଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
+    <string name="uninstall_update_title" msgid="824411791011583031">"ଅପଡେଟ୍‍ ଅନଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
+    <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ହେଉଛି ନିମ୍ନ ଆପ୍‍ର ଏକ ଅଂଶ।"</string>
+    <string name="uninstall_application_text" msgid="3816830743706143980">"ଆପଣ ଏହି ଆପ୍‍ ଅନଇନଷ୍ଟଲ୍‌ କରିବାକୁ ଚାହାଁନ୍ତି କି?"</string>
+    <string name="uninstall_application_text_all_users" msgid="575491774380227119">"ଆପଣ "<b>"ସମସ୍ତ"</b>" ୟୁଜର୍‌ଙ୍କ ପାଇଁ ଏହି ଆପ୍‌କୁ ଅନଷ୍ଟଲ୍‍ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି? ଡିଭାଇସ୍‌ରେ ଥିବା "<b>"ସମସ୍ତ"</b>" ୟୁଜର୍‌ ଆପ୍ଲିକେଶନ୍‍ ଏବଂ ତାହାର ଡାଟା ବାହାର କରିଦିଆଯିବ।"</string>
+    <string name="uninstall_application_text_user" msgid="498072714173920526">"ୟୁଜର୍‌ <xliff:g id="USERNAME">%1$s</xliff:g>ଙ୍କ ପାଇଁ ଆପଣ ଏହି ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରିବେ କି?"</string>
+    <string name="uninstall_update_text" msgid="863648314632448705">"ଏହି ଆପ୍‍ ଫ୍ୟାକ୍ଟୋରୀ ଭର୍ସନ୍‍‍ ସହ ବଦଳାଇବେ? ସମସ୍ତ ଡାଟା କାଢ଼ିଦିଆଯିବ।"</string>
+    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"ଏହି ଆପ୍‍ ଫ୍ୟାକ୍ଟୋରୀ ଭର୍ସନ୍‍‍ ସହ ବଦଳାଇବେ? ସମସ୍ତ ଡାଟା ବାହାର କରିଦିଆଯିବ। ୱର୍କ ପ୍ରୋଫାଇଲ୍‍ ଥିବା ସମେତ, ଏହାଦ୍ୱାରା ଡିଭାଇସରେ ଥିବା ସମସ୍ତ ୟୁଜର୍‌ ପ୍ରଭାବିତ ହେବେ।"</string>
+    <string name="uninstalling_notification_channel" msgid="840153394325714653">"ଅନଇନଷ୍ଟଲ୍‌ ଚାଲୁଛି"</string>
+    <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ବିଫଳ ହୋଇଥିବା ଅନଇନଷ୍ଟଲ୍‌"</string>
+    <string name="uninstalling" msgid="8709566347688966845">"ଅନ୍‌ଇନଷ୍ଟଲ୍‌ କରାଯାଉଛି…"</string>
+    <string name="uninstalling_app" msgid="8866082646836981397">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ଅନଇନଷ୍ଟଲ୍‍ କରାଯାଉଛି…"</string>
+    <string name="uninstall_done" msgid="439354138387969269">"ଅନଇନଷ୍ଟଲ୍‌ ହୋଇଗଲା।"</string>
+    <string name="uninstall_done_app" msgid="4588850984473605768">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>କୁ ଅନଇନଷ୍ଟଲ୍‌ କରାଗଲା"</string>
+    <string name="uninstall_failed" msgid="1847750968168364332">"ଅନଇନଷ୍ଟଲ୍‌ କରିହେଲା ନାହିଁ।"</string>
+    <string name="uninstall_failed_app" msgid="5506028705017601412">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ଅନଇନଷ୍ଟଲ୍‍ କରିବା ସଫଳ ହେଲାନାହିଁ।"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"ସକ୍ରିୟ ଡିଭାଇସ୍‍ ଆପ୍‍ ଅନଇନଷ୍ଟଲ୍‌ କରିହେବ ନାହିଁ"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"<xliff:g id="USERNAME">%1$s</xliff:g>ଙ୍କ ପାଇଁ ସକ୍ରିୟ ଡିଭାଇସ୍‍ ଆଡମିନ୍‍ ଆପ୍‍ ଅନଇନଷ୍ଟଲ୍‍ କରାଯାଇପାରିବ ନାହିଁ"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"କିଛି ୟୁଜର୍‌ କିମ୍ବା ପ୍ରୋଫାଇଲ୍‍ ପାଇଁ ଏହି ଆପ୍‍ ଆବଶ୍ୟକ ଏବଂ ଅନ୍ୟ ସମସ୍ତଙ୍କ ପାଇଁ ଅନଇନଷ୍ଟଲ୍‍ କରାଯାଇଥିଲା"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"ଏହି ଆପ୍‍ ଆପଣଙ୍କ ପ୍ରୋଫାଇଲ୍‍ ପାଇଁ ଆବଶ୍ୟକ ଅଟେ ଏବଂ ଅନଇନଷ୍ଟଲ୍‍ କରାଯାଇପାରିବ ନାହିଁ।"</string>
+    <string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"ଆପଣଙ୍କ ଡିଭାଇସ୍‍ ଆଡମିନିଷ୍ଟ୍ରେଟର୍‍ ଦ୍ୱାରା ଏହି ଆପ୍‍ ଆବଶ୍ୟକ କରାଯାଇଥାଏ ଏବଂ ଏହାକୁ ଅନ୍‍ଇନଷ୍ଟଲ୍‍ କରିହେବ ନାହିଁ।"</string>
+    <string name="manage_device_administrators" msgid="3092696419363842816">"ଡିଭାଇସ୍‌ ଆଡମିନ୍‌ ଆପ୍‌ ପରିଚାଳନା କରନ୍ତୁ"</string>
+    <string name="manage_users" msgid="1243995386982560813">"ୟୁଜର୍‌ଙ୍କୁ ପରିଚାଳନା କରନ୍ତୁ"</string>
+    <string name="uninstall_failed_msg" msgid="2176744834786696012">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଅନଇନଷ୍ଟଲ୍‍ କରିହେଲା ନାହିଁ।"</string>
+    <string name="Parse_error_dlg_text" msgid="1661404001063076789">"ଏହି ପ୍ୟାକେଜ୍‍ ପାର୍ସ କରିବାରେ ସମସ୍ୟା ଥିଲା।"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8664785993465117517">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"ୱିଅର୍‌ରେ ଇନଷ୍ଟଲ୍‍/ଅନଇନଷ୍ଟଲ୍‍ କାର୍ଯ୍ୟ ସପୋର୍ଟ କରେନାହିଁ।"</string>
+    <string name="message_staging" msgid="8032722385658438567">"ଆପ୍‍ ପର୍ଯ୍ୟାୟଭୁକ୍ତ କରାଯାଉଛି…"</string>
+    <string name="app_name_unknown" msgid="6881210203354323926">"ଅଜଣା"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"ଆପଣଙ୍କ ସୁରକ୍ଷା ପାଇଁ, ଆପଣଙ୍କ ଟାବ୍‌ଲେଟ୍‌କୁ ଏହି ସୋର୍ସରୁ ଆସିଥିବା ଅଜଣା ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରିବାକୁ ଅନୁମତି ଦିଆଯାଇନାହିଁ।"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"ଆପଣଙ୍କ ସୁରକ୍ଷା ପାଇଁ, ଆପଣଙ୍କ ଟିଭିକୁ ଏହି ସୋର୍ସରୁ ଆସିଥିବା ଅଜଣା ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରିବାକୁ ଅନୁମତି ଦିଆଯାଇନାହିଁ।"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"ଆପଣଙ୍କ ସୁରକ୍ଷା ପାଇଁ, ଆପଣଙ୍କ ଫୋନ୍‌କୁ ଏହି ସୋର୍ସରୁ ଆସିଥିବା ଅଜଣା ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରିବାକୁ ଅନୁମତି ଦିଆଯାଇନାହିଁ।"</string>
+    <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"ଅଜଣା ଆପ୍‌ ଦ୍ୱାରା ଆପଣଙ୍କ ଫୋନ୍‍ ଏବଂ ବ୍ୟକ୍ତିଗତ ଡାଟାକୁ ନଷ୍ଟ କରାଯାଇପାରିବାର ସମ୍ଭାବନା ବହୁତ ଅଧିକ। ଏହି ଆପ୍‌କୁ ଇନଷ୍ଟଲ୍‌ କରିବାର ଅର୍ଥ ହେଉଛି ଆପଣଙ୍କ ଫୋନ୍‌ରେ ଘଟିବା କୌଣସି ପ୍ରକାର କ୍ଷତି କିମ୍ବା ସେଗୁଡ଼ିକର ବ୍ୟବହାରରୁ ହେବା କୌଣସି ପ୍ରକାର ଡାଟାର ହାନୀ ପାଇଁ ଆପଣ ଦାୟୀ ରହିବାକୁ ରାଜି ହୁଅନ୍ତି।"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ଅଜଣା ଆପ୍‌ ଦ୍ୱାରା ଆପଣଙ୍କ ଟାବଲେଟ୍‍ ଏବଂ ବ୍ୟକ୍ତିଗତ ଡାଟାକୁ ନଷ୍ଟ କରାଯାଇପାରିବାର ସମ୍ଭାବନା ବହୁତ ଅଧିକ। ଏହି ଆପ୍‌କୁ ଇନଷ୍ଟଲ୍‌ କରିବାର ଅର୍ଥ ହେଉଛି ଆପଣଙ୍କ ଟାବ୍‌ଲେଟ୍‌ରେ ଘଟିବା କୌଣସି ପ୍ରକାର କ୍ଷତି କିମ୍ବା ସେଗୁଡ଼ିକର ବ୍ୟବହାରରୁ ହେବା କୌଣସି ପ୍ରକାର ଡାଟାର ହାନୀ ପାଇଁ ଆପଣ ଦାୟୀ ରହିବାକୁ ରାଜି ହୁଅନ୍ତି।"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ଅଜଣା ଆପ୍‌ ଦ୍ୱାରା ଆପଣଙ୍କ ଟିଭି ଏବଂ ବ୍ୟକ୍ତିଗତ ଡାଟାକୁ ନଷ୍ଟ କରାଯାଇପାରିବାର ସମ୍ଭାବନା ବହୁତ ଅଧିକ। ଏହି ଆପ୍‌କୁ ଇନଷ୍ଟଲ୍‌ କରିବାର ଅର୍ଥ ହେଉଛି ଆପଣଙ୍କ ଟିଭିରେ ଘଟିବା କୌଣସି ପ୍ରକାର କ୍ଷତି କିମ୍ବା ସେଗୁଡ଼ିକର ବ୍ୟବହାରରୁ ହେବା କୌଣସି ପ୍ରକାର ଡାଟାର ହାନୀ ପାଇଁ ଆପଣ ଦାୟୀ ରହିବାକୁ ରାଜି ହୁଅନ୍ତି।"</string>
+    <string name="anonymous_source_continue" msgid="4375745439457209366">"ଜାରି ରଖନ୍ତୁ"</string>
+    <string name="external_sources_settings" msgid="4046964413071713807">"ସେଟିଙ୍ଗ"</string>
+    <string name="wear_app_channel" msgid="1960809674709107850">"ୱିଅର୍‍ ଆପ୍‍ ଇନଷ୍ଟଲ୍‌/ଅନଇନଷ୍ଟଲ୍‍ କରାଯାଉଛି"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"ଆପ୍ ଇନ୍‌ଷ୍ଟଲ୍‌ କରାଯାଇଥିବା ବିଜ୍ଞପ୍ତି"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"ସଫଳତାପୂର୍ବକ ଇନ୍‌ଷ୍ଟଲ୍‌ କରାଗଲା"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” ସଫଳତାପୂର୍ବକ ଇନ୍‌ଷ୍ଚଲ୍ କରାଗଲା"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml
index 6a1d7eb..7c519a0 100644
--- a/packages/PackageInstaller/res/values-pa/strings.xml
+++ b/packages/PackageInstaller/res/values-pa/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ਜਾਰੀ ਰੱਖੋ"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"ਵੀਅਰ ਐਪਾਂ ਸਥਾਪਤ ਜਾਂ ਅਣਸਥਾਪਤ ਕਰਨਾ"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"ਐਪ ਸਥਾਪਨਾ ਦੀ ਸੂਚਨਾ"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"ਸਫਲਤਾਪੂਰਵਕ ਸਥਾਪਤ ਕੀਤੀ ਗਈ"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਸਥਾਪਤ ਕੀਤਾ ਗਿਆ"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-pl/strings.xml b/packages/PackageInstaller/res/values-pl/strings.xml
index 01b2db9..19b05a9 100644
--- a/packages/PackageInstaller/res/values-pl/strings.xml
+++ b/packages/PackageInstaller/res/values-pl/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Dalej"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ustawienia"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalacja/usuwanie aplikacji na Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Powiadomienie o zainstalowaniu aplikacji"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Zainstalowano"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Zainstalowano aplikację „<xliff:g id="APPNAME">%1$s</xliff:g>”"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-pt-rBR/strings.xml b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
index 47289f9..1a31837 100644
--- a/packages/PackageInstaller/res/values-pt-rBR/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Configurações"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps do Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notificação de app instalado"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Instalação concluída"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” instalado"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-pt-rPT/strings.xml b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
index 871b0a1..d45dc1c 100644
--- a/packages/PackageInstaller/res/values-pt-rPT/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Definições"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalar/desinstalar aplicações Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notificação de aplicação instalada"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Aplicação instalada com êxito"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Aplicação \"<xliff:g id="APPNAME">%1$s</xliff:g>\" instalada com êxito"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-pt/strings.xml b/packages/PackageInstaller/res/values-pt/strings.xml
index 47289f9..1a31837 100644
--- a/packages/PackageInstaller/res/values-pt/strings.xml
+++ b/packages/PackageInstaller/res/values-pt/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Configurações"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps do Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notificação de app instalado"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Instalação concluída"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” instalado"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml
index 43b0dea..8bcc969 100644
--- a/packages/PackageInstaller/res/values-ro/strings.xml
+++ b/packages/PackageInstaller/res/values-ro/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuați"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Setări"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Se (dez)instalează aplicațiile Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notificare de aplicație instalată"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Aplicația a fost instalată"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"„<xliff:g id="APPNAME">%1$s</xliff:g>” a fost instalată"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ru/strings.xml b/packages/PackageInstaller/res/values-ru/strings.xml
index 63287f4..4e5d1ac 100644
--- a/packages/PackageInstaller/res/values-ru/strings.xml
+++ b/packages/PackageInstaller/res/values-ru/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Продолжить"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Настройки"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Установка/удаление прилож. для Wear OS"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Уведомление об установке приложения"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Установлено"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Приложение \"<xliff:g id="APPNAME">%1$s</xliff:g>\" установлено"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-si/strings.xml b/packages/PackageInstaller/res/values-si/strings.xml
index 2e926af..7d854dc 100644
--- a/packages/PackageInstaller/res/values-si/strings.xml
+++ b/packages/PackageInstaller/res/values-si/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ඉදිරියට යන්න"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"සැකසීම්"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear යෙදුම් ස්ථාපනය/අස්ථාපනය කරමින්"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"යෙදුම් ස්ථාපනය කළ දැනුම්දීම"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"සාර්ථකව ස්ථාපනය කරන ලදී"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" සාර්ථකව ස්ථාපනය කරන ලදි"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-sk/strings.xml b/packages/PackageInstaller/res/values-sk/strings.xml
index f8e1e01..36614bc 100644
--- a/packages/PackageInstaller/res/values-sk/strings.xml
+++ b/packages/PackageInstaller/res/values-sk/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Pokračovať"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Nastavenia"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Inštalácia/odinštalovanie aplikácií Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Upozornenie na inštaláciu aplikácie"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Úspešne nainštalovaná"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Aplikácia <xliff:g id="APPNAME">%1$s</xliff:g> bola nainštalovaná"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-sl/strings.xml b/packages/PackageInstaller/res/values-sl/strings.xml
index d67edd5..a679e08 100644
--- a/packages/PackageInstaller/res/values-sl/strings.xml
+++ b/packages/PackageInstaller/res/values-sl/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Naprej"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Nastavitve"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Nameščanje/odstranjev. aplikacij za Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Obvestilo o namestitvi aplikacije"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Uspešno nameščeno"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Uspešno nameščeno: »<xliff:g id="APPNAME">%1$s</xliff:g>«"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-sq/strings.xml b/packages/PackageInstaller/res/values-sq/strings.xml
index 7c06656..dd35024 100644
--- a/packages/PackageInstaller/res/values-sq/strings.xml
+++ b/packages/PackageInstaller/res/values-sq/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Vazhdo"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Cilësimet"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Instalimi/çinstalimi i aplikacioneve të Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Njoftimi për aplikacionin e instaluar"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"U instalua me sukses"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” u instalua me sukses"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-sr/strings.xml b/packages/PackageInstaller/res/values-sr/strings.xml
index 9ff859a..c25b8e0f 100644
--- a/packages/PackageInstaller/res/values-sr/strings.xml
+++ b/packages/PackageInstaller/res/values-sr/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Настави"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Подешавања"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Инсталирање/деинсталирање Wear апликац."</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Обавештење о инсталирању апликације"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Инсталирана је"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Апликација „<xliff:g id="APPNAME">%1$s</xliff:g>“ је инсталирана"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-sv/strings.xml b/packages/PackageInstaller/res/values-sv/strings.xml
index 43c2aad..0c66b53c 100644
--- a/packages/PackageInstaller/res/values-sv/strings.xml
+++ b/packages/PackageInstaller/res/values-sv/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Fortsätt"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Inställningar"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear-appar installeras/avinstalleras"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Avisering om installerad app"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Appen har installerats"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"<xliff:g id="APPNAME">%1$s</xliff:g> har installerats"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-sw/strings.xml b/packages/PackageInstaller/res/values-sw/strings.xml
index 1c07291..4557f66 100644
--- a/packages/PackageInstaller/res/values-sw/strings.xml
+++ b/packages/PackageInstaller/res/values-sw/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Endelea"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Mipangilio"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Inasakinisha/inaondoa programu za Android Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Arifa ya kusakinishwa kwa programu"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Imesakinishwa"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"<xliff:g id="APPNAME">%1$s</xliff:g> imesakinishwa"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ta/strings.xml b/packages/PackageInstaller/res/values-ta/strings.xml
index c067bd2..dab459a 100644
--- a/packages/PackageInstaller/res/values-ta/strings.xml
+++ b/packages/PackageInstaller/res/values-ta/strings.xml
@@ -29,9 +29,9 @@
     <string name="install_failed" msgid="5777824004474125469">"ஆப்ஸ் நிறுவப்படவில்லை."</string>
     <string name="install_failed_blocked" msgid="8512284352994752094">"இந்தத் தொகுப்பு நிறுவப்படுவதிலிருந்து தடுக்கப்பட்டது."</string>
     <string name="install_failed_conflict" msgid="3493184212162521426">"இந்தத் தொகுப்பு ஏற்கனவே உள்ள தொகுப்புடன் முரண்படுவதால் ஆப்ஸ் நிறுவப்படவில்லை."</string>
-    <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"ஆப்ஸ் உங்கள் டேப்லெட்டுடன் இணக்கமற்றதாக உள்ளதால் நிறுவப்படவில்லை."</string>
-    <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"இந்த ஆப்ஸ் உங்கள் டிவியுடன் இணக்கமற்றதாக உள்ளது."</string>
-    <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"ஆப்ஸ் உங்கள் மொபைலுடன் இணக்கமற்றதாக உள்ளதால் நிறுவப்படவில்லை."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"ஆப்ஸ் உங்கள் டேப்லெட்டில் இயங்குமாறு இல்லாததால் நிறுவப்படவில்லை."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"இந்த ஆப்ஸ் உங்கள் டிவியில் இயங்காது."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"ஆப்ஸ் உங்கள் மொபைலில் இயங்குமாறு இல்லாததால் நிறுவப்படவில்லை."</string>
     <string name="install_failed_invalid_apk" msgid="8581007676422623930">"இந்தத் தொகுப்பு செல்லாததுபோல் இருப்பதால் ஆப்ஸ் நிறுவப்படவில்லை."</string>
     <string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸை உங்கள் டேப்லெட்டில் நிறுவ இயலவில்லை."</string>
     <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸை உங்கள் டிவியில் நிறுவ இயலவில்லை."</string>
@@ -51,7 +51,7 @@
     <string name="generic_error_dlg_title" msgid="5863195085927067752">"பிழை"</string>
     <string name="generic_error_dlg_text" msgid="5287861443265795232">"ஆப்ஸை நிறுவல் நீக்க இயலவில்லை."</string>
     <string name="uninstall_application_title" msgid="4045420072401428123">"ஆப்ஸை நிறுவல் நீக்குதல்"</string>
-    <string name="uninstall_update_title" msgid="824411791011583031">"புதுப்பிப்பை நிறுவல் நீக்கு"</string>
+    <string name="uninstall_update_title" msgid="824411791011583031">"புதுப்பிப்பை நிறுவல் நீக்குதல்"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> என்பது பின்வரும் ஆப்ஸின் பகுதியாகும்:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"இந்த ஆப்ஸை நிறுவல் நீக்க விரும்புகிறீர்களா?"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"இந்த ஆப்ஸை "<b>"அனைத்துப்"</b>" பயனர்களுக்கும் நிறுவல் நீக்க விரும்புகிறீர்களா? ஆப்ஸும் அதன் தரவும் சாதனத்திலுள்ள "<b>"அனைத்துப்"</b>" பயனர்களிடமிருந்தும் அகற்றப்படும்."</string>
@@ -68,24 +68,27 @@
     <string name="uninstall_failed_app" msgid="5506028705017601412">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ஐ நிறுவல் நீக்குவதில் தோல்வி."</string>
     <string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"செயலிலுள்ள \'சாதன நிர்வாகி ஆப்ஸை\' நிறுவல் நீக்க இயலாது"</string>
     <string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"<xliff:g id="USERNAME">%1$s</xliff:g> என்பவருக்குச் \'செயலிலுள்ள சாதன நிர்வாகி ஆப்ஸை’ நிறுவல் நீக்க இயலாது"</string>
-    <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"இந்த ஆப்ஸ் சில பயனர்களுக்கோ சுயவிவரங்களுக்கோ தேவைப்படுகிறது. மற்றவர்களுக்கு இது நிறுவல் நீக்கப்பட்டது"</string>
-    <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"உங்கள் சுயவிவரத்திற்கு இந்த ஆப்ஸ் தேவைப்படுவதால் இதை நிறுவல் நீக்க இயலாது."</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"இந்த ஆப்ஸ் சில பயனர்களுக்கோ கணக்குகளுக்கோ தேவைப்படுகிறது. மற்றவர்களுக்கு இது நிறுவல் நீக்கப்பட்டது"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"உங்கள் கணக்கிற்கு இந்த ஆப்ஸ் தேவைப்படுவதால் இதை நிறுவல் நீக்க இயலாது."</string>
     <string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"உங்கள் சாதன நிர்வாகிக்கு இந்த ஆப்ஸ் தேவைப்படுவதால் இதை நிறுவல் நீக்க இயலாது."</string>
     <string name="manage_device_administrators" msgid="3092696419363842816">"’சாதன நிர்வாகி ஆப்ஸை’ நிர்வகி"</string>
     <string name="manage_users" msgid="1243995386982560813">"\'பயனர்களை\' நிர்வகி"</string>
     <string name="uninstall_failed_msg" msgid="2176744834786696012">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸை நிறுவல் நீக்க இயலவில்லை."</string>
     <string name="Parse_error_dlg_text" msgid="1661404001063076789">"தொகுப்பைப் பாகுபடுத்திப் பார்ப்பதில் சிக்கல் ஏற்பட்டது."</string>
     <string name="wear_not_allowed_dlg_title" msgid="8664785993465117517">"Android Wear"</string>
-    <string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"நிறுவல்கள்/நிறுவல் நீக்கங்கள் Wearரில் செய்ய இயலாது"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"Wearரில் நிறுவல்கள்/நிறுவல் நீக்கங்கள் செய்ய இயலாது"</string>
     <string name="message_staging" msgid="8032722385658438567">"ஆப்ஸ் தயாராகிறது…"</string>
     <string name="app_name_unknown" msgid="6881210203354323926">"அறியப்படாதது"</string>
-    <string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"உங்கள் சாதனத்தின் பாதுகாப்பிற்காக, இந்த மூலத்திலிருந்து பெற்ற அறியப்படாத ஆப்ஸை டேப்லெட்டில் நிறுவ அனுமதியில்லை."</string>
-    <string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"உங்கள் சாதனத்தின் பாதுகாப்பிற்காக, இந்த மூலத்திலிருந்து பெற்ற அறியப்படாத ஆப்ஸை டிவியில் நிறுவ அனுமதியில்லை."</string>
-    <string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"உங்கள் சாதனத்தின் பாதுகாப்பிற்காக, இந்த மூலத்திலிருந்து பெற்ற அறியப்படாத ஆப்ஸை மொபைலில் நிறுவ அனுமதியில்லை."</string>
-    <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"அறியப்படாத ஆப்ஸால் உங்கள் மொபைலும் தனிப்பட்ட தரவும் மிக எளிதாகப் பாதிப்புக்குள்ளாகும். இந்த ஆப்ஸை நிறுவுவதன் மூலம், இதைப் பயன்படுத்தும்போது மொபைலில் ஏதேனும் சிக்கல் ஏற்பட்டாலோ உங்களது தரவை இழந்தாலோ அதற்கு நீங்களே பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
-    <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"அறியப்படாத ஆப்ஸால் உங்கள் டேப்லெட்டும் தனிப்பட்ட தரவும் மிக எளிதாகப் பாதிப்புக்குள்ளாகும். இந்த ஆப்ஸை நிறுவுவதன் மூலம், இதைப் பயன்படுத்தும்போது டேப்லெட்டில் ஏதேனும் சிக்கல் ஏற்பட்டாலோ உங்களது தரவை இழந்தாலோ அதற்கு நீங்களே பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
-    <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"அறியப்படாத ஆப்ஸால் உங்கள் டிவியும் தனிப்பட்ட தரவும் மிக எளிதாகப் பாதிப்புக்குள்ளாகும். இந்த ஆப்ஸை நிறுவுவதன் மூலம், இதைப் பயன்படுத்தும்போது டிவியில் ஏதேனும் சிக்கல் ஏற்பட்டாலோ உங்களது தரவை இழந்தாலோ அதற்கு நீங்களே பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"உங்கள் பாதுகாப்பிற்காக, இந்த மூலத்திலிருந்து பெற்ற அறியப்படாத ஆப்ஸை டேப்லெட்டில் நிறுவ அனுமதியில்லை."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"உங்கள் பாதுகாப்பிற்காக, இந்த மூலத்திலிருந்து பெற்ற அறியப்படாத ஆப்ஸை டிவியில் நிறுவ அனுமதியில்லை."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"உங்கள் பாதுகாப்பிற்காக, இந்த மூலத்திலிருந்து பெற்ற அறியப்படாத ஆப்ஸை மொபைலில் நிறுவ அனுமதியில்லை."</string>
+    <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"அறியப்படாத ஆப்ஸால் உங்கள் மொபைலும் தனிப்பட்ட தரவும் மிக எளிதாகப் பாதிப்புக்குள்ளாகலாம். இந்த ஆப்ஸை நிறுவுவதன் மூலம், இதைப் பயன்படுத்தும்போது மொபைலில் ஏதேனும் சிக்கல் ஏற்பட்டாலோ உங்களது தரவை இழந்தாலோ அதற்கு நீங்களே பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"அறியப்படாத ஆப்ஸால் உங்கள் டேப்லெட்டும் தனிப்பட்ட தரவும் மிக எளிதாகப் பாதிப்புக்குள்ளாகலாம். இந்த ஆப்ஸை நிறுவுவதன் மூலம், இதைப் பயன்படுத்தும்போது டேப்லெட்டில் ஏதேனும் சிக்கல் ஏற்பட்டாலோ உங்களது தரவை இழந்தாலோ அதற்கு நீங்களே பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"அறியப்படாத ஆப்ஸால் உங்கள் டிவியும் தனிப்பட்ட தரவும் மிக எளிதாகப் பாதிப்புக்குள்ளாகலாம். இந்த ஆப்ஸை நிறுவுவதன் மூலம், இதைப் பயன்படுத்தும்போது டிவியில் ஏதேனும் சிக்கல் ஏற்பட்டாலோ உங்களது தரவை இழந்தாலோ அதற்கு நீங்களே பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"தொடர்க"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"அமைப்புகள்"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear ஆப்ஸை நிறுவுதல்/நிறுவல் நீக்குதல்"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"ஆப்ஸ் நிறுவப்பட்டது குறித்த அறிவிப்பு"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"வெற்றிகரமாக நிறுவப்பட்டது"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" வெற்றிகரமாக நிறுவப்பட்டது"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index fd0a63f..64c5da7 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"కొనసాగించు"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"సెట్టింగ్‌లు"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear యాప్‌లను ఇన్‌స్టాల్/అన్‌ఇన్‌స్టాల్ చేస్తోంది"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"యాప్ ఇన్‌స్టాల్ చేయబడిందనే నోటిఫికేషన్"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"విజయవంతంగా ఇన్‌స్టాల్ చేయబడింది"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” విజయవంతంగా ఇన్‌స్టాల్ చేయబడింది"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-th/strings.xml b/packages/PackageInstaller/res/values-th/strings.xml
index a1f537f..b6595d0 100644
--- a/packages/PackageInstaller/res/values-th/strings.xml
+++ b/packages/PackageInstaller/res/values-th/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"ดำเนินการต่อ"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"การตั้งค่า"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"กำลังติดตั้ง/ถอนการติดตั้งแอป Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"การแจ้งเตือนว่าติดตั้งแอปแล้ว"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"ติดตั้งเรียบร้อยแล้ว"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"ติดตั้ง “<xliff:g id="APPNAME">%1$s</xliff:g>” สำเร็จแล้ว"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-tl/strings.xml b/packages/PackageInstaller/res/values-tl/strings.xml
index eace11e..e7b15fa 100644
--- a/packages/PackageInstaller/res/values-tl/strings.xml
+++ b/packages/PackageInstaller/res/values-tl/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Magpatuloy"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Mga Setting"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Ini-install/ina-uninstall ang wear app"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notification na na-install ang app"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Matagumpay na na-install"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Matagumpay na na-install ang “<xliff:g id="APPNAME">%1$s</xliff:g>”"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml
index 99575e0..33dbc3a9 100644
--- a/packages/PackageInstaller/res/values-tr/strings.xml
+++ b/packages/PackageInstaller/res/values-tr/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Devam"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ayarlar"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear uygulamalarını yükleme/kaldırma"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Uygulama yükleme bildirimi"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Başarıyla yüklendi"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" başarıyla yüklendi"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-uk/strings.xml b/packages/PackageInstaller/res/values-uk/strings.xml
index b3bebf1..d9cb954 100644
--- a/packages/PackageInstaller/res/values-uk/strings.xml
+++ b/packages/PackageInstaller/res/values-uk/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Продовжити"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Налаштування"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Встановлення або видалення додатків Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Сповіщення: додаток установлено"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Установлено"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Додаток <xliff:g id="APPNAME">%1$s</xliff:g> установлено"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-ur/strings.xml b/packages/PackageInstaller/res/values-ur/strings.xml
new file mode 100644
index 0000000..61b1579
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ur/strings.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="7488448184431507488">"پیکیج انسٹالر"</string>
+    <string name="install" msgid="711829760615509273">"انسٹال کریں"</string>
+    <string name="done" msgid="6632441120016885253">"ہو گیا"</string>
+    <string name="cancel" msgid="1018267193425558088">"منسوخ کریں"</string>
+    <string name="installing" msgid="4921993079741206516">"انسٹال ہو رہی ہے…"</string>
+    <string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> کو انسٹال کیا جا رہا ہے…"</string>
+    <string name="install_done" msgid="5987363587661783896">"ایپ انسٹال ہو گئی۔"</string>
+    <string name="install_confirm_question" msgid="8176284075816604590">"کیا آپ یہ ایپلیکیشن انسٹال کرنا چاہتے ہیں؟"</string>
+    <string name="install_confirm_question_update" msgid="7942235418781274635">"کیا آپ اس موجودہ ایپلیکیشن میں ایک اپ ڈیٹ انسٹال کرنا چاہتے ہیں؟ آپ کا موجودہ ڈیٹا ضائع نہیں ہوگا۔"</string>
+    <string name="install_confirm_question_update_system" msgid="4713001702777910263">"کیا آپ پہلے سے شامل اس ایپلیکیشن میں ایک اپ ڈیٹ انسٹال کرنا چاہتے ہیں؟ آپ کا موجودہ ڈیٹا ضائع نہیں ہوگا۔"</string>
+    <string name="install_failed" msgid="5777824004474125469">"ایپ انسٹال نہیں ہوئی۔"</string>
+    <string name="install_failed_blocked" msgid="8512284352994752094">"پیکج کو انسٹال ہونے سے مسدود کر دیا گیا تھا۔"</string>
+    <string name="install_failed_conflict" msgid="3493184212162521426">"ایپ انسٹال نہیں ہوئی کیونکہ پیکج ایک موجودہ پیکیج سے متصادم ہے۔"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"ایپ انسٹال نہیں ہوئی کیونکہ ایپ آپ کے ٹیبلیٹ کے ساتھ مطابقت پذیر نہیں ہے۔"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"‏یہ ایپ آپ کے TV کے ساتھ مطابقت پذیر نہیں ہے۔"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"ایپ انسٹال نہیں ہوئی کیونکہ ایپ آپ کے فون کے ساتھ مطابقت پذیر نہیں ہے۔"</string>
+    <string name="install_failed_invalid_apk" msgid="8581007676422623930">"ایپ انسٹال نہیں ہوئی کیونکہ پیکیج غلط معلوم ہوتا ہے۔"</string>
+    <string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو آپ کے ٹیبلیٹ پر انسٹال نہیں کیا جا سکا۔"</string>
+    <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"‏<xliff:g id="APP_NAME">%1$s</xliff:g> کو آپ کے TV پر انسٹال نہیں کیا جا سکا۔"</string>
+    <string name="install_failed_msg" product="default" msgid="6484461562647915707">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو آپ کے فون پر انسٹال نہیں کیا جا سکا۔"</string>
+    <string name="launch" msgid="3952550563999890101">"کھولیں"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"آپ کا منتظم نامعلوم ذرائع سے حاصل شدہ ایپس کو انسٹال کرنے کی اجازت نہیں دیتا ہے"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"یہ صارف نامعلوم ایپس کو انسٹال نہیں کر سکتا"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"اس صارف کو ایپس انسٹال کرنے کی اجازت نہیں ہے"</string>
+    <string name="ok" msgid="7871959885003339302">"ٹھیک ہے"</string>
+    <string name="manage_applications" msgid="5400164782453975580">"ایپس منظم کریں"</string>
+    <string name="out_of_space_dlg_title" msgid="4156690013884649502">"جگہ نہیں ہے"</string>
+    <string name="out_of_space_dlg_text" msgid="8727714096031856231">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو انسٹال نہیں کیا جا سکا۔ کچھ جگہ خالی کریں اور دوبارہ کوشش کریں۔"</string>
+    <string name="app_not_found_dlg_title" msgid="5107924008597470285">"ایپ نہیں ملی"</string>
+    <string name="app_not_found_dlg_text" msgid="5219983779377811611">"ایپ انسٹال کردہ ایپس کی فہرست میں نہیں ملی۔"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"اجازت نہیں ہے"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"موجودہ صارف کو اس اَن انسٹالیشن کو انجام دینے کی اجازت نہیں ہے۔"</string>
+    <string name="generic_error_dlg_title" msgid="5863195085927067752">"خرابی"</string>
+    <string name="generic_error_dlg_text" msgid="5287861443265795232">"ایپ اَن انسٹال نہیں ہو سکی۔"</string>
+    <string name="uninstall_application_title" msgid="4045420072401428123">"ایپ کو اَن انسٹال کریں"</string>
+    <string name="uninstall_update_title" msgid="824411791011583031">"اپ ڈیٹ اَن انسٹال کریں"</string>
+    <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> درج ذیل ایپ کا حصہ ہے:"</string>
+    <string name="uninstall_application_text" msgid="3816830743706143980">"کیا آپ اس ایپ کو اَن انسٹال کرنا چاہتے ہیں؟"</string>
+    <string name="uninstall_application_text_all_users" msgid="575491774380227119">"کیا آپ "<b>"سبھی"</b>" صارفین کیلئے اس ایپ کو اَن انسٹال کرنا چاہتے ہیں؟ ایپلیکیشن اور اس کا ڈیٹا آلہ پر موجود "<b>"سبھی"</b>" صارفین سے ہٹا دیا جائے گا۔"</string>
+    <string name="uninstall_application_text_user" msgid="498072714173920526">"کیا آپ اس ایپ کو صارف <xliff:g id="USERNAME">%1$s</xliff:g> کیلئے اَن انسٹال کرنا چاہتے ہیں؟"</string>
+    <string name="uninstall_update_text" msgid="863648314632448705">"اس ایپ کو فیکٹری ورژن سے تبدیل کریں؟ تمام ڈیٹا ہٹا دیا جائے گا۔"</string>
+    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"اس ایپ کو فیکٹری ورژن سے تبدیل کریں؟ تمام ڈیٹا ہٹا دیا جائے گا۔ اس سے دفتری پروفائلز کے حاملین سمیت اس آلہ کے تمام صارفین متاثر ہوں گے۔"</string>
+    <string name="uninstalling_notification_channel" msgid="840153394325714653">"چل رہے اَن انسٹالز"</string>
+    <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ناکام اَن انسٹالز"</string>
+    <string name="uninstalling" msgid="8709566347688966845">"اَن انسٹال ہو رہا ہے…"</string>
+    <string name="uninstalling_app" msgid="8866082646836981397">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ان انسٹال ہو رہا ہے…"</string>
+    <string name="uninstall_done" msgid="439354138387969269">"اَن انسٹال مکمل ہو گیا۔"</string>
+    <string name="uninstall_done_app" msgid="4588850984473605768">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> اَن انسٹال ہو گیا"</string>
+    <string name="uninstall_failed" msgid="1847750968168364332">"اَن انسٹال ناکام ہو گیا۔"</string>
+    <string name="uninstall_failed_app" msgid="5506028705017601412">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> کو ان انسٹال کرنا ناکام ہو گیا۔"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"فعال آلہ کے منتظم کی ایپ کو اَن انسٹال نہیں کیا جا سکتا"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"<xliff:g id="USERNAME">%1$s</xliff:g> کی فعال آلہ کے منتظم کی ایپ کو اَن انسٹال نہیں کیا جا سکتا"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"یہ ایپ کچھ صارفین یا پروفائلز کیلئے درکار ہے اور دیگر کیلئے اَن انسٹال کر دی گئی"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"یہ ایپ آپ کے پروفائل کیلئے درکار ہے اور اسے اَن انسٹال نہیں کیا جا سکتا۔"</string>
+    <string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"آپ کے آلہ کے منتظم کو اس ایپ کی ضرورت ہے اور اسے اَن انسٹال نہیں کیا جا سکتا۔"</string>
+    <string name="manage_device_administrators" msgid="3092696419363842816">"آلہ کے منتظم کی ایپس کا نظم کریں"</string>
+    <string name="manage_users" msgid="1243995386982560813">"صارفین کا نظم کریں"</string>
+    <string name="uninstall_failed_msg" msgid="2176744834786696012">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو اَن انسٹال نہیں کیا جا سکا۔"</string>
+    <string name="Parse_error_dlg_text" msgid="1661404001063076789">"پیکیج کو پارس کرنے میں ایک مسئلہ تھا۔"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8664785993465117517">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="704615521550939237">"‏\'انسٹال/اَن انسٹال کی کارروائیاں\' Wear پر تعاون یافتہ نہیں ہیں۔"</string>
+    <string name="message_staging" msgid="8032722385658438567">"ایپ کی ٹیسٹنگ ہو رہی ہے…"</string>
+    <string name="app_name_unknown" msgid="6881210203354323926">"نامعلوم"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"آپ کی سیکیورٹی کے مدنظر، آپ کے ٹیبلیٹ کو اس ذریعے سے نامعلوم ایپس انسٹال کرنے کی اجازت نہیں ہے۔"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"‏آپ کی سیکیورٹی کے مدنظر، آپ کے TV کو اس ذریعے سے نامعلوم ایپس انسٹال کرنے کی اجازت نہیں ہے۔"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"آپ کی سیکیورٹی کے مدنظر، آپ کے فون کو اس ذریعے سے نامعلوم ایپس انسٹال کرنے کی اجازت نہیں ہے۔"</string>
+    <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"آپ کے فون اور ذاتی ڈیٹا کو نامعلوم ایپس کی جانب سے حملے کا زیادہ خطرہ ہے۔ اس ایپ کو انسٹال کر کے، آپ اس بات سے اتفاق کرتے ہیں کہ آپ اس سے اپنے فون کو ہونے والے کسی بھی نقصان یا ڈیٹا کے نقصان کے لئے خود ذمہ دار ہیں۔"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"آپ کے ٹیبلیٹ اور ذاتی ڈیٹا کو نامعلوم ایپس کی جانب سے حملے کا زیادہ خطرہ ہے۔ اس ایپ کو انسٹال کر کے، آپ اس بات سے اتفاق کرتے ہیں کہ آپ اس سے اپنے ٹیبلیٹ کو ہونے والے کسی بھی نقصان یا ڈیٹا کے نقصان کے لئے خود ذمہ دار ہیں۔"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"‏آپ کے TV اور ذاتی ڈیٹا کو نامعلوم ایپس کی جانب سے حملے کا زیادہ خطرہ ہے۔ اس ایپ کو انسٹال کر کے، آپ اس بات سے اتفاق کرتے ہیں کہ آپ اس سے اپنے TV کو ہونے والے کسی بھی نقصان یا ڈیٹا کے نقصان کے لئے خود ذمہ دار ہیں۔"</string>
+    <string name="anonymous_source_continue" msgid="4375745439457209366">"جاری رکھیں"</string>
+    <string name="external_sources_settings" msgid="4046964413071713807">"ترتیبات"</string>
+    <string name="wear_app_channel" msgid="1960809674709107850">"‏wear ایپس کو انسٹال/اَن انسٹال کرنا"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"ایپ انسٹال ہونے کی اطلاع"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"کامیابی کے ساتھ انسٹال ہو گئی"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"\"<xliff:g id="APPNAME">%1$s</xliff:g>\" کامیابی کے ساتھ انسٹال ہو گئی"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-uz/strings.xml b/packages/PackageInstaller/res/values-uz/strings.xml
index e692e0d..dedf00c 100644
--- a/packages/PackageInstaller/res/values-uz/strings.xml
+++ b/packages/PackageInstaller/res/values-uz/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Davom etish"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Sozlamalar"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear ilovalarini o‘rnatish/o‘chirish"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Ilova oʻrnatilgani haqida bildirishnoma"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"O‘rnatildi"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” o‘rnatildi"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-vi/strings.xml b/packages/PackageInstaller/res/values-vi/strings.xml
index 91aa71c..7043093 100644
--- a/packages/PackageInstaller/res/values-vi/strings.xml
+++ b/packages/PackageInstaller/res/values-vi/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Tiếp tục"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Cài đặt"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Cài đặt/gỡ cài đặt ứng dụng Wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Thông báo đã cài đặt ứng dụng"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Đã cài đặt thành công"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Đã cài đặt thành công “<xliff:g id="APPNAME">%1$s</xliff:g>”"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-zh-rCN/strings.xml b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
index d3b5e68..a91f00f 100644
--- a/packages/PackageInstaller/res/values-zh-rCN/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"继续"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"设置"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"正在安装/卸载 Wear 应用"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"“应用已安装”通知"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"已成功安装"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>”安装成功"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-zh-rHK/strings.xml b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
index bb3605f..286e360 100644
--- a/packages/PackageInstaller/res/values-zh-rHK/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"繼續"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"設定"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"正在安裝/解除安裝 Wear 應用程式"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"「應用程式已安裝」通知"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"已成功安裝"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"「<xliff:g id="APPNAME">%1$s</xliff:g>」已成功安裝"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-zh-rTW/strings.xml b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
index 513b16a..b0de2c25 100644
--- a/packages/PackageInstaller/res/values-zh-rTW/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"繼續"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"設定"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"安裝/解除安裝中的 Wear 應用程式"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"「應用程式已安裝」通知"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"安裝成功"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"已成功安裝「<xliff:g id="APPNAME">%1$s</xliff:g>」"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-zu/strings.xml b/packages/PackageInstaller/res/values-zu/strings.xml
index bc91ed1..5d28479 100644
--- a/packages/PackageInstaller/res/values-zu/strings.xml
+++ b/packages/PackageInstaller/res/values-zu/strings.xml
@@ -88,4 +88,7 @@
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Qhubeka"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Izilungiselelo"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Ifaka/ikhipha izinhlelo zokusebenza ze-wear"</string>
+    <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Isaziso sokufakwa kohlelo lokusebenza"</string>
+    <string name="notification_installation_success_message" msgid="6450467996056038442">"Ifakwe ngempumelelo"</string>
+    <string name="notification_installation_success_status" msgid="3172502643504323321">"Kufakwe ngempumelelo i-\"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
 </resources>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java
index 1eb423e..74c7b58 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.provider.Settings;
 import android.util.Log;
 
 /**
@@ -29,11 +30,11 @@
     private static final String TAG = PackageInstalledReceiver.class.getSimpleName();
 
     private static final boolean DEBUG = false;
-    private static final boolean APP_INSTALLED_NOTIFICATION_ENABLED = false;
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (!APP_INSTALLED_NOTIFICATION_ENABLED) {
+        if (Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED, 0) == 0) {
             return;
         }
 
diff --git a/packages/PrintSpooler/res/values-mr/strings.xml b/packages/PrintSpooler/res/values-mr/strings.xml
index 5593f66..b055d82 100644
--- a/packages/PrintSpooler/res/values-mr/strings.xml
+++ b/packages/PrintSpooler/res/values-mr/strings.xml
@@ -88,7 +88,7 @@
     <string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटरवर कोणतेही कनेक्‍शन नाही"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"अज्ञात"</string>
     <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> वापरायची?"</string>
-    <string name="print_service_security_warning_summary" msgid="1427434625361692006">"आपला दस्तऐवज प्रिंटरपर्यंत पोहचण्‍यापूर्वी एक किंवा अधिक सर्व्हरद्वारे जाऊ शकतो."</string>
+    <string name="print_service_security_warning_summary" msgid="1427434625361692006">"तुमचा दस्तऐवज प्रिंटरपर्यंत पोहचण्‍यापूर्वी एक किंवा अधिक सर्व्हरद्वारे जाऊ शकतो."</string>
   <string-array name="color_mode_labels">
     <item msgid="7602948745415174937">"कृष्‍ण धवल"</item>
     <item msgid="2762241247228983754">"रंग"</item>
diff --git a/packages/SettingsLib/HelpUtils/res/values-hi/strings.xml b/packages/SettingsLib/HelpUtils/res/values-hi/strings.xml
new file mode 100644
index 0000000..cfc6961
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2018 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="help_feedback_label" msgid="4550436169116444686">"सहायता और सुझाव"</string>
+</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-or/strings.xml b/packages/SettingsLib/SearchWidget/res/values-or/strings.xml
new file mode 100644
index 0000000..3523037
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2018 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="search_menu" msgid="1604061903696928905">"ସର୍ଚ୍ଚ ସେଟିଙ୍ଗ"</string>
+</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-ur/strings.xml b/packages/SettingsLib/SearchWidget/res/values-ur/strings.xml
new file mode 100644
index 0000000..8b62fd4
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2018 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="search_menu" msgid="1604061903696928905">"ترتیبات تلاش کریں"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 5c11db1..83c4b8e 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -240,7 +240,7 @@
     <string name="wifi_unmetered_label" msgid="6124098729457992931">"بدون قياس"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"أحجام ذاكرة التخزين المؤقت للتسجيل"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"حدد أحجامًا أكبر لكل ذاكرة تخزين مؤقت للتسجيل"</string>
-    <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"هل تريد محو سعة التخزين الدائمة للمسجِّل؟"</string>
+    <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"هل تريد محو مساحة التخزين الدائمة للمسجِّل؟"</string>
     <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"عندما نتوقف عن رصد أي أخطاء باستخدام المسجِّل الدائم مرة أخرى، يتعين علينا محو بيانات المسجِّل الموجودة على جهازك."</string>
     <string name="select_logpersist_title" msgid="7530031344550073166">"تخزين بيانات المسجِّل باستمرار على الجهاز"</string>
     <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"تحديد مخازن السجلات المؤقتة المراد تخزينها باستمرار على الجهاز"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 72dd8cf..5638947 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -446,6 +446,6 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"Data: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Durada"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pregunta sempre"</string>
-    <string name="zen_mode_forever" msgid="2704305038191592967">"Fins que no ho desactivi"</string>
+    <string name="zen_mode_forever" msgid="2704305038191592967">"Fins que no ho desactivis"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ara mateix"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 5a11e72..5da253c 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -317,7 +317,7 @@
     <string name="show_all_anrs" msgid="4924885492787069007">"‏הצגת מקרי ANR ברקע"</string>
     <string name="show_all_anrs_summary" msgid="6636514318275139826">"הצגת תיבת דו-שיח של \'אפליקציה לא מגיבה\' עבור אפליקציות שפועלות ברקע"</string>
     <string name="show_notification_channel_warnings" msgid="1399948193466922683">"אזהרות לגבי ערוץ הודעות"</string>
-    <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"הצגת אזהרה כשאפליקציה שולחת הודעה ללא ערוץ חוקי"</string>
+    <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"הצגת אזהרה כשאפליקציה שולחת התראה ללא ערוץ חוקי"</string>
     <string name="force_allow_on_external" msgid="3215759785081916381">"אילוץ הרשאת אפליקציות באחסון חיצוני"</string>
     <string name="force_allow_on_external_summary" msgid="3640752408258034689">"מאפשר כתיבה של כל אפליקציה באחסון חיצוני, ללא התחשבות בערכי המניפסט"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"אלץ יכולת קביעת גודל של הפעילויות"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index df122e2..d693077 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -415,7 +415,7 @@
     <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Kiwango maalum (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menyu"</string>
     <string name="retail_demo_reset_message" msgid="118771671364131297">"Weka nenosiri ili urejeshe mipangilio ya kiwandani ikiwa katika hali ya onyesho."</string>
-    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Inayofuata"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Endelea"</string>
     <string name="retail_demo_reset_title" msgid="696589204029930100">"Nenosiri linahitajika"</string>
     <string name="active_input_method_subtypes" msgid="3596398805424733238">"Mbinu zinazotumika"</string>
     <string name="use_system_language_to_select_input_method_subtypes" msgid="5747329075020379587">"Tumia lugha za mfumo"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 508adbd..e2a34f4 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -21,35 +21,47 @@
     <!-- Toast message when Wi-Fi cannot scan for networks -->
     <string name="wifi_fail_to_scan">Can\'t scan for networks</string>
     <!-- Do not translate.  Concise terminology for wifi with WEP security -->
-    <string name="wifi_security_short_wep">WEP</string>
+    <string name="wifi_security_short_wep" translatable="false">WEP</string>
     <!-- Do not translate.  Concise terminology for wifi with WPA security -->
-    <string name="wifi_security_short_wpa">WPA</string>
+    <string name="wifi_security_short_wpa" translatable="false">WPA</string>
     <!-- Do not translate.  Concise terminology for wifi with WPA2 security -->
-    <string name="wifi_security_short_wpa2">WPA2</string>
+    <string name="wifi_security_short_wpa2" translatable="false">WPA2</string>
     <!-- Do not translate.  Concise terminology for wifi with both WPA/WPA2 security -->
-    <string name="wifi_security_short_wpa_wpa2">WPA/WPA2</string>
+    <string name="wifi_security_short_wpa_wpa2" translatable="false">WPA/WPA2</string>
     <!-- Do not translate.  Concise terminology for wifi with unknown PSK type -->
-    <string name="wifi_security_short_psk_generic">@string/wifi_security_short_wpa_wpa2</string>
+    <string name="wifi_security_short_psk_generic" translatable="false">@string/wifi_security_short_wpa_wpa2</string>
     <!-- Do not translate.  Concise terminology for wifi with 802.1x EAP security -->
-    <string name="wifi_security_short_eap">802.1x</string>
+    <string name="wifi_security_short_eap" translatable="false">802.1x</string>
+    <!-- Do not translate.  Concise terminology for wifi with WPA3 security -->
+    <string name="wifi_security_short_sae" translatable="false">WPA3</string>
+    <!-- Do not translate.  Concise terminology for wifi with OWE security -->
+    <string name="wifi_security_short_owe" translatable="false">OWE</string>
+    <!-- Do not translate.  Concise terminology for wifi with 802.1x EAP Suite-B security -->
+    <string name="wifi_security_short_eap_suiteb" translatable="false">Suite-B</string>
 
     <!-- Used in Wi-Fi settings dialogs when Wi-Fi does not have any security. -->
-    <string name="wifi_security_none">None</string>
+    <string name="wifi_security_none" translatable="false">None</string>
 
     <!-- Do not translate.  Terminology for wifi with WEP security -->
-    <string name="wifi_security_wep">WEP</string>
+    <string name="wifi_security_wep" translatable="false">WEP</string>
     <!-- Do not translate.  Terminology for wifi with WPA security -->
-    <string name="wifi_security_wpa">WPA PSK</string>
+    <string name="wifi_security_wpa" translatable="false">WPA-Personal</string>
     <!-- Do not translate.  Terminology for wifi with WPA2 security -->
-    <string name="wifi_security_wpa2">WPA2 PSK</string>
+    <string name="wifi_security_wpa2" translatable="false">WPA2-Personal</string>
     <!-- Do not translate.  Terminology for wifi with both WPA/WPA2 security, or unknown -->
-    <string name="wifi_security_wpa_wpa2">WPA/WPA2 PSK</string>
+    <string name="wifi_security_wpa_wpa2" translatable="false">WPA/WPA2-Personal</string>
     <!-- Do not translate.  Terminology for wifi with unknown PSK type -->
-    <string name="wifi_security_psk_generic">@string/wifi_security_wpa_wpa2</string>
+    <string name="wifi_security_psk_generic" translatable="false">@string/wifi_security_wpa_wpa2</string>
     <!-- Do not translate.  Concise terminology for wifi with 802.1x EAP security -->
-    <string name="wifi_security_eap">802.1x EAP</string>
+    <string name="wifi_security_eap" translatable="false">WPA/WPA2-Enterprise</string>
     <!-- Do not translate.  Concise terminology for Passpoint network -->
-    <string name="wifi_security_passpoint">Passpoint</string>
+    <string name="wifi_security_passpoint" translatable="false">Passpoint</string>
+    <!-- Do not translate.  Terminology for wifi with WPA3 security -->
+    <string name="wifi_security_sae" translatable="false">WPA3-Personal</string>
+    <!-- Do not translate.  Terminology for wifi with OWE security -->
+    <string name="wifi_security_owe" translatable="false">Enhanced Open</string>
+    <!-- Do not translate.  Concise terminology for wifi with 802.1x EAP Suite-B security -->
+    <string name="wifi_security_eap_suiteb" translatable="false">WPA3-Enterprise</string>
 
     <!-- Summary for the remembered network. -->
     <string name="wifi_remembered">Saved</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 240a192..a936df2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -254,7 +254,8 @@
                                 user.isAdmin() ? mAdminRetrieveFlags : mRetrieveFlags,
                                 user.id);
                 mApplications.addAll(list.getList());
-            } catch (RemoteException e) {
+            } catch (Exception e) {
+                Log.e(TAG, "Error during doResumeIfNeededLocked", e);
             }
         }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index a842229..3152e65 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -127,4 +127,17 @@
     default void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
             int state, int bluetoothProfile) {
     }
+
+    /**
+     * Called when ACL connection state is changed. It listens to
+     * {@link android.bluetooth.BluetoothDevice#ACTION_ACL_CONNECTED} and {@link
+     * android.bluetooth.BluetoothDevice#ACTION_ACL_DISCONNECTED}
+     *
+     * @param cachedDevice Bluetooth device that changed
+     * @param state        the Bluetooth device connection state, the possible values are:
+     *                     {@link android.bluetooth.BluetoothAdapter#STATE_DISCONNECTED},
+     *                     {@link android.bluetooth.BluetoothAdapter#STATE_CONNECTED}
+     */
+    default void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 7124096..2b7babd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -26,9 +26,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
 import com.android.settingslib.R;
 
 import java.util.ArrayList;
@@ -54,21 +58,32 @@
     private final BroadcastReceiver mProfileBroadcastReceiver = new BluetoothBroadcastReceiver();
     private final Collection<BluetoothCallback> mCallbacks = new ArrayList<>();
     private final android.os.Handler mReceiverHandler;
+    private final UserHandle mUserHandle;
     private final Context mContext;
 
     interface Handler {
         void onReceive(Context context, Intent intent, BluetoothDevice device);
     }
 
+    /**
+     * Creates BluetoothEventManager with the ability to pass in {@link UserHandle} that tells it to
+     * listen for bluetooth events for that particular userHandle.
+     *
+     * <p> If passing in userHandle that's different from the user running the process,
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission is required. If
+     * userHandle passed in is {@code null}, we register event receiver for the
+     * {@code context.getUser()} handle.
+     */
     BluetoothEventManager(LocalBluetoothAdapter adapter,
             CachedBluetoothDeviceManager deviceManager, Context context,
-            android.os.Handler handler) {
+            android.os.Handler handler, @Nullable UserHandle userHandle) {
         mLocalAdapter = adapter;
         mDeviceManager = deviceManager;
         mAdapterIntentFilter = new IntentFilter();
         mProfileIntentFilter = new IntentFilter();
         mHandlerMap = new HashMap<>();
         mContext = context;
+        mUserHandle = userHandle;
         mReceiverHandler = handler;
 
         // Bluetooth on/off broadcasts
@@ -104,7 +119,11 @@
         addHandler(TelephonyManager.ACTION_PHONE_STATE_CHANGED,
                 new AudioModeChangedHandler());
 
-        mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
+        // ACL connection changed broadcasts
+        addHandler(BluetoothDevice.ACTION_ACL_CONNECTED, new AclStateChangedHandler());
+        addHandler(BluetoothDevice.ACTION_ACL_DISCONNECTED, new AclStateChangedHandler());
+
+        registerAdapterIntentReceiver();
     }
 
     /** Register to start receiving callbacks for Bluetooth events. */
@@ -121,10 +140,31 @@
         }
     }
 
+    @VisibleForTesting
     void registerProfileIntentReceiver() {
-        mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
+        registerIntentReceiver(mProfileBroadcastReceiver, mProfileIntentFilter);
     }
 
+    @VisibleForTesting
+    void registerAdapterIntentReceiver() {
+        registerIntentReceiver(mBroadcastReceiver, mAdapterIntentFilter);
+    }
+
+    /**
+     * Registers the provided receiver to receive the broadcasts that correspond to the
+     * passed intent filter, in the context of the provided handler.
+     */
+    private void registerIntentReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        if (mUserHandle == null) {
+            // If userHandle has not been provided, simply call registerReceiver.
+            mContext.registerReceiver(receiver, filter, null, mReceiverHandler);
+        } else {
+            // userHandle was explicitly specified, so need to call multi-user aware API.
+            mContext.registerReceiverAsUser(receiver, mUserHandle, filter, null, mReceiverHandler);
+        }
+    }
+
+    @VisibleForTesting
     void addProfileHandler(String action, Handler handler) {
         mHandlerMap.put(action, handler);
         mProfileIntentFilter.addAction(action);
@@ -171,7 +211,6 @@
                 callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
             }
         }
-        mDeviceManager.onProfileConnectionStateChanged(device, state, bluetoothProfile);
     }
 
     private void dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
@@ -201,7 +240,17 @@
         }
     }
 
-    private void addHandler(String action, Handler handler) {
+    private void dispatchAclStateChanged(CachedBluetoothDevice activeDevice,
+            int state) {
+        synchronized (mCallbacks) {
+            for (BluetoothCallback callback : mCallbacks) {
+                callback.onAclConnectionStateChanged(activeDevice, state);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    void addHandler(String action, Handler handler) {
         mHandlerMap.put(action, handler);
         mAdapterIntentFilter.addAction(action);
     }
@@ -411,6 +460,32 @@
         }
     }
 
+    private class AclStateChangedHandler implements Handler {
+        @Override
+        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            final String action = intent.getAction();
+            if (action == null) {
+                Log.w(TAG, "AclStateChangedHandler: action is null");
+                return;
+            }
+            final CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
+            final int state;
+            switch (action) {
+                case BluetoothDevice.ACTION_ACL_CONNECTED:
+                    state = BluetoothAdapter.STATE_CONNECTED;
+                    break;
+                case BluetoothDevice.ACTION_ACL_DISCONNECTED:
+                    state = BluetoothAdapter.STATE_DISCONNECTED;
+                    break;
+                default:
+                    Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
+                    return;
+
+            }
+            dispatchAclStateChanged(activeDevice, state);
+        }
+    }
+
     private class AudioModeChangedHandler implements Handler {
 
         @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index a2e30df..b4e82e9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -29,6 +29,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.settingslib.R;
 
 import java.util.ArrayList;
@@ -36,8 +38,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import androidx.annotation.VisibleForTesting;
-
 /**
  * CachedBluetoothDevice represents a remote Bluetooth device. It contains
  * attributes of the device (such as the address, name, RSSI, etc.) and
@@ -54,11 +54,12 @@
     private final Context mContext;
     private final BluetoothAdapter mLocalAdapter;
     private final LocalBluetoothProfileManager mProfileManager;
-    private final BluetoothDevice mDevice;
+    BluetoothDevice mDevice;
     private long mHiSyncId;
     // Need this since there is no method for getting RSSI
-    private short mRssi;
-
+    short mRssi;
+    // mProfiles and mRemovedProfiles does not do swap() between main and sub device. It is
+    // because current sub device is only for HearingAid and its profile is the same.
     private final List<LocalBluetoothProfile> mProfiles =
             Collections.synchronizedList(new ArrayList<>());
 
@@ -69,7 +70,7 @@
     // Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
     private boolean mLocalNapRoleConnected;
 
-    private boolean mJustDiscovered;
+    boolean mJustDiscovered;
 
     private int mMessageRejectionCount;
 
@@ -92,6 +93,8 @@
     private boolean mIsActiveDeviceA2dp = false;
     private boolean mIsActiveDeviceHeadset = false;
     private boolean mIsActiveDeviceHearingAid = false;
+    // Group second device for Hearing Aid
+    private CachedBluetoothDevice mSubDevice;
 
     CachedBluetoothDevice(Context context, LocalBluetoothProfileManager profileManager,
             BluetoothDevice device) {
@@ -1064,4 +1067,28 @@
         return hearingAidProfile != null && hearingAidProfile.getConnectionStatus(mDevice) ==
                 BluetoothProfile.STATE_CONNECTED;
     }
+
+    public CachedBluetoothDevice getSubDevice() {
+        return mSubDevice;
+    }
+
+    public void setSubDevice(CachedBluetoothDevice subDevice) {
+        mSubDevice = subDevice;
+    }
+
+    public void switchSubDeviceContent() {
+        // Backup from main device
+        BluetoothDevice tmpDevice = mDevice;
+        short tmpRssi = mRssi;
+        boolean tmpJustDiscovered = mJustDiscovered;
+        // Set main device from sub device
+        mDevice = mSubDevice.mDevice;
+        mRssi = mSubDevice.mRssi;
+        mJustDiscovered = mSubDevice.mJustDiscovered;
+        // Set sub device from backup
+        mSubDevice.mDevice = tmpDevice;
+        mSubDevice.mRssi = tmpRssi;
+        mSubDevice.mJustDiscovered = tmpJustDiscovered;
+        fetchActiveDevices();
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 21cf0c2..f7f6589 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -18,8 +18,6 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.util.Log;
 
@@ -27,12 +25,8 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
-import java.util.Set;
 
 /**
  * CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
@@ -45,20 +39,14 @@
     private final LocalBluetoothManager mBtManager;
 
     @VisibleForTesting
-    final List<CachedBluetoothDevice> mCachedDevices =
-        new ArrayList<CachedBluetoothDevice>();
-    // Contains the list of hearing aid devices that should not be shown in the UI.
+    final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>();
     @VisibleForTesting
-    final List<CachedBluetoothDevice> mHearingAidDevicesNotAddedInCache
-        = new ArrayList<CachedBluetoothDevice>();
-    // Maintains a list of devices which are added in mCachedDevices and have hiSyncIds.
-    @VisibleForTesting
-    final Map<Long, CachedBluetoothDevice> mCachedDevicesMapForHearingAids
-        = new HashMap<Long, CachedBluetoothDevice>();
+    HearingAidDeviceManager mHearingAidDeviceManager;
 
     CachedBluetoothDeviceManager(Context context, LocalBluetoothManager localBtManager) {
         mContext = context;
         mBtManager = localBtManager;
+        mHearingAidDeviceManager = new HearingAidDeviceManager(localBtManager, mCachedDevices);
     }
 
     public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
@@ -92,12 +80,13 @@
             if (cachedDevice.getDevice().equals(device)) {
                 return cachedDevice;
             }
-        }
-        for (CachedBluetoothDevice notCachedDevice : mHearingAidDevicesNotAddedInCache) {
-            if (notCachedDevice.getDevice().equals(device)) {
-                return notCachedDevice;
+            // Check sub devices if it exists
+            CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+            if (subDevice != null && subDevice.getDevice().equals(device)) {
+                return subDevice;
             }
         }
+
         return null;
     }
 
@@ -111,24 +100,10 @@
         LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
         CachedBluetoothDevice newDevice = new CachedBluetoothDevice(mContext, profileManager,
                 device);
-        if (profileManager.getHearingAidProfile() != null
-            && profileManager.getHearingAidProfile().getHiSyncId(newDevice.getDevice())
-                != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
-            newDevice.setHiSyncId(profileManager.getHearingAidProfile()
-                .getHiSyncId(newDevice.getDevice()));
-        }
-        // Just add one of the hearing aids from a pair in the list that is shown in the UI.
-        if (isPairAddedInCache(newDevice.getHiSyncId())) {
-            synchronized (this) {
-                mHearingAidDevicesNotAddedInCache.add(newDevice);
-            }
-        } else {
-            synchronized (this) {
+        mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice);
+        synchronized (this) {
+            if (!mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) {
                 mCachedDevices.add(newDevice);
-                if (newDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
-                    && !mCachedDevicesMapForHearingAids.containsKey(newDevice.getHiSyncId())) {
-                    mCachedDevicesMapForHearingAids.put(newDevice.getHiSyncId(), newDevice);
-                }
                 mBtManager.getEventManager().dispatchDeviceAdded(newDevice);
             }
         }
@@ -137,49 +112,18 @@
     }
 
     /**
-     * Returns true if the one of the two hearing aid devices is already cached for UI.
-     *
-     * @param long hiSyncId
-     * @return {@code True} if one of the two hearing aid devices is is already cached for UI.
-     */
-    private synchronized boolean isPairAddedInCache(long hiSyncId) {
-        if (hiSyncId == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
-            return false;
-        }
-        if(mCachedDevicesMapForHearingAids.containsKey(hiSyncId)) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
      * Returns device summary of the pair of the hearing aid passed as the parameter.
      *
      * @param CachedBluetoothDevice device
-     * @return Device summary, or if the pair does not exist or if its not a hearing aid,
+     * @return Device summary, or if the pair does not exist or if it is not a hearing aid,
      * then {@code null}.
      */
-    public synchronized String getHearingAidPairDeviceSummary(CachedBluetoothDevice device) {
-        String pairDeviceSummary = null;
-        if (device.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
-            for (CachedBluetoothDevice hearingAidDevice : mHearingAidDevicesNotAddedInCache) {
-                if (hearingAidDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
-                    && hearingAidDevice.getHiSyncId() == device.getHiSyncId()) {
-                    pairDeviceSummary = hearingAidDevice.getConnectionSummary();
-                }
-            }
+    public synchronized String getSubDeviceSummary(CachedBluetoothDevice device) {
+        CachedBluetoothDevice subDevice = device.getSubDevice();
+        if (subDevice != null && subDevice.isConnected()) {
+            return subDevice.getConnectionSummary();
         }
-        return pairDeviceSummary;
-    }
-
-    /**
-     * Adds the 2nd hearing aid in a pair in a list that maintains the hearing aids that are
-     * not dispalyed in the UI.
-     *
-     * @param CachedBluetoothDevice device
-     */
-    public synchronized void addDeviceNotaddedInMap(CachedBluetoothDevice device) {
-        mHearingAidDevicesNotAddedInCache.add(device);
+        return null;
     }
 
     /**
@@ -187,28 +131,8 @@
      * Hearing Aid Service is connected and the HiSyncId's are now available.
      * @param LocalBluetoothProfileManager profileManager
      */
-    public synchronized void updateHearingAidsDevices(LocalBluetoothProfileManager profileManager) {
-        HearingAidProfile profileProxy = profileManager.getHearingAidProfile();
-        if (profileProxy == null) {
-            log("updateHearingAidsDevices: getHearingAidProfile() is null");
-            return;
-        }
-        final Set<Long> syncIdChangedSet = new HashSet<Long>();
-        for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
-            if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
-                continue;
-            }
-
-            long newHiSyncId = profileProxy.getHiSyncId(cachedDevice.getDevice());
-
-            if (newHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
-                cachedDevice.setHiSyncId(newHiSyncId);
-                syncIdChangedSet.add(newHiSyncId);
-            }
-        }
-        for (Long syncId : syncIdChangedSet) {
-            onHiSyncIdChanged(syncId);
-        }
+    public synchronized void updateHearingAidsDevices() {
+        mHearingAidDeviceManager.updateHearingAidsDevices();
     }
 
     /**
@@ -232,15 +156,21 @@
     }
 
     public synchronized void clearNonBondedDevices() {
-
-        mCachedDevicesMapForHearingAids.entrySet().removeIf(entries
-            -> entries.getValue().getBondState() == BluetoothDevice.BOND_NONE);
-
+        clearNonBondedSubDevices();
         mCachedDevices.removeIf(cachedDevice
             -> cachedDevice.getBondState() == BluetoothDevice.BOND_NONE);
+    }
 
-        mHearingAidDevicesNotAddedInCache.removeIf(hearingAidDevice
-            -> hearingAidDevice.getBondState() == BluetoothDevice.BOND_NONE);
+    private void clearNonBondedSubDevices() {
+        for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
+            CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+            CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+            if (subDevice != null
+                    && subDevice.getDevice().getBondState() == BluetoothDevice.BOND_NONE) {
+                // Sub device exists and it is not bonded
+                cachedDevice.setSubDevice(null);
+            }
+        }
     }
 
     public synchronized void onScanningStateChanged(boolean started) {
@@ -250,10 +180,10 @@
         for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
             CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
             cachedDevice.setJustDiscovered(false);
-        }
-        for (int i = mHearingAidDevicesNotAddedInCache.size() - 1; i >= 0; i--) {
-            CachedBluetoothDevice notCachedDevice = mHearingAidDevicesNotAddedInCache.get(i);
-            notCachedDevice.setJustDiscovered(false);
+            final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+            if (subDevice != null) {
+                subDevice.setJustDiscovered(false);
+            }
         }
     }
 
@@ -277,162 +207,45 @@
         if (bluetoothState == BluetoothAdapter.STATE_TURNING_OFF) {
             for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
                 CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+                CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+                if (subDevice != null) {
+                    if (subDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
+                        cachedDevice.setSubDevice(null);
+                    }
+                }
                 if (cachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
                     cachedDevice.setJustDiscovered(false);
                     mCachedDevices.remove(i);
-                    if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
-                        && mCachedDevicesMapForHearingAids.containsKey(cachedDevice.getHiSyncId()))
-                    {
-                        mCachedDevicesMapForHearingAids.remove(cachedDevice.getHiSyncId());
-                    }
-                }
-            }
-            for (int i = mHearingAidDevicesNotAddedInCache.size() - 1; i >= 0; i--) {
-                CachedBluetoothDevice notCachedDevice = mHearingAidDevicesNotAddedInCache.get(i);
-                if (notCachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
-                    notCachedDevice.setJustDiscovered(false);
-                    mHearingAidDevicesNotAddedInCache.remove(i);
                 }
             }
         }
     }
 
     public synchronized void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
-                                                   int bluetoothProfile) {
+            int bluetoothProfile) {
         for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
             boolean isActive = Objects.equals(cachedDevice, activeDevice);
             cachedDevice.onActiveDeviceChanged(isActive, bluetoothProfile);
         }
     }
 
-    public synchronized void onHiSyncIdChanged(long hiSyncId) {
-        int firstMatchedIndex = -1;
-
-        for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
-            CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
-            if (cachedDevice.getHiSyncId() == hiSyncId) {
-                if (firstMatchedIndex != -1) {
-                    /* Found the second one */
-                    int indexToRemoveFromUi;
-                    CachedBluetoothDevice deviceToRemoveFromUi;
-
-                    // Since the hiSyncIds have been updated for a connected pair of hearing aids,
-                    // we remove the entry of one the hearing aids from the UI. Unless the
-                    // hiSyncId get updated, the system does not know it is a hearing aid, so we add
-                    // both the hearing aids as separate entries in the UI first, then remove one
-                    // of them after the hiSyncId is populated. We will choose the device that
-                    // is not connected to be removed.
-                    if (cachedDevice.isConnected()) {
-                        indexToRemoveFromUi = firstMatchedIndex;
-                        deviceToRemoveFromUi = mCachedDevices.get(firstMatchedIndex);
-                        mCachedDevicesMapForHearingAids.put(hiSyncId, cachedDevice);
-                    } else {
-                        indexToRemoveFromUi = i;
-                        deviceToRemoveFromUi = cachedDevice;
-                        mCachedDevicesMapForHearingAids.put(hiSyncId,
-                                                            mCachedDevices.get(firstMatchedIndex));
-                    }
-
-                    mCachedDevices.remove(indexToRemoveFromUi);
-                    mHearingAidDevicesNotAddedInCache.add(deviceToRemoveFromUi);
-                    log("onHiSyncIdChanged: removed from UI device=" + deviceToRemoveFromUi
-                        + ", with hiSyncId=" + hiSyncId);
-                    mBtManager.getEventManager().dispatchDeviceRemoved(deviceToRemoveFromUi);
-                    break;
-                } else {
-                    mCachedDevicesMapForHearingAids.put(hiSyncId, cachedDevice);
-                    firstMatchedIndex = i;
-                }
-            }
-        }
-    }
-
-    private CachedBluetoothDevice getHearingAidOtherDevice(CachedBluetoothDevice thisDevice,
-                                                           long hiSyncId) {
-        if (hiSyncId == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
-            return null;
-        }
-
-        // Searched the lists for the other side device with the matching hiSyncId.
-        for (CachedBluetoothDevice notCachedDevice : mHearingAidDevicesNotAddedInCache) {
-            if ((hiSyncId == notCachedDevice.getHiSyncId()) &&
-                (!Objects.equals(notCachedDevice, thisDevice))) {
-                return notCachedDevice;
-            }
-        }
-
-        CachedBluetoothDevice cachedDevice = mCachedDevicesMapForHearingAids.get(hiSyncId);
-        if (!Objects.equals(cachedDevice, thisDevice)) {
-            return cachedDevice;
-        }
-        return null;
-    }
-
-    private void hearingAidSwitchDisplayDevice(CachedBluetoothDevice toDisplayDevice,
-                                           CachedBluetoothDevice toHideDevice, long hiSyncId)
-    {
-        log("hearingAidSwitchDisplayDevice: toDisplayDevice=" + toDisplayDevice
-            + ", toHideDevice=" + toHideDevice);
-
-        // Remove the "toHideDevice" device from the UI.
-        mHearingAidDevicesNotAddedInCache.add(toHideDevice);
-        mCachedDevices.remove(toHideDevice);
-        mBtManager.getEventManager().dispatchDeviceRemoved(toHideDevice);
-
-        // Add the "toDisplayDevice" device to the UI.
-        mHearingAidDevicesNotAddedInCache.remove(toDisplayDevice);
-        mCachedDevices.add(toDisplayDevice);
-        mCachedDevicesMapForHearingAids.put(hiSyncId, toDisplayDevice);
-        mBtManager.getEventManager().dispatchDeviceAdded(toDisplayDevice);
-    }
-
-    public synchronized void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
-                                                             int state, int bluetoothProfile) {
-        if (bluetoothProfile == BluetoothProfile.HEARING_AID
-            && cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
-            && cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
-
-            long hiSyncId = cachedDevice.getHiSyncId();
-
-            CachedBluetoothDevice otherDevice = getHearingAidOtherDevice(cachedDevice, hiSyncId);
-            if (otherDevice == null) {
-                // no other side device. Nothing to do.
-                return;
-            }
-
-            if (state == BluetoothProfile.STATE_CONNECTED &&
-                mHearingAidDevicesNotAddedInCache.contains(cachedDevice)) {
-                hearingAidSwitchDisplayDevice(cachedDevice, otherDevice, hiSyncId);
-            } else if (state == BluetoothProfile.STATE_DISCONNECTED
-                       && otherDevice.isConnected()) {
-                CachedBluetoothDevice mapDevice = mCachedDevicesMapForHearingAids.get(hiSyncId);
-                if ((mapDevice != null) && (Objects.equals(cachedDevice, mapDevice))) {
-                    hearingAidSwitchDisplayDevice(otherDevice, cachedDevice, hiSyncId);
-                }
-            }
-        }
+    public synchronized boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice
+            cachedDevice, int state) {
+        return mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
+                state);
     }
 
     public synchronized void onDeviceUnpaired(CachedBluetoothDevice device) {
-        final long hiSyncId = device.getHiSyncId();
-
-        if (hiSyncId == BluetoothHearingAid.HI_SYNC_ID_INVALID) return;
-
-        for (int i = mHearingAidDevicesNotAddedInCache.size() - 1; i >= 0; i--) {
-            CachedBluetoothDevice cachedDevice = mHearingAidDevicesNotAddedInCache.get(i);
-            if (cachedDevice.getHiSyncId() == hiSyncId) {
-                // TODO: Look for more cleanups on unpairing the device.
-                mHearingAidDevicesNotAddedInCache.remove(i);
-                if (device == cachedDevice) continue;
-                log("onDeviceUnpaired: Unpair device=" + cachedDevice);
-                cachedDevice.unpair();
-            }
-        }
-
-        CachedBluetoothDevice mappedDevice = mCachedDevicesMapForHearingAids.get(hiSyncId);
-        if ((mappedDevice != null) && (!Objects.equals(device, mappedDevice))) {
-            log("onDeviceUnpaired: Unpair mapped device=" + mappedDevice);
-            mappedDevice.unpair();
+        CachedBluetoothDevice mainDevice = mHearingAidDeviceManager.findMainDevice(device);
+        CachedBluetoothDevice subDevice = device.getSubDevice();
+        if (subDevice != null) {
+            // Main device is unpaired, to unpair sub device
+            subDevice.unpair();
+            device.setSubDevice(null);
+        } else if (mainDevice != null) {
+            // Sub device unpaired, to unpair main device
+            mainDevice.unpair();
+            mainDevice.setSubDevice(null);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
new file mode 100644
index 0000000..20ece69
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2018 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.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothProfile;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * HearingAidDeviceManager manages the set of remote HearingAid Bluetooth devices.
+ */
+public class HearingAidDeviceManager {
+    private static final String TAG = "HearingAidDeviceManager";
+    private static final boolean DEBUG = BluetoothUtils.D;
+
+    private final LocalBluetoothManager mBtManager;
+    private final List<CachedBluetoothDevice> mCachedDevices;
+    HearingAidDeviceManager(LocalBluetoothManager localBtManager,
+            List<CachedBluetoothDevice> CachedDevices) {
+        mBtManager = localBtManager;
+        mCachedDevices = CachedDevices;
+    }
+
+    void initHearingAidDeviceIfNeeded(CachedBluetoothDevice newDevice) {
+        long hiSyncId = getHiSyncId(newDevice.getDevice());
+        if (isValidHiSyncId(hiSyncId)) {
+            // Once hiSyncId is valid, assign hiSyncId
+            newDevice.setHiSyncId(hiSyncId);
+        }
+    }
+
+    private long getHiSyncId(BluetoothDevice device) {
+        LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
+        HearingAidProfile profileProxy = profileManager.getHearingAidProfile();
+        if (profileProxy != null) {
+            return profileProxy.getHiSyncId(device);
+        }
+        return BluetoothHearingAid.HI_SYNC_ID_INVALID;
+    }
+
+    boolean setSubDeviceIfNeeded(CachedBluetoothDevice newDevice) {
+        final long hiSyncId = newDevice.getHiSyncId();
+        if (isValidHiSyncId(hiSyncId)) {
+            final CachedBluetoothDevice hearingAidDevice = getCachedDevice(hiSyncId);
+            // Just add one of the hearing aids from a pair in the list that is shown in the UI.
+            // Once there is another device with the same hiSyncId, to add new device as sub
+            // device.
+            if (hearingAidDevice != null) {
+                hearingAidDevice.setSubDevice(newDevice);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isValidHiSyncId(long hiSyncId) {
+        return hiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID;
+    }
+
+    private CachedBluetoothDevice getCachedDevice(long hiSyncId) {
+        for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
+            CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+            if (cachedDevice.getHiSyncId() == hiSyncId) {
+                return cachedDevice;
+            }
+        }
+        return null;
+    }
+
+    // To collect all HearingAid devices and call #onHiSyncIdChanged to group device by HiSyncId
+    void updateHearingAidsDevices() {
+        final Set<Long> newSyncIdSet = new HashSet<Long>();
+        for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+            // Do nothing if HiSyncId has been assigned
+            if (!isValidHiSyncId(cachedDevice.getHiSyncId())) {
+                final long newHiSyncId = getHiSyncId(cachedDevice.getDevice());
+                // Do nothing if there is no HiSyncId on Bluetooth device
+                if (isValidHiSyncId(newHiSyncId)) {
+                    cachedDevice.setHiSyncId(newHiSyncId);
+                    newSyncIdSet.add(newHiSyncId);
+                }
+            }
+        }
+        for (Long syncId : newSyncIdSet) {
+            onHiSyncIdChanged(syncId);
+        }
+    }
+
+    // Group devices by hiSyncId
+    @VisibleForTesting
+    void onHiSyncIdChanged(long hiSyncId) {
+        int firstMatchedIndex = -1;
+
+        for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
+            CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+            if (cachedDevice.getHiSyncId() != hiSyncId) {
+                continue;
+            }
+            if (firstMatchedIndex == -1) {
+                // Found the first one
+                firstMatchedIndex = i;
+                continue;
+            }
+            // Found the second one
+            int indexToRemoveFromUi;
+            CachedBluetoothDevice subDevice;
+            CachedBluetoothDevice mainDevice;
+            // Since the hiSyncIds have been updated for a connected pair of hearing aids,
+            // we remove the entry of one the hearing aids from the UI. Unless the
+            // hiSyncId get updated, the system does not know it is a hearing aid, so we add
+            // both the hearing aids as separate entries in the UI first, then remove one
+            // of them after the hiSyncId is populated. We will choose the device that
+            // is not connected to be removed.
+            if (cachedDevice.isConnected()) {
+                mainDevice = cachedDevice;
+                indexToRemoveFromUi = firstMatchedIndex;
+                subDevice = mCachedDevices.get(firstMatchedIndex);
+            } else {
+                mainDevice = mCachedDevices.get(firstMatchedIndex);
+                indexToRemoveFromUi = i;
+                subDevice = cachedDevice;
+            }
+
+            mainDevice.setSubDevice(subDevice);
+            mCachedDevices.remove(indexToRemoveFromUi);
+            log("onHiSyncIdChanged: removed from UI device =" + subDevice
+                    + ", with hiSyncId=" + hiSyncId);
+            mBtManager.getEventManager().dispatchDeviceRemoved(subDevice);
+            break;
+        }
+    }
+
+    // @return {@code true}, the event is processed inside the method. It is for updating
+    // hearing aid device on main-sub relationship when receiving connected or disconnected.
+    // @return {@code false}, it is not hearing aid device or to process it same as other profiles
+    boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice cachedDevice,
+            int state) {
+        switch (state) {
+            case BluetoothProfile.STATE_CONNECTED:
+                onHiSyncIdChanged(cachedDevice.getHiSyncId());
+                CachedBluetoothDevice mainDevice = findMainDevice(cachedDevice);
+                if (mainDevice != null){
+                    if (mainDevice.isConnected()) {
+                        // When main device exists and in connected state, receiving sub device
+                        // connection. To refresh main device UI
+                        mainDevice.refresh();
+                        return true;
+                    } else {
+                        // When both Hearing Aid devices are disconnected, receiving sub device
+                        // connection. To switch content and dispatch to notify UI change
+                        mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice);
+                        mainDevice.switchSubDeviceContent();
+                        mainDevice.refresh();
+                        // It is necessary to do remove and add for updating the mapping on
+                        // preference and device
+                        mBtManager.getEventManager().dispatchDeviceAdded(mainDevice);
+                        return true;
+                    }
+                }
+                break;
+            case BluetoothProfile.STATE_DISCONNECTED:
+                mainDevice = findMainDevice(cachedDevice);
+                if (mainDevice != null) {
+                    // When main device exists, receiving sub device disconnection
+                    // To update main device UI
+                    mainDevice.refresh();
+                    return true;
+                }
+                CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+                if (subDevice != null && subDevice.isConnected()) {
+                    // Main device is disconnected and sub device is connected
+                    // To copy data from sub device to main device
+                    mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
+                    cachedDevice.switchSubDeviceContent();
+                    cachedDevice.refresh();
+                    // It is necessary to do remove and add for updating the mapping on
+                    // preference and device
+                    mBtManager.getEventManager().dispatchDeviceAdded(cachedDevice);
+                    return true;
+                }
+                break;
+        }
+        return false;
+    }
+
+    CachedBluetoothDevice findMainDevice(CachedBluetoothDevice device) {
+        for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+            if (isValidHiSyncId(cachedDevice.getHiSyncId())) {
+                CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+                if (subDevice != null && subDevice.equals(device)) {
+                    return cachedDevice;
+                }
+            }
+        }
+        return null;
+    }
+
+    private void log(String msg) {
+        if (DEBUG) {
+            Log.d(TAG, msg);
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 8bc0acf..adb5ab3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -71,7 +71,7 @@
             }
 
             // Check current list of CachedDevices to see if any are Hearing Aid devices.
-            mDeviceManager.updateHearingAidsDevices(mProfileManager);
+            mDeviceManager.updateHearingAidsDevices();
 
             mIsProfileReady=true;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
index 7fe6205..53c6075 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
@@ -18,10 +18,14 @@
 
 import android.content.Context;
 import android.os.Handler;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.lang.ref.WeakReference;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresPermission;
+
 /**
  * LocalBluetoothManager provides a simplified interface on top of a subset of
  * the Bluetooth API. Note that {@link #getInstance} will return null
@@ -49,6 +53,7 @@
     /** The broadcast receiver event manager. */
     private final BluetoothEventManager mEventManager;
 
+    @Nullable
     public static synchronized LocalBluetoothManager getInstance(Context context,
             BluetoothManagerCallback onInitCallback) {
         if (sInstance == null) {
@@ -57,10 +62,11 @@
                 return null;
             }
             // This will be around as long as this process is
-            Context appContext = context.getApplicationContext();
-            sInstance = new LocalBluetoothManager(adapter, appContext, null);
+            sInstance = new LocalBluetoothManager(adapter, context, /* handler= */ null,
+                    /* userHandle= */ null);
             if (onInitCallback != null) {
-                onInitCallback.onBluetoothManagerInitialized(appContext, sInstance);
+                onInitCallback.onBluetoothManagerInitialized(context.getApplicationContext(),
+                        sInstance);
             }
         }
 
@@ -71,22 +77,43 @@
      * Returns a new instance of {@link LocalBluetoothManager} or null if Bluetooth is not
      * supported for this hardware. This instance should be globally cached by the caller.
      */
+    @Nullable
     public static LocalBluetoothManager create(Context context, Handler handler) {
         LocalBluetoothAdapter adapter = LocalBluetoothAdapter.getInstance();
         if (adapter == null) {
             return null;
         }
-        return new LocalBluetoothManager(adapter, context.getApplicationContext(), handler);
+        return new LocalBluetoothManager(adapter, context, handler, /* userHandle= */ null);
     }
 
-    private LocalBluetoothManager(
-            LocalBluetoothAdapter adapter, Context context, Handler handler) {
-        mContext = context;
+    /**
+     * Returns a new instance of {@link LocalBluetoothManager} or null if Bluetooth is not
+     * supported for this hardware. This instance should be globally cached by the caller.
+     *
+     * <p> Allows to specify a {@link UserHandle} for which to receive bluetooth events.
+     *
+     * <p> Requires {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
+     */
+    @Nullable
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    public static LocalBluetoothManager create(Context context, Handler handler,
+            UserHandle userHandle) {
+        LocalBluetoothAdapter adapter = LocalBluetoothAdapter.getInstance();
+        if (adapter == null) {
+            return null;
+        }
+        return new LocalBluetoothManager(adapter, context, handler,
+                userHandle);
+    }
+
+    private LocalBluetoothManager(LocalBluetoothAdapter adapter, Context context, Handler handler,
+            UserHandle userHandle) {
+        mContext = context.getApplicationContext();
         mLocalAdapter = adapter;
-        mCachedDeviceManager = new CachedBluetoothDeviceManager(context, this);
-        mEventManager = new BluetoothEventManager(mLocalAdapter,
-                mCachedDeviceManager, context, handler);
-        mProfileManager = new LocalBluetoothProfileManager(context,
+        mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, this);
+        mEventManager = new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mContext,
+                handler, userHandle);
+        mProfileManager = new LocalBluetoothProfileManager(mContext,
                 mLocalAdapter, mCachedDeviceManager, mEventManager);
 
         mProfileManager.updateLocalProfiles();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 9653972..29c6d71 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -40,7 +40,6 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.R;
 import com.android.internal.util.CollectionUtils;
 
 import java.util.ArrayList;
@@ -289,21 +288,21 @@
                 (newState == BluetoothProfile.STATE_CONNECTED)) {
                 // Check if the HiSyncID has being initialized
                 if (cachedDevice.getHiSyncId() == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
-
                     long newHiSyncId = getHearingAidProfile().getHiSyncId(cachedDevice.getDevice());
-
                     if (newHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
                         cachedDevice.setHiSyncId(newHiSyncId);
-                        mDeviceManager.onHiSyncIdChanged(newHiSyncId);
                     }
                 }
             }
-
             cachedDevice.onProfileStateChanged(mProfile, newState);
-            cachedDevice.refresh();
             // Dispatch profile changed after device update
-            mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
-                    mProfile.getProfileId());
+            if (!(cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
+                    && mDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
+                    newState))) {
+                cachedDevice.refresh();
+                mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
+                        mProfile.getProfileId());
+            }
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
index 379a820..a16838c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
@@ -55,6 +55,7 @@
 
     /**
      * Logs an user action.
+     *
      * @deprecated use {@link #action(int, int, Pair[])}
      */
     @Deprecated
@@ -62,6 +63,7 @@
 
     /**
      * Logs an user action.
+     *
      * @deprecated use {@link #action(int, boolean, Pair[])}
      */
     @Deprecated
@@ -76,4 +78,10 @@
      * Logs a count.
      */
     void count(Context context, String name, int value);
+
+    /**
+     * Generically log action into statsd.
+     */
+    default void action(int attribution, int action, int pageId, String key, int value) {
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
index 662fa10..e1f9111 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
@@ -30,7 +30,7 @@
  * FeatureProvider for metrics.
  */
 public class MetricsFeatureProvider {
-    private List<LogWriter> mLoggerWriters;
+    protected List<LogWriter> mLoggerWriters;
 
     public MetricsFeatureProvider() {
         mLoggerWriters = new ArrayList<>();
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index dd8bfda..92fd868 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -51,6 +51,8 @@
     public static final String CATEGORY_GESTURES = "com.android.settings.category.ia.gestures";
     public static final String CATEGORY_NIGHT_DISPLAY =
             "com.android.settings.category.ia.night_display";
+    public static final String CATEGORY_PRIVACY =
+            "com.android.settings.category.ia.privacy";
 
     public static final Map<String, String> KEY_COMPAT_MAP;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
index 78e807c..8c03918 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
@@ -16,18 +16,11 @@
 
 package com.android.settingslib.license;
 
-import static com.android.settingslib.license.LicenseHtmlLoaderCompat.generateHtmlFile;
-import static com.android.settingslib.license.LicenseHtmlLoaderCompat.getCachedHtmlFile;
-import static com.android.settingslib.license.LicenseHtmlLoaderCompat.getVaildXmlFiles;
-import static com.android.settingslib.license.LicenseHtmlLoaderCompat.isCachedHtmlFileOutdated;
-
 import android.content.Context;
-import android.util.Log;
 
 import com.android.settingslib.utils.AsyncLoader;
 
 import java.io.File;
-import java.util.List;
 
 /**
  * LicenseHtmlLoader is a loader which loads a license html file from default license xml files.
@@ -44,27 +37,10 @@
 
     @Override
     public File loadInBackground() {
-        return generateHtmlFromDefaultXmlFiles();
+        return new LicenseHtmlLoaderCompat(mContext).loadInBackground();
     }
 
     @Override
     protected void onDiscardResult(File f) {
     }
-
-    private File generateHtmlFromDefaultXmlFiles() {
-        final List<File> xmlFiles = getVaildXmlFiles();
-        if (xmlFiles.isEmpty()) {
-            Log.e(TAG, "No notice file exists.");
-            return null;
-        }
-
-        File cachedHtmlFile = getCachedHtmlFile(mContext);
-        if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
-                || generateHtmlFile(mContext, xmlFiles, cachedHtmlFile)) {
-            return cachedHtmlFile;
-        }
-
-        return null;
-    }
-
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
index ca62485..0b69963 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
@@ -73,7 +73,7 @@
         return null;
     }
 
-    static List<File> getVaildXmlFiles() {
+    private List<File> getVaildXmlFiles() {
         final List<File> xmlFiles = new ArrayList();
         for (final String xmlPath : DEFAULT_LICENSE_XML_PATHS) {
             File file = new File(xmlPath);
@@ -84,11 +84,11 @@
         return xmlFiles;
     }
 
-    static File getCachedHtmlFile(Context context) {
+    private File getCachedHtmlFile(Context context) {
         return new File(context.getCacheDir(), NOTICE_HTML_FILE_NAME);
     }
 
-    static boolean isCachedHtmlFileOutdated(List<File> xmlFiles, File cachedHtmlFile) {
+    private boolean isCachedHtmlFileOutdated(List<File> xmlFiles, File cachedHtmlFile) {
         boolean outdated = true;
         if (cachedHtmlFile.exists() && cachedHtmlFile.length() != 0) {
             outdated = false;
@@ -102,7 +102,7 @@
         return outdated;
     }
 
-    static boolean generateHtmlFile(Context context, List<File> xmlFiles, File htmlFile) {
+    private boolean generateHtmlFile(Context context, List<File> xmlFiles, File htmlFile) {
         return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile,
                 context.getString(R.string.notice_header));
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index e950e8e..523361d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -158,9 +158,12 @@
      * These values are matched in string arrays -- changes must be kept in sync
      */
     public static final int SECURITY_NONE = 0;
-    public static final int SECURITY_WEP = 1;
-    public static final int SECURITY_PSK = 2;
-    public static final int SECURITY_EAP = 3;
+    public static final int SECURITY_OWE = 1;
+    public static final int SECURITY_WEP = 2;
+    public static final int SECURITY_PSK = 3;
+    public static final int SECURITY_SAE = 4;
+    public static final int SECURITY_EAP = 5;
+    public static final int SECURITY_EAP_SUITE_B = 6;
 
     private static final int PSK_UNKNOWN = 0;
     private static final int PSK_WPA = 1;
@@ -433,7 +436,7 @@
         if (isConnectable()) {
             builder.append(',').append("connectable");
         }
-        if (security != SECURITY_NONE) {
+        if ((security != SECURITY_NONE) && (security != SECURITY_OWE)) {
             builder.append(',').append(securityToString(security, pskType));
         }
         builder.append(",level=").append(getLevel());
@@ -720,6 +723,9 @@
             case SECURITY_EAP:
                 return concise ? context.getString(R.string.wifi_security_short_eap) :
                     context.getString(R.string.wifi_security_eap);
+            case SECURITY_EAP_SUITE_B:
+                return concise ? context.getString(R.string.wifi_security_short_eap_suiteb) :
+                        context.getString(R.string.wifi_security_eap_suiteb);
             case SECURITY_PSK:
                 switch (pskType) {
                     case PSK_WPA:
@@ -739,6 +745,12 @@
             case SECURITY_WEP:
                 return concise ? context.getString(R.string.wifi_security_short_wep) :
                     context.getString(R.string.wifi_security_wep);
+            case SECURITY_SAE:
+                return concise ? context.getString(R.string.wifi_security_short_sae) :
+                    context.getString(R.string.wifi_security_sae);
+            case SECURITY_OWE:
+                return concise ? context.getString(R.string.wifi_security_short_owe) :
+                    context.getString(R.string.wifi_security_owe);
             case SECURITY_NONE:
             default:
                 return concise ? "" : context.getString(R.string.wifi_security_none);
@@ -980,13 +992,20 @@
      * Can only be called for unsecured networks.
      */
     public void generateOpenNetworkConfig() {
-        if (security != SECURITY_NONE)
+        if ((security != SECURITY_NONE) && (security != SECURITY_OWE)) {
             throw new IllegalStateException();
+        }
         if (mConfig != null)
             return;
         mConfig = new WifiConfiguration();
         mConfig.SSID = AccessPoint.convertToQuotedString(ssid);
-        mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
+
+        if (security == SECURITY_NONE) {
+            mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
+        } else {
+            mConfig.allowedKeyManagement.set(KeyMgmt.OWE);
+            mConfig.requirePMF = true;
+        }
     }
 
     public void saveWifiState(Bundle savedState) {
@@ -1288,22 +1307,38 @@
     private static int getSecurity(ScanResult result) {
         if (result.capabilities.contains("WEP")) {
             return SECURITY_WEP;
+        } else if (result.capabilities.contains("SAE")) {
+            return SECURITY_SAE;
         } else if (result.capabilities.contains("PSK")) {
             return SECURITY_PSK;
+        } else if (result.capabilities.contains("EAP_SUITE_B_192")) {
+            return SECURITY_EAP_SUITE_B;
         } else if (result.capabilities.contains("EAP")) {
             return SECURITY_EAP;
+        } else if (result.capabilities.contains("OWE")) {
+            return SECURITY_OWE;
         }
+
         return SECURITY_NONE;
     }
 
     static int getSecurity(WifiConfiguration config) {
+        if (config.allowedKeyManagement.get(KeyMgmt.SAE)) {
+            return SECURITY_SAE;
+        }
         if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
             return SECURITY_PSK;
         }
+        if (config.allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
+            return SECURITY_EAP_SUITE_B;
+        }
         if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
                 config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
             return SECURITY_EAP;
         }
+        if (config.allowedKeyManagement.get(KeyMgmt.OWE)) {
+            return SECURITY_OWE;
+        }
         return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE;
     }
 
@@ -1321,6 +1356,12 @@
             return "PSK";
         } else if (security == SECURITY_EAP) {
             return "EAP";
+        } else if (security == SECURITY_SAE) {
+            return "SAE";
+        } else if (security == SECURITY_EAP_SUITE_B) {
+            return "SUITE_B";
+        } else if (security == SECURITY_OWE) {
+            return "OWE";
         }
         return "NONE";
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index f3c43cc..db364a3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -200,7 +200,8 @@
         if (frictionImageView == null || mFrictionSld == null) {
             return;
         }
-        if (mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
+        if ((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE)
+                && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE)) {
             mFrictionSld.setState(STATE_SECURED);
         } else if (mAccessPoint.isMetered()) {
             mFrictionSld.setState(STATE_METERED);
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
new file mode 100644
index 0000000..d0ab46a
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/bluetooth/BluetoothEventManagerIntegTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 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.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Test that verifies that BluetoothEventManager can receive broadcasts for non-current
+ * users for all bluetooth events.
+ *
+ * <p>Creation and deletion of users takes a long time, so marking this as a LargeTest.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothEventManagerIntegTest {
+    private static final int LATCH_TIMEOUT = 4;
+
+    private Context mContext;
+    private UserManager mUserManager;
+    private BluetoothEventManager mBluetoothEventManager;
+
+    private UserInfo mOtherUser;
+    private final Intent mTestIntent = new Intent("Test intent");
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mUserManager = UserManager.get(mContext);
+
+        mBluetoothEventManager = new BluetoothEventManager(
+                mock(LocalBluetoothAdapter.class), mock(CachedBluetoothDeviceManager.class),
+                mContext, /* handler= */ null, UserHandle.ALL);
+
+        // Create and start another user in the background.
+        mOtherUser = mUserManager.createUser("TestUser", /* flags= */ 0);
+        try {
+            ActivityManager.getService().startUserInBackground(mOtherUser.id);
+        } catch (RemoteException e) {
+            fail("Count't create an additional user.");
+        }
+    }
+
+    @After
+    public void tearDown() {
+        if (mOtherUser != null) {
+            mUserManager.removeUser(mOtherUser.id);
+        }
+    }
+
+    /**
+     * Verify that MultiUserAwareBluetoothEventManager's adapter receiver handles events coming from
+     * users other than current user.
+     */
+    @Test
+    public void registerAdapterReceiver_ifIntentFromAnotherUser_broadcastIsReceived()
+            throws Exception {
+        // Create a latch to listen for the intent.
+        final CountDownLatch broadcastLatch = new CountDownLatch(1);
+
+        // Register adapter receiver.
+        mBluetoothEventManager.addHandler(mTestIntent.getAction(),
+                (context, intent, device) -> broadcastLatch.countDown());
+        mBluetoothEventManager.registerAdapterIntentReceiver();
+
+        // Send broadcast from another user.
+        mContext.sendBroadcastAsUser(mTestIntent, mOtherUser.getUserHandle());
+
+        // Wait to receive it.
+        assertTrue(broadcastLatch.await(LATCH_TIMEOUT, SECONDS));
+    }
+
+    /**
+     * Verify that MultiUserAwareBluetoothEventManager's profile receiver handles events coming from
+     * users other than current user.
+     */
+    @Test
+    public void registerProfileReceiver_ifIntentFromAnotherUser_broadcastIsReceived()
+            throws Exception {
+        // Create a latch to listen for the intent.
+        final CountDownLatch broadcastLatch = new CountDownLatch(1);
+
+        // Register profile receiver.
+        mBluetoothEventManager.addProfileHandler(mTestIntent.getAction(),
+                (context, intent, device) -> broadcastLatch.countDown());
+        mBluetoothEventManager.registerProfileIntentReceiver();
+
+        // Send broadcast from another user.
+        mContext.sendBroadcastAsUser(mTestIntent, mOtherUser.getUserHandle());
+
+        // Wait to receive it.
+        assertTrue(broadcastLatch.await(LATCH_TIMEOUT, SECONDS));
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index 19ce4242..b307b47 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -86,21 +86,21 @@
     @Mock
     private StorageStatsManager mStorageStatsManager;
 
-    @Implements(value = IconDrawableFactory.class, inheritImplementationMethods = true)
+    @Implements(value = IconDrawableFactory.class)
     public static class ShadowIconDrawableFactory {
 
         @Implementation
-        public Drawable getBadgedIcon(ApplicationInfo appInfo) {
+        protected Drawable getBadgedIcon(ApplicationInfo appInfo) {
             return new ColorDrawable(0);
         }
     }
 
-    @Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true)
+    @Implements(value = ApplicationPackageManager.class)
     public static class ShadowPackageManager extends
             org.robolectric.shadows.ShadowApplicationPackageManager {
 
         @Implementation
-        public ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
+        protected ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
             ResolveInfo resolveInfo = new ResolveInfo();
             resolveInfo.activityInfo = new ActivityInfo();
             resolveInfo.activityInfo.packageName = HOME_PACKAGE_NAME;
@@ -139,6 +139,7 @@
         when(mStorageStatsManager.queryStatsForPackage(ArgumentMatchers.any(UUID.class),
                 anyString(), ArgumentMatchers.any(UserHandle.class))).thenReturn(storageStats);
 
+        ApplicationsState.sInstance = null;
         mApplicationsState = ApplicationsState.getInstance(RuntimeEnvironment.application);
         mApplicationsState.clearEntries();
     }
@@ -189,8 +190,8 @@
         Session session = mApplicationsState.newSession(mCallbacks);
         session.onResume();
 
-        addApp(HOME_PACKAGE_NAME,1);
-        addApp(LAUNCHABLE_PACKAGE_NAME,2);
+        addApp(HOME_PACKAGE_NAME, 1);
+        addApp(LAUNCHABLE_PACKAGE_NAME, 2);
         session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
         processAllMessages();
         verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -219,7 +220,7 @@
         session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
         session.onResume();
 
-        addApp(LAUNCHABLE_PACKAGE_NAME,1);
+        addApp(LAUNCHABLE_PACKAGE_NAME, 1);
         session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
         processAllMessages();
         verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -240,7 +241,7 @@
         session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES);
         session.onResume();
 
-        addApp(LAUNCHABLE_PACKAGE_NAME,1);
+        addApp(LAUNCHABLE_PACKAGE_NAME, 1);
         session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
         processAllMessages();
         verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -261,7 +262,7 @@
         session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP);
         session.onResume();
 
-        addApp(HOME_PACKAGE_NAME,1);
+        addApp(HOME_PACKAGE_NAME, 1);
         session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
         processAllMessages();
         verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
@@ -283,7 +284,7 @@
         session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER);
         session.onResume();
 
-        addApp(LAUNCHABLE_PACKAGE_NAME,1);
+        addApp(LAUNCHABLE_PACKAGE_NAME, 1);
         session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
         processAllMessages();
         verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 14bfb27..c147d5e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -15,12 +15,22 @@
  */
 package com.android.settingslib.bluetooth;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
@@ -43,6 +53,8 @@
     private BluetoothCallback mBluetoothCallback;
     @Mock
     private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
 
     private Context mContext;
     private Intent mIntent;
@@ -54,7 +66,30 @@
         mContext = RuntimeEnvironment.application;
 
         mBluetoothEventManager = new BluetoothEventManager(mLocalAdapter,
-                mCachedDeviceManager, mContext, null);
+                mCachedDeviceManager, mContext, /* handler= */ null, /* userHandle= */ null);
+        when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
+    }
+
+    @Test
+    public void ifUserHandleIsNull_registerReceiverIsCalled() {
+        Context mockContext = mock(Context.class);
+        BluetoothEventManager eventManager =
+                new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mockContext,
+                        /* handler= */ null, /* userHandle= */ null);
+
+        verify(mockContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class),
+                eq(null), eq(null));
+    }
+
+    @Test
+    public void ifUserHandleSpecified_registerReceiverAsUserIsCalled() {
+        Context mockContext = mock(Context.class);
+        BluetoothEventManager eventManager =
+                new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mockContext,
+                        /* handler= */ null, UserHandle.ALL);
+
+        verify(mockContext).registerReceiverAsUser(any(BroadcastReceiver.class), eq(UserHandle.ALL),
+                any(IntentFilter.class), eq(null), eq(null));
     }
 
     /**
@@ -96,8 +131,29 @@
 
         verify(mBluetoothCallback).onProfileConnectionStateChanged(mCachedBluetoothDevice,
                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
+    }
 
-        verify(mCachedDeviceManager).onProfileConnectionStateChanged(mCachedBluetoothDevice,
-                BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
+    @Test
+    public void dispatchAclConnectionStateChanged_aclDisconnected_shouldDispatchCallback() {
+        mBluetoothEventManager.registerCallback(mBluetoothCallback);
+        mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothAdapter.STATE_DISCONNECTED);
+    }
+
+    @Test
+    public void dispatchAclConnectionStateChanged_aclConnected_shouldDispatchCallback() {
+        mBluetoothEventManager.registerCallback(mBluetoothCallback);
+        mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothAdapter.STATE_CONNECTED);
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 62b5688..9c75491 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -18,7 +18,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothClass;
@@ -80,6 +82,7 @@
     private CachedBluetoothDevice mCachedDevice2;
     private CachedBluetoothDevice mCachedDevice3;
     private CachedBluetoothDeviceManager mCachedDeviceManager;
+    private HearingAidDeviceManager mHearingAidDeviceManager;
     private Context mContext;
 
     @Before
@@ -105,13 +108,12 @@
         when(mA2dpProfile.isProfileReady()).thenReturn(true);
         when(mPanProfile.isProfileReady()).thenReturn(true);
         when(mHearingAidProfile.isProfileReady()).thenReturn(true);
+        doAnswer((invocation) -> mHearingAidProfile).
+                when(mLocalProfileManager).getHearingAidProfile();
         mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
-        mCachedDevice1 = spy(
-            new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
-        mCachedDevice2 = spy(
-            new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
-        mCachedDevice3 = spy(
-            new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3));
+        mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
+        mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
+        mCachedDevice3 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3));
     }
 
     /**
@@ -133,6 +135,76 @@
     }
 
     /**
+     * Test to verify getSubDevice(), new device has the same HiSyncId.
+     */
+    @Test
+    public void addDevice_sameHiSyncId_validSubDevice() {
+        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
+        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+
+        assertThat(cachedDevice1.getSubDevice()).isEqualTo(cachedDevice2);
+    }
+
+    /**
+     * Test to verify getSubDevice(), new device has the different HiSyncId.
+     */
+    @Test
+    public void addDevice_differentHiSyncId_validSubDevice() {
+        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
+        doAnswer((invocation) -> HISYNCID2).when(mHearingAidProfile).getHiSyncId(mDevice2);
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+
+        assertThat(cachedDevice1.getSubDevice()).isNull();
+    }
+
+    /**
+     * Test to verify addDevice(), new device has the same HiSyncId.
+     */
+    @Test
+    public void addDevice_sameHiSyncId_validCachedDevices_mainDevicesAdded_subDevicesNotAdded() {
+        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
+        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+
+        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+        assertThat(devices).contains(cachedDevice1);
+        assertThat(devices).doesNotContain(cachedDevice2);
+    }
+
+    /**
+     * Test to verify addDevice(), new device has the different HiSyncId.
+     */
+    @Test
+    public void addDevice_differentHiSyncId_validCachedDevices_bothAdded() {
+        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
+        doAnswer((invocation) -> HISYNCID2).when(mHearingAidProfile).getHiSyncId(mDevice2);
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+
+        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+        assertThat(devices).contains(cachedDevice1);
+        assertThat(devices).contains(cachedDevice2);
+    }
+
+    /**
+     * Test to verify findDevice(), new device has the same HiSyncId.
+     */
+    @Test
+    public void findDevice_sameHiSyncId_foundBothDevice() {
+        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
+        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+
+        assertThat(mCachedDeviceManager.findDevice(mDevice1)).isEqualTo(cachedDevice1);
+        assertThat(mCachedDeviceManager.findDevice(mDevice2)).isEqualTo(cachedDevice2);
+    }
+
+    /**
      * Test to verify getName().
      */
     @Test
@@ -190,453 +262,89 @@
     }
 
     /**
-     * Test to verify clearNonBondedDevices() for hearing aids.
+     * Test to verify clearNonBondedDevices() for hearing aids sub device.
      */
     @Test
-    public void clearNonBondedDevices_HearingAids_nonBondedHAsClearedFromCachedDevicesMap() {
+    public void clearNonBondedDevices_nonBondedSubDevice() {
         when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+        cachedDevice1.setSubDevice(cachedDevice2);
 
-        mCachedDevice1.setHiSyncId(HISYNCID1);
-        mCachedDevice2.setHiSyncId(HISYNCID2);
-        mCachedDeviceManager.mCachedDevicesMapForHearingAids.put(HISYNCID1, mCachedDevice1);
-        mCachedDeviceManager.mCachedDevicesMapForHearingAids.put(HISYNCID2, mCachedDevice2);
-
+        assertThat(cachedDevice1.getSubDevice()).isEqualTo(cachedDevice2);
         mCachedDeviceManager.clearNonBondedDevices();
 
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
-            .doesNotContain(mCachedDevice2);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
-            .contains(mCachedDevice1);
+        assertThat(cachedDevice1.getSubDevice()).isNull();
     }
 
     /**
-     * Test to verify onHiSyncIdChanged() for hearing aid devices with same HiSyncId.
+     * Test to verify OnDeviceUnpaired() for main hearing Aid device unpair.
      */
     @Test
-    public void onHiSyncIdChanged_sameHiSyncId_populateInDifferentLists() {
-        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
-        assertThat(cachedDevice1).isNotNull();
-        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
-        assertThat(cachedDevice2).isNotNull();
-
-        // Since both devices do not have hiSyncId, they should be added in mCachedDevices.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();
-
-        cachedDevice1.setHiSyncId(HISYNCID1);
-        cachedDevice2.setHiSyncId(HISYNCID1);
-        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
-
-        // Since both devices have the same hiSyncId, one should remain in mCachedDevices
-        // and the other should be removed from mCachedDevices and get added in
-        // mHearingAidDevicesNotAddedInCache. The one that is in mCachedDevices should also be
-        // added in mCachedDevicesMapForHearingAids.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).hasSize(1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
-        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
-        assertThat(devices).contains(cachedDevice2);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids)
-            .containsKey(HISYNCID1);
-    }
-
-    /**
-     * Test to verify onHiSyncIdChanged() for 2 hearing aid devices with same HiSyncId but one
-     * device is connected and other is disconnected. The connected device should be chosen.
-     */
-    @Test
-    public void onHiSyncIdChanged_sameHiSyncIdAndOneConnected_chooseConnectedDevice() {
-        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
-        assertThat(cachedDevice1).isNotNull();
-        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
-        assertThat(cachedDevice2).isNotNull();
-        cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
-        cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
-
-        /* Device 1 is connected and Device 2 is disconnected */
-        when(mHearingAidProfile.getConnectionStatus(mDevice1)).
-            thenReturn(BluetoothProfile.STATE_CONNECTED);
-        when(mHearingAidProfile.getConnectionStatus(mDevice2)).
-            thenReturn(BluetoothProfile.STATE_DISCONNECTED);
-
-        // Since both devices do not have hiSyncId, they should be added in mCachedDevices.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();
-
-        cachedDevice1.setHiSyncId(HISYNCID1);
-        cachedDevice2.setHiSyncId(HISYNCID1);
-        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
-
-        // Only the connected device, device 1, should be visible to UI.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).
-            containsExactly(HISYNCID1, cachedDevice1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).
-            containsExactly(cachedDevice2);
-    }
-
-    /**
-     * Test to verify onHiSyncIdChanged() for hearing aid devices with different HiSyncId.
-     */
-    @Test
-    public void onHiSyncIdChanged_differentHiSyncId_populateInSameList() {
-        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
-        assertThat(cachedDevice1).isNotNull();
-        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
-        assertThat(cachedDevice2).isNotNull();
-
-        // Since both devices do not have hiSyncId, they should be added in mCachedDevices.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();
-
-        cachedDevice1.setHiSyncId(HISYNCID1);
-        cachedDevice2.setHiSyncId(HISYNCID2);
-        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
-        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID2);
-
-        // Since both devices do not have same hiSyncId, they should remain in mCachedDevices and
-        // also be added in mCachedDevicesMapForHearingAids.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(2);
-        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
-        assertThat(devices).contains(cachedDevice2);
-        assertThat(devices).contains(cachedDevice1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
-            .contains(cachedDevice1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
-            .contains(cachedDevice2);
-    }
-
-    /**
-     * Test to verify onProfileConnectionStateChanged() for single hearing aid device connection.
-     */
-    @Test
-    public void onProfileConnectionStateChanged_singleDeviceConnected_visible() {
-        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
-        assertThat(cachedDevice1).isNotNull();
-        cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
-
-        // Since both devices do not have hiSyncId, they should be added in mCachedDevices.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();
-
-        cachedDevice1.setHiSyncId(HISYNCID1);
-        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
-
-        // Connect the Device 1
-        mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice1,
-            BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
-
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).
-            containsExactly(HISYNCID1, cachedDevice1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
-
-        // Disconnect the Device 1
-        mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice1,
-            BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.HEARING_AID);
-
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).
-            containsExactly(HISYNCID1, cachedDevice1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
-    }
-
-    /**
-     * Test to verify onProfileConnectionStateChanged() for two hearing aid devices where both
-     * devices are disconnected and they get connected.
-     */
-    @Test
-    public void onProfileConnectionStateChanged_twoDevicesConnected_oneDeviceVisible() {
-        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
-        assertThat(cachedDevice1).isNotNull();
-        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
-        assertThat(cachedDevice2).isNotNull();
-        cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
-        cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+    public void onDeviceUnpaired_unpairMainDevice() {
         when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
-        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
-
-        // Since both devices do not have hiSyncId, they should be added in mCachedDevices.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();
-
-        cachedDevice1.setHiSyncId(HISYNCID1);
-        cachedDevice2.setHiSyncId(HISYNCID1);
-        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
-
-        // There should be one cached device but can be either one.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
-
-        // Connect the Device 1
-        mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice1,
-            BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
-
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).
-            containsExactly(HISYNCID1, cachedDevice1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice2);
-        assertThat(mCachedDeviceManager.mCachedDevices).contains(cachedDevice1);
-
-        when(mHearingAidProfile.getConnectionStatus(mDevice1)).
-            thenReturn(BluetoothProfile.STATE_CONNECTED);
-        when(mHearingAidProfile.getConnectionStatus(mDevice2)).
-            thenReturn(BluetoothProfile.STATE_DISCONNECTED);
-
-        // Connect the Device 2
-        mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice2,
-            BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
-
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).hasSize(1);
-        assertThat(mCachedDeviceManager.mCachedDevices).hasSize(1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
-    }
-
-    /**
-     * Test to verify onProfileConnectionStateChanged() for two hearing aid devices where both
-     * devices are connected and they get disconnected.
-     */
-    @Test
-    public void onProfileConnectionStateChanged_twoDevicesDisconnected_oneDeviceVisible() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
-        assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
-        assertThat(cachedDevice2).isNotNull();
-        cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
-        cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
-
-        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
-        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
-        when(mHearingAidProfile.getConnectionStatus(mDevice1)).
-            thenReturn(BluetoothProfile.STATE_CONNECTED);
-        when(mHearingAidProfile.getConnectionStatus(mDevice2)).
-            thenReturn(BluetoothProfile.STATE_CONNECTED);
-
-        // Since both devices do not have hiSyncId, they should be added in mCachedDevices.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();
-
         cachedDevice1.setHiSyncId(HISYNCID1);
         cachedDevice2.setHiSyncId(HISYNCID1);
-        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
-
-        /* Disconnect the Device 1 */
-        mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice1,
-            BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.HEARING_AID);
-
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice2);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice1);
-        assertThat(mCachedDeviceManager.mCachedDevices).contains(cachedDevice2);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids)
-            .containsExactly(HISYNCID1, cachedDevice2);
-
-        when(mHearingAidProfile.getConnectionStatus(mDevice1)).
-            thenReturn(BluetoothProfile.STATE_DISCONNECTED);
-        when(mHearingAidProfile.getConnectionStatus(mDevice2)).
-            thenReturn(BluetoothProfile.STATE_CONNECTED);
-
-        /* Disconnect the Device 2 */
-        mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice2,
-            BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.HEARING_AID);
-
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).hasSize(1);
-        assertThat(mCachedDeviceManager.mCachedDevices).hasSize(1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
-    }
-
-    /**
-     * Test to verify OnDeviceUnpaired() for a paired hearing Aid device pair.
-     */
-    @Test
-    public void onDeviceUnpaired_bothHearingAidsPaired_removesItsPairFromList() {
-        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
-        assertThat(cachedDevice1).isNotNull();
-        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
-        assertThat(cachedDevice2).isNotNull();
-
-        cachedDevice1.setHiSyncId(HISYNCID1);
-        cachedDevice2.setHiSyncId(HISYNCID1);
-        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
-
-        // Check if one device is in mCachedDevices and one in mHearingAidDevicesNotAddedInCache.
-        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
-        assertThat(devices).contains(cachedDevice2);
-        assertThat(devices).doesNotContain(cachedDevice1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache)
-            .doesNotContain(cachedDevice2);
+        cachedDevice1.setSubDevice(cachedDevice2);
 
         // Call onDeviceUnpaired for the one in mCachedDevices.
         mCachedDeviceManager.onDeviceUnpaired(cachedDevice2);
-
-        // Check if its pair is removed from mHearingAidDevicesNotAddedInCache.
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache)
-            .doesNotContain(cachedDevice1);
+        verify(mDevice1).removeBond();
     }
 
     /**
-     * Test to verify OnDeviceUnpaired() for paired hearing Aid devices which are not a pair.
+     * Test to verify OnDeviceUnpaired() for sub hearing Aid device unpair.
      */
     @Test
-    public void onDeviceUnpaired_bothHearingAidsNotPaired_doesNotRemoveAnyDeviceFromList() {
+    public void onDeviceUnpaired_unpairSubDevice() {
+        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
-        assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
-        assertThat(cachedDevice2).isNotNull();
-        CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice3);
-        assertThat(cachedDevice2).isNotNull();
+        cachedDevice1.setSubDevice(cachedDevice2);
 
-        cachedDevice1.setHiSyncId(HISYNCID1);
-        cachedDevice2.setHiSyncId(HISYNCID1);
-        cachedDevice3.setHiSyncId(HISYNCID2);
-        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
-        mCachedDeviceManager.onHiSyncIdChanged(HISYNCID2);
-
-        // Check if one device is in mCachedDevices and one in mHearingAidDevicesNotAddedInCache.
-        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
-        assertThat(devices).contains(cachedDevice2);
-        assertThat(devices).contains(cachedDevice3);
-        assertThat(devices).doesNotContain(cachedDevice1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache)
-            .doesNotContain(cachedDevice2);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache)
-            .doesNotContain(cachedDevice3);
-
-        // Call onDeviceUnpaired for the one in mCachedDevices with no pair.
-        mCachedDeviceManager.onDeviceUnpaired(cachedDevice3);
-
-        // Check if no list is changed.
-        devices = mCachedDeviceManager.getCachedDevicesCopy();
-        assertThat(devices).contains(cachedDevice2);
-        assertThat(devices).contains(cachedDevice3);
-        assertThat(devices).doesNotContain(cachedDevice1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache)
-            .doesNotContain(cachedDevice2);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache)
-            .doesNotContain(cachedDevice3);
+        // Call onDeviceUnpaired for the one in mCachedDevices.
+        mCachedDeviceManager.onDeviceUnpaired(cachedDevice1);
+        verify(mDevice2).removeBond();
     }
 
     /**
-     * Test to verify addDevice() for hearing aid devices with same HiSyncId.
+     * Test to verify getSubDeviceSummary() for disconnected sub device
      */
     @Test
-    public void addDevice_hearingAidDevicesWithSameHiSyncId_populateInDifferentLists() {
-        doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager)
-            .getHearingAidProfile();
-        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
-        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
+    public void getSubDeviceSummary_SubDeviceDisconnected() {
+        when(mCachedDevice2.isConnected()).thenReturn(false);
+        mCachedDevice1.setSubDevice(mCachedDevice2);
+        mCachedDeviceManager.getSubDeviceSummary(mCachedDevice1);
 
-        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
-        assertThat(cachedDevice1).isNotNull();
-        // The first hearing aid device should be populated in mCachedDevice and
-        // mCachedDevicesMapForHearingAids.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
-            .contains(cachedDevice1);
-
-        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
-        assertThat(cachedDevice2).isNotNull();
-        // The second hearing aid device should be populated in mHearingAidDevicesNotAddedInCache.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).hasSize(1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
+        verify(mCachedDevice2, never()).getConnectionSummary();
     }
 
     /**
-     * Test to verify addDevice() for hearing aid devices with different HiSyncId.
+     * Test to verify getSubDeviceSummary() for connected sub device
      */
     @Test
-    public void addDevice_hearingAidDevicesWithDifferentHiSyncId_populateInSameList() {
-        doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager)
-            .getHearingAidProfile();
-        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
-        doAnswer((invocation) -> HISYNCID2).when(mHearingAidProfile).getHiSyncId(mDevice2);
-        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
-        assertThat(cachedDevice1).isNotNull();
-        // The first hearing aid device should be populated in mCachedDevice and
-        // mCachedDevicesMapForHearingAids.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
-            .contains(cachedDevice1);
+    public void getSubDeviceSummary_SubDeviceConnected() {
+        when(mCachedDevice2.isConnected()).thenReturn(true);
+        mCachedDevice1.setSubDevice(mCachedDevice2);
+        mCachedDeviceManager.getSubDeviceSummary(mCachedDevice1);
 
-        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
-        assertThat(cachedDevice2).isNotNull();
-        // The second hearing aid device should also be populated in mCachedDevice
-        // and mCachedDevicesMapForHearingAids as its not a pair of the first one.
-        assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
-        assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(2);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
-            .contains(cachedDevice1);
-        assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
-            .contains(cachedDevice2);
-    }
-
-    /**
-     * Test to verify getHearingAidPairDeviceSummary() for hearing aid devices with same HiSyncId.
-     */
-    @Test
-    public void getHearingAidPairDeviceSummary_bothHearingAidsPaired_returnsSummaryOfPair() {
-        mCachedDevice1.setHiSyncId(HISYNCID1);
-        mCachedDevice2.setHiSyncId(HISYNCID1);
-        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
-        mCachedDeviceManager.mHearingAidDevicesNotAddedInCache.add(mCachedDevice2);
-        doAnswer((invocation) -> DEVICE_SUMMARY_1).when(mCachedDevice1).getConnectionSummary();
-        doAnswer((invocation) -> DEVICE_SUMMARY_2).when(mCachedDevice2).getConnectionSummary();
-
-        assertThat(mCachedDeviceManager.getHearingAidPairDeviceSummary(mCachedDevice1))
-            .isEqualTo(DEVICE_SUMMARY_2);
-    }
-
-    /**
-     * Test to verify getHearingAidPairDeviceSummary() for hearing aid devices with different
-     * HiSyncId.
-     */
-    @Test
-    public void getHearingAidPairDeviceSummary_bothHearingAidsNotPaired_returnsNull() {
-        mCachedDevice1.setHiSyncId(HISYNCID1);
-        mCachedDevice2.setHiSyncId(HISYNCID2);
-        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
-        mCachedDeviceManager.mHearingAidDevicesNotAddedInCache.add(mCachedDevice2);
-        doAnswer((invocation) -> DEVICE_SUMMARY_1).when(mCachedDevice1).getConnectionSummary();
-        doAnswer((invocation) -> DEVICE_SUMMARY_2).when(mCachedDevice2).getConnectionSummary();
-
-        assertThat(mCachedDeviceManager.getHearingAidPairDeviceSummary(mCachedDevice1))
-            .isEqualTo(null);
+        verify(mCachedDevice2).getConnectionSummary();
     }
 
     /**
      * Test to verify updateHearingAidsDevices().
      */
     @Test
-    public void updateHearingAidDevices_hiSyncIdAvailable_setsHiSyncId() {
-        doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager)
-            .getHearingAidProfile();
-        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
-        doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
-        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
-        mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
-        mCachedDeviceManager.updateHearingAidsDevices(mLocalProfileManager);
+    public void updateHearingAidDevices_directToHearingAidDeviceManager() {
+        mHearingAidDeviceManager = spy(new HearingAidDeviceManager(mLocalBluetoothManager,
+                mCachedDeviceManager.mCachedDevices));
+        mCachedDeviceManager.mHearingAidDeviceManager = mHearingAidDeviceManager;
+        mCachedDeviceManager.updateHearingAidsDevices();
 
-        // Assert that the mCachedDevice1 and mCachedDevice2 have an updated HiSyncId.
-        assertThat(mCachedDevice1.getHiSyncId()).isEqualTo(HISYNCID1);
-        assertThat(mCachedDevice2.getHiSyncId()).isEqualTo(HISYNCID1);
+        verify(mHearingAidDeviceManager).updateHearingAidsDevices();
     }
 
     /**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index f6201dd..7f9a5af 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -48,6 +48,10 @@
     private final static String DEVICE_ALIAS = "TestAlias";
     private final static String DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF";
     private final static String DEVICE_ALIAS_NEW = "TestAliasNew";
+    private final static short RSSI_1 = 10;
+    private final static short RSSI_2 = 11;
+    private final static boolean JUSTDISCOVERED_1 = true;
+    private final static boolean JUSTDISCOVERED_2 = false;
     @Mock
     private LocalBluetoothProfileManager mProfileManager;
     @Mock
@@ -60,6 +64,8 @@
     private HearingAidProfile mHearingAidProfile;
     @Mock
     private BluetoothDevice mDevice;
+    @Mock
+    private BluetoothDevice mSubDevice;
     private CachedBluetoothDevice mCachedDevice;
     private ShadowAudioManager mShadowAudioManager;
     private Context mContext;
@@ -657,4 +663,39 @@
         doReturn(status).when(profile).getConnectionStatus(mDevice);
         mCachedDevice.onProfileStateChanged(profile, status);
     }
+
+    @Test
+    public void getSubDevice_setSubDevice() {
+        CachedBluetoothDevice subCachedDevice = new CachedBluetoothDevice(mContext, mProfileManager,
+                mSubDevice);
+        mCachedDevice.setSubDevice(subCachedDevice);
+
+        assertThat(mCachedDevice.getSubDevice()).isEqualTo(subCachedDevice);
+    }
+
+    @Test
+    public void switchSubDeviceContent() {
+        CachedBluetoothDevice subCachedDevice = new CachedBluetoothDevice(mContext, mProfileManager,
+                mSubDevice);
+        mCachedDevice.mRssi = RSSI_1;
+        mCachedDevice.mJustDiscovered = JUSTDISCOVERED_1;
+        subCachedDevice.mRssi = RSSI_2;
+        subCachedDevice.mJustDiscovered = JUSTDISCOVERED_2;
+        mCachedDevice.setSubDevice(subCachedDevice);
+
+        assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_1);
+        assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
+        assertThat(mCachedDevice.mDevice).isEqualTo(mDevice);
+        assertThat(subCachedDevice.mRssi).isEqualTo(RSSI_2);
+        assertThat(subCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
+        assertThat(subCachedDevice.mDevice).isEqualTo(mSubDevice);
+        mCachedDevice.switchSubDeviceContent();
+
+        assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_2);
+        assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
+        assertThat(mCachedDevice.mDevice).isEqualTo(mSubDevice);
+        assertThat(subCachedDevice.mRssi).isEqualTo(RSSI_1);
+        assertThat(subCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
+        assertThat(subCachedDevice.mDevice).isEqualTo(mDevice);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
new file mode 100644
index 0000000..cb1b12d
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2018 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.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class HearingAidDeviceManagerTest {
+    private final static long HISYNCID1 = 10;
+    private final static long HISYNCID2 = 11;
+    private final static String DEVICE_NAME_1 = "TestName_1";
+    private final static String DEVICE_NAME_2 = "TestName_2";
+    private final static String DEVICE_ALIAS_1 = "TestAlias_1";
+    private final static String DEVICE_ALIAS_2 = "TestAlias_2";
+    private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
+    private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
+    private final BluetoothClass DEVICE_CLASS =
+            new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
+    @Mock
+    private LocalBluetoothProfileManager mLocalProfileManager;
+    @Mock
+    private LocalBluetoothManager mLocalBluetoothManager;
+    @Mock
+    private BluetoothEventManager mBluetoothEventManager;
+    @Mock
+    private HearingAidProfile mHearingAidProfile;
+    @Mock
+    private BluetoothDevice mDevice1;
+    @Mock
+    private BluetoothDevice mDevice2;
+    private CachedBluetoothDevice mCachedDevice1;
+    private CachedBluetoothDevice mCachedDevice2;
+    private CachedBluetoothDeviceManager mCachedDeviceManager;
+    private HearingAidDeviceManager mHearingAidDeviceManager;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
+        when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
+        when(mDevice1.getName()).thenReturn(DEVICE_NAME_1);
+        when(mDevice2.getName()).thenReturn(DEVICE_NAME_2);
+        when(mDevice1.getAliasName()).thenReturn(DEVICE_ALIAS_1);
+        when(mDevice2.getAliasName()).thenReturn(DEVICE_ALIAS_2);
+        when(mDevice1.getBluetoothClass()).thenReturn(DEVICE_CLASS);
+        when(mDevice2.getBluetoothClass()).thenReturn(DEVICE_CLASS);
+        when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
+        when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
+        when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
+
+        mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
+        mHearingAidDeviceManager = spy(new HearingAidDeviceManager(mLocalBluetoothManager,
+                mCachedDeviceManager.mCachedDevices));
+        mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
+        mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
+    }
+
+    /**
+     * Test initHearingAidDeviceIfNeeded, a valid HiSyncId will be assigned
+     */
+    @Test
+    public void initHearingAidDeviceIfNeeded_validHiSyncId_verifyHiSyncId() {
+        when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
+
+        assertThat(mCachedDevice1.getHiSyncId()).isNotEqualTo(HISYNCID1);
+        mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1);
+
+        assertThat(mCachedDevice1.getHiSyncId()).isEqualTo(HISYNCID1);
+    }
+
+    /**
+     * Test initHearingAidDeviceIfNeeded, an invalid HiSyncId will not be assigned
+     */
+    @Test
+    public void initHearingAidDeviceIfNeeded_invalidHiSyncId_notToSetHiSyncId() {
+        when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(
+                BluetoothHearingAid.HI_SYNC_ID_INVALID);
+        mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1);
+
+        verify(mCachedDevice1, never()).setHiSyncId(anyLong());
+    }
+
+    /**
+     * Test setSubDeviceIfNeeded, a device with same HiSyncId will be set as sub device
+     */
+    @Test
+    public void setSubDeviceIfNeeded_sameHiSyncId_setSubDevice() {
+        mCachedDevice1.setHiSyncId(HISYNCID1);
+        mCachedDevice2.setHiSyncId(HISYNCID1);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mHearingAidDeviceManager.setSubDeviceIfNeeded(mCachedDevice2);
+
+        assertThat(mCachedDevice1.getSubDevice()).isEqualTo(mCachedDevice2);
+    }
+
+    /**
+     * Test setSubDeviceIfNeeded, a device with different HiSyncId will not be set as sub device
+     */
+    @Test
+    public void setSubDeviceIfNeeded_differentHiSyncId_notSetSubDevice() {
+        mCachedDevice1.setHiSyncId(HISYNCID1);
+        mCachedDevice2.setHiSyncId(HISYNCID2);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mHearingAidDeviceManager.setSubDeviceIfNeeded(mCachedDevice2);
+
+        assertThat(mCachedDevice1.getSubDevice()).isNull();
+    }
+
+    /**
+     * Test updateHearingAidsDevices, to link two devices with the same HiSyncId.
+     * When first paired devices is connected and second paired device is disconnected, first
+     * paired device would be set as main device and second device will be removed from
+     * CachedDevices list.
+     */
+    @Test
+    public void updateHearingAidsDevices_firstPairedDevicesConnected_verifySubDevice() {
+        when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HISYNCID1);
+        when(mCachedDevice1.isConnected()).thenReturn(true);
+        when(mCachedDevice2.isConnected()).thenReturn(false);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
+
+        assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isTrue();
+        assertThat(mCachedDevice1.getSubDevice()).isNull();
+        mHearingAidDeviceManager.updateHearingAidsDevices();
+
+        assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isFalse();
+        assertThat(mCachedDevice1.getSubDevice()).isEqualTo(mCachedDevice2);
+    }
+
+    /**
+     * Test updateHearingAidsDevices, to link two devices with the same HiSyncId.
+     * When second paired devices is connected and first paired device is disconnected, second
+     * paired device would be set as main device and first device will be removed from
+     * CachedDevices list.
+     */
+    @Test
+    public void updateHearingAidsDevices_secondPairedDeviceConnected_verifySubDevice() {
+        when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HISYNCID1);
+        when(mCachedDevice1.isConnected()).thenReturn(false);
+        when(mCachedDevice2.isConnected()).thenReturn(true);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
+
+        assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice1)).isTrue();
+        assertThat(mCachedDevice2.getSubDevice()).isNull();
+        mHearingAidDeviceManager.updateHearingAidsDevices();
+
+        assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice1)).isFalse();
+        assertThat(mCachedDevice2.getSubDevice()).isEqualTo(mCachedDevice1);
+    }
+
+    /**
+     * Test updateHearingAidsDevices, to link two devices with the same HiSyncId.
+     * When both devices are connected, to build up main and sub relationship and to remove sub
+     * device from CachedDevices list.
+     */
+    @Test
+    public void updateHearingAidsDevices_BothConnected_verifySubDevice() {
+        when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HISYNCID1);
+        when(mCachedDevice1.isConnected()).thenReturn(true);
+        when(mCachedDevice2.isConnected()).thenReturn(true);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
+
+        assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isTrue();
+        assertThat(mCachedDevice1.getSubDevice()).isNull();
+        mHearingAidDeviceManager.updateHearingAidsDevices();
+
+        assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isFalse();
+        assertThat(mCachedDevice1.getSubDevice()).isEqualTo(mCachedDevice2);
+    }
+
+    /**
+     * Test updateHearingAidsDevices, dispatch callback
+     */
+    @Test
+    public void updateHearingAidsDevices_dispatchDeviceRemovedCallback() {
+        when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
+        when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HISYNCID1);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
+        mHearingAidDeviceManager.updateHearingAidsDevices();
+
+        verify(mBluetoothEventManager).dispatchDeviceRemoved(mCachedDevice1);
+    }
+
+    /**
+     * Test updateHearingAidsDevices, do nothing when HiSyncId is invalid
+     */
+    @Test
+    public void updateHearingAidsDevices_invalidHiSyncId_doNothing() {
+        when(mHearingAidProfile.getHiSyncId(mDevice1)).
+                thenReturn(BluetoothHearingAid.HI_SYNC_ID_INVALID);
+        when(mHearingAidProfile.getHiSyncId(mDevice2)).
+                thenReturn(BluetoothHearingAid.HI_SYNC_ID_INVALID);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
+        mHearingAidDeviceManager.updateHearingAidsDevices();
+
+        verify(mHearingAidDeviceManager, never()).onHiSyncIdChanged(anyLong());
+    }
+
+    /**
+     * Test onProfileConnectionStateChangedIfProcessed.
+     * When first hearing aid device is connected, to process it same as other generic devices.
+     * No need to process it.
+     */
+    @Test
+    public void onProfileConnectionStateChanged_connected_singleDevice_returnFalse() {
+        when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
+
+        assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
+                mCachedDevice1, BluetoothProfile.STATE_CONNECTED)).isFalse();
+    }
+
+    /**
+     * Test onProfileConnectionStateChangedIfProcessed.
+     * When a new hearing aid device is connected, to set it as sub device by onHiSyncIdChanged().
+     * And, to verify new device is not in CachedDevices list.
+     */
+    @Test
+    public void onProfileConnectionStateChanged_connected_newDevice_verifySubDevice() {
+        when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice1.isConnected()).thenReturn(true);
+        when(mCachedDevice2.isConnected()).thenReturn(true);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
+
+        assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isTrue();
+        assertThat(mCachedDevice1.getSubDevice()).isNull();
+        mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice1,
+                BluetoothProfile.STATE_CONNECTED);
+
+        assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isFalse();
+        assertThat(mCachedDevice1.getSubDevice()).isEqualTo(mCachedDevice2);
+        verify(mHearingAidDeviceManager).onHiSyncIdChanged(anyLong());
+    }
+
+    /**
+     * Test onProfileConnectionStateChangedIfProcessed.
+     * When sub device is disconnected, do nothing and return False for main device connected event
+     */
+    @Test
+    public void
+    onProfileConnectionStateChanged_connected_mainDevice_subDeviceDisconnected_returnFalse() {
+        when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice2.isConnected()).thenReturn(false);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDevice1.setSubDevice(mCachedDevice2);
+
+        assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
+                mCachedDevice1, BluetoothProfile.STATE_CONNECTED)).isFalse();
+        verify(mHearingAidDeviceManager).onHiSyncIdChanged(anyLong());
+    }
+
+    /**
+     * Test onProfileConnectionStateChangedIfProcessed.
+     * When main device is connected, do main device refresh() for sub device connected event
+     */
+    @Test
+    public void
+    onProfileConnectionStateChanged_connected_subDevice_mainDeviceConnected_verifyRefresh() {
+        when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice1.isConnected()).thenReturn(true);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDevice1.setSubDevice(mCachedDevice2);
+
+        assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
+                mCachedDevice2, BluetoothProfile.STATE_CONNECTED)).isTrue();
+        verify(mHearingAidDeviceManager).onHiSyncIdChanged(anyLong());
+        verify(mCachedDevice1).refresh();
+    }
+
+    /**
+     * Test onProfileConnectionStateChangedIfProcessed.
+     * When main device is disconnected, to verify switch() result for sub device connected
+     * event
+     */
+    @Test
+    public void onProfileConnectionStateChanged_connected_subDevice_mainDeviceDisconnected_switch()
+    {
+        when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice1.isConnected()).thenReturn(false);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDevice1.setSubDevice(mCachedDevice2);
+
+        assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1);
+        assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2);
+        assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
+                mCachedDevice2, BluetoothProfile.STATE_CONNECTED)).isTrue();
+
+        assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
+        assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
+        verify(mHearingAidDeviceManager).onHiSyncIdChanged(anyLong());
+        verify(mCachedDevice1).refresh();
+    }
+
+    /**
+     * Test onProfileConnectionStateChangedIfProcessed.
+     * When sub device is connected, to verify switch() result for main device disconnected
+     * event
+     */
+    @Test
+    public void onProfileConnectionStateChanged_disconnected_mainDevice_subDeviceConnected_switch()
+    {
+        when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice2.isConnected()).thenReturn(true);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDevice1.setSubDevice(mCachedDevice2);
+
+        assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1);
+        assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2);
+        assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
+                mCachedDevice1, BluetoothProfile.STATE_DISCONNECTED)).isTrue();
+
+        assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
+        assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
+        verify(mCachedDevice1).refresh();
+    }
+
+    /**
+     * Test onProfileConnectionStateChangedIfProcessed.
+     * When sub device is disconnected, do nothing and return False for main device disconnected
+     * event
+     */
+    @Test
+    public void
+    onProfileConnectionStateChanged_disconnected_mainDevice_subDeviceDisconnected_returnFalse() {
+        when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice2.isConnected()).thenReturn(false);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDevice1.setSubDevice(mCachedDevice2);
+
+        assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
+                mCachedDevice1, BluetoothProfile.STATE_DISCONNECTED)).isFalse();
+    }
+
+    /**
+     * Test onProfileConnectionStateChangedIfProcessed.
+     * Refresh main device UI for sub device disconnected event
+     */
+    @Test
+    public void onProfileConnectionStateChanged_disconnected_subDevice_verifyRefresh() {
+        when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDevice1.setSubDevice(mCachedDevice2);
+
+        assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
+                mCachedDevice2, BluetoothProfile.STATE_DISCONNECTED)).isTrue();
+        verify(mCachedDevice1).refresh();
+    }
+
+    @Test
+    public void findMainDevice() {
+        when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
+        when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDevice1.setSubDevice(mCachedDevice2);
+
+        assertThat(mHearingAidDeviceManager.findMainDevice(mCachedDevice2)).
+                isEqualTo(mCachedDevice1);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index 6f4c292..a3c3a54 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -55,6 +55,8 @@
 @RunWith(SettingsLibRobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothAdapter.class})
 public class LocalBluetoothProfileManagerTest {
+    private final static long HISYNCID = 10;
+
     @Mock
     private CachedBluetoothDeviceManager mDeviceManager;
     @Mock
@@ -76,9 +78,10 @@
         mContext = spy(RuntimeEnvironment.application);
         mLocalBluetoothAdapter = LocalBluetoothAdapter.getInstance();
         mEventManager = spy(new BluetoothEventManager(mLocalBluetoothAdapter, mDeviceManager,
-                mContext, null));
+                mContext, /* handler= */ null, /* userHandle= */ null));
         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         when(mDeviceManager.findDevice(mDevice)).thenReturn(mCachedBluetoothDevice);
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mDevice);
         mProfileManager = new LocalBluetoothProfileManager(mContext, mLocalBluetoothAdapter,
                 mDeviceManager, mEventManager);
     }
@@ -186,13 +189,14 @@
 
     /**
      * Verify BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED with uuid intent will dispatch to
-     * profile connection state changed callback
+     * CachedBluetoothDeviceManager method
      */
     @Test
-    public void stateChangedHandler_receiveHAPConnectionStateChanged_shouldDispatchCallback() {
+    public void stateChangedHandler_receiveHAPConnectionStateChanged_shouldDispatchDeviceManager() {
         mShadowBluetoothAdapter.setSupportedProfiles(generateList(
                 new int[] {BluetoothProfile.HEARING_AID}));
         mProfileManager.updateLocalProfiles();
+        when(mCachedBluetoothDevice.getHiSyncId()).thenReturn(HISYNCID);
 
         mIntent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
         mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
@@ -201,8 +205,8 @@
 
         mContext.sendBroadcast(mIntent);
 
-        verify(mEventManager).dispatchProfileConnectionStateChanged(mCachedBluetoothDevice,
-                BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
+        verify(mDeviceManager).onProfileConnectionStateChangedIfProcessed(mCachedBluetoothDevice,
+                BluetoothProfile.STATE_CONNECTED);
     }
 
     /**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
index a15f5fc..a0fa6b5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
@@ -18,11 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.os.UserManager;
 import android.content.Context;
 import android.provider.Settings;
 
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
-import com.android.settingslib.testutils.shadow.ShadowUserManager;
+
+import org.robolectric.shadows.ShadowUserManager;
+import org.robolectric.shadow.api.Shadow;
 
 import org.junit.After;
 import org.junit.Before;
@@ -32,20 +35,16 @@
 import org.robolectric.annotation.Config;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(shadows = ShadowUserManager.class)
 public class DevelopmentSettingsEnablerTest {
 
     private Context mContext;
+    private ShadowUserManager mUserManager;
 
     @Before
     public void setUp() {
         mContext = RuntimeEnvironment.application;
-        ShadowUserManager.getShadow().setIsAdminUser(true);
-    }
-
-    @After
-    public void tearDown() {
-        ShadowUserManager.getShadow().reset();
+        mUserManager = Shadow.extract(mContext.getSystemService(UserManager.class));
+        mUserManager.setIsAdminUser(true);
     }
 
     @Test
@@ -76,7 +75,7 @@
     public void isEnabled_settingsOn_noRestriction_notAdmin_shouldReturnFalse() {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
-        ShadowUserManager.getShadow().setIsAdminUser(false);
+        mUserManager.setIsAdminUser(false);
 
         assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
index 2a608399..d7b23b0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -39,6 +39,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.shadows.ShadowApplication;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
@@ -59,7 +60,7 @@
         MockitoAnnotations.initMocks(this);
         ShadowApplication shadowContext = ShadowApplication.getInstance();
         shadowContext.setSystemService(Context.USER_SERVICE, mUserManager);
-        mContext = spy(shadowContext.getApplicationContext());
+        mContext = spy(RuntimeEnvironment.application);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         mController = new ConcreteEnableAdbPreferenceController(mContext);
         mPreference = new SwitchPreference(mContext);
@@ -85,7 +86,6 @@
         assertThat(mPreference.isVisible()).isTrue();
     }
 
-
     @Test
     public void resetPreference_shouldUncheck() {
         when(mUserManager.isAdminUser()).thenReturn(true);
@@ -100,14 +100,14 @@
     @Test
     public void handlePreferenceTreeClick_shouldUpdateSettings() {
         when(mUserManager.isAdminUser()).thenReturn(true);
-        Settings.Secure.putInt(mContext.getContentResolver(),
+        Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.ADB_ENABLED, 1);
         mPreference.setChecked(true);
         mController.displayPreference(mScreen);
 
         mController.handlePreferenceTreeClick(mPreference);
 
-        assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
+        assertThat(Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.ADB_ENABLED, 0)).isEqualTo(0);
     }
 
@@ -126,7 +126,7 @@
     @Test
     public void updateState_settingsOn_shouldCheck() {
         when(mUserManager.isAdminUser()).thenReturn(true);
-        Settings.Secure.putInt(mContext.getContentResolver(),
+        Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.ADB_ENABLED, 1);
         mPreference.setChecked(false);
         mController.displayPreference(mScreen);
@@ -139,7 +139,7 @@
     @Test
     public void updateState_settingsOff_shouldUncheck() {
         when(mUserManager.isAdminUser()).thenReturn(true);
-        Settings.Secure.putInt(mContext.getContentResolver(),
+        Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.ADB_ENABLED, 0);
         mPreference.setChecked(true);
         mController.displayPreference(mScreen);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
index de96af4..d0b6dab 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
@@ -158,16 +158,16 @@
 
     @Test
     public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() throws Exception {
-        Global.putString(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, "null");
+        Global.putInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
 
         BatterySaverUtils.ensureAutoBatterySaver(mMockContext, BATTERY_SAVER_THRESHOLD_1);
 
-        assertThat(Secure.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
+        assertThat(Global.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
                 .isEqualTo(BATTERY_SAVER_THRESHOLD_1);
 
         // Once a positive number is set, ensureAutoBatterySaver() won't overwrite it.
         BatterySaverUtils.ensureAutoBatterySaver(mMockContext, BATTERY_SAVER_THRESHOLD_2);
-        assertThat(Secure.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
+        assertThat(Global.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
                 .isEqualTo(BATTERY_SAVER_THRESHOLD_1);
     }
 
@@ -182,8 +182,8 @@
         assertThat(Secure.getInt(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, -1))
                 .isEqualTo(-1); // not set.
 
-        BatterySaverUtils.setAutoBatterySaverTriggerLevel(mMockContext, BATTERY_SAVER_THRESHOLD_1 );
-        assertThat( Global.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
+        BatterySaverUtils.setAutoBatterySaverTriggerLevel(mMockContext, BATTERY_SAVER_THRESHOLD_1);
+        assertThat(Global.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
                 .isEqualTo(BATTERY_SAVER_THRESHOLD_1);
         assertThat(Secure.getInt(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, -1))
                 .isEqualTo(1);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
index 37d4d1d..5dbb5ca 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
@@ -89,10 +89,9 @@
         BluetoothDeviceLayerDrawable twinDrawable =
                 (BluetoothDeviceLayerDrawable) drawable.getConstantState().newDrawable();
 
-        assertThat(twinDrawable.getDrawable(0)).isEqualTo(drawable.getDrawable(0));
-        assertThat(twinDrawable.getDrawable(1)).isEqualTo(drawable.getDrawable(1));
-        assertThat(twinDrawable.getLayerInsetTop(1)).isEqualTo(
-                drawable.getLayerInsetTop(1));
+        assertThat(twinDrawable.getDrawable(0)).isNotNull();
+        assertThat(twinDrawable.getDrawable(1)).isNotNull();
+        assertThat(twinDrawable.getLayerInsetTop(1)).isEqualTo(drawable.getLayerInsetTop(1));
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
index c32cc99..c90de5f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
@@ -112,7 +112,6 @@
     @Implements(LicenseHtmlLoaderCompat.class)
     public static class ShadowLicenseHtmlLoaderCompat {
 
-
         public static List<File> sValidXmlFiles;
         public static File sCachedHtmlFile;
         public static boolean sIsCachedHtmlFileOutdated;
@@ -127,22 +126,24 @@
         }
 
         @Implementation
-        static List<File> getVaildXmlFiles() {
+        protected List<File> getVaildXmlFiles() {
             return sValidXmlFiles;
         }
 
         @Implementation
-        static File getCachedHtmlFile(Context context) {
+        protected File getCachedHtmlFile(Context context) {
             return sCachedHtmlFile;
         }
 
         @Implementation
-        static boolean isCachedHtmlFileOutdated(List<File> xmlFiles, File cachedHtmlFile) {
+        protected boolean isCachedHtmlFileOutdated(List<File> xmlFiles,
+                File cachedHtmlFile) {
             return sIsCachedHtmlFileOutdated;
         }
 
         @Implementation
-        static boolean generateHtmlFile(Context context, List<File> xmlFiles, File htmlFile) {
+        protected boolean generateHtmlFile(Context context, List<File> xmlFiles,
+                File htmlFile) {
             return sGenerateHtmlFileSucceeded;
         }
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/DrawableTestHelper.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/DrawableTestHelper.java
new file mode 100644
index 0000000..ad8de77
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/DrawableTestHelper.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 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.settingslib.testutils;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.drawable.Drawable;
+
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowDrawable;
+
+public class DrawableTestHelper {
+    public static void assertDrawableResId(Drawable drawable, int resId) {
+        final ShadowDrawable shadow = Shadows.shadowOf(drawable);
+        assertThat(shadow.getCreatedFromResId()).isEqualTo(resId);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
index 9b8c230..cae74c8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
@@ -25,7 +25,7 @@
 
 import java.util.List;
 
-@Implements(value = BluetoothAdapter.class, inheritImplementationMethods = true)
+@Implements(value = BluetoothAdapter.class)
 public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBluetoothAdapter {
 
     private List<Integer> mSupportedProfiles;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
index bbd3a53..a81e395 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.testutils.shadow;
 
+import android.annotation.UserIdInt;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.UserManager;
@@ -29,37 +30,21 @@
 import java.util.ArrayList;
 import java.util.List;
 
-@Implements(value = UserManager.class, inheritImplementationMethods = true)
+@Implements(value = UserManager.class)
 public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
 
-    private boolean mAdminUser;
-
-    public void setIsAdminUser(boolean isAdminUser) {
-        mAdminUser = isAdminUser;
-    }
-
-    @Resetter
-    public void reset() {
-        mAdminUser = false;
-    }
-
     @Implementation
-    public boolean isAdminUser() {
-        return mAdminUser;
-    }
-
-    @Implementation
-    public static UserManager get(Context context) {
+    protected static UserManager get(Context context) {
         return (UserManager) context.getSystemService(Context.USER_SERVICE);
     }
 
     @Implementation
-    public int[] getProfileIdsWithDisabled(int userId) {
-        return new int[] { 0 };
+    protected int[] getProfileIdsWithDisabled(int userId) {
+        return new int[]{0};
     }
 
     @Implementation
-    public List<UserInfo> getProfiles() {
+    protected List<UserInfo> getProfiles() {
         UserInfo userInfo = new UserInfo();
         userInfo.id = 0;
         List<UserInfo> userInfos = new ArrayList<>();
@@ -67,8 +52,9 @@
         return userInfos;
     }
 
-    public static ShadowUserManager getShadow() {
-        return (ShadowUserManager) Shadow.extract(
-                RuntimeEnvironment.application.getSystemService(UserManager.class));
+    @Implementation
+    protected List<UserInfo> getProfiles(@UserIdInt int userHandle) {
+        return getProfiles();
     }
-}
\ No newline at end of file
+
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
index f56c111..e030005 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
@@ -37,7 +37,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
 public class FooterPreferenceMixinCompatTest {
@@ -58,7 +58,7 @@
         mLifecycle = new Lifecycle(mLifecycleOwner);
         when(mFragment.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
         when(mFragment.getPreferenceManager().getContext())
-                .thenReturn(ShadowApplication.getInstance().getApplicationContext());
+                .thenReturn(RuntimeEnvironment.application);
         mMixin = new FooterPreferenceMixinCompat(mFragment, mLifecycle);
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
index 366b720..8817ff7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
@@ -37,6 +37,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.shadows.ShadowApplication;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
@@ -58,7 +59,7 @@
         mLifecycle = new Lifecycle(mLifecycleOwner);
         when(mFragment.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
         when(mFragment.getPreferenceManager().getContext())
-                .thenReturn(ShadowApplication.getInstance().getApplicationContext());
+                .thenReturn(RuntimeEnvironment.application);
         mMixin = new FooterPreferenceMixin(mFragment, mLifecycle);
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index 84a043e..e0eceb4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -31,7 +31,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
 public class FooterPreferenceTest {
@@ -40,7 +40,7 @@
 
     @Before
     public void setUp() {
-        mContext = ShadowApplication.getInstance().getApplicationContext();
+        mContext = RuntimeEnvironment.application;
     }
 
     @Test
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index c996620..ca0267c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -672,6 +672,9 @@
         dumpSetting(s, p,
                 Settings.Global.GPU_DEBUG_LAYER_APP,
                 GlobalSettingsProto.Gpu.DEBUG_LAYER_APP);
+        dumpSetting(s, p,
+                Settings.Global.GPU_DEBUG_LAYERS_GLES,
+                GlobalSettingsProto.Gpu.DEBUG_LAYERS_GLES);
         p.end(gpuToken);
 
         final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
@@ -1117,6 +1120,9 @@
         dumpSetting(s, p,
                 Settings.Global.SHOW_FIRST_CRASH_DIALOG,
                 GlobalSettingsProto.SHOW_FIRST_CRASH_DIALOG);
+        dumpSetting(s, p,
+                Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED,
+                GlobalSettingsProto.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED);
         // Settings.Global.SHOW_PROCESSES intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
@@ -1124,6 +1130,9 @@
         dumpSetting(s, p,
                 Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG,
                 GlobalSettingsProto.SHOW_MUTE_IN_CRASH_DIALOG);
+        dumpSetting(s, p,
+                Settings.Global.SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED,
+                GlobalSettingsProto.SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED);
 
         final long smartSelectToken = p.start(GlobalSettingsProto.SMART_SELECTION);
         dumpSetting(s, p,
@@ -1634,11 +1643,11 @@
                 Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
                 SecureSettingsProto.Accessibility.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES);
         dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED,
-                SecureSettingsProto.Accessibility.MINIMUM_UI_TIMEOUT_ENABLED);
+                Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS,
+                SecureSettingsProto.Accessibility.NON_INTERACTIVE_UI_TIMEOUT_MS);
         dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS,
-                SecureSettingsProto.Accessibility.MINIMUM_UI_TIMEOUT_MS);
+                Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS,
+                SecureSettingsProto.Accessibility.INTERACTIVE_UI_TIMEOUT_MS);
         p.end(accessibilityToken);
 
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index a6fadf9..379cfc7 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -124,7 +124,7 @@
 
         @Override
         public int onCommand(String cmd) {
-            if (cmd == null) {
+            if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) {
                 return handleDefaultCommands(cmd);
             }
 
diff --git a/packages/Shell/res/values-mr/strings.xml b/packages/Shell/res/values-mr/strings.xml
index aae8493..dc6a6126 100644
--- a/packages/Shell/res/values-mr/strings.xml
+++ b/packages/Shell/res/values-mr/strings.xml
@@ -23,12 +23,12 @@
     <string name="bugreport_updating_title" msgid="4423539949559634214">"दोष अहवालामध्‍ये तपशील जोडत आहे"</string>
     <string name="bugreport_updating_wait" msgid="3322151947853929470">"कृपया प्रतीक्षा करा..."</string>
     <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"फोनवर बग रीपोर्ट लवकरच दिसेल"</string>
-    <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"आपला बग रीपोर्ट शेअर करण्यासाठी निवडा"</string>
-    <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"आपला बग रीपोर्ट शेअर करण्यासाठी टॅप करा"</string>
-    <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"आपला बग रीपोर्ट स्क्रीनशॉटशिवाय शेअर करण्यासाठी टॅप करा किंवा स्क्रीनशॉट पूर्ण होण्याची प्रतीक्षा करा"</string>
-    <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय आपला बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
-    <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय आपला बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
-    <string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अॅपवर आपला विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string>
+    <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"तुमचा बग रीपोर्ट शेअर करण्यासाठी निवडा"</string>
+    <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा"</string>
+    <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"तुमचा बग रीपोर्ट स्क्रीनशॉटशिवाय शेअर करण्यासाठी टॅप करा किंवा स्क्रीनशॉट पूर्ण होण्याची प्रतीक्षा करा"</string>
+    <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
+    <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
+    <string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अॅपवर तुमचा विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string>
     <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"पुन्हा दर्शवू नका"</string>
     <string name="bugreport_storage_title" msgid="5332488144740527109">"बग रीपोर्ट"</string>
     <string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रीपोर्ट फाईल वाचणे शक्य झाले नाही"</string>
diff --git a/packages/SimAppDialog/res/drawable/illo_sim_app_dialog.xml b/packages/SimAppDialog/res/drawable/illo_sim_app_dialog.xml
new file mode 100644
index 0000000..8dd88b4
--- /dev/null
+++ b/packages/SimAppDialog/res/drawable/illo_sim_app_dialog.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2018 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.
+-->
+<!-- Empty drawable as this is not displayed by default. Must be provided by resource overlay. -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
index 5bcce4d..12f9bb6 100644
--- a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
@@ -37,6 +37,22 @@
             android:text="@string/install_carrier_app_description_default"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"/>
-    </LinearLayout>
+
+        <com.android.setupwizardlib.view.FillContentLayout
+            android:id="@+id/illo_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:visibility="gone">
+
+            <ImageView
+                android:src="@drawable/illo_sim_app_dialog"
+                style="@style/SuwContentIllustration"
+                android:contentDescription="@string/install_carrier_app_image_content_description"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"/>
+
+        </com.android.setupwizardlib.view.FillContentLayout>
+</LinearLayout>
 
 </com.android.setupwizardlib.GlifLayout>
diff --git a/packages/SimAppDialog/res/values/bools.xml b/packages/SimAppDialog/res/values/bools.xml
new file mode 100644
index 0000000..4953d5e
--- /dev/null
+++ b/packages/SimAppDialog/res/values/bools.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2018 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!--
+         Whether to show an illustration on the screen asking the user to download the carrier app.
+         May be set to true in a resource overlay as long as a drawable asset with ID
+         illo_sim_app_dialog is provided, along with a content description for accessibility with
+         string ID install_carrier_app_image_content_description.
+    -->
+    <bool name="show_sim_app_dialog_illo">false</bool>
+</resources>
diff --git a/packages/SimAppDialog/res/values/strings.xml b/packages/SimAppDialog/res/values/strings.xml
index 87941cb..9e8359c 100644
--- a/packages/SimAppDialog/res/values/strings.xml
+++ b/packages/SimAppDialog/res/values/strings.xml
@@ -32,4 +32,6 @@
     <string name="install_carrier_app_defer_action">Not now</string>
     <!-- Name of the button for downloading the carrier app [CHAR LIMIT=25] -->
     <string name="install_carrier_app_download_action">Download app</string>
-</resources>
\ No newline at end of file
+    <!-- Empty placeholder string for an illustration content description that is supplied via resource overlay. [DO NOT TRANSLATE] -->
+    <string name="install_carrier_app_image_content_description" translatable="false" />
+</resources>
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
index 9e9b80d..8e8d9f7 100644
--- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -65,6 +65,11 @@
         Button downloadButton = findViewById(R.id.download_button);
         downloadButton.setOnClickListener(this);
 
+        // Show/hide illo depending on whether one was provided in a resource overlay
+        boolean showIllo = getResources().getBoolean(R.bool.show_sim_app_dialog_illo);
+        View illoContainer = findViewById(R.id.illo_container);
+        illoContainer.setVisibility(showIllo ? View.VISIBLE : View.GONE);
+
         // Include carrier name in description text if its present in the intent
         Intent intent = getIntent();
         if (intent != null) {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 9c33116..a00baad 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -66,7 +66,7 @@
     libs: [
         "telephony-common",
         "android.car",
-        "android.car.user",
+        "android.car.userlib",
     ],
 
     aaptflags: [
@@ -120,7 +120,7 @@
         "android.test.runner",
         "telephony-common",
         "android.car",
-        "android.car.user",
+        "android.car.userlib",
         "android.test.base",
     ],
     aaptflags: [
@@ -146,7 +146,7 @@
     libs: [
         "telephony-common",
         "android.car",
-        "android.car.user",
+        "android.car.userlib",
     ],
 
     dxflags: ["--multi-dex"],
@@ -156,3 +156,43 @@
     ],
 
 }
+
+// Only used for products that are shipping legacy Recents
+android_app {
+    name: "SystemUIWithLegacyRecents",
+    overrides: [
+        "SystemUI",
+    ],
+
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+
+    dxflags: ["--multi-dex"],
+    aaptflags: [
+        "--extra-packages",
+        "com.android.keyguard",
+    ],
+    optimize: {
+        proguard_flags_files: ["proguard.flags", "legacy/recents/proguard.flags"],
+    },
+
+    static_libs: [
+        "SystemUI-core",
+    ],
+    libs: [
+        "telephony-common",
+        "android.car",
+        "android.car.userlib",
+    ],
+
+    srcs: [
+        "legacy/recents/src/**/*.java",
+        "legacy/recents/src/**/I*.aidl",
+    ],
+    resource_dirs: [
+        "legacy/recents/res",
+    ],
+
+    manifest: "legacy/recents/AndroidManifest.xml",
+}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index fef9ae8..b2bb883 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -128,9 +128,6 @@
     <!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
     <uses-permission android:name="android.permission.SET_WALLPAPER"/>
 
-    <!-- Recents -->
-    <uses-permission android:name="android.permission.BIND_APPWIDGET" />
-
     <!-- Wifi Display -->
     <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
 
@@ -139,6 +136,10 @@
     <!-- Screen Capturing -->
     <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" />
 
+    <!-- Screen Recording -->
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
     <!-- Assist -->
     <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
 
@@ -215,6 +216,9 @@
     <!-- Permission necessary to change car audio volume through CarAudioManager -->
     <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
 
+    <!-- Permission to control Android Debug Bridge (ADB) -->
+    <uses-permission android:name="android.permission.MANAGE_DEBUGGING" />
+
     <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
 
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
@@ -247,14 +251,14 @@
             android:exported="true"
         />
 
-        <!-- Recents depends on every user having their own SystemUI process, so on user switch,
-             ensure that the process is created by starting this service.
+        <!-- On user switch, this service is started to ensure that the associated SystemUI
+             process for the current user is started. See the resource
+             "config_systemUIServiceComponentsPerUser".
              -->
         <service android:name="SystemUISecondaryUserService"
-            android:exported="true"
+            android:exported="false"
             android:permission="com.android.systemui.permission.SELF" />
 
-
         <!-- started from PhoneWindowManager
              TODO: Should have an android:permission attribute -->
         <service android:name=".screenshot.TakeScreenshotService"
@@ -270,6 +274,10 @@
             </intent-filter>
         </receiver>
 
+        <activity android:name=".screenrecord.ScreenRecordDialog"
+            android:theme="@style/ScreenRecord" />
+        <service android:name=".screenrecord.RecordingService" />
+
         <receiver android:name=".SysuiRestartReceiver"
             android:exported="false">
             <intent-filter>
@@ -313,27 +321,6 @@
             </intent-filter>
         </activity-alias>
 
-        <!-- Service used by secondary users to register themselves with the system user. -->
-        <service android:name=".recents.RecentsSystemUserService"
-            android:exported="false"
-            android:permission="com.android.systemui.permission.SELF" />
-
-        <!-- Alternate Recents -->
-        <activity android:name=".recents.RecentsActivity"
-                  android:label="@string/accessibility_desc_recent_apps"
-                  android:exported="false"
-                  android:launchMode="singleInstance"
-                  android:excludeFromRecents="true"
-                  android:stateNotNeeded="true"
-                  android:resumeWhilePausing="true"
-                  android:resizeableActivity="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
-                  android:theme="@style/RecentsTheme.Wallpaper">
-            <intent-filter>
-                <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
-            </intent-filter>
-        </activity>
-
         <activity
             android:name=".stackdivider.ForcedResizableInfoActivity"
             android:theme="@style/ForcedResizableTheme"
@@ -392,9 +379,9 @@
             android:excludeFromRecents="true">
         </activity>
 
-        <!-- started from UsbDebuggingManager -->
+        <!-- started from AdbDebuggingManager -->
         <activity android:name=".usb.UsbDebuggingActivity"
-            android:permission="android.permission.MANAGE_USB"
+            android:permission="android.permission.MANAGE_DEBUGGING"
             android:theme="@style/Theme.SystemUI.Dialog.Alert"
             android:finishOnCloseSystemDialogs="true"
             android:excludeFromRecents="true">
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 47f2cdc..2f6e32b 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -6,20 +6,36 @@
 asc@google.com
 ashaikh@google.com
 beverlyt@google.com
+brockman@google.com
 cinek@google.com
 cwren@google.com
 dupin@google.com
+ethibodeau@google.com
 evanlaird@google.com
 jmonk@google.com
 jaggies@google.com
 jjaggi@google.com
+joshmcgrath@google.com
 juliacr@google.com
+juliatuttle@google.com
 kchyn@google.com
+kozynski@google.com
+kprevas@google.com
 madym@google.com
+mankoff@google.com
+nbenbernou@google.com
+nesciosquid@google.com
 ngmatthew@google.com
+ogunwale@google.com
+pixel@google.com
 roosa@google.com
 shahrk@google.com
+snoeberger@google.com
+steell@google.com
+stwu@google.com
 sunnygoyal@google.com
+susikp@google.com
+tsuji@google.com
 twickham@google.com
 winsonc@google.com
 
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
index 33c5551..a8ce196 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -164,7 +164,7 @@
 
 ### [com.android.systemui.biometrics.BiometricDialogImpl](/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java)
 
-Fingerprint UI.
+Biometric UI.
 
 ---
 
diff --git a/packages/SystemUI/legacy/recents/AndroidManifest.xml b/packages/SystemUI/legacy/recents/AndroidManifest.xml
new file mode 100644
index 0000000..0d8b3cd
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2018 Google Inc.
+ *
+ * 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"
+          xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.android.systemui"
+          android:sharedUserId="android.uid.systemui"
+          coreApp="true">
+
+    <application
+        android:name="com.android.systemui.SystemUIApplication">
+
+        <!-- Service used by secondary users to register themselves with the system user. -->
+        <service android:name=".recents.RecentsSystemUserService"
+            android:exported="false"
+            android:permission="com.android.systemui.permission.SELF" />
+
+        <!-- Alternate Recents -->
+        <activity android:name=".recents.RecentsActivity"
+                  android:label="@string/accessibility_desc_recent_apps"
+                  android:exported="false"
+                  android:launchMode="singleInstance"
+                  android:excludeFromRecents="true"
+                  android:stateNotNeeded="true"
+                  android:resumeWhilePausing="true"
+                  android:resizeableActivity="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
+                  android:theme="@style/RecentsTheme.Wallpaper">
+            <intent-filter>
+                <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
+            </intent-filter>
+        </activity>
+
+    </application>
+</manifest>
diff --git a/packages/SystemUI/legacy/recents/proguard.flags b/packages/SystemUI/legacy/recents/proguard.flags
new file mode 100644
index 0000000..c358949
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/proguard.flags
@@ -0,0 +1,14 @@
+-keepclassmembers class ** {
+    public void onBusEvent(**);
+    public void onInterprocessBusEvent(**);
+}
+-keepclassmembers class ** extends **.EventBus$InterprocessEvent {
+    public <init>(android.os.Bundle);
+}
+
+-keep class com.android.systemui.recents.views.TaskView {
+    public int getDim();
+    public void setDim(int);
+    public float getTaskProgress();
+    public void setTaskProgress(float);
+}
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/recents_fast_toggle_app_home_exit.xml b/packages/SystemUI/legacy/recents/res/anim/recents_fast_toggle_app_home_exit.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_fast_toggle_app_home_exit.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_fast_toggle_app_home_exit.xml
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml b/packages/SystemUI/legacy/recents/res/anim/recents_from_launcher_enter.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_from_launcher_enter.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_from_launcher_enter.xml
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml b/packages/SystemUI/legacy/recents/res/anim/recents_from_launcher_exit.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_from_launcher_exit.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_from_launcher_exit.xml
diff --git a/packages/SystemUI/res/anim/recents_from_unknown_enter.xml b/packages/SystemUI/legacy/recents/res/anim/recents_from_unknown_enter.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_from_unknown_enter.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_from_unknown_enter.xml
diff --git a/packages/SystemUI/res/anim/recents_from_unknown_exit.xml b/packages/SystemUI/legacy/recents/res/anim/recents_from_unknown_exit.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_from_unknown_exit.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_from_unknown_exit.xml
diff --git a/packages/SystemUI/res/anim/recents_launch_next_affiliated_task_bounce.xml b/packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_bounce.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_launch_next_affiliated_task_bounce.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_bounce.xml
diff --git a/packages/SystemUI/res/anim/recents_launch_next_affiliated_task_source.xml b/packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_source.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_launch_next_affiliated_task_source.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_source.xml
diff --git a/packages/SystemUI/res/anim/recents_launch_next_affiliated_task_target.xml b/packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_target.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_launch_next_affiliated_task_target.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_target.xml
diff --git a/packages/SystemUI/res/anim/recents_launch_prev_affiliated_task_bounce.xml b/packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_bounce.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_launch_prev_affiliated_task_bounce.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_bounce.xml
diff --git a/packages/SystemUI/res/anim/recents_launch_prev_affiliated_task_source.xml b/packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_source.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_launch_prev_affiliated_task_source.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_source.xml
diff --git a/packages/SystemUI/res/anim/recents_launch_prev_affiliated_task_target.xml b/packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_target.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_launch_prev_affiliated_task_target.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_target.xml
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml b/packages/SystemUI/legacy/recents/res/anim/recents_to_launcher_enter.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_to_launcher_enter.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_to_launcher_enter.xml
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml b/packages/SystemUI/legacy/recents/res/anim/recents_to_launcher_exit.xml
similarity index 100%
rename from packages/SystemUI/res/anim/recents_to_launcher_exit.xml
rename to packages/SystemUI/legacy/recents/res/anim/recents_to_launcher_exit.xml
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-hdpi/recents_lower_gradient.9.png
similarity index 100%
rename from packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
rename to packages/SystemUI/legacy/recents/res/drawable-hdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-hdpi/recents_status_gradient.9.png
similarity index 100%
rename from packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png
rename to packages/SystemUI/legacy/recents/res/drawable-hdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-mdpi/recents_lower_gradient.9.png
similarity index 100%
rename from packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
rename to packages/SystemUI/legacy/recents/res/drawable-mdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-mdpi/recents_status_gradient.9.png
similarity index 100%
rename from packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png
rename to packages/SystemUI/legacy/recents/res/drawable-mdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-xhdpi/recents_lower_gradient.9.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
rename to packages/SystemUI/legacy/recents/res/drawable-xhdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-xhdpi/recents_status_gradient.9.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png
rename to packages/SystemUI/legacy/recents/res/drawable-xhdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_lower_gradient.9.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
rename to packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_status_gradient.9.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png
rename to packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_task_shadow.9.png b/packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_task_shadow.9.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xxhdpi/recents_task_shadow.9.png
rename to packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_task_shadow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_lock_to_app_24dp.xml b/packages/SystemUI/legacy/recents/res/drawable/ic_lock_to_app_24dp.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_lock_to_app_24dp.xml
rename to packages/SystemUI/legacy/recents/res/drawable/ic_lock_to_app_24dp.xml
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_dismiss_dark.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_dismiss_dark.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_dismiss_dark.xml
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_light.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_dismiss_light.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_dismiss_light.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_dismiss_light.xml
diff --git a/packages/SystemUI/res/drawable/recents_empty.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_empty.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_empty.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_empty.xml
diff --git a/packages/SystemUI/res/drawable/recents_freeform_workspace_bg.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_freeform_workspace_bg.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_freeform_workspace_bg.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_freeform_workspace_bg.xml
diff --git a/packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_grid_task_view_focus_frame_background.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_grid_task_view_focus_frame_background.xml
diff --git a/packages/SystemUI/res/drawable/recents_info_dark.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_info_dark.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_info_dark.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_info_dark.xml
diff --git a/packages/SystemUI/res/drawable/recents_info_light.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_info_light.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_info_light.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_info_light.xml
diff --git a/packages/SystemUI/res/drawable/recents_lock_to_app_pin.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_lock_to_app_pin.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_lock_to_app_pin.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_lock_to_app_pin.xml
diff --git a/packages/SystemUI/res/drawable/recents_lock_to_task_button_bg.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_lock_to_task_button_bg.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_lock_to_task_button_bg.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_lock_to_task_button_bg.xml
diff --git a/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_low_ram_stack_button_background.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_low_ram_stack_button_background.xml
diff --git a/packages/SystemUI/res/drawable/recents_move_task_freeform_dark.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_freeform_dark.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_move_task_freeform_dark.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_move_task_freeform_dark.xml
diff --git a/packages/SystemUI/res/drawable/recents_move_task_freeform_light.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_freeform_light.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_move_task_freeform_light.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_move_task_freeform_light.xml
diff --git a/packages/SystemUI/res/drawable/recents_move_task_fullscreen_dark.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_fullscreen_dark.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_move_task_fullscreen_dark.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_move_task_fullscreen_dark.xml
diff --git a/packages/SystemUI/res/drawable/recents_move_task_fullscreen_light.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_fullscreen_light.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_move_task_fullscreen_light.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_move_task_fullscreen_light.xml
diff --git a/packages/SystemUI/res/drawable/recents_stack_action_background.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_stack_action_background.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/recents_stack_action_background.xml
rename to packages/SystemUI/legacy/recents/res/drawable/recents_stack_action_background.xml
diff --git a/packages/SystemUI/res/interpolator/recents_from_launcher_exit_interpolator.xml b/packages/SystemUI/legacy/recents/res/interpolator/recents_from_launcher_exit_interpolator.xml
similarity index 100%
rename from packages/SystemUI/res/interpolator/recents_from_launcher_exit_interpolator.xml
rename to packages/SystemUI/legacy/recents/res/interpolator/recents_from_launcher_exit_interpolator.xml
diff --git a/packages/SystemUI/res/interpolator/recents_launch_next_affiliated_task_bounce_scale.xml b/packages/SystemUI/legacy/recents/res/interpolator/recents_launch_next_affiliated_task_bounce_scale.xml
similarity index 100%
rename from packages/SystemUI/res/interpolator/recents_launch_next_affiliated_task_bounce_scale.xml
rename to packages/SystemUI/legacy/recents/res/interpolator/recents_launch_next_affiliated_task_bounce_scale.xml
diff --git a/packages/SystemUI/res/interpolator/recents_launch_prev_affiliated_task_bounce_ydelta.xml b/packages/SystemUI/legacy/recents/res/interpolator/recents_launch_prev_affiliated_task_bounce_ydelta.xml
similarity index 100%
rename from packages/SystemUI/res/interpolator/recents_launch_prev_affiliated_task_bounce_ydelta.xml
rename to packages/SystemUI/legacy/recents/res/interpolator/recents_launch_prev_affiliated_task_bounce_ydelta.xml
diff --git a/packages/SystemUI/res/interpolator/recents_to_launcher_enter_interpolator.xml b/packages/SystemUI/legacy/recents/res/interpolator/recents_to_launcher_enter_interpolator.xml
similarity index 100%
rename from packages/SystemUI/res/interpolator/recents_to_launcher_enter_interpolator.xml
rename to packages/SystemUI/legacy/recents/res/interpolator/recents_to_launcher_enter_interpolator.xml
diff --git a/packages/SystemUI/res/layout/recents.xml b/packages/SystemUI/legacy/recents/res/layout/recents.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents.xml
diff --git a/packages/SystemUI/res/layout/recents_empty.xml b/packages/SystemUI/legacy/recents/res/layout/recents_empty.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents_empty.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents_empty.xml
diff --git a/packages/SystemUI/res/layout/recents_grid_task_view.xml b/packages/SystemUI/legacy/recents/res/layout/recents_grid_task_view.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents_grid_task_view.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents_grid_task_view.xml
diff --git a/packages/SystemUI/res/layout/recents_incompatible_app_overlay.xml b/packages/SystemUI/legacy/recents/res/layout/recents_incompatible_app_overlay.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents_incompatible_app_overlay.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents_incompatible_app_overlay.xml
diff --git a/packages/SystemUI/res/layout/recents_low_ram_stack_action_button.xml b/packages/SystemUI/legacy/recents/res/layout/recents_low_ram_stack_action_button.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents_low_ram_stack_action_button.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents_low_ram_stack_action_button.xml
diff --git a/packages/SystemUI/res/layout/recents_search_bar.xml b/packages/SystemUI/legacy/recents/res/layout/recents_search_bar.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents_search_bar.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents_search_bar.xml
diff --git a/packages/SystemUI/res/layout/recents_stack_action_button.xml b/packages/SystemUI/legacy/recents/res/layout/recents_stack_action_button.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents_stack_action_button.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents_stack_action_button.xml
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/legacy/recents/res/layout/recents_task_view.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents_task_view.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents_task_view.xml
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/legacy/recents/res/layout/recents_task_view_header.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents_task_view_header.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents_task_view_header.xml
diff --git a/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml b/packages/SystemUI/legacy/recents/res/layout/recents_task_view_header_overlay.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents_task_view_header_overlay.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents_task_view_header_overlay.xml
diff --git a/packages/SystemUI/res/layout/recents_task_view_header_progress_bar.xml b/packages/SystemUI/legacy/recents/res/layout/recents_task_view_header_progress_bar.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents_task_view_header_progress_bar.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents_task_view_header_progress_bar.xml
diff --git a/packages/SystemUI/res/layout/recents_task_view_incompatible_app_toast.xml b/packages/SystemUI/legacy/recents/res/layout/recents_task_view_incompatible_app_toast.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents_task_view_incompatible_app_toast.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents_task_view_incompatible_app_toast.xml
diff --git a/packages/SystemUI/res/layout/recents_task_view_lock_to_app.xml b/packages/SystemUI/legacy/recents/res/layout/recents_task_view_lock_to_app.xml
similarity index 100%
rename from packages/SystemUI/res/layout/recents_task_view_lock_to_app.xml
rename to packages/SystemUI/legacy/recents/res/layout/recents_task_view_lock_to_app.xml
diff --git a/packages/SystemUI/legacy/recents/res/values-af/strings.xml b/packages/SystemUI/legacy/recents/res/values-af/strings.xml
new file mode 100644
index 0000000..736c810
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-af/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Oorsig."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Maak <xliff:g id="APP">%s</xliff:g> toe."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> is toegemaak."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Alle onlangse programme is toegemaak."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Maak <xliff:g id="APP">%s</xliff:g>-programinligting oop."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Begin tans <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Geen onlangse items nie"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Jy het alles toegemaak"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Programinligting"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"skermvaspen"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"soek"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Kon nie <xliff:g id="APP">%s</xliff:g> begin nie."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> is in veiligmodus gedeaktiveer."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Vee alles uit"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Sleep hierheen om verdeelde skerm te gebruik"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Verdeel horisontaal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Verdeel vertikaal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Verdeel gepasmaak"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Verdeel skerm na bo"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Verdeel skerm na links"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Verdeel skerm na regs"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-am/strings.xml b/packages/SystemUI/legacy/recents/res/values-am/strings.xml
new file mode 100644
index 0000000..2870be7
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-am/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"አጠቃላይ እይታ።"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> አስወግድ።"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ተሰናብቷል።"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ሁሉም የቅርብ ጊዜ ማመልከቻዎች ተሰናብተዋል።"</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"የ<xliff:g id="APP">%s</xliff:g> መተግበሪያ መረጃውን ይክፈቱ።"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> በመጀመር ላይ።"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"ሁሉንም ነገር አጽድተዋል"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"የመተግበሪያ መረጃ"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ማያ ገጽ መሰካት"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"ፈልግ"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>ን መጀመር አልተቻለም።"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> በጥንቃቄ ሁነታ ውስጥ ታግዷል።"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ሁሉንም አጽዳ"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"የተከፈለ ማያ ገጽን ለመጠቀም እዚህ ላይ ይጎትቱ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"አግድም ክፈል"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ቁልቁል ክፈል"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"በብጁ ክፈል"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ማያ ገጽ ወደ ላይ ክፈል"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"ማያ ገጽ ወደ ግራ ክፈል"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"ማያ ገጽ ወደ ቀኝ ክፈል"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ar/strings.xml b/packages/SystemUI/legacy/recents/res/values-ar/strings.xml
new file mode 100644
index 0000000..004de41
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-ar/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"النظرة عامة"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"إزالة <xliff:g id="APP">%s</xliff:g>"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"تمَّت إزالة <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"تمَّت إزالة كل التطبيقات المستخدمة مؤخرًا."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"فتح معلومات تطبيق <xliff:g id="APP">%s</xliff:g>"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"جارٍ بدء <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"ليست هناك عناصر تم استخدامها مؤخرًا"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"لقد محوتَ كل شيء"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"معلومات التطبيق"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"تثبيت الشاشة"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"بحث"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"تعذَّر بدء <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"تم إيقاف <xliff:g id="APP">%s</xliff:g> في الوضع الآمن."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"محو الكل"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"اسحب هنا لاستخدام وضع تقسيم الشاشة"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"تقسيم أفقي"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"تقسيم رأسي"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"تقسيم مخصَّص"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"تقسيم الشاشة بمحاذاة الجزء العلوي"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"تقسيم الشاشة بمحاذاة اليسار"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"تقسيم الشاشة بمحاذاة اليمين"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-az/strings.xml b/packages/SystemUI/legacy/recents/res/values-az/strings.xml
new file mode 100644
index 0000000..76ae02a
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-az/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"İcmal."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> tətbiqini silin."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> silindi."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Bütün son tətbiqlər silindi."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> tətbiq məlumatını açın."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> başladılır."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Ən son element yoxdur"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Hər şeyi sildiniz"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Tətbiq məlumatı"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ekran sancağı"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"axtarış"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> başladılmadı."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> təhlükəsiz rejimdə deaktiv edildi."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Hamısını silin"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Bölünmüş ekrandan istifadə etmək üçün bura sürüşdürün"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Horizontal Bölün"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Vertikal Bölün"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Fərdi Bölün"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Ekranı yuxarıya doğru bölün"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Ekranı sola doğru bölün"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Ekranı sağa doğru bölün"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/legacy/recents/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..3117eea
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Pregled."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Odbacite aplikaciju <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplikacija <xliff:g id="APP">%s</xliff:g> je odbačena."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Sve nedavno korišćene aplikacije su odbačene."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Otvorite informacije o aplikaciji <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Pokreće se <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Nema nedavnih stavki"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Obrisali ste sve"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informacije o aplikaciji"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"kačenje ekrana"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"pretraži"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Pokretanje aplikacije <xliff:g id="APP">%s</xliff:g> nije uspelo."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikacija <xliff:g id="APP">%s</xliff:g> je onemogućena u bezbednom režimu."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Obriši sve"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Prevucite ovde da biste koristili razdeljeni ekran"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Podeli horizontalno"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Podeli vertikalno"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Podeli prilagođeno"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Podeli ekran nagore"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Podeli ekran nalevo"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Podeli ekran nadesno"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-be/strings.xml b/packages/SystemUI/legacy/recents/res/values-be/strings.xml
new file mode 100644
index 0000000..8121846
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-be/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Агляд."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Закрыць праграму \"<xliff:g id="APP">%s</xliff:g>\"."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Праграма \"<xliff:g id="APP">%s</xliff:g>\" закрыта."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Усе нядаўнія праграмы закрыты."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Адкрыць інфармацыю пра праграму \"<xliff:g id="APP">%s</xliff:g>\"."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Запускаецца праграма \"<xliff:g id="APP">%s</xliff:g>\"."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Няма нядаўніх элементаў"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Вы ўсё выдалілі"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Інфармацыя пра праграму"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"замацаванне экрана"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"пошук"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Не ўдалося запусціць праграму \"<xliff:g id="APP">%s</xliff:g>\"."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Праграма \"<xliff:g id="APP">%s</xliff:g>\" адключана ў бяспечным рэжыме."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Ачысціць усё"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Перацягніце сюды, каб перайсці ў рэжым падзеленага экрана"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Падзяліць гарызантальна"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Падзяліць вертыкальна"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Падзяліць іншым чынам"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Падзяліць экран зверху"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Падзяліць экран злева"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Падзяліць экран справа"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-bg/strings.xml b/packages/SystemUI/legacy/recents/res/values-bg/strings.xml
new file mode 100644
index 0000000..3dda34f
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-bg/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Общ преглед."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Отхвърляне на <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Приложението <xliff:g id="APP">%s</xliff:g> е отхвърлено."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Всички скорошни приложения са отхвърлени."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Отворете информацията за приложението <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> се стартира."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Няма скорошни елементи"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Изчистихте всичко"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Информация за приложението"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"фиксиране на екрана"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"търсене"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> не можа да стартира."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Приложението <xliff:g id="APP">%s</xliff:g> е деактивирано в безопасния режим."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Изчистване на всичко"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Преместете тук с плъзгане, за да използвате режим за разделен екран"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Хоризонтално разделяне"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Вертикално разделяне"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Персонализирано разделяне"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Разделяне на екрана нагоре"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Разделяне на екрана наляво"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Разделяне на екрана надясно"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-bs/strings.xml b/packages/SystemUI/legacy/recents/res/values-bs/strings.xml
new file mode 100644
index 0000000..8e149ba8
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-bs/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Pregled."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Odbaci aplikaciju <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplikacija <xliff:g id="APP">%s</xliff:g> je odbačena."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Sve nedavno korištene aplikacije su odbačene."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Otvaranje informacija o aplikaciji <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Pokretanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Nema nedavnih stavki"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Sve ste obrisali"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informacije o aplikaciji"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"kačenje ekrana"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"pretraži"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Aplikacija <xliff:g id="APP">%s</xliff:g> nije pokrenuta."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikacija <xliff:g id="APP">%s</xliff:g> je onemogućena u sigurnom načinu rada."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Obriši sve"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Povucite ovdje za korištenje podijeljenog ekrana"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Podjela po horizontali"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Podjela po vertikali"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Prilagođena podjela"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Dijeli ekran nagore"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Dijeli ekran nalijevo"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Dijeli ekran nadesno"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ca/strings.xml b/packages/SystemUI/legacy/recents/res/values-ca/strings.xml
new file mode 100644
index 0000000..fff525c
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-ca/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Aplicacions recents."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Ignora <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"S\'ha ignorat <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"S\'han ignorat totes les aplicacions recents."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Obre la informació sobre l\'aplicació <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"S\'està iniciant <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"No hi ha cap element recent"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Ho has esborrat tot"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informació de l\'aplicació"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"fixació de pantalla"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"cerca"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"No s\'ha pogut iniciar <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"En mode segur, l\'aplicació <xliff:g id="APP">%s</xliff:g> està desactivada."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Esborra-ho tot"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Arrossega-ho aquí per utilitzar la pantalla dividida"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Divisió horitzontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Divisió vertical"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Divisió personalitzada"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Divideix la pantalla cap amunt"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Divideix la pantalla cap a l\'esquerra"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Divideix la pantalla cap a la dreta"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-cs/strings.xml b/packages/SystemUI/legacy/recents/res/values-cs/strings.xml
new file mode 100644
index 0000000..200f7a8
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-cs/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Přehled"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Zavřít aplikaci <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplikace <xliff:g id="APP">%s</xliff:g> byla odebrána."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Všechny naposledy použité aplikace byly odstraněny."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Otevře informace o aplikaci <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Spouštění aplikace <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Žádné nedávné položky"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Vše je vymazáno"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informace o aplikaci"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"připnutí obrazovky"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"hledat"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Aplikaci <xliff:g id="APP">%s</xliff:g> nelze spustit."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikace <xliff:g id="APP">%s</xliff:g> je v nouzovém režimu zakázána."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Vymazat vše"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Přetáhnutím sem aktivujete rozdělenou obrazovku"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Vodorovné rozdělení"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Svislé rozdělení"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Vlastní rozdělení"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Rozdělit obrazovku nahoru"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Rozdělit obrazovku vlevo"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Rozdělit obrazovku vpravo"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-da/strings.xml b/packages/SystemUI/legacy/recents/res/values-da/strings.xml
new file mode 100644
index 0000000..0a1690e
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-da/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Oversigt."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Fjern <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> er fjernet."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Alle de seneste apps er fjernet."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Åbn appoplysningerne for <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> åbnes."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Ingen nye elementer"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Du har ryddet alt"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Appoplysninger"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"skærmfastholdelse"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"søg"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> kunne ikke åbnes."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> er deaktiveret i sikker tilstand."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Ryd alle"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Træk hertil for at bruge opdelt skærm"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Opdel vandret"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Opdel lodret"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Opdel brugerdefineret"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Opdelt skærm øverst"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Opdelt skærm til venstre"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Opdelt skærm til højre"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-de/strings.xml b/packages/SystemUI/legacy/recents/res/values-de/strings.xml
new file mode 100644
index 0000000..97a6366
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-de/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Übersicht."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> entfernen."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> wurde entfernt."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Alle kürzlich verwendeten Apps wurden entfernt."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Infos zur <xliff:g id="APP">%s</xliff:g> App öffnen."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> wird gestartet."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Keine kürzlich verwendeten Elemente"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Du hast alles gelöscht"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"App-Info"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"Bildschirmfixierung"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"Suchen"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> konnte nicht gestartet werden."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ist im abgesicherten Modus deaktiviert."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Alle löschen"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Hierher ziehen, um den Bildschirm zu teilen"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Geteilt – horizontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Geteilt – vertikal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Geteilt – benutzerdefiniert"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Geteilten Bildschirm oben anzeigen"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Geteilten Bildschirm links anzeigen"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Geteilten Bildschirm rechts anzeigen"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-el/strings.xml b/packages/SystemUI/legacy/recents/res/values-el/strings.xml
new file mode 100644
index 0000000..90baf52
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-el/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Επισκόπηση."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Παράβλεψη εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> απορρίφθηκε."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Όλες οι πρόσφατες εφαρμογές παραβλέφθηκαν."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Άνοιγμα πληροφοριών εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Έναρξη εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Διαγράψατε όλα τα στοιχεία"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Πληροφορίες εφαρμογής"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"καρφίτσωμα οθόνης"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"αναζήτηση"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Δεν ήταν δυνατή η έναρξη της εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> έχει απενεργοποιηθεί στην ασφαλή λειτουργία."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Διαγραφή όλων"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Σύρετε εδώ για να χρησιμοποιήσετε τον διαχωρισμό οθόνης"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Οριζόντιος διαχωρισμός"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Κάθετος διαχωρισμός"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Προσαρμοσμένος διαχωρισμός"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Διαχωρισμός οθόνης στην κορυφή"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Διαχωρισμός οθόνης στα αριστερά"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Διαχωρισμός οθόνης στα δεξιά"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-en-rAU/strings.xml b/packages/SystemUI/legacy/recents/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..af1d055
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-en-rAU/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Overview."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"All recent applications dismissed."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"No recent items"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"You\'ve cleared everything"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Application Info"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"screen pinning"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"search"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Clear all"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Drag here to use split screen"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Split Horizontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Split Vertical"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Split Custom"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Split screen to the top"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Split screen to the left"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Split screen to the right"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-en-rCA/strings.xml b/packages/SystemUI/legacy/recents/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..af1d055
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-en-rCA/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Overview."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"All recent applications dismissed."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"No recent items"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"You\'ve cleared everything"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Application Info"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"screen pinning"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"search"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Clear all"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Drag here to use split screen"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Split Horizontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Split Vertical"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Split Custom"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Split screen to the top"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Split screen to the left"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Split screen to the right"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-en-rGB/strings.xml b/packages/SystemUI/legacy/recents/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..af1d055
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-en-rGB/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Overview."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"All recent applications dismissed."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"No recent items"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"You\'ve cleared everything"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Application Info"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"screen pinning"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"search"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Clear all"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Drag here to use split screen"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Split Horizontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Split Vertical"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Split Custom"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Split screen to the top"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Split screen to the left"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Split screen to the right"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-en-rIN/strings.xml b/packages/SystemUI/legacy/recents/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..af1d055
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-en-rIN/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Overview."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"All recent applications dismissed."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"No recent items"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"You\'ve cleared everything"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Application Info"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"screen pinning"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"search"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Clear all"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Drag here to use split screen"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Split Horizontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Split Vertical"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Split Custom"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Split screen to the top"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Split screen to the left"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Split screen to the right"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-en-rXC/strings.xml b/packages/SystemUI/legacy/recents/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..67477e9
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-en-rXC/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎Overview.‎‏‎‎‏‎"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‎‎‎‏‎‎‎‏‏‎‏‏‏‎‎Dismiss ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ dismissed.‎‏‎‎‏‎"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎All recent applications dismissed.‎‏‎‎‏‎"</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎Open ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ application info.‎‏‎‎‏‎"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‏‏‏‎Starting ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎No recent items‎‏‎‎‏‎"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‎You\'ve cleared everything‎‏‎‎‏‎"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‏‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‎‎‎‏‎‏‎‎Application Info‎‏‎‎‏‎"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‎‏‎‏‎‎‏‏‎‏‎‏‎screen pinning‎‏‎‎‏‎"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎search‎‏‎‎‏‎"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‎Could not start ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ is disabled in safe-mode.‎‏‎‎‏‎"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎Clear all‎‏‎‎‏‎"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‏‎‎‏‎‎‎‎Drag here to use split screen‎‏‎‎‏‎"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‎Split Horizontal‎‏‎‎‏‎"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‏‏‎‏‏‎‏‏‏‎‎‏‎‎‏‏‎‏‎Split Vertical‎‏‎‎‏‎"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‏‏‎Split Custom‎‏‎‎‏‎"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‎‎‏‏‏‎‎‎Split screen to the top‎‏‎‎‏‎"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎Split screen to the left‎‏‎‎‏‎"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‏‎‎Split screen to the right‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-es-rUS/strings.xml b/packages/SystemUI/legacy/recents/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..8bf3807
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-es-rUS/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Recientes"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Permite descartar <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> descartada"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Se descartaron todas las aplicaciones recientes."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Permite abrir la información de aplicación de <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Iniciando <xliff:g id="APP">%s</xliff:g>"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"No hay elementos recientes"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Todo borrado"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Información de la aplicación"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"Fijar pantalla"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"Buscar"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"No se pudo iniciar <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> está inhabilitada en modo seguro."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Borrar todo"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Arrastra hasta aquí para usar la pantalla dividida"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"División horizontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"División vertical"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"División personalizada"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Dividir pantalla en la parte superior"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Dividir pantalla a la izquierda"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Dividir pantalla a la derecha"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-es/strings.xml b/packages/SystemUI/legacy/recents/res/values-es/strings.xml
new file mode 100644
index 0000000..b70f318
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-es/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Aplicaciones recientes."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Ignorar la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Se ha ignorado la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Se han ignorado todas las aplicaciones recientes."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Abre la información de la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Iniciando la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"No hay elementos recientes"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Has borrado todo"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Información de la aplicación"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"bloqueo de pantalla"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"buscar"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"No se ha podido iniciar la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"La aplicación <xliff:g id="APP">%s</xliff:g> se ha inhabilitado en modo seguro."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Borrar todo"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Arrastra el elemento hasta aquí para utilizar la pantalla dividida"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"División horizontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"División vertical"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"División personalizada"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Dividir la pantalla en la parte superior"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Dividir la pantalla a la izquierda"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Dividir la pantalla a la derecha"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-et/strings.xml b/packages/SystemUI/legacy/recents/res/values-et/strings.xml
new file mode 100644
index 0000000..c1903af
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-et/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Ülevaade."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Rakendusest <xliff:g id="APP">%s</xliff:g> loobumine."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Rakendusest <xliff:g id="APP">%s</xliff:g> on loobutud."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Kõikidest hiljutistest rakendustest on loobutud."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Rakenduse <xliff:g id="APP">%s</xliff:g> teabe avamine."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Rakenduse <xliff:g id="APP">%s</xliff:g> käivitamine."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Hiljutisi üksusi pole"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Olete kõik ära kustutanud"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Rakenduse teave"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ekraanikuva kinnitamine"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"otsi"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Rakendust <xliff:g id="APP">%s</xliff:g> ei saanud käivitada."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Rakendus <xliff:g id="APP">%s</xliff:g> on turvarežiimis keelatud."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Kustuta kõik"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Jagatud ekraani kasutamiseks lohistage siia"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Horisontaalne poolitamine"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Vertikaalne poolitamine"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Kohandatud poolitamine"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Poolita ekraan üles"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Poolita ekraan vasakule"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Poolita ekraan paremale"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-eu/strings.xml b/packages/SystemUI/legacy/recents/res/values-eu/strings.xml
new file mode 100644
index 0000000..91e250f
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-eu/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Ikuspegi orokorra."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Baztertu <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Baztertu da <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Baztertu dira azken aplikazio guztiak."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Ireki <xliff:g id="APP">%s</xliff:g> aplikazioari buruzko informazioa."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> abiarazten."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Ez dago azkenaldi honetako ezer"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Dena garbitu duzu"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Aplikazioaren informazioa"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"pantaila-ainguratzea"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"bilatu"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Ezin izan da abiarazi <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> desgaituta dago modu seguruan."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Garbitu guztiak"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Arrastatu hona pantaila zatitzeko"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Zatitze horizontala"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Zatitze bertikala"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Zatitze pertsonalizatua"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Zatitu pantaila eta ezarri goian"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Zatitu pantaila eta ezarri ezkerrean"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Zatitu pantaila eta ezarri eskuinean"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-fa/strings.xml b/packages/SystemUI/legacy/recents/res/values-fa/strings.xml
new file mode 100644
index 0000000..61e87c1
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-fa/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"نمای کلی."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"رد کردن <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> نادیده گرفته شد."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"همه برنامه‌های اخیر رد شدند."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"باز کردن اطلاعات برنامه <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> درحال شروع به کار است."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"بدون موارد اخیر"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"همه‌چیز را پاک کرده‌اید"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"اطلاعات برنامه"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"پین کردن صفحه"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"جستجو"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> شروع نشد."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> در حالت ایمن غیرفعال است."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"پاک کردن همه"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"برای استفاده از تقسیم صفحه، به اینجا بکشید"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"تقسیم افقی"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"تقسیم عمودی"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"سفارشی کردن تقسیم"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"تقسیم کردن صفحه به بالا"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"تقسیم کردن صفحه به چپ"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"تقسیم کردن صفحه به راست"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-fi/strings.xml b/packages/SystemUI/legacy/recents/res/values-fi/strings.xml
new file mode 100644
index 0000000..bf2e461
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-fi/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Viimeisimmät"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Hylkää <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> hylättiin."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Kaikki viimeisimmät sovellukset on hylätty."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Avaa sovelluksen <xliff:g id="APP">%s</xliff:g> tiedot."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Käynnistetään <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Ei viimeaikaisia kohteita"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Kaikki on hoidettu"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Sovellustiedot"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"näytön kiinnitys"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"haku"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ei käynnistynyt."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> on poistettu käytöstä vikasietotilassa."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Poista kaikki"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Jaa näyttö vetämällä tähän."</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Vaakasuuntainen jako"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Pystysuuntainen jako"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Oma jako"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Jaa näyttö ylös"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Jaa näyttö vasemmalle"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Jaa näyttö oikealle"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-fr-rCA/strings.xml b/packages/SystemUI/legacy/recents/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..e60727e
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-fr-rCA/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Aperçu"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Supprimer <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> supprimée."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Toutes les applications récentes ont été supprimées."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Ouvre les détails de l\'application <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Lancement de <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Aucun élément récent"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Vous avez tout effacé"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Détails de l\'application"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"épinglage d\'écran"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"rechercher"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> est désactivée en mode sans échec."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Tout effacer"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Glissez l\'élément ici pour utiliser l\'écran partagé"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Séparation horizontale"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Séparation verticale"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Séparation personnalisée"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Écran partagé dans le haut"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Écran partagé à la gauche"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Écran partagé à la droite"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-fr/strings.xml b/packages/SystemUI/legacy/recents/res/values-fr/strings.xml
new file mode 100644
index 0000000..183b6be
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-fr/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Aperçu"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Supprimer l\'application <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Application <xliff:g id="APP">%s</xliff:g> supprimée."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Toutes les applications récentes ont été supprimées."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Ouvre les informations sur l\'application <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Lancement de l\'application <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Aucun élément récent"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Vous avez tout effacé"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informations sur l\'application"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"épinglage d\'écran"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"rechercher"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Impossible de lancer l\'application <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"L\'application <xliff:g id="APP">%s</xliff:g> est désactivée en mode sécurisé."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Tout effacer"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Faire glisser ici pour utiliser l\'écran partagé"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Séparation horizontale"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Séparation verticale"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Séparation personnalisée"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Partager l\'écran en haut"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Partager l\'écran sur la gauche"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Partager l\'écran sur la droite"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-gl/strings.xml b/packages/SystemUI/legacy/recents/res/values-gl/strings.xml
new file mode 100644
index 0000000..008c776
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-gl/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Visión xeral."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Rexeita <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Rexeitouse <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Rexeitáronse todas as aplicacións recentes."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Abre a información da aplicación <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Non hai elementos recentes"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Borraches todo"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Información da aplicación"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"fixación de pantalla"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"buscar"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Non se puido iniciar <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"A aplicación <xliff:g id="APP">%s</xliff:g> está desactivada no modo seguro."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Borrar todo"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Arrastra aquí para usar a pantalla dividida"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Dividir horizontalmente"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Dividir verticalmente"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Dividir de xeito personalizado"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Dividir pantalla arriba"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Dividir pantalla á esquerda"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Dividir pantalla á dereita"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-gu/strings.xml b/packages/SystemUI/legacy/recents/res/values-gu/strings.xml
new file mode 100644
index 0000000..33dc7e8
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-gu/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ઝલક."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> કાઢી નાખો."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> કાઢી નાખી."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"તાજેતરની બધી ઍપ્લિકેશનો કાઢી નાખવામાં આવી."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g>ની ઍપ્લિકેશન માહિતી ખોલો."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g>ને શરૂ કરી રહ્યાં છીએ."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"તાજેતરની કોઈ આઇટમ નથી"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"તમે બધું સાફ કર્યું"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"ઍપ્લિકેશનની માહિતી"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"સ્ક્રીન પિનિંગ"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"શોધો"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>ને શરૂ કરી શકાઈ નથી."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"સુરક્ષિત મોડમાં <xliff:g id="APP">%s</xliff:g>ને બંધ કરવામાં આવી છે."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"બધું સાફ કરો"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરવા માટે અહીં ખેંચો"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"સ્ક્રીનને આડી વિભાજિત કરો"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"સ્ક્રીનને ઊભી વિભાજિત કરો"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"સ્ક્રીનને કસ્ટમ વિભાજિત કરો"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"સ્ક્રીનને ઉપરની તરફ વિભાજિત કરો"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"સ્ક્રીનને ડાબી તરફ વિભાજિત કરો"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"સ્ક્રીનને જમણી તરફ વિભાજિત કરો"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-hi/strings.xml b/packages/SystemUI/legacy/recents/res/values-hi/strings.xml
new file mode 100644
index 0000000..c9ac2a0
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-hi/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"खास जानकारी."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> को खारिज करें."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> खारिज किया गया."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"हाल के सभी ऐप्लिकेशन खारिज कर दिए गए हैं."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ऐप्लिकेशन की जानकारी खोलें."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> शुरू किया जा रहा है."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"हाल का कोई आइटम नहीं है"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"आपने सब कुछ हटा दिया है"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"ऐप्लिकेशन की जानकारी"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"स्क्रीन पिन करना"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"खोजें"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> शुरू नहीं किया जा सका."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> को सुरक्षित-मोड में बंद किया गया."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"सभी ऐप्लिकेशन बंद करें"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"स्क्रीन को दो हिस्सों में बाँटने (स्प्लिट स्क्रीन) के लिए यहां से खींचें और छोड़ें"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"क्षैतिज रूप से दो हिस्सों में बाँटें (स्प्लिट करें)"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"लम्बवत रूप से दो हिस्सों में बाँटें (स्प्लिट करें)"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"अपने मुताबिक दो हिस्सों में बाँटें (स्प्लिट स्क्रीन करें)"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ऊपर की ओर दो स्क्रीन बनाएं"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"बाईं ओर दो स्क्रीन बनाएं"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"दाईं ओर दो स्क्रीन बनाएं"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-hr/strings.xml b/packages/SystemUI/legacy/recents/res/values-hr/strings.xml
new file mode 100644
index 0000000..88926a4
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-hr/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Pregled."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Odbacivanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Odbačena je aplikacija <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Odbačene su sve nedavne aplikacije."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Otvaranje informacija o aplikaciji <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Pokreće se aplikacija <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Nema nedavnih stavki"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Izbrisali ste sve"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informacije o aplikaciji"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"prikačivanje zaslona"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"pretraživanje"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Aplikacija <xliff:g id="APP">%s</xliff:g> nije pokrenuta."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikacija <xliff:g id="APP">%s</xliff:g> onemogućena je u sigurnom načinu."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Izbriši sve"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Povucite ovdje da biste upotrebljavali podijeljeni zaslon"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Podijeli vodoravno"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Podijeli okomito"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Podijeli prilagođeno"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Podijeli zaslon na vrhu"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Podijeli zaslon slijeva"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Podijeli zaslon zdesna"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-hu/strings.xml b/packages/SystemUI/legacy/recents/res/values-hu/strings.xml
new file mode 100644
index 0000000..d0429e7
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-hu/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Áttekintés."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"A(z) <xliff:g id="APP">%s</xliff:g> elvetése."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> eltávolítva."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Az összes alkalmazás eltávolítva a nemrég használtak közül."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"A(z) <xliff:g id="APP">%s</xliff:g> alkalmazás adatainak megnyitása."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"A(z) <xliff:g id="APP">%s</xliff:g> indítása."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Nincsenek mostanában használt elemek"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Mindent törölt"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Az alkalmazás adatai"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"képernyő rögzítése"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"keresés"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Nem lehet elindítani a következőt: <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"A(z) <xliff:g id="APP">%s</xliff:g> csökkentett módban le van tiltva."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Összes törlése"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Húzza ide az osztott képernyő használatához"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Osztott vízszintes"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Osztott függőleges"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Osztott egyéni"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Osztott képernyő felülre"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Osztott képernyő balra"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Osztott képernyő jobbra"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-hy/strings.xml b/packages/SystemUI/legacy/recents/res/values-hy/strings.xml
new file mode 100644
index 0000000..c56b691
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-hy/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Համատեսք:"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Հեռացնել <xliff:g id="APP">%s</xliff:g> հավելվածը ցուցակից:"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> հավելվածը հեռացվել է ցուցակից:"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Բոլոր վերջին հավելվածները հեռացվել են ցուցակից:"</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Բացել <xliff:g id="APP">%s</xliff:g> հավելվածի մասին տեղեկությունները"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> հավելվածը գործարկվում է:"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Այստեղ դեռ ոչինչ չկա"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Ցուցակը դատարկ է"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Հավելվածի մասին"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"էկրանի ամրացում"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"որոնում"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Չհաջողվեց գործարկել <xliff:g id="APP">%s</xliff:g> հավելվածը:"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> հավելվածը անվտանգ ռեժիմում անջատված է:"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Ջնջել բոլորը"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Քաշեք այստեղ՝ էկրանի տրոհումն օգտագործելու համար"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Հորիզոնական տրոհում"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Ուղղահայաց տրոհում"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Հատուկ տրոհում"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Տրոհել էկրանը վերևից"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Տրոհել էկրանը ձախից"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Տրոհել էկրանն աջից"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-in/strings.xml b/packages/SystemUI/legacy/recents/res/values-in/strings.xml
new file mode 100644
index 0000000..aa9dcfe
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-in/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Ringkasan."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Hapus <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dihapus."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Semua aplikasi yang baru dibuka telah dihapus."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Buka info aplikasi <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Memulai <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Tidak ada item yang baru dibuka"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Anda sudah menghapus semua"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Info Aplikasi"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"pin ke layar"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"telusuri"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Tidak dapat memulai <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> dinonaktifkan dalam mode aman."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Hapus semua"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Tarik ke sini untuk menggunakan layar terpisah"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Pisahkan Horizontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Pisahkan Vertikal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Pisahkan Khusus"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Pisahkan layar ke atas"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Pisahkan layar ke kiri"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Pisahkan layar ke kanan"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-is/strings.xml b/packages/SystemUI/legacy/recents/res/values-is/strings.xml
new file mode 100644
index 0000000..e0a555e
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-is/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Yfirlit."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Fjarlægja <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> fjarlægt."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Öll nýleg forrit fjarlægð."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Opna forritsupplýsingar <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Ræsir <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Engin nýleg atriði"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Þú hefur hreinsað allt"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Forritsupplýsingar"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"skjáfesting"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"leita"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Ekki var hægt að ræsa <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Slökkt er á <xliff:g id="APP">%s</xliff:g> í öruggri stillingu."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Hreinsa allt"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Dragðu hingað til að skipta skjánum"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Lárétt skipting"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Lóðrétt skipting"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Sérsniðin skipting"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Skipta skjá að ofanverðu"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Skipta skjá til vinstri"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Skipta skjá til hægri"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-it/strings.xml b/packages/SystemUI/legacy/recents/res/values-it/strings.xml
new file mode 100644
index 0000000..e04d560
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-it/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Panoramica."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Elimina <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> eliminata."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Tutte le applicazioni recenti sono state rimosse."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Mostra informazioni sull\'applicazione <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Avvio di <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Nessun elemento recente"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Hai cancellato tutto"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informazioni sull\'applicazione"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"blocco su schermo"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"cerca"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Impossibile avviare <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"L\'app <xliff:g id="APP">%s</xliff:g> è stata disattivata in modalità provvisoria."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Cancella tutto"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Trascina qui per utilizzare la modalità Schermo diviso"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Divisione in orizzontale"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Divisione in verticale"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Divisione personalizzata"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Schermo diviso in alto"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Schermo diviso a sinistra"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Schermo diviso a destra"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-iw/strings.xml b/packages/SystemUI/legacy/recents/res/values-iw/strings.xml
new file mode 100644
index 0000000..a96c709
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-iw/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"סקירה כללית."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"הסרה של <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> הוסרה."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"כל האפליקציות האחרונות הוסרו."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"פתיחת מידע על האפליקציה <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"מפעיל את <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"אין פריטים אחרונים"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"מחקת הכול"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"מידע על האפליקציה"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"הקפאת מסך"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"חיפוש"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"לא ניתן היה להפעיל את <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> מושבתת במצב בטוח."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ניקוי הכול"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"יש לגרור לכאן כדי להשתמש במסך מפוצל"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"פיצול אופקי"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"פיצול אנכי"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"פיצול מותאם אישית"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"פיצול מסך למעלה"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"פיצול מסך לשמאל"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"פיצול מסך לימין"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ja/strings.xml b/packages/SystemUI/legacy/recents/res/values-ja/strings.xml
new file mode 100644
index 0000000..4d7524c
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-ja/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"最近"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g>を削除します。"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g>を削除しました。"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"最近のアプリをすべて削除しました。"</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g>のアプリ情報を開きます。"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g>を開始しています。"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"最近のアイテムはありません"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"すべてのタスクを削除しました"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"アプリ情報"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"画面固定"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"検索"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>を開始できませんでした。"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g>はセーフモードでは無効になります。"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"すべて消去"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"分割画面を使用するにはここにドラッグします"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"横に分割"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"縦に分割"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"分割(カスタム)"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"画面を上に分割"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"画面を左に分割"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"画面を右に分割"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ka/strings.xml b/packages/SystemUI/legacy/recents/res/values-ka/strings.xml
new file mode 100644
index 0000000..088388b
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-ka/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"მიმოხილვა"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g>-ის დახურვა."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> დაიხურა."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ყველა ბოლოდროინდელი აპლიკაცია დაიხურა."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> აპლიკაციის ინფორმაციის გახსნა."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"მიმდინარეობს <xliff:g id="APP">%s</xliff:g>-ის გაშვება."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"ბოლოდროინდელი ერთეულები არ არის"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"ყველაფერი გასუფთავდა"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"აპლიკაციის ინფორმაცია"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ეკრანზე ჩამაგრება"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"ძიება"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>-ის გაშვება ვერ მოხერხდა."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> გათიშულია უსაფრთხო რეჟიმში."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ყველას გასუფთავება"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"ეკრანის გასაყოფად ჩავლებით გადმოიტანეთ აქ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"ჰორიზონტალური გაყოფა"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ვერტიკალური გაყოფა"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"მორგებული გაყოფა"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ეკრანის გაყოფა ზემოთ"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"ეკრანის გაყოფა მარცხნივ"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"ეკრანის გაყოფა მარჯვნივ"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-kk/strings.xml b/packages/SystemUI/legacy/recents/res/values-kk/strings.xml
new file mode 100644
index 0000000..9d4e01c
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-kk/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Жалпы ақпарат."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> қолданбасын өшіру."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> өшірілді."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Барлық қолданбалар \"Соңғылар\" тізімінен өшірілді."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> қолданбасы туралы ақпаратты ашу."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> іске қосылды."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Ешқандай соңғы элементтер жоқ"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Барлығын өшірдіңіз"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Қолданба туралы ақпарат"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"экранды бекіту"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"іздеу"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> іске қосылмады."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> қауіпсіз режимде өшіріледі."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Барлығын өшіру"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Экранды бөлу үшін осы жерге сүйреңіз"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Көлденеңінен бөлу"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Тігінен бөлу"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Бөлу (арнаулы)"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Экранды жоғары жағынан бөлу"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Экранды сол жағынан бөлу"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Экранды оң жағынан бөлу"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-km/strings.xml b/packages/SystemUI/legacy/recents/res/values-km/strings.xml
new file mode 100644
index 0000000..b7bfba6
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-km/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ទិដ្ឋភាពរួម។"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"ច្រានចោល <xliff:g id="APP">%s</xliff:g> ។"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"បាន​ច្រានចោល <xliff:g id="APP">%s</xliff:g> ។"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"បាន​ច្រានចោល​កម្មវិធីថ្មីៗ​ទាំងអស់។"</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"បើក​ព័ត៌មាន​កម្មវិធី <xliff:g id="APP">%s</xliff:g> ។"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"កំពុង​ចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> ។"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"មិនមានធាតុថ្មីៗទេ"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"អ្នក​បានសម្អាត​អ្វីៗ​គ្រប់យ៉ាង"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"ព័ត៌មាន​កម្មវិធី"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ការ​ភ្ជាប់​អេក្រង់"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"ស្វែង​រក"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"មិន​អាច​ចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> បានទេ។"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ត្រូវបាន​បិទ​ដំណើរការ​ក្នុងមុខងារ​សុវត្ថិភាព។"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"សម្អាត​ទាំងអស់"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"អូសនៅទីនេះដើម្បីប្រើអេក្រង់បំបែក"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"បំបែកផ្តេក"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"បំបែកបញ្ឈរ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"បំបែកផ្ទាល់ខ្លួន"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"បំបែក​អេក្រង់​ទៅ​ខាងលើ"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"បំបែក​អេក្រង់​ទៅ​ខាងឆ្វេង"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"បំបែក​អេក្រង់​ទៅ​ខាងស្តាំ"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-kn/strings.xml b/packages/SystemUI/legacy/recents/res/values-kn/strings.xml
new file mode 100644
index 0000000..84894c1
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-kn/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ಸಮಗ್ರ ನೋಟ."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> ವಜಾಗೊಳಿಸಿ."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ಇತ್ತೀಚಿನ ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳನ್ನು ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ಆ್ಯಪ್ ಮಾಹಿತಿ ತೆರೆಯಿರಿ."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"ನೀವು ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿರುವಿರಿ"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"ಆ್ಯಪ್ ಮಾಹಿತಿ"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ಸ್ಕ್ರೀನ್ ಪಿನ್ನಿಂಗ್"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"ಹುಡುಕಾಟ"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲು ಸಾದ್ಯವಾಗಲಿಲ್ಲ."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ಅನ್ನು ಸುರಕ್ಷಿತ ಮೋಡ್‌ನಲ್ಲಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"ವಿಭಜಿತ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬಳಸಲು ಇಲ್ಲಿ ಡ್ರ್ಯಾಗ್‌ ಮಾಡಿ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"ಅಡ್ಡಲಾಗಿ ವಿಭಜಿಸಿದ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ಲಂಬವಾಗಿ ವಿಭಜಿಸಿದ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"ಕಸ್ಟಮ್ ವಿಭಜಿಸಿದ"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ಮೇಲ್ಭಾಗಕ್ಕೆ ಸ್ಕ್ರೀನ್ ಅನ್ನು ವಿಭಜಿಸಿ"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"ಎಡಕ್ಕೆ ಸ್ಕ್ರೀನ್ ಅನ್ನು ವಿಭಜಿಸಿ"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"ಬಲಕ್ಕೆ ಸ್ಕ್ರೀನ್ ಅನ್ನು ವಿಭಜಿಸಿ"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ko/strings.xml b/packages/SystemUI/legacy/recents/res/values-ko/strings.xml
new file mode 100644
index 0000000..ee856bd
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-ko/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"최근 사용"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g>을(를) 닫습니다."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> 애플리케이션을 닫았습니다."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"최근 사용한 애플리케이션을 모두 닫았습니다."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> 애플리케이션 정보를 엽니다."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g>을(를) 시작하는 중입니다."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"최근 항목이 없습니다."</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"모든 항목을 삭제했습니다."</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"애플리케이션 정보"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"화면 고정"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"검색"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>을(를) 시작할 수 없습니다."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g>은(는) 안전 모드에서 사용 중지됩니다."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"모두 삭제"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"여기를 드래그하여 분할 화면 사용하기"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"수평 분할"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"수직 분할"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"맞춤 분할"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"위쪽으로 화면 분할"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"왼쪽으로 화면 분할"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"오른쪽으로 화면 분할"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ky/strings.xml b/packages/SystemUI/legacy/recents/res/values-ky/strings.xml
new file mode 100644
index 0000000..879e492
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-ky/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Сереп салуу."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> колдонмосун өчүрүү."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> колдонмосу өчүрүлдү."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Акыркы колдонмолордун баары өчүрүлдү."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> колдонмосу жөнүндө маалыматты ачыңыз."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ачылууда."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Акыркы колдонмолор жок"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Баарын тазаладыңыз"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Колдонмо жөнүндө маалымат"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"экран кадоо"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"издөө"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> колдонмосу ачылган жок"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> коопсуз режиминде өчүрүлдү."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Баарын тазалоо"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Экранды бөлүү үчүн бул жерге сүйрөңүз"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Туурасынан бөлүү"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Тигинен бөлүү"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Ыңгайлаштырылган бөлүү"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Экранды өйдө жакка бөлүү"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Экранды сол жакка бөлүү"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Экранды оң жакка бөлүү"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-lo/strings.xml b/packages/SystemUI/legacy/recents/res/values-lo/strings.xml
new file mode 100644
index 0000000..17f56b4
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-lo/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ພາບຮວມ."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"ປິດ <xliff:g id="APP">%s</xliff:g> ໄວ້."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"ປິດ <xliff:g id="APP">%s</xliff:g> ແລ້ວ."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ທຸກແອັບພລິເຄຊັນບໍ່ດົນມານີ້ຖືກປິດໄວ້ແລ້ວ."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"ເປີດຂໍ້ມູນແອັບພລິເຄຊັນ <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"ກຳລັງເປີດ <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"ບໍ່ມີລາຍການຫຼ້າສຸດ"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"ທ່ານລຶບລ້າງທຸກຢ່າງແລ້ວ"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"ຂໍ້ມູນແອັບພລິເຄຊັນ"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ການປັກໝຸດໜ້າຈໍ"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"ຊອກຫາ"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"ບໍ່ສາມາດເລີ່ມ <xliff:g id="APP">%s</xliff:g> ໄດ້."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ຖືກປິດໃຊ້ໃນໂໝດຄວາມມປອດໄພ."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ລຶບລ້າງທັງໝົດ"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"ລາກມາບ່ອນນີ້ເພື່ອໃຊ້ການແບ່ງໜ້າຈໍ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"ການແຍກລວງຂວາງ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ການແຍກລວງຕັ້ງ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"ການແຍກກຳນົດເອງ"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ແຍກໜ້າຈໍໄປທາງເທິງ"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"ແຍກໜ້າຈໍໄປທາງຊ້າຍ"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"ແຍກໜ້າຈໍໄປທາງຂວາ"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-lt/strings.xml b/packages/SystemUI/legacy/recents/res/values-lt/strings.xml
new file mode 100644
index 0000000..4a9eb83
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-lt/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Apžvalga."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Atsisakyti programos „<xliff:g id="APP">%s</xliff:g>“."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Atsisakyta programos „<xliff:g id="APP">%s</xliff:g>“."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Atsisakyta visų naujausių programų."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Atidaryti programos „<xliff:g id="APP">%s</xliff:g>“ informaciją."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Paleidžiama programa „<xliff:g id="APP">%s</xliff:g>“."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Nėra jokių naujausių elementų"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Viską išvalėte"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Programos informacija"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ekrano prisegimas"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"ieškoti"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Nepavyko paleisti programos „<xliff:g id="APP">%s</xliff:g>“."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Programa „<xliff:g id="APP">%s</xliff:g>“ išjungta saugos režimu."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Išvalyti viską"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Vilkite čia, kad naudotumėte skaidytą ekraną"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Horizontalus skaidymas"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Vertikalus skaidymas"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Tinkintas skaidymas"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Skaidyti ekraną į viršų"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Skaidyti ekraną į kairę"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Skaidyti ekraną į dešinę"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-lv/strings.xml b/packages/SystemUI/legacy/recents/res/values-lv/strings.xml
new file mode 100644
index 0000000..7d87e00
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-lv/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Pārskats."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Nerādīt lietotni <xliff:g id="APP">%s</xliff:g>"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Vairs netiek rādīta lietotne <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Vairs netiek rādīta neviena nesen izmantotā lietojumprogramma."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Atveriet lietojumprogrammas <xliff:g id="APP">%s</xliff:g> informāciju."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Notiek lietotnes <xliff:g id="APP">%s</xliff:g> palaišana."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Nav nesenu vienumu"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Visi uzdevumi ir notīrīti"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Lietojumprogrammas informācija"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"Piespraust ekrānu"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"Meklēt"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Nevarēja palaist lietotni <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Lietotne <xliff:g id="APP">%s</xliff:g> ir atspējota drošajā režīmā."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Notīrīt visu"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Velciet šeit, lai izmantotu ekrāna sadalīšanu"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Horizontāls dalījums"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Vertikāls dalījums"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Pielāgots dalījums"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Sadalīt ekrānu augšdaļā"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Sadalīt ekrānu kreisajā pusē"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Sadalīt ekrānu labajā pusē"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-mk/strings.xml b/packages/SystemUI/legacy/recents/res/values-mk/strings.xml
new file mode 100644
index 0000000..d8ced0b
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-mk/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Преглед."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Отфрлете ја <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> е отфрлена."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Сите неодамнешни апликации се отфрлени."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Отворете информации за апликацијата <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Се стартува <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Нема неодамнешни ставки"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Избришавте сѐ"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Информации за апликацијата"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"прикачување екран"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"пребарувај"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> не можеше да се стартува."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> е оневозможена во безбеден режим."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Избриши сѐ"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Повлечете тука за да користите поделен екран"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Подели хоризонтално"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Подели вертикално"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Подели приспособено"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Подели го екранот во горниот дел"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Подели го екранот на левата страна"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Подели го екранот на десната страна"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-mn/strings.xml b/packages/SystemUI/legacy/recents/res/values-mn/strings.xml
new file mode 100644
index 0000000..205f56c
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-mn/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Тойм."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g>-г үл хэрэгсэнэ үү."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g>-г үл хэрэгссэн."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Саяхны бүх аппыг үл хэрэгссэн."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> аппын мэдээллийг нээнэ үү."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж байна."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Саяхны зүйлс алга"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Та бүгдийг нь устгасан"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Аппын мэдээлэл"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"дэлгэц тогтоох"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"хайх"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж чадсангүй."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g>-г аюулгүй горимд идэвхгүй болгосон."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Бүгдийг устгах"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Хуваасан дэлгэцийг ашиглахын тулд энд чирэх"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Хэвтээ чиглэлд хуваах"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Босоо чиглэлд хуваах"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Хүссэн хэлбэрээр хуваах"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Дэлгэцийг дээд хэсэгт хуваах"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Дэлгэцийг зүүн хэсэгт хуваах"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Дэлгэцийг баруун хэсэгт хуваах"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-mr/strings.xml b/packages/SystemUI/legacy/recents/res/values-mr/strings.xml
new file mode 100644
index 0000000..51bce6d
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-mr/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"अवलोकन."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> डिसमिस करा."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> डिसमिस केले"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"अलीकडील सर्व अॅप्लिकेशन डिसमिस झाले."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> अॅप्लिकेशन माहिती उघडा."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> सुरू करत आहे."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"कोणतेही अलीकडील आयटम नाहीत"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"तुम्ही सर्वकाही साफ केले"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"अॅप्लिकेशन माहिती"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"स्‍क्रीन पिन करणे"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"शोधा"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> सुरू करता आले नाही."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> सुरक्षित मोडमध्ये बंद केले आहे."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"सर्व साफ करा"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"स्प्लिट स्क्रीन वापर करण्यासाठी येथे ड्रॅग करा"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"आडवे स्प्लिट करा"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"उभे स्प्लिट करा"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"कस्टम स्प्लिट करा"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"स्क्रीन वर स्प्लिट करा"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"स्क्रीन डावीकडे स्प्लिट करा"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"स्क्रीन उजवीकडे स्प्लिट करा"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ms/strings.xml b/packages/SystemUI/legacy/recents/res/values-ms/strings.xml
new file mode 100644
index 0000000..ae4461d
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-ms/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Ikhtisar."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Ketepikan <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> diketepikan."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Semua aplikasi terbaharu diketepikan."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Buka maklumat aplikasi <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Memulakan <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Tiada item terbaharu"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Anda telah mengetepikan semua item"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Maklumat Aplikasi"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"penyematan skrin"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"cari"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Tidak dapat memulakan <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> dilumpuhkan dalam mod selamat."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Kosongkan semua"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Seret ke sini untuk menggunakan skrin pisah"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Pisah Mendatar"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Pisah Menegak"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Pisah Tersuai"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Pisahkan skrin ke atas"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Pisahkan skrin ke kiri"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Pisahkan skrin ke kanan"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-my/strings.xml b/packages/SystemUI/legacy/recents/res/values-my/strings.xml
new file mode 100644
index 0000000..94fc662
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-my/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"အနှစ်ချုပ်။"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> ကို ပယ်မည်။"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ကို ဖယ်ထုတ်ထားသည်။"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"လတ်တလော အပလီကေးရှင်းအားလုံး ဖယ်ထုတ်ထားသည်။"</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> အပလီကေးရှင်း အချက်အလက်ကို ဖွင့်မည်။"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ကို စတင်နေသည်။"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"လတ်တလော ဖွင့်ထားသည်များ မရှိပါ"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"အားလုံးကို ဖယ်ရှားပြီးပါပြီ"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"အပလီကေးရှင်း အချက်အလက်"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"မျက်နှာပြင် ပင်ထိုးမှု"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"ရှာရန်"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ကို စတင်၍ မရပါ။"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"အန္တရာယ်ကင်းမှု စနစ်တွင် <xliff:g id="APP">%s</xliff:g> ကို ပိတ်ထားပါသည်။"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"အားလုံး ဖယ်ရှားရန်"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းကို အသုံးပြုရန် ဤနေရာသို့ ဖိဆွဲပါ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"အလျားလိုက် ခွဲရန်"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ထောင်လိုက် ခွဲရန်"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"စိတ်ကြိုက် ခွဲရန်"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"မျက်နှာပြင်ကို အပေါ်သို့ ခွဲရန်"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"မျက်နှာပြင်ကို ဘယ်ဘက်သို့ ခွဲရန်"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"မျက်နှာပြင်ကို ညာဘက်သို့ ခွဲရန်"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-nb/strings.xml b/packages/SystemUI/legacy/recents/res/values-nb/strings.xml
new file mode 100644
index 0000000..176986a
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-nb/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Oversikt."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Avvis <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> er avvist."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Alle nylig brukte apper er avvist."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Åpne appinformasjonen for <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Starter <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Ingen nylige elementer"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Du har fjernet alt"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Appinformasjon"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"én-appsmodus"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"søk"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Kunne ikke starte <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> er slått av i sikker modus."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Fjern alt"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Dra hit for å bruke delt skjerm"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Del horisontalt"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Del vertikalt"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Del tilpasset"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Delt skjerm øverst"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Delt skjerm til venstre"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Delt skjerm til høyre"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-nl/strings.xml b/packages/SystemUI/legacy/recents/res/values-nl/strings.xml
new file mode 100644
index 0000000..9714022
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-nl/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Overzicht."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> sluiten."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> verwijderd."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Alle recente apps gesloten."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"App-gegevens voor <xliff:g id="APP">%s</xliff:g> openen."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> starten."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Geen recente items"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Je hebt alles gewist"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"App-informatie"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"scherm vastzetten"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"zoeken"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Kan <xliff:g id="APP">%s</xliff:g> niet starten."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> is uitgeschakeld in de veilige modus"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Alles wissen"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Sleep hier naartoe om het scherm te splitsen"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Horizontaal splitsen"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Verticaal splitsen"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Aangepast splitsen"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Scherm bovenaan gesplitst"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Scherm links gesplitst"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Scherm rechts gesplitst"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-pa/strings.xml b/packages/SystemUI/legacy/recents/res/values-pa/strings.xml
new file mode 100644
index 0000000..4608561
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-pa/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ਰੂਪ-ਰੇਖਾ।"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਖਾਰਜ ਕਰੋ।"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ਖਾਰਜ ਕੀਤੀ ਗਈ।"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ਸਾਰੀਆਂ ਹਾਲੀਆ ਐਪਲੀਕੇਸ਼ਨਾਂ ਖਾਰਜ ਕੀਤੀਆਂ ਗਈਆਂ।"</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ਐਪਲੀਕੇਸ਼ਨਾਂ ਜਾਣਕਾਰੀ ਖੋਲ੍ਹੋ।"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ਚਾਲੂ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ।"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"ਕੋਈ ਹਾਲੀਆ ਆਈਟਮਾਂ ਨਹੀਂ"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"ਤੁਸੀਂ ਸਭ ਕੁਝ ਸਾਫ਼ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"ਐਪਲੀਕੇਸ਼ਨ ਜਾਣਕਾਰੀ"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ਸਕ੍ਰੀਨ ਪਿਨਿੰਗ"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"ਖੋਜ"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਚਾਲੂ ਨਹੀਂ ਕਰ ਸਕੇ।"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਸੁਰੱਖਿਅਤ-ਮੋਡ ਵਿੱਚ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ।"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਇੱਥੇ ਘਸੀਟੋ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"ਲੇਟਵੀਂ ਵੰਡ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ਖੜ੍ਹਵੀਂ ਵੰਡ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"ਵਿਉਂਤੀ ਵੰਡ"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ਸਕ੍ਰੀਨ ਨੂੰ ਉੱਪਰ ਵੱਲ ਵੰਡੋ"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"ਸਕ੍ਰੀਨ ਨੂੰ ਖੱਬੇ ਪਾਸੇ ਵੰਡੋ"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"ਸਕ੍ਰੀਨ ਨੂੰ ਸੱਜੇ ਪਾਸੇ ਵੰਡੋ"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-pl/strings.xml b/packages/SystemUI/legacy/recents/res/values-pl/strings.xml
new file mode 100644
index 0000000..50b4ad0
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-pl/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Przegląd."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Zamknij aplikację <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplikacja <xliff:g id="APP">%s</xliff:g> została zamknięta."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Wszystkie ostatnie aplikacje zostały zamknięte."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Otwórz informacje o aplikacji <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Uruchamiam aplikację <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Brak ostatnich elementów"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Wszystko zostało wyczyszczone"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informacje o aplikacji"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"przypinanie ekranu"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"szukaj"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Nie udało się uruchomić aplikacji <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikacja <xliff:g id="APP">%s</xliff:g> została wyłączona w trybie bezpiecznym."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Wyczyść wszystko"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Przeciągnij tutaj, by podzielić ekran"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Podziel poziomo"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Podziel pionowo"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Podziel niestandardowo"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Podziel ekran u góry"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Podziel ekran z lewej"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Podziel ekran z prawej"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-pt-rBR/strings.xml b/packages/SystemUI/legacy/recents/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..589b831
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-pt-rBR/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Visão geral."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Dispensar <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dispensado."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Todos os aplicativos recentes foram dispensados."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Abre informações do aplicativo <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Nenhum item recente"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Você limpou tudo"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informações do aplicativo"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"fixação de tela"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"pesquisar"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"O app <xliff:g id="APP">%s</xliff:g> fica desativado no modo de segurança."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Limpar tudo"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Arraste aqui para usar a tela dividida"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Divisão horizontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Divisão vertical"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Divisão personalizada"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Dividir a tela para a parte superior"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Dividir a tela para a esquerda"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Dividir a tela para a direita"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-pt-rPT/strings.xml b/packages/SystemUI/legacy/recents/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..e62e1c6
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-pt-rPT/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Vista geral."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Ignorar <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplicação <xliff:g id="APP">%s</xliff:g> ignorada."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Todas as aplicações recentes foram ignoradas."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Abrir as informações da aplicação <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"A iniciar a aplicação <xliff:g id="APP">%s</xliff:g>…"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Nenhum item recente"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Limpou tudo"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informações da aplicação"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"afixação no ecrã"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"pesquisar"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Não foi possível iniciar a aplicação <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"A aplicação <xliff:g id="APP">%s</xliff:g> está desativada no modo de segurança."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Limpar tudo"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Arraste aqui para utilizar o ecrã dividido"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Divisão horizontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Divisão vertical"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Divisão personalizada"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Ecrã dividido na parte superior"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Ecrã dividido à esquerda"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Ecrã dividido à direita"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-pt/strings.xml b/packages/SystemUI/legacy/recents/res/values-pt/strings.xml
new file mode 100644
index 0000000..589b831
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-pt/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Visão geral."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Dispensar <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dispensado."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Todos os aplicativos recentes foram dispensados."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Abre informações do aplicativo <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Nenhum item recente"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Você limpou tudo"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informações do aplicativo"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"fixação de tela"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"pesquisar"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"O app <xliff:g id="APP">%s</xliff:g> fica desativado no modo de segurança."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Limpar tudo"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Arraste aqui para usar a tela dividida"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Divisão horizontal"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Divisão vertical"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Divisão personalizada"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Dividir a tela para a parte superior"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Dividir a tela para a esquerda"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Dividir a tela para a direita"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ro/strings.xml b/packages/SystemUI/legacy/recents/res/values-ro/strings.xml
new file mode 100644
index 0000000..7f8f018
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-ro/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Recente."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Închideți <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> a fost închisă."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Toate aplicațiile recente au fost închise."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Deschideți informațiile despre aplicația <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Se inițiază <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Niciun element recent"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Ați șters tot"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informații despre aplicație"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"fixare pe ecran"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"căutați"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> nu a putut porni."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplicația <xliff:g id="APP">%s</xliff:g> este dezactivată în modul sigur."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Ștergeți tot"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Trageți aici pentru a folosi ecranul împărțit"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Împărțiți pe orizontală"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Împărțiți pe verticală"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Împărțiți personalizat"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Împărțiți ecranul în partea de sus"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Împărțiți ecranul la stânga"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Împărțiți ecranul la dreapta"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ru/strings.xml b/packages/SystemUI/legacy/recents/res/values-ru/strings.xml
new file mode 100644
index 0000000..1e988bb
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-ru/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Обзор."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Удалить приложение <xliff:g id="APP">%s</xliff:g> из списка."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Приложение <xliff:g id="APP">%s</xliff:g> удалено из списка."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Все недавние приложения удалены из списка."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Открыть информацию о приложении <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Запуск приложения <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Здесь пока ничего нет."</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Список пуст."</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Сведения о приложении"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"блокировка в приложении"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"поиск"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Не удалось запустить приложение \"<xliff:g id="APP">%s</xliff:g>\"."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" отключено в безопасном режиме."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Удалить все"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Перетащите сюда, чтобы разделить экран"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Разделить по горизонтали"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Разделить по вертикали"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Разделить по-другому"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Разделить экран по верхнему краю"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Разделить экран по левому краю"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Разделить экран по правому краю"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sk/strings.xml b/packages/SystemUI/legacy/recents/res/values-sk/strings.xml
new file mode 100644
index 0000000..cbffcaf
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-sk/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Prehľad"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Zrušiť aplikáciu <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplikácia <xliff:g id="APP">%s</xliff:g> bola zrušená."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Všetky nedávne aplikácie boli zrušené."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Otvoriť informácie o aplikácii <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Spúšťa sa aplikácia <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Žiadne nedávne položky"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Vymazali ste všetko"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informácie o aplikácii"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"pripnutie obrazovky"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"hľadať"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Aplikáciu <xliff:g id="APP">%s</xliff:g> sa nepodarilo spustiť."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikácia <xliff:g id="APP">%s</xliff:g> je v núdzovom režime zakázaná."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Vymazať všetko"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Presuňte okno sem a použite tak rozdelenú obrazovku"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Rozdeliť vodorovné"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Rozdeliť zvislé"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Rozdeliť vlastné"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Rozdelená obrazovka hore"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Rozdelená obrazovka naľavo"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Rozdelená obrazovka napravo"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sl/strings.xml b/packages/SystemUI/legacy/recents/res/values-sl/strings.xml
new file mode 100644
index 0000000..56b2ddb
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-sl/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Pregled."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Opustitev aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplikacija <xliff:g id="APP">%s</xliff:g> je bila odstranjena."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Vse nedavne aplikacije so bile opuščene."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Odpiranje podatkov o aplikaciji <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Zaganjanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Ni nedavnih elementov"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Vse ste počistili"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Podatki o aplikaciji"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"pripenjanje zaslona"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"išči"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Aplikacije <xliff:g id="APP">%s</xliff:g> ni bilo mogoče zagnati."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikacija <xliff:g id="APP">%s</xliff:g> je v varnem načinu onemogočena."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Počisti vse"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Povlecite sem za razdeljeni zaslon"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Razdeli vodoravno"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Razdeli navpično"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Razdeli po meri"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Razdeljen zaslon na vrhu"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Razdeljen zaslon na levi"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Razdeljen zaslon na desni"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sq/strings.xml b/packages/SystemUI/legacy/recents/res/values-sq/strings.xml
new file mode 100644
index 0000000..48aab37
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-sq/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Përmbledhja."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Largo <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> është hequr."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Të gjitha aplikacionet e fundit u larguan."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Hap informacionin e aplikacionit <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Po nis <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Nuk ka asnjë artikull të fundit"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"I ke pastruar të gjitha"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informacioni i aplikacionit"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"kyçja e ekranit"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"kërko"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> nuk mund të nisej."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> është i çaktivizuar në modalitetin e sigurt."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Pastroji të gjitha"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Zvarrit këtu për të përdorur ekranin e ndarë"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Horizontal i ndarë"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Vertikal i ndarë"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"I personalizuar i ndarë"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Ndaje ekranin lart"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Ndaje ekranin në të majtë"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Ndaje ekranin në të djathtë"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sr/strings.xml b/packages/SystemUI/legacy/recents/res/values-sr/strings.xml
new file mode 100644
index 0000000..9d5f126
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-sr/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Преглед."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Одбаците апликацију <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Апликација <xliff:g id="APP">%s</xliff:g> је одбачена."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Све недавно коришћене апликације су одбачене."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Отворите информације о апликацији <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Покреће се <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Нема недавних ставки"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Обрисали сте све"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Информације о апликацији"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"качење екрана"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"претражи"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Покретање апликације <xliff:g id="APP">%s</xliff:g> није успело."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Апликација <xliff:g id="APP">%s</xliff:g> је онемогућена у безбедном режиму."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Обриши све"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Превуците овде да бисте користили раздељени екран"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Подели хоризонтално"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Подели вертикално"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Подели прилагођено"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Подели екран нагоре"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Подели екран налево"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Подели екран надесно"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sv/strings.xml b/packages/SystemUI/legacy/recents/res/values-sv/strings.xml
new file mode 100644
index 0000000..b2ee34f
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-sv/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Översikt."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Ta bort <xliff:g id="APP">%s</xliff:g> från listan."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> togs bort från listan."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Alla appar har tagits bort från listan Senaste."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Öppna appinformation för <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Startar <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Listan är tom"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Du har tömt listan"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Appinformation"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"fästa skärmen"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"sök"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Det gick inte att starta appen <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> är inaktiverad i säkert läge."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Rensa alla"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Dra hit för att dela upp skärmen"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Dela vågrätt"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Dela lodrätt"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Dela anpassat"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Delad skärm till överkanten"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Delad skärm åt vänster"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Delad skärm åt höger"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sw/strings.xml b/packages/SystemUI/legacy/recents/res/values-sw/strings.xml
new file mode 100644
index 0000000..49e7fb8
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-sw/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Muhtasari."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Ondoa <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> imeondolewa."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Programu za hivi majuzi zimeondolewa."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Fungua maelezo kuhusu programu ya <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Inaanzisha <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Hakuna vipengee vya hivi majuzi"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Umeondoa vipengee vyote"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Maelezo ya Programu"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"kubandika kwenye skirini"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"tafuta"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Imeshindwa kuanzisha <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> imezimwa katika hali salama."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Ondoa zote"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Buruta hapa ili utumie skrini iliyogawanywa"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Gawanya Mlalo"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Gawanya Wima"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Maalum Iliyogawanywa"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Gawa skrini kuelekea juu"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Gawa skrini upande wa kushoto"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Gawa skrini upande wa kulia"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sw600dp/dimens.xml b/packages/SystemUI/legacy/recents/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..20d6670
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-sw600dp/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2012, 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>
+    <!-- The offsets the tasks animate from when recents is launched while docking -->
+    <dimen name="recents_task_stack_animation_launched_while_docking_offset">192dp</dimen>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-th/strings.xml b/packages/SystemUI/legacy/recents/res/values-th/strings.xml
new file mode 100644
index 0000000..b88d05e
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-th/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ภาพรวม"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"ยกเลิก <xliff:g id="APP">%s</xliff:g>"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ถูกนำออกไปแล้ว"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ปิดแอปพลิเคชันล่าสุดทั้งหมดแล้ว"</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"เปิดข้อมูลแอปพลิเคชัน <xliff:g id="APP">%s</xliff:g>"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"กำลังเริ่มต้น <xliff:g id="APP">%s</xliff:g>"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"ไม่มีรายการล่าสุด"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"คุณได้ล้างทุกอย่างแล้ว"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"ข้อมูลแอปพลิเคชัน"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"การตรึงหน้าจอ"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"ค้นหา"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"เริ่มใช้ <xliff:g id="APP">%s</xliff:g> ไม่ได้"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ปิดใช้ในโหมดปลอดภัย"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ล้างทั้งหมด"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"ลากมาที่นี่เพื่อใช้การแยกหน้าจอ"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"แยกในแนวนอน"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"แยกในแนวตั้ง"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"แยกแบบกำหนดเอง"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"แยกหน้าจอไปด้านบน"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"แยกหน้าจอไปทางซ้าย"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"แยกหน้าจอไปทางขวา"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-tl/strings.xml b/packages/SystemUI/legacy/recents/res/values-tl/strings.xml
new file mode 100644
index 0000000..d940d4e
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-tl/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Overview"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"I-dismiss ang <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Na-dismiss ang <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Na-dismiss ang lahat ng kamakailang application."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Buksan ang impormasyon ng <xliff:g id="APP">%s</xliff:g> application."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Sinisimulan ang <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Walang kamakailang item"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Na-clear mo ang lahat"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Impormasyon ng Application"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"pag-pin sa screen"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"hanapin"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Hindi masimulan ang <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Naka-disable ang <xliff:g id="APP">%s</xliff:g> sa safe-mode."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"I-clear lahat"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"I-drag dito para magamit ang split screen"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"I-split Pahalang"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"I-split Patayo"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Split Custom"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"I-split ang screen pataas"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"I-split ang screen pakaliwa"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"I-split ang screen pakanan"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-tr/strings.xml b/packages/SystemUI/legacy/recents/res/values-tr/strings.xml
new file mode 100644
index 0000000..982c57e
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-tr/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Genel Bakış."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> uygulamasını kapatır."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> kaldırıldı."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Tüm son uygulamalar kapatıldı."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> uygulama bilgilerini açar."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> başlatılıyor."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Yeni öğe yok"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Her şeyi sildiniz"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Uygulama Bilgileri"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ekran sabitleme"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"ara"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> başlatılamadı."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g>, güvenli modda devre dışıdır."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Tümünü temizle"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Ekranı bölünmüş olarak kullanmak için buraya sürükleyin"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Yatay Ayırma"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Dikey Ayırma"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Özel Ayırma"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Ekranı yukarıya doğru böl"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Ekranı sola doğru böl"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Ekranı sağa doğru böl"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-uk/strings.xml b/packages/SystemUI/legacy/recents/res/values-uk/strings.xml
new file mode 100644
index 0000000..0c0b709
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-uk/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Огляд."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Закрити додаток <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Додаток <xliff:g id="APP">%s</xliff:g> закрито."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Усі останні додатки закрито."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Відкрити інформацію про додаток <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Запуск додатка <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Немає останніх елементів"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Ви очистили все"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Інформація про додаток"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"закріпити екран"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"пошук"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Не вдалося запустити додаток <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Додаток <xliff:g id="APP">%s</xliff:g> вимкнено в безпечному режимі."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Очистити все"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Перетягніть сюди, щоб розділити екран"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Розділити горизонтально"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Розділити вертикально"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Розділити (власний варіант)"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Розділити екран угору"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Розділити екран уліво"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Розділити екран управо"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ur/strings.xml b/packages/SystemUI/legacy/recents/res/values-ur/strings.xml
new file mode 100644
index 0000000..32aae85
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-ur/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"مجموعی جائزہ۔"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> کو مسترد کریں۔"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> کو مسترد کر دیا گیا۔"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"سبھی حالیہ ایپلیکیشنز کو مسترد کر دیا گیا۔"</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ایپلیکیشن کی معلومات کھولیں۔"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> شروع ہو رہی ہے۔"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"کوئی حالیہ آئٹم نہیں"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"آپ نے سب کچھ صاف کر دیا ہے"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"ایپلیکیشن کی معلومات"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"اسکرین کو پن کرنا"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"تلاش کریں"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> کو شروع نہیں کیا جا سکا۔"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"محفوظ موڈ میں <xliff:g id="APP">%s</xliff:g> غیر فعال ہے۔"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"سبھی کو ہٹائیں"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"اسپلٹ اسکرین استعمال کرنے کے لیے یہاں گھسیٹیں"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"بلحاظ افقی تقسیم کریں"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"بلحاظ عمودی تقسیم کریں"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"بلحاظ حسب ضرورت تقسیم کریں"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"اسکرین کو اوپر کی جانب تقسیم کریں"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"اسکرین کو بائیں جانب تقسیم کریں"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"اسکرین کو دائیں جانب تقسیم کریں"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-uz/strings.xml b/packages/SystemUI/legacy/recents/res/values-uz/strings.xml
new file mode 100644
index 0000000..6f8b153
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-uz/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Umumiy nazar."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Olib tashlash: <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> olib tashlangan."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Yaqinda ishlatilgan barcha ilovalar olib tashlandi."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ilovasi haqidagi axborotlarni ochadi."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ishga tushirilmoqda."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Yaqinda ishlatilgan ilovalar yoʻq"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Hammasi tozalandi"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Ilova haqida axborot"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ekranni mahkamlash"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"qidiruv"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ilovasi ishga tushmadi."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"Xavfsiz rejimda <xliff:g id="APP">%s</xliff:g> ilovasi yopildi."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Ha"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Ekranni boʻlish xususiyatidan foydalanish uchun bu yerga torting"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Gorizontal yoʻnalishda boʻlish"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Vertikal yoʻnalishda boʻlish"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Boshqa usulda boʻlish"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Ekranni tepaga qadash"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Ekranni chap tomonga qadash"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Ekranni oʻng tomonga qadash"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-vi/strings.xml b/packages/SystemUI/legacy/recents/res/values-vi/strings.xml
new file mode 100644
index 0000000..f672a3d
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-vi/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Tổng quan."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Loại bỏ <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Đã loại bỏ <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Đã loại bỏ tất cả các ứng dụng gần đây."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Mở thông tin ứng dụng <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Khởi động <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Không có mục gần đây nào"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Bạn đã xóa mọi nội dung"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Thông tin ứng dụng"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"khóa màn hình"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"tìm kiếm"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Không thể khởi động <xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> bị tắt ở chế độ an toàn."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Xóa tất cả"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Kéo vào đây để sử dụng chế độ chia đôi màn hình"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Phân tách ngang"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Phân tách dọc"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Tùy chỉnh phân tách"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Chia đôi màn hình lên trên"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Chia đôi màn hình sang trái"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Chia đôi màn hình sang phải"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-zh-rCN/strings.xml b/packages/SystemUI/legacy/recents/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..993bfae
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-zh-rCN/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"概览。"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"移除<xliff:g id="APP">%s</xliff:g>。"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"已移除<xliff:g id="APP">%s</xliff:g>"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"已关闭所有最近用过的应用。"</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"打开<xliff:g id="APP">%s</xliff:g>应用信息。"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"正在启动<xliff:g id="APP">%s</xliff:g>。"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"近期没有任何内容"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"您已清除所有内容"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"应用信息"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"固定屏幕"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"搜索"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"无法启动<xliff:g id="APP">%s</xliff:g>。"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g>已在安全模式下停用。"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"全部清除"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"拖动到此处即可使用分屏功能"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"水平分割"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"垂直分割"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"自定义分割"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"将屏幕分隔线移到上方"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"将屏幕分隔线移到左侧"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"将屏幕分隔线移到右侧"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-zh-rHK/strings.xml b/packages/SystemUI/legacy/recents/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..b93d246
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-zh-rHK/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"概覽。"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"關閉「<xliff:g id="APP">%s</xliff:g>」。"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"「<xliff:g id="APP">%s</xliff:g>」已關閉。"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"所有最近使用的應用程式均已關閉。"</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"開啟「<xliff:g id="APP">%s</xliff:g>」應用程式的資料。"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"正在啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"最近沒有任何項目"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"您已清除所有工作"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"應用程式資料"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"螢幕固定"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"搜尋"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"「<xliff:g id="APP">%s</xliff:g>」在安全模式下為停用狀態。"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"全部清除"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"拖曳這裡即可分割螢幕"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"水平分割"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"垂直分割"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"自訂分割"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"將分割畫面顯示喺頂部"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"將分割畫面顯示喺左邊"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"將分割畫面顯示喺右邊"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-zh-rTW/strings.xml b/packages/SystemUI/legacy/recents/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..54d656d
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-zh-rTW/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"總覽。"</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"關閉「<xliff:g id="APP">%s</xliff:g>」。"</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"「<xliff:g id="APP">%s</xliff:g>」已關閉。"</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"最近使用的應用程式已全部關閉。"</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"開啟「<xliff:g id="APP">%s</xliff:g>」應用程式資訊。"</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"正在啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"最近沒有任何項目"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"你已清除所有工作"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"應用程式資訊"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"螢幕固定"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"搜尋"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"「<xliff:g id="APP">%s</xliff:g>」在安全模式中為停用狀態。"</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"全部清除"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"拖曳到這裡即可使用分割畫面"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"水平分割"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"垂直分割"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"自訂分割"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"將分割畫面顯示在頂端"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"將分割畫面顯示在左邊"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"將分割畫面顯示在右邊"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-zu/strings.xml b/packages/SystemUI/legacy/recents/res/values-zu/strings.xml
new file mode 100644
index 0000000..9cbc439
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values-zu/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Buka konke."</string>
+    <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Cashisa i-<xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"I-<xliff:g id="APP">%s</xliff:g> icashisiwe."</string>
+    <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Zonke izinhlelo zokusebenza zakamuva zicashisiwe."</string>
+    <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Vula ulwazi lohlelo lokusebenza le-<xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Iqala i-<xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_empty_message" msgid="7967713254531861311">"Azikho izinto zakamuva"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Usule yonke into"</string>
+    <string name="recents_app_info_button_label" msgid="8732926607391786762">"Ulwazi lohlelo lokusebenza"</string>
+    <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ukuphina isikrini"</string>
+    <string name="recents_search_bar_label" msgid="638132045925945941">"sesha"</string>
+    <string name="recents_launch_error_message" msgid="9107963563503438012">"Ayikwazanga ukuqalisa i-<xliff:g id="APP">%s</xliff:g>."</string>
+    <string name="recents_launch_disabled_message" msgid="826461671965217243">"I-<xliff:g id="APP">%s</xliff:g> ikhutshaziwe kumodi yokuphepha."</string>
+    <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Sula konke"</string>
+    <string name="recents_drag_hint_message" msgid="610417221848280136">"Hudulela lapha ukuze usebenzise ukuhlukanisa kwesikrini"</string>
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Hlukanisa ngokuvundlile"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Hlukanisa ngokumile"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Hlukanisa ngokwezifiso"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Hlukanisela isikrini phezulu"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Hlukanisela isikrini ngakwesokunxele"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Hlukanisela isikrini ngakwesokudla"</string>
+</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values/attrs.xml b/packages/SystemUI/legacy/recents/res/values/attrs.xml
new file mode 100644
index 0000000..ef4cd5b
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values/attrs.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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>
+
+    <declare-styleable name="RecentsPanelView">
+        <attr name="recentItemLayout" format="reference" />
+        <!-- Style for the "Clear all" button. -->
+        <attr name="clearAllStyle" format="reference" />
+        <attr name="clearAllBackgroundColor" format="reference" />
+    </declare-styleable>
+
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/values/colors.xml b/packages/SystemUI/legacy/recents/res/values/colors.xml
new file mode 100644
index 0000000..88b9b70
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values/colors.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2010, 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>
+	<!-- The disabled recents task bar background color. -->
+    <color name="recents_task_bar_disabled_background_color">#ff676767</color>
+    <!-- The default recents task bar background color. -->
+    <color name="recents_task_bar_default_background_color">#ffe6e6e6</color>
+    <!-- The default recents task view background color. -->
+    <color name="recents_task_view_default_background_color">#fff3f3f3</color>
+    <!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
+    <color name="recents_task_bar_light_text_color">#ffeeeeee</color>
+    <!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
+    <color name="recents_task_bar_dark_text_color">#cc000000</color>
+    <!-- The recents task bar light dismiss icon color to be drawn on top of dark backgrounds. -->
+    <color name="recents_task_bar_light_icon_color">#ccffffff</color>
+    <!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
+    <color name="recents_task_bar_dark_icon_color">#99000000</color>
+    <!-- The lock to task button background color. -->
+    <color name="recents_task_view_lock_to_app_button_background_color">#ffe6e6e6</color>
+    <!-- The lock to task button foreground color. -->
+    <color name="recents_task_view_lock_to_app_button_color">#ff666666</color>
+    <!-- The background color for the freeform workspace. -->
+    <color name="recents_freeform_workspace_bg_color">#33FFFFFF</color>
+
+    <!-- The background color for clear all button on light backgrounds if not transparent. -->
+    <color name="recents_clear_all_button_bg_light_color">#CCFFFFFF</color>
+    <!-- The background color for clear all button on dark backgrounds if not transparent. -->
+    <color name="recents_clear_all_button_bg_dark_color">#CC000000</color>
+
+    <!-- Shadow color for the first pixels around the fake shadow for recents. -->
+    <color name="fake_shadow_start_color">#44000000</color>
+
+    <!-- Shadow color for the furthest pixels around the fake shadow for recents. -->
+    <color name="fake_shadow_end_color">#03000000</color>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/values/config.xml b/packages/SystemUI/legacy/recents/res/values/config.xml
new file mode 100644
index 0000000..2ff9abf
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values/config.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+
+    <!-- Component to be used as the recents implementation.  Must implement the
+     RecentsImplementation interface.  This name is in the ComponentName flattened format
+     (package/class)  -->
+    <string name="config_recentsComponent" translatable="false">com.android.systemui.recents.LegacyRecentsImpl</string>
+
+    <!-- Whether recents should use hardware layers for its taskviews. This flag can be enabled
+    for devices where the java drawing of round rects may be slow -->
+    <bool name="config_recents_use_hardware_layers">false</bool>
+
+    <!-- The number of app thumbnails we keep in memory -->
+    <integer name="config_recents_max_thumbnail_count">10</integer>
+
+    <!-- The number of app icons we keep in memory -->
+    <integer name="config_recents_max_icon_count">20</integer>
+
+    <!-- Whether to use cheap, less good looking shadows for recents -->
+    <bool name="config_recents_fake_shadows">false</bool>
+
+    <!-- The duration in seconds to wait before the dismiss buttons are shown. -->
+    <integer name="recents_task_bar_dismiss_delay_seconds">1000</integer>
+
+    <!-- The duration for animating the task decorations in after transitioning from an app. -->
+    <integer name="recents_task_enter_from_app_duration">200</integer>
+
+    <!-- The duration for animating the task decorations in after transitioning from an app. -->
+    <integer name="recents_task_enter_from_affiliated_app_duration">125</integer>
+
+    <!-- The duration for animating the task decorations out before transitioning to an app. -->
+    <integer name="recents_task_exit_to_app_duration">125</integer>
+
+    <!-- The min animation duration for animating the nav bar scrim in. -->
+    <integer name="recents_nav_bar_scrim_enter_duration">400</integer>
+
+    <!-- The animation duration for scrolling the stack to a particular item. -->
+    <integer name="recents_animate_task_stack_scroll_duration">200</integer>
+
+    <!-- The delay to enforce between each alt-tab key press. -->
+    <integer name="recents_alt_tab_key_delay">200</integer>
+
+    <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
+    <integer name="recents_svelte_level">0</integer>
+
+    <!-- Recents: The relative range of visible tasks from the current scroll position
+         while the stack is focused. -->
+    <item name="recents_layout_focused_range_min" format="float" type="integer">-3</item>
+    <item name="recents_layout_focused_range_max" format="float" type="integer">2</item>
+
+    <!-- Recents: The relative range of visible tasks from the current scroll position
+         while the stack is not focused. -->
+    <item name="recents_layout_unfocused_range_min" format="float" type="integer">-2</item>
+    <item name="recents_layout_unfocused_range_max" format="float" type="integer">2.5</item>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/values/dimens.xml b/packages/SystemUI/legacy/recents/res/values/dimens.xml
new file mode 100644
index 0000000..528610e4
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values/dimens.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2006, 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>
+<!-- Recents Layout -->
+
+    <!-- The amount to inset the stack, specifically at the top and the other sides.  We also
+         don't want this to change across configurations that Recents can be opened in, so we
+         define them statically for all display sizes. -->
+    <dimen name="recents_layout_min_margin">16dp</dimen>
+    <dimen name="recents_layout_top_margin_phone">16dp</dimen>
+    <dimen name="recents_layout_top_margin_tablet">32dp</dimen>
+    <dimen name="recents_layout_top_margin_tablet_xlarge">40dp</dimen>
+    <dimen name="recents_layout_bottom_margin">16dp</dimen>
+    <dimen name="recents_layout_side_margin_phone">16dp</dimen>
+    <dimen name="recents_layout_side_margin_tablet">48dp</dimen>
+    <dimen name="recents_layout_side_margin_tablet_docked">16dp</dimen>
+    <dimen name="recents_layout_side_margin_tablet_xlarge">64dp</dimen>
+    <dimen name="recents_layout_side_margin_tablet_xlarge_docked">16dp</dimen>
+
+    <!-- The height between the top margin and the top of the focused task. -->
+    <dimen name="recents_layout_top_peek_size">48dp</dimen>
+    <!-- The height between the bottom margin and the top of task in front of the focused task. -->
+    <dimen name="recents_layout_bottom_peek_size">56dp</dimen>
+
+    <!-- The offset from the top and bottom of the stack of the focused task.  The bottom offset
+         will be additionally offset by the bottom system insets since it goes under the nav bar
+         in certain orientations. -->
+    <dimen name="recents_layout_initial_top_offset_phone_port">128dp</dimen>
+    <dimen name="recents_layout_initial_bottom_offset_phone_port">80dp</dimen>
+    <dimen name="recents_layout_initial_top_offset_phone_land">72dp</dimen>
+    <dimen name="recents_layout_initial_bottom_offset_phone_land">72dp</dimen>
+    <dimen name="recents_layout_initial_top_offset_tablet">160dp</dimen>
+    <dimen name="recents_layout_initial_bottom_offset_tablet">112dp</dimen>
+
+    <!-- The min/max translationZ for the tasks in the stack. -->
+    <dimen name="recents_layout_z_min">3dp</dimen>
+    <dimen name="recents_layout_z_max">24dp</dimen>
+
+    <!-- The margin between the freeform and stack.  We also don't want this to change across
+         configurations that Recents can be opened in, so we define them statically for all
+         display sizes. -->
+    <dimen name="recents_freeform_layout_bottom_margin">16dp</dimen>
+
+    <!-- The padding between each freeform task. -->
+    <dimen name="recents_freeform_layout_task_padding">8dp</dimen>
+
+<!-- Recents Views -->
+
+    <!-- The height of a task view bar.  This has to be large enough to cover the action bar
+         height in either orientation at this smallest width. -->
+    <dimen name="recents_task_view_header_height">56dp</dimen>
+    <dimen name="recents_task_view_header_height_tablet_land">64dp</dimen>
+
+    <!-- The padding of a button in the recents task view header. -->
+    <dimen name="recents_task_view_header_button_padding">16dp</dimen>
+    <dimen name="recents_task_view_header_button_padding_tablet_land">20dp</dimen>
+
+    <!-- The radius of the rounded corners on a task view and its shadow (which can be larger
+         to create a softer corner effect. -->
+    <dimen name="recents_task_view_rounded_corners_radius">2dp</dimen>
+    <dimen name="recents_task_view_shadow_rounded_corners_radius">6dp</dimen>
+
+    <!-- The amount of highlight to make on each task view. -->
+    <dimen name="recents_task_view_highlight">1dp</dimen>
+
+    <!-- The size of the lock-to-app button and its icon. -->
+    <dimen name="recents_lock_to_app_size">56dp</dimen>
+    <dimen name="recents_lock_to_app_icon_size">28dp</dimen>
+
+    <!-- The amount of overscroll allowed when flinging to the end of the stack. -->
+    <dimen name="recents_fling_overscroll_distance">24dp</dimen>
+
+    <!-- The size of the drag hint text. -->
+    <dimen name="recents_drag_hint_text_size">14sp</dimen>
+
+    <!-- The min alpha to apply to a task affiliation group color. -->
+    <item name="recents_task_affiliation_color_min_alpha_percentage" format="float" type="dimen">0.6</item>
+
+    <!-- The amount to offset when animating into an affiliate group. -->
+    <dimen name="recents_task_stack_animation_affiliate_enter_offset">32dp</dimen>
+
+    <!-- The offsets the tasks animate from when recents is launched while docking -->
+    <dimen name="recents_task_stack_animation_launched_while_docking_offset">144dp</dimen>
+
+    <!-- The amount to translate when animating the removal of a task. -->
+    <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
+
+    <!-- The alpha to apply to the recents row when it doesn't have focus -->
+    <item name="recents_recents_row_dim_alpha" format="float" type="dimen">0.5</item>
+
+    <!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
+         loading full resolution screenshots. -->
+    <dimen name="recents_fast_fling_velocity">600dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens_grid.xml b/packages/SystemUI/legacy/recents/res/values/dimens_grid.xml
similarity index 100%
rename from packages/SystemUI/res/values/dimens_grid.xml
rename to packages/SystemUI/legacy/recents/res/values/dimens_grid.xml
diff --git a/packages/SystemUI/legacy/recents/res/values/strings.xml b/packages/SystemUI/legacy/recents/res/values/strings.xml
new file mode 100644
index 0000000..4b44ba9
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values/strings.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+	
+    <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_recent_apps">Overview.</string>
+
+    <!-- Content description to tell the user that this button will remove an application from recents -->
+    <string name="accessibility_recents_item_will_be_dismissed">Dismiss <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
+    <!-- Content description to tell the user an application has been removed from recents -->
+    <string name="accessibility_recents_item_dismissed"><xliff:g id="app" example="Calendar">%s</xliff:g> dismissed.</string>
+    <!-- Content description to tell the user all applications has been removed from recents -->
+    <string name="accessibility_recents_all_items_dismissed">All recent applications dismissed.</string>
+    <!-- Content description to tell the user that this button will open application info for an application in recents -->
+    <string name="accessibility_recents_item_open_app_info">Open <xliff:g id="app" example="Calendar">%s</xliff:g> application info.</string>
+    <!-- Content description to tell the user an application has been launched from recents -->
+    <string name="accessibility_recents_item_launched">Starting <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
+
+    <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
+    <string name="recents_empty_message">No recent items</string>
+    <!-- Recents: The empty recents string after dismissing all tasks. [CHAR LIMIT=NONE] -->
+    <string name="recents_empty_message_dismissed_all">You\'ve cleared everything</string>
+    <!-- Recents: The info panel app info button string. [CHAR LIMIT=NONE] -->
+    <string name="recents_app_info_button_label">Application Info</string>
+    <!-- Recents: The screen pinning button. [CHAR LIMIT=NONE] -->
+    <string name="recents_lock_to_app_button_label">screen pinning</string>
+    <!-- Recents: Temporary string for the button in the recents search bar. [CHAR LIMIT=NONE] -->
+    <string name="recents_search_bar_label">search</string>
+    <!-- Recents: Launch error string. [CHAR LIMIT=NONE] -->
+    <string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
+    <!-- Recents: Launch disabled string. [CHAR LIMIT=NONE] -->
+    <string name="recents_launch_disabled_message"><xliff:g id="app" example="Calendar">%s</xliff:g> is disabled in safe-mode.</string>
+    <!-- Recents: Stack action button string. [CHAR LIMIT=NONE] -->
+    <string name="recents_stack_action_button_label">Clear all</string>
+    <!-- Recents: Hint text that shows on the drop targets to start multiwindow. [CHAR LIMIT=NONE] -->
+    <string name="recents_drag_hint_message">Drag here to use split screen</string>
+
+    <!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
+    <string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
+    <!-- Recents: MultiStack add stack split vertical radio button. [CHAR LIMIT=NONE] -->
+    <string name="recents_multistack_add_stack_dialog_split_vertical">Split Vertical</string>
+    <!-- Recents: MultiStack add stack split custom radio button. [CHAR LIMIT=NONE] -->
+    <string name="recents_multistack_add_stack_dialog_split_custom">Split Custom</string>
+    <!-- Recents: Accessibility split to the top -->
+    <string name="recents_accessibility_split_screen_top">Split screen to the top</string>
+    <!-- Recents: Accessibility split to the left -->
+    <string name="recents_accessibility_split_screen_left">Split screen to the left</string>
+    <!-- Recents: Accessibility split to the right -->
+    <string name="recents_accessibility_split_screen_right">Split screen to the right</string>
+
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/values/styles.xml b/packages/SystemUI/legacy/recents/res/values/styles.xml
new file mode 100644
index 0000000..eb16be7
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/res/values/styles.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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 xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <style name="RecentsTheme" parent="@android:style/Theme.Material">
+        <!-- NoTitle -->
+        <item name="android:windowNoTitle">true</item>
+        <!-- Misc -->
+        <item name="android:statusBarColor">@android:color/transparent</item>
+        <item name="android:navigationBarColor">@android:color/transparent</item>
+        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+        <item name="android:windowAnimationStyle">@null</item>
+        <item name="android:ambientShadowAlpha">0.35</item>
+    </style>
+
+    <!-- Recents theme -->
+    <style name="RecentsTheme.Wallpaper">
+        <item name="android:windowBackground">@*android:color/transparent</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        <item name="android:windowShowWallpaper">true</item>
+        <item name="android:windowDisablePreview">true</item>
+        <item name="clearAllStyle">@style/ClearAllButtonDefaultMargins</item>
+        <item name="clearAllBackgroundColor">@color/recents_clear_all_button_bg_dark_color</item>
+        <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
+        <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_dark</item>
+    </style>
+
+    <style name="RecentsTheme.Wallpaper.Light">
+        <item name="clearAllBackgroundColor">@color/recents_clear_all_button_bg_light_color</item>
+        <item name="wallpaperTextColor">@*android:color/primary_text_material_light</item>
+        <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_light</item>
+    </style>
+
+    <!-- Performance optimized Recents theme (no wallpaper) -->
+    <style name="RecentsTheme.NoWallpaper">
+        <item name="android:windowBackground">@android:color/black</item>
+        <item name="wallpaperTextColor">@android:color/white</item>
+        <item name="wallpaperTextColorSecondary">@android:color/white</item>
+    </style>
+
+  </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/Constants.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/Constants.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/Constants.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java
new file mode 100644
index 0000000..b7bb751
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.EventLog;
+import android.util.Log;
+import android.view.Display;
+import android.widget.Toast;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.EventLogConstants;
+import com.android.systemui.EventLogTags;
+import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.pip.PipUI;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
+import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
+import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
+import com.android.systemui.recents.events.component.ExpandPipEvent;
+import com.android.systemui.recents.events.component.HidePipMenuEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
+import com.android.systemui.recents.events.component.ShowUserToastEvent;
+import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
+import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.stackdivider.Divider;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An implementation of the SystemUI recents component, which supports both system and secondary
+ * users.
+ */
+public class LegacyRecentsImpl implements RecentsImplementation {
+
+    private final static String TAG = "Recents";
+
+    public final static int EVENT_BUS_PRIORITY = 1;
+    public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000;
+
+    public final static Set<String> RECENTS_ACTIVITIES = new HashSet<>();
+    static {
+        RECENTS_ACTIVITIES.add(RecentsImpl.RECENTS_ACTIVITY);
+    }
+
+    private static final String COUNTER_WINDOW_SUPPORTED = "window_enter_supported";
+    private static final String COUNTER_WINDOW_UNSUPPORTED = "window_enter_unsupported";
+    private static final String COUNTER_WINDOW_INCOMPATIBLE = "window_enter_incompatible";
+
+    private static SystemServicesProxy sSystemServicesProxy;
+    private static RecentsDebugFlags sDebugFlags;
+    private static RecentsTaskLoader sTaskLoader;
+    private static RecentsConfiguration sConfiguration;
+
+    private Context mContext;
+    private SysUiServiceProvider mSysUiServiceProvider;
+    private Handler mHandler;
+    private RecentsImpl mImpl;
+
+    // Only For system user, this is the callbacks instance we return to each secondary user
+    private RecentsSystemUser mSystemToUserCallbacks;
+
+    // Only for secondary users, this is the callbacks instance provided by the system user to make
+    // calls back
+    private IRecentsSystemUserCallbacks mUserToSystemCallbacks;
+
+    // The set of runnables to run after binding to the system user's service.
+    private final ArrayList<Runnable> mOnConnectRunnables = new ArrayList<>();
+
+    // Only for secondary users, this is the death handler for the binder from the system user
+    private final IBinder.DeathRecipient mUserToSystemCallbacksDeathRcpt = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            mUserToSystemCallbacks = null;
+            EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
+                    EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_UNBOUND,
+                    sSystemServicesProxy.getProcessUser());
+
+            // Retry after a fixed duration
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    registerWithSystemUser();
+                }
+            }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
+        }
+    };
+
+    // Only for secondary users, this is the service connection we use to connect to the system user
+    private final ServiceConnection mUserToSystemServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (service != null) {
+                mUserToSystemCallbacks = IRecentsSystemUserCallbacks.Stub.asInterface(
+                        service);
+                EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
+                        EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_BOUND,
+                        sSystemServicesProxy.getProcessUser());
+
+                // Listen for system user's death, so that we can reconnect later
+                try {
+                    service.linkToDeath(mUserToSystemCallbacksDeathRcpt, 0);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Lost connection to (System) SystemUI", e);
+                }
+
+                // Run each of the queued runnables
+                runAndFlushOnConnectRunnables();
+            }
+
+            // Unbind ourselves now that we've registered our callbacks.  The
+            // binder to the system user are still valid at this point.
+            mContext.unbindService(this);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            // Do nothing
+        }
+    };
+
+    /**
+     * Returns the callbacks interface that non-system users can call.
+     */
+    public IBinder getSystemUserCallbacks() {
+        return mSystemToUserCallbacks;
+    }
+
+    public static RecentsTaskLoader getTaskLoader() {
+        return sTaskLoader;
+    }
+
+
+    public static SystemServicesProxy getSystemServices() {
+        return sSystemServicesProxy;
+    }
+
+    public static RecentsConfiguration getConfiguration() {
+        return sConfiguration;
+    }
+
+    public static RecentsDebugFlags getDebugFlags() {
+        return sDebugFlags;
+    }
+
+    @Override
+    public void onStart(Context context, SysUiServiceProvider sysUiServiceProvider) {
+        mContext = context;
+        mSysUiServiceProvider = sysUiServiceProvider;
+        final Resources res = mContext.getResources();
+        final int defaultTaskBarBackgroundColor =
+                mContext.getColor(R.color.recents_task_bar_default_background_color);
+        final int defaultTaskViewBackgroundColor =
+                mContext.getColor(R.color.recents_task_view_default_background_color);
+        sDebugFlags = new RecentsDebugFlags();
+        sSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
+        sConfiguration = new RecentsConfiguration(mContext);
+        sTaskLoader = new RecentsTaskLoader(mContext,
+                // TODO: Once we start building the AAR, move these into the loader
+                res.getInteger(R.integer.config_recents_max_thumbnail_count),
+                res.getInteger(R.integer.config_recents_max_icon_count),
+                res.getInteger(R.integer.recents_svelte_level));
+        sTaskLoader.setDefaultColors(defaultTaskBarBackgroundColor, defaultTaskViewBackgroundColor);
+        mHandler = new Handler();
+        mImpl = new RecentsImpl(mContext);
+
+        // Register with the event bus
+        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
+        EventBus.getDefault().register(sSystemServicesProxy, EVENT_BUS_PRIORITY);
+        EventBus.getDefault().register(sTaskLoader, EVENT_BUS_PRIORITY);
+
+        // Due to the fact that RecentsActivity is per-user, we need to establish and interface for
+        // the system user's Recents component to pass events (like show/hide/toggleRecents) to the
+        // secondary user, and vice versa (like visibility change, screen pinning).
+        final int processUser = sSystemServicesProxy.getProcessUser();
+        if (sSystemServicesProxy.isSystemUser(processUser)) {
+            // For the system user, initialize an instance of the interface that we can pass to the
+            // secondary user
+            mSystemToUserCallbacks = new RecentsSystemUser(mContext, mImpl);
+        } else {
+            // For the secondary user, bind to the primary user's service to get a persistent
+            // interface to register its implementation and to later update its state
+            registerWithSystemUser();
+        }
+    }
+
+    @Override
+    public void onBootCompleted() {
+        mImpl.onBootCompleted();
+    }
+
+
+    @Override
+    public void growRecents() {
+        EventBus.getDefault().send(new RecentsGrowingEvent());
+    }
+
+    /**
+     * Shows the Recents.
+     */
+    @Override
+    public void showRecentApps(boolean triggeredFromAltTab) {
+        ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
+        int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents();
+        int currentUser = sSystemServicesProxy.getCurrentUser();
+        if (sSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
+                    true /* animate */, recentsGrowTarget);
+        } else {
+            if (mSystemToUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
+                                true /* animate */, recentsGrowTarget);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                }
+            }
+        }
+    }
+
+    /**
+     * Hides the Recents.
+     */
+    @Override
+    public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+        int currentUser = sSystemServicesProxy.getCurrentUser();
+        if (sSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
+        } else {
+            if (mSystemToUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                }
+            }
+        }
+    }
+
+    /**
+     * Toggles the Recents activity.
+     */
+    @Override
+    public void toggleRecentApps() {
+        int growTarget = getComponent(Divider.class).getView().growsRecents();
+        int currentUser = sSystemServicesProxy.getCurrentUser();
+        if (sSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.toggleRecents(growTarget);
+        } else {
+            if (mSystemToUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.toggleRecents(growTarget);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                }
+            }
+        }
+    }
+
+    /**
+     * Preloads info for the Recents activity.
+     */
+    @Override
+    public void preloadRecentApps() {
+        int currentUser = sSystemServicesProxy.getCurrentUser();
+        if (sSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.preloadRecents();
+        } else {
+            if (mSystemToUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.preloadRecents();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void cancelPreloadRecentApps() {
+        int currentUser = sSystemServicesProxy.getCurrentUser();
+        if (sSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.cancelPreloadingRecents();
+        } else {
+            if (mSystemToUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.cancelPreloadingRecents();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds, int metricsDockAction) {
+        Point realSize = new Point();
+        if (initialBounds == null) {
+            mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
+                    .getRealSize(realSize);
+            initialBounds = new Rect(0, 0, realSize.x, realSize.y);
+        }
+
+        int currentUser = sSystemServicesProxy.getCurrentUser();
+        ActivityManager.RunningTaskInfo runningTask =
+                ActivityManagerWrapper.getInstance().getRunningTask();
+        final int activityType = runningTask != null
+                ? runningTask.configuration.windowConfiguration.getActivityType()
+                : ACTIVITY_TYPE_UNDEFINED;
+        boolean screenPinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
+        boolean isRunningTaskInHomeOrRecentsStack =
+                activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
+        if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
+            logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode);
+            if (runningTask.supportsSplitScreenMultiWindow) {
+                if (metricsDockAction != -1) {
+                    MetricsLogger.action(mContext, metricsDockAction,
+                            runningTask.topActivity.flattenToShortString());
+                }
+                if (sSystemServicesProxy.isSystemUser(currentUser)) {
+                    mImpl.splitPrimaryTask(runningTask.id, stackCreateMode, initialBounds);
+                } else {
+                    if (mSystemToUserCallbacks != null) {
+                        IRecentsNonSystemUserCallbacks callbacks =
+                                mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                        if (callbacks != null) {
+                            try {
+                                callbacks.splitPrimaryTask(runningTask.id, stackCreateMode,
+                                        initialBounds);
+                            } catch (RemoteException e) {
+                                Log.e(TAG, "Callback failed", e);
+                            }
+                        } else {
+                            Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                        }
+                    }
+                }
+
+                return true;
+            } else {
+                EventBus.getDefault().send(new ShowUserToastEvent(
+                        R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT));
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    public static void logDockAttempt(Context ctx, ComponentName activity, int resizeMode) {
+        if (resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE) {
+            MetricsLogger.action(ctx, MetricsEvent.ACTION_WINDOW_DOCK_UNRESIZABLE,
+                    activity.flattenToShortString());
+        }
+        MetricsLogger.count(ctx, getMetricsCounterForResizeMode(resizeMode), 1);
+    }
+
+    private static String getMetricsCounterForResizeMode(int resizeMode) {
+        switch (resizeMode) {
+            case ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE:
+                return COUNTER_WINDOW_UNSUPPORTED;
+            case ActivityInfo.RESIZE_MODE_RESIZEABLE:
+            case ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION:
+                return COUNTER_WINDOW_SUPPORTED;
+            default:
+                return COUNTER_WINDOW_INCOMPATIBLE;
+        }
+    }
+
+    @Override
+    public void onAppTransitionFinished() {
+        if (!LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            // Fallback, reset the flag once an app transition ends
+            EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(
+                    false /* waitingForTransitionStart */));
+        }
+    }
+
+    /**
+     * Updates on configuration change.
+     */
+    public void onConfigurationChanged(Configuration newConfig) {
+        int currentUser = sSystemServicesProxy.getCurrentUser();
+        if (sSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.onConfigurationChanged();
+        } else {
+            if (mSystemToUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.onConfigurationChanged();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                }
+            }
+        }
+    }
+
+    /**
+     * Handle Recents activity visibility changed.
+     */
+    public final void onBusEvent(final RecentsVisibilityChangedEvent event) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        int processUser = ssp.getProcessUser();
+        if (ssp.isSystemUser(processUser)) {
+            mImpl.onVisibilityChanged(event.applicationContext, event.visible);
+        } else {
+            postToSystemUser(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mUserToSystemCallbacks.updateRecentsVisibility(event.visible);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                }
+            });
+        }
+
+        // This will catch the cases when a user launches from recents to another app
+        // (and vice versa) that is not in the recents stack (such as home or bugreport) and it
+        // would not reset the wait for transition flag. This will catch it and make sure that the
+        // flag is reset.
+        if (!event.visible) {
+            mImpl.setWaitingForTransitionStart(false);
+        }
+    }
+
+    public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        int processUser = ssp.getProcessUser();
+        if (ssp.isSystemUser(processUser)) {
+            final Divider divider = getComponent(Divider.class);
+            if (divider != null) {
+                divider.onDockedFirstAnimationFrame();
+            }
+        } else {
+            postToSystemUser(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mUserToSystemCallbacks.sendDockedFirstAnimationFrameEvent();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * Handle screen pinning request.
+     */
+    public final void onBusEvent(final ScreenPinningRequestEvent event) {
+        int processUser = sSystemServicesProxy.getProcessUser();
+        if (sSystemServicesProxy.isSystemUser(processUser)) {
+            mImpl.onStartScreenPinning(event.applicationContext, event.taskId);
+        } else {
+            postToSystemUser(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mUserToSystemCallbacks.startScreenPinning(event.taskId);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                }
+            });
+        }
+    }
+
+    public final void onBusEvent(final RecentsDrawnEvent event) {
+        int processUser = sSystemServicesProxy.getProcessUser();
+        if (sSystemServicesProxy.isSystemUser(processUser)) {
+            final Divider divider = getComponent(Divider.class);
+            if (divider != null) {
+                divider.onRecentsDrawn();
+            }
+        } else {
+            postToSystemUser(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mUserToSystemCallbacks.sendRecentsDrawnEvent();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                }
+            });
+        }
+    }
+
+    public final void onBusEvent(final DockedTopTaskEvent event) {
+        int processUser = sSystemServicesProxy.getProcessUser();
+        if (sSystemServicesProxy.isSystemUser(processUser)) {
+            final Divider divider = getComponent(Divider.class);
+            if (divider != null) {
+                divider.onDockedTopTask();
+            }
+        } else {
+            postToSystemUser(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mUserToSystemCallbacks.sendDockingTopTaskEvent(event.initialRect);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                }
+            });
+        }
+    }
+
+    public final void onBusEvent(final RecentsActivityStartingEvent event) {
+        int processUser = sSystemServicesProxy.getProcessUser();
+        if (sSystemServicesProxy.isSystemUser(processUser)) {
+            final Divider divider = getComponent(Divider.class);
+            if (divider != null) {
+                divider.onRecentsActivityStarting();
+            }
+        } else {
+            postToSystemUser(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mUserToSystemCallbacks.sendLaunchRecentsEvent();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                }
+            });
+        }
+    }
+
+    public final void onBusEvent(LaunchTaskFailedEvent event) {
+        // Reset the transition when tasks fail to launch
+        mImpl.setWaitingForTransitionStart(false);
+    }
+
+    public final void onBusEvent(ConfigurationChangedEvent event) {
+        // Update the configuration for the Recents component when the activity configuration
+        // changes as well
+        mImpl.onConfigurationChanged();
+    }
+
+    public final void onBusEvent(ShowUserToastEvent event) {
+        int currentUser = sSystemServicesProxy.getCurrentUser();
+        if (sSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.onShowCurrentUserToast(event.msgResId, event.msgLength);
+        } else {
+            if (mSystemToUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.showCurrentUserToast(event.msgResId, event.msgLength);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                }
+            }
+        }
+    }
+
+    public final void onBusEvent(SetWaitingForTransitionStartEvent event) {
+        int processUser = sSystemServicesProxy.getProcessUser();
+        if (sSystemServicesProxy.isSystemUser(processUser)) {
+            mImpl.setWaitingForTransitionStart(event.waitingForTransitionStart);
+        } else {
+            postToSystemUser(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mUserToSystemCallbacks.setWaitingForTransitionStartEvent(
+                                event.waitingForTransitionStart);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                }
+            });
+        }
+    }
+
+    public final void onBusEvent(ExpandPipEvent event) {
+        PipUI pipUi = getComponent(PipUI.class);
+        pipUi.expandPip();
+    }
+
+    public final void onBusEvent(HidePipMenuEvent event) {
+        PipUI pipUi = getComponent(PipUI.class);
+        event.getAnimationTrigger().increment();
+        pipUi.hidePipMenu(() -> {
+                event.getAnimationTrigger().increment();
+            }, () -> {
+                event.getAnimationTrigger().decrement();
+            });
+        event.getAnimationTrigger().decrement();
+    }
+
+    /**
+     * Attempts to register with the system user.
+     */
+    private void registerWithSystemUser() {
+        final int processUser = sSystemServicesProxy.getProcessUser();
+        postToSystemUser(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    mUserToSystemCallbacks.registerNonSystemUserCallbacks(
+                            new RecentsImplProxy(mImpl), processUser);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to register", e);
+                }
+            }
+        });
+    }
+
+    /**
+     * Runs the runnable in the system user's Recents context, connecting to the service if
+     * necessary.
+     */
+    private void postToSystemUser(final Runnable onConnectRunnable) {
+        mOnConnectRunnables.add(onConnectRunnable);
+        if (mUserToSystemCallbacks == null) {
+            Intent systemUserServiceIntent = new Intent();
+            systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class);
+            boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent,
+                    mUserToSystemServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
+            EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
+                    EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_BIND_SERVICE,
+                    sSystemServicesProxy.getProcessUser());
+            if (!bound) {
+                // Retry after a fixed duration
+                mHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        registerWithSystemUser();
+                    }
+                }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
+            }
+        } else {
+            runAndFlushOnConnectRunnables();
+        }
+    }
+
+    /**
+     * Runs all the queued runnables after a service connection is made.
+     */
+    private void runAndFlushOnConnectRunnables() {
+        for (Runnable r : mOnConnectRunnables) {
+            r.run();
+        }
+        mOnConnectRunnables.clear();
+    }
+
+    private <T> T getComponent(Class<T> clazz) {
+        return mSysUiServiceProvider.getComponent(clazz);
+    }
+
+    @Override
+    public void dump(PrintWriter pw) {
+        pw.println("Recents");
+        pw.println("  currentUserId=" + SystemServicesProxy.getInstance(mContext).getCurrentUser());
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java
new file mode 100644
index 0000000..cec97ab
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java
@@ -0,0 +1,863 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents;
+
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.TaskStackBuilder;
+import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+
+import com.android.internal.colorextraction.ColorExtractor;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.LatencyTracker;
+import com.android.systemui.DejankUtils;
+import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
+import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
+import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
+import com.android.systemui.recents.events.activity.PackagesChangedEvent;
+import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
+import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
+import com.android.systemui.recents.events.component.ActivityUnpinnedEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
+import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
+import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
+import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
+import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
+import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
+import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
+import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
+import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
+import com.android.systemui.recents.events.ui.UserInteractionEvent;
+import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.utilities.Utilities;
+import com.android.systemui.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.RecentsView;
+import com.android.systemui.recents.views.SystemBarScrimViews;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * The main Recents activity that is started from RecentsComponent.
+ */
+public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreDrawListener,
+        ColorExtractor.OnColorsChangedListener {
+
+    private final static String TAG = "RecentsActivity";
+    private final static boolean DEBUG = false;
+
+    public final static int EVENT_BUS_PRIORITY = LegacyRecentsImpl.EVENT_BUS_PRIORITY + 1;
+    public final static int INCOMPATIBLE_APP_ALPHA_DURATION = 150;
+
+    private PackageMonitor mPackageMonitor = new PackageMonitor() {
+            @Override
+            public void onPackageRemoved(String packageName, int uid) {
+                RecentsActivity.this.onPackageChanged(packageName, getChangingUserId());
+            }
+
+            @Override
+            public boolean onPackageChanged(String packageName, int uid, String[] components) {
+                RecentsActivity.this.onPackageChanged(packageName, getChangingUserId());
+                return true;
+            }
+
+            @Override
+            public void onPackageModified(String packageName) {
+                RecentsActivity.this.onPackageChanged(packageName, getChangingUserId());
+            }
+        };
+    private Handler mHandler = new Handler();
+    private long mLastTabKeyEventTime;
+    private boolean mFinishedOnStartup;
+    private boolean mIgnoreAltTabRelease;
+    private boolean mIsVisible;
+    private boolean mRecentsStartRequested;
+    private Configuration mLastConfig;
+
+    // Top level views
+    private RecentsView mRecentsView;
+    private SystemBarScrimViews mScrimViews;
+    private View mIncompatibleAppOverlay;
+
+    // Runnables to finish the Recents activity
+    private Intent mHomeIntent;
+
+    // The trigger to automatically launch the current task
+    private int mFocusTimerDuration;
+    private final UserInteractionEvent mUserInteractionEvent = new UserInteractionEvent();
+
+    // Theme and colors
+    private SysuiColorExtractor mColorExtractor;
+    private boolean mUsingDarkText;
+
+    /**
+     * A common Runnable to finish Recents by launching Home with an animation depending on the
+     * last activity launch state. Generally we always launch home when we exit Recents rather than
+     * just finishing the activity since we don't know what is behind Recents in the task stack.
+     */
+    class LaunchHomeRunnable implements Runnable {
+
+        Intent mLaunchIntent;
+        ActivityOptions mOpts;
+
+        /**
+         * Creates a finish runnable that starts the specified intent.
+         */
+        public LaunchHomeRunnable(Intent launchIntent, ActivityOptions opts) {
+            mLaunchIntent = launchIntent;
+            mOpts = opts;
+        }
+
+        @Override
+        public void run() {
+            try {
+                mHandler.post(() -> {
+                    ActivityOptions opts = mOpts;
+                    if (opts == null) {
+                        opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
+                                R.anim.recents_to_launcher_enter, R.anim.recents_to_launcher_exit);
+                    }
+                    startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
+                });
+            } catch (Exception e) {
+                Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
+            }
+        }
+    }
+
+    /**
+     * Broadcast receiver to handle messages from the system
+     */
+    final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context ctx, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+                // When the screen turns off, dismiss Recents to Home
+                dismissRecentsToHomeIfVisible(false);
+            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
+                // When switching users, dismiss Recents to Home similar to screen off
+                finish();
+            }
+        }
+    };
+
+    private final OnPreDrawListener mRecentsDrawnEventListener =
+            new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
+                    EventBus.getDefault().post(new RecentsDrawnEvent());
+                    if (LatencyTracker.isEnabled(getApplicationContext())) {
+                        DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(
+                                getApplicationContext()).onActionEnd(
+                                LatencyTracker.ACTION_TOGGLE_RECENTS));
+                    }
+                    DejankUtils.postAfterTraversal(() -> {
+                        LegacyRecentsImpl.getTaskLoader().startLoader(RecentsActivity.this);
+                        LegacyRecentsImpl.getTaskLoader().getHighResThumbnailLoader().setVisible(true);
+                    });
+                    return true;
+                }
+            };
+
+    /**
+     * Dismisses recents if we are already visible and the intent is to toggle the recents view.
+     */
+    boolean dismissRecentsToFocusedTask(int logCategory) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        if (ssp.isRecentsActivityVisible()) {
+            // If we have a focused Task, launch that Task now
+            if (mRecentsView.launchFocusedTask(logCategory)) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Dismisses recents back to the launch target task.
+     */
+    boolean dismissRecentsToLaunchTargetTaskOrHome() {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        if (ssp.isRecentsActivityVisible()) {
+            // If we have a focused Task, launch that Task now
+            if (mRecentsView.launchPreviousTask()) return true;
+            // If none of the other cases apply, then just go Home
+            dismissRecentsToHome(true /* animateTaskViews */);
+        }
+        return false;
+    }
+
+    /**
+     * Dismisses recents if we are already visible and the intent is to toggle the recents view.
+     */
+    boolean dismissRecentsToFocusedTaskOrHome() {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        if (ssp.isRecentsActivityVisible()) {
+            // If we have a focused Task, launch that Task now
+            if (mRecentsView.launchFocusedTask(0 /* logCategory */)) return true;
+            // If none of the other cases apply, then just go Home
+            dismissRecentsToHome(true /* animateTaskViews */);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Dismisses Recents directly to Home without checking whether it is currently visible.
+     */
+    void dismissRecentsToHome(boolean animateTaskViews) {
+        dismissRecentsToHome(animateTaskViews, null);
+    }
+
+    /**
+     * Dismisses Recents directly to Home without checking whether it is currently visible.
+     *
+     * @param overrideAnimation If not null, will override the default animation that is based on
+     *                          how Recents was launched.
+     */
+    void dismissRecentsToHome(boolean animateTaskViews, ActivityOptions overrideAnimation) {
+        DismissRecentsToHomeAnimationStarted dismissEvent =
+                new DismissRecentsToHomeAnimationStarted(animateTaskViews);
+        dismissEvent.addPostAnimationCallback(new LaunchHomeRunnable(mHomeIntent,
+                overrideAnimation));
+        ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
+        EventBus.getDefault().send(dismissEvent);
+    }
+
+    /** Dismisses Recents directly to Home if we currently aren't transitioning. */
+    boolean dismissRecentsToHomeIfVisible(boolean animated) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        if (ssp.isRecentsActivityVisible()) {
+            // Return to Home
+            dismissRecentsToHome(animated);
+            return true;
+        }
+        return false;
+    }
+
+    /** Called with the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mFinishedOnStartup = false;
+
+        // In the case that the activity starts up before the Recents component has initialized
+        // (usually when debugging/pushing the SysUI apk), just finish this activity.
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        if (ssp == null) {
+            mFinishedOnStartup = true;
+            finish();
+            return;
+        }
+
+        // Register this activity with the event bus
+        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
+
+        // Initialize the package monitor
+        mPackageMonitor.register(this, Looper.getMainLooper(), UserHandle.ALL,
+                true /* externalStorage */);
+
+        // Select theme based on wallpaper colors
+        mColorExtractor = Dependency.get(SysuiColorExtractor.class);
+        mColorExtractor.addOnColorsChangedListener(this);
+        mUsingDarkText = mColorExtractor.getColors(ColorExtractor.TYPE_DARK,
+                WallpaperManager.FLAG_SYSTEM, true).supportsDarkText();
+        setTheme(mUsingDarkText ? R.style.RecentsTheme_Wallpaper_Light
+                : R.style.RecentsTheme_Wallpaper);
+
+        // Set the Recents layout
+        setContentView(R.layout.recents);
+        takeKeyEvents(true);
+        mRecentsView = findViewById(R.id.recents_view);
+        mScrimViews = new SystemBarScrimViews(this);
+        getWindow().getAttributes().privateFlags |=
+                WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+        }
+
+        mLastConfig = new Configuration(Utilities.getAppConfiguration(this));
+
+        // Set the window background
+        mRecentsView.updateBackgroundScrim(getWindow(), isInMultiWindowMode());
+
+        // Create the home intent runnable
+        mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
+        mHomeIntent.addCategory(Intent.CATEGORY_HOME);
+        mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+        // Register the broadcast receiver to handle messages when the screen is turned off
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        registerReceiver(mSystemBroadcastReceiver, filter);
+
+        getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+        // Reload the stack view whenever we are made visible again
+        reloadStackView();
+
+        // Notify that recents is now visible
+        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
+        MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY);
+
+        // Getting system scrim colors ignoring wallpaper visibility since it should never be grey.
+        ColorExtractor.GradientColors systemColors = mColorExtractor.getColors(
+                ColorExtractor.TYPE_DARK, WallpaperManager.FLAG_SYSTEM, true);
+        // We don't want to interpolate colors because we're defining the initial state.
+        // Gradient should be set/ready when you open "Recents".
+        mRecentsView.setScrimColors(systemColors, false);
+
+        // Notify of the next draw
+        mRecentsView.getViewTreeObserver().addOnPreDrawListener(mRecentsDrawnEventListener);
+
+        // If Recents was restarted, then it should complete the enter animation with partially
+        // reset launch state with dock, app and home set to false
+        Object isRelaunching = getLastNonConfigurationInstance();
+        if (isRelaunching != null && isRelaunching instanceof Boolean && (boolean) isRelaunching) {
+            RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
+            launchState.launchedViaDockGesture = false;
+            launchState.launchedFromApp = false;
+            launchState.launchedFromHome = false;
+            onEnterAnimationComplete();
+        }
+        mRecentsStartRequested = false;
+    }
+
+    @Override
+    public void onColorsChanged(ColorExtractor colorExtractor, int which) {
+        if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
+            // Recents doesn't care about the wallpaper being visible or not, it always
+            // wants to scrim with wallpaper colors
+            ColorExtractor.GradientColors colors = mColorExtractor.getColors(
+                    WallpaperManager.FLAG_SYSTEM,
+                    ColorExtractor.TYPE_DARK, true /* ignoreVis */);
+            boolean darkText = colors.supportsDarkText();
+            if (darkText != mUsingDarkText) {
+                mUsingDarkText = darkText;
+                setTheme(mUsingDarkText ? R.style.RecentsTheme_Wallpaper_Light
+                        : R.style.RecentsTheme_Wallpaper);
+                mRecentsView.reevaluateStyles();
+            }
+            mRecentsView.setScrimColors(colors, true /* animated */);
+        }
+    }
+
+    /**
+     * Reloads the stack views upon launching Recents.
+     */
+    private void reloadStackView() {
+        // If the Recents component has preloaded a load plan, then use that to prevent
+        // reconstructing the task stack
+        RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
+        RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan();
+        if (loadPlan == null) {
+            loadPlan = new RecentsTaskLoadPlan(this);
+        }
+
+        // Start loading tasks according to the load plan
+        RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        if (!loadPlan.hasTasks()) {
+            loader.preloadTasks(loadPlan, launchState.launchedToTaskId);
+        }
+
+        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
+        loadOpts.runningTaskId = launchState.launchedToTaskId;
+        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
+        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
+        loader.loadTasks(loadPlan, loadOpts);
+        TaskStack stack = loadPlan.getTaskStack();
+        mRecentsView.onReload(stack, mIsVisible);
+
+        // Update the nav bar scrim, but defer the animation until the enter-window event
+        boolean animateNavBarScrim = !launchState.launchedViaDockGesture;
+        mScrimViews.updateNavBarScrim(animateNavBarScrim, stack.getTaskCount() > 0, null);
+
+        // If this is a new instance relaunched by AM, without going through the normal mechanisms,
+        // then we have to manually trigger the enter animation state
+        boolean wasLaunchedByAm = !launchState.launchedFromHome &&
+                !launchState.launchedFromApp;
+        if (wasLaunchedByAm) {
+            EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+        }
+
+        // Keep track of whether we launched from the nav bar button or via alt-tab
+        if (launchState.launchedWithAltTab) {
+            MetricsLogger.count(this, "overview_trigger_alttab", 1);
+        } else {
+            MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
+        }
+
+        // Keep track of whether we launched from an app or from home
+        if (launchState.launchedFromApp) {
+            Task launchTarget = stack.getLaunchTarget();
+            int launchTaskIndexInStack = launchTarget != null
+                    ? stack.indexOfTask(launchTarget)
+                    : 0;
+            MetricsLogger.count(this, "overview_source_app", 1);
+            // If from an app, track the stack index of the app in the stack (for affiliated tasks)
+            MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
+        } else {
+            MetricsLogger.count(this, "overview_source_home", 1);
+        }
+
+        // Keep track of the total stack task count
+        int taskCount = mRecentsView.getStack().getTaskCount();
+        MetricsLogger.histogram(this, "overview_task_count", taskCount);
+
+        // After we have resumed, set the visible state until the next onStop() call
+        mIsVisible = true;
+    }
+
+    @Override
+    public void onEnterAnimationComplete() {
+        super.onEnterAnimationComplete();
+        EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+
+        // Workaround for b/64694148: The animation started callback is not made (see
+        // RecentsImpl.getThumbnailTransitionActivityOptions) so reset the transition-waiting state
+        // once the enter animation has completed.
+        EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
+    }
+
+    @Override
+    public Object onRetainNonConfigurationInstance() {
+        return true;
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        mIgnoreAltTabRelease = false;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        // Notify of the config change
+        Configuration newDeviceConfiguration = Utilities.getAppConfiguration(this);
+        int numStackTasks = mRecentsView.getStack().getTaskCount();
+        EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
+                mLastConfig.orientation != newDeviceConfiguration.orientation,
+                mLastConfig.densityDpi != newDeviceConfiguration.densityDpi, numStackTasks > 0));
+
+        mLastConfig.updateFrom(newDeviceConfiguration);
+    }
+
+    @Override
+    public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
+        super.onMultiWindowModeChanged(isInMultiWindowMode);
+
+        // Set the window background
+        mRecentsView.updateBackgroundScrim(getWindow(), isInMultiWindowMode);
+
+        // Reload the task stack view if we are still visible to pick up the change in tasks that
+        // result from entering/exiting multi-window
+        if (mIsVisible) {
+            reloadTaskStack(isInMultiWindowMode, true /* sendConfigChangedEvent */);
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        // Notify that recents is now hidden
+        mIsVisible = false;
+        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
+        MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
+        LegacyRecentsImpl.getTaskLoader().getHighResThumbnailLoader().setVisible(false);
+
+        // When recents starts again before onStop, do not reset launch flags so entrance animation
+        // can run
+        if (!isChangingConfigurations() && !mRecentsStartRequested) {
+            // Workaround for b/22542869, if the RecentsActivity is started again, but without going
+            // through SystemUI, we need to reset the config launch flags to ensure that we do not
+            // wait on the system to send a signal that was never queued.
+            RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+            RecentsActivityLaunchState launchState = config.getLaunchState();
+            launchState.reset();
+        }
+
+        // Force a gc to attempt to clean up bitmap references more quickly (b/38258699)
+        LegacyRecentsImpl.getSystemServices().gc();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        // In the case that the activity finished on startup, just skip the unregistration below
+        if (mFinishedOnStartup) {
+            return;
+        }
+
+        // Unregister the system broadcast receivers
+        unregisterReceiver(mSystemBroadcastReceiver);
+
+        // Unregister any broadcast receivers for the task loader
+        mPackageMonitor.unregister();
+
+        EventBus.getDefault().unregister(this);
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        EventBus.getDefault().register(mScrimViews, EVENT_BUS_PRIORITY);
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        EventBus.getDefault().unregister(mScrimViews);
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+        RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
+        if (loader != null) {
+            loader.onTrimMemory(level);
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_TAB: {
+                int altTabKeyDelay = getResources().getInteger(R.integer.recents_alt_tab_key_delay);
+                boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
+                        mLastTabKeyEventTime) > altTabKeyDelay;
+                if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
+                    // Focus the next task in the stack
+                    final boolean backward = event.isShiftPressed();
+                    if (backward) {
+                        EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
+                    } else {
+                        EventBus.getDefault().send(new FocusNextTaskViewEvent());
+                    }
+                    mLastTabKeyEventTime = SystemClock.elapsedRealtime();
+
+                    // In the case of another ALT event, don't ignore the next release
+                    if (event.isAltPressed()) {
+                        mIgnoreAltTabRelease = false;
+                    }
+                }
+                return true;
+            }
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT: {
+                final Direction direction = NavigateTaskViewEvent.getDirectionFromKeyCode(keyCode);
+                EventBus.getDefault().send(new NavigateTaskViewEvent(direction));
+                return true;
+            }
+            case KeyEvent.KEYCODE_DEL:
+            case KeyEvent.KEYCODE_FORWARD_DEL: {
+                if (event.getRepeatCount() <= 0) {
+                    EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
+
+                    // Keep track of deletions by keyboard
+                    MetricsLogger.histogram(this, "overview_task_dismissed_source",
+                            Constants.Metrics.DismissSourceKeyboard);
+                    return true;
+                }
+            }
+            default:
+                break;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public void onUserInteraction() {
+        EventBus.getDefault().send(mUserInteractionEvent);
+    }
+
+    @Override
+    public void onBackPressed() {
+        // Back behaves like the recents button so just trigger a toggle event
+        EventBus.getDefault().send(new ToggleRecentsEvent());
+    }
+
+    /**** EventBus events ****/
+
+    public final void onBusEvent(ToggleRecentsEvent event) {
+        RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
+        if (launchState.launchedFromHome) {
+            dismissRecentsToHome(true /* animateTaskViews */);
+        } else {
+            dismissRecentsToLaunchTargetTaskOrHome();
+        }
+    }
+
+    public final void onBusEvent(RecentsActivityStartingEvent event) {
+        mRecentsStartRequested = true;
+    }
+
+    public final void onBusEvent(HideRecentsEvent event) {
+        if (event.triggeredFromAltTab) {
+            // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
+            if (!mIgnoreAltTabRelease) {
+                dismissRecentsToFocusedTaskOrHome();
+            }
+        } else if (event.triggeredFromHomeKey) {
+            dismissRecentsToHome(true /* animateTaskViews */);
+
+            // Cancel any pending dozes
+            EventBus.getDefault().send(mUserInteractionEvent);
+        } else {
+            // Do nothing
+        }
+    }
+
+    public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
+        mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
+        mRecentsView.invalidate();
+    }
+
+    public final void onBusEvent(ExitRecentsWindowFirstAnimationFrameEvent event) {
+        mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
+        mRecentsView.invalidate();
+    }
+
+    public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
+        mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
+        mRecentsView.invalidate();
+    }
+
+    public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
+        RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
+        int launchToTaskId = launchState.launchedToTaskId;
+        if (launchToTaskId != -1 &&
+                (event.launchTask == null || launchToTaskId != event.launchTask.key.id)) {
+            ActivityManagerWrapper am = ActivityManagerWrapper.getInstance();
+            am.cancelWindowTransition(launchState.launchedToTaskId);
+        }
+    }
+
+    public final void onBusEvent(ShowApplicationInfoEvent event) {
+        // Create a new task stack with the application info details activity
+        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+                Uri.fromParts("package", event.task.key.getComponent().getPackageName(), null));
+        intent.setComponent(intent.resolveActivity(getPackageManager()));
+        TaskStackBuilder.create(this)
+                .addNextIntentWithParentStack(intent).startActivities(null,
+                        new UserHandle(event.task.key.userId));
+
+        // Keep track of app-info invocations
+        MetricsLogger.count(this, "overview_app_info", 1);
+    }
+
+    public final void onBusEvent(ShowIncompatibleAppOverlayEvent event) {
+        if (mIncompatibleAppOverlay == null) {
+            mIncompatibleAppOverlay = Utilities.findViewStubById(this,
+                    R.id.incompatible_app_overlay_stub).inflate();
+            mIncompatibleAppOverlay.setWillNotDraw(false);
+            mIncompatibleAppOverlay.setVisibility(View.VISIBLE);
+        }
+        mIncompatibleAppOverlay.animate()
+                .alpha(1f)
+                .setDuration(INCOMPATIBLE_APP_ALPHA_DURATION)
+                .setInterpolator(Interpolators.ALPHA_IN)
+                .start();
+    }
+
+    public final void onBusEvent(HideIncompatibleAppOverlayEvent event) {
+        if (mIncompatibleAppOverlay != null) {
+            mIncompatibleAppOverlay.animate()
+                    .alpha(0f)
+                    .setDuration(INCOMPATIBLE_APP_ALPHA_DURATION)
+                    .setInterpolator(Interpolators.ALPHA_OUT)
+                    .start();
+        }
+    }
+
+    public final void onBusEvent(DeleteTaskDataEvent event) {
+        // Remove any stored data from the loader
+        RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
+        loader.deleteTaskData(event.task, false);
+
+        // Remove the task from activity manager
+        ActivityManagerWrapper.getInstance().removeTask(event.task.key.id);
+    }
+
+    public final void onBusEvent(TaskViewDismissedEvent event) {
+        mRecentsView.updateScrimOpacity();
+    }
+
+    public final void onBusEvent(AllTaskViewsDismissedEvent event) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        if (ssp.hasDockedTask()) {
+            mRecentsView.showEmptyView(event.msgResId);
+        } else {
+            // Just go straight home (no animation necessary because there are no more task views)
+            dismissRecentsToHome(false /* animateTaskViews */);
+        }
+
+        // Keep track of all-deletions
+        MetricsLogger.count(this, "overview_task_all_dismissed", 1);
+    }
+
+    public final void onBusEvent(LaunchTaskSucceededEvent event) {
+        MetricsLogger.histogram(this, "overview_task_launch_index", event.taskIndexFromStackFront);
+    }
+
+    public final void onBusEvent(LaunchTaskFailedEvent event) {
+        // Return to Home
+        dismissRecentsToHome(true /* animateTaskViews */);
+
+        MetricsLogger.count(this, "overview_task_launch_failed", 1);
+    }
+
+    public final void onBusEvent(ScreenPinningRequestEvent event) {
+        MetricsLogger.count(this, "overview_screen_pinned", 1);
+    }
+
+    public final void onBusEvent(StackViewScrolledEvent event) {
+        // Once the user has scrolled while holding alt-tab, then we should ignore the release of
+        // the key
+        mIgnoreAltTabRelease = true;
+    }
+
+    public final void onBusEvent(final DockedTopTaskEvent event) {
+        mRecentsView.getViewTreeObserver().addOnPreDrawListener(mRecentsDrawnEventListener);
+        mRecentsView.invalidate();
+    }
+
+    public final void onBusEvent(final ActivityUnpinnedEvent event) {
+        if (mIsVisible) {
+            // Skip the configuration change event as the PiP activity does not actually affect the
+            // config of recents
+            reloadTaskStack(isInMultiWindowMode(), false /* sendConfigChangedEvent */);
+        }
+    }
+
+    private void reloadTaskStack(boolean isInMultiWindowMode, boolean sendConfigChangedEvent) {
+        // Reload the task stack completely
+        RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
+        RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(this);
+        loader.preloadTasks(loadPlan, -1 /* runningTaskId */);
+
+        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
+        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
+        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
+        loader.loadTasks(loadPlan, loadOpts);
+
+        TaskStack stack = loadPlan.getTaskStack();
+        int numStackTasks = stack.getTaskCount();
+        boolean showDeferredAnimation = numStackTasks > 0;
+
+        if (sendConfigChangedEvent) {
+            EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */,
+                    false /* fromDeviceOrientationChange */, false /* fromDisplayDensityChange */,
+                    numStackTasks > 0));
+        }
+        EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode,
+                showDeferredAnimation, stack));
+    }
+
+    @Override
+    public boolean onPreDraw() {
+        mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
+        return true;
+    }
+
+    public void onPackageChanged(String packageName, int userId) {
+        LegacyRecentsImpl.getTaskLoader().onPackageChanged(packageName);
+        EventBus.getDefault().send(new PackagesChangedEvent(packageName, userId));
+    }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        EventBus.getDefault().dump(prefix, writer);
+        LegacyRecentsImpl.getTaskLoader().dump(prefix, writer);
+
+        String id = Integer.toHexString(System.identityHashCode(this));
+
+        writer.print(prefix); writer.print(TAG);
+        writer.print(" visible="); writer.print(mIsVisible ? "Y" : "N");
+        writer.print(" currentTime="); writer.print(System.currentTimeMillis());
+        writer.print(" [0x"); writer.print(id); writer.print("]");
+        writer.println();
+
+        if (mRecentsView != null) {
+            mRecentsView.dump(prefix, writer);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivityLaunchState.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivityLaunchState.java
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsConfiguration.java
new file mode 100644
index 0000000..ee53734
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+
+import android.os.SystemProperties;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.views.DockState;
+
+/**
+ * Represents the dock regions for each orientation.
+ */
+class DockRegion {
+    public static DockState[] PHONE_LANDSCAPE = {
+            // We only allow docking to the left in landscape for now on small devices
+            DockState.LEFT
+    };
+    public static DockState[] PHONE_PORTRAIT = {
+            // We only allow docking to the top for now on small devices
+            DockState.TOP
+    };
+    public static DockState[] TABLET_LANDSCAPE = {
+            DockState.LEFT,
+            DockState.RIGHT
+    };
+    public static DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT;
+}
+
+/**
+ * Application resources that can be retrieved from the application context and are not specifically
+ * tied to the current activity.
+ */
+public class RecentsConfiguration {
+
+    private static final int LARGE_SCREEN_MIN_DP = 600;
+    private static final int XLARGE_SCREEN_MIN_DP = 720;
+
+    // Launch states
+    public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState();
+
+    // Since the positions in Recents has to be calculated globally (before the RecentsActivity
+    // starts), we need to calculate some resource values ourselves, instead of relying on framework
+    // resources.
+    public final boolean isLargeScreen;
+    public final boolean isXLargeScreen;
+    public final int smallestWidth;
+
+    /** Misc **/
+    public boolean fakeShadows;
+    public int svelteLevel;
+
+    // Whether this product supports Grid-based Recents. If this is field is set to true, then
+    // Recents will layout task views in a grid mode when there's enough space in the screen.
+    public boolean isGridEnabled;
+
+    // Support for Android Recents for low ram devices. If this field is set to true, then Recents
+    // will use the alternative layout.
+    public boolean isLowRamDevice;
+
+    // Enable drag and drop split from Recents. Disabled for low ram devices.
+    public boolean dragToSplitEnabled;
+
+    private final Context mAppContext;
+
+    public RecentsConfiguration(Context context) {
+        // Load only resources that can not change after the first load either through developer
+        // settings or via multi window
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        mAppContext = context.getApplicationContext();
+        Resources res = mAppContext.getResources();
+        fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
+        svelteLevel = res.getInteger(R.integer.recents_svelte_level);
+        isGridEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
+        isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
+        dragToSplitEnabled = !isLowRamDevice;
+
+        float screenDensity = context.getResources().getDisplayMetrics().density;
+        smallestWidth = ssp.getDeviceSmallestWidth();
+        isLargeScreen = smallestWidth >= (int) (screenDensity * LARGE_SCREEN_MIN_DP);
+        isXLargeScreen = smallestWidth >= (int) (screenDensity * XLARGE_SCREEN_MIN_DP);
+    }
+
+    /**
+     * Returns the activity launch state.
+     * TODO: This will be refactored out of RecentsConfiguration.
+     */
+    public RecentsActivityLaunchState getLaunchState() {
+        return mLaunchState;
+    }
+
+    /**
+     * Returns the preferred dock states for the current orientation.
+     * @return a list of dock states for device and its orientation
+     */
+    public DockState[] getDockStatesForCurrentOrientation() {
+        boolean isLandscape = mAppContext.getResources().getConfiguration().orientation ==
+                Configuration.ORIENTATION_LANDSCAPE;
+        RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+        if (config.isLargeScreen) {
+            return isLandscape ? DockRegion.TABLET_LANDSCAPE : DockRegion.TABLET_PORTRAIT;
+        } else {
+            return isLandscape ? DockRegion.PHONE_LANDSCAPE : DockRegion.PHONE_PORTRAIT;
+        }
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsDebugFlags.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsDebugFlags.java
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImpl.java
new file mode 100644
index 0000000..3e5acab
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImpl.java
@@ -0,0 +1,1118 @@
+/*
+ * Copyright (C) 2015 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.systemui.recents;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.view.View.MeasureSpec;
+
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
+
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.trust.TrustManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentCallbacks2;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.MutableBoolean;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+
+import android.widget.Toast;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.pip.phone.ForegroundThread;
+import com.google.android.collect.Lists;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.policy.DockedDividerUtils;
+import com.android.systemui.R;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.LaunchMostRecentTaskRequestEvent;
+import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
+import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
+import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
+import com.android.systemui.recents.events.component.ActivityPinnedEvent;
+import com.android.systemui.recents.events.component.ActivityUnpinnedEvent;
+import com.android.systemui.recents.events.component.HidePipMenuEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
+import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
+import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
+import com.android.systemui.recents.misc.DozeTrigger;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
+import com.android.systemui.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
+import com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
+import com.android.systemui.recents.views.TaskStackView;
+import com.android.systemui.recents.views.TaskViewHeader;
+import com.android.systemui.recents.views.TaskViewTransform;
+import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
+import com.android.systemui.shared.recents.view.RecentsTransition;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.stackdivider.DividerView;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An implementation of the Recents component for the current user.  For secondary users, this can
+ * be called remotely from the system user.
+ */
+public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener {
+
+    private final static String TAG = "RecentsImpl";
+
+    // The minimum amount of time between each recents button press that we will handle
+    private final static int MIN_TOGGLE_DELAY_MS = 350;
+
+    // The duration within which the user releasing the alt tab (from when they pressed alt tab)
+    // that the fast alt-tab animation will run.  If the user's alt-tab takes longer than this
+    // duration, then we will toggle recents after this duration.
+    private final static int FAST_ALT_TAB_DELAY_MS = 225;
+
+    private final static ArraySet<TaskKey> EMPTY_SET = new ArraySet<>();
+
+    public final static String RECENTS_PACKAGE = "com.android.systemui";
+    public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
+
+    /**
+     * An implementation of SysUiTaskStackChangeListener, that allows us to listen for changes to the system
+     * task stacks and update recents accordingly.
+     */
+    class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
+
+        private OverviewProxyService mOverviewProxyService;
+
+        public TaskStackListenerImpl() {
+            mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+        }
+
+        @Override
+        public void onTaskStackChangedBackground() {
+            // Skip background preloading recents in SystemUI if the overview services is bound
+            if (mOverviewProxyService.isEnabled()) {
+                return;
+            }
+
+            // Check this is for the right user
+            if (!checkCurrentUserId(mContext, false /* debug */)) {
+                return;
+            }
+
+            // Preloads the next task
+            RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+            if (config.svelteLevel == RecentsTaskLoader.SVELTE_NONE) {
+                Rect windowRect = getWindowRect(null /* windowRectOverride */);
+                if (windowRect.isEmpty()) {
+                    return;
+                }
+
+                // Load the next task only if we aren't svelte
+                ActivityManager.RunningTaskInfo runningTaskInfo =
+                        ActivityManagerWrapper.getInstance().getRunningTask();
+                RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
+                RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
+                loader.preloadTasks(plan, -1);
+                TaskStack stack = plan.getTaskStack();
+                RecentsActivityLaunchState launchState = new RecentsActivityLaunchState();
+                RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+
+                synchronized (mBackgroundLayoutAlgorithm) {
+                    // This callback is made when a new activity is launched and the old one is
+                    // paused so ignore the current activity and try and preload the thumbnail for
+                    // the previous one.
+                    updateDummyStackViewLayout(mBackgroundLayoutAlgorithm, stack, windowRect);
+
+                    // Launched from app is always the worst case (in terms of how many
+                    // thumbnails/tasks visible)
+                    launchState.launchedFromApp = true;
+                    mBackgroundLayoutAlgorithm.update(plan.getTaskStack(), EMPTY_SET, launchState,
+                            -1 /* lastScrollPPresent */);
+                    VisibilityReport visibilityReport =
+                            mBackgroundLayoutAlgorithm.computeStackVisibilityReport(
+                                    stack.getTasks());
+
+                    launchOpts.runningTaskId = runningTaskInfo != null ? runningTaskInfo.id : -1;
+                    launchOpts.numVisibleTasks = visibilityReport.numVisibleTasks;
+                    launchOpts.numVisibleTaskThumbnails = visibilityReport.numVisibleThumbnails;
+                    launchOpts.onlyLoadForCache = true;
+                    launchOpts.onlyLoadPausedActivities = true;
+                    launchOpts.loadThumbnails = true;
+                }
+                loader.loadTasks(plan, launchOpts);
+            }
+        }
+
+        @Override
+        public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+            // Check this is for the right user
+            if (!checkCurrentUserId(mContext, false /* debug */)) {
+                return;
+            }
+
+            // This time needs to be fetched the same way the last active time is fetched in
+            // {@link TaskRecord#touchActiveTime}
+            LegacyRecentsImpl.getConfiguration().getLaunchState().launchedFromPipApp = true;
+            LegacyRecentsImpl.getConfiguration().getLaunchState().launchedWithNextPipApp = false;
+            EventBus.getDefault().send(new ActivityPinnedEvent(taskId));
+            consumeInstanceLoadPlan();
+            sLastPipTime = System.currentTimeMillis();
+        }
+
+        @Override
+        public void onActivityUnpinned() {
+            // Check this is for the right user
+            if (!checkCurrentUserId(mContext, false /* debug */)) {
+                return;
+            }
+
+            EventBus.getDefault().send(new ActivityUnpinnedEvent());
+            sLastPipTime = -1;
+        }
+
+        @Override
+        public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
+            // Check this is for the right user
+            if (!checkCurrentUserId(mContext, false /* debug */)) {
+                return;
+            }
+
+            EventBus.getDefault().send(new TaskSnapshotChangedEvent(taskId, snapshot));
+        }
+    }
+
+    protected static RecentsTaskLoadPlan sInstanceLoadPlan;
+    // Stores the last pinned task time
+    protected static long sLastPipTime = -1;
+    // Stores whether we are waiting for a transition to/from recents to start. During this time,
+    // we disallow the user from manually toggling recents until the transition has started.
+    private static boolean mWaitingForTransitionStart = false;
+    // Stores whether or not the user toggled while we were waiting for a transition to/from
+    // recents. In this case, we defer the toggle state until then and apply it immediately after.
+    private static boolean mToggleFollowingTransitionStart = true;
+
+    private Runnable mResetToggleFlagListener = new Runnable() {
+        @Override
+        public void run() {
+            setWaitingForTransitionStart(false);
+        }
+    };
+
+    private TrustManager mTrustManager;
+    protected Context mContext;
+    protected Handler mHandler;
+    TaskStackListenerImpl mTaskStackListener;
+    boolean mDraggingInRecents;
+    boolean mLaunchedWhileDocking;
+
+    // Task launching
+    Rect mTmpBounds = new Rect();
+    TaskViewTransform mTmpTransform = new TaskViewTransform();
+    int mTaskBarHeight;
+
+    // Header (for transition)
+    TaskViewHeader mHeaderBar;
+    final Object mHeaderBarLock = new Object();
+    private TaskStackView mDummyStackView;
+    private TaskStackLayoutAlgorithm mBackgroundLayoutAlgorithm;
+
+    // Variables to keep track of if we need to start recents after binding
+    protected boolean mTriggeredFromAltTab;
+    protected long mLastToggleTime;
+    DozeTrigger mFastAltTabTrigger = new DozeTrigger(FAST_ALT_TAB_DELAY_MS, new Runnable() {
+        @Override
+        public void run() {
+            // When this fires, then the user has not released alt-tab for at least
+            // FAST_ALT_TAB_DELAY_MS milliseconds
+            showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */,
+                    DividerView.INVALID_RECENTS_GROW_TARGET);
+        }
+    });
+
+    private OverviewProxyService.OverviewProxyListener mOverviewProxyListener =
+            new OverviewProxyService.OverviewProxyListener() {
+        @Override
+        public void onConnectionChanged(boolean isConnected) {
+            if (!isConnected) {
+                // Clear everything when the connection to the overview service
+                LegacyRecentsImpl.getTaskLoader().onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
+            }
+        }
+    };
+
+    // Used to reset the dummy stack view
+    private final TaskStack mEmptyTaskStack = new TaskStack();
+
+    public RecentsImpl(Context context) {
+        mContext = context;
+        mHandler = new Handler();
+        mBackgroundLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
+
+        // Initialize the static foreground thread
+        ForegroundThread.get();
+
+        // Register the task stack listener
+        mTaskStackListener = new TaskStackListenerImpl();
+        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+
+        // Initialize the static configuration resources
+        mDummyStackView = new TaskStackView(mContext);
+        reloadResources();
+
+        mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
+    }
+
+    public void onBootCompleted() {
+        // Skip preloading tasks if we are already bound to the service
+        if (Dependency.get(OverviewProxyService.class).isEnabled()) {
+            return;
+        }
+
+        // When we start, preload the data associated with the previous recent tasks.
+        // We can use a new plan since the caches will be the same.
+        RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
+        RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
+        loader.preloadTasks(plan, -1);
+        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+        launchOpts.numVisibleTasks = loader.getIconCacheSize();
+        launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
+        launchOpts.onlyLoadForCache = true;
+        loader.loadTasks(plan, launchOpts);
+    }
+
+    public void onConfigurationChanged() {
+        reloadResources();
+        mDummyStackView.reloadOnConfigurationChange();
+        synchronized (mBackgroundLayoutAlgorithm) {
+            mBackgroundLayoutAlgorithm.reloadOnConfigurationChange(mContext);
+        }
+    }
+
+    /**
+     * This is only called from the system user's Recents.  Secondary users will instead proxy their
+     * visibility change events through to the system user via
+     * {@link LegacyRecentsImpl#onBusEvent(RecentsVisibilityChangedEvent)}.
+     */
+    public void onVisibilityChanged(Context context, boolean visible) {
+        LegacyRecentsImpl.getSystemServices().setRecentsVisibility(visible);
+    }
+
+    /**
+     * This is only called from the system user's Recents.  Secondary users will instead proxy their
+     * visibility change events through to the system user via
+     * {@link LegacyRecentsImpl#onBusEvent(ScreenPinningRequestEvent)}.
+     */
+    public void onStartScreenPinning(Context context, int taskId) {
+        final StatusBar statusBar = getStatusBar();
+        if (statusBar != null) {
+            statusBar.showScreenPinningRequest(taskId, false);
+        }
+    }
+
+    public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
+            boolean animate, int growTarget) {
+        final SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        final MutableBoolean isHomeStackVisible = new MutableBoolean(true);
+        final boolean isRecentsVisible = LegacyRecentsImpl.getSystemServices().isRecentsActivityVisible(
+                isHomeStackVisible);
+        final boolean fromHome = isHomeStackVisible.value;
+        final boolean launchedWhileDockingTask =
+                LegacyRecentsImpl.getSystemServices().getSplitScreenPrimaryStack() != null;
+
+        mTriggeredFromAltTab = triggeredFromAltTab;
+        mDraggingInRecents = draggingInRecents;
+        mLaunchedWhileDocking = launchedWhileDockingTask;
+        if (mFastAltTabTrigger.isAsleep()) {
+            // Fast alt-tab duration has elapsed, fall through to showing Recents and reset
+            mFastAltTabTrigger.stopDozing();
+        } else if (mFastAltTabTrigger.isDozing()) {
+            // Fast alt-tab duration has not elapsed.  If this is triggered by a different
+            // showRecents() call, then ignore that call for now.
+            // TODO: We can not handle quick tabs that happen between the initial showRecents() call
+            //       that started the activity and the activity starting up.  The severity of this
+            //       is inversely proportional to the FAST_ALT_TAB_DELAY_MS duration though.
+            if (!triggeredFromAltTab) {
+                return;
+            }
+            mFastAltTabTrigger.stopDozing();
+        } else if (triggeredFromAltTab) {
+            // The fast alt-tab detector is not yet running, so start the trigger and wait for the
+            // hideRecents() call, or for the fast alt-tab duration to elapse
+            mFastAltTabTrigger.startDozing();
+            return;
+        }
+
+        try {
+            // Check if the top task is in the home stack, and start the recents activity
+            final boolean forceVisible = launchedWhileDockingTask || draggingInRecents;
+            if (forceVisible || !isRecentsVisible) {
+                ActivityManager.RunningTaskInfo runningTask =
+                        ActivityManagerWrapper.getInstance().getRunningTask();
+                startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
+                        isHomeStackVisible.value || fromHome, animate, growTarget);
+            }
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Failed to launch RecentsActivity", e);
+        }
+    }
+
+    public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+        if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
+            // The user has released alt-tab before the trigger has run, so just show the next
+            // task immediately
+            showNextTask();
+
+            // Cancel the fast alt-tab trigger
+            mFastAltTabTrigger.stopDozing();
+            return;
+        }
+
+        // Defer to the activity to handle hiding recents, if it handles it, then it must still
+        // be visible
+        EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
+                triggeredFromHomeKey));
+    }
+
+    public void toggleRecents(int growTarget) {
+        if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
+            return;
+        }
+
+        // Skip this toggle if we are already waiting to trigger recents via alt-tab
+        if (mFastAltTabTrigger.isDozing()) {
+            return;
+        }
+
+        if (mWaitingForTransitionStart) {
+            mToggleFollowingTransitionStart = true;
+            return;
+        }
+
+        mDraggingInRecents = false;
+        mLaunchedWhileDocking = false;
+        mTriggeredFromAltTab = false;
+
+        try {
+            MutableBoolean isHomeStackVisible = new MutableBoolean(true);
+            long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
+
+            SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+            if (ssp.isRecentsActivityVisible(isHomeStackVisible)) {
+                RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+                RecentsActivityLaunchState launchState = config.getLaunchState();
+                if (!launchState.launchedWithAltTab) {
+                    if (LegacyRecentsImpl.getConfiguration().isGridEnabled) {
+                        // Has the user tapped quickly?
+                        boolean isQuickTap = elapsedTime < ViewConfiguration.getDoubleTapTimeout();
+                        if (isQuickTap) {
+                            EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
+                        } else {
+                            EventBus.getDefault().post(new LaunchMostRecentTaskRequestEvent());
+                        }
+                    } else {
+                        // Launch the next focused task
+                        EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
+                    }
+                } else {
+                    // If the user has toggled it too quickly, then just eat up the event here (it's
+                    // better than showing a janky screenshot).
+                    // NOTE: Ideally, the screenshot mechanism would take the window transform into
+                    // account
+                    if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
+                        return;
+                    }
+
+                    EventBus.getDefault().post(new ToggleRecentsEvent());
+                    mLastToggleTime = SystemClock.elapsedRealtime();
+                }
+                return;
+            } else {
+                // If the user has toggled it too quickly, then just eat up the event here (it's
+                // better than showing a janky screenshot).
+                // NOTE: Ideally, the screenshot mechanism would take the window transform into
+                // account
+                if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
+                    return;
+                }
+
+                // Otherwise, start the recents activity
+                ActivityManager.RunningTaskInfo runningTask =
+                        ActivityManagerWrapper.getInstance().getRunningTask();
+                startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
+                        isHomeStackVisible.value, true /* animate */, growTarget);
+
+                // Only close the other system windows if we are actually showing recents
+                ActivityManagerWrapper.getInstance().closeSystemWindows(
+                        SYSTEM_DIALOG_REASON_RECENT_APPS);
+                mLastToggleTime = SystemClock.elapsedRealtime();
+            }
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Failed to launch RecentsActivity", e);
+        }
+    }
+
+    public void preloadRecents() {
+        if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
+            return;
+        }
+
+        // Skip preloading recents when keyguard is showing
+        final StatusBar statusBar = getStatusBar();
+        if (statusBar != null && statusBar.isKeyguardShowing()) {
+            return;
+        }
+
+        // Preload only the raw task list into a new load plan (which will be consumed by the
+        // RecentsActivity) only if there is a task to animate to.  Post this to ensure that we
+        // don't block the touch feedback on the nav bar button which triggers this.
+        mHandler.post(() -> {
+            SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+            if (!ssp.isRecentsActivityVisible(null)) {
+                ActivityManager.RunningTaskInfo runningTask =
+                        ActivityManagerWrapper.getInstance().getRunningTask();
+                if (runningTask == null) {
+                    return;
+                }
+
+                RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
+                sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);
+                loader.preloadTasks(sInstanceLoadPlan, runningTask.id);
+                TaskStack stack = sInstanceLoadPlan.getTaskStack();
+                if (stack.getTaskCount() > 0) {
+                    // Only preload the icon (but not the thumbnail since it may not have been taken
+                    // for the pausing activity)
+                    preloadIcon(runningTask.id);
+
+                    // At this point, we don't know anything about the stack state.  So only
+                    // calculate the dimensions of the thumbnail that we need for the transition
+                    // into Recents, but do not draw it until we construct the activity options when
+                    // we start Recents
+                    updateHeaderBarLayout(stack, null /* window rect override*/);
+                }
+            }
+        });
+    }
+
+    public void cancelPreloadingRecents() {
+        // Do nothing
+    }
+
+    public void onDraggingInRecents(float distanceFromTop) {
+        EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEvent(distanceFromTop));
+    }
+
+    public void onDraggingInRecentsEnded(float velocity) {
+        EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEndedEvent(velocity));
+    }
+
+    public void onShowCurrentUserToast(int msgResId, int msgLength) {
+        Toast.makeText(mContext, msgResId, msgLength).show();
+    }
+
+    /**
+     * Transitions to the next recent task in the stack.
+     */
+    public void showNextTask() {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
+        RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
+        loader.preloadTasks(plan, -1);
+        TaskStack focusedStack = plan.getTaskStack();
+
+        // Return early if there are no tasks in the focused stack
+        if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
+
+        // Return early if there is no running task
+        ActivityManager.RunningTaskInfo runningTask =
+                ActivityManagerWrapper.getInstance().getRunningTask();
+        if (runningTask == null) return;
+
+        // Find the task in the recents list
+        boolean isRunningTaskInHomeStack =
+                runningTask.configuration.windowConfiguration.getActivityType()
+                        == ACTIVITY_TYPE_HOME;
+        ArrayList<Task> tasks = focusedStack.getTasks();
+        Task toTask = null;
+        ActivityOptions launchOpts = null;
+        int taskCount = tasks.size();
+        for (int i = taskCount - 1; i >= 1; i--) {
+            Task task = tasks.get(i);
+            if (isRunningTaskInHomeStack) {
+                toTask = tasks.get(i - 1);
+                launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+                        R.anim.recents_launch_next_affiliated_task_target,
+                        R.anim.recents_fast_toggle_app_home_exit);
+                break;
+            } else if (task.key.id == runningTask.id) {
+                toTask = tasks.get(i - 1);
+                launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+                        R.anim.recents_launch_prev_affiliated_task_target,
+                        R.anim.recents_launch_prev_affiliated_task_source);
+                break;
+            }
+        }
+
+        // Return early if there is no next task
+        if (toTask == null) {
+            ssp.startInPlaceAnimationOnFrontMostApplication(
+                    ActivityOptions.makeCustomInPlaceAnimation(mContext,
+                            R.anim.recents_launch_prev_affiliated_task_bounce));
+            return;
+        }
+
+        // Launch the task
+        ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(toTask.key, launchOpts,
+                null /* resultCallback */, null /* resultCallbackHandler */);
+    }
+
+    /**
+     * Transitions to the next affiliated task.
+     */
+    public void showRelativeAffiliatedTask(boolean showNextTask) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
+        RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
+        loader.preloadTasks(plan, -1);
+        TaskStack focusedStack = plan.getTaskStack();
+
+        // Return early if there are no tasks in the focused stack
+        if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
+
+        // Return early if there is no running task (can't determine affiliated tasks in this case)
+        ActivityManager.RunningTaskInfo runningTask =
+                ActivityManagerWrapper.getInstance().getRunningTask();
+        final int activityType = runningTask.configuration.windowConfiguration.getActivityType();
+        if (runningTask == null) return;
+        // Return early if the running task is in the home/recents stack (optimization)
+        if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) return;
+
+        // Find the task in the recents list
+        ArrayList<Task> tasks = focusedStack.getTasks();
+        Task toTask = null;
+        ActivityOptions launchOpts = null;
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasks.get(i);
+            if (task.key.id == runningTask.id) {
+                if (showNextTask) {
+                    if ((i + 1) < taskCount) {
+                        toTask = tasks.get(i + 1);
+                        launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+                                R.anim.recents_launch_next_affiliated_task_target,
+                                R.anim.recents_launch_next_affiliated_task_source);
+                    }
+                } else {
+                    if ((i - 1) >= 0) {
+                        toTask = tasks.get(i - 1);
+                        launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+                                R.anim.recents_launch_prev_affiliated_task_target,
+                                R.anim.recents_launch_prev_affiliated_task_source);
+                    }
+                }
+                break;
+            }
+        }
+
+        // Return early if there is no next task
+        if (toTask == null) {
+            if (showNextTask) {
+                ssp.startInPlaceAnimationOnFrontMostApplication(
+                        ActivityOptions.makeCustomInPlaceAnimation(mContext,
+                                R.anim.recents_launch_next_affiliated_task_bounce));
+            } else {
+                ssp.startInPlaceAnimationOnFrontMostApplication(
+                        ActivityOptions.makeCustomInPlaceAnimation(mContext,
+                                R.anim.recents_launch_prev_affiliated_task_bounce));
+            }
+            return;
+        }
+
+        // Keep track of actually launched affiliated tasks
+        MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
+
+        // Launch the task
+        ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(toTask.key, launchOpts,
+                null /* resultListener */, null /* resultCallbackHandler */);
+    }
+
+    public void splitPrimaryTask(int taskId, int stackCreateMode, Rect initialBounds) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+
+        // Make sure we inform DividerView before we actually start the activity so we can change
+        // the resize mode already.
+        if (ssp.setTaskWindowingModeSplitScreenPrimary(taskId, stackCreateMode, initialBounds)) {
+            EventBus.getDefault().send(new DockedTopTaskEvent(initialBounds));
+        }
+    }
+
+    public void setWaitingForTransitionStart(boolean waitingForTransitionStart) {
+        if (mWaitingForTransitionStart == waitingForTransitionStart) {
+            return;
+        }
+
+        mWaitingForTransitionStart = waitingForTransitionStart;
+        if (!waitingForTransitionStart && mToggleFollowingTransitionStart) {
+            mHandler.post(() -> toggleRecents(DividerView.INVALID_RECENTS_GROW_TARGET));
+        }
+        mToggleFollowingTransitionStart = false;
+    }
+
+    /**
+     * Returns the preloaded load plan and invalidates it.
+     */
+    public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
+        RecentsTaskLoadPlan plan = sInstanceLoadPlan;
+        sInstanceLoadPlan = null;
+        return plan;
+    }
+
+    /**
+     * @return the time at which a task last entered picture-in-picture.
+     */
+    public static long getLastPipTime() {
+        return sLastPipTime;
+    }
+
+    /**
+     * Clears the time at which a task last entered picture-in-picture.
+     */
+    public static void clearLastPipTime() {
+        sLastPipTime = -1;
+    }
+
+    /**
+     * Reloads all the resources for the current configuration.
+     */
+    private void reloadResources() {
+        Resources res = mContext.getResources();
+
+        mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(mContext,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_grid_task_view_header_height);
+
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
+                null, false);
+        mHeaderBar.setLayoutDirection(res.getConfiguration().getLayoutDirection());
+    }
+
+    private void updateDummyStackViewLayout(TaskStackLayoutAlgorithm stackLayout,
+            TaskStack stack, Rect windowRect) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        Rect displayRect = ssp.getDisplayRect();
+        Rect systemInsets = new Rect();
+        ssp.getStableInsets(systemInsets);
+
+        // When docked, the nav bar insets are consumed and the activity is measured without insets.
+        // However, the window bounds include the insets, so we need to subtract them here to make
+        // them identical.
+        if (ssp.hasDockedTask()) {
+            if (systemInsets.bottom < windowRect.height()) {
+                // Only apply inset if it isn't going to cause the rect height to go negative.
+                windowRect.bottom -= systemInsets.bottom;
+            }
+            systemInsets.bottom = 0;
+        }
+        calculateWindowStableInsets(systemInsets, windowRect, displayRect);
+        windowRect.offsetTo(0, 0);
+
+        // Rebind the header bar and draw it for the transition
+        stackLayout.setSystemInsets(systemInsets);
+        if (stack != null) {
+            stackLayout.getTaskStackBounds(displayRect, windowRect, systemInsets.top,
+                    systemInsets.left, systemInsets.right, mTmpBounds);
+            stackLayout.reset();
+            stackLayout.initialize(displayRect, windowRect, mTmpBounds);
+        }
+    }
+
+    private Rect getWindowRect(Rect windowRectOverride) {
+       return windowRectOverride != null
+                ? new Rect(windowRectOverride)
+                : LegacyRecentsImpl.getSystemServices().getWindowRect();
+    }
+
+    /**
+     * Prepares the header bar layout for the next transition, if the task view bounds has changed
+     * since the last call, it will attempt to re-measure and layout the header bar to the new size.
+     *
+     * @param stack the stack to initialize the stack layout with
+     * @param windowRectOverride the rectangle to use when calculating the stack state which can
+     *                           be different from the current window rect if recents is resizing
+     *                           while being launched
+     */
+    private void updateHeaderBarLayout(TaskStack stack, Rect windowRectOverride) {
+        Rect windowRect = getWindowRect(windowRectOverride);
+        int taskViewWidth = 0;
+        boolean useGridLayout = mDummyStackView.useGridLayout();
+        updateDummyStackViewLayout(mDummyStackView.getStackAlgorithm(), stack, windowRect);
+        if (stack != null) {
+            TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
+            mDummyStackView.getStack().removeAllTasks(false /* notifyStackChanges */);
+            mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
+            // Get the width of a task view so that we know how wide to draw the header bar.
+            if (useGridLayout) {
+                TaskGridLayoutAlgorithm gridLayout = mDummyStackView.getGridAlgorithm();
+                gridLayout.initialize(windowRect);
+                taskViewWidth = (int) gridLayout.getTransform(0 /* taskIndex */,
+                        stack.getTaskCount(), new TaskViewTransform(),
+                        stackLayout).rect.width();
+            } else {
+                Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
+                if (!taskViewBounds.isEmpty()) {
+                    taskViewWidth = taskViewBounds.width();
+                }
+            }
+        }
+
+        if (stack != null && taskViewWidth > 0) {
+            synchronized (mHeaderBarLock) {
+                if (mHeaderBar.getMeasuredWidth() != taskViewWidth ||
+                        mHeaderBar.getMeasuredHeight() != mTaskBarHeight) {
+                    if (useGridLayout) {
+                        mHeaderBar.setShouldDarkenBackgroundColor(true);
+                        mHeaderBar.setNoUserInteractionState();
+                    }
+                    mHeaderBar.forceLayout();
+                    mHeaderBar.measure(
+                            MeasureSpec.makeMeasureSpec(taskViewWidth, MeasureSpec.EXACTLY),
+                            MeasureSpec.makeMeasureSpec(mTaskBarHeight, MeasureSpec.EXACTLY));
+                }
+                mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
+            }
+        }
+    }
+
+    /**
+     * Given the stable insets and the rect for our window, calculates the insets that affect our
+     * window.
+     */
+    private void calculateWindowStableInsets(Rect inOutInsets, Rect windowRect, Rect displayRect) {
+
+        // Display rect without insets - available app space
+        Rect appRect = new Rect(displayRect);
+        appRect.inset(inOutInsets);
+
+        // Our window intersected with available app space
+        Rect windowRectWithInsets = new Rect(windowRect);
+        windowRectWithInsets.intersect(appRect);
+        inOutInsets.left = windowRectWithInsets.left - windowRect.left;
+        inOutInsets.top = windowRectWithInsets.top - windowRect.top;
+        inOutInsets.right = windowRect.right - windowRectWithInsets.right;
+        inOutInsets.bottom = windowRect.bottom - windowRectWithInsets.bottom;
+    }
+
+    /**
+     * Preloads the icon of a task.
+     */
+    private void preloadIcon(int runningTaskId) {
+        // Ensure that we load the running task's icon
+        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+        launchOpts.runningTaskId = runningTaskId;
+        launchOpts.loadThumbnails = false;
+        launchOpts.onlyLoadForCache = true;
+        LegacyRecentsImpl.getTaskLoader().loadTasks(sInstanceLoadPlan, launchOpts);
+    }
+
+    /**
+     * Creates the activity options for a unknown state->recents transition.
+     */
+    protected ActivityOptions getUnknownTransitionActivityOptions() {
+        return ActivityOptions.makeCustomAnimation(mContext,
+                R.anim.recents_from_unknown_enter,
+                R.anim.recents_from_unknown_exit,
+                mHandler, null);
+    }
+
+    /**
+     * Creates the activity options for a home->recents transition.
+     */
+    protected ActivityOptions getHomeTransitionActivityOptions() {
+        return ActivityOptions.makeCustomAnimation(mContext,
+                R.anim.recents_from_launcher_enter,
+                R.anim.recents_from_launcher_exit,
+                mHandler, null);
+    }
+
+    /**
+     * Creates the activity options for an app->recents transition.
+     */
+    private Pair<ActivityOptions, AppTransitionAnimationSpecsFuture>
+            getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo runningTask,
+                    Rect windowOverrideRect) {
+        final boolean isLowRamDevice = LegacyRecentsImpl.getConfiguration().isLowRamDevice;
+
+        // Update the destination rect
+        Task toTask = new Task();
+        TaskViewTransform toTransform = getThumbnailTransitionTransform(mDummyStackView, toTask,
+                windowOverrideRect);
+
+        RectF toTaskRect = toTransform.rect;
+        AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(mHandler) {
+            @Override
+            public List<AppTransitionAnimationSpecCompat> composeSpecs() {
+                Rect rect = new Rect();
+                toTaskRect.round(rect);
+                Bitmap thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
+                return Lists.newArrayList(new AppTransitionAnimationSpecCompat(toTask.key.id,
+                        thumbnail, rect));
+            }
+        };
+
+        // For low end ram devices, wait for transition flag is reset when Recents entrance
+        // animation is complete instead of when the transition animation starts
+        return new Pair<>(RecentsTransition.createAspectScaleAnimation(mContext, mHandler,
+                false /* scaleUp */, future, isLowRamDevice ? null : mResetToggleFlagListener),
+                future);
+    }
+
+    /**
+     * Returns the transition rect for the given task id.
+     */
+    private TaskViewTransform getThumbnailTransitionTransform(TaskStackView stackView,
+            Task runningTaskOut, Rect windowOverrideRect) {
+        // Find the running task in the TaskStack
+        TaskStack stack = stackView.getStack();
+        Task launchTask = stack.getLaunchTarget();
+        if (launchTask != null) {
+            runningTaskOut.copyFrom(launchTask);
+        } else {
+            // If no task is specified or we can not find the task just use the front most one
+            launchTask = stack.getFrontMostTask();
+            runningTaskOut.copyFrom(launchTask);
+        }
+
+        // Get the transform for the running task
+        stackView.updateLayoutAlgorithm(true /* boundScroll */);
+        stackView.updateToInitialState();
+        stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
+                stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect);
+        return mTmpTransform;
+    }
+
+    /**
+     * Draws the header of a task used for the window animation into a bitmap.
+     */
+    private Bitmap drawThumbnailTransitionBitmap(Task toTask,
+            TaskViewTransform toTransform) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        int width = (int) toTransform.rect.width();
+        int height = (int) toTransform.rect.height();
+        if (toTransform != null && toTask.key != null && width > 0 && height > 0) {
+            synchronized (mHeaderBarLock) {
+                boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
+                mHeaderBar.onTaskViewSizeChanged(width, height);
+                if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
+                    return RecentsTransition.drawViewIntoHardwareBitmap(width, mTaskBarHeight,
+                            null, 1f, 0xFFff0000);
+                } else {
+                    // Workaround for b/27815919, reset the callback so that we do not trigger an
+                    // invalidate on the header bar as a result of updating the icon
+                    Drawable icon = mHeaderBar.getIconView().getDrawable();
+                    if (icon != null) {
+                        icon.setCallback(null);
+                    }
+                    mHeaderBar.bindToTask(toTask, false /* touchExplorationEnabled */,
+                            disabledInSafeMode);
+                    mHeaderBar.onTaskDataLoaded();
+                    mHeaderBar.setDimAlpha(toTransform.dimAlpha);
+                    return RecentsTransition.drawViewIntoHardwareBitmap(width, mTaskBarHeight,
+                            mHeaderBar, 1f, 0);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Shows the recents activity after dismissing the keyguard if visible
+     */
+    protected void startRecentsActivityAndDismissKeyguardIfNeeded(
+            final ActivityManager.RunningTaskInfo runningTask, final boolean isHomeStackVisible,
+            final boolean animate, final int growTarget) {
+        // Preload only if device for current user is unlocked
+        final StatusBar statusBar = getStatusBar();
+        if (statusBar != null && statusBar.isKeyguardShowing()) {
+            statusBar.executeRunnableDismissingKeyguard(() -> {
+                    // Flush trustmanager before checking device locked per user when preloading
+                    mTrustManager.reportKeyguardShowingChanged();
+                    mHandler.post(() -> startRecentsActivity(runningTask, isHomeStackVisible,
+                            animate, growTarget));
+                }, null,  true /* dismissShade */, false /* afterKeyguardGone */,
+                true /* deferred */);
+        } else {
+            startRecentsActivity(runningTask, isHomeStackVisible, animate, growTarget);
+        }
+    }
+
+    private void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
+            boolean isHomeStackVisible, boolean animate, int growTarget) {
+        RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
+        RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
+
+        int runningTaskId = !mLaunchedWhileDocking && (runningTask != null)
+                ? runningTask.id
+                : -1;
+
+        // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
+        // should always preload the tasks now. If we are dragging in recents, reload them as
+        // the stacks might have changed.
+        if (mLaunchedWhileDocking || mTriggeredFromAltTab || sInstanceLoadPlan == null) {
+            // Create a new load plan if preloadRecents() was never triggered
+            sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);
+        }
+        if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
+            loader.preloadTasks(sInstanceLoadPlan, runningTaskId);
+        }
+
+        TaskStack stack = sInstanceLoadPlan.getTaskStack();
+        boolean hasRecentTasks = stack.getTaskCount() > 0;
+        boolean useThumbnailTransition = (runningTask != null) && !isHomeStackVisible &&
+                hasRecentTasks;
+
+        // Update the launch state that we need in updateHeaderBarLayout()
+        launchState.launchedFromHome = !useThumbnailTransition && !mLaunchedWhileDocking;
+        launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
+        launchState.launchedFromPipApp = false;
+        launchState.launchedWithNextPipApp =
+                stack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime());
+        launchState.launchedViaDockGesture = mLaunchedWhileDocking;
+        launchState.launchedViaDragGesture = mDraggingInRecents;
+        launchState.launchedToTaskId = runningTaskId;
+        launchState.launchedWithAltTab = mTriggeredFromAltTab;
+
+        // Disable toggling of recents between starting the activity and it is visible and the app
+        // has started its transition into recents.
+        setWaitingForTransitionStart(useThumbnailTransition);
+
+        // Preload the icon (this will be a null-op if we have preloaded the icon already in
+        // preloadRecents())
+        preloadIcon(runningTaskId);
+
+        // Update the header bar if necessary
+        Rect windowOverrideRect = getWindowRectOverride(growTarget);
+        updateHeaderBarLayout(stack, windowOverrideRect);
+
+        // Prepare the dummy stack for the transition
+        TaskStackLayoutAlgorithm.VisibilityReport stackVr =
+                mDummyStackView.computeStackVisibilityReport();
+
+        // Update the remaining launch state
+        launchState.launchedNumVisibleTasks = stackVr.numVisibleTasks;
+        launchState.launchedNumVisibleThumbnails = stackVr.numVisibleThumbnails;
+
+        if (!animate) {
+            startRecentsActivity(ActivityOptions.makeCustomAnimation(mContext, -1, -1),
+                    null /* future */);
+            return;
+        }
+
+        Pair<ActivityOptions, AppTransitionAnimationSpecsFuture> pair;
+        if (useThumbnailTransition) {
+            // Try starting with a thumbnail transition
+            pair = getThumbnailTransitionActivityOptions(runningTask, windowOverrideRect);
+        } else {
+            // If there is no thumbnail transition, but is launching from home into recents, then
+            // use a quick home transition
+            pair = new Pair<>(hasRecentTasks
+                    ? getHomeTransitionActivityOptions()
+                    : getUnknownTransitionActivityOptions(), null);
+        }
+        startRecentsActivity(pair.first, pair.second);
+        mLastToggleTime = SystemClock.elapsedRealtime();
+    }
+
+    private Rect getWindowRectOverride(int growTarget) {
+        if (growTarget == DividerView.INVALID_RECENTS_GROW_TARGET) {
+            return SystemServicesProxy.getInstance(mContext).getWindowRect();
+        }
+        Rect result = new Rect();
+        Rect displayRect = LegacyRecentsImpl.getSystemServices().getDisplayRect();
+        DockedDividerUtils.calculateBoundsForPosition(growTarget, WindowManager.DOCKED_BOTTOM,
+                result, displayRect.width(), displayRect.height(),
+                LegacyRecentsImpl.getSystemServices().getDockedDividerSize(mContext));
+        return result;
+    }
+
+    private StatusBar getStatusBar() {
+        return SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+    }
+
+    /**
+     * Starts the recents activity.
+     */
+    private void startRecentsActivity(ActivityOptions opts,
+            final AppTransitionAnimationSpecsFuture future) {
+        Intent intent = new Intent();
+        intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+        HidePipMenuEvent hideMenuEvent = new HidePipMenuEvent();
+        hideMenuEvent.addPostAnimationCallback(() -> {
+            LegacyRecentsImpl.getSystemServices().startActivityAsUserAsync(intent, opts);
+            EventBus.getDefault().send(new RecentsActivityStartingEvent());
+            if (future != null) {
+                future.composeSpecsSynchronous();
+            }
+        });
+        EventBus.getDefault().send(hideMenuEvent);
+
+        // Once we have launched the activity, reset the dummy stack view tasks so we don't hold
+        // onto references to the same tasks consumed by the activity
+        mDummyStackView.setTasks(mEmptyTaskStack, false /* notifyStackChanges */);
+    }
+
+    /**** OnAnimationFinishedListener Implementation ****/
+
+    @Override
+    public void onAnimationFinished() {
+        EventBus.getDefault().post(new EnterRecentsWindowLastAnimationFrameEvent());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImplProxy.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImplProxy.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUser.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUser.java
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUserService.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUserService.java
new file mode 100644
index 0000000..b5a0181
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUserService.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 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.systemui.recents;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.systemui.SysUiServiceProvider;
+
+/**
+ * A strictly system-user service that is started by the secondary user's Recents (with a limited
+ * lifespan), to get the interface that the secondary user's Recents can call through to the system
+ * user's Recents.
+ */
+public class RecentsSystemUserService extends Service {
+
+    private static final String TAG = "RecentsSystemUserService";
+    private static final boolean DEBUG = false;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        LegacyRecentsImpl recents = SysUiServiceProvider.getComponent(this, LegacyRecentsImpl.class);
+        if (DEBUG) {
+            Log.d(TAG, "onBind: " + recents);
+        }
+        if (recents != null) {
+            return recents.getSystemUserCallbacks();
+        }
+        return null;
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/EventBus.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/EventBus.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowLastAnimationFrameEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/EnterRecentsWindowLastAnimationFrameEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowLastAnimationFrameEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/EnterRecentsWindowLastAnimationFrameEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ExitRecentsWindowFirstAnimationFrameEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ExitRecentsWindowFirstAnimationFrameEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/ExitRecentsWindowFirstAnimationFrameEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ExitRecentsWindowFirstAnimationFrameEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideRecentsEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/HideRecentsEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/HideRecentsEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/HideRecentsEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchMostRecentTaskRequestEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchMostRecentTaskRequestEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchMostRecentTaskRequestEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchMostRecentTaskRequestEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskStartedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskStartedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskStartedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskStartedEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
new file mode 100644
index 0000000..64eeafa
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.TaskStack;
+
+/**
+ * This is sent by the activity whenever the multi-window state has changed.
+ */
+public class MultiWindowStateChangedEvent extends EventBus.AnimatedEvent {
+
+    public final boolean inMultiWindow;
+    // This flag is only used when undocking a task
+    public final boolean showDeferredAnimation;
+    public final TaskStack stack;
+
+    public MultiWindowStateChangedEvent(boolean inMultiWindow, boolean showDeferredAnimation,
+            TaskStack stack) {
+        this.inMultiWindow = inMultiWindow;
+        this.showDeferredAnimation = showDeferredAnimation;
+        this.stack = stack;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowEmptyViewEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ShowEmptyViewEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowEmptyViewEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ShowEmptyViewEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowStackActionButtonEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ShowStackActionButtonEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowStackActionButtonEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ShowStackActionButtonEvent.java
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
new file mode 100644
index 0000000..0d614e8c
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 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.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.TaskStack;
+
+/**
+ * This is sent by the activity whenever the task stach has changed.
+ */
+public class TaskStackUpdatedEvent extends EventBus.AnimatedEvent {
+
+    /**
+     * A new TaskStack instance representing the latest stack state.
+     */
+    public final TaskStack stack;
+    public final boolean inMultiWindow;
+
+    public TaskStackUpdatedEvent(TaskStack stack, boolean inMultiWindow) {
+        this.stack = stack;
+        this.inMultiWindow = inMultiWindow;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleRecentsEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ToggleRecentsEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleRecentsEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ToggleRecentsEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ActivityPinnedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ActivityPinnedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/component/ActivityPinnedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ActivityPinnedEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ActivityUnpinnedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ActivityUnpinnedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/component/ActivityUnpinnedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ActivityUnpinnedEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ExpandPipEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ExpandPipEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/component/ExpandPipEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ExpandPipEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/HidePipMenuEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/HidePipMenuEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/component/HidePipMenuEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/HidePipMenuEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ShowUserToastEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ShowUserToastEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/component/ShowUserToastEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ShowUserToastEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissAllTaskViewsEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DismissAllTaskViewsEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissAllTaskViewsEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DismissAllTaskViewsEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/RecentsGrowingEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/RecentsGrowingEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/RecentsGrowingEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/RecentsGrowingEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java
new file mode 100644
index 0000000..9738124
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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.systemui.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.utilities.AnimationProps;
+import com.android.systemui.recents.views.TaskView;
+
+/**
+ * This event is sent when a {@link TaskView} has been dismissed and is no longer visible.
+ */
+public class TaskViewDismissedEvent extends EventBus.Event {
+
+    public final Task task;
+    public final TaskView taskView;
+    public final AnimationProps animation;
+
+    public TaskViewDismissedEvent(Task task, TaskView taskView, AnimationProps animation) {
+        this.task = task;
+        this.taskView = taskView;
+        this.animation = animation;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java
new file mode 100644
index 0000000..c11936e
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 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.systemui.recents.events.ui.dragndrop;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskView;
+
+/**
+ * This event is sent whenever a drag end is cancelled because of an error.
+ */
+public class DragEndCancelledEvent extends EventBus.AnimatedEvent {
+
+    public final TaskStack stack;
+    public final Task task;
+    public final TaskView taskView;
+
+    public DragEndCancelledEvent(TaskStack stack, Task task, TaskView taskView) {
+        this.stack = stack;
+        this.task = task;
+        this.taskView = taskView;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/DozeTrigger.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/DozeTrigger.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/FreePathInterpolator.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/FreePathInterpolator.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/misc/FreePathInterpolator.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/FreePathInterpolator.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
new file mode 100644
index 0000000..f8b61cd
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.misc;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.AppGlobals;
+import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
+import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+import android.util.Log;
+import android.util.MutableBoolean;
+import android.view.Display;
+import android.view.IDockedStackListener;
+import android.view.IWindowManager;
+import android.view.WindowManager;
+import android.view.WindowManager.KeyboardShortcutsReceiver;
+import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.app.AssistUtils;
+import com.android.internal.os.BackgroundThread;
+import com.android.systemui.Dependency;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.RecentsImpl;
+import com.android.systemui.statusbar.policy.UserInfoController;
+
+import java.util.List;
+
+/**
+ * Acts as a shim around the real system services that we need to access data from, and provides
+ * a point of injection when testing UI.
+ */
+public class SystemServicesProxy {
+    final static String TAG = "SystemServicesProxy";
+
+    final static BitmapFactory.Options sBitmapOptions;
+    static {
+        sBitmapOptions = new BitmapFactory.Options();
+        sBitmapOptions.inMutable = true;
+        sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
+    }
+
+    private static SystemServicesProxy sSystemServicesProxy;
+
+    AccessibilityManager mAccm;
+    ActivityManager mAm;
+    IActivityManager mIam;
+    IActivityTaskManager mIatm;
+    PackageManager mPm;
+    IPackageManager mIpm;
+    private final IDreamManager mDreamManager;
+    private final Context mContext;
+    AssistUtils mAssistUtils;
+    WindowManager mWm;
+    IWindowManager mIwm;
+    UserManager mUm;
+    Display mDisplay;
+    String mRecentsPackage;
+    private int mCurrentUserId;
+
+    boolean mIsSafeMode;
+
+    int mDummyThumbnailWidth;
+    int mDummyThumbnailHeight;
+    Paint mBgProtectionPaint;
+    Canvas mBgProtectionCanvas;
+
+    private final Runnable mGcRunnable = new Runnable() {
+        @Override
+        public void run() {
+            System.gc();
+            System.runFinalization();
+        }
+    };
+
+    private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+
+    private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
+            (String name, Drawable picture, String userAccount) ->
+                    mCurrentUserId = mAm.getCurrentUser();
+
+    /** Private constructor */
+    private SystemServicesProxy(Context context) {
+        mContext = context.getApplicationContext();
+        mAccm = AccessibilityManager.getInstance(context);
+        mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+        mIam = ActivityManager.getService();
+        mIatm = ActivityTaskManager.getService();
+        mPm = context.getPackageManager();
+        mIpm = AppGlobals.getPackageManager();
+        mAssistUtils = new AssistUtils(context);
+        mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        mIwm = WindowManagerGlobal.getWindowManagerService();
+        mUm = UserManager.get(context);
+        mDreamManager = IDreamManager.Stub.asInterface(
+                ServiceManager.checkService(DreamService.DREAM_SERVICE));
+        mDisplay = mWm.getDefaultDisplay();
+        mRecentsPackage = context.getPackageName();
+        mIsSafeMode = mPm.isSafeMode();
+        mCurrentUserId = mAm.getCurrentUser();
+
+        // Get the dummy thumbnail width/heights
+        Resources res = context.getResources();
+        int wId = com.android.internal.R.dimen.thumbnail_width;
+        int hId = com.android.internal.R.dimen.thumbnail_height;
+        mDummyThumbnailWidth = res.getDimensionPixelSize(wId);
+        mDummyThumbnailHeight = res.getDimensionPixelSize(hId);
+
+        // Create the protection paints
+        mBgProtectionPaint = new Paint();
+        mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
+        mBgProtectionPaint.setColor(0xFFffffff);
+        mBgProtectionCanvas = new Canvas();
+
+        // Since SystemServicesProxy can be accessed from a per-SysUI process component, create a
+        // per-process listener to keep track of the current user id to reduce the number of binder
+        // calls to fetch it.
+        UserInfoController userInfoController = Dependency.get(UserInfoController.class);
+        userInfoController.addCallback(mOnUserInfoChangedListener);
+    }
+
+    /**
+     * Returns the single instance of the {@link SystemServicesProxy}.
+     * This should only be called on the main thread.
+     */
+    public static synchronized SystemServicesProxy getInstance(Context context) {
+        if (sSystemServicesProxy == null) {
+            sSystemServicesProxy = new SystemServicesProxy(context);
+        }
+        return sSystemServicesProxy;
+    }
+
+    /**
+     * Requests a gc() from the background thread.
+     */
+    public void gc() {
+        BackgroundThread.getHandler().post(mGcRunnable);
+    }
+
+    /**
+     * Returns whether the recents activity is currently visible.
+     */
+    public boolean isRecentsActivityVisible() {
+        return isRecentsActivityVisible(null);
+    }
+
+    /**
+     * Returns whether the recents activity is currently visible.
+     *
+     * @param isHomeStackVisible if provided, will return whether the home stack is visible
+     *                           regardless of the recents visibility
+     *
+     * TODO(winsonc): Refactor this check to just use the recents activity lifecycle
+     */
+    public boolean isRecentsActivityVisible(MutableBoolean isHomeStackVisible) {
+        if (mIam == null) return false;
+
+        try {
+            List<StackInfo> stackInfos = mIatm.getAllStackInfos();
+            ActivityManager.StackInfo homeStackInfo = null;
+            ActivityManager.StackInfo fullscreenStackInfo = null;
+            ActivityManager.StackInfo recentsStackInfo = null;
+            for (int i = 0; i < stackInfos.size(); i++) {
+                final StackInfo stackInfo = stackInfos.get(i);
+                final WindowConfiguration winConfig = stackInfo.configuration.windowConfiguration;
+                final int activityType = winConfig.getActivityType();
+                final int windowingMode = winConfig.getWindowingMode();
+                if (homeStackInfo == null && activityType == ACTIVITY_TYPE_HOME) {
+                    homeStackInfo = stackInfo;
+                } else if (fullscreenStackInfo == null && activityType == ACTIVITY_TYPE_STANDARD
+                        && (windowingMode == WINDOWING_MODE_FULLSCREEN
+                            || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
+                    fullscreenStackInfo = stackInfo;
+                } else if (recentsStackInfo == null && activityType == ACTIVITY_TYPE_RECENTS) {
+                    recentsStackInfo = stackInfo;
+                }
+            }
+            boolean homeStackVisibleNotOccluded = isStackNotOccluded(homeStackInfo,
+                    fullscreenStackInfo);
+            boolean recentsStackVisibleNotOccluded = isStackNotOccluded(recentsStackInfo,
+                    fullscreenStackInfo);
+            if (isHomeStackVisible != null) {
+                isHomeStackVisible.value = homeStackVisibleNotOccluded;
+            }
+            ComponentName topActivity = recentsStackInfo != null ?
+                    recentsStackInfo.topActivity : null;
+            return (recentsStackVisibleNotOccluded && topActivity != null
+                    && topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE)
+                    && LegacyRecentsImpl.RECENTS_ACTIVITIES.contains(topActivity.getClassName()));
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    private boolean isStackNotOccluded(ActivityManager.StackInfo stackInfo,
+            ActivityManager.StackInfo fullscreenStackInfo) {
+        boolean stackVisibleNotOccluded = stackInfo == null || stackInfo.visible;
+        if (fullscreenStackInfo != null && stackInfo != null) {
+            boolean isFullscreenStackOccludingg = fullscreenStackInfo.visible &&
+                    fullscreenStackInfo.position > stackInfo.position;
+            stackVisibleNotOccluded &= !isFullscreenStackOccludingg;
+        }
+        return stackVisibleNotOccluded;
+    }
+
+    /**
+     * Returns whether this device is in the safe mode.
+     */
+    public boolean isInSafeMode() {
+        return mIsSafeMode;
+    }
+
+    /** Moves an already resumed task to the side of the screen to initiate split screen. */
+    public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
+            Rect initialBounds) {
+        if (mIatm == null) {
+            return false;
+        }
+
+        try {
+            return mIatm.setTaskWindowingModeSplitScreenPrimary(taskId, createMode,
+                    true /* onTop */, false /* animate */, initialBounds, true /* showRecents */);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    public ActivityManager.StackInfo getSplitScreenPrimaryStack() {
+        try {
+            return mIatm.getStackInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * @return whether there are any docked tasks for the current user.
+     */
+    public boolean hasDockedTask() {
+        if (mIam == null) return false;
+
+        ActivityManager.StackInfo stackInfo = getSplitScreenPrimaryStack();
+        if (stackInfo != null) {
+            int userId = getCurrentUser();
+            boolean hasUserTask = false;
+            for (int i = stackInfo.taskUserIds.length - 1; i >= 0 && !hasUserTask; i--) {
+                hasUserTask = (stackInfo.taskUserIds[i] == userId);
+            }
+            return hasUserTask;
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether there is a soft nav bar.
+     */
+    public boolean hasSoftNavigationBar() {
+        try {
+            return mIwm.hasNavigationBar();
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether the device has a transposed nav bar (on the right of the screen) in the
+     * current display orientation.
+     */
+    public boolean hasTransposedNavigationBar() {
+        Rect insets = new Rect();
+        getStableInsets(insets);
+        return insets.right > 0;
+    }
+
+    /** Set the task's windowing mode. */
+    public void setTaskWindowingMode(int taskId, int windowingMode) {
+        if (mIatm == null) return;
+
+        try {
+            mIatm.setTaskWindowingMode(taskId, windowingMode, false /* onTop */);
+        } catch (RemoteException | IllegalArgumentException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Returns whether the provided {@param userId} represents the system user.
+     */
+    public boolean isSystemUser(int userId) {
+        return userId == UserHandle.USER_SYSTEM;
+    }
+
+    /**
+     * Returns the current user id.  Used instead of KeyguardUpdateMonitor in SystemUI components
+     * that run in the non-primary SystemUI process.
+     */
+    public int getCurrentUser() {
+        return mCurrentUserId;
+    }
+
+    /**
+     * Returns the processes user id.
+     */
+    public int getProcessUser() {
+        if (mUm == null) return 0;
+        return mUm.getUserHandle();
+    }
+
+    /**
+     * Returns whether touch exploration is currently enabled.
+     */
+    public boolean isTouchExplorationEnabled() {
+        if (mAccm == null) return false;
+
+        return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled();
+    }
+
+    /**
+     * Returns whether the current task is in screen-pinning mode.
+     */
+    public boolean isScreenPinningActive() {
+        if (mIam == null) return false;
+
+        try {
+            return mIatm.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the smallest width/height.
+     */
+    public int getDeviceSmallestWidth() {
+        if (mDisplay == null) return 0;
+
+        Point smallestSizeRange = new Point();
+        Point largestSizeRange = new Point();
+        mDisplay.getCurrentSizeRange(smallestSizeRange, largestSizeRange);
+        return smallestSizeRange.x;
+    }
+
+    /**
+     * Returns the current display rect in the current display orientation.
+     */
+    public Rect getDisplayRect() {
+        Rect displayRect = new Rect();
+        if (mDisplay == null) return displayRect;
+
+        Point p = new Point();
+        mDisplay.getRealSize(p);
+        displayRect.set(0, 0, p.x, p.y);
+        return displayRect;
+    }
+
+    /**
+     * Returns the window rect for the RecentsActivity, based on the dimensions of the recents stack
+     */
+    public Rect getWindowRect() {
+        Rect windowRect = new Rect();
+        if (mIam == null) return windowRect;
+
+        try {
+            // Use the recents stack bounds, fallback to fullscreen stack if it is null
+            ActivityManager.StackInfo stackInfo =
+                    mIatm.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
+            if (stackInfo == null) {
+                stackInfo = mIatm.getStackInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            }
+            if (stackInfo != null) {
+                windowRect.set(stackInfo.bounds);
+            }
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        } finally {
+            return windowRect;
+        }
+    }
+
+    public void startActivityAsUserAsync(Intent intent, ActivityOptions opts) {
+        mUiOffloadThread.submit(() -> mContext.startActivityAsUser(intent,
+                opts != null ? opts.toBundle() : null, UserHandle.CURRENT));
+    }
+
+    /** Starts an in-place animation on the front most application windows. */
+    public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) {
+        if (mIam == null) return;
+
+        try {
+            mIatm.startInPlaceAnimationOnFrontMostApplication(
+                    opts == null ? null : opts.toBundle());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void registerDockedStackListener(IDockedStackListener listener) {
+        if (mWm == null) return;
+
+        try {
+            mIwm.registerDockedStackListener(listener);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Calculates the size of the dock divider in the current orientation.
+     */
+    public int getDockedDividerSize(Context context) {
+        Resources res = context.getResources();
+        int dividerWindowWidth = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.docked_stack_divider_thickness);
+        int dividerInsets = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.docked_stack_divider_insets);
+        return dividerWindowWidth - 2 * dividerInsets;
+    }
+
+    public void requestKeyboardShortcuts(
+            Context context, KeyboardShortcutsReceiver receiver, int deviceId) {
+        mWm.requestAppKeyboardShortcuts(receiver, deviceId);
+    }
+
+    public void getStableInsets(Rect outStableInsets) {
+        if (mWm == null) return;
+
+        try {
+            mIwm.getStableInsets(Display.DEFAULT_DISPLAY, outStableInsets);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Updates the visibility of recents.
+     */
+    public void setRecentsVisibility(final boolean visible) {
+        mUiOffloadThread.submit(() -> {
+            try {
+                mIwm.setRecentsVisibility(visible);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to reach window manager", e);
+            }
+        });
+    }
+
+    /**
+     * Updates the visibility of the picture-in-picture.
+     */
+    public void setPipVisibility(final boolean visible) {
+        mUiOffloadThread.submit(() -> {
+            try {
+                mIwm.setPipVisibility(visible);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to reach window manager", e);
+            }
+        });
+    }
+
+    public boolean isDreaming() {
+        try {
+            return mDreamManager.isDreaming();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to query dream manager.", e);
+        }
+        return false;
+    }
+
+    public void awakenDreamsAsync() {
+        mUiOffloadThread.submit(() -> {
+            try {
+                mDreamManager.awaken();
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+        });
+    }
+
+    public interface StartActivityFromRecentsResultListener {
+        void onStartActivityResult(boolean succeeded);
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/BackgroundTaskLoader.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/BackgroundTaskLoader.java
new file mode 100644
index 0000000..e85a7fb
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/BackgroundTaskLoader.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2017 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.systemui.recents.model;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+
+import com.android.systemui.shared.recents.model.IconLoader;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+/**
+ * Background task resource loader
+ */
+class BackgroundTaskLoader implements Runnable {
+    static String TAG = "BackgroundTaskLoader";
+    static boolean DEBUG = false;
+
+    private Context mContext;
+    private final HandlerThread mLoadThread;
+    private final Handler mLoadThreadHandler;
+    private final Handler mMainThreadHandler;
+
+    private final TaskResourceLoadQueue mLoadQueue;
+    private final IconLoader mIconLoader;
+
+    private boolean mStarted;
+    private boolean mCancelled;
+    private boolean mWaitingOnLoadQueue;
+
+    private final OnIdleChangedListener mOnIdleChangedListener;
+
+    /** Constructor, creates a new loading thread that loads task resources in the background */
+    public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
+            IconLoader iconLoader, OnIdleChangedListener onIdleChangedListener) {
+        mLoadQueue = loadQueue;
+        mIconLoader = iconLoader;
+        mMainThreadHandler = new Handler();
+        mOnIdleChangedListener = onIdleChangedListener;
+        mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
+                android.os.Process.THREAD_PRIORITY_BACKGROUND);
+        mLoadThread.start();
+        mLoadThreadHandler = new Handler(mLoadThread.getLooper());
+    }
+
+    /** Restarts the loader thread */
+    void start(Context context) {
+        mContext = context;
+        mCancelled = false;
+        if (!mStarted) {
+            // Start loading on the load thread
+            mStarted = true;
+            mLoadThreadHandler.post(this);
+        } else {
+            // Notify the load thread to start loading again
+            synchronized (mLoadThread) {
+                mLoadThread.notifyAll();
+            }
+        }
+    }
+
+    /** Requests the loader thread to stop after the current iteration */
+    void stop() {
+        // Mark as cancelled for the thread to pick up
+        mCancelled = true;
+        // If we are waiting for the load queue for more tasks, then we can just reset the
+        // Context now, since nothing is using it
+        if (mWaitingOnLoadQueue) {
+            mContext = null;
+        }
+    }
+
+    @Override
+    public void run() {
+        while (true) {
+            if (mCancelled) {
+                // We have to unset the context here, since the background thread may be using it
+                // when we call stop()
+                mContext = null;
+                // If we are cancelled, then wait until we are started again
+                synchronized(mLoadThread) {
+                    try {
+                        mLoadThread.wait();
+                    } catch (InterruptedException ie) {
+                        ie.printStackTrace();
+                    }
+                }
+            } else {
+                // If we've stopped the loader, then fall through to the above logic to wait on
+                // the load thread
+                processLoadQueueItem();
+
+                // If there are no other items in the list, then just wait until something is added
+                if (!mCancelled && mLoadQueue.isEmpty()) {
+                    synchronized(mLoadQueue) {
+                        try {
+                            mWaitingOnLoadQueue = true;
+                            mMainThreadHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    mOnIdleChangedListener.onIdleChanged(true);
+                                }
+                            });
+                            mLoadQueue.wait();
+                            mMainThreadHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    mOnIdleChangedListener.onIdleChanged(false);
+                                }
+                            });
+                            mWaitingOnLoadQueue = false;
+                        } catch (InterruptedException ie) {
+                            ie.printStackTrace();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * This needs to be in a separate method to work around an surprising interpreter behavior:
+     * The register will keep the local reference to cachedThumbnailData even if it falls out of
+     * scope. Putting it into a method fixes this issue.
+     */
+    private void processLoadQueueItem() {
+        // Load the next item from the queue
+        final Task t = mLoadQueue.nextTask();
+        if (t != null) {
+            final Drawable icon = mIconLoader.getIcon(t);
+            if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
+            final ThumbnailData thumbnailData =
+                    ActivityManagerWrapper.getInstance().getTaskThumbnail(t.key.id,
+                            true /* reducedResolution */);
+
+            if (!mCancelled) {
+                // Notify that the task data has changed
+                mMainThreadHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        t.notifyTaskDataLoaded(thumbnailData, icon);
+                    }
+                });
+            }
+        }
+    }
+
+    interface OnIdleChangedListener {
+        void onIdleChanged(boolean idle);
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/FilteredTaskList.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/FilteredTaskList.java
new file mode 100644
index 0000000..005be75
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/FilteredTaskList.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2017 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.systemui.recents.model;
+
+import android.util.ArrayMap;
+import android.util.SparseArray;
+
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A list of filtered tasks.
+ */
+class FilteredTaskList {
+
+    private final ArrayList<Task> mTasks = new ArrayList<>();
+    private final ArrayList<Task> mFilteredTasks = new ArrayList<>();
+    private final ArrayMap<TaskKey, Integer> mFilteredTaskIndices = new ArrayMap<>();
+    private TaskFilter mFilter;
+
+    /** Sets the task filter, and returns whether the set of filtered tasks have changed. */
+    boolean setFilter(TaskFilter filter) {
+        ArrayList<Task> prevFilteredTasks = new ArrayList<>(mFilteredTasks);
+        mFilter = filter;
+        updateFilteredTasks();
+        return !prevFilteredTasks.equals(mFilteredTasks);
+    }
+
+    /** Adds a new task to the task list */
+    void add(Task t) {
+        mTasks.add(t);
+        updateFilteredTasks();
+    }
+
+    /** Sets the list of tasks */
+    void set(List<Task> tasks) {
+        mTasks.clear();
+        mTasks.addAll(tasks);
+        updateFilteredTasks();
+    }
+
+    /** Removes a task from the base list only if it is in the filtered list */
+    boolean remove(Task t) {
+        if (mFilteredTasks.contains(t)) {
+            boolean removed = mTasks.remove(t);
+            updateFilteredTasks();
+            return removed;
+        }
+        return false;
+    }
+
+    /** Returns the index of this task in the list of filtered tasks */
+    int indexOf(Task t) {
+        if (t != null && mFilteredTaskIndices.containsKey(t.key)) {
+            return mFilteredTaskIndices.get(t.key);
+        }
+        return -1;
+    }
+
+    /** Returns the size of the list of filtered tasks */
+    int size() {
+        return mFilteredTasks.size();
+    }
+
+    /** Returns whether the filtered list contains this task */
+    boolean contains(Task t) {
+        return mFilteredTaskIndices.containsKey(t.key);
+    }
+
+    /** Updates the list of filtered tasks whenever the base task list changes */
+    private void updateFilteredTasks() {
+        mFilteredTasks.clear();
+        if (mFilter != null) {
+            // Create a sparse array from task id to Task
+            SparseArray<Task> taskIdMap = new SparseArray<>();
+            int taskCount = mTasks.size();
+            for (int i = 0; i < taskCount; i++) {
+                Task t = mTasks.get(i);
+                taskIdMap.put(t.key.id, t);
+            }
+
+            for (int i = 0; i < taskCount; i++) {
+                Task t = mTasks.get(i);
+                if (mFilter.acceptTask(taskIdMap, t, i)) {
+                    mFilteredTasks.add(t);
+                }
+            }
+        } else {
+            mFilteredTasks.addAll(mTasks);
+        }
+        updateFilteredTaskIndices();
+    }
+
+    /** Updates the mapping of tasks to indices. */
+    private void updateFilteredTaskIndices() {
+        int taskCount = mFilteredTasks.size();
+        mFilteredTaskIndices.clear();
+        for (int i = 0; i < taskCount; i++) {
+            Task t = mFilteredTasks.get(i);
+            mFilteredTaskIndices.put(t.key, i);
+        }
+    }
+
+    /** Returns the list of filtered tasks */
+    ArrayList<Task> getTasks() {
+        return mFilteredTasks;
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/HighResThumbnailLoader.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/HighResThumbnailLoader.java
new file mode 100644
index 0000000..34bc334
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/HighResThumbnailLoader.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2017 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.systemui.recents.model;
+
+import static android.os.Process.setThreadPriority;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+
+/**
+ * Loader class that loads full-resolution thumbnails when appropriate.
+ */
+public class HighResThumbnailLoader implements
+        TaskCallbacks, BackgroundTaskLoader.OnIdleChangedListener {
+
+    private final ActivityManagerWrapper mActivityManager;
+
+    @GuardedBy("mLoadQueue")
+    private final ArrayDeque<Task> mLoadQueue = new ArrayDeque<>();
+    @GuardedBy("mLoadQueue")
+    private final ArraySet<Task> mLoadingTasks = new ArraySet<>();
+    @GuardedBy("mLoadQueue")
+    private boolean mLoaderIdling;
+
+    private final ArrayList<Task> mVisibleTasks = new ArrayList<>();
+
+    private final Thread mLoadThread;
+    private final Handler mMainThreadHandler;
+    private final boolean mIsLowRamDevice;
+    private boolean mLoading;
+    private boolean mVisible;
+    private boolean mFlingingFast;
+    private boolean mTaskLoadQueueIdle;
+
+    public HighResThumbnailLoader(ActivityManagerWrapper activityManager, Looper looper,
+            boolean isLowRamDevice) {
+        mActivityManager = activityManager;
+        mMainThreadHandler = new Handler(looper);
+        mLoadThread = new Thread(mLoader, "Recents-HighResThumbnailLoader");
+        mLoadThread.start();
+        mIsLowRamDevice = isLowRamDevice;
+    }
+
+    public void setVisible(boolean visible) {
+        if (mIsLowRamDevice) {
+            return;
+        }
+        mVisible = visible;
+        updateLoading();
+    }
+
+    public void setFlingingFast(boolean flingingFast) {
+        if (mFlingingFast == flingingFast || mIsLowRamDevice) {
+            return;
+        }
+        mFlingingFast = flingingFast;
+        updateLoading();
+    }
+
+    @Override
+    public void onIdleChanged(boolean idle) {
+        setTaskLoadQueueIdle(idle);
+    }
+
+    /**
+     * Sets whether the other task load queue is idling. Avoid double-loading bitmaps by not
+     * starting this queue until the other queue is idling.
+     */
+    public void setTaskLoadQueueIdle(boolean idle) {
+        if (mIsLowRamDevice) {
+            return;
+        }
+        mTaskLoadQueueIdle = idle;
+        updateLoading();
+    }
+
+    @VisibleForTesting
+    boolean isLoading() {
+        return mLoading;
+    }
+
+    private void updateLoading() {
+        setLoading(mVisible && !mFlingingFast && mTaskLoadQueueIdle);
+    }
+
+    private void setLoading(boolean loading) {
+        if (loading == mLoading) {
+            return;
+        }
+        synchronized (mLoadQueue) {
+            mLoading = loading;
+            if (!loading) {
+                stopLoading();
+            } else {
+                startLoading();
+            }
+        }
+    }
+
+    @GuardedBy("mLoadQueue")
+    private void startLoading() {
+        for (int i = mVisibleTasks.size() - 1; i >= 0; i--) {
+            Task t = mVisibleTasks.get(i);
+            if ((t.thumbnail == null || t.thumbnail.reducedResolution)
+                    && !mLoadQueue.contains(t) && !mLoadingTasks.contains(t)) {
+                mLoadQueue.add(t);
+            }
+        }
+        mLoadQueue.notifyAll();
+    }
+
+    @GuardedBy("mLoadQueue")
+    private void stopLoading() {
+        mLoadQueue.clear();
+        mLoadQueue.notifyAll();
+    }
+
+    /**
+     * Needs to be called when a task becomes visible. Note that this is different from
+     * {@link TaskCallbacks#onTaskDataLoaded} as this method should only be called once when it
+     * becomes visible, whereas onTaskDataLoaded can be called multiple times whenever some data
+     * has been updated.
+     */
+    public void onTaskVisible(Task t) {
+        t.addCallback(this);
+        mVisibleTasks.add(t);
+        if ((t.thumbnail == null || t.thumbnail.reducedResolution) && mLoading) {
+            synchronized (mLoadQueue) {
+                mLoadQueue.add(t);
+                mLoadQueue.notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Needs to be called when a task becomes visible. See {@link #onTaskVisible} why this is
+     * different from {@link TaskCallbacks#onTaskDataUnloaded()}
+     */
+    public void onTaskInvisible(Task t) {
+        t.removeCallback(this);
+        mVisibleTasks.remove(t);
+        synchronized (mLoadQueue) {
+            mLoadQueue.remove(t);
+        }
+    }
+
+    @VisibleForTesting
+    void waitForLoaderIdle() {
+        while (true) {
+            synchronized (mLoadQueue) {
+                if (mLoadQueue.isEmpty() && mLoaderIdling) {
+                    return;
+                }
+            }
+            SystemClock.sleep(100);
+        }
+    }
+
+    @Override
+    public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
+        if (thumbnailData != null && !thumbnailData.reducedResolution) {
+            synchronized (mLoadQueue) {
+                mLoadQueue.remove(task);
+            }
+        }
+    }
+
+    @Override
+    public void onTaskDataUnloaded() {
+    }
+
+    @Override
+    public void onTaskWindowingModeChanged() {
+    }
+
+    private final Runnable mLoader = new Runnable() {
+
+        @Override
+        public void run() {
+            setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND + 1);
+            while (true) {
+                Task next = null;
+                synchronized (mLoadQueue) {
+                    if (!mLoading || mLoadQueue.isEmpty()) {
+                        try {
+                            mLoaderIdling = true;
+                            mLoadQueue.wait();
+                            mLoaderIdling = false;
+                        } catch (InterruptedException e) {
+                            // Don't care.
+                        }
+                    } else {
+                        next = mLoadQueue.poll();
+                        if (next != null) {
+                            mLoadingTasks.add(next);
+                        }
+                    }
+                }
+                if (next != null) {
+                    loadTask(next);
+                }
+            }
+        }
+
+        private void loadTask(final Task t) {
+            final ThumbnailData thumbnail = mActivityManager.getTaskThumbnail(t.key.id,
+                    false /* reducedResolution */);
+            mMainThreadHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    synchronized (mLoadQueue) {
+                        mLoadingTasks.remove(t);
+                    }
+                    if (mVisibleTasks.contains(t)) {
+                        t.notifyTaskDataLoaded(thumbnail, t.icon);
+                    }
+                }
+            });
+        }
+    };
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
new file mode 100644
index 0000000..7c6a76f
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.model;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.KeyguardManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.SparseBooleanArray;
+
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * This class stores the loading state as it goes through multiple stages of loading:
+ *   1) preloadRawTasks() will load the raw set of recents tasks from the system
+ *   2) preloadPlan() will construct a new task stack with all metadata and only icons and
+ *      thumbnails that are currently in the cache
+ *   3) executePlan() will actually load and fill in the icons and thumbnails according to the load
+ *      options specified, such that we can transition into the Recents activity seamlessly
+ */
+public class RecentsTaskLoadPlan {
+
+    /** The set of conditions to preload tasks. */
+    public static class PreloadOptions {
+        public boolean loadTitles = true;
+    }
+
+    /** The set of conditions to load tasks. */
+    public static class Options {
+        public int runningTaskId = -1;
+        public boolean loadIcons = true;
+        public boolean loadThumbnails = false;
+        public boolean onlyLoadForCache = false;
+        public boolean onlyLoadPausedActivities = false;
+        public int numVisibleTasks = 0;
+        public int numVisibleTaskThumbnails = 0;
+    }
+
+    private final Context mContext;
+    private final KeyguardManager mKeyguardManager;
+
+    private List<ActivityManager.RecentTaskInfo> mRawTasks;
+    private TaskStack mStack;
+
+    private final SparseBooleanArray mTmpLockedUsers = new SparseBooleanArray();
+
+    public RecentsTaskLoadPlan(Context context) {
+        mContext = context;
+        mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+    }
+
+    /**
+     * Preloads the list of recent tasks from the system. After this call, the TaskStack will
+     * have a list of all the recent tasks with their metadata, not including icons or
+     * thumbnails which were not cached and have to be loaded.
+     *
+     * The tasks will be ordered by:
+     * - least-recent to most-recent stack tasks
+     *
+     * Note: Do not lock, since this can be calling back to the loader, which separately also drives
+     * this call (callers should synchronize on the loader before making this call).
+     */
+    public void preloadPlan(PreloadOptions opts, RecentsTaskLoader loader, int runningTaskId,
+            int currentUserId) {
+        Resources res = mContext.getResources();
+        ArrayList<Task> allTasks = new ArrayList<>();
+        if (mRawTasks == null) {
+            mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
+                    ActivityTaskManager.getMaxRecentTasksStatic(), currentUserId);
+
+            // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
+            Collections.reverse(mRawTasks);
+        }
+
+        int taskCount = mRawTasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
+
+            // Compose the task key
+            final ComponentName sourceComponent = t.origActivity != null
+                    // Activity alias if there is one
+                    ? t.origActivity
+                    // The real activity if there is no alias (or the target if there is one)
+                    : t.realActivity;
+            final int windowingMode = t.configuration.windowConfiguration.getWindowingMode();
+            TaskKey taskKey = new TaskKey(t.persistentId, windowingMode, t.baseIntent,
+                    sourceComponent, t.userId, t.lastActiveTime);
+
+            boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM;
+            boolean isStackTask = !isFreeformTask;
+            boolean isLaunchTarget = taskKey.id == runningTaskId;
+
+            ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
+            if (info == null) {
+                continue;
+            }
+
+            // Load the title, icon, and color
+            String title = opts.loadTitles
+                    ? loader.getAndUpdateActivityTitle(taskKey, t.taskDescription)
+                    : "";
+            String titleDescription = opts.loadTitles
+                    ? loader.getAndUpdateContentDescription(taskKey, t.taskDescription)
+                    : "";
+            Drawable icon = isStackTask
+                    ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, false)
+                    : null;
+            ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
+                    false /* loadIfNotCached */, false /* storeInCache */);
+            int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
+            int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
+            boolean isSystemApp = (info != null) &&
+                    ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+
+            // TODO: Refactor to not do this every preload
+            if (mTmpLockedUsers.indexOfKey(t.userId) < 0) {
+                mTmpLockedUsers.put(t.userId, mKeyguardManager.isDeviceLocked(t.userId));
+            }
+            boolean isLocked = mTmpLockedUsers.get(t.userId);
+
+            // Add the task to the stack
+            Task task = new Task(taskKey, icon,
+                    thumbnail, title, titleDescription, activityColor, backgroundColor,
+                    isLaunchTarget, isStackTask, isSystemApp, t.supportsSplitScreenMultiWindow,
+                    t.taskDescription, t.resizeMode, t.topActivity, isLocked);
+
+            allTasks.add(task);
+        }
+
+        // Initialize the stacks
+        mStack = new TaskStack();
+        mStack.setTasks(allTasks, false /* notifyStackChanges */);
+    }
+
+    /**
+     * Called to apply the actual loading based on the specified conditions.
+     *
+     * Note: Do not lock, since this can be calling back to the loader, which separately also drives
+     * this call (callers should synchronize on the loader before making this call).
+     */
+    public void executePlan(Options opts, RecentsTaskLoader loader) {
+        Resources res = mContext.getResources();
+
+        // Iterate through each of the tasks and load them according to the load conditions.
+        ArrayList<Task> tasks = mStack.getTasks();
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasks.get(i);
+            TaskKey taskKey = task.key;
+
+            boolean isRunningTask = (task.key.id == opts.runningTaskId);
+            boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
+            boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
+
+            // If requested, skip the running task
+            if (opts.onlyLoadPausedActivities && isRunningTask) {
+                continue;
+            }
+
+            if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
+                if (task.icon == null) {
+                    task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
+                            true);
+                }
+            }
+            if (opts.loadThumbnails && isVisibleThumbnail) {
+                task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
+                        true /* loadIfNotCached */, true /* storeInCache */);
+            }
+        }
+    }
+
+    /**
+     * Returns the TaskStack from the preloaded list of recent tasks.
+     */
+    public TaskStack getTaskStack() {
+        return mStack;
+    }
+
+    /** Returns whether there are any tasks in any stacks. */
+    public boolean hasTasks() {
+        if (mStack != null) {
+            return mStack.getTaskCount() > 0;
+        }
+        return false;
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoader.java
new file mode 100644
index 0000000..012913a
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.model;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.content.ComponentCallbacks2;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Looper;
+import android.os.Trace;
+import android.util.Log;
+import android.util.LruCache;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.systemui.recents.model.RecentsTaskLoadPlan.Options;
+import com.android.systemui.recents.model.RecentsTaskLoadPlan.PreloadOptions;
+import com.android.systemui.shared.recents.model.IconLoader;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.shared.recents.model.TaskKeyLruCache;
+import com.android.systemui.shared.recents.model.TaskKeyLruCache.EvictionCallback;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+import java.io.PrintWriter;
+import java.util.Map;
+
+
+/**
+ * Recents task loader
+ */
+public class RecentsTaskLoader {
+    private static final String TAG = "RecentsTaskLoader";
+    private static final boolean DEBUG = false;
+
+    /** Levels of svelte in increasing severity/austerity. */
+    // No svelting.
+    public static final int SVELTE_NONE = 0;
+    // Limit thumbnail cache to number of visible thumbnails when Recents was loaded, disable
+    // caching thumbnails as you scroll.
+    public static final int SVELTE_LIMIT_CACHE = 1;
+    // Disable the thumbnail cache, load thumbnails asynchronously when the activity loads and
+    // evict all thumbnails when hidden.
+    public static final int SVELTE_DISABLE_CACHE = 2;
+    // Disable all thumbnail loading.
+    public static final int SVELTE_DISABLE_LOADING = 3;
+
+    // This activity info LruCache is useful because it can be expensive to retrieve ActivityInfos
+    // for many tasks, which we use to get the activity labels and icons.  Unlike the other caches
+    // below, this is per-package so we can't invalidate the items in the cache based on the last
+    // active time.  Instead, we rely on the PackageMonitor to keep us informed whenever a
+    // package in the cache has been updated, so that we may remove it.
+    private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
+    private final TaskKeyLruCache<Drawable> mIconCache;
+    private final TaskKeyLruCache<String> mActivityLabelCache;
+    private final TaskKeyLruCache<String> mContentDescriptionCache;
+    private final TaskResourceLoadQueue mLoadQueue;
+    private final IconLoader mIconLoader;
+    private final BackgroundTaskLoader mLoader;
+    private final HighResThumbnailLoader mHighResThumbnailLoader;
+    @GuardedBy("this")
+    private final TaskKeyStrongCache<ThumbnailData> mThumbnailCache = new TaskKeyStrongCache<>();
+    @GuardedBy("this")
+    private final TaskKeyStrongCache<ThumbnailData> mTempCache = new TaskKeyStrongCache<>();
+    private final int mMaxThumbnailCacheSize;
+    private final int mMaxIconCacheSize;
+    private int mNumVisibleTasksLoaded;
+    private int mSvelteLevel;
+
+    private int mDefaultTaskBarBackgroundColor;
+    private int mDefaultTaskViewBackgroundColor;
+
+    private EvictionCallback mClearActivityInfoOnEviction = new EvictionCallback() {
+        @Override
+        public void onEntryEvicted(TaskKey key) {
+            if (key != null) {
+                mActivityInfoCache.remove(key.getComponent());
+            }
+        }
+    };
+
+    public RecentsTaskLoader(Context context, int maxThumbnailCacheSize, int maxIconCacheSize,
+            int svelteLevel) {
+        mMaxThumbnailCacheSize = maxThumbnailCacheSize;
+        mMaxIconCacheSize = maxIconCacheSize;
+        mSvelteLevel = svelteLevel;
+
+        // Initialize the proxy, cache and loaders
+        int numRecentTasks = ActivityTaskManager.getMaxRecentTasksStatic();
+        mHighResThumbnailLoader = new HighResThumbnailLoader(ActivityManagerWrapper.getInstance(),
+                Looper.getMainLooper(), ActivityManager.isLowRamDeviceStatic());
+        mLoadQueue = new TaskResourceLoadQueue();
+        mIconCache = new TaskKeyLruCache<>(mMaxIconCacheSize, mClearActivityInfoOnEviction);
+        mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks, mClearActivityInfoOnEviction);
+        mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks,
+                mClearActivityInfoOnEviction);
+        mActivityInfoCache = new LruCache<>(numRecentTasks);
+
+        mIconLoader = createNewIconLoader(context, mIconCache, mActivityInfoCache);
+        mLoader = new BackgroundTaskLoader(mLoadQueue, mIconLoader, mHighResThumbnailLoader);
+    }
+
+    protected IconLoader createNewIconLoader(Context context,TaskKeyLruCache<Drawable> iconCache,
+            LruCache<ComponentName, ActivityInfo> activityInfoCache) {
+        return new IconLoader.DefaultIconLoader(context, iconCache, activityInfoCache);
+    }
+
+    /**
+     * Sets the default task bar/view colors if none are provided by the app.
+     */
+    public void setDefaultColors(int defaultTaskBarBackgroundColor,
+            int defaultTaskViewBackgroundColor) {
+        mDefaultTaskBarBackgroundColor = defaultTaskBarBackgroundColor;
+        mDefaultTaskViewBackgroundColor = defaultTaskViewBackgroundColor;
+    }
+
+    /** Returns the size of the app icon cache. */
+    public int getIconCacheSize() {
+        return mMaxIconCacheSize;
+    }
+
+    /** Returns the size of the thumbnail cache. */
+    public int getThumbnailCacheSize() {
+        return mMaxThumbnailCacheSize;
+    }
+
+    public HighResThumbnailLoader getHighResThumbnailLoader() {
+        return mHighResThumbnailLoader;
+    }
+
+    /** Preloads recents tasks using the specified plan to store the output. */
+    public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId) {
+        preloadTasks(plan, runningTaskId, ActivityManagerWrapper.getInstance().getCurrentUserId());
+    }
+
+    /** Preloads recents tasks using the specified plan to store the output. */
+    public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId,
+            int currentUserId) {
+        try {
+            Trace.beginSection("preloadPlan");
+            plan.preloadPlan(new PreloadOptions(), this, runningTaskId, currentUserId);
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    /** Begins loading the heavy task data according to the specified options. */
+    public synchronized void loadTasks(RecentsTaskLoadPlan plan, Options opts) {
+        if (opts == null) {
+            throw new RuntimeException("Requires load options");
+        }
+        if (opts.onlyLoadForCache && opts.loadThumbnails) {
+            // If we are loading for the cache, we'd like to have the real cache only include the
+            // visible thumbnails. However, we also don't want to reload already cached thumbnails.
+            // Thus, we copy over the current entries into a second cache, and clear the real cache,
+            // such that the real cache only contains visible thumbnails.
+            mTempCache.copyEntries(mThumbnailCache);
+            mThumbnailCache.evictAll();
+        }
+        plan.executePlan(opts, this);
+        mTempCache.evictAll();
+        if (!opts.onlyLoadForCache) {
+            mNumVisibleTasksLoaded = opts.numVisibleTasks;
+        }
+    }
+
+    /**
+     * Acquires the task resource data directly from the cache, loading if necessary.
+     */
+    public void loadTaskData(Task t) {
+        Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
+        icon = icon != null ? icon : mIconLoader.getDefaultIcon(t.key.userId);
+        mLoadQueue.addTask(t);
+        t.notifyTaskDataLoaded(t.thumbnail, icon);
+    }
+
+    /** Releases the task resource data back into the pool. */
+    public void unloadTaskData(Task t) {
+        mLoadQueue.removeTask(t);
+        t.notifyTaskDataUnloaded(mIconLoader.getDefaultIcon(t.key.userId));
+    }
+
+    /** Completely removes the resource data from the pool. */
+    public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
+        mLoadQueue.removeTask(t);
+        mIconCache.remove(t.key);
+        mActivityLabelCache.remove(t.key);
+        mContentDescriptionCache.remove(t.key);
+        if (notifyTaskDataUnloaded) {
+            t.notifyTaskDataUnloaded(mIconLoader.getDefaultIcon(t.key.userId));
+        }
+    }
+
+    /**
+     * Handles signals from the system, trimming memory when requested to prevent us from running
+     * out of memory.
+     */
+    public synchronized void onTrimMemory(int level) {
+        switch (level) {
+            case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
+                // Stop the loader immediately when the UI is no longer visible
+                stopLoader();
+                mIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
+                        mMaxIconCacheSize / 2));
+                break;
+            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
+            case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
+                // We are leaving recents, so trim the data a bit
+                mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
+                mActivityInfoCache.trimToSize(Math.max(1,
+                        ActivityTaskManager.getMaxRecentTasksStatic() / 2));
+                break;
+            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
+            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
+                // We are going to be low on memory
+                mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
+                mActivityInfoCache.trimToSize(Math.max(1,
+                        ActivityTaskManager.getMaxRecentTasksStatic() / 4));
+                break;
+            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
+            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
+                // We are low on memory, so release everything
+                mIconCache.evictAll();
+                mActivityInfoCache.evictAll();
+                // The cache is small, only clear the label cache when we are critical
+                mActivityLabelCache.evictAll();
+                mContentDescriptionCache.evictAll();
+                mThumbnailCache.evictAll();
+                break;
+            default:
+                break;
+        }
+    }
+
+    public void onPackageChanged(String packageName) {
+        // Remove all the cached activity infos for this package.  The other caches do not need to
+        // be pruned at this time, as the TaskKey expiration checks will flush them next time their
+        // cached contents are requested
+        Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
+        for (ComponentName cn : activityInfoCache.keySet()) {
+            if (cn.getPackageName().equals(packageName)) {
+                if (DEBUG) {
+                    Log.d(TAG, "Removing activity info from cache: " + cn);
+                }
+                mActivityInfoCache.remove(cn);
+            }
+        }
+    }
+
+    /**
+     * Returns the cached task label if the task key is not expired, updating the cache if it is.
+     */
+    String getAndUpdateActivityTitle(TaskKey taskKey, ActivityManager.TaskDescription td) {
+        // Return the task description label if it exists
+        if (td != null && td.getLabel() != null) {
+            return td.getLabel();
+        }
+        // Return the cached activity label if it exists
+        String label = mActivityLabelCache.getAndInvalidateIfModified(taskKey);
+        if (label != null) {
+            return label;
+        }
+        // All short paths failed, load the label from the activity info and cache it
+        ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
+        if (activityInfo != null) {
+            label = ActivityManagerWrapper.getInstance().getBadgedActivityLabel(activityInfo,
+                    taskKey.userId);
+            mActivityLabelCache.put(taskKey, label);
+            return label;
+        }
+        // If the activity info does not exist or fails to load, return an empty label for now,
+        // but do not cache it
+        return "";
+    }
+
+    /**
+     * Returns the cached task content description if the task key is not expired, updating the
+     * cache if it is.
+     */
+    String getAndUpdateContentDescription(TaskKey taskKey, ActivityManager.TaskDescription td) {
+        // Return the cached content description if it exists
+        String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
+        if (label != null) {
+            return label;
+        }
+
+        // All short paths failed, load the label from the activity info and cache it
+        ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
+        if (activityInfo != null) {
+            label = ActivityManagerWrapper.getInstance().getBadgedContentDescription(
+                    activityInfo, taskKey.userId, td);
+            if (td == null) {
+                // Only add to the cache if the task description is null, otherwise, it is possible
+                // for the task description to change between calls without the last active time
+                // changing (ie. between preloading and Overview starting) which would lead to stale
+                // content descriptions
+                // TODO: Investigate improving this
+                mContentDescriptionCache.put(taskKey, label);
+            }
+            return label;
+        }
+        // If the content description does not exist, return an empty label for now, but do not
+        // cache it
+        return "";
+    }
+
+    /**
+     * Returns the cached task icon if the task key is not expired, updating the cache if it is.
+     */
+    Drawable getAndUpdateActivityIcon(TaskKey taskKey, ActivityManager.TaskDescription td,
+            boolean loadIfNotCached) {
+        return mIconLoader.getAndInvalidateIfModified(taskKey, td, loadIfNotCached);
+    }
+
+    /**
+     * Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
+     */
+    synchronized ThumbnailData getAndUpdateThumbnail(TaskKey taskKey, boolean loadIfNotCached,
+            boolean storeInCache) {
+        ThumbnailData cached = mThumbnailCache.getAndInvalidateIfModified(taskKey);
+        if (cached != null) {
+            return cached;
+        }
+
+        cached = mTempCache.getAndInvalidateIfModified(taskKey);
+        if (cached != null) {
+            mThumbnailCache.put(taskKey, cached);
+            return cached;
+        }
+
+        if (loadIfNotCached) {
+            if (mSvelteLevel < SVELTE_DISABLE_LOADING) {
+                // Load the thumbnail from the system
+                ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance().getTaskThumbnail(
+                        taskKey.id, true /* reducedResolution */);
+                if (thumbnailData.thumbnail != null) {
+                    if (storeInCache) {
+                        mThumbnailCache.put(taskKey, thumbnailData);
+                    }
+                    return thumbnailData;
+                }
+            }
+        }
+
+        // We couldn't load any thumbnail
+        return null;
+    }
+
+    /**
+     * Returns the task's primary color if possible, defaulting to the default color if there is
+     * no specified primary color.
+     */
+    int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
+        if (td != null && td.getPrimaryColor() != 0) {
+            return td.getPrimaryColor();
+        }
+        return mDefaultTaskBarBackgroundColor;
+    }
+
+    /**
+     * Returns the task's background color if possible.
+     */
+    int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
+        if (td != null && td.getBackgroundColor() != 0) {
+            return td.getBackgroundColor();
+        }
+        return mDefaultTaskViewBackgroundColor;
+    }
+
+    /**
+     * Returns the activity info for the given task key, retrieving one from the system if the
+     * task key is expired.
+     */
+    ActivityInfo getAndUpdateActivityInfo(TaskKey taskKey) {
+        return mIconLoader.getAndUpdateActivityInfo(taskKey);
+    }
+
+    /**
+     * Starts loading tasks.
+     */
+    public void startLoader(Context ctx) {
+        mLoader.start(ctx);
+    }
+
+    /**
+     * Stops the task loader and clears all queued, pending task loads.
+     */
+    private void stopLoader() {
+        mLoader.stop();
+        mLoadQueue.clearTasks();
+    }
+
+    public synchronized void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "  ";
+
+        writer.print(prefix); writer.println(TAG);
+        writer.print(prefix); writer.println("Icon Cache");
+        mIconCache.dump(innerPrefix, writer);
+        writer.print(prefix); writer.println("Thumbnail Cache");
+        mThumbnailCache.dump(innerPrefix, writer);
+        writer.print(prefix); writer.println("Temp Thumbnail Cache");
+        mTempCache.dump(innerPrefix, writer);
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskFilter.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskFilter.java
new file mode 100644
index 0000000..9b734ec
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskFilter.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.systemui.recents.model;
+
+import android.util.SparseArray;
+import com.android.systemui.shared.recents.model.Task;
+
+/**
+ * An interface for a task filter to query whether a particular task should show in a stack.
+ */
+public interface TaskFilter {
+    /** Returns whether the filter accepts the specified task */
+    boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index);
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskKeyStrongCache.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskKeyStrongCache.java
new file mode 100644
index 0000000..27f2098
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskKeyStrongCache.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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.systemui.recents.model;
+
+import android.util.ArrayMap;
+
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+
+import com.android.systemui.shared.recents.model.TaskKeyCache;
+import com.android.systemui.shared.recents.model.TaskKeyLruCache;
+import java.io.PrintWriter;
+
+/**
+ * Like {@link TaskKeyLruCache}, but without LRU functionality.
+ */
+public class TaskKeyStrongCache<V> extends TaskKeyCache<V> {
+
+    private static final String TAG = "TaskKeyCache";
+
+    private final ArrayMap<Integer, V> mCache = new ArrayMap<>();
+
+    public final void copyEntries(TaskKeyStrongCache<V> other) {
+        for (int i = other.mKeys.size() - 1; i >= 0; i--) {
+            TaskKey key = other.mKeys.valueAt(i);
+            put(key, other.mCache.get(key.id));
+        }
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "  ";
+        writer.print(prefix); writer.print(TAG);
+        writer.print(" numEntries="); writer.print(mKeys.size());
+        writer.println();
+        int keyCount = mKeys.size();
+        for (int i = 0; i < keyCount; i++) {
+            writer.print(innerPrefix); writer.println(mKeys.get(mKeys.keyAt(i)));
+        }
+    }
+
+    @Override
+    protected V getCacheEntry(int id) {
+        return mCache.get(id);
+    }
+
+    @Override
+    protected void putCacheEntry(int id, V value) {
+        mCache.put(id, value);
+    }
+
+    @Override
+    protected void removeCacheEntry(int id) {
+        mCache.remove(id);
+    }
+
+    @Override
+    protected void evictAllCache() {
+        mCache.clear();
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskResourceLoadQueue.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskResourceLoadQueue.java
new file mode 100644
index 0000000..fe89ad5
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskResourceLoadQueue.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 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.systemui.recents.model;
+
+import com.android.systemui.shared.recents.model.Task;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * A Task load queue
+ */
+class TaskResourceLoadQueue {
+
+    private final ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<>();
+
+    /** Adds a new task to the load queue */
+    void addTask(Task t) {
+        if (!mQueue.contains(t)) {
+            mQueue.add(t);
+        }
+        synchronized(this) {
+            notifyAll();
+        }
+    }
+
+    /**
+     * Retrieves the next task from the load queue, as well as whether we want that task to be
+     * force reloaded.
+     */
+    Task nextTask() {
+        return mQueue.poll();
+    }
+
+    /** Removes a task from the load queue */
+    void removeTask(Task t) {
+        mQueue.remove(t);
+    }
+
+    /** Clears all the tasks from the load queue */
+    void clearTasks() {
+        mQueue.clear();
+    }
+
+    /** Returns whether the load queue is empty */
+    boolean isEmpty() {
+        return mQueue.isEmpty();
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskStack.java
new file mode 100644
index 0000000..01e6ba3
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskStack.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.model;
+
+import android.content.ComponentName;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.SparseArray;
+
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.recents.utilities.AnimationProps;
+import com.android.systemui.shared.system.PackageManagerWrapper;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * The task stack contains a list of multiple tasks.
+ */
+public class TaskStack {
+
+    private static final String TAG = "TaskStack";
+
+    /** Task stack callbacks */
+    public interface TaskStackCallbacks {
+        /**
+         * Notifies when a new task has been added to the stack.
+         */
+        void onStackTaskAdded(TaskStack stack, Task newTask);
+
+        /**
+         * Notifies when a task has been removed from the stack.
+         */
+        void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
+                AnimationProps animation, boolean fromDockGesture,
+                boolean dismissRecentsIfAllRemoved);
+
+        /**
+         * Notifies when all tasks have been removed from the stack.
+         */
+        void onStackTasksRemoved(TaskStack stack);
+
+        /**
+         * Notifies when tasks in the stack have been updated.
+         */
+        void onStackTasksUpdated(TaskStack stack);
+    }
+
+    private final ArrayList<Task> mRawTaskList = new ArrayList<>();
+    private final FilteredTaskList mStackTaskList = new FilteredTaskList();
+    private TaskStackCallbacks mCb;
+
+    public TaskStack() {
+        // Ensure that we only show stack tasks
+        mStackTaskList.setFilter(new TaskFilter() {
+            @Override
+            public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
+                return  t.isStackTask;
+            }
+        });
+    }
+
+    /** Sets the callbacks for this task stack. */
+    public void setCallbacks(TaskStackCallbacks cb) {
+        mCb = cb;
+    }
+
+    /**
+     * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
+     * how they should update themselves.
+     */
+    public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) {
+        removeTask(t, animation, fromDockGesture, true /* dismissRecentsIfAllRemoved */);
+    }
+
+    /**
+     * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
+     * how they should update themselves.
+     */
+    public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture,
+            boolean dismissRecentsIfAllRemoved) {
+        if (mStackTaskList.contains(t)) {
+            mStackTaskList.remove(t);
+            Task newFrontMostTask = getFrontMostTask();
+            if (mCb != null) {
+                // Notify that a task has been removed
+                mCb.onStackTaskRemoved(this, t, newFrontMostTask, animation,
+                        fromDockGesture, dismissRecentsIfAllRemoved);
+            }
+        }
+        mRawTaskList.remove(t);
+    }
+
+    /**
+     * Removes all tasks from the stack.
+     */
+    public void removeAllTasks(boolean notifyStackChanges) {
+        ArrayList<Task> tasks = mStackTaskList.getTasks();
+        for (int i = tasks.size() - 1; i >= 0; i--) {
+            Task t = tasks.get(i);
+            mStackTaskList.remove(t);
+            mRawTaskList.remove(t);
+        }
+        if (mCb != null && notifyStackChanges) {
+            // Notify that all tasks have been removed
+            mCb.onStackTasksRemoved(this);
+        }
+    }
+
+
+    /**
+     * @see #setTasks(List, boolean)
+     */
+    public void setTasks(TaskStack stack, boolean notifyStackChanges) {
+        setTasks(stack.mRawTaskList, notifyStackChanges);
+    }
+
+    /**
+     * Sets a few tasks in one go, without calling any callbacks.
+     *
+     * @param tasks the new set of tasks to replace the current set.
+     * @param notifyStackChanges whether or not to callback on specific changes to the list of tasks.
+     */
+    public void setTasks(List<Task> tasks, boolean notifyStackChanges) {
+        // Compute a has set for each of the tasks
+        ArrayMap<TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
+        ArrayMap<TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
+        ArrayList<Task> addedTasks = new ArrayList<>();
+        ArrayList<Task> removedTasks = new ArrayList<>();
+        ArrayList<Task> allTasks = new ArrayList<>();
+
+        // Disable notifications if there are no callbacks
+        if (mCb == null) {
+            notifyStackChanges = false;
+        }
+
+        // Remove any tasks that no longer exist
+        int taskCount = mRawTaskList.size();
+        for (int i = taskCount - 1; i >= 0; i--) {
+            Task task = mRawTaskList.get(i);
+            if (!newTasksMap.containsKey(task.key)) {
+                if (notifyStackChanges) {
+                    removedTasks.add(task);
+                }
+            }
+        }
+
+        // Add any new tasks
+        taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task newTask = tasks.get(i);
+            Task currentTask = currentTasksMap.get(newTask.key);
+            if (currentTask == null && notifyStackChanges) {
+                addedTasks.add(newTask);
+            } else if (currentTask != null) {
+                // The current task has bound callbacks, so just copy the data from the new task
+                // state and add it back into the list
+                currentTask.copyFrom(newTask);
+                newTask = currentTask;
+            }
+            allTasks.add(newTask);
+        }
+
+        // Sort all the tasks to ensure they are ordered correctly
+        for (int i = allTasks.size() - 1; i >= 0; i--) {
+            allTasks.get(i).temporarySortIndexInStack = i;
+        }
+
+        mStackTaskList.set(allTasks);
+        mRawTaskList.clear();
+        mRawTaskList.addAll(allTasks);
+
+        // Only callback for the removed tasks after the stack has updated
+        int removedTaskCount = removedTasks.size();
+        Task newFrontMostTask = getFrontMostTask();
+        for (int i = 0; i < removedTaskCount; i++) {
+            mCb.onStackTaskRemoved(this, removedTasks.get(i), newFrontMostTask,
+                    AnimationProps.IMMEDIATE, false /* fromDockGesture */,
+                    true /* dismissRecentsIfAllRemoved */);
+        }
+
+        // Only callback for the newly added tasks after this stack has been updated
+        int addedTaskCount = addedTasks.size();
+        for (int i = 0; i < addedTaskCount; i++) {
+            mCb.onStackTaskAdded(this, addedTasks.get(i));
+        }
+
+        // Notify that the task stack has been updated
+        if (notifyStackChanges) {
+            mCb.onStackTasksUpdated(this);
+        }
+    }
+
+    /**
+     * Gets the front-most task in the stack.
+     */
+    public Task getFrontMostTask() {
+        ArrayList<Task> stackTasks = mStackTaskList.getTasks();
+        if (stackTasks.isEmpty()) {
+            return null;
+        }
+        return stackTasks.get(stackTasks.size() - 1);
+    }
+
+    /** Gets the task keys */
+    public ArrayList<TaskKey> getTaskKeys() {
+        ArrayList<TaskKey> taskKeys = new ArrayList<>();
+        ArrayList<Task> tasks = computeAllTasksList();
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasks.get(i);
+            taskKeys.add(task.key);
+        }
+        return taskKeys;
+    }
+
+    /**
+     * Returns the set of "active" (non-historical) tasks in the stack that have been used recently.
+     */
+    public ArrayList<Task> getTasks() {
+        return mStackTaskList.getTasks();
+    }
+
+    /**
+     * Computes a set of all the active and historical tasks.
+     */
+    public ArrayList<Task> computeAllTasksList() {
+        ArrayList<Task> tasks = new ArrayList<>();
+        tasks.addAll(mStackTaskList.getTasks());
+        return tasks;
+    }
+
+    /**
+     * Returns the number of stack tasks.
+     */
+    public int getTaskCount() {
+        return mStackTaskList.size();
+    }
+
+    /**
+     * Returns the task in stack tasks which is the launch target.
+     */
+    public Task getLaunchTarget() {
+        ArrayList<Task> tasks = mStackTaskList.getTasks();
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasks.get(i);
+            if (task.isLaunchTarget) {
+                return task;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns whether the next launch target should actually be the PiP task.
+     */
+    public boolean isNextLaunchTargetPip(long lastPipTime) {
+        Task launchTarget = getLaunchTarget();
+        Task nextLaunchTarget = getNextLaunchTargetRaw();
+        if (nextLaunchTarget != null && lastPipTime > 0) {
+            // If the PiP time is more recent than the next launch target, then launch the PiP task
+            return lastPipTime > nextLaunchTarget.key.lastActiveTime;
+        } else if (launchTarget != null && lastPipTime > 0 && getTaskCount() == 1) {
+            // Otherwise, if there is no next launch target, but there is a PiP, then launch
+            // the PiP task
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the task in stack tasks which should be launched next if Recents are toggled
+     * again, or null if there is no task to be launched. Callers should check
+     * {@link #isNextLaunchTargetPip(long)} before fetching the next raw launch target from the
+     * stack.
+     */
+    public Task getNextLaunchTarget() {
+        Task nextLaunchTarget = getNextLaunchTargetRaw();
+        if (nextLaunchTarget != null) {
+            return nextLaunchTarget;
+        }
+        return getTasks().get(getTaskCount() - 1);
+    }
+
+    private Task getNextLaunchTargetRaw() {
+        int taskCount = getTaskCount();
+        if (taskCount == 0) {
+            return null;
+        }
+        int launchTaskIndex = indexOfTask(getLaunchTarget());
+        if (launchTaskIndex != -1 && launchTaskIndex > 0) {
+            return getTasks().get(launchTaskIndex - 1);
+        }
+        return null;
+    }
+
+    /** Returns the index of this task in this current task stack */
+    public int indexOfTask(Task t) {
+        return mStackTaskList.indexOf(t);
+    }
+
+    /** Finds the task with the specified task id. */
+    public Task findTaskWithId(int taskId) {
+        ArrayList<Task> tasks = computeAllTasksList();
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasks.get(i);
+            if (task.key.id == taskId) {
+                return task;
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Computes the components of tasks in this stack that have been removed as a result of a change
+     * in the specified package.
+     */
+    public ArraySet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
+        // Identify all the tasks that should be removed as a result of the package being removed.
+        // Using a set to ensure that we callback once per unique component.
+        ArraySet<ComponentName> existingComponents = new ArraySet<>();
+        ArraySet<ComponentName> removedComponents = new ArraySet<>();
+        ArrayList<TaskKey> taskKeys = getTaskKeys();
+        int taskKeyCount = taskKeys.size();
+        for (int i = 0; i < taskKeyCount; i++) {
+            TaskKey t = taskKeys.get(i);
+
+            // Skip if this doesn't apply to the current user
+            if (t.userId != userId) continue;
+
+            ComponentName cn = t.getComponent();
+            if (cn.getPackageName().equals(packageName)) {
+                if (existingComponents.contains(cn)) {
+                    // If we know that the component still exists in the package, then skip
+                    continue;
+                }
+                if (PackageManagerWrapper.getInstance().getActivityInfo(cn, userId) != null) {
+                    existingComponents.add(cn);
+                } else {
+                    removedComponents.add(cn);
+                }
+            }
+        }
+        return removedComponents;
+    }
+
+    @Override
+    public String toString() {
+        String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
+        ArrayList<Task> tasks = mStackTaskList.getTasks();
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            str += "    " + tasks.get(i).toString() + "\n";
+        }
+        return str;
+    }
+
+    /**
+     * Given a list of tasks, returns a map of each task's key to the task.
+     */
+    private ArrayMap<TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
+        ArrayMap<TaskKey, Task> map = new ArrayMap<>(tasks.size());
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasks.get(i);
+            map.put(task.key, task);
+        }
+        return map;
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "  ";
+
+        writer.print(prefix); writer.print(TAG);
+        writer.print(" numStackTasks="); writer.print(mStackTaskList.size());
+        writer.println();
+        ArrayList<Task> tasks = mStackTaskList.getTasks();
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            tasks.get(i).dump(innerPrefix, writer);
+        }
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/AnimationProps.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/AnimationProps.java
new file mode 100644
index 0000000..5463998
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/AnimationProps.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2016 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.systemui.recents.utilities;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.util.SparseArray;
+import android.util.SparseLongArray;
+import android.view.View;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * The generic set of animation properties to animate a {@link View}. The animation can have
+ * different interpolators, start delays and durations for each of the different properties.
+ */
+public class AnimationProps {
+
+    private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+    public static final AnimationProps IMMEDIATE = new AnimationProps(0, LINEAR_INTERPOLATOR);
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ALL, TRANSLATION_X, TRANSLATION_Y, TRANSLATION_Z, ALPHA, SCALE, BOUNDS})
+    public @interface PropType {}
+
+    public static final int ALL = 0;
+    public static final int TRANSLATION_X = 1;
+    public static final int TRANSLATION_Y = 2;
+    public static final int TRANSLATION_Z = 3;
+    public static final int ALPHA = 4;
+    public static final int SCALE = 5;
+    public static final int BOUNDS = 6;
+    public static final int DIM_ALPHA = 7;
+
+    private SparseLongArray mPropStartDelay;
+    private SparseLongArray mPropDuration;
+    private SparseArray<Interpolator> mPropInterpolators;
+    private Animator.AnimatorListener mListener;
+
+    /**
+     * The builder constructor.
+     */
+    public AnimationProps() {}
+
+    /**
+     * Creates an animation with a default {@param duration} and {@param interpolator} for all
+     * properties in this animation.
+     */
+    public AnimationProps(int duration, Interpolator interpolator) {
+        this(0, duration, interpolator, null);
+    }
+
+    /**
+     * Creates an animation with a default {@param duration} and {@param interpolator} for all
+     * properties in this animation.
+     */
+    public AnimationProps(int duration, Interpolator interpolator,
+            Animator.AnimatorListener listener) {
+        this(0, duration, interpolator, listener);
+    }
+
+    /**
+     * Creates an animation with a default {@param startDelay}, {@param duration} and
+     * {@param interpolator} for all properties in this animation.
+     */
+    public AnimationProps(int startDelay, int duration, Interpolator interpolator) {
+        this(startDelay, duration, interpolator, null);
+    }
+
+    /**
+     * Creates an animation with a default {@param startDelay}, {@param duration} and
+     * {@param interpolator} for all properties in this animation.
+     */
+    public AnimationProps(int startDelay, int duration, Interpolator interpolator,
+            Animator.AnimatorListener listener) {
+        setStartDelay(ALL, startDelay);
+        setDuration(ALL, duration);
+        setInterpolator(ALL, interpolator);
+        setListener(listener);
+    }
+
+    /**
+     * Creates a new {@link AnimatorSet} that will animate the given animators.  Callers need to
+     * manually apply the individual animation properties for each of the animators respectively.
+     */
+    public AnimatorSet createAnimator(List<Animator> animators) {
+        AnimatorSet anim = new AnimatorSet();
+        if (mListener != null) {
+            anim.addListener(mListener);
+        }
+        anim.playTogether(animators);
+        return anim;
+    }
+
+    /**
+     * Applies the specific start delay, duration and interpolator to the given {@param animator}
+     * for the specified {@param propertyType}.
+     */
+    public <T extends ValueAnimator> T apply(@PropType int propertyType, T animator) {
+        animator.setStartDelay(getStartDelay(propertyType));
+        animator.setDuration(getDuration(propertyType));
+        animator.setInterpolator(getInterpolator(propertyType));
+        return animator;
+    }
+
+    /**
+     * Sets a start delay for a specific property.
+     */
+    public AnimationProps setStartDelay(@PropType int propertyType, int startDelay) {
+        if (mPropStartDelay == null) {
+            mPropStartDelay = new SparseLongArray();
+        }
+        mPropStartDelay.append(propertyType, startDelay);
+        return this;
+    }
+
+    /**
+     * Returns the start delay for a specific property.
+     */
+    public long getStartDelay(@PropType int propertyType) {
+        if (mPropStartDelay != null) {
+            long startDelay = mPropStartDelay.get(propertyType, -1);
+            if (startDelay != -1) {
+                return startDelay;
+            }
+            return mPropStartDelay.get(ALL, 0);
+        }
+        return 0;
+    }
+
+    /**
+     * Sets a duration for a specific property.
+     */
+    public AnimationProps setDuration(@PropType int propertyType, int duration) {
+        if (mPropDuration == null) {
+            mPropDuration = new SparseLongArray();
+        }
+        mPropDuration.append(propertyType, duration);
+        return this;
+    }
+
+    /**
+     * Returns the duration for a specific property.
+     */
+    public long getDuration(@PropType int propertyType) {
+        if (mPropDuration != null) {
+            long duration = mPropDuration.get(propertyType, -1);
+            if (duration != -1) {
+                return duration;
+            }
+            return mPropDuration.get(ALL, 0);
+        }
+        return 0;
+    }
+
+    /**
+     * Sets an interpolator for a specific property.
+     */
+    public AnimationProps setInterpolator(@PropType int propertyType, Interpolator interpolator) {
+        if (mPropInterpolators == null) {
+            mPropInterpolators = new SparseArray<>();
+        }
+        mPropInterpolators.append(propertyType, interpolator);
+        return this;
+    }
+
+    /**
+     * Returns the interpolator for a specific property, falling back to the general interpolator
+     * if there is no specific property interpolator.
+     */
+    public Interpolator getInterpolator(@PropType int propertyType) {
+        if (mPropInterpolators != null) {
+            Interpolator interp = mPropInterpolators.get(propertyType);
+            if (interp != null) {
+                return interp;
+            }
+            return mPropInterpolators.get(ALL, LINEAR_INTERPOLATOR);
+        }
+        return LINEAR_INTERPOLATOR;
+    }
+
+    /**
+     * Sets an animator listener for this animation.
+     */
+    public AnimationProps setListener(Animator.AnimatorListener listener) {
+        mListener = listener;
+        return this;
+    }
+
+    /**
+     * Returns the animator listener for this animation.
+     */
+    public Animator.AnimatorListener getListener() {
+        return mListener;
+    }
+
+    /**
+     * Returns whether this animation has any duration.
+     */
+    public boolean isImmediate() {
+        int count = mPropDuration.size();
+        for (int i = 0; i < count; i++) {
+            if (mPropDuration.valueAt(i) > 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/Utilities.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/Utilities.java
new file mode 100644
index 0000000..ff58eba
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/Utilities.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.utilities;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.RectEvaluator;
+import android.annotation.FloatRange;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Trace;
+import android.util.ArraySet;
+import android.util.IntProperty;
+import android.util.Property;
+import android.util.TypedValue;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.ViewRootImpl;
+import android.view.ViewStub;
+
+import com.android.systemui.shared.recents.utilities.RectFEvaluator;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/* Common code */
+public class Utilities {
+
+    public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
+            new IntProperty<Drawable>("drawableAlpha") {
+                @Override
+                public void setValue(Drawable object, int alpha) {
+                    object.setAlpha(alpha);
+                }
+
+                @Override
+                public Integer get(Drawable object) {
+                    return object.getAlpha();
+                }
+            };
+
+    public static final Property<Drawable, Rect> DRAWABLE_RECT =
+            new Property<Drawable, Rect>(Rect.class, "drawableBounds") {
+                @Override
+                public void set(Drawable object, Rect bounds) {
+                    object.setBounds(bounds);
+                }
+
+                @Override
+                public Rect get(Drawable object) {
+                    return object.getBounds();
+                }
+            };
+
+    public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator();
+    public static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
+
+    /**
+     * @return the first parent walking up the view hierarchy that has the given class type.
+     *
+     * @param parentClass must be a class derived from {@link View}
+     */
+    public static <T extends View> T findParent(View v, Class<T> parentClass) {
+        ViewParent parent = v.getParent();
+        while (parent != null) {
+            if (parentClass.isAssignableFrom(parent.getClass())) {
+                return (T) parent;
+            }
+            parent = parent.getParent();
+        }
+        return null;
+    }
+
+    /**
+     * Initializes the {@param setOut} with the given object.
+     */
+    public static <T> ArraySet<T> objectToSet(T obj, ArraySet<T> setOut) {
+        setOut.clear();
+        if (obj != null) {
+            setOut.add(obj);
+        }
+        return setOut;
+    }
+
+    /**
+     * Replaces the contents of {@param setOut} with the contents of the {@param array}.
+     */
+    public static <T> ArraySet<T> arrayToSet(T[] array, ArraySet<T> setOut) {
+        setOut.clear();
+        if (array != null) {
+            Collections.addAll(setOut, array);
+        }
+        return setOut;
+    }
+
+    /**
+     * @return the clamped {@param value} between the provided {@param min} and {@param max}.
+     */
+    public static int clamp(int value, int min, int max) {
+        return Math.max(min, Math.min(max, value));
+    }
+
+    /**
+     * @return the clamped {@param value} between 0 and 1.
+     */
+    public static float clamp01(float value) {
+        return Math.max(0f, Math.min(1f, value));
+    }
+
+    /**
+     * Scales the {@param value} to be proportionally between the {@param min} and
+     * {@param max} values.
+     *
+     * @param value must be between 0 and 1
+     */
+    public static float mapRange(@FloatRange(from=0.0,to=1.0) float value, float min, float max) {
+        return min + (value * (max - min));
+    }
+
+    /**
+     * Scales the {@param value} proportionally from {@param min} and {@param max} to 0 and 1.
+     *
+     * @param value must be between {@param min} and {@param max}
+     */
+    public static float unmapRange(float value, float min, float max) {
+        return (value - min) / (max - min);
+    }
+
+    /** Scales a rect about its centroid */
+    public static void scaleRectAboutCenter(RectF r, float scale) {
+        if (scale != 1.0f) {
+            float cx = r.centerX();
+            float cy = r.centerY();
+            r.offset(-cx, -cy);
+            r.left *= scale;
+            r.top *= scale;
+            r.right *= scale;
+            r.bottom *= scale;
+            r.offset(cx, cy);
+        }
+    }
+
+    /** Returns the base color overlaid with another overlay color with a specified alpha. */
+    public static int getColorWithOverlay(int baseColor, int overlayColor, float overlayAlpha) {
+        return Color.rgb(
+            (int) (overlayAlpha * Color.red(baseColor) +
+                    (1f - overlayAlpha) * Color.red(overlayColor)),
+            (int) (overlayAlpha * Color.green(baseColor) +
+                    (1f - overlayAlpha) * Color.green(overlayColor)),
+            (int) (overlayAlpha * Color.blue(baseColor) +
+                    (1f - overlayAlpha) * Color.blue(overlayColor)));
+    }
+
+    /**
+     * Cancels an animation ensuring that if it has listeners, onCancel and onEnd
+     * are not called.
+     */
+    public static void cancelAnimationWithoutCallbacks(Animator animator) {
+        if (animator != null && animator.isStarted()) {
+            removeAnimationListenersRecursive(animator);
+            animator.cancel();
+        }
+    }
+
+    /**
+     * Recursively removes all the listeners of all children of this animator
+     */
+    public static void removeAnimationListenersRecursive(Animator animator) {
+        if (animator instanceof AnimatorSet) {
+            ArrayList<Animator> animators = ((AnimatorSet) animator).getChildAnimations();
+            for (int i = animators.size() - 1; i >= 0; i--) {
+                removeAnimationListenersRecursive(animators.get(i));
+            }
+        }
+        animator.removeAllListeners();
+    }
+
+    /**
+     * Sets the given {@link View}'s frame from its current translation.
+     */
+    public static void setViewFrameFromTranslation(View v) {
+        RectF taskViewRect = new RectF(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+        taskViewRect.offset(v.getTranslationX(), v.getTranslationY());
+        v.setTranslationX(0);
+        v.setTranslationY(0);
+        v.setLeftTopRightBottom((int) taskViewRect.left, (int) taskViewRect.top,
+                (int) taskViewRect.right, (int) taskViewRect.bottom);
+    }
+
+    /**
+     * Returns a view stub for the given view id.
+     */
+    public static ViewStub findViewStubById(View v, int stubId) {
+        return (ViewStub) v.findViewById(stubId);
+    }
+
+    /**
+     * Returns a view stub for the given view id.
+     */
+    public static ViewStub findViewStubById(Activity a, int stubId) {
+        return (ViewStub) a.findViewById(stubId);
+    }
+
+    /**
+     * Used for debugging, converts DP to PX.
+     */
+    public static float dpToPx(Resources res, float dp) {
+        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
+    }
+
+    /**
+     * Adds a trace event for debugging.
+     */
+    public static void addTraceEvent(String event) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, event);
+        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+    }
+
+    /**
+     * Returns whether this view, or one of its descendants have accessibility focus.
+     */
+    public static boolean isDescendentAccessibilityFocused(View v) {
+        if (v.isAccessibilityFocused()) {
+            return true;
+        }
+
+        if (v instanceof ViewGroup) {
+            ViewGroup vg = (ViewGroup) v;
+            int childCount = vg.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                if (isDescendentAccessibilityFocused(vg.getChildAt(i))) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the application configuration, which is independent of the activity's current
+     * configuration in multiwindow.
+     */
+    public static Configuration getAppConfiguration(Context context) {
+        return context.getApplicationContext().getResources().getConfiguration();
+    }
+
+    /**
+     * @return The next frame name for the specified surface or -1 if the surface is no longer
+     *         valid.
+     */
+    public static long getNextFrameNumber(Surface s) {
+        return s != null && s.isValid()
+                ? s.getNextFrameNumber()
+                : -1;
+
+    }
+
+    /**
+     * @return The surface for the specified view.
+     */
+    public static @Nullable Surface getSurface(View v) {
+        ViewRootImpl viewRoot = v.getViewRootImpl();
+        if (viewRoot == null) {
+            return null;
+        }
+        return viewRoot.mSurface;
+    }
+
+    /**
+     * Returns a lightweight dump of a rect.
+     */
+    public static String dumpRect(Rect r) {
+        if (r == null) {
+            return "N:0,0-0,0";
+        }
+        return r.left + "," + r.top + "-" + r.right + "," + r.bottom;
+    }
+
+    /**
+     * Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
+     */
+    public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) {
+        Message msg = h.obtainMessage().setCallback(r);
+        h.sendMessageAtFrontOfQueue(msg);
+    }
+
+    /** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
+    public static float computeContrastBetweenColors(int bg, int fg) {
+        float bgR = Color.red(bg) / 255f;
+        float bgG = Color.green(bg) / 255f;
+        float bgB = Color.blue(bg) / 255f;
+        bgR = (bgR < 0.03928f) ? bgR / 12.92f : (float) Math.pow((bgR + 0.055f) / 1.055f, 2.4f);
+        bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f);
+        bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f);
+        float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB;
+
+        float fgR = Color.red(fg) / 255f;
+        float fgG = Color.green(fg) / 255f;
+        float fgB = Color.blue(fg) / 255f;
+        fgR = (fgR < 0.03928f) ? fgR / 12.92f : (float) Math.pow((fgR + 0.055f) / 1.055f, 2.4f);
+        fgG = (fgG < 0.03928f) ? fgG / 12.92f : (float) Math.pow((fgG + 0.055f) / 1.055f, 2.4f);
+        fgB = (fgB < 0.03928f) ? fgB / 12.92f : (float) Math.pow((fgB + 0.055f) / 1.055f, 2.4f);
+        float fgL = 0.2126f * fgR + 0.7152f * fgG + 0.0722f * fgB;
+
+        return Math.abs((fgL + 0.05f) / (bgL + 0.05f));
+    }
+
+    /**
+     * @return the clamped {@param value} between the provided {@param min} and {@param max}.
+     */
+    public static float clamp(float value, float min, float max) {
+        return Math.max(min, Math.min(max, value));
+    }
+
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/AnimateableViewBounds.java
new file mode 100644
index 0000000..e188506
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+import com.android.systemui.recents.utilities.Utilities;
+
+/**
+ * An outline provider that has a clip and outline that can be animated.
+ */
+public class AnimateableViewBounds extends ViewOutlineProvider {
+
+    private static final float MIN_ALPHA = 0.1f;
+    private static final float MAX_ALPHA = 0.8f;
+
+    protected View mSourceView;
+    protected Rect mClipRect = new Rect();
+    protected Rect mClipBounds = new Rect();
+    protected Rect mLastClipBounds = new Rect();
+    protected int mCornerRadius;
+    protected float mAlpha = 1f;
+
+    public AnimateableViewBounds(View source, int cornerRadius) {
+        mSourceView = source;
+        mCornerRadius = cornerRadius;
+    }
+
+    /**
+     * Resets the right and bottom clip for this view.
+     */
+    public void reset() {
+        mClipRect.set(0, 0, 0, 0);
+        updateClipBounds();
+    }
+
+    @Override
+    public void getOutline(View view, Outline outline) {
+        outline.setAlpha(Utilities.mapRange(mAlpha, MIN_ALPHA, MAX_ALPHA));
+        if (mCornerRadius > 0) {
+            outline.setRoundRect(mClipRect.left, mClipRect.top,
+                    mSourceView.getWidth() - mClipRect.right,
+                    mSourceView.getHeight() - mClipRect.bottom,
+                    mCornerRadius);
+        } else {
+            outline.setRect(mClipRect.left, mClipRect.top,
+                    mSourceView.getWidth() - mClipRect.right,
+                    mSourceView.getHeight() - mClipRect.bottom);
+        }
+    }
+
+    /**
+     * Sets the view outline alpha.
+     */
+    public void setAlpha(float alpha) {
+        if (Float.compare(alpha, mAlpha) != 0) {
+            mAlpha = alpha;
+            // TODO, If both clip and alpha change in the same frame, only invalidate once
+            mSourceView.invalidateOutline();
+        }
+    }
+
+    /**
+     * @return the outline alpha.
+     */
+    public float getAlpha() {
+        return mAlpha;
+    }
+
+    /**
+     * Sets the top clip.
+     */
+    public void setClipTop(int top) {
+        mClipRect.top = top;
+        updateClipBounds();
+    }
+
+    /**
+     * @return the top clip.
+     */
+    public int getClipTop() {
+        return mClipRect.top;
+    }
+
+    /**
+     * Sets the bottom clip.
+     */
+    public void setClipBottom(int bottom) {
+        mClipRect.bottom = bottom;
+        updateClipBounds();
+    }
+
+    /**
+     * @return the bottom clip.
+     */
+    public int getClipBottom() {
+        return mClipRect.bottom;
+    }
+
+    /**
+     * @return the clip bounds.
+     */
+    public Rect getClipBounds() {
+        return mClipBounds;
+    }
+
+    protected void updateClipBounds() {
+        mClipBounds.set(Math.max(0, mClipRect.left), Math.max(0, mClipRect.top),
+                mSourceView.getWidth() - Math.max(0, mClipRect.right),
+                mSourceView.getHeight() - Math.max(0, mClipRect.bottom));
+        if (!mLastClipBounds.equals(mClipBounds)) {
+            mSourceView.setClipBounds(mClipBounds);
+            // TODO, If both clip and alpha change in the same frame, only invalidate once
+            mSourceView.invalidateOutline();
+            mLastClipBounds.set(mClipBounds);
+        }
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DockState.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DockState.java
new file mode 100644
index 0000000..d9c921c
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DockState.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2017 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.systemui.recents.views;
+
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
+import android.util.IntProperty;
+import android.view.animation.Interpolator;
+
+import com.android.internal.policy.DockedDividerUtils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.utilities.Utilities;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+
+/**
+ * The various possible dock states when dragging and dropping a task.
+ */
+public class DockState implements DropTarget {
+
+    public static final int DOCK_AREA_BG_COLOR = 0xFFffffff;
+    public static final int DOCK_AREA_GRID_BG_COLOR = 0xFF000000;
+
+    // The rotation to apply to the hint text
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({HORIZONTAL, VERTICAL})
+    public @interface TextOrientation {}
+    private static final int HORIZONTAL = 0;
+    private static final int VERTICAL = 1;
+
+    private static final int DOCK_AREA_ALPHA = 80;
+    public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, 255, HORIZONTAL,
+            null, null, null);
+    public static final DockState LEFT = new DockState(DOCKED_LEFT,
+            SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL,
+            new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1),
+            new RectF(0, 0, 0.5f, 1));
+    public static final DockState TOP = new DockState(DOCKED_TOP,
+            SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
+            new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f),
+            new RectF(0, 0, 1, 0.5f));
+    public static final DockState RIGHT = new DockState(DOCKED_RIGHT,
+            SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL,
+            new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1),
+            new RectF(0.5f, 0, 1, 1));
+    public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM,
+            SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
+            new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1),
+            new RectF(0, 0.5f, 1, 1));
+
+    @Override
+    public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
+            boolean isCurrentTarget) {
+        if (isCurrentTarget) {
+            getMappedRect(expandedTouchDockArea, width, height, mTmpRect);
+            return mTmpRect.contains(x, y);
+        } else {
+            getMappedRect(touchArea, width, height, mTmpRect);
+            updateBoundsWithSystemInsets(mTmpRect, insets);
+            return mTmpRect.contains(x, y);
+        }
+    }
+
+    // Represents the view state of this dock state
+    public static class ViewState {
+        private static final IntProperty<ViewState> HINT_ALPHA =
+                new IntProperty<ViewState>("drawableAlpha") {
+                    @Override
+                    public void setValue(ViewState object, int alpha) {
+                        object.mHintTextAlpha = alpha;
+                        object.dockAreaOverlay.invalidateSelf();
+                    }
+
+                    @Override
+                    public Integer get(ViewState object) {
+                        return object.mHintTextAlpha;
+                    }
+                };
+
+        public final int dockAreaAlpha;
+        public final ColorDrawable dockAreaOverlay;
+        public final int hintTextAlpha;
+        public final int hintTextOrientation;
+
+        private final int mHintTextResId;
+        private String mHintText;
+        private Paint mHintTextPaint;
+        private Point mHintTextBounds = new Point();
+        private int mHintTextAlpha = 255;
+        private AnimatorSet mDockAreaOverlayAnimator;
+        private Rect mTmpRect = new Rect();
+
+        private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation,
+                int hintTextResId) {
+            dockAreaAlpha = areaAlpha;
+            dockAreaOverlay = new ColorDrawable(LegacyRecentsImpl.getConfiguration().isGridEnabled
+                    ? DOCK_AREA_GRID_BG_COLOR : DOCK_AREA_BG_COLOR);
+            dockAreaOverlay.setAlpha(0);
+            hintTextAlpha = hintAlpha;
+            hintTextOrientation = hintOrientation;
+            mHintTextResId = hintTextResId;
+            mHintTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            mHintTextPaint.setColor(Color.WHITE);
+        }
+
+        /**
+         * Updates the view state with the given context.
+         */
+        public void update(Context context) {
+            Resources res = context.getResources();
+            mHintText = context.getString(mHintTextResId);
+            mHintTextPaint.setTextSize(res.getDimensionPixelSize(
+                    R.dimen.recents_drag_hint_text_size));
+            mHintTextPaint.getTextBounds(mHintText, 0, mHintText.length(), mTmpRect);
+            mHintTextBounds.set((int) mHintTextPaint.measureText(mHintText), mTmpRect.height());
+        }
+
+        /**
+         * Draws the current view state.
+         */
+        public void draw(Canvas canvas) {
+            // Draw the overlay background
+            if (dockAreaOverlay.getAlpha() > 0) {
+                dockAreaOverlay.draw(canvas);
+            }
+
+            // Draw the hint text
+            if (mHintTextAlpha > 0) {
+                Rect bounds = dockAreaOverlay.getBounds();
+                int x = bounds.left + (bounds.width() - mHintTextBounds.x) / 2;
+                int y = bounds.top + (bounds.height() + mHintTextBounds.y) / 2;
+                mHintTextPaint.setAlpha(mHintTextAlpha);
+                if (hintTextOrientation == VERTICAL) {
+                    canvas.save();
+                    canvas.rotate(-90f, bounds.centerX(), bounds.centerY());
+                }
+                canvas.drawText(mHintText, x, y, mHintTextPaint);
+                if (hintTextOrientation == VERTICAL) {
+                    canvas.restore();
+                }
+            }
+        }
+
+        /**
+         * Creates a new bounds and alpha animation.
+         */
+        public void startAnimation(Rect bounds, int areaAlpha, int hintAlpha, int duration,
+                Interpolator interpolator, boolean animateAlpha, boolean animateBounds) {
+            if (mDockAreaOverlayAnimator != null) {
+                mDockAreaOverlayAnimator.cancel();
+            }
+
+            ObjectAnimator anim;
+            ArrayList<Animator> animators = new ArrayList<>();
+            if (dockAreaOverlay.getAlpha() != areaAlpha) {
+                if (animateAlpha) {
+                    anim = ObjectAnimator.ofInt(dockAreaOverlay,
+                            Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), areaAlpha);
+                    anim.setDuration(duration);
+                    anim.setInterpolator(interpolator);
+                    animators.add(anim);
+                } else {
+                    dockAreaOverlay.setAlpha(areaAlpha);
+                }
+            }
+            if (mHintTextAlpha != hintAlpha) {
+                if (animateAlpha) {
+                    anim = ObjectAnimator.ofInt(this, HINT_ALPHA, mHintTextAlpha,
+                            hintAlpha);
+                    anim.setDuration(150);
+                    anim.setInterpolator(hintAlpha > mHintTextAlpha
+                            ? Interpolators.ALPHA_IN
+                            : Interpolators.ALPHA_OUT);
+                    animators.add(anim);
+                } else {
+                    mHintTextAlpha = hintAlpha;
+                    dockAreaOverlay.invalidateSelf();
+                }
+            }
+            if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) {
+                if (animateBounds) {
+                    PropertyValuesHolder prop = PropertyValuesHolder.ofObject(
+                            Utilities.DRAWABLE_RECT, Utilities.RECT_EVALUATOR,
+                            new Rect(dockAreaOverlay.getBounds()), bounds);
+                    anim = ObjectAnimator.ofPropertyValuesHolder(dockAreaOverlay, prop);
+                    anim.setDuration(duration);
+                    anim.setInterpolator(interpolator);
+                    animators.add(anim);
+                } else {
+                    dockAreaOverlay.setBounds(bounds);
+                }
+            }
+            if (!animators.isEmpty()) {
+                mDockAreaOverlayAnimator = new AnimatorSet();
+                mDockAreaOverlayAnimator.playTogether(animators);
+                mDockAreaOverlayAnimator.start();
+            }
+        }
+    }
+
+    public final int dockSide;
+    public final int createMode;
+    public final ViewState viewState;
+    private final RectF touchArea;
+    private final RectF dockArea;
+    private final RectF expandedTouchDockArea;
+    private static final Rect mTmpRect = new Rect();
+
+    /**
+     * @param createMode used to pass to ActivityManager to dock the task
+     * @param touchArea the area in which touch will initiate this dock state
+     * @param dockArea the visible dock area
+     * @param expandedTouchDockArea the area in which touch will continue to dock after entering
+     *                              the initial touch area.  This is also the new dock area to
+     *                              draw.
+     */
+    DockState(int dockSide, int createMode, int dockAreaAlpha, int hintTextAlpha,
+            @TextOrientation int hintTextOrientation, RectF touchArea, RectF dockArea,
+            RectF expandedTouchDockArea) {
+        this.dockSide = dockSide;
+        this.createMode = createMode;
+        this.viewState = new ViewState(dockAreaAlpha, hintTextAlpha, hintTextOrientation,
+                R.string.recents_drag_hint_message);
+        this.dockArea = dockArea;
+        this.touchArea = touchArea;
+        this.expandedTouchDockArea = expandedTouchDockArea;
+    }
+
+    /**
+     * Updates the dock state with the given context.
+     */
+    public void update(Context context) {
+        viewState.update(context);
+    }
+
+    /**
+     * Returns the docked task bounds with the given {@param width} and {@param height}.
+     */
+    public Rect getPreDockedBounds(int width, int height, Rect insets) {
+        getMappedRect(dockArea, width, height, mTmpRect);
+        return updateBoundsWithSystemInsets(mTmpRect, insets);
+    }
+
+    /**
+     * Returns the expanded docked task bounds with the given {@param width} and
+     * {@param height}.
+     */
+    public Rect getDockedBounds(int width, int height, int dividerSize, Rect insets,
+            Resources res) {
+        // Calculate the docked task bounds
+        boolean isHorizontalDivision =
+                res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
+        int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+                insets, width, height, dividerSize);
+        Rect newWindowBounds = new Rect();
+        DockedDividerUtils.calculateBoundsForPosition(position, dockSide, newWindowBounds,
+                width, height, dividerSize);
+        return newWindowBounds;
+    }
+
+    /**
+     * Returns the task stack bounds with the given {@param width} and
+     * {@param height}.
+     */
+    public Rect getDockedTaskStackBounds(Rect displayRect, int width, int height,
+            int dividerSize, Rect insets, TaskStackLayoutAlgorithm layoutAlgorithm,
+            Resources res, Rect windowRectOut) {
+        // Calculate the inverse docked task bounds
+        boolean isHorizontalDivision =
+                res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
+        int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+                insets, width, height, dividerSize);
+        DockedDividerUtils.calculateBoundsForPosition(position,
+                DockedDividerUtils.invertDockSide(dockSide), windowRectOut, width, height,
+                dividerSize);
+
+        // Calculate the task stack bounds from the new window bounds
+        Rect taskStackBounds = new Rect();
+        // If the task stack bounds is specifically under the dock area, then ignore the top
+        // inset
+        int top = dockArea.bottom < 1f
+                ? 0
+                : insets.top;
+        // For now, ignore the left insets since we always dock on the left and show Recents
+        // on the right
+        layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, 0, insets.right,
+                taskStackBounds);
+        return taskStackBounds;
+    }
+
+    /**
+     * Returns the expanded bounds in certain dock sides such that the bounds account for the
+     * system insets (namely the vertical nav bar).  This call modifies and returns the given
+     * {@param bounds}.
+     */
+    private Rect updateBoundsWithSystemInsets(Rect bounds, Rect insets) {
+        if (dockSide == DOCKED_LEFT) {
+            bounds.right += insets.left;
+        } else if (dockSide == DOCKED_RIGHT) {
+            bounds.left -= insets.right;
+        }
+        return bounds;
+    }
+
+    /**
+     * Returns the mapped rect to the given dimensions.
+     */
+    private void getMappedRect(RectF bounds, int width, int height, Rect out) {
+        out.set((int) (bounds.left * width), (int) (bounds.top * height),
+                (int) (bounds.right * width), (int) (bounds.bottom * height));
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DropTarget.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DropTarget.java
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FakeShadowDrawable.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FakeShadowDrawable.java
new file mode 100644
index 0000000..86b4297
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FakeShadowDrawable.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.RecentsConfiguration;
+
+/**
+ * A rounded rectangle drawable which also includes a shadow around. This is mostly copied from
+ * frameworks/support/v7/cardview/eclair-mr1/android/support/v7/widget/
+ * RoundRectDrawableWithShadow.java revision c42ba8c000d1e6ce85e152dfc17089a0a69e739f with a few
+ * modifications to suit our needs in SystemUI.
+ */
+class FakeShadowDrawable extends Drawable {
+    // used to calculate content padding
+    final static double COS_45 = Math.cos(Math.toRadians(45));
+
+    final static float SHADOW_MULTIPLIER = 1.5f;
+
+    final float mInsetShadow; // extra shadow to avoid gaps between card and shadow
+
+    Paint mCornerShadowPaint;
+
+    Paint mEdgeShadowPaint;
+
+    final RectF mCardBounds;
+
+    float mCornerRadius;
+
+    Path mCornerShadowPath;
+
+    // updated value with inset
+    float mMaxShadowSize;
+
+    // actual value set by developer
+    float mRawMaxShadowSize;
+
+    // multiplied value to account for shadow offset
+    float mShadowSize;
+
+    // actual value set by developer
+    float mRawShadowSize;
+
+    private boolean mDirty = true;
+
+    private final int mShadowStartColor;
+
+    private final int mShadowEndColor;
+
+    private boolean mAddPaddingForCorners = true;
+
+    /**
+     * If shadow size is set to a value above max shadow, we print a warning
+     */
+    private boolean mPrintedShadowClipWarning = false;
+
+    public FakeShadowDrawable(Resources resources, RecentsConfiguration config) {
+        mShadowStartColor = resources.getColor(R.color.fake_shadow_start_color);
+        mShadowEndColor = resources.getColor(R.color.fake_shadow_end_color);
+        mInsetShadow = resources.getDimension(R.dimen.fake_shadow_inset);
+        setShadowSize(resources.getDimensionPixelSize(R.dimen.fake_shadow_size),
+                resources.getDimensionPixelSize(R.dimen.fake_shadow_size));
+        mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+        mCornerShadowPaint.setStyle(Paint.Style.FILL);
+        mCornerShadowPaint.setDither(true);
+        mCornerRadius = LegacyRecentsImpl.getConfiguration().isGridEnabled ?
+                resources.getDimensionPixelSize(
+                    R.dimen.recents_grid_task_view_rounded_corners_radius) :
+                resources.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+        mCardBounds = new RectF();
+        mEdgeShadowPaint = new Paint(mCornerShadowPaint);
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mCornerShadowPaint.setAlpha(alpha);
+        mEdgeShadowPaint.setAlpha(alpha);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+        mDirty = true;
+    }
+
+    void setShadowSize(float shadowSize, float maxShadowSize) {
+        if (shadowSize < 0 || maxShadowSize < 0) {
+            throw new IllegalArgumentException("invalid shadow size");
+        }
+        if (shadowSize > maxShadowSize) {
+            shadowSize = maxShadowSize;
+            if (!mPrintedShadowClipWarning) {
+                Log.w("CardView", "Shadow size is being clipped by the max shadow size. See "
+                        + "{CardView#setMaxCardElevation}.");
+                mPrintedShadowClipWarning = true;
+            }
+        }
+        if (mRawShadowSize == shadowSize && mRawMaxShadowSize == maxShadowSize) {
+            return;
+        }
+        mRawShadowSize = shadowSize;
+        mRawMaxShadowSize = maxShadowSize;
+        mShadowSize = shadowSize * SHADOW_MULTIPLIER + mInsetShadow;
+        mMaxShadowSize = maxShadowSize + mInsetShadow;
+        mDirty = true;
+        invalidateSelf();
+    }
+
+    @Override
+    public boolean getPadding(Rect padding) {
+        int vOffset = (int) Math.ceil(calculateVerticalPadding(mRawMaxShadowSize, mCornerRadius,
+                mAddPaddingForCorners));
+        int hOffset = (int) Math.ceil(calculateHorizontalPadding(mRawMaxShadowSize, mCornerRadius,
+                mAddPaddingForCorners));
+        padding.set(hOffset, vOffset, hOffset, vOffset);
+        return true;
+    }
+
+    static float calculateVerticalPadding(float maxShadowSize, float cornerRadius,
+            boolean addPaddingForCorners) {
+        if (addPaddingForCorners) {
+            return (float) (maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius);
+        } else {
+            return maxShadowSize * SHADOW_MULTIPLIER;
+        }
+    }
+
+    static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius,
+            boolean addPaddingForCorners) {
+        if (addPaddingForCorners) {
+            return (float) (maxShadowSize + (1 - COS_45) * cornerRadius);
+        } else {
+            return maxShadowSize;
+        }
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mCornerShadowPaint.setColorFilter(colorFilter);
+        mEdgeShadowPaint.setColorFilter(colorFilter);
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.OPAQUE;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mDirty) {
+            buildComponents(getBounds());
+            mDirty = false;
+        }
+        canvas.translate(0, mRawShadowSize / 4);
+        drawShadow(canvas);
+        canvas.translate(0, -mRawShadowSize / 4);
+    }
+
+    private void drawShadow(Canvas canvas) {
+        final float edgeShadowTop = -mCornerRadius - mShadowSize;
+        final float inset = mCornerRadius + mInsetShadow + mRawShadowSize / 2;
+        final boolean drawHorizontalEdges = mCardBounds.width() - 2 * inset > 0;
+        final boolean drawVerticalEdges = mCardBounds.height() - 2 * inset > 0;
+        // LT
+        int saved = canvas.save();
+        canvas.translate(mCardBounds.left + inset, mCardBounds.top + inset);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        if (drawHorizontalEdges) {
+            canvas.drawRect(0, edgeShadowTop,
+                    mCardBounds.width() - 2 * inset, -mCornerRadius,
+                    mEdgeShadowPaint);
+        }
+        canvas.restoreToCount(saved);
+        // RB
+        saved = canvas.save();
+        canvas.translate(mCardBounds.right - inset, mCardBounds.bottom - inset);
+        canvas.rotate(180f);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        if (drawHorizontalEdges) {
+            canvas.drawRect(0, edgeShadowTop,
+                    mCardBounds.width() - 2 * inset, -mCornerRadius + mShadowSize,
+                    mEdgeShadowPaint);
+        }
+        canvas.restoreToCount(saved);
+        // LB
+        saved = canvas.save();
+        canvas.translate(mCardBounds.left + inset, mCardBounds.bottom - inset);
+        canvas.rotate(270f);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        if (drawVerticalEdges) {
+            canvas.drawRect(0, edgeShadowTop,
+                    mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
+        }
+        canvas.restoreToCount(saved);
+        // RT
+        saved = canvas.save();
+        canvas.translate(mCardBounds.right - inset, mCardBounds.top + inset);
+        canvas.rotate(90f);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        if (drawVerticalEdges) {
+            canvas.drawRect(0, edgeShadowTop,
+                    mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
+        }
+        canvas.restoreToCount(saved);
+    }
+
+    private void buildShadowCorners() {
+        RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);
+        RectF outerBounds = new RectF(innerBounds);
+        outerBounds.inset(-mShadowSize, -mShadowSize);
+
+        if (mCornerShadowPath == null) {
+            mCornerShadowPath = new Path();
+        } else {
+            mCornerShadowPath.reset();
+        }
+        mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
+        mCornerShadowPath.moveTo(-mCornerRadius, 0);
+        mCornerShadowPath.rLineTo(-mShadowSize, 0);
+        // outer arc
+        mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false);
+        // inner arc
+        mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false);
+        mCornerShadowPath.close();
+
+        float startRatio = mCornerRadius / (mCornerRadius + mShadowSize);
+        mCornerShadowPaint.setShader(new RadialGradient(0, 0, mCornerRadius + mShadowSize,
+                new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
+                new float[]{0f, startRatio, 1f}
+                , Shader.TileMode.CLAMP));
+
+        // we offset the content shadowSize/2 pixels up to make it more realistic.
+        // this is why edge shadow shader has some extra space
+        // When drawing bottom edge shadow, we use that extra space.
+        mEdgeShadowPaint.setShader(new LinearGradient(0, -mCornerRadius + mShadowSize, 0,
+                -mCornerRadius - mShadowSize,
+                new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
+                new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP));
+    }
+
+    private void buildComponents(Rect bounds) {
+        // Card is offset SHADOW_MULTIPLIER * maxShadowSize to account for the shadow shift.
+        // We could have different top-bottom offsets to avoid extra gap above but in that case
+        // center aligning Views inside the CardView would be problematic.
+        final float verticalOffset = mMaxShadowSize * SHADOW_MULTIPLIER;
+        mCardBounds.set(bounds.left + mMaxShadowSize, bounds.top + verticalOffset,
+                bounds.right - mMaxShadowSize, bounds.bottom - verticalOffset);
+        buildShadowCorners();
+    }
+
+    float getMinWidth() {
+        final float content = 2 *
+                Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow + mRawMaxShadowSize / 2);
+        return content + (mRawMaxShadowSize + mInsetShadow) * 2;
+    }
+
+    float getMinHeight() {
+        final float content = 2 * Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow
+                        + mRawMaxShadowSize * SHADOW_MULTIPLIER / 2);
+        return content + (mRawMaxShadowSize * SHADOW_MULTIPLIER + mInsetShadow) * 2;
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FixedSizeImageView.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FixedSizeImageView.java
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsEntrancePathInterpolator.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsEntrancePathInterpolator.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/views/RecentsEntrancePathInterpolator.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsEntrancePathInterpolator.java
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsTransitionComposer.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsTransitionComposer.java
new file mode 100644
index 0000000..ce66318
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsTransitionComposer.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.util.Log;
+
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.RecentsDebugFlags;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
+import com.android.systemui.shared.recents.view.RecentsTransition;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A helper class to create the transition app animation specs to/from Recents
+ */
+public class RecentsTransitionComposer {
+
+    private static final String TAG = "RecentsTransitionComposer";
+
+    private Context mContext;
+    private TaskViewTransform mTmpTransform = new TaskViewTransform();
+
+    public RecentsTransitionComposer(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Composes a single animation spec for the given {@link TaskView}
+     */
+    private static AppTransitionAnimationSpecCompat composeAnimationSpec(TaskStackView stackView,
+            TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
+        Bitmap b = null;
+        if (addHeaderBitmap) {
+            b = composeHeaderBitmap(taskView, transform);
+            if (b == null) {
+                return null;
+            }
+        }
+
+        Rect taskRect = new Rect();
+        transform.rect.round(taskRect);
+        // Disable in for low ram devices because each task does in Recents does not have fullscreen
+        // height (stackView height) and when transitioning to fullscreen app, the code below would
+        // force the task thumbnail to full stackView height immediately causing the transition
+        // jarring.
+        if (!LegacyRecentsImpl.getConfiguration().isLowRamDevice && taskView.getTask() !=
+                stackView.getStack().getFrontMostTask()) {
+            taskRect.bottom = taskRect.top + stackView.getMeasuredHeight();
+        }
+        return new AppTransitionAnimationSpecCompat(taskView.getTask().key.id, b, taskRect);
+    }
+
+    /**
+     * Composes the transition spec when docking a task, which includes a full task bitmap.
+     */
+    public List<AppTransitionAnimationSpecCompat> composeDockAnimationSpec(TaskView taskView,
+            Rect bounds) {
+        mTmpTransform.fillIn(taskView);
+        Task task = taskView.getTask();
+        Bitmap buffer = RecentsTransitionComposer.composeTaskBitmap(taskView, mTmpTransform);
+        return Collections.singletonList(new AppTransitionAnimationSpecCompat(task.key.id, buffer,
+                bounds));
+    }
+
+    /**
+     * Composes the animation specs for all the tasks in the target stack.
+     */
+    public List<AppTransitionAnimationSpecCompat> composeAnimationSpecs(final Task task,
+            final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) {
+        // Calculate the offscreen task rect (for tasks that are not backed by views)
+        TaskView taskView = stackView.getChildViewForTask(task);
+        TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
+        Rect offscreenTaskRect = new Rect();
+        stackLayout.getFrontOfStackTransform().rect.round(offscreenTaskRect);
+
+        // If this is a full screen stack, the transition will be towards the single, full screen
+        // task. We only need the transition spec for this task.
+
+        // TODO: Sometimes targetStackId is not initialized after reboot, so we also have to
+        // check for INVALID_STACK_ID (now WINDOWING_MODE_UNDEFINED)
+        if (windowingMode == WINDOWING_MODE_FULLSCREEN
+                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                || activityType == ACTIVITY_TYPE_ASSISTANT
+                || windowingMode == WINDOWING_MODE_UNDEFINED) {
+            List<AppTransitionAnimationSpecCompat> specs = new ArrayList<>();
+            if (taskView == null) {
+                specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
+            } else {
+                mTmpTransform.fillIn(taskView);
+                stackLayout.transformToScreenCoordinates(mTmpTransform, windowRect);
+                AppTransitionAnimationSpecCompat spec = composeAnimationSpec(stackView, taskView,
+                        mTmpTransform, true /* addHeaderBitmap */);
+                if (spec != null) {
+                    specs.add(spec);
+                }
+            }
+            return specs;
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * Composes a single animation spec for the given {@link Task}
+     */
+    private static AppTransitionAnimationSpecCompat composeOffscreenAnimationSpec(Task task,
+            Rect taskRect) {
+        return new AppTransitionAnimationSpecCompat(task.key.id, null, taskRect);
+    }
+
+    public static Bitmap composeTaskBitmap(TaskView taskView, TaskViewTransform transform) {
+        float scale = transform.scale;
+        int fromWidth = (int) (transform.rect.width() * scale);
+        int fromHeight = (int) (transform.rect.height() * scale);
+        if (fromWidth == 0 || fromHeight == 0) {
+            Log.e(TAG, "Could not compose thumbnail for task: " + taskView.getTask() +
+                    " at transform: " + transform);
+
+            return RecentsTransition.drawViewIntoHardwareBitmap(1, 1, null, 1f, 0x00ffffff);
+        } else {
+            if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
+                return RecentsTransition.drawViewIntoHardwareBitmap(fromWidth, fromHeight, null, 1f,
+                        0xFFff0000);
+            } else {
+                return RecentsTransition.drawViewIntoHardwareBitmap(fromWidth, fromHeight, taskView,
+                        scale, 0);
+            }
+        }
+    }
+
+    private static Bitmap composeHeaderBitmap(TaskView taskView,
+            TaskViewTransform transform) {
+        float scale = transform.scale;
+        int headerWidth = (int) (transform.rect.width());
+        int headerHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale);
+        if (headerWidth == 0 || headerHeight == 0) {
+            return null;
+        }
+
+        if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
+            return RecentsTransition.drawViewIntoHardwareBitmap(headerWidth, headerHeight, null, 1f,
+                    0xFFff0000);
+        } else {
+            return RecentsTransition.drawViewIntoHardwareBitmap(headerWidth, headerHeight,
+                    taskView.mHeaderView, scale, 0);
+        }
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
new file mode 100644
index 0000000..dfa38ba
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
@@ -0,0 +1,1078 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
+
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewPropertyAnimator;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.internal.colorextraction.ColorExtractor;
+import com.android.internal.colorextraction.drawable.GradientDrawable;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settingslib.Utils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.RecentsActivityLaunchState;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
+import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
+import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
+import com.android.systemui.recents.events.activity.ShowEmptyViewEvent;
+import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
+import com.android.systemui.recents.events.component.ExpandPipEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
+import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
+import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
+import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
+import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
+import com.android.systemui.shared.recents.view.RecentsTransition;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.stackdivider.WindowManagerProxy;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.phone.ScrimController;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This view is the the top level layout that contains TaskStacks (which are laid out according
+ * to their SpaceNode bounds.
+ */
+public class RecentsView extends FrameLayout {
+
+    private static final String TAG = "RecentsView";
+
+    private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200;
+
+    private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134;
+    private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100;
+
+    private static final int BUSY_RECENTS_TASK_COUNT = 3;
+
+    private Handler mHandler;
+    private TaskStackView mTaskStackView;
+    private TextView mStackActionButton;
+    private TextView mEmptyView;
+    private final float mStackButtonShadowRadius;
+    private final PointF mStackButtonShadowDistance;
+    private final int mStackButtonShadowColor;
+
+    private boolean mAwaitingFirstLayout = true;
+
+    @ViewDebug.ExportedProperty(category="recents")
+    Rect mSystemInsets = new Rect();
+    private int mDividerSize;
+
+    private float mBusynessFactor;
+    private GradientDrawable mBackgroundScrim;
+    private ColorDrawable mMultiWindowBackgroundScrim;
+    private ValueAnimator mBackgroundScrimAnimator;
+    private Point mTmpDisplaySize = new Point();
+
+    private final AnimatorUpdateListener mUpdateBackgroundScrimAlpha = (animation) -> {
+        int alpha = (Integer) animation.getAnimatedValue();
+        mBackgroundScrim.setAlpha(alpha);
+        mMultiWindowBackgroundScrim.setAlpha(alpha);
+    };
+
+    private RecentsTransitionComposer mTransitionHelper;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
+    private RecentsViewTouchHandler mTouchHandler;
+    private final FlingAnimationUtils mFlingAnimationUtils;
+
+    public RecentsView(Context context) {
+        this(context, null);
+    }
+
+    public RecentsView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        setWillNotDraw(false);
+
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        mHandler = new Handler();
+        mTransitionHelper = new RecentsTransitionComposer(getContext());
+        mDividerSize = ssp.getDockedDividerSize(context);
+        mTouchHandler = new RecentsViewTouchHandler(this);
+        mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
+        mBackgroundScrim = new GradientDrawable(context);
+        mMultiWindowBackgroundScrim = new ColorDrawable();
+
+        LayoutInflater inflater = LayoutInflater.from(context);
+        mEmptyView = (TextView) inflater.inflate(R.layout.recents_empty, this, false);
+        addView(mEmptyView);
+
+        if (mStackActionButton != null) {
+            removeView(mStackActionButton);
+        }
+        mStackActionButton = (TextView) inflater.inflate(LegacyRecentsImpl.getConfiguration()
+                        .isLowRamDevice
+                    ? R.layout.recents_low_ram_stack_action_button
+                    : R.layout.recents_stack_action_button,
+                this, false);
+
+        mStackButtonShadowRadius = mStackActionButton.getShadowRadius();
+        mStackButtonShadowDistance = new PointF(mStackActionButton.getShadowDx(),
+                mStackActionButton.getShadowDy());
+        mStackButtonShadowColor = mStackActionButton.getShadowColor();
+        addView(mStackActionButton);
+
+        reevaluateStyles();
+    }
+
+    public void reevaluateStyles() {
+        int textColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
+        boolean usingDarkText = Color.luminance(textColor) < 0.5f;
+
+        mEmptyView.setTextColor(textColor);
+        mEmptyView.setCompoundDrawableTintList(new ColorStateList(new int[][]{
+                {android.R.attr.state_enabled}}, new int[]{textColor}));
+
+        if (mStackActionButton != null) {
+            mStackActionButton.setTextColor(textColor);
+            // Enable/disable shadow if text color is already dark.
+            if (usingDarkText) {
+                mStackActionButton.setShadowLayer(0, 0, 0, 0);
+            } else {
+                mStackActionButton.setShadowLayer(mStackButtonShadowRadius,
+                        mStackButtonShadowDistance.x, mStackButtonShadowDistance.y,
+                        mStackButtonShadowColor);
+            }
+        }
+
+        // Let's also require dark status and nav bars if the text is dark
+        int systemBarsStyle = usingDarkText ? View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
+                View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0;
+
+        setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+                systemBarsStyle);
+    }
+
+    /**
+     * Called from RecentsActivity when it is relaunched.
+     */
+    public void onReload(TaskStack stack, boolean isResumingFromVisible) {
+        final RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+        final RecentsActivityLaunchState launchState = config.getLaunchState();
+        final boolean isTaskStackEmpty = stack.getTaskCount() == 0;
+
+        if (mTaskStackView == null) {
+            isResumingFromVisible = false;
+            mTaskStackView = new TaskStackView(getContext());
+            mTaskStackView.setSystemInsets(mSystemInsets);
+            addView(mTaskStackView);
+        }
+
+        // Reset the state
+        mAwaitingFirstLayout = !isResumingFromVisible;
+
+        // Update the stack
+        mTaskStackView.onReload(isResumingFromVisible);
+        updateStack(stack, true /* setStackViewTasks */);
+        updateBusyness();
+
+        if (isResumingFromVisible) {
+            // If we are already visible, then restore the background scrim
+            animateBackgroundScrim(getOpaqueScrimAlpha(), DEFAULT_UPDATE_SCRIM_DURATION);
+        } else {
+            // If we are already occluded by the app, then set the final background scrim alpha now.
+            // Otherwise, defer until the enter animation completes to animate the scrim alpha with
+            // the tasks for the home animation.
+            if (launchState.launchedViaDockGesture || launchState.launchedFromApp
+                    || isTaskStackEmpty) {
+                mBackgroundScrim.setAlpha((int) (getOpaqueScrimAlpha() * 255));
+            } else {
+                mBackgroundScrim.setAlpha(0);
+            }
+            mMultiWindowBackgroundScrim.setAlpha(mBackgroundScrim.getAlpha());
+        }
+    }
+
+    /**
+     * Called from RecentsActivity when the task stack is updated.
+     */
+    public void updateStack(TaskStack stack, boolean setStackViewTasks) {
+        if (setStackViewTasks) {
+            mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
+        }
+
+        // Update the top level view's visibilities
+        if (stack.getTaskCount() > 0) {
+            hideEmptyView();
+        } else {
+            showEmptyView(R.string.recents_empty_message);
+        }
+    }
+
+    /**
+     * Animates the scrim opacity based on how many tasks are visible.
+     * Called from {@link RecentsActivity} when tasks are dismissed.
+     */
+    public void updateScrimOpacity() {
+        if (updateBusyness()) {
+            animateBackgroundScrim(getOpaqueScrimAlpha(), DEFAULT_UPDATE_SCRIM_DURATION);
+        }
+    }
+
+    /**
+     * Updates the busyness factor.
+     *
+     * @return True if it changed.
+     */
+    private boolean updateBusyness() {
+        final int taskCount = mTaskStackView.getStack().getTaskCount();
+        final float busyness = Math.min(taskCount, BUSY_RECENTS_TASK_COUNT)
+                / (float) BUSY_RECENTS_TASK_COUNT;
+        if (mBusynessFactor == busyness) {
+            return false;
+        } else {
+            mBusynessFactor = busyness;
+            return true;
+        }
+    }
+
+    /**
+     * Returns the current TaskStack.
+     */
+    public TaskStack getStack() {
+        return mTaskStackView.getStack();
+    }
+
+    /**
+     * Returns the window background scrim.
+     */
+    public void updateBackgroundScrim(Window window, boolean isInMultiWindow) {
+        if (isInMultiWindow) {
+            mBackgroundScrim.setCallback(null);
+            window.setBackgroundDrawable(mMultiWindowBackgroundScrim);
+        } else {
+            mMultiWindowBackgroundScrim.setCallback(null);
+            window.setBackgroundDrawable(mBackgroundScrim);
+        }
+    }
+
+    /** Launches the focused task from the first stack if possible */
+    public boolean launchFocusedTask(int logEvent) {
+        if (mTaskStackView != null) {
+            Task task = mTaskStackView.getFocusedTask();
+            if (task != null) {
+                TaskView taskView = mTaskStackView.getChildViewForTask(task);
+                EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, false));
+
+                if (logEvent != 0) {
+                    MetricsLogger.action(getContext(), logEvent,
+                            task.key.getComponent().toString());
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Launches the task that recents was launched from if possible */
+    public boolean launchPreviousTask() {
+        if (LegacyRecentsImpl.getConfiguration().getLaunchState().launchedFromPipApp) {
+            // If the app auto-entered PiP on the way to Recents, then just re-expand it
+            EventBus.getDefault().send(new ExpandPipEvent());
+            return true;
+        }
+
+        if (mTaskStackView != null) {
+            Task task = getStack().getLaunchTarget();
+            if (task != null) {
+                TaskView taskView = mTaskStackView.getChildViewForTask(task);
+                EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, false));
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Hides the task stack and shows the empty view.
+     */
+    public void showEmptyView(int msgResId) {
+        mTaskStackView.setVisibility(View.INVISIBLE);
+        mEmptyView.setText(msgResId);
+        mEmptyView.setVisibility(View.VISIBLE);
+        mEmptyView.bringToFront();
+        mStackActionButton.bringToFront();
+    }
+
+    /**
+     * Shows the task stack and hides the empty view.
+     */
+    public void hideEmptyView() {
+        mEmptyView.setVisibility(View.INVISIBLE);
+        mTaskStackView.setVisibility(View.VISIBLE);
+        mTaskStackView.bringToFront();
+        mStackActionButton.bringToFront();
+    }
+
+    /**
+     * Set the color of the scrim.
+     *
+     * @param scrimColors Colors to use.
+     * @param animated Interpolate colors if true.
+     */
+    public void setScrimColors(ColorExtractor.GradientColors scrimColors, boolean animated) {
+        mBackgroundScrim.setColors(scrimColors, animated);
+        int alpha = mMultiWindowBackgroundScrim.getAlpha();
+        mMultiWindowBackgroundScrim.setColor(scrimColors.getMainColor());
+        mMultiWindowBackgroundScrim.setAlpha(alpha);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+        EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 2);
+        super.onAttachedToWindow();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        EventBus.getDefault().unregister(this);
+        EventBus.getDefault().unregister(mTouchHandler);
+    }
+
+    /**
+     * This is called with the full size of the window since we are handling our own insets.
+     */
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        int height = MeasureSpec.getSize(heightMeasureSpec);
+
+        if (mTaskStackView.getVisibility() != GONE) {
+            mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        // Measure the empty view to the full size of the screen
+        if (mEmptyView.getVisibility() != GONE) {
+            measureChild(mEmptyView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
+        }
+
+        // Measure the stack action button within the constraints of the space above the stack
+        Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect();
+        measureChild(mStackActionButton,
+                MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST),
+                MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST));
+
+        setMeasuredDimension(width, height);
+    }
+
+    /**
+     * This is called with the full size of the window since we are handling our own insets.
+     */
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (mTaskStackView.getVisibility() != GONE) {
+            mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
+        }
+
+        // Layout the empty view
+        if (mEmptyView.getVisibility() != GONE) {
+            int leftRightInsets = mSystemInsets.left + mSystemInsets.right;
+            int topBottomInsets = mSystemInsets.top + mSystemInsets.bottom;
+            int childWidth = mEmptyView.getMeasuredWidth();
+            int childHeight = mEmptyView.getMeasuredHeight();
+            int childLeft = left + mSystemInsets.left +
+                    Math.max(0, (right - left - leftRightInsets - childWidth)) / 2;
+            int childTop = top + mSystemInsets.top +
+                    Math.max(0, (bottom - top - topBottomInsets - childHeight)) / 2;
+            mEmptyView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
+        }
+
+        // Needs to know the screen size since the gradient never scales up or down
+        // even when bounds change.
+        mContext.getDisplay().getRealSize(mTmpDisplaySize);
+        mBackgroundScrim.setScreenSize(mTmpDisplaySize.x, mTmpDisplaySize.y);
+        mBackgroundScrim.setBounds(left, top, right, bottom);
+        mMultiWindowBackgroundScrim.setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
+
+        // Layout the stack action button such that its drawable is start-aligned with the
+        // stack, vertically centered in the available space above the stack
+        Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
+        mStackActionButton.layout(buttonBounds.left, buttonBounds.top, buttonBounds.right,
+                buttonBounds.bottom);
+
+        if (mAwaitingFirstLayout) {
+            mAwaitingFirstLayout = false;
+            // If launched via dragging from the nav bar, then we should translate the whole view
+            // down offscreen
+            RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
+            if (launchState.launchedViaDragGesture) {
+                setTranslationY(getMeasuredHeight());
+            } else {
+                setTranslationY(0f);
+            }
+
+            if (LegacyRecentsImpl.getConfiguration().isLowRamDevice
+                    && mEmptyView.getVisibility() == View.VISIBLE) {
+                animateEmptyView(true /* show */, null /* postAnimationTrigger */);
+            }
+        }
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        mSystemInsets.set(insets.getSystemWindowInsetsAsRect());
+        mTaskStackView.setSystemInsets(mSystemInsets);
+        requestLayout();
+        return insets;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        return mTouchHandler.onInterceptTouchEvent(ev);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        return mTouchHandler.onTouchEvent(ev);
+    }
+
+    @Override
+    public void onDrawForeground(Canvas canvas) {
+        super.onDrawForeground(canvas);
+
+        ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
+        for (int i = visDockStates.size() - 1; i >= 0; i--) {
+            visDockStates.get(i).viewState.draw(canvas);
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
+        for (int i = visDockStates.size() - 1; i >= 0; i--) {
+            Drawable d = visDockStates.get(i).viewState.dockAreaOverlay;
+            if (d == who) {
+                return true;
+            }
+        }
+        return super.verifyDrawable(who);
+    }
+
+    /**** EventBus Events ****/
+
+    public final void onBusEvent(LaunchTaskEvent event) {
+        launchTaskFromRecents(getStack(), event.task, mTaskStackView, event.taskView,
+                event.screenPinningRequested, event.targetWindowingMode, event.targetActivityType);
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            EventBus.getDefault().send(new HideStackActionButtonEvent(false /* translate */));
+        }
+    }
+
+    public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
+        int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
+        // Hide the stack action button
+        EventBus.getDefault().send(new HideStackActionButtonEvent());
+        animateBackgroundScrim(0f, taskViewExitToHomeDuration);
+
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            animateEmptyView(false /* show */, event.getAnimationTrigger());
+        }
+    }
+
+    public final void onBusEvent(DragStartEvent event) {
+        updateVisibleDockRegions(LegacyRecentsImpl.getConfiguration().getDockStatesForCurrentOrientation(),
+                true /* isDefaultDockState */, DockState.NONE.viewState.dockAreaAlpha,
+                DockState.NONE.viewState.hintTextAlpha,
+                true /* animateAlpha */, false /* animateBounds */);
+
+        // Temporarily hide the stack action button without changing visibility
+        if (mStackActionButton != null) {
+            mStackActionButton.animate()
+                    .alpha(0f)
+                    .setDuration(HIDE_STACK_ACTION_BUTTON_DURATION)
+                    .setInterpolator(Interpolators.ALPHA_OUT)
+                    .start();
+        }
+    }
+
+    public final void onBusEvent(DragDropTargetChangedEvent event) {
+        if (event.dropTarget == null || !(event.dropTarget instanceof DockState)) {
+            updateVisibleDockRegions(
+                    LegacyRecentsImpl.getConfiguration().getDockStatesForCurrentOrientation(),
+                    true /* isDefaultDockState */, DockState.NONE.viewState.dockAreaAlpha,
+                    DockState.NONE.viewState.hintTextAlpha,
+                    true /* animateAlpha */, true /* animateBounds */);
+        } else {
+            final DockState dockState = (DockState) event.dropTarget;
+            updateVisibleDockRegions(new DockState[] {dockState},
+                    false /* isDefaultDockState */, -1, -1, true /* animateAlpha */,
+                    true /* animateBounds */);
+        }
+        if (mStackActionButton != null) {
+            event.addPostAnimationCallback(new Runnable() {
+                @Override
+                public void run() {
+                    // Move the clear all button to its new position
+                    Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
+                    mStackActionButton.setLeftTopRightBottom(buttonBounds.left, buttonBounds.top,
+                            buttonBounds.right, buttonBounds.bottom);
+                }
+            });
+        }
+    }
+
+    public final void onBusEvent(final DragEndEvent event) {
+        // Handle the case where we drop onto a dock region
+        if (event.dropTarget instanceof DockState) {
+            final DockState dockState = (DockState) event.dropTarget;
+
+            // Hide the dock region
+            updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, -1,
+                    false /* animateAlpha */, false /* animateBounds */);
+
+            // We translated the view but we need to animate it back from the current layout-space
+            // rect to its final layout-space rect
+            Utilities.setViewFrameFromTranslation(event.taskView);
+
+            final ActivityOptions options = ActivityOptionsCompat.makeSplitScreenOptions(
+                    dockState.createMode == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
+            if (ActivityManagerWrapper.getInstance().startActivityFromRecents(event.task.key.id,
+                    options)) {
+                final Runnable animStartedListener = () -> {
+                    EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
+                    // Remove the task and don't bother relaying out, as all the tasks
+                    // will be relaid out when the stack changes on the multiwindow
+                    // change event
+                    getStack().removeTask(event.task, null, true /* fromDockGesture */);
+                };
+                final Rect taskRect = getTaskRect(event.taskView);
+                AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(
+                        getHandler()) {
+                    @Override
+                    public List<AppTransitionAnimationSpecCompat> composeSpecs() {
+                        return mTransitionHelper.composeDockAnimationSpec(event.taskView, taskRect);
+                    }
+                };
+                WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
+                        future, animStartedListener, getHandler(), true /* scaleUp */);
+                MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
+                        event.task.getTopComponent().flattenToShortString());
+            } else {
+                EventBus.getDefault().send(new DragEndCancelledEvent(getStack(), event.task,
+                        event.taskView));
+            }
+        } else {
+            // Animate the overlay alpha back to 0
+            updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1,
+                    true /* animateAlpha */, false /* animateBounds */);
+        }
+
+        // Show the stack action button again without changing visibility
+        if (mStackActionButton != null) {
+            mStackActionButton.animate()
+                    .alpha(1f)
+                    .setDuration(SHOW_STACK_ACTION_BUTTON_DURATION)
+                    .setInterpolator(Interpolators.ALPHA_IN)
+                    .start();
+        }
+    }
+
+    public final void onBusEvent(final DragEndCancelledEvent event) {
+        // Animate the overlay alpha back to 0
+        updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1,
+                true /* animateAlpha */, false /* animateBounds */);
+    }
+
+    private Rect getTaskRect(TaskView taskView) {
+        int[] location = taskView.getLocationOnScreen();
+        int viewX = location[0];
+        int viewY = location[1];
+        return new Rect(viewX, viewY,
+                (int) (viewX + taskView.getWidth() * taskView.getScaleX()),
+                (int) (viewY + taskView.getHeight() * taskView.getScaleY()));
+    }
+
+    public final void onBusEvent(DraggingInRecentsEvent event) {
+        if (mTaskStackView.getTaskViews().size() > 0) {
+            setTranslationY(event.distanceFromTop - mTaskStackView.getTaskViews().get(0).getY());
+        }
+    }
+
+    public final void onBusEvent(DraggingInRecentsEndedEvent event) {
+        ViewPropertyAnimator animator = animate();
+        if (event.velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+            animator.translationY(getHeight());
+            animator.withEndAction(new Runnable() {
+                @Override
+                public void run() {
+                    WindowManagerProxy.getInstance().maximizeDockedStack();
+                }
+            });
+            mFlingAnimationUtils.apply(animator, getTranslationY(), getHeight(), event.velocity);
+        } else {
+            animator.translationY(0f);
+            animator.setListener(null);
+            mFlingAnimationUtils.apply(animator, getTranslationY(), 0, event.velocity);
+        }
+        animator.start();
+    }
+
+    public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
+        RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
+        if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp
+                && getStack().getTaskCount() > 0) {
+            animateBackgroundScrim(getOpaqueScrimAlpha(),
+                    TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
+        }
+    }
+
+    public final void onBusEvent(AllTaskViewsDismissedEvent event) {
+        EventBus.getDefault().send(new HideStackActionButtonEvent());
+    }
+
+    public final void onBusEvent(DismissAllTaskViewsEvent event) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        if (!ssp.hasDockedTask()) {
+            // Animate the background away only if we are dismissing Recents to home
+            animateBackgroundScrim(0f, DEFAULT_UPDATE_SCRIM_DURATION);
+        }
+    }
+
+    public final void onBusEvent(ShowStackActionButtonEvent event) {
+        showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, event.translate);
+    }
+
+    public final void onBusEvent(HideStackActionButtonEvent event) {
+        hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
+    }
+
+    public final void onBusEvent(MultiWindowStateChangedEvent event) {
+        updateStack(event.stack, false /* setStackViewTasks */);
+    }
+
+    public final void onBusEvent(ShowEmptyViewEvent event) {
+        showEmptyView(R.string.recents_empty_message);
+    }
+
+    /**
+     * Shows the stack action button.
+     */
+    private void showStackActionButton(final int duration, final boolean translate) {
+        final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
+        if (mStackActionButton.getVisibility() == View.INVISIBLE) {
+            mStackActionButton.setVisibility(View.VISIBLE);
+            mStackActionButton.setAlpha(0f);
+            if (translate) {
+                mStackActionButton.setTranslationY(mStackActionButton.getMeasuredHeight() *
+                        (LegacyRecentsImpl.getConfiguration().isLowRamDevice ? 1 : -0.25f));
+            } else {
+                mStackActionButton.setTranslationY(0f);
+            }
+            postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+                @Override
+                public void run() {
+                    if (translate) {
+                        mStackActionButton.animate()
+                            .translationY(0f);
+                    }
+                    mStackActionButton.animate()
+                            .alpha(1f)
+                            .setDuration(duration)
+                            .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                            .start();
+                }
+            });
+        }
+        postAnimationTrigger.flushLastDecrementRunnables();
+    }
+
+    /**
+     * Hides the stack action button.
+     */
+    private void hideStackActionButton(int duration, boolean translate) {
+        final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
+        hideStackActionButton(duration, translate, postAnimationTrigger);
+        postAnimationTrigger.flushLastDecrementRunnables();
+    }
+
+    /**
+     * Hides the stack action button.
+     */
+    private void hideStackActionButton(int duration, boolean translate,
+                                       final ReferenceCountedTrigger postAnimationTrigger) {
+        if (mStackActionButton.getVisibility() == View.VISIBLE) {
+            if (translate) {
+                mStackActionButton.animate().translationY(mStackActionButton.getMeasuredHeight()
+                        * (LegacyRecentsImpl.getConfiguration().isLowRamDevice ? 1 : -0.25f));
+            }
+            mStackActionButton.animate()
+                    .alpha(0f)
+                    .setDuration(duration)
+                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mStackActionButton.setVisibility(View.INVISIBLE);
+                            postAnimationTrigger.decrement();
+                        }
+                    })
+                    .start();
+            postAnimationTrigger.increment();
+        }
+    }
+
+    /**
+     * Animates a translation in the Y direction and fades in/out for empty view to show or hide it.
+     * @param show whether to translate up and fade in the empty view to the center of the screen
+     * @param postAnimationTrigger to keep track of the animation
+     */
+    private void animateEmptyView(boolean show, ReferenceCountedTrigger postAnimationTrigger) {
+        float start = mTaskStackView.getStackAlgorithm().getTaskRect().height() / 4;
+        mEmptyView.setTranslationY(show ? start : 0);
+        mEmptyView.setAlpha(show ? 0f : 1f);
+        ViewPropertyAnimator animator = mEmptyView.animate()
+                .setDuration(150)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .translationY(show ? 0 : start)
+                .alpha(show ? 1f : 0f);
+
+        if (postAnimationTrigger != null) {
+            animator.setListener(postAnimationTrigger.decrementOnAnimationEnd());
+            postAnimationTrigger.increment();
+        }
+        animator.start();
+    }
+
+    /**
+     * Updates the dock region to match the specified dock state.
+     */
+    private void updateVisibleDockRegions(DockState[] newDockStates,
+            boolean isDefaultDockState, int overrideAreaAlpha, int overrideHintAlpha,
+            boolean animateAlpha, boolean animateBounds) {
+        ArraySet<DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates,
+                new ArraySet<DockState>());
+        ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
+        for (int i = visDockStates.size() - 1; i >= 0; i--) {
+            DockState dockState = visDockStates.get(i);
+            DockState.ViewState viewState = dockState.viewState;
+            if (newDockStates == null || !newDockStatesSet.contains(dockState)) {
+                // This is no longer visible, so hide it
+                viewState.startAnimation(null, 0, 0, TaskStackView.SLOW_SYNC_STACK_DURATION,
+                        Interpolators.FAST_OUT_SLOW_IN, animateAlpha, animateBounds);
+            } else {
+                // This state is now visible, update the bounds and show it
+                int areaAlpha = overrideAreaAlpha != -1
+                        ? overrideAreaAlpha
+                        : viewState.dockAreaAlpha;
+                int hintAlpha = overrideHintAlpha != -1
+                        ? overrideHintAlpha
+                        : viewState.hintTextAlpha;
+                Rect bounds = isDefaultDockState
+                        ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
+                                mSystemInsets)
+                        : dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
+                                mDividerSize, mSystemInsets, getResources());
+                if (viewState.dockAreaOverlay.getCallback() != this) {
+                    viewState.dockAreaOverlay.setCallback(this);
+                    viewState.dockAreaOverlay.setBounds(bounds);
+                }
+                viewState.startAnimation(bounds, areaAlpha, hintAlpha,
+                        TaskStackView.SLOW_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN,
+                        animateAlpha, animateBounds);
+            }
+        }
+    }
+
+    /**
+     * Scrim alpha based on how busy recents is:
+     * Scrim will be {@link ScrimController#GRADIENT_SCRIM_ALPHA} when the stack is empty,
+     * and {@link ScrimController#GRADIENT_SCRIM_ALPHA_BUSY} when it's full.
+     *
+     * @return Alpha from 0 to 1.
+     */
+    private float getOpaqueScrimAlpha() {
+        return MathUtils.map(0, 1, ScrimController.GRADIENT_SCRIM_ALPHA,
+                ScrimController.GRADIENT_SCRIM_ALPHA_BUSY, mBusynessFactor);
+    }
+
+    /**
+     * Animates the background scrim to the given {@param alpha}.
+     */
+    private void animateBackgroundScrim(float alpha, int duration) {
+        Utilities.cancelAnimationWithoutCallbacks(mBackgroundScrimAnimator);
+        // Calculate the absolute alpha to animate from
+        final int fromAlpha = mBackgroundScrim.getAlpha();
+        final int toAlpha = (int) (alpha * 255);
+        mBackgroundScrimAnimator = ValueAnimator.ofInt(fromAlpha, toAlpha);
+        mBackgroundScrimAnimator.setDuration(duration);
+        mBackgroundScrimAnimator.setInterpolator(toAlpha > fromAlpha
+                ? Interpolators.ALPHA_IN
+                : Interpolators.ALPHA_OUT);
+        mBackgroundScrimAnimator.addUpdateListener(mUpdateBackgroundScrimAlpha);
+        mBackgroundScrimAnimator.start();
+    }
+
+    /**
+     * @return the bounds of the stack action button.
+     */
+    Rect getStackActionButtonBoundsFromStackLayout() {
+        Rect actionButtonRect = new Rect(
+                mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect());
+        int left, top;
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            Rect windowRect = LegacyRecentsImpl.getSystemServices().getWindowRect();
+            int spaceLeft = windowRect.width() - mSystemInsets.left - mSystemInsets.right;
+            left = (spaceLeft - mStackActionButton.getMeasuredWidth()) / 2 + mSystemInsets.left;
+            top = windowRect.height() - (mStackActionButton.getMeasuredHeight()
+                    + mSystemInsets.bottom + mStackActionButton.getPaddingBottom() / 2);
+        } else {
+            left = isLayoutRtl()
+                ? actionButtonRect.left - mStackActionButton.getPaddingLeft()
+                : actionButtonRect.right + mStackActionButton.getPaddingRight()
+                        - mStackActionButton.getMeasuredWidth();
+            top = actionButtonRect.top +
+                (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2;
+        }
+        actionButtonRect.set(left, top, left + mStackActionButton.getMeasuredWidth(),
+                top + mStackActionButton.getMeasuredHeight());
+        return actionButtonRect;
+    }
+
+    View getStackActionButton() {
+        return mStackActionButton;
+    }
+
+    /**
+     * Launches the specified {@link Task}.
+     */
+    public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
+            final TaskStackView stackView, final TaskView taskView,
+            final boolean screenPinningRequested, final int windowingMode, final int activityType) {
+
+        final Runnable animStartedListener;
+        final AppTransitionAnimationSpecsFuture transitionFuture;
+        if (taskView != null) {
+
+            // Fetch window rect here already in order not to be blocked on lock contention in WM
+            // when the future calls it.
+            final Rect windowRect = LegacyRecentsImpl.getSystemServices().getWindowRect();
+            transitionFuture = new AppTransitionAnimationSpecsFuture(stackView.getHandler()) {
+                @Override
+                public List<AppTransitionAnimationSpecCompat> composeSpecs() {
+                    return mTransitionHelper.composeAnimationSpecs(task, stackView, windowingMode,
+                            activityType, windowRect);
+                }
+            };
+            animStartedListener = new Runnable() {
+                private boolean mHandled;
+
+                @Override
+                public void run() {
+                    if (mHandled) {
+                        return;
+                    }
+                    mHandled = true;
+
+                    // If we are launching into another task, cancel the previous task's
+                    // window transition
+                    EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+                    EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+                    stackView.cancelAllTaskViewAnimations();
+
+                    if (screenPinningRequested) {
+                        // Request screen pinning after the animation runs
+                        mHandler.postDelayed(() -> {
+                            EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext,
+                                    task.key.id));
+                        }, 350);
+                    }
+
+                    if (!LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+                        // Reset the state where we are waiting for the transition to start
+                        EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
+                    }
+                }
+            };
+        } else {
+            // This is only the case if the task is not on screen (scrolled offscreen for example)
+            transitionFuture = null;
+            animStartedListener = new Runnable() {
+                private boolean mHandled;
+
+                @Override
+                public void run() {
+                    if (mHandled) {
+                        return;
+                    }
+                    mHandled = true;
+
+                    // If we are launching into another task, cancel the previous task's
+                    // window transition
+                    EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+                    EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+                    stackView.cancelAllTaskViewAnimations();
+
+                    if (!LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+                        // Reset the state where we are waiting for the transition to start
+                        EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
+                    }
+                }
+            };
+        }
+
+        EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(true));
+        final ActivityOptions opts = RecentsTransition.createAspectScaleAnimation(mContext,
+                mHandler, true /* scaleUp */, transitionFuture != null ? transitionFuture : null,
+                animStartedListener);
+        if (taskView == null) {
+            // If there is no task view, then we do not need to worry about animating out occluding
+            // task views, and we can launch immediately
+            startTaskActivity(stack, task, taskView, opts, transitionFuture,
+                    windowingMode, activityType);
+        } else {
+            LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView,
+                    screenPinningRequested);
+            EventBus.getDefault().send(launchStartedEvent);
+            startTaskActivity(stack, task, taskView, opts, transitionFuture, windowingMode,
+                    activityType);
+        }
+        ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
+    }
+
+    /**
+     * Starts the activity for the launch task.
+     *
+     * @param taskView this is the {@link TaskView} that we are launching from. This can be null if
+     *                 we are toggling recents and the launch-to task is now offscreen.
+     */
+    private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView,
+            ActivityOptions opts, AppTransitionAnimationSpecsFuture transitionFuture,
+            int windowingMode, int activityType) {
+        ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(task.key, opts,
+                windowingMode, activityType, succeeded -> {
+            if (succeeded) {
+                // Keep track of the index of the task launch
+                int taskIndexFromFront = 0;
+                int taskIndex = stack.indexOfTask(task);
+                if (taskIndex > -1) {
+                    taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
+                }
+                EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
+            } else {
+                Log.e(TAG, mContext.getString(R.string.recents_launch_error_message, task.title));
+
+                // Dismiss the task if we fail to launch it
+                if (taskView != null) {
+                    taskView.dismissTask();
+                }
+
+                // Keep track of failed launches
+                EventBus.getDefault().send(new LaunchTaskFailedEvent());
+            }
+        }, getHandler());
+        if (transitionFuture != null) {
+            mHandler.post(transitionFuture::composeSpecsSynchronous);
+        }
+    }
+
+    @Override
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        super.requestDisallowInterceptTouchEvent(disallowIntercept);
+        mTouchHandler.cancelStackActionButtonClick();
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "  ";
+        String id = Integer.toHexString(System.identityHashCode(this));
+
+        writer.print(prefix); writer.print(TAG);
+        writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N");
+        writer.print(" insets="); writer.print(Utilities.dumpRect(mSystemInsets));
+        writer.print(" [0x"); writer.print(id); writer.print("]");
+        writer.println();
+
+        if (getStack() != null) {
+            getStack().dump(innerPrefix, writer);
+        }
+        if (mTaskStackView != null) {
+            mTaskStackView.dump(innerPrefix, writer);
+        }
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
new file mode 100644
index 0000000..1a827d5
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.app.ActivityTaskManager;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.PointerIcon;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewDebug;
+
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
+import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
+import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
+import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+
+/**
+ * Handles touch events for a RecentsView.
+ */
+public class RecentsViewTouchHandler {
+
+    private RecentsView mRv;
+
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task")
+    private Task mDragTask;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task_view_")
+    private TaskView mTaskView;
+
+    @ViewDebug.ExportedProperty(category="recents")
+    private Point mTaskViewOffset = new Point();
+    @ViewDebug.ExportedProperty(category="recents")
+    private Point mDownPos = new Point();
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mDragRequested;
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mIsDragging;
+    private float mDragSlop;
+    private int mDeviceId = -1;
+
+    private DropTarget mLastDropTarget;
+    private DividerSnapAlgorithm mDividerSnapAlgorithm;
+    private ArrayList<DropTarget> mDropTargets = new ArrayList<>();
+    private ArrayList<DockState> mVisibleDockStates = new ArrayList<>();
+
+    public RecentsViewTouchHandler(RecentsView rv) {
+        mRv = rv;
+        mDragSlop = ViewConfiguration.get(rv.getContext()).getScaledTouchSlop();
+        updateSnapAlgorithm();
+    }
+
+    private void updateSnapAlgorithm() {
+        Rect insets = new Rect();
+        SystemServicesProxy.getInstance(mRv.getContext()).getStableInsets(insets);
+        mDividerSnapAlgorithm = DividerSnapAlgorithm.create(mRv.getContext(), insets);
+    }
+
+    /**
+     * Registers a new drop target for the current drag only.
+     */
+    public void registerDropTargetForCurrentDrag(DropTarget target) {
+        mDropTargets.add(target);
+    }
+
+    /**
+     * Returns the set of visible dock states for this current drag.
+     */
+    public ArrayList<DockState> getVisibleDockStates() {
+        return mVisibleDockStates;
+    }
+
+    /** Touch preprocessing for handling below */
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        return handleTouchEvent(ev) || mDragRequested;
+    }
+
+    /** Handles touch events once we have intercepted them */
+    public boolean onTouchEvent(MotionEvent ev) {
+        handleTouchEvent(ev);
+        if (ev.getAction() == MotionEvent.ACTION_UP && mRv.getStack().getTaskCount() == 0) {
+            EventBus.getDefault().send(new HideRecentsEvent(false, true));
+        }
+        return true;
+    }
+
+    /**** Events ****/
+
+    public final void onBusEvent(DragStartEvent event) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        mRv.getParent().requestDisallowInterceptTouchEvent(true);
+        mDragRequested = true;
+        // We defer starting the actual drag handling until the user moves past the drag slop
+        mIsDragging = false;
+        mDragTask = event.task;
+        mTaskView = event.taskView;
+        mDropTargets.clear();
+
+        int[] recentsViewLocation = new int[2];
+        mRv.getLocationInWindow(recentsViewLocation);
+        mTaskViewOffset.set(mTaskView.getLeft() - recentsViewLocation[0] + event.tlOffset.x,
+                mTaskView.getTop() - recentsViewLocation[1] + event.tlOffset.y);
+
+        // Change space coordinates relative to the view to RecentsView when user initiates a touch
+        if (event.isUserTouchInitiated) {
+            float x = mDownPos.x - mTaskViewOffset.x;
+            float y = mDownPos.y - mTaskViewOffset.y;
+            mTaskView.setTranslationX(x);
+            mTaskView.setTranslationY(y);
+        }
+
+        mVisibleDockStates.clear();
+        if (ActivityTaskManager.supportsMultiWindow(mRv.getContext()) && !ssp.hasDockedTask()
+                && mDividerSnapAlgorithm.isSplitScreenFeasible()) {
+            LegacyRecentsImpl.logDockAttempt(mRv.getContext(), event.task.getTopComponent(),
+                    event.task.resizeMode);
+            if (!event.task.isDockable) {
+                EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent());
+            } else {
+                // Add the dock state drop targets (these take priority)
+                DockState[] dockStates = LegacyRecentsImpl.getConfiguration()
+                        .getDockStatesForCurrentOrientation();
+                for (DockState dockState : dockStates) {
+                    registerDropTargetForCurrentDrag(dockState);
+                    dockState.update(mRv.getContext());
+                    mVisibleDockStates.add(dockState);
+                }
+            }
+        }
+
+        // Request other drop targets to register themselves
+        EventBus.getDefault().send(new DragStartInitializeDropTargetsEvent(event.task,
+                event.taskView, this));
+        if (mDeviceId != -1) {
+            InputDevice device = InputDevice.getDevice(mDeviceId);
+            if (device != null) {
+                device.setPointerType(PointerIcon.TYPE_GRABBING);
+            }
+        }
+    }
+
+    public final void onBusEvent(DragEndEvent event) {
+        if (!mDragTask.isDockable) {
+            EventBus.getDefault().send(new HideIncompatibleAppOverlayEvent());
+        }
+        mDragRequested = false;
+        mDragTask = null;
+        mTaskView = null;
+        mLastDropTarget = null;
+    }
+
+    public final void onBusEvent(ConfigurationChangedEvent event) {
+        if (event.fromDisplayDensityChange || event.fromDeviceOrientationChange) {
+            updateSnapAlgorithm();
+        }
+    }
+
+    void cancelStackActionButtonClick() {
+        mRv.getStackActionButton().setPressed(false);
+    }
+
+    private boolean isWithinStackActionButton(float x, float y) {
+        Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
+        return mRv.getStackActionButton().getVisibility() == View.VISIBLE &&
+                mRv.getStackActionButton().pointInView(x - rect.left, y - rect.top, 0 /* slop */);
+    }
+
+    private void changeStackActionButtonDrawableHotspot(float x, float y) {
+        Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
+        mRv.getStackActionButton().drawableHotspotChanged(x - rect.left, y - rect.top);
+    }
+
+    /**
+     * Handles dragging touch events
+     */
+    private boolean handleTouchEvent(MotionEvent ev) {
+        int action = ev.getActionMasked();
+        boolean consumed = false;
+        float evX = ev.getX();
+        float evY = ev.getY();
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mDownPos.set((int) evX, (int) evY);
+                mDeviceId = ev.getDeviceId();
+
+                if (isWithinStackActionButton(evX, evY)) {
+                    changeStackActionButtonDrawableHotspot(evX, evY);
+                    mRv.getStackActionButton().setPressed(true);
+                }
+                break;
+            case MotionEvent.ACTION_MOVE: {
+                float x = evX - mTaskViewOffset.x;
+                float y = evY - mTaskViewOffset.y;
+
+                if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
+                    changeStackActionButtonDrawableHotspot(evX, evY);
+                }
+
+                if (mDragRequested) {
+                    if (!mIsDragging) {
+                        mIsDragging = Math.hypot(evX - mDownPos.x, evY - mDownPos.y) > mDragSlop;
+                    }
+                    if (mIsDragging) {
+                        int width = mRv.getMeasuredWidth();
+                        int height = mRv.getMeasuredHeight();
+
+                        DropTarget currentDropTarget = null;
+
+                        // Give priority to the current drop target to retain the touch handling
+                        if (mLastDropTarget != null) {
+                            if (mLastDropTarget.acceptsDrop((int) evX, (int) evY, width, height,
+                                    mRv.mSystemInsets, true /* isCurrentTarget */)) {
+                                currentDropTarget = mLastDropTarget;
+                            }
+                        }
+
+                        // Otherwise, find the next target to handle this event
+                        if (currentDropTarget == null) {
+                            for (DropTarget target : mDropTargets) {
+                                if (target.acceptsDrop((int) evX, (int) evY, width, height,
+                                        mRv.mSystemInsets, false /* isCurrentTarget */)) {
+                                    currentDropTarget = target;
+                                    break;
+                                }
+                            }
+                        }
+                        if (mLastDropTarget != currentDropTarget) {
+                            mLastDropTarget = currentDropTarget;
+                            EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
+                                    currentDropTarget));
+                        }
+                    }
+                    mTaskView.setTranslationX(x);
+                    mTaskView.setTranslationY(y);
+                }
+                break;
+            }
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL: {
+                if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
+                    EventBus.getDefault().send(new DismissAllTaskViewsEvent());
+                    consumed = true;
+                }
+                cancelStackActionButtonClick();
+                if (mDragRequested) {
+                    boolean cancelled = action == MotionEvent.ACTION_CANCEL;
+                    if (cancelled) {
+                        EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask, null));
+                    }
+                    EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView,
+                            !cancelled ? mLastDropTarget : null));
+                    break;
+                }
+                mDeviceId = -1;
+            }
+        }
+        return consumed;
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/SystemBarScrimViews.java
new file mode 100644
index 0000000..22c12b4
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
+import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
+import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
+import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.utilities.AnimationProps;
+
+/** Manages the scrims for the various system bars. */
+public class SystemBarScrimViews {
+
+    private static final int DEFAULT_ANIMATION_DURATION = 150;
+
+    private Context mContext;
+
+    private View mNavBarScrimView;
+
+    private boolean mHasNavBarScrim;
+    private boolean mShouldAnimateNavBarScrim;
+    private boolean mHasTransposedNavBar;
+    private boolean mHasDockedTasks;
+    private int mNavBarScrimEnterDuration;
+
+    public SystemBarScrimViews(RecentsActivity activity) {
+        mContext = activity;
+        mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
+        mNavBarScrimView.forceHasOverlappingRendering(false);
+        mNavBarScrimEnterDuration = activity.getResources().getInteger(
+                R.integer.recents_nav_bar_scrim_enter_duration);
+        mHasNavBarScrim = LegacyRecentsImpl.getSystemServices().hasTransposedNavigationBar();
+        mHasDockedTasks = LegacyRecentsImpl.getSystemServices().hasDockedTask();
+    }
+
+    /**
+     * Updates the nav bar scrim.
+     */
+    public void updateNavBarScrim(boolean animateNavBarScrim, boolean hasStackTasks,
+            AnimationProps animation) {
+        prepareEnterRecentsAnimation(isNavBarScrimRequired(hasStackTasks), animateNavBarScrim);
+        if (animateNavBarScrim && animation != null) {
+            animateNavBarScrimVisibility(true, animation);
+        }
+    }
+
+    /**
+     * Prepares the scrim views for animating when entering Recents. This will be called before
+     * the first draw, unless we are updating the scrim on configuration change.
+     */
+    private void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) {
+        mHasNavBarScrim = hasNavBarScrim;
+        mShouldAnimateNavBarScrim = animateNavBarScrim;
+
+        mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
+                View.VISIBLE : View.INVISIBLE);
+    }
+
+    /**
+     * Animates the nav bar scrim visibility.
+     */
+    private void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) {
+        int toY = 0;
+        if (visible) {
+            mNavBarScrimView.setVisibility(View.VISIBLE);
+            mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
+        } else {
+            toY = mNavBarScrimView.getMeasuredHeight();
+        }
+        if (animation != AnimationProps.IMMEDIATE) {
+            mNavBarScrimView.animate()
+                    .translationY(toY)
+                    .setDuration(animation.getDuration(AnimationProps.BOUNDS))
+                    .setInterpolator(animation.getInterpolator(AnimationProps.BOUNDS))
+                    .start();
+        } else {
+            mNavBarScrimView.setTranslationY(toY);
+        }
+    }
+
+    /**
+     * @return Whether to show the nav bar scrim.
+     */
+    private boolean isNavBarScrimRequired(boolean hasStackTasks) {
+        return hasStackTasks && !mHasTransposedNavBar && !mHasDockedTasks;
+    }
+
+    /**** EventBus events ****/
+
+    /**
+     * Starts animating the scrim views when entering Recents.
+     */
+    public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
+        if (mHasNavBarScrim) {
+            AnimationProps animation = mShouldAnimateNavBarScrim
+                    ? new AnimationProps()
+                            .setDuration(AnimationProps.BOUNDS, mNavBarScrimEnterDuration)
+                            .setInterpolator(AnimationProps.BOUNDS, Interpolators.DECELERATE_QUINT)
+                    : AnimationProps.IMMEDIATE;
+            animateNavBarScrimVisibility(true, animation);
+        }
+    }
+
+    /**
+     * Starts animating the scrim views when leaving Recents (either via launching a task, or
+     * going home).
+     */
+    public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
+        if (mHasNavBarScrim) {
+            AnimationProps animation = createBoundsAnimation(
+                    TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
+            animateNavBarScrimVisibility(false, animation);
+        }
+    }
+
+    public final void onBusEvent(DismissAllTaskViewsEvent event) {
+        if (mHasNavBarScrim) {
+            AnimationProps animation = createBoundsAnimation(
+                    TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
+            animateNavBarScrimVisibility(false, animation);
+        }
+    }
+
+    public final void onBusEvent(ConfigurationChangedEvent event) {
+        if (event.fromDeviceOrientationChange) {
+            mHasNavBarScrim = LegacyRecentsImpl.getSystemServices().hasTransposedNavigationBar();
+        }
+        animateScrimToCurrentNavBarState(event.hasStackTasks);
+    }
+
+    public final void onBusEvent(MultiWindowStateChangedEvent event) {
+        mHasDockedTasks = event.inMultiWindow;
+        animateScrimToCurrentNavBarState(event.stack.getTaskCount() > 0);
+    }
+
+    public final void onBusEvent(final DragEndEvent event) {
+        // Hide the nav bar scrims once we drop to a dock region
+        if (event.dropTarget instanceof DockState) {
+            animateScrimToCurrentNavBarState(false /* hasStackTasks */);
+        }
+    }
+
+    public final void onBusEvent(final DragEndCancelledEvent event) {
+        // Restore the scrims to the normal state
+        animateScrimToCurrentNavBarState(event.stack.getTaskCount() > 0);
+    }
+
+    /**
+     * Animates the scrim to match the state of the current nav bar.
+     */
+    private void animateScrimToCurrentNavBarState(boolean hasStackTasks) {
+        boolean hasNavBarScrim = isNavBarScrimRequired(hasStackTasks);
+        if (mHasNavBarScrim != hasNavBarScrim) {
+            AnimationProps animation = hasNavBarScrim
+                    ? createBoundsAnimation(DEFAULT_ANIMATION_DURATION)
+                    : AnimationProps.IMMEDIATE;
+            animateNavBarScrimVisibility(hasNavBarScrim, animation);
+        }
+        mHasNavBarScrim = hasNavBarScrim;
+    }
+
+    /**
+     * @return a default animation to aniamte the bounds of the scrim.
+     */
+    private AnimationProps createBoundsAnimation(int duration) {
+        return new AnimationProps()
+                .setDuration(AnimationProps.BOUNDS, duration)
+                .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
new file mode 100644
index 0000000..5574934
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -0,0 +1,705 @@
+/*
+ * Copyright (C) 2015 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.systemui.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.util.Log;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.RecentsActivityLaunchState;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsDebugFlags;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
+import com.android.systemui.recents.utilities.AnimationProps;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A helper class to create task view animations for {@link TaskView}s in a {@link TaskStackView},
+ * but not the contents of the {@link TaskView}s.
+ */
+public class TaskStackAnimationHelper {
+
+    /**
+     * Callbacks from the helper to coordinate view-content animations with view animations.
+     */
+    public interface Callbacks {
+        /**
+         * Callback to prepare for the start animation for the launch target {@link TaskView}.
+         */
+        void onPrepareLaunchTargetForEnterAnimation();
+
+        /**
+         * Callback to start the animation for the launch target {@link TaskView}.
+         */
+        void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
+                boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger);
+
+        /**
+         * Callback to start the animation for the launch target {@link TaskView} when it is
+         * launched from Recents.
+         */
+        void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
+                ReferenceCountedTrigger postAnimationTrigger);
+
+        /**
+         * Callback to start the animation for the front {@link TaskView} if there is no launch
+         * target.
+         */
+        void onStartFrontTaskEnterAnimation(boolean screenPinningEnabled);
+    }
+
+    private static final int DOUBLE_FRAME_OFFSET_MS = 33;
+    private static final int FRAME_OFFSET_MS = 16;
+
+    private static final int ENTER_EXIT_NUM_ANIMATING_TASKS = 5;
+
+    private static final int ENTER_FROM_HOME_ALPHA_DURATION = 100;
+    public static final int ENTER_FROM_HOME_TRANSLATION_DURATION = 300;
+    private static final Interpolator ENTER_FROM_HOME_ALPHA_INTERPOLATOR = Interpolators.LINEAR;
+
+    public static final int EXIT_TO_HOME_TRANSLATION_DURATION = 200;
+    private static final Interpolator EXIT_TO_HOME_TRANSLATION_INTERPOLATOR =
+            new PathInterpolator(0.4f, 0, 0.6f, 1f);
+
+    private static final int DISMISS_TASK_DURATION = 175;
+    private static final int DISMISS_ALL_TASKS_DURATION = 200;
+    private static final Interpolator DISMISS_ALL_TRANSLATION_INTERPOLATOR =
+            new PathInterpolator(0.4f, 0, 1f, 1f);
+
+    private static final Interpolator FOCUS_NEXT_TASK_INTERPOLATOR =
+            new PathInterpolator(0.4f, 0, 0, 1f);
+    private static final Interpolator FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR =
+            new PathInterpolator(0, 0, 0, 1f);
+    private static final Interpolator FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR =
+            Interpolators.LINEAR_OUT_SLOW_IN;
+
+    private static final Interpolator ENTER_WHILE_DOCKING_INTERPOLATOR =
+            Interpolators.LINEAR_OUT_SLOW_IN;
+
+    private final int mEnterAndExitFromHomeTranslationOffset;
+    private TaskStackView mStackView;
+
+    private TaskViewTransform mTmpTransform = new TaskViewTransform();
+    private ArrayList<TaskViewTransform> mTmpCurrentTaskTransforms = new ArrayList<>();
+    private ArrayList<TaskViewTransform> mTmpFinalTaskTransforms = new ArrayList<>();
+
+    public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
+        mStackView = stackView;
+        mEnterAndExitFromHomeTranslationOffset = LegacyRecentsImpl.getConfiguration().isGridEnabled
+                ? 0 : DOUBLE_FRAME_OFFSET_MS;
+    }
+
+    /**
+     * Prepares the stack views and puts them in their initial animation state while visible, before
+     * the in-app enter animations start (after the window-transition completes).
+     */
+    public void prepareForEnterAnimation() {
+        RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        Resources res = mStackView.getResources();
+        Resources appResources = mStackView.getContext().getApplicationContext().getResources();
+
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mStackView.getScroller();
+        TaskStack stack = mStackView.getStack();
+        Task launchTargetTask = stack.getLaunchTarget();
+
+        // Break early if there are no tasks
+        if (stack.getTaskCount() == 0) {
+            return;
+        }
+
+        int offscreenYOffset = stackLayout.mStackRect.height();
+        int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
+                R.dimen.recents_task_stack_animation_affiliate_enter_offset);
+        int launchedWhileDockingOffset = res.getDimensionPixelSize(
+                R.dimen.recents_task_stack_animation_launched_while_docking_offset);
+        boolean isLandscape = appResources.getConfiguration().orientation
+                == Configuration.ORIENTATION_LANDSCAPE;
+
+        float top = 0;
+        final boolean isLowRamDevice = LegacyRecentsImpl.getConfiguration().isLowRamDevice;
+        if (isLowRamDevice && launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
+            stackLayout.getStackTransform(launchTargetTask, stackScroller.getStackScroll(),
+                    mTmpTransform, null /* frontTransform */);
+            top = mTmpTransform.rect.top;
+        }
+
+        // Prepare each of the task views for their enter animation from front to back
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        for (int i = taskViews.size() - 1; i >= 0; i--) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+
+            // Get the current transform for the task, which will be used to position it offscreen
+            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
+                    null);
+
+            if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
+                if (task.isLaunchTarget) {
+                    tv.onPrepareLaunchTargetForEnterAnimation();
+                } else if (isLowRamDevice && i >= taskViews.size() -
+                            (TaskStackLowRamLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT + 1)
+                        && !RecentsDebugFlags.Static.DisableRecentsLowRamEnterExitAnimation) {
+                    // Move the last 2nd and 3rd last tasks in-app animation to match the motion of
+                    // the last task's app transition
+                    stackLayout.getStackTransform(task, stackScroller.getStackScroll(),
+                            mTmpTransform, null);
+                    mTmpTransform.rect.offset(0, -top);
+                    mTmpTransform.alpha = 0f;
+                    mStackView.updateTaskViewToTransform(tv, mTmpTransform,
+                            AnimationProps.IMMEDIATE);
+                    stackLayout.getStackTransform(task, stackScroller.getStackScroll(),
+                            mTmpTransform, null);
+                    mTmpTransform.alpha = 1f;
+                    // Duration see {@link
+                    // com.android.server.wm.AppTransition#DEFAULT_APP_TRANSITION_DURATION}
+                    mStackView.updateTaskViewToTransform(tv, mTmpTransform,
+                            new AnimationProps(336, Interpolators.FAST_OUT_SLOW_IN));
+                }
+            } else if (launchState.launchedFromHome) {
+                if (isLowRamDevice) {
+                    mTmpTransform.rect.offset(0, stackLayout.getTaskRect().height() / 4);
+                } else {
+                    // Move the task view off screen (below) so we can animate it in
+                    mTmpTransform.rect.offset(0, offscreenYOffset);
+                }
+                mTmpTransform.alpha = 0f;
+                mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
+            } else if (launchState.launchedViaDockGesture) {
+                int offset = isLandscape
+                        ? launchedWhileDockingOffset
+                        : (int) (offscreenYOffset * 0.9f);
+                mTmpTransform.rect.offset(0, offset);
+                mTmpTransform.alpha = 0f;
+                mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
+            }
+        }
+    }
+
+    /**
+     * Starts the in-app enter animation, which animates the {@link TaskView}s to their final places
+     * depending on how Recents was triggered.
+     */
+    public void startEnterAnimation(final ReferenceCountedTrigger postAnimationTrigger) {
+        RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        Resources res = mStackView.getResources();
+        Resources appRes = mStackView.getContext().getApplicationContext().getResources();
+
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mStackView.getScroller();
+        TaskStack stack = mStackView.getStack();
+        Task launchTargetTask = stack.getLaunchTarget();
+
+        // Break early if there are no tasks
+        if (stack.getTaskCount() == 0) {
+            return;
+        }
+
+        final boolean isLowRamDevice = LegacyRecentsImpl.getConfiguration().isLowRamDevice;
+        int taskViewEnterFromAppDuration = res.getInteger(
+                R.integer.recents_task_enter_from_app_duration);
+        int taskViewEnterFromAffiliatedAppDuration = res.getInteger(
+                R.integer.recents_task_enter_from_affiliated_app_duration);
+        int dockGestureAnimDuration = appRes.getInteger(
+                R.integer.long_press_dock_anim_duration);
+
+        // Since low ram devices have an animation when entering app -> recents, do not allow
+        // toggle until the animation is complete
+        if (launchState.launchedFromApp && !launchState.launchedViaDockGesture && isLowRamDevice) {
+            postAnimationTrigger.addLastDecrementRunnable(() -> EventBus.getDefault()
+                .send(new SetWaitingForTransitionStartEvent(false)));
+        }
+
+        // Create enter animations for each of the views from front to back
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = taskViewCount - 1; i >= 0; i--) {
+            int taskIndexFromFront = taskViewCount - i - 1;
+            int taskIndexFromBack = i;
+            final TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+
+            // Get the current transform for the task, which will be updated to the final transform
+            // to animate to depending on how recents was invoked
+            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
+                    null);
+
+            if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
+                if (task.isLaunchTarget) {
+                    tv.onStartLaunchTargetEnterAnimation(mTmpTransform,
+                            taskViewEnterFromAppDuration, mStackView.mScreenPinningEnabled,
+                            postAnimationTrigger);
+                }
+
+            } else if (launchState.launchedFromHome) {
+                // Animate the tasks up, but offset the animations to be relative to the front-most
+                // task animation
+                final float startOffsetFraction = (float) (Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS,
+                        taskIndexFromFront) * mEnterAndExitFromHomeTranslationOffset) /
+                        ENTER_FROM_HOME_TRANSLATION_DURATION;
+                AnimationProps taskAnimation = new AnimationProps()
+                        .setInterpolator(AnimationProps.ALPHA, ENTER_FROM_HOME_ALPHA_INTERPOLATOR)
+                        .setListener(postAnimationTrigger.decrementOnAnimationEnd());
+                if (isLowRamDevice) {
+                    taskAnimation.setInterpolator(AnimationProps.BOUNDS,
+                            Interpolators.FAST_OUT_SLOW_IN)
+                            .setDuration(AnimationProps.BOUNDS, 150)
+                            .setDuration(AnimationProps.ALPHA, 150);
+                } else {
+                    taskAnimation.setStartDelay(AnimationProps.ALPHA,
+                                Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
+                                        FRAME_OFFSET_MS)
+                            .setInterpolator(AnimationProps.BOUNDS,
+                                new RecentsEntrancePathInterpolator(0f, 0f, 0.2f, 1f,
+                                        startOffsetFraction))
+                            .setDuration(AnimationProps.BOUNDS, ENTER_FROM_HOME_TRANSLATION_DURATION)
+                            .setDuration(AnimationProps.ALPHA, ENTER_FROM_HOME_ALPHA_DURATION);
+                }
+                postAnimationTrigger.increment();
+                mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+                if (i == taskViewCount - 1) {
+                    tv.onStartFrontTaskEnterAnimation(mStackView.mScreenPinningEnabled);
+                }
+            } else if (launchState.launchedViaDockGesture) {
+                // Animate the tasks up - add some delay to match the divider animation
+                AnimationProps taskAnimation = new AnimationProps()
+                        .setDuration(AnimationProps.BOUNDS, dockGestureAnimDuration +
+                                (taskIndexFromBack * DOUBLE_FRAME_OFFSET_MS))
+                        .setInterpolator(AnimationProps.BOUNDS,
+                                ENTER_WHILE_DOCKING_INTERPOLATOR)
+                        .setStartDelay(AnimationProps.BOUNDS, 48)
+                        .setListener(postAnimationTrigger.decrementOnAnimationEnd());
+                postAnimationTrigger.increment();
+                mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+            }
+        }
+    }
+
+    /**
+     * Starts an in-app animation to hide all the task views so that we can transition back home.
+     */
+    public void startExitToHomeAnimation(boolean animated,
+            ReferenceCountedTrigger postAnimationTrigger) {
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStack stack = mStackView.getStack();
+
+        // Break early if there are no tasks
+        if (stack.getTaskCount() == 0) {
+            return;
+        }
+
+        int offscreenYOffset = stackLayout.mStackRect.height();
+
+        // Create the animations for each of the tasks
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            int taskIndexFromFront = taskViewCount - i - 1;
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+
+            if (mStackView.isIgnoredTask(task)) {
+                continue;
+            }
+
+            // Animate the tasks down
+            AnimationProps taskAnimation;
+            if (animated) {
+                int delay = Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS , taskIndexFromFront) *
+                        mEnterAndExitFromHomeTranslationOffset;
+                taskAnimation = new AnimationProps()
+                        .setDuration(AnimationProps.BOUNDS, EXIT_TO_HOME_TRANSLATION_DURATION)
+                        .setListener(postAnimationTrigger.decrementOnAnimationEnd());
+                if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+                    taskAnimation.setInterpolator(AnimationProps.BOUNDS,
+                            Interpolators.FAST_OUT_SLOW_IN);
+                } else {
+                    taskAnimation.setStartDelay(AnimationProps.BOUNDS, delay)
+                            .setInterpolator(AnimationProps.BOUNDS,
+                                    EXIT_TO_HOME_TRANSLATION_INTERPOLATOR);
+                }
+                postAnimationTrigger.increment();
+            } else {
+                taskAnimation = AnimationProps.IMMEDIATE;
+            }
+
+            mTmpTransform.fillIn(tv);
+            if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+                taskAnimation.setInterpolator(AnimationProps.ALPHA,
+                                EXIT_TO_HOME_TRANSLATION_INTERPOLATOR)
+                        .setDuration(AnimationProps.ALPHA, EXIT_TO_HOME_TRANSLATION_DURATION);
+                mTmpTransform.rect.offset(0, stackLayout.mTaskStackLowRamLayoutAlgorithm
+                        .getTaskRect().height() / 4);
+                mTmpTransform.alpha = 0f;
+            } else {
+                mTmpTransform.rect.offset(0, offscreenYOffset);
+            }
+            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+        }
+    }
+
+    /**
+     * Starts the animation for the launching task view, hiding any tasks that might occlude the
+     * window transition for the launching task.
+     */
+    public void startLaunchTaskAnimation(TaskView launchingTaskView, boolean screenPinningRequested,
+            final ReferenceCountedTrigger postAnimationTrigger) {
+        Resources res = mStackView.getResources();
+
+        int taskViewExitToAppDuration = res.getInteger(
+                R.integer.recents_task_exit_to_app_duration);
+        int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
+                R.dimen.recents_task_stack_animation_affiliate_enter_offset);
+
+        Task launchingTask = launchingTaskView.getTask();
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+
+            if (tv == launchingTaskView) {
+                tv.setClipViewInStack(false);
+                postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+                    @Override
+                    public void run() {
+                        tv.setClipViewInStack(true);
+                    }
+                });
+                tv.onStartLaunchTargetLaunchAnimation(taskViewExitToAppDuration,
+                        screenPinningRequested, postAnimationTrigger);
+            }
+        }
+    }
+
+    /**
+     * Starts the delete animation for the specified {@link TaskView}.
+     */
+    public void startDeleteTaskAnimation(final TaskView deleteTaskView, boolean gridLayout,
+            final ReferenceCountedTrigger postAnimationTrigger) {
+        if (gridLayout) {
+            startTaskGridDeleteTaskAnimation(deleteTaskView, postAnimationTrigger);
+        } else {
+            startTaskStackDeleteTaskAnimation(deleteTaskView, postAnimationTrigger);
+        }
+    }
+
+    /**
+     * Starts the delete animation for all the {@link TaskView}s.
+     */
+    public void startDeleteAllTasksAnimation(final List<TaskView> taskViews, boolean gridLayout,
+            final ReferenceCountedTrigger postAnimationTrigger) {
+        if (gridLayout) {
+            for (int i = 0; i < taskViews.size(); i++) {
+                startTaskGridDeleteTaskAnimation(taskViews.get(i), postAnimationTrigger);
+            }
+        } else {
+            startTaskStackDeleteAllTasksAnimation(taskViews, postAnimationTrigger);
+        }
+    }
+
+    /**
+     * Starts the animation to focus the next {@link TaskView} when paging through recents.
+     *
+     * @return whether or not this will trigger a scroll in the stack
+     */
+    public boolean startScrollToFocusedTaskAnimation(Task newFocusedTask,
+            boolean requestViewFocus) {
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mStackView.getScroller();
+        TaskStack stack = mStackView.getStack();
+
+        final float curScroll = stackScroller.getStackScroll();
+        final float newScroll = stackScroller.getBoundedStackScroll(
+                stackLayout.getStackScrollForTask(newFocusedTask));
+        boolean willScrollToFront = newScroll > curScroll;
+        boolean willScroll = Float.compare(newScroll, curScroll) != 0;
+
+        // Get the current set of task transforms
+        int taskViewCount = mStackView.getTaskViews().size();
+        ArrayList<Task> stackTasks = stack.getTasks();
+        mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
+
+        // Pick up the newly visible views after the scroll
+        mStackView.bindVisibleTaskViews(newScroll);
+
+        // Update the internal state
+        stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
+        stackScroller.setStackScroll(newScroll, null /* animation */);
+        mStackView.cancelDeferredTaskViewLayoutAnimation();
+
+        // Get the final set of task transforms
+        mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
+                true /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
+
+        // Focus the task view
+        TaskView newFocusedTaskView = mStackView.getChildViewForTask(newFocusedTask);
+        if (newFocusedTaskView == null) {
+            // Log the error if we have no task view, and skip the animation
+            Log.e("TaskStackAnimationHelper", "b/27389156 null-task-view prebind:" + taskViewCount +
+                    " postbind:" + mStackView.getTaskViews().size() + " prescroll:" + curScroll +
+                    " postscroll: " + newScroll);
+            return false;
+        }
+        newFocusedTaskView.setFocusedState(true, requestViewFocus);
+
+        // Setup the end listener to return all the hidden views to the view pool after the
+        // focus animation
+        ReferenceCountedTrigger postAnimTrigger = new ReferenceCountedTrigger();
+        postAnimTrigger.addLastDecrementRunnable(new Runnable() {
+            @Override
+            public void run() {
+                mStackView.bindVisibleTaskViews(newScroll);
+            }
+        });
+
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        taskViewCount = taskViews.size();
+        int newFocusTaskViewIndex = taskViews.indexOf(newFocusedTaskView);
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+
+            if (mStackView.isIgnoredTask(task)) {
+                continue;
+            }
+
+            int taskIndex = stackTasks.indexOf(task);
+            TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
+            TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
+
+            // Update the task to the initial state (for the newly picked up tasks)
+            mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
+
+            int duration;
+            Interpolator interpolator;
+            if (willScrollToFront) {
+                duration = calculateStaggeredAnimDuration(i);
+                interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
+            } else {
+                if (i < newFocusTaskViewIndex) {
+                    duration = 150 + ((newFocusTaskViewIndex - i - 1) * 50);
+                    interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
+                } else if (i > newFocusTaskViewIndex) {
+                    duration = Math.max(100, 150 - ((i - newFocusTaskViewIndex - 1) * 50));
+                    interpolator = FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR;
+                } else {
+                    duration = 200;
+                    interpolator = FOCUS_NEXT_TASK_INTERPOLATOR;
+                }
+            }
+
+            AnimationProps anim = new AnimationProps()
+                    .setDuration(AnimationProps.BOUNDS, duration)
+                    .setInterpolator(AnimationProps.BOUNDS, interpolator)
+                    .setListener(postAnimTrigger.decrementOnAnimationEnd());
+            postAnimTrigger.increment();
+            mStackView.updateTaskViewToTransform(tv, toTransform, anim);
+        }
+        return willScroll;
+    }
+
+    /**
+     * Starts the animation to go to the initial stack layout with a task focused.  In addition, the
+     * previous task will be animated in after the scroll completes.
+     */
+    public void startNewStackScrollAnimation(TaskStack newStack,
+            ReferenceCountedTrigger animationTrigger) {
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mStackView.getScroller();
+
+        // Get the current set of task transforms
+        ArrayList<Task> stackTasks = newStack.getTasks();
+        mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
+
+        // Update the stack
+        mStackView.setTasks(newStack, false /* allowNotifyStackChanges */);
+        mStackView.updateLayoutAlgorithm(false /* boundScroll */);
+
+        // Pick up the newly visible views after the scroll
+        final float newScroll = stackLayout.mInitialScrollP;
+        mStackView.bindVisibleTaskViews(newScroll);
+
+        // Update the internal state
+        stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
+        stackLayout.setTaskOverridesForInitialState(newStack, true /* ignoreScrollToFront */);
+        stackScroller.setStackScroll(newScroll);
+        mStackView.cancelDeferredTaskViewLayoutAnimation();
+
+        // Get the final set of task transforms
+        mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
+                false /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
+
+        // Hide the front most task view until the scroll is complete
+        Task frontMostTask = newStack.getFrontMostTask();
+        final TaskView frontMostTaskView = mStackView.getChildViewForTask(frontMostTask);
+        final TaskViewTransform frontMostTransform = mTmpFinalTaskTransforms.get(
+                stackTasks.indexOf(frontMostTask));
+        if (frontMostTaskView != null) {
+            mStackView.updateTaskViewToTransform(frontMostTaskView,
+                    stackLayout.getFrontOfStackTransform(), AnimationProps.IMMEDIATE);
+        }
+
+        // Setup the end listener to return all the hidden views to the view pool after the
+        // focus animation
+        animationTrigger.addLastDecrementRunnable(new Runnable() {
+            @Override
+            public void run() {
+                mStackView.bindVisibleTaskViews(newScroll);
+
+                // Now, animate in the front-most task
+                if (frontMostTaskView != null) {
+                    mStackView.updateTaskViewToTransform(frontMostTaskView, frontMostTransform,
+                            new AnimationProps(75, 250, FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR));
+                }
+            }
+        });
+
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+
+            if (mStackView.isIgnoredTask(task)) {
+                continue;
+            }
+            if (task == frontMostTask && frontMostTaskView != null) {
+                continue;
+            }
+
+            int taskIndex = stackTasks.indexOf(task);
+            TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
+            TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
+
+            // Update the task to the initial state (for the newly picked up tasks)
+            mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
+
+            int duration = calculateStaggeredAnimDuration(i);
+            Interpolator interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
+
+            AnimationProps anim = new AnimationProps()
+                    .setDuration(AnimationProps.BOUNDS, duration)
+                    .setInterpolator(AnimationProps.BOUNDS, interpolator)
+                    .setListener(animationTrigger.decrementOnAnimationEnd());
+            animationTrigger.increment();
+            mStackView.updateTaskViewToTransform(tv, toTransform, anim);
+        }
+    }
+
+    /**
+     * Caclulates a staggered duration for {@link #startScrollToFocusedTaskAnimation} and
+     * {@link #startNewStackScrollAnimation}.
+     */
+    private int calculateStaggeredAnimDuration(int i) {
+        return Math.max(100, 100 + ((i - 1) * 50));
+    }
+
+    private void startTaskGridDeleteTaskAnimation(final TaskView deleteTaskView,
+            final ReferenceCountedTrigger postAnimationTrigger) {
+        postAnimationTrigger.increment();
+        postAnimationTrigger.addLastDecrementRunnable(() -> {
+            mStackView.getTouchHandler().onChildDismissed(deleteTaskView);
+        });
+        deleteTaskView.animate().setDuration(300).scaleX(0.9f).scaleY(0.9f).alpha(0).setListener(
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        postAnimationTrigger.decrement();
+                    }}).start();
+    }
+
+    private void startTaskStackDeleteTaskAnimation(final TaskView deleteTaskView,
+            final ReferenceCountedTrigger postAnimationTrigger) {
+        TaskStackViewTouchHandler touchHandler = mStackView.getTouchHandler();
+        touchHandler.onBeginManualDrag(deleteTaskView);
+
+        postAnimationTrigger.increment();
+        postAnimationTrigger.addLastDecrementRunnable(() -> {
+            touchHandler.onChildDismissed(deleteTaskView);
+        });
+
+        final float dismissSize = touchHandler.getScaledDismissSize();
+        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+        animator.setDuration(400);
+        animator.addUpdateListener((animation) -> {
+            float progress = (Float) animation.getAnimatedValue();
+            deleteTaskView.setTranslationX(progress * dismissSize);
+            touchHandler.updateSwipeProgress(deleteTaskView, true, progress);
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                postAnimationTrigger.decrement();
+            }
+        });
+        animator.start();
+    }
+
+    private void startTaskStackDeleteAllTasksAnimation(final List<TaskView> taskViews,
+            final ReferenceCountedTrigger postAnimationTrigger) {
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+
+        int offscreenXOffset = mStackView.getMeasuredWidth() - stackLayout.getTaskRect().left;
+
+        int taskViewCount = taskViews.size();
+        for (int i = taskViewCount - 1; i >= 0; i--) {
+            TaskView tv = taskViews.get(i);
+            int taskIndexFromFront = taskViewCount - i - 1;
+            int startDelay = taskIndexFromFront * DOUBLE_FRAME_OFFSET_MS;
+
+            // Disabling clipping with the stack while the view is animating away
+            tv.setClipViewInStack(false);
+
+            // Compose the new animation and transform and star the animation
+            AnimationProps taskAnimation = new AnimationProps(startDelay,
+                    DISMISS_ALL_TASKS_DURATION, DISMISS_ALL_TRANSLATION_INTERPOLATOR,
+                    new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            postAnimationTrigger.decrement();
+
+                            // Re-enable clipping with the stack (we will reuse this view)
+                            tv.setClipViewInStack(true);
+                        }
+                    });
+            postAnimationTrigger.increment();
+
+            mTmpTransform.fillIn(tv);
+            mTmpTransform.rect.offset(offscreenXOffset, 0);
+            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+        }
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
new file mode 100644
index 0000000..58a3f12
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -0,0 +1,1283 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.ViewDebug;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.RecentsActivityLaunchState;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsDebugFlags;
+import com.android.systemui.recents.misc.FreePathInterpolator;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
+import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Used to describe a visible range that can be normalized to [0, 1].
+ */
+class Range {
+    final float relativeMin;
+    final float relativeMax;
+    float origin;
+    float min;
+    float max;
+
+    public Range(float relMin, float relMax) {
+        min = relativeMin = relMin;
+        max = relativeMax = relMax;
+    }
+
+    /**
+     * Offsets this range to a given absolute position.
+     */
+    public void offset(float x) {
+        this.origin = x;
+        min = x + relativeMin;
+        max = x + relativeMax;
+    }
+
+    /**
+     * Returns x normalized to the range 0 to 1 such that 0 = min, 0.5 = origin and 1 = max
+     *
+     * @param x is an absolute value in the same domain as origin
+     */
+    public float getNormalizedX(float x) {
+        if (x < origin) {
+            return 0.5f + 0.5f * (x - origin) / -relativeMin;
+        } else {
+            return 0.5f + 0.5f * (x - origin) / relativeMax;
+        }
+    }
+
+    /**
+     * Given a normalized {@param x} value in this range, projected onto the full range to get an
+     * absolute value about the given {@param origin}.
+     */
+    public float getAbsoluteX(float normX) {
+        if (normX < 0.5f) {
+            return (normX - 0.5f) / 0.5f * -relativeMin;
+        } else {
+            return (normX - 0.5f) / 0.5f * relativeMax;
+        }
+    }
+
+    /**
+     * Returns whether a value at an absolute x would be within range.
+     */
+    public boolean isInRange(float absX) {
+        return (absX >= Math.floor(min)) && (absX <= Math.ceil(max));
+    }
+}
+
+/**
+ * The layout logic for a TaskStackView.  This layout needs to be able to calculate the stack layout
+ * without an activity-specific context only with the information passed in.  This layout can have
+ * two states focused and unfocused, and in the focused state, there is a task that is displayed
+ * more prominently in the stack.
+ */
+public class TaskStackLayoutAlgorithm {
+
+    private static final String TAG = "TaskStackLayoutAlgorithm";
+
+    // The distribution of view bounds alpha
+    // XXX: This is a hack because you can currently set the max alpha to be > 1f
+    public static final float OUTLINE_ALPHA_MIN_VALUE = 0f;
+    public static final float OUTLINE_ALPHA_MAX_VALUE = 2f;
+
+    // The medium/maximum dim on the tasks
+    private static final float MED_DIM = 0.15f;
+    private static final float MAX_DIM = 0.25f;
+
+    // The various focus states
+    public static final int STATE_FOCUSED = 1;
+    public static final int STATE_UNFOCUSED = 0;
+
+    // The side that an offset is anchored
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FROM_TOP, FROM_BOTTOM})
+    public @interface AnchorSide {}
+    private static final int FROM_TOP = 0;
+    private static final int FROM_BOTTOM = 1;
+
+    // The extent that we care about when calculating fractions
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({WIDTH, HEIGHT})
+    public @interface Extent {}
+    private static final int WIDTH = 0;
+    private static final int HEIGHT = 1;
+
+    public interface TaskStackLayoutAlgorithmCallbacks {
+        void onFocusStateChanged(int prevFocusState, int curFocusState);
+    }
+
+    /**
+     * @return True if we should use the grid layout.
+     */
+    boolean useGridLayout() {
+        return LegacyRecentsImpl.getConfiguration().isGridEnabled;
+    }
+
+    // A report of the visibility state of the stack
+    public static class VisibilityReport {
+        public int numVisibleTasks;
+        public int numVisibleThumbnails;
+
+        public VisibilityReport(int tasks, int thumbnails) {
+            numVisibleTasks = tasks;
+            numVisibleThumbnails = thumbnails;
+        }
+    }
+
+    Context mContext;
+    private TaskStackLayoutAlgorithmCallbacks mCb;
+
+    // The task bounds (untransformed) for layout.  This rect is anchored at mTaskRoot.
+    @ViewDebug.ExportedProperty(category="recents")
+    public Rect mTaskRect = new Rect();
+    // The stack bounds, inset from the top system insets, and runs to the bottom of the screen
+    @ViewDebug.ExportedProperty(category="recents")
+    public Rect mStackRect = new Rect();
+    // This is the current system insets
+    @ViewDebug.ExportedProperty(category="recents")
+    public Rect mSystemInsets = new Rect();
+
+    // The visible ranges when the stack is focused and unfocused
+    private Range mUnfocusedRange;
+    private Range mFocusedRange;
+
+    // This is the bounds of the stack action above the stack rect
+    @ViewDebug.ExportedProperty(category="recents")
+    private Rect mStackActionButtonRect = new Rect();
+    // The base top margin for the stack from the system insets
+    @ViewDebug.ExportedProperty(category="recents")
+    private int mBaseTopMargin;
+    // The base side margin for the stack from the system insets
+    @ViewDebug.ExportedProperty(category="recents")
+    private int mBaseSideMargin;
+    // The base bottom margin for the stack from the system insets
+    @ViewDebug.ExportedProperty(category="recents")
+    private int mBaseBottomMargin;
+    private int mMinMargin;
+
+    // The initial offset that the focused task is from the top
+    @ViewDebug.ExportedProperty(category="recents")
+    private int mInitialTopOffset;
+    private int mBaseInitialTopOffset;
+    // The initial offset that the launch-from task is from the bottom
+    @ViewDebug.ExportedProperty(category="recents")
+    private int mInitialBottomOffset;
+    private int mBaseInitialBottomOffset;
+
+    // The height between the top margin and the top of the focused task
+    @ViewDebug.ExportedProperty(category="recents")
+    private int mFocusedTopPeekHeight;
+    // The height between the bottom margin and the top of task in front of the focused task
+    @ViewDebug.ExportedProperty(category="recents")
+    private int mFocusedBottomPeekHeight;
+
+    // The offset from the bottom of the stack to the bottom of the bounds when the stack is
+    // scrolled to the front
+    @ViewDebug.ExportedProperty(category="recents")
+    private int mStackBottomOffset;
+
+    /** The height, in pixels, of each task view's title bar. */
+    private int mTitleBarHeight;
+
+    // The paths defining the motion of the tasks when the stack is focused and unfocused
+    private Path mUnfocusedCurve;
+    private Path mFocusedCurve;
+    private FreePathInterpolator mUnfocusedCurveInterpolator;
+    private FreePathInterpolator mFocusedCurveInterpolator;
+
+    // The paths defining the distribution of the dim to apply to tasks in the stack when focused
+    // and unfocused
+    private Path mUnfocusedDimCurve;
+    private Path mFocusedDimCurve;
+    private FreePathInterpolator mUnfocusedDimCurveInterpolator;
+    private FreePathInterpolator mFocusedDimCurveInterpolator;
+
+    // The state of the stack focus (0..1), which controls the transition of the stack from the
+    // focused to non-focused state
+    @ViewDebug.ExportedProperty(category="recents")
+    private int mFocusState;
+
+    // The smallest scroll progress, at this value, the back most task will be visible
+    @ViewDebug.ExportedProperty(category="recents")
+    float mMinScrollP;
+    // The largest scroll progress, at this value, the front most task will be visible above the
+    // navigation bar
+    @ViewDebug.ExportedProperty(category="recents")
+    float mMaxScrollP;
+    // The initial progress that the scroller is set when you first enter recents
+    @ViewDebug.ExportedProperty(category="recents")
+    float mInitialScrollP;
+    // The task progress for the front-most task in the stack
+    @ViewDebug.ExportedProperty(category="recents")
+    float mFrontMostTaskP;
+
+    // The last computed task counts
+    @ViewDebug.ExportedProperty(category="recents")
+    int mNumStackTasks;
+
+    // The min/max z translations
+    @ViewDebug.ExportedProperty(category="recents")
+    int mMinTranslationZ;
+    @ViewDebug.ExportedProperty(category="recents")
+    public int mMaxTranslationZ;
+
+    // Optimization, allows for quick lookup of task -> index
+    private SparseIntArray mTaskIndexMap = new SparseIntArray();
+    private SparseArray<Float> mTaskIndexOverrideMap = new SparseArray<>();
+
+    TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
+    TaskStackLowRamLayoutAlgorithm mTaskStackLowRamLayoutAlgorithm;
+
+    // The transform to place TaskViews at the front and back of the stack respectively
+    TaskViewTransform mBackOfStackTransform = new TaskViewTransform();
+    TaskViewTransform mFrontOfStackTransform = new TaskViewTransform();
+
+    public TaskStackLayoutAlgorithm(Context context, TaskStackLayoutAlgorithmCallbacks cb) {
+        mContext = context;
+        mCb = cb;
+        mTaskGridLayoutAlgorithm = new TaskGridLayoutAlgorithm(context);
+        mTaskStackLowRamLayoutAlgorithm = new TaskStackLowRamLayoutAlgorithm(context);
+        reloadOnConfigurationChange(context);
+    }
+
+    /**
+     * Reloads the layout for the current configuration.
+     */
+    public void reloadOnConfigurationChange(Context context) {
+        Resources res = context.getResources();
+        mFocusedRange = new Range(res.getFloat(R.integer.recents_layout_focused_range_min),
+                res.getFloat(R.integer.recents_layout_focused_range_max));
+        mUnfocusedRange = new Range(res.getFloat(R.integer.recents_layout_unfocused_range_min),
+                res.getFloat(R.integer.recents_layout_unfocused_range_max));
+        mFocusState = getInitialFocusState();
+        mFocusedTopPeekHeight = res.getDimensionPixelSize(R.dimen.recents_layout_top_peek_size);
+        mFocusedBottomPeekHeight =
+                res.getDimensionPixelSize(R.dimen.recents_layout_bottom_peek_size);
+        mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_min);
+        mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_max);
+        mBaseInitialTopOffset = getDimensionForDevice(context,
+                R.dimen.recents_layout_initial_top_offset_phone_port,
+                R.dimen.recents_layout_initial_top_offset_phone_land,
+                R.dimen.recents_layout_initial_top_offset_tablet,
+                R.dimen.recents_layout_initial_top_offset_tablet,
+                R.dimen.recents_layout_initial_top_offset_tablet,
+                R.dimen.recents_layout_initial_top_offset_tablet,
+                R.dimen.recents_layout_initial_top_offset_tablet);
+        mBaseInitialBottomOffset = getDimensionForDevice(context,
+                R.dimen.recents_layout_initial_bottom_offset_phone_port,
+                R.dimen.recents_layout_initial_bottom_offset_phone_land,
+                R.dimen.recents_layout_initial_bottom_offset_tablet,
+                R.dimen.recents_layout_initial_bottom_offset_tablet,
+                R.dimen.recents_layout_initial_bottom_offset_tablet,
+                R.dimen.recents_layout_initial_bottom_offset_tablet,
+                R.dimen.recents_layout_initial_bottom_offset_tablet);
+        mTaskGridLayoutAlgorithm.reloadOnConfigurationChange(context);
+        mTaskStackLowRamLayoutAlgorithm.reloadOnConfigurationChange(context);
+        mMinMargin = res.getDimensionPixelSize(R.dimen.recents_layout_min_margin);
+        mBaseTopMargin = getDimensionForDevice(context,
+                R.dimen.recents_layout_top_margin_phone,
+                R.dimen.recents_layout_top_margin_tablet,
+                R.dimen.recents_layout_top_margin_tablet_xlarge,
+                R.dimen.recents_layout_top_margin_tablet);
+        mBaseSideMargin = getDimensionForDevice(context,
+                R.dimen.recents_layout_side_margin_phone,
+                R.dimen.recents_layout_side_margin_tablet,
+                R.dimen.recents_layout_side_margin_tablet_xlarge,
+                R.dimen.recents_layout_side_margin_tablet);
+        mBaseBottomMargin = res.getDimensionPixelSize(R.dimen.recents_layout_bottom_margin);
+        mTitleBarHeight = getDimensionForDevice(mContext,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_grid_task_view_header_height);
+    }
+
+    /**
+     * Resets this layout when the stack view is reset.
+     */
+    public void reset() {
+        mTaskIndexOverrideMap.clear();
+        setFocusState(getInitialFocusState());
+    }
+
+    /**
+     * Sets the system insets.
+     */
+    public boolean setSystemInsets(Rect systemInsets) {
+        boolean changed = !mSystemInsets.equals(systemInsets);
+        mSystemInsets.set(systemInsets);
+        mTaskGridLayoutAlgorithm.setSystemInsets(systemInsets);
+        mTaskStackLowRamLayoutAlgorithm.setSystemInsets(systemInsets);
+        return changed;
+    }
+
+    /**
+     * Sets the focused state.
+     */
+    public void setFocusState(int focusState) {
+        int prevFocusState = mFocusState;
+        mFocusState = focusState;
+        updateFrontBackTransforms();
+        if (mCb != null) {
+            mCb.onFocusStateChanged(prevFocusState, focusState);
+        }
+    }
+
+    /**
+     * Gets the focused state.
+     */
+    public int getFocusState() {
+        return mFocusState;
+    }
+
+    /**
+     * Computes the stack and task rects.  The given task stack bounds already has the top/right
+     * insets and left/right padding already applied.
+     */
+    public void initialize(Rect displayRect, Rect windowRect, Rect taskStackBounds) {
+        Rect lastStackRect = new Rect(mStackRect);
+
+        int topMargin = getScaleForExtent(windowRect, displayRect, mBaseTopMargin, mMinMargin, HEIGHT);
+        int bottomMargin = getScaleForExtent(windowRect, displayRect, mBaseBottomMargin, mMinMargin,
+                HEIGHT);
+        mInitialTopOffset = getScaleForExtent(windowRect, displayRect, mBaseInitialTopOffset,
+                mMinMargin, HEIGHT);
+        mInitialBottomOffset = mBaseInitialBottomOffset;
+
+        // Compute the stack bounds
+        mStackBottomOffset = mSystemInsets.bottom + bottomMargin;
+        mStackRect.set(taskStackBounds);
+        mStackRect.top += topMargin;
+
+        // The stack action button will take the full un-padded header space above the stack
+        mStackActionButtonRect.set(mStackRect.left, mStackRect.top - topMargin,
+                mStackRect.right, mStackRect.top + mFocusedTopPeekHeight);
+
+        // Anchor the task rect top aligned to the stack rect
+        int height = mStackRect.height() - mInitialTopOffset - mStackBottomOffset;
+        mTaskRect.set(mStackRect.left, mStackRect.top, mStackRect.right, mStackRect.top + height);
+
+        if (mTaskRect.width() <= 0 || mTaskRect.height() <= 0) {
+            // Logging for b/36654830
+            Log.e(TAG, "Invalid task rect: taskRect=" + mTaskRect + " stackRect=" + mStackRect
+                    + " displayRect=" + displayRect + " windowRect=" + windowRect
+                    + " taskStackBounds=" + taskStackBounds);
+        }
+
+        // Short circuit here if the stack rects haven't changed so we don't do all the work below
+        if (!lastStackRect.equals(mStackRect)) {
+            // Reinitialize the focused and unfocused curves
+            mUnfocusedCurve = constructUnfocusedCurve();
+            mUnfocusedCurveInterpolator = new FreePathInterpolator(mUnfocusedCurve);
+            mFocusedCurve = constructFocusedCurve();
+            mFocusedCurveInterpolator = new FreePathInterpolator(mFocusedCurve);
+            mUnfocusedDimCurve = constructUnfocusedDimCurve();
+            mUnfocusedDimCurveInterpolator = new FreePathInterpolator(mUnfocusedDimCurve);
+            mFocusedDimCurve = constructFocusedDimCurve();
+            mFocusedDimCurveInterpolator = new FreePathInterpolator(mFocusedDimCurve);
+
+            updateFrontBackTransforms();
+        }
+
+        // Initialize the grid layout
+        mTaskGridLayoutAlgorithm.initialize(windowRect);
+        mTaskStackLowRamLayoutAlgorithm.initialize(windowRect);
+    }
+
+    /**
+     * Computes the minimum and maximum scroll progress values and the progress values for each task
+     * in the stack.
+     */
+    public void update(TaskStack stack, ArraySet<Task.TaskKey> ignoreTasksSet,
+            RecentsActivityLaunchState launchState, float lastScrollPPercent) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+
+        // Clear the progress map
+        mTaskIndexMap.clear();
+
+        // Return early if we have no tasks
+        ArrayList<Task> tasks = stack.getTasks();
+        if (tasks.isEmpty()) {
+            mFrontMostTaskP = 0;
+            mMinScrollP = mMaxScrollP = mInitialScrollP = 0;
+            mNumStackTasks = 0;
+            return;
+        }
+
+        // Filter the set of stack tasks
+        ArrayList<Task> stackTasks = new ArrayList<>();
+        for (int i = 0; i < tasks.size(); i++) {
+            Task task = tasks.get(i);
+            if (ignoreTasksSet.contains(task.key)) {
+                continue;
+            }
+            stackTasks.add(task);
+        }
+        mNumStackTasks = stackTasks.size();
+
+        // Put each of the tasks in the progress map at a fixed index (does not need to actually
+        // map to a scroll position, just by index)
+        int taskCount = stackTasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = stackTasks.get(i);
+            mTaskIndexMap.put(task.key.id, i);
+        }
+
+        // Calculate the min/max/initial scroll
+        Task launchTask = stack.getLaunchTarget();
+        int launchTaskIndex = launchTask != null
+                ? stack.indexOfTask(launchTask)
+                : mNumStackTasks - 1;
+        if (getInitialFocusState() == STATE_FOCUSED) {
+            int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
+            float maxBottomNormX = getNormalizedXFromFocusedY(maxBottomOffset, FROM_BOTTOM);
+            mFocusedRange.offset(0f);
+            mMinScrollP = 0;
+            mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
+                    Math.max(0, mFocusedRange.getAbsoluteX(maxBottomNormX)));
+            if (launchState.launchedFromHome || launchState.launchedFromPipApp
+                    || launchState.launchedWithNextPipApp) {
+                mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
+            } else {
+                mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
+            }
+        } else if (mNumStackTasks == 1) {
+            // If there is one stack task, ignore the min/max/initial scroll positions
+            mMinScrollP = 0;
+            mMaxScrollP = 0;
+            mInitialScrollP = 0;
+        } else {
+            // Set the max scroll to be the point where the front most task is visible with the
+            // stack bottom offset
+            int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
+            float maxBottomNormX = getNormalizedXFromUnfocusedY(maxBottomOffset, FROM_BOTTOM);
+            mUnfocusedRange.offset(0f);
+            mMinScrollP = LegacyRecentsImpl.getConfiguration().isLowRamDevice
+                    ? mTaskStackLowRamLayoutAlgorithm.getMinScrollP()
+                    : 0;
+            mMaxScrollP = LegacyRecentsImpl.getConfiguration().isLowRamDevice
+                    ? mTaskStackLowRamLayoutAlgorithm.getMaxScrollP(taskCount)
+                    : Math.max(mMinScrollP, (mNumStackTasks - 1) -
+                    Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX)));
+            boolean scrollToFront = launchState.launchedFromHome || launchState.launchedFromPipApp
+                    || launchState.launchedWithNextPipApp || launchState.launchedViaDockGesture;
+
+            if (launchState.launchedWithAltTab) {
+                mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
+            } else if (0 <= lastScrollPPercent && lastScrollPPercent <= 1) {
+                mInitialScrollP = Utilities.mapRange(lastScrollPPercent, mMinScrollP, mMaxScrollP);
+            } else if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+                mInitialScrollP = mTaskStackLowRamLayoutAlgorithm.getInitialScrollP(mNumStackTasks,
+                        scrollToFront);
+            } else if (scrollToFront) {
+                mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
+            } else {
+                // We are overriding the initial two task positions, so set the initial scroll
+                // position to match the second task (aka focused task) position
+                float initialTopNormX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
+                mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP, (mNumStackTasks - 2))
+                        - Math.max(0, mUnfocusedRange.getAbsoluteX(initialTopNormX)));
+            }
+        }
+    }
+
+    /**
+     * Creates task overrides to ensure the initial stack layout if necessary.
+     */
+    public void setTaskOverridesForInitialState(TaskStack stack, boolean ignoreScrollToFront) {
+        RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
+
+        mTaskIndexOverrideMap.clear();
+
+        boolean scrollToFront = launchState.launchedFromHome ||
+                launchState.launchedFromPipApp ||
+                launchState.launchedWithNextPipApp ||
+                launchState.launchedViaDockGesture;
+        if (getInitialFocusState() == STATE_UNFOCUSED && mNumStackTasks > 1) {
+            if (ignoreScrollToFront || (!launchState.launchedWithAltTab && !scrollToFront)) {
+                // Set the initial scroll to the predefined state (which differs from the stack)
+                float [] initialNormX = null;
+                float minBottomTaskNormX = getNormalizedXFromUnfocusedY(mSystemInsets.bottom +
+                        mInitialBottomOffset, FROM_BOTTOM);
+                float maxBottomTaskNormX = getNormalizedXFromUnfocusedY(mFocusedTopPeekHeight +
+                        mTaskRect.height() - mMinMargin, FROM_TOP);
+                if (mNumStackTasks <= 2) {
+                    // For small stacks, position the tasks so that they are top aligned to under
+                    // the action button, but ensure that it is at least a certain offset from the
+                    // bottom of the stack
+                    initialNormX = new float[] {
+                            Math.min(maxBottomTaskNormX, minBottomTaskNormX),
+                            getNormalizedXFromUnfocusedY(mFocusedTopPeekHeight, FROM_TOP)
+                    };
+                } else {
+                    initialNormX = new float[] {
+                            minBottomTaskNormX,
+                            getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP)
+                    };
+                }
+
+                mUnfocusedRange.offset(0f);
+                List<Task> tasks = stack.getTasks();
+                int taskCount = tasks.size();
+                for (int i = taskCount - 1; i >= 0; i--) {
+                    int indexFromFront = taskCount - i - 1;
+                    if (indexFromFront >= initialNormX.length) {
+                        break;
+                    }
+                    float newTaskProgress = mInitialScrollP +
+                            mUnfocusedRange.getAbsoluteX(initialNormX[indexFromFront]);
+                    mTaskIndexOverrideMap.put(tasks.get(i).key.id, newTaskProgress);
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds and override task progress for the given task when transitioning from focused to
+     * unfocused state.
+     */
+    public void addUnfocusedTaskOverride(Task task, float stackScroll) {
+        if (mFocusState != STATE_UNFOCUSED) {
+            mFocusedRange.offset(stackScroll);
+            mUnfocusedRange.offset(stackScroll);
+            float focusedRangeX = mFocusedRange.getNormalizedX(mTaskIndexMap.get(task.key.id));
+            float focusedY = mFocusedCurveInterpolator.getInterpolation(focusedRangeX);
+            float unfocusedRangeX = mUnfocusedCurveInterpolator.getX(focusedY);
+            float unfocusedTaskProgress = stackScroll + mUnfocusedRange.getAbsoluteX(unfocusedRangeX);
+            if (Float.compare(focusedRangeX, unfocusedRangeX) != 0) {
+                mTaskIndexOverrideMap.put(task.key.id, unfocusedTaskProgress);
+            }
+        }
+    }
+
+    /**
+     * Adds and override task progress for the given task when transitioning from focused to
+     * unfocused state.
+     */
+    public void addUnfocusedTaskOverride(TaskView taskView, float stackScroll) {
+        mFocusedRange.offset(stackScroll);
+        mUnfocusedRange.offset(stackScroll);
+
+        Task task = taskView.getTask();
+        int top = taskView.getTop() - mTaskRect.top;
+        float focusedRangeX = getNormalizedXFromFocusedY(top, FROM_TOP);
+        float unfocusedRangeX = getNormalizedXFromUnfocusedY(top, FROM_TOP);
+        float unfocusedTaskProgress = stackScroll + mUnfocusedRange.getAbsoluteX(unfocusedRangeX);
+        if (Float.compare(focusedRangeX, unfocusedRangeX) != 0) {
+            mTaskIndexOverrideMap.put(task.key.id, unfocusedTaskProgress);
+        }
+    }
+
+    public void clearUnfocusedTaskOverrides() {
+        mTaskIndexOverrideMap.clear();
+    }
+
+    /**
+     * Updates this stack when a scroll happens.
+     *
+     */
+    public float updateFocusStateOnScroll(float lastTargetStackScroll, float targetStackScroll,
+            float lastStackScroll) {
+        if (targetStackScroll == lastStackScroll || LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            return targetStackScroll;
+        }
+
+        float deltaScroll = targetStackScroll - lastStackScroll;
+        float deltaTargetScroll = targetStackScroll - lastTargetStackScroll;
+        float newScroll = targetStackScroll;
+        mUnfocusedRange.offset(targetStackScroll);
+        for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
+            int taskId = mTaskIndexOverrideMap.keyAt(i);
+            float x = mTaskIndexMap.get(taskId);
+            float overrideX = mTaskIndexOverrideMap.get(taskId, 0f);
+            float newOverrideX = overrideX + deltaScroll;
+            if (isInvalidOverrideX(x, overrideX, newOverrideX)) {
+                // Remove the override once we reach the original task index
+                mTaskIndexOverrideMap.removeAt(i);
+            } else if ((overrideX >= x && deltaScroll <= 0f) ||
+                    (overrideX <= x && deltaScroll >= 0f)) {
+                // Scrolling from override x towards x, then lock the task in place
+                mTaskIndexOverrideMap.put(taskId, newOverrideX);
+            } else {
+                // Scrolling override x away from x, we should still move the scroll towards x
+                newScroll = lastStackScroll;
+                newOverrideX = overrideX - deltaTargetScroll;
+                if (isInvalidOverrideX(x, overrideX, newOverrideX)) {
+                    mTaskIndexOverrideMap.removeAt(i);
+                } else{
+                    mTaskIndexOverrideMap.put(taskId, newOverrideX);
+                }
+            }
+        }
+        return newScroll;
+    }
+
+    private boolean isInvalidOverrideX(float x, float overrideX, float newOverrideX) {
+        boolean outOfBounds = mUnfocusedRange.getNormalizedX(newOverrideX) < 0f ||
+                mUnfocusedRange.getNormalizedX(newOverrideX) > 1f;
+        return outOfBounds || (overrideX >= x && x >= newOverrideX) ||
+                (overrideX <= x && x <= newOverrideX);
+    }
+
+    /**
+     * Returns the default focus state.
+     */
+    public int getInitialFocusState() {
+        RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
+        RecentsDebugFlags debugFlags = LegacyRecentsImpl.getDebugFlags();
+        if (launchState.launchedWithAltTab) {
+            return STATE_FOCUSED;
+        } else {
+            return STATE_UNFOCUSED;
+        }
+    }
+
+    public Rect getStackActionButtonRect() {
+        return useGridLayout()
+                ? mTaskGridLayoutAlgorithm.getStackActionButtonRect() : mStackActionButtonRect;
+    }
+
+    /**
+     * Returns the TaskViewTransform that would put the task just off the back of the stack.
+     */
+    public TaskViewTransform getBackOfStackTransform() {
+        return mBackOfStackTransform;
+    }
+
+    /**
+     * Returns the TaskViewTransform that would put the task just off the front of the stack.
+     */
+    public TaskViewTransform getFrontOfStackTransform() {
+        return mFrontOfStackTransform;
+    }
+
+    /**
+     * Returns whether this stack layout has been initialized.
+     */
+    public boolean isInitialized() {
+        return !mStackRect.isEmpty();
+    }
+
+    /**
+     * Computes the maximum number of visible tasks and thumbnails when the scroll is at the initial
+     * stack scroll.  Requires that update() is called first.
+     */
+    public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
+        if (useGridLayout()) {
+            return mTaskGridLayoutAlgorithm.computeStackVisibilityReport(tasks);
+        }
+
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            return mTaskStackLowRamLayoutAlgorithm.computeStackVisibilityReport(tasks);
+        }
+
+        // Ensure minimum visibility count
+        if (tasks.size() <= 1) {
+            return new VisibilityReport(1, 1);
+        }
+
+        // Otherwise, walk backwards in the stack and count the number of tasks and visible
+        // thumbnails and add that to the total task count
+        TaskViewTransform tmpTransform = new TaskViewTransform();
+        Range currentRange = getInitialFocusState() > 0f ? mFocusedRange : mUnfocusedRange;
+        currentRange.offset(mInitialScrollP);
+        int taskBarHeight = mContext.getResources().getDimensionPixelSize(
+                R.dimen.recents_task_view_header_height);
+        int numVisibleTasks = 0;
+        int numVisibleThumbnails = 0;
+        float prevScreenY = Integer.MAX_VALUE;
+        for (int i = tasks.size() - 1; i >= 0; i--) {
+            Task task = tasks.get(i);
+
+            // Skip invisible
+            float taskProgress = getStackScrollForTask(task);
+            if (!currentRange.isInRange(taskProgress)) {
+                continue;
+            }
+
+            getStackTransform(taskProgress, taskProgress, mInitialScrollP, mFocusState,
+                    tmpTransform, null, false /* ignoreSingleTaskCase */, false /* forceUpdate */);
+            float screenY = tmpTransform.rect.top;
+            boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
+            if (hasVisibleThumbnail) {
+                numVisibleThumbnails++;
+                numVisibleTasks++;
+                prevScreenY = screenY;
+            } else {
+                // Once we hit the next front most task that does not have a visible thumbnail,
+                // walk through remaining visible set
+                for (int j = i; j >= 0; j--) {
+                    taskProgress = getStackScrollForTask(tasks.get(j));
+                    if (!currentRange.isInRange(taskProgress)) {
+                        break;
+                    }
+                    numVisibleTasks++;
+                }
+                break;
+            }
+        }
+        return new VisibilityReport(numVisibleTasks, numVisibleThumbnails);
+    }
+
+    /**
+     * Returns the transform for the given task.  This transform is relative to the mTaskRect, which
+     * is what the view is measured and laid out with.
+     */
+    public TaskViewTransform getStackTransform(Task task, float stackScroll,
+            TaskViewTransform transformOut, TaskViewTransform frontTransform) {
+        return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
+                false /* forceUpdate */, false /* ignoreTaskOverrides */);
+    }
+
+    public TaskViewTransform getStackTransform(Task task, float stackScroll,
+            TaskViewTransform transformOut, TaskViewTransform frontTransform,
+            boolean ignoreTaskOverrides) {
+        return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
+                false /* forceUpdate */, ignoreTaskOverrides);
+    }
+
+    public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState,
+            TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate,
+            boolean ignoreTaskOverrides) {
+        if (useGridLayout()) {
+            int taskIndex = mTaskIndexMap.get(task.key.id);
+            int taskCount = mTaskIndexMap.size();
+            mTaskGridLayoutAlgorithm.getTransform(taskIndex, taskCount, transformOut, this);
+            return transformOut;
+        } else if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            if (task == null) {
+                transformOut.reset();
+                return transformOut;
+            }
+            int taskIndex = mTaskIndexMap.get(task.key.id);
+            mTaskStackLowRamLayoutAlgorithm.getTransform(taskIndex, stackScroll, transformOut,
+                    mNumStackTasks, this);
+            return transformOut;
+        } else {
+            // Return early if we have an invalid index
+            int nonOverrideTaskProgress = mTaskIndexMap.get(task.key.id, -1);
+            if (task == null || nonOverrideTaskProgress == -1) {
+                transformOut.reset();
+                return transformOut;
+            }
+            float taskProgress = ignoreTaskOverrides
+                    ? nonOverrideTaskProgress
+                    : getStackScrollForTask(task);
+
+            getStackTransform(taskProgress, nonOverrideTaskProgress, stackScroll, focusState,
+                    transformOut, frontTransform, false /* ignoreSingleTaskCase */, forceUpdate);
+            return transformOut;
+        }
+    }
+
+    /**
+     * Like {@link #getStackTransform}, but in screen coordinates
+     */
+    public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
+            TaskViewTransform transformOut, TaskViewTransform frontTransform,
+            Rect windowOverrideRect) {
+        TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
+                transformOut, frontTransform, true /* forceUpdate */,
+                false /* ignoreTaskOverrides */);
+        return transformToScreenCoordinates(transform, windowOverrideRect);
+    }
+
+    /**
+     * Transforms the given {@param transformOut} to the screen coordinates, overriding the current
+     * window rectangle with {@param windowOverrideRect} if non-null.
+     */
+    TaskViewTransform transformToScreenCoordinates(TaskViewTransform transformOut,
+            Rect windowOverrideRect) {
+        Rect windowRect = windowOverrideRect != null
+                ? windowOverrideRect
+                : LegacyRecentsImpl.getSystemServices().getWindowRect();
+        transformOut.rect.offset(windowRect.left, windowRect.top);
+        if (useGridLayout()) {
+            // Draw the thumbnail a little lower to perfectly coincide with the view we are
+            // transitioning to, where the header bar has already been drawn.
+            transformOut.rect.offset(0, mTitleBarHeight);
+        }
+        return transformOut;
+    }
+
+    /**
+     * Update/get the transform.
+     *
+     * @param ignoreSingleTaskCase When set, will ensure that the transform computed does not take
+     *                             into account the special single-task case.  This is only used
+     *                             internally to ensure that we can calculate the transform for any
+     *                             position in the stack.
+     */
+    public void getStackTransform(float taskProgress, float nonOverrideTaskProgress,
+            float stackScroll, int focusState, TaskViewTransform transformOut,
+            TaskViewTransform frontTransform, boolean ignoreSingleTaskCase, boolean forceUpdate) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+
+        // Ensure that the task is in range
+        mUnfocusedRange.offset(stackScroll);
+        mFocusedRange.offset(stackScroll);
+        boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress);
+        boolean focusedVisible = mFocusedRange.isInRange(taskProgress);
+
+        // Skip if the task is not visible
+        if (!forceUpdate && !unfocusedVisible && !focusedVisible) {
+            transformOut.reset();
+            return;
+        }
+
+        // Map the absolute task progress to the normalized x at the stack scroll.  We use this to
+        // calculate positions along the curve.
+        mUnfocusedRange.offset(stackScroll);
+        mFocusedRange.offset(stackScroll);
+        float unfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
+        float focusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
+
+        // Map the absolute task progress to the normalized x at the bounded stack scroll.  We use
+        // this to calculate bounded properties, like translationZ and outline alpha.
+        float boundedStackScroll = Utilities.clamp(stackScroll, mMinScrollP, mMaxScrollP);
+        mUnfocusedRange.offset(boundedStackScroll);
+        mFocusedRange.offset(boundedStackScroll);
+        float boundedScrollUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
+        float boundedScrollUnfocusedNonOverrideRangeX =
+                mUnfocusedRange.getNormalizedX(nonOverrideTaskProgress);
+
+        // Map the absolute task progress to the normalized x at the upper bounded stack scroll.
+        // We use this to calculate the dim, which is bounded only on one end.
+        float lowerBoundedStackScroll = Utilities.clamp(stackScroll, -Float.MAX_VALUE, mMaxScrollP);
+        mUnfocusedRange.offset(lowerBoundedStackScroll);
+        mFocusedRange.offset(lowerBoundedStackScroll);
+        float lowerBoundedUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
+        float lowerBoundedFocusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
+
+        int x = (mStackRect.width() - mTaskRect.width()) / 2;
+        int y;
+        float z;
+        float dimAlpha;
+        float viewOutlineAlpha;
+        if (mNumStackTasks == 1 && !ignoreSingleTaskCase) {
+            // When there is exactly one task, then decouple the task from the stack and just move
+            // in screen space
+            float tmpP = (mMinScrollP - stackScroll) / mNumStackTasks;
+            int centerYOffset = (mStackRect.top - mTaskRect.top) +
+                    (mStackRect.height() - mSystemInsets.bottom - mTaskRect.height()) / 2;
+            y = centerYOffset + getYForDeltaP(tmpP, 0);
+            z = mMaxTranslationZ;
+            dimAlpha = 0f;
+            viewOutlineAlpha = OUTLINE_ALPHA_MIN_VALUE +
+                    (OUTLINE_ALPHA_MAX_VALUE - OUTLINE_ALPHA_MIN_VALUE) / 2f;
+
+        } else {
+            // Otherwise, update the task to the stack layout
+            int unfocusedY = (int) ((1f - mUnfocusedCurveInterpolator.getInterpolation(
+                    unfocusedRangeX)) * mStackRect.height());
+            int focusedY = (int) ((1f - mFocusedCurveInterpolator.getInterpolation(
+                    focusedRangeX)) * mStackRect.height());
+            float unfocusedDim = mUnfocusedDimCurveInterpolator.getInterpolation(
+                    lowerBoundedUnfocusedRangeX);
+            float focusedDim = mFocusedDimCurveInterpolator.getInterpolation(
+                    lowerBoundedFocusedRangeX);
+
+            // Special case, because we override the initial task positions differently for small
+            // stacks, we clamp the dim to 0 in the initial position, and then only modulate the
+            // dim when the task is scrolled back towards the top of the screen
+            if (mNumStackTasks <= 2 && nonOverrideTaskProgress == 0f) {
+                if (boundedScrollUnfocusedRangeX >= 0.5f) {
+                    unfocusedDim = 0f;
+                } else {
+                    float offset = mUnfocusedDimCurveInterpolator.getInterpolation(0.5f);
+                    unfocusedDim -= offset;
+                    unfocusedDim *= MAX_DIM / (MAX_DIM - offset);
+                }
+            }
+            y = (mStackRect.top - mTaskRect.top) +
+                    (int) com.android.systemui.recents.utilities.Utilities
+                            .mapRange(focusState, unfocusedY, focusedY);
+            z = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedNonOverrideRangeX),
+                    mMinTranslationZ, mMaxTranslationZ);
+            dimAlpha = com.android.systemui.recents.utilities.Utilities
+                    .mapRange(focusState, unfocusedDim, focusedDim);
+            viewOutlineAlpha = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedRangeX),
+                    OUTLINE_ALPHA_MIN_VALUE, OUTLINE_ALPHA_MAX_VALUE);
+        }
+
+        // Fill out the transform
+        transformOut.scale = 1f;
+        transformOut.alpha = 1f;
+        transformOut.translationZ = z;
+        transformOut.dimAlpha = dimAlpha;
+        transformOut.viewOutlineAlpha = viewOutlineAlpha;
+        transformOut.rect.set(mTaskRect);
+        transformOut.rect.offset(x, y);
+        Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+        transformOut.visible = (transformOut.rect.top < mStackRect.bottom) &&
+                (frontTransform == null || transformOut.rect.top != frontTransform.rect.top);
+    }
+
+    /**
+     * Returns the untransformed task view bounds.
+     */
+    public Rect getUntransformedTaskViewBounds() {
+        return new Rect(mTaskRect);
+    }
+
+    /**
+     * Returns the scroll progress to scroll to such that the top of the task is at the top of the
+     * stack.
+     */
+    float getStackScrollForTask(Task t) {
+        Float overrideP = mTaskIndexOverrideMap.get(t.key.id, null);
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice || overrideP == null) {
+            return (float) mTaskIndexMap.get(t.key.id, 0);
+        }
+        return overrideP;
+    }
+
+    /**
+     * Returns the original scroll progress to scroll to such that the top of the task is at the top
+     * of the stack.
+     */
+    float getStackScrollForTaskIgnoreOverrides(Task t) {
+        return (float) mTaskIndexMap.get(t.key.id, 0);
+    }
+
+    /**
+     * Returns the scroll progress to scroll to such that the top of the task at the initial top
+     * offset (which is at the task's brightest point).
+     */
+    float getStackScrollForTaskAtInitialOffset(Task t) {
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
+            return mTaskStackLowRamLayoutAlgorithm.getInitialScrollP(mNumStackTasks,
+                    launchState.launchedFromHome || launchState.launchedFromPipApp
+                            || launchState.launchedWithNextPipApp);
+        }
+        float normX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
+        mUnfocusedRange.offset(0f);
+        return Utilities.clamp((float) mTaskIndexMap.get(t.key.id, 0) - Math.max(0,
+                mUnfocusedRange.getAbsoluteX(normX)), mMinScrollP, mMaxScrollP);
+    }
+
+    /**
+     * Maps a movement in screen y, relative to {@param downY}, to a movement in along the arc
+     * length of the curve.  We know the curve is mostly flat, so we just map the length of the
+     * screen along the arc-length proportionally (1/arclength).
+     */
+    public float getDeltaPForY(int downY, int y) {
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            return mTaskStackLowRamLayoutAlgorithm.scrollToPercentage(downY - y);
+        }
+        float deltaP = (float) (y - downY) / mStackRect.height() *
+                mUnfocusedCurveInterpolator.getArcLength();
+        return -deltaP;
+    }
+
+    /**
+     * This is the inverse of {@link #getDeltaPForY}.  Given a movement along the arc length
+     * of the curve, map back to the screen y.
+     */
+    public int getYForDeltaP(float downScrollP, float p) {
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            return mTaskStackLowRamLayoutAlgorithm.percentageToScroll(downScrollP - p);
+        }
+        int y = (int) ((p - downScrollP) * mStackRect.height() *
+                (1f / mUnfocusedCurveInterpolator.getArcLength()));
+        return -y;
+    }
+
+    /**
+     * Returns the task stack bounds in the current orientation.  This rect takes into account the
+     * top and right system insets (but not the bottom inset) and left/right paddings, but _not_
+     * the top/bottom padding or insets.
+     */
+    public void getTaskStackBounds(Rect displayRect, Rect windowRect, int topInset, int leftInset,
+            int rightInset, Rect taskStackBounds) {
+        taskStackBounds.set(windowRect.left + leftInset, windowRect.top + topInset,
+                windowRect.right - rightInset, windowRect.bottom);
+
+        // Ensure that the new width is at most the smaller display edge size
+        int sideMargin = getScaleForExtent(windowRect, displayRect, mBaseSideMargin, mMinMargin,
+                WIDTH);
+        int targetStackWidth = taskStackBounds.width() - 2 * sideMargin;
+        if (Utilities.getAppConfiguration(mContext).orientation
+                == Configuration.ORIENTATION_LANDSCAPE) {
+            // If we are in landscape, calculate the width of the stack in portrait and ensure that
+            // we are not larger than that size
+            Rect portraitDisplayRect = new Rect(0, 0,
+                    Math.min(displayRect.width(), displayRect.height()),
+                    Math.max(displayRect.width(), displayRect.height()));
+            int portraitSideMargin = getScaleForExtent(portraitDisplayRect, portraitDisplayRect,
+                    mBaseSideMargin, mMinMargin, WIDTH);
+            targetStackWidth = Math.min(targetStackWidth,
+                    portraitDisplayRect.width() - 2 * portraitSideMargin);
+        }
+        taskStackBounds.inset((taskStackBounds.width() - targetStackWidth) / 2, 0);
+    }
+
+    /**
+     * Retrieves resources that are constant regardless of the current configuration of the device.
+     */
+    public static int getDimensionForDevice(Context ctx, int phoneResId,
+            int tabletResId, int xlargeTabletResId, int gridLayoutResId) {
+        return getDimensionForDevice(ctx, phoneResId, phoneResId, tabletResId, tabletResId,
+                xlargeTabletResId, xlargeTabletResId, gridLayoutResId);
+    }
+
+    /**
+     * Retrieves resources that are constant regardless of the current configuration of the device.
+     */
+    public static int getDimensionForDevice(Context ctx, int phonePortResId, int phoneLandResId,
+            int tabletPortResId, int tabletLandResId, int xlargeTabletPortResId,
+            int xlargeTabletLandResId, int gridLayoutResId) {
+        RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+        Resources res = ctx.getResources();
+        boolean isLandscape = Utilities.getAppConfiguration(ctx).orientation ==
+                Configuration.ORIENTATION_LANDSCAPE;
+        if (config.isGridEnabled) {
+            return res.getDimensionPixelSize(gridLayoutResId);
+        } else if (config.isXLargeScreen) {
+            return res.getDimensionPixelSize(isLandscape
+                    ? xlargeTabletLandResId
+                    : xlargeTabletPortResId);
+        } else if (config.isLargeScreen) {
+            return res.getDimensionPixelSize(isLandscape
+                    ? tabletLandResId
+                    : tabletPortResId);
+        } else {
+            return res.getDimensionPixelSize(isLandscape
+                    ? phoneLandResId
+                    : phonePortResId);
+        }
+    }
+
+    /**
+     * Returns the normalized x on the unfocused curve given an absolute Y position (relative to the
+     * stack height).
+     */
+    private float getNormalizedXFromUnfocusedY(float y, @AnchorSide int fromSide) {
+        float offset = (fromSide == FROM_TOP)
+                ? mStackRect.height() - y
+                : y;
+        float offsetPct = offset / mStackRect.height();
+        return mUnfocusedCurveInterpolator.getX(offsetPct);
+    }
+
+    /**
+     * Returns the normalized x on the focused curve given an absolute Y position (relative to the
+     * stack height).
+     */
+    private float getNormalizedXFromFocusedY(float y, @AnchorSide int fromSide) {
+        float offset = (fromSide == FROM_TOP)
+                ? mStackRect.height() - y
+                : y;
+        float offsetPct = offset / mStackRect.height();
+        return mFocusedCurveInterpolator.getX(offsetPct);
+    }
+
+    /**
+     * Creates a new path for the focused curve.
+     */
+    private Path constructFocusedCurve() {
+        // Initialize the focused curve. This curve is a piecewise curve composed of several
+        // linear pieces that goes from (0,1) through (0.5, peek height offset),
+        // (0.5, bottom task offsets), and (1,0).
+        float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
+        float bottomPeekHeightPct = (float) (mStackBottomOffset + mFocusedBottomPeekHeight) /
+                mStackRect.height();
+        float minBottomPeekHeightPct = (float) (mFocusedTopPeekHeight + mTaskRect.height() -
+                mMinMargin) / mStackRect.height();
+        Path p = new Path();
+        p.moveTo(0f, 1f);
+        p.lineTo(0.5f, 1f - topPeekHeightPct);
+        p.lineTo(1f - (0.5f / mFocusedRange.relativeMax), Math.max(1f - minBottomPeekHeightPct,
+                bottomPeekHeightPct));
+        p.lineTo(1f, 0f);
+        return p;
+    }
+
+    /**
+     * Creates a new path for the unfocused curve.
+     */
+    private Path constructUnfocusedCurve() {
+        // Initialize the unfocused curve. This curve is a piecewise curve composed of two quadradic
+        // beziers that goes from (0,1) through (0.5, peek height offset) and ends at (1,0).  This
+        // ensures that we match the range, at which 0.5 represents the stack scroll at the current
+        // task progress.  Because the height offset can change depending on a resource, we compute
+        // the control point of the second bezier such that between it and a first known point,
+        // there is a tangent at (0.5, peek height offset).
+        float cpoint1X = 0.4f;
+        float cpoint1Y = 0.975f;
+        float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
+        float slope = ((1f - topPeekHeightPct) - cpoint1Y) / (0.5f - cpoint1X);
+        float b = 1f - slope * cpoint1X;
+        float cpoint2X = 0.65f;
+        float cpoint2Y = slope * cpoint2X + b;
+        Path p = new Path();
+        p.moveTo(0f, 1f);
+        p.cubicTo(0f, 1f, cpoint1X, cpoint1Y, 0.5f, 1f - topPeekHeightPct);
+        p.cubicTo(0.5f, 1f - topPeekHeightPct, cpoint2X, cpoint2Y, 1f, 0f);
+        return p;
+    }
+
+    /**
+     * Creates a new path for the focused dim curve.
+     */
+    private Path constructFocusedDimCurve() {
+        Path p = new Path();
+        // The focused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused
+        // task), then goes back to max dim at the next task
+        p.moveTo(0f, MAX_DIM);
+        p.lineTo(0.5f, 0f);
+        p.lineTo(0.5f + (0.5f / mFocusedRange.relativeMax), MAX_DIM);
+        p.lineTo(1f, MAX_DIM);
+        return p;
+    }
+
+    /**
+     * Creates a new path for the unfocused dim curve.
+     */
+    private Path constructUnfocusedDimCurve() {
+        float focusX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
+        float cpoint2X = focusX + (1f - focusX) / 2;
+        Path p = new Path();
+        // The unfocused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused
+        // task), then goes back to max dim towards the front of the stack
+        p.moveTo(0f, MAX_DIM);
+        p.cubicTo(focusX * 0.5f, MAX_DIM, focusX * 0.75f, MAX_DIM * 0.75f, focusX, 0f);
+        p.cubicTo(cpoint2X, 0f, cpoint2X, MED_DIM, 1f, MED_DIM);
+        return p;
+    }
+
+    /**
+     * Scales the given {@param value} to the scale of the {@param instance} rect relative to the
+     * {@param other} rect in the {@param extent} side.
+     */
+    private int getScaleForExtent(Rect instance, Rect other, int value, int minValue,
+                                  @Extent int extent) {
+        if (extent == WIDTH) {
+            float scale = Utilities.clamp01((float) instance.width() / other.width());
+            return Math.max(minValue, (int) (scale * value));
+        } else if (extent == HEIGHT) {
+            float scale = Utilities.clamp01((float) instance.height() / other.height());
+            return Math.max(minValue, (int) (scale * value));
+        }
+        return value;
+    }
+
+    /**
+     * Updates the current transforms that would put a TaskView at the front and back of the stack.
+     */
+    private void updateFrontBackTransforms() {
+        // Return early if we have not yet initialized
+        if (mStackRect.isEmpty()) {
+            return;
+        }
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            mTaskStackLowRamLayoutAlgorithm.getBackOfStackTransform(mBackOfStackTransform, this);
+            mTaskStackLowRamLayoutAlgorithm.getFrontOfStackTransform(mFrontOfStackTransform, this);
+            return;
+        }
+
+        float min = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMin,
+                mFocusedRange.relativeMin);
+        float max = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMax,
+                mFocusedRange.relativeMax);
+        getStackTransform(min, min, 0f, mFocusState, mBackOfStackTransform, null,
+                true /* ignoreSingleTaskCase */, true /* forceUpdate */);
+        getStackTransform(max, max, 0f, mFocusState, mFrontOfStackTransform, null,
+                true /* ignoreSingleTaskCase */, true /* forceUpdate */);
+        mBackOfStackTransform.visible = true;
+        mFrontOfStackTransform.visible = true;
+    }
+
+    /**
+     * Returns the proper task rectangle according to the current grid state.
+     */
+    public Rect getTaskRect() {
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            return mTaskStackLowRamLayoutAlgorithm.getTaskRect();
+        }
+        return useGridLayout() ? mTaskGridLayoutAlgorithm.getTaskGridRect() : mTaskRect;
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "  ";
+
+        writer.print(prefix); writer.print(TAG);
+        writer.write(" numStackTasks="); writer.print(mNumStackTasks);
+        writer.println();
+
+        writer.print(innerPrefix);
+        writer.print("insets="); writer.print(Utilities.dumpRect(mSystemInsets));
+        writer.print(" stack="); writer.print(Utilities.dumpRect(mStackRect));
+        writer.print(" task="); writer.print(Utilities.dumpRect(mTaskRect));
+        writer.print(" actionButton="); writer.print(
+                Utilities.dumpRect(mStackActionButtonRect));
+        writer.println();
+
+        writer.print(innerPrefix);
+        writer.print("minScroll="); writer.print(mMinScrollP);
+        writer.print(" maxScroll="); writer.print(mMaxScrollP);
+        writer.print(" initialScroll="); writer.print(mInitialScrollP);
+        writer.println();
+
+        writer.print(innerPrefix);
+        writer.print("focusState="); writer.print(mFocusState);
+        writer.println();
+
+        if (mTaskIndexOverrideMap.size() > 0) {
+            for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
+                int taskId = mTaskIndexOverrideMap.keyAt(i);
+                float x = mTaskIndexMap.get(taskId);
+                float overrideX = mTaskIndexOverrideMap.get(taskId, 0f);
+
+                writer.print(innerPrefix);
+                writer.print("taskId= "); writer.print(taskId);
+                writer.print(" x= "); writer.print(x);
+                writer.print(" overrideX= "); writer.print(overrideX);
+                writer.println();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackView.java
new file mode 100644
index 0000000..14fd149
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackView.java
@@ -0,0 +1,2288 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.MutableBoolean;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+import android.widget.ScrollView;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.RecentsActivityLaunchState;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsDebugFlags;
+import com.android.systemui.recents.RecentsImpl;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
+import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
+import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
+import com.android.systemui.recents.events.activity.LaunchMostRecentTaskRequestEvent;
+import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
+import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
+import com.android.systemui.recents.events.activity.PackagesChangedEvent;
+import com.android.systemui.recents.events.activity.ShowEmptyViewEvent;
+import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
+import com.android.systemui.recents.events.component.ActivityPinnedEvent;
+import com.android.systemui.recents.events.component.ExpandPipEvent;
+import com.android.systemui.recents.events.component.HidePipMenuEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
+import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
+import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
+import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
+import com.android.systemui.recents.events.ui.UserInteractionEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
+import com.android.systemui.recents.misc.DozeTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.utilities.AnimationProps;
+import com.android.systemui.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.grid.GridTaskView;
+import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
+import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
+
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/* The visual representation of a task stack view */
+public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
+        TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
+        TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks,
+        ViewPool.ViewPoolConsumer<TaskView, Task> {
+
+    private static final String TAG = "TaskStackView";
+
+    // The thresholds at which to show/hide the stack action button.
+    private static final float SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
+    private static final float HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
+
+    public static final int DEFAULT_SYNC_STACK_DURATION = 200;
+    public static final int SLOW_SYNC_STACK_DURATION = 250;
+    private static final int DRAG_SCALE_DURATION = 175;
+    static final float DRAG_SCALE_FACTOR = 1.05f;
+
+    private static final int LAUNCH_NEXT_SCROLL_BASE_DURATION = 216;
+    private static final int LAUNCH_NEXT_SCROLL_INCR_DURATION = 32;
+
+    // The actions to perform when resetting to initial state,
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({INITIAL_STATE_UPDATE_NONE, INITIAL_STATE_UPDATE_ALL, INITIAL_STATE_UPDATE_LAYOUT_ONLY})
+    public @interface InitialStateAction {}
+    /** Do not update the stack and layout to the initial state. */
+    private static final int INITIAL_STATE_UPDATE_NONE = 0;
+    /** Update both the stack and layout to the initial state. */
+    private static final int INITIAL_STATE_UPDATE_ALL = 1;
+    /** Update only the layout to the initial state. */
+    private static final int INITIAL_STATE_UPDATE_LAYOUT_ONLY = 2;
+
+    private LayoutInflater mInflater;
+    private TaskStack mStack = new TaskStack();
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
+    TaskStackLayoutAlgorithm mLayoutAlgorithm;
+    // The stable layout algorithm is only used to calculate the task rect with the stable bounds
+    private TaskStackLayoutAlgorithm mStableLayoutAlgorithm;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
+    private TaskStackViewScroller mStackScroller;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
+    private TaskStackViewTouchHandler mTouchHandler;
+    private TaskStackAnimationHelper mAnimationHelper;
+    private ViewPool<TaskView, Task> mViewPool;
+
+    private ArrayList<TaskView> mTaskViews = new ArrayList<>();
+    private ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
+    private ArraySet<Task.TaskKey> mIgnoreTasks = new ArraySet<>();
+    private AnimationProps mDeferredTaskViewLayoutAnimation = null;
+
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="doze_")
+    private DozeTrigger mUIDozeTrigger;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="focused_task_")
+    private Task mFocusedTask;
+
+    private int mTaskCornerRadiusPx;
+    private int mDividerSize;
+    private int mStartTimerIndicatorDuration;
+
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mTaskViewsClipDirty = true;
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mEnterAnimationComplete = false;
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mStackReloaded = false;
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mFinishedLayoutAfterStackReload = false;
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mLaunchNextAfterFirstMeasure = false;
+    @ViewDebug.ExportedProperty(category="recents")
+    @InitialStateAction
+    private int mInitialState = INITIAL_STATE_UPDATE_ALL;
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mInMeasureLayout = false;
+    @ViewDebug.ExportedProperty(category="recents")
+    boolean mTouchExplorationEnabled;
+    @ViewDebug.ExportedProperty(category="recents")
+    boolean mScreenPinningEnabled;
+
+    // The stable stack bounds are the full bounds that we were measured with from RecentsView
+    @ViewDebug.ExportedProperty(category="recents")
+    private Rect mStableStackBounds = new Rect();
+    // The current stack bounds are dynamic and may change as the user drags and drops
+    @ViewDebug.ExportedProperty(category="recents")
+    private Rect mStackBounds = new Rect();
+    // The current window bounds at the point we were measured
+    @ViewDebug.ExportedProperty(category="recents")
+    private Rect mStableWindowRect = new Rect();
+    // The current window bounds are dynamic and may change as the user drags and drops
+    @ViewDebug.ExportedProperty(category="recents")
+    private Rect mWindowRect = new Rect();
+    // The current display bounds
+    @ViewDebug.ExportedProperty(category="recents")
+    private Rect mDisplayRect = new Rect();
+    // The current display orientation
+    @ViewDebug.ExportedProperty(category="recents")
+    private int mDisplayOrientation = Configuration.ORIENTATION_UNDEFINED;
+
+    private Rect mTmpRect = new Rect();
+    private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
+    private List<TaskView> mTmpTaskViews = new ArrayList<>();
+    private TaskViewTransform mTmpTransform = new TaskViewTransform();
+    private int[] mTmpIntPair = new int[2];
+    private boolean mResetToInitialStateWhenResized;
+    private int mLastWidth;
+    private int mLastHeight;
+    private boolean mStackActionButtonVisible;
+
+    // Percentage of last ScrollP from the min to max scrollP that lives after configuration changes
+    private float mLastScrollPPercent = -1;
+
+    // We keep track of the task view focused by user interaction and draw a frame around it in the
+    // grid layout.
+    private TaskViewFocusFrame mTaskViewFocusFrame;
+
+    private Task mPrefetchingTask;
+    private final float mFastFlingVelocity;
+
+    // A convenience update listener to request updating clipping of tasks
+    private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
+            new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    if (!mTaskViewsClipDirty) {
+                        mTaskViewsClipDirty = true;
+                        invalidate();
+                    }
+                }
+            };
+
+    private DropTarget mStackDropTarget = new DropTarget() {
+        @Override
+        public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
+                boolean isCurrentTarget) {
+            // This drop target has a fixed bounds and should be checked last, so just fall through
+            // if it is the current target
+            if (!isCurrentTarget) {
+                return mLayoutAlgorithm.mStackRect.contains(x, y);
+            }
+            return false;
+        }
+    };
+
+    public TaskStackView(Context context) {
+        super(context);
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        Resources res = context.getResources();
+
+        // Set the stack first
+        mStack.setCallbacks(this);
+        mViewPool = new ViewPool<>(context, this);
+        mInflater = LayoutInflater.from(context);
+        mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
+        mStableLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
+        mStackScroller = new TaskStackViewScroller(context, this, mLayoutAlgorithm);
+        mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
+        mAnimationHelper = new TaskStackAnimationHelper(context, this);
+        mTaskCornerRadiusPx = LegacyRecentsImpl.getConfiguration().isGridEnabled ?
+                res.getDimensionPixelSize(R.dimen.recents_grid_task_view_rounded_corners_radius) :
+                res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+        mFastFlingVelocity = res.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
+        mDividerSize = ssp.getDockedDividerSize(context);
+        mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
+        mDisplayRect = ssp.getDisplayRect();
+        mStackActionButtonVisible = false;
+
+        // Create a frame to draw around the focused task view
+        if (LegacyRecentsImpl.getConfiguration().isGridEnabled) {
+            mTaskViewFocusFrame = new TaskViewFocusFrame(mContext, this,
+                mLayoutAlgorithm.mTaskGridLayoutAlgorithm);
+            addView(mTaskViewFocusFrame);
+            getViewTreeObserver().addOnGlobalFocusChangeListener(mTaskViewFocusFrame);
+        }
+
+        int taskBarDismissDozeDelaySeconds = getResources().getInteger(
+                R.integer.recents_task_bar_dismiss_delay_seconds);
+        mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
+            @Override
+            public void run() {
+                // Show the task bar dismiss buttons
+                List<TaskView> taskViews = getTaskViews();
+                int taskViewCount = taskViews.size();
+                for (int i = 0; i < taskViewCount; i++) {
+                    TaskView tv = taskViews.get(i);
+                    tv.startNoUserInteractionAnimation();
+                }
+            }
+        });
+        setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+        super.onAttachedToWindow();
+        readSystemFlags();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        EventBus.getDefault().unregister(this);
+    }
+
+    /**
+     * Called from RecentsActivity when it is relaunched.
+     */
+    void onReload(boolean isResumingFromVisible) {
+        if (!isResumingFromVisible) {
+            // Reset the focused task
+            resetFocusedTask(getFocusedTask());
+        }
+
+        // Reset the state of each of the task views
+        List<TaskView> taskViews = new ArrayList<>();
+        taskViews.addAll(getTaskViews());
+        taskViews.addAll(mViewPool.getViews());
+        for (int i = taskViews.size() - 1; i >= 0; i--) {
+            taskViews.get(i).onReload(isResumingFromVisible);
+        }
+
+        // Reset the stack state
+        readSystemFlags();
+        mTaskViewsClipDirty = true;
+        mUIDozeTrigger.stopDozing();
+        if (!isResumingFromVisible) {
+            mStackScroller.reset();
+            mStableLayoutAlgorithm.reset();
+            mLayoutAlgorithm.reset();
+            mLastScrollPPercent = -1;
+        }
+
+        // Since we always animate to the same place in (the initial state), always reset the stack
+        // to the initial state when resuming
+        mStackReloaded = true;
+        mFinishedLayoutAfterStackReload = false;
+        mLaunchNextAfterFirstMeasure = false;
+        mInitialState = INITIAL_STATE_UPDATE_ALL;
+        requestLayout();
+    }
+
+    /**
+     * Sets the stack tasks of this TaskStackView from the given TaskStack.
+     */
+    public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) {
+        boolean isInitialized = mLayoutAlgorithm.isInitialized();
+
+        // Only notify if we are already initialized, otherwise, everything will pick up all the
+        // new and old tasks when we next layout
+        mStack.setTasks(stack, allowNotifyStackChanges && isInitialized);
+    }
+
+    /** Returns the task stack. */
+    public TaskStack getStack() {
+        return mStack;
+    }
+
+    /**
+     * Updates this TaskStackView to the initial state.
+     */
+    public void updateToInitialState() {
+        mStackScroller.setStackScrollToInitialState();
+        mLayoutAlgorithm.setTaskOverridesForInitialState(mStack, false /* ignoreScrollToFront */);
+    }
+
+    /** Updates the list of task views */
+    void updateTaskViewsList() {
+        mTaskViews.clear();
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View v = getChildAt(i);
+            if (v instanceof TaskView) {
+                mTaskViews.add((TaskView) v);
+            }
+        }
+    }
+
+    /** Gets the list of task views */
+    List<TaskView> getTaskViews() {
+        return mTaskViews;
+    }
+
+    /**
+     * Returns the front most task view.
+     */
+    private TaskView getFrontMostTaskView() {
+        List<TaskView> taskViews = getTaskViews();
+        if (taskViews.isEmpty()) {
+            return null;
+        }
+        return taskViews.get(taskViews.size() - 1);
+    }
+
+    /**
+     * Finds the child view given a specific {@param task}.
+     */
+    public TaskView getChildViewForTask(Task t) {
+        List<TaskView> taskViews = getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskView tv = taskViews.get(i);
+            if (tv.getTask() == t) {
+                return tv;
+            }
+        }
+        return null;
+    }
+
+    /** Returns the stack algorithm for this task stack. */
+    public TaskStackLayoutAlgorithm getStackAlgorithm() {
+        return mLayoutAlgorithm;
+    }
+
+    /** Returns the grid algorithm for this task stack. */
+    public TaskGridLayoutAlgorithm getGridAlgorithm() {
+        return mLayoutAlgorithm.mTaskGridLayoutAlgorithm;
+    }
+
+    /**
+     * Returns the touch handler for this task stack.
+     */
+    public TaskStackViewTouchHandler getTouchHandler() {
+        return mTouchHandler;
+    }
+
+    /**
+     * Adds a task to the ignored set.
+     */
+    void addIgnoreTask(Task task) {
+        mIgnoreTasks.add(task.key);
+    }
+
+    /**
+     * Removes a task from the ignored set.
+     */
+    void removeIgnoreTask(Task task) {
+        mIgnoreTasks.remove(task.key);
+    }
+
+    /**
+     * Returns whether the specified {@param task} is ignored.
+     */
+    boolean isIgnoredTask(Task task) {
+        return mIgnoreTasks.contains(task.key);
+    }
+
+    /**
+     * Computes the task transforms at the current stack scroll for all visible tasks. If a valid
+     * target stack scroll is provided (ie. is different than {@param curStackScroll}), then the
+     * visible range includes all tasks at the target stack scroll. This is useful for ensure that
+     * all views necessary for a transition or animation will be visible at the start.
+     *
+     * @param taskTransforms The set of task view transforms to reuse, this list will be sized to
+     *                       match the size of {@param tasks}
+     * @param tasks The set of tasks for which to generate transforms
+     * @param curStackScroll The current stack scroll
+     * @param targetStackScroll The stack scroll that we anticipate we are going to be scrolling to.
+     *                          The range of the union of the visible views at the current and
+     *                          target stack scrolls will be returned.
+     * @param ignoreTasksSet The set of tasks to skip for purposes of calculaing the visible range.
+     *                       Transforms will still be calculated for the ignore tasks.
+     * @return the front and back most visible task indices (there may be non visible tasks in
+     *         between this range)
+     */
+    int[] computeVisibleTaskTransforms(ArrayList<TaskViewTransform> taskTransforms,
+            ArrayList<Task> tasks, float curStackScroll, float targetStackScroll,
+            ArraySet<Task.TaskKey> ignoreTasksSet, boolean ignoreTaskOverrides) {
+        int taskCount = tasks.size();
+        int[] visibleTaskRange = mTmpIntPair;
+        visibleTaskRange[0] = -1;
+        visibleTaskRange[1] = -1;
+        boolean useTargetStackScroll = Float.compare(curStackScroll, targetStackScroll) != 0;
+
+        // We can reuse the task transforms where possible to reduce object allocation
+        matchTaskListSize(tasks, taskTransforms);
+
+        // Update the stack transforms
+        TaskViewTransform frontTransform = null;
+        TaskViewTransform frontTransformAtTarget = null;
+        TaskViewTransform transform = null;
+        TaskViewTransform transformAtTarget = null;
+        for (int i = taskCount - 1; i >= 0; i--) {
+            Task task = tasks.get(i);
+
+            // Calculate the current and (if necessary) the target transform for the task
+            transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
+                    taskTransforms.get(i), frontTransform, ignoreTaskOverrides);
+            if (useTargetStackScroll && !transform.visible) {
+                // If we have a target stack scroll and the task is not currently visible, then we
+                // just update the transform at the new scroll
+                // TODO: Optimize this
+                transformAtTarget = mLayoutAlgorithm.getStackTransform(task, targetStackScroll,
+                    new TaskViewTransform(), frontTransformAtTarget);
+                if (transformAtTarget.visible) {
+                    transform.copyFrom(transformAtTarget);
+                }
+            }
+
+            // For ignore tasks, only calculate the stack transform and skip the calculation of the
+            // visible stack indices
+            if (ignoreTasksSet.contains(task.key)) {
+                continue;
+            }
+
+            frontTransform = transform;
+            frontTransformAtTarget = transformAtTarget;
+            if (transform.visible) {
+                if (visibleTaskRange[0] < 0) {
+                    visibleTaskRange[0] = i;
+                }
+                visibleTaskRange[1] = i;
+            }
+        }
+        return visibleTaskRange;
+    }
+
+    /**
+     * Binds the visible {@link TaskView}s at the given target scroll.
+     */
+    void bindVisibleTaskViews(float targetStackScroll) {
+        bindVisibleTaskViews(targetStackScroll, false /* ignoreTaskOverrides */);
+    }
+
+    /**
+     * Synchronizes the set of children {@link TaskView}s to match the visible set of tasks in the
+     * current {@link TaskStack}. This call does not continue on to update their position to the
+     * computed {@link TaskViewTransform}s of the visible range, but only ensures that they will
+     * be added/removed from the view hierarchy and placed in the correct Z order and initial
+     * position (if not currently on screen).
+     *
+     * @param targetStackScroll If provided, will ensure that the set of visible {@link TaskView}s
+     *                          includes those visible at the current stack scroll, and all at the
+     *                          target stack scroll.
+     * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for
+     *                            tasks at their non-overridden task progress
+     */
+    void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
+        // Get all the task transforms
+        ArrayList<Task> tasks = mStack.getTasks();
+        int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
+                mStackScroller.getStackScroll(), targetStackScroll, mIgnoreTasks,
+                ignoreTaskOverrides);
+
+        // Return all the invisible children to the pool
+        mTmpTaskViewMap.clear();
+        List<TaskView> taskViews = getTaskViews();
+        int lastFocusedTaskIndex = -1;
+        int taskViewCount = taskViews.size();
+        for (int i = taskViewCount - 1; i >= 0; i--) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+
+            // Skip ignored tasks
+            if (mIgnoreTasks.contains(task.key)) {
+                continue;
+            }
+
+            // It is possible for the set of lingering TaskViews to differ from the stack if the
+            // stack was updated before the relayout.  If the task view is no longer in the stack,
+            // then just return it back to the view pool.
+            int taskIndex = mStack.indexOfTask(task);
+            TaskViewTransform transform = null;
+            if (taskIndex != -1) {
+                transform = mCurrentTaskTransforms.get(taskIndex);
+            }
+
+            if (transform != null && transform.visible) {
+                mTmpTaskViewMap.put(task.key, tv);
+            } else {
+                if (mTouchExplorationEnabled && Utilities.isDescendentAccessibilityFocused(tv)) {
+                    lastFocusedTaskIndex = taskIndex;
+                    resetFocusedTask(task);
+                }
+                mViewPool.returnViewToPool(tv);
+            }
+        }
+
+        // Pick up all the newly visible children
+        for (int i = tasks.size() - 1; i >= 0; i--) {
+            Task task = tasks.get(i);
+            TaskViewTransform transform = mCurrentTaskTransforms.get(i);
+
+            // Skip ignored tasks
+            if (mIgnoreTasks.contains(task.key)) {
+                continue;
+            }
+
+            // Skip the invisible stack tasks
+            if (!transform.visible) {
+                continue;
+            }
+
+            TaskView tv = mTmpTaskViewMap.get(task.key);
+            if (tv == null) {
+                tv = mViewPool.pickUpViewFromPool(task, task);
+                if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
+                    updateTaskViewToTransform(tv, mLayoutAlgorithm.getBackOfStackTransform(),
+                            AnimationProps.IMMEDIATE);
+                } else {
+                    updateTaskViewToTransform(tv, mLayoutAlgorithm.getFrontOfStackTransform(),
+                            AnimationProps.IMMEDIATE);
+                }
+            } else {
+                // Reattach it in the right z order
+                final int taskIndex = mStack.indexOfTask(task);
+                final int insertIndex = findTaskViewInsertIndex(task, taskIndex);
+                if (insertIndex != getTaskViews().indexOf(tv)){
+                    detachViewFromParent(tv);
+                    attachViewToParent(tv, insertIndex, tv.getLayoutParams());
+                    updateTaskViewsList();
+                }
+            }
+        }
+
+        updatePrefetchingTask(tasks, visibleTaskRange[0], visibleTaskRange[1]);
+
+        // Update the focus if the previous focused task was returned to the view pool
+        if (lastFocusedTaskIndex != -1) {
+            int newFocusedTaskIndex = (lastFocusedTaskIndex < visibleTaskRange[1])
+                    ? visibleTaskRange[1]
+                    : visibleTaskRange[0];
+            setFocusedTask(newFocusedTaskIndex, false /* scrollToTask */,
+                    true /* requestViewFocus */);
+            TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
+            if (focusedTaskView != null) {
+                focusedTaskView.requestAccessibilityFocus();
+            }
+        }
+    }
+
+    /**
+     * @see #relayoutTaskViews(AnimationProps, ArrayMap<Task, AnimationProps>, boolean)
+     */
+    public void relayoutTaskViews(AnimationProps animation) {
+        relayoutTaskViews(animation, null /* animationOverrides */,
+                false /* ignoreTaskOverrides */);
+    }
+
+    /**
+     * Relayout the the visible {@link TaskView}s to their current transforms as specified by the
+     * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any
+     * animations that are current running on those task views, and will ensure that the children
+     * {@link TaskView}s will match the set of visible tasks in the stack.  If a {@link Task} has
+     * an animation provided in {@param animationOverrides}, that will be used instead.
+     */
+    private void relayoutTaskViews(AnimationProps animation,
+            ArrayMap<Task, AnimationProps> animationOverrides, boolean ignoreTaskOverrides) {
+        // If we had a deferred animation, cancel that
+        cancelDeferredTaskViewLayoutAnimation();
+
+        // Synchronize the current set of TaskViews
+        bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTaskOverrides);
+
+        // Animate them to their final transforms with the given animation
+        List<TaskView> taskViews = getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+
+            if (mIgnoreTasks.contains(task.key)) {
+                continue;
+            }
+
+            int taskIndex = mStack.indexOfTask(task);
+            TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
+            if (animationOverrides != null && animationOverrides.containsKey(task)) {
+                animation = animationOverrides.get(task);
+            }
+
+            updateTaskViewToTransform(tv, transform, animation);
+        }
+    }
+
+    /**
+     * Posts an update to synchronize the {@link TaskView}s with the stack on the next frame.
+     */
+    void relayoutTaskViewsOnNextFrame(AnimationProps animation) {
+        mDeferredTaskViewLayoutAnimation = animation;
+        invalidate();
+    }
+
+    /**
+     * Called to update a specific {@link TaskView} to a given {@link TaskViewTransform} with a
+     * given set of {@link AnimationProps} properties.
+     */
+    public void updateTaskViewToTransform(TaskView taskView, TaskViewTransform transform,
+            AnimationProps animation) {
+        if (taskView.isAnimatingTo(transform)) {
+            return;
+        }
+        taskView.cancelTransformAnimation();
+        taskView.updateViewPropertiesToTaskTransform(transform, animation,
+                mRequestUpdateClippingListener);
+    }
+
+    /**
+     * Returns the current task transforms of all tasks, falling back to the stack layout if there
+     * is no {@link TaskView} for the task.
+     */
+    public void getCurrentTaskTransforms(ArrayList<Task> tasks,
+            ArrayList<TaskViewTransform> transformsOut) {
+        matchTaskListSize(tasks, transformsOut);
+        int focusState = mLayoutAlgorithm.getFocusState();
+        for (int i = tasks.size() - 1; i >= 0; i--) {
+            Task task = tasks.get(i);
+            TaskViewTransform transform = transformsOut.get(i);
+            TaskView tv = getChildViewForTask(task);
+            if (tv != null) {
+                transform.fillIn(tv);
+            } else {
+                mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
+                        focusState, transform, null, true /* forceUpdate */,
+                        false /* ignoreTaskOverrides */);
+            }
+            transform.visible = true;
+        }
+    }
+
+    /**
+     * Returns the task transforms for all the tasks in the stack if the stack was at the given
+     * {@param stackScroll} and {@param focusState}.
+     */
+    public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks,
+            boolean ignoreTaskOverrides, ArrayList<TaskViewTransform> transformsOut) {
+        matchTaskListSize(tasks, transformsOut);
+        for (int i = tasks.size() - 1; i >= 0; i--) {
+            Task task = tasks.get(i);
+            TaskViewTransform transform = transformsOut.get(i);
+            mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
+                    true /* forceUpdate */, ignoreTaskOverrides);
+            transform.visible = true;
+        }
+    }
+
+    /**
+     * Cancels the next deferred task view layout.
+     */
+    void cancelDeferredTaskViewLayoutAnimation() {
+        mDeferredTaskViewLayoutAnimation = null;
+    }
+
+    /**
+     * Cancels all {@link TaskView} animations.
+     */
+    void cancelAllTaskViewAnimations() {
+        List<TaskView> taskViews = getTaskViews();
+        for (int i = taskViews.size() - 1; i >= 0; i--) {
+            final TaskView tv = taskViews.get(i);
+            if (!mIgnoreTasks.contains(tv.getTask().key)) {
+                tv.cancelTransformAnimation();
+            }
+        }
+    }
+
+    /**
+     * Updates the clip for each of the task views from back to front.
+     */
+    private void clipTaskViews() {
+        // We never clip task views in grid layout
+        if (LegacyRecentsImpl.getConfiguration().isGridEnabled) {
+            return;
+        }
+
+        // Update the clip on each task child
+        List<TaskView> taskViews = getTaskViews();
+        TaskView tmpTv = null;
+        TaskView prevVisibleTv = null;
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskView tv = taskViews.get(i);
+            TaskView frontTv = null;
+            int clipBottom = 0;
+
+            if (isIgnoredTask(tv.getTask())) {
+                // For each of the ignore tasks, update the translationZ of its TaskView to be
+                // between the translationZ of the tasks immediately underneath it
+                if (prevVisibleTv != null) {
+                    tv.setTranslationZ(Math.max(tv.getTranslationZ(),
+                            prevVisibleTv.getTranslationZ() + 0.1f));
+                }
+            }
+
+            if (i < (taskViewCount - 1) && tv.shouldClipViewInStack()) {
+                // Find the next view to clip against
+                for (int j = i + 1; j < taskViewCount; j++) {
+                    tmpTv = taskViews.get(j);
+
+                    if (tmpTv.shouldClipViewInStack()) {
+                        frontTv = tmpTv;
+                        break;
+                    }
+                }
+
+                // Clip against the next view, this is just an approximation since we are
+                // stacked and we can make assumptions about the visibility of the this
+                // task relative to the ones in front of it.
+                if (frontTv != null) {
+                    float taskBottom = tv.getBottom();
+                    float frontTaskTop = frontTv.getTop();
+                    if (frontTaskTop < taskBottom) {
+                        // Map the stack view space coordinate (the rects) to view space
+                        clipBottom = (int) (taskBottom - frontTaskTop) - mTaskCornerRadiusPx;
+                    }
+                }
+            }
+            tv.getViewBounds().setClipBottom(clipBottom);
+            tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
+            prevVisibleTv = tv;
+        }
+        mTaskViewsClipDirty = false;
+    }
+
+    public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
+        updateLayoutAlgorithm(boundScrollToNewMinMax, LegacyRecentsImpl.getConfiguration().getLaunchState());
+    }
+
+    /**
+     * Updates the layout algorithm min and max virtual scroll bounds.
+     */
+   public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
+           RecentsActivityLaunchState launchState) {
+        // Compute the min and max scroll values
+        mLayoutAlgorithm.update(mStack, mIgnoreTasks, launchState, mLastScrollPPercent);
+
+        if (boundScrollToNewMinMax) {
+            mStackScroller.boundScroll();
+        }
+    }
+
+    /**
+     * Updates the stack layout to its stable places.
+     */
+    private void updateLayoutToStableBounds() {
+        mWindowRect.set(mStableWindowRect);
+        mStackBounds.set(mStableStackBounds);
+        mLayoutAlgorithm.setSystemInsets(mStableLayoutAlgorithm.mSystemInsets);
+        mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
+        updateLayoutAlgorithm(true /* boundScroll */);
+    }
+
+    /** Returns the scroller. */
+    public TaskStackViewScroller getScroller() {
+        return mStackScroller;
+    }
+
+    /**
+     * Sets the focused task to the provided (bounded taskIndex).
+     *
+     * @return whether or not the stack will scroll as a part of this focus change
+     */
+    public boolean setFocusedTask(int taskIndex, boolean scrollToTask,
+            final boolean requestViewFocus) {
+        return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
+    }
+
+    /**
+     * Sets the focused task to the provided (bounded focusTaskIndex).
+     *
+     * @return whether or not the stack will scroll as a part of this focus change
+     */
+    public boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
+            boolean requestViewFocus, int timerIndicatorDuration) {
+        // Find the next task to focus
+        int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
+                Utilities.clamp(focusTaskIndex, 0, mStack.getTaskCount() - 1) : -1;
+        final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
+                mStack.getTasks().get(newFocusedTaskIndex) : null;
+
+        // Reset the last focused task state if changed
+        if (mFocusedTask != null) {
+            // Cancel the timer indicator, if applicable
+            if (timerIndicatorDuration > 0) {
+                final TaskView tv = getChildViewForTask(mFocusedTask);
+                if (tv != null) {
+                    tv.getHeaderView().cancelFocusTimerIndicator();
+                }
+            }
+
+            resetFocusedTask(mFocusedTask);
+        }
+
+        boolean willScroll = false;
+        mFocusedTask = newFocusedTask;
+
+        if (newFocusedTask != null) {
+            // Start the timer indicator, if applicable
+            if (timerIndicatorDuration > 0) {
+                final TaskView tv = getChildViewForTask(mFocusedTask);
+                if (tv != null) {
+                    tv.getHeaderView().startFocusTimerIndicator(timerIndicatorDuration);
+                } else {
+                    // The view is null; set a flag for later
+                    mStartTimerIndicatorDuration = timerIndicatorDuration;
+                }
+            }
+
+            if (scrollToTask) {
+                // Cancel any running enter animations at this point when we scroll or change focus
+                if (!mEnterAnimationComplete) {
+                    cancelAllTaskViewAnimations();
+                }
+
+                mLayoutAlgorithm.clearUnfocusedTaskOverrides();
+                willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
+                        requestViewFocus);
+                if (willScroll) {
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+                }
+            } else {
+                // Focus the task view
+                TaskView newFocusedTaskView = getChildViewForTask(newFocusedTask);
+                if (newFocusedTaskView != null) {
+                    newFocusedTaskView.setFocusedState(true, requestViewFocus);
+                }
+            }
+            // Any time a task view gets the focus, we move the focus frame around it.
+            if (mTaskViewFocusFrame != null) {
+                mTaskViewFocusFrame.moveGridTaskViewFocus(getChildViewForTask(newFocusedTask));
+            }
+        }
+        return willScroll;
+    }
+
+    /**
+     * Sets the focused task relative to the currently focused task.
+     *
+     * @param forward whether to go to the next task in the stack (along the curve) or the previous
+     * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
+     *                       if the currently focused task is not a stack task, will set the focus
+     *                       to the first visible stack task
+     * @param animated determines whether to actually draw the highlight along with the change in
+     *                            focus.
+     */
+    public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) {
+        setRelativeFocusedTask(forward, stackTasksOnly, animated, false, 0);
+    }
+
+    /**
+     * Sets the focused task relative to the currently focused task.
+     *
+     * @param forward whether to go to the next task in the stack (along the curve) or the previous
+     * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
+     *                       if the currently focused task is not a stack task, will set the focus
+     *                       to the first visible stack task
+     * @param animated determines whether to actually draw the highlight along with the change in
+     *                            focus.
+     * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
+     *                               happens.
+     * @param timerIndicatorDuration the duration to initialize the auto-advance timer indicator
+     */
+    public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
+                                       boolean cancelWindowAnimations, int timerIndicatorDuration) {
+        Task focusedTask = getFocusedTask();
+        int newIndex = mStack.indexOfTask(focusedTask);
+        if (focusedTask != null) {
+            if (stackTasksOnly) {
+                List<Task> tasks =  mStack.getTasks();
+                // Try the next task if it is a stack task
+                int tmpNewIndex = newIndex + (forward ? -1 : 1);
+                if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
+                    newIndex = tmpNewIndex;
+                }
+            } else {
+                // No restrictions, lets just move to the new task (looping forward/backwards if
+                // necessary)
+                int taskCount = mStack.getTaskCount();
+                newIndex = (newIndex + (forward ? -1 : 1) + taskCount) % taskCount;
+            }
+        } else {
+            // We don't have a focused task
+            float stackScroll = mStackScroller.getStackScroll();
+            ArrayList<Task> tasks = mStack.getTasks();
+            int taskCount = tasks.size();
+            if (useGridLayout()) {
+                // For the grid layout, we directly set focus to the most recently used task
+                // no matter we're moving forwards or backwards.
+                newIndex = taskCount - 1;
+            } else {
+                // For the grid layout we pick a proper task to focus, according to the current
+                // stack scroll.
+                if (forward) {
+                    // Walk backwards and focus the next task smaller than the current stack scroll
+                    for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
+                        float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
+                        if (Float.compare(taskP, stackScroll) <= 0) {
+                            break;
+                        }
+                    }
+                } else {
+                    // Walk forwards and focus the next task larger than the current stack scroll
+                    for (newIndex = 0; newIndex < taskCount; newIndex++) {
+                        float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
+                        if (Float.compare(taskP, stackScroll) >= 0) {
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        if (newIndex != -1) {
+            boolean willScroll = setFocusedTask(newIndex, true /* scrollToTask */,
+                    true /* requestViewFocus */, timerIndicatorDuration);
+            if (willScroll && cancelWindowAnimations) {
+                // As we iterate to the next/previous task, cancel any current/lagging window
+                // transition animations
+                EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
+            }
+        }
+    }
+
+    /**
+     * Resets the focused task.
+     */
+    public void resetFocusedTask(Task task) {
+        if (task != null) {
+            TaskView tv = getChildViewForTask(task);
+            if (tv != null) {
+                tv.setFocusedState(false, false /* requestViewFocus */);
+            }
+        }
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+        }
+        mFocusedTask = null;
+    }
+
+    /**
+     * Returns the focused task.
+     */
+    public Task getFocusedTask() {
+        return mFocusedTask;
+    }
+
+    /**
+     * Returns the accessibility focused task.
+     */
+    Task getAccessibilityFocusedTask() {
+        List<TaskView> taskViews = getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskView tv = taskViews.get(i);
+            if (Utilities.isDescendentAccessibilityFocused(tv)) {
+                return tv.getTask();
+            }
+        }
+        TaskView frontTv = getFrontMostTaskView();
+        if (frontTv != null) {
+            return frontTv.getTask();
+        }
+        return null;
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        List<TaskView> taskViews = getTaskViews();
+        int taskViewCount = taskViews.size();
+        if (taskViewCount > 0) {
+            TaskView backMostTask = taskViews.get(0);
+            TaskView frontMostTask = taskViews.get(taskViewCount - 1);
+            event.setFromIndex(mStack.indexOfTask(backMostTask.getTask()));
+            event.setToIndex(mStack.indexOfTask(frontMostTask.getTask()));
+            event.setContentDescription(frontMostTask.getTask().title);
+        }
+        event.setItemCount(mStack.getTaskCount());
+
+        int stackHeight = mLayoutAlgorithm.mStackRect.height();
+        event.setScrollY((int) (mStackScroller.getStackScroll() * stackHeight));
+        event.setMaxScrollY((int) (mLayoutAlgorithm.mMaxScrollP * stackHeight));
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        List<TaskView> taskViews = getTaskViews();
+        int taskViewCount = taskViews.size();
+        if (taskViewCount > 1) {
+            // Find the accessibility focused task
+            Task focusedTask = getAccessibilityFocusedTask();
+            info.setScrollable(true);
+            int focusedTaskIndex = mStack.indexOfTask(focusedTask);
+            if (focusedTaskIndex > 0 || !mStackActionButtonVisible) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+            }
+            if (0 <= focusedTaskIndex && focusedTaskIndex < mStack.getTaskCount() - 1) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+            }
+        }
+    }
+
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return ScrollView.class.getName();
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (super.performAccessibilityAction(action, arguments)) {
+            return true;
+        }
+        Task focusedTask = getAccessibilityFocusedTask();
+        int taskIndex = mStack.indexOfTask(focusedTask);
+        if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
+            switch (action) {
+                case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+                    setFocusedTask(taskIndex + 1, true /* scrollToTask */, true /* requestViewFocus */,
+                            0);
+                    return true;
+                }
+                case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+                    setFocusedTask(taskIndex - 1, true /* scrollToTask */, true /* requestViewFocus */,
+                            0);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        return mTouchHandler.onInterceptTouchEvent(ev);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        return mTouchHandler.onTouchEvent(ev);
+    }
+
+    @Override
+    public boolean onGenericMotionEvent(MotionEvent ev) {
+        return mTouchHandler.onGenericMotionEvent(ev);
+    }
+
+    @Override
+    public void computeScroll() {
+        if (mStackScroller.computeScroll()) {
+            // Notify accessibility
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+            LegacyRecentsImpl.getTaskLoader().getHighResThumbnailLoader().setFlingingFast(
+                    mStackScroller.getScrollVelocity() > mFastFlingVelocity);
+        }
+        if (mDeferredTaskViewLayoutAnimation != null) {
+            relayoutTaskViews(mDeferredTaskViewLayoutAnimation);
+            mTaskViewsClipDirty = true;
+            mDeferredTaskViewLayoutAnimation = null;
+        }
+        if (mTaskViewsClipDirty) {
+            clipTaskViews();
+        }
+        mLastScrollPPercent = Utilities.clamp(Utilities.unmapRange(mStackScroller.getStackScroll(),
+            mLayoutAlgorithm.mMinScrollP, mLayoutAlgorithm.mMaxScrollP), 0, 1);
+    }
+
+    /**
+     * Computes the maximum number of visible tasks and thumbnails. Requires that
+     * updateLayoutForStack() is called first.
+     */
+    public TaskStackLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
+        return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getTasks());
+    }
+
+    /**
+     * Updates the system insets.
+     */
+    public void setSystemInsets(Rect systemInsets) {
+        boolean changed = false;
+        changed |= mStableLayoutAlgorithm.setSystemInsets(systemInsets);
+        changed |= mLayoutAlgorithm.setSystemInsets(systemInsets);
+        if (changed) {
+            requestLayout();
+        }
+    }
+
+    /**
+     * This is called with the full window width and height to allow stack view children to
+     * perform the full screen transition down.
+     */
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        mInMeasureLayout = true;
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        int height = MeasureSpec.getSize(heightMeasureSpec);
+
+        // Update the stable stack bounds, but only update the current stack bounds if the stable
+        // bounds have changed.  This is because we may get spurious measures while dragging where
+        // our current stack bounds reflect the target drop region.
+        mLayoutAlgorithm.getTaskStackBounds(mDisplayRect, new Rect(0, 0, width, height),
+                mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.left,
+                mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
+        if (!mTmpRect.equals(mStableStackBounds)) {
+            mStableStackBounds.set(mTmpRect);
+            mStackBounds.set(mTmpRect);
+            mStableWindowRect.set(0, 0, width, height);
+            mWindowRect.set(0, 0, width, height);
+        }
+
+        // Compute the rects in the stack algorithm
+        mStableLayoutAlgorithm.initialize(mDisplayRect, mStableWindowRect, mStableStackBounds);
+        mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
+        updateLayoutAlgorithm(false /* boundScroll */);
+
+        // If this is the first layout, then scroll to the front of the stack, then update the
+        // TaskViews with the stack so that we can lay them out
+        boolean resetToInitialState = (width != mLastWidth || height != mLastHeight)
+                && mResetToInitialStateWhenResized;
+        if (!mFinishedLayoutAfterStackReload || mInitialState != INITIAL_STATE_UPDATE_NONE
+                || resetToInitialState) {
+            if (mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY || resetToInitialState) {
+                updateToInitialState();
+                mResetToInitialStateWhenResized = false;
+            }
+            if (mFinishedLayoutAfterStackReload) {
+                mInitialState = INITIAL_STATE_UPDATE_NONE;
+            }
+        }
+        // If we got the launch-next event before the first layout pass, then re-send it after the
+        // initial state has been updated
+        if (mLaunchNextAfterFirstMeasure) {
+            mLaunchNextAfterFirstMeasure = false;
+            EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
+        }
+
+        // Rebind all the views, including the ignore ones
+        bindVisibleTaskViews(mStackScroller.getStackScroll(), false /* ignoreTaskOverrides */);
+
+        // Measure each of the TaskViews
+        mTmpTaskViews.clear();
+        mTmpTaskViews.addAll(getTaskViews());
+        mTmpTaskViews.addAll(mViewPool.getViews());
+        int taskViewCount = mTmpTaskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            measureTaskView(mTmpTaskViews.get(i));
+        }
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.measure();
+        }
+
+        setMeasuredDimension(width, height);
+        mLastWidth = width;
+        mLastHeight = height;
+        mInMeasureLayout = false;
+    }
+
+    /**
+     * Measures a TaskView.
+     */
+    private void measureTaskView(TaskView tv) {
+        Rect padding = new Rect();
+        if (tv.getBackground() != null) {
+            tv.getBackground().getPadding(padding);
+        }
+        mTmpRect.set(mStableLayoutAlgorithm.getTaskRect());
+        mTmpRect.union(mLayoutAlgorithm.getTaskRect());
+        tv.measure(
+                MeasureSpec.makeMeasureSpec(mTmpRect.width() + padding.left + padding.right,
+                        MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(mTmpRect.height() + padding.top + padding.bottom,
+                        MeasureSpec.EXACTLY));
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        // Layout each of the TaskViews
+        mTmpTaskViews.clear();
+        mTmpTaskViews.addAll(getTaskViews());
+        mTmpTaskViews.addAll(mViewPool.getViews());
+        int taskViewCount = mTmpTaskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            layoutTaskView(changed, mTmpTaskViews.get(i));
+        }
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.layout();
+        }
+
+        if (changed) {
+            if (mStackScroller.isScrollOutOfBounds()) {
+                mStackScroller.boundScroll();
+            }
+        }
+
+        // Relayout all of the task views including the ignored ones
+        relayoutTaskViews(AnimationProps.IMMEDIATE);
+        clipTaskViews();
+
+        if (!mFinishedLayoutAfterStackReload) {
+            // Prepare the task enter animations (this can be called numerous times)
+            mInitialState = INITIAL_STATE_UPDATE_NONE;
+            onFirstLayout();
+
+            if (mStackReloaded) {
+                mFinishedLayoutAfterStackReload = true;
+                tryStartEnterAnimation();
+            }
+        }
+    }
+
+    /**
+     * Lays out a TaskView.
+     */
+    private void layoutTaskView(boolean changed, TaskView tv) {
+        if (changed) {
+            Rect padding = new Rect();
+            if (tv.getBackground() != null) {
+                tv.getBackground().getPadding(padding);
+            }
+            mTmpRect.set(mStableLayoutAlgorithm.getTaskRect());
+            mTmpRect.union(mLayoutAlgorithm.getTaskRect());
+            tv.cancelTransformAnimation();
+            tv.layout(mTmpRect.left - padding.left, mTmpRect.top - padding.top,
+                    mTmpRect.right + padding.right, mTmpRect.bottom + padding.bottom);
+        } else {
+            // If the layout has not changed, then just lay it out again in-place
+            tv.layout(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
+        }
+    }
+
+    /** Handler for the first layout. */
+    void onFirstLayout() {
+        // Setup the view for the enter animation
+        mAnimationHelper.prepareForEnterAnimation();
+
+        // Set the task focused state without requesting view focus, and leave the focus animations
+        // until after the enter-animation
+        RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+
+        // We set the initial focused task view iff the following conditions are satisfied:
+        // 1. Recents is showing task views in stack layout.
+        // 2. Recents is launched with ALT + TAB.
+        boolean setFocusOnFirstLayout = !useGridLayout() || launchState.launchedWithAltTab;
+        if (setFocusOnFirstLayout) {
+            int focusedTaskIndex = getInitialFocusTaskIndex(launchState, mStack.getTaskCount(),
+                useGridLayout());
+            if (focusedTaskIndex != -1) {
+                setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
+                        false /* requestViewFocus */);
+            }
+        }
+        updateStackActionButtonVisibility();
+    }
+
+    public boolean isTouchPointInView(float x, float y, TaskView tv) {
+        mTmpRect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
+        mTmpRect.offset((int) tv.getTranslationX(), (int) tv.getTranslationY());
+        return mTmpRect.contains((int) x, (int) y);
+    }
+
+    /**
+     * Returns a non-ignored task in the {@param tasks} list that can be used as an achor when
+     * calculating the scroll position before and after a layout change.
+     */
+    public Task findAnchorTask(List<Task> tasks, MutableBoolean isFrontMostTask) {
+        for (int i = tasks.size() - 1; i >= 0; i--) {
+            Task task = tasks.get(i);
+
+            // Ignore deleting tasks
+            if (isIgnoredTask(task)) {
+                if (i == tasks.size() - 1) {
+                    isFrontMostTask.value = true;
+                }
+                continue;
+            }
+            return task;
+        }
+        return null;
+    }
+
+    /**** TaskStackCallbacks Implementation ****/
+
+    @Override
+    public void onStackTaskAdded(TaskStack stack, Task newTask) {
+        // Update the min/max scroll and animate other task views into their new positions
+        updateLayoutAlgorithm(true /* boundScroll */);
+
+        // Animate all the tasks into place
+        relayoutTaskViews(!mFinishedLayoutAfterStackReload
+                ? AnimationProps.IMMEDIATE
+                : new AnimationProps(DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN));
+    }
+
+    /**
+     * We expect that the {@link TaskView} associated with the removed task is already hidden.
+     */
+    @Override
+    public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
+            AnimationProps animation, boolean fromDockGesture, boolean dismissRecentsIfAllRemoved) {
+        if (mFocusedTask == removedTask) {
+            resetFocusedTask(removedTask);
+        }
+
+        // Remove the view associated with this task, we can't rely on updateTransforms
+        // to work here because the task is no longer in the list
+        TaskView tv = getChildViewForTask(removedTask);
+        if (tv != null) {
+            mViewPool.returnViewToPool(tv);
+        }
+
+        // Remove the task from the ignored set
+        removeIgnoreTask(removedTask);
+
+        // If requested, relayout with the given animation
+        if (animation != null) {
+            updateLayoutAlgorithm(true /* boundScroll */);
+            relayoutTaskViews(animation);
+        }
+
+        // Update the new front most task's action button
+        if (mScreenPinningEnabled && newFrontMostTask != null) {
+            TaskView frontTv = getChildViewForTask(newFrontMostTask);
+            if (frontTv != null) {
+                frontTv.showActionButton(true /* fadeIn */, DEFAULT_SYNC_STACK_DURATION);
+            }
+        }
+
+        // If there are no remaining tasks, then just close recents
+        if (mStack.getTaskCount() == 0) {
+            if (dismissRecentsIfAllRemoved) {
+                EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture
+                        ? R.string.recents_empty_message
+                        : R.string.recents_empty_message_dismissed_all));
+            } else {
+                EventBus.getDefault().send(new ShowEmptyViewEvent());
+            }
+        }
+    }
+
+    @Override
+    public void onStackTasksRemoved(TaskStack stack) {
+        // Reset the focused task
+        resetFocusedTask(getFocusedTask());
+
+        // Return all the views to the pool
+        List<TaskView> taskViews = new ArrayList<>();
+        taskViews.addAll(getTaskViews());
+        for (int i = taskViews.size() - 1; i >= 0; i--) {
+            mViewPool.returnViewToPool(taskViews.get(i));
+        }
+
+        // Remove all the ignore tasks
+        mIgnoreTasks.clear();
+
+        // If there are no remaining tasks, then just close recents
+        EventBus.getDefault().send(new AllTaskViewsDismissedEvent(
+                R.string.recents_empty_message_dismissed_all));
+    }
+
+    @Override
+    public void onStackTasksUpdated(TaskStack stack) {
+        if (!mFinishedLayoutAfterStackReload) {
+            return;
+        }
+
+        // Update the layout and immediately layout
+        updateLayoutAlgorithm(false /* boundScroll */);
+        relayoutTaskViews(AnimationProps.IMMEDIATE);
+
+        // Rebind all the task views.  This will not trigger new resources to be loaded
+        // unless they have actually changed
+        List<TaskView> taskViews = getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskView tv = taskViews.get(i);
+            bindTaskView(tv, tv.getTask());
+        }
+    }
+
+    /**** ViewPoolConsumer Implementation ****/
+
+    @Override
+    public TaskView createView(Context context) {
+        if (LegacyRecentsImpl.getConfiguration().isGridEnabled) {
+            return (GridTaskView) mInflater.inflate(R.layout.recents_grid_task_view, this, false);
+        } else {
+            return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
+        }
+    }
+
+    @Override
+    public void onReturnViewToPool(TaskView tv) {
+        final Task task = tv.getTask();
+
+        // Unbind the task from the task view
+        unbindTaskView(tv, task);
+
+        // Reset the view properties and view state
+        tv.clearAccessibilityFocus();
+        tv.resetViewProperties();
+        tv.setFocusedState(false, false /* requestViewFocus */);
+        tv.setClipViewInStack(false);
+        if (mScreenPinningEnabled) {
+            tv.hideActionButton(false /* fadeOut */, 0 /* duration */, false /* scaleDown */, null);
+        }
+
+        // Detach the view from the hierarchy
+        detachViewFromParent(tv);
+        // Update the task views list after removing the task view
+        updateTaskViewsList();
+    }
+
+    @Override
+    public void onPickUpViewFromPool(TaskView tv, Task task, boolean isNewView) {
+        // Find the index where this task should be placed in the stack
+        int taskIndex = mStack.indexOfTask(task);
+        int insertIndex = findTaskViewInsertIndex(task, taskIndex);
+
+        // Add/attach the view to the hierarchy
+        if (isNewView) {
+            if (mInMeasureLayout) {
+                // If we are measuring the layout, then just add the view normally as it will be
+                // laid out during the layout pass
+                addView(tv, insertIndex);
+            } else {
+                // Otherwise, this is from a bindVisibleTaskViews() call outside the measure/layout
+                // pass, and we should layout the new child ourselves
+                ViewGroup.LayoutParams params = tv.getLayoutParams();
+                if (params == null) {
+                    params = generateDefaultLayoutParams();
+                }
+                addViewInLayout(tv, insertIndex, params, true /* preventRequestLayout */);
+                measureTaskView(tv);
+                layoutTaskView(true /* changed */, tv);
+            }
+        } else {
+            attachViewToParent(tv, insertIndex, tv.getLayoutParams());
+        }
+        // Update the task views list after adding the new task view
+        updateTaskViewsList();
+
+        // Bind the task view to the new task
+        bindTaskView(tv, task);
+
+        // Set the new state for this view, including the callbacks and view clipping
+        tv.setCallbacks(this);
+        tv.setTouchEnabled(true);
+        tv.setClipViewInStack(true);
+        if (mFocusedTask == task) {
+            tv.setFocusedState(true, false /* requestViewFocus */);
+            if (mStartTimerIndicatorDuration > 0) {
+                // The timer indicator couldn't be started before, so start it now
+                tv.getHeaderView().startFocusTimerIndicator(mStartTimerIndicatorDuration);
+                mStartTimerIndicatorDuration = 0;
+            }
+        }
+
+        // Restore the action button visibility if it is the front most task view
+        if (mScreenPinningEnabled && tv.getTask() == mStack.getFrontMostTask()) {
+            tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
+        }
+    }
+
+    @Override
+    public boolean hasPreferredData(TaskView tv, Task preferredData) {
+        return (tv.getTask() == preferredData);
+    }
+
+    private void bindTaskView(TaskView tv, Task task) {
+        // Rebind the task and request that this task's data be filled into the TaskView
+        tv.onTaskBound(task, mTouchExplorationEnabled, mDisplayOrientation, mDisplayRect);
+
+        // If the doze trigger has already fired, then update the state for this task view
+        if (mUIDozeTrigger.isAsleep() ||
+                useGridLayout() || LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            tv.setNoUserInteractionState();
+        }
+
+        if (task == mPrefetchingTask) {
+            task.notifyTaskDataLoaded(task.thumbnail, task.icon);
+        } else {
+            // Load the task data
+            LegacyRecentsImpl.getTaskLoader().loadTaskData(task);
+        }
+        LegacyRecentsImpl.getTaskLoader().getHighResThumbnailLoader().onTaskVisible(task);
+    }
+
+    private void unbindTaskView(TaskView tv, Task task) {
+        if (task != mPrefetchingTask) {
+            // Report that this task's data is no longer being used
+            LegacyRecentsImpl.getTaskLoader().unloadTaskData(task);
+        }
+        LegacyRecentsImpl.getTaskLoader().getHighResThumbnailLoader().onTaskInvisible(task);
+    }
+
+    private void updatePrefetchingTask(ArrayList<Task> tasks, int frontIndex, int backIndex) {
+        Task t = null;
+        boolean somethingVisible = frontIndex != -1 && backIndex != -1;
+        if (somethingVisible && frontIndex < tasks.size() - 1) {
+            t = tasks.get(frontIndex + 1);
+        }
+        if (mPrefetchingTask != t) {
+            if (mPrefetchingTask != null) {
+                int index = tasks.indexOf(mPrefetchingTask);
+                if (index < backIndex || index > frontIndex) {
+                    LegacyRecentsImpl.getTaskLoader().unloadTaskData(mPrefetchingTask);
+                }
+            }
+            mPrefetchingTask = t;
+            if (t != null) {
+                LegacyRecentsImpl.getTaskLoader().loadTaskData(t);
+            }
+        }
+    }
+
+    private void clearPrefetchingTask() {
+        if (mPrefetchingTask != null) {
+            LegacyRecentsImpl.getTaskLoader().unloadTaskData(mPrefetchingTask);
+        }
+        mPrefetchingTask = null;
+    }
+
+    /**** TaskViewCallbacks Implementation ****/
+
+    @Override
+    public void onTaskViewClipStateChanged(TaskView tv) {
+        if (!mTaskViewsClipDirty) {
+            mTaskViewsClipDirty = true;
+            invalidate();
+        }
+    }
+
+    /**** TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks ****/
+
+    @Override
+    public void onFocusStateChanged(int prevFocusState, int curFocusState) {
+        if (mDeferredTaskViewLayoutAnimation == null) {
+            mUIDozeTrigger.poke();
+            relayoutTaskViewsOnNextFrame(AnimationProps.IMMEDIATE);
+        }
+    }
+
+    /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
+
+    @Override
+    public void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation) {
+        mUIDozeTrigger.poke();
+        if (animation != null) {
+            relayoutTaskViewsOnNextFrame(animation);
+        }
+
+        // In grid layout, the stack action button always remains visible.
+        if (mEnterAnimationComplete && !useGridLayout()) {
+            if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+                // Show stack button when user drags down to show older tasks on low ram devices
+                if (mStack.getTaskCount() > 0 && !mStackActionButtonVisible
+                        && mTouchHandler.mIsScrolling && curScroll - prevScroll < 0) {
+                    // Going up
+                    EventBus.getDefault().send(
+                            new ShowStackActionButtonEvent(true /* translate */));
+                }
+                return;
+            }
+            if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
+                    curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
+                    mStack.getTaskCount() > 0) {
+                EventBus.getDefault().send(new ShowStackActionButtonEvent(true /* translate */));
+            } else if (prevScroll < HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
+                    curScroll >= HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
+                EventBus.getDefault().send(new HideStackActionButtonEvent());
+            }
+        }
+    }
+
+    /**** EventBus Events ****/
+
+    public final void onBusEvent(PackagesChangedEvent event) {
+        // Compute which components need to be removed
+        ArraySet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
+                event.packageName, event.userId);
+
+        // For other tasks, just remove them directly if they no longer exist
+        ArrayList<Task> tasks = mStack.getTasks();
+        for (int i = tasks.size() - 1; i >= 0; i--) {
+            final Task t = tasks.get(i);
+            if (removedComponents.contains(t.key.getComponent())) {
+                final TaskView tv = getChildViewForTask(t);
+                if (tv != null) {
+                    // For visible children, defer removing the task until after the animation
+                    tv.dismissTask();
+                } else {
+                    // Otherwise, remove the task from the stack immediately
+                    mStack.removeTask(t, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
+                }
+            }
+        }
+    }
+
+    public final void onBusEvent(LaunchTaskEvent event) {
+        // Cancel any doze triggers once a task is launched
+        mUIDozeTrigger.stopDozing();
+    }
+
+    public final void onBusEvent(LaunchMostRecentTaskRequestEvent event) {
+        if (mStack.getTaskCount() > 0) {
+            Task mostRecentTask = mStack.getFrontMostTask();
+            launchTask(mostRecentTask);
+        }
+    }
+
+    public final void onBusEvent(ShowStackActionButtonEvent event) {
+        mStackActionButtonVisible = true;
+    }
+
+    public final void onBusEvent(HideStackActionButtonEvent event) {
+        mStackActionButtonVisible = false;
+    }
+
+    public final void onBusEvent(LaunchNextTaskRequestEvent event) {
+        if (!mFinishedLayoutAfterStackReload) {
+            mLaunchNextAfterFirstMeasure = true;
+            return;
+        }
+
+        if (mStack.getTaskCount() == 0) {
+            if (RecentsImpl.getLastPipTime() != -1) {
+                EventBus.getDefault().send(new ExpandPipEvent());
+                MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
+                        "pip");
+            } else {
+                // If there are no tasks, then just hide recents back to home.
+                EventBus.getDefault().send(new HideRecentsEvent(false, true));
+            }
+            return;
+        }
+
+        if (!LegacyRecentsImpl.getConfiguration().getLaunchState().launchedFromPipApp
+                && mStack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime())) {
+            // If the launch task is in the pinned stack, then expand the PiP now
+            EventBus.getDefault().send(new ExpandPipEvent());
+            MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK, "pip");
+        } else {
+            final Task launchTask = mStack.getNextLaunchTarget();
+            if (launchTask != null) {
+                // Defer launching the task until the PiP menu has been dismissed (if it is
+                // showing at all)
+                HidePipMenuEvent hideMenuEvent = new HidePipMenuEvent();
+                hideMenuEvent.addPostAnimationCallback(() -> {
+                    launchTask(launchTask);
+                });
+                EventBus.getDefault().send(hideMenuEvent);
+                MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
+                        launchTask.key.getComponent().toString());
+            }
+        }
+    }
+
+    public final void onBusEvent(LaunchTaskStartedEvent event) {
+        mAnimationHelper.startLaunchTaskAnimation(event.taskView, event.screenPinningRequested,
+                event.getAnimationTrigger());
+    }
+
+    public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
+        // Stop any scrolling
+        mTouchHandler.cancelNonDismissTaskAnimations();
+        mStackScroller.stopScroller();
+        mStackScroller.stopBoundScrollAnimation();
+        cancelDeferredTaskViewLayoutAnimation();
+
+        // Start the task animations
+        mAnimationHelper.startExitToHomeAnimation(event.animated, event.getAnimationTrigger());
+
+        // Dismiss the grid task view focus frame
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+        }
+    }
+
+    public final void onBusEvent(DismissFocusedTaskViewEvent event) {
+        if (mFocusedTask != null) {
+            if (mTaskViewFocusFrame != null) {
+                mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+            }
+            TaskView tv = getChildViewForTask(mFocusedTask);
+            if (tv != null) {
+                tv.dismissTask();
+            }
+            resetFocusedTask(mFocusedTask);
+        }
+    }
+
+    public final void onBusEvent(DismissTaskViewEvent event) {
+        // For visible children, defer removing the task until after the animation
+        mAnimationHelper.startDeleteTaskAnimation(
+                event.taskView, useGridLayout(), event.getAnimationTrigger());
+    }
+
+    public final void onBusEvent(final DismissAllTaskViewsEvent event) {
+        // Keep track of the tasks which will have their data removed
+        ArrayList<Task> tasks = new ArrayList<>(mStack.getTasks());
+        mAnimationHelper.startDeleteAllTasksAnimation(
+                getTaskViews(), useGridLayout(), event.getAnimationTrigger());
+        event.addPostAnimationCallback(new Runnable() {
+            @Override
+            public void run() {
+                // Announce for accessibility
+                announceForAccessibility(getContext().getString(
+                        R.string.accessibility_recents_all_items_dismissed));
+
+                // Remove all tasks and delete the task data for all tasks
+                mStack.removeAllTasks(true /* notifyStackChanges */);
+                for (int i = tasks.size() - 1; i >= 0; i--) {
+                    EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));
+                }
+
+                MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS_ALL);
+            }
+        });
+
+    }
+
+    public final void onBusEvent(TaskViewDismissedEvent event) {
+        // Announce for accessibility
+        announceForAccessibility(getContext().getString(
+                R.string.accessibility_recents_item_dismissed, event.task.title));
+
+        if (useGridLayout() && event.animation != null) {
+            event.animation.setListener(new AnimatorListenerAdapter() {
+                public void onAnimationEnd(Animator animator) {
+                    if (mTaskViewFocusFrame != null) {
+                        // Resize the grid layout task view focus frame
+                        mTaskViewFocusFrame.resize();
+                    }
+                }
+            });
+        }
+
+        // Remove the task from the stack
+        mStack.removeTask(event.task, event.animation, false /* fromDockGesture */);
+        EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
+        if (mStack.getTaskCount() > 0 && LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
+        }
+
+        MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
+                event.task.key.getComponent().toString());
+    }
+
+    public final void onBusEvent(FocusNextTaskViewEvent event) {
+        // Stop any scrolling
+        mStackScroller.stopScroller();
+        mStackScroller.stopBoundScrollAnimation();
+
+        setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false, 0);
+    }
+
+    public final void onBusEvent(FocusPreviousTaskViewEvent event) {
+        // Stop any scrolling
+        mStackScroller.stopScroller();
+        mStackScroller.stopBoundScrollAnimation();
+
+        setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
+    }
+
+    public final void onBusEvent(NavigateTaskViewEvent event) {
+        if (useGridLayout()) {
+            final int taskCount = mStack.getTaskCount();
+            final int currentIndex = mStack.indexOfTask(getFocusedTask());
+            final int nextIndex = mLayoutAlgorithm.mTaskGridLayoutAlgorithm.navigateFocus(taskCount,
+                    currentIndex, event.direction);
+            setFocusedTask(nextIndex, false, true);
+        } else {
+            switch (event.direction) {
+                case UP:
+                    EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
+                    break;
+                case DOWN:
+                    EventBus.getDefault().send(new FocusNextTaskViewEvent());
+                    break;
+            }
+        }
+    }
+
+    public final void onBusEvent(UserInteractionEvent event) {
+        // Poke the doze trigger on user interaction
+        mUIDozeTrigger.poke();
+
+        RecentsDebugFlags debugFlags = LegacyRecentsImpl.getDebugFlags();
+        if (mFocusedTask != null) {
+            TaskView tv = getChildViewForTask(mFocusedTask);
+            if (tv != null) {
+                tv.getHeaderView().cancelFocusTimerIndicator();
+            }
+        }
+    }
+
+    public final void onBusEvent(DragStartEvent event) {
+        // Ensure that the drag task is not animated
+        addIgnoreTask(event.task);
+
+        // Enlarge the dragged view slightly
+        float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
+        mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
+                mTmpTransform, null);
+        mTmpTransform.scale = finalScale;
+        mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
+        mTmpTransform.dimAlpha = 0f;
+        updateTaskViewToTransform(event.taskView, mTmpTransform,
+                new AnimationProps(DRAG_SCALE_DURATION, Interpolators.FAST_OUT_SLOW_IN));
+    }
+
+    public final void onBusEvent(DragDropTargetChangedEvent event) {
+        AnimationProps animation = new AnimationProps(SLOW_SYNC_STACK_DURATION,
+                Interpolators.FAST_OUT_SLOW_IN);
+        boolean ignoreTaskOverrides = false;
+        if (event.dropTarget instanceof DockState) {
+            // Calculate the new task stack bounds that matches the window size that Recents will
+            // have after the drop
+            final DockState dockState = (DockState) event.dropTarget;
+            Rect systemInsets = new Rect(mStableLayoutAlgorithm.mSystemInsets);
+            // When docked, the nav bar insets are consumed and the activity is measured without
+            // insets.  However, the window bounds include the insets, so we need to subtract them
+            // here to make them identical.
+            int height = getMeasuredHeight();
+            height -= systemInsets.bottom;
+            systemInsets.bottom = 0;
+            mStackBounds.set(dockState.getDockedTaskStackBounds(mDisplayRect, getMeasuredWidth(),
+                    height, mDividerSize, systemInsets,
+                    mLayoutAlgorithm, getResources(), mWindowRect));
+            mLayoutAlgorithm.setSystemInsets(systemInsets);
+            mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
+            updateLayoutAlgorithm(true /* boundScroll */);
+            ignoreTaskOverrides = true;
+        } else {
+            // Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging
+            // task view, so add it back to the ignore set after updating the layout
+            removeIgnoreTask(event.task);
+            updateLayoutToStableBounds();
+            addIgnoreTask(event.task);
+        }
+        relayoutTaskViews(animation, null /* animationOverrides */, ignoreTaskOverrides);
+    }
+
+    public final void onBusEvent(final DragEndEvent event) {
+        // We don't handle drops on the dock regions
+        if (event.dropTarget instanceof DockState) {
+            // However, we do need to reset the overrides, since the last state of this task stack
+            // view layout was ignoring task overrides (see DragDropTargetChangedEvent handler)
+            mLayoutAlgorithm.clearUnfocusedTaskOverrides();
+            return;
+        }
+
+        // Restore the task, so that relayout will apply to it below
+        removeIgnoreTask(event.task);
+
+        // Convert the dragging task view back to its final layout-space rect
+        Utilities.setViewFrameFromTranslation(event.taskView);
+
+        // Animate all the tasks into place
+        ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>();
+        animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION,
+                Interpolators.FAST_OUT_SLOW_IN,
+                event.getAnimationTrigger().decrementOnAnimationEnd()));
+        relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION,
+                Interpolators.FAST_OUT_SLOW_IN));
+        event.getAnimationTrigger().increment();
+    }
+
+    public final void onBusEvent(final DragEndCancelledEvent event) {
+        // Restore the pre-drag task stack bounds, including the dragging task view
+        removeIgnoreTask(event.task);
+        updateLayoutToStableBounds();
+
+        // Convert the dragging task view back to its final layout-space rect
+        Utilities.setViewFrameFromTranslation(event.taskView);
+
+        // Animate all the tasks into place
+        ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>();
+        animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION,
+                Interpolators.FAST_OUT_SLOW_IN,
+                event.getAnimationTrigger().decrementOnAnimationEnd()));
+        relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION,
+                Interpolators.FAST_OUT_SLOW_IN));
+        event.getAnimationTrigger().increment();
+    }
+
+    public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
+        mEnterAnimationComplete = true;
+        tryStartEnterAnimation();
+    }
+
+    private void tryStartEnterAnimation() {
+        if (!mStackReloaded || !mFinishedLayoutAfterStackReload || !mEnterAnimationComplete) {
+            return;
+        }
+
+        if (mStack.getTaskCount() > 0) {
+            // Start the task enter animations
+            ReferenceCountedTrigger trigger = new ReferenceCountedTrigger();
+            mAnimationHelper.startEnterAnimation(trigger);
+
+            // Add a runnable to the post animation ref counter to clear all the views
+            trigger.addLastDecrementRunnable(() -> {
+                // Start the dozer to trigger to trigger any UI that shows after a timeout
+                mUIDozeTrigger.startDozing();
+
+                // Update the focused state here -- since we only set the focused task without
+                // requesting view focus in onFirstLayout(), actually request view focus and
+                // animate the focused state if we are alt-tabbing now, after the window enter
+                // animation is completed
+                if (mFocusedTask != null) {
+                    RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+                    RecentsActivityLaunchState launchState = config.getLaunchState();
+                    setFocusedTask(mStack.indexOfTask(mFocusedTask),
+                            false /* scrollToTask */, launchState.launchedWithAltTab);
+                    TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
+                    if (mTouchExplorationEnabled && focusedTaskView != null) {
+                        focusedTaskView.requestAccessibilityFocus();
+                    }
+                }
+            });
+        }
+
+        // This flag is only used to choreograph the enter animation, so we can reset it here
+        mStackReloaded = false;
+    }
+
+    public final void onBusEvent(final MultiWindowStateChangedEvent event) {
+        if (event.inMultiWindow || !event.showDeferredAnimation) {
+            setTasks(event.stack, true /* allowNotifyStackChanges */);
+        } else {
+            // Reset the launch state before handling the multiwindow change
+            RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
+            launchState.reset();
+
+            // Defer until the next frame to ensure that we have received all the system insets, and
+            // initial layout updates
+            event.getAnimationTrigger().increment();
+            post(new Runnable() {
+                @Override
+                public void run() {
+                    // Scroll the stack to the front to see the undocked task
+                    mAnimationHelper.startNewStackScrollAnimation(event.stack,
+                            event.getAnimationTrigger());
+                    event.getAnimationTrigger().decrement();
+                }
+            });
+        }
+    }
+
+    public final void onBusEvent(ConfigurationChangedEvent event) {
+        if (event.fromDeviceOrientationChange) {
+            mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
+            mDisplayRect = LegacyRecentsImpl.getSystemServices().getDisplayRect();
+
+            // Always stop the scroller, otherwise, we may continue setting the stack scroll to the
+            // wrong bounds in the new layout
+            mStackScroller.stopScroller();
+        }
+        reloadOnConfigurationChange();
+
+        // Notify the task views of the configuration change so they can reload their resources
+        if (!event.fromMultiWindow) {
+            mTmpTaskViews.clear();
+            mTmpTaskViews.addAll(getTaskViews());
+            mTmpTaskViews.addAll(mViewPool.getViews());
+            int taskViewCount = mTmpTaskViews.size();
+            for (int i = 0; i < taskViewCount; i++) {
+                mTmpTaskViews.get(i).onConfigurationChanged();
+            }
+        }
+
+        // Update the Clear All button in case we're switching in or out of grid layout.
+        updateStackActionButtonVisibility();
+
+        // Trigger a new layout and update to the initial state if necessary. When entering split
+        // screen, the multi-window configuration change event can happen after the stack is already
+        // reloaded (but pending measure/layout), in this case, do not override the intiial state
+        // and just wait for the upcoming measure/layout pass.
+        if (event.fromMultiWindow && mInitialState == INITIAL_STATE_UPDATE_NONE) {
+            mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY;
+            requestLayout();
+        } else if (event.fromDeviceOrientationChange) {
+            mInitialState = INITIAL_STATE_UPDATE_ALL;
+            requestLayout();
+        }
+    }
+
+    public final void onBusEvent(RecentsGrowingEvent event) {
+        mResetToInitialStateWhenResized = true;
+    }
+
+    public final void onBusEvent(RecentsVisibilityChangedEvent event) {
+        if (!event.visible) {
+            if (mTaskViewFocusFrame != null) {
+                mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+            }
+
+            List<TaskView> taskViews = new ArrayList<>(getTaskViews());
+            for (int i = 0; i < taskViews.size(); i++) {
+                mViewPool.returnViewToPool(taskViews.get(i));
+            }
+            clearPrefetchingTask();
+
+            // We can not reset mEnterAnimationComplete in onReload() because when docking the top
+            // task, we can receive the enter animation callback before onReload(), so reset it
+            // here onces Recents is not visible
+            mEnterAnimationComplete = false;
+        }
+    }
+
+    public final void onBusEvent(ActivityPinnedEvent event) {
+        // If an activity enters PiP while Recents is open, remove the stack task associated with
+        // the new PiP task
+        Task removeTask = mStack.findTaskWithId(event.taskId);
+        if (removeTask != null) {
+            // In this case, we remove the task, but if the last task is removed, don't dismiss
+            // Recents to home
+            mStack.removeTask(removeTask, AnimationProps.IMMEDIATE, false /* fromDockGesture */,
+                    false /* dismissRecentsIfAllRemoved */);
+        }
+        updateLayoutAlgorithm(false /* boundScroll */);
+        updateToInitialState();
+    }
+
+    public void reloadOnConfigurationChange() {
+        mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
+        mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
+    }
+
+    /**
+     * Returns the insert index for the task in the current set of task views. If the given task
+     * is already in the task view list, then this method returns the insert index assuming it
+     * is first removed at the previous index.
+     *
+     * @param task the task we are finding the index for
+     * @param taskIndex the index of the task in the stack
+     */
+    private int findTaskViewInsertIndex(Task task, int taskIndex) {
+        if (taskIndex != -1) {
+            List<TaskView> taskViews = getTaskViews();
+            boolean foundTaskView = false;
+            int taskViewCount = taskViews.size();
+            for (int i = 0; i < taskViewCount; i++) {
+                Task tvTask = taskViews.get(i).getTask();
+                if (tvTask == task) {
+                    foundTaskView = true;
+                } else if (taskIndex < mStack.indexOfTask(tvTask)) {
+                    if (foundTaskView) {
+                        return i - 1;
+                    } else {
+                        return i;
+                    }
+                }
+            }
+        }
+        return -1;
+    }
+
+    private void launchTask(Task task) {
+        // Stop all animations
+        cancelAllTaskViewAnimations();
+
+        float curScroll = mStackScroller.getStackScroll();
+        float targetScroll = mLayoutAlgorithm.getStackScrollForTaskAtInitialOffset(task);
+        float absScrollDiff = Math.abs(targetScroll - curScroll);
+        if (getChildViewForTask(task) == null || absScrollDiff > 0.35f) {
+            int duration = (int) (LAUNCH_NEXT_SCROLL_BASE_DURATION +
+                    absScrollDiff * LAUNCH_NEXT_SCROLL_INCR_DURATION);
+            mStackScroller.animateScroll(targetScroll,
+                    duration, new Runnable() {
+                        @Override
+                        public void run() {
+                            EventBus.getDefault().send(new LaunchTaskEvent(
+                                    getChildViewForTask(task), task, null,
+                                    false /* screenPinningRequested */));
+                        }
+                    });
+        } else {
+            EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(task), task, null,
+                    false /* screenPinningRequested */));
+        }
+    }
+
+    /**
+     * Check whether we should use the grid layout.
+     */
+    public boolean useGridLayout() {
+        return mLayoutAlgorithm.useGridLayout();
+    }
+
+    /**
+     * Reads current system flags related to accessibility and screen pinning.
+     */
+    private void readSystemFlags() {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
+        mScreenPinningEnabled = ActivityManagerWrapper.getInstance().isScreenPinningEnabled()
+                && !ActivityManagerWrapper.getInstance().isLockToAppActive();
+    }
+
+    private void updateStackActionButtonVisibility() {
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            return;
+        }
+
+        // Always show the button in grid layout.
+        if (useGridLayout() ||
+                (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
+                        mStack.getTaskCount() > 0)) {
+            EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
+        } else {
+            EventBus.getDefault().send(new HideStackActionButtonEvent());
+        }
+    }
+
+    /**
+     * Returns the task to focus given the current launch state.
+     */
+    private int getInitialFocusTaskIndex(RecentsActivityLaunchState launchState, int numTasks,
+            boolean useGridLayout) {
+        if (launchState.launchedFromApp) {
+            if (useGridLayout) {
+                // If coming from another app to the grid layout, focus the front most task
+                return numTasks - 1;
+            }
+
+            // If coming from another app, focus the next task
+            return Math.max(0, numTasks - 2);
+        } else {
+            // If coming from home, focus the front most task
+            return numTasks - 1;
+        }
+    }
+
+    /**
+     * Updates {@param transforms} to be the same size as {@param tasks}.
+     */
+    private void matchTaskListSize(List<Task> tasks, List<TaskViewTransform> transforms) {
+        // We can reuse the task transforms where possible to reduce object allocation
+        int taskTransformCount = transforms.size();
+        int taskCount = tasks.size();
+        if (taskTransformCount < taskCount) {
+            // If there are less transforms than tasks, then add as many transforms as necessary
+            for (int i = taskTransformCount; i < taskCount; i++) {
+                transforms.add(new TaskViewTransform());
+            }
+        } else if (taskTransformCount > taskCount) {
+            // If there are more transforms than tasks, then just subset the transform list
+            transforms.subList(taskCount, taskTransformCount).clear();
+        }
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "  ";
+        String id = Integer.toHexString(System.identityHashCode(this));
+
+        writer.print(prefix); writer.print(TAG);
+        writer.print(" hasDefRelayout=");
+        writer.print(mDeferredTaskViewLayoutAnimation != null ? "Y" : "N");
+        writer.print(" clipDirty="); writer.print(mTaskViewsClipDirty ? "Y" : "N");
+        writer.print(" awaitingStackReload="); writer.print(mFinishedLayoutAfterStackReload ? "Y" : "N");
+        writer.print(" initialState="); writer.print(mInitialState);
+        writer.print(" inMeasureLayout="); writer.print(mInMeasureLayout ? "Y" : "N");
+        writer.print(" enterAnimCompleted="); writer.print(mEnterAnimationComplete ? "Y" : "N");
+        writer.print(" touchExplorationOn="); writer.print(mTouchExplorationEnabled ? "Y" : "N");
+        writer.print(" screenPinningOn="); writer.print(mScreenPinningEnabled ? "Y" : "N");
+        writer.print(" numIgnoreTasks="); writer.print(mIgnoreTasks.size());
+        writer.print(" numViewPool="); writer.print(mViewPool.getViews().size());
+        writer.print(" stableStackBounds="); writer.print(
+                Utilities.dumpRect(mStableStackBounds));
+        writer.print(" stackBounds="); writer.print(
+                Utilities.dumpRect(mStackBounds));
+        writer.print(" stableWindow="); writer.print(
+                Utilities.dumpRect(mStableWindowRect));
+        writer.print(" window="); writer.print(Utilities.dumpRect(mWindowRect));
+        writer.print(" display="); writer.print(Utilities.dumpRect(mDisplayRect));
+        writer.print(" orientation="); writer.print(mDisplayOrientation);
+        writer.print(" [0x"); writer.print(id); writer.print("]");
+        writer.println();
+
+        if (mFocusedTask != null) {
+            writer.print(innerPrefix);
+            writer.print("Focused task: ");
+            mFocusedTask.dump("", writer);
+        }
+
+        int numTaskViews = mTaskViews.size();
+        for (int i = 0; i < numTaskViews; i++) {
+            mTaskViews.get(i).dump(innerPrefix, writer);
+        }
+
+        mLayoutAlgorithm.dump(innerPrefix, writer);
+        mStackScroller.dump(innerPrefix, writer);
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewScroller.java
new file mode 100644
index 0000000..42efe59
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.util.FloatProperty;
+import android.util.Log;
+import android.util.Property;
+import android.view.ViewConfiguration;
+import android.view.ViewDebug;
+import android.widget.OverScroller;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.utilities.AnimationProps;
+import com.android.systemui.recents.utilities.Utilities;
+import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+import java.io.PrintWriter;
+
+/* The scrolling logic for a TaskStackView */
+public class TaskStackViewScroller {
+
+    private static final String TAG = "TaskStackViewScroller";
+    private static final boolean DEBUG = false;
+
+    public interface TaskStackViewScrollerCallbacks {
+        void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation);
+    }
+
+    /**
+     * A Property wrapper around the <code>stackScroll</code> functionality handled by the
+     * {@link #setStackScroll(float)} and
+     * {@link #getStackScroll()} methods.
+     */
+    private static final Property<TaskStackViewScroller, Float> STACK_SCROLL =
+            new FloatProperty<TaskStackViewScroller>("stackScroll") {
+                @Override
+                public void setValue(TaskStackViewScroller object, float value) {
+                    object.setStackScroll(value);
+                }
+
+                @Override
+                public Float get(TaskStackViewScroller object) {
+                    return object.getStackScroll();
+                }
+            };
+
+    Context mContext;
+    TaskStackLayoutAlgorithm mLayoutAlgorithm;
+    TaskStackViewScrollerCallbacks mCb;
+
+    @ViewDebug.ExportedProperty(category="recents")
+    float mStackScrollP;
+    @ViewDebug.ExportedProperty(category="recents")
+    float mLastDeltaP = 0f;
+    float mFlingDownScrollP;
+    int mFlingDownY;
+
+    OverScroller mScroller;
+    ObjectAnimator mScrollAnimator;
+    float mFinalAnimatedScroll;
+
+    final FlingAnimationUtils mFlingAnimationUtils;
+
+    public TaskStackViewScroller(Context context, TaskStackViewScrollerCallbacks cb,
+            TaskStackLayoutAlgorithm layoutAlgorithm) {
+        mContext = context;
+        mCb = cb;
+        mScroller = new OverScroller(context);
+        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            mScroller.setFriction(0.06f);
+        }
+        mLayoutAlgorithm = layoutAlgorithm;
+        mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
+    }
+
+    /** Resets the task scroller. */
+    void reset() {
+        mStackScrollP = 0f;
+        mLastDeltaP = 0f;
+    }
+
+    void resetDeltaScroll() {
+        mLastDeltaP = 0f;
+    }
+
+    /** Gets the current stack scroll */
+    public float getStackScroll() {
+        return mStackScrollP;
+    }
+
+    /**
+     * Sets the current stack scroll immediately.
+     */
+    public void setStackScroll(float s) {
+        setStackScroll(s, AnimationProps.IMMEDIATE);
+    }
+
+    /**
+     * Sets the current stack scroll immediately, and returns the difference between the target
+     * scroll and the actual scroll after accounting for the effect on the focus state.
+     */
+    public float setDeltaStackScroll(float downP, float deltaP) {
+        float targetScroll = downP + deltaP;
+        float newScroll = mLayoutAlgorithm.updateFocusStateOnScroll(downP + mLastDeltaP, targetScroll,
+                mStackScrollP);
+        setStackScroll(newScroll, AnimationProps.IMMEDIATE);
+        mLastDeltaP = deltaP;
+        return newScroll - targetScroll;
+    }
+
+    /**
+     * Sets the current stack scroll, but indicates to the callback the preferred animation to
+     * update to this new scroll.
+     */
+    public void setStackScroll(float newScroll, AnimationProps animation) {
+        float prevScroll = mStackScrollP;
+        mStackScrollP = newScroll;
+        if (mCb != null) {
+            mCb.onStackScrollChanged(prevScroll, mStackScrollP, animation);
+        }
+    }
+
+    /**
+     * Sets the current stack scroll to the initial state when you first enter recents.
+     * @return whether the stack progress changed.
+     */
+    public boolean setStackScrollToInitialState() {
+        float prevScroll = mStackScrollP;
+        setStackScroll(mLayoutAlgorithm.mInitialScrollP);
+        return Float.compare(prevScroll, mStackScrollP) != 0;
+    }
+
+    /**
+     * Starts a fling that is coordinated with the {@link TaskStackViewTouchHandler}.
+     */
+    public void fling(float downScrollP, int downY, int y, int velY, int minY, int maxY,
+            int overscroll) {
+        if (DEBUG) {
+            Log.d(TAG, "fling: " + downScrollP + ", downY: " + downY + ", y: " + y +
+                    ", velY: " + velY + ", minY: " + minY + ", maxY: " + maxY);
+        }
+        mFlingDownScrollP = downScrollP;
+        mFlingDownY = downY;
+        mScroller.fling(0, y, 0, velY, 0, 0, minY, maxY, 0, overscroll);
+    }
+
+    /** Bounds the current scroll if necessary */
+    public boolean boundScroll() {
+        float curScroll = getStackScroll();
+        float newScroll = getBoundedStackScroll(curScroll);
+        if (Float.compare(newScroll, curScroll) != 0) {
+            setStackScroll(newScroll);
+            return true;
+        }
+        return false;
+    }
+
+    /** Returns the bounded stack scroll */
+    float getBoundedStackScroll(float scroll) {
+        return Utilities.clamp(scroll, mLayoutAlgorithm.mMinScrollP, mLayoutAlgorithm.mMaxScrollP);
+    }
+
+    /** Returns the amount that the absolute value of how much the scroll is out of bounds. */
+    float getScrollAmountOutOfBounds(float scroll) {
+        if (scroll < mLayoutAlgorithm.mMinScrollP) {
+            return Math.abs(scroll - mLayoutAlgorithm.mMinScrollP);
+        } else if (scroll > mLayoutAlgorithm.mMaxScrollP) {
+            return Math.abs(scroll - mLayoutAlgorithm.mMaxScrollP);
+        }
+        return 0f;
+    }
+
+    /** Returns whether the specified scroll is out of bounds */
+    boolean isScrollOutOfBounds() {
+        return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0;
+    }
+
+    /**
+     * Scrolls the closest task and snaps into place. Only used in recents for low ram devices.
+     * @param velocity of scroll
+     */
+    void scrollToClosestTask(int velocity) {
+        float stackScroll = getStackScroll();
+
+        // Skip if not in low ram layout and if the scroll is out of min and max bounds
+        if (!LegacyRecentsImpl.getConfiguration().isLowRamDevice || stackScroll < mLayoutAlgorithm.mMinScrollP
+                || stackScroll > mLayoutAlgorithm.mMaxScrollP) {
+            return;
+        }
+        TaskStackLowRamLayoutAlgorithm algorithm = mLayoutAlgorithm.mTaskStackLowRamLayoutAlgorithm;
+
+        float flingThreshold = ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity();
+        if (Math.abs(velocity) > flingThreshold) {
+            int minY = algorithm.percentageToScroll(mLayoutAlgorithm.mMinScrollP);
+            int maxY = algorithm.percentageToScroll(mLayoutAlgorithm.mMaxScrollP);
+
+            // Calculate the fling and snap to closest task from final y position, computeScroll()
+            // never runs when cancelled with animateScroll() and the overscroll is not calculated
+            // here
+            fling(0 /* downScrollP */, 0 /* downY */, algorithm.percentageToScroll(stackScroll),
+                    -velocity, minY, maxY, 0 /* overscroll */);
+            float pos = algorithm.scrollToPercentage(mScroller.getFinalY());
+
+            float newScrollP = algorithm.getClosestTaskP(pos, mLayoutAlgorithm.mNumStackTasks,
+                    velocity);
+            ValueAnimator animator = ObjectAnimator.ofFloat(stackScroll, newScrollP);
+            mFlingAnimationUtils.apply(animator, algorithm.percentageToScroll(stackScroll),
+                    algorithm.percentageToScroll(newScrollP), velocity);
+            animateScroll(newScrollP, (int) animator.getDuration(), animator.getInterpolator(),
+                    null /* postRunnable */);
+        } else {
+            float newScrollP = algorithm.getClosestTaskP(stackScroll,
+                    mLayoutAlgorithm.mNumStackTasks, velocity);
+            animateScroll(newScrollP, 300, Interpolators.ACCELERATE_DECELERATE,
+                    null /* postRunnable */);
+        }
+    }
+
+    /** Animates the stack scroll into bounds */
+    ObjectAnimator animateBoundScroll() {
+        // TODO: Take duration for snap back
+        float curScroll = getStackScroll();
+        float newScroll = getBoundedStackScroll(curScroll);
+        if (Float.compare(newScroll, curScroll) != 0) {
+            // Start a new scroll animation
+            animateScroll(newScroll, null /* postScrollRunnable */);
+        }
+        return mScrollAnimator;
+    }
+
+    /** Animates the stack scroll */
+    void animateScroll(float newScroll, final Runnable postRunnable) {
+        int duration = mContext.getResources().getInteger(
+                R.integer.recents_animate_task_stack_scroll_duration);
+        animateScroll(newScroll, duration, postRunnable);
+    }
+
+    /** Animates the stack scroll */
+    void animateScroll(float newScroll, int duration, final Runnable postRunnable) {
+        animateScroll(newScroll, duration, Interpolators.LINEAR_OUT_SLOW_IN, postRunnable);
+    }
+
+    /** Animates the stack scroll with time interpolator */
+    void animateScroll(float newScroll, int duration, TimeInterpolator interpolator,
+            final Runnable postRunnable) {
+        ObjectAnimator an = ObjectAnimator.ofFloat(this, STACK_SCROLL, getStackScroll(), newScroll);
+        an.setDuration(duration);
+        an.setInterpolator(interpolator);
+        animateScroll(newScroll, an, postRunnable);
+    }
+
+    /** Animates the stack scroll with animator */
+    private void animateScroll(float newScroll, ObjectAnimator animator,
+            final Runnable postRunnable) {
+        // Finish any current scrolling animations
+        if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
+            setStackScroll(mFinalAnimatedScroll);
+            mScroller.forceFinished(true);
+        }
+        stopScroller();
+        stopBoundScrollAnimation();
+
+        if (Float.compare(mStackScrollP, newScroll) != 0) {
+            mFinalAnimatedScroll = newScroll;
+            mScrollAnimator = animator;
+            mScrollAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (postRunnable != null) {
+                        postRunnable.run();
+                    }
+                    mScrollAnimator.removeAllListeners();
+                }
+            });
+            mScrollAnimator.start();
+        } else {
+            if (postRunnable != null) {
+                postRunnable.run();
+            }
+        }
+    }
+
+    /** Aborts any current stack scrolls */
+    void stopBoundScrollAnimation() {
+        Utilities.cancelAnimationWithoutCallbacks(mScrollAnimator);
+    }
+
+    /**** OverScroller ****/
+
+    /** Called from the view draw, computes the next scroll. */
+    boolean computeScroll() {
+        if (mScroller.computeScrollOffset()) {
+            float deltaP = mLayoutAlgorithm.getDeltaPForY(mFlingDownY, mScroller.getCurrY());
+            mFlingDownScrollP += setDeltaStackScroll(mFlingDownScrollP, deltaP);
+            if (DEBUG) {
+                Log.d(TAG, "computeScroll: " + (mFlingDownScrollP + deltaP));
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /** Returns whether the overscroller is scrolling. */
+    boolean isScrolling() {
+        return !mScroller.isFinished();
+    }
+
+    float getScrollVelocity() {
+        return mScroller.getCurrVelocity();
+    }
+
+    /** Stops the scroller and any current fling. */
+    void stopScroller() {
+        if (!mScroller.isFinished()) {
+            mScroller.abortAnimation();
+        }
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        writer.print(prefix); writer.print(TAG);
+        writer.print(" stackScroll:"); writer.print(mStackScrollP);
+        writer.println();
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
new file mode 100644
index 0000000..dd6926c
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -0,0 +1,705 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Path;
+import android.util.ArrayMap;
+import android.util.MutableBoolean;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewDebug;
+import android.view.ViewParent;
+import android.view.animation.Interpolator;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.SwipeHelper;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
+import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
+import com.android.systemui.recents.misc.FreePathInterpolator;
+import com.android.systemui.recents.utilities.AnimationProps;
+import com.android.systemui.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Handles touch events for a TaskStackView.
+ */
+class TaskStackViewTouchHandler implements SwipeHelper.Callback {
+
+    private static final int INACTIVE_POINTER_ID = -1;
+    private static final float CHALLENGING_SWIPE_ESCAPE_VELOCITY = 800f; // dp/sec
+    // The min overscroll is the amount of task progress overscroll we want / the max overscroll
+    // curve value below
+    private static final float MAX_OVERSCROLL = 0.7f / 0.3f;
+    private static final Interpolator OVERSCROLL_INTERP;
+    static {
+        Path OVERSCROLL_PATH = new Path();
+        OVERSCROLL_PATH.moveTo(0, 0);
+        OVERSCROLL_PATH.cubicTo(0.2f, 0.175f, 0.25f, 0.3f, 1f, 0.3f);
+        OVERSCROLL_INTERP = new FreePathInterpolator(OVERSCROLL_PATH);
+    }
+
+    Context mContext;
+    TaskStackView mSv;
+    TaskStackViewScroller mScroller;
+    VelocityTracker mVelocityTracker;
+    FlingAnimationUtils mFlingAnimUtils;
+    ValueAnimator mScrollFlingAnimator;
+
+    @ViewDebug.ExportedProperty(category="recents")
+    boolean mIsScrolling;
+    float mDownScrollP;
+    int mDownX, mDownY;
+    int mLastY;
+    int mActivePointerId = INACTIVE_POINTER_ID;
+    int mOverscrollSize;
+    TaskView mActiveTaskView = null;
+
+    int mMinimumVelocity;
+    int mMaximumVelocity;
+    // The scroll touch slop is used to calculate when we start scrolling
+    int mScrollTouchSlop;
+    // Used to calculate when a tap is outside a task view rectangle.
+    final int mWindowTouchSlop;
+
+    private final StackViewScrolledEvent mStackViewScrolledEvent = new StackViewScrolledEvent();
+
+    // The current and final set of task transforms, sized to match the list of tasks in the stack
+    private ArrayList<Task> mCurrentTasks = new ArrayList<>();
+    private ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
+    private ArrayList<TaskViewTransform> mFinalTaskTransforms = new ArrayList<>();
+    private ArrayMap<View, Animator> mSwipeHelperAnimations = new ArrayMap<>();
+    private TaskViewTransform mTmpTransform = new TaskViewTransform();
+    private float mTargetStackScroll;
+
+    SwipeHelper mSwipeHelper;
+    boolean mInterceptedBySwipeHelper;
+
+    public TaskStackViewTouchHandler(Context context, TaskStackView sv,
+            TaskStackViewScroller scroller) {
+        Resources res = context.getResources();
+        ViewConfiguration configuration = ViewConfiguration.get(context);
+        mContext = context;
+        mSv = sv;
+        mScroller = scroller;
+        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
+        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+        mScrollTouchSlop = configuration.getScaledTouchSlop();
+        mWindowTouchSlop = configuration.getScaledWindowTouchSlop();
+        mFlingAnimUtils = new FlingAnimationUtils(context, 0.2f);
+        mOverscrollSize = res.getDimensionPixelSize(R.dimen.recents_fling_overscroll_distance);
+        mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, context) {
+            @Override
+            protected float getSize(View v) {
+                return getScaledDismissSize();
+            }
+
+            @Override
+            protected void prepareDismissAnimation(View v, Animator anim) {
+                mSwipeHelperAnimations.put(v, anim);
+            }
+
+            @Override
+            protected void prepareSnapBackAnimation(View v, Animator anim) {
+                anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+                mSwipeHelperAnimations.put(v, anim);
+            }
+
+            @Override
+            protected float getUnscaledEscapeVelocity() {
+                return CHALLENGING_SWIPE_ESCAPE_VELOCITY;
+            }
+
+            @Override
+            protected long getMaxEscapeAnimDuration() {
+                return 700;
+            }
+        };
+        mSwipeHelper.setDisableHardwareLayers(true);
+    }
+
+    /** Velocity tracker helpers */
+    void initOrResetVelocityTracker() {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        } else {
+            mVelocityTracker.clear();
+        }
+    }
+    void recycleVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+    }
+
+    /** Touch preprocessing for handling below */
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        // Pass through to swipe helper if we are swiping
+        mInterceptedBySwipeHelper = isSwipingEnabled() && mSwipeHelper.onInterceptTouchEvent(ev);
+        if (mInterceptedBySwipeHelper) {
+            return true;
+        }
+
+        return handleTouchEvent(ev);
+    }
+
+    /** Handles touch events once we have intercepted them */
+    public boolean onTouchEvent(MotionEvent ev) {
+        // Pass through to swipe helper if we are swiping
+        if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
+            return true;
+        }
+
+        handleTouchEvent(ev);
+        return true;
+    }
+
+    /**
+     * Finishes all scroll-fling and non-dismissing animations currently running.
+     */
+    public void cancelNonDismissTaskAnimations() {
+        Utilities.cancelAnimationWithoutCallbacks(mScrollFlingAnimator);
+        if (!mSwipeHelperAnimations.isEmpty()) {
+            // For the non-dismissing tasks, freeze the position into the task overrides
+            List<TaskView> taskViews = mSv.getTaskViews();
+            for (int i = taskViews.size() - 1; i >= 0; i--) {
+                TaskView tv = taskViews.get(i);
+
+                if (mSv.isIgnoredTask(tv.getTask())) {
+                    continue;
+                }
+
+                tv.cancelTransformAnimation();
+                mSv.getStackAlgorithm().addUnfocusedTaskOverride(tv, mTargetStackScroll);
+            }
+            mSv.getStackAlgorithm().setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
+            // Update the scroll to the final scroll position from onBeginDrag()
+            mSv.getScroller().setStackScroll(mTargetStackScroll, null);
+
+            mSwipeHelperAnimations.clear();
+        }
+        mActiveTaskView = null;
+    }
+
+    private boolean handleTouchEvent(MotionEvent ev) {
+        // Short circuit if we have no children
+        if (mSv.getTaskViews().size() == 0) {
+            return false;
+        }
+
+        final TaskStackLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm;
+        int action = ev.getAction();
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN: {
+                // Stop the current scroll if it is still flinging
+                mScroller.stopScroller();
+                mScroller.stopBoundScrollAnimation();
+                mScroller.resetDeltaScroll();
+                cancelNonDismissTaskAnimations();
+                mSv.cancelDeferredTaskViewLayoutAnimation();
+
+                // Save the touch down info
+                mDownX = (int) ev.getX();
+                mDownY = (int) ev.getY();
+                mLastY = mDownY;
+                mDownScrollP = mScroller.getStackScroll();
+                mActivePointerId = ev.getPointerId(0);
+                mActiveTaskView = findViewAtPoint(mDownX, mDownY);
+
+                // Initialize the velocity tracker
+                initOrResetVelocityTracker();
+                mVelocityTracker.addMovement(ev);
+                break;
+            }
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                final int index = ev.getActionIndex();
+                mActivePointerId = ev.getPointerId(index);
+                mDownX = (int) ev.getX(index);
+                mDownY = (int) ev.getY(index);
+                mLastY = mDownY;
+                mDownScrollP = mScroller.getStackScroll();
+                mScroller.resetDeltaScroll();
+                mVelocityTracker.addMovement(ev);
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: {
+                int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (activePointerIndex == -1) {
+                    break;
+                }
+                int y = (int) ev.getY(activePointerIndex);
+                int x = (int) ev.getX(activePointerIndex);
+                if (!mIsScrolling) {
+                    int yDiff = Math.abs(y - mDownY);
+                    int xDiff = Math.abs(x - mDownX);
+                    if (Math.abs(y - mDownY) > mScrollTouchSlop && yDiff > xDiff) {
+                        mIsScrolling = true;
+                        float stackScroll = mScroller.getStackScroll();
+                        List<TaskView> taskViews = mSv.getTaskViews();
+                        for (int i = taskViews.size() - 1; i >= 0; i--) {
+                            layoutAlgorithm.addUnfocusedTaskOverride(taskViews.get(i).getTask(),
+                                    stackScroll);
+                        }
+                        layoutAlgorithm.setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
+
+                        // Disallow parents from intercepting touch events
+                        final ViewParent parent = mSv.getParent();
+                        if (parent != null) {
+                            parent.requestDisallowInterceptTouchEvent(true);
+                        }
+
+                        MetricsLogger.action(mSv.getContext(), MetricsEvent.OVERVIEW_SCROLL);
+                        mLastY = mDownY = y;
+                    }
+                }
+                if (mIsScrolling) {
+                    // If we just move linearly on the screen, then that would map to 1/arclength
+                    // of the curve, so just move the scroll proportional to that
+                    float deltaP = layoutAlgorithm.getDeltaPForY(mDownY, y);
+
+                    // Modulate the overscroll to prevent users from pulling the stack too far
+                    float minScrollP = layoutAlgorithm.mMinScrollP;
+                    float maxScrollP = layoutAlgorithm.mMaxScrollP;
+                    float curScrollP = mDownScrollP + deltaP;
+                    if (curScrollP < minScrollP || curScrollP > maxScrollP) {
+                        float clampedScrollP = Utilities.clamp(curScrollP, minScrollP, maxScrollP);
+                        float overscrollP = (curScrollP - clampedScrollP);
+                        float maxOverscroll = LegacyRecentsImpl.getConfiguration().isLowRamDevice
+                                ? layoutAlgorithm.mTaskStackLowRamLayoutAlgorithm.getMaxOverscroll()
+                                : MAX_OVERSCROLL;
+                        float overscrollX = Math.abs(overscrollP) / maxOverscroll;
+                        float interpX = OVERSCROLL_INTERP.getInterpolation(overscrollX);
+                        curScrollP = clampedScrollP + Math.signum(overscrollP) *
+                                (interpX * maxOverscroll);
+                    }
+                    mDownScrollP += mScroller.setDeltaStackScroll(mDownScrollP,
+                            curScrollP - mDownScrollP);
+                    mStackViewScrolledEvent.updateY(y - mLastY);
+                    EventBus.getDefault().send(mStackViewScrolledEvent);
+                }
+
+                mLastY = y;
+                mVelocityTracker.addMovement(ev);
+                break;
+            }
+            case MotionEvent.ACTION_POINTER_UP: {
+                int pointerIndex = ev.getActionIndex();
+                int pointerId = ev.getPointerId(pointerIndex);
+                if (pointerId == mActivePointerId) {
+                    // Select a new active pointer id and reset the motion state
+                    final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
+                    mActivePointerId = ev.getPointerId(newPointerIndex);
+                    mDownX = (int) ev.getX(pointerIndex);
+                    mDownY = (int) ev.getY(pointerIndex);
+                    mLastY = mDownY;
+                    mDownScrollP = mScroller.getStackScroll();
+                }
+                mVelocityTracker.addMovement(ev);
+                break;
+            }
+            case MotionEvent.ACTION_UP: {
+                mVelocityTracker.addMovement(ev);
+                mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                int y = (int) ev.getY(activePointerIndex);
+                int velocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
+                if (mIsScrolling) {
+                    if (mScroller.isScrollOutOfBounds()) {
+                        mScroller.animateBoundScroll();
+                    } else if (Math.abs(velocity) > mMinimumVelocity &&
+                            !LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+                        float minY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
+                                layoutAlgorithm.mMaxScrollP);
+                        float maxY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
+                                layoutAlgorithm.mMinScrollP);
+                        mScroller.fling(mDownScrollP, mDownY, y, velocity, (int) minY, (int) maxY,
+                                mOverscrollSize);
+                        mSv.invalidate();
+                    }
+
+                    // Reset the focused task after the user has scrolled, but we have no scrolling
+                    // in grid layout and therefore we don't want to reset the focus there.
+                    if (!mSv.mTouchExplorationEnabled && !mSv.useGridLayout()) {
+                        if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+                            mScroller.scrollToClosestTask(velocity);
+                        } else {
+                            mSv.resetFocusedTask(mSv.getFocusedTask());
+                        }
+                    }
+                } else if (mActiveTaskView == null) {
+                    // This tap didn't start on a task.
+                    maybeHideRecentsFromBackgroundTap((int) ev.getX(), (int) ev.getY());
+                }
+
+                mActivePointerId = INACTIVE_POINTER_ID;
+                mIsScrolling = false;
+                recycleVelocityTracker();
+                break;
+            }
+            case MotionEvent.ACTION_CANCEL: {
+                mActivePointerId = INACTIVE_POINTER_ID;
+                mIsScrolling = false;
+                recycleVelocityTracker();
+                break;
+            }
+        }
+        return mIsScrolling;
+    }
+
+    /** Hides recents if the up event at (x, y) is a tap on the background area. */
+    void maybeHideRecentsFromBackgroundTap(int x, int y) {
+        // Ignore the up event if it's too far from its start position. The user might have been
+        // trying to scroll or swipe.
+        int dx = Math.abs(mDownX - x);
+        int dy = Math.abs(mDownY - y);
+        if (dx > mScrollTouchSlop || dy > mScrollTouchSlop) {
+            return;
+        }
+
+        // Shift the tap position toward the center of the task stack and check to see if it would
+        // have hit a view. The user might have tried to tap on a task and missed slightly.
+        int shiftedX = x;
+        if (x > (mSv.getRight() - mSv.getLeft()) / 2) {
+            shiftedX -= mWindowTouchSlop;
+        } else {
+            shiftedX += mWindowTouchSlop;
+        }
+        if (findViewAtPoint(shiftedX, y) != null) {
+            return;
+        }
+
+        // Disallow tapping above and below the stack to dismiss recents
+        if (x > mSv.mLayoutAlgorithm.mStackRect.left && x < mSv.mLayoutAlgorithm.mStackRect.right) {
+            return;
+        }
+
+        // The user intentionally tapped on the background, which is like a tap on the "desktop".
+        // Hide recents and transition to the launcher.
+        EventBus.getDefault().send(new HideRecentsEvent(false, true));
+    }
+
+    /** Handles generic motion events */
+    public boolean onGenericMotionEvent(MotionEvent ev) {
+        if ((ev.getSource() & InputDevice.SOURCE_CLASS_POINTER) ==
+                InputDevice.SOURCE_CLASS_POINTER) {
+            int action = ev.getAction();
+            switch (action & MotionEvent.ACTION_MASK) {
+                case MotionEvent.ACTION_SCROLL:
+                    // Find the front most task and scroll the next task to the front
+                    float vScroll = ev.getAxisValue(MotionEvent.AXIS_VSCROLL);
+                    if (vScroll > 0) {
+                        mSv.setRelativeFocusedTask(true, true /* stackTasksOnly */,
+                                false /* animated */);
+                    } else {
+                        mSv.setRelativeFocusedTask(false, true /* stackTasksOnly */,
+                                false /* animated */);
+                    }
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**** SwipeHelper Implementation ****/
+
+    @Override
+    public View getChildAtPosition(MotionEvent ev) {
+        TaskView tv = findViewAtPoint((int) ev.getX(), (int) ev.getY());
+        if (tv != null && canChildBeDismissed(tv)) {
+            return tv;
+        }
+        return null;
+    }
+
+    @Override
+    public boolean canChildBeDismissed(View v) {
+        // Disallow dismissing an already dismissed task
+        TaskView tv = (TaskView) v;
+        Task task = tv.getTask();
+        return !mSwipeHelperAnimations.containsKey(v) &&
+                (mSv.getStack().indexOfTask(task) != -1);
+    }
+
+    /**
+     * Starts a manual drag that goes through the same swipe helper path.
+     */
+    public void onBeginManualDrag(TaskView v) {
+        mActiveTaskView = v;
+        mSwipeHelperAnimations.put(v, null);
+        onBeginDrag(v);
+    }
+
+    @Override
+    public void onBeginDrag(View v) {
+        TaskView tv = (TaskView) v;
+
+        // Disable clipping with the stack while we are swiping
+        tv.setClipViewInStack(false);
+        // Disallow touch events from this task view
+        tv.setTouchEnabled(false);
+        // Disallow parents from intercepting touch events
+        final ViewParent parent = mSv.getParent();
+        if (parent != null) {
+            parent.requestDisallowInterceptTouchEvent(true);
+        }
+
+        // Add this task to the set of tasks we are deleting
+        mSv.addIgnoreTask(tv.getTask());
+
+        // Determine if we are animating the other tasks while dismissing this task
+        mCurrentTasks = new ArrayList<Task>(mSv.getStack().getTasks());
+        MutableBoolean isFrontMostTask = new MutableBoolean(false);
+        Task anchorTask = mSv.findAnchorTask(mCurrentTasks, isFrontMostTask);
+        TaskStackLayoutAlgorithm layoutAlgorithm = mSv.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mSv.getScroller();
+        if (anchorTask != null) {
+            // Get the current set of task transforms
+            mSv.getCurrentTaskTransforms(mCurrentTasks, mCurrentTaskTransforms);
+
+            // Get the stack scroll of the task to anchor to (since we are removing something, the
+            // front most task will be our anchor task)
+            float prevAnchorTaskScroll = 0;
+            boolean pullStackForward = mCurrentTasks.size() > 0;
+            if (pullStackForward) {
+                if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+                    float index = layoutAlgorithm.getStackScrollForTask(anchorTask);
+                    prevAnchorTaskScroll = mSv.getStackAlgorithm().mTaskStackLowRamLayoutAlgorithm
+                            .getScrollPForTask((int) index);
+                } else {
+                    prevAnchorTaskScroll = layoutAlgorithm.getStackScrollForTask(anchorTask);
+                }
+            }
+
+            // Calculate where the views would be without the deleting tasks
+            mSv.updateLayoutAlgorithm(false /* boundScroll */);
+
+            float newStackScroll = stackScroller.getStackScroll();
+            if (isFrontMostTask.value) {
+                // Bound the stack scroll to pull tasks forward if necessary
+                newStackScroll = stackScroller.getBoundedStackScroll(newStackScroll);
+            } else if (pullStackForward) {
+                // Otherwise, offset the scroll by the movement of the anchor task
+                float anchorTaskScroll =
+                        layoutAlgorithm.getStackScrollForTaskIgnoreOverrides(anchorTask);
+                if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+                    float index = layoutAlgorithm.getStackScrollForTask(anchorTask);
+                    anchorTaskScroll = mSv.getStackAlgorithm().mTaskStackLowRamLayoutAlgorithm
+                            .getScrollPForTask((int) index);
+                }
+                float stackScrollOffset = (anchorTaskScroll - prevAnchorTaskScroll);
+                if (layoutAlgorithm.getFocusState() != TaskStackLayoutAlgorithm.STATE_FOCUSED
+                        && !LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+                    // If we are focused, we don't want the front task to move, but otherwise, we
+                    // allow the back task to move up, and the front task to move back
+                    stackScrollOffset *= 0.75f;
+                }
+                newStackScroll = stackScroller.getBoundedStackScroll(stackScroller.getStackScroll()
+                        + stackScrollOffset);
+            }
+
+            // Pick up the newly visible views, not including the deleting tasks
+            mSv.bindVisibleTaskViews(newStackScroll, true /* ignoreTaskOverrides */);
+
+            // Get the final set of task transforms (with task removed)
+            mSv.getLayoutTaskTransforms(newStackScroll, TaskStackLayoutAlgorithm.STATE_UNFOCUSED,
+                    mCurrentTasks, true /* ignoreTaskOverrides */, mFinalTaskTransforms);
+
+            // Set the target to scroll towards upon dismissal
+            mTargetStackScroll = newStackScroll;
+
+            /*
+             * Post condition: All views that will be visible as a part of the gesture are retrieved
+             *                 and at their initial positions.  The stack is still at the current
+             *                 scroll, but the layout is updated without the task currently being
+             *                 dismissed.  The final layout is in the unfocused stack state, which
+             *                 will be applied when the current task is dismissed.
+             */
+        }
+    }
+
+    @Override
+    public boolean updateSwipeProgress(View v, boolean dismissable, float swipeProgress) {
+        // Only update the swipe progress for the surrounding tasks if the dismiss animation was not
+        // preempted from a call to cancelNonDismissTaskAnimations
+        if ((mActiveTaskView == v || mSwipeHelperAnimations.containsKey(v)) &&
+                !LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            updateTaskViewTransforms(
+                    Interpolators.FAST_OUT_SLOW_IN.getInterpolation(swipeProgress));
+        }
+        return true;
+    }
+
+    /**
+     * Called after the {@link TaskView} is finished animating away.
+     */
+    @Override
+    public void onChildDismissed(View v) {
+        TaskView tv = (TaskView) v;
+
+        // Re-enable clipping with the stack (we will reuse this view)
+        tv.setClipViewInStack(true);
+        // Re-enable touch events from this task view
+        tv.setTouchEnabled(true);
+        // Update the scroll to the final scroll position before laying out the tasks during dismiss
+        if (mSwipeHelperAnimations.containsKey(v)) {
+            mSv.getScroller().setStackScroll(mTargetStackScroll, null);
+        }
+        // Remove the task view from the stack, ignoring the animation if we've started dragging
+        // again
+        EventBus.getDefault().send(new TaskViewDismissedEvent(tv.getTask(), tv,
+                mSwipeHelperAnimations.containsKey(v)
+                    ? new AnimationProps(TaskStackView.DEFAULT_SYNC_STACK_DURATION,
+                        Interpolators.FAST_OUT_SLOW_IN)
+                    : null));
+        // Only update the final scroll and layout state (set in onBeginDrag()) if the dismiss
+        // animation was not preempted from a call to cancelNonDismissTaskAnimations
+        if (mSwipeHelperAnimations.containsKey(v)) {
+            // Update the focus state to the final focus state
+            mSv.getStackAlgorithm().setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
+            mSv.getStackAlgorithm().clearUnfocusedTaskOverrides();
+            // Stop tracking this deletion animation
+            mSwipeHelperAnimations.remove(v);
+        }
+        // Keep track of deletions by keyboard
+        MetricsLogger.histogram(tv.getContext(), "overview_task_dismissed_source",
+                Constants.Metrics.DismissSourceSwipeGesture);
+    }
+
+    /**
+     * Called after the {@link TaskView} is finished animating back into the list.
+     * onChildDismissed() calls.
+     */
+    @Override
+    public void onChildSnappedBack(View v, float targetLeft) {
+        TaskView tv = (TaskView) v;
+
+        // Re-enable clipping with the stack
+        tv.setClipViewInStack(true);
+        // Re-enable touch events from this task view
+        tv.setTouchEnabled(true);
+
+        // Stop tracking this deleting task, and update the layout to include this task again.  The
+        // stack scroll does not need to be reset, since the scroll has not actually changed in
+        // onBeginDrag().
+        mSv.removeIgnoreTask(tv.getTask());
+        mSv.updateLayoutAlgorithm(false /* boundScroll */);
+        mSv.relayoutTaskViews(AnimationProps.IMMEDIATE);
+        mSwipeHelperAnimations.remove(v);
+    }
+
+    @Override
+    public void onDragCancelled(View v) {
+        // Do nothing
+    }
+
+    @Override
+    public boolean isAntiFalsingNeeded() {
+        return false;
+    }
+
+    @Override
+    public float getFalsingThresholdFactor() {
+        return 0;
+    }
+
+    /**
+     * Interpolates the non-deleting tasks to their final transforms from their current transforms.
+     */
+    private void updateTaskViewTransforms(float dismissFraction) {
+        List<TaskView> taskViews = mSv.getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+
+            if (mSv.isIgnoredTask(task)) {
+                continue;
+            }
+
+            int taskIndex = mCurrentTasks.indexOf(task);
+            if (taskIndex == -1) {
+                // If a task was added to the stack view after the start of the dismiss gesture,
+                // just ignore it
+                continue;
+            }
+
+            TaskViewTransform fromTransform = mCurrentTaskTransforms.get(taskIndex);
+            TaskViewTransform toTransform = mFinalTaskTransforms.get(taskIndex);
+
+            mTmpTransform.copyFrom(fromTransform);
+            // We only really need to interpolate the bounds, progress and translation
+            mTmpTransform.rect.set(Utilities.RECTF_EVALUATOR.evaluate(dismissFraction,
+                    fromTransform.rect, toTransform.rect));
+            mTmpTransform.dimAlpha = fromTransform.dimAlpha + (toTransform.dimAlpha -
+                    fromTransform.dimAlpha) * dismissFraction;
+            mTmpTransform.viewOutlineAlpha = fromTransform.viewOutlineAlpha +
+                    (toTransform.viewOutlineAlpha - fromTransform.viewOutlineAlpha) *
+                            dismissFraction;
+            mTmpTransform.translationZ = fromTransform.translationZ +
+                    (toTransform.translationZ - fromTransform.translationZ) * dismissFraction;
+
+            mSv.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
+        }
+    }
+
+    /** Returns the view at the specified coordinates */
+    private TaskView findViewAtPoint(int x, int y) {
+        List<Task> tasks = mSv.getStack().getTasks();
+        int taskCount = tasks.size();
+        for (int i = taskCount - 1; i >= 0; i--) {
+            TaskView tv = mSv.getChildViewForTask(tasks.get(i));
+            if (tv != null && tv.getVisibility() == View.VISIBLE) {
+                if (mSv.isTouchPointInView(x, y, tv)) {
+                    return tv;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the scaled size used to calculate the dismiss fraction.
+     */
+    public float getScaledDismissSize() {
+        return 1.5f * Math.max(mSv.getWidth(), mSv.getHeight());
+    }
+
+    /**
+     * Returns whether swiping is enabled.
+     */
+    private boolean isSwipingEnabled() {
+        return !mSv.useGridLayout();
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskView.java
new file mode 100644
index 0000000..ab0bf95
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskView.java
@@ -0,0 +1,737 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.Property;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewOutlineProvider;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.LaunchTaskEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.utilities.AnimationProps;
+import com.android.systemui.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * A {@link TaskView} represents a fixed view of a task. Because the TaskView's layout is directed
+ * solely by the {@link TaskStackView}, we make it a fixed size layout which allows relayouts down
+ * the view hierarchy, but not upwards from any of its children (the TaskView will relayout itself
+ * with the previous bounds if any child requests layout).
+ */
+public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks,
+        TaskStackAnimationHelper.Callbacks, View.OnClickListener, View.OnLongClickListener {
+
+    /** The TaskView callbacks */
+    interface TaskViewCallbacks {
+        void onTaskViewClipStateChanged(TaskView tv);
+    }
+
+    /**
+     * The dim overlay is generally calculated from the task progress, but occasionally (like when
+     * launching) needs to be animated independently of the task progress.  This call is only used
+     * when animating the task into Recents, when the header dim is already applied
+     */
+    public static final Property<TaskView, Float> DIM_ALPHA_WITHOUT_HEADER =
+            new FloatProperty<TaskView>("dimAlphaWithoutHeader") {
+                @Override
+                public void setValue(TaskView tv, float dimAlpha) {
+                    tv.setDimAlphaWithoutHeader(dimAlpha);
+                }
+
+                @Override
+                public Float get(TaskView tv) {
+                    return tv.getDimAlpha();
+                }
+            };
+
+    /**
+     * The dim overlay is generally calculated from the task progress, but occasionally (like when
+     * launching) needs to be animated independently of the task progress.
+     */
+    public static final Property<TaskView, Float> DIM_ALPHA =
+            new FloatProperty<TaskView>("dimAlpha") {
+                @Override
+                public void setValue(TaskView tv, float dimAlpha) {
+                    tv.setDimAlpha(dimAlpha);
+                }
+
+                @Override
+                public Float get(TaskView tv) {
+                    return tv.getDimAlpha();
+                }
+            };
+
+    /**
+     * The dim overlay is generally calculated from the task progress, but occasionally (like when
+     * launching) needs to be animated independently of the task progress.
+     */
+    public static final Property<TaskView, Float> VIEW_OUTLINE_ALPHA =
+            new FloatProperty<TaskView>("viewOutlineAlpha") {
+                @Override
+                public void setValue(TaskView tv, float alpha) {
+                    tv.getViewBounds().setAlpha(alpha);
+                }
+
+                @Override
+                public Float get(TaskView tv) {
+                    return tv.getViewBounds().getAlpha();
+                }
+            };
+
+    @ViewDebug.ExportedProperty(category="recents")
+    private float mDimAlpha;
+    private float mActionButtonTranslationZ;
+
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="task_")
+    private Task mTask;
+    private boolean mTaskBound;
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mClipViewInStack = true;
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mTouchExplorationEnabled;
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mIsDisabledInSafeMode;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="view_bounds_")
+    private AnimateableViewBounds mViewBounds;
+
+    private AnimatorSet mTransformAnimation;
+    private ObjectAnimator mDimAnimator;
+    private ObjectAnimator mOutlineAnimator;
+    private final TaskViewTransform mTargetAnimationTransform = new TaskViewTransform();
+    private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
+
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
+    protected TaskViewThumbnail mThumbnailView;
+    @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
+    protected TaskViewHeader mHeaderView;
+    private View mActionButtonView;
+    private View mIncompatibleAppToastView;
+    private TaskViewCallbacks mCb;
+
+    @ViewDebug.ExportedProperty(category="recents")
+    private Point mDownTouchPos = new Point();
+
+    private Toast mDisabledAppToast;
+
+    public TaskView(Context context) {
+        this(context, null);
+    }
+
+    public TaskView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+        Resources res = context.getResources();
+        mViewBounds = createOutlineProvider();
+        if (config.fakeShadows) {
+            setBackground(new FakeShadowDrawable(res, config));
+        }
+        setOutlineProvider(mViewBounds);
+        setOnLongClickListener(this);
+        setAccessibilityDelegate(new TaskViewAccessibilityDelegate(this));
+    }
+
+    /** Set callback */
+    void setCallbacks(TaskViewCallbacks cb) {
+        mCb = cb;
+    }
+
+    /**
+     * Called from RecentsActivity when it is relaunched.
+     */
+    void onReload(boolean isResumingFromVisible) {
+        resetNoUserInteractionState();
+        if (!isResumingFromVisible) {
+            resetViewProperties();
+        }
+    }
+
+    /** Gets the task */
+    public Task getTask() {
+        return mTask;
+    }
+
+    /* Create an outline provider to clip and outline the view */
+    protected AnimateableViewBounds createOutlineProvider() {
+        return new AnimateableViewBounds(this, mContext.getResources().getDimensionPixelSize(
+            R.dimen.recents_task_view_shadow_rounded_corners_radius));
+    }
+
+    /** Returns the view bounds. */
+    AnimateableViewBounds getViewBounds() {
+        return mViewBounds;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        // Bind the views
+        mHeaderView = findViewById(R.id.task_view_bar);
+        mThumbnailView = findViewById(R.id.task_view_thumbnail);
+        mThumbnailView.updateClipToTaskBar(mHeaderView);
+        mActionButtonView = findViewById(R.id.lock_to_app_fab);
+        mActionButtonView.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                // Set the outline to match the FAB background
+                outline.setOval(0, 0, mActionButtonView.getWidth(), mActionButtonView.getHeight());
+                outline.setAlpha(0.35f);
+            }
+        });
+        mActionButtonView.setOnClickListener(this);
+        mActionButtonTranslationZ = mActionButtonView.getTranslationZ();
+    }
+
+    /**
+     * Update the task view when the configuration changes.
+     */
+    protected void onConfigurationChanged() {
+        mHeaderView.onConfigurationChanged();
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        if (w > 0 && h > 0) {
+            mHeaderView.onTaskViewSizeChanged(w, h);
+            mThumbnailView.onTaskViewSizeChanged(w, h);
+
+            mActionButtonView.setTranslationX(w - getMeasuredWidth());
+            mActionButtonView.setTranslationY(h - getMeasuredHeight());
+        }
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mDownTouchPos.set((int) (ev.getX() * getScaleX()), (int) (ev.getY() * getScaleY()));
+        }
+        return super.onInterceptTouchEvent(ev);
+    }
+
+    @Override
+    protected void measureContents(int width, int height) {
+        int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
+        int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;
+        int widthSpec = MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY);
+        int heightSpec = MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY);
+
+        // Measure the content
+        measureChildren(widthSpec, heightSpec);
+
+        setMeasuredDimension(width, height);
+    }
+
+    void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform,
+            AnimationProps toAnimation, ValueAnimator.AnimatorUpdateListener updateCallback) {
+        RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
+        cancelTransformAnimation();
+
+        // Compose the animations for the transform
+        mTmpAnimators.clear();
+        toTransform.applyToTaskView(this, mTmpAnimators, toAnimation, !config.fakeShadows);
+        if (toAnimation.isImmediate()) {
+            if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
+                setDimAlpha(toTransform.dimAlpha);
+            }
+            if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
+                mViewBounds.setAlpha(toTransform.viewOutlineAlpha);
+            }
+            // Manually call back to the animator listener and update callback
+            if (toAnimation.getListener() != null) {
+                toAnimation.getListener().onAnimationEnd(null);
+            }
+            if (updateCallback != null) {
+                updateCallback.onAnimationUpdate(null);
+            }
+        } else {
+            // Both the progress and the update are a function of the bounds movement of the task
+            if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
+                mDimAnimator = ObjectAnimator.ofFloat(this, DIM_ALPHA, getDimAlpha(),
+                        toTransform.dimAlpha);
+                mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, mDimAnimator));
+            }
+            if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
+                mOutlineAnimator = ObjectAnimator.ofFloat(this, VIEW_OUTLINE_ALPHA,
+                        mViewBounds.getAlpha(), toTransform.viewOutlineAlpha);
+                mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, mOutlineAnimator));
+            }
+            if (updateCallback != null) {
+                ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1);
+                updateCallbackAnim.addUpdateListener(updateCallback);
+                mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, updateCallbackAnim));
+            }
+
+            // Create the animator
+            mTransformAnimation = toAnimation.createAnimator(mTmpAnimators);
+            mTransformAnimation.start();
+            mTargetAnimationTransform.copyFrom(toTransform);
+        }
+    }
+
+    /** Resets this view's properties */
+    void resetViewProperties() {
+        cancelTransformAnimation();
+        setDimAlpha(0);
+        setVisibility(View.VISIBLE);
+        getViewBounds().reset();
+        getHeaderView().reset();
+        TaskViewTransform.reset(this);
+
+        mActionButtonView.setScaleX(1f);
+        mActionButtonView.setScaleY(1f);
+        mActionButtonView.setAlpha(0f);
+        mActionButtonView.setTranslationX(0f);
+        mActionButtonView.setTranslationY(0f);
+        mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
+        if (mIncompatibleAppToastView != null) {
+            mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    /**
+     * @return whether we are animating towards {@param transform}
+     */
+    boolean isAnimatingTo(TaskViewTransform transform) {
+        return mTransformAnimation != null && mTransformAnimation.isStarted()
+                && mTargetAnimationTransform.isSame(transform);
+    }
+
+    /**
+     * Cancels any current transform animations.
+     */
+    public void cancelTransformAnimation() {
+        cancelDimAnimationIfExists();
+        Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
+        Utilities.cancelAnimationWithoutCallbacks(mOutlineAnimator);
+    }
+
+    private void cancelDimAnimationIfExists() {
+        if (mDimAnimator != null) {
+            mDimAnimator.cancel();
+        }
+    }
+
+    /** Enables/disables handling touch on this task view. */
+    public void setTouchEnabled(boolean enabled) {
+        setOnClickListener(enabled ? this : null);
+    }
+
+    /** Animates this task view if the user does not interact with the stack after a certain time. */
+    public void startNoUserInteractionAnimation() {
+        mHeaderView.startNoUserInteractionAnimation();
+    }
+
+    /** Mark this task view that the user does has not interacted with the stack after a certain time. */
+    void setNoUserInteractionState() {
+        mHeaderView.setNoUserInteractionState();
+    }
+
+    /** Resets the state tracking that the user has not interacted with the stack after a certain time. */
+    void resetNoUserInteractionState() {
+        mHeaderView.resetNoUserInteractionState();
+    }
+
+    /** Dismisses this task. */
+    void dismissTask() {
+        // Animate out the view and call the callback
+        final TaskView tv = this;
+        DismissTaskViewEvent dismissEvent = new DismissTaskViewEvent(tv);
+        dismissEvent.addPostAnimationCallback(new Runnable() {
+            @Override
+            public void run() {
+                EventBus.getDefault().send(new TaskViewDismissedEvent(mTask, tv,
+                        new AnimationProps(TaskStackView.DEFAULT_SYNC_STACK_DURATION,
+                                Interpolators.FAST_OUT_SLOW_IN)));
+            }
+        });
+        EventBus.getDefault().send(dismissEvent);
+    }
+
+    /**
+     * Returns whether this view should be clipped, or any views below should clip against this
+     * view.
+     */
+    boolean shouldClipViewInStack() {
+        if (getVisibility() != View.VISIBLE || LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
+            return false;
+        }
+        return mClipViewInStack;
+    }
+
+    /** Sets whether this view should be clipped, or clipped against. */
+    void setClipViewInStack(boolean clip) {
+        if (clip != mClipViewInStack) {
+            mClipViewInStack = clip;
+            if (mCb != null) {
+                mCb.onTaskViewClipStateChanged(this);
+            }
+        }
+    }
+
+    public TaskViewHeader getHeaderView() {
+        return mHeaderView;
+    }
+
+    /**
+     * Sets the current dim.
+     */
+    public void setDimAlpha(float dimAlpha) {
+        mDimAlpha = dimAlpha;
+        mThumbnailView.setDimAlpha(dimAlpha);
+        mHeaderView.setDimAlpha(dimAlpha);
+    }
+
+    /**
+     * Sets the current dim without updating the header's dim.
+     */
+    public void setDimAlphaWithoutHeader(float dimAlpha) {
+        mDimAlpha = dimAlpha;
+        mThumbnailView.setDimAlpha(dimAlpha);
+    }
+
+    /**
+     * Returns the current dim.
+     */
+    public float getDimAlpha() {
+        return mDimAlpha;
+    }
+
+    /**
+     * Explicitly sets the focused state of this task.
+     */
+    public void setFocusedState(boolean isFocused, boolean requestViewFocus) {
+        if (isFocused) {
+            if (requestViewFocus && !isFocused()) {
+                requestFocus();
+            }
+        } else {
+            if (isAccessibilityFocused() && mTouchExplorationEnabled) {
+                clearAccessibilityFocus();
+            }
+        }
+    }
+
+    /**
+     * Shows the action button.
+     * @param fadeIn whether or not to animate the action button in.
+     * @param fadeInDuration the duration of the action button animation, only used if
+     *                       {@param fadeIn} is true.
+     */
+    public void showActionButton(boolean fadeIn, int fadeInDuration) {
+        mActionButtonView.setVisibility(View.VISIBLE);
+
+        if (fadeIn && mActionButtonView.getAlpha() < 1f) {
+            mActionButtonView.animate()
+                    .alpha(1f)
+                    .scaleX(1f)
+                    .scaleY(1f)
+                    .setDuration(fadeInDuration)
+                    .setInterpolator(Interpolators.ALPHA_IN)
+                    .start();
+        } else {
+            mActionButtonView.setScaleX(1f);
+            mActionButtonView.setScaleY(1f);
+            mActionButtonView.setAlpha(1f);
+            mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
+        }
+    }
+
+    /**
+     * Immediately hides the action button.
+     *
+     * @param fadeOut whether or not to animate the action button out.
+     */
+    public void hideActionButton(boolean fadeOut, int fadeOutDuration, boolean scaleDown,
+            final Animator.AnimatorListener animListener) {
+        if (fadeOut && mActionButtonView.getAlpha() > 0f) {
+            if (scaleDown) {
+                float toScale = 0.9f;
+                mActionButtonView.animate()
+                        .scaleX(toScale)
+                        .scaleY(toScale);
+            }
+            mActionButtonView.animate()
+                    .alpha(0f)
+                    .setDuration(fadeOutDuration)
+                    .setInterpolator(Interpolators.ALPHA_OUT)
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (animListener != null) {
+                                animListener.onAnimationEnd(null);
+                            }
+                            mActionButtonView.setVisibility(View.INVISIBLE);
+                        }
+                    })
+                    .start();
+        } else {
+            mActionButtonView.setAlpha(0f);
+            mActionButtonView.setVisibility(View.INVISIBLE);
+            if (animListener != null) {
+                animListener.onAnimationEnd(null);
+            }
+        }
+    }
+
+    /**** TaskStackAnimationHelper.Callbacks Implementation ****/
+
+    @Override
+    public void onPrepareLaunchTargetForEnterAnimation() {
+        // These values will be animated in when onStartLaunchTargetEnterAnimation() is called
+        setDimAlphaWithoutHeader(0);
+        mActionButtonView.setAlpha(0f);
+        if (mIncompatibleAppToastView != null &&
+                mIncompatibleAppToastView.getVisibility() == View.VISIBLE) {
+            mIncompatibleAppToastView.setAlpha(0f);
+        }
+    }
+
+    @Override
+    public void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
+            boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger) {
+        cancelDimAnimationIfExists();
+
+        // Dim the view after the app window transitions down into recents
+        postAnimationTrigger.increment();
+        AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
+        mDimAnimator = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
+                DIM_ALPHA_WITHOUT_HEADER, getDimAlpha(), transform.dimAlpha));
+        mDimAnimator.addListener(postAnimationTrigger.decrementOnAnimationEnd());
+        mDimAnimator.start();
+
+        if (screenPinningEnabled) {
+            showActionButton(true /* fadeIn */, duration /* fadeInDuration */);
+        }
+
+        if (mIncompatibleAppToastView != null &&
+                mIncompatibleAppToastView.getVisibility() == View.VISIBLE) {
+            mIncompatibleAppToastView.animate()
+                    .alpha(1f)
+                    .setDuration(duration)
+                    .setInterpolator(Interpolators.ALPHA_IN)
+                    .start();
+        }
+    }
+
+    @Override
+    public void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
+            ReferenceCountedTrigger postAnimationTrigger) {
+        Utilities.cancelAnimationWithoutCallbacks(mDimAnimator);
+
+        // Un-dim the view before/while launching the target
+        AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
+        mDimAnimator = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
+                DIM_ALPHA, getDimAlpha(), 0));
+        mDimAnimator.start();
+
+        postAnimationTrigger.increment();
+        hideActionButton(true /* fadeOut */, duration,
+                !screenPinningRequested /* scaleDown */,
+                postAnimationTrigger.decrementOnAnimationEnd());
+    }
+
+    @Override
+    public void onStartFrontTaskEnterAnimation(boolean screenPinningEnabled) {
+        if (screenPinningEnabled) {
+            showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
+        }
+    }
+
+    /**** TaskCallbacks Implementation ****/
+
+    public void onTaskBound(Task t, boolean touchExplorationEnabled, int displayOrientation,
+            Rect displayRect) {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        mTouchExplorationEnabled = touchExplorationEnabled;
+        mTask = t;
+        mTaskBound = true;
+        mTask.addCallback(this);
+        mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
+        mThumbnailView.bindToTask(mTask, mIsDisabledInSafeMode, displayOrientation, displayRect);
+        mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
+
+        if (!t.isDockable && ssp.hasDockedTask()) {
+            if (mIncompatibleAppToastView == null) {
+                mIncompatibleAppToastView = Utilities.findViewStubById(this,
+                        R.id.incompatible_app_toast_stub).inflate();
+                TextView msg = findViewById(com.android.internal.R.id.message);
+                msg.setText(R.string.dock_non_resizeble_failed_to_dock_text);
+            }
+            mIncompatibleAppToastView.setVisibility(View.VISIBLE);
+        } else if (mIncompatibleAppToastView != null) {
+            mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    @Override
+    public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
+        if (mTaskBound) {
+            // Update each of the views to the new task data
+            mThumbnailView.onTaskDataLoaded(thumbnailData);
+            mHeaderView.onTaskDataLoaded();
+        }
+    }
+
+    @Override
+    public void onTaskDataUnloaded() {
+        // Unbind each of the views from the task and remove the task callback
+        mTask.removeCallback(this);
+        mThumbnailView.unbindFromTask();
+        mHeaderView.unbindFromTask(mTouchExplorationEnabled);
+        mTaskBound = false;
+    }
+
+    @Override
+    public void onTaskWindowingModeChanged() {
+        // Force rebind the header, the thumbnail does not change due to stack changes
+        mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
+        mHeaderView.onTaskDataLoaded();
+    }
+
+    /**** View.OnClickListener Implementation ****/
+
+    @Override
+     public void onClick(final View v) {
+        if (mIsDisabledInSafeMode) {
+            Context context = getContext();
+            String msg = context.getString(R.string.recents_launch_disabled_message, mTask.title);
+            if (mDisabledAppToast != null) {
+                mDisabledAppToast.cancel();
+            }
+            mDisabledAppToast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
+            mDisabledAppToast.show();
+            return;
+        }
+
+        boolean screenPinningRequested = false;
+        if (v == mActionButtonView) {
+            // Reset the translation of the action button before we animate it out
+            mActionButtonView.setTranslationZ(0f);
+            screenPinningRequested = true;
+        }
+        EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, screenPinningRequested));
+
+        MetricsLogger.action(v.getContext(), MetricsEvent.ACTION_OVERVIEW_SELECT,
+                mTask.key.getComponent().toString());
+    }
+
+    /**** View.OnLongClickListener Implementation ****/
+
+    @Override
+    public boolean onLongClick(View v) {
+        if (!LegacyRecentsImpl.getConfiguration().dragToSplitEnabled) {
+            return false;
+        }
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        boolean inBounds = false;
+        Rect clipBounds = new Rect(mViewBounds.getClipBounds());
+        if (!clipBounds.isEmpty()) {
+            // If we are clipping the view to the bounds, manually do the hit test.
+            clipBounds.scale(getScaleX());
+            inBounds = clipBounds.contains(mDownTouchPos.x, mDownTouchPos.y);
+        } else {
+            // Otherwise just make sure we're within the view's bounds.
+            inBounds = mDownTouchPos.x <= getWidth() && mDownTouchPos.y <= getHeight();
+        }
+        if (v == this && inBounds && !ssp.hasDockedTask()) {
+            // Start listening for drag events
+            setClipViewInStack(false);
+
+            mDownTouchPos.x += ((1f - getScaleX()) * getWidth()) / 2;
+            mDownTouchPos.y += ((1f - getScaleY()) * getHeight()) / 2;
+
+            EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+            EventBus.getDefault().send(new DragStartEvent(mTask, this, mDownTouchPos));
+            return true;
+        }
+        return false;
+    }
+
+    /**** Events ****/
+
+    public final void onBusEvent(DragEndEvent event) {
+        if (!(event.dropTarget instanceof DockState)) {
+            event.addPostAnimationCallback(() -> {
+                // Reset the clip state for the drag view after the end animation completes
+                setClipViewInStack(true);
+            });
+        }
+        EventBus.getDefault().unregister(this);
+    }
+
+    public final void onBusEvent(DragEndCancelledEvent event) {
+        // Reset the clip state for the drag view after the cancel animation completes
+        event.addPostAnimationCallback(() -> {
+            setClipViewInStack(true);
+        });
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "  ";
+
+        writer.print(prefix); writer.print("TaskView");
+        writer.print(" mTask="); writer.print(mTask.key.id);
+        writer.println();
+
+        mThumbnailView.dump(innerPrefix, writer);
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
new file mode 100644
index 0000000..7bcad75
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 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.systemui.recents.views;
+
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import com.android.systemui.R;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+
+public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate {
+    private static final String TAG = "TaskViewAccessibilityDelegate";
+
+    private final TaskView mTaskView;
+
+    protected static final int SPLIT_TASK_TOP = R.id.action_split_task_to_top;
+    protected static final int SPLIT_TASK_LEFT = R.id.action_split_task_to_left;
+    protected static final int SPLIT_TASK_RIGHT = R.id.action_split_task_to_right;
+
+    protected final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
+
+    public TaskViewAccessibilityDelegate(TaskView taskView) {
+        mTaskView = taskView;
+        Context context = taskView.getContext();
+        mActions.put(SPLIT_TASK_TOP, new AccessibilityAction(SPLIT_TASK_TOP,
+                context.getString(R.string.recents_accessibility_split_screen_top)));
+        mActions.put(SPLIT_TASK_LEFT, new AccessibilityAction(SPLIT_TASK_LEFT,
+                context.getString(R.string.recents_accessibility_split_screen_left)));
+        mActions.put(SPLIT_TASK_RIGHT, new AccessibilityAction(SPLIT_TASK_RIGHT,
+                context.getString(R.string.recents_accessibility_split_screen_right)));
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(host, info);
+        if (ActivityTaskManager.supportsSplitScreenMultiWindow(mTaskView.getContext())
+                && !LegacyRecentsImpl.getSystemServices().hasDockedTask()) {
+            DockState[] dockStates = LegacyRecentsImpl.getConfiguration()
+                    .getDockStatesForCurrentOrientation();
+            for (DockState dockState: dockStates) {
+                if (dockState == DockState.TOP) {
+                    info.addAction(mActions.get(SPLIT_TASK_TOP));
+                } else if (dockState == DockState.LEFT) {
+                    info.addAction(mActions.get(SPLIT_TASK_LEFT));
+                } else if (dockState == DockState.RIGHT) {
+                    info.addAction(mActions.get(SPLIT_TASK_RIGHT));
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityAction(View host, int action, Bundle args) {
+        if (action == SPLIT_TASK_TOP) {
+            simulateDragIntoMultiwindow(DockState.TOP);
+        } else if (action == SPLIT_TASK_LEFT) {
+            simulateDragIntoMultiwindow(DockState.LEFT);
+        } else if (action == SPLIT_TASK_RIGHT) {
+            simulateDragIntoMultiwindow(DockState.RIGHT);
+        } else {
+            return super.performAccessibilityAction(host, action, args);
+        }
+        return true;
+    }
+
+    /** Simulate a user drag event to split the screen to the respected side */
+    private void simulateDragIntoMultiwindow(DockState dockState) {
+        EventBus.getDefault().send(new DragStartEvent(mTaskView.getTask(), mTaskView,
+                new Point(0,0), false /* isUserTouchInitiated */));
+        EventBus.getDefault().send(new DragEndEvent(mTaskView.getTask(), mTaskView, dockState));
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewHeader.java
new file mode 100644
index 0000000..21c0234
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.CountDownTimer;
+import androidx.core.graphics.ColorUtils;
+import android.util.AttributeSet;
+import android.util.IconDrawableFactory;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.LaunchTaskEvent;
+import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.model.Task;
+
+/* The task bar view */
+public class TaskViewHeader extends FrameLayout
+        implements View.OnClickListener, View.OnLongClickListener {
+
+    private static IconDrawableFactory sDrawableFactory;
+
+    private static final float HIGHLIGHT_LIGHTNESS_INCREMENT = 0.075f;
+    private static final float OVERLAY_LIGHTNESS_INCREMENT = -0.0625f;
+    private static final int OVERLAY_REVEAL_DURATION = 250;
+    private static final long FOCUS_INDICATOR_INTERVAL_MS = 30;
+
+    /**
+     * A color drawable that draws a slight highlight at the top to help it stand out.
+     */
+    private class HighlightColorDrawable extends Drawable {
+
+        private Paint mHighlightPaint = new Paint();
+        private Paint mBackgroundPaint = new Paint();
+        private int mColor;
+        private float mDimAlpha;
+
+        public HighlightColorDrawable() {
+            mBackgroundPaint.setColor(Color.argb(255, 0, 0, 0));
+            mBackgroundPaint.setAntiAlias(true);
+            mHighlightPaint.setColor(Color.argb(255, 255, 255, 255));
+            mHighlightPaint.setAntiAlias(true);
+        }
+
+        public void setColorAndDim(int color, float dimAlpha) {
+            if (mColor != color || Float.compare(mDimAlpha, dimAlpha) != 0) {
+                mColor = color;
+                mDimAlpha = dimAlpha;
+                if (mShouldDarkenBackgroundColor) {
+                    color = getSecondaryColor(color, false /* useLightOverlayColor */);
+                }
+                mBackgroundPaint.setColor(color);
+
+                ColorUtils.colorToHSL(color, mTmpHSL);
+                // TODO: Consider using the saturation of the color to adjust the lightness as well
+                mTmpHSL[2] = Math.min(1f,
+                        mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
+                mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
+
+                invalidateSelf();
+            }
+        }
+
+        @Override
+        public void setColorFilter(@Nullable ColorFilter colorFilter) {
+            // Do nothing
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+            // Do nothing
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            // Draw the highlight at the top edge (but put the bottom edge just out of view)
+            canvas.drawRoundRect(0, 0, mTaskViewRect.width(),
+                    2 * Math.max(mHighlightHeight, mCornerRadius),
+                    mCornerRadius, mCornerRadius, mHighlightPaint);
+
+            // Draw the background with the rounded corners
+            canvas.drawRoundRect(0, mHighlightHeight, mTaskViewRect.width(),
+                    getHeight() + mCornerRadius,
+                    mCornerRadius, mCornerRadius, mBackgroundPaint);
+        }
+
+        @Override
+        public int getOpacity() {
+            return PixelFormat.OPAQUE;
+        }
+
+        public int getColor() {
+            return mColor;
+        }
+    }
+
+    Task mTask;
+
+    // Header views
+    ImageView mIconView;
+    TextView mTitleView;
+    ImageView mMoveTaskButton;
+    ImageView mDismissButton;
+    FrameLayout mAppOverlayView;
+    ImageView mAppIconView;
+    ImageView mAppInfoView;
+    TextView mAppTitleView;
+    ProgressBar mFocusTimerIndicator;
+
+    // Header drawables
+    @ViewDebug.ExportedProperty(category="recents")
+    Rect mTaskViewRect = new Rect();
+    int mHeaderBarHeight;
+    int mHeaderButtonPadding;
+    int mCornerRadius;
+    int mHighlightHeight;
+    @ViewDebug.ExportedProperty(category="recents")
+    float mDimAlpha;
+    Drawable mLightDismissDrawable;
+    Drawable mDarkDismissDrawable;
+    Drawable mLightFullscreenIcon;
+    Drawable mDarkFullscreenIcon;
+    Drawable mLightInfoIcon;
+    Drawable mDarkInfoIcon;
+    int mTaskBarViewLightTextColor;
+    int mTaskBarViewDarkTextColor;
+    int mDisabledTaskBarBackgroundColor;
+    String mDismissDescFormat;
+    String mAppInfoDescFormat;
+    int mTaskWindowingMode = WINDOWING_MODE_UNDEFINED;
+
+    // Header background
+    private HighlightColorDrawable mBackground;
+    private HighlightColorDrawable mOverlayBackground;
+    private float[] mTmpHSL = new float[3];
+
+    // Header dim, which is only used when task view hardware layers are not used
+    private Paint mDimLayerPaint = new Paint();
+
+    // Whether the background color should be darkened to differentiate from the primary color.
+    // Used in grid layout.
+    private boolean mShouldDarkenBackgroundColor = false;
+
+    private CountDownTimer mFocusTimerCountDown;
+
+    public TaskViewHeader(Context context) {
+        this(context, null);
+    }
+
+    public TaskViewHeader(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        setWillNotDraw(false);
+
+        // Load the dismiss resources
+        Resources res = context.getResources();
+        mLightDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_light);
+        mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
+        mCornerRadius = LegacyRecentsImpl.getConfiguration().isGridEnabled ?
+                res.getDimensionPixelSize(R.dimen.recents_grid_task_view_rounded_corners_radius) :
+                res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+        mHighlightHeight = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
+        mTaskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
+        mTaskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
+        mLightFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_light);
+        mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark);
+        mLightInfoIcon = context.getDrawable(R.drawable.recents_info_light);
+        mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark);
+        mDisabledTaskBarBackgroundColor =
+                context.getColor(R.color.recents_task_bar_disabled_background_color);
+        mDismissDescFormat = mContext.getString(
+                R.string.accessibility_recents_item_will_be_dismissed);
+        mAppInfoDescFormat = mContext.getString(R.string.accessibility_recents_item_open_app_info);
+
+        // Configure the background and dim
+        mBackground = new HighlightColorDrawable();
+        mBackground.setColorAndDim(Color.argb(255, 0, 0, 0), 0f);
+        setBackground(mBackground);
+        mOverlayBackground = new HighlightColorDrawable();
+        mDimLayerPaint.setColor(Color.argb(255, 0, 0, 0));
+        mDimLayerPaint.setAntiAlias(true);
+    }
+
+    /**
+     * Resets this header along with the TaskView.
+     */
+    public void reset() {
+        hideAppOverlay(true /* immediate */);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+
+        // Initialize the icon and description views
+        mIconView = findViewById(R.id.icon);
+        mIconView.setOnLongClickListener(this);
+        mTitleView = findViewById(R.id.title);
+        mDismissButton = findViewById(R.id.dismiss_task);
+
+        onConfigurationChanged();
+    }
+
+    /**
+     * Programmatically sets the layout params for a header bar layout.  This is necessary because
+     * we can't get resources based on the current configuration, but instead need to get them
+     * based on the device configuration.
+     */
+    private void updateLayoutParams(View icon, View title, View secondaryButton, View button) {
+        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, mHeaderBarHeight, Gravity.TOP);
+        setLayoutParams(lp);
+        lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.START);
+        icon.setLayoutParams(lp);
+        lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
+        lp.setMarginStart(mHeaderBarHeight);
+        lp.setMarginEnd(mMoveTaskButton != null
+                ? 2 * mHeaderBarHeight
+                : mHeaderBarHeight);
+        title.setLayoutParams(lp);
+        if (secondaryButton != null) {
+            lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
+            lp.setMarginEnd(mHeaderBarHeight);
+            secondaryButton.setLayoutParams(lp);
+            secondaryButton.setPadding(mHeaderButtonPadding, mHeaderButtonPadding,
+                    mHeaderButtonPadding, mHeaderButtonPadding);
+        }
+        lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
+        button.setLayoutParams(lp);
+        button.setPadding(mHeaderButtonPadding, mHeaderButtonPadding, mHeaderButtonPadding,
+                mHeaderButtonPadding);
+    }
+
+    /**
+     * Update the header view when the configuration changes.
+     */
+    public void onConfigurationChanged() {
+        // Update the dimensions of everything in the header. We do this because we need to use
+        // resources for the display, and not the current configuration.
+        Resources res = getResources();
+        int headerBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_grid_task_view_header_height);
+        int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
+                R.dimen.recents_task_view_header_button_padding,
+                R.dimen.recents_task_view_header_button_padding,
+                R.dimen.recents_task_view_header_button_padding,
+                R.dimen.recents_task_view_header_button_padding_tablet_land,
+                R.dimen.recents_task_view_header_button_padding,
+                R.dimen.recents_task_view_header_button_padding_tablet_land,
+                R.dimen.recents_grid_task_view_header_button_padding);
+        if (headerBarHeight != mHeaderBarHeight || headerButtonPadding != mHeaderButtonPadding) {
+            mHeaderBarHeight = headerBarHeight;
+            mHeaderButtonPadding = headerButtonPadding;
+            updateLayoutParams(mIconView, mTitleView, mMoveTaskButton, mDismissButton);
+            if (mAppOverlayView != null) {
+                updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
+            }
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        // Since we update the position of children based on the width of the parent and this view
+        // recompute these changes with the new view size
+        onTaskViewSizeChanged(mTaskViewRect.width(), mTaskViewRect.height());
+    }
+
+    /**
+     * Called when the task view frame changes, allowing us to move the contents of the header
+     * to match the frame changes.
+     */
+    public void onTaskViewSizeChanged(int width, int height) {
+        mTaskViewRect.set(0, 0, width, height);
+
+        boolean showTitle = true;
+        boolean showMoveIcon = true;
+        boolean showDismissIcon = true;
+        int rightInset = width - getMeasuredWidth();
+
+        mTitleView.setVisibility(showTitle ? View.VISIBLE : View.INVISIBLE);
+        if (mMoveTaskButton != null) {
+            mMoveTaskButton.setVisibility(showMoveIcon ? View.VISIBLE : View.INVISIBLE);
+            mMoveTaskButton.setTranslationX(rightInset);
+        }
+        mDismissButton.setVisibility(showDismissIcon ? View.VISIBLE : View.INVISIBLE);
+        mDismissButton.setTranslationX(rightInset);
+
+        setLeftTopRightBottom(0, 0, width, getMeasuredHeight());
+    }
+
+    @Override
+    public void onDrawForeground(Canvas canvas) {
+        super.onDrawForeground(canvas);
+
+        // Draw the dim layer with the rounded corners
+        canvas.drawRoundRect(0, 0, mTaskViewRect.width(), getHeight() + mCornerRadius,
+                mCornerRadius, mCornerRadius, mDimLayerPaint);
+    }
+
+    /** Starts the focus timer. */
+    public void startFocusTimerIndicator(int duration) {
+        if (mFocusTimerIndicator == null) {
+            return;
+        }
+
+        mFocusTimerIndicator.setVisibility(View.VISIBLE);
+        mFocusTimerIndicator.setMax(duration);
+        mFocusTimerIndicator.setProgress(duration);
+        if (mFocusTimerCountDown != null) {
+            mFocusTimerCountDown.cancel();
+        }
+        mFocusTimerCountDown = new CountDownTimer(duration,
+                FOCUS_INDICATOR_INTERVAL_MS) {
+            public void onTick(long millisUntilFinished) {
+                mFocusTimerIndicator.setProgress((int) millisUntilFinished);
+            }
+
+            public void onFinish() {
+                // Do nothing
+            }
+        }.start();
+    }
+
+    /** Cancels the focus timer. */
+    public void cancelFocusTimerIndicator() {
+        if (mFocusTimerIndicator == null) {
+            return;
+        }
+
+        if (mFocusTimerCountDown != null) {
+            mFocusTimerCountDown.cancel();
+            mFocusTimerIndicator.setProgress(0);
+            mFocusTimerIndicator.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    /** Only exposed for the workaround for b/27815919. */
+    public ImageView getIconView() {
+        return mIconView;
+    }
+
+    /** Returns the secondary color for a primary color. */
+    int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
+        int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
+        return Utilities.getColorWithOverlay(primaryColor, overlayColor, 0.8f);
+    }
+
+    /**
+     * Sets the dim alpha, only used when we are not using hardware layers.
+     * (see RecentsConfiguration.useHardwareLayers)
+     */
+    public void setDimAlpha(float dimAlpha) {
+        if (Float.compare(mDimAlpha, dimAlpha) != 0) {
+            mDimAlpha = dimAlpha;
+            mTitleView.setAlpha(1f - dimAlpha);
+            updateBackgroundColor(mBackground.getColor(), dimAlpha);
+        }
+    }
+
+    /**
+     * Updates the background and highlight colors for this header.
+     */
+    private void updateBackgroundColor(int color, float dimAlpha) {
+        if (mTask != null) {
+            mBackground.setColorAndDim(color, dimAlpha);
+            // TODO: Consider using the saturation of the color to adjust the lightness as well
+            ColorUtils.colorToHSL(color, mTmpHSL);
+            mTmpHSL[2] = Math.min(1f, mTmpHSL[2] + OVERLAY_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
+            mOverlayBackground.setColorAndDim(ColorUtils.HSLToColor(mTmpHSL), dimAlpha);
+            mDimLayerPaint.setAlpha((int) (dimAlpha * 255));
+            invalidate();
+        }
+    }
+
+    /**
+     * Sets whether the background color should be darkened to differentiate from the primary color.
+     */
+    public void setShouldDarkenBackgroundColor(boolean flag) {
+        mShouldDarkenBackgroundColor = flag;
+    }
+
+    /**
+     * Binds the bar view to the task.
+     */
+    public void bindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
+        mTask = t;
+
+        int primaryColor = disabledInSafeMode
+                ? mDisabledTaskBarBackgroundColor
+                : t.colorPrimary;
+        if (mBackground.getColor() != primaryColor) {
+            updateBackgroundColor(primaryColor, mDimAlpha);
+        }
+        if (!mTitleView.getText().toString().equals(t.title)) {
+            mTitleView.setText(t.title);
+        }
+        mTitleView.setContentDescription(t.titleDescription);
+        mTitleView.setTextColor(t.useLightOnPrimaryColor ?
+                mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
+        mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
+                mLightDismissDrawable : mDarkDismissDrawable);
+        mDismissButton.setContentDescription(String.format(mDismissDescFormat, t.titleDescription));
+        mDismissButton.setOnClickListener(this);
+        mDismissButton.setClickable(false);
+        ((RippleDrawable) mDismissButton.getBackground()).setForceSoftware(true);
+
+        // In accessibility, a single click on the focused app info button will show it
+        if (touchExplorationEnabled) {
+            mIconView.setContentDescription(String.format(mAppInfoDescFormat, t.titleDescription));
+            mIconView.setOnClickListener(this);
+            mIconView.setClickable(true);
+        }
+    }
+
+    /**
+     * Called when the bound task's data has loaded and this view should update to reflect the
+     * changes.
+     */
+    public void onTaskDataLoaded() {
+        if (mTask != null && mTask.icon != null) {
+            mIconView.setImageDrawable(mTask.icon);
+        }
+    }
+
+    /** Unbinds the bar view from the task */
+    void unbindFromTask(boolean touchExplorationEnabled) {
+        mTask = null;
+        mIconView.setImageDrawable(null);
+        if (touchExplorationEnabled) {
+            mIconView.setClickable(false);
+        }
+    }
+
+    /** Animates this task bar if the user does not interact with the stack after a certain time. */
+    void startNoUserInteractionAnimation() {
+        int duration = getResources().getInteger(R.integer.recents_task_enter_from_app_duration);
+        mDismissButton.setVisibility(View.VISIBLE);
+        mDismissButton.setClickable(true);
+        if (mDismissButton.getVisibility() == VISIBLE) {
+            mDismissButton.animate()
+                    .alpha(1f)
+                    .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
+                    .setDuration(duration)
+                    .start();
+        } else {
+            mDismissButton.setAlpha(1f);
+        }
+        if (mMoveTaskButton != null) {
+            if (mMoveTaskButton.getVisibility() == VISIBLE) {
+                mMoveTaskButton.setVisibility(View.VISIBLE);
+                mMoveTaskButton.setClickable(true);
+                mMoveTaskButton.animate()
+                        .alpha(1f)
+                        .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
+                        .setDuration(duration)
+                        .start();
+            } else {
+                mMoveTaskButton.setAlpha(1f);
+            }
+        }
+    }
+
+    /**
+     * Mark this task view that the user does has not interacted with the stack after a certain
+     * time.
+     */
+    public void setNoUserInteractionState() {
+        mDismissButton.setVisibility(View.VISIBLE);
+        mDismissButton.animate().cancel();
+        mDismissButton.setAlpha(1f);
+        mDismissButton.setClickable(true);
+        if (mMoveTaskButton != null) {
+            mMoveTaskButton.setVisibility(View.VISIBLE);
+            mMoveTaskButton.animate().cancel();
+            mMoveTaskButton.setAlpha(1f);
+            mMoveTaskButton.setClickable(true);
+        }
+    }
+
+    /**
+     * Resets the state tracking that the user has not interacted with the stack after a certain
+     * time.
+     */
+    void resetNoUserInteractionState() {
+        mDismissButton.setVisibility(View.INVISIBLE);
+        mDismissButton.setAlpha(0f);
+        mDismissButton.setClickable(false);
+        if (mMoveTaskButton != null) {
+            mMoveTaskButton.setVisibility(View.INVISIBLE);
+            mMoveTaskButton.setAlpha(0f);
+            mMoveTaskButton.setClickable(false);
+        }
+    }
+
+    @Override
+    protected int[] onCreateDrawableState(int extraSpace) {
+
+        // Don't forward our state to the drawable - we do it manually in onTaskViewFocusChanged.
+        // This is to prevent layer trashing when the view is pressed.
+        return new int[] {};
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v == mIconView) {
+            // In accessibility, a single click on the focused app info button will show it
+            EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
+        } else if (v == mDismissButton) {
+            TaskView tv = Utilities.findParent(this, TaskView.class);
+            tv.dismissTask();
+
+            // Keep track of deletions by the dismiss button
+            MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
+                    Constants.Metrics.DismissSourceHeaderButton);
+        } else if (v == mMoveTaskButton) {
+            TaskView tv = Utilities.findParent(this, TaskView.class);
+            EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, null, false,
+                    mTaskWindowingMode, ACTIVITY_TYPE_UNDEFINED));
+        } else if (v == mAppInfoView) {
+            EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
+        } else if (v == mAppIconView) {
+            hideAppOverlay(false /* immediate */);
+        }
+    }
+
+    @Override
+    public boolean onLongClick(View v) {
+        if (v == mIconView) {
+            showAppOverlay();
+            return true;
+        } else if (v == mAppIconView) {
+            hideAppOverlay(false /* immediate */);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Shows the application overlay.
+     */
+    private void showAppOverlay() {
+        // Skip early if the task is invalid
+        SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
+        ComponentName cn = mTask.key.getComponent();
+        int userId = mTask.key.userId;
+        ActivityInfo activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(cn, userId);
+        if (activityInfo == null) {
+            return;
+        }
+
+        // Inflate the overlay if necessary
+        if (mAppOverlayView == null) {
+            mAppOverlayView = (FrameLayout) Utilities.findViewStubById(this,
+                    R.id.app_overlay_stub).inflate();
+            mAppOverlayView.setBackground(mOverlayBackground);
+            mAppIconView = (ImageView) mAppOverlayView.findViewById(R.id.app_icon);
+            mAppIconView.setOnClickListener(this);
+            mAppIconView.setOnLongClickListener(this);
+            mAppInfoView = (ImageView) mAppOverlayView.findViewById(R.id.app_info);
+            mAppInfoView.setOnClickListener(this);
+            mAppTitleView = (TextView) mAppOverlayView.findViewById(R.id.app_title);
+            updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
+        }
+
+        // Update the overlay contents for the current app
+        mAppTitleView.setText(ActivityManagerWrapper.getInstance().getBadgedApplicationLabel(
+                activityInfo.applicationInfo, userId));
+        mAppTitleView.setTextColor(mTask.useLightOnPrimaryColor ?
+                mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
+        mAppIconView.setImageDrawable(getIconDrawableFactory().getBadgedIcon(
+                activityInfo.applicationInfo, userId));
+        mAppInfoView.setImageDrawable(mTask.useLightOnPrimaryColor
+                ? mLightInfoIcon
+                : mDarkInfoIcon);
+        mAppOverlayView.setVisibility(View.VISIBLE);
+
+        int x = mIconView.getLeft() + mIconView.getWidth() / 2;
+        int y = mIconView.getTop() + mIconView.getHeight() / 2;
+        Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y, 0,
+                getWidth());
+        revealAnim.setDuration(OVERLAY_REVEAL_DURATION);
+        revealAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        revealAnim.start();
+    }
+
+    /**
+     * Hide the application overlay.
+     */
+    private void hideAppOverlay(boolean immediate) {
+        // Skip if we haven't even loaded the overlay yet
+        if (mAppOverlayView == null) {
+            return;
+        }
+
+        if (immediate) {
+            mAppOverlayView.setVisibility(View.GONE);
+        } else {
+            int x = mIconView.getLeft() + mIconView.getWidth() / 2;
+            int y = mIconView.getTop() + mIconView.getHeight() / 2;
+            Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y,
+                    getWidth(), 0);
+            revealAnim.setDuration(OVERLAY_REVEAL_DURATION);
+            revealAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+            revealAnim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mAppOverlayView.setVisibility(View.GONE);
+                }
+            });
+            revealAnim.start();
+        }
+    }
+
+    private static IconDrawableFactory getIconDrawableFactory() {
+        if (sDrawableFactory == null) {
+            sDrawableFactory = IconDrawableFactory.newInstance(AppGlobals.getInitialApplication());
+        }
+        return sDrawableFactory;
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewThumbnail.java
new file mode 100644
index 0000000..68f85a5
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.LightingColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewDebug;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
+import com.android.systemui.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import java.io.PrintWriter;
+
+
+/**
+ * The task thumbnail view.  It implements an image view that allows for animating the dim and
+ * alpha of the thumbnail image.
+ */
+public class TaskViewThumbnail extends View {
+
+    private static final ColorMatrix TMP_FILTER_COLOR_MATRIX = new ColorMatrix();
+    private static final ColorMatrix TMP_BRIGHTNESS_COLOR_MATRIX = new ColorMatrix();
+
+    private Task mTask;
+
+    private int mDisplayOrientation = Configuration.ORIENTATION_UNDEFINED;
+    private Rect mDisplayRect = new Rect();
+
+    // Drawing
+    @ViewDebug.ExportedProperty(category="recents")
+    protected Rect mTaskViewRect = new Rect();
+    @ViewDebug.ExportedProperty(category="recents")
+    protected Rect mThumbnailRect = new Rect();
+    @ViewDebug.ExportedProperty(category="recents")
+    protected float mThumbnailScale;
+    private float mFullscreenThumbnailScale = 1f;
+    /** The height, in pixels, of the task view's title bar. */
+    private int mTitleBarHeight;
+    private boolean mSizeToFit = false;
+    private boolean mOverlayHeaderOnThumbnailActionBar = true;
+    private ThumbnailData mThumbnailData;
+
+    protected int mCornerRadius;
+    @ViewDebug.ExportedProperty(category="recents")
+    private float mDimAlpha;
+    private Matrix mMatrix = new Matrix();
+    private Paint mDrawPaint = new Paint();
+    protected Paint mLockedPaint = new Paint();
+    protected Paint mBgFillPaint = new Paint();
+    protected BitmapShader mBitmapShader;
+    protected boolean mUserLocked = false;
+    private LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
+
+    // Clip the top of the thumbnail against the opaque header bar that overlaps this view
+    private View mTaskBar;
+
+    // Visibility optimization, if the thumbnail height is less than the height of the header
+    // bar for the task view, then just mark this thumbnail view as invisible
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mInvisible;
+
+    @ViewDebug.ExportedProperty(category="recents")
+    private boolean mDisabledInSafeMode;
+
+    public TaskViewThumbnail(Context context) {
+        this(context, null);
+    }
+
+    public TaskViewThumbnail(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mDrawPaint.setColorFilter(mLightingColorFilter);
+        mDrawPaint.setFilterBitmap(true);
+        mDrawPaint.setAntiAlias(true);
+        Resources res = getResources();
+        mCornerRadius = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+        mBgFillPaint.setColor(Color.WHITE);
+        mLockedPaint.setColor(Color.WHITE);
+        mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
+    }
+
+    /**
+     * Called when the task view frame changes, allowing us to move the contents of the header
+     * to match the frame changes.
+     */
+    public void onTaskViewSizeChanged(int width, int height) {
+        // Return early if the bounds have not changed
+        if (mTaskViewRect.width() == width && mTaskViewRect.height() == height) {
+            return;
+        }
+
+        mTaskViewRect.set(0, 0, width, height);
+        setLeftTopRightBottom(0, 0, width, height);
+        updateThumbnailMatrix();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mInvisible) {
+            return;
+        }
+
+        int viewWidth = mTaskViewRect.width();
+        int viewHeight = mTaskViewRect.height();
+        int thumbnailWidth = Math.min(viewWidth,
+                (int) (mThumbnailRect.width() * mThumbnailScale));
+        int thumbnailHeight = Math.min(viewHeight,
+                (int) (mThumbnailRect.height() * mThumbnailScale));
+
+        if (mUserLocked) {
+            canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
+                    mLockedPaint);
+        } else if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
+            int topOffset = 0;
+            if (mTaskBar != null && mOverlayHeaderOnThumbnailActionBar) {
+                topOffset = mTaskBar.getHeight() - mCornerRadius;
+            }
+
+            // Draw the background, there will be some small overdraw with the thumbnail
+            if (thumbnailWidth < viewWidth) {
+                // Portrait thumbnail on a landscape task view
+                canvas.drawRoundRect(Math.max(0, thumbnailWidth - mCornerRadius), topOffset,
+                        viewWidth, viewHeight,
+                        mCornerRadius, mCornerRadius, mBgFillPaint);
+            }
+            if (thumbnailHeight < viewHeight) {
+                // Landscape thumbnail on a portrait task view
+                canvas.drawRoundRect(0, Math.max(topOffset, thumbnailHeight - mCornerRadius),
+                        viewWidth, viewHeight,
+                        mCornerRadius, mCornerRadius, mBgFillPaint);
+            }
+
+            // Draw the thumbnail
+            canvas.drawRoundRect(0, topOffset, thumbnailWidth, thumbnailHeight,
+                    mCornerRadius, mCornerRadius, mDrawPaint);
+        } else {
+            canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
+                    mBgFillPaint);
+        }
+    }
+
+    /** Sets the thumbnail to a given bitmap. */
+    void setThumbnail(ThumbnailData thumbnailData) {
+        if (thumbnailData != null && thumbnailData.thumbnail != null) {
+            Bitmap bm = thumbnailData.thumbnail;
+            bm.prepareToDraw();
+            mFullscreenThumbnailScale = thumbnailData.scale;
+            mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+            mDrawPaint.setShader(mBitmapShader);
+            mThumbnailRect.set(0, 0,
+                    bm.getWidth() - thumbnailData.insets.left - thumbnailData.insets.right,
+                    bm.getHeight() - thumbnailData.insets.top - thumbnailData.insets.bottom);
+            mThumbnailData = thumbnailData;
+            updateThumbnailMatrix();
+            updateThumbnailPaintFilter();
+        } else {
+            mBitmapShader = null;
+            mDrawPaint.setShader(null);
+            mThumbnailRect.setEmpty();
+            mThumbnailData = null;
+        }
+    }
+
+    /** Updates the paint to draw the thumbnail. */
+    void updateThumbnailPaintFilter() {
+        if (mInvisible) {
+            return;
+        }
+        int mul = (int) ((1.0f - mDimAlpha) * 255);
+        if (mBitmapShader != null) {
+            if (mDisabledInSafeMode) {
+                // Brightness: C-new = C-old*(1-amount) + amount
+                TMP_FILTER_COLOR_MATRIX.setSaturation(0);
+                float scale = 1f - mDimAlpha;
+                float[] mat = TMP_BRIGHTNESS_COLOR_MATRIX.getArray();
+                mat[0] = scale;
+                mat[6] = scale;
+                mat[12] = scale;
+                mat[4] = mDimAlpha * 255f;
+                mat[9] = mDimAlpha * 255f;
+                mat[14] = mDimAlpha * 255f;
+                TMP_FILTER_COLOR_MATRIX.preConcat(TMP_BRIGHTNESS_COLOR_MATRIX);
+                ColorMatrixColorFilter filter = new ColorMatrixColorFilter(TMP_FILTER_COLOR_MATRIX);
+                mDrawPaint.setColorFilter(filter);
+                mBgFillPaint.setColorFilter(filter);
+                mLockedPaint.setColorFilter(filter);
+            } else {
+                mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
+                mDrawPaint.setColorFilter(mLightingColorFilter);
+                mDrawPaint.setColor(0xFFffffff);
+                mBgFillPaint.setColorFilter(mLightingColorFilter);
+                mLockedPaint.setColorFilter(mLightingColorFilter);
+            }
+        } else {
+            int grey = mul;
+            mDrawPaint.setColorFilter(null);
+            mDrawPaint.setColor(Color.argb(255, grey, grey, grey));
+        }
+        if (!mInvisible) {
+            invalidate();
+        }
+    }
+
+    /**
+     * Updates the scale of the bitmap relative to this view.
+     */
+    public void updateThumbnailMatrix() {
+        mThumbnailScale = 1f;
+        if (mBitmapShader != null && mThumbnailData != null) {
+            if (mTaskViewRect.isEmpty()) {
+                // If we haven't measured , skip the thumbnail drawing and only draw the background
+                // color
+                mThumbnailScale = 0f;
+            } else if (mSizeToFit) {
+                // Make sure we fill the entire space regardless of the orientation.
+                float viewAspectRatio = (float) mTaskViewRect.width() /
+                        (float) (mTaskViewRect.height() - mTitleBarHeight);
+                float thumbnailAspectRatio =
+                        (float) mThumbnailRect.width() / (float) mThumbnailRect.height();
+                if (viewAspectRatio > thumbnailAspectRatio) {
+                    mThumbnailScale =
+                            (float) mTaskViewRect.width() / (float) mThumbnailRect.width();
+                } else {
+                    mThumbnailScale = (float) (mTaskViewRect.height() - mTitleBarHeight)
+                            / (float) mThumbnailRect.height();
+                }
+            } else {
+                float invThumbnailScale = 1f / mFullscreenThumbnailScale;
+                if (mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT) {
+                    if (mThumbnailData.orientation == Configuration.ORIENTATION_PORTRAIT) {
+                        // If we are in the same orientation as the screenshot, just scale it to the
+                        // width of the task view
+                        mThumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
+                    } else {
+                        // Scale the landscape thumbnail up to app size, then scale that to the task
+                        // view size to match other portrait screenshots
+                        mThumbnailScale = invThumbnailScale *
+                                ((float) mTaskViewRect.width() / mDisplayRect.width());
+                    }
+                } else {
+                    // Otherwise, scale the screenshot to fit 1:1 in the current orientation
+                    mThumbnailScale = invThumbnailScale;
+                }
+            }
+            mMatrix.setTranslate(-mThumbnailData.insets.left * mFullscreenThumbnailScale,
+                    -mThumbnailData.insets.top * mFullscreenThumbnailScale);
+            mMatrix.postScale(mThumbnailScale, mThumbnailScale);
+            mBitmapShader.setLocalMatrix(mMatrix);
+        }
+        if (!mInvisible) {
+            invalidate();
+        }
+    }
+
+    /** Sets whether the thumbnail should be resized to fit the task view in all orientations. */
+    public void setSizeToFit(boolean flag) {
+        mSizeToFit = flag;
+    }
+
+    /**
+     * Sets whether the header should overlap (and hide) the action bar in the thumbnail, or
+     * be stacked just above it.
+     */
+    public void setOverlayHeaderOnThumbnailActionBar(boolean flag) {
+        mOverlayHeaderOnThumbnailActionBar = flag;
+    }
+
+    /** Updates the clip rect based on the given task bar. */
+    void updateClipToTaskBar(View taskBar) {
+        mTaskBar = taskBar;
+        invalidate();
+    }
+
+    /** Updates the visibility of the the thumbnail. */
+    void updateThumbnailVisibility(int clipBottom) {
+        boolean invisible = mTaskBar != null && (getHeight() - clipBottom) <= mTaskBar.getHeight();
+        if (invisible != mInvisible) {
+            mInvisible = invisible;
+            if (!mInvisible) {
+                updateThumbnailPaintFilter();
+            }
+        }
+    }
+
+    /**
+     * Sets the dim alpha, only used when we are not using hardware layers.
+     * (see RecentsConfiguration.useHardwareLayers)
+     */
+    public void setDimAlpha(float dimAlpha) {
+        mDimAlpha = dimAlpha;
+        updateThumbnailPaintFilter();
+    }
+
+    /**
+     * Returns the {@link Paint} used to draw a task screenshot, or {@link #mLockedPaint} if the
+     * thumbnail shouldn't be drawn because it belongs to a locked user.
+     */
+    protected Paint getDrawPaint() {
+        if (mUserLocked) {
+            return mLockedPaint;
+        }
+        return mDrawPaint;
+    }
+
+    /**
+     * Binds the thumbnail view to the task.
+     */
+    void bindToTask(Task t, boolean disabledInSafeMode, int displayOrientation, Rect displayRect) {
+        mTask = t;
+        mDisabledInSafeMode = disabledInSafeMode;
+        mDisplayOrientation = displayOrientation;
+        mDisplayRect.set(displayRect);
+        if (t.colorBackground != 0) {
+            mBgFillPaint.setColor(t.colorBackground);
+        }
+        if (t.colorPrimary != 0) {
+            mLockedPaint.setColor(t.colorPrimary);
+        }
+        mUserLocked = t.isLocked;
+        EventBus.getDefault().register(this);
+    }
+
+    /**
+     * Called when the bound task's data has loaded and this view should update to reflect the
+     * changes.
+     */
+    void onTaskDataLoaded(ThumbnailData thumbnailData) {
+        setThumbnail(thumbnailData);
+    }
+
+    /** Unbinds the thumbnail view from the task */
+    void unbindFromTask() {
+        mTask = null;
+        setThumbnail(null);
+        EventBus.getDefault().unregister(this);
+    }
+
+    public final void onBusEvent(TaskSnapshotChangedEvent event) {
+        if (mTask == null || event.taskId != mTask.key.id || event.thumbnailData == null
+                || event.thumbnailData.thumbnail == null) {
+            return;
+        }
+        setThumbnail(event.thumbnailData);
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        writer.print(prefix); writer.print("TaskViewThumbnail");
+        writer.print(" mTaskViewRect="); writer.print(Utilities.dumpRect(mTaskViewRect));
+        writer.print(" mThumbnailRect="); writer.print(Utilities.dumpRect(mThumbnailRect));
+        writer.print(" mThumbnailScale="); writer.print(mThumbnailScale);
+        writer.print(" mDimAlpha="); writer.print(mDimAlpha);
+        writer.println();
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewTransform.java
new file mode 100644
index 0000000..48a7336
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.Property;
+import android.view.View;
+
+import com.android.systemui.recents.utilities.AnimationProps;
+import com.android.systemui.recents.utilities.Utilities;
+
+import java.util.ArrayList;
+
+/**
+ * The visual properties for a {@link TaskView}.
+ */
+public class TaskViewTransform {
+
+    public static final Property<View, Rect> LTRB =
+            new Property<View, Rect>(Rect.class, "leftTopRightBottom") {
+
+                private Rect mTmpRect = new Rect();
+
+                @Override
+                public void set(View v, Rect ltrb) {
+                    v.setLeftTopRightBottom(ltrb.left, ltrb.top, ltrb.right, ltrb.bottom);
+                }
+
+                @Override
+                public Rect get(View v) {
+                    mTmpRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+                    return mTmpRect;
+                }
+            };
+
+    public float translationZ = 0;
+    public float scale = 1f;
+    public float alpha = 1f;
+    public float dimAlpha = 0f;
+    public float viewOutlineAlpha = 0f;
+
+    public boolean visible = false;
+
+    // This is a window-space rect used for positioning the task in the stack
+    public RectF rect = new RectF();
+
+    /**
+     * Fills int this transform from the state of the given TaskView.
+     */
+    public void fillIn(TaskView tv) {
+        translationZ = tv.getTranslationZ();
+        scale = tv.getScaleX();
+        alpha = tv.getAlpha();
+        visible = true;
+        dimAlpha = tv.getDimAlpha();
+        viewOutlineAlpha = tv.getViewBounds().getAlpha();
+        rect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
+    }
+
+    /**
+     * Copies the transform state from another {@link TaskViewTransform}.
+     */
+    public void copyFrom(TaskViewTransform other) {
+        translationZ = other.translationZ;
+        scale = other.scale;
+        alpha = other.alpha;
+        visible = other.visible;
+        dimAlpha = other.dimAlpha;
+        viewOutlineAlpha = other.viewOutlineAlpha;
+        rect.set(other.rect);
+    }
+
+    /**
+     * @return whether {@param other} is the same transform as this
+     */
+    public boolean isSame(TaskViewTransform other) {
+        return translationZ == other.translationZ
+                && scale == other.scale
+                && other.alpha == alpha
+                && dimAlpha == other.dimAlpha
+                && visible == other.visible
+                && rect.equals(other.rect);
+    }
+
+    /**
+     * Resets the current transform.
+     */
+    public void reset() {
+        translationZ = 0;
+        scale = 1f;
+        alpha = 1f;
+        dimAlpha = 0f;
+        viewOutlineAlpha = 0f;
+        visible = false;
+        rect.setEmpty();
+    }
+
+    /** Convenience functions to compare against current property values */
+    public boolean hasAlphaChangedFrom(float v) {
+        return (Float.compare(alpha, v) != 0);
+    }
+
+    public boolean hasScaleChangedFrom(float v) {
+        return (Float.compare(scale, v) != 0);
+    }
+
+    public boolean hasTranslationZChangedFrom(float v) {
+        return (Float.compare(translationZ, v) != 0);
+    }
+
+    public boolean hasRectChangedFrom(View v) {
+        return ((int) rect.left != v.getLeft()) || ((int) rect.right != v.getRight()) ||
+                ((int) rect.top != v.getTop()) || ((int) rect.bottom != v.getBottom());
+    }
+
+    /**
+     * Applies this transform to a view.
+     */
+    public void applyToTaskView(TaskView v, ArrayList<Animator> animators,
+            AnimationProps animation, boolean allowShadows) {
+        // Return early if not visible
+        if (!visible) {
+            return;
+        }
+
+        if (animation.isImmediate()) {
+            if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
+                v.setTranslationZ(translationZ);
+            }
+            if (hasScaleChangedFrom(v.getScaleX())) {
+                v.setScaleX(scale);
+                v.setScaleY(scale);
+            }
+            if (hasAlphaChangedFrom(v.getAlpha())) {
+                v.setAlpha(alpha);
+            }
+            if (hasRectChangedFrom(v)) {
+                v.setLeftTopRightBottom((int) rect.left, (int) rect.top, (int) rect.right,
+                        (int) rect.bottom);
+            }
+        } else {
+            if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
+                ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.TRANSLATION_Z,
+                        v.getTranslationZ(), translationZ);
+                animators.add(animation.apply(AnimationProps.TRANSLATION_Z, anim));
+            }
+            if (hasScaleChangedFrom(v.getScaleX())) {
+                ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(v,
+                        PropertyValuesHolder.ofFloat(View.SCALE_X, v.getScaleX(), scale),
+                        PropertyValuesHolder.ofFloat(View.SCALE_Y, v.getScaleX(), scale));
+                animators.add(animation.apply(AnimationProps.SCALE, anim));
+            }
+            if (hasAlphaChangedFrom(v.getAlpha())) {
+                ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.ALPHA, v.getAlpha(), alpha);
+                animators.add(animation.apply(AnimationProps.ALPHA, anim));
+            }
+            if (hasRectChangedFrom(v)) {
+                Rect fromViewRect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+                Rect toViewRect = new Rect();
+                rect.round(toViewRect);
+                ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(v,
+                        PropertyValuesHolder.ofObject(LTRB, Utilities.RECT_EVALUATOR,
+                                fromViewRect, toViewRect));
+                animators.add(animation.apply(AnimationProps.BOUNDS, anim));
+            }
+        }
+    }
+
+    /** Reset the transform on a view. */
+    public static void reset(TaskView v) {
+        v.setTranslationX(0f);
+        v.setTranslationY(0f);
+        v.setTranslationZ(0f);
+        v.setScaleX(1f);
+        v.setScaleY(1f);
+        v.setAlpha(1f);
+        v.getViewBounds().setClipBottom(0);
+        v.setLeftTopRightBottom(0, 0, 0, 0);
+    }
+
+    @Override
+    public String toString() {
+        return "R: " + rect + " V: " + visible;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/ViewPool.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/ViewPool.java
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
new file mode 100644
index 0000000..a029478
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 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.systemui.recents.views.grid;
+
+import android.view.View;
+import com.android.systemui.recents.views.AnimateableViewBounds;
+
+/* An outline provider for grid-based task views. */
+class AnimateableGridViewBounds extends AnimateableViewBounds {
+
+    public AnimateableGridViewBounds(View source, int cornerRadius) {
+        super(source, cornerRadius);
+    }
+
+    @Override
+    protected void updateClipBounds() {
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskView.java
new file mode 100644
index 0000000..8b4700c
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskView.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 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.systemui.recents.views.grid;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import com.android.systemui.R;
+import com.android.systemui.recents.views.AnimateableViewBounds;
+import com.android.systemui.recents.views.TaskView;
+
+public class GridTaskView extends TaskView {
+
+    /** The height, in pixels, of the header view. */
+    private int mHeaderHeight;
+
+    public GridTaskView(Context context) {
+        this(context, null);
+    }
+
+    public GridTaskView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public GridTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public GridTaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mHeaderHeight = context.getResources().getDimensionPixelSize(
+                R.dimen.recents_grid_task_view_header_height);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        // Show the full thumbnail and don't overlap with the header.
+        mThumbnailView.setSizeToFit(true);
+        mThumbnailView.setOverlayHeaderOnThumbnailActionBar(false);
+        mThumbnailView.updateThumbnailMatrix();
+        mThumbnailView.setTranslationY(mHeaderHeight);
+        mHeaderView.setShouldDarkenBackgroundColor(true);
+    }
+
+    @Override
+    protected AnimateableViewBounds createOutlineProvider() {
+        return new AnimateableGridViewBounds(this, mContext.getResources().getDimensionPixelSize(
+            R.dimen.recents_task_view_shadow_rounded_corners_radius));
+    }
+
+    @Override
+    protected void onConfigurationChanged() {
+        super.onConfigurationChanged();
+        mHeaderHeight = mContext.getResources().getDimensionPixelSize(
+                R.dimen.recents_grid_task_view_header_height);
+        mThumbnailView.setTranslationY(mHeaderHeight);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
rename to packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
new file mode 100644
index 0000000..719eaa7
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2016 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.systemui.recents.views.grid;
+
+import static com.android.systemui.recents.views.TaskStackLayoutAlgorithm.*;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
+import com.android.systemui.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
+import com.android.systemui.recents.views.TaskViewTransform;
+
+import java.util.ArrayList;
+
+public class TaskGridLayoutAlgorithm  {
+
+    private final String TAG = "TaskGridLayoutAlgorithm";
+    public static final int MAX_LAYOUT_TASK_COUNT = 8;
+
+    /** The horizontal padding around the whole recents view. */
+    private int mPaddingLeftRight;
+    /** The vertical padding around the whole recents view. */
+    private int mPaddingTopBottom;
+    /** The padding between task views. */
+    private int mPaddingTaskView;
+
+    private Rect mWindowRect;
+    private Point mScreenSize = new Point();
+
+    private Rect mTaskGridRect;
+
+    /** The height, in pixels, of each task view's title bar. */
+    private int mTitleBarHeight;
+
+    /** The aspect ratio of each task thumbnail, without the title bar. */
+    private float mAppAspectRatio;
+    private Rect mSystemInsets = new Rect();
+
+    /** The thickness of the focused task view frame. */
+    private int mFocusedFrameThickness;
+
+    /**
+     * When the amount of tasks is determined, the size and position of every task view can be
+     * decided. Each instance of TaskGridRectInfo store the task view information for a certain
+     * amount of tasks.
+     */
+    class TaskGridRectInfo {
+        Rect size;
+        int[] xOffsets;
+        int[] yOffsets;
+        int tasksPerLine;
+        int lines;
+
+        TaskGridRectInfo(int taskCount) {
+            size = new Rect();
+            xOffsets = new int[taskCount];
+            yOffsets = new int[taskCount];
+
+            int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
+            tasksPerLine = getTasksPerLine(layoutTaskCount);
+            lines = layoutTaskCount < 4 ? 1 : 2;
+
+            // A couple of special cases.
+            boolean landscapeWindow = mWindowRect.width() > mWindowRect.height();
+            boolean landscapeTaskView = mAppAspectRatio > 1;
+            // If we're in portrait but task views are landscape, show more lines of fewer tasks.
+            if (!landscapeWindow && landscapeTaskView) {
+                tasksPerLine = layoutTaskCount < 2 ? 1 : 2;
+                lines = layoutTaskCount < 3 ? 1 : (
+                        layoutTaskCount < 5 ? 2 : (
+                                layoutTaskCount < 7 ? 3 : 4));
+            }
+            // If we're in landscape but task views are portrait, show fewer lines of more tasks.
+            if (landscapeWindow && !landscapeTaskView) {
+                tasksPerLine = layoutTaskCount < 7 ? layoutTaskCount : 6;
+                lines = layoutTaskCount < 7 ? 1 : 2;
+            }
+
+            int taskWidth, taskHeight;
+            int maxTaskWidth = (mWindowRect.width() - 2 * mPaddingLeftRight
+                - (tasksPerLine - 1) * mPaddingTaskView) / tasksPerLine;
+            int maxTaskHeight = (mWindowRect.height() - 2 * mPaddingTopBottom
+                - (lines - 1) * mPaddingTaskView) / lines;
+
+            if (maxTaskHeight >= maxTaskWidth / mAppAspectRatio + mTitleBarHeight) {
+                // Width bound.
+                taskWidth = maxTaskWidth;
+                // Here we should round the height to the nearest integer.
+                taskHeight = (int) (maxTaskWidth / mAppAspectRatio + mTitleBarHeight + 0.5);
+            } else {
+                // Height bound.
+                taskHeight = maxTaskHeight;
+                // Here we should round the width to the nearest integer.
+                taskWidth = (int) ((taskHeight - mTitleBarHeight) * mAppAspectRatio + 0.5);
+            }
+            size.set(0, 0, taskWidth, taskHeight);
+
+            int emptySpaceX = mWindowRect.width() - 2 * mPaddingLeftRight
+                - (tasksPerLine * taskWidth) - (tasksPerLine - 1) * mPaddingTaskView;
+            int emptySpaceY = mWindowRect.height() - 2 * mPaddingTopBottom
+                - (lines * taskHeight) - (lines - 1) * mPaddingTaskView;
+            for (int taskIndex = 0; taskIndex < taskCount; taskIndex++) {
+                // We also need to invert the index in order to display the most recent tasks first.
+                int taskLayoutIndex = taskCount - taskIndex - 1;
+
+                int xIndex = taskLayoutIndex % tasksPerLine;
+                int yIndex = taskLayoutIndex / tasksPerLine;
+                xOffsets[taskIndex] = mWindowRect.left +
+                    emptySpaceX / 2 + mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
+                yOffsets[taskIndex] = mWindowRect.top +
+                    emptySpaceY / 2 + mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
+            }
+        }
+
+        private int getTasksPerLine(int taskCount) {
+            switch(taskCount) {
+                case 0:
+                    return 0;
+                case 1:
+                    return 1;
+                case 2:
+                case 4:
+                    return 2;
+                case 3:
+                case 5:
+                case 6:
+                    return 3;
+                case 7:
+                case 8:
+                    return 4;
+                default:
+                    throw new IllegalArgumentException("Unsupported task count " + taskCount);
+            }
+        }
+    }
+
+    /**
+     * We can find task view sizes and positions from mTaskGridRectInfoList[k - 1] when there
+     * are k tasks.
+     */
+    private TaskGridRectInfo[] mTaskGridRectInfoList;
+
+    public TaskGridLayoutAlgorithm(Context context) {
+        reloadOnConfigurationChange(context);
+    }
+
+    public void reloadOnConfigurationChange(Context context) {
+        Resources res = context.getResources();
+        mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
+        mFocusedFrameThickness = res.getDimensionPixelSize(
+            R.dimen.recents_grid_task_view_focused_frame_thickness);
+
+        mTaskGridRect = new Rect();
+        mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
+
+        WindowManager windowManager = (WindowManager) context
+                .getSystemService(Context.WINDOW_SERVICE);
+        windowManager.getDefaultDisplay().getRealSize(mScreenSize);
+
+        updateAppAspectRatio();
+    }
+
+    /**
+     * Returns the proper task view transform of a certain task view, according to its index and the
+     * amount of task views.
+     * @param taskIndex     The index of the task view whose transform we want. It's never greater
+     *                      than {@link MAX_LAYOUT_TASK_COUNT}.
+     * @param taskCount     The current amount of task views.
+     * @param transformOut  The result transform that this method returns.
+     * @param stackLayout   The base stack layout algorithm.
+     * @return  The expected transform of the (taskIndex)th task view.
+     */
+    public TaskViewTransform getTransform(int taskIndex, int taskCount,
+        TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
+        if (taskCount == 0) {
+            transformOut.reset();
+            return transformOut;
+        }
+
+        TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
+        mTaskGridRect.set(gridInfo.size);
+
+        int x = gridInfo.xOffsets[taskIndex];
+        int y = gridInfo.yOffsets[taskIndex];
+        float z = stackLayout.mMaxTranslationZ;
+
+        // We always set the dim alpha to 0, since we don't want grid task views to dim.
+        float dimAlpha = 0f;
+        // We always set the alpha of the view outline to 1, to make sure the shadow is visible.
+        float viewOutlineAlpha = 1f;
+
+        // We also need to invert the index in order to display the most recent tasks first.
+        int taskLayoutIndex = taskCount - taskIndex - 1;
+        boolean isTaskViewVisible = taskLayoutIndex < MAX_LAYOUT_TASK_COUNT;
+
+        // Fill out the transform
+        transformOut.scale = 1f;
+        transformOut.alpha = isTaskViewVisible ? 1f : 0f;
+        transformOut.translationZ = z;
+        transformOut.dimAlpha = dimAlpha;
+        transformOut.viewOutlineAlpha = viewOutlineAlpha;
+        transformOut.rect.set(mTaskGridRect);
+        transformOut.rect.offset(x, y);
+        Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+        // We only show the 8 most recent tasks.
+        transformOut.visible = isTaskViewVisible;
+        return transformOut;
+    }
+
+    /**
+     * Return the proper task index to focus for arrow key navigation.
+     * @param taskCount             The amount of tasks.
+     * @param currentFocusedIndex   The index of the currently focused task.
+     * @param direction             The direction we're navigating.
+     * @return  The index of the task that should get the focus.
+     */
+    public int navigateFocus(int taskCount, int currentFocusedIndex, Direction direction) {
+        if (taskCount < 1 || taskCount > MAX_LAYOUT_TASK_COUNT) {
+            return -1;
+        }
+        if (currentFocusedIndex == -1) {
+            return 0;
+        }
+        int newIndex = currentFocusedIndex;
+        final TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
+        final int currentLine = (taskCount - 1 - currentFocusedIndex) / gridInfo.tasksPerLine;
+        switch (direction) {
+            case UP:
+                newIndex += gridInfo.tasksPerLine;
+                newIndex = newIndex >= taskCount ? currentFocusedIndex : newIndex;
+                break;
+            case DOWN:
+                newIndex -= gridInfo.tasksPerLine;
+                newIndex = newIndex < 0 ? currentFocusedIndex : newIndex;
+                break;
+            case LEFT:
+                newIndex++;
+                final int leftMostIndex = (taskCount - 1) - currentLine * gridInfo.tasksPerLine;
+                newIndex = newIndex > leftMostIndex ? currentFocusedIndex : newIndex;
+                break;
+            case RIGHT:
+                newIndex--;
+                int rightMostIndex =
+                    (taskCount - 1) - (currentLine + 1) * gridInfo.tasksPerLine + 1;
+                rightMostIndex = rightMostIndex < 0 ? 0 : rightMostIndex;
+                newIndex = newIndex < rightMostIndex ? currentFocusedIndex : newIndex;
+                break;
+        }
+        return newIndex;
+    }
+
+    public void initialize(Rect windowRect) {
+        mWindowRect = windowRect;
+        // Define paddings in terms of percentage of the total area.
+        mPaddingLeftRight = (int) (0.025f * Math.min(mWindowRect.width(), mWindowRect.height()));
+        mPaddingTopBottom = (int) (0.1 * mWindowRect.height());
+
+        // Pre-calculate the positions and offsets of task views so that we can reuse them directly
+        // in the future.
+        mTaskGridRectInfoList = new TaskGridRectInfo[MAX_LAYOUT_TASK_COUNT];
+        for (int i = 0; i < MAX_LAYOUT_TASK_COUNT; i++) {
+            mTaskGridRectInfoList[i] = new TaskGridRectInfo(i + 1);
+        }
+    }
+
+    public void setSystemInsets(Rect systemInsets) {
+        mSystemInsets = systemInsets;
+        updateAppAspectRatio();
+    }
+
+    private void updateAppAspectRatio() {
+        int usableWidth = mScreenSize.x - mSystemInsets.left - mSystemInsets.right;
+        int usableHeight = mScreenSize.y - mSystemInsets.top - mSystemInsets.bottom;
+        mAppAspectRatio = (float) usableWidth / (float) usableHeight;
+    }
+
+    public Rect getStackActionButtonRect() {
+        Rect buttonRect = new Rect(mWindowRect);
+        buttonRect.right -= mPaddingLeftRight;
+        buttonRect.left += mPaddingLeftRight;
+        buttonRect.bottom = buttonRect.top + mPaddingTopBottom;
+        return buttonRect;
+    }
+
+    public void updateTaskGridRect(int taskCount) {
+        if (taskCount > 0) {
+            TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
+            mTaskGridRect.set(gridInfo.size);
+        }
+    }
+
+    public Rect getTaskGridRect() {
+        return mTaskGridRect;
+    }
+
+    public int getFocusFrameThickness() {
+        return mFocusedFrameThickness;
+    }
+
+    public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
+        int visibleCount = Math.min(TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT, tasks.size());
+        return new VisibilityReport(visibleCount, visibleCount);
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
new file mode 100644
index 0000000..1655f6c
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 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.systemui.recents.views.grid;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
+import com.android.systemui.R;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskStackView;
+
+public class TaskViewFocusFrame extends View implements OnGlobalFocusChangeListener {
+
+    private TaskStackView mSv;
+    private TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
+    public TaskViewFocusFrame(Context context) {
+        this(context, null);
+    }
+
+    public TaskViewFocusFrame(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr,
+        int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        setBackground(mContext.getDrawable(
+            R.drawable.recents_grid_task_view_focus_frame_background));
+        setFocusable(false);
+        hide();
+    }
+
+    public TaskViewFocusFrame(Context context, TaskStackView stackView,
+        TaskGridLayoutAlgorithm taskGridLayoutAlgorithm) {
+        this(context);
+        mSv = stackView;
+        mTaskGridLayoutAlgorithm = taskGridLayoutAlgorithm;
+    }
+
+    /**
+     * Measure the width and height of the focus frame according to the current grid task view size.
+     */
+    public void measure() {
+        int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
+        Rect rect = mTaskGridLayoutAlgorithm.getTaskGridRect();
+        measure(
+            MeasureSpec.makeMeasureSpec(rect.width() + thickness * 2, MeasureSpec.EXACTLY),
+            MeasureSpec.makeMeasureSpec(rect.height() + thickness * 2, MeasureSpec.EXACTLY));
+    }
+
+    /**
+     * Layout the focus frame with its size.
+     */
+    public void layout() {
+        layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
+    }
+
+    /**
+     * Update the current size of grid task view and the focus frame.
+     */
+    public void resize() {
+        if (mSv.useGridLayout()) {
+            mTaskGridLayoutAlgorithm.updateTaskGridRect(mSv.getStack().getTaskCount());
+            measure();
+            requestLayout();
+        }
+    }
+
+    /**
+     * Move the task view focus frame to surround the newly focused view. If it's {@code null} or
+     * it's not an instance of GridTaskView, we hide the focus frame.
+     * @param newFocus The newly focused view.
+     */
+    public void moveGridTaskViewFocus(View newFocus) {
+        if (mSv.useGridLayout()) {
+            // The frame only shows up in the grid layout. It shouldn't show up in the stack
+            // layout including when we're in the split screen.
+            if (newFocus instanceof GridTaskView) {
+                // If the focus goes to a GridTaskView, we show the frame and layout it.
+                int[] location = new int[2];
+                newFocus.getLocationInWindow(location);
+                int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
+                setTranslationX(location[0] - thickness);
+                setTranslationY(location[1] - thickness);
+                show();
+            } else {
+                // If focus goes to other views, we hide the frame.
+                hide();
+            }
+        }
+    }
+
+    @Override
+    public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+        if (!mSv.useGridLayout()) {
+            return;
+        }
+        if (newFocus == null) {
+            // We're going to touch mode, unset the focus.
+            moveGridTaskViewFocus(null);
+            return;
+        }
+        if (oldFocus == null) {
+            // We're returning from touch mode, set the focus to the previously focused task.
+            final TaskStack stack = mSv.getStack();
+            final int taskCount = stack.getTaskCount();
+            final int k = stack.indexOfTask(mSv.getFocusedTask());
+            final int taskIndexToFocus = k == -1 ? (taskCount - 1) : (k % taskCount);
+            mSv.setFocusedTask(taskIndexToFocus, false, true);
+        }
+    }
+
+    private void show() {
+        setAlpha(1f);
+    }
+
+    private void hide() {
+        setAlpha(0f);
+    }
+}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java
new file mode 100644
index 0000000..15c7c87
--- /dev/null
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2017 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.systemui.recents.views.lowram;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.ViewConfiguration;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.LegacyRecentsImpl;
+import com.android.systemui.recents.RecentsActivityLaunchState;
+import com.android.systemui.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
+import com.android.systemui.recents.views.TaskViewTransform;
+
+import java.util.ArrayList;
+
+import static com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
+
+public class TaskStackLowRamLayoutAlgorithm {
+
+    private static final String TAG = "TaskStackLowRamLayoutAlgorithm";
+    private static final float MAX_OVERSCROLL = 0.2f / 0.3f;
+
+    public static final int MAX_LAYOUT_TASK_COUNT = 9;
+    public static final int NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME = 2;
+    public static final int NUM_TASK_VISIBLE_LAUNCHED_FROM_APP =
+            NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME + 1;
+    private Rect mWindowRect;
+
+    private int mFlingThreshold;
+    private int mPadding;
+    private int mPaddingLeftRight;
+    private int mTopOffset;
+    private int mPaddingEndTopBottom;
+    private Rect mTaskRect = new Rect();
+    private Rect mSystemInsets = new Rect();
+
+    public TaskStackLowRamLayoutAlgorithm(Context context) {
+        reloadOnConfigurationChange(context);
+    }
+
+    public void reloadOnConfigurationChange(Context context) {
+        mPadding = context.getResources()
+                .getDimensionPixelSize(R.dimen.recents_layout_side_margin_phone);
+        mFlingThreshold = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
+    }
+
+    public void initialize(Rect windowRect) {
+        mWindowRect = windowRect;
+        if (mWindowRect.height() > 0) {
+            int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
+            int windowWidth = mWindowRect.width() - mSystemInsets.right - mSystemInsets.left;
+            int width = Math.min(windowWidth, windowHeight) - mPadding * 2;
+            boolean isLandscape = windowWidth > windowHeight;
+            mTaskRect.set(0, 0, width, isLandscape ? width * 2 / 3 : width);
+            mPaddingLeftRight = (windowWidth - mTaskRect.width()) / 2;
+            mPaddingEndTopBottom = (windowHeight - mTaskRect.height()) / 2;
+
+            // Compute the top offset to center tasks in the middle of the screen
+            mTopOffset = (getTotalHeightOfTasks(MAX_LAYOUT_TASK_COUNT) - windowHeight) / 2;
+        }
+    }
+
+    public void setSystemInsets(Rect systemInsets) {
+        mSystemInsets = systemInsets;
+    }
+
+    public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
+        RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
+        int maxVisible = launchState.launchedFromHome || launchState.launchedFromPipApp
+                    || launchState.launchedWithNextPipApp
+                ? NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME
+                : NUM_TASK_VISIBLE_LAUNCHED_FROM_APP;
+        int visibleCount = Math.min(maxVisible, tasks.size());
+        return new VisibilityReport(visibleCount, visibleCount);
+    }
+
+    public void getFrontOfStackTransform(TaskViewTransform transformOut,
+            TaskStackLayoutAlgorithm stackLayout) {
+        if (mWindowRect == null) {
+            transformOut.reset();
+            return;
+        }
+
+        // Calculate the static task y position 2 tasks after/below the middle/current task
+        int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
+        int bottomOfCurrentTask = (windowHeight + mTaskRect.height()) / 2;
+        int y = bottomOfCurrentTask + mTaskRect.height() + mPadding * 2;
+        fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, true);
+    }
+
+    public void getBackOfStackTransform(TaskViewTransform transformOut,
+            TaskStackLayoutAlgorithm stackLayout) {
+        if (mWindowRect == null) {
+            transformOut.reset();
+            return;
+        }
+
+        // Calculate the static task y position 2 tasks before/above the middle/current task
+        int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
+        int topOfCurrentTask = (windowHeight - mTaskRect.height()) / 2;
+        int y = topOfCurrentTask - (mTaskRect.height() + mPadding) * 2;
+        fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, true);
+    }
+
+    public TaskViewTransform getTransform(int taskIndex, float stackScroll,
+            TaskViewTransform transformOut, int taskCount, TaskStackLayoutAlgorithm stackLayout) {
+        if (taskCount == 0) {
+            transformOut.reset();
+            return transformOut;
+        }
+        boolean visible = true;
+        int y;
+        if (taskCount > 1) {
+            y = getTaskTopFromIndex(taskIndex) - percentageToScroll(stackScroll);
+
+            // Check visibility from the bottom of the task
+            visible = y + mPadding + getTaskRect().height() > 0;
+        } else {
+            int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
+            y = (windowHeight - mTaskRect.height()) / 2 - percentageToScroll(stackScroll);
+        }
+        fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, visible);
+        return transformOut;
+    }
+
+    /**
+     * Finds the closest task to the scroll percentage in the y axis and returns the percentage of
+     * the task to scroll to.
+     * @param scrollP percentage to find nearest to
+     * @param numTasks number of tasks in recents stack
+     * @param velocity speed of fling
+     */
+    public float getClosestTaskP(float scrollP, int numTasks, int velocity) {
+        int y = percentageToScroll(scrollP);
+
+        int lastY = getTaskTopFromIndex(0) - mPaddingEndTopBottom;
+        for (int i = 1; i < numTasks; i++) {
+            int taskY = getTaskTopFromIndex(i) - mPaddingEndTopBottom;
+            int diff = taskY - y;
+            if (diff > 0) {
+                int diffPrev = Math.abs(y - lastY);
+                boolean useNext = diff > diffPrev;
+                if (Math.abs(velocity) > mFlingThreshold) {
+                    useNext = velocity > 0;
+                }
+                return useNext
+                        ? scrollToPercentage(lastY) : scrollToPercentage(taskY);
+            }
+            lastY = taskY;
+        }
+        return scrollToPercentage(lastY);
+    }
+
+    /**
+     * Convert a scroll value to a percentage
+     * @param scroll a scroll value
+     * @return a percentage that represents the scroll from the total height of tasks
+     */
+    public float scrollToPercentage(int scroll) {
+        return (float) scroll / (mTaskRect.height() + mPadding);
+    }
+
+    /**
+     * Converts a percentage to the scroll value from the total height of tasks
+     * @param p a percentage that represents the scroll value
+     * @return a scroll value in pixels
+     */
+    public int percentageToScroll(float p) {
+        return (int) (p * (mTaskRect.height() + mPadding));
+    }
+
+    /**
+     * Get the min scroll progress for low ram layout. This computes the top position of the
+     * first task and reduce by the end padding to center the first task
+     * @return position of max scroll
+     */
+    public float getMinScrollP() {
+        return getScrollPForTask(0);
+    }
+
+    /**
+     * Get the max scroll progress for low ram layout. This computes the top position of the last
+     * task and reduce by the end padding to center the last task
+     * @param taskCount the amount of tasks in the recents stack
+     * @return position of max scroll
+     */
+    public float getMaxScrollP(int taskCount) {
+        return getScrollPForTask(taskCount - 1);
+    }
+
+    /**
+     * Get the initial scroll value whether launched from home or from an app.
+     * @param taskCount the amount of tasks currently in recents
+     * @param fromHome if launching recents from home or not
+     * @return from home it will return max value and from app it will return 2nd last task
+     */
+    public float getInitialScrollP(int taskCount, boolean fromHome) {
+        if (fromHome) {
+            return getMaxScrollP(taskCount);
+        }
+        if (taskCount < 2) {
+            return 0;
+        }
+        return getScrollPForTask(taskCount - 2);
+    }
+
+    /**
+     * Get the scroll progress for any task
+     * @param taskIndex task index to get the scroll progress of
+     * @return scroll progress of task
+     */
+    public float getScrollPForTask(int taskIndex) {
+        return scrollToPercentage(getTaskTopFromIndex(taskIndex) - mPaddingEndTopBottom);
+    }
+
+    public Rect getTaskRect() {
+        return mTaskRect;
+    }
+
+    public float getMaxOverscroll() {
+        return MAX_OVERSCROLL;
+    }
+
+    private int getTaskTopFromIndex(int index) {
+        return getTotalHeightOfTasks(index) - mTopOffset;
+    }
+
+    private int getTotalHeightOfTasks(int taskCount) {
+        return taskCount * mTaskRect.height() + (taskCount + 1) * mPadding;
+    }
+
+    private void fillStackTransform(TaskViewTransform transformOut, int y, int translationZ,
+            boolean visible) {
+        transformOut.scale = 1f;
+        transformOut.alpha = 1f;
+        transformOut.translationZ = translationZ;
+        transformOut.dimAlpha = 0f;
+        transformOut.viewOutlineAlpha = 1f;
+        transformOut.rect.set(getTaskRect());
+        transformOut.rect.offset(mPaddingLeftRight + mSystemInsets.left, y);
+        Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+        transformOut.visible = visible;
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingPlugin.java
new file mode 100644
index 0000000..dce02e1
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingPlugin.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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.systemui.plugins;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Used to capture Falsing data (related to unlocking the screen).
+ *
+ * The intent is that the data can later be analyzed to validate the quality of the
+ * {@link com.android.systemui.classifier.FalsingManager}.
+ */
+@ProvidesInterface(action = FalsingPlugin.ACTION, version = FalsingPlugin.VERSION)
+public interface FalsingPlugin extends Plugin {
+    String ACTION = "com.android.systemui.action.FALSING_PLUGIN";
+    int VERSION = 1;
+
+    /**
+     * Called when there is data to be recorded.
+     *
+     * @param success Indicates whether the action is considered a success.
+     * @param data The raw data to be recorded for analysis.
+     */
+    void dataCollected(boolean success, byte[] data);
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index bf4374a..bca3530 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -116,6 +116,7 @@
         public boolean isTransient = false;
         public String expandedAccessibilityClassName;
         public SlashState slash;
+        public boolean handlesLongClick = true;
 
         public boolean copyTo(State other) {
             if (other == null) throw new IllegalArgumentException();
@@ -133,7 +134,8 @@
                     || !Objects.equals(other.state, state)
                     || !Objects.equals(other.isTransient, isTransient)
                     || !Objects.equals(other.dualTarget, dualTarget)
-                    || !Objects.equals(other.slash, slash);
+                    || !Objects.equals(other.slash, slash)
+                    || !Objects.equals(other.handlesLongClick, handlesLongClick);
             other.icon = icon;
             other.iconSupplier = iconSupplier;
             other.label = label;
@@ -146,6 +148,7 @@
             other.dualTarget = dualTarget;
             other.isTransient = isTransient;
             other.slash = slash != null ? slash.copy() : null;
+            other.handlesLongClick = handlesLongClick;
             return changed;
         }
 
diff --git a/packages/SystemUI/plugin/update_plugin_lib.sh b/packages/SystemUI/plugin/update_plugin_lib.sh
index a105b45..34f4895 100755
--- a/packages/SystemUI/plugin/update_plugin_lib.sh
+++ b/packages/SystemUI/plugin/update_plugin_lib.sh
@@ -5,7 +5,7 @@
 mkdir /tmp/plugin_classes
 
 # Compile the jar
-javac -cp $ANDROID_BUILD_TOP/out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar:$ANDROID_BUILD_TOP/out/target/common/obj/JAVA_LIBRARIES/core-all_intermediates/classes.jar `find src -name *.java` -d /tmp/plugin_classes/
+javac -cp $ANDROID_BUILD_TOP/out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar:$ANDROID_BUILD_TOP/out/target/common/obj/JAVA_LIBRARIES/core-all_intermediates/classes.jar `find ../plugin*/src -name *.java` -d /tmp/plugin_classes/
 echo "" >> /tmp/plugin_classes/manifest.txt
 jar cvfm SystemUIPluginLib.jar /tmp/plugin_classes/manifest.txt -C /tmp/plugin_classes .
 
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index fa4c8b5..ee94aed 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -10,6 +10,7 @@
   public void setGlowScale(float);
 }
 
+-keep class com.android.systemui.recents.OverviewProxyRecentsImpl
 -keep class com.android.systemui.statusbar.car.CarStatusBar
 -keep class com.android.systemui.statusbar.phone.StatusBar
 -keep class com.android.systemui.statusbar.tv.TvStatusBar
@@ -17,21 +18,6 @@
 -keep class com.android.systemui.SystemUIFactory
 -keep class * extends com.android.systemui.SystemUI
 
--keepclassmembers class ** {
-    public void onBusEvent(**);
-    public void onInterprocessBusEvent(**);
-}
--keepclassmembers class ** extends **.EventBus$InterprocessEvent {
-    public <init>(android.os.Bundle);
-}
-
--keep class com.android.systemui.recents.views.TaskView {
-    public int getDim();
-    public void setDim(int);
-    public float getTaskProgress();
-    public void setTaskProgress(float);
-}
-
 -keepclasseswithmembers class * {
     public <init>(android.content.Context, android.util.AttributeSet);
 }
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index 00f8f86..b51ad1c 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -28,17 +28,6 @@
     android:clipToPadding="false"
     android:orientation="vertical"
     android:layout_centerHorizontal="true">
-    <view class="com.android.keyguard.KeyguardSliceView$TitleView"
-              android:id="@+id/title"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:layout_marginBottom="@dimen/widget_title_bottom_margin"
-              android:paddingStart="64dp"
-              android:paddingEnd="64dp"
-              android:visibility="gone"
-              android:textColor="?attr/wallpaperTextColor"
-              android:theme="@style/TextAppearance.Keyguard"
-    />
     <view class="com.android.keyguard.KeyguardSliceView$Row"
               android:id="@+id/row"
               android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 0462347..9baeaaa 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -64,6 +64,7 @@
         <item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
         <item name="android:fontFeatureSettings">@*android:string/config_headlineFontFeatureSettings</item>
+        <item name="android:ellipsize">none</item>
     </style>
 
     <style name="BouncerSecurityContainer">
diff --git a/packages/SystemUI/res/drawable/biometric_dialog_bg.xml b/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
index 335448d..d041556 100644
--- a/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
+++ b/packages/SystemUI/res/drawable/biometric_dialog_bg.xml
@@ -17,10 +17,10 @@
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="@color/biometric_dialog_bg_color" />
+    <solid android:color="?android:attr/colorBackgroundFloating" />
     <corners android:radius="1dp"
         android:topLeftRadius="@dimen/biometric_dialog_corner_size"
         android:topRightRadius="@dimen/biometric_dialog_corner_size"
-        android:bottomLeftRadius="0dp"
-        android:bottomRightRadius="0dp"/>
-</shape>
\ No newline at end of file
+        android:bottomLeftRadius="@dimen/biometric_dialog_corner_size"
+        android:bottomRightRadius="@dimen/biometric_dialog_corner_size"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
index 05fd467..af25e44 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
@@ -36,7 +36,7 @@
                         android:name="_R_G_L_2_G_D_0_P_0"
                         android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_biometric_color"
+                        android:strokeColor="?android:attr/colorAccent"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -47,7 +47,7 @@
                         android:name="_R_G_L_2_G_D_1_P_0"
                         android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_biometric_color"
+                        android:strokeColor="?android:attr/colorAccent"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -58,7 +58,7 @@
                         android:name="_R_G_L_2_G_D_2_P_0"
                         android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_biometric_color"
+                        android:strokeColor="?android:attr/colorAccent"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -69,7 +69,7 @@
                         android:name="_R_G_L_2_G_D_3_P_0"
                         android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_biometric_color"
+                        android:strokeColor="?android:attr/colorAccent"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -80,7 +80,7 @@
                         android:name="_R_G_L_2_G_D_4_P_0"
                         android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_biometric_color"
+                        android:strokeColor="?android:attr/colorAccent"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -97,7 +97,7 @@
                         android:name="_R_G_L_1_G_D_0_P_0"
                         android:pathData=" M0 -9 C4.97,-9 9,-4.97 9,0 C9,4.97 4.97,9 0,9 C-4.97,9 -9,4.97 -9,0 C-9,-4.97 -4.97,-9 0,-9c "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_error_color"
+                        android:strokeColor="?android:attr/colorError"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="2"
@@ -118,7 +118,7 @@
                         <path
                             android:name="_R_G_L_0_G_D_0_P_0"
                             android:fillAlpha="1"
-                            android:fillColor="@color/biometric_dialog_error_color"
+                            android:fillColor="?android:attr/colorError"
                             android:fillType="nonZero"
                             android:pathData=" M1.1 3.94 C1.1,4.55 0.61,5.04 0,5.04 C-0.61,5.04 -1.1,4.55 -1.1,3.94 C-1.1,3.33 -0.61,2.84 0,2.84 C0.61,2.84 1.1,3.33 1.1,3.94c " />
                     </group>
@@ -131,7 +131,7 @@
                         <path
                             android:name="_R_G_L_0_G_D_0_P_1"
                             android:fillAlpha="1"
-                            android:fillColor="@color/biometric_dialog_error_color"
+                            android:fillColor="?android:attr/colorError"
                             android:fillType="nonZero"
                             android:pathData=" M1 -4.06 C1,-4.06 1,-0.06 1,-0.06 C1,0.49 0.55,0.94 0,0.94 C-0.55,0.94 -1,0.49 -1,-0.06 C-1,-0.06 -1,-4.06 -1,-4.06 C-1,-4.61 -0.55,-5.06 0,-5.06 C0.55,-5.06 1,-4.61 1,-4.06c " />
                     </group>
@@ -522,4 +522,4 @@
             </set>
         </aapt:attr>
     </target>
-</animated-vector>
\ No newline at end of file
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
index fd0ab22..1a7a846 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
@@ -36,7 +36,7 @@
                         android:name="_R_G_L_3_G_D_0_P_0"
                         android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_biometric_color"
+                        android:strokeColor="?android:attr/colorAccent"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -47,7 +47,7 @@
                         android:name="_R_G_L_3_G_D_1_P_0"
                         android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_biometric_color"
+                        android:strokeColor="?android:attr/colorAccent"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -58,7 +58,7 @@
                         android:name="_R_G_L_3_G_D_2_P_0"
                         android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_biometric_color"
+                        android:strokeColor="?android:attr/colorAccent"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -69,7 +69,7 @@
                         android:name="_R_G_L_3_G_D_3_P_0"
                         android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_biometric_color"
+                        android:strokeColor="?android:attr/colorAccent"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -80,7 +80,7 @@
                         android:name="_R_G_L_3_G_D_4_P_0"
                         android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_biometric_color"
+                        android:strokeColor="?android:attr/colorAccent"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -101,7 +101,7 @@
                         android:name="_R_G_L_2_G_D_0_P_0"
                         android:pathData=" M-25.36 -24.41 C-25.93,-24.31 -26.49,-24.27 -26.81,-24.27 C-28.11,-24.27 -29.35,-24.62 -30.43,-25.4 C-32.11,-26.6 -33.2,-28.57 -33.2,-30.79 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_error_color"
+                        android:strokeColor="?android:attr/colorError"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -112,7 +112,7 @@
                         android:name="_R_G_L_2_G_D_1_P_0"
                         android:pathData=" M-36.14 -21.78 C-37.15,-22.98 -37.72,-23.7 -38.51,-25.29 C-39.33,-26.94 -39.82,-28.78 -39.82,-30.77 C-39.82,-34.43 -36.85,-37.4 -33.19,-37.4 C-29.52,-37.4 -26.55,-34.43 -26.55,-30.77 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_error_color"
+                        android:strokeColor="?android:attr/colorError"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -123,7 +123,7 @@
                         android:name="_R_G_L_2_G_D_2_P_0"
                         android:pathData=" M-42.19 -25.68 C-42.95,-27.82 -43.09,-29.54 -43.09,-30.8 C-43.09,-32.27 -42.84,-33.65 -42.27,-34.9 C-40.71,-38.35 -37.24,-40.75 -33.2,-40.75 C-27.71,-40.75 -23.26,-36.3 -23.26,-30.8 C-23.26,-28.97 -24.74,-27.49 -26.57,-27.49 C-28.4,-27.49 -29.89,-28.97 -29.89,-30.8 C-29.89,-32.64 -31.37,-34.12 -33.2,-34.12 C-35.04,-34.12 -36.52,-32.64 -36.52,-30.8 C-36.52,-28.23 -35.53,-25.92 -33.92,-24.22 C-32.69,-22.93 -31.48,-22.12 -29.44,-21.53 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_error_color"
+                        android:strokeColor="?android:attr/colorError"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -134,7 +134,7 @@
                         android:name="_R_G_L_2_G_D_3_P_0"
                         android:pathData=" M-44.06 -38.17 C-42.87,-39.94 -41.39,-41.41 -39.51,-42.44 C-37.62,-43.47 -35.46,-44.05 -33.16,-44.05 C-30.88,-44.05 -28.72,-43.47 -26.85,-42.45 C-24.97,-41.43 -23.48,-39.97 -22.29,-38.21 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_error_color"
+                        android:strokeColor="?android:attr/colorError"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -145,7 +145,7 @@
                         android:name="_R_G_L_2_G_D_4_P_0"
                         android:pathData=" M-25.72 -45.45 C-27.99,-46.76 -30.43,-47.52 -33.28,-47.52 C-36.13,-47.52 -38.51,-46.74 -40.62,-45.45 "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_error_color"
+                        android:strokeColor="?android:attr/colorError"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="1.45"
@@ -162,7 +162,7 @@
                         android:name="_R_G_L_1_G_D_0_P_0"
                         android:pathData=" M0 -9 C4.97,-9 9,-4.97 9,0 C9,4.97 4.97,9 0,9 C-4.97,9 -9,4.97 -9,0 C-9,-4.97 -4.97,-9 0,-9c "
                         android:strokeAlpha="1"
-                        android:strokeColor="@color/biometric_dialog_error_color"
+                        android:strokeColor="?android:attr/colorError"
                         android:strokeLineCap="round"
                         android:strokeLineJoin="round"
                         android:strokeWidth="2"
@@ -183,7 +183,7 @@
                         <path
                             android:name="_R_G_L_0_G_D_0_P_0"
                             android:fillAlpha="1"
-                            android:fillColor="@color/biometric_dialog_error_color"
+                            android:fillColor="?android:attr/colorError"
                             android:fillType="nonZero"
                             android:pathData=" M1.1 3.94 C1.1,4.55 0.61,5.04 0,5.04 C-0.61,5.04 -1.1,4.55 -1.1,3.94 C-1.1,3.33 -0.61,2.84 0,2.84 C0.61,2.84 1.1,3.33 1.1,3.94c " />
                     </group>
@@ -196,7 +196,7 @@
                         <path
                             android:name="_R_G_L_0_G_D_0_P_1"
                             android:fillAlpha="1"
-                            android:fillColor="@color/biometric_dialog_error_color"
+                            android:fillColor="?android:attr/colorError"
                             android:fillType="nonZero"
                             android:pathData=" M1 -4.06 C1,-4.06 1,-0.06 1,-0.06 C1,0.49 0.55,0.94 0,0.94 C-0.55,0.94 -1,0.49 -1,-0.06 C-1,-0.06 -1,-4.06 -1,-4.06 C-1,-4.61 -0.55,-5.06 0,-5.06 C0.55,-5.06 1,-4.61 1,-4.06c " />
                     </group>
@@ -851,4 +851,4 @@
             </set>
         </aapt:attr>
     </target>
-</animated-vector>
\ No newline at end of file
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
new file mode 100644
index 0000000..8247c27
--- /dev/null
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#bbbbbb" />
+    <padding android:padding="@dimen/ongoing_appops_chip_bg_padding" />
+    <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
index 0417e2e..67c0adf 100644
--- a/packages/SystemUI/res/layout/biometric_dialog.xml
+++ b/packages/SystemUI/res/layout/biometric_dialog.xml
@@ -31,7 +31,8 @@
 
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content">
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
 
         <!-- This is not a Space since Spaces cannot be clicked. The width of this changes depending
          on horizontal/portrait orientation -->
@@ -43,11 +44,13 @@
 
         <LinearLayout
             android:id="@+id/dialog"
-            android:layout_width="0dp"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical"
-            android:elevation="2dp"
-            android:background="@drawable/biometric_dialog_bg">
+            android:background="@drawable/biometric_dialog_bg"
+            android:layout_marginBottom="@dimen/biometric_dialog_border_padding"
+            android:layout_marginLeft="@dimen/biometric_dialog_border_padding"
+            android:layout_marginRight="@dimen/biometric_dialog_border_padding">
 
             <TextView
                 android:id="@+id/title"
@@ -63,7 +66,7 @@
                 android:singleLine="true"
                 android:ellipsize="marquee"
                 android:marqueeRepeatLimit="marquee_forever"
-                android:textColor="@color/biometric_dialog_text_dark_color"/>
+                android:textColor="?android:attr/textColorPrimary"/>
 
             <TextView
                 android:id="@+id/subtitle"
@@ -78,7 +81,7 @@
                 android:singleLine="true"
                 android:ellipsize="marquee"
                 android:marqueeRepeatLimit="marquee_forever"
-                android:textColor="@color/biometric_dialog_text_dark_color"/>
+                android:textColor="?android:attr/textColorPrimary"/>
 
             <TextView
                 android:id="@+id/description"
@@ -90,7 +93,7 @@
                 android:paddingTop="8dp"
                 android:textSize="16sp"
                 android:maxLines="4"
-                android:textColor="@color/biometric_dialog_text_dark_color"/>
+                android:textColor="?android:attr/textColorPrimary"/>
 
             <ImageView
                 android:id="@+id/biometric_icon"
@@ -112,7 +115,7 @@
                 android:gravity="center_horizontal"
                 android:accessibilityLiveRegion="polite"
                 android:contentDescription="@string/accessibility_biometric_dialog_help_area"
-                android:textColor="@color/biometric_dialog_text_light_color"/>
+                android:textColor="?android:attr/textColorSecondary"/>
 
             <LinearLayout
                 android:layout_width="match_parent"
@@ -164,4 +167,4 @@
 
     </LinearLayout>
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
new file mode 100644
index 0000000..5e952e3
--- /dev/null
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 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.
+-->
+
+<com.android.systemui.privacy.OngoingPrivacyChip
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/privacy_chip"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:layout_margin="@dimen/ongoing_appops_chip_margin"
+    android:gravity="center_vertical|end"
+    android:orientation="horizontal"
+    android:paddingStart="@dimen/ongoing_appops_chip_side_padding"
+    android:paddingEnd="@dimen/ongoing_appops_chip_side_padding"
+    android:background="@drawable/privacy_chip_bg"
+    android:focusable="true">
+
+        <LinearLayout
+            android:id="@+id/icons_container"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            android:gravity="center_vertical|start"
+            />
+
+        <TextView
+            android:id="@+id/app_name"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            android:gravity="center_vertical|end"
+        />
+</com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
new file mode 100644
index 0000000..b5e24a0
--- /dev/null
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:fillViewport ="true"
+            android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/dialog_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:padding="@dimen/ongoing_appops_dialog_content_padding">
+
+        <LinearLayout
+            android:id="@+id/icons_container"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+            android:orientation="horizontal"
+            android:gravity="center"
+            android:importantForAccessibility="no"
+        />
+
+        <LinearLayout
+            android:id="@+id/text_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:gravity="start"
+        />
+    </LinearLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml
new file mode 100644
index 0000000..5595b13
--- /dev/null
+++ b/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 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.
+-->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:textDirection="locale"
+    android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary"
+/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index 680112c..007070e 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -46,6 +46,8 @@
         android:layout_weight="1"
         android:gravity="center_vertical|center_horizontal" />
 
+    <include layout="@layout/ongoing_privacy_chip" />
+
     <com.android.systemui.BatteryMeterView
         android:id="@+id/battery"
         android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
new file mode 100644
index 0000000..6c5c7fa
--- /dev/null
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:clipChildren="false"
+              android:clipToPadding="false"
+              android:gravity="top"
+              android:orientation="vertical">
+
+    <Space
+        android:layout_width="match_parent"
+        android:layout_height="10dp"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:background="@android:color/white">
+        <CheckBox
+            android:id="@+id/checkbox_mic"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/screenrecord_mic_label"/>
+        <CheckBox
+            android:id="@+id/checkbox_taps"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/screenrecord_taps_label"/>
+        <Button
+            android:id="@+id/record_button"
+            android:layout_width="match_parent"
+            android:layout_height="50dp"
+            android:text="@string/screenrecord_start_label"
+        />
+    </LinearLayout>
+
+    <Space
+        android:layout_width="match_parent"
+        android:layout_height="10dp"/>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_no_recent_apps.xml b/packages/SystemUI/res/layout/status_bar_no_recent_apps.xml
deleted file mode 100644
index adfaed13..0000000
--- a/packages/SystemUI/res/layout/status_bar_no_recent_apps.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2011, 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.
-*/
--->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="match_parent"
-    android:layout_width="match_parent"
-    >
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="20dp"
-        android:textColor="@android:color/holo_blue_light"
-        android:text="@string/status_bar_no_recent_apps"
-        android:gravity="center_horizontal"
-        android:layout_gravity="center"
-    />
-</FrameLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 395de19..1835a56 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Stelsel-UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Maak skoon"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Verwyder uit lys"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Program Info"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Jou onlangse skerms verskyn hier"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Maak onlangse programme toe"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d skerms in Oorsig</item>
-      <item quantity="one">1 skerm in Oorsig</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Geen kennisgewings"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Voortdurend"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Kennisgewings"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"maak foon oop"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"maak stembystand oop"</string>
     <string name="camera_label" msgid="7261107956054836961">"maak kamera oop"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Kies nuwe taakuitleg"</string>
     <string name="cancel" msgid="6442560571259935130">"Kanselleer"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Hulpboodskapgebied"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bevestig"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Maak <xliff:g id="APP">%s</xliff:g> toe."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> verwerp."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle onlangse programme is toegemaak."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Maak <xliff:g id="APP">%s</xliff:g>-programinligting oop."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Begin tans <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Kennisgewing is toegemaak."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Kennisgewingskerm."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Vinnige instellings."</string>
@@ -319,8 +305,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Onbenoemde toestel"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Gereed om uit te saai"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Geen toestelle beskikbaar nie"</string>
-    <!-- no translation found for quick_settings_cast_no_wifi (2696477881905521882) -->
-    <skip />
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi is nie gekoppel nie"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Helderheid"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Keer kleure om"</string>
@@ -356,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC is gedeaktiveer"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC is geaktiveer"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Geen onlangse items nie"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Jy het alles toegemaak"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Programinligting"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"skermvaspen"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"soek"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Kon nie <xliff:g id="APP">%s</xliff:g> begin nie."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> is in veiligmodus gedeaktiveer."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Vee alles uit"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Trek hier om verdeelde skerm te gebruik"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Swiep op om programme te wissel"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Sleep regs om programme vinnig te wissel"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Verdeel horisontaal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Verdeel vertikaal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Verdeel gepasmaak"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Verdeel skerm na bo"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Verdeel skerm na links"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Verdeel skerm na regs"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Wissel oorsig"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Gelaai"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Laai tans"</string>
@@ -619,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimeer"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Hou aan om kennisgewings van hierdie program af te wys?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Hierdie kennisgewings kan nie afgeskakel word nie"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Hierdie program gebruik tans die kamera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Hierdie program gebruik tans die mikrofoon."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Hierdie program wys tans bo-oor ander programme op jou skerm."</string>
@@ -753,6 +725,8 @@
     <item msgid="2139628951880142927">"Wys persentasie wanneer gelaai word (verstek)"</item>
     <item msgid="3327323682209964956">"Moenie hierdie ikoon wys nie"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Ander"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Skermverdeler"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Volskerm links"</string>
@@ -784,8 +758,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Aangemeld as <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Geen internet nie"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Maak besonderhede oop."</string>
-    <!-- no translation found for accessibility_quick_settings_not_available (4190068184294019846) -->
-    <skip />
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Onbeskikbaar weens <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Maak <xliff:g id="ID_1">%s</xliff:g>-instellings oop."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Wysig volgorde van instellings."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Bladsy <xliff:g id="ID_1">%1$d</xliff:g> van <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index fcad97b..dea4256 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"የስርዓት UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"አጽዳ"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"ከዝርዝር አስወግድ"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"የትግበራ መረጃ"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"የቅርብ ጊዜ ማያ ገጾችዎ እዚህ ይታያሉ"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"የቅርብ ጊዜ መተግበሪያዎችን ሰርዝ"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d ማያ ገጾች በአጠቃላይ እይታ ውስጥ</item>
-      <item quantity="other">%d ማያ ገጾች በአጠቃላይ እይታ ውስጥ</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"ምንም ማሳወቂያዎች የሉም"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"በመካሄድ ላይ ያለ"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"ማሳወቂያዎች"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ስልክ ክፈት"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"የድምጽ ረዳትን ክፈት"</string>
     <string name="camera_label" msgid="7261107956054836961">"ካሜራ ክፈት"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"የአዲስ ተግባር አቀማመጥን ይምረጡ"</string>
     <string name="cancel" msgid="6442560571259935130">"ይቅር"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"የእገዛ መልዕክት አካባቢ"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"አረጋግጥ"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> አስወግድ።"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ተሰናብቷል::"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ሁሉም የቅርብ ጊዜ ማመልከቻዎች ተሰናብተዋል።"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"የ<xliff:g id="APP">%s</xliff:g> መተግበሪያ መረጃውን ይክፈቱ።"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> በመጀመር ላይ።"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"ማሳወቂያ ተወግዷል።"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"የማሳወቂያ ጥላ።"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"ፈጣን ቅንብሮች።"</string>
@@ -319,8 +305,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ያልተሰየመ መሳሪያ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"ለመውሰድ ዝግጁ"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"ምንም መሣሪያዎች አይገኙም"</string>
-    <!-- no translation found for quick_settings_cast_no_wifi (2696477881905521882) -->
-    <skip />
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi አልተገናኘም"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ብሩህነት"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ራስ-ሰር"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ቀለማትን ግልብጥ"</string>
@@ -356,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"ኤንኤፍሲ"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"ኤንኤፍሲ ተሰናክሏል"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"ኤንኤፍሲ ነቅቷል"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ሁሉንም ነገር አጽድተዋል"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"የመተግበሪያ መረጃ"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ማያ ገጽ መሰካት"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"ፈልግ"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ን መጀመር አልተቻለም።"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> በጥንቃቄ ሁነታ ውስጥ ታግዷል።"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ሁሉንም አጽዳ"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"የተከፈለ ማያ ገጽን ለመጠቀም እዚህ ላይ ይጎትቱ"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"መተግበሪያዎችን ለመቀየር ወደ ላይ ያንሸራትቱ"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"መተግበሪያዎችን በፍጥነት ለመቀየር ወደ ቀኝ ይጎትቱ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"አግድም ክፈል"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ቁልቁል ክፈል"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"በብጁ ክፈል"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"ማያ ገጽ ወደ ላይ ክፈል"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"ማያ ገጽ ወደ ግራ ክፈል"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"ማያ ገጽ ወደ ቀኝ ክፈል"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"አጠቃላይ እይታን ቀያይር"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ባትሪ ሞልቷል"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ኃይል በመሙላት ላይ"</string>
@@ -619,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"አሳንስ"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"ከዚህ መተግበሪያ ማሳወቂያዎችን ማሳየት ይቀጥል?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"እነዚህ ማሳወቂያዎች ሊጠፉ አይችሉም"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"ይህ መተግበሪያ ካሜራውን እየተጠቀመ ነው።"</string>
     <string name="appops_microphone" msgid="741508267659494555">"ይህ መተግበሪያ ማይክሮፎኑን እየተጠቀመ ነው።"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"ይህ መተግበሪያ በማያ ገጽዎ ላይ ባሉ ሌሎች መተግበሪያዎች ላይ እያሳየ ነው።"</string>
@@ -753,6 +725,8 @@
     <item msgid="2139628951880142927">"የባትሪ ኃይል በሚሞላበት ጊዜ መቶኛ አሳይ (ነባሪ)"</item>
     <item msgid="3327323682209964956">"ይህን አዶ አታሳይ"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"ሌላ"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"የተከፈለ የማያ ገጽ ከፋይ"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"የግራ ሙሉ ማያ ገጽ"</string>
@@ -784,8 +758,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"እንደ <xliff:g id="ID_1">%s</xliff:g> ሆነው ገብተዋል"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"ምንም በይነመረብ የለም"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"ዝርዝሮችን ክፈት።"</string>
-    <!-- no translation found for accessibility_quick_settings_not_available (4190068184294019846) -->
-    <skip />
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"በ<xliff:g id="REASON">%s</xliff:g> ምክንያት አይገኝም"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"የ<xliff:g id="ID_1">%s</xliff:g> ቅንብሮችን ክፈት።"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"የቅንብሮድ ቅደም-ተከተል አርትዕ።"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"ገጽ <xliff:g id="ID_1">%1$d</xliff:g> ከ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 1b55d39..b3ac2cd 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -21,18 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"واجهة مستخدم النظام"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"محو"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"إزالة من القائمة"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"معلومات التطبيق"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"تظهر شاشاتك المعروضة مؤخرًا هنا"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"إزالة التطبيقات الحديثة"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="zero">‏ لا توجد أي شاشات (%d) في النظرة العامة</item>
-      <item quantity="two">‏شاشتان (%d) في النظرة العامة</item>
-      <item quantity="few">‏%d شاشات في النظرة العامة</item>
-      <item quantity="many">‏%d شاشة في النظرة العامة</item>
-      <item quantity="other">‏%d من الشاشات في النظرة العامة</item>
-      <item quantity="one">شاشة واحدة في النظرة العامة</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"ليس هناك أي اشعارات"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"مستمر"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"الإشعارات"</string>
@@ -105,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"فتح الهاتف"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"فتح المساعد الصوتي"</string>
     <string name="camera_label" msgid="7261107956054836961">"فتح الكاميرا"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"تحديد تنسيق جديد للمهمة"</string>
     <string name="cancel" msgid="6442560571259935130">"إلغاء"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"منطقة رسالة المساعدة"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"تأكيد"</string>
@@ -192,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"إزالة <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"تمت إزالة <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"تم تجاهل كل التطبيقات المستخدمة مؤخرًا."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"فتح معلومات تطبيق <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"جارٍ بدء <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"تم تجاهل الإشعار."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"مركز الإشعارات."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"الإعدادات السريعة."</string>
@@ -367,23 +349,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"‏الاتصال القريب المدى (NFC)"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"تم إيقاف الاتصال القريب المدى"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"تم تفعيل الاتصال القريب المدى"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"ليست هناك عناصر تم استخدامها مؤخرًا"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"لقد محوتَ كل شيء"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"معلومات التطبيق"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"تثبيت الشاشة"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"بحث"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"تعذر بدء <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"تم إيقاف <xliff:g id="APP">%s</xliff:g> في الوضع الآمن."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"مسح الكل"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"اسحب هنا لاستخدام وضع تقسيم الشاشة"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"مرّر سريعًا لأعلى لتبديل التطبيقات"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"اسحب لليسار للتبديل السريع بين التطبيقات"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"تقسيم أفقي"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"تقسيم رأسي"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"تقسيم مخصص"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"تقسيم الشاشة بمحاذاة الجزء العلوي"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"تقسيم الشاشة بمحاذاة اليسار"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"تقسيم الشاشة بمحاذاة اليمين"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"تبديل \"النظرة العامة\""</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"تم الشحن"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"جارٍ الشحن"</string>
@@ -634,6 +601,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"تصغير"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"هل تريد الاستمرار في تلقي إشعارات من هذا التطبيق؟"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"يتعذَّر إيقاف هذه الإشعارات."</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"يستخدم هذا التطبيق الكاميرا."</string>
     <string name="appops_microphone" msgid="741508267659494555">"يستخدم هذا التطبيق الميكروفون."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"يتم عرض هذا التطبيق فوق التطبيقات الأخرى على شاشتك."</string>
@@ -776,6 +745,8 @@
     <item msgid="2139628951880142927">"عرض النسبة المئوية عند الشحن (تلقائي)"</item>
     <item msgid="3327323682209964956">"عدم عرض هذا الرمز"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"غير ذلك"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"أداة تقسيم الشاشة"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"عرض النافذة اليسرى بملء الشاشة"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 65c5fb5..2574838 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"ছিষ্টেম ইউআই"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"মচক"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"সূচীৰ পৰা আঁতৰাওক"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"এপ্ সম্পৰ্কীয় তথ্য"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"আপোনাৰ শেহতীয়া স্ক্ৰীণ ইয়াত প্ৰকট হ\'ব"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"শেহতীয়া এপসমূহক আঁতৰাওক"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d খন স্ক্ৰীণ অৱলোকনত আছে</item>
-      <item quantity="other">%d খন স্ক্ৰীণ অৱলোকনত আছে</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"কোনো জাননী নাই"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"চলিত"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"জাননীসমূহ"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ফ\'ন খোলক"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"কণ্ঠধ্বনিৰে সহায় খোলক"</string>
     <string name="camera_label" msgid="7261107956054836961">"কেমেৰা খোলক"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"নতুন কাৰ্যৰ চানেকি বাছনি কৰক"</string>
     <string name="cancel" msgid="6442560571259935130">"বাতিল কৰক"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"সহায় বাৰ্তাৰ ক্ষেত্ৰ"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"নিশ্চিত কৰক"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>ক আঁতৰাব।"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> আঁতৰোৱা হৈছে৷"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"শেহতীয়া সকলো এপ্লিকেশ্বন অগ্ৰাহ্য কৰা হৈছে।"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> এপ্লিকেশ্বনৰ তথ্য় খোলক।"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> আৰম্ভ কৰা হৈছে।"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"জাননী অগ্ৰাহ্য কৰা হৈছে।"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"জাননী পেনেল।"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"ক্ষিপ্ৰ ছেটিংসমূহ।"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC নিষ্ক্ৰিয় হৈ আছে"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC সক্ষম হৈ আছে"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"কোনো শেহতীয়া বস্তু নাই"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"আপুনি সকলোবোৰ খালী কৰিছে"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"এপ্লিকেশ্বনৰ তথ্য"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"স্ক্ৰীণ পিনিং"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"সন্ধান কৰক"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> আৰম্ভ কৰিব পৰা নগ\'ল৷"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g>টো সুৰক্ষিত ম\'ডত অক্ষম কৰা হ\'ল।"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"সকলো মচক"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"বিভাজিত স্ক্ৰীণ ব্য়ৱহাৰ কৰিবলৈ ইয়ালৈ টানি আনি এৰক"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"আনটো এপ্ ব্য়ৱহাৰ কৰিবলৈ ওপৰলৈ ছোৱাইপ কৰক"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"খৰতকীয়াকৈ আনটো এপ্ ব্য়ৱহাৰ কৰিবলৈ সোঁফালে টানক"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"আনুভূমিকভাৱে বিভাজিত কৰক"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"উলম্বভাৱে বিভাজিত কৰক"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"উপযোগিতা অনুসৰি বিভাজিত কৰক"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"স্ক্ৰীণখনক ওপৰফাললৈ ভাগ কৰক"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"স্ক্ৰীণখনক বাওঁফাললৈ ভাগ কৰক"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"স্ক্ৰীণখনক সোঁফাললৈ ভাগ কৰক"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"অৱলোকন ট’গল কৰক"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"চ্চার্জ হ\'ল"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"চ্চার্জ হৈ আছে"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"সৰু কৰক"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"এই এপটোৰ জাননী দেখুওৱাই থাকিব লাগিবনে?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"এই জাননীসমূহ বন্ধ কৰিব নোৱাৰি"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"এই এপে কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
     <string name="appops_microphone" msgid="741508267659494555">"এই এপে মাইক্ৰ\'ফ\'ন ব্য়ৱহাৰ কৰি আছে।"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"এই এপটো আপোনাৰ স্ক্ৰীণত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ আছে।"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"চ্চাৰ্জ কৰি থকাৰ সময়ত শতাংশ দেখুৱাওক (ডিফ\'ল্ট)"</item>
     <item msgid="3327323682209964956">"এই আইকনটো নেদেখুৱাব"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"অন্যান্য"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"স্প্লিট স্ক্ৰীণৰ বিভাজক"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"বাওঁফালৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 07476aa..58947da 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Sistemin İstifadə İnterfeysi"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Təmizlə"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Siyahıdan sil"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Tətbiq infosu"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Your recent screens appear here"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Son tətbiqləri kənarlaşdır"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other"> İcmalda %d ekran</item>
-      <item quantity="one">İcmalda 1 ekran</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Bildiriş yoxdu"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Davam edir"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Bildirişlər"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"telefonu açın"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"səs yardımçısını açın"</string>
     <string name="camera_label" msgid="7261107956054836961">"kemaranı açın"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Yeni tapşırıq sxemi seçin"</string>
     <string name="cancel" msgid="6442560571259935130">"Ləğv et"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Yardım mesajı bölməsi"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Təsdiq"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> kənarlaşdırın."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> çıxarıldı."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Bütün son tətbiqlər kənarlaşdırıldı."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> tətbiqi haqqında məlumatı açın."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> başlanır."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Bildiriş uzaqlaşdırıldı."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bildiriş kölgəsi."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Tez ayarlar."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC deaktiv edilib"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC aktiv edilib"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Son elementlər yoxdur"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Hərşeyi təmizlədiniz"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Tətbiq haqqında"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekran sancağı"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"axtarış"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> başlana bilmir."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> güvənli rejimdə deaktiv edildi."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Hamısını silin"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Ekranı bölmək üçün bura sürüşdürün"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Tətbiqi dəyişmək üçün yuxarı sürüşdürün"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Tətbiqləri cəld dəyişmək üçün sağa çəkin"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Üfüqi Böl"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Şaquli Böl"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Fərdi Böl"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Ekranı yuxarıdan ayırın"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Ekranı soldan ayırın"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Ekranı sağdan ayırın"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"İcmala Keçin"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Dolub"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Enerji doldurulur"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Kiçildin"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu tətbiqin bildirişləri göstərilməyə davam edilsin?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Bu bildirişlər deaktiv edilə bilməz"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> vasitəsilə"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Bu tətbiq kameradan istifadə edir."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Bu tətbiq mikrofondan istifadə edir."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Bu tətbiqdə ekranda digər tətbiqlərin üzərində göstərilir."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Enerji dolan zaman faizi göstərin (defolt)"</item>
     <item msgid="3327323682209964956">"Bu piktoqramı göstərməyin"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Aşağı prioritet bildiriş işarələrini göstərin"</string>
     <string name="other" msgid="4060683095962566764">"Digər"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Bölünmüş ekran ayırıcısı"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Sol tam ekran"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index db3834b..b2bcda8 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -21,15 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"UI sistema"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Obriši"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Uklanjanje sa liste"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informacije o aplikaciji"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Nedavni ekrani se pojavljuju ovde"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Odbaci nedavne aplikacije"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d ekran u Pregledu</item>
-      <item quantity="few">%d ekrana u Pregledu</item>
-      <item quantity="other">%d ekrana u Pregledu</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nema obaveštenja"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Tekuće"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Obaveštenja"</string>
@@ -102,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"otvori telefon"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"otvori glasovnu pomoć"</string>
     <string name="camera_label" msgid="7261107956054836961">"otvori kameru"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Izaberi novi raspored zadataka"</string>
     <string name="cancel" msgid="6442560571259935130">"Otkaži"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Oblast poruke za pomoć"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdi"</string>
@@ -189,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Odbacite <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> je odbačena."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Sve nedavno korišćene aplikacije su odbačene."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Otvorite informacije o aplikaciji <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Pokrećemo <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obaveštenje je odbačeno."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Prozor sa obaveštenjima."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Brza podešavanja."</string>
@@ -358,23 +343,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC je onemogućen"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC je omogućen"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Nema nedavnih stavki"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Obrisali ste sve"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"kačenje ekrana"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"pretraži"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Pokretanje aplikacije <xliff:g id="APP">%s</xliff:g> nije uspelo."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikacija <xliff:g id="APP">%s</xliff:g> je onemogućena u bezbednom režimu."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Obriši sve"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Prevucite ovde da biste koristili razdeljeni ekran"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Prevucite nagore da biste menjali aplikacije"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Prevucite udesno da biste brzo promenili aplikacije"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podeli horizontalno"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podeli vertikalno"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Prilagođeno deljenje"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Podeli ekran nagore"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Podeli ekran nalevo"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Podeli ekran nadesno"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Uključi/isključi pregled"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjena je"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Punjenje"</string>
@@ -622,6 +592,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Umanji"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite li da se obaveštenja iz ove aplikacije i dalje prikazuju?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ne možete da isključite ova obaveštenja"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Ova aplikacija koristi kameru."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Ova aplikacija koristi mikrofon."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Ova aplikacija se prikazuje preko drugih aplikacija na ekranu."</string>
@@ -758,6 +730,8 @@
     <item msgid="2139628951880142927">"Prikaži procenat tokom punjenja (podrazumevano)"</item>
     <item msgid="3327323682209964956">"Ne prikazuj ovu ikonu"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Drugo"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Razdelnik podeljenog ekrana"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Režim celog ekrana za levi ekran"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 7b19b36..ac430ba 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -21,16 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Інтэрфейс карыстальніка сістэмы"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ачысціць"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Выдаліць са спісу"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Звесткі аб прыкладанні"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Вашы апошнія экраны з\'яўляюцца тут"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Закрыць нядаўнія прыкладаннi"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d экран у Аглядзе</item>
-      <item quantity="few">%d экраны ў Аглядзе</item>
-      <item quantity="many">%d экранаў у Аглядзе</item>
-      <item quantity="other">%d экрана ў Аглядзе</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Без апавяшчэнняў"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Пастаянныя"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Апавяшчэнні"</string>
@@ -103,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"адкрыць тэлефон"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"адкрыць галасавую дапамогу"</string>
     <string name="camera_label" msgid="7261107956054836961">"адкрыць камеру"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Выберыце новы макет заданняў"</string>
     <string name="cancel" msgid="6442560571259935130">"Скасаваць"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Поле даведачнага паведамлення"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Пацвердзіць"</string>
@@ -192,11 +181,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Выдаліць <xliff:g id="APP">%s</xliff:g> са спіса апошніх."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> выдалены."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Усе апошнія праграмы адхілены."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Адкрыць інфармацыю пра праграму \"<xliff:g id="APP">%s</xliff:g>\"."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Запускаецца <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Апавяшчэнне прапушчана."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Цень апавяшчэння.."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Хуткія налады."</string>
@@ -363,23 +347,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC адключаны"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC уключаны"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Няма нядаўніх элементаў"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Вы ачысцілі усё"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Звесткі аб праграме"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"замацаванне экрана"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"пошук"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Не атрымалася запусціць <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> адключана ў бяспечным рэжыме."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Ачысціць усё"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Перацягніце сюды, каб перайсці ў рэжым падзеленага экрана"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Правядзіце ўверх, каб пераключыць праграмы"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Каб хутка пераключыцца паміж праграмамі, перацягніце ўправа"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Падзяліць гарызантальна"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Падзяліць вертыкальна"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Падзяліць іншым чынам"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Падзяліць экран зверху"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Падзяліць экран злева"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Падзяліць экран справа"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Уключыць/выключыць агляд"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Зараджаны"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарадка"</string>
@@ -628,6 +597,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Згарнуць"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Працягваць паказваць апавяшчэнні гэтай праграмы?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Немагчыма адключыць гэтыя апавяшчэнні"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Гэта праграма выкарыстоўвае камеру."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Гэта праграма выкарыстоўвае мікрафон."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Гэта праграма паказваецца на экране паверх іншых праграм."</string>
@@ -766,6 +737,8 @@
     <item msgid="2139628951880142927">"Паказваць працэнты падчас зарадкі (стандартна)"</item>
     <item msgid="3327323682209964956">"Не паказваць гэты значок"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Іншае"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Раздзяляльнік падзеленага экрана"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Левы экран – поўнаэкранны рэжым"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 74b1c44..c6bc414 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Системен ПИ"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Изчистване"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Премахване от списъка"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Информация за приложението"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Скорошните ви екрани се показват тук"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Отхвърляне на скорошните приложения"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d екрана в панела за общ преглед</item>
-      <item quantity="one">1 екран в панела за общ преглед</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Няма известия"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"В момента"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Известия"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"отваряне на телефона"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"отваряне на гласовата помощ"</string>
     <string name="camera_label" msgid="7261107956054836961">"отваряне на камерата"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Избиране на ново оформление за задачите"</string>
     <string name="cancel" msgid="6442560571259935130">"Отказ"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Област за помощно съобщение"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Потвърждаване"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Отхвърляне на <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Приложението <xliff:g id="APP">%s</xliff:g> е отхвърлено."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Всички скорошни приложения са отхвърлени."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Отворете информацията за приложението <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> се стартира."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Известието е отхвърлено."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Падащ панел с известия."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Бързи настройки."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"КБП"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"КБП е деактивирана"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"КБП е активирана"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Няма скорошни елементи"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Изчистихте всичко"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Информация за приложението"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"фиксиране на екрана"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"търсене"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> не можа да стартира."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Приложението <xliff:g id="APP">%s</xliff:g> е деактивирано в безопасния режим."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Изчистване на всичко"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Преместете тук с плъзгане, за да използвате режим за разделен екран"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Прекарайте пръст нагоре, за да превключите между приложенията"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Плъзнете надясно за бързо превключване между приложенията"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Хоризонтално разделяне"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Вертикално разделяне"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Персонализирано разделяне"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Разделяне на екрана нагоре"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Разделяне на екрана наляво"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Разделяне на екрана надясно"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Превключване на общия преглед"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Заредена"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарежда се"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Намаляване"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Да продължат ли да се показват известията от това приложение?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Тези известия не могат да бъдат изключени"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Това приложение използва камерата."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Това приложение използва микрофона."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Това приложение се показва върху други приложения на екрана."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Процентът да се показва при зареждане (по подразбиране)"</item>
     <item msgid="3327323682209964956">"Тази икона да не се показва"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Друго"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Разделител в режима за разделен екран"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Ляв екран: Показване на цял екран"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 5223a99..412f90cf 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"সিস্টেম UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"সাফ করুন"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"তালিকা থেকে সরান"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"অ্যাপের তথ্য"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"আপনার সাম্প্রতিক স্ক্রীনগুলো এখানে দেখা যাবে"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"সাম্প্রতিক অ্যাপ্লিকেশানগুলি খারিজ করুন"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">ওভারভিউ-এ %dটি স্ক্রিন</item>
-      <item quantity="other">ওভারভিউ-এ %dটি স্ক্রিন</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"কোনো বিজ্ঞপ্তি নেই"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"চলতে-থাকা"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"বিজ্ঞপ্তি"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ফোন খুলুন"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"ভয়েস সহায়তা খুলুন"</string>
     <string name="camera_label" msgid="7261107956054836961">"ক্যামেরা খুলুন"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"নতুন কার্য লেআউট বেছে নিন"</string>
     <string name="cancel" msgid="6442560571259935130">"বাতিল করুন"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"সহায়তার মেসেজ দেখানোর জায়গা"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"কনফার্ম করুন"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> খারিজ করুন।"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> খারিজ করা হয়েছে৷"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"সমস্ত সাম্প্রতিক অ্যাপ্লিকেশন খারিজ করা হয়েছে।"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> অ্যাপ্লিকেশানের তথ্য খুলবে৷"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> তারাঙ্কিত করা হচ্ছে।"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"বিজ্ঞপ্তি খারিজ করা হয়েছে৷"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"বিজ্ঞপ্তি শেড৷"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"দ্রুত সেটিংস৷"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC অক্ষম করা আছে"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC সক্ষম করা আছে"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"কোনো সাম্প্রতিক আইটেম নেই"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"আপনি সবকিছু সাফ করেছেন"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"অ্যাপ্লিকেশানের তথ্য"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"স্ক্রিন পিন করা"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"সার্চ"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> শুরু করা যায়নি৷"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"নিরাপদ মোডে <xliff:g id="APP">%s</xliff:g> অক্ষম করা হয়েছে৷"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"সবকিছু সাফ করুন"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"বিভক্ত স্ক্রিন ব্যবহার করতে এখানে টেনে আনুন"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"অন্য অ্যাপে যেতে উপরের দিকে সোয়াইপ করুন"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"একটি অ্যাপ ছেড়ে দ্রুত অন্য অ্যাপে যেতে ডান দিকে টেনে আনুন"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"অনুভূমিক স্প্লিট"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"উল্লম্ব স্প্লিট"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"কাস্টম স্প্লিট করুন"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"স্ক্রিনটি উপরের দিকে বিভক্ত করুন"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"স্ক্রিনটি বাঁদিকে বিভক্ত করুন"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"স্ক্রিনটি ডানদিকে বিভক্ত করুন"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"\'এক নজরে\' বৈশিষ্ট্যটি চালু বা বন্ধ করুন"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"চার্জ হয়েছে"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"চার্জ হচ্ছে"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"ছোট করে দিন"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"এই অ্যাপের বিজ্ঞপ্তি পরেও দেখে যেতে চান?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"এই বিজ্ঞপ্তিগুলি বন্ধ করা যাবে না"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"এই অ্যাপটি ক্যামেরা ব্যবহার করছে।"</string>
     <string name="appops_microphone" msgid="741508267659494555">"এই অ্যাপটি মাইক্রোফোন ব্যবহার করছে।"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"এই অ্যাপটি স্ক্রিনে অন্যান্য অ্যাপের উপরে দেখানো হচ্ছে।"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"চার্জ করার সময় শতাংশ দেখান (ডিফল্ট)"</item>
     <item msgid="3327323682209964956">"এই আইকনটি দেখাবেন না"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"অন্যান্য"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"বিভক্ত-স্ক্রিন বিভাজক"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"বাঁ দিকের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index ca09219..42616a6 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -21,15 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Sistemski UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Obriši"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Uklanjanje sa spiska"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informacije o aplikaciji"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Ovdje se prikazuju nedavno korišteni ekrani"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Odbaci nedavne aplikacije"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d ekran u Pregledu</item>
-      <item quantity="few">%d ekrana u Pregledu</item>
-      <item quantity="other">%d ekrana u Pregledu</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nema obavještenja"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"U toku"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Obavještenja"</string>
@@ -102,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"otvori telefon"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"otvori glasovnu pomoć"</string>
     <string name="camera_label" msgid="7261107956054836961">"otvori kameru"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Odaberite novi raspored zadataka"</string>
     <string name="cancel" msgid="6442560571259935130">"Otkaži"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Prostor za poruku za pomoć"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdite"</string>
@@ -189,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Odbaci aplikaciju <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> uklonjena."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Sve nedavno korištene aplikacije su odbačene."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Otvaranje informacija o aplikaciji <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Pokrećem aplikaciju <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obavještenje je uklonjeno."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Obavještenja sa sjenčenjem."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Brze postavke."</string>
@@ -321,7 +306,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Neimenovani uređaj"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Spreman za emitiranje"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Nema dostupnih uređaja"</string>
-    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi mreža nije povezana"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"WiFi mreža nije povezana"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Osvjetljenje"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverzija boja"</string>
@@ -358,23 +343,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC je onemogućen"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC je omogućen"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Nema nedavnih stavki"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Sve ste obrisali"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"kačenje ekrana"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"pretraživanje"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacija <xliff:g id="APP">%s</xliff:g> nije pokrenuta."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> je onemogućena u sigurnom načinu rada."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Obriši sve"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Povucite ovdje za korištenje podijeljenog ekrana"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Prevucite prema gore za promjenu aplikacije"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Prevucite udesno za brzu promjenu aplikacija"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podjela po horizontali"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podjela po vertikali"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Prilagođena podjela"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dijeli ekran nagore"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dijeli ekran nalijevo"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dijeli ekran nadesno"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Pregled uključivanja/isključivanja"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjeno"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Punjenje"</string>
@@ -624,6 +594,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimiziraj"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Nastaviti prikazivanje obavještenja iz ove aplikacije?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ova obavještenja nije moguće isključiti"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"putem aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Ova aplikacija koristi kameru."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Ova aplikacija koristi mikrofon."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Ova aplikacija prekriva druge aplikacije na ekranu."</string>
@@ -760,6 +731,7 @@
     <item msgid="2139628951880142927">"Pokaži postotak u toku punjenja (zadano)"</item>
     <item msgid="3327323682209964956">"Ne prikazuj ovu ikonu"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Prikaži ikone obavijesti niskog prioriteta"</string>
     <string name="other" msgid="4060683095962566764">"Ostalo"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Razdjelnik ekrana"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Lijevo cijeli ekran"</string>
@@ -791,7 +763,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Prijavljeni ste kao <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Nema internetske veze"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Otvori detalje."</string>
-    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Nije dostupno jer <xliff:g id="REASON">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Nije dostupno zbog razloga <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Otvori postavke za: <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Urediti raspored postavki."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Stranica <xliff:g id="ID_1">%1$d</xliff:g> od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 7b4fdad..d7300f6 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"IU del sistema"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Esborra"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Elimina de la llista"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informació de l\'aplicació"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Aquí es mostren les teves pantalles recents."</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Ignora les aplicacions recents"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d pantalles en la visió general</item>
-      <item quantity="one">1 pantalla en la visió general</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Cap notificació"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Continu"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificacions"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"obre el telèfon"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"obre l\'assistència per veu"</string>
     <string name="camera_label" msgid="7261107956054836961">"obre la càmera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Selecciona el disseny de la tasca nova"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancel·la"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Àrea de missatge d\'ajuda"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirma"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ignora <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"S\'ha omès <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"S\'han descartat totes les aplicacions recents."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Obre la informació sobre l\'aplicació <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"S\'està iniciant <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificació omesa."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Àrea de notificacions"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Configuració ràpida"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"L\'NFC està desactivada"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"L\'NFC està activada"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"No hi ha cap element recent"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Ho has esborrat tot"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informació de l\'aplicació"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixació de pantalla"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"No s\'ha pogut iniciar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"En mode segur, l\'aplicació <xliff:g id="APP">%s</xliff:g> està desactivada."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Esborra-ho tot"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Arrossega-ho aquí per utilitzar la pantalla dividida"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Llisca cap amunt per canviar d\'aplicació"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Arrossega el dit cap a la dreta per canviar ràpidament d\'aplicació"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisió horitzontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisió vertical"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisió personalitzada"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Divideix la pantalla cap amunt"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Divideix la pantalla cap a l\'esquerra"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Divideix la pantalla cap a la dreta"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Activa o desactiva Aplicacions recents"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"S\'està carregant"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimitza"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Vols continuar rebent notificacions d\'aquesta aplicació?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Aquestes notificacions no es poden desactivar"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"mitjançant <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Aquesta aplicació utilitza la càmera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Aquesta aplicació utilitza el micròfon."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Aquesta aplicació es mostra sobre altres aplicacions a la pantalla."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Mostra el percentatge quan es carregui (opció predeterminada)"</item>
     <item msgid="3327323682209964956">"No mostris aquesta icona"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Mostra les icones de notificació amb prioritat baixa"</string>
     <string name="other" msgid="4060683095962566764">"Altres"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Divisor de pantalles"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Pantalla esquerra completa"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index f8f79ce..0efbe7d 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -21,16 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"UI systému"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Vymazat"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Odebrat ze seznamu"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"O aplikaci"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Zde budou zobrazeny vaše poslední obrazovky"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Zavřít nové aplikace"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="few">%d obrazovky v Přehledu</item>
-      <item quantity="many">%d obrazovky v Přehledu</item>
-      <item quantity="other">%d obrazovek v Přehledu</item>
-      <item quantity="one">1 obrazovka v Přehledu</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Žádná oznámení"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Probíhající"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Oznámení"</string>
@@ -103,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"otevřít telefon"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"otevřít hlasovou asistenci"</string>
     <string name="camera_label" msgid="7261107956054836961">"spustit fotoaparát"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Vybrat nové rozvržení úkolů"</string>
     <string name="cancel" msgid="6442560571259935130">"Zrušit"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Oblast pro zprávu nápovědy"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdit"</string>
@@ -190,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Zavřít aplikaci <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikace <xliff:g id="APP">%s</xliff:g> byla odebrána."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Všechny naposledy použité aplikace byly odstraněny."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Otevře informace o aplikaci <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Spouštění aplikace <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Oznámení je zavřeno."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Panel oznámení."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Rychlé nastavení."</string>
@@ -361,23 +345,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC je vypnuto"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC je zapnuto"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Žádné nedávné položky"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vše je vymazáno"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informace o aplikaci"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"připnutí obrazovky"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"vyhledat"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikaci <xliff:g id="APP">%s</xliff:g> nelze spustit."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikace <xliff:g id="APP">%s</xliff:g> je v nouzovém režimu zakázána."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Vymazat vše"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Rozdělenou obrazovku můžete použít přetažením zde"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Přejetím nahoru přepnete aplikace"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Přetažením doprava rychle přepnete aplikace"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Vodorovné rozdělení"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikální rozdělení"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Vlastní rozdělení"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Rozdělit obrazovku nahoru"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Rozdělit obrazovku vlevo"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Rozdělit obrazovku vpravo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Přepnout přehled"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nabito"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Nabíjení"</string>
@@ -626,6 +595,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimalizovat"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Mají se oznámení z této aplikace nadále zobrazovat?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Tato oznámení nelze deaktivovat"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Tato aplikace využívá fotoaparát."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Tato aplikace využívá mikrofon."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Tato aplikace se zobrazuje přes ostatní aplikace na obrazovce."</string>
@@ -764,6 +735,8 @@
     <item msgid="2139628951880142927">"Zobrazovat procento při nabíjení (výchozí nastavení)"</item>
     <item msgid="3327323682209964956">"Tuto ikonu nezobrazovat"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Jiné"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Čára rozdělující obrazovku"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Levá část na celou obrazovku"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 7894361..d470582 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"System-UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ryd"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Fjern fra listen"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Appinfo"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Dine seneste skærme vises her"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Luk de seneste apps"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d skærmbilleder i Oversigt</item>
-      <item quantity="other">%d skærmbilleder i Oversigt</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ingen underretninger"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"I gang"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Underretninger"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"åbn telefon"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"åbn taleassistent"</string>
     <string name="camera_label" msgid="7261107956054836961">"åbn kamera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Vælg nyt opgavelayout"</string>
     <string name="cancel" msgid="6442560571259935130">"Annuller"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Område med hjælpemeddelelse"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bekræft"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Afvis <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> er annulleret."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle de seneste apps er lukket."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Åbn appoplysningerne for <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> startes."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Underretningen er annulleret."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Underretningspanel."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Hurtige indstillinger."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC er deaktiveret"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC er aktiveret"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Ingen nye elementer"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Du har ryddet alt"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Oplysninger om applikationen"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"skærmfastholdelse"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"søg"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> kunne ikke startes."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> er deaktiveret i sikker tilstand."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Ryd alle"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Træk hertil for at bruge opdelt skærm"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Stryg opad for at skifte apps"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Træk til højre for hurtigt at skifte app"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Opdel vandret"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Opdel lodret"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Opdel brugerdefineret"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Opdelt skærm øverst"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Opdelt skærm til venstre"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Opdelt skærm til højre"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Slå Oversigt til/fra"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Opladet"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Oplader"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimer"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Vil du fortsætte med at se underretninger fra denne app?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Disse underretninger kan ikke deaktiveres"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Denne app anvender kameraet."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Denne app anvender mikrofonen."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Denne app vises over andre apps på din skærm."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Vis procent ved opladning (standard)"</item>
     <item msgid="3327323682209964956">"Vis ikke dette ikon"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Andet"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Adskiller til opdelt skærm"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Vis venstre del i fuld skærm"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 47aaaed..7fcb02c 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"System-UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Löschen"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Aus Liste entfernen"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"App-Info"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Hier siehst du deine zuletzt geöffneten Apps."</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Kürzlich geöffnete Apps schließen"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d Bildschirme in der Übersicht</item>
-      <item quantity="one">1 Bildschirm in der Übersicht</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Keine Benachrichtigungen"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Aktuell"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Benachrichtigungen"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"Telefon öffnen"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"Sprachassistent öffnen"</string>
     <string name="camera_label" msgid="7261107956054836961">"Kamera öffnen"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Neues Aufgabenlayout auswählen"</string>
     <string name="cancel" msgid="6442560571259935130">"Abbrechen"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Bereich für die Hilfemeldung"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bestätigen"</string>
@@ -192,11 +183,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> beenden"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> entfernt"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle kürzlich verwendeten Apps wurden entfernt."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Infos zur App \"<xliff:g id="APP">%s</xliff:g>\" öffnen."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> wird gestartet."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Benachrichtigung geschlossen"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Benachrichtigungsleiste"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Schnelleinstellungen"</string>
@@ -216,9 +202,9 @@
     <string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"Der Flugmodus ist aktiviert."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="2960643943620637020">"lautlos"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"nur Wecker"</string>
-    <string name="accessibility_quick_settings_dnd" msgid="6607873236717185815">"Nicht stören."</string>
-    <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"\"Nicht stören\" deaktiviert"</string>
-    <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"\"Nicht stören\" aktiviert"</string>
+    <string name="accessibility_quick_settings_dnd" msgid="6607873236717185815">"Bitte nicht stören."</string>
+    <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"\"Bitte nicht stören\" deaktiviert"</string>
+    <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"\"Bitte nicht stören\" aktiviert"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="6341675755803320038">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"Bluetooth deaktiviert"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"Bluetooth aktiviert"</string>
@@ -280,7 +266,7 @@
     <string name="start_dreams" msgid="5640361424498338327">"Bildschirmschoner"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_header_onboarding_text" msgid="8030309023792936283">"Halte die Symbole gedrückt, um weitere Optionen zu sehen"</string>
-    <string name="quick_settings_dnd_label" msgid="8735855737575028208">"Nicht stören"</string>
+    <string name="quick_settings_dnd_label" msgid="8735855737575028208">"Bitte nicht stören"</string>
     <string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"Nur wichtige Unterbrechungen"</string>
     <string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Nur Wecker"</string>
     <string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"Lautlos"</string>
@@ -359,23 +345,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ist deaktiviert"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ist aktiviert"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Keine kürzlich verwendeten Elemente"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Du hast alles gelöscht"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"App-Info"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Bildschirmfixierung"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"Suche"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> konnte nicht gestartet werden."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ist im abgesicherten Modus deaktiviert."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Alle schließen"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Hierher ziehen, um den Bildschirm zu teilen"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Nach oben wischen, um Apps zu wechseln"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Zum schnellen Wechseln der Apps nach rechts ziehen"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Geteilte Schaltfläche – horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Geteilte Schaltfläche – vertikal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Geteilte Schaltfläche – benutzerdefiniert"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Geteilten Bildschirm oben anzeigen"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Geteilten Bildschirm auf linker Seite anzeigen"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Geteilten Bildschirm auf der rechten Seite anzeigen"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Übersicht ein-/ausblenden"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Aufgeladen"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Wird aufgeladen"</string>
@@ -622,6 +593,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimieren"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Benachrichtigungen dieser App weiterhin anzeigen?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Diese Benachrichtigungen können nicht deaktiviert werden"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"über <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Diese App verwendet die Kamera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Diese App verwendet das Mikrofon."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Diese App wird über anderen Apps auf dem Bildschirm angezeigt."</string>
@@ -699,9 +671,9 @@
     <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
     <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
     <string name="tuner_full_zen_title" msgid="4540823317772234308">"Einschließlich Lautstärkeregler anzeigen"</string>
-    <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Nicht stören"</string>
+    <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Bitte nicht stören"</string>
     <string name="volume_dnd_silent" msgid="4363882330723050727">"Tastenkombination für Lautstärketasten"</string>
-    <string name="volume_up_silent" msgid="7141255269783588286">"\"Nicht stören\" bei \"Lauter\" deaktivieren"</string>
+    <string name="volume_up_silent" msgid="7141255269783588286">"\"Bitte nicht stören\" bei \"Lauter\" deaktivieren"</string>
     <string name="battery" msgid="7498329822413202973">"Akku"</string>
     <string name="clock" msgid="7416090374234785905">"Uhr"</string>
     <string name="headset" msgid="4534219457597457353">"Headset"</string>
@@ -756,6 +728,7 @@
     <item msgid="2139628951880142927">"Prozentwert beim Laden anzeigen (Standardeinstellung)"</item>
     <item msgid="3327323682209964956">"Dieses Symbol nicht anzeigen"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Symbole für Benachrichtigungen mit einer niedrigen Priorität anzeigen"</string>
     <string name="other" msgid="4060683095962566764">"Sonstiges"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Bildschirmteiler"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Vollbild links"</string>
@@ -840,10 +813,10 @@
     <string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="wifi_is_off" msgid="1838559392210456893">"WLAN ist deaktiviert"</string>
     <string name="bt_is_off" msgid="2640685272289706392">"Bluetooth ist deaktiviert"</string>
-    <string name="dnd_is_off" msgid="6167780215212497572">"\"Nicht stören\" ist deaktiviert"</string>
-    <string name="qs_dnd_prompt_auto_rule" msgid="862559028345233052">"\"Nicht stören\" wurde von einer automatischen Regel aktiviert (<xliff:g id="ID_1">%s</xliff:g>)."</string>
-    <string name="qs_dnd_prompt_app" msgid="7978037419334156034">"\"Nicht stören\" wurde von einer App aktiviert (<xliff:g id="ID_1">%s</xliff:g>)."</string>
-    <string name="qs_dnd_prompt_auto_rule_app" msgid="2599343675391111951">"\"Nicht stören\" wurde von einer automatischen Regel oder einer App aktiviert."</string>
+    <string name="dnd_is_off" msgid="6167780215212497572">"\"Bitte nicht stören\" ist deaktiviert"</string>
+    <string name="qs_dnd_prompt_auto_rule" msgid="862559028345233052">"\"Bitte nicht stören\" wurde von einer automatischen Regel aktiviert (<xliff:g id="ID_1">%s</xliff:g>)."</string>
+    <string name="qs_dnd_prompt_app" msgid="7978037419334156034">"\"Bitte nicht stören\" wurde von einer App aktiviert (<xliff:g id="ID_1">%s</xliff:g>)."</string>
+    <string name="qs_dnd_prompt_auto_rule_app" msgid="2599343675391111951">"\"Bitte nicht stören\" wurde von einer automatischen Regel oder einer App aktiviert."</string>
     <string name="qs_dnd_until" msgid="3469471136280079874">"Bis <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="qs_dnd_keep" msgid="1825009164681928736">"Beibehalten"</string>
     <string name="qs_dnd_replace" msgid="8019520786644276623">"Ersetzen"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index ea16ba4..12f0d532 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"UI συστήματ."</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Διαγραφή"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Κατάργηση από τη λίστα"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Πληροφορίες εφαρμογής"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Οι πρόσφατες οθόνες σας εμφανίζονται εδώ"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Παράβλεψη πρόσφατων εφαρμογών"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d οθόνες στην Επισκόπηση</item>
-      <item quantity="one">1 οθόνη στην Επισκόπηση</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Δεν υπάρχουν ειδοποιήσεις"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Εν εξελίξει"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ειδοποιήσεις"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"άνοιγμα τηλεφώνου"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"άνοιγμα φωνητικής υποβοήθησης"</string>
     <string name="camera_label" msgid="7261107956054836961">"άνοιγμα φωτογραφικής μηχανής"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Επιλέξτε τη νέα διάταξη εργασίας"</string>
     <string name="cancel" msgid="6442560571259935130">"Ακύρωση"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Περιοχή μηνυμάτων βοήθειας"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Επιβεβαίωση"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Παράβλεψη <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Απορρίφθηκαν <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Έγινε παράβλεψη όλων των πρόσφατων εφαρμογών."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Άνοιγμα πληροφοριών εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Έναρξη <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Η ειδοποίηση έχει απορριφθεί."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Πλαίσιο σκίασης ειδοποιήσεων."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Γρήγορες ρυθμίσεις."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Το NFC είναι απενεργοποιημένο"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Το NFC είναι ενεργοποιημένο"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Έχει γίνει διαγραφή όλων των στοιχείων"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Πληροφορίες εφαρμογής"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"καρφίτσωμα οθόνης"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"αναζήτηση"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Δεν ήταν δυνατή η εκκίνηση της εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> έχει απενεργοποιηθεί στην ασφαλή λειτουργία."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Διαγραφή όλων"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Σύρετε εδώ για να χρησιμοποιήσετε τον διαχωρισμό οθόνης"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Σύρετε προς τα επάνω για εναλλαγή των εφαρμογών"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Σύρετε προς τα δεξιά για γρήγορη εναλλαγή εφαρμογών"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Οριζόντιος διαχωρισμός"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Κάθετος διαχωρισμός"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Προσαρμοσμένος διαχωρισμός"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Διαχωρισμός οθόνης στην κορυφή"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Διαχωρισμός οθόνης στα αριστερά"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Διαχωρισμός οθόνης στα δεξιά"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Εναλλαγή επισκόπησης"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Φορτίστηκε"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Φόρτιση"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Ελαχιστοποίηση"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Να συνεχίσουν να εμφανίζονται ειδοποιήσεις από αυτήν την εφαρμογή;"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Αδύνατη η απενεργοποίηση αυτών των ειδοποιήσεων"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Αυτή η εφαρμογή χρησιμοποιεί την κάμερα."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Αυτή η εφαρμογή χρησιμοποιεί το μικρόφωνο."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Αυτή η εφαρμογή εμφανίζεται πάνω σε άλλες εφαρμογές στην οθόνη σας."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Να εμφανίζεται ποσοστό κατά τη φόρτιση (προεπιλογή)"</item>
     <item msgid="3327323682209964956">"Να μην εμφανίζεται αυτό το εικονίδιο"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Άλλο"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Διαχωριστικό οθόνης"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Αριστερή πλήρης οθόνη"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index b7da383..82c91c7 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"System UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Clear"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Remove from list"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"App info"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Your recent screens appear here"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Dismiss recent apps"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d screens in Overview</item>
-      <item quantity="one">1 screen in Overview</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No notifications"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"open phone"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"open voice assist"</string>
     <string name="camera_label" msgid="7261107956054836961">"open camera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancel"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"All recent applications dismissed."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification dismissed."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notification shade."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Quick settings."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC is disabled"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC is enabled"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"No recent items"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"You\'ve cleared everything"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"screen pinning"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Clear all"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Drag here to use split screen"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Swipe up to switch apps"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Drag right to quickly switch apps"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Toggle Overview"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimise"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"This app is using the camera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"This app is using the microphone."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"This app is displaying over other apps on your screen."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Show percentage when charging (default)"</item>
     <item msgid="3327323682209964956">"Don\'t show this icon"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Show low-priority notification icons"</string>
     <string name="other" msgid="4060683095962566764">"Other"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Split screen divider"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Left full screen"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 19e9b64..361310e 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"System UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Clear"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Remove from list"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"App info"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Your recent screens appear here"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Dismiss recent apps"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d screens in Overview</item>
-      <item quantity="one">1 screen in Overview</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No notifications"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"open phone"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"open voice assist"</string>
     <string name="camera_label" msgid="7261107956054836961">"open camera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancel"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"All recent applications dismissed."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification dismissed."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notification shade."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Quick settings."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC is disabled"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC is enabled"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"No recent items"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"You\'ve cleared everything"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"screen pinning"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Clear all"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Drag here to use split screen"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Swipe up to switch apps"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Drag right to quickly switch apps"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Toggle Overview"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimise"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"This app is using the camera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"This app is using the microphone."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"This app is displaying over other apps on your screen."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Show percentage when charging (default)"</item>
     <item msgid="3327323682209964956">"Don\'t show this icon"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Show low-priority notification icons"</string>
     <string name="other" msgid="4060683095962566764">"Other"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Split screen divider"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Left full screen"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index b7da383..82c91c7 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"System UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Clear"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Remove from list"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"App info"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Your recent screens appear here"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Dismiss recent apps"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d screens in Overview</item>
-      <item quantity="one">1 screen in Overview</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No notifications"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"open phone"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"open voice assist"</string>
     <string name="camera_label" msgid="7261107956054836961">"open camera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancel"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"All recent applications dismissed."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification dismissed."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notification shade."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Quick settings."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC is disabled"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC is enabled"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"No recent items"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"You\'ve cleared everything"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"screen pinning"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Clear all"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Drag here to use split screen"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Swipe up to switch apps"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Drag right to quickly switch apps"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Toggle Overview"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimise"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"This app is using the camera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"This app is using the microphone."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"This app is displaying over other apps on your screen."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Show percentage when charging (default)"</item>
     <item msgid="3327323682209964956">"Don\'t show this icon"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Show low-priority notification icons"</string>
     <string name="other" msgid="4060683095962566764">"Other"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Split screen divider"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Left full screen"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index b7da383..82c91c7 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"System UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Clear"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Remove from list"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"App info"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Your recent screens appear here"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Dismiss recent apps"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d screens in Overview</item>
-      <item quantity="one">1 screen in Overview</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No notifications"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"open phone"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"open voice assist"</string>
     <string name="camera_label" msgid="7261107956054836961">"open camera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancel"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Help message area"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirm"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"All recent applications dismissed."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification dismissed."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notification shade."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Quick settings."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC is disabled"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC is enabled"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"No recent items"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"You\'ve cleared everything"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"screen pinning"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Clear all"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Drag here to use split screen"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Swipe up to switch apps"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Drag right to quickly switch apps"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Toggle Overview"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimise"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"via <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"This app is using the camera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"This app is using the microphone."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"This app is displaying over other apps on your screen."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Show percentage when charging (default)"</item>
     <item msgid="3327323682209964956">"Don\'t show this icon"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Show low-priority notification icons"</string>
     <string name="other" msgid="4060683095962566764">"Other"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Split screen divider"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Left full screen"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 97ddf61..8005d79 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‎‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‎‏‎‎System UI‎‏‎‎‏‎"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‎Clear‎‏‎‎‏‎"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‎Remove from list‎‏‎‎‏‎"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‏‎‏‎‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‏‎‎‎‏‎‎‏‎App info‎‏‎‎‏‎"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‏‎‏‏‏‎‎‏‎‎‏‏‏‎‏‎‏‎Your recent screens appear here‎‏‎‎‏‎"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‏‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‎Dismiss recent apps‎‏‎‎‏‎"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‎‎‏‎‎‏‏‏‎%d screens in Overview‎‏‎‎‏‎</item>
-      <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‎‎‏‎‎‏‏‏‎1 screen in Overview‎‏‎‎‏‎</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎No notifications‎‏‎‎‏‎"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎Ongoing‎‏‎‎‏‎"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‎Notifications‎‏‎‎‏‎"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎open phone‎‏‎‎‏‎"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‏‎‎open voice assist‎‏‎‎‏‎"</string>
     <string name="camera_label" msgid="7261107956054836961">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‎open camera‎‏‎‎‏‎"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‎‏‏‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‎‎Select new task layout‎‏‎‎‏‎"</string>
     <string name="cancel" msgid="6442560571259935130">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎Cancel‎‏‎‎‏‎"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‎‏‎‎‏‏‎‏‏‏‏‏‎Help message area‎‏‎‎‏‎"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‎‎‎‎‎‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎Confirm‎‏‎‎‏‎"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎Dismiss ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ dismissed.‎‏‎‎‏‎"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‎‎‎All recent applications dismissed.‎‏‎‎‏‎"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‎‏‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‏‏‎‎‏‎‏‎‎‎Open ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ application info.‎‏‎‎‏‎"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‏‎‎‎‏‎‎‏‏‎Starting ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‏‎‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎Notification dismissed.‎‏‎‎‏‎"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‎‎‎Notification shade.‎‏‎‎‏‎"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‏‎‎‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎Quick settings.‎‏‎‎‏‎"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎NFC‎‏‎‎‏‎"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‏‎NFC is disabled‎‏‎‎‏‎"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‎NFC is enabled‎‏‎‎‏‎"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎No recent items‎‏‎‎‏‎"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‏‎You\'ve cleared everything‎‏‎‎‏‎"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎Application Info‎‏‎‎‏‎"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎screen pinning‎‏‎‎‏‎"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‎‏‎‏‎search‎‏‎‎‏‎"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‏‎‎Could not start ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ is disabled in safe-mode.‎‏‎‎‏‎"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‎‏‏‏‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‏‎Clear all‎‏‎‎‏‎"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‎‏‏‎‏‎‏‎‏‏‏‏‏‎‎‎‏‎‎‎‏‎Drag here to use split screen‎‏‎‎‏‎"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎‎‏‎Swipe up to switch apps‎‏‎‎‏‎"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‏‏‏‎‏‎Drag right to quickly switch apps‎‏‎‎‏‎"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‎‏‎‎‎Split Horizontal‎‏‎‎‏‎"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‎‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎Split Vertical‎‏‎‎‏‎"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎Split Custom‎‏‎‎‏‎"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‏‏‏‏‎Split screen to the top‎‏‎‎‏‎"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‎‏‏‏‎‎‏‏‎Split screen to the left‎‏‎‎‏‎"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‏‎‏‎‏‎‏‎‎‎‏‏‎Split screen to the right‎‏‎‎‏‎"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‎Toggle Overview‎‏‎‎‏‎"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎Charged‎‏‎‎‏‎"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‎‎‎‎‏‎‏‎‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎‎‎Charging‎‏‎‎‏‎"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‏‎‏‎‏‏‏‎‎‎‏‏‏‎‎‏‏‎Minimize‎‏‎‎‏‎"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‎‏‎Keep showing notifications from this app?‎‏‎‎‏‎"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‏‎‏‏‎‏‏‎‏‎‎‏‏‎‏‎‏‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‎These notifications can\'t be turned off‎‏‎‎‏‎"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎via ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="appops_camera" msgid="8100147441602585776">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‎‎‎‎‎This app is using the camera.‎‏‎‎‏‎"</string>
     <string name="appops_microphone" msgid="741508267659494555">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‏‎‏‏‎This app is using the microphone.‎‏‎‎‏‎"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎This app is displaying over other apps on your screen.‎‏‎‎‏‎"</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‎‎‎‏‎‎‏‏‏‏‎Show percentage when charging (default)‎‏‎‎‏‎"</item>
     <item msgid="3327323682209964956">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‏‏‎‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‏‏‎‎‎Don\'t show this icon‎‏‎‎‏‎"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‎‎Show low-priority notification icons‎‏‎‎‏‎"</string>
     <string name="other" msgid="4060683095962566764">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‎‎‎‎‏‏‎‏‏‎‎‎Other‎‏‎‎‏‎"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‎Split-screen divider‎‏‎‎‏‎"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎Left full screen‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 35499f0..3f3187b 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"IU del sistema"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Eliminar"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Eliminar de la lista"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Información de la aplicación"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Las pantallas recientes aparecen aquí."</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Rechazar aplicaciones recientes"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d pantallas en Recientes</item>
-      <item quantity="one">1 pantalla en Recientes</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No hay notificaciones"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Continuo"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificaciones"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"abrir teléfono"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"abrir el asistente de voz"</string>
     <string name="camera_label" msgid="7261107956054836961">"abrir cámara"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Selecciona el nuevo diseño de la tarea."</string>
     <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área de mensajes de ayuda"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Rechazar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> descartada."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Se descartaron todas las aplicaciones recientes."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Abre la información de la aplicación de <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación ignorada"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Pantalla de notificaciones"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Configuración rápida"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"La tecnología NFC está inhabilitada"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"La tecnología NFC está habilitada"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"No hay elementos recientes"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Todo borrado"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Fijar pantalla"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"No se pudo iniciar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> está inhabilitada en modo seguro."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Borrar todo"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Arrastra hasta aquí para usar la pantalla dividida"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Desliza el dedo hacia arriba para cambiar de app"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Arrastra a la derecha para cambiar aplicaciones rápidamente"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"División horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"División vertical"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"División personalizada"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dividir pantalla en la parte superior"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dividir pantalla a la izquierda"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dividir pantalla a la derecha"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Ocultar o mostrar Recientes"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Cargando"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"¿Quieres seguir viendo las notificaciones de esta app?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"No se pueden desactivar estas notificaciones"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Esta app está usando la cámara."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Esta app está usando el micrófono."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Esta app se muestra sobre otras apps en la pantalla."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Mostrar el porcentaje durante la carga (predeterminado)"</item>
     <item msgid="3327323682209964956">"No mostrar este ícono"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Otros"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Divisor de pantalla dividida"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Pantalla izquierda completa"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 54364a5..356ef45 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"UI del sistema"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Borrar"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Quitar de la lista"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Información de la aplicación"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Aquí aparecerán tus aplicaciones recientes"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Descartar aplicaciones recientes"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d pantallas en Aplicaciones recientes</item>
-      <item quantity="one">1 pantalla en Aplicaciones recientes</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No tienes notificaciones"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Entrante"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificaciones"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"abrir teléfono"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"abrir el asistente de voz"</string>
     <string name="camera_label" msgid="7261107956054836961">"abrir cámara"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Seleccionar diseño de tarea nueva"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área de mensaje de ayuda"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Descartar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Se ha eliminado <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Se han ignorado todas las aplicaciones recientes."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Abre la información de la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación ignorada"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Pantalla de notificaciones"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Ajustes rápidos"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"La conexión NFC está inhabilitada"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"La conexión NFC está habilitada"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"No hay elementos recientes"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Has rechazado todo"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fijación de pantalla"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"No se ha podido iniciar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"La aplicación <xliff:g id="APP">%s</xliff:g> se ha inhabilitado en modo seguro."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Borrar todo"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Arrastra hasta aquí para utilizar la pantalla dividida"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Desliza el dedo hacia arriba para cambiar de aplicación"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Arrastra hacia la derecha para cambiar rápidamente de aplicación"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"División horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"División vertical"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"División personalizada"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dividir la pantalla en la parte superior"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dividir la pantalla a la izquierda"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dividir la pantalla a la derecha"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Mostrar u ocultar aplicaciones recientes"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Cargando"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"¿Quieres seguir viendo las notificaciones de esta aplicación?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Estas notificaciones no se pueden desactivar"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"mediante <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Esta aplicación está usando la cámara."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Esta aplicación está usando el micrófono."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Esta aplicación se está mostrando sobre otras aplicaciones en tu pantalla."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Mostrar porcentaje durante la carga (predeterminado)"</item>
     <item msgid="3327323682209964956">"No mostrar este icono"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Mostrar iconos de notificaciones con prioridad baja"</string>
     <string name="other" msgid="4060683095962566764">"Otros"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Dividir la pantalla"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Pantalla izquierda completa"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 90e9334..f125b84 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Süsteemi UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Tühjenda"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Loendist eemaldamine"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Rakenduse teave"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Teie viimane ekraanikuva ilmub siia"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Loobu hiljutistest rakendustest"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d ekraani jaotises Ülevaade</item>
-      <item quantity="one">1 ekraan jaotises Ülevaade</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Teatisi pole"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Jätkuv"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Märguanded"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ava telefon"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"ava häälabi"</string>
     <string name="camera_label" msgid="7261107956054836961">"ava kaamera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Uue toimingu paigutuse valimine"</string>
     <string name="cancel" msgid="6442560571259935130">"Tühista"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Abisõnumi ala"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Kinnita"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Rakendusest <xliff:g id="APP">%s</xliff:g> loobumine."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Loobusite rakendusest <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Kõikidest hiljutistest rakendustest on loobutud"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Rakenduse <xliff:g id="APP">%s</xliff:g> teabe avamine."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Rakenduse <xliff:g id="APP">%s</xliff:g> käivitamine."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Märguandest on loobutud."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Märguande vari."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Kiirseaded."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC on keelatud"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC on lubatud"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Hiljutisi üksusi pole"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Olete kõik ära kustutanud"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Rakenduse teave"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekraanikuva kinnitamine"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"otsing"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Rakendust <xliff:g id="APP">%s</xliff:g> ei saanud käivitada."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Rakendus <xliff:g id="APP">%s</xliff:g> on turvarežiimis keelatud."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Kustuta kõik"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Jagatud ekraani kasutamiseks lohistage siia"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Rakenduste vahetamiseks pühkige üles"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Lohistage paremale, et rakendusi kiiresti vahetada"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horisontaalne poolitamine"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikaalne poolitamine"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Kohandatud poolitamine"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Poolita ekraan üles"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Poolita ekraan vasakule"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Poolita ekraan paremale"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Lehe Ülevaade sisse- ja väljalülitamine"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Laetud"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Laadimine"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimeeri"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Kas jätkata selle rakenduse märguannete kuvamist?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Neid märguandeid ei saa välja lülitada"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"See rakendus kasutab kaamerat."</string>
     <string name="appops_microphone" msgid="741508267659494555">"See rakendus kasutab mikrofoni."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"See rakendus kuvatakse teie ekraanil muude rakenduste peal."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Kuva protsent laadimisel (vaikimisi)"</item>
     <item msgid="3327323682209964956">"Ära kuva seda ikooni"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Muu"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Ekraanijagaja"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Vasak täisekraan"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index c6314e4..6e3f094 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Sistemaren interfazea"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Garbitu"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Kendu zerrendatik"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Aplikazioaren informazioa"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Ikusitako azken pantailak erakusten dira hemen"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Baztertu azken aplikazioak"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d pantaila daude ikuspegi orokorrean</item>
-      <item quantity="one">Pantaila bat dago ikuspegi orokorrean</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ez dago jakinarazpenik"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Abian"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Jakinarazpenak"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ireki telefonoan"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"ireki ahots-laguntza"</string>
     <string name="camera_label" msgid="7261107956054836961">"ireki kamera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Hautatu zereginen diseinua"</string>
     <string name="cancel" msgid="6442560571259935130">"Utzi"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Laguntza-mezuaren eremua"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Berretsi"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Baztertu <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> baztertu da."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Azken aplikazio guztiak baztertu da."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Ireki <xliff:g id="APP">%s</xliff:g> aplikazioari buruzko informazioa."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> hasten."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Jakinarazpena baztertu da."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Jakinarazpenen panela."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Ezarpen bizkorrak."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Desgaituta dago NFC"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Gaituta dago NFC"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Ez dago azkenaldi honetako ezer"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Dena garbitu duzu"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Aplikazioaren informazioa"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pantaila-ainguratzea"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"bilatu"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Ezin izan da hasi <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> desgaituta dago modu seguruan."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Garbitu guztiak"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Arrastatu hau pantaila zatitzeko"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Egin gora aplikazioa aldatzeko"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Arrastatu eskuinera aplikazioa azkar aldatzeko"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Zatitze horizontala"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Zatitze bertikala"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Zatitze pertsonalizatua"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Zatitu pantaila eta ezarri goian"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Zatitu pantaila eta ezarri ezkerrean"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Zatitu pantaila eta ezarri eskuinean"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Aldatu ikuspegi orokorra"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Kargatuta"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Kargatzen"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimizatu"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Aplikazio honen jakinarazpenak erakusten jarraitzea nahi duzu?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Jakinarazpen hauek ezin dira desaktibatu"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren bidez"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Kamera erabiltzen ari da aplikazioa."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Mikrofonoa erabiltzen ari da aplikazioa."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Pantailako beste aplikazioen gainean agertzen da aplikazioa."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Erakutsi ehunekoa kargatu bitartean (balio lehenetsia)"</item>
     <item msgid="3327323682209964956">"Ez erakutsi ikonoa"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Erakutsi lehentasun txikiko jakinarazpenen ikonoak"</string>
     <string name="other" msgid="4060683095962566764">"Beste bat"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Pantaila-zatitzailea"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Ezarri ezkerraldea pantaila osoan"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index ca38030..e785f76 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"رابط کاربر سیستم"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"پاک کردن"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"حذف از فهرست"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"اطلاعات برنامه"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"صفحه‌های اخیر شما اینجا نمایان می‌شوند"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"رد کردن برنامه‌های اخیر"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">‏%d صفحه در نمای کلی</item>
-      <item quantity="other">‏%d صفحه در نمای کلی</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"اعلانی موجود نیست"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"در حال انجام"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"اعلان‌ها"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"باز کردن تلفن"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"«دستیار صوتی» را باز کنید"</string>
     <string name="camera_label" msgid="7261107956054836961">"باز کردن دوربین"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"انتخاب طرح‌بندی جدید کار"</string>
     <string name="cancel" msgid="6442560571259935130">"لغو"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"بخش پیام راهنما"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"تأیید"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"رد کردن <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> نادیده گرفته شد."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"همه برنامه‌های اخیر رد شدند."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"باز کردن اطلاعات برنامه <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> در حال شروع به کار است."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"اعلان ردشد."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"مجموعه اعلان."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"تنظیمات سریع."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"‏NFC غیرفعال است"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"‏NFC فعال است"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"بدون موارد اخیر"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"همه‌چیز را پاک کرده‌اید"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"اطلاعات برنامه"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"پین کردن صفحه"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"جستجو"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> شروع نشد."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> در حالت ایمن غیرفعال است."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"پاک کردن همه"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"برای استفاده از تقسیم صفحه، به اینجا بکشید"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"برای تغییر برنامه‌ها،‌ تند به بالا بکشید"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"برای جابه‌جایی سریع میان برنامه‌ها، به چپ بکشید"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"تقسیم افقی"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"تقسیم عمودی"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"سفارشی کردن تقسیم"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"تقسیم کردن صفحه به بالا"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"تقسیم کردن صفحه به چپ"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"تقسیم کردن صفحه به راست"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"تغییر وضعیت نمای کلی"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"شارژ کامل شد"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"در حال شارژ شدن"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"کوچک کردن"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"نمایش اعلان از این برنامه ادامه یابد؟"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"نمی‌توان این اعلان‌ها را خاموش کرد"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"این برنامه از دوربین استفاده می‌کند."</string>
     <string name="appops_microphone" msgid="741508267659494555">"این برنامه از میکروفون استفاده می‌کند."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"این برنامه روی برنامه‌های دیگر در صفحه‌نمایش نشان داده می‌شود."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"هنگام شارژ شدن درصد نشان داده شود (پیش‌فرض)"</item>
     <item msgid="3327323682209964956">"این نماد نشان داده نشود"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"موارد دیگر"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"تقسیم‌کننده صفحه"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"تمام‌صفحه چپ"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 412bee1..af35a85 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Käyttöliitt."</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Tyhjennä"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Poista luettelosta"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Sovelluksen tiedot"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Äskettäin käytetyt ruudut näkyvät tässä"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Hylkää viimeaikaiset sovellukset"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d näyttöä Yleistä-kohdassa</item>
-      <item quantity="one">1 näyttö Yleistä-kohdassa</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ei ilmoituksia"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Käynnissä olevat"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ilmoitukset"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"avaa puhelin"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"Avaa ääniapuri"</string>
     <string name="camera_label" msgid="7261107956054836961">"avaa kamera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Valitse uusi tehtävien asettelu"</string>
     <string name="cancel" msgid="6442560571259935130">"Peruuta"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Ohjeviestialue"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Vahvista"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Hylätään <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> hylättiin."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Kaikki viimeisimmät sovellukset on hylätty."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Avaa sovelluksen <xliff:g id="APP">%s</xliff:g> tiedot."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Käynnistetään <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Ilmoitus hylätty."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Ilmoitusalue."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Pika-asetukset."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC on poistettu käytöstä"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC on käytössä"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Ei viimeaikaisia kohteita"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Kaikki on hoidettu."</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Sovellustiedot"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"näytön kiinnitys"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"haku"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Sovelluksen <xliff:g id="APP">%s</xliff:g> käynnistäminen epäonnistui."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> on poistettu käytöstä vikasietotilassa."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tyhjennä kaikki"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Jaa näyttö vetämällä tähän."</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Vaihda sovellusta pyyhkäisemällä ylös"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Vaihda sovellusta nopeasti vetämällä oikealle"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Vaakasuuntainen jako"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Pystysuuntainen jako"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Muokattu jako"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Jaa näyttö ylös"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Jaa näyttö vasemmalle"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Jaa näyttö oikealle"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Näytä/piilota viimeisimmät"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Ladattu"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Ladataan"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Pienennä"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Jatketaanko ilmoitusten näyttämistä tästä sovelluksesta?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Näitä ilmoituksia ei voi poistaa käytöstä"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Tämä sovellus käyttää kameraa."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Tämä sovellus käyttää mikrofonia."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Tämä sovellus näkyy näytöllä muiden sovellusten päällä."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Näytä prosenttiluku latauksen aikana (oletus)"</item>
     <item msgid="3327323682209964956">"Älä näytä tätä kuvaketta"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Näytä vähemmän tärkeät ilmoituskuvakkeet"</string>
     <string name="other" msgid="4060683095962566764">"Muu"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Näytön jakaja"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Vasen koko näytölle"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 8821178..06f178a 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"IU système"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Effacer"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Supprimer de la liste"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informations sur l\'application"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Vos écrans récents s\'affichent ici"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Masquer les applications récentes"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">Aperçu de %d écran</item>
-      <item quantity="other">Aperçu de %d écrans</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Aucune notification"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"En cours"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"Ouvrir le téléphone"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"ouvrir l\'assistance vocale"</string>
     <string name="camera_label" msgid="7261107956054836961">"Ouvrir l\'appareil photo"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Sélectionner un nouveau format de tâche"</string>
     <string name="cancel" msgid="6442560571259935130">"Annuler"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Zone de message d\'aide"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmer"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Supprimer <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Application \"<xliff:g id="APP">%s</xliff:g>\" ignorée."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toutes les applications récentes ont été supprimées."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Ouvre les détails de l\'application <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Lancement de <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification masquée"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Volet des notifications"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Paramètres rapides"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC désactivée"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC activée"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Aucun élément récent"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vous avez tout effacé"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Détails de l\'application"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"épinglage d\'écran"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> est désactivée en mode sans échec."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Effacer tout"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Glissez l\'élément ici pour utiliser l\'écran partagé"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Balayez vers le haut pour changer d\'application"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Balayez l\'écran vers la droite pour changer rapidement d\'application"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Séparation horizontale"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Séparation verticale"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Séparation personnalisée"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Écran partagé dans le haut"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Écran partagé à la gauche"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Écran partagé à la droite"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Basculer l\'aperçu"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Chargée"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Charge en cours..."</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Réduire"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuer à afficher les notifications de cette application?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ces notifications ne peuvent pas être désactivées"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Cette application utilise l\'appareil photo."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Cette application utilise le microphone."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Cette application superpose du contenu par-dessus d\'autres applications à l\'écran."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Montrer le pourcentage durant la charge (par défaut)"</item>
     <item msgid="3327323682209964956">"Ne pas afficher cette icône"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Autre"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Séparateur d\'écran partagé"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Plein écran à la gauche"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index e37722a..7157cb5 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"L\'interface"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Effacer"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Supprimer de la liste"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Infos application"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Vos écrans récents s\'affichent ici"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Masquer les applications récentes"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d écran dans Aperçu</item>
-      <item quantity="other">%d écrans dans Aperçu</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Aucune notification"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"En cours"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ouvrir le téléphone"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"ouvrir l\'assistance vocale"</string>
     <string name="camera_label" msgid="7261107956054836961">"ouvrir l\'appareil photo"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Sélectionner un nouveau plan de tâche"</string>
     <string name="cancel" msgid="6442560571259935130">"Annuler"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Zone de message d\'aide"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmer"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Supprimer <xliff:g id="APP">%s</xliff:g>"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Application \"<xliff:g id="APP">%s</xliff:g>\" ignorée."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toutes les applications récentes ont été supprimées."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Ouvre les informations sur l\'application <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Lancement de <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification masquée"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Volet des notifications"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Paramètres rapides"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"La technologie NFC est désactivée"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"La technologie NFC est activée"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Aucun élément récent"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vous avez tout effacé."</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Infos application"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"épinglage d\'écran"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"L\'application <xliff:g id="APP">%s</xliff:g> est désactivée en mode sécurisé."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tout fermer"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Faire glisser ici pour utiliser l\'écran partagé"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Balayer l\'écran vers le haut pour changer d\'application"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Déplacer vers la droite pour changer rapidement d\'application"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Séparation horizontale"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Séparation verticale"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Séparation personnalisée"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Partager l\'écran en haut"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Partager l\'écran sur la gauche"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Partager l\'écran sur la droite"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Activer/Désactiver l\'aperçu"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Chargé"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"En charge"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Réduire"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuer d\'afficher les notifications de cette application ?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ces notifications ne peuvent pas être désactivées"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Cette application utilise la caméra."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Cette application utilise le micro."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Cette application se superpose aux autres applications sur l\'écran."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Afficher le pourcentage lorsque l\'appareil est en charge (option par défaut)"</item>
     <item msgid="3327323682209964956">"Ne plus afficher cette icône"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Autre"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Séparateur d\'écran partagé"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Écran de gauche en plein écran"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 80f3c93..f17243e 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"IU sistema"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Borrar"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Quitar da lista"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Información da aplicación"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"As túas pantallas recentes aparecen aquí"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Rexeitar aplicacións recentes"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d pantallas en visión xeral</item>
-      <item quantity="one">Unha pantalla en visión xeral</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Non hai notificacións"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"En curso"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificacións"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"abrir teléfono"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"abrir asistente de voz"</string>
     <string name="camera_label" msgid="7261107956054836961">"abrir cámara"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Seleccionar novo deseño de tarefas"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área de mensaxes de axuda"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Rexeitar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Rexeitouse <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Rexeitáronse todas as aplicacións recentes."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Abre a información da aplicación <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación rexeitada"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Sombra de notificación"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Configuración rápida"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"A opción NFC está desactivada"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"A opción NFC está activada"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Non hai elementos recentes"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Borraches todo"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Información da aplicación"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixación de pantalla"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Non foi posible iniciar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"A aplicación <xliff:g id="APP">%s</xliff:g> está desactivada no modo seguro"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Borrar todo"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Arrastrar aquí para usar a pantalla dividida"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Pasar o dedo cara arriba para cambiar de aplicación"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Arrastra cara á dereita para cambiar de aplicacións rapidamente"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Dividir en horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dividir en vertical"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Dividir de xeito personalizado"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dividir pantalla na parte superior"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dividir pantalla á esquerda"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dividir pantalla á dereita"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Activar/desactivar Visión xeral"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Cargando"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Queres seguir mostrando as notificacións desta aplicación?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Non se poden desactivar estas notificacións"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Esta aplicación está utilizando a cámara."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Esta aplicación está utilizando o micrófono."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Esta aplicación móstrase sobre outras aplicacións da pantalla."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Mostrar porcentaxe durante a carga (predeterminado)"</item>
     <item msgid="3327323682209964956">"Non mostrar esta icona"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Outros"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Divisor de pantalla dividida"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Pantalla completa á esquerda"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index cf5692b..1d55555 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"સિસ્ટમ UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"સાફ કરો"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"સૂચિમાંથી દૂર કરો"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"ઍપ્લિકેશન માહિતી"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"તમારી તાજેતરની સ્ક્રીન્સ અહીં દેખાય છે"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"તાજેતરની ઍપ્લિકેશનો કાઢી નાખો."</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">વિહંગાવલોકનમાં %d સ્ક્રીન્સ</item>
-      <item quantity="other">વિહંગાવલોકનમાં %d સ્ક્રીન્સ</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"કોઈ સૂચનાઓ નથી"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ચાલુ"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"નોટિફિકેશનો"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ફોન ખોલો"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"વૉઇસ સહાય ખોલો"</string>
     <string name="camera_label" msgid="7261107956054836961">"કૅમેરો ખોલો"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"નવું કાર્ય લેઆઉટ પસંદ કરો"</string>
     <string name="cancel" msgid="6442560571259935130">"રદ કરો"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"સહાય સંદેશનું ક્ષેત્ર"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"કન્ફર્મ કરો"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> કાઢી નાખો."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> કાઢી નાખી."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"તમામ તાજેતરની ઍપ્લિકેશનો કાઢી નાખી."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> ઍપ્લિકેશન માહિતી ખોલો."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> પ્રારંભ કરી રહ્યું છે."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"સૂચના કાઢી નાખી."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"નોટિફિકેશન શેડ."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"ઝડપી સેટિંગ્સ."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC અક્ષમ કરેલ છે"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC સક્ષમ કરેલ છે"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"કોઇ તાજેતરની આઇટમ્સ નથી"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"તમે બધું સાફ કર્યું"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"ઍપ્લિકેશન માહિતી"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"સ્ક્રીન પિનિંગ"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"શોધ"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> પ્રારંભ કરી શકાયું નથી."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"સુરક્ષિત મોડમાં <xliff:g id="APP">%s</xliff:g> અક્ષમ કરેલ છે."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"બધું સાફ કરો"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરવા માટે અહીં ખેંચો"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ઍપ સ્વિચ કરવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"ઍપને ઝડપથી સ્વિચ કરવા માટે જમણે ખેંચો"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"આડું વિભક્ત કરો"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ઊભું વિભક્ત કરો"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"કસ્ટમ વિભક્ત કરો"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"સ્ક્રીનને ઉપરની તરફ વિભાજિત કરો"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"સ્ક્રીનને ડાબી તરફ વિભાજિત કરો"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"સ્ક્રીનને જમણી તરફ વિભાજિત કરો"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"ઝલકને ટૉગલ કરો"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ચાર્જ થઈ ગયું"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ચાર્જ થઈ રહ્યું છે"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"નાનું કરો"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"આ ઍપમાંથી નોટિફિકેશન બતાવવાનું ચાલુ રાખીએ?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"આ નોટિફિકેશન બંધ કરી શકશો નહીં"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"આ ઍપ કૅમેરાનો ઉપયોગ કરી રહી છે."</string>
     <string name="appops_microphone" msgid="741508267659494555">"આ ઍપ માઇક્રોફોનનો ઉપયોગ કરી રહી છે."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"આ ઍપ તમારી સ્ક્રીન પરની અન્ય ઍપની ઉપર પ્રદર્શિત થઈ રહી છે."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"ચાર્જ થાય ત્યારે ટકાવારી બતાવો (ડિફોલ્ટ)"</item>
     <item msgid="3327323682209964956">"આ આઇકન બતાવશો નહીં"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"અન્ય"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"સ્પ્લિટ-સ્ક્રીન વિભાજક"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"ડાબી પૂર્ણ સ્ક્રીન"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index ac34e9a..e6004f3 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"सिस्‍टम यूआई"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"साफ़ करें"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"सूची से निकालें"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"ऐप की जानकारी"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"आपकी हाल की स्‍क्रीन यहां दिखाई देती हैं"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"हाल ही के ऐप्स  खारिज करें"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d स्क्रीन की खास जानकारी</item>
-      <item quantity="other">%d स्क्रीन की खास जानकारी</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"कोई सूचना नहीं है"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ऑनगोइंग"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"सूचनाएं"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"फ़ोन खोलें"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"आवाज़ से डिवाइस को इस्तेमाल करें"</string>
     <string name="camera_label" msgid="7261107956054836961">"कैमरा खोलें"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"नया कार्य लेआउट चुनें"</string>
     <string name="cancel" msgid="6442560571259935130">"रद्द करें"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"सहायता का मैसेज दिखाने की जगह"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"पुष्टि करें"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> को ख़ारिज करें."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> खा़रिज कर दिया गया."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"हाल ही के सभी ऐप्लिकेशन ख़ारिज कर दिए गए."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> ऐप्लिकेशन की जानकारी खोलें."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> प्रारंभ हो रहा है."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"सूचना खारिज की गई."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"सूचना शेड."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"त्वरित सेटिंग."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"एनएफ़सी"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC बंद है"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC चालू है"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"हाल ही का कोई आइटम नहीं"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"आपने सब कुछ साफ़ कर दिया है"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"ऐप्लिकेशन की जानकारी"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"स्क्रीन पिन करना"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"सर्च"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> प्रारंभ नहीं किया जा सका."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> को सुरक्षित-मोड में बंद किया गया."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Clear all"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"स्क्रीन के दो हिस्से में बंट जाने, स्पिल्ट स्क्रीन, का इस्तेमाल करने के लिए यहां खींचें और छोडें"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ऐप्लिकेशन बदलने के लिए ऊपर स्वाइप करें"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"ऐप्लिकेशन को झटपट स्विच करने के लिए उसे दाईं ओर खींचें और छोड़ें"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"क्षैतिज रूप से विभाजित करें"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"लम्बवत रूप से विभाजित करें"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"अपने मुताबिक बांटें"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"ऊपर की ओर दो स्क्रीन बनाएं"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"बाईं ओर दो स्क्रीन बनाएं"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"दाईं ओर दो स्क्रीन बनाएं"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"खास जानकारी टॉगल करें"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज हो गई है"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"चार्ज हो रही है"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"सूचनाएं छोटी करें"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"इस ऐप्लिकेशन से जुड़ी सूचनाएं दिखाना जारी रखें?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"ये सूचनाएं दिखाया जाना बंद नहीं किया जा सकता"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"यह ऐप्लिकेशन कैमरे का इस्तेमाल कर रहा है."</string>
     <string name="appops_microphone" msgid="741508267659494555">"यह ऐप्लिकेशन माइक्रोफ़ोन का इस्तेमाल कर रहा है."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"यह ऐप्लिकेशन आपकी स्क्रीन पर इस्तेमाल हो रहे दूसरे ऐप्लिकेशन के ऊपर दिखाया जा रहा है."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"चार्ज होते समय प्रतिशत दिखाएं (डिफ़ॉल्ट)"</item>
     <item msgid="3327323682209964956">"इस आइकॉन को ना दिखाएं"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"अन्य"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"विभाजित स्क्रीन विभाजक"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"बाईं स्क्रीन को पूर्ण स्क्रीन बनाएं"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 5df6999..777a9ff 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -21,15 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"UI sustava"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Očisti"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Ukloni s popisa"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informacije o aplikaciji"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Ovdje se pojavljuju vaši nedavni zasloni"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Odbaci nedavne aplikacije"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d zaslon u Pregledu</item>
-      <item quantity="few">%d zaslona u Pregledu</item>
-      <item quantity="other">%d zaslona u Pregledu</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Bez obavijesti"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"U tijeku"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Obavijesti"</string>
@@ -102,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"otvaranje telefona"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"otvaranje glasovne pomoći"</string>
     <string name="camera_label" msgid="7261107956054836961">"otvaranje fotoaparata"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Odaberite novi izgled zadataka"</string>
     <string name="cancel" msgid="6442560571259935130">"Odustani"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Područje poruke za pomoć"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdi"</string>
@@ -189,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Odbacivanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> odbačena je."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Odbačene su sve nedavne aplikacije."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Otvaranje informacija o aplikaciji <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Pokretanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obavijest je odbačena."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Zaslon obavijesti."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Brze postavke."</string>
@@ -358,23 +343,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC je onemogućen"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC je omogućen"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Nema nedavnih stavki"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Izbrisali ste sve"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"prikvačivanje zaslona"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"pretraži"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacija <xliff:g id="APP">%s</xliff:g> nije pokrenuta."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikacija <xliff:g id="APP">%s</xliff:g> onemogućena je u sigurnom načinu."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Izbriši sve"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Povucite ovdje da biste upotrebljavali podijeljeni zaslon"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Prijeđite prstom prema gore da biste promijenili aplikaciju"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Povucite udesno da biste brzo promijenili aplikaciju"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podijeli vodoravno"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podijeli okomito"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Podijeli prilagođeno"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Podijeli zaslon na vrhu"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Podijeli zaslon slijeva"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Podijeli zaslon zdesna"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Uključivanje/isključivanje pregleda"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjeno"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Punjenje"</string>
@@ -622,6 +592,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimiziraj"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite li da se obavijesti te aplikacije nastave prikazivati?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Te se obavijesti ne mogu isključiti"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"putem aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Ova aplikacija upotrebljava kameru."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Ova aplikacija upotrebljava mikrofon."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Ova se aplikacija prikazuje preko drugih aplikacija na zaslonu."</string>
@@ -758,6 +729,7 @@
     <item msgid="2139628951880142927">"Prikazuj postotak tijekom punjenja (zadano)"</item>
     <item msgid="3327323682209964956">"Ne prikazuj tu ikonu"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Prikaži ikone obavijesti niskog prioriteta"</string>
     <string name="other" msgid="4060683095962566764">"Ostalo"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Razdjelnik podijeljenog zaslona"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Lijevi zaslon u cijeli zaslon"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index f09a8b3..16db000 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Rendszer UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Törlés"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Eltávolítás a listából"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Alkalmazásinformáció"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"A legutóbbi képernyők itt jelennek meg"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Újabb alkalmazások elvetése"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d képernyő áttekintés alatt</item>
-      <item quantity="one">1 képernyő áttekintés alatt</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nincs értesítés"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Folyamatban van"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Értesítések"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"telefon megnyitása"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"hangsegéd megnyitása"</string>
     <string name="camera_label" msgid="7261107956054836961">"kamera megnyitása"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Új feladatelrendezés kiválasztása"</string>
     <string name="cancel" msgid="6442560571259935130">"Mégse"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Súgószöveg területe"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Megerősítés"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"A(z) <xliff:g id="APP">%s</xliff:g> elvetése."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> eltávolítva."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Az összes alkalmazás eltávolítva a nemrég használtak közül."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"A(z) <xliff:g id="APP">%s</xliff:g> alkalmazás adatainak megnyitása."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"A(z) <xliff:g id="APP">%s</xliff:g> indítása."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Értesítés elvetve."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Értesítési felület."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Gyorsbeállítások."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Az NFC ki van kapcsolva"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Az NFC be van kapcsolva"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Nincsenek mostanában használt elemek"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Mindent törölt"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Az alkalmazás adatai"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"képernyő rögzítése"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"keresés"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Nem lehet elindítani a következőt: <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"A(z) <xliff:g id="APP">%s</xliff:g> csökkentett módban ki van kapcsolva."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Összes törlése"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Húzza ide az osztott képernyő használatához"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Váltás az alkalmazások között felfelé csúsztatással"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Húzza jobbra az ujját az alkalmazások közötti gyors váltáshoz"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Osztott vízszintes"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Osztott függőleges"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Osztott egyéni"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Osztott képernyő felülre"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Osztott képernyő balra"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Osztott képernyő jobbra"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Áttekintés be- és kikapcsolása"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Feltöltve"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Töltés"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Kis méret"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Továbbra is megjelenjenek az alkalmazás értesítései?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ezeket az értesítéseket nem lehet kikapcsolni"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Ez az alkalmazás használja a kamerát."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Ez az alkalmazás használja a mikrofont."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Ez az alkalmazás a képernyőn lévő egyéb alkalmazások előtt jelenik meg."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Százalékos érték töltés közben látható (alapértelmezett)"</item>
     <item msgid="3327323682209964956">"Ne jelenjen meg ez az ikon"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Egyéb"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Elválasztó az osztott nézetben"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Bal oldali teljes képernyőre"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 8973ecf..c1ee1d7 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Համակարգային UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Մաքրել"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Հեռացնել ցանկից"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Տեղեկություններ ծրագրի մասին"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Ձեր վերջին էկրանները տեսանելի են այստեղ"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Անտեսել վերջին ծրագրերը"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">Համատեսքում ցուցադրված է %d էկրան</item>
-      <item quantity="other">Համատեսքում ցուցադրված է %d էկրան</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ծանուցումներ չկան"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ընթացիկ"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ծանուցումներ"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"բացել հեռախոսը"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"բացեք ձայնային հուշումը"</string>
     <string name="camera_label" msgid="7261107956054836961">"բացել ֆոտոխցիկը"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Ընտրել առաջադրանքի նոր դասավորություն"</string>
     <string name="cancel" msgid="6442560571259935130">"Չեղարկել"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Օգնության հաղորդագրության դաշտ"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Հաստատել"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Անտեսել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>-ը անտեսված է:"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Բոլոր վերջին հավելվածները հեռացվել են ցուցակից:"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Բացել <xliff:g id="APP">%s</xliff:g> հավելվածի մասին տեղեկությունները"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Մեկնարկել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Ծանուցումը անտեսվեց:"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Ծանուցումների վահանակ:"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Արագ կարգավորումներ:"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC-ն անջատված է"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC-ն միացված է"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Վերջին տարրեր չկան"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Դուք ջնջել եք ամենը"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Հավելվածի մասին"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"էկրանի ամրացում"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"որոնել"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Հնարավոր չէ գործարկել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> հավելվածը անվտանգ ռեժիմում անջատված է:"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Մաքրել բոլորը"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Քաշեք այստեղ՝ էկրանի տրոհումն օգտագործելու համար"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Սահեցրեք վերև՝ մյուս հավելվածին անցնելու համար"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Քաշեք աջ՝ հավելվածների միջև անցնելու համար"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Հորիզոնական տրոհում"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Ուղղահայաց տրոհում"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Հատուկ տրոհում"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Տրոհել էկրանը վերևից"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Տրոհել էկրանը ձախից"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Տրոհել էկրանն աջից"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Միացնել/անջատել համատեսքը"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Լիցքավորված է"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Լիցքավորվում է"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Ծալել"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Ցուցադրե՞լ ծանուցումներ այս հավելվածից։"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Այս ծանուցումները հնարավոր չէ անջատել"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Այս հավելվածն օգտագործում է տեսախցիկը:"</string>
     <string name="appops_microphone" msgid="741508267659494555">"Այս հավելվածն օգտագործում է խոսափողը:"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Այս հավելվածը ցուցադրվում է մյուս հավելվածների վրայից:"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Ցույց տալ տոկոսը լիցքավորելու ժամանակ (կանխադրված է)"</item>
     <item msgid="3327323682209964956">"Ցույց չտալ այս պատկերակը"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Այլ"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Տրոհված էկրանի բաժանիչ"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Ձախ էկրանը՝ լիաէկրան"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 2e3040c..1cd4d30 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Sistem UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Bersihkan"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Hapus dari daftar"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Info aplikasi"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Layar terkini Anda muncul di sini"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Tutup aplikasi terbaru"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d layar dalam Ringkasan</item>
-      <item quantity="one">1 layar dalam Ringkasan</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Tidak ada notifikasi"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Berkelanjutan"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifikasi"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"buka ponsel"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"buka bantuan suara"</string>
     <string name="camera_label" msgid="7261107956054836961">"buka kamera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Pilih tata letak tugas baru"</string>
     <string name="cancel" msgid="6442560571259935130">"Batal"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Area pesan bantuan"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Konfirmasi"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Menyingkirkan <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> disingkirkan."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Semua aplikasi terbaru telah ditutup."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Buka info aplikasi <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Memulai <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notifikasi disingkirkan."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bayangan pemberitahuan."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Setelan cepat."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC dinonaktifkan"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC diaktifkan"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Tidak ada item baru-baru ini"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Anda sudah menghapus semua"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Info Aplikasi"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pin ke layar"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"telusuri"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Tidak dapat memulai <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> dinonaktifkan dalam mode aman."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Hapus semua"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Tarik ke sini untuk menggunakan layar terpisah"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Geser ke atas untuk beralih aplikasi"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Tarik ke kanan untuk beralih aplikasi dengan cepat"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Pisahkan Horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Pisahkan Vertikal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Pisahkan Khusus"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Pisahkan layar ke atas"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Pisahkan layar ke kiri"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Pisahkan layar ke kanan"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Aktifkan Ringkasan"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Terisi"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Mengisi daya"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Perkecil"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Terus tampilkan notifikasi dari aplikasi ini?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Notifikasi ini tidak dapat dinonaktifkan"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"melalui <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Aplikasi ini sedang menggunakan kamera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Aplikasi ini sedang menggunakan mikrofon."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Aplikasi ini ditampilkan di atas aplikasi lain di layar."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Tampilkan persentase saat mengisi daya (default)"</item>
     <item msgid="3327323682209964956">"Jangan tampilkan ikon ini"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Tampilkan ikon notifikasi prioritas rendah"</string>
     <string name="other" msgid="4060683095962566764">"Lainnya"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Pembagi layar terpisah"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Layar penuh di kiri"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index fd15fc3..92d86ae 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Kerfisviðmót"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Hreinsa"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Fjarlægja af lista"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Upplýsingar um forrit"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Nýlegar skjámyndir birtast hér"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Hunsa nýleg forrit"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d skjámynd í yfirliti</item>
-      <item quantity="other">%d skjámyndir í yfirliti</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Engar tilkynningar"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Áframhaldandi"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Tilkynningar"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"opna síma"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"opna raddaðstoð"</string>
     <string name="camera_label" msgid="7261107956054836961">"opna myndavél"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Velja nýtt útlit verkefna"</string>
     <string name="cancel" msgid="6442560571259935130">"Hætta við"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Svæði hjálparskilaboða"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Staðfesta"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Hunsa <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> vísað frá."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Öll nýleg forrit fjarlægð."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Opna forritsupplýsingar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Ræsir <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Tilkynningu lokað."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Tilkynningasvæði."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Flýtistillingar."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Slökkt á NFC"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Kveikt á NFC"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Engin nýleg atriði"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Þú hefur hreinsað allt"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Forritsupplýsingar"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"skjáfesting"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"leita"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Ekki var hægt að ræsa <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Slökkt er á <xliff:g id="APP">%s</xliff:g> í öruggri stillingu."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Hreinsa allt"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Dragðu hingað til að skipta skjánum"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Strjúktu upp til að skipta á milli forrita"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Dragðu til hægri til að skipta hratt á milli forrita"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Lárétt skipting"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Lóðrétt skipting"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Sérsniðin skipting"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Skipta skjá að ofanverðu"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Skipta skjá til vinstri"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Skipta skjá til hægri"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Kveikja/slökkva á yfirliti"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Fullhlaðin"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Í hleðslu"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minnka"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Sýna áfram tilkynningar frá þessu forriti?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ekki er hægt að slökkva á þessum tilkynningum"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"með <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Þetta forrit er að nota myndavélina."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Þetta forrit er að nota hljóðnemann."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Þetta forrit er að birta efni yfir öðrum forritum á skjánum þínum."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Sýna hlutfall meðan á hleðslu stendur (sjálfgefið)"</item>
     <item msgid="3327323682209964956">"Ekki sýna þetta tákn"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Sýna tákn fyrir tilkynningar með litlum forgangi"</string>
     <string name="other" msgid="4060683095962566764">"Annað"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Skjáskipting"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Vinstri á öllum skjánum"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 37fabb8..3cac6c9 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"UI sistema"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Cancella"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Rimuovi dall\'elenco"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informazioni applicazione"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Le tue schermate recenti vengono visualizzate in questa sezione"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Ignora app recenti"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d schermate in Panoramica</item>
-      <item quantity="one">1 schermata in Panoramica</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nessuna notifica"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"In corso"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifiche"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"apri telefono"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"apri Voice Assist"</string>
     <string name="camera_label" msgid="7261107956054836961">"apri fotocamera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Seleziona un nuovo layout per le attività"</string>
     <string name="cancel" msgid="6442560571259935130">"Annulla"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Area dei messaggi di assistenza"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confermo"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Elimina <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> eliminata."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Tutte le applicazioni recenti sono state rimosse."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Mostra informazioni sull\'applicazione <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Avvio di <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notifica eliminata."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Area notifiche."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Impostazioni rapide."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC non attiva"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC attiva"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Nessun elemento recente"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Hai cancellato tutto"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informazioni sull\'applicazione"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"blocco su schermo"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Impossibile avviare <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"L\'app <xliff:g id="APP">%s</xliff:g> è stata disattivata in modalità provvisoria."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Cancella tutto"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Trascina qui per utilizzare la modalità Schermo diviso"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Scorri verso l\'alto per passare ad altre app"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Trascina verso destra per cambiare velocemente app"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisione in orizzontale"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisione in verticale"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisione personalizzata"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Schermo diviso in alto"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Schermo diviso a sinistra"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Schermo diviso a destra"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Attiva/disattiva la panoramica"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carica"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"In carica"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Riduci a icona"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuare a ricevere notifiche da questa app?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Queste notifiche non possono essere disattivate"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"tramite <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Questa app sta utilizzando la fotocamera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Questa app sta utilizzando il microfono."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Questa app è visualizzata sopra altre app sullo schermo."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Mostra la percentuale quando in carica (opzione predefinita)"</item>
     <item msgid="3327323682209964956">"Non mostrare questa icona"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Mostra icone di notifiche con priorità bassa"</string>
     <string name="other" msgid="4060683095962566764">"Altro"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Strumento per schermo diviso"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Schermata sinistra a schermo intero"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 0d60e6c..a65b917 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -21,17 +21,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"ממשק משתמש של המערכת"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"ניקוי"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"הסר מהרשימה"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"פרטי אפליקציה"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"המסכים האחרונים מופיעים כאן"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"סגור אפליקציות אחרונות"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="two">‏%d מסכים ב’סקירה‘</item>
-      <item quantity="many">‏%d מסכים ב’סקירה‘</item>
-      <item quantity="other">‏%d מסכים ב’סקירה‘</item>
-      <item quantity="one">מסך אחד ב’סקירה‘</item>
-    </plurals>
-    <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"אין הודעות"</string>
+    <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"אין התראות"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"מתמשך"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"התראות"</string>
     <string name="battery_low_title" msgid="9187898087363540349">"ייתכן שהסוללה תתרוקן בקרוב"</string>
@@ -103,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"פתח את הטלפון"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"פתח את המסייע הקולי"</string>
     <string name="camera_label" msgid="7261107956054836961">"פתח את המצלמה"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"בחר פריסה חדשה להצגת משימות"</string>
     <string name="cancel" msgid="6442560571259935130">"ביטול"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"אזור הודעת עזרה"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"אישור"</string>
@@ -179,7 +168,7 @@
     <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"טעינת סוללה, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"הגדרות מערכת"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"התראות"</string>
-    <string name="accessibility_overflow_action" msgid="5681882033274783311">"הצגת כל ההודעות"</string>
+    <string name="accessibility_overflow_action" msgid="5681882033274783311">"הצגת כל ההתראות"</string>
     <string name="accessibility_remove_notification" msgid="3603099514902182350">"מחיקת התראה"</string>
     <string name="accessibility_gps_enabled" msgid="3511469499240123019">"‏GPS מופעל."</string>
     <string name="accessibility_gps_acquiring" msgid="8959333351058967158">"‏השגת GPS."</string>
@@ -190,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"סגור את <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> נדחה."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"כל האפליקציות האחרונות נסגרו."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"פתח מידע על האפליקציה <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"מפעיל את <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"התראה נדחתה."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"לוח התראות."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"הגדרות מהירות."</string>
@@ -263,10 +247,10 @@
     <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
     <string name="notification_group_overflow_indicator_ambient" msgid="879560382990377886">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, +<xliff:g id="OVERFLOW">%2$s</xliff:g>"</string>
     <plurals name="notification_group_overflow_description" formatted="false" msgid="4579313201268495404">
-      <item quantity="two">יש בפנים עוד <xliff:g id="NUMBER_1">%s</xliff:g> הודעות.</item>
-      <item quantity="many">יש בפנים עוד <xliff:g id="NUMBER_1">%s</xliff:g> הודעות.</item>
-      <item quantity="other">יש בפנים עוד <xliff:g id="NUMBER_1">%s</xliff:g> הודעות.</item>
-      <item quantity="one">יש בפנים עוד הודעה <xliff:g id="NUMBER_0">%s</xliff:g>.</item>
+      <item quantity="two">עוד <xliff:g id="NUMBER_1">%s</xliff:g> התראות נוספות.</item>
+      <item quantity="many">עוד <xliff:g id="NUMBER_1">%s</xliff:g> התראות.</item>
+      <item quantity="other">עוד <xliff:g id="NUMBER_1">%s</xliff:g> התראות נוספות.</item>
+      <item quantity="one">יש התראה נוספת.<xliff:g id="NUMBER_0">%s</xliff:g>.</item>
     </plurals>
     <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"הגדרת התראות"</string>
     <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"הגדרות <xliff:g id="APP_NAME">%s</xliff:g>"</string>
@@ -361,23 +345,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"‏NFC מושבת"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"‏NFC מופעל"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"אין פריטים אחרונים"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"מחקת הכול"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"מידע על האפליקציה"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"הצמדת מסך"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"חיפוש"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"לא ניתן היה להפעיל את <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> מושבת במצב בטוח."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"נקה הכל"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"גרור לכאן כדי להשתמש במסך מפוצל"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"יש להחליק מעלה כדי להחליף אפליקציות"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"יש לגרור ימינה כדי לעבור במהירות בין אפליקציות"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"פיצול אופקי"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"פיצול אנכי"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"פיצול מותאם אישית"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"פיצול מסך למעלה"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"פיצול מסך לשמאל"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"פיצול מסך לימין"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"החלפת מצב של מסכים אחרונים"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"טעון"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"טוען"</string>
@@ -514,7 +483,7 @@
     <string name="keyguard_indication_trust_granted" msgid="4985003749105182372">"הנעילה בוטלה על ידי <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_trust_managed" msgid="8319646760022357585">"<xliff:g id="TRUST_AGENT">%1$s</xliff:g> פועל"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"המכשיר יישאר נעול עד שתבטל את נעילתו באופן ידני"</string>
-    <string name="hidden_notifications_title" msgid="7139628534207443290">"קבל הודעות מהר יותר"</string>
+    <string name="hidden_notifications_title" msgid="7139628534207443290">"קבלה מהירה של התראות"</string>
     <string name="hidden_notifications_text" msgid="2326409389088668981">"צפה בהן לפני שתבטל נעילה"</string>
     <string name="hidden_notifications_cancel" msgid="3690709735122344913">"לא, תודה"</string>
     <string name="hidden_notifications_setup" msgid="41079514801976810">"הגדר"</string>
@@ -608,14 +577,14 @@
     <string name="enable_bluetooth_title" msgid="5027037706500635269">"‏האם להפעיל את ה-Bluetooth?"</string>
     <string name="enable_bluetooth_message" msgid="9106595990708985385">"‏כדי לחבר את המקלדת לטאבלט, תחילה עליך להפעיל את ה-Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"הפעל"</string>
-    <string name="show_silently" msgid="6841966539811264192">"הצג הודעות בלי להשמיע צליל"</string>
-    <string name="block" msgid="2734508760962682611">"חסום את כל ההודעות"</string>
+    <string name="show_silently" msgid="6841966539811264192">"הצגת התראות בלי להשמיע צליל"</string>
+    <string name="block" msgid="2734508760962682611">"חסימת כל ההודעות"</string>
     <string name="do_not_silence" msgid="6878060322594892441">"לא להשתיק"</string>
     <string name="do_not_silence_block" msgid="4070647971382232311">"לא להשתיק או לחסום"</string>
     <string name="tuner_full_importance_settings" msgid="3207312268609236827">"פקדים של הודעות הפעלה"</string>
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"פועל"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"כבוי"</string>
-    <string name="power_notification_controls_description" msgid="4372459941671353358">"בעזרת פקדים של התראות הפעלה, אפשר להגדיר רמת חשיבות מ-0 עד 5 להתראות אפליקציה. \n\n"<b>"רמה 5"</b>" \n- הצגה בראש רשימת ההודעות \n- אפשר הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 4"</b>" \n- מנע הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 3"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n\n"<b>"רמה 2"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n\n"<b>"רמה 1"</b>" \n- מניעת הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n- הסתרה ממסך הנעילה ומשורת הסטטוס \n- הצגה בתחתית רשימת ההתראות \n\n"<b>"רמה 0"</b>" \n- חסימה את כל ההתראות מהאפליקציה"</string>
+    <string name="power_notification_controls_description" msgid="4372459941671353358">"בעזרת פקדים של התראות הפעלה, אפשר להגדיר רמת חשיבות מ-0 עד 5 להתראות אפליקציה. \n\n"<b>"רמה 5"</b>" \n- הצגה בראש רשימת ההתראות \n- אפשר הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 4"</b>" \n- מנע הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 3"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n\n"<b>"רמה 2"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n\n"<b>"רמה 1"</b>" \n- מניעת הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n- הסתרה ממסך הנעילה ומשורת הסטטוס \n- הצגה בתחתית רשימת ההתראות \n\n"<b>"רמה 0"</b>" \n- חסימה את כל ההתראות מהאפליקציה"</string>
     <string name="notification_header_default_channel" msgid="7506845022070889909">"התראות"</string>
     <string name="notification_channel_disabled" msgid="344536703863700565">"ההודעות האלה לא יוצגו לך יותר"</string>
     <string name="notification_channel_minimized" msgid="1664411570378910931">"ההודעות האלה ימוזערו"</string>
@@ -626,6 +595,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"מזעור"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"שנמשיך להציג לך הודעות מהאפליקציה הזאת?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"לא ניתן לכבות את ההודעות האלה"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"האפליקציה הזו משתמשת במצלמה."</string>
     <string name="appops_microphone" msgid="741508267659494555">"האפליקציה הזו משתמשת במיקרופון."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"האפליקציה הזו מוצגת מעל אפליקציות אחרות במסך."</string>
@@ -644,7 +615,7 @@
     <string name="inline_undo" msgid="558916737624706010">"ביטול"</string>
     <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="notification_menu_gear_description" msgid="2204480013726775108">"בקרת התראות"</string>
-    <string name="notification_menu_snooze_description" msgid="3653669438131034525">"אפשרויות של דחיית הודעות לטיפול בהמשך"</string>
+    <string name="notification_menu_snooze_description" msgid="3653669438131034525">"אפשרויות של דחיית התראות לטיפול בהמשך"</string>
     <string name="notification_menu_snooze_action" msgid="1112254519029621372">"הפעלת נודניק"</string>
     <string name="snooze_undo" msgid="6074877317002985129">"ביטול"</string>
     <string name="snoozed_for_time" msgid="2390718332980204462">"נדחה לטיפול בעוד <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
@@ -764,6 +735,8 @@
     <item msgid="2139628951880142927">"הצג באחוזים בזמן טעינה (ברירת מחדל)"</item>
     <item msgid="3327323682209964956">"אל תציג את הסמל הזה"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"אחר"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"מחלק מסך מפוצל"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"מסך שמאלי מלא"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index c651f9e..6617fdd 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"システムUI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"通知を消去"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"リストから削除"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"アプリ情報"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"ここに最近の画面が表示されます"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"最近使ったアプリをクリア"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">[最近]に%d個の画面があります</item>
-      <item quantity="one">[最近]に1個の画面があります</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"通知なし"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"実行中"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"電話を起動"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"音声アシストを開く"</string>
     <string name="camera_label" msgid="7261107956054836961">"カメラを起動"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"新しいタスクレイアウトの選択"</string>
     <string name="cancel" msgid="6442560571259935130">"キャンセル"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ヘルプ メッセージ領域"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"確認"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>を削除します。"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>は削除されました。"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"最近のアプリケーションをすべて消去しました。"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"「<xliff:g id="APP">%s</xliff:g>」のアプリ情報を開きます。"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>を開始しています。"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"通知が削除されました。"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"通知シェード"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"クイック設定"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC は無効です"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC は有効です"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"最近のタスクはありません"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"すべてのタスクを消去しました"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"アプリ情報"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"画面固定"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"検索"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>を開始できません。"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"「<xliff:g id="APP">%s</xliff:g>」はセーフモードでは無効になります。"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"すべて消去"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"分割画面を使用するにはここにドラッグします"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"アプリを切り替えるには上にスワイプ"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"右にドラッグするとアプリを素早く切り替えることができます"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"横に分割"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"縦に分割"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"分割(カスタム)"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"画面を上に分割"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"画面を左に分割"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"画面を右に分割"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"概要を切り替え"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"充電が完了しました"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"充電しています"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"このアプリからの通知を今後も表示しますか?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"この通知を OFF にすることはできません"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"このアプリはカメラを使用しています。"</string>
     <string name="appops_microphone" msgid="741508267659494555">"このアプリはマイクを使用しています。"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"このアプリは画面上で他のアプリの上に重ねて表示されます。"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"変更時に割合を表示(デフォルト)"</item>
     <item msgid="3327323682209964956">"このアイコンを表示しない"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"その他"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"分割画面の分割線"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"左全画面"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index f91ffb0..359e689 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"სისტემის UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"გასუფთავება"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"სიიდან ამოშლა"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"აპის შესახებ"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"თქვენი ბოლო ეკრანები აქ გამოჩნდება"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"ბოლო აპების გაუქმება"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d ეკრანი მიმოხილვაში</item>
-      <item quantity="one">1 ეკრანი მიმოხილვაში</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"შეტყობინებები არ არის."</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"მიმდინარე"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"შეტყობინებები"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ტელეფონის გახსნა"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"ხმოვანი დახმარების გახსნა"</string>
     <string name="camera_label" msgid="7261107956054836961">"კამერის გახსნა"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"ახალი ამოცანის განლაგების არჩევა"</string>
     <string name="cancel" msgid="6442560571259935130">"გაუქმება"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"დამხმარე შეტყობინების არე"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"დადასტურება"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>-ის უგულებელყოფა."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ამოშლილია სიიდან."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ყველა ბოლო აპლიკაცია გაუქმდა."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> აპლიკაციის ინფორმაციის გახსნა."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> იწყება."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"შეტყობინება წაიშალა."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"შეტყობინებების ფარდა"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"სწრაფი პარამეტრები"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC გათიშულია"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ჩართულია"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"ბოლოს გამოყენებული ერთეულები არ არის"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ყველაფერი გასუფთავდა"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"აპლიკაციის შესახებ"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ეკრანზე ჩამაგრება"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"ძიება"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>-ის გამოძახება ვერ მოხერხდა."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> გათიშულია უსაფრთხო რეჟიმში."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ყველას გასუფთავება"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"ეკრანის გასაყოფად, ჩავლებით გადმოიტანეთ აქ"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"გადაფურცლეთ ზემოთ აპების გადასართავად"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"აპების სწრაფად გადასართავად ჩავლებით გადაიტანეთ მარჯვნივ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ჰორიზონტალური გაყოფა"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ვერტიკალური გაყოფა"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ინდივიდუალური გაყობა"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"ეკრანის გაყოფა ზემოთ"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"ეკრანის გაყოფა მარცხნივ"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"ეკრანის გაყოფა მარჯვნივ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"მიმოხილვის გადართვა"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"დატენილია"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"მიმდინარეობს დატენვა"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"ჩაკეცვა"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"გაგრძელდეს შეტყობინებათა ჩვენება ამ აპიდან?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"ამ შეტყობინებათა გამორთვა ვერ მოხერხდება"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"ეს აპი იყენებს კამერას."</string>
     <string name="appops_microphone" msgid="741508267659494555">"ეს აპი იყენებს მიკროფონს."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"ეს აპი თქვენს ეკრანზე ფარავს სხვა აპებს."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"პროცენტულობის დატენვისას ჩვენება (ნაგულისხმევი)"</item>
     <item msgid="3327323682209964956">"აღარ მაჩვენო ეს ხატულა"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"სხვა"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"გაყოფილი ეკრანის რეჟიმის გამყოფი"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"მარცხენა ნაწილის სრულ ეკრანზე გაშლა"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index b79ea7b..6598bf0 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Жүйе интерфейсі"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Тазалау"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Тізімнен алу"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Қолданба ақпараты"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Мұнда жақындағы экрандар көрсетіледі"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Жуықта қолданылған қолданбаларды қоспау"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">«Шолу» ішінде %d экран</item>
-      <item quantity="one">«Шолу» ішінде 1 экран</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Хабарлар жоқ"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ағымдағы"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Хабарлар"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"телефонды ашу"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"ашық дауыс көмекшісі"</string>
     <string name="camera_label" msgid="7261107956054836961">"камераны ашу"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Жаңа тапсырма пішімін таңдау"</string>
     <string name="cancel" msgid="6442560571259935130">"Бас тарту"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Анықтама хабары аумағы"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Растау"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> қолданбасынан бас тарту."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> алынып тасталған."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Барлық жақындағы қабылданбаған қолданбалар."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> қолданбасы туралы ақпаратты ашады."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> іске қосылуда."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Хабар алынып тасталды."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Хабарландыру тақтасы"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Жылдам параметрлер."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC өшірулі"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC қосулы"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Жақындағы элементтер жоқ"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Сіз барлығын өшірдіңіз"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Қолданба туралы ақпарат"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"экранды бекіту"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"іздеу"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> іске қосу мүмкін болмады."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> қауіпсіз режимде өшіріледі."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Барлығын тазалау"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Бөлінген экранды пайдалану үшін осында сүйреңіз"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Қолданбалар арасында ауысу үшін жоғары сырғытыңыз"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Қолданбаларды жылдам ауыстырып қосу үшін оңға қарай сүйреңіз"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Бөлінген көлденең"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Бөлінген тік"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Бөлінген теңшелетін"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Экранды жоғарыға қарай бөлу"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Экранды солға қарай бөлу"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Экранды оңға қарай бөлу"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Шолуды қосу/өшіру"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Зарядталды"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарядталуда"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Жасыру"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Осы қолданбаның хабарландырулары көрсетілсін бе?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Хабарландыруларды өшіру мүмкін емес"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Бұл қолданба камераны пайдалануда."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Бұл қолданба микрофонды пайдалануда."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Бұл қолданба экранда басқа қолданбалардың үстінен көрсетіліп тұр."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Зарядтау кезінде пайызды көрсету (әдепкі)"</item>
     <item msgid="3327323682209964956">"Бұл белгішені көрсетпеу"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Басқа"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Бөлінген экран бөлгіші"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Сол жағын толық экранға шығару"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 0731a5e..8545ad6 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"ចំណុច​ប្រទាក់​ប្រព័ន្ធ"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"សម្អាត"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"យក​ចេញ​ពី​បញ្ជី"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"ព័ត៌មាន​កម្មវិធី"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"អេក្រង់​បច្ចុប្បន្ន​របស់​អ្នក​បង្ហាញ​នៅ​ទីនេះ"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"បដិសេធ​កម្មវិធី​ថ្មីៗ"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">អេក្រង់ %d ក្នុងទិដ្ឋភាព</item>
-      <item quantity="one">អេក្រង់ 1 ក្នុងទិដ្ឋភាព</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"គ្មាន​ការ​ជូន​ដំណឹង"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"បន្ត"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"ការ​ជូន​ដំណឹង"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"បើក​ទូរស័ព្ទ"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"បើកជំនួយសំឡេង"</string>
     <string name="camera_label" msgid="7261107956054836961">"បើក​ម៉ាស៊ីន​ថត"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"ជ្រើសប្លង់ភារកិច្ចថ្មី"</string>
     <string name="cancel" msgid="6442560571259935130">"បោះ​បង់​"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"តំបន់សារ​ជំនួយ"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"បញ្ជាក់"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"បោះបង់ <xliff:g id="APP">%s</xliff:g> ។"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសេធ។"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"កម្មវិធីថ្មីៗទាំងអស់ត្រូវបានបោះបង់។"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"បើកព័ត៌មានកម្មវិធី <xliff:g id="APP">%s</xliff:g>"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"ចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> ។"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"បាន​បដិសេធ​ការ​ជូនដំណឹង"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"ពណ៌​ការ​ជូន​ដំណឹង"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"ការ​កំណត់​រហ័ស។"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"បាន​បិទ NFC"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"បាន​បើក NFC"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"មិនមានធាតុថ្មីៗទេ"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"អ្នកបានជម្រះអ្វីៗទាំងអស់"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"ព័ត៌មាន​កម្មវិធី"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ការ​ភ្ជាប់​អេក្រង់"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"មិន​អាច​ចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> ទេ។"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ត្រូវបានបិទដំណើរការក្នុងរបៀបសុវត្ថិភាព"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"សម្អាតទាំងអស់"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"អូសនៅទីនេះដើម្បីប្រើអេក្រង់បំបែក"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"អូស​ឡើង​លើ​ដើម្បី​ប្តូរ​កម្មវិធី"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"អូសទៅស្ដាំដើម្បីប្ដូរកម្មវិធីបានរហ័ស"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"បំបែកផ្តេក"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"បំបែកបញ្ឈរ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"បំបែកផ្ទាល់ខ្លួន"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"បំបែក​អេក្រង់​ទៅ​ខាងលើ"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"បំបែក​អេក្រង់​ទៅ​ខាងឆ្វេង"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"បំបែក​អេក្រង់​ទៅ​ខាងស្តាំ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"បិទ/បើក​ទិដ្ឋភាពរួម"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"បាន​បញ្ចូល​ថ្ម​​"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"កំពុងសាក​ថ្ម"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"បង្រួម"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"បន្ត​បង្ហាញ​ការជូនដំណឹង​ពីកម្មវិធីនេះ?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"​មិនអាច​បិទការជូនដំណឹង​ទាំងនេះបានទេ"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"កម្មវិធីនេះ​កំពុងប្រើ​កាមេរ៉ា។"</string>
     <string name="appops_microphone" msgid="741508267659494555">"កម្មវិធីនេះ​កំពុងប្រើ​មីក្រូហ្វូន។"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"កម្មវិធីនេះ​កំពុងបង្ហាញ​ពីលើកម្មវិធី​ផ្សេងទៀត​នៅលើអេក្រង់​របស់អ្នក។"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"បង្ហាញភាគរយនៅពេលសាកថ្ម (លំនាំដើម)"</item>
     <item msgid="3327323682209964956">"កុំបង្ហាញរូបតំណាងនេះ"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"ផ្សេងៗ"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"កម្មវិធីចែកអេក្រង់បំបែក"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"អេក្រង់ពេញខាងឆ្វេង"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index c4c642f..ccbb31e 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"ಸಿಸ್ಟಂ UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"ತೆರವುಮಾಡು"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"ಪಟ್ಟಿಯಿಂದ ತೆಗೆದುಹಾಕು"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"ನಿಮ್ಮ ಇತ್ತೀಚಿನ ಪರದೆಗಳು ಇಲ್ಲಿ ಕಾಣಿಸಿಕೊಳ್ಳುತ್ತವೆ"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"ಇತ್ತೀಚಿನ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ವಜಾಗೊಳಿಸಿ"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">ಸಮಗ್ರ ನೋಟದಲ್ಲಿರುವ %d ಪರದೆಗಳು</item>
-      <item quantity="other">ಸಮಗ್ರ ನೋಟದಲ್ಲಿರುವ %d ಪರದೆಗಳು</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"ಯಾವುದೇ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"ಅಧಿಸೂಚನೆಗಳು"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ಫೋನ್ ತೆರೆಯಿರಿ"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"ಧ್ವನಿ ಸಹಾಯಕವನ್ನು ತೆರೆ"</string>
     <string name="camera_label" msgid="7261107956054836961">"ಕ್ಯಾಮರಾ ತೆರೆಯಿರಿ"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"ಹೊಸ ಕಾರ್ಯ ವಿನ್ಯಾಸವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡಿ"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ಸಹಾಯ ಸಂದೇಶ ಪ್ರದೇಶ"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"ದೃಢೀಕರಿಸಿ"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ವಜಾಗೊಳಿಸಿ."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ಇತ್ತೀಚಿನ ಎಲ್ಲಾ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ ತೆರೆಯಿರಿ."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"ಅಧಿಸೂಚನೆ ವಜಾಗೊಂಡಿದೆ."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"ಅಧಿಸೂಚನೆಯ ಛಾಯೆ."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‍ಗಳು."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ಸಕ್ರಿಯಗೊಂಡಿದೆ"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ನೀವು ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿರುವಿರಿ"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ಸ್ಕ್ರೀನ್ ಪಿನ್ನಿಂಗ್"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"ಹುಡುಕಾಟ"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲು ಸಾದ್ಯವಿಲ್ಲ."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ಅನ್ನು ಸುರಕ್ಷಿತ ಮೋಡ್‌ನಲ್ಲಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"ವಿಭಜಿತ ಪರದೆಯನ್ನು ಬಳಸಲು ಇಲ್ಲಿ ಡ್ರ್ಯಾಗ್‌ ಮಾಡಿ"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಬದಲಿಸಲು ತ್ವರಿತವಾಗಿ ಬಲಕ್ಕೆ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ಅಡ್ಡಲಾಗಿ ವಿಭಜಿಸಿದ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ಲಂಬವಾಗಿ ವಿಭಜಿಸಿದ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ಕಸ್ಟಮ್ ವಿಭಜಿಸಿದ"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"ಮೇಲ್ಭಾಗಕ್ಕೆ ಪರದೆಯನ್ನು ವಿಭಜಿಸಿ"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"ಎಡಕ್ಕೆ ಪರದೆಯನ್ನು ವಿಭಜಿಸಿ"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"ಬಲಕ್ಕೆ ಪರದೆಯನ್ನು ವಿಭಜಿಸಿ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"ಟಾಗಲ್ ನ ಅವಲೋಕನ"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"ಕಿರಿದುಗೊಳಿಸಿ"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"ಈ ಅಪ್ಲಿಕೇಶನ್‌ನಿಂದ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೋರಿಸುತ್ತಲೇ ಇರಬೇಕೆ?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆಫ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಕ್ಯಾಮರಾವನ್ನು ಬಳಸುತ್ತಿದೆ."</string>
     <string name="appops_microphone" msgid="741508267659494555">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಮೈಕ್ರೊಫೋನ್ ಅನ್ನು ಬಳಸುತ್ತಿದೆ."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ಇತರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಮೇಲಿಂದ ಪ್ರದರ್ಶಿಸುತ್ತಿದೆ."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"ಚಾರ್ಜ್‌ ಮಾಡುವಾಗ ಪ್ರತಿಶತವನ್ನು ತೋರಿಸು (ಡಿಫಾಲ್ಟ್‌)"</item>
     <item msgid="3327323682209964956">"ಈ ಐಕಾನ್ ತೋರಿಸಬೇಡ"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"ಇತರ"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"ಸ್ಪ್ಲಿಟ್-ಪರದೆ ಡಿವೈಡರ್"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"ಎಡ ಪೂರ್ಣ ಪರದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 9014880..716e2f2 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"시스템 UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"지우기"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"목록에서 삭제"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"앱 정보"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"여기에 최근 화면이 표시됩니다."</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"최근에 사용한 앱 숨기기"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">최근 사용에 화면 %d개 있음</item>
-      <item quantity="one">최근 사용에 화면 1개 있음</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"알림 없음"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"진행 중"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"알림"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"휴대전화 열기"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"음성 지원 열기"</string>
     <string name="camera_label" msgid="7261107956054836961">"카메라 열기"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"새 작업 레이아웃 선택"</string>
     <string name="cancel" msgid="6442560571259935130">"취소"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"도움말 메시지 영역"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"확인"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>을(를) 숨깁니다."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>이(가) 제거되었습니다."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"최근 사용한 애플리케이션을 모두 닫았습니다."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> 애플리케이션 정보를 엽니다."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>을(를) 시작하는 중입니다."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"알림이 제거되었습니다."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"알림 세부정보"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"빠른 설정"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC 사용 중지됨"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC 사용 설정됨"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"최근 항목이 없습니다."</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"모든 항목을 삭제했습니다."</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"애플리케이션 정보"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"화면 고정"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"검색"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>을(를) 시작할 수 없습니다."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g>은(는) 안전 모드에서 사용 중지됩니다."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"모두 지우기"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"여기를 드래그하여 분할 화면 사용하기"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"위로 스와이프하여 앱 전환"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"앱을 빠르게 전환하려면 오른쪽으로 드래그"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"수평 분할"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"수직 분할"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"맞춤 분할"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"위쪽으로 화면 분할"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"왼쪽으로 화면 분할"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"오른쪽으로 화면 분할"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"최근 사용 버튼 전환"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"충전됨"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"충전 중"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"최소화"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"이 앱의 알림을 계속 표시하시겠습니까?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"이 알림은 끌 수 없습니다"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"앱이 카메라를 사용 중입니다."</string>
     <string name="appops_microphone" msgid="741508267659494555">"앱이 마이크를 사용 중입니다."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"앱이 화면의 다른 앱 위에 표시되고 있습니다."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"충전할 때 퍼센트 표시(기본값)"</item>
     <item msgid="3327323682209964956">"이 아이콘 표시 안함"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"기타"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"화면 분할기"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"왼쪽 화면 전체화면"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 51cb1b1..2f49cdb 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Тутум UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Тазалоо"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Тизмеден алып салуу"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Колдонмо тууралуу"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Акыркы экрандарыңыз бул жерден көрүнөт"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Акыркы колдонмолорду жок кылуу"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d экран Көз жүгүртүүдө</item>
-      <item quantity="one">1 экран Көз жүгүртүүдө</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Билдирме жок"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Учурдагы"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Билдирмелер"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"телефонду ачуу"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"үн жардамчысысын ачуу"</string>
     <string name="camera_label" msgid="7261107956054836961">"камераны ачуу"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Жаңы тапшырманын планын тандаңыз"</string>
     <string name="cancel" msgid="6442560571259935130">"Жокко чыгаруу"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Жардам билдирүүсү"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Ырастоо"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> этибарга албоо."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> жок болду."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Акыркы колдонмолордун баары көз жаздымда калтырылды."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> колдонмосу жөнүндө маалыматты ачыңыз."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> иштеп баштоодо."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Эскертме жок кылынды."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Билдирмелер тактасы."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Тез тууралоолор."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC өчүрүлгөн"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC иштетилген"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Акыркы колдонмолор жок"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Баарын тазаладыңыз"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Колдонмо жөнүндө маалымат"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"экран кадоо"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"издөө"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> баштай алган жок."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> коопсуз режиминде өчүрүлдү."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Баарын тазалоо"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Экранды бөлүү үчүн бул жерге сүйрөңүз"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Колдонмолорду которуштуруу үчүн өйдө сүрүңүз"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Колдонмолорду тез которуштуруу үчүн оңго сүйрөңүз"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Туурасынан бөлүү"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Тигинен бөлүү"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Ыңгайлаштырылган бөлүү"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Экранды өйдө жакка бөлүү"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Экранды сол жакка бөлүү"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Экранды оң жакка бөлүү"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Сереп салууну өчүрүү/күйгүзүү"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Кубатталды"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Кубатталууда"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Кичирейтүү"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Бул колдонмонун эскертмелери көрсөтүлө берсинби?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Бул эскертмелерди өчүрүүгө болбойт"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Бул колдонмо камераны колдонууда."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Бул колдонмо микрофонду колдонууда."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Бул колдонмо экрандагы башка терезелердин үстүнөн көрсөтүлүүдө."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Кубаттоо учурунда пайызы көрсөтүлсүн (демейки)"</item>
     <item msgid="3327323682209964956">"Бул сөлөкөт көрсөтүлбөсүн"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Башка"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Экранды бөлгүч"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Сол жактагы экранды толук экран режимине өткөрүү"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index d442258..dd874d5 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"ສ່ວນຕິດຕໍ່ຜູ່ໃຊ້ຂອງລະບົບ"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"ລຶບ"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"ເອົາອອກຈາກລາຍການ"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"ຂໍ້ມູນແອັບຯ"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"ໜ້າຈໍຫຼ້າສຸດຂອງທ່ານຈະປາກົດຢູ່ບ່ອນນີ້"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"ປິດແອັບຯຫຼ້າສຸດທີ່ໃຊ້"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d ໜ້າ​ຈໍ​ຢູ່​ໃນ​ພາບ​ລວມ</item>
-      <item quantity="one">1 ​ໜ້າ​ຈໍ​ຢູ່​ໃນ​ພາບ​ລວມ</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"ບໍ່ມີການແຈ້ງເຕືອນ"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ດຳເນີນຢູ່"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"ການແຈ້ງເຕືອນ"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"​ເປີດ​​ແປ້ນ​ໂທ​ລະ​ສັບ"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"ຊ່ວ​ເຫຼືອ​ເປີດ​ສຽງ"</string>
     <string name="camera_label" msgid="7261107956054836961">"ເປີດ​ກ້ອງ"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"ເລືອກ​ແຜນ​ຜັງ​ໜ້າ​ວຽກ​ໃໝ່"</string>
     <string name="cancel" msgid="6442560571259935130">"ຍົກເລີກ"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ຊ່ວຍພື້ນທີ່ຂໍ້ຄວາມ"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"ຢືນຢັນ"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"ປິດ <xliff:g id="APP">%s</xliff:g> ໄວ້."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"ປິດ <xliff:g id="APP">%s</xliff:g> ແລ້ວ."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ທຸກ​ແອັບ​ພ​ລິ​ເຄ​ຊັນ​ບໍ່​ດົນ​ມາ​ນີ້​ຖືກ​ປ່ອຍ​ໄປ."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"ເປີດຂໍ້ມູນແອັບພລິເຄຊັນ <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"ກຳ​ລັງ​ເປີດ <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"ປິດການແຈ້ງເຕືອນແລ້ວ."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"ໜ້າຈໍແຈ້ງເຕືອນ."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"ການຕັ້ງຄ່າດ່ວນ."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC is disabled"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC is enabled"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"ບໍ່ມີລາຍການຫຼ້າສຸດ"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ທ່ານລຶບລ້າງທຸກຢ່າງແລ້ວ"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"​ຂໍ້​ມູນ​ແອັບ​ພ​ລິ​ເຄ​ຊັນ"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ການ​ປັກ​ໝຸດ​ໜ້າ​ຈໍ​"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"ຊອກຫາ"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"ບໍ່​ສາ​ມາດ​ເລີ່ມ <xliff:g id="APP">%s</xliff:g> ໄດ້."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ຖືກປິດໃຊ້ໃນໂໝດຄວາມມປອດໄພ."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ລຶບລ້າງທັງໝົດ"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"ລາກມາບ່ອນນີ້ເພື່ອໃຊ້ການແບ່ງໜ້າຈໍ"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ປັດຂື້ນເພື່ອສະຫຼັບແອັບ"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"ລາກໄປຂວາເພື່ອສະຫຼັບແອັບດ່ວນ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ການ​ແຍກ​ລວງ​ຂວາງ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ການ​ແຍກ​ລວງ​ຕັ້ງ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ການ​ແຍກ​ກຳ​ນົດ​ເອງ"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"ສະຫຼັບພາບຮວມ"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ສາກເຕັມແລ້ວ."</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ກຳລັງສາກໄຟ"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"ຫຍໍ້"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"ສະແດງການແຈ້ງເຕືອນຈາກແອັບນີ້ຕໍ່ໄປບໍ?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"ບໍ່ສາມາດປິດການແຈ້ງເຕືອນເຫຼົ່ານີ້ໄດ້"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"ແອັບນີ້ກຳລັງໃຊ້ກ້ອງຢູ່."</string>
     <string name="appops_microphone" msgid="741508267659494555">"ແອັບນີ້ກຳລັງໃຊ້ໄມໂຄຣໂຟນຢູ່."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"ແອັບນີ້ກຳລັງສະແດງຜົນບັງແອັບອື່ນຢູ່ໜ້າຈໍຂອງທ່ານ."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"ສະແດງເປີເຊັນເມື່ອກຳລັງສາກໄຟ (ຄ່າເລີ່ມຕົ້ນ)"</item>
     <item msgid="3327323682209964956">"ຢ່າສະແດງໄອຄອນນີ້"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"ອື່ນໆ"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"ຕົວຂັ້ນການແບ່ງໜ້າຈໍ"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"ເຕັມໜ້າຈໍຊ້າຍ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index e26df8b..68e64d3 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -21,16 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Sistemos NS"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Išvalyti"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Pašalinti iš sąrašo"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Programos informacija"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Čia rodomi naujausi ekranai"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Atsisakyti naujausių programų"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d ekrano apžvalga</item>
-      <item quantity="few">%d ekranų apžvalga</item>
-      <item quantity="many">%d ekrano apžvalga</item>
-      <item quantity="other">%d ekranų apžvalga</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nėra įspėjimų"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Vykstantys"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Pranešimai"</string>
@@ -103,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"atidaryti telefoną"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"atidaryti „Voice Assist“"</string>
     <string name="camera_label" msgid="7261107956054836961">"atidaryti fotoaparatą"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Pasirinkti naują užduoties išdėstymą"</string>
     <string name="cancel" msgid="6442560571259935130">"Atšaukti"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Pagalbos pranešimo sritis"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Patvirtinkite"</string>
@@ -190,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Atsisakyti <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Atsisakyta programos „<xliff:g id="APP">%s</xliff:g>“."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Atsisakyta visų naujausių programų."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Atidaryti programos „<xliff:g id="APP">%s</xliff:g>“ informaciją."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Paleidžiama <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Pranešimo atsisakyta."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Pranešimų gaubtas."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Spartieji nustatymai."</string>
@@ -361,23 +345,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"ALR"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"ALR išjungtas"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"ALR įjungtas"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Nėra jokių naujausių elementų"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Viską išvalėte"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Programos informacija"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekrano prisegimas"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"paieška"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Nepavyko paleisti <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Programa „<xliff:g id="APP">%s</xliff:g>“ išjungta saugos režimu."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Išvalyti viską"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Vilkite čia, kad naudotumėte skaidytą ekraną"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Perbraukite aukštyn, kad perjungtumėte programas"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Vilkite į dešinę, kad greitai perjungtumėte programas"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontalus skaidymas"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikalus skaidymas"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Tinkintas skaidymas"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Skaidyti ekraną į viršų"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Skaidyti ekraną į kairę"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Skaidyti ekraną į dešinę"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Perjungti apžvalgą"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Įkrautas"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Kraunamas"</string>
@@ -626,6 +595,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Sumažinti"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Toliau rodyti iš šios programos gautus pranešimus?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Šių pranešimų negalima išjungti"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"naudojant „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Ši programa naudoja fotoaparatą."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Ši programa naudoja mikrofoną."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Ši programa rodoma ekrane virš kitų programų."</string>
@@ -764,6 +734,7 @@
     <item msgid="2139628951880142927">"Rodyti procentus kraunant (numatytasis nustatymas)"</item>
     <item msgid="3327323682209964956">"Nerodyti šios piktogramos"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Rodyti mažo prioriteto pranešimų piktogramas"</string>
     <string name="other" msgid="4060683095962566764">"Kita"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Skaidyto ekrano daliklis"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Kairysis ekranas viso ekrano režimu"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 6f5e230..eb5e92c 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -21,15 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Sistēmas UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Notīrīt"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Noņemšana no saraksta"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Lietotnes informācija"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Jūsu pēdējie ekrāni tiek rādīti šeit."</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Nerādīt nesen izmantotās lietotnes"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="zero">%d ekrānu sadaļā Kopsavilkums</item>
-      <item quantity="one">%d ekrāns sadaļā Kopsavilkums</item>
-      <item quantity="other">%d ekrāni sadaļā Kopsavilkums</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nav paziņojumu"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Notiekošs"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Paziņojumi"</string>
@@ -102,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"atvērt tālruni"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"atvērt balss palīgu"</string>
     <string name="camera_label" msgid="7261107956054836961">"atvērt kameru"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Atlasiet jaunu uzdevumu izkārtojumu"</string>
     <string name="cancel" msgid="6442560571259935130">"Atcelt"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Palīdzības ziņojuma apgabals"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Apstiprināt"</string>
@@ -189,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Nerādīt lietotni <xliff:g id="APP">%s</xliff:g>"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Lietotne <xliff:g id="APP">%s</xliff:g> vairs netiek rādīta."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Visas nesen izmantotās lietojumprogrammas tika noņemtas."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Atveriet lietojumprogrammas <xliff:g id="APP">%s</xliff:g> informāciju."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Notiek lietotnes <xliff:g id="APP">%s</xliff:g> palaišana."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Paziņojums netiek rādīts."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Paziņojumu panelis"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Ātrie iestatījumi"</string>
@@ -358,23 +343,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ir atspējoti"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ir iespējoti"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Nav nesenu vienumu"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Visi uzdevumi ir notīrīti"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informācija par lietojumprogrammu"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Piespraust ekrānu"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"Meklēt"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Nevarēja palaist lietotni <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Lietotne <xliff:g id="APP">%s</xliff:g> ir atspējota drošajā režīmā."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Notīrīt visu"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Velciet šeit, lai izmantotu ekrāna sadalīšanu"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Velciet augšup, lai pārslēgtu lietotnes"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Lai ātri pārslēgtu lietotnes, velciet pa labi"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontāls dalījums"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikāls dalījums"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Pielāgots dalījums"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Sadalīt ekrānu augšdaļā"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Sadalīt ekrānu kreisajā pusē"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Sadalīt ekrānu labajā pusē"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Pārskata pārslēgšana"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Akumulators uzlādēts"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Notiek uzlāde"</string>
@@ -622,6 +592,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimizēt"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Vai turpināt rādīt paziņojumus no šīs lietotnes?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Šos paziņojumus nevar izslēgt."</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Šajā lietotnē tiek izmantota kamera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Šajā lietotnē tiek izmantots mikrofons."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Šī lietotne tiek rādīta ekrānā pāri citām lietotnēm."</string>
@@ -758,6 +730,8 @@
     <item msgid="2139628951880142927">"Rādīt procentuālo vērtību uzlādes laikā (noklusējums)"</item>
     <item msgid="3327323682209964956">"Nerādīt šo ikonu"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Citi"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Ekrāna sadalītājs"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Kreisā daļa pa visu ekrānu"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index d13193b7..9c42805 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Кориснички интерфејс на систем"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Исчисти"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Отстрани од списокот"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Информации за апликацијата"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Вашите неодамнешни екрани се појавуваат тука"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Отфрли ги скорешните апликации"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d екран во Краток преглед</item>
-      <item quantity="other">%d екрани во Краток преглед</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Нема известувања"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Во тек"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Известувања"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"отвори телефон"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"отвори гласовна помош"</string>
     <string name="camera_label" msgid="7261107956054836961">"отвори камера"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Изберете нов распоред на задача"</string>
     <string name="cancel" msgid="6442560571259935130">"Откажи"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Област за пораки за помош"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Потврди"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Отфрли <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> е отфрлена."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Сите неодамнешни апликации се отфрлени."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Отвори информации за апликацијата <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Се стартува <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Известувањето е отфрлено."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Панел за известување"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Брзи поставки."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC е оневозможено"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC е овозможено"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Нема неодамнешни ставки"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Исчистивте сѐ"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Информации за апликацијата"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"прикачување екран"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"пребарај"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> не може да се вклучи."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> е оневозможен во безбеден режим."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Исчисти ги сите"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Повлечете тука за да користите поделен екран"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Повлечете нагоре за да се префрлите од една на друга апликација"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Повлечете надесно за брзо префрлање меѓу апликациите"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Раздели хоризонтално"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Раздели вертикално"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Раздели прилагодено"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Поделен екран во горниот дел"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Поделен екран на левата страна"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Поделен екран на десната страна"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Вклучи/исклучи преглед"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Наполнета"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Се полни"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Минимизирај"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Дали да продолжат да се прикажуваат известувања од апликацијава?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Известувањава не може да се исклучат"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Апликацијава ја користи камерата."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Апликацијава го користи микрофонот."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Апликацијава се прикажува врз други апликации на вашиот екран."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Прикажи процент кога се полни (стандардно)"</item>
     <item msgid="3327323682209964956">"Не прикажувај ја иконава"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Друго"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Разделник на поделен екран"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Левиот на цел екран"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index a5cad4e..62196ea 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"സിസ്റ്റം UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"മായ്‌ക്കുക"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"ലിസ്‌റ്റിൽ നിന്നും നീക്കംചെയ്യുക"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"ആപ്പ് വിവരം"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"നിങ്ങളുടെ പുതിയ സ്ക്രീനുകൾ ഇവിടെ ദൃശ്യമാകുന്നു"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"സമീപകാല അപ്ലിക്കേഷനുകൾ നിരസിക്കുക"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">ചുരുക്കവിവരണത്തിലെ %d സ്‌ക്രീനുകൾ</item>
-      <item quantity="one">ചുരുക്കവിവരണത്തിലെ ഒരു സ്‌ക്രീൻ</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"അറിയിപ്പുകൾ ഒന്നുമില്ല"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"നടന്നുകൊണ്ടിരിക്കുന്നവ"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"അറിയിപ്പുകൾ"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ഫോൺ തുറക്കുക"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"വോയ്‌സ് അസിസ്റ്റ് തുറക്കുക"</string>
     <string name="camera_label" msgid="7261107956054836961">"ക്യാമറ തുറക്കുക"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"പുതിയ ടാസ്‌ക് ലേഔട്ട് തിരഞ്ഞെടുക്കുക"</string>
     <string name="cancel" msgid="6442560571259935130">"റദ്ദാക്കുക"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"സഹായ സന്ദേശ ഏരിയ"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"സ്ഥിരീകരിക്കുക"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> നിരസിക്കുക."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> നിരസിച്ചു."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"അടുത്തിടെയുള്ള എല്ലാ അപ്ലിക്കേഷനും നിരസിച്ചു."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> ആപ്പ് വിവരങ്ങൾ തുറക്കുക."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ആരംഭിക്കുന്നു."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"അറിയിപ്പ് നിരസിച്ചു."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"അറിയിപ്പ് ഷെയ്‌ഡ്."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"ദ്രുത ക്രമീകരണങ്ങൾ."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC പ്രവർത്തനക്ഷമമാക്കി"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"സമീപകാല ഇനങ്ങൾ ഒന്നുമില്ല"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"നിങ്ങൾ എല്ലാം മായ്ച്ചിരിക്കുന്നു"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"ആപ്പ് വിവരം"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"സ്ക്രീൻ പിൻ ചെയ്യൽ"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"തിരയുക"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ആരംഭിക്കാനായില്ല."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"സുരക്ഷിത മോഡിൽ <xliff:g id="APP">%s</xliff:g> പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"എല്ലാം മായ്‌ക്കുക"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"സ്പ്ലിറ്റ് സ്ക്രീൻ ഉപയോഗിക്കുന്നതിന് ഇവിടെ വലിച്ചിടുക"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ആപ്പുകൾ മാറാൻ മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"ആപ്പുകൾ പെട്ടെന്ന് മാറാൻ വലത്തോട്ട് വലിച്ചിടുക"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"തിരശ്ചീനമായി വേർതിരിക്കുക"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ലംബമായി വേർതിരിക്കുക"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ഇഷ്‌ടാനുസൃതമായി വേർതിരിക്കുക"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"സ്ക്രീൻ മുകളിലേക്ക് സ്പ്ലിറ്റ് ചെയ്യുക"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"സ്ക്രീൻ ഇടതുവശത്തേക്ക് സ്പ്ലിറ്റ് ചെയ്യുക"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"സ്ക്രീൻ വലതുവശത്തേക്ക് പിളർത്തുക"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"അവലോകനം മാറ്റുക"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ചാർജായി"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ചാർജ്ജുചെയ്യുന്നു"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"ചെറുതാക്കുക‍"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"ഈ ആപ്പിൽ നിന്നുള്ള അറിയിപ്പുകൾ തുടർന്നും കാണിക്കണോ?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"ഈ അറിയിപ്പുകൾ ഓഫാക്കാനാവില്ല"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"ഈ ആപ്പ് ക്യാമറ ഉപയോഗിക്കുന്നുണ്ട്."</string>
     <string name="appops_microphone" msgid="741508267659494555">"ഈ ആപ്പ് മൈക്രോഫോൺ ഉപയോഗിക്കുന്നു."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"ഈ ആപ്പ് നിങ്ങളുടെ സ്‌ക്രീനിലെ മറ്റ് ആപ്പുകൾക്ക് മുകളിൽ പ്രദർശിപ്പിക്കുന്നു."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"ചാർജ്ജുചെയ്യുമ്പോൾ ശതമാനം കാണിക്കുക (ഡിഫോൾട്ട്)"</item>
     <item msgid="3327323682209964956">"ഈ ഐക്കൺ കാണിക്കരുത്"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"മറ്റുള്ളവ"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"സ്പ്ലിറ്റ്-സ്ക്രീൻ ഡിവൈഡർ"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"ഇടത് പൂർണ്ണ സ്ക്രീൻ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 978fc0e..223f492 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -21,12 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Систем UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Цэвэрлэх"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Жагсаалтаас устгах"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Апп мэдээлэл"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Таны саяхны дэлгэц энд харагдах болно"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Сүүлийн апп-уудыг хаах"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for status_bar_accessibility_recent_apps (9138535907802238759) -->
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Мэдэгдэл байхгүй"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Гарсан"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Мэдэгдэл"</string>
@@ -99,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"утас нээх"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"дуут туслахыг нээнэ"</string>
     <string name="camera_label" msgid="7261107956054836961">"камер нээх"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Шинэ ажиллах талбарыг сонгоно уу"</string>
     <string name="cancel" msgid="6442560571259935130">"Цуцлах"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Тусламжийн зурвасын хэсэг"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Баталгаажуулах"</string>
@@ -186,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>-г хаах."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> байхгүй."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Хамгийн сүүлийн бүх програмыг арилгасан байна."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> апп-н мэдээллийг нээнэ үү."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж байна."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Мэдэгдэл хаагдсан."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Мэдэгдлийн хураангуй самбар"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Шуурхай тохиргоо."</string>
@@ -353,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC-г цуцалсан"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC-г идэвхжүүлсэн"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Сүүлийн үеийн зүйл байхгүй"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Та бүгдийг нь устгасан"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Аппликешны мэдээлэл"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"дэлгэц тогтоох"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"хайх"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж чадсангүй."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g>-г аюулгүй горимд идэвхгүй болгосон."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Бүгдийг арилгах"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Хуваагдсан дэлгэцийг ашиглахын тулд энд чирэх"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Апп сэлгэхийн тулд дээш шударна уу"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Аппуудыг хурдан сэлгэхийн тулд баруун тийш чирнэ үү"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Хэвтээ чиглэлд хуваах"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Босоо чиглэлд хуваах"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Хүссэн хэлбэрээр хуваах"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Дэлгэцийг дээд хэсэгт хуваах"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Дэлгэцийг зүүн хэсэгт хуваах"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Дэлгэцийг баруун хэсэгт хуваах"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Тоймыг унтраах/асаах"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Цэнэглэгдсэн"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Цэнэглэж байна"</string>
@@ -616,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Багасгах"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Энэ аппаас мэдэгдэл харуулсан хэвээр байх уу?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Эдгээр мэдэгдлийг унтраах боломжгүй"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Энэ апп камерыг ашиглаж байна."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Энэ апп микрофоныг ашиглаж байна."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Энэ аппыг таны дэлгэцэд бусад аппын дээр харуулж байна."</string>
@@ -750,6 +725,8 @@
     <item msgid="2139628951880142927">"Цэнэглэх үед хувийг тогтмол харуулах (өгөгдмөл)"</item>
     <item msgid="3327323682209964956">"Энэ дүрс тэмдгийг бүү үзүүл"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Бусад"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"\"Дэлгэц хуваах\" хуваагч"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Зүүн талын бүтэн дэлгэц"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 103ca46..da37acbf 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"सिस्टम UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"साफ करा"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"सूचीमधून काढा"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"अॅप माहिती"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"आपल्या अलीकडील स्क्रीन येथे दिसतात"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"अलीकडील अॅप्स डिसमिस करा"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">विहंगावलोकनात %d स्क्रीन</item>
-      <item quantity="other">विहंगावलोकनात %d स्क्रीन</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"सूचना नाहीत"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"सुरु असलेले"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"सूचना"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"फोन उघडा"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"व्हॉइस सहाय्य उघडा"</string>
     <string name="camera_label" msgid="7261107956054836961">"कॅमेरा उघडा"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"नवीन कार्य लेआउट निवडा"</string>
     <string name="cancel" msgid="6442560571259935130">"रद्द करा"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"मदत मेसेज परिसर"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"खात्री करा"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> डिसमिस करा."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> डिसमिस केला."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"अलीकडील सर्व अॅप्लिकेशन डिसमिस झाले."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> अॅप्लिकेशन माहिती उघडा."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> प्रारंभ करत आहे."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"सूचना डिसमिस केल्या."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"सूचना शेड."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"द्रुत सेटिंग्ज."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC अक्षम केले आहे"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC सक्षम केले आहे"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"अलीकडील कोणतेही आयटम नाहीत"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"तुम्ही सर्वकाही साफ केले"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"अॅप्लिकेशन माहिती"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"स्‍क्रीन पिन करणे"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"शोधा"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> प्रारंभ करणे शक्य झाले नाही."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> सुरक्षित-मोडमध्ये अक्षम केला आहे."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"सर्व साफ करा"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"विभाजित स्क्रीन वापर करण्यासाठी येथे ड्रॅग करा"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"अ‍ॅप्स स्विच करण्यासाठी वर स्वाइप करा"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"अॅप्स वर झटपट स्विच करण्यासाठी उजवीकडे ड्रॅग करा"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"क्षैतिज विभाजित करा"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"अनुलंब विभाजित करा"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"कस्टम विभाजित करा"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"स्क्रीन शीर्षस्थानी विभाजित करा"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"स्क्रीन डावीकडे विभाजित करा"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"स्क्रीन उजवीकडे विभाजित करा"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"अवलोकन टॉगल करा."</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज झाली"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"चार्ज होत आहे"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"लहान करा"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"या अ‍ॅपकडील सूचना दाखवणे सुरू ठेवायचे?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"या सूचना बंद करता येत नाहीत"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"हे अॅप कॅमेरा वापरत आहे."</string>
     <string name="appops_microphone" msgid="741508267659494555">"हे अॅप मायक्रोफोन वापरत आहे."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"हे अॅप स्क्रीनवरील इतर अॅप्स वर प्रदर्शित होत आहे."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"चार्ज करताना टक्केवारी दर्शवा (डीफॉल्ट)"</item>
     <item msgid="3327323682209964956">"हे आयकन दाखवू नका"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"अन्य"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"विभाजित-स्क्रीन विभाजक"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"डावी फुल स्क्रीन"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 7a6a1e3..cb4dd14 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"UI Sistem"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Pdm bersih"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Alih keluar dari senarai"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Maklumat aplikasi"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Skrin terbaru anda terpapar di sini"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Buang aplikasi terbaharu"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d skrin dalam Gambaran Keseluruhan</item>
-      <item quantity="one">1 skrin dalam Gambaran Keseluruhan</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Tiada pemberitahuan"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Sedang berlangsung"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Pemberitahuan"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"buka telefon"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"buka bantuan suara"</string>
     <string name="camera_label" msgid="7261107956054836961">"buka kamera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Pilih reka letak tugas baharu"</string>
     <string name="cancel" msgid="6442560571259935130">"Batal"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Bahagian mesej bantuan"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Sahkan"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ketepikan <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ditolak."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Semua aplikasi terbaharu diketepikan."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Buka maklumat aplikasi <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Memulakan <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Pemberitahuan diketepikan."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bidai pemberitahuan."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Tetapan pantas."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC dilumpuhkan"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC didayakan"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Tiada item terbaharu"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Anda telah mengetepikan semua item"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Maklumat Aplikasi"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"penyematan skrin"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"cari"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Tidak dapat memulakan <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> dilumpuhkan dalam mod selamat."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Kosongkan semua"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Seret ke sini untuk menggunakan skrin pisah"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Leret ke atas untuk menukar apl"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Seret ke kanan untuk beralih apl dengan pantas"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Mendatar Terpisah"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Menegak Terpisah"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Tersuai Terpisah"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Pisahkan skrin ke atas"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Pisahkan skrin ke kiri"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Pisahkan skrin ke kanan"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Togol Ikhtisar"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Sudah dicas"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Mengecas"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimumkan"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Terus tunjukkan pemberitahuan daripada apl ini?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Pemberitahuan ini tidak boleh dimatikan"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Apl ini sedang menggunakan kamera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Apl ini sedang menggunakan mikrofon."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Apl ini dipaparkan di atas apl lain pada skrin anda."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Tunjukkan peratusan semasa mengecas (lalai)"</item>
     <item msgid="3327323682209964956">"Jangan tunjukkan ikon ini"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Lain-lain"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Pembahagi skrin pisah"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Skrin penuh kiri"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 62970aa..777d96e 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"စနစ်၏UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"ရှင်းရန်"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"စာရင်းမှ ဖယ်မည်"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"အက်ပ်အချက်အလက်များ"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"သင်၏ မကြာမီက မျက်နှာပြင်များ ဒီမှာ ပေါ်လာကြမည်"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"လတ်တလောအက်ပ်များအား ပယ်ရန်"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">ခြုံကြည့်မှုထဲမှ မျက်နှာပြင် %d ခု</item>
-      <item quantity="one">ခြုံကြည့်မှုထဲမှ မျက်နှာပြင် 1 ခု</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"အကြောင်းကြားချက်များ မရှိ"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"လက်ရှိအသုံးပြုမှု"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"အကြောင်းကြားချက်များ။"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ဖုန်းကို ဖွင့်ရန်"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"အသံ အကူအညီအား ဖွင့်ရန်"</string>
     <string name="camera_label" msgid="7261107956054836961">"ကင်မရာ ဖွင့်ရန်"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"အလုပ်သစ်စီစဥ်မှုကို ရွေးပါ။"</string>
     <string name="cancel" msgid="6442560571259935130">"မလုပ်တော့"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"အကူအညီမက်ဆေ့ဂျ် နေရာ"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"အတည်ပြုပါ"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ကို ပယ်ရန်"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ထုတ်ထားသည်။"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"မကြာသေးမီက အပလီကေးရှင်းများအားလုံး ဖယ်ထုတ်ပြီးပါပြီ။"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> အပလီကေးရှင်းအချက်အလက်ကို ဖွင့်ပါ။"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>ကို စတင်နေသည်။"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"အကြောင်းကြားချက်ကိုဖယ်ရှားပြီး"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"အ​ကြောင်းကြားစာအကွက်"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"အမြန်လုပ် အပြင်အဆင်"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ကို ပိတ်ထားသည်"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ကို ဖွင့်ထားသည်"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"မကြာမီကဖွင့်ထားသည်များ မရှိပါ"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"သင်အားလုံးကို ရှင်းလင်းပြီးပါပြီ"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"အပလီကေးရှင်းအင်ဖို"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"မျက်နှာပြင် ပင်ထိုးမှု"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"ရှာဖွေရန်"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ကို မစနိုင်ပါ။"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ကို ဘေးကင်းလုံခြုံသည့်မုဒ်တွင် ပိတ်ထားပါသည်။"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"အားလုံး ဖယ်ရှားပါ"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"မျက်နှာပြင် ခွဲခြမ်းပြသခြင်းကို အသုံးပြုရန် ဤနေရာသို့ ပွတ်၍ဆွဲထည့်ပါ"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"အက်ပ်များကို ဖွင့်ရန် အပေါ်သို့ ပွတ်ဆွဲပါ"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"အက်ပ်များကို ပြောင်းရန် ညာဘက်သို့ ဖိဆွဲပါ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ရေပြင်ညီ ပိုင်းမည်"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ဒေါင်လိုက်ပိုင်းမည်"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"စိတ်ကြိုက် ပိုင်းမည်"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"မျက်နှာပြင်ကို အပေါ်သို့ ခွဲရန်"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"မျက်နှာပြင်ကို ဘယ်ဘက်သို့ ခွဲရန်"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"မျက်နှာပြင်ကို ညာဘက်သို့ ခွဲရန်"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"ဖွင့်၊ ပိတ် အနှစ်ချုပ်"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"အားသွင်းပြီး"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"အားသွင်းနေ"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"ချုံ့ရန်"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"ဤအက်ပ်ထံမှ အကြောင်းကြားချက်များကို ဆက်ပြလိုပါသလား။"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"ဤအကြောင်းကြားချက်များကို ပိတ်၍မရပါ"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"ဤအက်ပ်က ကင်မရာကို အသုံးပြုနေသည်။"</string>
     <string name="appops_microphone" msgid="741508267659494555">"ဤအက်ပ်က မိုက်ခရိုဖုန်းကို အသုံးပြုနေသည်။"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"ဤအက်ပ်က ဖန်သားမျက်နှာပြင်ပေါ်ရှိ အခြားအက်ပ်များ အပေါ်မှ ထပ်ပြီး ပြသနေပါသည်။"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"အားသွင်းနေစဉ်တွင် ရာခိုင်နှုန်းကိုပြပါ (ပုံသေ)"</item>
     <item msgid="3327323682209964956">"ဤသင်္ကေတပုံကို မပြပါနှင့်"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"အခြား"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"မျက်နှာပြင်ခွဲခြမ်း ပိုင်းခြားပေးသည့်စနစ်"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"ဘယ်ဘက် မျက်နှာပြင်အပြည့်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 81ff8e2..9b27df5 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Sys.gr.snitt"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Fjern"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Fjern fra listen"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Info om appen"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"De sist brukte skjermene dine vises her"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Avvis nylig brukte apper"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d skjermer i oversikten</item>
-      <item quantity="one">1 skjerm i oversikten</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ingen varslinger"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Aktiviteter"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Varsler"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"åpne telefonen"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"åpne talehjelp"</string>
     <string name="camera_label" msgid="7261107956054836961">"åpne kamera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Velg en ny utforming for oppgaver"</string>
     <string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Område for hjelpemelding"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bekreft"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Avvis <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> avvist."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle nylig brukte apper er avvist."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Åpne appinformasjonen for <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starter <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Varselet ble skjult."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Varselskygge."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Hurtiginnstillinger."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC er slått av"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC er slått på"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Ingen nylige elementer"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Du har fjernet alt"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformasjon"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"én-appsmodus"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"Søk"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Kunne ikke starte <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> er slått av i sikker modus."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tøm alt"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Dra hit for å bruke delt skjerm"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Sveip opp for å bytte apper"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Dra til høyre for å bytte apper raskt"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Del horisontalt"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Del vertikalt"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Del tilpasset"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Delt skjerm øverst"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Delt skjerm til venstre"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Delt skjerm til høyre"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Slå oversikten av eller på"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Oppladet"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Lader"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimer"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Vil du fortsette å vise varsler fra denne appen?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Du kan ikke slå av disse varslene"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Denne appen bruker kameraet."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Denne appen bruker mikrofonen."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Denne appen vises over andre apper på skjermen."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Vis prosentandel under lading (standard)"</item>
     <item msgid="3327323682209964956">"Ikke vis dette ikonet"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Annet"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Skilleelement for delt skjerm"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Utvid den venstre delen av skjermen til hele skjermen"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 4f2845e..5119e5b 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"प्रणाली UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"हटाउनुहोस्"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"सूचीबाट हटाउनुहोस्"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"अनुप्रयोगको जानकारी"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"तपाईँको हालको स्क्रिन यहाँ प्रकट हुन्छ"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"नयाँ अनुप्रयोगहरू खारेज गर्नुहोस्"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other"> अवलोकनमा %d स्क्रिनहरू</item>
-      <item quantity="one">अवलोकनमा 1 स्क्रिन</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"कुनै सूचनाहरू छैन"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"चलिरहेको"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"सूचनाहरू"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"फोन खोल्नुहोस्"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"आवाज सहायता खोल्नुहोस्"</string>
     <string name="camera_label" msgid="7261107956054836961">"क्यामेरा खोल्नुहोस्"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"नयाँ कार्य लेआउट चयन गर्नुहोस्"</string>
     <string name="cancel" msgid="6442560571259935130">"रद्द गर्नुहोस्"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"मद्दतसम्बन्धी सन्देशको क्षेत्र"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"पुष्टि गर्नुहोस्"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> खारेज गर्नुहोस्।"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> खारेज गरिएको छ।"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"सबै हालका अनुप्रयोगहरू खारेज गरियो।"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> अनुप्रयोग सम्बन्धी जानकारी खोल्नुहोस्।"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>सुरु गर्दै।"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"सूचना खारेज।"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"सूचना कक्ष।"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"द्रुत सेटिङहरू"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC लाई असक्षम पारिएको छ"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC लाई सक्षम पारिएको छ"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"हालका कुनै पनि वस्तुहरू छैनन्"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"तपाईँले सबै कुरा खाली गर्नुभएको छ"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"अनुप्रयोग जानकारी"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"स्क्रिन पिन गर्दै"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"खोजी गर्नुहोस्"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"सुरु गर्न सकिएन <xliff:g id="APP">%s</xliff:g>।"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> लाई सुरक्षित-मोडमा असक्षम गरिएको छ।"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"सबै हटाउनुहोस्"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"विभाजित स्क्रिनको प्रयोग गर्नाका लागि यहाँ तान्नुहोस्"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"अनुप्रयोगहरू बदल्न माथितिर स्वाइप गर्नुहोस्"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"अनुप्रयोगहरू बदल्न द्रुत गतिमा दायाँतिर ड्र्याग गर्नुहोस्"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"तेर्सो रूपमा विभाजन गर्नुहोस्"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ठाडो रूपमा विभाजन गर्नुहोस्"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"आफू अनुकूल विभाजन गर्नुहोस्"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"विभाजित-स्क्रिनलाई शीर्ष स्थानमा राख्नुहोस्‌"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"विभाजित-स्क्रिनलाई बायाँतर्फ राख्नुहोस्‌"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"विभाजित-स्क्रिनलाई दायाँतर्फ राख्नुहोस्‌"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"परिदृश्य टगल गर्नुहोस्"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज भयो"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"चार्ज हुँदै"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"सानो बनाउनुहोस्"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"यो अनुप्रयोगका सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"यी सूचनाहरूलाई निष्क्रिय पार्न सकिँदैन"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"यो अनुप्रयोगले क्यामेराको प्रयोग गर्दै छ।"</string>
     <string name="appops_microphone" msgid="741508267659494555">"यो अनुप्रयोगले माइक्रोफोनको प्रयोग गर्दै छ।"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"यो अनुप्रयोगले तपाईंको स्क्रिनका अन्य अनुप्रयोगहरूमाथि प्रदर्शन गर्दै छ।"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"चार्ज गर्दा प्रतिशत देखाउनुहोस् (पूर्वनिर्धारित)"</item>
     <item msgid="3327323682209964956">"यो आइकन नदेखाउनुहोस्"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"अन्य"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"विभाजित-स्क्रिन छुट्याउने"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"बायाँ भाग पूर्ण स्क्रिन"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 60ca532..448a36e 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Systeem-UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wissen"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Verwijderen uit lijst"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"App-info"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Je recente schermen worden hier weergegeven"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Recente apps negeren"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d schermen in Overzicht</item>
-      <item quantity="one">1 scherm in Overzicht</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Geen meldingen"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Actief"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meldingen"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"telefoon openen"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"spraakassistent openen"</string>
     <string name="camera_label" msgid="7261107956054836961">"camera openen"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Nieuwe taakindeling selecteren"</string>
     <string name="cancel" msgid="6442560571259935130">"Annuleren"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Gebied voor Help-berichten"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bevestigen"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> sluiten."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> verwijderd."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle recente apps gesloten."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"App-gegevens voor <xliff:g id="APP">%s</xliff:g> openen."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> starten."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Melding verwijderd."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Meldingenpaneel."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Snelle instellingen."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC is uitgeschakeld"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC is ingeschakeld"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Geen recente items"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Je hebt alles gewist"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"App-informatie"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"scherm vastzetten"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"zoeken"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Kan <xliff:g id="APP">%s</xliff:g> niet starten."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> is uitgeschakeld in de veilige modus"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Alles wissen"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Sleep hier naartoe om het scherm te splitsen"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Veeg omhoog om te schakelen tussen apps"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Sleep naar rechts om snel tussen apps te schakelen"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontaal splitsen"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Verticaal splitsen"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Aangepast splitsen"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Scherm bovenaan gesplitst"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Scherm links gesplitst"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Scherm rechts gesplitst"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Overzicht in-/uitschakelen"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Opgeladen"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Opladen"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimaliseren"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Meldingen van deze app blijven weergeven?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Deze meldingen kunnen niet worden uitgeschakeld"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Deze app gebruikt de camera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Deze app gebruikt de microfoon."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Deze app wordt over andere apps op je scherm heen weergegeven."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Percentage weergeven tijdens opladen (standaard)"</item>
     <item msgid="3327323682209964956">"Dit pictogram niet weergeven"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Overig"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Scheiding voor gesplitst scherm"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Linkerscherm op volledig scherm"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index a78f5cd..a9441fa 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"ସିଷ୍ଟମ୍ UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"ଖାଲି କରନ୍ତୁ"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"ତାଲିକାରୁ ବାହାର କରନ୍ତୁ"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"ଆପ୍‍ ସୂଚନା"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"ଆପଣଙ୍କ ସମ୍ପ୍ରତି ସ୍କ୍ରୀନ୍‍‌ ଏଠାରେ ଦେଖାଯାଉଛି"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"କିଛି ସମୟ ପୂର୍ବରୁ ଇନଷ୍ଟଲ୍‌ ହୋଇଥିବା ଆପ୍‌ଗୁଡ଼ିକୁ ଖାରଜ କରନ୍ତୁ"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">ଓଭର୍‌ଭ୍ୟୁରେ %d ସ୍କ୍ରୀନ୍‍</item>
-      <item quantity="one">ଓଭର୍‌ଭ୍ୟୁରେ 1 ସ୍କ୍ରୀନ୍‍</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ଚାଲୁଅଛି"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"ବିଜ୍ଞପ୍ତି"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ଫୋନ୍‌ ଖୋଲନ୍ତୁ"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"ଭଏସ୍‍ ସହାୟକ ଖୋଲନ୍ତୁ"</string>
     <string name="camera_label" msgid="7261107956054836961">"କ୍ୟାମେରା ଖୋଲନ୍ତୁ"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"ନୂଆ ଟାସ୍କ ଲେଆଉଟ୍‍ ଚୟନ କରନ୍ତୁ"</string>
     <string name="cancel" msgid="6442560571259935130">"କ୍ୟାନ୍ସଲ୍‍ କରନ୍ତୁ"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ସାହାଯ୍ୟ ମେସେଜ୍ କ୍ଷେତ୍ର"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"ନିଶ୍ଚିତ କରନ୍ତୁ"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ଖାରଜ।"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ଖାରଜ କରିଦିଆଗଲା।"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ସମସ୍ତ ସମ୍ପ୍ରତି ଆପ୍ଲିକେଶନଗୁଡ଼ିକ ଖାରଜ କରାଯାଇଛି।"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> ଆପ୍ଲିକେଶନ୍‍ ସୂଚନା ଖୋଲନ୍ତୁ।"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ଆରମ୍ଭ ହେଉଛି।"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"ବିଜ୍ଞପ୍ତି ଖାରଜ କରାଗଲା।"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"ବିଜ୍ଞପ୍ତି ଶେଡ୍‍।"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"ଦ୍ରୁତ ସେଟିଙ୍ଗ।"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ଅକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ସକ୍ଷମ କରାଯାଇଛି"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"କୌଣସି ସାମ୍ପ୍ରତିକ ଆଇଟମ୍ ନାହିଁ"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ଆପଣ ସୁବୁକିଛି ଖାଲି କରିଦେଇଛନ୍ତି"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"ଆପ୍ଲିକେଶନ୍‍ ସୂଚନା"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ସ୍କ୍ରୀନ୍‌ ଲକ୍‌"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> କୁ ଆରମ୍ଭ କରାଯାଇପାରିଲା ନାହିଁ।"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ସୁରକ୍ଷିତ-ମୋଡ୍‌ରେ ଅକ୍ଷମ ଅଟେ।"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"ସ୍ପ୍ଲିଟ୍‍ ସ୍କ୍ରୀନ୍‍ ବ୍ୟବହାର କରିବା ପାଇଁ ଏଠାକୁ ଡ୍ରାଗ୍‌ କରନ୍ତୁ"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ଆପ୍‌କୁ ବଦଳ କରିବା ପାଇଁ ସ୍ଵାଇପ୍ କରନ୍ତୁ"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"ଆପ୍‌ଗୁଡ଼ିକ ମଧ୍ୟରେ ଶୀଘ୍ର ବଦଳ କରିବା ପାଇଁ ଡାହାଣକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ଭୂସମାନ୍ତର ଭାବରେ ଭାଗ କରନ୍ତୁ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ଭୂଲମ୍ବ ଭାବରେ ଭାଗ କରନ୍ତୁ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"କଷ୍ଟମ୍‍ କରି ଭାଗ କରନ୍ତୁ"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"ସ୍କ୍ରୀନ୍‌କୁ ଉପର ଆଡ଼କୁ ଭାଗ କରନ୍ତୁ"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"ସ୍କ୍ରୀନ୍‌କୁ ବାମ ଆଡ଼କୁ ଭାଗ କରନ୍ତୁ"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"ସ୍କ୍ରୀନ୍‌କୁ ଡାହାଣ ଆଡ଼କୁ ଭାଗ କରନ୍ତୁ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"ସଂକ୍ଷିପ୍ତ ବିବରଣୀକୁ ଟୋଗଲ୍ କରନ୍ତୁ"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ଚାର୍ଜ ହୋଇଗଲା"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ଚାର୍ଜ କରାଯାଉଛି"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"ଛୋଟ କରନ୍ତୁ"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"ଏହି ଆପ୍‌ରୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଦେଖାଇବା ଜାରି ରଖିବେ?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ବନ୍ଦ କରିହେବ ନାହିଁ"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"ଏହି ଆପ୍ କ୍ୟାମେରା ବ୍ୟବହାର କରୁଛି।"</string>
     <string name="appops_microphone" msgid="741508267659494555">"ଏହି ଆପ୍, ମାଇକ୍ରୋଫୋନ୍‍ ବ୍ୟବହାର କରୁଛି।"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"ଏହି ଆପ୍, ଆପଣଙ୍କର ସ୍କ୍ରୀନ୍ ଉପରେ ଥିବା ଅନ୍ୟ ଆପ୍ ଉପରେ ପ୍ରଦର୍ଶିତ ହେଉଛି।"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"ଚାର୍ଜ କରାଯିବାବେଳେ ଶତକଡ଼ା ଦେଖାନ୍ତୁ (ଡିଫଲ୍ଟ)"</item>
     <item msgid="3327323682209964956">"ଏହି ଆଇକନ୍‍ ଦେଖାନ୍ତୁ ନାହିଁ"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"ଅନ୍ୟାନ୍ୟ"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନ ବିଭାଜକ"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"ବାମ ପଟକୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍ କରନ୍ତୁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 7e9a916..57971eb 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"ਸਿਸਟਮ UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"ਹਟਾਓ"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"ਸੂਚੀ ਵਿੱਚੋਂ ਹਟਾਓ"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"ਐਪ ਜਾਣਕਾਰੀ"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"ਤੁਹਾਡੀਆਂ ਹਾਲੀਆ ਸਕ੍ਰੀਨਾਂ ਇੱਥੇ ਪ੍ਰਗਟ ਹੋਣਗੀਆਂ"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"ਹਾਲੀਆ ਐਪਾਂ ਨੂੰ ਖਾਰਜ ਕਰੋ"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">ਰੂਪ-ਰੇਖਾ ਵਿੱਚ %d ਸਕ੍ਰੀਨਾਂ</item>
-      <item quantity="other">ਰੂਪ-ਰੇਖਾ ਵਿੱਚ %d ਸਕ੍ਰੀਨਾਂ</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"ਕੋਈ ਸੂਚਨਾਵਾਂ ਨਹੀਂ"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ਜਾਰੀ"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"ਸੂਚਨਾਵਾਂ"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ਫ਼ੋਨ ਖੋਲ੍ਹੋ"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"ਅਵਾਜ਼ੀ ਸਹਾਇਕ ਖੋਲ੍ਹੋ"</string>
     <string name="camera_label" msgid="7261107956054836961">"ਕੈਮਰਾ ਖੋਲ੍ਹੋ"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"ਨਵਾਂ ਕੰਮ ਲੇਆਉਟ ਚੁਣੋ"</string>
     <string name="cancel" msgid="6442560571259935130">"ਰੱਦ ਕਰੋ"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"ਮਦਦ ਸੁਨੇਹਾ ਖੇਤਰ"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"ਪੁਸ਼ਟੀ ਕਰੋ"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਖਾਰਜ ਕਰੋ।"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ਰੱਦ ਕੀਤਾ।"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ਸਾਰੀਆਂ ਹਾਲੀਆ ਐਪਲੀਕੇਸ਼ਨਾਂ ਰੱਦ ਕੀਤੀਆਂ।"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> ਐਪਲੀਕੇਸ਼ਨਾਂ ਜਾਣਕਾਰੀ ਖੋਲ੍ਹੋ।"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ਚਾਲੂ ਕਰ ਰਿਹਾ ਹੈ।"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"ਸੂਚਨਾ ਰੱਦ ਕੀਤੀ।"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"ਸੂਚਨਾ ਸ਼ੇਡ।"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ।"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ਨੂੰ ਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"ਕੋਈ ਹਾਲੀਆ ਆਈਟਮਾਂ ਨਹੀਂ"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ਤੁਸੀਂ ਸਭ ਕੁਝ ਸਾਫ਼ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"ਐਪਲੀਕੇਸ਼ਨ ਜਾਣਕਾਰੀ"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ਸਕ੍ਰੀਨ ਪਿਨਿੰਗ"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"ਖੋਜੋ"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਚਾਲੂ ਨਹੀਂ ਕਰ ਸਕਿਆ।"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਸੁਰੱਖਿਅਤ-ਮੋਡ ਵਿੱਚ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ।"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਇੱਥੇ ਘਸੀਟੋ"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"ਐਪਾਂ ਵਿਚਾਲੇ ਤੇਜ਼ੀ ਨਾਲ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਘਸੀਟੋ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ਹੌਰੀਜ਼ੌਂਟਲ ਸਪਲਿਟ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ਵਰਟੀਕਲ ਸਪਲਿਟ"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ਵਿਉਂਂਤੀ ਸਪਲਿਟ"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"ਸਕ੍ਰੀਨ ਨੂੰ ਉੱਪਰ ਵੱਲ ਵਿਭਾਜਿਤ ਕਰੋ"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"ਸਕ੍ਰੀਨ ਨੂੰ ਖੱਬੇ ਪਾਸੇ ਵਿਭਾਜਿਤ ਕਰੋ"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"ਸਕ੍ਰੀਨ ਨੂੰ ਸੱਜੇ ਪਾਸੇ ਵਿਭਾਜਿਤ ਕਰੋ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"ਰੂਪ-ਰੇਖਾ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ਚਾਰਜ ਹੋਇਆ"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ਚਾਰਜ ਕਰ ਰਿਹਾ ਹੈ"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"ਛੋਟਾ ਕਰੋ"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"ਕੀ ਇਸ ਐਪ ਤੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"ਇਨ੍ਹਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"ਇਹ ਐਪ ਕੈਮਰੇ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ।"</string>
     <string name="appops_microphone" msgid="741508267659494555">"ਇਹ ਐਪ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ।"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"ਇਹ ਐਪ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਹੋਰਾਂ ਐਪਾਂ ਉੱਪਰ ਦਿਖਾਈ ਜਾ ਰਹੀ ਹੈ।"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"ਚਾਰਜਿੰਗ ਦੌਰਾਨ ਪ੍ਰਤੀਸ਼ਤਤਾ ਦਿਖਾਓ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item>
     <item msgid="3327323682209964956">"ਇਸ ਪ੍ਰਤੀਕ ਨੂੰ ਨਾ ਦਿਖਾਓ"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"ਹੋਰ"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਡਿਵਾਈਡਰ"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"ਖੱਬੇ ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 22ef98c..2510aba 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -21,16 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Interfejs"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wyczyść"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Usuń z listy"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informacje o aplikacji"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Tutaj pojawią się ostatnie ekrany"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Ukryj ostatnie aplikacje"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="few">%d ekrany w widoku przeglądu</item>
-      <item quantity="many">%d ekranów w widoku przeglądu</item>
-      <item quantity="other">%d ekranu w widoku przeglądu</item>
-      <item quantity="one">1 ekran w widoku przeglądu</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Brak powiadomień"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Bieżące"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Powiadomienia"</string>
@@ -103,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"otwórz telefon"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"otwórz pomoc głosową"</string>
     <string name="camera_label" msgid="7261107956054836961">"otwórz aparat"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Wybierz nowy układ zadań"</string>
     <string name="cancel" msgid="6442560571259935130">"Anuluj"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Obszar komunikatu pomocy"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potwierdź"</string>
@@ -190,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Usuń stąd <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>: zamknięto."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Wszystkie ostatnie aplikacje zostały zamknięte."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Otwórz informacje o aplikacji <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Uruchamiam <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Zamknięto powiadomienie."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Obszar powiadomień."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Szybkie ustawienia."</string>
@@ -361,23 +345,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"Komunikacja NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Komunikacja NFC jest wyłączona"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Komunikacja NFC jest włączona"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Brak ostatnich elementów"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Wszystko zostało wyczyszczone"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacje o aplikacji"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"przypinanie ekranu"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"szukaj"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Nie udało się uruchomić aplikacji <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikacja <xliff:g id="APP">%s</xliff:g> została wyłączona w trybie bezpiecznym."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Wyczyść wszystko"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Przeciągnij tutaj, by podzielić ekran"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Przesuń w górę, by przełączyć aplikacje"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Szybko przeciągnij w prawo, by przełączyć aplikacje"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podziel poziomo"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podziel pionowo"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Podziel niestandardowo"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Podziel ekran u góry"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Podziel ekran z lewej"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Podziel ekran z prawej"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Przełącz Przegląd"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Naładowana"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Ładowanie"</string>
@@ -626,6 +595,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimalizuj"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Nadal pokazywać powiadomienia z tej aplikacji?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Tych powiadomień nie można wyłączyć"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Ta aplikacja używa aparatu."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Ta aplikacja używa mikrofonu."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Ta aplikacja wyświetla się nad innymi aplikacjami na ekranie."</string>
@@ -764,6 +735,8 @@
     <item msgid="2139628951880142927">"Pokazuj procent podczas ładowania (domyślnie)"</item>
     <item msgid="3327323682209964956">"Nie pokazuj tej ikony"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Inne"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Linia dzielenia ekranu"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Lewa część ekranu na pełnym ekranie"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index f26d1ba..507c6b0 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Interf sist"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Remover da lista"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Inform. do app"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Suas telas recentes aparecem aqui"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Dispensar apps recentes"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d telas em \"Visão geral\"</item>
-      <item quantity="other">%d telas em \"Visão geral\"</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"abrir telefone"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"abrir assistência de voz"</string>
     <string name="camera_label" msgid="7261107956054836961">"abrir câmera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo layout da tarefa"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área da mensagem de ajuda"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Descartar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> descartado."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Todos os apps recentes foram dispensados."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Abre informações do app <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificação dispensada."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Aba de notificações."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Configurações rápidas."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"A NFC está desativada"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"A NFC está ativada"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Nenhum item recente"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Você limpou tudo"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações do app"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixação de tela"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"O app <xliff:g id="APP">%s</xliff:g> está desativado no modo de segurança."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Limpar tudo"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Arraste aqui para usar a tela dividida"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Deslize para cima para alternar entre os apps"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Arraste para a direita para alternar rapidamente entre os apps"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dividir a tela para a parte superior"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dividir a tela para a esquerda"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dividir a tela para a direita"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Alternar Visão geral"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Carregando"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuar mostrando notificações desse app?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Não é possível desativar essas notificações"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"pelo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Este app está usando a câmera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Este app está usando o microfone."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Este app está sobreposto a outros apps na sua tela."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Mostrar porcentagem durante o carregamento (padrão)"</item>
     <item msgid="3327323682209964956">"Não mostrar este ícone"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Mostrar ícones de notificações de baixa prioridade"</string>
     <string name="other" msgid="4060683095962566764">"Outros"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Divisor de tela"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Lado esquerdo em tela cheia"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index e6ed4dc..f3c993f 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"IU do sistema"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Remover da lista"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Info. da aplicação"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Os ecrãs recentes aparecem aqui"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Ignorar aplicações recentes"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d ecrãs na Vista geral</item>
-      <item quantity="one">1 ecrã na Vista geral</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em curso"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"abrir telemóvel"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"abrir assistente de voz"</string>
     <string name="camera_label" msgid="7261107956054836961">"abrir câmara"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo esquema de tarefa"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área da mensagem de ajuda"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ignorar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ignorado."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Todas as aplicações recentes foram ignoradas."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Abrir as informações da aplicação <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"A iniciar <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificação ignorada."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Painel de notificações."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Definições rápidas."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"O NFC está desativado"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"O NFC está ativado"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Nenhum item recente"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Limpou tudo"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações da aplicação"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixação no ecrã"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar o <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"O <xliff:g id="APP">%s</xliff:g> está desativado no modo de segurança."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Limpar tudo"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Arraste aqui para utilizar o ecrã dividido"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Deslizar rapidamente para cima para mudar de aplicação"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Arraste para a direita para mudar rapidamente de aplicação."</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Ecrã dividido na parte superior"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Ecrã dividido à esquerda"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Ecrã dividido à direita"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Ativar/desativar Vista geral"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"A carregar"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Pretende continuar a ver notificações desta aplicação?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Não é possível desativar estas notificações."</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"através da aplicação <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Esta aplicação está a utilizar a câmara."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Esta aplicação está a utilizar o microfone."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Esta aplicação está a sobrepor-se a outras aplicações no ecrã."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Mostrar a percentagem durante o carregamento (predefinição)"</item>
     <item msgid="3327323682209964956">"Não mostrar este ícone"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Mostrar ícones de notificações de prioridade baixa"</string>
     <string name="other" msgid="4060683095962566764">"Outro"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Divisor do ecrã dividido"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Ecrã esquerdo inteiro"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index f26d1ba..507c6b0 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Interf sist"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Remover da lista"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Inform. do app"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Suas telas recentes aparecem aqui"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Dispensar apps recentes"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d telas em \"Visão geral\"</item>
-      <item quantity="other">%d telas em \"Visão geral\"</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"abrir telefone"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"abrir assistência de voz"</string>
     <string name="camera_label" msgid="7261107956054836961">"abrir câmera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo layout da tarefa"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Área da mensagem de ajuda"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmar"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Descartar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> descartado."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Todos os apps recentes foram dispensados."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Abre informações do app <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificação dispensada."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Aba de notificações."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Configurações rápidas."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"A NFC está desativada"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"A NFC está ativada"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Nenhum item recente"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Você limpou tudo"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações do app"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixação de tela"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"O app <xliff:g id="APP">%s</xliff:g> está desativado no modo de segurança."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Limpar tudo"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Arraste aqui para usar a tela dividida"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Deslize para cima para alternar entre os apps"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Arraste para a direita para alternar rapidamente entre os apps"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dividir a tela para a parte superior"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dividir a tela para a esquerda"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dividir a tela para a direita"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Alternar Visão geral"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Carregando"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuar mostrando notificações desse app?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Não é possível desativar essas notificações"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"pelo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Este app está usando a câmera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Este app está usando o microfone."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Este app está sobreposto a outros apps na sua tela."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Mostrar porcentagem durante o carregamento (padrão)"</item>
     <item msgid="3327323682209964956">"Não mostrar este ícone"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Mostrar ícones de notificações de baixa prioridade"</string>
     <string name="other" msgid="4060683095962566764">"Outros"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Divisor de tela"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Lado esquerdo em tela cheia"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index c553f6e..53d8277 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -21,15 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"UI sistem"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ștergeți"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Eliminați din listă"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Info aplicație"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Ecranele dvs. recente apar aici"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Renunțați la aplicațiile recente"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="few">%d ecrane în Recente</item>
-      <item quantity="other">%d de ecrane în Recente</item>
-      <item quantity="one">Un ecran în Recente</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nicio notificare"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"În desfășurare"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificări"</string>
@@ -102,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"deschideți telefonul"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"deschideți asistentul vocal"</string>
     <string name="camera_label" msgid="7261107956054836961">"deschideți camera foto"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Selectați noul aspect pentru activitate"</string>
     <string name="cancel" msgid="6442560571259935130">"Anulați"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Zona mesajelor de ajutor"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Confirmați"</string>
@@ -189,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Închideți <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> a fost eliminată."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toate aplicațiile recente au fost închise."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Deschideți informațiile despre aplicația <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Se inițiază <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificarea a fost închisă."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Fereastră pentru notificări."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Setări rapide."</string>
@@ -358,23 +343,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Serviciul NFC este dezactivat"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Serviciul NFC este activat"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Niciun element recent"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Ați șters tot"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informații despre aplicație"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixarea ecranului"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"căutare"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> nu a putut porni."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplicația <xliff:g id="APP">%s</xliff:g> este dezactivată în modul sigur."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Ștergeți tot"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Trageți aici pentru a folosi ecranul împărțit"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Glisați în sus pentru a comuta între aplicații"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Glisați la dreapta pentru a comuta rapid între aplicații"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divizare pe orizontală"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divizare pe verticală"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divizare personalizată"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Divizați ecranul în partea de sus"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Divizați ecranul la stânga"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Divizați ecranul la dreapta"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Comutați secțiunea Recente"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Încărcată"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Se încarcă"</string>
@@ -622,6 +592,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimizați"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Doriți să continuați afișarea notificărilor de la această aplicație?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Aceste notificări nu pot fi dezactivate"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Această aplicație folosește camera foto."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Această aplicație folosește microfonul."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Această aplicație se afișează pe alte aplicații de pe ecran."</string>
@@ -758,6 +730,8 @@
     <item msgid="2139628951880142927">"Afișează procentajul când se încarcă (prestabilit)"</item>
     <item msgid="3327323682209964956">"Nu afișa această pictogramă"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Altele"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Separator pentru ecranul împărțit"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Partea stângă pe ecran complet"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 07aa66a..b180f87 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -21,16 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Интерфейс системы"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Очистить"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Удалить из списка"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"О приложении"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Здесь будут показаны недавние приложения."</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Закрыть недавние приложения"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">Показан %d экран</item>
-      <item quantity="few">Показано %d экрана</item>
-      <item quantity="many">Показано %d экранов</item>
-      <item quantity="other">Показано %d экранов</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Нет уведомлений"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Текущие"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Уведомления"</string>
@@ -103,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"Открыть телефон."</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"включить аудиоподсказки"</string>
     <string name="camera_label" msgid="7261107956054836961">"Открыть камеру."</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Выберите другой макет"</string>
     <string name="cancel" msgid="6442560571259935130">"Отмена"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Справочное сообщение"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Подтвердить"</string>
@@ -190,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Удаление приложения <xliff:g id="APP">%s</xliff:g> из списка."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" удалено из списка."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Все недавние приложения закрыты."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Открыть информацию о приложении \"<xliff:g id="APP">%s</xliff:g>\""</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Запуск приложения <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Уведомление закрыто"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Панель уведомлений"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Быстрые настройки"</string>
@@ -361,23 +345,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"Модуль NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Модуль NFC отключен"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Модуль NFC включен"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Здесь пока ничего нет."</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Вы очистили всё"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Сведения о приложении"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Заблокировать в приложении"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"поиск"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Не удалось запустить приложение \"<xliff:g id="APP">%s</xliff:g>\""</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" отключено в безопасном режиме."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Очистить все"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Перетащите сюда, чтобы разделить экран"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Чтобы переключиться между приложениями, проведите по экрану вверх."</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Перетащите вправо, чтобы быстро переключиться между приложениями"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Разделить по горизонтали"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Разделить по вертикали"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Разделить по-другому"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Разделить экран по верхнему краю"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Разделить экран по левому краю"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Разделить экран по правому краю"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Переключить режим обзора"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Батарея заряжена"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарядка батареи"</string>
@@ -626,6 +595,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Свернуть"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Показывать уведомления от этого приложения?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Эти уведомления нельзя отключить."</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Это приложение использует камеру."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Это приложение использует микрофон."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Это приложение располагается поверх других приложений."</string>
@@ -764,6 +735,8 @@
     <item msgid="2139628951880142927">"Показывать процент во время зарядки (по умолчанию)"</item>
     <item msgid="3327323682209964956">"Не показывать этот значок"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Другое"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Разделитель экрана"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Левый во весь экран"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index aac62a2..0eae821 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"පද්ධති UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"හිස් කරන්න"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"ලැයිස්තුවෙන් ඉවත් කරන්න"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"යෙදුම් තොරතුරු"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"මෙහි ඔබගේ මෑතක තිර පෙන්නුම් කරයි"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"මෑත යෙදුම් ඉවතලන්න"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">විශ්ලේෂණය තුළ තිර %d යි</item>
-      <item quantity="other">විශ්ලේෂණය තුළ තිර %d</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"දැනුම්දීම් නැත"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"දැනට පවතින"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"දැනුම්දීම්"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"දුරකථනය විවෘත කරන්න"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"විවෘත හඬ සහාය"</string>
     <string name="camera_label" msgid="7261107956054836961">"කැමරාව විවෘත කරන්න"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"නව කාර්යය සැකැස්ම තෝරන්න"</string>
     <string name="cancel" msgid="6442560571259935130">"අවලංගු කරන්න"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"උදවු පණිවිඩ ප්‍රදේශය"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"තහවුරු කරන්න"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ඉවතලන්න."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> අස් කර ඇත."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"සියලුම මෑත යෙඳුම් අස් කරන ලදි."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> යෙදුම් තොරතුරු විවෘත කරයි."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ආරම්භ කරමින්."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"දැනුම්දීම නිෂ්ප්‍රභා කරඇත."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"දැනුම්දීම් ආවරණය."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"ක්ෂණික සැකසීම්."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC අබලයි"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC සබලයි"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"මෑත අයිතම නැත"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ඔබ සියලු දේ හිස් කර ඇත"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"යෙදුම් තොරතුරු"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"තිර ඇමිණීම"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"සෙවීම"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ආරම්භ කළ නොහැක."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ආරක්ෂිත ප්‍රකාරය තුළ අබලයි."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"සියල්ල හිස් කරන්න"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"බෙදුම් තිරය භාවිත කිරීමට මෙතැනට අදින්න"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"යෙදුම් මාරු කිරීමට ස්වයිප් කරන්න"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"ඉක්මනින් යෙදුම් මාරු කිරීමට දකුණට අදින්න"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"තිරස්ව වෙන් කරන්න"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"සිරස්ව වෙන් කරන්න"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"අභිමත ලෙස වෙන් කරන්න"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"තිරය ඉහළට බෙදන්න"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"තිරය වමට බෙදන්න"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"තිරය දකුණට බෙදන්න"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"දළ විශ්ලේෂණය ටොගල කරන්න"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"අරෝපිතයි"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ආරෝපණය වෙමින්"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"කුඩා කරන්න"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"මෙම යෙදුම වෙතින් දැනුම්දීම් පෙන්වමින් තබන්නද?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"මෙම දැනුම්දීම් ක්‍රියාවිරහිත කළ නොහැකිය"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"මෙම යෙදුම කැමරාව භාවිතා කරයි."</string>
     <string name="appops_microphone" msgid="741508267659494555">"මෙම යෙදුම මයික්‍රෆෝනය භාවිතා කරයි."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"මෙම යෙදුම් ඔබගේ තිරය මත අනෙකුත් යෙදුම්වලට උඩින් සංදර්ශනය වේ."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"ආරෝපණය වන විට ප්‍රතිශතය පෙන්වන්න (පෙරනිමි)"</item>
     <item msgid="3327323682209964956">"මෙම නිරූපකය නොපෙන්වන්න"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"වෙනත්"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"බෙදුම්-තිර වෙන්කරණය"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"වම් පූර්ණ තිරය"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 58e176a..6c6ad14 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -21,16 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"UI systému"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Vymazať"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Odstrániť zo zoznamu"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"O aplikácii"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Vaše nedávne obrazovky sa zobrazia tu."</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Zatvoriť nedávne aplikácie"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="few">%d obrazovky v Prehľade</item>
-      <item quantity="many">%d obrazovky v Prehľade</item>
-      <item quantity="other">%d obrazoviek v Prehľade</item>
-      <item quantity="one">1 obrazovka v Prehľade</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Žiadne upozornenia"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Prebiehajúce"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Upozornenia"</string>
@@ -103,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"otvoriť telefón"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"otvoriť hlasového asistenta"</string>
     <string name="camera_label" msgid="7261107956054836961">"spustiť fotoaparát"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Vyberte nové rozloženie úlohy"</string>
     <string name="cancel" msgid="6442560571259935130">"Zrušiť"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Oblasť správy pomocníka"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potvrdiť"</string>
@@ -190,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Zavrieť aplikáciu <xliff:g id="APP">%s</xliff:g>"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikácia <xliff:g id="APP">%s</xliff:g> bola zrušená."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Všetky nedávne aplikácie boli odmietnuté."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Otvoriť informácie o aplikácii <xliff:g id="APP">%s</xliff:g>"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Spúšťa sa aplikácia <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Upozornenie bolo zrušené."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Panel upozornení."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Rýchle nastavenia."</string>
@@ -361,23 +345,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC je deaktivované"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC je aktivované"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Žiadne nedávne položky"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vymazali ste všetko"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informácie o aplikácii"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pripnutie obrazovky"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"hľadať"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikáciu <xliff:g id="APP">%s</xliff:g> sa nepodarilo spustiť"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikácia <xliff:g id="APP">%s</xliff:g> je v núdzovom režime zakázaná."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Vymazať všetko"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Presuňte okno sem a použite tak rozdelenú obrazovku"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Potiahnutím nahor prepnete aplikácie"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Presunutím doprava rýchlo prepnete aplikácie"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Rozdeliť vodorovné"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Rozdeliť zvislé"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Rozdeliť vlastné"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Rozdelená obrazovka hore"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Rozdelená obrazovka naľavo"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Rozdelená obrazovka napravo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Prepnúť prehľad"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nabitá"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Nabíja sa"</string>
@@ -626,6 +595,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimalizovať"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Majú sa upozornenia z tejto aplikácie naďalej zobrazovať?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Tieto upozornenia sa nedajú vypnúť"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Táto aplikácia používa fotoaparát."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Táto aplikácia používa mikrofón."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Táto aplikácia sa zobrazuje cez ďalšie aplikácie na obrazovke."</string>
@@ -764,6 +735,8 @@
     <item msgid="2139628951880142927">"Zobrazovať percentá počas nabíjania (predvolené)"</item>
     <item msgid="3327323682209964956">"Nezobrazovať túto ikonu"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Ďalšie"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Rozdeľovač obrazovky"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Ľavá – na celú obrazovku"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 1323fcd..f94ee60 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -21,16 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Sistemski uporabniški vmesnik"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Počisti"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Odstrani s seznama"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Podatki o aplikaciji"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Vaši nedavni zasloni so prikazani tu"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Zapre nedavne aplikacije"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d zaslon v pregledu</item>
-      <item quantity="two">%d zaslona v pregledu</item>
-      <item quantity="few">%d zasloni v pregledu</item>
-      <item quantity="other">%d zaslonov v pregledu</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ni obvestil"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Trenutno"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Obvestila"</string>
@@ -103,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"odpri telefon"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"odpri glasovnega pomočnika"</string>
     <string name="camera_label" msgid="7261107956054836961">"odpri fotoaparat"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Izberite novo postavitev opravil"</string>
     <string name="cancel" msgid="6442560571259935130">"Prekliči"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Območje sporočila pomoči"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Potrdite"</string>
@@ -190,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Opusti aplikacijo <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> je bila odstranjena."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Vse nedavne aplikacije so bile opuščene."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Odpiranje podatkov o aplikaciji <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Zaganjanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obvestilo je bilo odstranjeno."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Zaslon z obvestili."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Hitre nastavitve."</string>
@@ -361,23 +345,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Tehnologija NFC je onemogočena"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Tehnologija NFC je omogočena"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Ni nedavnih elementov"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vse te počistili"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Podatki o aplikaciji"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pripenjanje zaslona"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"iskanje"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacije <xliff:g id="APP">%s</xliff:g> ni bilo mogoče zagnati."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikacija <xliff:g id="APP">%s</xliff:g> je v varnem načinu onemogočena."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Izbriši vse"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Povlecite sem za razdeljeni zaslon"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Za preklop aplikacij povlecite navzgor"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Povlecite v desno za hiter preklop med aplikacijami"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Razdeli vodoravno"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Razdeli navpično"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Razdeli po meri"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Razdeljen zaslon na vrhu"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Razdeljen zaslon na levi"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Razdeljen zaslon na desni"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Vklop/izklop pregleda"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Akumulator napolnjen"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Polnjenje"</string>
@@ -626,6 +595,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimiraj"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite, da so obvestila te aplikacije še naprej prikazana?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Teh obvestil ni mogoče izklopiti"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Ta aplikacija uporablja fotoaparat."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Ta aplikacija uporablja mikrofon."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Ta aplikacija prekriva druge aplikacije na zaslonu."</string>
@@ -764,6 +735,8 @@
     <item msgid="2139628951880142927">"Prikaži odstotek med polnjenjem (privzeto)"</item>
     <item msgid="3327323682209964956">"Ne prikaži te ikone"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Drugo"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Razdelilnik zaslonov"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Levi v celozaslonski način"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 8f08665..7869cd9 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Ndërfaqja e përdoruesit të sistemit"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Pastro"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Hiq nga lista"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informacioni i aplikacionit"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Ekranet e tua më të fundit shfaqen këtu"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Largo aplikacionet më të fundit"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d ekrane te \"Përmbledhja\"</item>
-      <item quantity="one">1 ekran te \"Përmbledhja\"</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Asnjë njoftim"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Në vazhdim"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Njoftimet"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"hap telefonin"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"hap ndihmën zanore"</string>
     <string name="camera_label" msgid="7261107956054836961">"hap kamerën"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Zgjidh strukturën e re të detyrës"</string>
     <string name="cancel" msgid="6442560571259935130">"Anulo"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Zona e mesazhit të ndihmës"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Konfirmo"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Largo <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> është hequr."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Të gjitha aplikacionet e fundit u larguan."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Hap informacionin e aplikacionit <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Po nis <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Njoftimi është hequr."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Streha e njoftimeve."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Cilësime të shpejta."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC është çaktivizuar"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC është aktivizuar"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Nuk ka asnjë artikull të fundit"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"I ke pastruar të gjitha"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacioni i aplikacionit"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"gozhdimi i ekranit"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"kërko"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> nuk mundi të nisej."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> është i çaktivizuar në modalitetin e sigurt."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Pastroji të gjitha"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Zvarrit këtu për të përdorur ekranin e ndarë"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Rrëshqit shpejt lart për të ndërruar aplikacionet"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Zvarrit djathtas për të ndërruar aplikacionet me shpejtësi"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Ndaje horizontalisht"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Ndaj vertikalisht"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Ndaj të personalizuarën"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Ndaje ekranin lart"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Ndaje ekranin në të majtë"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Ndaje ekranin në të djathtë"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Kalo te përmbledhja"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"I ngarkuar"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Po ngarkohet"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimizo"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Do të vazhdosh t\'i shfaqësh njoftimet nga ky aplikacion?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Këto njoftime nuk mund të çaktivizohen"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Ky aplikacion po përdor kamerën."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Ky aplikacion po përdor mikrofonin."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Ky aplikacion po shfaqet mbi aplikacionet e tjera në ekran."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Shfaq përqindjen gjatë ngarkimit (e parazgjedhur)"</item>
     <item msgid="3327323682209964956">"Mos e shfaq këtë ikonë"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Të tjera"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Ndarësi i ekranit të ndarë"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Ekrani i plotë majtas"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 609f332..420056e 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -21,15 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"UI система"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Обриши"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Уклањање са листе"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Информације о апликацији"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Недавни екрани се појављују овде"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Одбаци недавне апликације"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d екран у Прегледу</item>
-      <item quantity="few">%d екрана у Прегледу</item>
-      <item quantity="other">%d екрана у Прегледу</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Нема обавештења"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Текуће"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Обавештења"</string>
@@ -102,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"отвори телефон"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"отвори гласовну помоћ"</string>
     <string name="camera_label" msgid="7261107956054836961">"отвори камеру"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Изабери нови распоред задатака"</string>
     <string name="cancel" msgid="6442560571259935130">"Откажи"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Област поруке за помоћ"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Потврди"</string>
@@ -189,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Одбаците <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Апликација <xliff:g id="APP">%s</xliff:g> је одбачена."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Све недавно коришћене апликације су одбачене."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Отворите информације о апликацији <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Покрећемо <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Обавештење је одбачено."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Прозор са обавештењима."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Брза подешавања."</string>
@@ -358,23 +343,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC је онемогућен"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC је омогућен"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Нема недавних ставки"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Обрисали сте све"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Информације о апликацији"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"качење екрана"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"претражи"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Покретање апликације <xliff:g id="APP">%s</xliff:g> није успело."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Апликација <xliff:g id="APP">%s</xliff:g> је онемогућена у безбедном режиму."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Обриши све"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Превуците овде да бисте користили раздељени екран"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Превуците нагоре да бисте мењали апликације"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Превуците удесно да бисте брзо променили апликације"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Подели хоризонтално"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Подели вертикално"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Прилагођено дељење"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Подели екран нагоре"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Подели екран налево"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Подели екран надесно"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Укључи/искључи преглед"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Напуњена је"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Пуњење"</string>
@@ -622,6 +592,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Умањи"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Желите ли да се обавештења из ове апликације и даље приказују?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Не можете да искључите ова обавештења"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Ова апликација користи камеру."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Ова апликација користи микрофон."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Ова апликација се приказује преко других апликација на екрану."</string>
@@ -758,6 +730,8 @@
     <item msgid="2139628951880142927">"Прикажи проценат током пуњења (подразумевано)"</item>
     <item msgid="3327323682209964956">"Не приказуј ову икону"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Друго"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Разделник подељеног екрана"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Режим целог екрана за леви екран"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 72ae99b..238cc9b 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Gränssnitt"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ta bort"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Ta bort från listan"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Info om appen"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Dina senaste skärmar visas här"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Avvisa nya appar"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d skärmar i översikten</item>
-      <item quantity="one">En skärm i översikten</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Inga aviseringar"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Pågående"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Aviseringar"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"öppna mobilen"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"öppna röstassistenten"</string>
     <string name="camera_label" msgid="7261107956054836961">"öppna kameran"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Välj en ny layout för uppgiften"</string>
     <string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Område för hjälpmeddelande"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Bekräfta"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ta bort <xliff:g id="APP">%s</xliff:g> från listan."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> togs bort permanent."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alla appar har tagits bort från listan Senaste."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Öppna appinformation för <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Startar <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Meddelandet ignorerades."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Meddelandepanel."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Snabbinställningar."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC är inaktiverat"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC är aktiverat"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Listan med de senaste åtgärderna är tom"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Du har tömt listan"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformation"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fästa skärmen"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"sök"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Det gick inte att starta appen <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> är inaktiverad i säkert läge."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Rensa alla"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Dra hit för att dela upp skärmen"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Byt appar genom att svepa uppåt"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Tryck och dra åt höger för att snabbt byta mellan appar"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Dela horisontellt"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dela vertikalt"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Dela anpassad"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Delad skärm till överkanten"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Delad skärm åt vänster"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Delad skärm åt höger"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Aktivera och inaktivera översikten"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Laddat"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Laddar"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Minimera"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Vill du fortsätta visa aviseringar för den här appen?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"De här aviseringarna kan inte inaktiveras"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Kameran används av appen."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Mikrofonen används av appen."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Appen visas över andra appar på skärmen."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Visa procent under laddning (standard)"</item>
     <item msgid="3327323682209964956">"Visa inte den här ikonen"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Annat"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Avdelare för delad skärm"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Helskärm på vänster skärm"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index a73d0e7..54e8544 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Kiolesura"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Futa"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Ondoa kwenye orodha"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Taarifa za programu-matumizi"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Skrini zako za hivi majuzi huonekana hapa"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Ondosha programu za hivi karibuni"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">Skrini %d katika Muhtasari</item>
-      <item quantity="one">Skrini 1 katika Muhtasari</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Hakuna arifa"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Inaendelea"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Arifa"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"fungua simu"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"fungua mapendekezo ya sauti"</string>
     <string name="camera_label" msgid="7261107956054836961">"fungua kamera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Chagua muundo mpya wa kazi"</string>
     <string name="cancel" msgid="6442560571259935130">"Ghairi"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Sehemu ya ujumbe wa usaidizi"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Thibitisha"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ondoa <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> imeondolewa."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Programu za hivi majuzi zimeondolewa."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Fungua maelezo kuhusu programu ya <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Inaanzisha <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Arifa imetupwa."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Kivuli cha arifa."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Mipangilio ya haraka."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC imezimwa"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC imewashwa"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Hakuna vipengee vya hivi karibuni"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Umeondoa vipengee vyote"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Maelezo ya Programu"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"kubandika kwenye skirini"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"tafuta"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Haikuweza kuanzisha <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> imezimwa katika hali salama."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Futa zote"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Buruta hapa ili utumie skrini iliyogawanywa"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Telezesha kidole juu ili ubadilishe programu"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Buruta kulia ili ubadilishe programu haraka"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Gawanya Mlalo"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Gawanya Wima"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Maalum Iliyogawanywa"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Gawa skrini kuelekea juu"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Gawa skrini upande wa kushoto"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Gawa skrini upande wa kulia"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Washa Muhtasari"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Betri imejaa"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Inachaji"</string>
@@ -618,6 +589,7 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Punguza"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Ungependa kuendelea kuonyesha arifa kutoka programu hii?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Huwezi kuzima arifa hizi"</string>
+    <string name="notification_delegate_header" msgid="9167022191405284627">"kupitia <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="appops_camera" msgid="8100147441602585776">"Programu hii inatumia kamera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Programu hii inatumia maikrofoni."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Programu hii inachomoza kwenye programu zingine zilizo katika skrini yako."</string>
@@ -752,6 +724,7 @@
     <item msgid="2139628951880142927">"Onyesha asilimia wakati inachaji (chaguomsingi)"</item>
     <item msgid="3327323682209964956">"Usionyeshe aikoni hii"</item>
   </string-array>
+    <string name="tuner_low_priority" msgid="1325884786608312358">"Onyesha aikoni za arifa zisizo muhimu"</string>
     <string name="other" msgid="4060683095962566764">"Nyingine"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Kitenganishi cha skrini inayogawanywa"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Skrini nzima ya kushoto"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index eb5c180..8934183 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -88,7 +88,4 @@
 
     <!-- Keyboard shortcuts helper -->
     <dimen name="ksh_layout_width">488dp</dimen>
-
-    <!-- The offsets the tasks animate from when recents is launched while docking -->
-    <dimen name="recents_task_stack_animation_launched_while_docking_offset">192dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 02457d9..becd903 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"சாதனத்தின் UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"அழி"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"பட்டியலில் இருந்து அகற்று"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"பயன்பாட்டுத் தகவல்"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"சமீபத்திய திரைகள் இங்கு தோன்றும்"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"சமீபத்திய பயன்பாடுகளை நிராகரி"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">மேலோட்டப் பார்வையில் %d திரைகள்</item>
-      <item quantity="one">மேலோட்டப் பார்வையில் 1 திரை</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"அறிவிப்புகள் இல்லை"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"செயலில் இருக்கும்"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"அறிவிப்புகள்"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ஃபோனைத் திற"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"குரல் உதவியைத் திற"</string>
     <string name="camera_label" msgid="7261107956054836961">"கேமராவைத் திற"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"புதிய பணி தளவமைப்பைத் தேர்ந்தெடுக்கவும்"</string>
     <string name="cancel" msgid="6442560571259935130">"ரத்துசெய்"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"உதவிச் செய்திக்கான பகுதி"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"உறுதிப்படுத்துக"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ஐ நிராகரி."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> விலக்கப்பட்டது."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"எல்லா சமீபத்திய பயன்பாடுகளும் விலக்கப்பட்டன."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> பயன்பாட்டின் தகவலைத் திற."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ஐத் தொடங்குகிறது."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"அறிவிப்பு நிராகரிக்கப்பட்டது."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"அறிவிப்பு விவரம்."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"உடனடி அமைப்பு."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC முடக்கப்பட்டது"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC இயக்கப்பட்டது"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"சமீபத்திய பணிகள் இல்லை"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"எல்லாவற்றையும் அழித்துவிட்டீர்கள்"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"பயன்பாட்டு தகவல்"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"திரையை பின் செய்தல்"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"தேடு"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ஐத் தொடங்க முடியவில்லை."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"பாதுகாப்புப் பயன்முறையில் <xliff:g id="APP">%s</xliff:g> முடக்கப்பட்டது."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"அனைத்தையும் அழி"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"திரைப் பிரிப்பைப் பயன்படுத்த, இங்கே இழுக்கவும்"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ஆப்ஸிற்கு இடையே மாற்றுவதற்கு, மேல்நோக்கி ஸ்வைப் செய்க"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"ஆப்ஸை வேகமாக மாற்ற, வலப்புறம் இழுக்கவும்"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"கிடைமட்டமாகப் பிரி"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"செங்குத்தாகப் பிரி"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"தனிவிருப்பத்தில் பிரி"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"திரையை மேல்புறமாகப் பிரி"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"திரையை இடப்புறமாகப் பிரி"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"திரையை வலப்புறமாகப் பிரி"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"மேலோட்டப் பார்வையை நிலைமாற்று"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"சார்ஜ் செய்யப்பட்டது"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"சார்ஜ் ஆகிறது"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"சிறிதாக்கு"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"இந்தப் பயன்பாட்டின் அறிவிப்புகளைத் தொடர்ந்து காட்டவா?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"இந்த அறிவிப்புகளை ஆஃப் செய்ய முடியாது"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"இந்த ஆப்ஸானது கேமராவை உபயோகிக்கிறது."</string>
     <string name="appops_microphone" msgid="741508267659494555">"இந்த ஆப்ஸானது, மைக்ரோஃபோனை உபயோகிக்கிறது."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"இந்த ஆப்ஸானது, உங்கள் திரையில் பிற ஆப்ஸின் இடைமுகத்தின் மேல் தோன்றுகிறது."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"சார்ஜ் செய்யும் போது சதவீதத்தைக் காட்டு (இயல்பு)"</item>
     <item msgid="3327323682209964956">"இந்த ஐகானைக் காட்டாதே"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"மற்றவை"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"திரையைப் பிரிக்கும் பிரிப்பான்"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"இடது புறம் முழுத் திரை"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index a6d7699..b895ae6 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"సిస్టమ్ UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"క్లియర్ చేయండి"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"జాబితా నుండి తీసివేయండి"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"యాప్ సమాచారం"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"మీ ఇటీవలి స్క్రీన్‌లు ఇక్కడ కనిపిస్తాయి"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"ఇటీవలి అనువర్తనాలను తీసివేయండి"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">స్థూలదృష్టిలో %d స్క్రీన్‌లు ఉన్నాయి</item>
-      <item quantity="one">స్థూలదృష్టిలో 1 స్క్రీన్ ఉంది</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"నోటిఫికేషన్‌లు లేవు"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"కొనసాగుతున్నవి"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"నోటిఫికేషన్‌లు"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"ఫోన్‌ను తెరువు"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"వాయిస్ అసిస్టెంట్‌ను తెరువు"</string>
     <string name="camera_label" msgid="7261107956054836961">"కెమెరాను తెరువు"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"కొత్త విధి లేఅవుట్‌ను ఎంచుకోండి"</string>
     <string name="cancel" msgid="6442560571259935130">"రద్దు చేయి"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"సహాయ సందేశ ప్రాంతం"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"నిర్ధారించు"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>ని తీసివేయండి."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> తీసివేయబడింది."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"అన్ని ఇటీవలి అనువర్తనాలు తీసివేయబడ్డాయి."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> అనువర్తన సమాచారాన్ని తెరుస్తుంది."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>ని ప్రారంభిస్తోంది."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"నోటిఫికేషన్ తీసివేయబడింది."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"నోటిఫికేషన్ షేడ్."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"శీఘ్ర సెట్టింగ్‌లు."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC నిలిపివేయబడింది"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ప్రారంభించబడింది"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"ఇటీవలి అంశాలు ఏవీ లేవు"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"మీరు అన్నింటినీ తీసివేసారు"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"అనువర్తన సమాచారం"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"స్క్రీన్ పిన్నింగ్"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"వెతుకు"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ని ప్రారంభించడం సాధ్యపడలేదు."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> సురక్షిత-మోడ్‌లో నిలిపివేయబడింది."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"అన్నీ తీసివేయి"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"విభజన స్క్రీన్‌ను ఉపయోగించడానికి ఇక్కడ లాగండి"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"యాప్‌లను మార్చడం కోసం ఎగువకు స్వైప్ చేయండి"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"యాప్‌లను శీఘ్రంగా స్విచ్ చేయడానికి కుడి వైపుకు లాగండి"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"సమతలంగా విభజించు"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"లంబంగా విభజించు"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"అనుకూలంగా విభజించు"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"స్క్రీన్‌ని ఎగువకు విభజించు"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"స్క్రీన్‌ని ఎడమ వైపుకి విభజించు"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"స్క్రీన్‌ని కుడి వైపుకి విభజించు"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"స్థూలదృష్టిని టోగుల్ చేయి"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ఛార్జ్ చేయబడింది"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ఛార్జ్ అవుతోంది"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"కుదించు"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"ఈ యాప్ నుండి నోటిఫికేషన్‌లను చూపిస్తూ ఉండాలా?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"ఈ నోటిఫికేషన్‌లను ఆఫ్ చేయలేరు"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"ఈ యాప్ ఈ కెమెరాను ఉపయోగిస్తోంది."</string>
     <string name="appops_microphone" msgid="741508267659494555">"ఈ యాప్ మైక్రోఫోన్‌ను ఉపయోగిస్తుంది."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"ఈ యాప్ మీ స్క్రీన్‌లోని ఇతర యాప్‌లపై ప్రదర్శించబడుతోంది."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"ఛార్జ్ అవుతున్నప్పుడు శాతాన్ని చూపు (డిఫాల్ట్)"</item>
     <item msgid="3327323682209964956">"ఈ చిహ్నాన్ని చూపవద్దు"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"ఇతరం"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"విభజన స్క్రీన్ విభాగిని"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"ఎడమవైపు పూర్తి స్క్రీన్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 8c78687..4df01ed 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"ส่วนติดต่อผู้ใช้ของระบบ"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"ล้างข้อมูล"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"ลบจากรายการ"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"ข้อมูลแอปพลิเคชัน"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"หน้าจอล่าสุดของคุณแสดงที่นี่"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"ปิดแอปพลิเคชันล่าสุด"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d หน้าจอในภาพรวม</item>
-      <item quantity="one">1 หน้าจอในภาพรวม</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"ไม่มีการแจ้งเตือน"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ดำเนินอยู่"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"การแจ้งเตือน"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"เปิดโทรศัพท์"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"เปิดตัวช่วยเสียง"</string>
     <string name="camera_label" msgid="7261107956054836961">"เปิดกล้อง"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"เลือกรูปแบบงานใหม่"</string>
     <string name="cancel" msgid="6442560571259935130">"ยกเลิก"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"พื้นที่ข้อความช่วยเหลือ"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"ยืนยัน"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"ยกเลิก <xliff:g id="APP">%s</xliff:g>"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ถูกลบไปแล้ว"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ปิดแอปพลิเคชันล่าสุดทั้งหมดแล้ว"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"เปิดข้อมูลแอปพลิเคชัน <xliff:g id="APP">%s</xliff:g>"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"กำลังเริ่มต้น <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"ปิดการแจ้งเตือนแล้ว"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"หน้าต่างแจ้งเตือน"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"การตั้งค่าด่วน"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ถูกปิดใช้งาน"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"เปิดใช้งาน NFC แล้ว"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"ไม่มีรายการล่าสุด"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"คุณได้ล้างทุกอย่างแล้ว"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"ข้อมูลแอปพลิเคชัน"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"การตรึงหน้าจอ"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"ค้นหา"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"ไม่สามารถเริ่มใช้ <xliff:g id="APP">%s</xliff:g>"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ปิดใช้ในโหมดปลอดภัย"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ล้างทั้งหมด"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"ลากมาที่นี่เพื่อใช้การแยกหน้าจอ"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"เลื่อนขึ้นเพื่อสลับแอป"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"ลากไปทางขวาเพื่อสลับแอปอย่างรวดเร็ว"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"แยกในแนวนอน"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"แยกในแนวตั้ง"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"แยกแบบกำหนดเอง"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"แยกหน้าจอไปด้านบน"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"แยกหน้าจอไปทางซ้าย"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"แยกหน้าจอไปทางขวา"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"สลับภาพรวม"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ชาร์จแล้ว"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"กำลังชาร์จ"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"ย่อเล็กสุด"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"แสดงการแจ้งเตือนจากแอปนี้ต่อไปไหม"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"ปิดการแจ้งเตือนเหล่านี้ไม่ได้"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"แอปนี้กำลังใช้กล้อง"</string>
     <string name="appops_microphone" msgid="741508267659494555">"แอปนี้กำลังใช้ไมโครโฟน"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"แอปนี้กำลังแสดงทับแอปอื่นๆ ในหน้าจอ"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"แสดงเปอร์เซ็นต์เมื่อชาร์จ (ค่าเริ่มต้น)"</item>
     <item msgid="3327323682209964956">"อย่าแสดงไอคอนนี้"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"อื่นๆ"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"เส้นแบ่งหน้าจอ"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"เต็มหน้าจอทางซ้าย"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 21cf1f2..71a26ac 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"UI ng System"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"I-clear"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Alisin mula sa listahan"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Impormasyon ng app"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Lumalabas dito ang iyong kamakailang screen"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Huwag pansinin ang kamakailang apps"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d screen sa Pangkalahatang-ideya</item>
-      <item quantity="other">%d na screen sa Pangkalahatang-ideya</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Walang mga notification"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Nagpapatuloy"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Mga Notification"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"buksan ang telepono"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"buksan ang voice assist"</string>
     <string name="camera_label" msgid="7261107956054836961">"buksan ang camera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Pumili ng bagong layout ng gawain"</string>
     <string name="cancel" msgid="6442560571259935130">"Kanselahin"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Lugar ng mensahe ng tulong"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Kumpirmahin"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"I-dismiss ang <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Hindi pinansin ang <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Na-dismiss ang lahat ng kamakailang application."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Buksan ang impormasyon ng <xliff:g id="APP">%s</xliff:g> application."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Sinisimulan ang <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Na-dismiss ang notification."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notification shade."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Mga mabilisang setting."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Naka-disable ang NFC"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Naka-enable ang NFC"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Walang kamakailang item"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Na-clear mo ang lahat"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Impormasyon ng Application"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pagpi-pin sa screen"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"maghanap"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Hindi masimulan <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Naka-disable ang <xliff:g id="APP">%s</xliff:g> sa safe-mode."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"I-clear lahat"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"I-drag dito upang magamit ang split screen"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Mag-swipe pataas upang lumipat ng app"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"I-drag pakanan para mabilisang magpalipat-lipat ng app"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Custom"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"I-split ang screen pataas"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"I-split ang screen pakaliwa"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"I-split ang screen pakanan"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"I-toggle ang Overview"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nasingil na"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Nagcha-charge"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"I-minimize"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Patuloy na ipakita ang mga notification mula sa app na ito?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Hindi maaaring i-off ang mga notification na ito"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Ginagamit ng app na ito ang camera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Ginagamit ng app na ito ang mikropono."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Ipinapakita ang app na ito sa ibabaw ng iba pang app sa iyong screen."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Ipakita ang porsyento kapag nagcha-charge (default)"</item>
     <item msgid="3327323682209964956">"Huwag ipakita ang icon na ito"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Iba pa"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Divider ng split-screen"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"I-full screen ang nasa kaliwa"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index fb200ec..b3809c4 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Sist Arayüzü"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Temizle"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Listeden kaldır"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Uygulama bilgileri"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Son ekranlarınız burada görünür"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Son uygulamaları kapat"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">Genel Bakış\'ta %d ekran</item>
-      <item quantity="one">Genel Bakış\'ta 1 ekran</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Bildirim yok"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Sürüyor"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Bildirimler"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"telefonu aç"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"sesli yardımı aç"</string>
     <string name="camera_label" msgid="7261107956054836961">"kamerayı aç"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Yeni görev düzenini seçin"</string>
     <string name="cancel" msgid="6442560571259935130">"İptal"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Yardım mesajı alanı"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Onaylayın"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> uygulamasını kapat."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> kaldırıldı."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Tüm son uygulamalar kapatıldı."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> uygulaması bilgilerini açın."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> başlatılıyor."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Bildirim kapatıldı."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bildirim gölgesi."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Hızlı ayarlar."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC devre dışı"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC etkin"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Yeni öğe yok"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Her şeyi sildiniz"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Uygulama Bilgileri"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekran sabitleme"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"ara"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> başlatılamadı."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g>, güvenli modda devre dışıdır."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tümünü temizle"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Ekranı bölünmüş olarak kullanmak için burayı sürükleyin"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Uygulamalar arasında geçiş yapmak için yukarı kaydırın"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Uygulamaları hızlıca değiştirmek için sağa kaydırın"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Yatay Ayırma"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dikey Ayırma"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Özel Ayırma"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Ekranı yukarıya doğru böl"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Ekranı sola doğru böl"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Ekranı sağa doğru böl"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Genel bakışı aç/kapat"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Şarj oldu"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Şarj oluyor"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Küçült"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu uygulamadan gelen bildirimler gösterilmeye devam edilsin mi?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Bu bildirimler kapatılamaz"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Bu uygulama kamerayı kullanıyor."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Bu uygulama mikrofonu kullanıyor."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Bu uygulama, ekranınızdaki diğer uygulamaların üzerinde görüntüleniyor."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Şarj olurken yüzdeyi göster (varsayılan)"</item>
     <item msgid="3327323682209964956">"Bu simgeyi gösterme"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Diğer"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Bölünmüş ekran ayırıcı"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Solda tam ekran"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 82b56a2..a52ec2a 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -21,16 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Інтерфейс системи"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Очист."</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Видалити зі списку"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Про додаток"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Ваші останні екрани відображаються тут"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Відхилити останні програми"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">На панелі огляду %d екран</item>
-      <item quantity="few">На панелі огляду %d екрани</item>
-      <item quantity="many">На панелі огляду %d екранів</item>
-      <item quantity="other">На панелі огляду %d екрана</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Немає сповіщень"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Поточні"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Сповіщення"</string>
@@ -103,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"відкрити телефон"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"запустити голосові підказки"</string>
     <string name="camera_label" msgid="7261107956054836961">"відкрити камеру"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Виберіть новий макет завдання"</string>
     <string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Область довідкового повідомлення"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Підтвердити"</string>
@@ -190,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Видалити додаток <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Програму <xliff:g id="APP">%s</xliff:g> закрито."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Усі останні додатки закрито."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Відкрити інформацію про додаток <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Запуск додатка <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Сповіщення відхилено."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Панель сповіщень."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Швидке налаштування."</string>
@@ -361,23 +345,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC вимкнено"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ввімкнено"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Немає нещодавніх завдань"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Ви очистили все"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Інформація про додаток"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"закріпити екран"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"пошук"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Не вдалося запустити <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Додаток <xliff:g id="APP">%s</xliff:g> вимкнено в безпечному режимі."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Очистити все"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Перетягніть сюди, щоб увімкнути режим розділеного екрана"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Проводьте пальцем угору, щоб переходити між додатками"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Перетягуйте праворуч, щоб швидко переходити між додатками"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Розділити горизонтально"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Розділити вертикально"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Розділити (власний варіант)"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Розділити екран угорі"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Розділити екран ліворуч"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Розділити екран праворуч"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Увімкнути або вимкнути огляд"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Заряджено"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Заряджається"</string>
@@ -626,6 +595,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Згорнути"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Чи показувати сповіщення з цього додатка надалі?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ці сповіщення не можна вимкнути"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Цей додаток використовує камеру."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Цей додаток використовує мікрофон."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Цей додаток відображається поверх інших додатків на екрані."</string>
@@ -764,6 +735,8 @@
     <item msgid="2139628951880142927">"Показувати відсотки під час заряджання (за умовчанням)"</item>
     <item msgid="3327323682209964956">"Не показувати цей значок"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Інше"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Розділювач екрана"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Ліве вікно на весь екран"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index b02ceae..eb0c60b 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"‏سسٹم UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"صاف کریں"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"فہرست سے ہٹائیں"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"ایپ کی معلومات"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"آپ کی حالیہ اسکرینز یہاں ظاہر ہوتی ہیں"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"حالیہ ایپس برخاست کریں"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">‏عمومی جائزہ میں ‎%d اسکرینز</item>
-      <item quantity="one">عمومی جائزہ میں 1 اسکرین</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"کوئی اطلاعات نہیں ہیں"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"جاری"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"اطلاعات"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"فون کھولیں"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"صوتی معاون کھولیں"</string>
     <string name="camera_label" msgid="7261107956054836961">"کیمرا کھولیں"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"نئے کام کا لے آؤٹ منتخب کریں"</string>
     <string name="cancel" msgid="6442560571259935130">"منسوخ کریں"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"امدادی پیغام کا علاقہ"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"تصدیق کریں"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> کو مسترد کریں۔"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> کو ہٹا دیا گیا۔"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"سبھی حالیہ ایپلیکیشنز کو برخاست کر دیا گیا۔"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> ایپلیکیشن معلومات کھولیں۔"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> شروع ہو رہی ہے۔"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"اطلاع مسترد ہوگئی۔"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"اطلاعاتی شیڈ۔"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"فوری ترتیبات۔"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"‏NFC غیر فعال ہے"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"‏NFC فعال ہے"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"کوئی حالیہ آئٹم نہیں"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"آپ نے سب کچھ صاف کر دیا ہے"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"ایپلیکیشن کی معلومات"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"اسکرین کو پن کرنا"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"تلاش کریں"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> کو شروع نہیں کیا جا سکا۔"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"محفوظ موڈ میں <xliff:g id="APP">%s</xliff:g> غیر فعال ہوتی ہے۔"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"سبھی کو صاف کریں"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"اسپلٹ اسکرین استعمال کرنے کیلئے یہاں گھسیٹیں"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"ایپس سوئچ کرنے کیلئے اوپر سوائپ کریں"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"تیزی سے ایپس کو سوئچ کرنے کے لیے دائیں طرف گھسیٹیں"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"بلحاظ افقی الگ کریں"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"بلحاظ عمودی الگ کریں"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"بلحاظ حسب ضرورت الگ کریں"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"اسکرین کو اوپر کی جانب تقسیم کریں"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"اسکرین کو بائیں جانب تقسیم کریں"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"اسکرین کو دائیں جانب تقسیم کریں"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"مجموعی جائزہ ٹوگل کریں"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"چارج ہوگئی"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"چارج ہو رہی ہے"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"چھوٹا کریں"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"اس ایپ کی طرف سے اطلاعات دکھانا جاری رکھیں؟"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"ان اطلاعات کو آف نہیں کیا جا سکتا"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"یہ ایپ کیمرے کا استعمال کر رہی ہے۔"</string>
     <string name="appops_microphone" msgid="741508267659494555">"یہ ایپ مائیکروفون کا استعمال کر رہی ہے۔"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"یہ ایپ آپ کی اسکرین پر دیگر ایپس پر ڈسپلے کر رہی ہے۔"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"چارج ہوتے وقت فیصد دکھائیں (ڈیفالٹ)"</item>
     <item msgid="3327323682209964956">"یہ آئیکن نہ دکھائیں"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"دیگر"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"سپلٹ اسکرین تقسیم کار"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"بائیں فل اسکرین"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index a128e7f..e7c3300 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"UI tizimi"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Tozalash"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Ro‘yxatdan o‘chirish"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Ilova haqida"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Yaqinda ish-gan ilovalar bu yerda ko‘rinadi"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"So‘nggi dasturlarni tozalash"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">Umumiy ma’lumot bo‘limida %d ta ekran bor</item>
-      <item quantity="one">Umumiy ma’lumot bo‘limida 1 ta ekran bor</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Bildirishnomalar yo‘q"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Hali bajarilmagan"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Eslatmalar"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"telefonni ochish"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"ovozli yordamni yoqish"</string>
     <string name="camera_label" msgid="7261107956054836961">"kamerani ochish"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Yangi vazifa tartibini tanlash"</string>
     <string name="cancel" msgid="6442560571259935130">"Bekor qilish"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Yordam xabari"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"OK"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Olib tashlash: <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> olib tashlangan."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Yaqinda ishlatilgan barcha ilovalar olib tashlandi."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> ilovasi haqidagi ma’lumotlarni ochadi."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ishga tushirilmoqda."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Xabarnoma e‘tiborsiz qoldirildi."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Xabarnoma soyasi."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Tezkor sozlamalar."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC o‘chiq"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC yoniq"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Hozircha hech narsa yo‘q"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Hammasi o‘chirildi"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Ilova haqida ma’lumot"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekranni mahkamlash"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"qidirish"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"“<xliff:g id="APP">%s</xliff:g>” ilovasini ishga tushirib bo‘lmadi."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Xavfsiz rejimda <xliff:g id="APP">%s</xliff:g> ilovasi o‘chirib qo‘yildi."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Hammasini tozalash"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Ekranni bo‘lish xususiyatidan foydalanish uchun bu yerga torting"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Ilovalarni almashtirish uchun ekranni tepaga suring"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Ilovalarni tezkor almashtirish uchun o‘ngga torting"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Gorizontal yo‘nalishda bo‘lish"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikal yo‘nalishda bo‘lish"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Boshqa usulda bo‘lish"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Ekranni tepaga qadash"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Ekranni chap tomonga qadash"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Ekranni o‘ng tomonga qadash"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Umumiy nazar rejimini almashtirish"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Batareya quvvati to‘ldi"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Quvvat olmoqda"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Kichraytirish"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu ilovadan keladigan bildirishnomalar chiqaversinmi?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Bu bildirishnomalarni chiqmaydigan qilish imkonsiz"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Bu ilova kameradan foydalanmoqda."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Bu ilova mikrofondan foydalanmoqda."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Bu ilova ekranda boshqa ilovalar ustidan ochilgan."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Quvvat olayotganda foizda ko‘rsatilsin (birlamchi)"</item>
     <item msgid="3327323682209964956">"Bu belgi boshqa ko‘rsatilmasin"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Boshqa"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Ekranni ikkiga bo‘lish chizig‘i"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Chapda to‘liq ekran"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index c7b9178..4356ac5 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Giao diện người dùng hệ thống"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Xóa"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Xóa khỏi danh sách"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Thông tin ứng dụng"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Màn hình gần đây của bạn sẽ xuất hiện tại đây"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Loại bỏ các ứng dụng gần đây"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">%d màn hình trong Tổng quan</item>
-      <item quantity="one">1 màn hình trong Tổng quan</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Không có thông báo nào"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Đang diễn ra"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Thông báo"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"mở điện thoại"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"mở trợ lý thoại"</string>
     <string name="camera_label" msgid="7261107956054836961">"mở máy ảnh"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Chọn bố cục tác vụ mới"</string>
     <string name="cancel" msgid="6442560571259935130">"Hủy"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Vùng thông báo trợ giúp"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Xác nhận"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Xóa bỏ <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> đã bị loại bỏ."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Đã bỏ qua tất cả các ứng dụng gần đây."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Mở thông tin ứng dụng <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Bắt đầu <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Đã loại bỏ thông báo."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bóng thông báo."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Cài đặt nhanh."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC đã được tắt"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC đã được bật"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Không có mục gần đây nào"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Bạn đã xóa mọi nội dung"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Thông tin ứng dụng"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"khóa màn hình"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"tìm kiếm"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Không thể khởi động <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> bị tắt ở chế độ an toàn."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Xóa tất cả"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Kéo vào đây để sử dụng chế độ chia đôi màn hình"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Vuốt lên để chuyển đổi ứng dụng"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Kéo sang phải để chuyển đổi nhanh giữa các ứng dụng"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Phân tách ngang"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Phân tách dọc"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Tùy chỉnh phân tách"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Chia đôi màn hình lên trên"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Chia đôi màn hình sang trái"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Chia đôi màn hình sang phải"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Bật/tắt chế độ xem Tổng quan"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Đã sạc đầy"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Đang sạc"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Thu nhỏ"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Tiếp tục hiển thị các thông báo từ ứng dụng này?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Không thể tắt các thông báo này"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Ứng dụng này đang sử dụng máy ảnh."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Ứng dụng này đang sử dụng micrô."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Ứng dụng này đang hiển thị chồng lên các ứng dụng khác trên màn hình."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Hiển thị phần trăm khi sạc (mặc định)"</item>
     <item msgid="3327323682209964956">"Không hiển thị biểu tượng này"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Khác"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Bộ chia chia đôi màn hình"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Toàn màn hình bên trái"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 0bfcdcf..5424266 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"系统界面"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"从列表中删除"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"应用信息"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"您最近浏览过的屏幕会显示在此处"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"关闭最近运行的应用"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">概览中有 %d 个屏幕</item>
-      <item quantity="one">概览中有 1 个屏幕</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"无通知"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"正在进行的"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"打开电话"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"打开语音助理"</string>
     <string name="camera_label" msgid="7261107956054836961">"打开相机"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"选择新的任务布局"</string>
     <string name="cancel" msgid="6442560571259935130">"取消"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"帮助消息区域"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"确认"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"移除<xliff:g id="APP">%s</xliff:g>。"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"已删除<xliff:g id="APP">%s</xliff:g>"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"已关闭所有最近用过的应用。"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"打开<xliff:g id="APP">%s</xliff:g>应用信息。"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"正在启动<xliff:g id="APP">%s</xliff:g>。"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"已关闭通知。"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"通知栏。"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"快捷设置。"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC 已停用"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC 已启用"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"近期没有任何内容"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"您已清除所有内容"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"应用信息"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"固定屏幕"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"搜索"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"无法启动<xliff:g id="APP">%s</xliff:g>。"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g>已在安全模式下停用。"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"全部清除"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"拖动到此处即可使用分屏功能"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"向上滑动可切换应用"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"向右拖动可快速切换应用"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自定义分割"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"将屏幕分隔线移到上方"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"将屏幕分隔线移到左侧"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"将屏幕分隔线移到右侧"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"切换概览"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"已充满"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"正在充电"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"要继续显示来自此应用的通知吗?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"无法关闭这些通知"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"此应用正在使用摄像头。"</string>
     <string name="appops_microphone" msgid="741508267659494555">"此应用正在使用麦克风。"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"此应用正显示在屏幕上其他应用的上层。"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"充电时显示百分比(默认)"</item>
     <item msgid="3327323682209964956">"不显示此图标"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"其他"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"分屏分隔线"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"左侧全屏"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index cf93802..38e8b3c 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"系統使用者介面"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"從清單中移除"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"應用程式資料"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"您最近的螢幕顯示在這裡"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"關閉最近使用的應用程式"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">概覽中有 %d 個畫面</item>
-      <item quantity="one">概覽中有 1 個畫面</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"無通知"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"持續進行"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"開啟電話"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"開啟語音助手"</string>
     <string name="camera_label" msgid="7261107956054836961">"開啟相機"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"選取新的工作版面配置"</string>
     <string name="cancel" msgid="6442560571259935130">"取消"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"說明訊息區域"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"確認"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"關閉「<xliff:g id="APP">%s</xliff:g>」。"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"「<xliff:g id="APP">%s</xliff:g>」已關閉。"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"所有最近使用的應用程式均已關閉。"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"開啟「<xliff:g id="APP">%s</xliff:g>」應用程式的資料。"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"正在啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"通知已關閉。"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"通知欄。"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"快速設定。"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC 已停用"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC 已啟用"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"沒有最近項目"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"您已清除所有項目"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"應用程式資料"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"螢幕固定"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"「<xliff:g id="APP">%s</xliff:g>」已在安全模式中停用。"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"全部清除"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"在這裡拖曳即可分割螢幕"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"向上滑動即可切換應用程式"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"向右拖曳即可快速切換應用程式"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自訂分割"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"將分割畫面顯示喺頂部"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"將分割畫面顯示喺左邊"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"將分割畫面顯示喺右邊"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"切換概覽"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"已完成充電"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"充電中"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"要繼續顯示此應用程式的通知嗎?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"無法關閉這些通知"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"此應用程式目前使用相機。"</string>
     <string name="appops_microphone" msgid="741508267659494555">"此應用程式目前使用麥克風。"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"此應用程式目前透過其他應用程式在畫面上顯示內容。"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"充電時顯示百分比 (預設)"</item>
     <item msgid="3327323682209964956">"不顯示這個圖示"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"其他"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"分割畫面分隔線"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"左邊全螢幕"</string>
@@ -826,7 +801,7 @@
     <string name="notification_channel_general" msgid="4525309436693914482">"一般訊息"</string>
     <string name="notification_channel_storage" msgid="3077205683020695313">"儲存空間"</string>
     <string name="notification_channel_hints" msgid="7323870212489152689">"提示"</string>
-    <string name="instant_apps" msgid="6647570248119804907">"即時應用程式"</string>
+    <string name="instant_apps" msgid="6647570248119804907">"免安裝應用程式"</string>
     <string name="instant_apps_title" msgid="8738419517367449783">"<xliff:g id="APP">%1$s</xliff:g> 運作中"</string>
     <string name="instant_apps_message" msgid="1183313016396018086">"已開啟免安裝應用程式。"</string>
     <string name="instant_apps_message_with_help" msgid="6179830437630729747">"已開啟免安裝應用程式。輕按即可瞭解詳情。"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index c5a2f90..e399c44 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"系統 UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"從清單中移除"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"應用程式資訊"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"你最近的螢幕會顯示在這裡"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"關閉最近使用的應用程式"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="other">總覽中有 %d 個畫面</item>
-      <item quantity="one">總覽中有 1 個畫面</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"沒有通知"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"進行中"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"開啟電話"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"開啟語音小幫手"</string>
     <string name="camera_label" msgid="7261107956054836961">"開啟攝影機"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"選取新工作版面配置"</string>
     <string name="cancel" msgid="6442560571259935130">"取消"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"說明訊息區域"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"確認"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"關閉「<xliff:g id="APP">%s</xliff:g>」。"</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"「<xliff:g id="APP">%s</xliff:g>」已關閉。"</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"最近使用的應用程式已全部關閉。"</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"開啟「<xliff:g id="APP">%s</xliff:g>」應用程式資訊。"</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"正在啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"已關閉通知。"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"通知欄。"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"快捷設定。"</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC 已停用"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC 已啟用"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"最近沒有任何項目"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"你已清除所有工作"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"應用程式資訊"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"螢幕固定"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"「<xliff:g id="APP">%s</xliff:g>」在安全模式中為停用狀態。"</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"全部清除"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"拖曳到這裡即可使用分割畫面"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"向上滑動即可切換應用程式"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"向右拖曳即可快速切換應用程式"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自訂分割"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"將分割畫面顯示在頂端"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"將分割畫面顯示在左邊"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"將分割畫面顯示在右邊"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"切換總覽"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"已充飽"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"充電中"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"要繼續顯示這個應用程式的通知嗎?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"無法關閉這些通知"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"這個應用程式正在使用相機。"</string>
     <string name="appops_microphone" msgid="741508267659494555">"這個應用程式正在使用麥克風。"</string>
     <string name="appops_overlay" msgid="6165912637560323464">"這個應用程式顯示在畫面上其他應用程式的上層。"</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"充電時顯示百分比 (預設)"</item>
     <item msgid="3327323682209964956">"不顯示這個圖示"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"其他"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"分割畫面分隔線"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"以全螢幕顯示左側畫面"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 2fc207a..3887f14 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -21,14 +21,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7164937344850004466">"Uhlelo lwe-UI"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Sula"</string>
-    <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Susa ohlwini"</string>
-    <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Ulwazi lwensiza"</string>
-    <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Izikrini zakho zakamuva zivela lapha"</string>
-    <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Susa izinhlelo zokusebenza zakamumva"</string>
-    <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">%d izikrini eziku-Buka konke</item>
-      <item quantity="other">%d izikrini eziku-Buka konke</item>
-    </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Azikho izaziso"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Okuqhubekayo"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Izaziso"</string>
@@ -101,7 +93,6 @@
     <string name="phone_label" msgid="2320074140205331708">"vula ifoni"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"vula isilekeleli sezwi"</string>
     <string name="camera_label" msgid="7261107956054836961">"vula ikhamera"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Khetha isakhiwo somsebenzi omusha"</string>
     <string name="cancel" msgid="6442560571259935130">"Khansela"</string>
     <string name="accessibility_biometric_dialog_help_area" msgid="8953787076940186847">"Indawo yosizo lomlayezo"</string>
     <string name="biometric_dialog_confirm" msgid="6468457350041712674">"Qinisekisa"</string>
@@ -188,11 +179,6 @@
     <skip />
     <!-- no translation found for accessibility_work_mode (702887484664647430) -->
     <skip />
-    <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Cashisa i-<xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ivaliwe."</string>
-    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Zonke izinhlelo zokusebenza zakamuva zicashisiwe."</string>
-    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Vula ulwazi lohlelo lokusebenza le-<xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iqala i-<xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Isaziso sichithiwe."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Umthunzi wesaziso."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Izilingiselelo ezisheshayo."</string>
@@ -355,23 +341,8 @@
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"I-NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"I-NFC ikhutshaziwe"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"I-NFC inikwe amandla"</string>
-    <string name="recents_empty_message" msgid="808480104164008572">"Azikho izinto zakamuva"</string>
-    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Usule yonke into"</string>
-    <string name="recents_app_info_button_label" msgid="2890317189376000030">"Ulwazi lohlelo lokusebenza"</string>
-    <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ukuphina isikrini"</string>
-    <string name="recents_search_bar_label" msgid="8074997400187836677">"sesha"</string>
-    <string name="recents_launch_error_message" msgid="2969287838120550506">"Ayikwazanga ukuqala i-<xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"I-<xliff:g id="APP">%s</xliff:g> ikhutshaziwe kumodi yokuphepha."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Sula konke"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Hudulela lapha ukuze usebenzise ukuhlukanisa kwesikrini"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Swayiphela phezulu ukuze ushintshe izinhlelo zokusebenza"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Hudula ngqo ukuze ushintshe ngokushesha izinhlelo zokusebenza"</string>
-    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Hlukanisa okuvundlile"</string>
-    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Hlukanisa okumile"</string>
-    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Hlukanisa kwezifiso"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Hlukanisela isikrini phezulu"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Hlukanisela isikrini ngakwesokunxele"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Hlukanisela isikrini ngakwesokudla"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Guqula ukubuka konke"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Kushajiwe"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Iyashaja"</string>
@@ -618,6 +589,8 @@
     <string name="inline_minimize_button" msgid="966233327974702195">"Nciphisa"</string>
     <string name="inline_keep_showing_app" msgid="1723113469580031041">"Qhubeka nokubonisa izaziso kusuka kulolu hlelo lokusebenza?"</string>
     <string name="notification_unblockable_desc" msgid="1037434112919403708">"Lezi zaziso azikwazi ukuvalwa"</string>
+    <!-- no translation found for notification_delegate_header (9167022191405284627) -->
+    <skip />
     <string name="appops_camera" msgid="8100147441602585776">"Lolu hlelo lokusebenza lusebenzisa ikhamera."</string>
     <string name="appops_microphone" msgid="741508267659494555">"Lolu hlelo lokusebenza lusebenzisa imakrofoni."</string>
     <string name="appops_overlay" msgid="6165912637560323464">"Lolu hlelo lokusebenza luboniswa ngaphezulu kwezinye izinhlelo zokusebenza kusikrini sakho."</string>
@@ -752,6 +725,8 @@
     <item msgid="2139628951880142927">"Bonisa iphesentheji uma ishaja (okuzenzakalelayo)"</item>
     <item msgid="3327323682209964956">"Ungabonisi lesi sithonjana"</item>
   </string-array>
+    <!-- no translation found for tuner_low_priority (1325884786608312358) -->
+    <skip />
     <string name="other" msgid="4060683095962566764">"Okunye"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"Isihlukanisi sokuhlukanisa isikrini"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Isikrini esigcwele esingakwesokunxele"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index f50ef82..baaf9d6 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -30,12 +30,6 @@
     <declare-styleable name="NotificationLinearLayout">
         <attr name="insetLeft" format="dimension" />
     </declare-styleable>
-    <declare-styleable name="RecentsPanelView">
-        <attr name="recentItemLayout" format="reference" />
-        <!-- Style for the "Clear all" button. -->
-        <attr name="clearAllStyle" format="reference" />
-        <attr name="clearAllBackgroundColor" format="reference" />
-    </declare-styleable>
     <declare-styleable name="DeadZone">
         <attr name="minSize" format="dimension" />
         <attr name="maxSize" format="dimension" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d1320a3..b3567f8 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -39,32 +39,6 @@
     <!-- Tint color for the content on the notification overflow card. -->
     <color name="keyguard_overflow_content_color">#ff686868</color>
 
-    <!-- The disabled recents task bar background color. -->
-    <color name="recents_task_bar_disabled_background_color">#ff676767</color>
-    <!-- The default recents task bar background color. -->
-    <color name="recents_task_bar_default_background_color">#ffe6e6e6</color>
-    <!-- The default recents task view background color. -->
-    <color name="recents_task_view_default_background_color">#fff3f3f3</color>
-    <!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
-    <color name="recents_task_bar_light_text_color">#ffeeeeee</color>
-    <!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
-    <color name="recents_task_bar_dark_text_color">#cc000000</color>
-    <!-- The recents task bar light dismiss icon color to be drawn on top of dark backgrounds. -->
-    <color name="recents_task_bar_light_icon_color">#ccffffff</color>
-    <!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
-    <color name="recents_task_bar_dark_icon_color">#99000000</color>
-    <!-- The lock to task button background color. -->
-    <color name="recents_task_view_lock_to_app_button_background_color">#ffe6e6e6</color>
-    <!-- The lock to task button foreground color. -->
-    <color name="recents_task_view_lock_to_app_button_color">#ff666666</color>
-    <!-- The background color for the freeform workspace. -->
-    <color name="recents_freeform_workspace_bg_color">#33FFFFFF</color>
-
-    <!-- The background color for clear all button on light backgrounds if not transparent. -->
-    <color name="recents_clear_all_button_bg_light_color">#CCFFFFFF</color>
-    <!-- The background color for clear all button on dark backgrounds if not transparent. -->
-    <color name="recents_clear_all_button_bg_dark_color">#CC000000</color>
-
     <color name="keyguard_affordance">#ffffffff</color>
 
     <!-- The color of the legacy notification background -->
@@ -110,12 +84,6 @@
     <!-- The shadow color for light navigation bar icons. -->
     <color name="nav_key_button_shadow_color">#30000000</color>
 
-    <!-- Shadow color for the first pixels around the fake shadow for recents. -->
-    <color name="fake_shadow_start_color">#44000000</color>
-
-    <!-- Shadow color for the furthest pixels around the fake shadow for recents. -->
-    <color name="fake_shadow_end_color">#03000000</color>
-
     <color name="screen_pinning_request_window_bg">#80000000</color>
 
     <color name="segmented_buttons_background">#14FFFFFF</color><!-- 8% white -->
@@ -161,12 +129,7 @@
     <color name="smart_reply_button_stroke">#ffdadce0</color>
 
     <!-- Biometric dialog colors -->
-    <color name="biometric_dialog_bg_color">#ffffffff</color> <!-- 100% white -->
-    <color name="biometric_dialog_text_dark_color">#dd000000</color> <!-- 87% black -->
-    <color name="biometric_dialog_text_light_color">#89000000</color> <!-- 54% black -->
     <color name="biometric_dialog_dim_color">#80000000</color> <!-- 50% black -->
-    <color name="biometric_dialog_error_color">#fff44336</color> <!-- red -->
-    <color name="biometric_dialog_biometric_color">#ff008577</color> <!-- teal -->
 
     <!-- Logout button -->
     <color name="logout_button_bg_color">#ccffffff</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 6378309..d8648fa 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -20,19 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <!-- Whether recents should use hardware layers for its taskviews. This flag can be enabled
-    for devices where the java drawing of round rects may be slow -->
-    <bool name="config_recents_use_hardware_layers">false</bool>
-
-    <!-- The number of app thumbnails we keep in memory -->
-    <integer name="config_recents_max_thumbnail_count">10</integer>
-
-    <!-- The number of app icons we keep in memory -->
-    <integer name="config_recents_max_icon_count">20</integer>
-
-    <!-- Whether to use cheap, less good looking shadows for recents -->
-    <bool name="config_recents_fake_shadows">false</bool>
-
     <!-- Whether to clip notification contents with a rounded rectangle. Might be expensive on
          certain GPU's and thus can be turned off with only minimal visual impact. -->
     <bool name="config_notifications_round_rect_clipping">true</bool>
@@ -45,6 +32,11 @@
      interface.  This name is in the ComponentName flattened format (package/class)  -->
     <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
 
+    <!-- Component to be used as the recents implementation.  Must implement the
+     RecentsImplementation interface.  This name is in the ComponentName flattened format
+     (package/class)  -->
+    <string name="config_recentsComponent" translatable="false">com.android.systemui.recents.OverviewProxyRecentsImpl</string>
+
     <!-- Whether or not we show the number in the bar. -->
     <bool name="config_statusBarShowNumber">false</bool>
 
@@ -155,7 +147,7 @@
 
     <!-- The number of milliseconds before the ambient notification auto-dismisses. This will
          override the default pulse length. -->
-    <integer name="ambient_notification_decay">6000</integer>
+    <integer name="ambient_notification_decay">10000</integer>
 
     <!-- Minimum display time for a heads up notification, in milliseconds. -->
     <integer name="ambient_notification_minimum_time">2000</integer>
@@ -163,30 +155,6 @@
     <!-- The number of milliseconds to extend ambient pulse by when prompted (e.g. on touch) -->
     <integer name="ambient_notification_extension_time">6000</integer>
 
-    <!-- The duration in seconds to wait before the dismiss buttons are shown. -->
-    <integer name="recents_task_bar_dismiss_delay_seconds">1000</integer>
-
-    <!-- The duration for animating the task decorations in after transitioning from an app. -->
-    <integer name="recents_task_enter_from_app_duration">200</integer>
-
-    <!-- The duration for animating the task decorations in after transitioning from an app. -->
-    <integer name="recents_task_enter_from_affiliated_app_duration">125</integer>
-
-    <!-- The duration for animating the task decorations out before transitioning to an app. -->
-    <integer name="recents_task_exit_to_app_duration">125</integer>
-
-    <!-- The min animation duration for animating the nav bar scrim in. -->
-    <integer name="recents_nav_bar_scrim_enter_duration">400</integer>
-
-    <!-- The animation duration for scrolling the stack to a particular item. -->
-    <integer name="recents_animate_task_stack_scroll_duration">200</integer>
-
-    <!-- The delay to enforce between each alt-tab key press. -->
-    <integer name="recents_alt_tab_key_delay">200</integer>
-
-    <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
-    <integer name="recents_svelte_level">0</integer>
-
     <!-- In multi-window, determines whether the stack where recents lives should grow from
          the smallest position when being launched. -->
     <bool name="recents_grow_in_multiwindow">true</bool>
@@ -194,16 +162,6 @@
     <!-- Animation duration when using long press on recents to dock -->
     <integer name="long_press_dock_anim_duration">250</integer>
 
-    <!-- Recents: The relative range of visible tasks from the current scroll position
-         while the stack is focused. -->
-    <item name="recents_layout_focused_range_min" format="float" type="integer">-3</item>
-    <item name="recents_layout_focused_range_max" format="float" type="integer">2</item>
-
-    <!-- Recents: The relative range of visible tasks from the current scroll position
-         while the stack is not focused. -->
-    <item name="recents_layout_unfocused_range_min" format="float" type="integer">-2</item>
-    <item name="recents_layout_unfocused_range_max" format="float" type="integer">2.5</item>
-
     <!-- Whether to enable KeyguardService or not -->
     <bool name="config_enableKeyguardService">true</bool>
 
@@ -362,7 +320,6 @@
     <string-array name="config_systemUIServiceComponentsPerUser" translatable="false">
         <item>com.android.systemui.Dependency</item>
         <item>com.android.systemui.util.NotificationChannels</item>
-        <item>com.android.systemui.recents.Recents</item>
     </string-array>
 
     <!-- Nav bar button default ordering/layout -->
@@ -504,5 +461,7 @@
     <bool name="config_pipEnableDismissDragToEdge">true</bool>
 
     <!-- SystemUI Plugins that can be loaded on user builds. -->
-    <string-array name="config_pluginWhitelist" translatable="false" />
+    <string-array name="config_pluginWhitelist" translatable="false">
+        <item>com.android.systemui</item>
+    </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ab7dec9..3050e79 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -793,102 +793,11 @@
     <dimen name="ksh_item_padding">4dp</dimen>
     <dimen name="ksh_item_margin_start">4dp</dimen>
 
-<!-- Recents Layout -->
-
-    <!-- The amount to inset the stack, specifically at the top and the other sides.  We also
-         don't want this to change across configurations that Recents can be opened in, so we
-         define them statically for all display sizes. -->
-    <dimen name="recents_layout_min_margin">16dp</dimen>
-    <dimen name="recents_layout_top_margin_phone">16dp</dimen>
-    <dimen name="recents_layout_top_margin_tablet">32dp</dimen>
-    <dimen name="recents_layout_top_margin_tablet_xlarge">40dp</dimen>
-    <dimen name="recents_layout_bottom_margin">16dp</dimen>
-    <dimen name="recents_layout_side_margin_phone">16dp</dimen>
-    <dimen name="recents_layout_side_margin_tablet">48dp</dimen>
-    <dimen name="recents_layout_side_margin_tablet_docked">16dp</dimen>
-    <dimen name="recents_layout_side_margin_tablet_xlarge">64dp</dimen>
-    <dimen name="recents_layout_side_margin_tablet_xlarge_docked">16dp</dimen>
-
-    <!-- The height between the top margin and the top of the focused task. -->
-    <dimen name="recents_layout_top_peek_size">48dp</dimen>
-    <!-- The height between the bottom margin and the top of task in front of the focused task. -->
-    <dimen name="recents_layout_bottom_peek_size">56dp</dimen>
-
-    <!-- The offset from the top and bottom of the stack of the focused task.  The bottom offset
-         will be additionally offset by the bottom system insets since it goes under the nav bar
-         in certain orientations. -->
-    <dimen name="recents_layout_initial_top_offset_phone_port">128dp</dimen>
-    <dimen name="recents_layout_initial_bottom_offset_phone_port">80dp</dimen>
-    <dimen name="recents_layout_initial_top_offset_phone_land">72dp</dimen>
-    <dimen name="recents_layout_initial_bottom_offset_phone_land">72dp</dimen>
-    <dimen name="recents_layout_initial_top_offset_tablet">160dp</dimen>
-    <dimen name="recents_layout_initial_bottom_offset_tablet">112dp</dimen>
-
-    <!-- The min/max translationZ for the tasks in the stack. -->
-    <dimen name="recents_layout_z_min">3dp</dimen>
-    <dimen name="recents_layout_z_max">24dp</dimen>
-
-    <!-- The margin between the freeform and stack.  We also don't want this to change across
-         configurations that Recents can be opened in, so we define them statically for all
-         display sizes. -->
-    <dimen name="recents_freeform_layout_bottom_margin">16dp</dimen>
-
-    <!-- The padding between each freeform task. -->
-    <dimen name="recents_freeform_layout_task_padding">8dp</dimen>
-
-<!-- Recents Views -->
-
-    <!-- The height of a task view bar.  This has to be large enough to cover the action bar
-         height in either orientation at this smallest width. -->
-    <dimen name="recents_task_view_header_height">56dp</dimen>
-    <dimen name="recents_task_view_header_height_tablet_land">64dp</dimen>
-
-    <!-- The padding of a button in the recents task view header. -->
-    <dimen name="recents_task_view_header_button_padding">16dp</dimen>
-    <dimen name="recents_task_view_header_button_padding_tablet_land">20dp</dimen>
-
-    <!-- The radius of the rounded corners on a task view and its shadow (which can be larger
-         to create a softer corner effect. -->
-    <dimen name="recents_task_view_rounded_corners_radius">2dp</dimen>
-    <dimen name="recents_task_view_shadow_rounded_corners_radius">6dp</dimen>
-
-    <!-- The amount of highlight to make on each task view. -->
-    <dimen name="recents_task_view_highlight">1dp</dimen>
-
-    <!-- The size of the lock-to-app button and its icon. -->
-    <dimen name="recents_lock_to_app_size">56dp</dimen>
-    <dimen name="recents_lock_to_app_icon_size">28dp</dimen>
-
-    <!-- The amount of overscroll allowed when flinging to the end of the stack. -->
-    <dimen name="recents_fling_overscroll_distance">24dp</dimen>
-
-    <!-- The size of the drag hint text. -->
-    <dimen name="recents_drag_hint_text_size">14sp</dimen>
-
     <!-- The size of corner radius of the arrow in the onboarding toast. -->
     <dimen name="recents_onboarding_toast_arrow_corner_radius">2dp</dimen>
     <!-- The start margin of quick scrub onboarding toast. -->
     <dimen name="recents_quick_scrub_onboarding_margin_start">8dp</dimen>
 
-    <!-- The min alpha to apply to a task affiliation group color. -->
-    <item name="recents_task_affiliation_color_min_alpha_percentage" format="float" type="dimen">0.6</item>
-
-    <!-- The amount to offset when animating into an affiliate group. -->
-    <dimen name="recents_task_stack_animation_affiliate_enter_offset">32dp</dimen>
-
-    <!-- The offsets the tasks animate from when recents is launched while docking -->
-    <dimen name="recents_task_stack_animation_launched_while_docking_offset">144dp</dimen>
-
-    <!-- The amount to translate when animating the removal of a task. -->
-    <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
-
-    <!-- The alpha to apply to the recents row when it doesn't have focus -->
-    <item name="recents_recents_row_dim_alpha" format="float" type="dimen">0.5</item>
-
-    <!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
-         loading full resolution screenshots. -->
-    <dimen name="recents_fast_fling_velocity">600dp</dimen>
-
     <!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
     <dimen name="pip_dismiss_gradient_height">176dp</dimen>
 
@@ -935,7 +844,7 @@
     <dimen name="burn_in_prevention_offset_y">50dp</dimen>
 
     <!-- The maximum offset in either direction that icons move to prevent burn-in on AOD. -->
-    <dimen name="default_burn_in_prevention_offset">5dp</dimen>
+    <dimen name="default_burn_in_prevention_offset">15dp</dimen>
 
     <dimen name="corner_size">8dp</dimen>
     <dimen name="top_padding">0dp</dimen>
@@ -980,6 +889,7 @@
     <dimen name="biometric_dialog_biometric_icon_size">64dp</dimen>
     <dimen name="biometric_dialog_corner_size">4dp</dimen>
     <dimen name="biometric_dialog_animation_translation_offset">350dp</dimen>
+    <dimen name="biometric_dialog_border_padding">4dp</dimen>
 
     <!-- Wireless Charging Animation values -->
     <dimen name="wireless_charging_dots_radius_start">0dp</dimen>
@@ -1023,4 +933,19 @@
     <!-- How much we expand the touchable region of the status bar below the notch to catch touches
          that just start below the notch. -->
     <dimen name="display_cutout_touchable_region_size">12dp</dimen>
+
+    <!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
+    <dimen name="ongoing_appops_dialog_icon_height">48dp</dimen>
+    <!-- Margin between text lines in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_text_margin">15dp</dimen>
+    <!-- Padding around Ongoing App Ops dialog content -->
+    <dimen name="ongoing_appops_dialog_content_padding">24dp</dimen>
+    <!-- Margins around the Ongoing App Ops chip. In landscape, the side margins are 0 -->
+    <dimen name="ongoing_appops_chip_margin">12dp</dimen>
+    <!-- Start and End padding for Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_side_padding">6dp</dimen>
+    <!-- Padding between background of Ongoing App Ops chip and content -->
+    <dimen name="ongoing_appops_chip_bg_padding">4dp</dimen>
+    <!-- Radius of Ongoing App Ops chip corners -->
+    <dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 42e19aa..6f5d657 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -24,26 +24,6 @@
          all of the currently visible notifications. [CHAR LIMIT=10]-->
     <string name="status_bar_clear_all_button">Clear</string>
 
-    <!-- Title shown in recents popup for removing an application from the list -->
-    <string name="status_bar_recent_remove_item_title">Remove from list</string>
-
-    <!-- Title shown in recents popup for inspecting an application's properties -->
-    <string name="status_bar_recent_inspect_item_title">App info</string>
-
-    <!-- Message shown in the middle of the screen after clicking on the recent apps button
-         when there are no recent apps to show. Also used for accessibility. [CHAR LIMIT=45]-->
-    <string name="status_bar_no_recent_apps">Your recent screens appear here</string>
-
-    <!-- Content description for the button to dismiss Recent Apps (only present on large
-         devices) -->
-    <string name="status_bar_accessibility_dismiss_recents">Dismiss recent apps</string>
-
-    <!-- Message that is read when you enter recent apps in TalkBack -->
-    <plurals name="status_bar_accessibility_recent_apps">
-        <item quantity="one">1 screen in Overview</item>
-        <item quantity="other">%d screens in Overview</item>
-    </plurals>
-
     <!-- The label in the bar at the top of the status bar when there are no notifications
          showing.  [CHAR LIMIT=40]-->
     <string name="status_bar_no_notifications_title">No notifications</string>
@@ -205,6 +185,39 @@
     <string name="screenshot_failed_to_capture_text">Taking screenshots isn\'t allowed by the app or
         your organization</string>
 
+    <!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
+    <string name="screenrecord_name">Screen Recording</string>
+    <!-- Description of the screen recording notification channel [CHAR LIMIT=NONE]-->
+    <string name="screenrecord_channel_description">Ongoing notification for a screen record session</string>
+    <!-- Label for the button to begin screen recording [CHAR LIMIT=NONE]-->
+    <string name="screenrecord_start_label">Start Recording</string>
+    <!-- Label for the checkbox to enable microphone input during screen recording [CHAR LIMIT=NONE]-->
+    <string name="screenrecord_mic_label">Record voiceover</string>
+    <!-- Label for the checkbox to enable showing location of touches during screen recording [CHAR LIMIT=NONE]-->
+    <string name="screenrecord_taps_label">Show taps</string>
+    <!-- Label for notification action to stop and save the screen recording [CHAR LIMIT=35] -->
+    <string name="screenrecord_stop_label">Stop</string>
+    <!-- Label for notification action to pause screen recording [CHAR LIMIT=35] -->
+    <string name="screenrecord_pause_label">Pause</string>
+    <!-- Label for notification action to resume screen recording [CHAR LIMIT=35] -->
+    <string name="screenrecord_resume_label">Resume</string>
+    <!-- Label for notification action to cancel and discard screen recording [CHAR LIMIT=35] -->
+    <string name="screenrecord_cancel_label">Cancel</string>
+    <!-- Label for notification action to share screen recording [CHAR LIMIT=35] -->
+    <string name="screenrecord_share_label">Share</string>
+    <!-- Label for notification action to delete a screen recording file [CHAR LIMIT=35] -->
+    <string name="screenrecord_delete_label">Delete</string>
+    <!-- A toast message shown after successfully canceling a screen recording [CHAR LIMIT=NONE] -->
+    <string name="screenrecord_cancel_success">Screen recording canceled</string>
+    <!-- Notification text shown after saving a screen recording to prompt the user to view it [CHAR LIMIT=100] -->
+    <string name="screenrecord_save_message">Screen recording saved, tap to view</string>
+    <!-- A toast message shown after successfully deleting a screen recording [CHAR LIMIT=NONE] -->
+    <string name="screenrecord_delete_description">Screen recording deleted</string>
+    <!-- A toast message shown when there is an error deleting a screen recording [CHAR LIMIT=NONE] -->
+    <string name="screenrecord_delete_error">Error deleting screen recording</string>
+    <!-- A toast message shown when the screen recording cannot be started due to insufficient permissions [CHAR LIMIT=NONE] -->
+    <string name="screenrecord_permission_error">Failed to get permissions</string>
+
     <!-- Title for the USB function chooser in UsbPreferenceActivity. [CHAR LIMIT=30] -->
     <string name="usb_preference_title">USB file transfer options</string>
     <!-- Label for the MTP USB function in UsbPreferenceActivity. [CHAR LIMIT=50] -->
@@ -252,8 +265,6 @@
     <string name="voice_assist_label">open voice assist</string>
     <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
     <string name="camera_label">open camera</string>
-    <!-- Caption for "Recents resize" developer debug feature. [CHAR LIMIT=NONE] -->
-    <string name="recents_caption_resize">Select new task layout</string>
     <!-- Button name for "Cancel". [CHAR LIMIT=NONE] -->
     <string name="cancel">Cancel</string>
 
@@ -477,16 +488,6 @@
     <!-- Content description of the work mode icon in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_work_mode">@string/quick_settings_work_mode_label</string>
 
-    <!-- Content description to tell the user that this button will remove an application from recents -->
-    <string name="accessibility_recents_item_will_be_dismissed">Dismiss <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
-    <!-- Content description to tell the user an application has been removed from recents -->
-    <string name="accessibility_recents_item_dismissed"><xliff:g id="app" example="Calendar">%s</xliff:g> dismissed.</string>
-    <!-- Content description to tell the user all applications has been removed from recents -->
-    <string name="accessibility_recents_all_items_dismissed">All recent applications dismissed.</string>
-    <!-- Content description to tell the user that this button will open application info for an application in recents -->
-    <string name="accessibility_recents_item_open_app_info">Open <xliff:g id="app" example="Calendar">%s</xliff:g> application info.</string>
-    <!-- Content description to tell the user an application has been launched from recents -->
-    <string name="accessibility_recents_item_launched">Starting <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
     <!-- Content description to tell the user a notification has been removed from the notification shade -->
     <string name="accessibility_notification_dismissed">Notification dismissed.</string>
 
@@ -836,42 +837,10 @@
     <!-- QuickSettings: NFC (on) [CHAR LIMIT=NONE] -->
     <string name="quick_settings_nfc_on">NFC is enabled</string>
 
-    <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
-    <string name="recents_empty_message">No recent items</string>
-    <!-- Recents: The empty recents string after dismissing all tasks. [CHAR LIMIT=NONE] -->
-    <string name="recents_empty_message_dismissed_all">You\'ve cleared everything</string>
-    <!-- Recents: The info panel app info button string. [CHAR LIMIT=NONE] -->
-    <string name="recents_app_info_button_label">Application Info</string>
-    <!-- Recents: The screen pinning button. [CHAR LIMIT=NONE] -->
-    <string name="recents_lock_to_app_button_label">screen pinning</string>
-    <!-- Recents: Temporary string for the button in the recents search bar. [CHAR LIMIT=NONE] -->
-    <string name="recents_search_bar_label">search</string>
-    <!-- Recents: Launch error string. [CHAR LIMIT=NONE] -->
-    <string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
-    <!-- Recents: Launch disabled string. [CHAR LIMIT=NONE] -->
-    <string name="recents_launch_disabled_message"><xliff:g id="app" example="Calendar">%s</xliff:g> is disabled in safe-mode.</string>
-    <!-- Recents: Stack action button string. [CHAR LIMIT=NONE] -->
-    <string name="recents_stack_action_button_label">Clear all</string>
-    <!-- Recents: Hint text that shows on the drop targets to start multiwindow. [CHAR LIMIT=NONE] -->
-    <string name="recents_drag_hint_message">Drag here to use split screen</string>
     <!-- Recents: Text that shows above the navigation bar after launching a few apps. [CHAR LIMIT=NONE] -->
     <string name="recents_swipe_up_onboarding">Swipe up to switch apps</string>
     <!-- Recents: Text that shows above the navigation bar after launching several apps. [CHAR LIMIT=NONE] -->
     <string name="recents_quick_scrub_onboarding">Drag right to quickly switch apps</string>
-
-    <!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
-    <string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
-    <!-- Recents: MultiStack add stack split vertical radio button. [CHAR LIMIT=NONE] -->
-    <string name="recents_multistack_add_stack_dialog_split_vertical">Split Vertical</string>
-    <!-- Recents: MultiStack add stack split custom radio button. [CHAR LIMIT=NONE] -->
-    <string name="recents_multistack_add_stack_dialog_split_custom">Split Custom</string>
-    <!-- Recents: Accessibility split to the top -->
-    <string name="recents_accessibility_split_screen_top">Split screen to the top</string>
-    <!-- Recents: Accessibility split to the left -->
-    <string name="recents_accessibility_split_screen_left">Split screen to the left</string>
-    <!-- Recents: Accessibility split to the right -->
-    <string name="recents_accessibility_split_screen_right">Split screen to the right</string>
-
     <!-- QuickStep: Accessibility to toggle overview [CHAR LIMIT=40] -->
     <string name="quick_step_accessibility_toggle_overview">Toggle Overview</string>
 
@@ -1915,6 +1884,9 @@
         <item>Don\'t show this icon</item>
     </string-array>
 
+    <!-- SysUI Tuner: Switch for showing low-priority notification icons in status bar [CHAR LIMIT=NONE] -->
+    <string name="tuner_low_priority">Show low-priority notification icons</string>
+
     <!-- SysUI Tuner: Other section -->
     <string name="other">Other</string>
 
@@ -2159,7 +2131,7 @@
     <string name="app_info">App info</string>
 
     <!-- Action label for switching to a browser for an instant app [CHAR LIMIT=20] -->
-    <string name="go_to_web">Go to web</string>
+    <string name="go_to_web">Go to browser</string>
 
     <!-- Quick settings tile for toggling mobile data [CHAR LIMIT=20] -->
     <string name="mobile_data">Mobile data</string>
@@ -2266,4 +2238,39 @@
          app for debugging. Will not be seen by users. [CHAR LIMIT=20] -->
     <string name="heap_dump_tile_name">Dump SysUI Heap</string>
 
+    <!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
+    <string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
+
+    <!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]-->
+    <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
+
+    <!-- Action on Ongoing Privacy Dialog to open application [CHAR LIMIT=10]-->
+    <string name="ongoing_privacy_dialog_open_app">Open app</string>
+
+    <!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]-->
+    <string name="ongoing_privacy_dialog_cancel">Cancel</string>
+
+    <!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]-->
+    <string name="ongoing_privacy_dialog_okay">Okay</string>
+
+    <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=10]-->
+    <string name="ongoing_privacy_dialog_open_settings">Settings</string>
+
+    <!-- Text for item in Ongoing Privacy Dialog when only one app is using a particular type of app op [CHAR LIMIT=NONE] -->
+    <string name="ongoing_privacy_dialog_app_item"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="type" example="camera">%2$s</xliff:g> for the last <xliff:g id="time" example="3">%3$d</xliff:g> min</string>
+
+    <!-- Text for item in Ongoing Privacy Dialog when only multiple apps are using a particular type of app op [CHAR LIMIT=NONE] -->
+    <string name="ongoing_privacy_dialog_apps_item"><xliff:g id="apps" example="Camera, Phone">%1$s</xliff:g> are using your <xliff:g id="type" example="camera">%2$s</xliff:g></string>
+
+    <!-- Text for Ongoing Privacy Dialog when a single app is using app ops [CHAR LIMIT=NONE] -->
+    <string name="ongoing_privacy_dialog_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g></string>
+
+    <!-- Text for camera app op [CHAR LIMIT=12]-->
+    <string name="privacy_type_camera">camera</string>
+
+    <!-- Text for location app op [CHAR LIMIT=12]-->
+    <string name="privacy_type_location">location</string>
+
+    <!-- Text for microphone app op [CHAR LIMIT=12]-->
+    <string name="privacy_type_microphone">microphone</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6446367..6244e1c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -16,35 +16,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <style name="RecentsTheme" parent="@android:style/Theme.Material">
-        <!-- NoTitle -->
-        <item name="android:windowNoTitle">true</item>
-        <!-- Misc -->
-        <item name="android:statusBarColor">@android:color/transparent</item>
-        <item name="android:navigationBarColor">@android:color/transparent</item>
-        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
-        <item name="android:windowAnimationStyle">@null</item>
-        <item name="android:ambientShadowAlpha">0.35</item>
-    </style>
-
-    <!-- Recents theme -->
-    <style name="RecentsTheme.Wallpaper">
-        <item name="android:windowBackground">@*android:color/transparent</item>
-        <item name="android:colorBackgroundCacheHint">@null</item>
-        <item name="android:windowShowWallpaper">true</item>
-        <item name="android:windowDisablePreview">true</item>
-        <item name="clearAllStyle">@style/ClearAllButtonDefaultMargins</item>
-        <item name="clearAllBackgroundColor">@color/recents_clear_all_button_bg_dark_color</item>
-        <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
-        <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_dark</item>
-    </style>
-
-    <style name="RecentsTheme.Wallpaper.Light">
-        <item name="clearAllBackgroundColor">@color/recents_clear_all_button_bg_light_color</item>
-        <item name="wallpaperTextColor">@*android:color/primary_text_material_light</item>
-        <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_light</item>
-    </style>
-
     <style name="ClearAllButtonDefaultMargins">
         <item name="android:layout_marginStart">0dp</item>
         <item name="android:layout_marginTop">0dp</item>
@@ -52,13 +23,6 @@
         <item name="android:layout_marginBottom">0dp</item>
     </style>
 
-    <!-- Performance optimized Recents theme (no wallpaper) -->
-    <style name="RecentsTheme.NoWallpaper">
-        <item name="android:windowBackground">@android:color/black</item>
-        <item name="wallpaperTextColor">@android:color/white</item>
-        <item name="wallpaperTextColorSecondary">@android:color/white</item>
-    </style>
-
     <!-- Theme used for the activity that shows when the system forced an app to be resizable -->
     <style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
         <item name="android:windowBackground">@drawable/forced_resizable_background</item>
@@ -293,11 +257,6 @@
         <item name="android:windowExitAnimation">@*android:anim/shrink_fade_out_from_bottom</item>
     </style>
 
-    <style name="Animation.RecentPanel">
-        <item name="android:windowEnterAnimation">@*android:anim/grow_fade_in_from_bottom</item>
-        <item name="android:windowExitAnimation">@*android:anim/shrink_fade_out_from_bottom</item>
-    </style>
-
     <style name="Animation.NavigationBarFadeIn">
         <item name="android:windowEnterAnimation">@anim/navbar_fade_in</item>
         <item name="android:windowExitAnimation">@null</item>
@@ -544,4 +503,13 @@
         <item name="chargingAnimColor">@android:color/white</item>
         <item name="android:textColor">@android:color/white</item>
     </style>
+
+    <!-- Screen recording -->
+    <style name="ScreenRecord" parent="Theme.SystemUI.Dialog.GlobalActions">
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:backgroundDimEnabled">true</item>
+        <item name="android:windowCloseOnTouchOutside">true</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/res/xml/fileprovider.xml b/packages/SystemUI/res/xml/fileprovider.xml
index 4aaa90f..fa6468f 100644
--- a/packages/SystemUI/res/xml/fileprovider.xml
+++ b/packages/SystemUI/res/xml/fileprovider.xml
@@ -17,4 +17,5 @@
 
 <paths xmlns:android="http://schemas.android.com/apk/res/android">
     <cache-path name="leak" path="leak/"/>
+    <external-path name="screenrecord" path="."/>
 </paths>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 46ea494..6eec5dc 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -98,6 +98,11 @@
             android:summary="%s"
             android:entries="@array/clock_options" />
 
+        <com.android.systemui.tuner.TunerSwitch
+            android:key="low_priority"
+            android:title="@string/tuner_low_priority"
+            sysui:defValue="false" />
+
     </PreferenceScreen>
 
     <PreferenceScreen
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 8cc6091..8e7fadb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -158,6 +158,10 @@
         // If a plugin is detected in the stack of a crash then this will be called for that
         // plugin, if the plugin causing a crash cannot be identified, they are all disabled
         // assuming one of them must be bad.
+        if (mWhitelistedPlugins.contains(info.mPackage)) {
+            // Don't disable whitelisted plugins as they are a part of the OS.
+            return;
+        }
         Log.w(TAG, "Disabling plugin " + info.mPackage + "/" + info.mClass);
         mManager.getPluginEnabler().setEnabled(new ComponentName(info.mPackage, info.mClass),
                 false);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
index 208f4fe..3f907a8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
@@ -27,6 +27,8 @@
     // must be one of the channels created in NotificationChannels.java
     String NOTIFICATION_CHANNEL_ID = "ALR";
 
+    String[] getWhitelistedPlugins();
+
     <T extends Plugin> T getOneShotPlugin(Class<T> cls);
     <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls);
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 87f2934..dc2a9bd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -210,6 +210,10 @@
             Uri uri = intent.getData();
             ComponentName component = ComponentName.unflattenFromString(
                     uri.toString().substring(10));
+            if (mWhitelistedPlugins.contains(component.getPackageName())) {
+                // Don't disable whitelisted plugins as they are a part of the OS.
+                return;
+            }
             getPluginEnabler().setEnabled(component, false);
             mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
                     SystemMessage.NOTE_PLUGIN);
@@ -246,7 +250,11 @@
                         SystemMessage.NOTE_PLUGIN, nb.build(), UserHandle.ALL);
             }
             if (clearClassLoader(pkg)) {
-                Toast.makeText(mContext, "Reloading " + pkg, Toast.LENGTH_LONG).show();
+                if (Build.IS_ENG) {
+                    Toast.makeText(mContext, "Reloading " + pkg, Toast.LENGTH_LONG).show();
+                } else {
+                    Log.v(TAG, "Reloading " + pkg);
+                }
             }
             if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
                 for (PluginInstanceManager manager : mPluginMap.values()) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
deleted file mode 100644
index 114d69e..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.shared.recents.model;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.util.Log;
-
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-/**
- * Background task resource loader
- */
-@Deprecated
-class BackgroundTaskLoader implements Runnable {
-    static String TAG = "BackgroundTaskLoader";
-    static boolean DEBUG = false;
-
-    private Context mContext;
-    private final HandlerThread mLoadThread;
-    private final Handler mLoadThreadHandler;
-    private final Handler mMainThreadHandler;
-
-    private final TaskResourceLoadQueue mLoadQueue;
-    private final IconLoader mIconLoader;
-
-    private boolean mStarted;
-    private boolean mCancelled;
-    private boolean mWaitingOnLoadQueue;
-
-    private final OnIdleChangedListener mOnIdleChangedListener;
-
-    /** Constructor, creates a new loading thread that loads task resources in the background */
-    public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
-            IconLoader iconLoader, OnIdleChangedListener onIdleChangedListener) {
-        mLoadQueue = loadQueue;
-        mIconLoader = iconLoader;
-        mMainThreadHandler = new Handler();
-        mOnIdleChangedListener = onIdleChangedListener;
-        mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
-                android.os.Process.THREAD_PRIORITY_BACKGROUND);
-        mLoadThread.start();
-        mLoadThreadHandler = new Handler(mLoadThread.getLooper());
-    }
-
-    /** Restarts the loader thread */
-    void start(Context context) {
-        mContext = context;
-        mCancelled = false;
-        if (!mStarted) {
-            // Start loading on the load thread
-            mStarted = true;
-            mLoadThreadHandler.post(this);
-        } else {
-            // Notify the load thread to start loading again
-            synchronized (mLoadThread) {
-                mLoadThread.notifyAll();
-            }
-        }
-    }
-
-    /** Requests the loader thread to stop after the current iteration */
-    void stop() {
-        // Mark as cancelled for the thread to pick up
-        mCancelled = true;
-        // If we are waiting for the load queue for more tasks, then we can just reset the
-        // Context now, since nothing is using it
-        if (mWaitingOnLoadQueue) {
-            mContext = null;
-        }
-    }
-
-    @Override
-    public void run() {
-        while (true) {
-            if (mCancelled) {
-                // We have to unset the context here, since the background thread may be using it
-                // when we call stop()
-                mContext = null;
-                // If we are cancelled, then wait until we are started again
-                synchronized(mLoadThread) {
-                    try {
-                        mLoadThread.wait();
-                    } catch (InterruptedException ie) {
-                        ie.printStackTrace();
-                    }
-                }
-            } else {
-                // If we've stopped the loader, then fall through to the above logic to wait on
-                // the load thread
-                processLoadQueueItem();
-
-                // If there are no other items in the list, then just wait until something is added
-                if (!mCancelled && mLoadQueue.isEmpty()) {
-                    synchronized(mLoadQueue) {
-                        try {
-                            mWaitingOnLoadQueue = true;
-                            mMainThreadHandler.post(new Runnable() {
-                                @Override
-                                public void run() {
-                                    mOnIdleChangedListener.onIdleChanged(true);
-                                }
-                            });
-                            mLoadQueue.wait();
-                            mMainThreadHandler.post(new Runnable() {
-                                @Override
-                                public void run() {
-                                    mOnIdleChangedListener.onIdleChanged(false);
-                                }
-                            });
-                            mWaitingOnLoadQueue = false;
-                        } catch (InterruptedException ie) {
-                            ie.printStackTrace();
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * This needs to be in a separate method to work around an surprising interpreter behavior:
-     * The register will keep the local reference to cachedThumbnailData even if it falls out of
-     * scope. Putting it into a method fixes this issue.
-     */
-    private void processLoadQueueItem() {
-        // Load the next item from the queue
-        final Task t = mLoadQueue.nextTask();
-        if (t != null) {
-            final Drawable icon = mIconLoader.getIcon(t);
-            if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
-            final ThumbnailData thumbnailData =
-                    ActivityManagerWrapper.getInstance().getTaskThumbnail(t.key.id,
-                            true /* reducedResolution */);
-
-            if (!mCancelled) {
-                // Notify that the task data has changed
-                mMainThreadHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        t.notifyTaskDataLoaded(thumbnailData, icon);
-                    }
-                });
-            }
-        }
-    }
-
-    interface OnIdleChangedListener {
-        void onIdleChanged(boolean idle);
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java
deleted file mode 100644
index 7e0f8fe..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.shared.recents.model;
-
-import android.util.ArrayMap;
-import android.util.SparseArray;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A list of filtered tasks.
- */
-@Deprecated
-class FilteredTaskList {
-
-    private final ArrayList<Task> mTasks = new ArrayList<>();
-    private final ArrayList<Task> mFilteredTasks = new ArrayList<>();
-    private final ArrayMap<TaskKey, Integer> mFilteredTaskIndices = new ArrayMap<>();
-    private TaskFilter mFilter;
-
-    /** Sets the task filter, and returns whether the set of filtered tasks have changed. */
-    boolean setFilter(TaskFilter filter) {
-        ArrayList<Task> prevFilteredTasks = new ArrayList<>(mFilteredTasks);
-        mFilter = filter;
-        updateFilteredTasks();
-        return !prevFilteredTasks.equals(mFilteredTasks);
-    }
-
-    /** Adds a new task to the task list */
-    void add(Task t) {
-        mTasks.add(t);
-        updateFilteredTasks();
-    }
-
-    /** Sets the list of tasks */
-    void set(List<Task> tasks) {
-        mTasks.clear();
-        mTasks.addAll(tasks);
-        updateFilteredTasks();
-    }
-
-    /** Removes a task from the base list only if it is in the filtered list */
-    boolean remove(Task t) {
-        if (mFilteredTasks.contains(t)) {
-            boolean removed = mTasks.remove(t);
-            updateFilteredTasks();
-            return removed;
-        }
-        return false;
-    }
-
-    /** Returns the index of this task in the list of filtered tasks */
-    int indexOf(Task t) {
-        if (t != null && mFilteredTaskIndices.containsKey(t.key)) {
-            return mFilteredTaskIndices.get(t.key);
-        }
-        return -1;
-    }
-
-    /** Returns the size of the list of filtered tasks */
-    int size() {
-        return mFilteredTasks.size();
-    }
-
-    /** Returns whether the filtered list contains this task */
-    boolean contains(Task t) {
-        return mFilteredTaskIndices.containsKey(t.key);
-    }
-
-    /** Updates the list of filtered tasks whenever the base task list changes */
-    private void updateFilteredTasks() {
-        mFilteredTasks.clear();
-        if (mFilter != null) {
-            // Create a sparse array from task id to Task
-            SparseArray<Task> taskIdMap = new SparseArray<>();
-            int taskCount = mTasks.size();
-            for (int i = 0; i < taskCount; i++) {
-                Task t = mTasks.get(i);
-                taskIdMap.put(t.key.id, t);
-            }
-
-            for (int i = 0; i < taskCount; i++) {
-                Task t = mTasks.get(i);
-                if (mFilter.acceptTask(taskIdMap, t, i)) {
-                    mFilteredTasks.add(t);
-                }
-            }
-        } else {
-            mFilteredTasks.addAll(mTasks);
-        }
-        updateFilteredTaskIndices();
-    }
-
-    /** Updates the mapping of tasks to indices. */
-    private void updateFilteredTaskIndices() {
-        int taskCount = mFilteredTasks.size();
-        mFilteredTaskIndices.clear();
-        for (int i = 0; i < taskCount; i++) {
-            Task t = mFilteredTasks.get(i);
-            mFilteredTaskIndices.put(t.key, i);
-        }
-    }
-
-    /** Returns the list of filtered tasks */
-    ArrayList<Task> getTasks() {
-        return mFilteredTasks;
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java
deleted file mode 100644
index f02bc5a..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.shared.recents.model;
-
-import static android.os.Process.setThreadPriority;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-
-/**
- * Loader class that loads full-resolution thumbnails when appropriate.
- */
-@Deprecated
-public class HighResThumbnailLoader implements
-        TaskCallbacks, BackgroundTaskLoader.OnIdleChangedListener {
-
-    private final ActivityManagerWrapper mActivityManager;
-
-    @GuardedBy("mLoadQueue")
-    private final ArrayDeque<Task> mLoadQueue = new ArrayDeque<>();
-    @GuardedBy("mLoadQueue")
-    private final ArraySet<Task> mLoadingTasks = new ArraySet<>();
-    @GuardedBy("mLoadQueue")
-    private boolean mLoaderIdling;
-
-    private final ArrayList<Task> mVisibleTasks = new ArrayList<>();
-
-    private final Thread mLoadThread;
-    private final Handler mMainThreadHandler;
-    private final boolean mIsLowRamDevice;
-    private boolean mLoading;
-    private boolean mVisible;
-    private boolean mFlingingFast;
-    private boolean mTaskLoadQueueIdle;
-
-    public HighResThumbnailLoader(ActivityManagerWrapper activityManager, Looper looper,
-            boolean isLowRamDevice) {
-        mActivityManager = activityManager;
-        mMainThreadHandler = new Handler(looper);
-        mLoadThread = new Thread(mLoader, "Recents-HighResThumbnailLoader");
-        mLoadThread.start();
-        mIsLowRamDevice = isLowRamDevice;
-    }
-
-    public void setVisible(boolean visible) {
-        if (mIsLowRamDevice) {
-            return;
-        }
-        mVisible = visible;
-        updateLoading();
-    }
-
-    public void setFlingingFast(boolean flingingFast) {
-        if (mFlingingFast == flingingFast || mIsLowRamDevice) {
-            return;
-        }
-        mFlingingFast = flingingFast;
-        updateLoading();
-    }
-
-    @Override
-    public void onIdleChanged(boolean idle) {
-        setTaskLoadQueueIdle(idle);
-    }
-
-    /**
-     * Sets whether the other task load queue is idling. Avoid double-loading bitmaps by not
-     * starting this queue until the other queue is idling.
-     */
-    public void setTaskLoadQueueIdle(boolean idle) {
-        if (mIsLowRamDevice) {
-            return;
-        }
-        mTaskLoadQueueIdle = idle;
-        updateLoading();
-    }
-
-    @VisibleForTesting
-    boolean isLoading() {
-        return mLoading;
-    }
-
-    private void updateLoading() {
-        setLoading(mVisible && !mFlingingFast && mTaskLoadQueueIdle);
-    }
-
-    private void setLoading(boolean loading) {
-        if (loading == mLoading) {
-            return;
-        }
-        synchronized (mLoadQueue) {
-            mLoading = loading;
-            if (!loading) {
-                stopLoading();
-            } else {
-                startLoading();
-            }
-        }
-    }
-
-    @GuardedBy("mLoadQueue")
-    private void startLoading() {
-        for (int i = mVisibleTasks.size() - 1; i >= 0; i--) {
-            Task t = mVisibleTasks.get(i);
-            if ((t.thumbnail == null || t.thumbnail.reducedResolution)
-                    && !mLoadQueue.contains(t) && !mLoadingTasks.contains(t)) {
-                mLoadQueue.add(t);
-            }
-        }
-        mLoadQueue.notifyAll();
-    }
-
-    @GuardedBy("mLoadQueue")
-    private void stopLoading() {
-        mLoadQueue.clear();
-        mLoadQueue.notifyAll();
-    }
-
-    /**
-     * Needs to be called when a task becomes visible. Note that this is different from
-     * {@link TaskCallbacks#onTaskDataLoaded} as this method should only be called once when it
-     * becomes visible, whereas onTaskDataLoaded can be called multiple times whenever some data
-     * has been updated.
-     */
-    public void onTaskVisible(Task t) {
-        t.addCallback(this);
-        mVisibleTasks.add(t);
-        if ((t.thumbnail == null || t.thumbnail.reducedResolution) && mLoading) {
-            synchronized (mLoadQueue) {
-                mLoadQueue.add(t);
-                mLoadQueue.notifyAll();
-            }
-        }
-    }
-
-    /**
-     * Needs to be called when a task becomes visible. See {@link #onTaskVisible} why this is
-     * different from {@link TaskCallbacks#onTaskDataUnloaded()}
-     */
-    public void onTaskInvisible(Task t) {
-        t.removeCallback(this);
-        mVisibleTasks.remove(t);
-        synchronized (mLoadQueue) {
-            mLoadQueue.remove(t);
-        }
-    }
-
-    @VisibleForTesting
-    void waitForLoaderIdle() {
-        while (true) {
-            synchronized (mLoadQueue) {
-                if (mLoadQueue.isEmpty() && mLoaderIdling) {
-                    return;
-                }
-            }
-            SystemClock.sleep(100);
-        }
-    }
-
-    @Override
-    public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
-        if (thumbnailData != null && !thumbnailData.reducedResolution) {
-            synchronized (mLoadQueue) {
-                mLoadQueue.remove(task);
-            }
-        }
-    }
-
-    @Override
-    public void onTaskDataUnloaded() {
-    }
-
-    @Override
-    public void onTaskWindowingModeChanged() {
-    }
-
-    private final Runnable mLoader = new Runnable() {
-
-        @Override
-        public void run() {
-            setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND + 1);
-            while (true) {
-                Task next = null;
-                synchronized (mLoadQueue) {
-                    if (!mLoading || mLoadQueue.isEmpty()) {
-                        try {
-                            mLoaderIdling = true;
-                            mLoadQueue.wait();
-                            mLoaderIdling = false;
-                        } catch (InterruptedException e) {
-                            // Don't care.
-                        }
-                    } else {
-                        next = mLoadQueue.poll();
-                        if (next != null) {
-                            mLoadingTasks.add(next);
-                        }
-                    }
-                }
-                if (next != null) {
-                    loadTask(next);
-                }
-            }
-        }
-
-        private void loadTask(final Task t) {
-            final ThumbnailData thumbnail = mActivityManager.getTaskThumbnail(t.key.id,
-                    false /* reducedResolution */);
-            mMainThreadHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    synchronized (mLoadQueue) {
-                        mLoadingTasks.remove(t);
-                    }
-                    if (mVisibleTasks.contains(t)) {
-                        t.notifyTaskDataLoaded(thumbnail, t.icon);
-                    }
-                }
-            });
-        }
-    };
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
deleted file mode 100644
index 8b3ae42..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.shared.recents.model;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.KeyguardManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.util.SparseBooleanArray;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-
-/**
- * This class stores the loading state as it goes through multiple stages of loading:
- *   1) preloadRawTasks() will load the raw set of recents tasks from the system
- *   2) preloadPlan() will construct a new task stack with all metadata and only icons and
- *      thumbnails that are currently in the cache
- *   3) executePlan() will actually load and fill in the icons and thumbnails according to the load
- *      options specified, such that we can transition into the Recents activity seamlessly
- */
-@Deprecated
-public class RecentsTaskLoadPlan {
-
-    /** The set of conditions to preload tasks. */
-    public static class PreloadOptions {
-        public boolean loadTitles = true;
-    }
-
-    /** The set of conditions to load tasks. */
-    public static class Options {
-        public int runningTaskId = -1;
-        public boolean loadIcons = true;
-        public boolean loadThumbnails = false;
-        public boolean onlyLoadForCache = false;
-        public boolean onlyLoadPausedActivities = false;
-        public int numVisibleTasks = 0;
-        public int numVisibleTaskThumbnails = 0;
-    }
-
-    private final Context mContext;
-    private final KeyguardManager mKeyguardManager;
-
-    private List<ActivityManager.RecentTaskInfo> mRawTasks;
-    private TaskStack mStack;
-
-    private final SparseBooleanArray mTmpLockedUsers = new SparseBooleanArray();
-
-    public RecentsTaskLoadPlan(Context context) {
-        mContext = context;
-        mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
-    }
-
-    /**
-     * Preloads the list of recent tasks from the system. After this call, the TaskStack will
-     * have a list of all the recent tasks with their metadata, not including icons or
-     * thumbnails which were not cached and have to be loaded.
-     *
-     * The tasks will be ordered by:
-     * - least-recent to most-recent stack tasks
-     *
-     * Note: Do not lock, since this can be calling back to the loader, which separately also drives
-     * this call (callers should synchronize on the loader before making this call).
-     */
-    public void preloadPlan(PreloadOptions opts, RecentsTaskLoader loader, int runningTaskId,
-            int currentUserId) {
-        Resources res = mContext.getResources();
-        ArrayList<Task> allTasks = new ArrayList<>();
-        if (mRawTasks == null) {
-            mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
-                    ActivityTaskManager.getMaxRecentTasksStatic(), currentUserId);
-
-            // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
-            Collections.reverse(mRawTasks);
-        }
-
-        int taskCount = mRawTasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
-
-            // Compose the task key
-            final ComponentName sourceComponent = t.origActivity != null
-                    // Activity alias if there is one
-                    ? t.origActivity
-                    // The real activity if there is no alias (or the target if there is one)
-                    : t.realActivity;
-            final int windowingMode = t.configuration.windowConfiguration.getWindowingMode();
-            TaskKey taskKey = new TaskKey(t.persistentId, windowingMode, t.baseIntent,
-                    sourceComponent, t.userId, t.lastActiveTime);
-
-            boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM;
-            boolean isStackTask = !isFreeformTask;
-            boolean isLaunchTarget = taskKey.id == runningTaskId;
-
-            ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
-            if (info == null) {
-                continue;
-            }
-
-            // Load the title, icon, and color
-            String title = opts.loadTitles
-                    ? loader.getAndUpdateActivityTitle(taskKey, t.taskDescription)
-                    : "";
-            String titleDescription = opts.loadTitles
-                    ? loader.getAndUpdateContentDescription(taskKey, t.taskDescription)
-                    : "";
-            Drawable icon = isStackTask
-                    ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, false)
-                    : null;
-            ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
-                    false /* loadIfNotCached */, false /* storeInCache */);
-            int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
-            int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
-            boolean isSystemApp = (info != null) &&
-                    ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
-
-            // TODO: Refactor to not do this every preload
-            if (mTmpLockedUsers.indexOfKey(t.userId) < 0) {
-                mTmpLockedUsers.put(t.userId, mKeyguardManager.isDeviceLocked(t.userId));
-            }
-            boolean isLocked = mTmpLockedUsers.get(t.userId);
-
-            // Add the task to the stack
-            Task task = new Task(taskKey, icon,
-                    thumbnail, title, titleDescription, activityColor, backgroundColor,
-                    isLaunchTarget, isStackTask, isSystemApp, t.supportsSplitScreenMultiWindow,
-                    t.taskDescription, t.resizeMode, t.topActivity, isLocked);
-
-            allTasks.add(task);
-        }
-
-        // Initialize the stacks
-        mStack = new TaskStack();
-        mStack.setTasks(allTasks, false /* notifyStackChanges */);
-    }
-
-    /**
-     * Called to apply the actual loading based on the specified conditions.
-     *
-     * Note: Do not lock, since this can be calling back to the loader, which separately also drives
-     * this call (callers should synchronize on the loader before making this call).
-     */
-    public void executePlan(Options opts, RecentsTaskLoader loader) {
-        Resources res = mContext.getResources();
-
-        // Iterate through each of the tasks and load them according to the load conditions.
-        ArrayList<Task> tasks = mStack.getTasks();
-        int taskCount = tasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            Task task = tasks.get(i);
-            TaskKey taskKey = task.key;
-
-            boolean isRunningTask = (task.key.id == opts.runningTaskId);
-            boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
-            boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
-
-            // If requested, skip the running task
-            if (opts.onlyLoadPausedActivities && isRunningTask) {
-                continue;
-            }
-
-            if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
-                if (task.icon == null) {
-                    task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
-                            true);
-                }
-            }
-            if (opts.loadThumbnails && isVisibleThumbnail) {
-                task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
-                        true /* loadIfNotCached */, true /* storeInCache */);
-            }
-        }
-    }
-
-    /**
-     * Returns the TaskStack from the preloaded list of recent tasks.
-     */
-    public TaskStack getTaskStack() {
-        return mStack;
-    }
-
-    /** Returns whether there are any tasks in any stacks. */
-    public boolean hasTasks() {
-        if (mStack != null) {
-            return mStack.getTaskCount() > 0;
-        }
-        return false;
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
deleted file mode 100644
index b50aa76..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.shared.recents.model;
-
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.content.ComponentCallbacks2;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.graphics.drawable.Drawable;
-import android.os.Looper;
-import android.os.Trace;
-import android.util.Log;
-import android.util.LruCache;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.Options;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.model.TaskKeyLruCache.EvictionCallback;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.io.PrintWriter;
-import java.util.Map;
-
-
-/**
- * Recents task loader
- */
-@Deprecated
-public class RecentsTaskLoader {
-    private static final String TAG = "RecentsTaskLoader";
-    private static final boolean DEBUG = false;
-
-    /** Levels of svelte in increasing severity/austerity. */
-    // No svelting.
-    public static final int SVELTE_NONE = 0;
-    // Limit thumbnail cache to number of visible thumbnails when Recents was loaded, disable
-    // caching thumbnails as you scroll.
-    public static final int SVELTE_LIMIT_CACHE = 1;
-    // Disable the thumbnail cache, load thumbnails asynchronously when the activity loads and
-    // evict all thumbnails when hidden.
-    public static final int SVELTE_DISABLE_CACHE = 2;
-    // Disable all thumbnail loading.
-    public static final int SVELTE_DISABLE_LOADING = 3;
-
-    // This activity info LruCache is useful because it can be expensive to retrieve ActivityInfos
-    // for many tasks, which we use to get the activity labels and icons.  Unlike the other caches
-    // below, this is per-package so we can't invalidate the items in the cache based on the last
-    // active time.  Instead, we rely on the PackageMonitor to keep us informed whenever a
-    // package in the cache has been updated, so that we may remove it.
-    private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
-    private final TaskKeyLruCache<Drawable> mIconCache;
-    private final TaskKeyLruCache<String> mActivityLabelCache;
-    private final TaskKeyLruCache<String> mContentDescriptionCache;
-    private final TaskResourceLoadQueue mLoadQueue;
-    private final IconLoader mIconLoader;
-    private final BackgroundTaskLoader mLoader;
-    private final HighResThumbnailLoader mHighResThumbnailLoader;
-    @GuardedBy("this")
-    private final TaskKeyStrongCache<ThumbnailData> mThumbnailCache = new TaskKeyStrongCache<>();
-    @GuardedBy("this")
-    private final TaskKeyStrongCache<ThumbnailData> mTempCache = new TaskKeyStrongCache<>();
-    private final int mMaxThumbnailCacheSize;
-    private final int mMaxIconCacheSize;
-    private int mNumVisibleTasksLoaded;
-    private int mSvelteLevel;
-
-    private int mDefaultTaskBarBackgroundColor;
-    private int mDefaultTaskViewBackgroundColor;
-
-    private EvictionCallback mClearActivityInfoOnEviction = new EvictionCallback() {
-        @Override
-        public void onEntryEvicted(TaskKey key) {
-            if (key != null) {
-                mActivityInfoCache.remove(key.getComponent());
-            }
-        }
-    };
-
-    public RecentsTaskLoader(Context context, int maxThumbnailCacheSize, int maxIconCacheSize,
-            int svelteLevel) {
-        mMaxThumbnailCacheSize = maxThumbnailCacheSize;
-        mMaxIconCacheSize = maxIconCacheSize;
-        mSvelteLevel = svelteLevel;
-
-        // Initialize the proxy, cache and loaders
-        int numRecentTasks = ActivityTaskManager.getMaxRecentTasksStatic();
-        mHighResThumbnailLoader = new HighResThumbnailLoader(ActivityManagerWrapper.getInstance(),
-                Looper.getMainLooper(), ActivityManager.isLowRamDeviceStatic());
-        mLoadQueue = new TaskResourceLoadQueue();
-        mIconCache = new TaskKeyLruCache<>(mMaxIconCacheSize, mClearActivityInfoOnEviction);
-        mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks, mClearActivityInfoOnEviction);
-        mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks,
-                mClearActivityInfoOnEviction);
-        mActivityInfoCache = new LruCache<>(numRecentTasks);
-
-        mIconLoader = createNewIconLoader(context, mIconCache, mActivityInfoCache);
-        mLoader = new BackgroundTaskLoader(mLoadQueue, mIconLoader, mHighResThumbnailLoader);
-    }
-
-    protected IconLoader createNewIconLoader(Context context,TaskKeyLruCache<Drawable> iconCache,
-            LruCache<ComponentName, ActivityInfo> activityInfoCache) {
-        return new IconLoader.DefaultIconLoader(context, iconCache, activityInfoCache);
-    }
-
-    /**
-     * Sets the default task bar/view colors if none are provided by the app.
-     */
-    public void setDefaultColors(int defaultTaskBarBackgroundColor,
-            int defaultTaskViewBackgroundColor) {
-        mDefaultTaskBarBackgroundColor = defaultTaskBarBackgroundColor;
-        mDefaultTaskViewBackgroundColor = defaultTaskViewBackgroundColor;
-    }
-
-    /** Returns the size of the app icon cache. */
-    public int getIconCacheSize() {
-        return mMaxIconCacheSize;
-    }
-
-    /** Returns the size of the thumbnail cache. */
-    public int getThumbnailCacheSize() {
-        return mMaxThumbnailCacheSize;
-    }
-
-    public HighResThumbnailLoader getHighResThumbnailLoader() {
-        return mHighResThumbnailLoader;
-    }
-
-    /** Preloads recents tasks using the specified plan to store the output. */
-    public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId) {
-        preloadTasks(plan, runningTaskId, ActivityManagerWrapper.getInstance().getCurrentUserId());
-    }
-
-    /** Preloads recents tasks using the specified plan to store the output. */
-    public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId,
-            int currentUserId) {
-        try {
-            Trace.beginSection("preloadPlan");
-            plan.preloadPlan(new PreloadOptions(), this, runningTaskId, currentUserId);
-        } finally {
-            Trace.endSection();
-        }
-    }
-
-    /** Begins loading the heavy task data according to the specified options. */
-    public synchronized void loadTasks(RecentsTaskLoadPlan plan, Options opts) {
-        if (opts == null) {
-            throw new RuntimeException("Requires load options");
-        }
-        if (opts.onlyLoadForCache && opts.loadThumbnails) {
-            // If we are loading for the cache, we'd like to have the real cache only include the
-            // visible thumbnails. However, we also don't want to reload already cached thumbnails.
-            // Thus, we copy over the current entries into a second cache, and clear the real cache,
-            // such that the real cache only contains visible thumbnails.
-            mTempCache.copyEntries(mThumbnailCache);
-            mThumbnailCache.evictAll();
-        }
-        plan.executePlan(opts, this);
-        mTempCache.evictAll();
-        if (!opts.onlyLoadForCache) {
-            mNumVisibleTasksLoaded = opts.numVisibleTasks;
-        }
-    }
-
-    /**
-     * Acquires the task resource data directly from the cache, loading if necessary.
-     */
-    public void loadTaskData(Task t) {
-        Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
-        icon = icon != null ? icon : mIconLoader.getDefaultIcon(t.key.userId);
-        mLoadQueue.addTask(t);
-        t.notifyTaskDataLoaded(t.thumbnail, icon);
-    }
-
-    /** Releases the task resource data back into the pool. */
-    public void unloadTaskData(Task t) {
-        mLoadQueue.removeTask(t);
-        t.notifyTaskDataUnloaded(mIconLoader.getDefaultIcon(t.key.userId));
-    }
-
-    /** Completely removes the resource data from the pool. */
-    public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
-        mLoadQueue.removeTask(t);
-        mIconCache.remove(t.key);
-        mActivityLabelCache.remove(t.key);
-        mContentDescriptionCache.remove(t.key);
-        if (notifyTaskDataUnloaded) {
-            t.notifyTaskDataUnloaded(mIconLoader.getDefaultIcon(t.key.userId));
-        }
-    }
-
-    /**
-     * Handles signals from the system, trimming memory when requested to prevent us from running
-     * out of memory.
-     */
-    public synchronized void onTrimMemory(int level) {
-        switch (level) {
-            case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
-                // Stop the loader immediately when the UI is no longer visible
-                stopLoader();
-                mIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
-                        mMaxIconCacheSize / 2));
-                break;
-            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
-            case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
-                // We are leaving recents, so trim the data a bit
-                mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
-                mActivityInfoCache.trimToSize(Math.max(1,
-                        ActivityTaskManager.getMaxRecentTasksStatic() / 2));
-                break;
-            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
-            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
-                // We are going to be low on memory
-                mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
-                mActivityInfoCache.trimToSize(Math.max(1,
-                        ActivityTaskManager.getMaxRecentTasksStatic() / 4));
-                break;
-            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
-            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
-                // We are low on memory, so release everything
-                mIconCache.evictAll();
-                mActivityInfoCache.evictAll();
-                // The cache is small, only clear the label cache when we are critical
-                mActivityLabelCache.evictAll();
-                mContentDescriptionCache.evictAll();
-                mThumbnailCache.evictAll();
-                break;
-            default:
-                break;
-        }
-    }
-
-    public void onPackageChanged(String packageName) {
-        // Remove all the cached activity infos for this package.  The other caches do not need to
-        // be pruned at this time, as the TaskKey expiration checks will flush them next time their
-        // cached contents are requested
-        Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
-        for (ComponentName cn : activityInfoCache.keySet()) {
-            if (cn.getPackageName().equals(packageName)) {
-                if (DEBUG) {
-                    Log.d(TAG, "Removing activity info from cache: " + cn);
-                }
-                mActivityInfoCache.remove(cn);
-            }
-        }
-    }
-
-    /**
-     * Returns the cached task label if the task key is not expired, updating the cache if it is.
-     */
-    String getAndUpdateActivityTitle(TaskKey taskKey, ActivityManager.TaskDescription td) {
-        // Return the task description label if it exists
-        if (td != null && td.getLabel() != null) {
-            return td.getLabel();
-        }
-        // Return the cached activity label if it exists
-        String label = mActivityLabelCache.getAndInvalidateIfModified(taskKey);
-        if (label != null) {
-            return label;
-        }
-        // All short paths failed, load the label from the activity info and cache it
-        ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
-        if (activityInfo != null) {
-            label = ActivityManagerWrapper.getInstance().getBadgedActivityLabel(activityInfo,
-                    taskKey.userId);
-            mActivityLabelCache.put(taskKey, label);
-            return label;
-        }
-        // If the activity info does not exist or fails to load, return an empty label for now,
-        // but do not cache it
-        return "";
-    }
-
-    /**
-     * Returns the cached task content description if the task key is not expired, updating the
-     * cache if it is.
-     */
-    String getAndUpdateContentDescription(TaskKey taskKey, ActivityManager.TaskDescription td) {
-        // Return the cached content description if it exists
-        String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
-        if (label != null) {
-            return label;
-        }
-
-        // All short paths failed, load the label from the activity info and cache it
-        ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
-        if (activityInfo != null) {
-            label = ActivityManagerWrapper.getInstance().getBadgedContentDescription(
-                    activityInfo, taskKey.userId, td);
-            if (td == null) {
-                // Only add to the cache if the task description is null, otherwise, it is possible
-                // for the task description to change between calls without the last active time
-                // changing (ie. between preloading and Overview starting) which would lead to stale
-                // content descriptions
-                // TODO: Investigate improving this
-                mContentDescriptionCache.put(taskKey, label);
-            }
-            return label;
-        }
-        // If the content description does not exist, return an empty label for now, but do not
-        // cache it
-        return "";
-    }
-
-    /**
-     * Returns the cached task icon if the task key is not expired, updating the cache if it is.
-     */
-    Drawable getAndUpdateActivityIcon(TaskKey taskKey, ActivityManager.TaskDescription td,
-            boolean loadIfNotCached) {
-        return mIconLoader.getAndInvalidateIfModified(taskKey, td, loadIfNotCached);
-    }
-
-    /**
-     * Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
-     */
-    synchronized ThumbnailData getAndUpdateThumbnail(TaskKey taskKey, boolean loadIfNotCached,
-            boolean storeInCache) {
-        ThumbnailData cached = mThumbnailCache.getAndInvalidateIfModified(taskKey);
-        if (cached != null) {
-            return cached;
-        }
-
-        cached = mTempCache.getAndInvalidateIfModified(taskKey);
-        if (cached != null) {
-            mThumbnailCache.put(taskKey, cached);
-            return cached;
-        }
-
-        if (loadIfNotCached) {
-            if (mSvelteLevel < SVELTE_DISABLE_LOADING) {
-                // Load the thumbnail from the system
-                ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance().getTaskThumbnail(
-                        taskKey.id, true /* reducedResolution */);
-                if (thumbnailData.thumbnail != null) {
-                    if (storeInCache) {
-                        mThumbnailCache.put(taskKey, thumbnailData);
-                    }
-                    return thumbnailData;
-                }
-            }
-        }
-
-        // We couldn't load any thumbnail
-        return null;
-    }
-
-    /**
-     * Returns the task's primary color if possible, defaulting to the default color if there is
-     * no specified primary color.
-     */
-    int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
-        if (td != null && td.getPrimaryColor() != 0) {
-            return td.getPrimaryColor();
-        }
-        return mDefaultTaskBarBackgroundColor;
-    }
-
-    /**
-     * Returns the task's background color if possible.
-     */
-    int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
-        if (td != null && td.getBackgroundColor() != 0) {
-            return td.getBackgroundColor();
-        }
-        return mDefaultTaskViewBackgroundColor;
-    }
-
-    /**
-     * Returns the activity info for the given task key, retrieving one from the system if the
-     * task key is expired.
-     */
-    ActivityInfo getAndUpdateActivityInfo(TaskKey taskKey) {
-        return mIconLoader.getAndUpdateActivityInfo(taskKey);
-    }
-
-    /**
-     * Starts loading tasks.
-     */
-    public void startLoader(Context ctx) {
-        mLoader.start(ctx);
-    }
-
-    /**
-     * Stops the task loader and clears all queued, pending task loads.
-     */
-    private void stopLoader() {
-        mLoader.stop();
-        mLoadQueue.clearTasks();
-    }
-
-    public synchronized void dump(String prefix, PrintWriter writer) {
-        String innerPrefix = prefix + "  ";
-
-        writer.print(prefix); writer.println(TAG);
-        writer.print(prefix); writer.println("Icon Cache");
-        mIconCache.dump(innerPrefix, writer);
-        writer.print(prefix); writer.println("Thumbnail Cache");
-        mThumbnailCache.dump(innerPrefix, writer);
-        writer.print(prefix); writer.println("Temp Thumbnail Cache");
-        mTempCache.dump(innerPrefix, writer);
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 368e503..7558efa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -143,7 +143,7 @@
      * The temporary sort index in the stack, used when ordering the stack.
      */
     @Deprecated
-    int temporarySortIndexInStack;
+    public int temporarySortIndexInStack;
 
     /**
      * The icon is the task description icon (if provided), which falls back to the activity icon,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java
deleted file mode 100644
index d378189..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.shared.recents.model;
-
-import android.util.SparseArray;
-
-/**
- * An interface for a task filter to query whether a particular task should show in a stack.
- */
-@Deprecated
-public interface TaskFilter {
-    /** Returns whether the filter accepts the specified task */
-    boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index);
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java
index 0ba2c3b..b2c79a4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java
@@ -58,7 +58,7 @@
     }
 
     /** Trims the cache to a specific size */
-    final void trimToSize(int cacheSize) {
+    public final void trimToSize(int cacheSize) {
         mCache.trimToSize(cacheSize);
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyStrongCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyStrongCache.java
deleted file mode 100644
index 4408ece..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyStrongCache.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.shared.recents.model;
-
-import android.util.ArrayMap;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-
-import java.io.PrintWriter;
-
-/**
- * Like {@link TaskKeyLruCache}, but without LRU functionality.
- */
-public class TaskKeyStrongCache<V> extends TaskKeyCache<V> {
-
-    private static final String TAG = "TaskKeyCache";
-
-    private final ArrayMap<Integer, V> mCache = new ArrayMap<>();
-
-    final void copyEntries(TaskKeyStrongCache<V> other) {
-        for (int i = other.mKeys.size() - 1; i >= 0; i--) {
-            TaskKey key = other.mKeys.valueAt(i);
-            put(key, other.mCache.get(key.id));
-        }
-    }
-
-    public void dump(String prefix, PrintWriter writer) {
-        String innerPrefix = prefix + "  ";
-        writer.print(prefix); writer.print(TAG);
-        writer.print(" numEntries="); writer.print(mKeys.size());
-        writer.println();
-        int keyCount = mKeys.size();
-        for (int i = 0; i < keyCount; i++) {
-            writer.print(innerPrefix); writer.println(mKeys.get(mKeys.keyAt(i)));
-        }
-    }
-
-    @Override
-    protected V getCacheEntry(int id) {
-        return mCache.get(id);
-    }
-
-    @Override
-    protected void putCacheEntry(int id, V value) {
-        mCache.put(id, value);
-    }
-
-    @Override
-    protected void removeCacheEntry(int id) {
-        mCache.remove(id);
-    }
-
-    @Override
-    protected void evictAllCache() {
-        mCache.clear();
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java
deleted file mode 100644
index 6b9b9f5..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.shared.recents.model;
-
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-/**
- * A Task load queue
- */
-@Deprecated
-class TaskResourceLoadQueue {
-
-    private final ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<>();
-
-    /** Adds a new task to the load queue */
-    void addTask(Task t) {
-        if (!mQueue.contains(t)) {
-            mQueue.add(t);
-        }
-        synchronized(this) {
-            notifyAll();
-        }
-    }
-
-    /**
-     * Retrieves the next task from the load queue, as well as whether we want that task to be
-     * force reloaded.
-     */
-    Task nextTask() {
-        return mQueue.poll();
-    }
-
-    /** Removes a task from the load queue */
-    void removeTask(Task t) {
-        mQueue.remove(t);
-    }
-
-    /** Clears all the tasks from the load queue */
-    void clearTasks() {
-        mQueue.clear();
-    }
-
-    /** Returns whether the load queue is empty */
-    boolean isEmpty() {
-        return mQueue.isEmpty();
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java
deleted file mode 100644
index fd92bca..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.shared.recents.model;
-
-import android.content.ComponentName;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.SparseArray;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-
-/**
- * The task stack contains a list of multiple tasks.
- */
-@Deprecated
-public class TaskStack {
-
-    private static final String TAG = "TaskStack";
-
-    /** Task stack callbacks */
-    public interface TaskStackCallbacks {
-        /**
-         * Notifies when a new task has been added to the stack.
-         */
-        void onStackTaskAdded(TaskStack stack, Task newTask);
-
-        /**
-         * Notifies when a task has been removed from the stack.
-         */
-        void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
-                AnimationProps animation, boolean fromDockGesture,
-                boolean dismissRecentsIfAllRemoved);
-
-        /**
-         * Notifies when all tasks have been removed from the stack.
-         */
-        void onStackTasksRemoved(TaskStack stack);
-
-        /**
-         * Notifies when tasks in the stack have been updated.
-         */
-        void onStackTasksUpdated(TaskStack stack);
-    }
-
-    private final ArrayList<Task> mRawTaskList = new ArrayList<>();
-    private final FilteredTaskList mStackTaskList = new FilteredTaskList();
-    private TaskStackCallbacks mCb;
-
-    public TaskStack() {
-        // Ensure that we only show stack tasks
-        mStackTaskList.setFilter(new TaskFilter() {
-            @Override
-            public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
-                return  t.isStackTask;
-            }
-        });
-    }
-
-    /** Sets the callbacks for this task stack. */
-    public void setCallbacks(TaskStackCallbacks cb) {
-        mCb = cb;
-    }
-
-    /**
-     * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
-     * how they should update themselves.
-     */
-    public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) {
-        removeTask(t, animation, fromDockGesture, true /* dismissRecentsIfAllRemoved */);
-    }
-
-    /**
-     * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
-     * how they should update themselves.
-     */
-    public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture,
-            boolean dismissRecentsIfAllRemoved) {
-        if (mStackTaskList.contains(t)) {
-            mStackTaskList.remove(t);
-            Task newFrontMostTask = getFrontMostTask();
-            if (mCb != null) {
-                // Notify that a task has been removed
-                mCb.onStackTaskRemoved(this, t, newFrontMostTask, animation,
-                        fromDockGesture, dismissRecentsIfAllRemoved);
-            }
-        }
-        mRawTaskList.remove(t);
-    }
-
-    /**
-     * Removes all tasks from the stack.
-     */
-    public void removeAllTasks(boolean notifyStackChanges) {
-        ArrayList<Task> tasks = mStackTaskList.getTasks();
-        for (int i = tasks.size() - 1; i >= 0; i--) {
-            Task t = tasks.get(i);
-            mStackTaskList.remove(t);
-            mRawTaskList.remove(t);
-        }
-        if (mCb != null && notifyStackChanges) {
-            // Notify that all tasks have been removed
-            mCb.onStackTasksRemoved(this);
-        }
-    }
-
-
-    /**
-     * @see #setTasks(List, boolean)
-     */
-    public void setTasks(TaskStack stack, boolean notifyStackChanges) {
-        setTasks(stack.mRawTaskList, notifyStackChanges);
-    }
-
-    /**
-     * Sets a few tasks in one go, without calling any callbacks.
-     *
-     * @param tasks the new set of tasks to replace the current set.
-     * @param notifyStackChanges whether or not to callback on specific changes to the list of tasks.
-     */
-    public void setTasks(List<Task> tasks, boolean notifyStackChanges) {
-        // Compute a has set for each of the tasks
-        ArrayMap<TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
-        ArrayMap<TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
-        ArrayList<Task> addedTasks = new ArrayList<>();
-        ArrayList<Task> removedTasks = new ArrayList<>();
-        ArrayList<Task> allTasks = new ArrayList<>();
-
-        // Disable notifications if there are no callbacks
-        if (mCb == null) {
-            notifyStackChanges = false;
-        }
-
-        // Remove any tasks that no longer exist
-        int taskCount = mRawTaskList.size();
-        for (int i = taskCount - 1; i >= 0; i--) {
-            Task task = mRawTaskList.get(i);
-            if (!newTasksMap.containsKey(task.key)) {
-                if (notifyStackChanges) {
-                    removedTasks.add(task);
-                }
-            }
-        }
-
-        // Add any new tasks
-        taskCount = tasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            Task newTask = tasks.get(i);
-            Task currentTask = currentTasksMap.get(newTask.key);
-            if (currentTask == null && notifyStackChanges) {
-                addedTasks.add(newTask);
-            } else if (currentTask != null) {
-                // The current task has bound callbacks, so just copy the data from the new task
-                // state and add it back into the list
-                currentTask.copyFrom(newTask);
-                newTask = currentTask;
-            }
-            allTasks.add(newTask);
-        }
-
-        // Sort all the tasks to ensure they are ordered correctly
-        for (int i = allTasks.size() - 1; i >= 0; i--) {
-            allTasks.get(i).temporarySortIndexInStack = i;
-        }
-
-        mStackTaskList.set(allTasks);
-        mRawTaskList.clear();
-        mRawTaskList.addAll(allTasks);
-
-        // Only callback for the removed tasks after the stack has updated
-        int removedTaskCount = removedTasks.size();
-        Task newFrontMostTask = getFrontMostTask();
-        for (int i = 0; i < removedTaskCount; i++) {
-            mCb.onStackTaskRemoved(this, removedTasks.get(i), newFrontMostTask,
-                    AnimationProps.IMMEDIATE, false /* fromDockGesture */,
-                    true /* dismissRecentsIfAllRemoved */);
-        }
-
-        // Only callback for the newly added tasks after this stack has been updated
-        int addedTaskCount = addedTasks.size();
-        for (int i = 0; i < addedTaskCount; i++) {
-            mCb.onStackTaskAdded(this, addedTasks.get(i));
-        }
-
-        // Notify that the task stack has been updated
-        if (notifyStackChanges) {
-            mCb.onStackTasksUpdated(this);
-        }
-    }
-
-    /**
-     * Gets the front-most task in the stack.
-     */
-    public Task getFrontMostTask() {
-        ArrayList<Task> stackTasks = mStackTaskList.getTasks();
-        if (stackTasks.isEmpty()) {
-            return null;
-        }
-        return stackTasks.get(stackTasks.size() - 1);
-    }
-
-    /** Gets the task keys */
-    public ArrayList<TaskKey> getTaskKeys() {
-        ArrayList<TaskKey> taskKeys = new ArrayList<>();
-        ArrayList<Task> tasks = computeAllTasksList();
-        int taskCount = tasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            Task task = tasks.get(i);
-            taskKeys.add(task.key);
-        }
-        return taskKeys;
-    }
-
-    /**
-     * Returns the set of "active" (non-historical) tasks in the stack that have been used recently.
-     */
-    public ArrayList<Task> getTasks() {
-        return mStackTaskList.getTasks();
-    }
-
-    /**
-     * Computes a set of all the active and historical tasks.
-     */
-    public ArrayList<Task> computeAllTasksList() {
-        ArrayList<Task> tasks = new ArrayList<>();
-        tasks.addAll(mStackTaskList.getTasks());
-        return tasks;
-    }
-
-    /**
-     * Returns the number of stack tasks.
-     */
-    public int getTaskCount() {
-        return mStackTaskList.size();
-    }
-
-    /**
-     * Returns the task in stack tasks which is the launch target.
-     */
-    public Task getLaunchTarget() {
-        ArrayList<Task> tasks = mStackTaskList.getTasks();
-        int taskCount = tasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            Task task = tasks.get(i);
-            if (task.isLaunchTarget) {
-                return task;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns whether the next launch target should actually be the PiP task.
-     */
-    public boolean isNextLaunchTargetPip(long lastPipTime) {
-        Task launchTarget = getLaunchTarget();
-        Task nextLaunchTarget = getNextLaunchTargetRaw();
-        if (nextLaunchTarget != null && lastPipTime > 0) {
-            // If the PiP time is more recent than the next launch target, then launch the PiP task
-            return lastPipTime > nextLaunchTarget.key.lastActiveTime;
-        } else if (launchTarget != null && lastPipTime > 0 && getTaskCount() == 1) {
-            // Otherwise, if there is no next launch target, but there is a PiP, then launch
-            // the PiP task
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns the task in stack tasks which should be launched next if Recents are toggled
-     * again, or null if there is no task to be launched. Callers should check
-     * {@link #isNextLaunchTargetPip(long)} before fetching the next raw launch target from the
-     * stack.
-     */
-    public Task getNextLaunchTarget() {
-        Task nextLaunchTarget = getNextLaunchTargetRaw();
-        if (nextLaunchTarget != null) {
-            return nextLaunchTarget;
-        }
-        return getTasks().get(getTaskCount() - 1);
-    }
-
-    private Task getNextLaunchTargetRaw() {
-        int taskCount = getTaskCount();
-        if (taskCount == 0) {
-            return null;
-        }
-        int launchTaskIndex = indexOfTask(getLaunchTarget());
-        if (launchTaskIndex != -1 && launchTaskIndex > 0) {
-            return getTasks().get(launchTaskIndex - 1);
-        }
-        return null;
-    }
-
-    /** Returns the index of this task in this current task stack */
-    public int indexOfTask(Task t) {
-        return mStackTaskList.indexOf(t);
-    }
-
-    /** Finds the task with the specified task id. */
-    public Task findTaskWithId(int taskId) {
-        ArrayList<Task> tasks = computeAllTasksList();
-        int taskCount = tasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            Task task = tasks.get(i);
-            if (task.key.id == taskId) {
-                return task;
-            }
-        }
-        return null;
-    }
-    
-    /**
-     * Computes the components of tasks in this stack that have been removed as a result of a change
-     * in the specified package.
-     */
-    public ArraySet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
-        // Identify all the tasks that should be removed as a result of the package being removed.
-        // Using a set to ensure that we callback once per unique component.
-        ArraySet<ComponentName> existingComponents = new ArraySet<>();
-        ArraySet<ComponentName> removedComponents = new ArraySet<>();
-        ArrayList<TaskKey> taskKeys = getTaskKeys();
-        int taskKeyCount = taskKeys.size();
-        for (int i = 0; i < taskKeyCount; i++) {
-            TaskKey t = taskKeys.get(i);
-
-            // Skip if this doesn't apply to the current user
-            if (t.userId != userId) continue;
-
-            ComponentName cn = t.getComponent();
-            if (cn.getPackageName().equals(packageName)) {
-                if (existingComponents.contains(cn)) {
-                    // If we know that the component still exists in the package, then skip
-                    continue;
-                }
-                if (PackageManagerWrapper.getInstance().getActivityInfo(cn, userId) != null) {
-                    existingComponents.add(cn);
-                } else {
-                    removedComponents.add(cn);
-                }
-            }
-        }
-        return removedComponents;
-    }
-
-    @Override
-    public String toString() {
-        String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
-        ArrayList<Task> tasks = mStackTaskList.getTasks();
-        int taskCount = tasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            str += "    " + tasks.get(i).toString() + "\n";
-        }
-        return str;
-    }
-
-    /**
-     * Given a list of tasks, returns a map of each task's key to the task.
-     */
-    private ArrayMap<TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
-        ArrayMap<TaskKey, Task> map = new ArrayMap<>(tasks.size());
-        int taskCount = tasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            Task task = tasks.get(i);
-            map.put(task.key, task);
-        }
-        return map;
-    }
-
-    public void dump(String prefix, PrintWriter writer) {
-        String innerPrefix = prefix + "  ";
-
-        writer.print(prefix); writer.print(TAG);
-        writer.print(" numStackTasks="); writer.print(mStackTaskList.size());
-        writer.println();
-        ArrayList<Task> tasks = mStackTaskList.getTasks();
-        int taskCount = tasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            tasks.get(i).dump(innerPrefix, writer);
-        }
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java
deleted file mode 100644
index 26f6b59..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2016 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.systemui.shared.recents.utilities;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.annotation.IntDef;
-import android.util.SparseArray;
-import android.util.SparseLongArray;
-import android.view.View;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-
-/**
- * The generic set of animation properties to animate a {@link View}. The animation can have
- * different interpolators, start delays and durations for each of the different properties.
- */
-@Deprecated
-public class AnimationProps {
-
-    private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
-    public static final AnimationProps IMMEDIATE = new AnimationProps(0, LINEAR_INTERPOLATOR);
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({ALL, TRANSLATION_X, TRANSLATION_Y, TRANSLATION_Z, ALPHA, SCALE, BOUNDS})
-    public @interface PropType {}
-
-    public static final int ALL = 0;
-    public static final int TRANSLATION_X = 1;
-    public static final int TRANSLATION_Y = 2;
-    public static final int TRANSLATION_Z = 3;
-    public static final int ALPHA = 4;
-    public static final int SCALE = 5;
-    public static final int BOUNDS = 6;
-    public static final int DIM_ALPHA = 7;
-
-    private SparseLongArray mPropStartDelay;
-    private SparseLongArray mPropDuration;
-    private SparseArray<Interpolator> mPropInterpolators;
-    private Animator.AnimatorListener mListener;
-
-    /**
-     * The builder constructor.
-     */
-    public AnimationProps() {}
-
-    /**
-     * Creates an animation with a default {@param duration} and {@param interpolator} for all
-     * properties in this animation.
-     */
-    public AnimationProps(int duration, Interpolator interpolator) {
-        this(0, duration, interpolator, null);
-    }
-
-    /**
-     * Creates an animation with a default {@param duration} and {@param interpolator} for all
-     * properties in this animation.
-     */
-    public AnimationProps(int duration, Interpolator interpolator,
-            Animator.AnimatorListener listener) {
-        this(0, duration, interpolator, listener);
-    }
-
-    /**
-     * Creates an animation with a default {@param startDelay}, {@param duration} and
-     * {@param interpolator} for all properties in this animation.
-     */
-    public AnimationProps(int startDelay, int duration, Interpolator interpolator) {
-        this(startDelay, duration, interpolator, null);
-    }
-
-    /**
-     * Creates an animation with a default {@param startDelay}, {@param duration} and
-     * {@param interpolator} for all properties in this animation.
-     */
-    public AnimationProps(int startDelay, int duration, Interpolator interpolator,
-            Animator.AnimatorListener listener) {
-        setStartDelay(ALL, startDelay);
-        setDuration(ALL, duration);
-        setInterpolator(ALL, interpolator);
-        setListener(listener);
-    }
-
-    /**
-     * Creates a new {@link AnimatorSet} that will animate the given animators.  Callers need to
-     * manually apply the individual animation properties for each of the animators respectively.
-     */
-    public AnimatorSet createAnimator(List<Animator> animators) {
-        AnimatorSet anim = new AnimatorSet();
-        if (mListener != null) {
-            anim.addListener(mListener);
-        }
-        anim.playTogether(animators);
-        return anim;
-    }
-
-    /**
-     * Applies the specific start delay, duration and interpolator to the given {@param animator}
-     * for the specified {@param propertyType}.
-     */
-    public <T extends ValueAnimator> T apply(@PropType int propertyType, T animator) {
-        animator.setStartDelay(getStartDelay(propertyType));
-        animator.setDuration(getDuration(propertyType));
-        animator.setInterpolator(getInterpolator(propertyType));
-        return animator;
-    }
-
-    /**
-     * Sets a start delay for a specific property.
-     */
-    public AnimationProps setStartDelay(@PropType int propertyType, int startDelay) {
-        if (mPropStartDelay == null) {
-            mPropStartDelay = new SparseLongArray();
-        }
-        mPropStartDelay.append(propertyType, startDelay);
-        return this;
-    }
-
-    /**
-     * Returns the start delay for a specific property.
-     */
-    public long getStartDelay(@PropType int propertyType) {
-        if (mPropStartDelay != null) {
-            long startDelay = mPropStartDelay.get(propertyType, -1);
-            if (startDelay != -1) {
-                return startDelay;
-            }
-            return mPropStartDelay.get(ALL, 0);
-        }
-        return 0;
-    }
-
-    /**
-     * Sets a duration for a specific property.
-     */
-    public AnimationProps setDuration(@PropType int propertyType, int duration) {
-        if (mPropDuration == null) {
-            mPropDuration = new SparseLongArray();
-        }
-        mPropDuration.append(propertyType, duration);
-        return this;
-    }
-
-    /**
-     * Returns the duration for a specific property.
-     */
-    public long getDuration(@PropType int propertyType) {
-        if (mPropDuration != null) {
-            long duration = mPropDuration.get(propertyType, -1);
-            if (duration != -1) {
-                return duration;
-            }
-            return mPropDuration.get(ALL, 0);
-        }
-        return 0;
-    }
-
-    /**
-     * Sets an interpolator for a specific property.
-     */
-    public AnimationProps setInterpolator(@PropType int propertyType, Interpolator interpolator) {
-        if (mPropInterpolators == null) {
-            mPropInterpolators = new SparseArray<>();
-        }
-        mPropInterpolators.append(propertyType, interpolator);
-        return this;
-    }
-
-    /**
-     * Returns the interpolator for a specific property, falling back to the general interpolator
-     * if there is no specific property interpolator.
-     */
-    public Interpolator getInterpolator(@PropType int propertyType) {
-        if (mPropInterpolators != null) {
-            Interpolator interp = mPropInterpolators.get(propertyType);
-            if (interp != null) {
-                return interp;
-            }
-            return mPropInterpolators.get(ALL, LINEAR_INTERPOLATOR);
-        }
-        return LINEAR_INTERPOLATOR;
-    }
-
-    /**
-     * Sets an animator listener for this animation.
-     */
-    public AnimationProps setListener(Animator.AnimatorListener listener) {
-        mListener = listener;
-        return this;
-    }
-
-    /**
-     * Returns the animator listener for this animation.
-     */
-    public Animator.AnimatorListener getListener() {
-        return mListener;
-    }
-
-    /**
-     * Returns whether this animation has any duration.
-     */
-    public boolean isImmediate() {
-        int count = mPropDuration.size();
-        for (int i = 0; i < count; i++) {
-            if (mPropDuration.valueAt(i) > 0) {
-                return false;
-            }
-        }
-        return true;
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 7d159b7..7dffc26 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -16,158 +16,19 @@
 
 package com.android.systemui.shared.recents.utilities;
 
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.RectEvaluator;
-import android.annotation.FloatRange;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Message;
-import android.os.Trace;
-import android.util.ArraySet;
-import android.util.IntProperty;
-import android.util.Property;
-import android.util.TypedValue;
-import android.view.Surface;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.ViewRootImpl;
-import android.view.ViewStub;
-
-import java.util.ArrayList;
-import java.util.Collections;
 
 /* Common code */
 public class Utilities {
 
-    public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
-            new IntProperty<Drawable>("drawableAlpha") {
-                @Override
-                public void setValue(Drawable object, int alpha) {
-                    object.setAlpha(alpha);
-                }
-
-                @Override
-                public Integer get(Drawable object) {
-                    return object.getAlpha();
-                }
-            };
-
-    public static final Property<Drawable, Rect> DRAWABLE_RECT =
-            new Property<Drawable, Rect>(Rect.class, "drawableBounds") {
-                @Override
-                public void set(Drawable object, Rect bounds) {
-                    object.setBounds(bounds);
-                }
-
-                @Override
-                public Rect get(Drawable object) {
-                    return object.getBounds();
-                }
-            };
-
-    public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator();
-    public static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
-
     /**
-     * @return the first parent walking up the view hierarchy that has the given class type.
-     *
-     * @param parentClass must be a class derived from {@link View}
+     * Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
      */
-    public static <T extends View> T findParent(View v, Class<T> parentClass) {
-        ViewParent parent = v.getParent();
-        while (parent != null) {
-            if (parentClass.isAssignableFrom(parent.getClass())) {
-                return (T) parent;
-            }
-            parent = parent.getParent();
-        }
-        return null;
-    }
-
-    /**
-     * Initializes the {@param setOut} with the given object.
-     */
-    public static <T> ArraySet<T> objectToSet(T obj, ArraySet<T> setOut) {
-        setOut.clear();
-        if (obj != null) {
-            setOut.add(obj);
-        }
-        return setOut;
-    }
-
-    /**
-     * Replaces the contents of {@param setOut} with the contents of the {@param array}.
-     */
-    public static <T> ArraySet<T> arrayToSet(T[] array, ArraySet<T> setOut) {
-        setOut.clear();
-        if (array != null) {
-            Collections.addAll(setOut, array);
-        }
-        return setOut;
-    }
-
-    /**
-     * @return the clamped {@param value} between the provided {@param min} and {@param max}.
-     */
-    public static float clamp(float value, float min, float max) {
-        return Math.max(min, Math.min(max, value));
-    }
-
-    /**
-     * @return the clamped {@param value} between the provided {@param min} and {@param max}.
-     */
-    public static int clamp(int value, int min, int max) {
-        return Math.max(min, Math.min(max, value));
-    }
-
-    /**
-     * @return the clamped {@param value} between 0 and 1.
-     */
-    public static float clamp01(float value) {
-        return Math.max(0f, Math.min(1f, value));
-    }
-
-    /**
-     * Scales the {@param value} to be proportionally between the {@param min} and
-     * {@param max} values.
-     *
-     * @param value must be between 0 and 1
-     */
-    public static float mapRange(@FloatRange(from=0.0,to=1.0) float value, float min, float max) {
-        return min + (value * (max - min));
-    }
-
-    /**
-     * Scales the {@param value} proportionally from {@param min} and {@param max} to 0 and 1.
-     *
-     * @param value must be between {@param min} and {@param max}
-     */
-    public static float unmapRange(float value, float min, float max) {
-        return (value - min) / (max - min);
-    }
-
-    /** Scales a rect about its centroid */
-    public static void scaleRectAboutCenter(RectF r, float scale) {
-        if (scale != 1.0f) {
-            float cx = r.centerX();
-            float cy = r.centerY();
-            r.offset(-cx, -cy);
-            r.left *= scale;
-            r.top *= scale;
-            r.right *= scale;
-            r.bottom *= scale;
-            r.offset(cx, cy);
-        }
+    public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) {
+        Message msg = h.obtainMessage().setCallback(r);
+        h.sendMessageAtFrontOfQueue(msg);
     }
 
     /** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
@@ -179,7 +40,7 @@
         bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f);
         bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f);
         float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB;
-        
+
         float fgR = Color.red(fg) / 255f;
         float fgG = Color.green(fg) / 255f;
         float fgB = Color.blue(fg) / 255f;
@@ -191,147 +52,10 @@
         return Math.abs((fgL + 0.05f) / (bgL + 0.05f));
     }
 
-    /** Returns the base color overlaid with another overlay color with a specified alpha. */
-    public static int getColorWithOverlay(int baseColor, int overlayColor, float overlayAlpha) {
-        return Color.rgb(
-            (int) (overlayAlpha * Color.red(baseColor) +
-                    (1f - overlayAlpha) * Color.red(overlayColor)),
-            (int) (overlayAlpha * Color.green(baseColor) +
-                    (1f - overlayAlpha) * Color.green(overlayColor)),
-            (int) (overlayAlpha * Color.blue(baseColor) +
-                    (1f - overlayAlpha) * Color.blue(overlayColor)));
-    }
-
     /**
-     * Cancels an animation ensuring that if it has listeners, onCancel and onEnd
-     * are not called.
+     * @return the clamped {@param value} between the provided {@param min} and {@param max}.
      */
-    public static void cancelAnimationWithoutCallbacks(Animator animator) {
-        if (animator != null && animator.isStarted()) {
-            removeAnimationListenersRecursive(animator);
-            animator.cancel();
-        }
-    }
-
-    /**
-     * Recursively removes all the listeners of all children of this animator
-     */
-    public static void removeAnimationListenersRecursive(Animator animator) {
-        if (animator instanceof AnimatorSet) {
-            ArrayList<Animator> animators = ((AnimatorSet) animator).getChildAnimations();
-            for (int i = animators.size() - 1; i >= 0; i--) {
-                removeAnimationListenersRecursive(animators.get(i));
-            }
-        }
-        animator.removeAllListeners();
-    }
-
-    /**
-     * Sets the given {@link View}'s frame from its current translation.
-     */
-    public static void setViewFrameFromTranslation(View v) {
-        RectF taskViewRect = new RectF(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
-        taskViewRect.offset(v.getTranslationX(), v.getTranslationY());
-        v.setTranslationX(0);
-        v.setTranslationY(0);
-        v.setLeftTopRightBottom((int) taskViewRect.left, (int) taskViewRect.top,
-                (int) taskViewRect.right, (int) taskViewRect.bottom);
-    }
-
-    /**
-     * Returns a view stub for the given view id.
-     */
-    public static ViewStub findViewStubById(View v, int stubId) {
-        return (ViewStub) v.findViewById(stubId);
-    }
-
-    /**
-     * Returns a view stub for the given view id.
-     */
-    public static ViewStub findViewStubById(Activity a, int stubId) {
-        return (ViewStub) a.findViewById(stubId);
-    }
-
-    /**
-     * Used for debugging, converts DP to PX.
-     */
-    public static float dpToPx(Resources res, float dp) {
-        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
-    }
-
-    /**
-     * Adds a trace event for debugging.
-     */
-    public static void addTraceEvent(String event) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, event);
-        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-    }
-
-    /**
-     * Returns whether this view, or one of its descendants have accessibility focus.
-     */
-    public static boolean isDescendentAccessibilityFocused(View v) {
-        if (v.isAccessibilityFocused()) {
-            return true;
-        }
-
-        if (v instanceof ViewGroup) {
-            ViewGroup vg = (ViewGroup) v;
-            int childCount = vg.getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                if (isDescendentAccessibilityFocused(vg.getChildAt(i))) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns the application configuration, which is independent of the activity's current
-     * configuration in multiwindow.
-     */
-    public static Configuration getAppConfiguration(Context context) {
-        return context.getApplicationContext().getResources().getConfiguration();
-    }
-
-    /**
-     * @return The next frame name for the specified surface or -1 if the surface is no longer
-     *         valid.
-     */
-    public static long getNextFrameNumber(Surface s) {
-        return s != null && s.isValid()
-                ? s.getNextFrameNumber()
-                : -1;
-
-    }
-
-    /**
-     * @return The surface for the specified view.
-     */
-    public static @Nullable Surface getSurface(View v) {
-        ViewRootImpl viewRoot = v.getViewRootImpl();
-        if (viewRoot == null) {
-            return null;
-        }
-        return viewRoot.mSurface;
-    }
-
-    /**
-     * Returns a lightweight dump of a rect.
-     */
-    public static String dumpRect(Rect r) {
-        if (r == null) {
-            return "N:0,0-0,0";
-        }
-        return r.left + "," + r.top + "-" + r.right + "," + r.bottom;
-    }
-
-    /**
-     * Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
-     */
-    public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) {
-        Message msg = h.obtainMessage().setCallback(r);
-        h.sendMessageAtFrontOfQueue(msg);
+    public static float clamp(float value, float min, float max) {
+        return Math.max(min, Math.min(max, value));
     }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java
deleted file mode 100644
index 30bea32..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.shared.recents.view;
-
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewOutlineProvider;
-
-import com.android.systemui.shared.recents.utilities.Utilities;
-
-/**
- * An outline provider that has a clip and outline that can be animated.
- */
-@Deprecated
-public class AnimateableViewBounds extends ViewOutlineProvider {
-
-    private static final float MIN_ALPHA = 0.1f;
-    private static final float MAX_ALPHA = 0.8f;
-
-    protected View mSourceView;
-    protected Rect mClipRect = new Rect();
-    protected Rect mClipBounds = new Rect();
-    protected Rect mLastClipBounds = new Rect();
-    protected int mCornerRadius;
-    protected float mAlpha = 1f;
-
-    public AnimateableViewBounds(View source, int cornerRadius) {
-        mSourceView = source;
-        mCornerRadius = cornerRadius;
-    }
-
-    /**
-     * Resets the right and bottom clip for this view.
-     */
-    public void reset() {
-        mClipRect.set(0, 0, 0, 0);
-        updateClipBounds();
-    }
-
-    @Override
-    public void getOutline(View view, Outline outline) {
-        outline.setAlpha(Utilities.mapRange(mAlpha, MIN_ALPHA, MAX_ALPHA));
-        if (mCornerRadius > 0) {
-            outline.setRoundRect(mClipRect.left, mClipRect.top,
-                    mSourceView.getWidth() - mClipRect.right,
-                    mSourceView.getHeight() - mClipRect.bottom,
-                    mCornerRadius);
-        } else {
-            outline.setRect(mClipRect.left, mClipRect.top,
-                    mSourceView.getWidth() - mClipRect.right,
-                    mSourceView.getHeight() - mClipRect.bottom);
-        }
-    }
-
-    /**
-     * Sets the view outline alpha.
-     */
-    public void setAlpha(float alpha) {
-        if (Float.compare(alpha, mAlpha) != 0) {
-            mAlpha = alpha;
-            // TODO, If both clip and alpha change in the same frame, only invalidate once
-            mSourceView.invalidateOutline();
-        }
-    }
-
-    /**
-     * @return the outline alpha.
-     */
-    public float getAlpha() {
-        return mAlpha;
-    }
-
-    /**
-     * Sets the top clip.
-     */
-    public void setClipTop(int top) {
-        mClipRect.top = top;
-        updateClipBounds();
-    }
-
-    /**
-     * @return the top clip.
-     */
-    public int getClipTop() {
-        return mClipRect.top;
-    }
-
-    /**
-     * Sets the bottom clip.
-     */
-    public void setClipBottom(int bottom) {
-        mClipRect.bottom = bottom;
-        updateClipBounds();
-    }
-
-    /**
-     * @return the bottom clip.
-     */
-    public int getClipBottom() {
-        return mClipRect.bottom;
-    }
-
-    /**
-     * @return the clip bounds.
-     */
-    public Rect getClipBounds() {
-        return mClipBounds;
-    }
-
-    protected void updateClipBounds() {
-        mClipBounds.set(Math.max(0, mClipRect.left), Math.max(0, mClipRect.top),
-                mSourceView.getWidth() - Math.max(0, mClipRect.right),
-                mSourceView.getHeight() - Math.max(0, mClipRect.bottom));
-        if (!mLastClipBounds.equals(mClipBounds)) {
-            mSourceView.setClipBounds(mClipBounds);
-            // TODO, If both clip and alpha change in the same frame, only invalidate once
-            mSourceView.invalidateOutline();
-            mLastClipBounds.set(mClipBounds);
-        }
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java
new file mode 100644
index 0000000..5bc1f25
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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.systemui.shared.system;
+
+import android.util.StatsLog;
+
+/**
+ * Wrapper class to make StatsLog hidden API accessible.
+ */
+public class StatsLogCompat {
+
+    /**
+     * StatsLog.write(StatsLog.LAUNCHER_EVENT, int action, int src_state, int dst_state,
+     *                byte[] extension, boolean is_swipe_up_enabled);
+     */
+    public static void write(int action, int srcState, int dstState, byte [] extension,
+            boolean swipeUpEnabled) {
+        StatsLog.write(19, action, srcState, dstState, extension,
+                swipeUpEnabled);
+    }
+}
diff --git a/packages/SystemUI/shared/tests/Android.mk b/packages/SystemUI/shared/tests/Android.mk
deleted file mode 100644
index 02774c9..0000000
--- a/packages/SystemUI/shared/tests/Android.mk
+++ /dev/null
@@ -1,59 +0,0 @@
-# Copyright (C) 2017 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.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTOC_FLAGS := -I$(LOCAL_PATH)/..
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
-
-LOCAL_PACKAGE_NAME := SystemUISharedLibTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-# Add local path sources as well as shared lib sources
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-	SystemUISharedLib \
-    metrics-helper-lib \
-    android-support-test \
-    mockito-target-inline-minus-junit4 \
-    SystemUI-proto \
-    SystemUI-tags \
-    testables \
-    truth-prebuilt \
-
-LOCAL_MULTILIB := both
-
-LOCAL_JNI_SHARED_LIBRARIES := \
-    libdexmakerjvmtiagent \
-    libmultiplejvmtiagentsinterferenceagent
-
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
-
-# sign this with platform cert, so this test is allowed to inject key events into
-# UI it doesn't own. This is necessary to allow screenshots to be taken
-LOCAL_CERTIFICATE := platform
-
-ifeq ($(EXCLUDE_SYSTEMUI_TESTS),)
-    include $(BUILD_PACKAGE)
-endif
diff --git a/packages/SystemUI/shared/tests/AndroidManifest.xml b/packages/SystemUI/shared/tests/AndroidManifest.xml
deleted file mode 100644
index 5974b76..0000000
--- a/packages/SystemUI/shared/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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.systemui.shared.tests">
-
-    <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
-
-    <application android:debuggable="true">
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.testing.TestableInstrumentation"
-        android:targetPackage="com.android.systemui.shared.tests"
-        android:label="Tests for SystemUISharedLib">
-    </instrumentation>
-</manifest>
diff --git a/packages/SystemUI/shared/tests/AndroidTest.xml b/packages/SystemUI/shared/tests/AndroidTest.xml
deleted file mode 100644
index b3de836..0000000
--- a/packages/SystemUI/shared/tests/AndroidTest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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="Runs Tests for SystemUISharedLib.">
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
-        <option name="test-file-name" value="SystemUISharedLibTests.apk" />
-    </target_preparer>
-
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-suite-tag" value="framework-base-presubmit" />
-    <option name="test-tag" value="SystemUISharedLibTests" />
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="com.android.systemui.shared.tests" />
-        <option name="runner" value="android.testing.TestableInstrumentation" />
-    </test>
-</configuration>
diff --git a/packages/SystemUI/shared/tests/src/com/android/systemui/shared/SysuiSharedLibTestCase.java b/packages/SystemUI/shared/tests/src/com/android/systemui/shared/SysuiSharedLibTestCase.java
deleted file mode 100644
index 04b341e..0000000
--- a/packages/SystemUI/shared/tests/src/com/android/systemui/shared/SysuiSharedLibTestCase.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.shared;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.MessageQueue;
-import android.support.test.InstrumentationRegistry;
-
-import org.junit.After;
-import org.junit.Before;
-
-/**
- * Base class that does System UI Shared Lib specific setup.
- */
-public abstract class SysuiSharedLibTestCase {
-
-    private static final String TAG = "SysuiSharedLibTestCase";
-
-    private Handler mHandler;
-    private Context mContext = InstrumentationRegistry.getContext();
-
-    @Before
-    public void SysuiSetup() throws Exception {
-        // Enable shared class loader to test package-private classes/methods
-        System.setProperty("dexmaker.share_classloader", "true");
-    }
-
-    @After
-    public void SysuiTeardown() {
-        // Do nothing
-    }
-
-    public Context getContext() {
-        return mContext;
-    }
-
-    protected void waitForIdleSync() {
-        if (mHandler == null) {
-            mHandler = new Handler(Looper.getMainLooper());
-        }
-        waitForIdleSync(mHandler);
-    }
-
-    public static void waitForIdleSync(Handler h) {
-        validateThread(h.getLooper());
-        Idler idler = new Idler(null);
-        h.getLooper().getQueue().addIdleHandler(idler);
-        // Ensure we are non-idle, so the idle handler can run.
-        h.post(new EmptyRunnable());
-        idler.waitForIdle();
-    }
-
-    private static final void validateThread(Looper l) {
-        if (Looper.myLooper() == l) {
-            throw new RuntimeException(
-                "This method can not be called from the looper being synced");
-        }
-    }
-
-    public static final class EmptyRunnable implements Runnable {
-        public void run() {
-        }
-    }
-
-    public static final class Idler implements MessageQueue.IdleHandler {
-        private final Runnable mCallback;
-        private boolean mIdle;
-
-        public Idler(Runnable callback) {
-            mCallback = callback;
-            mIdle = false;
-        }
-
-        @Override
-        public boolean queueIdle() {
-            if (mCallback != null) {
-                mCallback.run();
-            }
-            synchronized (this) {
-                mIdle = true;
-                notifyAll();
-            }
-            return false;
-        }
-
-        public void waitForIdle() {
-            synchronized (this) {
-                while (!mIdle) {
-                    try {
-                        wait();
-                    } catch (InterruptedException e) {
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/shared/tests/src/com/android/systemui/shared/recents/model/HighResThumbnailLoaderTest.java b/packages/SystemUI/shared/tests/src/com/android/systemui/shared/recents/model/HighResThumbnailLoaderTest.java
deleted file mode 100644
index 3b647c1..0000000
--- a/packages/SystemUI/shared/tests/src/com/android/systemui/shared/recents/model/HighResThumbnailLoaderTest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.shared.recents.model;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.os.Looper;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.systemui.shared.SysuiSharedLibTestCase;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * runtest --path frameworks/base/packages/SystemUI/shared/tests/src/com/android/systemui/shared/recents/model/HighResThumbnailLoaderTest.java
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class HighResThumbnailLoaderTest extends SysuiSharedLibTestCase {
-
-    private HighResThumbnailLoader mLoader;
-
-    @Mock
-    private ActivityManagerWrapper mMockActivityManagerWrapper;
-    @Mock
-    private Task mTask;
-
-    private ThumbnailData mThumbnailData = new ThumbnailData();
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mLoader = new HighResThumbnailLoader(mMockActivityManagerWrapper, Looper.getMainLooper(),
-                false /* reducedResolution */);
-        mTask.key = new TaskKey(0, WINDOWING_MODE_UNDEFINED, null, null, 0, 0);
-        when(mMockActivityManagerWrapper.getTaskThumbnail(anyInt(), anyBoolean()))
-                .thenReturn(mThumbnailData);
-        mLoader.setVisible(true);
-        mLoader.setTaskLoadQueueIdle(true);
-    }
-
-    @Test
-    public void testLoading() throws Exception {
-        mLoader.setVisible(true);
-        assertTrue(mLoader.isLoading());
-        mLoader.setVisible(false);
-        assertFalse(mLoader.isLoading());
-        mLoader.setVisible(true);
-        mLoader.setFlingingFast(true);
-        assertFalse(mLoader.isLoading());
-        mLoader.setFlingingFast(false);
-        assertTrue(mLoader.isLoading());
-        mLoader.setFlingingFast(false);
-        mLoader.setTaskLoadQueueIdle(false);
-        assertFalse(mLoader.isLoading());
-        mLoader.setTaskLoadQueueIdle(true);
-        assertTrue(mLoader.isLoading());
-    }
-
-    @Test
-    public void testLoad() throws Exception {
-        mLoader.onTaskVisible(mTask);
-        mLoader.waitForLoaderIdle();
-        waitForIdleSync();
-        verify(mTask).notifyTaskDataLoaded(mThumbnailData, null);
-    }
-
-    @Test
-    public void testFlinging_notLoaded() throws Exception {
-        mLoader.setFlingingFast(true);
-        mLoader.onTaskVisible(mTask);
-        mLoader.waitForLoaderIdle();
-        waitForIdleSync();
-        verify(mTask, never()).notifyTaskDataLoaded(mThumbnailData, null);
-    }
-
-    /**
-     * Tests whether task is loaded after stopping to fling
-     */
-    @Test
-    public void testAfterFlinging() throws Exception {
-        mLoader.setFlingingFast(true);
-        mLoader.onTaskVisible(mTask);
-        mLoader.setFlingingFast(false);
-        mLoader.waitForLoaderIdle();
-        waitForIdleSync();
-        verify(mTask).notifyTaskDataLoaded(mThumbnailData, null);
-    }
-
-    @Test
-    public void testAlreadyLoaded() throws Exception {
-        mTask.thumbnail = new ThumbnailData();
-        mTask.thumbnail.reducedResolution = false;
-        mLoader.onTaskVisible(mTask);
-        mLoader.waitForLoaderIdle();
-        waitForIdleSync();
-        verify(mTask, never()).notifyTaskDataLoaded(mThumbnailData, null);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 28eff46..013745a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -16,6 +16,8 @@
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.plugins.PluginManager;
 
+import java.util.Objects;
+
 /**
  * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
  */
@@ -35,6 +37,10 @@
                 public void onPluginConnected(ClockPlugin plugin, Context pluginContext) {
                     View view = plugin.getView();
                     if (view != null) {
+                        disconnectPlugin();
+                        // For now, assume that the most recently connected plugin is the
+                        // selected clock face. In the future, the user should be able to
+                        // pick a clock face from the available plugins.
                         mClockPlugin = plugin;
                         addView(view, -1,
                                 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
@@ -46,10 +52,8 @@
 
                 @Override
                 public void onPluginDisconnected(ClockPlugin plugin) {
-                    View view = plugin.getView();
-                    if (view != null) {
-                        mClockPlugin = null;
-                        removeView(view);
+                    if (Objects.equals(plugin, mClockPlugin)) {
+                        disconnectPlugin();
                         mClockView.setVisibility(View.VISIBLE);
                     }
                 }
@@ -148,6 +152,16 @@
         }
     }
 
+    private void disconnectPlugin() {
+        if (mClockPlugin != null) {
+            View view = mClockPlugin.getView();
+            if (view != null) {
+                removeView(view);
+            }
+            mClockPlugin = null;
+        }
+    }
+
     @VisibleForTesting (otherwise = VisibleForTesting.NONE)
     PluginListener getClockPluginListener() {
         return mClockPluginListener;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 2bc0e45c..e051317 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -15,54 +15,164 @@
  */
 package com.android.keyguard;
 
-import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Display.DEFAULT_DISPLAY;
 
+import android.annotation.Nullable;
 import android.app.Presentation;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnDismissListener;
 import android.graphics.Point;
+import android.hardware.display.DisplayManager;
 import android.media.MediaRouter;
 import android.media.MediaRouter.RouteInfo;
 import android.os.Bundle;
-import android.util.Slog;
+import android.util.Log;
+import android.util.SparseArray;
 import android.view.Display;
 import android.view.View;
 import android.view.WindowManager;
 
+import java.util.function.BooleanSupplier;
+
 // TODO(multi-display): Support multiple external displays
 public class KeyguardDisplayManager {
     protected static final String TAG = "KeyguardDisplayManager";
     private static boolean DEBUG = KeyguardConstants.DEBUG;
 
     private final ViewMediatorCallback mCallback;
+
     private final MediaRouter mMediaRouter;
+    private final DisplayManager mDisplayService;
     private final Context mContext;
 
-    Presentation mPresentation;
     private boolean mShowing;
 
+    private final SparseArray<Presentation> mPresentations = new SparseArray<>();
+
+    private final DisplayManager.DisplayListener mDisplayListener =
+            new DisplayManager.DisplayListener() {
+
+        @Override
+        public void onDisplayAdded(int displayId) {
+            final Display display = mDisplayService.getDisplay(displayId);
+            if (mShowing) {
+                notifyIfChanged(() -> showPresentation(display));
+            }
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            if (displayId == DEFAULT_DISPLAY) return;
+            final Display display = mDisplayService.getDisplay(displayId);
+            if (display != null && mShowing) {
+                final Presentation presentation = mPresentations.get(displayId);
+                if (presentation != null && !presentation.getDisplay().equals(display)) {
+                    hidePresentation(displayId);
+                    showPresentation(display);
+                }
+            }
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            notifyIfChanged(() -> hidePresentation(displayId));
+        }
+    };
+
     public KeyguardDisplayManager(Context context, ViewMediatorCallback callback) {
         mContext = context;
         mCallback = callback;
-        mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+        mMediaRouter = mContext.getSystemService(MediaRouter.class);
+        mDisplayService = mContext.getSystemService(DisplayManager.class);
+        mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
+    }
+
+    /**
+     * @param display The display to show the presentation on.
+     * @return {@code true} if a presentation was added.
+     *         {@code false} if the presentation cannot be added on that display or the presentation
+     *         was already there.
+     */
+    private boolean showPresentation(Display display) {
+        if (display == null || display.getDisplayId() == DEFAULT_DISPLAY) return false;
+        if (DEBUG) Log.i(TAG, "Keyguard enabled on display: " + display);
+        final int displayId = display.getDisplayId();
+        Presentation presentation = mPresentations.get(displayId);
+        if (presentation == null) {
+            presentation = new KeyguardPresentation(mContext, display);
+            presentation.setOnDismissListener(dialog -> {
+                if (null != mPresentations.get(displayId)) {
+                    mPresentations.remove(displayId);
+                }
+            });
+            try {
+                presentation.show();
+            } catch (WindowManager.InvalidDisplayException ex) {
+                Log.w(TAG, "Invalid display:", ex);
+                presentation = null;
+            }
+            if (presentation != null) {
+                mPresentations.append(displayId, presentation);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @param displayId The id of the display to hide the presentation off.
+     * @return {@code true} if the a presentation was removed.
+     *         {@code false} if the presentation was not added before.
+     */
+    private boolean hidePresentation(int displayId) {
+        final Presentation presentation = mPresentations.get(displayId);
+        if (presentation != null) {
+            presentation.dismiss();
+            mPresentations.remove(displayId);
+            return true;
+        }
+        return false;
+    }
+
+    private void notifyIfChanged(BooleanSupplier updateMethod) {
+        if (updateMethod.getAsBoolean()) {
+            final int[] displayList = getPresentationDisplayIds();
+            mCallback.onSecondaryDisplayShowingChanged(displayList);
+        }
+    }
+
+    /**
+     * @return An array of displayId's on which a {@link KeyguardPresentation} is showing on.
+     */
+    @Nullable
+    private int[] getPresentationDisplayIds() {
+        final int size = mPresentations.size();
+        if (size == 0) return null;
+
+        final int[] displayIds = new int[size];
+        for (int i = mPresentations.size() - 1; i >= 0; i--) {
+            final Presentation presentation = mPresentations.valueAt(i);
+            if (presentation != null) {
+                displayIds[i] = presentation.getDisplay().getDisplayId();
+            }
+        }
+        return displayIds;
     }
 
     public void show() {
         if (!mShowing) {
-            if (DEBUG) Slog.v(TAG, "show");
+            if (DEBUG) Log.v(TAG, "show");
             mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
                     mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
-            updateDisplays(true);
+            notifyIfChanged(() -> updateDisplays(true /* showing */));
         }
         mShowing = true;
     }
 
     public void hide() {
         if (mShowing) {
-            if (DEBUG) Slog.v(TAG, "hide");
+            if (DEBUG) Log.v(TAG, "hide");
             mMediaRouter.removeCallback(mMediaRouterCallback);
-            updateDisplays(false);
+            notifyIfChanged(() -> updateDisplays(false /* showing */));
         }
         mShowing = false;
     }
@@ -71,71 +181,38 @@
             new MediaRouter.SimpleCallback() {
         @Override
         public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
-            if (DEBUG) Slog.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
-            updateDisplays(mShowing);
+            if (DEBUG) Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
+            notifyIfChanged(() -> updateDisplays(mShowing));
         }
 
         @Override
         public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
-            if (DEBUG) Slog.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
-            updateDisplays(mShowing);
+            if (DEBUG) Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
+            notifyIfChanged(() -> updateDisplays(mShowing));
         }
 
         @Override
         public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
-            if (DEBUG) Slog.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
-            updateDisplays(mShowing);
+            if (DEBUG) Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
+            notifyIfChanged(() -> updateDisplays(mShowing));
         }
     };
 
-    private OnDismissListener mOnDismissListener = new OnDismissListener() {
-
-        @Override
-        public void onDismiss(DialogInterface dialog) {
-            mPresentation = null;
-        }
-    };
-
-    protected void updateDisplays(boolean showing) {
-        Presentation originalPresentation = mPresentation;
+    protected boolean updateDisplays(boolean showing) {
+        boolean changed = false;
         if (showing) {
-            MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
-                    MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
-            boolean useDisplay = route != null
-                    && route.getPlaybackType() == MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
-            Display presentationDisplay = useDisplay ? route.getPresentationDisplay() : null;
-
-            if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
-                if (DEBUG) Slog.v(TAG, "Display gone: " + mPresentation.getDisplay());
-                mPresentation.dismiss();
-                mPresentation = null;
-            }
-
-            if (mPresentation == null && presentationDisplay != null) {
-                if (DEBUG) Slog.i(TAG, "Keyguard enabled on display: " + presentationDisplay);
-                mPresentation = new KeyguardPresentation(mContext, presentationDisplay,
-                        R.style.keyguard_presentation_theme);
-                mPresentation.setOnDismissListener(mOnDismissListener);
-                try {
-                    mPresentation.show();
-                } catch (WindowManager.InvalidDisplayException ex) {
-                    Slog.w(TAG, "Invalid display:", ex);
-                    mPresentation = null;
-                }
+            final Display[] displays = mDisplayService.getDisplays();
+            for (Display display : displays) {
+                changed |= showPresentation(display);
             }
         } else {
-            if (mPresentation != null) {
-                mPresentation.dismiss();
-                mPresentation = null;
+            changed = mPresentations.size() > 0;
+            for (int i = mPresentations.size() - 1; i >= 0; i--) {
+                mPresentations.valueAt(i).dismiss();
             }
+            mPresentations.clear();
         }
-
-        // mPresentation is only updated when the display changes
-        if (mPresentation != originalPresentation) {
-            final int displayId = mPresentation != null
-                    ? mPresentation.getDisplay().getDisplayId() : INVALID_DISPLAY;
-            mCallback.onSecondaryDisplayShowingChanged(displayId);
-        }
+        return changed;
     }
 
     private final static class KeyguardPresentation extends Presentation {
@@ -157,9 +234,10 @@
             }
         };
 
-        public KeyguardPresentation(Context context, Display display, int theme) {
-            super(context, display, theme);
+        KeyguardPresentation(Context context, Display display) {
+            super(context, display, R.style.keyguard_presentation_theme);
             getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+            setCancelable(false);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 7479152..79966f7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -17,21 +17,20 @@
 package com.android.keyguard;
 
 import static android.app.slice.Slice.HINT_LIST_ITEM;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
 
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.annotation.ColorInt;
 import android.app.PendingIntent;
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.Observer;
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Trace;
 import android.provider.Settings;
-import android.text.Layout;
 import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
@@ -41,25 +40,9 @@
 import android.view.animation.Animation;
 import android.widget.Button;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.ColorUtils;
-import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.keyguard.KeyguardSliceProvider;
-import com.android.systemui.statusbar.AlphaOptimizedTextView;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.function.Consumer;
-
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Observer;
 import androidx.slice.Slice;
 import androidx.slice.SliceItem;
 import androidx.slice.SliceViewManager;
@@ -69,6 +52,20 @@
 import androidx.slice.widget.SliceContent;
 import androidx.slice.widget.SliceLiveData;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
+import com.android.settingslib.Utils;
+import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
+import com.android.systemui.keyguard.KeyguardSliceProvider;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
 /**
  * View visible under the clock on the lock screen and AoD.
  */
@@ -80,19 +77,17 @@
 
     private final HashMap<View, PendingIntent> mClickActions;
     private Uri mKeyguardSliceUri;
-    @VisibleForTesting
-    TextView mTitle;
     private Row mRow;
     private int mTextColor;
     private float mDarkAmount = 0;
 
     private LiveData<Slice> mLiveData;
+    private int mDisplayId = INVALID_DISPLAY;
     private int mIconSize;
     /**
      * Runnable called whenever the view contents change.
      */
     private Runnable mContentChangeListener;
-    private boolean mHasHeader;
     private Slice mSlice;
     private boolean mPulsing;
 
@@ -128,7 +123,6 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mTitle = findViewById(R.id.title);
         mRow = findViewById(R.id.row);
         mTextColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
         mIconSize = (int) mContext.getResources().getDimension(R.dimen.widget_icon_size);
@@ -138,6 +132,7 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
+        mDisplayId = getDisplay().getDisplayId();
         // Make sure we always have the most current slice
         mLiveData.observeForever(this);
         Dependency.get(ConfigurationController.class).addCallback(this);
@@ -147,14 +142,16 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
 
-        mLiveData.removeObserver(this);
+        // TODO(b/117344873) Remove below work around after this issue be fixed.
+        if (mDisplayId == DEFAULT_DISPLAY) {
+            mLiveData.removeObserver(this);
+        }
         Dependency.get(ConfigurationController.class).removeCallback(this);
     }
 
     private void showSlice() {
         Trace.beginSection("KeyguardSliceView#showSlice");
         if (mPulsing || mSlice == null) {
-            mTitle.setVisibility(GONE);
             mRow.setVisibility(GONE);
             if (mContentChangeListener != null) {
                 mContentChangeListener.run();
@@ -164,8 +161,9 @@
 
         ListContent lc = new ListContent(getContext(), mSlice);
         SliceContent headerContent = lc.getHeader();
-        mHasHeader = headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
-        List<SliceContent> subItems = new ArrayList<SliceContent>();
+        boolean hasHeader = headerContent != null
+                && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
+        List<SliceContent> subItems = new ArrayList<>();
         for (int i = 0; i < lc.getRowItems().size(); i++) {
             SliceContent subItem = lc.getRowItems().get(i);
             String itemUri = subItem.getSliceItem().getSlice().getUri().toString();
@@ -174,21 +172,11 @@
                 subItems.add(subItem);
             }
         }
-        if (!mHasHeader) {
-            mTitle.setVisibility(GONE);
-        } else {
-            mTitle.setVisibility(VISIBLE);
-
-            RowContent header = lc.getHeader();
-            SliceItem mainTitle = header.getTitleItem();
-            CharSequence title = mainTitle != null ? mainTitle.getText() : null;
-            mTitle.setText(title);
-        }
 
         mClickActions.clear();
         final int subItemsCount = subItems.size();
         final int blendedColor = getTextColor();
-        final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
+        final int startIndex = hasHeader ? 1 : 0; // First item is header; skip it
         mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE);
         for (int i = startIndex; i < subItemsCount; i++) {
             RowContent rc = (RowContent) subItems.get(i);
@@ -200,7 +188,7 @@
                 button = new KeyguardSliceButton(mContext);
                 button.setTextColor(blendedColor);
                 button.setTag(itemTag);
-                final int viewIndex = i - (mHasHeader ? 1 : 0);
+                final int viewIndex = i - (hasHeader ? 1 : 0);
                 mRow.addView(button, viewIndex);
             }
 
@@ -303,7 +291,6 @@
 
     private void updateTextColors() {
         final int blendedColor = getTextColor();
-        mTitle.setTextColor(blendedColor);
         int childCount = mRow.getChildCount();
         for (int i = 0; i < childCount; i++) {
             View v = mRow.getChildAt(i);
@@ -333,10 +320,6 @@
         mContentChangeListener = contentChangeListener;
     }
 
-    public boolean hasHeader() {
-        return mHasHeader;
-    }
-
     /**
      * LiveData observer lifecycle.
      * @param slice the new slice content.
@@ -352,7 +335,7 @@
         setupUri(newValue);
     }
 
-    public void setupUri(String uriString) {
+    private void setupUri(String uriString) {
         if (uriString == null) {
             uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI;
         }
@@ -564,46 +547,6 @@
         }
     }
 
-    /**
-     * A text view that will split its contents in 2 lines when possible.
-     */
-    static class TitleView extends AlphaOptimizedTextView {
-
-        public TitleView(Context context) {
-            super(context);
-        }
-
-        public TitleView(Context context, AttributeSet attrs) {
-            super(context, attrs);
-        }
-
-        public TitleView(Context context, AttributeSet attrs, int defStyleAttr) {
-            super(context, attrs, defStyleAttr);
-        }
-
-        public TitleView(Context context, AttributeSet attrs, int defStyleAttr,
-                int defStyleRes) {
-            super(context, attrs, defStyleAttr, defStyleRes);
-        }
-
-        @Override
-        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-            Layout layout = getLayout();
-            int lineCount = layout.getLineCount();
-            boolean ellipsizing = layout.getEllipsisCount(lineCount - 1) != 0;
-            if (lineCount > 0 && !ellipsizing) {
-                CharSequence title = getText();
-                CharSequence bestLineBreak = findBestLineBreak(title);
-                if (!TextUtils.equals(title, bestLineBreak)) {
-                    setText(bestLineBreak);
-                    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-                }
-            }
-        }
-    }
-
     private class SliceViewTransitionListener implements LayoutTransition.TransitionListener {
         @Override
         public void startTransition(LayoutTransition transition, ViewGroup container, View view,
@@ -619,15 +562,6 @@
                             .setInterpolator(Interpolators.ALPHA_IN)
                             .start();
                     break;
-                case LayoutTransition.DISAPPEARING:
-                    if (view == mTitle) {
-                        // Translate the view to the inverse of its height, so the layout event
-                        // won't misposition it.
-                        LayoutParams params = (LayoutParams) mTitle.getLayoutParams();
-                        int margin = params.topMargin + params.bottomMargin;
-                        mTitle.setTranslationY(-mTitle.getHeight() - margin);
-                    }
-                    break;
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index db78667..a403b75 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -200,10 +200,9 @@
      * Moves clock, adjusting margins when slice content changes.
      */
     private void onSliceContentChanged() {
-        boolean smallClock = mKeyguardSlice.hasHeader() || mPulsing;
         RelativeLayout.LayoutParams layoutParams =
                 (RelativeLayout.LayoutParams) mClockView.getLayoutParams();
-        layoutParams.bottomMargin = smallClock ? mSmallClockPadding : 0;
+        layoutParams.bottomMargin = mPulsing ? mSmallClockPadding : 0;
         mClockView.setLayoutParams(layoutParams);
     }
 
@@ -214,17 +213,15 @@
     public void onLayoutChange(View view, int left, int top, int right, int bottom,
             int oldLeft, int oldTop, int oldRight, int oldBottom) {
         int heightOffset = mPulsing || mWasPulsing ? 0 : getHeight() - mLastLayoutHeight;
-        boolean hasHeader = mKeyguardSlice.hasHeader();
-        boolean smallClock = hasHeader || mPulsing;
         long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION;
-        long delay = smallClock || mWasPulsing ? 0 : duration / 4;
+        long delay = mPulsing || mWasPulsing ? 0 : duration / 4;
         mWasPulsing = false;
 
         boolean shouldAnimate = mKeyguardSlice.getLayoutTransition() != null
                 && mKeyguardSlice.getLayoutTransition().isRunning();
         if (view == mClockView) {
-            float clockScale = smallClock ? mSmallClockScale : 1;
-            Paint.Style style = smallClock ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL;
+            float clockScale = mPulsing ? mSmallClockScale : 1;
+            Paint.Style style = mPulsing ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL;
             mClockView.animate().cancel();
             if (shouldAnimate) {
                 mClockView.setY(oldTop + heightOffset);
@@ -431,11 +428,6 @@
             mWasPulsing = true;
         }
         mPulsing = pulsing;
-        // Animation can look really weird when the slice has a header, let's hide the views
-        // immediately instead of fading them away.
-        if (mKeyguardSlice.hasHeader()) {
-            animate = false;
-        }
         mKeyguardSlice.setPulsing(pulsing, animate);
         updateDozeVisibleViews();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index c7685f8..6b0a7a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -88,6 +88,7 @@
 import com.android.settingslib.WirelessUtils;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+
 import com.google.android.collect.Lists;
 
 import java.io.FileDescriptor;
@@ -236,6 +237,8 @@
     private boolean mIsDreaming;
     private final DevicePolicyManager mDevicePolicyManager;
     private boolean mLogoutEnabled;
+    private boolean mFingerprintLockedOut;
+    private boolean mFaceLockedOut;
 
     /**
      * Short delay before restarting biometric authentication after a successful try
@@ -624,7 +627,7 @@
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
                 && mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
             setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
-            startListeningForFingerprint();
+            updateFingerprintListeningState();
         } else {
             setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
         }
@@ -637,6 +640,11 @@
             }
         }
 
+        if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
+                || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
+            mFingerprintLockedOut = true;
+        }
+
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
             mLockPatternUtils.requireStrongAuth(
                     LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
@@ -652,6 +660,7 @@
     }
 
     private void handleFingerprintLockoutReset() {
+        mFingerprintLockedOut = false;
         updateFingerprintListeningState();
     }
 
@@ -659,7 +668,7 @@
         boolean wasRunning = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
         boolean isRunning = fingerprintRunningState == BIOMETRIC_STATE_RUNNING;
         mFingerprintRunningState = fingerprintRunningState;
-
+        if (DEBUG) Log.v(TAG, "Fingerprint State: " + mFingerprintRunningState);
         // Clients of KeyguardUpdateMonitor don't care about the internal state about the
         // asynchronousness of the cancel cycle. So only notify them if the actualy running state
         // has changed.
@@ -774,7 +783,7 @@
         if (msgId == FaceManager.FACE_ERROR_CANCELED
                 && mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
             setFaceRunningState(BIOMETRIC_STATE_STOPPED);
-            startListeningForFace();
+            updateFaceListeningState();
         } else {
             setFaceRunningState(BIOMETRIC_STATE_STOPPED);
         }
@@ -787,6 +796,11 @@
             }
         }
 
+        if (msgId == FaceManager.FACE_ERROR_LOCKOUT
+                || msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
+            mFaceLockedOut = true;
+        }
+
         if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
             mLockPatternUtils.requireStrongAuth(
                     LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
@@ -803,6 +817,7 @@
     }
 
     private void handleFaceLockoutReset() {
+        mFaceLockedOut = false;
         updateFaceListeningState();
     }
 
@@ -810,7 +825,7 @@
         boolean wasRunning = mFaceRunningState == BIOMETRIC_STATE_RUNNING;
         boolean isRunning = faceRunningState == BIOMETRIC_STATE_RUNNING;
         mFaceRunningState = faceRunningState;
-
+        if (DEBUG) Log.v(TAG, "Face State: " + mFaceRunningState);
         // Clients of KeyguardUpdateMonitor don't care about the internal state or about the
         // asynchronousness of the cancel cycle. So only notify them if the actualy running state
         // has changed.
@@ -1557,7 +1572,7 @@
                 (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
                 shouldListenForFingerprintAssistant() || (mKeyguardOccluded && mIsDreaming))
                 && !mSwitchingUser && !isFingerprintDisabled(getCurrentUser())
-                && !mKeyguardGoingAway;
+                && !mKeyguardGoingAway && !mFingerprintLockedOut;
     }
 
     private boolean shouldListenForFace() {
@@ -1565,7 +1580,7 @@
                 (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
                 shouldListenForFaceAssistant() || (mKeyguardOccluded && mIsDreaming))
                 && !mSwitchingUser && !isFaceDisabled(getCurrentUser())
-                && !mKeyguardGoingAway && mFaceSettingEnabledForUser;
+                && !mKeyguardGoingAway && !mFaceLockedOut && mFaceSettingEnabledForUser;
     }
 
 
diff --git a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
index 41b86a7..a07c5cb 100644
--- a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
@@ -96,9 +96,9 @@
     int getBouncerPromptReason();
 
     /**
-     * Invoked when the secondary display showing a keyguard window changes.
+     * Invoked when the secondary displays showing a keyguard window changes.
      */
-    void onSecondaryDisplayShowingChanged(int displayId);
+    void onSecondaryDisplayShowingChanged(int[] displayId);
 
     /**
      * Consumes a message that was enqueued to be displayed on the next time the bouncer shows up.
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 494880e..b7844bc 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -22,6 +22,7 @@
 import android.os.Looper;
 import android.os.Process;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.view.IWindowManager;
@@ -33,14 +34,17 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.Preconditions;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.appops.AppOpsControllerImpl;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.PluginInitializerImpl;
 import com.android.systemui.plugins.PluginDependencyProvider;
+import com.android.systemui.plugins.PluginInitializerImpl;
+import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.plugins.PluginManagerImpl;
 import com.android.systemui.plugins.VolumeDialogController;
@@ -49,18 +53,17 @@
 import com.android.systemui.power.PowerNotificationWarnings;
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.notification.AppOpsListener;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
+import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
 import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
@@ -301,7 +304,8 @@
                 new PluginDependencyProvider(get(PluginManager.class)));
 
         mProviders.put(LocalBluetoothManager.class, () ->
-                LocalBluetoothManager.create(mContext, getDependency(BG_HANDLER)));
+                LocalBluetoothManager.create(mContext, getDependency(BG_HANDLER),
+                        UserHandle.ALL));
 
         mProviders.put(VolumeDialogController.class, () ->
                 new VolumeDialogControllerImpl(mContext));
@@ -336,8 +340,6 @@
 
         mProviders.put(EnhancedEstimates.class, () -> new EnhancedEstimatesImpl());
 
-        mProviders.put(AppOpsListener.class, () -> new AppOpsListener(mContext));
-
         mProviders.put(VibratorHelper.class, () -> new VibratorHelper(mContext));
 
         mProviders.put(IStatusBarService.class, () -> IStatusBarService.Stub.asInterface(
@@ -357,6 +359,9 @@
 
         mProviders.put(InitController.class, InitController::new);
 
+        mProviders.put(AppOpsController.class, () ->
+                new AppOpsControllerImpl(mContext, getDependency(BG_LOOPER)));
+
         // Put all dependencies above here so the factory can override them if it wants.
         SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
 
diff --git a/packages/SystemUI/src/com/android/systemui/InitController.java b/packages/SystemUI/src/com/android/systemui/InitController.java
index 52ba66a..81d3251 100644
--- a/packages/SystemUI/src/com/android/systemui/InitController.java
+++ b/packages/SystemUI/src/com/android/systemui/InitController.java
@@ -22,6 +22,11 @@
  */
 public class InitController {
 
+    /**
+     * If a task is added after all tasks are executed, then we've done something terribly wrong
+     */
+    private boolean mTasksExecuted = false;
+
     private final ArrayList<Runnable> mTasks = new ArrayList<>();
 
     /**
@@ -29,6 +34,9 @@
      * @param runnable the task to be executed
      */
     public void addPostInitTask(Runnable runnable) {
+        if (mTasksExecuted) {
+            throw new IllegalStateException("post init tasks have already been executed!");
+        }
         mTasks.add(runnable);
     }
 
@@ -39,5 +47,7 @@
         while (!mTasks.isEmpty()) {
             mTasks.remove(0).run();
         }
+
+        mTasksExecuted = true;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
deleted file mode 100644
index 1bf8750..0000000
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ /dev/null
@@ -1,535 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui;
-
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_UP;
-import static android.view.MotionEvent.ACTION_CANCEL;
-
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
-import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.PatternMatcher;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.MotionEvent;
-import com.android.systemui.OverviewProxyService.OverviewProxyListener;
-import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.CallbackController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class to send information from overview to launcher with a binder.
- */
-public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable {
-
-    private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
-
-    public static final String TAG_OPS = "OverviewProxyService";
-    public static final boolean DEBUG_OVERVIEW_PROXY = false;
-    private static final long BACKOFF_MILLIS = 1000;
-    private static final long DEFERRED_CALLBACK_MILLIS = 5000;
-
-    // Max backoff caps at 5 mins
-    private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
-
-    // Default interaction flags if swipe up is disabled before connecting to launcher
-    private static final int DEFAULT_DISABLE_SWIPE_UP_STATE = FLAG_DISABLE_SWIPE_UP
-            | FLAG_SHOW_OVERVIEW_BUTTON;
-
-    private final Context mContext;
-    private final Handler mHandler;
-    private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
-    private final ComponentName mRecentsComponentName;
-    private final DeviceProvisionedController mDeviceProvisionedController
-            = Dependency.get(DeviceProvisionedController.class);
-    private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
-    private final Intent mQuickStepIntent;
-
-    private IOverviewProxy mOverviewProxy;
-    private int mConnectionBackoffAttempts;
-    private @InteractionType int mInteractionFlags;
-    private boolean mIsEnabled;
-    private int mCurrentBoundedUserId = -1;
-    private float mBackButtonAlpha;
-    private MotionEvent mStatusBarGestureDownEvent;
-
-    private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
-
-        public void startScreenPinning(int taskId) {
-            if (!verifyCaller("startScreenPinning")) {
-                return;
-            }
-            long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> {
-                    StatusBar statusBar = SysUiServiceProvider.getComponent(mContext,
-                            StatusBar.class);
-                    if (statusBar != null) {
-                        statusBar.showScreenPinningRequest(taskId, false /* allowCancel */);
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        public void onStatusBarMotionEvent(MotionEvent event) {
-            if (!verifyCaller("onStatusBarMotionEvent")) {
-                return;
-            }
-            long token = Binder.clearCallingIdentity();
-            try {
-                // TODO move this logic to message queue
-                mHandler.post(()->{
-                    StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
-                    if (bar != null) {
-                        bar.dispatchNotificationsPanelTouchEvent(event);
-
-                        int action = event.getActionMasked();
-                        if (action == ACTION_DOWN) {
-                            mStatusBarGestureDownEvent = MotionEvent.obtain(event);
-                        }
-                        if (action == ACTION_UP || action == ACTION_CANCEL) {
-                            mStatusBarGestureDownEvent.recycle();
-                            mStatusBarGestureDownEvent = null;
-                        }
-                        event.recycle();
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        public void onSplitScreenInvoked() {
-            if (!verifyCaller("onSplitScreenInvoked")) {
-                return;
-            }
-            long token = Binder.clearCallingIdentity();
-            try {
-                Divider divider = SysUiServiceProvider.getComponent(mContext, Divider.class);
-                if (divider != null) {
-                    divider.onDockedFirstAnimationFrame();
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        public void onOverviewShown(boolean fromHome) {
-            if (!verifyCaller("onOverviewShown")) {
-                return;
-            }
-            long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> {
-                    for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
-                        mConnectionCallbacks.get(i).onOverviewShown(fromHome);
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        public void setInteractionState(@InteractionType int flags) {
-            if (!verifyCaller("setInteractionState")) {
-                return;
-            }
-            long token = Binder.clearCallingIdentity();
-            try {
-                if (mInteractionFlags != flags) {
-                    mInteractionFlags = flags;
-                    mHandler.post(() -> {
-                        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
-                            mConnectionCallbacks.get(i).onInteractionFlagsChanged(flags);
-                        }
-                    });
-                }
-            } finally {
-                Prefs.putInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS, mInteractionFlags);
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        public Rect getNonMinimizedSplitScreenSecondaryBounds() {
-            if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) {
-                return null;
-            }
-            long token = Binder.clearCallingIdentity();
-            try {
-                Divider divider = SysUiServiceProvider.getComponent(mContext, Divider.class);
-                if (divider != null) {
-                    return divider.getView().getNonMinimizedSplitScreenSecondaryBounds();
-                }
-                return null;
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        public void setBackButtonAlpha(float alpha, boolean animate) {
-            if (!verifyCaller("setBackButtonAlpha")) {
-                return;
-            }
-            long token = Binder.clearCallingIdentity();
-            try {
-                mBackButtonAlpha = alpha;
-                mHandler.post(() -> {
-                    notifyBackButtonAlphaChanged(alpha, animate);
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        private boolean verifyCaller(String reason) {
-            final int callerId = Binder.getCallingUserHandle().getIdentifier();
-            if (callerId != mCurrentBoundedUserId) {
-                Log.w(TAG_OPS, "Launcher called sysui with invalid user: " + callerId + ", reason: "
-                        + reason);
-                return false;
-            }
-            return true;
-        }
-    };
-
-    private final Runnable mDeferredConnectionCallback = () -> {
-        Log.w(TAG_OPS, "Binder supposed established connection but actual connection to service "
-            + "timed out, trying again");
-        retryConnectionWithBackoff();
-    };
-
-    private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            updateEnabledState();
-
-            // When launcher service is disabled, reset interaction flags because it is inactive
-            if (!isEnabled()) {
-                mInteractionFlags = getDefaultInteractionFlags();
-                Prefs.remove(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS);
-            }
-
-            // Reconnect immediately, instead of waiting for resume to arrive.
-            startConnectionToCurrentUser();
-        }
-    };
-
-    private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            mHandler.removeCallbacks(mDeferredConnectionCallback);
-            mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
-            mConnectionBackoffAttempts = 0;
-            mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
-            // Listen for launcher's death
-            try {
-                service.linkToDeath(mOverviewServiceDeathRcpt, 0);
-            } catch (RemoteException e) {
-                Log.e(TAG_OPS, "Lost connection to launcher service", e);
-            }
-            try {
-                mOverviewProxy.onBind(mSysUiProxy);
-            } catch (RemoteException e) {
-                mCurrentBoundedUserId = -1;
-                Log.e(TAG_OPS, "Failed to call onBind()", e);
-            }
-            notifyConnectionChanged();
-        }
-
-        @Override
-        public void onNullBinding(ComponentName name) {
-            Log.w(TAG_OPS, "Null binding of '" + name + "', try reconnecting");
-            mCurrentBoundedUserId = -1;
-            retryConnectionWithBackoff();
-        }
-
-        @Override
-        public void onBindingDied(ComponentName name) {
-            Log.w(TAG_OPS, "Binding died of '" + name + "', try reconnecting");
-            mCurrentBoundedUserId = -1;
-            retryConnectionWithBackoff();
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            // Do nothing
-            mCurrentBoundedUserId = -1;
-        }
-    };
-
-    private final DeviceProvisionedListener mDeviceProvisionedCallback =
-                new DeviceProvisionedListener() {
-            @Override
-            public void onUserSetupChanged() {
-                if (mDeviceProvisionedController.isCurrentUserSetup()) {
-                    internalConnectToCurrentUser();
-                }
-            }
-
-            @Override
-            public void onUserSwitched() {
-                mConnectionBackoffAttempts = 0;
-                internalConnectToCurrentUser();
-            }
-        };
-
-    // This is the death handler for the binder from the launcher service
-    private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
-            = this::cleanupAfterDeath;
-
-    public OverviewProxyService(Context context) {
-        mContext = context;
-        mHandler = new Handler();
-        mConnectionBackoffAttempts = 0;
-        mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
-                com.android.internal.R.string.config_recentsComponentName));
-        mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
-                .setPackage(mRecentsComponentName.getPackageName());
-        mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS,
-                getDefaultInteractionFlags());
-
-        // Listen for the package update changes.
-        if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) {
-            updateEnabledState();
-            mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
-            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-            filter.addDataScheme("package");
-            filter.addDataSchemeSpecificPart(mRecentsComponentName.getPackageName(),
-                    PatternMatcher.PATTERN_LITERAL);
-            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-            mContext.registerReceiver(mLauncherStateChangedReceiver, filter);
-        }
-    }
-
-    public float getBackButtonAlpha() {
-        return mBackButtonAlpha;
-    }
-
-    public void cleanupAfterDeath() {
-        if (mStatusBarGestureDownEvent != null) {
-            mHandler.post(()-> {
-                StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
-                if (bar != null) {
-                    System.out.println("MERONG dispatchNotificationPanelTouchEvent");
-                    mStatusBarGestureDownEvent.setAction(MotionEvent.ACTION_CANCEL);
-                    bar.dispatchNotificationsPanelTouchEvent(mStatusBarGestureDownEvent);
-                    mStatusBarGestureDownEvent.recycle();
-                    mStatusBarGestureDownEvent = null;
-                }
-            });
-        }
-        startConnectionToCurrentUser();
-    }
-
-    public void startConnectionToCurrentUser() {
-        if (mHandler.getLooper() != Looper.myLooper()) {
-            mHandler.post(mConnectionRunnable);
-        } else {
-            internalConnectToCurrentUser();
-        }
-    }
-
-    private void internalConnectToCurrentUser() {
-        disconnectFromLauncherService();
-
-        // If user has not setup yet or already connected, do not try to connect
-        if (!mDeviceProvisionedController.isCurrentUserSetup() || !isEnabled()) {
-            Log.v(TAG_OPS, "Cannot attempt connection, is setup "
-                + mDeviceProvisionedController.isCurrentUserSetup() + ", is enabled "
-                + isEnabled());
-            return;
-        }
-        mHandler.removeCallbacks(mConnectionRunnable);
-        Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
-                .setPackage(mRecentsComponentName.getPackageName());
-        boolean bound = false;
-        try {
-            bound = mContext.bindServiceAsUser(launcherServiceIntent,
-                    mOverviewServiceConnection,
-                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
-                    UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
-        } catch (SecurityException e) {
-            Log.e(TAG_OPS, "Unable to bind because of security error", e);
-        }
-        if (bound) {
-            // Ensure that connection has been established even if it thinks it is bound
-            mHandler.postDelayed(mDeferredConnectionCallback, DEFERRED_CALLBACK_MILLIS);
-        } else {
-            // Retry after exponential backoff timeout
-            retryConnectionWithBackoff();
-        }
-    }
-
-    private void retryConnectionWithBackoff() {
-        if (mHandler.hasCallbacks(mConnectionRunnable)) {
-            return;
-        }
-        final long timeoutMs = (long) Math.min(
-                Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts), MAX_BACKOFF_MILLIS);
-        mHandler.postDelayed(mConnectionRunnable, timeoutMs);
-        mConnectionBackoffAttempts++;
-        Log.w(TAG_OPS, "Failed to connect on attempt " + mConnectionBackoffAttempts
-                + " will try again in " + timeoutMs + "ms");
-    }
-
-    @Override
-    public void addCallback(OverviewProxyListener listener) {
-        mConnectionCallbacks.add(listener);
-        listener.onConnectionChanged(mOverviewProxy != null);
-        listener.onInteractionFlagsChanged(mInteractionFlags);
-    }
-
-    @Override
-    public void removeCallback(OverviewProxyListener listener) {
-        mConnectionCallbacks.remove(listener);
-    }
-
-    public boolean shouldShowSwipeUpUI() {
-        return isEnabled() && ((mInteractionFlags & FLAG_DISABLE_SWIPE_UP) == 0);
-    }
-
-    public boolean isEnabled() {
-        return mIsEnabled;
-    }
-
-    public IOverviewProxy getProxy() {
-        return mOverviewProxy;
-    }
-
-    public int getInteractionFlags() {
-        return mInteractionFlags;
-    }
-
-    private void disconnectFromLauncherService() {
-        if (mOverviewProxy != null) {
-            mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
-            mContext.unbindService(mOverviewServiceConnection);
-            mOverviewProxy = null;
-            notifyBackButtonAlphaChanged(1f, false /* animate */);
-            notifyConnectionChanged();
-        }
-    }
-
-    private int getDefaultInteractionFlags() {
-        // If there is no settings available use device default or get it from settings
-        final boolean defaultState = getSwipeUpDefaultValue();
-        final boolean swipeUpEnabled = getSwipeUpSettingAvailable()
-                ? getSwipeUpEnabledFromSettings(defaultState)
-                : defaultState;
-        return swipeUpEnabled ? 0 : DEFAULT_DISABLE_SWIPE_UP_STATE;
-    }
-
-    private void notifyBackButtonAlphaChanged(float alpha, boolean animate) {
-        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
-            mConnectionCallbacks.get(i).onBackButtonAlphaChanged(alpha, animate);
-        }
-    }
-
-    private void notifyConnectionChanged() {
-        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
-            mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
-        }
-    }
-
-    public void notifyQuickStepStarted() {
-        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
-            mConnectionCallbacks.get(i).onQuickStepStarted();
-        }
-    }
-
-    public void notifyQuickScrubStarted() {
-        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
-            mConnectionCallbacks.get(i).onQuickScrubStarted();
-        }
-    }
-
-    private void updateEnabledState() {
-        mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
-                MATCH_DIRECT_BOOT_UNAWARE,
-                ActivityManagerWrapper.getInstance().getCurrentUserId()) != null;
-    }
-
-    private boolean getSwipeUpDefaultValue() {
-        return mContext.getResources()
-                .getBoolean(com.android.internal.R.bool.config_swipe_up_gesture_default);
-    }
-
-    private boolean getSwipeUpSettingAvailable() {
-        return mContext.getResources()
-                .getBoolean(com.android.internal.R.bool.config_swipe_up_gesture_setting_available);
-    }
-
-    private boolean getSwipeUpEnabledFromSettings(boolean defaultValue) {
-        return Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED, defaultValue ? 1 : 0) == 1;
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println(TAG_OPS + " state:");
-        pw.print("  recentsComponentName="); pw.println(mRecentsComponentName);
-        pw.print("  isConnected="); pw.println(mOverviewProxy != null);
-        pw.print("  isCurrentUserSetup="); pw.println(mDeviceProvisionedController
-                .isCurrentUserSetup());
-        pw.print("  connectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
-        pw.print("  interactionFlags="); pw.println(mInteractionFlags);
-
-        pw.print("  quickStepIntent="); pw.println(mQuickStepIntent);
-        pw.print("  quickStepIntentResolved="); pw.println(isEnabled());
-
-        final boolean swipeUpDefaultValue = getSwipeUpDefaultValue();
-        final boolean swipeUpEnabled = getSwipeUpEnabledFromSettings(swipeUpDefaultValue);
-        pw.print("  swipeUpSetting="); pw.println(swipeUpEnabled);
-        pw.print("  swipeUpSettingDefault="); pw.println(swipeUpDefaultValue);
-    }
-
-    public interface OverviewProxyListener {
-        default void onConnectionChanged(boolean isConnected) {}
-        default void onQuickStepStarted() {}
-        default void onInteractionFlagsChanged(@InteractionType int flags) {}
-        default void onOverviewShown(boolean fromHome) {}
-        default void onQuickScrubStarted() {}
-        default void onBackButtonAlphaChanged(float alpha, boolean animate) {}
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
deleted file mode 100644
index fb343f9..0000000
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2013 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.systemui;
-
-import android.graphics.Rect;
-import android.view.Display;
-import android.view.View;
-
-public interface RecentsComponent {
-    void showRecentApps(boolean triggeredFromAltTab);
-
-    /**
-     * Docks the top-most task and opens recents.
-     */
-    boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
-            int metricsDockAction);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 3fe9944..e3584cf 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -33,10 +33,11 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityEvent;
+
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 public class SwipeHelper implements Gefingerpoken {
     static final String TAG = "com.android.systemui.SwipeHelper";
@@ -604,13 +605,15 @@
                     }
                     // don't let items that can't be dismissed be dragged more than
                     // maxScrollDistance
-                    if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) {
+                    if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(mCurrView,
+                            delta > 0)) {
                         float size = getSize(mCurrView);
                         float maxScrollDistance = MAX_SCROLL_SIZE_FRACTION * size;
                         if (absDelta >= size) {
                             delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
                         } else {
-                            delta = maxScrollDistance * (float) Math.sin((delta/size)*(Math.PI/2));
+                            delta = maxScrollDistance * (float) Math.sin(
+                                    (delta / size) * (Math.PI / 2));
                         }
                     }
 
@@ -674,9 +677,11 @@
     }
 
     public boolean isDismissGesture(MotionEvent ev) {
+        float translation = getTranslation(mCurrView);
         return ev.getActionMasked() == MotionEvent.ACTION_UP
+                && !mFalsingManager.isUnlockingDisabled()
                 && !isFalseGesture(ev) && (swipedFastEnough() || swipedFarEnough())
-                && mCallback.canChildBeDismissed(mCurrView);
+                && mCallback.canChildBeDismissedInDirection(mCurrView, translation > 0);
     }
 
     public boolean isFalseGesture(MotionEvent ev) {
@@ -707,6 +712,16 @@
 
         boolean canChildBeDismissed(View v);
 
+        /**
+         * Returns true if the provided child can be dismissed by a swipe in the given direction.
+         *
+         * @param isRightOrDown {@code true} if the swipe direction is right or down,
+         *                      {@code false} if it is left or up.
+         */
+        default boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
+            return canChildBeDismissed(v);
+        }
+
         boolean isAntiFalsingNeeded();
 
         void onBeginDrag(View v);
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
index 69e347c9..46e004c 100644
--- a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.analytics;
 
+import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session;
+import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.PhoneEvent;
+
 import android.content.Context;
 import android.database.ContentObserver;
 import android.hardware.Sensor;
@@ -32,13 +35,15 @@
 import android.view.MotionEvent;
 import android.widget.Toast;
 
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.FalsingPlugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.PluginManager;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.PhoneEvent;
-
 /**
  * Tracks touch, sensor and phone events when the lockscreen is on. If the phone is unlocked
  * the data containing these events is saved to a file. This data is collected
@@ -53,6 +58,8 @@
     private static final String COLLECT_BAD_TOUCHES = "data_collector_collect_bad_touches";
     private static final String ALLOW_REJECTED_TOUCH_REPORTS =
             "data_collector_allow_rejected_touch_reports";
+    private static final String DISABLE_UNLOCKING_FOR_FALSING_COLLECTION =
+            "data_collector_disable_unlocking";
 
     private static final long TIMEOUT_MILLIS = 11000; // 11 seconds.
     public static final boolean DEBUG = false;
@@ -65,14 +72,16 @@
     private SensorLoggerSession mCurrentSession = null;
 
     private boolean mEnableCollector = false;
-    private boolean mTimeoutActive = false;
     private boolean mCollectBadTouches = false;
     private boolean mCornerSwiping = false;
     private boolean mTrackingStarted = false;
     private boolean mAllowReportRejectedTouch = false;
+    private boolean mDisableUnlocking = false;
 
     private static DataCollector sInstance = null;
 
+    private FalsingPlugin mFalsingPlugin = null;
+
     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
         @Override
         public void onChange(boolean selfChange) {
@@ -80,6 +89,16 @@
         }
     };
 
+    private final PluginListener mPluginListener = new PluginListener<FalsingPlugin>() {
+        public void onPluginConnected(FalsingPlugin plugin, Context context) {
+            mFalsingPlugin = plugin;
+        }
+
+        public void onPluginDisconnected(FalsingPlugin plugin) {
+            mFalsingPlugin = null;
+        }
+    };
+
     private DataCollector(Context context) {
         mContext = context;
 
@@ -98,7 +117,14 @@
                 mSettingsObserver,
                 UserHandle.USER_ALL);
 
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(DISABLE_UNLOCKING_FOR_FALSING_COLLECTION), false,
+                mSettingsObserver,
+                UserHandle.USER_ALL);
+
         updateConfiguration();
+
+        Dependency.get(PluginManager.class).addPluginListener(mPluginListener, FalsingPlugin.class);
     }
 
     public static DataCollector getInstance(Context context) {
@@ -118,6 +144,9 @@
         mAllowReportRejectedTouch = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
                 mContext.getContentResolver(),
                 ALLOW_REJECTED_TOUCH_REPORTS, 0);
+        mDisableUnlocking = mEnableCollector && Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
+                mContext.getContentResolver(),
+                DISABLE_UNLOCKING_FOR_FALSING_COLLECTION, 0);
     }
 
     private boolean sessionEntrypoint() {
@@ -144,7 +173,7 @@
         SensorLoggerSession session = mCurrentSession;
         mCurrentSession = null;
 
-        if (mEnableCollector) {
+        if (mEnableCollector || mDisableUnlocking) {
             session.end(System.currentTimeMillis(), result);
             queueSession(session);
         }
@@ -181,24 +210,28 @@
             @Override
             public void run() {
                 byte[] b = Session.toByteArray(currentSession.toProto());
-                String dir = mContext.getFilesDir().getAbsolutePath();
-                if (currentSession.getResult() != Session.SUCCESS) {
-                    if (!mCollectBadTouches) {
-                        return;
-                    }
-                    dir += "/bad_touches";
+
+                if (mFalsingPlugin != null) {
+                    mFalsingPlugin.dataCollected(currentSession.getResult() == Session.SUCCESS, b);
                 } else {
-                    dir += "/good_touches";
-                }
+                    String dir = mContext.getFilesDir().getAbsolutePath();
+                    if (currentSession.getResult() != Session.SUCCESS) {
+                        if (!mDisableUnlocking && !mCollectBadTouches) {
+                            return;
+                        }
+                        dir += "/bad_touches";
+                    } else if (!mDisableUnlocking) {
+                        dir += "/good_touches";
+                    }
 
-                File file = new File(dir);
-                file.mkdir();
-                File touch = new File(file, "trace_" + System.currentTimeMillis());
-
-                try {
-                    new FileOutputStream(touch).write(b);
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
+                    File file = new File(dir);
+                    file.mkdir();
+                    File touch = new File(file, "trace_" + System.currentTimeMillis());
+                    try {
+                        new FileOutputStream(touch).write(b);
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
                 }
             }
         });
@@ -208,19 +241,6 @@
     public synchronized void onSensorChanged(SensorEvent event) {
         if (isEnabled() && mCurrentSession != null) {
             mCurrentSession.addSensorEvent(event, System.nanoTime());
-            enforceTimeout();
-        }
-    }
-
-    private void enforceTimeout() {
-        if (mTimeoutActive) {
-            if (System.currentTimeMillis() - mCurrentSession.getStartTimestampMillis()
-                    > TIMEOUT_MILLIS) {
-                onSessionEnd(Session.UNKNOWN);
-                if (DEBUG) {
-                    Log.i(TAG, "Analytics timed out.");
-                }
-            }
         }
     }
 
@@ -233,9 +253,12 @@
      *         rejected touch report.
      */
     public boolean isEnabled() {
-        return mEnableCollector || mAllowReportRejectedTouch;
+        return mEnableCollector || mAllowReportRejectedTouch || mDisableUnlocking;
     }
 
+    public boolean isUnlockingDisabled() {
+        return mDisableUnlocking;
+    }
     /**
      * @return true if the full data set for data gathering should be collected - including
      *         extensive sensor data, which is is not normally included with rejected touch reports.
@@ -450,7 +473,6 @@
             }
             mCurrentSession.addMotionEvent(event);
             mCurrentSession.setTouchArea(width, height);
-            enforceTimeout();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
new file mode 100644
index 0000000..9f363f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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.systemui.appops;
+
+/**
+ * Item to store information of active applications using different APP OPS
+ */
+public class AppOpItem {
+
+    private int mCode;
+    private int mUid;
+    private String mPackageName;
+    private long mTimeStarted;
+
+    public AppOpItem(int code, int uid, String packageName, long timeStarted) {
+        this.mCode = code;
+        this.mUid = uid;
+        this.mPackageName = packageName;
+        this.mTimeStarted = timeStarted;
+    }
+
+    public int getCode() {
+        return mCode;
+    }
+
+    public int getUid() {
+        return mUid;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public long getTimeStarted() {
+        return mTimeStarted;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java
new file mode 100644
index 0000000..4966fc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 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.systemui.appops;
+
+import java.util.List;
+
+/**
+ * Controller to keep track of applications that have requested access to given App Ops.
+ *
+ * It can be subscribed to with callbacks. Additionally, it passes on the information to
+ * NotificationPresenter to be displayed to the user.
+ */
+public interface AppOpsController {
+
+    /**
+     * Callback to notify when the state of active AppOps tracked by the controller has changed
+     */
+    interface Callback {
+        void onActiveStateChanged(int code, int uid, String packageName, boolean active);
+    }
+
+    /**
+     * Adds a callback that will get notified when an AppOp of the type the controller tracks
+     * changes
+     *
+     * @param opsCodes App Ops the callback was interested in checking
+     * @param cb Callback to report changes
+     *
+     * @see #removeCallback(int[], Callback)
+     */
+    void addCallback(int[] opsCodes, Callback cb);
+
+    /**
+     * Removes a callback from those notifified when an AppOp of the type the controller tracks
+     * changes
+     *
+     * @param opsCodes App Ops the callback is interested in checking
+     * @param cb Callback to stop reporting changes
+     *
+     * @see #addCallback(int[], Callback)
+     */
+    void removeCallback(int[] opsCodes, Callback cb);
+
+    /**
+     * Returns a copy of the list containing all the active AppOps that the controller tracks.
+     *
+     * @return List of active AppOps information
+     */
+    List<AppOpItem> getActiveAppOps();
+
+    /**
+     * Returns a copy of the list containing all the active AppOps that the controller tracks, for
+     * a given user id.
+     *
+     * @param userId User id to track
+     *
+     * @return List of active AppOps information for that user id
+     */
+    List<AppOpItem> getActiveAppOpsForUser(int userId);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
new file mode 100644
index 0000000..906a210
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2018 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.systemui.appops;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Controller to keep track of applications that have requested access to given App Ops
+ *
+ * It can be subscribed to with callbacks. Additionally, it passes on the information to
+ * NotificationPresenter to be displayed to the user.
+ */
+public class AppOpsControllerImpl implements AppOpsController,
+        AppOpsManager.OnOpActiveChangedListener {
+
+    private static final long LOCATION_TIME_DELAY_MS = 5000;
+    private static final String TAG = "AppOpsControllerImpl";
+    private static final boolean DEBUG = false;
+    private final Context mContext;
+
+    protected final AppOpsManager mAppOps;
+    private final H mBGHandler;
+    private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
+    private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
+    @GuardedBy("mActiveItems")
+    private final List<AppOpItem> mActiveItems = new ArrayList<>();
+
+    protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA,
+            AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+            AppOpsManager.OP_RECORD_AUDIO,
+            AppOpsManager.OP_COARSE_LOCATION,
+            AppOpsManager.OP_FINE_LOCATION};
+
+    public AppOpsControllerImpl(Context context, Looper bgLooper) {
+        mContext = context;
+        mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        mBGHandler = new H(bgLooper);
+        final int numOps = OPS.length;
+        for (int i = 0; i < numOps; i++) {
+            mCallbacksByCode.put(OPS[i], new ArraySet<>());
+        }
+    }
+
+    @VisibleForTesting
+    protected void setListening(boolean listening) {
+        if (listening) {
+            mAppOps.startWatchingActive(OPS, this);
+        } else {
+            mAppOps.stopWatchingActive(this);
+        }
+    }
+
+    /**
+     * Adds a callback that will get notifified when an AppOp of the type the controller tracks
+     * changes
+     *
+     * @param callback Callback to report changes
+     * @param opsCodes App Ops the callback is interested in checking
+     *
+     * @see #removeCallback(int[], Callback)
+     */
+    @Override
+    public void addCallback(int[] opsCodes, AppOpsController.Callback callback) {
+        boolean added = false;
+        final int numCodes = opsCodes.length;
+        for (int i = 0; i < numCodes; i++) {
+            if (mCallbacksByCode.containsKey(opsCodes[i])) {
+                mCallbacksByCode.get(opsCodes[i]).add(callback);
+                added = true;
+            } else {
+                if (DEBUG) Log.wtf(TAG, "APP_OP " + opsCodes[i] + " not supported");
+            }
+        }
+        if (added) mCallbacks.add(callback);
+        if (!mCallbacks.isEmpty()) setListening(true);
+    }
+
+    /**
+     * Removes a callback from those notified when an AppOp of the type the controller tracks
+     * changes
+     *
+     * @param callback Callback to stop reporting changes
+     * @param opsCodes App Ops the callback was interested in checking
+     *
+     * @see #addCallback(int[], Callback)
+     */
+    @Override
+    public void removeCallback(int[] opsCodes, AppOpsController.Callback callback) {
+        final int numCodes = opsCodes.length;
+        for (int i = 0; i < numCodes; i++) {
+            if (mCallbacksByCode.containsKey(opsCodes[i])) {
+                mCallbacksByCode.get(opsCodes[i]).remove(callback);
+            }
+        }
+        mCallbacks.remove(callback);
+        if (mCallbacks.isEmpty()) setListening(false);
+    }
+
+    private AppOpItem getAppOpItem(int code, int uid, String packageName) {
+        final int itemsQ = mActiveItems.size();
+        for (int i = 0; i < itemsQ; i++) {
+            AppOpItem item = mActiveItems.get(i);
+            if (item.getCode() == code && item.getUid() == uid
+                    && item.getPackageName().equals(packageName)) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    private boolean updateActives(int code, int uid, String packageName, boolean active) {
+        synchronized (mActiveItems) {
+            AppOpItem item = getAppOpItem(code, uid, packageName);
+            if (item == null && active) {
+                item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
+                mActiveItems.add(item);
+                if (code == AppOpsManager.OP_COARSE_LOCATION
+                        || code == AppOpsManager.OP_FINE_LOCATION) {
+                    mBGHandler.scheduleRemoval(item, LOCATION_TIME_DELAY_MS);
+                }
+                if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
+                return true;
+            } else if (item != null && !active) {
+                mActiveItems.remove(item);
+                if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
+                return true;
+            } else if (item != null && active
+                    && (code == AppOpsManager.OP_COARSE_LOCATION
+                            || code == AppOpsManager.OP_FINE_LOCATION)) {
+                mBGHandler.scheduleRemoval(item, LOCATION_TIME_DELAY_MS);
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Returns a copy of the list containing all the active AppOps that the controller tracks.
+     *
+     * @return List of active AppOps information
+     */
+    public List<AppOpItem> getActiveAppOps() {
+        synchronized (mActiveItems) {
+            return new ArrayList<>(mActiveItems);
+        }
+    }
+
+    /**
+     * Returns a copy of the list containing all the active AppOps that the controller tracks, for
+     * a given user id.
+     *
+     * @param userId User id to track
+     *
+     * @return List of active AppOps information for that user id
+     */
+    public List<AppOpItem> getActiveAppOpsForUser(int userId) {
+        List<AppOpItem> list = new ArrayList<>();
+        synchronized (mActiveItems) {
+            final int numActiveItems = mActiveItems.size();
+            for (int i = 0; i < numActiveItems; i++) {
+                AppOpItem item = mActiveItems.get(i);
+                if (UserHandle.getUserId(item.getUid()) == userId) {
+                    list.add(item);
+                }
+            }
+        }
+        return list;
+    }
+
+    @Override
+    public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
+        if (updateActives(code, uid, packageName, active)) {
+            for (Callback cb: mCallbacksByCode.get(code)) {
+                cb.onActiveStateChanged(code, uid, packageName, active);
+            }
+        }
+    }
+
+    private final class H extends Handler {
+        H(Looper looper) {
+            super(looper);
+        }
+
+        public void scheduleRemoval(AppOpItem item, long timeToRemoval) {
+            removeCallbacksAndMessages(item);
+            postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    onOpActiveChanged(item.getCode(), item.getUid(),
+                            item.getPackageName(), false);
+                }
+            }, item, timeToRemoval);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index 8fc4689..67bc8b6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.IBiometricPromptReceiver;
@@ -53,6 +54,7 @@
     private static final int MSG_BUTTON_POSITIVE = 8;
 
     private Map<Integer, BiometricDialogView> mDialogs; // BiometricAuthenticator type, view
+    private SomeArgs mCurrentDialogArgs;
     private BiometricDialogView mCurrentDialog;
     private WindowManager mWindowManager;
     private IBiometricPromptReceiver mReceiver;
@@ -64,7 +66,7 @@
         public void handleMessage(Message msg) {
             switch(msg.what) {
                 case MSG_SHOW_DIALOG:
-                    handleShowDialog((SomeArgs) msg.obj);
+                    handleShowDialog((SomeArgs) msg.obj, false /* skipAnimation */);
                     break;
                 case MSG_BIOMETRIC_AUTHENTICATED:
                     handleBiometricAuthenticated();
@@ -116,6 +118,15 @@
 
     @Override
     public void start() {
+        createDialogs();
+
+        if (!mDialogs.isEmpty()) {
+            getComponent(CommandQueue.class).addCallbacks(this);
+            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+        }
+    }
+
+    private void createDialogs() {
         final PackageManager pm = mContext.getPackageManager();
         mDialogs = new HashMap<>();
         if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
@@ -125,11 +136,6 @@
             mDialogs.put(BiometricAuthenticator.TYPE_FINGERPRINT,
                     new FingerprintDialogView(mContext, mCallback));
         }
-
-        if (!mDialogs.isEmpty()) {
-            getComponent(CommandQueue.class).addCallbacks(this);
-            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        }
     }
 
     @Override
@@ -172,7 +178,8 @@
         mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
     }
 
-    private void handleShowDialog(SomeArgs args) {
+    private void handleShowDialog(SomeArgs args, boolean skipAnimation) {
+        mCurrentDialogArgs = args;
         final int type = args.argi1;
         mCurrentDialog = mDialogs.get(type);
 
@@ -188,6 +195,7 @@
         mReceiver = (IBiometricPromptReceiver) args.arg2;
         mCurrentDialog.setBundle((Bundle)args.arg1);
         mCurrentDialog.setRequireConfirmation((boolean)args.arg3);
+        mCurrentDialog.setSkipIntro(skipAnimation);
         mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
         mDialogShowing = true;
     }
@@ -268,4 +276,18 @@
     private void handleUserCanceled() {
         handleHideDialog(true /* userCanceled */);
     }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        final boolean wasShowing = mDialogShowing;
+        if (mDialogShowing) {
+            mCurrentDialog.forceRemove();
+            mDialogShowing = false;
+        }
+        createDialogs();
+        if (wasShowing) {
+            handleShowDialog(mCurrentDialogArgs, true /* skipAnimation */);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index 7d77929..1faae9d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -17,7 +17,7 @@
 package com.android.systemui.biometrics;
 
 import android.content.Context;
-import android.graphics.Color;
+import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
 import android.hardware.biometrics.BiometricPrompt;
 import android.os.Binder;
@@ -42,6 +42,7 @@
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.util.leak.RotationUtils;
 
 /**
  * Abstract base class. Shows a dialog for BiometricPrompt.
@@ -66,7 +67,7 @@
     private final float mAnimationTranslationOffset;
     private final int mErrorColor;
     private final int mTextColor;
-    private final float mDisplayWidth;
+    private final float mDialogWidth;
     private final DialogViewCallback mCallback;
 
     private ViewGroup mLayout;
@@ -76,6 +77,7 @@
     private int mLastState;
     private boolean mAnimatingAway;
     private boolean mWasForceRemoved;
+    private boolean mSkipIntro;
     protected boolean mRequireConfirmation;
 
     protected abstract void updateIcon(int lastState, int newState);
@@ -122,14 +124,16 @@
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mAnimationTranslationOffset = getResources()
                 .getDimension(R.dimen.biometric_dialog_animation_translation_offset);
-        mErrorColor = Color.parseColor(
-                getResources().getString(R.color.biometric_dialog_error_color));
-        mTextColor = Color.parseColor(
-                getResources().getString(R.color.biometric_dialog_text_light_color));
+
+        TypedArray array = getContext().obtainStyledAttributes(
+                new int[]{android.R.attr.colorError, android.R.attr.textColorSecondary});
+        mErrorColor = array.getColor(0, 0);
+        mTextColor = array.getColor(1, 0);
+        array.recycle();
 
         DisplayMetrics metrics = new DisplayMetrics();
         mWindowManager.getDefaultDisplay().getMetrics(metrics);
-        mDisplayWidth = metrics.widthPixels;
+        mDialogWidth = Math.min(metrics.widthPixels, metrics.heightPixels);
 
         // Create the dialog
         LayoutInflater factory = LayoutInflater.from(getContext());
@@ -167,7 +171,6 @@
         final ImageView icon = mLayout.findViewById(R.id.biometric_icon);
 
         icon.setContentDescription(getResources().getString(getIconDescriptionResourceId()));
-        mErrorText.setText(getResources().getString(getHintStringResourceId()));
 
         setDismissesDialog(space);
         setDismissesDialog(leftSpace);
@@ -189,13 +192,17 @@
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
+        mErrorText.setText(getHintStringResourceId());
+
         final TextView title = mLayout.findViewById(R.id.title);
         final TextView subtitle = mLayout.findViewById(R.id.subtitle);
         final TextView description = mLayout.findViewById(R.id.description);
         final Button negative = mLayout.findViewById(R.id.button2);
         final Button positive = mLayout.findViewById(R.id.button1);
 
-        mDialog.getLayoutParams().width = (int) mDisplayWidth;
+        if (RotationUtils.getRotation(mContext) != RotationUtils.ROTATION_NONE) {
+            mDialog.getLayoutParams().width = (int) mDialogWidth;
+        }
 
         mLastState = STATE_NONE;
         updateState(STATE_AUTHENTICATING);
@@ -225,20 +232,21 @@
 
         negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
 
-        if (!mWasForceRemoved) {
-            // Dim the background and slide the dialog up
-            mDialog.setTranslationY(mAnimationTranslationOffset);
-            mLayout.setAlpha(0f);
-            postOnAnimation(mShowAnimationRunnable);
-        } else {
+        if (mWasForceRemoved || mSkipIntro) {
             // Show the dialog immediately
             mLayout.animate().cancel();
             mDialog.animate().cancel();
             mDialog.setAlpha(1.0f);
             mDialog.setTranslationY(0);
             mLayout.setAlpha(1.0f);
+        } else {
+            // Dim the background and slide the dialog up
+            mDialog.setTranslationY(mAnimationTranslationOffset);
+            mLayout.setAlpha(0f);
+            postOnAnimation(mShowAnimationRunnable);
         }
         mWasForceRemoved = false;
+        mSkipIntro = false;
     }
 
     private void setDismissesDialog(View v) {
@@ -293,6 +301,13 @@
         mWasForceRemoved = true;
     }
 
+    /**
+     * Skip the intro animation
+     */
+    public void setSkipIntro(boolean skip) {
+        mSkipIntro = skip;
+    }
+
     public boolean isAnimatingAway() {
         return mAnimatingAway;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 3d578c3..2c61da3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -201,6 +201,9 @@
         return mHumanInteractionClassifier.isEnabled() || mDataCollector.isEnabled();
     }
 
+    public boolean isUnlockingDisabled() {
+        return mDataCollector.isUnlockingDisabled();
+    }
     /**
      * @return true if the classifier determined that this is not a human interacting with the phone
      */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
index dcb3882..30dfd36 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
@@ -20,5 +20,19 @@
  * Interface for class that cares about doze states.
  */
 public interface DozeReceiver {
+
+    /**
+     * If device enters or leaves doze mode
+     */
     void setDozing(boolean dozing);
+
+    /**
+     * Invoked every time a minute is elapsed in doze mode
+     */
+    void dozeTimeTick();
+
+    /**
+     * When view is double tapped in doze mode.
+     */
+    void onDozeDoubleTap();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index c566460..5d99c57 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -141,7 +141,7 @@
     }
 
     private void updateBrightnessAndReady() {
-        if (mRegistered) {
+        if (mRegistered || mDebugBrightnessBucket != -1) {
             int sensorValue = mDebugBrightnessBucket == -1
                     ? mLastSensorValue : mDebugBrightnessBucket;
             int brightness = computeBrightness(sensorValue);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 512cd82..dc7b1ef 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -82,6 +82,7 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.util.EmergencyAffordanceManager;
+import com.android.internal.util.ScreenRecordHelper;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.Dependency;
@@ -158,6 +159,7 @@
     private final boolean mShowSilentToggle;
     private final EmergencyAffordanceManager mEmergencyAffordanceManager;
     private final ScreenshotHelper mScreenshotHelper;
+    private final ScreenRecordHelper mScreenRecordHelper;
 
     /**
      * @param context everything needs a context :(
@@ -199,6 +201,7 @@
 
         mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
         mScreenshotHelper = new ScreenshotHelper(context);
+        mScreenRecordHelper = new ScreenRecordHelper(context);
 
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
@@ -522,7 +525,7 @@
     }
 
 
-    private class ScreenshotAction extends SinglePressAction {
+    private class ScreenshotAction extends SinglePressAction implements LongPressAction {
         public ScreenshotAction() {
             super(R.drawable.ic_screenshot, R.string.global_action_screenshot);
         }
@@ -552,6 +555,16 @@
         public boolean showBeforeProvisioning() {
             return false;
         }
+
+        @Override
+        public boolean onLongPress() {
+            if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SCREENRECORD_LONG_PRESS)) {
+                mScreenRecordHelper.launchRecordPrompt();
+            } else {
+                onPress();
+            }
+            return true;
+        }
     }
 
     private class BugReportAction extends SinglePressAction implements LongPressAction {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 4988f07..fe1b356 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -17,7 +17,6 @@
 package com.android.systemui.keyguard;
 
 import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
-import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.internal.telephony.IccCardConstants.State.ABSENT;
 import static com.android.internal.telephony.IccCardConstants.State.PIN_REQUIRED;
@@ -95,6 +94,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Mediates requests related to the keyguard.  This includes queries about the
@@ -246,8 +246,8 @@
     // AOD is enabled and status bar is in AOD state.
     private boolean mAodShowing;
 
-    // display id of the secondary display on which we have put a keyguard window
-    private int mSecondaryDisplayShowing = INVALID_DISPLAY;
+    // display ids of the external display on which we have put a keyguard window
+    private int[] mSecondaryDisplaysShowing;
 
     /** Cached value of #isInputRestricted */
     private boolean mInputRestricted;
@@ -700,9 +700,9 @@
         }
 
         @Override
-        public void onSecondaryDisplayShowingChanged(int displayId) {
+        public void onSecondaryDisplayShowingChanged(int[] displayIds) {
             synchronized (KeyguardViewMediator.this) {
-                setShowingLocked(mShowing, mAodShowing, displayId, false);
+                setShowingLocked(mShowing, mAodShowing, displayIds, false);
             }
         }
     };
@@ -749,10 +749,10 @@
             setShowingLocked(!shouldWaitForProvisioning()
                     && !mLockPatternUtils.isLockScreenDisabled(
                             KeyguardUpdateMonitor.getCurrentUser()),
-                    mAodShowing, mSecondaryDisplayShowing, true /* forceCallbacks */);
+                    mAodShowing, mSecondaryDisplaysShowing, true /* forceCallbacks */);
         } else {
             // The system's keyguard is disabled or missing.
-            setShowingLocked(false, mAodShowing, mSecondaryDisplayShowing, true);
+            setShowingLocked(false, mAodShowing, mSecondaryDisplaysShowing, true);
         }
 
         mStatusBarKeyguardViewManager =
@@ -1776,11 +1776,11 @@
     }
 
     private void updateActivityLockScreenState(boolean showing, boolean aodShowing,
-            int secondaryDisplayShowing) {
+            int[] secondaryDisplaysShowing) {
         mUiOffloadThread.submit(() -> {
             try {
                 ActivityTaskManager.getService().setLockScreenShown(showing, aodShowing,
-                        secondaryDisplayShowing);
+                        secondaryDisplaysShowing);
             } catch (RemoteException e) {
             }
         });
@@ -1895,7 +1895,8 @@
 
             if (!mHiding) {
                 // Tell ActivityManager that we canceled the keyguardExitAnimation.
-                setShowingLocked(mShowing, mAodShowing, mSecondaryDisplayShowing, true /* force */);
+                setShowingLocked(mShowing, mAodShowing, mSecondaryDisplaysShowing,
+                        true /* force */);
                 return;
             }
             mHiding = false;
@@ -2164,22 +2165,23 @@
     }
 
     private void setShowingLocked(boolean showing, boolean aodShowing) {
-        setShowingLocked(showing, aodShowing, mSecondaryDisplayShowing,
+        setShowingLocked(showing, aodShowing, mSecondaryDisplaysShowing,
                 false /* forceCallbacks */);
     }
 
-    private void setShowingLocked(boolean showing, boolean aodShowing, int secondaryDisplayShowing,
-            boolean forceCallbacks) {
+    private void setShowingLocked(boolean showing, boolean aodShowing,
+            int[] secondaryDisplaysShowing, boolean forceCallbacks) {
         final boolean notifyDefaultDisplayCallbacks = showing != mShowing
                 || aodShowing != mAodShowing || forceCallbacks;
-        if (notifyDefaultDisplayCallbacks || secondaryDisplayShowing != mSecondaryDisplayShowing) {
+        if (notifyDefaultDisplayCallbacks
+                || !Arrays.equals(secondaryDisplaysShowing, mSecondaryDisplaysShowing)) {
             mShowing = showing;
             mAodShowing = aodShowing;
-            mSecondaryDisplayShowing = secondaryDisplayShowing;
+            mSecondaryDisplaysShowing = secondaryDisplaysShowing;
             if (notifyDefaultDisplayCallbacks) {
                 notifyDefaultDisplayCallbacks(showing);
             }
-            updateActivityLockScreenState(showing, aodShowing, secondaryDisplayShowing);
+            updateActivityLockScreenState(showing, aodShowing, secondaryDisplaysShowing);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
index 36dbb0f..e0fc31b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
@@ -21,9 +21,11 @@
 
 import java.io.PrintWriter;
 
-public interface  BasePipManager {
+public interface BasePipManager {
     void initialize(Context context);
     void showPictureInPictureMenu();
+    default void expandPip() {}
+    default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {}
     void onConfigurationChanged(Configuration newConfig);
-    void dump(PrintWriter pw);
+    default void dump(PrintWriter pw) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
index 864a6f9..70b581a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -24,8 +24,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import com.android.systemui.SystemUI;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.component.ExpandPipEvent;
 import com.android.systemui.statusbar.CommandQueue;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -68,7 +66,11 @@
     }
 
     public void expandPip() {
-        EventBus.getDefault().send(new ExpandPipEvent());
+        mPipManager.expandPip();
+    }
+
+    public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {
+        mPipManager.hidePipMenu(onStartCallback, onEndCallback);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 04746c1..08208e5 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -38,8 +38,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.pip.BasePipManager;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.component.ExpandPipEvent;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -197,7 +195,6 @@
                 mMenuController, mInputConsumerController);
         mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
                 mTouchHandler.getMotionHelper());
-        EventBus.getDefault().register(this);
     }
 
     /**
@@ -210,11 +207,20 @@
     /**
      * Expands the PIP.
      */
-    public final void onBusEvent(ExpandPipEvent event) {
+    @Override
+    public void expandPip() {
         mTouchHandler.getMotionHelper().expandPip(false /* skipAnimation */);
     }
 
     /**
+     * Hides the PIP menu.
+     */
+    @Override
+    public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {
+        mMenuController.hideMenu(onStartCallback, onEndCallback);
+    }
+
+    /**
      * Sent from KEYCODE_WINDOW handler in PhoneWindowManager, to request the menu to be shown.
      */
     public void showPictureInPictureMenu() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 2dc531a..b746c19 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -72,8 +72,6 @@
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.component.HidePipMenuEvent;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -151,7 +149,7 @@
                     cancelDelayedFinish();
                     break;
                 case MESSAGE_HIDE_MENU:
-                    hideMenu();
+                    hideMenu((Runnable) msg.obj);
                     break;
                 case MESSAGE_UPDATE_ACTIONS: {
                     final Bundle data = (Bundle) msg.obj;
@@ -275,7 +273,6 @@
         super.onStop();
 
         cancelDelayedFinish();
-        EventBus.getDefault().unregister(this);
     }
 
     @Override
@@ -335,19 +332,6 @@
         // Do nothing
     }
 
-    public final void onBusEvent(HidePipMenuEvent event) {
-        if (mMenuState != MENU_STATE_NONE) {
-            // If the menu is visible in either the closed or full state, then hide the menu and
-            // trigger the animation trigger afterwards
-            event.getAnimationTrigger().increment();
-            hideMenu(() -> {
-                mHandler.post(() -> {
-                    event.getAnimationTrigger().decrement();
-                });
-            }, true /* notifyMenuVisibility */, false /* isDismissing */);
-        }
-    }
-
     private void showMenu(int menuState, Rect stackBounds, Rect movementBounds,
             boolean allowMenuTimeout, boolean resizeMenuOnShow) {
         mAllowMenuTimeout = allowMenuTimeout;
@@ -398,8 +382,11 @@
     }
 
     private void hideMenu() {
-        hideMenu(null /* animationFinishedRunnable */, true /* notifyMenuVisibility */,
-                false /* isDismissing */);
+        hideMenu(null);
+    }
+
+    private void hideMenu(Runnable animationEndCallback) {
+        hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false /* isDismissing */);
     }
 
     private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
@@ -450,9 +437,6 @@
         }
         notifyActivityCallback(mMessenger);
 
-        // Register for HidePipMenuEvents once we notify the controller of this activity
-        EventBus.getDefault().register(this);
-
         ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS);
         if (actions != null) {
             mActions.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 360fe73..56b8324 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -19,9 +19,9 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
-import android.app.ActivityTaskManager;
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
 import android.app.IActivityManager;
 import android.app.RemoteAction;
 import android.content.Context;
@@ -37,14 +37,8 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Log;
-import android.view.IWindowManager;
-
 import com.android.systemui.pip.phone.PipMediaController.ActionListener;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.component.HidePipMenuEvent;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.shared.system.InputConsumerController;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -131,7 +125,7 @@
     // The dismiss fraction update is sent frequently, so use a temporary bundle for the message
     private Bundle mTmpDismissFractionData = new Bundle();
 
-    private ReferenceCountedTrigger mOnAttachDecrementTrigger;
+    private Runnable mOnAnimationEndRunnable;
     private boolean mStartActivityRequested;
     private long mStartActivityRequestedTime;
     private Messenger mToActivityMessenger;
@@ -171,9 +165,9 @@
                 case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
                     mToActivityMessenger = msg.replyTo;
                     setStartActivityRequested(false);
-                    if (mOnAttachDecrementTrigger != null) {
-                        mOnAttachDecrementTrigger.decrement();
-                        mOnAttachDecrementTrigger = null;
+                    if (mOnAnimationEndRunnable != null) {
+                        mOnAnimationEndRunnable.run();
+                        mOnAnimationEndRunnable = null;
                     }
                     // Mark the menu as invisible once the activity finishes as well
                     if (mToActivityMessenger == null) {
@@ -188,9 +182,9 @@
 
     private Runnable mStartActivityRequestedTimeoutRunnable = () -> {
         setStartActivityRequested(false);
-        if (mOnAttachDecrementTrigger != null) {
-            mOnAttachDecrementTrigger.decrement();
-            mOnAttachDecrementTrigger = null;
+        if (mOnAnimationEndRunnable != null) {
+            mOnAnimationEndRunnable.run();
+            mOnAnimationEndRunnable = null;
         }
         Log.e(TAG, "Expected start menu activity request timed out");
     };
@@ -209,8 +203,6 @@
         mActivityManager = activityManager;
         mMediaController = mediaController;
         mInputConsumerController = inputConsumerController;
-
-        EventBus.getDefault().register(this);
     }
 
     public boolean isMenuActivityVisible() {
@@ -353,6 +345,36 @@
     }
 
     /**
+     * Hides the menu activity.
+     */
+    public void hideMenu(Runnable onStartCallback, Runnable onEndCallback) {
+        if (mStartActivityRequested) {
+            // If the menu has been start-requested, but not actually started, then we defer the
+            // trigger callback until the menu has started and called back to the controller.
+            mOnAnimationEndRunnable = onEndCallback;
+            onStartCallback.run();
+
+            // Fallback for b/63752800, we have started the PipMenuActivity but it has not made any
+            // callbacks. Don't continue to wait for the menu to show past some timeout.
+            mHandler.removeCallbacks(mStartActivityRequestedTimeoutRunnable);
+            mHandler.postDelayed(mStartActivityRequestedTimeoutRunnable,
+                    START_ACTIVITY_REQUEST_TIMEOUT_MS);
+        } else if (mMenuState != MENU_STATE_NONE && mToActivityMessenger != null) {
+            // If the menu is visible in either the closed or full state, then hide the menu and
+            // trigger the animation trigger afterwards
+            onStartCallback.run();
+            Message m = Message.obtain();
+            m.what = PipMenuActivity.MESSAGE_HIDE_MENU;
+            m.obj = onEndCallback;
+            try {
+                mToActivityMessenger.send(m);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not notify hide menu", e);
+            }
+        }
+    }
+
+    /**
      * Preemptively mark the menu as invisible, used when we are directly manipulating the pinned
      * stack and don't want to trigger a resize which can animate the stack in a conflicting way
      * (ie. when manually expanding or dismissing).
@@ -496,21 +518,6 @@
         mStartActivityRequestedTime = requested ? SystemClock.uptimeMillis() : 0;
     }
 
-    public final void onBusEvent(HidePipMenuEvent event) {
-        if (mStartActivityRequested) {
-            // If the menu has been start-requested, but not actually started, then we defer the
-            // trigger callback until the menu has started and called back to the controller.
-            mOnAttachDecrementTrigger = event.getAnimationTrigger();
-            mOnAttachDecrementTrigger.increment();
-
-            // Fallback for b/63752800, we have started the PipMenuActivity but it has not made any
-            // callbacks. Don't continue to wait for the menu to show past some timeout.
-            mHandler.removeCallbacks(mStartActivityRequestedTimeoutRunnable);
-            mHandler.postDelayed(mStartActivityRequestedTimeoutRunnable,
-                    START_ACTIVITY_REQUEST_TIMEOUT_MS);
-        }
-    }
-
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 43e9db7..e17e0bc 100755
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -758,9 +758,4 @@
             WindowManagerWrapper.getInstance().setPipVisibility(visible);
         });
     }
-
-    @Override
-    public void dump(PrintWriter pw) {
-        // Do nothing
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 568a039..35ae899 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -399,8 +399,8 @@
             if (b != null) {
                 mThermalService = IThermalService.Stub.asInterface(b);
                 try {
-                    mThermalService.registerThermalEventListener(
-                        new ThermalEventListener());
+                    mThermalService.registerThermalEventListenerWithType(
+                            new ThermalEventListener(), Temperature.TYPE_SKIN);
                 } catch (RemoteException e) {
                     // Should never happen.
                 }
@@ -552,7 +552,7 @@
 
     // Thermal event received from vendor thermal management subsystem
     private final class ThermalEventListener extends IThermalEventListener.Stub {
-        @Override public void notifyThrottling(boolean isThrottling, Temperature temp) {
+        @Override public void notifyThrottling(Temperature temp) {
             // Trigger an update of the temperature warning.  Only one
             // callback can be enabled at a time, so remove any existing
             // callback; updateTemperatureWarning will schedule another one.
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
new file mode 100644
index 0000000..3953139d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 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.systemui.privacy
+
+import android.app.ActivityManager
+import android.app.AppOpsManager
+import android.content.Context
+import android.graphics.Color
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.AttributeSet
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.systemui.Dependency
+import com.android.systemui.R
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+
+class OngoingPrivacyChip @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttrs: Int = 0,
+    defStyleRes: Int = 0
+) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) {
+
+    companion object {
+        val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
+                AppOpsManager.OP_RECORD_AUDIO,
+                AppOpsManager.OP_COARSE_LOCATION,
+                AppOpsManager.OP_FINE_LOCATION)
+    }
+
+    private lateinit var appName: TextView
+    private lateinit var iconsContainer: LinearLayout
+    private var privacyList = emptyList<PrivacyItem>()
+    private val appOpsController = Dependency.get(AppOpsController::class.java)
+    private val userManager = context.getSystemService(UserManager::class.java)
+    private val currentUser = ActivityManager.getCurrentUser()
+    private val currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+    private var listening = false
+
+    var builder = PrivacyDialogBuilder(context, privacyList)
+
+    private val callback = object : AppOpsController.Callback {
+        override fun onActiveStateChanged(
+            code: Int,
+            uid: Int,
+            packageName: String,
+            active: Boolean
+        ) {
+            val userId = UserHandle.getUserId(uid)
+            if (userId in currentUserIds) {
+                updatePrivacyList()
+            }
+        }
+    }
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+
+        appName = findViewById(R.id.app_name)
+        iconsContainer = findViewById(R.id.icons_container)
+    }
+
+    fun setListening(listen: Boolean) {
+        if (listening == listen) return
+        listening = listen
+        if (listening) {
+            appOpsController.addCallback(OPS, callback)
+            updatePrivacyList()
+        } else {
+            appOpsController.removeCallback(OPS, callback)
+        }
+    }
+
+    private fun updatePrivacyList() {
+        privacyList = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
+                .mapNotNull { toPrivacyItem(it) }
+        builder = PrivacyDialogBuilder(context, privacyList)
+        updateView()
+    }
+
+    private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
+        val type: PrivacyType = when (appOpItem.code) {
+            AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
+            AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
+            AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
+            AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
+            else -> return null
+        }
+        val app = PrivacyApplication(appOpItem.packageName, context)
+        return PrivacyItem(type, app, appOpItem.timeStarted)
+    }
+
+    // Should only be called if the builder icons or app changed
+    private fun updateView() {
+        fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) {
+            iconsContainer.removeAllViews()
+            dialogBuilder.generateIcons().forEach {
+                it.mutate()
+                it.setTint(Color.WHITE)
+                iconsContainer.addView(ImageView(context).apply {
+                    setImageDrawable(it)
+                    maxHeight = this@OngoingPrivacyChip.height
+                })
+            }
+        }
+
+        if (privacyList.isEmpty()) {
+            visibility = GONE
+            return
+        } else {
+            generateContentDescription()
+            visibility = VISIBLE
+            setIcons(builder, iconsContainer)
+            appName.visibility = GONE
+            builder.app?.let {
+                appName.apply {
+                    setText(it.applicationName)
+                    setTextColor(Color.WHITE)
+                    visibility = VISIBLE
+                }
+            }
+        }
+        requestLayout()
+    }
+
+    private fun generateContentDescription() {
+        val typesText = builder.generateTypesText()
+        if (builder.app != null) {
+            contentDescription = context.getString(R.string.ongoing_privacy_chip_content_single_app,
+                    builder.app?.applicationName, typesText)
+        } else {
+            contentDescription = context.getString(
+                    R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
new file mode 100644
index 0000000..1d0e16e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 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.systemui.privacy
+
+import android.app.AlertDialog
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import android.graphics.drawable.Drawable
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.systemui.Dependency
+import com.android.systemui.R
+import com.android.systemui.plugins.ActivityStarter
+
+class OngoingPrivacyDialog constructor(
+    val context: Context,
+    val dialogBuilder: PrivacyDialogBuilder
+) {
+
+    val iconHeight = context.resources.getDimensionPixelSize(
+            R.dimen.ongoing_appops_dialog_icon_height)
+    val textMargin = context.resources.getDimensionPixelSize(
+            R.dimen.ongoing_appops_dialog_text_margin)
+    val iconColor = context.resources.getColor(
+            com.android.internal.R.color.text_color_primary, context.theme)
+
+    fun createDialog(): Dialog {
+        val builder = AlertDialog.Builder(context)
+                .setNeutralButton(R.string.ongoing_privacy_dialog_open_settings, null)
+        if (dialogBuilder.app != null) {
+            builder.setPositiveButton(R.string.ongoing_privacy_dialog_open_app,
+                    object : DialogInterface.OnClickListener {
+                        val intent = context.packageManager
+                                .getLaunchIntentForPackage(dialogBuilder.app.packageName)
+
+                        override fun onClick(dialog: DialogInterface?, which: Int) {
+                            Dependency.get(ActivityStarter::class.java).startActivity(intent, false)
+                        }
+                    })
+            builder.setNegativeButton(R.string.ongoing_privacy_dialog_cancel, null)
+        } else {
+            builder.setPositiveButton(R.string.ongoing_privacy_dialog_okay, null)
+        }
+        builder.setView(getContentView())
+        return builder.create()
+    }
+
+    fun getContentView(): View {
+        val layoutInflater = LayoutInflater.from(context)
+        val contentView = layoutInflater.inflate(R.layout.ongoing_privacy_dialog_content, null)
+
+        val iconsContainer = contentView.findViewById(R.id.icons_container) as LinearLayout
+        val textContainer = contentView.findViewById(R.id.text_container) as LinearLayout
+
+        addIcons(dialogBuilder, iconsContainer)
+        val lm = ViewGroup.MarginLayoutParams(
+                ViewGroup.MarginLayoutParams.WRAP_CONTENT,
+                ViewGroup.MarginLayoutParams.WRAP_CONTENT)
+        lm.topMargin = textMargin
+        val now = System.currentTimeMillis()
+        dialogBuilder.generateText(now).forEach {
+            val text = layoutInflater.inflate(R.layout.ongoing_privacy_text_item, null) as TextView
+            text.setText(it)
+            textContainer.addView(text, lm)
+        }
+        return contentView
+    }
+
+    private fun addIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: LinearLayout) {
+
+        fun LinearLayout.addIcon(icon: Drawable) {
+            val image = ImageView(context).apply {
+                setImageDrawable(icon.apply {
+                    setBounds(0, 0, iconHeight, iconHeight)
+                    maxHeight = this@addIcon.height
+                })
+                adjustViewBounds = true
+            }
+            addView(image, LinearLayout.LayoutParams.WRAP_CONTENT,
+                    LinearLayout.LayoutParams.MATCH_PARENT)
+        }
+
+        dialogBuilder.generateIcons().forEach {
+            it.mutate()
+            it.setTint(iconColor)
+            iconsContainer.addIcon(it)
+        }
+        dialogBuilder.app.let {
+            it?.icon?.let { iconsContainer.addIcon(it) }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
new file mode 100644
index 0000000..2f86f78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 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.systemui.privacy
+
+import android.content.Context
+import com.android.systemui.R
+import java.lang.IllegalStateException
+import java.lang.Math.max
+
+class PrivacyDialogBuilder(val context: Context, itemsList: List<PrivacyItem>) {
+    companion object {
+        val MILLIS_IN_MINUTE: Long = 1000 * 60
+    }
+
+    private val itemsByType: Map<PrivacyType, List<PrivacyItem>>
+    val app: PrivacyApplication?
+
+    init {
+        itemsByType = itemsList.groupBy { it.privacyType }
+        val apps = itemsList.map { it.application }.distinct()
+        val singleApp = apps.size == 1
+        app = if (singleApp) apps.get(0) else null
+    }
+
+    private fun buildTextForItem(type: PrivacyType, now: Long): String {
+        val items = itemsByType.getOrDefault(type, emptyList<PrivacyItem>())
+        return when (items.size) {
+            0 -> throw IllegalStateException("List cannot be empty")
+            1 -> {
+                val item = items.get(0)
+                val minutesUsed = max(((now - item.timeStarted) / MILLIS_IN_MINUTE).toInt(), 1)
+                context.getString(R.string.ongoing_privacy_dialog_app_item,
+                        item.application.applicationName, type.getName(context), minutesUsed)
+            }
+            else -> {
+                val apps = items.map { it.application.applicationName }.joinToString()
+                context.getString(R.string.ongoing_privacy_dialog_apps_item,
+                        apps, type.getName(context))
+            }
+        }
+    }
+
+    private fun buildTextForApp(types: Set<PrivacyType>): List<String> {
+        app?.let {
+            val typesText = types.map { it.getName(context) }.sorted().joinToString()
+            return listOf(context.getString(R.string.ongoing_privacy_dialog_single_app,
+                    it.applicationName, typesText))
+        } ?: throw IllegalStateException("There has to be a single app")
+    }
+
+    fun generateText(now: Long): List<String> {
+        if (app == null || itemsByType.keys.size == 1) {
+            return itemsByType.keys.map { buildTextForItem(it, now) }
+        } else {
+            return buildTextForApp(itemsByType.keys)
+        }
+    }
+
+    fun generateTypesText() = itemsByType.keys.map { it.getName(context) }.sorted().joinToString()
+
+    fun generateIcons() = itemsByType.keys.map { it.getIcon(context) }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
new file mode 100644
index 0000000..f409902
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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.systemui.privacy
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
+import com.android.systemui.R
+
+typealias Privacy = PrivacyType
+
+enum class PrivacyType(val nameId: Int, val iconId: Int) {
+    TYPE_CAMERA(R.string.privacy_type_camera, com.android.internal.R.drawable.ic_camera),
+    TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location),
+    TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.ic_mic_26dp);
+
+    fun getName(context: Context) = context.resources.getString(nameId)
+
+    fun getIcon(context: Context) = context.resources.getDrawable(iconId, null)
+}
+
+data class PrivacyItem(
+    val privacyType: PrivacyType,
+    val application: PrivacyApplication,
+    val timeStarted: Long
+)
+
+data class PrivacyApplication(val packageName: String, val context: Context) {
+    var icon: Drawable? = null
+    var applicationName: String
+
+    init {
+        try {
+            val app: ApplicationInfo = context.packageManager
+                    .getApplicationInfo(packageName, 0)
+            icon = context.packageManager.getApplicationIcon(app)
+            applicationName = context.packageManager.getApplicationLabel(app) as String
+        } catch (e: PackageManager.NameNotFoundException) {
+            applicationName = packageName
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index b988c55..3ee6195 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -21,6 +21,7 @@
 import android.annotation.ColorInt;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
+import android.app.Dialog;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -31,33 +32,38 @@
 import android.graphics.Rect;
 import android.media.AudioManager;
 import android.os.Handler;
+import android.os.Looper;
 import android.provider.AlarmClock;
 import android.service.notification.ZenModeConfig;
-import android.widget.FrameLayout;
-import androidx.annotation.VisibleForTesting;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
 import android.view.View;
 import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.settingslib.Utils;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.privacy.OngoingPrivacyChip;
+import com.android.systemui.privacy.OngoingPrivacyDialog;
 import com.android.systemui.qs.QSDetail.Callback;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
-import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.phone.StatusIconContainer;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.policy.DateView;
@@ -118,6 +124,7 @@
     private BatteryMeterView mBatteryMeterView;
     private Clock mClockView;
     private DateView mDateView;
+    private OngoingPrivacyChip mPrivacyChip;
 
     private NextAlarmController mAlarmController;
     private ZenModeController mZenController;
@@ -185,6 +192,8 @@
         mClockView = findViewById(R.id.clock);
         mClockView.setOnClickListener(this);
         mDateView = findViewById(R.id.date);
+        mPrivacyChip = findViewById(R.id.privacy_chip);
+        mPrivacyChip.setOnClickListener(this);
     }
 
     private void updateStatusText() {
@@ -205,7 +214,7 @@
 
         boolean ringerVisible = false;
         if (!ZenModeConfig.isZenOverridingRinger(mZenController.getZen(),
-                mZenController.getConfig())) {
+                mZenController.getConsolidatedPolicy())) {
             if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
                 mRingerModeIcon.setImageResource(R.drawable.stat_sys_ringer_vibrate);
                 mRingerModeTextView.setText(R.string.qs_status_phone_vibrate);
@@ -263,6 +272,13 @@
                 newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
         mBatteryMeterView.useWallpaperTextColor(shouldUseWallpaperTextColor);
         mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor);
+
+        MarginLayoutParams lm = (MarginLayoutParams) mPrivacyChip.getLayoutParams();
+        int sideMargins = lm.leftMargin;
+        int topBottomMargins = (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
+                ? 0 : sideMargins;
+        lm.setMargins(sideMargins, topBottomMargins, sideMargins, topBottomMargins);
+        mPrivacyChip.setLayoutParams(lm);
     }
 
     @Override
@@ -421,6 +437,7 @@
             return;
         }
         mHeaderQsPanel.setListening(listening);
+        mPrivacyChip.setListening(listening);
         mListening = listening;
 
         if (listening) {
@@ -443,6 +460,19 @@
         } else if (v == mBatteryMeterView) {
             Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
                     Intent.ACTION_POWER_USAGE_SUMMARY),0);
+        } else if (v == mPrivacyChip) {
+            Handler mUiHandler = new Handler(Looper.getMainLooper());
+            mUiHandler.post(() -> {
+                Dialog mDialog = new OngoingPrivacyDialog(mContext,
+                        mPrivacyChip.getBuilder()).createDialog();
+                mDialog.getWindow().setType(
+                        WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+                SystemUIDialog.setShowForAllUsers(mDialog, true);
+                SystemUIDialog.registerDismissListener(mDialog);
+                SystemUIDialog.setWindowOnTop(mDialog);
+                mUiHandler.post(() -> mDialog.show());
+                mHost.collapsePanels();
+            });
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index d42127e..0638998 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -194,6 +194,7 @@
         }
 
         setClickable(state.state != Tile.STATE_UNAVAILABLE);
+        setLongClickable(state.handlesLongClick);
         mIcon.setIcon(state, allowAnimations);
         setContentDescription(state.contentDescription);
 
@@ -287,10 +288,14 @@
                 info.setText(label);
                 info.setChecked(b);
                 info.setCheckable(true);
-                info.addAction(
-                        new AccessibilityNodeInfo.AccessibilityAction(
-                                AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.getId(),
-                                getResources().getString(R.string.accessibility_long_click_tile)));
+                if (isLongClickable()) {
+                    info.addAction(
+                            new AccessibilityNodeInfo.AccessibilityAction(
+                                    AccessibilityNodeInfo.AccessibilityAction
+                                            .ACTION_LONG_CLICK.getId(),
+                                    getResources().getString(
+                                            R.string.accessibility_long_click_tile)));
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index f2ead1c..d7ac253 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager;
 import android.content.Intent;
-import android.graphics.drawable.Drawable;
 import android.provider.MediaStore;
 import android.service.quicksettings.Tile;
 import android.widget.Switch;
@@ -50,7 +49,9 @@
 
     @Override
     public BooleanState newTileState() {
-        return new BooleanState();
+        BooleanState state = new BooleanState();
+        state.handlesLongClick = false;
+        return state;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
new file mode 100644
index 0000000..661b958
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+
+import android.app.ActivityManager;
+import android.app.trust.TrustManager;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Display;
+import android.widget.Toast;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * An implementation of the Recents interface which proxies to the OverviewProxyService.
+ */
+public class OverviewProxyRecentsImpl implements RecentsImplementation {
+
+    private final static String TAG = "OverviewProxyRecentsImpl";
+
+    private SysUiServiceProvider mSysUiServiceProvider;
+    private Context mContext;
+    private Handler mHandler;
+    private TrustManager mTrustManager;
+    private OverviewProxyService mOverviewProxyService;
+
+    @Override
+    public void onStart(Context context, SysUiServiceProvider sysUiServiceProvider) {
+        mContext = context;
+        mSysUiServiceProvider = sysUiServiceProvider;
+        mHandler = new Handler();
+        mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
+        mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+    }
+
+    @Override
+    public void showRecentApps(boolean triggeredFromAltTab) {
+        IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
+        if (overviewProxy != null) {
+            try {
+                overviewProxy.onOverviewShown(triggeredFromAltTab);
+                return;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to send overview show event to launcher.", e);
+            }
+        } else {
+            // Do nothing
+        }
+    }
+
+    @Override
+    public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+        IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
+        if (overviewProxy != null) {
+            try {
+                overviewProxy.onOverviewHidden(triggeredFromAltTab, triggeredFromHomeKey);
+                return;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to send overview hide event to launcher.", e);
+            }
+        } else {
+            // Do nothing
+        }
+    }
+
+    @Override
+    public void toggleRecentApps() {
+        // If connected to launcher service, let it handle the toggle logic
+        IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
+        if (overviewProxy != null) {
+            final Runnable toggleRecents = () -> {
+                try {
+                    if (mOverviewProxyService.getProxy() != null) {
+                        mOverviewProxyService.getProxy().onOverviewToggle();
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Cannot send toggle recents through proxy service.", e);
+                }
+            };
+            // Preload only if device for current user is unlocked
+            final StatusBar statusBar = mSysUiServiceProvider.getComponent(StatusBar.class);
+            if (statusBar != null && statusBar.isKeyguardShowing()) {
+                statusBar.executeRunnableDismissingKeyguard(() -> {
+                        // Flush trustmanager before checking device locked per user
+                        mTrustManager.reportKeyguardShowingChanged();
+                        mHandler.post(toggleRecents);
+                    }, null,  true /* dismissShade */, false /* afterKeyguardGone */,
+                    true /* deferred */);
+            } else {
+                toggleRecents.run();
+            }
+            return;
+        } else {
+            // Do nothing
+        }
+    }
+
+    @Override
+    public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
+            int metricsDockAction) {
+        Point realSize = new Point();
+        if (initialBounds == null) {
+            mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
+                    .getRealSize(realSize);
+            initialBounds = new Rect(0, 0, realSize.x, realSize.y);
+        }
+
+        ActivityManager.RunningTaskInfo runningTask =
+                ActivityManagerWrapper.getInstance().getRunningTask();
+        final int activityType = runningTask != null
+                ? runningTask.configuration.windowConfiguration.getActivityType()
+                : ACTIVITY_TYPE_UNDEFINED;
+        boolean screenPinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
+        boolean isRunningTaskInHomeOrRecentsStack =
+                activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
+        if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
+            if (runningTask.supportsSplitScreenMultiWindow) {
+                if (ActivityManagerWrapper.getInstance().setTaskWindowingModeSplitScreenPrimary(
+                        runningTask.id, stackCreateMode, initialBounds)) {
+                    // The overview service is handling split screen, so just skip the wait for the
+                    // first draw and notify the divider to start animating now
+                    final Divider divider = mSysUiServiceProvider.getComponent(Divider.class);
+                    if (divider != null) {
+                        divider.onRecentsDrawn();
+                    }
+                    return true;
+                }
+            } else {
+                Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
+                        Toast.LENGTH_SHORT).show();
+            }
+        }
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
new file mode 100644
index 0000000..19f7675
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2017 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.systemui.recents;
+
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.ACTION_CANCEL;
+
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
+import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.PatternMatcher;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.MotionEvent;
+import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
+import com.android.systemui.Prefs;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class to send information from overview to launcher with a binder.
+ */
+public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable {
+
+    private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
+
+    public static final String TAG_OPS = "OverviewProxyService";
+    public static final boolean DEBUG_OVERVIEW_PROXY = false;
+    private static final long BACKOFF_MILLIS = 1000;
+    private static final long DEFERRED_CALLBACK_MILLIS = 5000;
+
+    // Max backoff caps at 5 mins
+    private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
+
+    // Default interaction flags if swipe up is disabled before connecting to launcher
+    private static final int DEFAULT_DISABLE_SWIPE_UP_STATE = FLAG_DISABLE_SWIPE_UP
+            | FLAG_SHOW_OVERVIEW_BUTTON;
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
+    private final ComponentName mRecentsComponentName;
+    private final DeviceProvisionedController mDeviceProvisionedController
+            = Dependency.get(DeviceProvisionedController.class);
+    private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
+    private final Intent mQuickStepIntent;
+
+    private IOverviewProxy mOverviewProxy;
+    private int mConnectionBackoffAttempts;
+    private @InteractionType int mInteractionFlags;
+    private boolean mIsEnabled;
+    private int mCurrentBoundedUserId = -1;
+    private float mBackButtonAlpha;
+    private MotionEvent mStatusBarGestureDownEvent;
+
+    private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
+
+        public void startScreenPinning(int taskId) {
+            if (!verifyCaller("startScreenPinning")) {
+                return;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                mHandler.post(() -> {
+                    StatusBar statusBar = SysUiServiceProvider.getComponent(mContext,
+                            StatusBar.class);
+                    if (statusBar != null) {
+                        statusBar.showScreenPinningRequest(taskId, false /* allowCancel */);
+                    }
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        public void onStatusBarMotionEvent(MotionEvent event) {
+            if (!verifyCaller("onStatusBarMotionEvent")) {
+                return;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                // TODO move this logic to message queue
+                mHandler.post(()->{
+                    StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+                    if (bar != null) {
+                        bar.dispatchNotificationsPanelTouchEvent(event);
+
+                        int action = event.getActionMasked();
+                        if (action == ACTION_DOWN) {
+                            mStatusBarGestureDownEvent = MotionEvent.obtain(event);
+                        }
+                        if (action == ACTION_UP || action == ACTION_CANCEL) {
+                            mStatusBarGestureDownEvent.recycle();
+                            mStatusBarGestureDownEvent = null;
+                        }
+                        event.recycle();
+                    }
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        public void onSplitScreenInvoked() {
+            if (!verifyCaller("onSplitScreenInvoked")) {
+                return;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                Divider divider = SysUiServiceProvider.getComponent(mContext, Divider.class);
+                if (divider != null) {
+                    divider.onDockedFirstAnimationFrame();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        public void onOverviewShown(boolean fromHome) {
+            if (!verifyCaller("onOverviewShown")) {
+                return;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                mHandler.post(() -> {
+                    for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+                        mConnectionCallbacks.get(i).onOverviewShown(fromHome);
+                    }
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        public void setInteractionState(@InteractionType int flags) {
+            if (!verifyCaller("setInteractionState")) {
+                return;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                if (mInteractionFlags != flags) {
+                    mInteractionFlags = flags;
+                    mHandler.post(() -> {
+                        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+                            mConnectionCallbacks.get(i).onInteractionFlagsChanged(flags);
+                        }
+                    });
+                }
+            } finally {
+                Prefs.putInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS, mInteractionFlags);
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        public Rect getNonMinimizedSplitScreenSecondaryBounds() {
+            if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) {
+                return null;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                Divider divider = SysUiServiceProvider.getComponent(mContext, Divider.class);
+                if (divider != null) {
+                    return divider.getView().getNonMinimizedSplitScreenSecondaryBounds();
+                }
+                return null;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        public void setBackButtonAlpha(float alpha, boolean animate) {
+            if (!verifyCaller("setBackButtonAlpha")) {
+                return;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                mBackButtonAlpha = alpha;
+                mHandler.post(() -> {
+                    notifyBackButtonAlphaChanged(alpha, animate);
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        private boolean verifyCaller(String reason) {
+            final int callerId = Binder.getCallingUserHandle().getIdentifier();
+            if (callerId != mCurrentBoundedUserId) {
+                Log.w(TAG_OPS, "Launcher called sysui with invalid user: " + callerId + ", reason: "
+                        + reason);
+                return false;
+            }
+            return true;
+        }
+    };
+
+    private final Runnable mDeferredConnectionCallback = () -> {
+        Log.w(TAG_OPS, "Binder supposed established connection but actual connection to service "
+            + "timed out, trying again");
+        retryConnectionWithBackoff();
+    };
+
+    private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            updateEnabledState();
+
+            // When launcher service is disabled, reset interaction flags because it is inactive
+            if (!isEnabled()) {
+                mInteractionFlags = getDefaultInteractionFlags();
+                Prefs.remove(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS);
+            }
+
+            // Reconnect immediately, instead of waiting for resume to arrive.
+            startConnectionToCurrentUser();
+        }
+    };
+
+    private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mHandler.removeCallbacks(mDeferredConnectionCallback);
+            mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
+            mConnectionBackoffAttempts = 0;
+            mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
+            // Listen for launcher's death
+            try {
+                service.linkToDeath(mOverviewServiceDeathRcpt, 0);
+            } catch (RemoteException e) {
+                Log.e(TAG_OPS, "Lost connection to launcher service", e);
+            }
+            try {
+                mOverviewProxy.onBind(mSysUiProxy);
+            } catch (RemoteException e) {
+                mCurrentBoundedUserId = -1;
+                Log.e(TAG_OPS, "Failed to call onBind()", e);
+            }
+            notifyConnectionChanged();
+        }
+
+        @Override
+        public void onNullBinding(ComponentName name) {
+            Log.w(TAG_OPS, "Null binding of '" + name + "', try reconnecting");
+            mCurrentBoundedUserId = -1;
+            retryConnectionWithBackoff();
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            Log.w(TAG_OPS, "Binding died of '" + name + "', try reconnecting");
+            mCurrentBoundedUserId = -1;
+            retryConnectionWithBackoff();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            // Do nothing
+            mCurrentBoundedUserId = -1;
+        }
+    };
+
+    private final DeviceProvisionedListener mDeviceProvisionedCallback =
+                new DeviceProvisionedListener() {
+            @Override
+            public void onUserSetupChanged() {
+                if (mDeviceProvisionedController.isCurrentUserSetup()) {
+                    internalConnectToCurrentUser();
+                }
+            }
+
+            @Override
+            public void onUserSwitched() {
+                mConnectionBackoffAttempts = 0;
+                internalConnectToCurrentUser();
+            }
+        };
+
+    // This is the death handler for the binder from the launcher service
+    private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
+            = this::cleanupAfterDeath;
+
+    public OverviewProxyService(Context context) {
+        mContext = context;
+        mHandler = new Handler();
+        mConnectionBackoffAttempts = 0;
+        mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
+                com.android.internal.R.string.config_recentsComponentName));
+        mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
+                .setPackage(mRecentsComponentName.getPackageName());
+        mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS,
+                getDefaultInteractionFlags());
+
+        // Listen for the package update changes.
+        if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) {
+            updateEnabledState();
+            mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
+            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+            filter.addDataScheme("package");
+            filter.addDataSchemeSpecificPart(mRecentsComponentName.getPackageName(),
+                    PatternMatcher.PATTERN_LITERAL);
+            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+            mContext.registerReceiver(mLauncherStateChangedReceiver, filter);
+        }
+    }
+
+    public float getBackButtonAlpha() {
+        return mBackButtonAlpha;
+    }
+
+    public void cleanupAfterDeath() {
+        if (mStatusBarGestureDownEvent != null) {
+            mHandler.post(()-> {
+                StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+                if (bar != null) {
+                    System.out.println("MERONG dispatchNotificationPanelTouchEvent");
+                    mStatusBarGestureDownEvent.setAction(MotionEvent.ACTION_CANCEL);
+                    bar.dispatchNotificationsPanelTouchEvent(mStatusBarGestureDownEvent);
+                    mStatusBarGestureDownEvent.recycle();
+                    mStatusBarGestureDownEvent = null;
+                }
+            });
+        }
+        startConnectionToCurrentUser();
+    }
+
+    public void startConnectionToCurrentUser() {
+        if (mHandler.getLooper() != Looper.myLooper()) {
+            mHandler.post(mConnectionRunnable);
+        } else {
+            internalConnectToCurrentUser();
+        }
+    }
+
+    private void internalConnectToCurrentUser() {
+        disconnectFromLauncherService();
+
+        // If user has not setup yet or already connected, do not try to connect
+        if (!mDeviceProvisionedController.isCurrentUserSetup() || !isEnabled()) {
+            Log.v(TAG_OPS, "Cannot attempt connection, is setup "
+                + mDeviceProvisionedController.isCurrentUserSetup() + ", is enabled "
+                + isEnabled());
+            return;
+        }
+        mHandler.removeCallbacks(mConnectionRunnable);
+        Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
+                .setPackage(mRecentsComponentName.getPackageName());
+        boolean bound = false;
+        try {
+            bound = mContext.bindServiceAsUser(launcherServiceIntent,
+                    mOverviewServiceConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+                    UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
+        } catch (SecurityException e) {
+            Log.e(TAG_OPS, "Unable to bind because of security error", e);
+        }
+        if (bound) {
+            // Ensure that connection has been established even if it thinks it is bound
+            mHandler.postDelayed(mDeferredConnectionCallback, DEFERRED_CALLBACK_MILLIS);
+        } else {
+            // Retry after exponential backoff timeout
+            retryConnectionWithBackoff();
+        }
+    }
+
+    private void retryConnectionWithBackoff() {
+        if (mHandler.hasCallbacks(mConnectionRunnable)) {
+            return;
+        }
+        final long timeoutMs = (long) Math.min(
+                Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts), MAX_BACKOFF_MILLIS);
+        mHandler.postDelayed(mConnectionRunnable, timeoutMs);
+        mConnectionBackoffAttempts++;
+        Log.w(TAG_OPS, "Failed to connect on attempt " + mConnectionBackoffAttempts
+                + " will try again in " + timeoutMs + "ms");
+    }
+
+    @Override
+    public void addCallback(OverviewProxyListener listener) {
+        mConnectionCallbacks.add(listener);
+        listener.onConnectionChanged(mOverviewProxy != null);
+        listener.onInteractionFlagsChanged(mInteractionFlags);
+    }
+
+    @Override
+    public void removeCallback(OverviewProxyListener listener) {
+        mConnectionCallbacks.remove(listener);
+    }
+
+    public boolean shouldShowSwipeUpUI() {
+        return isEnabled() && ((mInteractionFlags & FLAG_DISABLE_SWIPE_UP) == 0);
+    }
+
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    public IOverviewProxy getProxy() {
+        return mOverviewProxy;
+    }
+
+    public int getInteractionFlags() {
+        return mInteractionFlags;
+    }
+
+    private void disconnectFromLauncherService() {
+        if (mOverviewProxy != null) {
+            mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
+            mContext.unbindService(mOverviewServiceConnection);
+            mOverviewProxy = null;
+            notifyBackButtonAlphaChanged(1f, false /* animate */);
+            notifyConnectionChanged();
+        }
+    }
+
+    private int getDefaultInteractionFlags() {
+        // If there is no settings available use device default or get it from settings
+        final boolean defaultState = getSwipeUpDefaultValue();
+        final boolean swipeUpEnabled = getSwipeUpSettingAvailable()
+                ? getSwipeUpEnabledFromSettings(defaultState)
+                : defaultState;
+        return swipeUpEnabled ? 0 : DEFAULT_DISABLE_SWIPE_UP_STATE;
+    }
+
+    private void notifyBackButtonAlphaChanged(float alpha, boolean animate) {
+        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+            mConnectionCallbacks.get(i).onBackButtonAlphaChanged(alpha, animate);
+        }
+    }
+
+    private void notifyConnectionChanged() {
+        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+            mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
+        }
+    }
+
+    public void notifyQuickStepStarted() {
+        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+            mConnectionCallbacks.get(i).onQuickStepStarted();
+        }
+    }
+
+    public void notifyQuickScrubStarted() {
+        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+            mConnectionCallbacks.get(i).onQuickScrubStarted();
+        }
+    }
+
+    private void updateEnabledState() {
+        mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
+                MATCH_DIRECT_BOOT_UNAWARE,
+                ActivityManagerWrapper.getInstance().getCurrentUserId()) != null;
+    }
+
+    private boolean getSwipeUpDefaultValue() {
+        return mContext.getResources()
+                .getBoolean(com.android.internal.R.bool.config_swipe_up_gesture_default);
+    }
+
+    private boolean getSwipeUpSettingAvailable() {
+        return mContext.getResources()
+                .getBoolean(com.android.internal.R.bool.config_swipe_up_gesture_setting_available);
+    }
+
+    private boolean getSwipeUpEnabledFromSettings(boolean defaultValue) {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED, defaultValue ? 1 : 0) == 1;
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(TAG_OPS + " state:");
+        pw.print("  recentsComponentName="); pw.println(mRecentsComponentName);
+        pw.print("  isConnected="); pw.println(mOverviewProxy != null);
+        pw.print("  isCurrentUserSetup="); pw.println(mDeviceProvisionedController
+                .isCurrentUserSetup());
+        pw.print("  connectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
+        pw.print("  interactionFlags="); pw.println(mInteractionFlags);
+
+        pw.print("  quickStepIntent="); pw.println(mQuickStepIntent);
+        pw.print("  quickStepIntentResolved="); pw.println(isEnabled());
+
+        final boolean swipeUpDefaultValue = getSwipeUpDefaultValue();
+        final boolean swipeUpEnabled = getSwipeUpEnabledFromSettings(swipeUpDefaultValue);
+        pw.print("  swipeUpSetting="); pw.println(swipeUpEnabled);
+        pw.print("  swipeUpSettingDefault="); pw.println(swipeUpDefaultValue);
+    }
+
+    public interface OverviewProxyListener {
+        default void onConnectionChanged(boolean isConnected) {}
+        default void onQuickStepStarted() {}
+        default void onInteractionFlagsChanged(@InteractionType int flags) {}
+        default void onOverviewShown(boolean fromHome) {}
+        default void onQuickScrubStarted() {}
+        default void onBackButtonAlphaChanged(float alpha, boolean animate) {}
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 74f6c2d..de22d21 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -16,232 +16,29 @@
 
 package com.android.systemui.recents;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
-
-import android.app.ActivityManager;
-import android.app.trust.TrustManager;
-import android.content.ComponentName;
 import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
 import android.provider.Settings;
-import android.util.EventLog;
-import android.util.Log;
-import android.view.Display;
-import android.widget.Toast;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
-import com.android.systemui.EventLogConstants;
-import com.android.systemui.EventLogTags;
-import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
-import com.android.systemui.RecentsComponent;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
-import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.SystemUI;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
-import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
-import com.android.systemui.recents.events.component.ShowUserToastEvent;
-import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
-
-import com.android.systemui.statusbar.phone.StatusBar;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
 
 /**
- * An implementation of the SystemUI recents component, which supports both system and secondary
- * users.
+ * A proxy to a Recents implementation.
  */
-public class Recents extends SystemUI
-        implements RecentsComponent, CommandQueue.Callbacks {
+public class Recents extends SystemUI implements CommandQueue.Callbacks {
 
-    private final static String TAG = "Recents";
-
-    public final static int EVENT_BUS_PRIORITY = 1;
-    public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000;
-
-    public final static Set<String> RECENTS_ACTIVITIES = new HashSet<>();
-    static {
-        RECENTS_ACTIVITIES.add(RecentsImpl.RECENTS_ACTIVITY);
-    }
-
-    private static final String COUNTER_WINDOW_SUPPORTED = "window_enter_supported";
-    private static final String COUNTER_WINDOW_UNSUPPORTED = "window_enter_unsupported";
-    private static final String COUNTER_WINDOW_INCOMPATIBLE = "window_enter_incompatible";
-
-    private static SystemServicesProxy sSystemServicesProxy;
-    private static RecentsDebugFlags sDebugFlags;
-    private static RecentsTaskLoader sTaskLoader;
-    private static RecentsConfiguration sConfiguration;
-
-    private OverviewProxyService mOverviewProxyService;
-
-    private Handler mHandler;
-    private RecentsImpl mImpl;
-    private TrustManager mTrustManager;
-    private int mDraggingInRecentsCurrentUser;
-
-    // Only For system user, this is the callbacks instance we return to each secondary user
-    private RecentsSystemUser mSystemToUserCallbacks;
-
-    // Only for secondary users, this is the callbacks instance provided by the system user to make
-    // calls back
-    private IRecentsSystemUserCallbacks mUserToSystemCallbacks;
-
-    // The set of runnables to run after binding to the system user's service.
-    private final ArrayList<Runnable> mOnConnectRunnables = new ArrayList<>();
-
-    // Only for secondary users, this is the death handler for the binder from the system user
-    private final IBinder.DeathRecipient mUserToSystemCallbacksDeathRcpt = new IBinder.DeathRecipient() {
-        @Override
-        public void binderDied() {
-            mUserToSystemCallbacks = null;
-            EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
-                    EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_UNBOUND,
-                    sSystemServicesProxy.getProcessUser());
-
-            // Retry after a fixed duration
-            mHandler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    registerWithSystemUser();
-                }
-            }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
-        }
-    };
-
-    // Only for secondary users, this is the service connection we use to connect to the system user
-    private final ServiceConnection mUserToSystemServiceConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            if (service != null) {
-                mUserToSystemCallbacks = IRecentsSystemUserCallbacks.Stub.asInterface(
-                        service);
-                EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
-                        EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_BOUND,
-                        sSystemServicesProxy.getProcessUser());
-
-                // Listen for system user's death, so that we can reconnect later
-                try {
-                    service.linkToDeath(mUserToSystemCallbacksDeathRcpt, 0);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Lost connection to (System) SystemUI", e);
-                }
-
-                // Run each of the queued runnables
-                runAndFlushOnConnectRunnables();
-            }
-
-            // Unbind ourselves now that we've registered our callbacks.  The
-            // binder to the system user are still valid at this point.
-            mContext.unbindService(this);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            // Do nothing
-        }
-    };
-
-    /**
-     * Returns the callbacks interface that non-system users can call.
-     */
-    public IBinder getSystemUserCallbacks() {
-        return mSystemToUserCallbacks;
-    }
-
-    public static RecentsTaskLoader getTaskLoader() {
-        return sTaskLoader;
-    }
-
-
-    public static SystemServicesProxy getSystemServices() {
-        return sSystemServicesProxy;
-    }
-
-    public static RecentsConfiguration getConfiguration() {
-        return sConfiguration;
-    }
-
-    public static RecentsDebugFlags getDebugFlags() {
-        return sDebugFlags;
-    }
+    private RecentsImplementation mImpl;
 
     @Override
     public void start() {
-        final Resources res = mContext.getResources();
-        final int defaultTaskBarBackgroundColor =
-                mContext.getColor(R.color.recents_task_bar_default_background_color);
-        final int defaultTaskViewBackgroundColor =
-                mContext.getColor(R.color.recents_task_view_default_background_color);
-        sDebugFlags = new RecentsDebugFlags();
-        sSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
-        sConfiguration = new RecentsConfiguration(mContext);
-        sTaskLoader = new RecentsTaskLoader(mContext,
-                // TODO: Once we start building the AAR, move these into the loader
-                res.getInteger(R.integer.config_recents_max_thumbnail_count),
-                res.getInteger(R.integer.config_recents_max_icon_count),
-                res.getInteger(R.integer.recents_svelte_level));
-        sTaskLoader.setDefaultColors(defaultTaskBarBackgroundColor, defaultTaskViewBackgroundColor);
-        mHandler = new Handler();
-        mImpl = new RecentsImpl(mContext);
-        mOverviewProxyService = Dependency.get(OverviewProxyService.class);
-
-        // Register with the event bus
-        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
-        EventBus.getDefault().register(sSystemServicesProxy, EVENT_BUS_PRIORITY);
-        EventBus.getDefault().register(sTaskLoader, EVENT_BUS_PRIORITY);
-
-        // Due to the fact that RecentsActivity is per-user, we need to establish and interface for
-        // the system user's Recents component to pass events (like show/hide/toggleRecents) to the
-        // secondary user, and vice versa (like visibility change, screen pinning).
-        final int processUser = sSystemServicesProxy.getProcessUser();
-        if (sSystemServicesProxy.isSystemUser(processUser)) {
-            // For the system user, initialize an instance of the interface that we can pass to the
-            // secondary user
-            getComponent(CommandQueue.class).addCallbacks(this);
-            mSystemToUserCallbacks = new RecentsSystemUser(mContext, mImpl);
-        } else {
-            // For the secondary user, bind to the primary user's service to get a persistent
-            // interface to register its implementation and to later update its state
-            registerWithSystemUser();
-        }
+        getComponent(CommandQueue.class).addCallbacks(this);
         putComponent(Recents.class, this);
-
-        mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
+        mImpl = createRecentsImplementationFromConfig();
+        mImpl.onStart(mContext, this);
     }
 
     @Override
@@ -249,13 +46,20 @@
         mImpl.onBootCompleted();
     }
 
-    public void growRecents() {
-        EventBus.getDefault().send(new RecentsGrowingEvent());
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        mImpl.onConfigurationChanged(newConfig);
     }
 
-    /**
-     * Shows the Recents.
-     */
+    @Override
+    public void appTransitionFinished() {
+        mImpl.onAppTransitionFinished();
+    }
+
+    public void growRecents() {
+        mImpl.growRecents();
+    }
+
     @Override
     public void showRecentApps(boolean triggeredFromAltTab) {
         // Ensure the device has been provisioned before allowing the user to interact with
@@ -264,43 +68,9 @@
             return;
         }
 
-        IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
-        if (overviewProxy != null) {
-            try {
-                overviewProxy.onOverviewShown(triggeredFromAltTab);
-                return;
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to send overview show event to launcher.", e);
-            }
-        }
-
-        ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
-        int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents();
-        int currentUser = sSystemServicesProxy.getCurrentUser();
-        if (sSystemServicesProxy.isSystemUser(currentUser)) {
-            mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
-                    true /* animate */, recentsGrowTarget);
-        } else {
-            if (mSystemToUserCallbacks != null) {
-                IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
-                if (callbacks != null) {
-                    try {
-                        callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
-                                true /* animate */, recentsGrowTarget);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                } else {
-                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
-                }
-            }
-        }
+        mImpl.showRecentApps(triggeredFromAltTab);
     }
 
-    /**
-     * Hides the Recents.
-     */
     @Override
     public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
         // Ensure the device has been provisioned before allowing the user to interact with
@@ -309,39 +79,9 @@
             return;
         }
 
-        IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
-        if (overviewProxy != null) {
-            try {
-                overviewProxy.onOverviewHidden(triggeredFromAltTab, triggeredFromHomeKey);
-                return;
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to send overview hide event to launcher.", e);
-            }
-        }
-
-        int currentUser = sSystemServicesProxy.getCurrentUser();
-        if (sSystemServicesProxy.isSystemUser(currentUser)) {
-            mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
-        } else {
-            if (mSystemToUserCallbacks != null) {
-                IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
-                if (callbacks != null) {
-                    try {
-                        callbacks.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                } else {
-                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
-                }
-            }
-        }
+        mImpl.hideRecentApps(triggeredFromAltTab, triggeredFromHomeKey);
     }
 
-    /**
-     * Toggles the Recents activity.
-     */
     @Override
     public void toggleRecentApps() {
         // Ensure the device has been provisioned before allowing the user to interact with
@@ -350,57 +90,9 @@
             return;
         }
 
-        // If connected to launcher service, let it handle the toggle logic
-        IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
-        if (overviewProxy != null) {
-            final Runnable toggleRecents = () -> {
-                try {
-                    if (mOverviewProxyService.getProxy() != null) {
-                        mOverviewProxyService.getProxy().onOverviewToggle();
-                    }
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Cannot send toggle recents through proxy service.", e);
-                }
-            };
-            // Preload only if device for current user is unlocked
-            final StatusBar statusBar = getComponent(StatusBar.class);
-            if (statusBar != null && statusBar.isKeyguardShowing()) {
-                statusBar.executeRunnableDismissingKeyguard(() -> {
-                        // Flush trustmanager before checking device locked per user
-                        mTrustManager.reportKeyguardShowingChanged();
-                        mHandler.post(toggleRecents);
-                    }, null,  true /* dismissShade */, false /* afterKeyguardGone */,
-                    true /* deferred */);
-            } else {
-                toggleRecents.run();
-            }
-            return;
-        }
-
-        int growTarget = getComponent(Divider.class).getView().growsRecents();
-        int currentUser = sSystemServicesProxy.getCurrentUser();
-        if (sSystemServicesProxy.isSystemUser(currentUser)) {
-            mImpl.toggleRecents(growTarget);
-        } else {
-            if (mSystemToUserCallbacks != null) {
-                IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
-                if (callbacks != null) {
-                    try {
-                        callbacks.toggleRecents(growTarget);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                } else {
-                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
-                }
-            }
-        }
+        mImpl.toggleRecentApps();
     }
 
-    /**
-     * Preloads info for the Recents activity.
-     */
     @Override
     public void preloadRecentApps() {
         // Ensure the device has been provisioned before allowing the user to interact with
@@ -409,29 +101,7 @@
             return;
         }
 
-        if (mOverviewProxyService.getProxy() != null) {
-            // TODO: Proxy to Launcher
-            return;
-        }
-
-        int currentUser = sSystemServicesProxy.getCurrentUser();
-        if (sSystemServicesProxy.isSystemUser(currentUser)) {
-            mImpl.preloadRecents();
-        } else {
-            if (mSystemToUserCallbacks != null) {
-                IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
-                if (callbacks != null) {
-                    try {
-                        callbacks.preloadRecents();
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                } else {
-                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
-                }
-            }
-        }
+        mImpl.preloadRecentApps();
     }
 
     @Override
@@ -442,32 +112,9 @@
             return;
         }
 
-        if (mOverviewProxyService.getProxy() != null) {
-            // TODO: Proxy to Launcher
-            return;
-        }
-
-        int currentUser = sSystemServicesProxy.getCurrentUser();
-        if (sSystemServicesProxy.isSystemUser(currentUser)) {
-            mImpl.cancelPreloadingRecents();
-        } else {
-            if (mSystemToUserCallbacks != null) {
-                IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
-                if (callbacks != null) {
-                    try {
-                        callbacks.cancelPreloadingRecents();
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                } else {
-                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
-                }
-            }
-        }
+        mImpl.cancelPreloadRecentApps();
     }
 
-    @Override
     public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
             int metricsDockAction) {
         // Ensure the device has been provisioned before allowing the user to interact with
@@ -476,380 +123,7 @@
             return false;
         }
 
-        Point realSize = new Point();
-        if (initialBounds == null) {
-            mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
-                    .getRealSize(realSize);
-            initialBounds = new Rect(0, 0, realSize.x, realSize.y);
-        }
-
-        int currentUser = sSystemServicesProxy.getCurrentUser();
-        ActivityManager.RunningTaskInfo runningTask =
-                ActivityManagerWrapper.getInstance().getRunningTask();
-        final int activityType = runningTask != null
-                ? runningTask.configuration.windowConfiguration.getActivityType()
-                : ACTIVITY_TYPE_UNDEFINED;
-        boolean screenPinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
-        boolean isRunningTaskInHomeOrRecentsStack =
-                activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
-        if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
-            logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode);
-            if (runningTask.supportsSplitScreenMultiWindow) {
-                if (metricsDockAction != -1) {
-                    MetricsLogger.action(mContext, metricsDockAction,
-                            runningTask.topActivity.flattenToShortString());
-                }
-                if (sSystemServicesProxy.isSystemUser(currentUser)) {
-                    mImpl.splitPrimaryTask(runningTask.id, stackCreateMode, initialBounds);
-                } else {
-                    if (mSystemToUserCallbacks != null) {
-                        IRecentsNonSystemUserCallbacks callbacks =
-                                mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
-                        if (callbacks != null) {
-                            try {
-                                callbacks.splitPrimaryTask(runningTask.id, stackCreateMode,
-                                        initialBounds);
-                            } catch (RemoteException e) {
-                                Log.e(TAG, "Callback failed", e);
-                            }
-                        } else {
-                            Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
-                        }
-                    }
-                }
-                mDraggingInRecentsCurrentUser = currentUser;
-
-                if (mOverviewProxyService.getProxy() != null) {
-                    // The overview service is handling split screen, so just skip the wait for the
-                    // first draw and notify the divider to start animating now
-                    EventBus.getDefault().post(new RecentsDrawnEvent());
-                }
-
-                return true;
-            } else {
-                EventBus.getDefault().send(new ShowUserToastEvent(
-                        R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT));
-                return false;
-            }
-        } else {
-            return false;
-        }
-    }
-
-    public static void logDockAttempt(Context ctx, ComponentName activity, int resizeMode) {
-        if (resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE) {
-            MetricsLogger.action(ctx, MetricsEvent.ACTION_WINDOW_DOCK_UNRESIZABLE,
-                    activity.flattenToShortString());
-        }
-        MetricsLogger.count(ctx, getMetricsCounterForResizeMode(resizeMode), 1);
-    }
-
-    private static String getMetricsCounterForResizeMode(int resizeMode) {
-        switch (resizeMode) {
-            case ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE:
-                return COUNTER_WINDOW_UNSUPPORTED;
-            case ActivityInfo.RESIZE_MODE_RESIZEABLE:
-            case ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION:
-                return COUNTER_WINDOW_SUPPORTED;
-            default:
-                return COUNTER_WINDOW_INCOMPATIBLE;
-        }
-    }
-
-    public void showNextAffiliatedTask() {
-        // Ensure the device has been provisioned before allowing the user to interact with
-        // recents
-        if (!isUserSetup()) {
-            return;
-        }
-
-        mImpl.showNextAffiliatedTask();
-    }
-
-    public void showPrevAffiliatedTask() {
-        // Ensure the device has been provisioned before allowing the user to interact with
-        // recents
-        if (!isUserSetup()) {
-            return;
-        }
-
-        mImpl.showPrevAffiliatedTask();
-    }
-
-    @Override
-    public void appTransitionFinished() {
-        if (!Recents.getConfiguration().isLowRamDevice) {
-            // Fallback, reset the flag once an app transition ends
-            EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(
-                    false /* waitingForTransitionStart */));
-        }
-    }
-
-    /**
-     * Updates on configuration change.
-     */
-    public void onConfigurationChanged(Configuration newConfig) {
-        int currentUser = sSystemServicesProxy.getCurrentUser();
-        if (sSystemServicesProxy.isSystemUser(currentUser)) {
-            mImpl.onConfigurationChanged();
-        } else {
-            if (mSystemToUserCallbacks != null) {
-                IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
-                if (callbacks != null) {
-                    try {
-                        callbacks.onConfigurationChanged();
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                } else {
-                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
-                }
-            }
-        }
-    }
-
-    /**
-     * Handle Recents activity visibility changed.
-     */
-    public final void onBusEvent(final RecentsVisibilityChangedEvent event) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        int processUser = ssp.getProcessUser();
-        if (ssp.isSystemUser(processUser)) {
-            mImpl.onVisibilityChanged(event.applicationContext, event.visible);
-        } else {
-            postToSystemUser(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        mUserToSystemCallbacks.updateRecentsVisibility(event.visible);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                }
-            });
-        }
-
-        // This will catch the cases when a user launches from recents to another app
-        // (and vice versa) that is not in the recents stack (such as home or bugreport) and it
-        // would not reset the wait for transition flag. This will catch it and make sure that the
-        // flag is reset.
-        if (!event.visible) {
-            mImpl.setWaitingForTransitionStart(false);
-        }
-    }
-
-    public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        int processUser = ssp.getProcessUser();
-        if (ssp.isSystemUser(processUser)) {
-            final Divider divider = getComponent(Divider.class);
-            if (divider != null) {
-                divider.onDockedFirstAnimationFrame();
-            }
-        } else {
-            postToSystemUser(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        mUserToSystemCallbacks.sendDockedFirstAnimationFrameEvent();
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                }
-            });
-        }
-    }
-
-    /**
-     * Handle screen pinning request.
-     */
-    public final void onBusEvent(final ScreenPinningRequestEvent event) {
-        int processUser = sSystemServicesProxy.getProcessUser();
-        if (sSystemServicesProxy.isSystemUser(processUser)) {
-            mImpl.onStartScreenPinning(event.applicationContext, event.taskId);
-        } else {
-            postToSystemUser(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        mUserToSystemCallbacks.startScreenPinning(event.taskId);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                }
-            });
-        }
-    }
-
-    public final void onBusEvent(final RecentsDrawnEvent event) {
-        int processUser = sSystemServicesProxy.getProcessUser();
-        if (sSystemServicesProxy.isSystemUser(processUser)) {
-            final Divider divider = getComponent(Divider.class);
-            if (divider != null) {
-                divider.onRecentsDrawn();
-            }
-        } else {
-            postToSystemUser(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        mUserToSystemCallbacks.sendRecentsDrawnEvent();
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                }
-            });
-        }
-    }
-
-    public final void onBusEvent(final DockedTopTaskEvent event) {
-        int processUser = sSystemServicesProxy.getProcessUser();
-        if (sSystemServicesProxy.isSystemUser(processUser)) {
-            final Divider divider = getComponent(Divider.class);
-            if (divider != null) {
-                divider.onDockedTopTask();
-            }
-        } else {
-            postToSystemUser(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        mUserToSystemCallbacks.sendDockingTopTaskEvent(event.initialRect);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                }
-            });
-        }
-    }
-
-    public final void onBusEvent(final RecentsActivityStartingEvent event) {
-        int processUser = sSystemServicesProxy.getProcessUser();
-        if (sSystemServicesProxy.isSystemUser(processUser)) {
-            final Divider divider = getComponent(Divider.class);
-            if (divider != null) {
-                divider.onRecentsActivityStarting();
-            }
-        } else {
-            postToSystemUser(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        mUserToSystemCallbacks.sendLaunchRecentsEvent();
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                }
-            });
-        }
-    }
-
-    public final void onBusEvent(LaunchTaskFailedEvent event) {
-        // Reset the transition when tasks fail to launch
-        mImpl.setWaitingForTransitionStart(false);
-    }
-
-    public final void onBusEvent(ConfigurationChangedEvent event) {
-        // Update the configuration for the Recents component when the activity configuration
-        // changes as well
-        mImpl.onConfigurationChanged();
-    }
-
-    public final void onBusEvent(ShowUserToastEvent event) {
-        int currentUser = sSystemServicesProxy.getCurrentUser();
-        if (sSystemServicesProxy.isSystemUser(currentUser)) {
-            mImpl.onShowCurrentUserToast(event.msgResId, event.msgLength);
-        } else {
-            if (mSystemToUserCallbacks != null) {
-                IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
-                if (callbacks != null) {
-                    try {
-                        callbacks.showCurrentUserToast(event.msgResId, event.msgLength);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                } else {
-                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
-                }
-            }
-        }
-    }
-
-    public final void onBusEvent(SetWaitingForTransitionStartEvent event) {
-        int processUser = sSystemServicesProxy.getProcessUser();
-        if (sSystemServicesProxy.isSystemUser(processUser)) {
-            mImpl.setWaitingForTransitionStart(event.waitingForTransitionStart);
-        } else {
-            postToSystemUser(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        mUserToSystemCallbacks.setWaitingForTransitionStartEvent(
-                                event.waitingForTransitionStart);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Callback failed", e);
-                    }
-                }
-            });
-        }
-    }
-
-    /**
-     * Attempts to register with the system user.
-     */
-    private void registerWithSystemUser() {
-        final int processUser = sSystemServicesProxy.getProcessUser();
-        postToSystemUser(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mUserToSystemCallbacks.registerNonSystemUserCallbacks(
-                            new RecentsImplProxy(mImpl), processUser);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to register", e);
-                }
-            }
-        });
-    }
-
-    /**
-     * Runs the runnable in the system user's Recents context, connecting to the service if
-     * necessary.
-     */
-    private void postToSystemUser(final Runnable onConnectRunnable) {
-        mOnConnectRunnables.add(onConnectRunnable);
-        if (mUserToSystemCallbacks == null) {
-            Intent systemUserServiceIntent = new Intent();
-            systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class);
-            boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent,
-                    mUserToSystemServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
-            EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
-                    EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_BIND_SERVICE,
-                    sSystemServicesProxy.getProcessUser());
-            if (!bound) {
-                // Retry after a fixed duration
-                mHandler.postDelayed(new Runnable() {
-                    @Override
-                    public void run() {
-                        registerWithSystemUser();
-                    }
-                }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
-            }
-        } else {
-            runAndFlushOnConnectRunnables();
-        }
-    }
-
-    /**
-     * Runs all the queued runnables after a service connection is made.
-     */
-    private void runAndFlushOnConnectRunnables() {
-        for (Runnable r : mOnConnectRunnables) {
-            r.run();
-        }
-        mOnConnectRunnables.clear();
+        return mImpl.splitPrimaryTask(stackCreateMode, initialBounds, metricsDockAction);
     }
 
     /**
@@ -861,9 +135,30 @@
                 (Settings.Secure.getInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 0) != 0);
     }
 
+    /**
+     * @return The recents implementation from the config.
+     */
+    private RecentsImplementation createRecentsImplementationFromConfig() {
+        final String clsName = mContext.getString(R.string.config_recentsComponent);
+        if (clsName == null || clsName.length() == 0) {
+            throw new RuntimeException("No recents component configured", null);
+        }
+        Class<?> cls = null;
+        try {
+            cls = mContext.getClassLoader().loadClass(clsName);
+        } catch (Throwable t) {
+            throw new RuntimeException("Error loading recents component: " + clsName, t);
+        }
+        try {
+            RecentsImplementation impl = (RecentsImplementation) cls.newInstance();
+            return impl;
+        } catch (Throwable t) {
+            throw new RuntimeException("Error creating recents component: " + clsName, t);
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("Recents");
-        pw.println("  currentUserId=" + SystemServicesProxy.getInstance(mContext).getCurrentUser());
+        mImpl.dump(pw);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
deleted file mode 100644
index 36a1255..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ /dev/null
@@ -1,864 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents;
-
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.app.TaskStackBuilder;
-import android.app.WallpaperManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnPreDrawListener;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.LatencyTracker;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
-import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
-import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
-import com.android.systemui.recents.events.activity.PackagesChangedEvent;
-import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
-import com.android.systemui.recents.events.component.ActivityUnpinnedEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
-import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
-import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
-import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
-import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
-import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
-import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
-import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
-import com.android.systemui.recents.events.ui.UserInteractionEvent;
-import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.recents.views.RecentsView;
-import com.android.systemui.recents.views.SystemBarScrimViews;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import com.android.systemui.shared.system.WindowManagerWrapper;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * The main Recents activity that is started from RecentsComponent.
- */
-public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreDrawListener,
-        ColorExtractor.OnColorsChangedListener {
-
-    private final static String TAG = "RecentsActivity";
-    private final static boolean DEBUG = false;
-
-    public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
-    public final static int INCOMPATIBLE_APP_ALPHA_DURATION = 150;
-
-    private PackageMonitor mPackageMonitor = new PackageMonitor() {
-            @Override
-            public void onPackageRemoved(String packageName, int uid) {
-                RecentsActivity.this.onPackageChanged(packageName, getChangingUserId());
-            }
-
-            @Override
-            public boolean onPackageChanged(String packageName, int uid, String[] components) {
-                RecentsActivity.this.onPackageChanged(packageName, getChangingUserId());
-                return true;
-            }
-
-            @Override
-            public void onPackageModified(String packageName) {
-                RecentsActivity.this.onPackageChanged(packageName, getChangingUserId());
-            }
-        };
-    private Handler mHandler = new Handler();
-    private long mLastTabKeyEventTime;
-    private boolean mFinishedOnStartup;
-    private boolean mIgnoreAltTabRelease;
-    private boolean mIsVisible;
-    private boolean mRecentsStartRequested;
-    private Configuration mLastConfig;
-
-    // Top level views
-    private RecentsView mRecentsView;
-    private SystemBarScrimViews mScrimViews;
-    private View mIncompatibleAppOverlay;
-
-    // Runnables to finish the Recents activity
-    private Intent mHomeIntent;
-
-    // The trigger to automatically launch the current task
-    private int mFocusTimerDuration;
-    private final UserInteractionEvent mUserInteractionEvent = new UserInteractionEvent();
-
-    // Theme and colors
-    private SysuiColorExtractor mColorExtractor;
-    private boolean mUsingDarkText;
-
-    /**
-     * A common Runnable to finish Recents by launching Home with an animation depending on the
-     * last activity launch state. Generally we always launch home when we exit Recents rather than
-     * just finishing the activity since we don't know what is behind Recents in the task stack.
-     */
-    class LaunchHomeRunnable implements Runnable {
-
-        Intent mLaunchIntent;
-        ActivityOptions mOpts;
-
-        /**
-         * Creates a finish runnable that starts the specified intent.
-         */
-        public LaunchHomeRunnable(Intent launchIntent, ActivityOptions opts) {
-            mLaunchIntent = launchIntent;
-            mOpts = opts;
-        }
-
-        @Override
-        public void run() {
-            try {
-                mHandler.post(() -> {
-                    ActivityOptions opts = mOpts;
-                    if (opts == null) {
-                        opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
-                                R.anim.recents_to_launcher_enter, R.anim.recents_to_launcher_exit);
-                    }
-                    startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
-                });
-            } catch (Exception e) {
-                Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
-            }
-        }
-    }
-
-    /**
-     * Broadcast receiver to handle messages from the system
-     */
-    final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context ctx, Intent intent) {
-            String action = intent.getAction();
-            if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                // When the screen turns off, dismiss Recents to Home
-                dismissRecentsToHomeIfVisible(false);
-            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
-                // When switching users, dismiss Recents to Home similar to screen off
-                finish();
-            }
-        }
-    };
-
-    private final OnPreDrawListener mRecentsDrawnEventListener =
-            new ViewTreeObserver.OnPreDrawListener() {
-                @Override
-                public boolean onPreDraw() {
-                    mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
-                    EventBus.getDefault().post(new RecentsDrawnEvent());
-                    if (LatencyTracker.isEnabled(getApplicationContext())) {
-                        DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(
-                                getApplicationContext()).onActionEnd(
-                                LatencyTracker.ACTION_TOGGLE_RECENTS));
-                    }
-                    DejankUtils.postAfterTraversal(() -> {
-                        Recents.getTaskLoader().startLoader(RecentsActivity.this);
-                        Recents.getTaskLoader().getHighResThumbnailLoader().setVisible(true);
-                    });
-                    return true;
-                }
-            };
-
-    /**
-     * Dismisses recents if we are already visible and the intent is to toggle the recents view.
-     */
-    boolean dismissRecentsToFocusedTask(int logCategory) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        if (ssp.isRecentsActivityVisible()) {
-            // If we have a focused Task, launch that Task now
-            if (mRecentsView.launchFocusedTask(logCategory)) return true;
-        }
-        return false;
-    }
-
-    /**
-     * Dismisses recents back to the launch target task.
-     */
-    boolean dismissRecentsToLaunchTargetTaskOrHome() {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        if (ssp.isRecentsActivityVisible()) {
-            // If we have a focused Task, launch that Task now
-            if (mRecentsView.launchPreviousTask()) return true;
-            // If none of the other cases apply, then just go Home
-            dismissRecentsToHome(true /* animateTaskViews */);
-        }
-        return false;
-    }
-
-    /**
-     * Dismisses recents if we are already visible and the intent is to toggle the recents view.
-     */
-    boolean dismissRecentsToFocusedTaskOrHome() {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        if (ssp.isRecentsActivityVisible()) {
-            // If we have a focused Task, launch that Task now
-            if (mRecentsView.launchFocusedTask(0 /* logCategory */)) return true;
-            // If none of the other cases apply, then just go Home
-            dismissRecentsToHome(true /* animateTaskViews */);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Dismisses Recents directly to Home without checking whether it is currently visible.
-     */
-    void dismissRecentsToHome(boolean animateTaskViews) {
-        dismissRecentsToHome(animateTaskViews, null);
-    }
-
-    /**
-     * Dismisses Recents directly to Home without checking whether it is currently visible.
-     *
-     * @param overrideAnimation If not null, will override the default animation that is based on
-     *                          how Recents was launched.
-     */
-    void dismissRecentsToHome(boolean animateTaskViews, ActivityOptions overrideAnimation) {
-        DismissRecentsToHomeAnimationStarted dismissEvent =
-                new DismissRecentsToHomeAnimationStarted(animateTaskViews);
-        dismissEvent.addPostAnimationCallback(new LaunchHomeRunnable(mHomeIntent,
-                overrideAnimation));
-        ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
-        EventBus.getDefault().send(dismissEvent);
-    }
-
-    /** Dismisses Recents directly to Home if we currently aren't transitioning. */
-    boolean dismissRecentsToHomeIfVisible(boolean animated) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        if (ssp.isRecentsActivityVisible()) {
-            // Return to Home
-            dismissRecentsToHome(animated);
-            return true;
-        }
-        return false;
-    }
-
-    /** Called with the activity is first created. */
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mFinishedOnStartup = false;
-
-        // In the case that the activity starts up before the Recents component has initialized
-        // (usually when debugging/pushing the SysUI apk), just finish this activity.
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        if (ssp == null) {
-            mFinishedOnStartup = true;
-            finish();
-            return;
-        }
-
-        // Register this activity with the event bus
-        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
-
-        // Initialize the package monitor
-        mPackageMonitor.register(this, Looper.getMainLooper(), UserHandle.ALL,
-                true /* externalStorage */);
-
-        // Select theme based on wallpaper colors
-        mColorExtractor = Dependency.get(SysuiColorExtractor.class);
-        mColorExtractor.addOnColorsChangedListener(this);
-        mUsingDarkText = mColorExtractor.getColors(ColorExtractor.TYPE_DARK,
-                WallpaperManager.FLAG_SYSTEM, true).supportsDarkText();
-        setTheme(mUsingDarkText ? R.style.RecentsTheme_Wallpaper_Light
-                : R.style.RecentsTheme_Wallpaper);
-
-        // Set the Recents layout
-        setContentView(R.layout.recents);
-        takeKeyEvents(true);
-        mRecentsView = findViewById(R.id.recents_view);
-        mScrimViews = new SystemBarScrimViews(this);
-        getWindow().getAttributes().privateFlags |=
-                WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
-        if (Recents.getConfiguration().isLowRamDevice) {
-            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
-        }
-
-        mLastConfig = new Configuration(Utilities.getAppConfiguration(this));
-
-        // Set the window background
-        mRecentsView.updateBackgroundScrim(getWindow(), isInMultiWindowMode());
-
-        // Create the home intent runnable
-        mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
-        mHomeIntent.addCategory(Intent.CATEGORY_HOME);
-        mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-
-        // Register the broadcast receiver to handle messages when the screen is turned off
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
-        registerReceiver(mSystemBroadcastReceiver, filter);
-
-        getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION);
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-
-        // Reload the stack view whenever we are made visible again
-        reloadStackView();
-
-        // Notify that recents is now visible
-        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
-        MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY);
-
-        // Getting system scrim colors ignoring wallpaper visibility since it should never be grey.
-        ColorExtractor.GradientColors systemColors = mColorExtractor.getColors(
-                ColorExtractor.TYPE_DARK, WallpaperManager.FLAG_SYSTEM, true);
-        // We don't want to interpolate colors because we're defining the initial state.
-        // Gradient should be set/ready when you open "Recents".
-        mRecentsView.setScrimColors(systemColors, false);
-
-        // Notify of the next draw
-        mRecentsView.getViewTreeObserver().addOnPreDrawListener(mRecentsDrawnEventListener);
-
-        // If Recents was restarted, then it should complete the enter animation with partially
-        // reset launch state with dock, app and home set to false
-        Object isRelaunching = getLastNonConfigurationInstance();
-        if (isRelaunching != null && isRelaunching instanceof Boolean && (boolean) isRelaunching) {
-            RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-            launchState.launchedViaDockGesture = false;
-            launchState.launchedFromApp = false;
-            launchState.launchedFromHome = false;
-            onEnterAnimationComplete();
-        }
-        mRecentsStartRequested = false;
-    }
-
-    @Override
-    public void onColorsChanged(ColorExtractor colorExtractor, int which) {
-        if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
-            // Recents doesn't care about the wallpaper being visible or not, it always
-            // wants to scrim with wallpaper colors
-            ColorExtractor.GradientColors colors = mColorExtractor.getColors(
-                    WallpaperManager.FLAG_SYSTEM,
-                    ColorExtractor.TYPE_DARK, true /* ignoreVis */);
-            boolean darkText = colors.supportsDarkText();
-            if (darkText != mUsingDarkText) {
-                mUsingDarkText = darkText;
-                setTheme(mUsingDarkText ? R.style.RecentsTheme_Wallpaper_Light
-                        : R.style.RecentsTheme_Wallpaper);
-                mRecentsView.reevaluateStyles();
-            }
-            mRecentsView.setScrimColors(colors, true /* animated */);
-        }
-    }
-
-    /**
-     * Reloads the stack views upon launching Recents.
-     */
-    private void reloadStackView() {
-        // If the Recents component has preloaded a load plan, then use that to prevent
-        // reconstructing the task stack
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-        RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan();
-        if (loadPlan == null) {
-            loadPlan = new RecentsTaskLoadPlan(this);
-        }
-
-        // Start loading tasks according to the load plan
-        RecentsConfiguration config = Recents.getConfiguration();
-        RecentsActivityLaunchState launchState = config.getLaunchState();
-        if (!loadPlan.hasTasks()) {
-            loader.preloadTasks(loadPlan, launchState.launchedToTaskId);
-        }
-
-        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
-        loadOpts.runningTaskId = launchState.launchedToTaskId;
-        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
-        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
-        loader.loadTasks(loadPlan, loadOpts);
-        TaskStack stack = loadPlan.getTaskStack();
-        mRecentsView.onReload(stack, mIsVisible);
-
-        // Update the nav bar scrim, but defer the animation until the enter-window event
-        boolean animateNavBarScrim = !launchState.launchedViaDockGesture;
-        mScrimViews.updateNavBarScrim(animateNavBarScrim, stack.getTaskCount() > 0, null);
-
-        // If this is a new instance relaunched by AM, without going through the normal mechanisms,
-        // then we have to manually trigger the enter animation state
-        boolean wasLaunchedByAm = !launchState.launchedFromHome &&
-                !launchState.launchedFromApp;
-        if (wasLaunchedByAm) {
-            EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
-        }
-
-        // Keep track of whether we launched from the nav bar button or via alt-tab
-        if (launchState.launchedWithAltTab) {
-            MetricsLogger.count(this, "overview_trigger_alttab", 1);
-        } else {
-            MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
-        }
-
-        // Keep track of whether we launched from an app or from home
-        if (launchState.launchedFromApp) {
-            Task launchTarget = stack.getLaunchTarget();
-            int launchTaskIndexInStack = launchTarget != null
-                    ? stack.indexOfTask(launchTarget)
-                    : 0;
-            MetricsLogger.count(this, "overview_source_app", 1);
-            // If from an app, track the stack index of the app in the stack (for affiliated tasks)
-            MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
-        } else {
-            MetricsLogger.count(this, "overview_source_home", 1);
-        }
-
-        // Keep track of the total stack task count
-        int taskCount = mRecentsView.getStack().getTaskCount();
-        MetricsLogger.histogram(this, "overview_task_count", taskCount);
-
-        // After we have resumed, set the visible state until the next onStop() call
-        mIsVisible = true;
-    }
-
-    @Override
-    public void onEnterAnimationComplete() {
-        super.onEnterAnimationComplete();
-        EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
-
-        // Workaround for b/64694148: The animation started callback is not made (see
-        // RecentsImpl.getThumbnailTransitionActivityOptions) so reset the transition-waiting state
-        // once the enter animation has completed.
-        EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
-    }
-
-    @Override
-    public Object onRetainNonConfigurationInstance() {
-        return true;
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-
-        mIgnoreAltTabRelease = false;
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-
-        // Notify of the config change
-        Configuration newDeviceConfiguration = Utilities.getAppConfiguration(this);
-        int numStackTasks = mRecentsView.getStack().getTaskCount();
-        EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
-                mLastConfig.orientation != newDeviceConfiguration.orientation,
-                mLastConfig.densityDpi != newDeviceConfiguration.densityDpi, numStackTasks > 0));
-
-        mLastConfig.updateFrom(newDeviceConfiguration);
-    }
-
-    @Override
-    public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
-        super.onMultiWindowModeChanged(isInMultiWindowMode);
-
-        // Set the window background
-        mRecentsView.updateBackgroundScrim(getWindow(), isInMultiWindowMode);
-
-        // Reload the task stack view if we are still visible to pick up the change in tasks that
-        // result from entering/exiting multi-window
-        if (mIsVisible) {
-            reloadTaskStack(isInMultiWindowMode, true /* sendConfigChangedEvent */);
-        }
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        // Notify that recents is now hidden
-        mIsVisible = false;
-        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
-        MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
-        Recents.getTaskLoader().getHighResThumbnailLoader().setVisible(false);
-
-        // When recents starts again before onStop, do not reset launch flags so entrance animation
-        // can run
-        if (!isChangingConfigurations() && !mRecentsStartRequested) {
-            // Workaround for b/22542869, if the RecentsActivity is started again, but without going
-            // through SystemUI, we need to reset the config launch flags to ensure that we do not
-            // wait on the system to send a signal that was never queued.
-            RecentsConfiguration config = Recents.getConfiguration();
-            RecentsActivityLaunchState launchState = config.getLaunchState();
-            launchState.reset();
-        }
-
-        // Force a gc to attempt to clean up bitmap references more quickly (b/38258699)
-        Recents.getSystemServices().gc();
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-
-        // In the case that the activity finished on startup, just skip the unregistration below
-        if (mFinishedOnStartup) {
-            return;
-        }
-
-        // Unregister the system broadcast receivers
-        unregisterReceiver(mSystemBroadcastReceiver);
-
-        // Unregister any broadcast receivers for the task loader
-        mPackageMonitor.unregister();
-
-        EventBus.getDefault().unregister(this);
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        EventBus.getDefault().register(mScrimViews, EVENT_BUS_PRIORITY);
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        EventBus.getDefault().unregister(mScrimViews);
-    }
-
-    @Override
-    public void onTrimMemory(int level) {
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-        if (loader != null) {
-            loader.onTrimMemory(level);
-        }
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_TAB: {
-                int altTabKeyDelay = getResources().getInteger(R.integer.recents_alt_tab_key_delay);
-                boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
-                        mLastTabKeyEventTime) > altTabKeyDelay;
-                if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
-                    // Focus the next task in the stack
-                    final boolean backward = event.isShiftPressed();
-                    if (backward) {
-                        EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
-                    } else {
-                        EventBus.getDefault().send(new FocusNextTaskViewEvent());
-                    }
-                    mLastTabKeyEventTime = SystemClock.elapsedRealtime();
-
-                    // In the case of another ALT event, don't ignore the next release
-                    if (event.isAltPressed()) {
-                        mIgnoreAltTabRelease = false;
-                    }
-                }
-                return true;
-            }
-            case KeyEvent.KEYCODE_DPAD_UP:
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-            case KeyEvent.KEYCODE_DPAD_RIGHT: {
-                final Direction direction = NavigateTaskViewEvent.getDirectionFromKeyCode(keyCode);
-                EventBus.getDefault().send(new NavigateTaskViewEvent(direction));
-                return true;
-            }
-            case KeyEvent.KEYCODE_DEL:
-            case KeyEvent.KEYCODE_FORWARD_DEL: {
-                if (event.getRepeatCount() <= 0) {
-                    EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
-
-                    // Keep track of deletions by keyboard
-                    MetricsLogger.histogram(this, "overview_task_dismissed_source",
-                            Constants.Metrics.DismissSourceKeyboard);
-                    return true;
-                }
-            }
-            default:
-                break;
-        }
-        return super.onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public void onUserInteraction() {
-        EventBus.getDefault().send(mUserInteractionEvent);
-    }
-
-    @Override
-    public void onBackPressed() {
-        // Back behaves like the recents button so just trigger a toggle event
-        EventBus.getDefault().send(new ToggleRecentsEvent());
-    }
-
-    /**** EventBus events ****/
-
-    public final void onBusEvent(ToggleRecentsEvent event) {
-        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-        if (launchState.launchedFromHome) {
-            dismissRecentsToHome(true /* animateTaskViews */);
-        } else {
-            dismissRecentsToLaunchTargetTaskOrHome();
-        }
-    }
-
-    public final void onBusEvent(RecentsActivityStartingEvent event) {
-        mRecentsStartRequested = true;
-    }
-
-    public final void onBusEvent(HideRecentsEvent event) {
-        if (event.triggeredFromAltTab) {
-            // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
-            if (!mIgnoreAltTabRelease) {
-                dismissRecentsToFocusedTaskOrHome();
-            }
-        } else if (event.triggeredFromHomeKey) {
-            dismissRecentsToHome(true /* animateTaskViews */);
-
-            // Cancel any pending dozes
-            EventBus.getDefault().send(mUserInteractionEvent);
-        } else {
-            // Do nothing
-        }
-    }
-
-    public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
-        mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
-        mRecentsView.invalidate();
-    }
-
-    public final void onBusEvent(ExitRecentsWindowFirstAnimationFrameEvent event) {
-        mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
-        mRecentsView.invalidate();
-    }
-
-    public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
-        mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
-        mRecentsView.invalidate();
-    }
-
-    public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
-        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-        int launchToTaskId = launchState.launchedToTaskId;
-        if (launchToTaskId != -1 &&
-                (event.launchTask == null || launchToTaskId != event.launchTask.key.id)) {
-            ActivityManagerWrapper am = ActivityManagerWrapper.getInstance();
-            am.cancelWindowTransition(launchState.launchedToTaskId);
-        }
-    }
-
-    public final void onBusEvent(ShowApplicationInfoEvent event) {
-        // Create a new task stack with the application info details activity
-        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
-                Uri.fromParts("package", event.task.key.getComponent().getPackageName(), null));
-        intent.setComponent(intent.resolveActivity(getPackageManager()));
-        TaskStackBuilder.create(this)
-                .addNextIntentWithParentStack(intent).startActivities(null,
-                        new UserHandle(event.task.key.userId));
-
-        // Keep track of app-info invocations
-        MetricsLogger.count(this, "overview_app_info", 1);
-    }
-
-    public final void onBusEvent(ShowIncompatibleAppOverlayEvent event) {
-        if (mIncompatibleAppOverlay == null) {
-            mIncompatibleAppOverlay = Utilities.findViewStubById(this,
-                    R.id.incompatible_app_overlay_stub).inflate();
-            mIncompatibleAppOverlay.setWillNotDraw(false);
-            mIncompatibleAppOverlay.setVisibility(View.VISIBLE);
-        }
-        mIncompatibleAppOverlay.animate()
-                .alpha(1f)
-                .setDuration(INCOMPATIBLE_APP_ALPHA_DURATION)
-                .setInterpolator(Interpolators.ALPHA_IN)
-                .start();
-    }
-
-    public final void onBusEvent(HideIncompatibleAppOverlayEvent event) {
-        if (mIncompatibleAppOverlay != null) {
-            mIncompatibleAppOverlay.animate()
-                    .alpha(0f)
-                    .setDuration(INCOMPATIBLE_APP_ALPHA_DURATION)
-                    .setInterpolator(Interpolators.ALPHA_OUT)
-                    .start();
-        }
-    }
-
-    public final void onBusEvent(DeleteTaskDataEvent event) {
-        // Remove any stored data from the loader
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-        loader.deleteTaskData(event.task, false);
-
-        // Remove the task from activity manager
-        ActivityManagerWrapper.getInstance().removeTask(event.task.key.id);
-    }
-
-    public final void onBusEvent(TaskViewDismissedEvent event) {
-        mRecentsView.updateScrimOpacity();
-    }
-
-    public final void onBusEvent(AllTaskViewsDismissedEvent event) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        if (ssp.hasDockedTask()) {
-            mRecentsView.showEmptyView(event.msgResId);
-        } else {
-            // Just go straight home (no animation necessary because there are no more task views)
-            dismissRecentsToHome(false /* animateTaskViews */);
-        }
-
-        // Keep track of all-deletions
-        MetricsLogger.count(this, "overview_task_all_dismissed", 1);
-    }
-
-    public final void onBusEvent(LaunchTaskSucceededEvent event) {
-        MetricsLogger.histogram(this, "overview_task_launch_index", event.taskIndexFromStackFront);
-    }
-
-    public final void onBusEvent(LaunchTaskFailedEvent event) {
-        // Return to Home
-        dismissRecentsToHome(true /* animateTaskViews */);
-
-        MetricsLogger.count(this, "overview_task_launch_failed", 1);
-    }
-
-    public final void onBusEvent(ScreenPinningRequestEvent event) {
-        MetricsLogger.count(this, "overview_screen_pinned", 1);
-    }
-
-    public final void onBusEvent(StackViewScrolledEvent event) {
-        // Once the user has scrolled while holding alt-tab, then we should ignore the release of
-        // the key
-        mIgnoreAltTabRelease = true;
-    }
-
-    public final void onBusEvent(final DockedTopTaskEvent event) {
-        mRecentsView.getViewTreeObserver().addOnPreDrawListener(mRecentsDrawnEventListener);
-        mRecentsView.invalidate();
-    }
-
-    public final void onBusEvent(final ActivityUnpinnedEvent event) {
-        if (mIsVisible) {
-            // Skip the configuration change event as the PiP activity does not actually affect the
-            // config of recents
-            reloadTaskStack(isInMultiWindowMode(), false /* sendConfigChangedEvent */);
-        }
-    }
-
-    private void reloadTaskStack(boolean isInMultiWindowMode, boolean sendConfigChangedEvent) {
-        // Reload the task stack completely
-        RecentsConfiguration config = Recents.getConfiguration();
-        RecentsActivityLaunchState launchState = config.getLaunchState();
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-        RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(this);
-        loader.preloadTasks(loadPlan, -1 /* runningTaskId */);
-
-        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
-        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
-        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
-        loader.loadTasks(loadPlan, loadOpts);
-
-        TaskStack stack = loadPlan.getTaskStack();
-        int numStackTasks = stack.getTaskCount();
-        boolean showDeferredAnimation = numStackTasks > 0;
-
-        if (sendConfigChangedEvent) {
-            EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */,
-                    false /* fromDeviceOrientationChange */, false /* fromDisplayDensityChange */,
-                    numStackTasks > 0));
-        }
-        EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode,
-                showDeferredAnimation, stack));
-    }
-
-    @Override
-    public boolean onPreDraw() {
-        mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
-        return true;
-    }
-
-    public void onPackageChanged(String packageName, int userId) {
-        Recents.getTaskLoader().onPackageChanged(packageName);
-        EventBus.getDefault().send(new PackagesChangedEvent(packageName, userId));
-    }
-
-    @Override
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        super.dump(prefix, fd, writer, args);
-        EventBus.getDefault().dump(prefix, writer);
-        Recents.getTaskLoader().dump(prefix, writer);
-
-        String id = Integer.toHexString(System.identityHashCode(this));
-
-        writer.print(prefix); writer.print(TAG);
-        writer.print(" visible="); writer.print(mIsVisible ? "Y" : "N");
-        writer.print(" currentTime="); writer.print(System.currentTimeMillis());
-        writer.print(" [0x"); writer.print(id); writer.print("]");
-        writer.println();
-
-        if (mRecentsView != null) {
-            mRecentsView.dump(prefix, writer);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
deleted file mode 100644
index 68df1d5..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-
-import android.os.SystemProperties;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.views.DockState;
-import com.android.systemui.shared.recents.model.TaskStack;
-
-/**
- * Represents the dock regions for each orientation.
- */
-class DockRegion {
-    public static DockState[] PHONE_LANDSCAPE = {
-            // We only allow docking to the left in landscape for now on small devices
-            DockState.LEFT
-    };
-    public static DockState[] PHONE_PORTRAIT = {
-            // We only allow docking to the top for now on small devices
-            DockState.TOP
-    };
-    public static DockState[] TABLET_LANDSCAPE = {
-            DockState.LEFT,
-            DockState.RIGHT
-    };
-    public static DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT;
-}
-
-/**
- * Application resources that can be retrieved from the application context and are not specifically
- * tied to the current activity.
- */
-public class RecentsConfiguration {
-
-    private static final int LARGE_SCREEN_MIN_DP = 600;
-    private static final int XLARGE_SCREEN_MIN_DP = 720;
-
-    // Launch states
-    public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState();
-
-    // Since the positions in Recents has to be calculated globally (before the RecentsActivity
-    // starts), we need to calculate some resource values ourselves, instead of relying on framework
-    // resources.
-    public final boolean isLargeScreen;
-    public final boolean isXLargeScreen;
-    public final int smallestWidth;
-
-    /** Misc **/
-    public boolean fakeShadows;
-    public int svelteLevel;
-
-    // Whether this product supports Grid-based Recents. If this is field is set to true, then
-    // Recents will layout task views in a grid mode when there's enough space in the screen.
-    public boolean isGridEnabled;
-
-    // Support for Android Recents for low ram devices. If this field is set to true, then Recents
-    // will use the alternative layout.
-    public boolean isLowRamDevice;
-
-    // Enable drag and drop split from Recents. Disabled for low ram devices.
-    public boolean dragToSplitEnabled;
-
-    private final Context mAppContext;
-
-    public RecentsConfiguration(Context context) {
-        // Load only resources that can not change after the first load either through developer
-        // settings or via multi window
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        mAppContext = context.getApplicationContext();
-        Resources res = mAppContext.getResources();
-        fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
-        svelteLevel = res.getInteger(R.integer.recents_svelte_level);
-        isGridEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
-        isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
-        dragToSplitEnabled = !isLowRamDevice;
-
-        float screenDensity = context.getResources().getDisplayMetrics().density;
-        smallestWidth = ssp.getDeviceSmallestWidth();
-        isLargeScreen = smallestWidth >= (int) (screenDensity * LARGE_SCREEN_MIN_DP);
-        isXLargeScreen = smallestWidth >= (int) (screenDensity * XLARGE_SCREEN_MIN_DP);
-    }
-
-    /**
-     * Returns the activity launch state.
-     * TODO: This will be refactored out of RecentsConfiguration.
-     */
-    public RecentsActivityLaunchState getLaunchState() {
-        return mLaunchState;
-    }
-
-    /**
-     * Returns the preferred dock states for the current orientation.
-     * @return a list of dock states for device and its orientation
-     */
-    public DockState[] getDockStatesForCurrentOrientation() {
-        boolean isLandscape = mAppContext.getResources().getConfiguration().orientation ==
-                Configuration.ORIENTATION_LANDSCAPE;
-        RecentsConfiguration config = Recents.getConfiguration();
-        if (config.isLargeScreen) {
-            return isLandscape ? DockRegion.TABLET_LANDSCAPE : DockRegion.TABLET_PORTRAIT;
-        } else {
-            return isLandscape ? DockRegion.PHONE_LANDSCAPE : DockRegion.PHONE_PORTRAIT;
-        }
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
deleted file mode 100644
index d95c731..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ /dev/null
@@ -1,1132 +0,0 @@
-/*
- * Copyright (C) 2015 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.systemui.recents;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.view.View.MeasureSpec;
-
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
-
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.trust.TrustManager;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentCallbacks2;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.MutableBoolean;
-import android.util.Pair;
-import android.view.LayoutInflater;
-import android.view.ViewConfiguration;
-import android.view.WindowManager;
-
-import android.widget.Toast;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.OverviewProxyService;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.pip.phone.ForegroundThread;
-import com.google.android.collect.Lists;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.policy.DockedDividerUtils;
-import com.android.systemui.R;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.activity.LaunchMostRecentTaskRequestEvent;
-import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
-import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
-import com.android.systemui.recents.events.component.ActivityPinnedEvent;
-import com.android.systemui.recents.events.component.ActivityUnpinnedEvent;
-import com.android.systemui.recents.events.component.HidePipMenuEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
-import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
-import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
-import com.android.systemui.recents.misc.DozeTrigger;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
-import com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
-import com.android.systemui.recents.views.TaskStackView;
-import com.android.systemui.recents.views.TaskViewHeader;
-import com.android.systemui.recents.views.TaskViewTransform;
-import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
-import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.stackdivider.DividerView;
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An implementation of the Recents component for the current user.  For secondary users, this can
- * be called remotely from the system user.
- */
-public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener {
-
-    private final static String TAG = "RecentsImpl";
-
-    // The minimum amount of time between each recents button press that we will handle
-    private final static int MIN_TOGGLE_DELAY_MS = 350;
-
-    // The duration within which the user releasing the alt tab (from when they pressed alt tab)
-    // that the fast alt-tab animation will run.  If the user's alt-tab takes longer than this
-    // duration, then we will toggle recents after this duration.
-    private final static int FAST_ALT_TAB_DELAY_MS = 225;
-
-    private final static ArraySet<TaskKey> EMPTY_SET = new ArraySet<>();
-
-    public final static String RECENTS_PACKAGE = "com.android.systemui";
-    public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
-
-    /**
-     * An implementation of SysUiTaskStackChangeListener, that allows us to listen for changes to the system
-     * task stacks and update recents accordingly.
-     */
-    class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
-
-        private OverviewProxyService mOverviewProxyService;
-
-        public TaskStackListenerImpl() {
-            mOverviewProxyService = Dependency.get(OverviewProxyService.class);
-        }
-
-        @Override
-        public void onTaskStackChangedBackground() {
-            // Skip background preloading recents in SystemUI if the overview services is bound
-            if (mOverviewProxyService.isEnabled()) {
-                return;
-            }
-
-            // Check this is for the right user
-            if (!checkCurrentUserId(mContext, false /* debug */)) {
-                return;
-            }
-
-            // Preloads the next task
-            RecentsConfiguration config = Recents.getConfiguration();
-            if (config.svelteLevel == RecentsTaskLoader.SVELTE_NONE) {
-                Rect windowRect = getWindowRect(null /* windowRectOverride */);
-                if (windowRect.isEmpty()) {
-                    return;
-                }
-
-                // Load the next task only if we aren't svelte
-                ActivityManager.RunningTaskInfo runningTaskInfo =
-                        ActivityManagerWrapper.getInstance().getRunningTask();
-                RecentsTaskLoader loader = Recents.getTaskLoader();
-                RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
-                loader.preloadTasks(plan, -1);
-                TaskStack stack = plan.getTaskStack();
-                RecentsActivityLaunchState launchState = new RecentsActivityLaunchState();
-                RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
-
-                synchronized (mBackgroundLayoutAlgorithm) {
-                    // This callback is made when a new activity is launched and the old one is
-                    // paused so ignore the current activity and try and preload the thumbnail for
-                    // the previous one.
-                    updateDummyStackViewLayout(mBackgroundLayoutAlgorithm, stack, windowRect);
-
-                    // Launched from app is always the worst case (in terms of how many
-                    // thumbnails/tasks visible)
-                    launchState.launchedFromApp = true;
-                    mBackgroundLayoutAlgorithm.update(plan.getTaskStack(), EMPTY_SET, launchState,
-                            -1 /* lastScrollPPresent */);
-                    VisibilityReport visibilityReport =
-                            mBackgroundLayoutAlgorithm.computeStackVisibilityReport(
-                                    stack.getTasks());
-
-                    launchOpts.runningTaskId = runningTaskInfo != null ? runningTaskInfo.id : -1;
-                    launchOpts.numVisibleTasks = visibilityReport.numVisibleTasks;
-                    launchOpts.numVisibleTaskThumbnails = visibilityReport.numVisibleThumbnails;
-                    launchOpts.onlyLoadForCache = true;
-                    launchOpts.onlyLoadPausedActivities = true;
-                    launchOpts.loadThumbnails = true;
-                }
-                loader.loadTasks(plan, launchOpts);
-            }
-        }
-
-        @Override
-        public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
-            // Check this is for the right user
-            if (!checkCurrentUserId(mContext, false /* debug */)) {
-                return;
-            }
-
-            // This time needs to be fetched the same way the last active time is fetched in
-            // {@link TaskRecord#touchActiveTime}
-            Recents.getConfiguration().getLaunchState().launchedFromPipApp = true;
-            Recents.getConfiguration().getLaunchState().launchedWithNextPipApp = false;
-            EventBus.getDefault().send(new ActivityPinnedEvent(taskId));
-            consumeInstanceLoadPlan();
-            sLastPipTime = System.currentTimeMillis();
-        }
-
-        @Override
-        public void onActivityUnpinned() {
-            // Check this is for the right user
-            if (!checkCurrentUserId(mContext, false /* debug */)) {
-                return;
-            }
-
-            EventBus.getDefault().send(new ActivityUnpinnedEvent());
-            sLastPipTime = -1;
-        }
-
-        @Override
-        public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
-            // Check this is for the right user
-            if (!checkCurrentUserId(mContext, false /* debug */)) {
-                return;
-            }
-
-            EventBus.getDefault().send(new TaskSnapshotChangedEvent(taskId, snapshot));
-        }
-    }
-
-    protected static RecentsTaskLoadPlan sInstanceLoadPlan;
-    // Stores the last pinned task time
-    protected static long sLastPipTime = -1;
-    // Stores whether we are waiting for a transition to/from recents to start. During this time,
-    // we disallow the user from manually toggling recents until the transition has started.
-    private static boolean mWaitingForTransitionStart = false;
-    // Stores whether or not the user toggled while we were waiting for a transition to/from
-    // recents. In this case, we defer the toggle state until then and apply it immediately after.
-    private static boolean mToggleFollowingTransitionStart = true;
-
-    private Runnable mResetToggleFlagListener = new Runnable() {
-        @Override
-        public void run() {
-            setWaitingForTransitionStart(false);
-        }
-    };
-
-    private TrustManager mTrustManager;
-    protected Context mContext;
-    protected Handler mHandler;
-    TaskStackListenerImpl mTaskStackListener;
-    boolean mDraggingInRecents;
-    boolean mLaunchedWhileDocking;
-
-    // Task launching
-    Rect mTmpBounds = new Rect();
-    TaskViewTransform mTmpTransform = new TaskViewTransform();
-    int mTaskBarHeight;
-
-    // Header (for transition)
-    TaskViewHeader mHeaderBar;
-    final Object mHeaderBarLock = new Object();
-    private TaskStackView mDummyStackView;
-    private TaskStackLayoutAlgorithm mBackgroundLayoutAlgorithm;
-
-    // Variables to keep track of if we need to start recents after binding
-    protected boolean mTriggeredFromAltTab;
-    protected long mLastToggleTime;
-    DozeTrigger mFastAltTabTrigger = new DozeTrigger(FAST_ALT_TAB_DELAY_MS, new Runnable() {
-        @Override
-        public void run() {
-            // When this fires, then the user has not released alt-tab for at least
-            // FAST_ALT_TAB_DELAY_MS milliseconds
-            showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */,
-                    DividerView.INVALID_RECENTS_GROW_TARGET);
-        }
-    });
-
-    private OverviewProxyService.OverviewProxyListener mOverviewProxyListener =
-            new OverviewProxyService.OverviewProxyListener() {
-        @Override
-        public void onConnectionChanged(boolean isConnected) {
-            if (!isConnected) {
-                // Clear everything when the connection to the overview service
-                Recents.getTaskLoader().onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
-            }
-        }
-    };
-
-    // Used to reset the dummy stack view
-    private final TaskStack mEmptyTaskStack = new TaskStack();
-
-    public RecentsImpl(Context context) {
-        mContext = context;
-        mHandler = new Handler();
-        mBackgroundLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
-
-        // Initialize the static foreground thread
-        ForegroundThread.get();
-
-        // Register the task stack listener
-        mTaskStackListener = new TaskStackListenerImpl();
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
-
-        // Initialize the static configuration resources
-        mDummyStackView = new TaskStackView(mContext);
-        reloadResources();
-
-        mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
-    }
-
-    public void onBootCompleted() {
-        // Skip preloading tasks if we are already bound to the service
-        if (Dependency.get(OverviewProxyService.class).isEnabled()) {
-            return;
-        }
-
-        // When we start, preload the data associated with the previous recent tasks.
-        // We can use a new plan since the caches will be the same.
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-        RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
-        loader.preloadTasks(plan, -1);
-        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
-        launchOpts.numVisibleTasks = loader.getIconCacheSize();
-        launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
-        launchOpts.onlyLoadForCache = true;
-        loader.loadTasks(plan, launchOpts);
-    }
-
-    public void onConfigurationChanged() {
-        reloadResources();
-        mDummyStackView.reloadOnConfigurationChange();
-        synchronized (mBackgroundLayoutAlgorithm) {
-            mBackgroundLayoutAlgorithm.reloadOnConfigurationChange(mContext);
-        }
-    }
-
-    /**
-     * This is only called from the system user's Recents.  Secondary users will instead proxy their
-     * visibility change events through to the system user via
-     * {@link Recents#onBusEvent(RecentsVisibilityChangedEvent)}.
-     */
-    public void onVisibilityChanged(Context context, boolean visible) {
-        Recents.getSystemServices().setRecentsVisibility(visible);
-    }
-
-    /**
-     * This is only called from the system user's Recents.  Secondary users will instead proxy their
-     * visibility change events through to the system user via
-     * {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
-     */
-    public void onStartScreenPinning(Context context, int taskId) {
-        final StatusBar statusBar = getStatusBar();
-        if (statusBar != null) {
-            statusBar.showScreenPinningRequest(taskId, false);
-        }
-    }
-
-    public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
-            boolean animate, int growTarget) {
-        final SystemServicesProxy ssp = Recents.getSystemServices();
-        final MutableBoolean isHomeStackVisible = new MutableBoolean(true);
-        final boolean isRecentsVisible = Recents.getSystemServices().isRecentsActivityVisible(
-                isHomeStackVisible);
-        final boolean fromHome = isHomeStackVisible.value;
-        final boolean launchedWhileDockingTask =
-                Recents.getSystemServices().getSplitScreenPrimaryStack() != null;
-
-        mTriggeredFromAltTab = triggeredFromAltTab;
-        mDraggingInRecents = draggingInRecents;
-        mLaunchedWhileDocking = launchedWhileDockingTask;
-        if (mFastAltTabTrigger.isAsleep()) {
-            // Fast alt-tab duration has elapsed, fall through to showing Recents and reset
-            mFastAltTabTrigger.stopDozing();
-        } else if (mFastAltTabTrigger.isDozing()) {
-            // Fast alt-tab duration has not elapsed.  If this is triggered by a different
-            // showRecents() call, then ignore that call for now.
-            // TODO: We can not handle quick tabs that happen between the initial showRecents() call
-            //       that started the activity and the activity starting up.  The severity of this
-            //       is inversely proportional to the FAST_ALT_TAB_DELAY_MS duration though.
-            if (!triggeredFromAltTab) {
-                return;
-            }
-            mFastAltTabTrigger.stopDozing();
-        } else if (triggeredFromAltTab) {
-            // The fast alt-tab detector is not yet running, so start the trigger and wait for the
-            // hideRecents() call, or for the fast alt-tab duration to elapse
-            mFastAltTabTrigger.startDozing();
-            return;
-        }
-
-        try {
-            // Check if the top task is in the home stack, and start the recents activity
-            final boolean forceVisible = launchedWhileDockingTask || draggingInRecents;
-            if (forceVisible || !isRecentsVisible) {
-                ActivityManager.RunningTaskInfo runningTask =
-                        ActivityManagerWrapper.getInstance().getRunningTask();
-                startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
-                        isHomeStackVisible.value || fromHome, animate, growTarget);
-            }
-        } catch (ActivityNotFoundException e) {
-            Log.e(TAG, "Failed to launch RecentsActivity", e);
-        }
-    }
-
-    public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
-        if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
-            // The user has released alt-tab before the trigger has run, so just show the next
-            // task immediately
-            showNextTask();
-
-            // Cancel the fast alt-tab trigger
-            mFastAltTabTrigger.stopDozing();
-            return;
-        }
-
-        // Defer to the activity to handle hiding recents, if it handles it, then it must still
-        // be visible
-        EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
-                triggeredFromHomeKey));
-    }
-
-    public void toggleRecents(int growTarget) {
-        if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
-            return;
-        }
-
-        // Skip this toggle if we are already waiting to trigger recents via alt-tab
-        if (mFastAltTabTrigger.isDozing()) {
-            return;
-        }
-
-        if (mWaitingForTransitionStart) {
-            mToggleFollowingTransitionStart = true;
-            return;
-        }
-
-        mDraggingInRecents = false;
-        mLaunchedWhileDocking = false;
-        mTriggeredFromAltTab = false;
-
-        try {
-            MutableBoolean isHomeStackVisible = new MutableBoolean(true);
-            long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
-
-            SystemServicesProxy ssp = Recents.getSystemServices();
-            if (ssp.isRecentsActivityVisible(isHomeStackVisible)) {
-                RecentsConfiguration config = Recents.getConfiguration();
-                RecentsActivityLaunchState launchState = config.getLaunchState();
-                if (!launchState.launchedWithAltTab) {
-                    if (Recents.getConfiguration().isGridEnabled) {
-                        // Has the user tapped quickly?
-                        boolean isQuickTap = elapsedTime < ViewConfiguration.getDoubleTapTimeout();
-                        if (isQuickTap) {
-                            EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
-                        } else {
-                            EventBus.getDefault().post(new LaunchMostRecentTaskRequestEvent());
-                        }
-                    } else {
-                        // Launch the next focused task
-                        EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
-                    }
-                } else {
-                    // If the user has toggled it too quickly, then just eat up the event here (it's
-                    // better than showing a janky screenshot).
-                    // NOTE: Ideally, the screenshot mechanism would take the window transform into
-                    // account
-                    if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
-                        return;
-                    }
-
-                    EventBus.getDefault().post(new ToggleRecentsEvent());
-                    mLastToggleTime = SystemClock.elapsedRealtime();
-                }
-                return;
-            } else {
-                // If the user has toggled it too quickly, then just eat up the event here (it's
-                // better than showing a janky screenshot).
-                // NOTE: Ideally, the screenshot mechanism would take the window transform into
-                // account
-                if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
-                    return;
-                }
-
-                // Otherwise, start the recents activity
-                ActivityManager.RunningTaskInfo runningTask =
-                        ActivityManagerWrapper.getInstance().getRunningTask();
-                startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
-                        isHomeStackVisible.value, true /* animate */, growTarget);
-
-                // Only close the other system windows if we are actually showing recents
-                ActivityManagerWrapper.getInstance().closeSystemWindows(
-                        SYSTEM_DIALOG_REASON_RECENT_APPS);
-                mLastToggleTime = SystemClock.elapsedRealtime();
-            }
-        } catch (ActivityNotFoundException e) {
-            Log.e(TAG, "Failed to launch RecentsActivity", e);
-        }
-    }
-
-    public void preloadRecents() {
-        if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
-            return;
-        }
-
-        // Skip preloading recents when keyguard is showing
-        final StatusBar statusBar = getStatusBar();
-        if (statusBar != null && statusBar.isKeyguardShowing()) {
-            return;
-        }
-
-        // Preload only the raw task list into a new load plan (which will be consumed by the
-        // RecentsActivity) only if there is a task to animate to.  Post this to ensure that we
-        // don't block the touch feedback on the nav bar button which triggers this.
-        mHandler.post(() -> {
-            SystemServicesProxy ssp = Recents.getSystemServices();
-            if (!ssp.isRecentsActivityVisible(null)) {
-                ActivityManager.RunningTaskInfo runningTask =
-                        ActivityManagerWrapper.getInstance().getRunningTask();
-                if (runningTask == null) {
-                    return;
-                }
-
-                RecentsTaskLoader loader = Recents.getTaskLoader();
-                sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);
-                loader.preloadTasks(sInstanceLoadPlan, runningTask.id);
-                TaskStack stack = sInstanceLoadPlan.getTaskStack();
-                if (stack.getTaskCount() > 0) {
-                    // Only preload the icon (but not the thumbnail since it may not have been taken
-                    // for the pausing activity)
-                    preloadIcon(runningTask.id);
-
-                    // At this point, we don't know anything about the stack state.  So only
-                    // calculate the dimensions of the thumbnail that we need for the transition
-                    // into Recents, but do not draw it until we construct the activity options when
-                    // we start Recents
-                    updateHeaderBarLayout(stack, null /* window rect override*/);
-                }
-            }
-        });
-    }
-
-    public void cancelPreloadingRecents() {
-        // Do nothing
-    }
-
-    public void onDraggingInRecents(float distanceFromTop) {
-        EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEvent(distanceFromTop));
-    }
-
-    public void onDraggingInRecentsEnded(float velocity) {
-        EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEndedEvent(velocity));
-    }
-
-    public void onShowCurrentUserToast(int msgResId, int msgLength) {
-        Toast.makeText(mContext, msgResId, msgLength).show();
-    }
-
-    /**
-     * Transitions to the next recent task in the stack.
-     */
-    public void showNextTask() {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-        RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
-        loader.preloadTasks(plan, -1);
-        TaskStack focusedStack = plan.getTaskStack();
-
-        // Return early if there are no tasks in the focused stack
-        if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
-
-        // Return early if there is no running task
-        ActivityManager.RunningTaskInfo runningTask =
-                ActivityManagerWrapper.getInstance().getRunningTask();
-        if (runningTask == null) return;
-
-        // Find the task in the recents list
-        boolean isRunningTaskInHomeStack =
-                runningTask.configuration.windowConfiguration.getActivityType()
-                        == ACTIVITY_TYPE_HOME;
-        ArrayList<Task> tasks = focusedStack.getTasks();
-        Task toTask = null;
-        ActivityOptions launchOpts = null;
-        int taskCount = tasks.size();
-        for (int i = taskCount - 1; i >= 1; i--) {
-            Task task = tasks.get(i);
-            if (isRunningTaskInHomeStack) {
-                toTask = tasks.get(i - 1);
-                launchOpts = ActivityOptions.makeCustomAnimation(mContext,
-                        R.anim.recents_launch_next_affiliated_task_target,
-                        R.anim.recents_fast_toggle_app_home_exit);
-                break;
-            } else if (task.key.id == runningTask.id) {
-                toTask = tasks.get(i - 1);
-                launchOpts = ActivityOptions.makeCustomAnimation(mContext,
-                        R.anim.recents_launch_prev_affiliated_task_target,
-                        R.anim.recents_launch_prev_affiliated_task_source);
-                break;
-            }
-        }
-
-        // Return early if there is no next task
-        if (toTask == null) {
-            ssp.startInPlaceAnimationOnFrontMostApplication(
-                    ActivityOptions.makeCustomInPlaceAnimation(mContext,
-                            R.anim.recents_launch_prev_affiliated_task_bounce));
-            return;
-        }
-
-        // Launch the task
-        ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(toTask.key, launchOpts,
-                null /* resultCallback */, null /* resultCallbackHandler */);
-    }
-
-    /**
-     * Transitions to the next affiliated task.
-     */
-    public void showRelativeAffiliatedTask(boolean showNextTask) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-        RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
-        loader.preloadTasks(plan, -1);
-        TaskStack focusedStack = plan.getTaskStack();
-
-        // Return early if there are no tasks in the focused stack
-        if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
-
-        // Return early if there is no running task (can't determine affiliated tasks in this case)
-        ActivityManager.RunningTaskInfo runningTask =
-                ActivityManagerWrapper.getInstance().getRunningTask();
-        final int activityType = runningTask.configuration.windowConfiguration.getActivityType();
-        if (runningTask == null) return;
-        // Return early if the running task is in the home/recents stack (optimization)
-        if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) return;
-
-        // Find the task in the recents list
-        ArrayList<Task> tasks = focusedStack.getTasks();
-        Task toTask = null;
-        ActivityOptions launchOpts = null;
-        int taskCount = tasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            Task task = tasks.get(i);
-            if (task.key.id == runningTask.id) {
-                if (showNextTask) {
-                    if ((i + 1) < taskCount) {
-                        toTask = tasks.get(i + 1);
-                        launchOpts = ActivityOptions.makeCustomAnimation(mContext,
-                                R.anim.recents_launch_next_affiliated_task_target,
-                                R.anim.recents_launch_next_affiliated_task_source);
-                    }
-                } else {
-                    if ((i - 1) >= 0) {
-                        toTask = tasks.get(i - 1);
-                        launchOpts = ActivityOptions.makeCustomAnimation(mContext,
-                                R.anim.recents_launch_prev_affiliated_task_target,
-                                R.anim.recents_launch_prev_affiliated_task_source);
-                    }
-                }
-                break;
-            }
-        }
-
-        // Return early if there is no next task
-        if (toTask == null) {
-            if (showNextTask) {
-                ssp.startInPlaceAnimationOnFrontMostApplication(
-                        ActivityOptions.makeCustomInPlaceAnimation(mContext,
-                                R.anim.recents_launch_next_affiliated_task_bounce));
-            } else {
-                ssp.startInPlaceAnimationOnFrontMostApplication(
-                        ActivityOptions.makeCustomInPlaceAnimation(mContext,
-                                R.anim.recents_launch_prev_affiliated_task_bounce));
-            }
-            return;
-        }
-
-        // Keep track of actually launched affiliated tasks
-        MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
-
-        // Launch the task
-        ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(toTask.key, launchOpts,
-                null /* resultListener */, null /* resultCallbackHandler */);
-    }
-
-    public void showNextAffiliatedTask() {
-        // Keep track of when the affiliated task is triggered
-        MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
-        showRelativeAffiliatedTask(true);
-    }
-
-    public void showPrevAffiliatedTask() {
-        // Keep track of when the affiliated task is triggered
-        MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
-        showRelativeAffiliatedTask(false);
-    }
-
-    public void splitPrimaryTask(int taskId, int stackCreateMode, Rect initialBounds) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-
-        // Make sure we inform DividerView before we actually start the activity so we can change
-        // the resize mode already.
-        if (ssp.setTaskWindowingModeSplitScreenPrimary(taskId, stackCreateMode, initialBounds)) {
-            EventBus.getDefault().send(new DockedTopTaskEvent(initialBounds));
-        }
-    }
-
-    public void setWaitingForTransitionStart(boolean waitingForTransitionStart) {
-        if (mWaitingForTransitionStart == waitingForTransitionStart) {
-            return;
-        }
-
-        mWaitingForTransitionStart = waitingForTransitionStart;
-        if (!waitingForTransitionStart && mToggleFollowingTransitionStart) {
-            mHandler.post(() -> toggleRecents(DividerView.INVALID_RECENTS_GROW_TARGET));
-        }
-        mToggleFollowingTransitionStart = false;
-    }
-
-    /**
-     * Returns the preloaded load plan and invalidates it.
-     */
-    public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
-        RecentsTaskLoadPlan plan = sInstanceLoadPlan;
-        sInstanceLoadPlan = null;
-        return plan;
-    }
-
-    /**
-     * @return the time at which a task last entered picture-in-picture.
-     */
-    public static long getLastPipTime() {
-        return sLastPipTime;
-    }
-
-    /**
-     * Clears the time at which a task last entered picture-in-picture.
-     */
-    public static void clearLastPipTime() {
-        sLastPipTime = -1;
-    }
-
-    /**
-     * Reloads all the resources for the current configuration.
-     */
-    private void reloadResources() {
-        Resources res = mContext.getResources();
-
-        mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(mContext,
-                R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height_tablet_land,
-                R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height_tablet_land,
-                R.dimen.recents_grid_task_view_header_height);
-
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
-                null, false);
-        mHeaderBar.setLayoutDirection(res.getConfiguration().getLayoutDirection());
-    }
-
-    private void updateDummyStackViewLayout(TaskStackLayoutAlgorithm stackLayout,
-            TaskStack stack, Rect windowRect) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        Rect displayRect = ssp.getDisplayRect();
-        Rect systemInsets = new Rect();
-        ssp.getStableInsets(systemInsets);
-
-        // When docked, the nav bar insets are consumed and the activity is measured without insets.
-        // However, the window bounds include the insets, so we need to subtract them here to make
-        // them identical.
-        if (ssp.hasDockedTask()) {
-            if (systemInsets.bottom < windowRect.height()) {
-                // Only apply inset if it isn't going to cause the rect height to go negative.
-                windowRect.bottom -= systemInsets.bottom;
-            }
-            systemInsets.bottom = 0;
-        }
-        calculateWindowStableInsets(systemInsets, windowRect, displayRect);
-        windowRect.offsetTo(0, 0);
-
-        // Rebind the header bar and draw it for the transition
-        stackLayout.setSystemInsets(systemInsets);
-        if (stack != null) {
-            stackLayout.getTaskStackBounds(displayRect, windowRect, systemInsets.top,
-                    systemInsets.left, systemInsets.right, mTmpBounds);
-            stackLayout.reset();
-            stackLayout.initialize(displayRect, windowRect, mTmpBounds);
-        }
-    }
-
-    private Rect getWindowRect(Rect windowRectOverride) {
-       return windowRectOverride != null
-                ? new Rect(windowRectOverride)
-                : Recents.getSystemServices().getWindowRect();
-    }
-
-    /**
-     * Prepares the header bar layout for the next transition, if the task view bounds has changed
-     * since the last call, it will attempt to re-measure and layout the header bar to the new size.
-     *
-     * @param stack the stack to initialize the stack layout with
-     * @param windowRectOverride the rectangle to use when calculating the stack state which can
-     *                           be different from the current window rect if recents is resizing
-     *                           while being launched
-     */
-    private void updateHeaderBarLayout(TaskStack stack, Rect windowRectOverride) {
-        Rect windowRect = getWindowRect(windowRectOverride);
-        int taskViewWidth = 0;
-        boolean useGridLayout = mDummyStackView.useGridLayout();
-        updateDummyStackViewLayout(mDummyStackView.getStackAlgorithm(), stack, windowRect);
-        if (stack != null) {
-            TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
-            mDummyStackView.getStack().removeAllTasks(false /* notifyStackChanges */);
-            mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
-            // Get the width of a task view so that we know how wide to draw the header bar.
-            if (useGridLayout) {
-                TaskGridLayoutAlgorithm gridLayout = mDummyStackView.getGridAlgorithm();
-                gridLayout.initialize(windowRect);
-                taskViewWidth = (int) gridLayout.getTransform(0 /* taskIndex */,
-                        stack.getTaskCount(), new TaskViewTransform(),
-                        stackLayout).rect.width();
-            } else {
-                Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
-                if (!taskViewBounds.isEmpty()) {
-                    taskViewWidth = taskViewBounds.width();
-                }
-            }
-        }
-
-        if (stack != null && taskViewWidth > 0) {
-            synchronized (mHeaderBarLock) {
-                if (mHeaderBar.getMeasuredWidth() != taskViewWidth ||
-                        mHeaderBar.getMeasuredHeight() != mTaskBarHeight) {
-                    if (useGridLayout) {
-                        mHeaderBar.setShouldDarkenBackgroundColor(true);
-                        mHeaderBar.setNoUserInteractionState();
-                    }
-                    mHeaderBar.forceLayout();
-                    mHeaderBar.measure(
-                            MeasureSpec.makeMeasureSpec(taskViewWidth, MeasureSpec.EXACTLY),
-                            MeasureSpec.makeMeasureSpec(mTaskBarHeight, MeasureSpec.EXACTLY));
-                }
-                mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
-            }
-        }
-    }
-
-    /**
-     * Given the stable insets and the rect for our window, calculates the insets that affect our
-     * window.
-     */
-    private void calculateWindowStableInsets(Rect inOutInsets, Rect windowRect, Rect displayRect) {
-
-        // Display rect without insets - available app space
-        Rect appRect = new Rect(displayRect);
-        appRect.inset(inOutInsets);
-
-        // Our window intersected with available app space
-        Rect windowRectWithInsets = new Rect(windowRect);
-        windowRectWithInsets.intersect(appRect);
-        inOutInsets.left = windowRectWithInsets.left - windowRect.left;
-        inOutInsets.top = windowRectWithInsets.top - windowRect.top;
-        inOutInsets.right = windowRect.right - windowRectWithInsets.right;
-        inOutInsets.bottom = windowRect.bottom - windowRectWithInsets.bottom;
-    }
-
-    /**
-     * Preloads the icon of a task.
-     */
-    private void preloadIcon(int runningTaskId) {
-        // Ensure that we load the running task's icon
-        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
-        launchOpts.runningTaskId = runningTaskId;
-        launchOpts.loadThumbnails = false;
-        launchOpts.onlyLoadForCache = true;
-        Recents.getTaskLoader().loadTasks(sInstanceLoadPlan, launchOpts);
-    }
-
-    /**
-     * Creates the activity options for a unknown state->recents transition.
-     */
-    protected ActivityOptions getUnknownTransitionActivityOptions() {
-        return ActivityOptions.makeCustomAnimation(mContext,
-                R.anim.recents_from_unknown_enter,
-                R.anim.recents_from_unknown_exit,
-                mHandler, null);
-    }
-
-    /**
-     * Creates the activity options for a home->recents transition.
-     */
-    protected ActivityOptions getHomeTransitionActivityOptions() {
-        return ActivityOptions.makeCustomAnimation(mContext,
-                R.anim.recents_from_launcher_enter,
-                R.anim.recents_from_launcher_exit,
-                mHandler, null);
-    }
-
-    /**
-     * Creates the activity options for an app->recents transition.
-     */
-    private Pair<ActivityOptions, AppTransitionAnimationSpecsFuture>
-            getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo runningTask,
-                    Rect windowOverrideRect) {
-        final boolean isLowRamDevice = Recents.getConfiguration().isLowRamDevice;
-
-        // Update the destination rect
-        Task toTask = new Task();
-        TaskViewTransform toTransform = getThumbnailTransitionTransform(mDummyStackView, toTask,
-                windowOverrideRect);
-
-        RectF toTaskRect = toTransform.rect;
-        AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(mHandler) {
-            @Override
-            public List<AppTransitionAnimationSpecCompat> composeSpecs() {
-                Rect rect = new Rect();
-                toTaskRect.round(rect);
-                Bitmap thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
-                return Lists.newArrayList(new AppTransitionAnimationSpecCompat(toTask.key.id,
-                        thumbnail, rect));
-            }
-        };
-
-        // For low end ram devices, wait for transition flag is reset when Recents entrance
-        // animation is complete instead of when the transition animation starts
-        return new Pair<>(RecentsTransition.createAspectScaleAnimation(mContext, mHandler,
-                false /* scaleUp */, future, isLowRamDevice ? null : mResetToggleFlagListener),
-                future);
-    }
-
-    /**
-     * Returns the transition rect for the given task id.
-     */
-    private TaskViewTransform getThumbnailTransitionTransform(TaskStackView stackView,
-            Task runningTaskOut, Rect windowOverrideRect) {
-        // Find the running task in the TaskStack
-        TaskStack stack = stackView.getStack();
-        Task launchTask = stack.getLaunchTarget();
-        if (launchTask != null) {
-            runningTaskOut.copyFrom(launchTask);
-        } else {
-            // If no task is specified or we can not find the task just use the front most one
-            launchTask = stack.getFrontMostTask();
-            runningTaskOut.copyFrom(launchTask);
-        }
-
-        // Get the transform for the running task
-        stackView.updateLayoutAlgorithm(true /* boundScroll */);
-        stackView.updateToInitialState();
-        stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
-                stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect);
-        return mTmpTransform;
-    }
-
-    /**
-     * Draws the header of a task used for the window animation into a bitmap.
-     */
-    private Bitmap drawThumbnailTransitionBitmap(Task toTask,
-            TaskViewTransform toTransform) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        int width = (int) toTransform.rect.width();
-        int height = (int) toTransform.rect.height();
-        if (toTransform != null && toTask.key != null && width > 0 && height > 0) {
-            synchronized (mHeaderBarLock) {
-                boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
-                mHeaderBar.onTaskViewSizeChanged(width, height);
-                if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
-                    return RecentsTransition.drawViewIntoHardwareBitmap(width, mTaskBarHeight,
-                            null, 1f, 0xFFff0000);
-                } else {
-                    // Workaround for b/27815919, reset the callback so that we do not trigger an
-                    // invalidate on the header bar as a result of updating the icon
-                    Drawable icon = mHeaderBar.getIconView().getDrawable();
-                    if (icon != null) {
-                        icon.setCallback(null);
-                    }
-                    mHeaderBar.bindToTask(toTask, false /* touchExplorationEnabled */,
-                            disabledInSafeMode);
-                    mHeaderBar.onTaskDataLoaded();
-                    mHeaderBar.setDimAlpha(toTransform.dimAlpha);
-                    return RecentsTransition.drawViewIntoHardwareBitmap(width, mTaskBarHeight,
-                            mHeaderBar, 1f, 0);
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Shows the recents activity after dismissing the keyguard if visible
-     */
-    protected void startRecentsActivityAndDismissKeyguardIfNeeded(
-            final ActivityManager.RunningTaskInfo runningTask, final boolean isHomeStackVisible,
-            final boolean animate, final int growTarget) {
-        // Preload only if device for current user is unlocked
-        final StatusBar statusBar = getStatusBar();
-        if (statusBar != null && statusBar.isKeyguardShowing()) {
-            statusBar.executeRunnableDismissingKeyguard(() -> {
-                    // Flush trustmanager before checking device locked per user when preloading
-                    mTrustManager.reportKeyguardShowingChanged();
-                    mHandler.post(() -> startRecentsActivity(runningTask, isHomeStackVisible,
-                            animate, growTarget));
-                }, null,  true /* dismissShade */, false /* afterKeyguardGone */,
-                true /* deferred */);
-        } else {
-            startRecentsActivity(runningTask, isHomeStackVisible, animate, growTarget);
-        }
-    }
-
-    private void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
-            boolean isHomeStackVisible, boolean animate, int growTarget) {
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-
-        int runningTaskId = !mLaunchedWhileDocking && (runningTask != null)
-                ? runningTask.id
-                : -1;
-
-        // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
-        // should always preload the tasks now. If we are dragging in recents, reload them as
-        // the stacks might have changed.
-        if (mLaunchedWhileDocking || mTriggeredFromAltTab || sInstanceLoadPlan == null) {
-            // Create a new load plan if preloadRecents() was never triggered
-            sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);
-        }
-        if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
-            loader.preloadTasks(sInstanceLoadPlan, runningTaskId);
-        }
-
-        TaskStack stack = sInstanceLoadPlan.getTaskStack();
-        boolean hasRecentTasks = stack.getTaskCount() > 0;
-        boolean useThumbnailTransition = (runningTask != null) && !isHomeStackVisible &&
-                hasRecentTasks;
-
-        // Update the launch state that we need in updateHeaderBarLayout()
-        launchState.launchedFromHome = !useThumbnailTransition && !mLaunchedWhileDocking;
-        launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
-        launchState.launchedFromPipApp = false;
-        launchState.launchedWithNextPipApp =
-                stack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime());
-        launchState.launchedViaDockGesture = mLaunchedWhileDocking;
-        launchState.launchedViaDragGesture = mDraggingInRecents;
-        launchState.launchedToTaskId = runningTaskId;
-        launchState.launchedWithAltTab = mTriggeredFromAltTab;
-
-        // Disable toggling of recents between starting the activity and it is visible and the app
-        // has started its transition into recents.
-        setWaitingForTransitionStart(useThumbnailTransition);
-
-        // Preload the icon (this will be a null-op if we have preloaded the icon already in
-        // preloadRecents())
-        preloadIcon(runningTaskId);
-
-        // Update the header bar if necessary
-        Rect windowOverrideRect = getWindowRectOverride(growTarget);
-        updateHeaderBarLayout(stack, windowOverrideRect);
-
-        // Prepare the dummy stack for the transition
-        TaskStackLayoutAlgorithm.VisibilityReport stackVr =
-                mDummyStackView.computeStackVisibilityReport();
-
-        // Update the remaining launch state
-        launchState.launchedNumVisibleTasks = stackVr.numVisibleTasks;
-        launchState.launchedNumVisibleThumbnails = stackVr.numVisibleThumbnails;
-
-        if (!animate) {
-            startRecentsActivity(ActivityOptions.makeCustomAnimation(mContext, -1, -1),
-                    null /* future */);
-            return;
-        }
-
-        Pair<ActivityOptions, AppTransitionAnimationSpecsFuture> pair;
-        if (useThumbnailTransition) {
-            // Try starting with a thumbnail transition
-            pair = getThumbnailTransitionActivityOptions(runningTask, windowOverrideRect);
-        } else {
-            // If there is no thumbnail transition, but is launching from home into recents, then
-            // use a quick home transition
-            pair = new Pair<>(hasRecentTasks
-                    ? getHomeTransitionActivityOptions()
-                    : getUnknownTransitionActivityOptions(), null);
-        }
-        startRecentsActivity(pair.first, pair.second);
-        mLastToggleTime = SystemClock.elapsedRealtime();
-    }
-
-    private Rect getWindowRectOverride(int growTarget) {
-        if (growTarget == DividerView.INVALID_RECENTS_GROW_TARGET) {
-            return SystemServicesProxy.getInstance(mContext).getWindowRect();
-        }
-        Rect result = new Rect();
-        Rect displayRect = Recents.getSystemServices().getDisplayRect();
-        DockedDividerUtils.calculateBoundsForPosition(growTarget, WindowManager.DOCKED_BOTTOM,
-                result, displayRect.width(), displayRect.height(),
-                Recents.getSystemServices().getDockedDividerSize(mContext));
-        return result;
-    }
-
-    private StatusBar getStatusBar() {
-        return SysUiServiceProvider.getComponent(mContext, StatusBar.class);
-    }
-
-    /**
-     * Starts the recents activity.
-     */
-    private void startRecentsActivity(ActivityOptions opts,
-            final AppTransitionAnimationSpecsFuture future) {
-        Intent intent = new Intent();
-        intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
-        HidePipMenuEvent hideMenuEvent = new HidePipMenuEvent();
-        hideMenuEvent.addPostAnimationCallback(() -> {
-            Recents.getSystemServices().startActivityAsUserAsync(intent, opts);
-            EventBus.getDefault().send(new RecentsActivityStartingEvent());
-            if (future != null) {
-                future.composeSpecsSynchronous();
-            }
-        });
-        EventBus.getDefault().send(hideMenuEvent);
-
-        // Once we have launched the activity, reset the dummy stack view tasks so we don't hold
-        // onto references to the same tasks consumed by the activity
-        mDummyStackView.setTasks(mEmptyTaskStack, false /* notifyStackChanges */);
-    }
-
-    /**** OnAnimationFinishedListener Implementation ****/
-
-    @Override
-    public void onAnimationFinished() {
-        EventBus.getDefault().post(new EnterRecentsWindowLastAnimationFrameEvent());
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
new file mode 100644
index 0000000..8a04c11
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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.systemui.recents;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import com.android.systemui.SysUiServiceProvider;
+import java.io.PrintWriter;
+
+interface RecentsImplementation {
+    default void onStart(Context context, SysUiServiceProvider sysUiServiceProvider) {}
+    default void onBootCompleted() {}
+    default void onAppTransitionFinished() {}
+    default void onConfigurationChanged(Configuration newConfig) {}
+
+    default void preloadRecentApps() {}
+    default void cancelPreloadRecentApps() {}
+    default void showRecentApps(boolean triggeredFromAltTab) {}
+    default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {}
+    default void toggleRecentApps() {}
+    default void growRecents() {}
+    default boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
+            int metricsDockAction) {
+        return false;
+    }
+
+    default void dump(PrintWriter pw) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index db2b69f..af0ebdc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -59,13 +59,11 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
-import com.android.systemui.OverviewProxyService;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-
+import com.android.systemui.shared.system.TaskStackChangeListener;
 import java.io.PrintWriter;
 import java.util.Collections;
 import java.util.HashSet;
@@ -119,7 +117,7 @@
     private int mNumAppsLaunchedSinceSwipeUpTipDismiss;
     private int mOverviewOpenedCountSinceQuickScrubTipDismiss;
 
-    private final SysUiTaskStackChangeListener mTaskListener = new SysUiTaskStackChangeListener() {
+    private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() {
         private String mLastPackageName;
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
deleted file mode 100644
index b4212d3..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2010 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.systemui.recents;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.util.Log;
-
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.SystemUIApplication;
-
-/**
- * A strictly system-user service that is started by the secondary user's Recents (with a limited
- * lifespan), to get the interface that the secondary user's Recents can call through to the system
- * user's Recents.
- */
-public class RecentsSystemUserService extends Service {
-
-    private static final String TAG = "RecentsSystemUserService";
-    private static final boolean DEBUG = false;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        Recents recents = SysUiServiceProvider.getComponent(this, Recents.class);
-        if (DEBUG) {
-            Log.d(TAG, "onBind: " + recents);
-        }
-        if (recents != null) {
-            return recents.getSystemUserCallbacks();
-        }
-        return null;
-    }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index b7eee36..f92c50a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -29,7 +29,6 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
-import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -42,9 +41,9 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.statusbar.phone.NavigationBarView;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.leak.RotationUtils;
@@ -220,8 +219,7 @@
             mLayout.findViewById(R.id.screen_pinning_text_area)
                     .setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
             View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
-            if (Recents.getSystemServices() != null &&
-                    Recents.getSystemServices().hasSoftNavigationBar()) {
+            if (WindowManagerWrapper.getInstance().hasSoftNavigationBar()) {
                 buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
                 swapChildrenIfRtlAndVertical(buttons);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
deleted file mode 100644
index e4972b1..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 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.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.TaskStack;
-
-/**
- * This is sent by the activity whenever the multi-window state has changed.
- */
-public class MultiWindowStateChangedEvent extends EventBus.AnimatedEvent {
-
-    public final boolean inMultiWindow;
-    // This flag is only used when undocking a task
-    public final boolean showDeferredAnimation;
-    public final TaskStack stack;
-
-    public MultiWindowStateChangedEvent(boolean inMultiWindow, boolean showDeferredAnimation,
-            TaskStack stack) {
-        this.inMultiWindow = inMultiWindow;
-        this.showDeferredAnimation = showDeferredAnimation;
-        this.stack = stack;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
deleted file mode 100644
index 51d02b5..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 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.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.TaskStack;
-
-/**
- * This is sent by the activity whenever the task stach has changed.
- */
-public class TaskStackUpdatedEvent extends EventBus.AnimatedEvent {
-
-    /**
-     * A new TaskStack instance representing the latest stack state.
-     */
-    public final TaskStack stack;
-    public final boolean inMultiWindow;
-
-    public TaskStackUpdatedEvent(TaskStack stack, boolean inMultiWindow) {
-        this.stack = stack;
-        this.inMultiWindow = inMultiWindow;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java
deleted file mode 100644
index 881a64a..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 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.systemui.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.recents.views.TaskView;
-
-/**
- * This event is sent when a {@link TaskView} has been dismissed and is no longer visible.
- */
-public class TaskViewDismissedEvent extends EventBus.Event {
-
-    public final Task task;
-    public final TaskView taskView;
-    public final AnimationProps animation;
-
-    public TaskViewDismissedEvent(Task task, TaskView taskView, AnimationProps animation) {
-        this.task = task;
-        this.taskView = taskView;
-        this.animation = animation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java
deleted file mode 100644
index 297afc5..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2016 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.systemui.recents.events.ui.dragndrop;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskView;
-
-/**
- * This event is sent whenever a drag end is cancelled because of an error.
- */
-public class DragEndCancelledEvent extends EventBus.AnimatedEvent {
-
-    public final TaskStack stack;
-    public final Task task;
-    public final TaskView taskView;
-
-    public DragEndCancelledEvent(TaskStack stack, Task task, TaskView taskView) {
-        this.stack = stack;
-        this.task = task;
-        this.taskView = taskView;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
deleted file mode 100644
index 350fe78..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.misc;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.StackInfo;
-import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.app.AppGlobals;
-import android.app.IActivityManager;
-import android.app.IActivityTaskManager;
-import android.app.WindowConfiguration;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.service.dreams.DreamService;
-import android.service.dreams.IDreamManager;
-import android.util.Log;
-import android.util.MutableBoolean;
-import android.view.Display;
-import android.view.IDockedStackListener;
-import android.view.IWindowManager;
-import android.view.WindowManager;
-import android.view.WindowManager.KeyboardShortcutsReceiver;
-import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.internal.app.AssistUtils;
-import com.android.internal.os.BackgroundThread;
-import com.android.systemui.Dependency;
-import com.android.systemui.UiOffloadThread;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsImpl;
-import com.android.systemui.statusbar.policy.UserInfoController;
-
-import java.util.List;
-
-/**
- * Acts as a shim around the real system services that we need to access data from, and provides
- * a point of injection when testing UI.
- */
-public class SystemServicesProxy {
-    final static String TAG = "SystemServicesProxy";
-
-    final static BitmapFactory.Options sBitmapOptions;
-    static {
-        sBitmapOptions = new BitmapFactory.Options();
-        sBitmapOptions.inMutable = true;
-        sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
-    }
-
-    private static SystemServicesProxy sSystemServicesProxy;
-
-    AccessibilityManager mAccm;
-    ActivityManager mAm;
-    IActivityManager mIam;
-    IActivityTaskManager mIatm;
-    PackageManager mPm;
-    IPackageManager mIpm;
-    private final IDreamManager mDreamManager;
-    private final Context mContext;
-    AssistUtils mAssistUtils;
-    WindowManager mWm;
-    IWindowManager mIwm;
-    UserManager mUm;
-    Display mDisplay;
-    String mRecentsPackage;
-    private int mCurrentUserId;
-
-    boolean mIsSafeMode;
-
-    int mDummyThumbnailWidth;
-    int mDummyThumbnailHeight;
-    Paint mBgProtectionPaint;
-    Canvas mBgProtectionCanvas;
-
-    private final Runnable mGcRunnable = new Runnable() {
-        @Override
-        public void run() {
-            System.gc();
-            System.runFinalization();
-        }
-    };
-
-    private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
-
-    private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
-            (String name, Drawable picture, String userAccount) ->
-                    mCurrentUserId = mAm.getCurrentUser();
-
-    /** Private constructor */
-    private SystemServicesProxy(Context context) {
-        mContext = context.getApplicationContext();
-        mAccm = AccessibilityManager.getInstance(context);
-        mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
-        mIam = ActivityManager.getService();
-        mIatm = ActivityTaskManager.getService();
-        mPm = context.getPackageManager();
-        mIpm = AppGlobals.getPackageManager();
-        mAssistUtils = new AssistUtils(context);
-        mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        mIwm = WindowManagerGlobal.getWindowManagerService();
-        mUm = UserManager.get(context);
-        mDreamManager = IDreamManager.Stub.asInterface(
-                ServiceManager.checkService(DreamService.DREAM_SERVICE));
-        mDisplay = mWm.getDefaultDisplay();
-        mRecentsPackage = context.getPackageName();
-        mIsSafeMode = mPm.isSafeMode();
-        mCurrentUserId = mAm.getCurrentUser();
-
-        // Get the dummy thumbnail width/heights
-        Resources res = context.getResources();
-        int wId = com.android.internal.R.dimen.thumbnail_width;
-        int hId = com.android.internal.R.dimen.thumbnail_height;
-        mDummyThumbnailWidth = res.getDimensionPixelSize(wId);
-        mDummyThumbnailHeight = res.getDimensionPixelSize(hId);
-
-        // Create the protection paints
-        mBgProtectionPaint = new Paint();
-        mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
-        mBgProtectionPaint.setColor(0xFFffffff);
-        mBgProtectionCanvas = new Canvas();
-
-        // Since SystemServicesProxy can be accessed from a per-SysUI process component, create a
-        // per-process listener to keep track of the current user id to reduce the number of binder
-        // calls to fetch it.
-        UserInfoController userInfoController = Dependency.get(UserInfoController.class);
-        userInfoController.addCallback(mOnUserInfoChangedListener);
-    }
-
-    /**
-     * Returns the single instance of the {@link SystemServicesProxy}.
-     * This should only be called on the main thread.
-     */
-    public static synchronized SystemServicesProxy getInstance(Context context) {
-        if (sSystemServicesProxy == null) {
-            sSystemServicesProxy = new SystemServicesProxy(context);
-        }
-        return sSystemServicesProxy;
-    }
-
-    /**
-     * Requests a gc() from the background thread.
-     */
-    public void gc() {
-        BackgroundThread.getHandler().post(mGcRunnable);
-    }
-
-    /**
-     * Returns whether the recents activity is currently visible.
-     */
-    public boolean isRecentsActivityVisible() {
-        return isRecentsActivityVisible(null);
-    }
-
-    /**
-     * Returns whether the recents activity is currently visible.
-     *
-     * @param isHomeStackVisible if provided, will return whether the home stack is visible
-     *                           regardless of the recents visibility
-     *
-     * TODO(winsonc): Refactor this check to just use the recents activity lifecycle
-     */
-    public boolean isRecentsActivityVisible(MutableBoolean isHomeStackVisible) {
-        if (mIam == null) return false;
-
-        try {
-            List<StackInfo> stackInfos = mIatm.getAllStackInfos();
-            ActivityManager.StackInfo homeStackInfo = null;
-            ActivityManager.StackInfo fullscreenStackInfo = null;
-            ActivityManager.StackInfo recentsStackInfo = null;
-            for (int i = 0; i < stackInfos.size(); i++) {
-                final StackInfo stackInfo = stackInfos.get(i);
-                final WindowConfiguration winConfig = stackInfo.configuration.windowConfiguration;
-                final int activityType = winConfig.getActivityType();
-                final int windowingMode = winConfig.getWindowingMode();
-                if (homeStackInfo == null && activityType == ACTIVITY_TYPE_HOME) {
-                    homeStackInfo = stackInfo;
-                } else if (fullscreenStackInfo == null && activityType == ACTIVITY_TYPE_STANDARD
-                        && (windowingMode == WINDOWING_MODE_FULLSCREEN
-                            || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
-                    fullscreenStackInfo = stackInfo;
-                } else if (recentsStackInfo == null && activityType == ACTIVITY_TYPE_RECENTS) {
-                    recentsStackInfo = stackInfo;
-                }
-            }
-            boolean homeStackVisibleNotOccluded = isStackNotOccluded(homeStackInfo,
-                    fullscreenStackInfo);
-            boolean recentsStackVisibleNotOccluded = isStackNotOccluded(recentsStackInfo,
-                    fullscreenStackInfo);
-            if (isHomeStackVisible != null) {
-                isHomeStackVisible.value = homeStackVisibleNotOccluded;
-            }
-            ComponentName topActivity = recentsStackInfo != null ?
-                    recentsStackInfo.topActivity : null;
-            return (recentsStackVisibleNotOccluded && topActivity != null
-                    && topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE)
-                    && Recents.RECENTS_ACTIVITIES.contains(topActivity.getClassName()));
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        }
-        return false;
-    }
-
-    private boolean isStackNotOccluded(ActivityManager.StackInfo stackInfo,
-            ActivityManager.StackInfo fullscreenStackInfo) {
-        boolean stackVisibleNotOccluded = stackInfo == null || stackInfo.visible;
-        if (fullscreenStackInfo != null && stackInfo != null) {
-            boolean isFullscreenStackOccludingg = fullscreenStackInfo.visible &&
-                    fullscreenStackInfo.position > stackInfo.position;
-            stackVisibleNotOccluded &= !isFullscreenStackOccludingg;
-        }
-        return stackVisibleNotOccluded;
-    }
-
-    /**
-     * Returns whether this device is in the safe mode.
-     */
-    public boolean isInSafeMode() {
-        return mIsSafeMode;
-    }
-
-    /** Moves an already resumed task to the side of the screen to initiate split screen. */
-    public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
-            Rect initialBounds) {
-        if (mIatm == null) {
-            return false;
-        }
-
-        try {
-            return mIatm.setTaskWindowingModeSplitScreenPrimary(taskId, createMode,
-                    true /* onTop */, false /* animate */, initialBounds, true /* showRecents */);
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        }
-        return false;
-    }
-
-    public ActivityManager.StackInfo getSplitScreenPrimaryStack() {
-        try {
-            return mIatm.getStackInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-
-    /**
-     * @return whether there are any docked tasks for the current user.
-     */
-    public boolean hasDockedTask() {
-        if (mIam == null) return false;
-
-        ActivityManager.StackInfo stackInfo = getSplitScreenPrimaryStack();
-        if (stackInfo != null) {
-            int userId = getCurrentUser();
-            boolean hasUserTask = false;
-            for (int i = stackInfo.taskUserIds.length - 1; i >= 0 && !hasUserTask; i--) {
-                hasUserTask = (stackInfo.taskUserIds[i] == userId);
-            }
-            return hasUserTask;
-        }
-        return false;
-    }
-
-    /**
-     * Returns whether there is a soft nav bar.
-     */
-    public boolean hasSoftNavigationBar() {
-        try {
-            return mIwm.hasNavigationBar();
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        }
-        return false;
-    }
-
-    /**
-     * Returns whether the device has a transposed nav bar (on the right of the screen) in the
-     * current display orientation.
-     */
-    public boolean hasTransposedNavigationBar() {
-        Rect insets = new Rect();
-        getStableInsets(insets);
-        return insets.right > 0;
-    }
-
-    /** Set the task's windowing mode. */
-    public void setTaskWindowingMode(int taskId, int windowingMode) {
-        if (mIatm == null) return;
-
-        try {
-            mIatm.setTaskWindowingMode(taskId, windowingMode, false /* onTop */);
-        } catch (RemoteException | IllegalArgumentException e) {
-            e.printStackTrace();
-        }
-    }
-
-    /**
-     * Returns whether the provided {@param userId} represents the system user.
-     */
-    public boolean isSystemUser(int userId) {
-        return userId == UserHandle.USER_SYSTEM;
-    }
-
-    /**
-     * Returns the current user id.  Used instead of KeyguardUpdateMonitor in SystemUI components
-     * that run in the non-primary SystemUI process.
-     */
-    public int getCurrentUser() {
-        return mCurrentUserId;
-    }
-
-    /**
-     * Returns the processes user id.
-     */
-    public int getProcessUser() {
-        if (mUm == null) return 0;
-        return mUm.getUserHandle();
-    }
-
-    /**
-     * Returns whether touch exploration is currently enabled.
-     */
-    public boolean isTouchExplorationEnabled() {
-        if (mAccm == null) return false;
-
-        return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled();
-    }
-
-    /**
-     * Returns whether the current task is in screen-pinning mode.
-     */
-    public boolean isScreenPinningActive() {
-        if (mIam == null) return false;
-
-        try {
-            return mIatm.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the smallest width/height.
-     */
-    public int getDeviceSmallestWidth() {
-        if (mDisplay == null) return 0;
-
-        Point smallestSizeRange = new Point();
-        Point largestSizeRange = new Point();
-        mDisplay.getCurrentSizeRange(smallestSizeRange, largestSizeRange);
-        return smallestSizeRange.x;
-    }
-
-    /**
-     * Returns the current display rect in the current display orientation.
-     */
-    public Rect getDisplayRect() {
-        Rect displayRect = new Rect();
-        if (mDisplay == null) return displayRect;
-
-        Point p = new Point();
-        mDisplay.getRealSize(p);
-        displayRect.set(0, 0, p.x, p.y);
-        return displayRect;
-    }
-
-    /**
-     * Returns the window rect for the RecentsActivity, based on the dimensions of the recents stack
-     */
-    public Rect getWindowRect() {
-        Rect windowRect = new Rect();
-        if (mIam == null) return windowRect;
-
-        try {
-            // Use the recents stack bounds, fallback to fullscreen stack if it is null
-            ActivityManager.StackInfo stackInfo =
-                    mIatm.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
-            if (stackInfo == null) {
-                stackInfo = mIatm.getStackInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-            }
-            if (stackInfo != null) {
-                windowRect.set(stackInfo.bounds);
-            }
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        } finally {
-            return windowRect;
-        }
-    }
-
-    public void startActivityAsUserAsync(Intent intent, ActivityOptions opts) {
-        mUiOffloadThread.submit(() -> mContext.startActivityAsUser(intent,
-                opts != null ? opts.toBundle() : null, UserHandle.CURRENT));
-    }
-
-    /** Starts an in-place animation on the front most application windows. */
-    public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) {
-        if (mIam == null) return;
-
-        try {
-            mIatm.startInPlaceAnimationOnFrontMostApplication(
-                    opts == null ? null : opts.toBundle());
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    public void registerDockedStackListener(IDockedStackListener listener) {
-        if (mWm == null) return;
-
-        try {
-            mIwm.registerDockedStackListener(listener);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    /**
-     * Calculates the size of the dock divider in the current orientation.
-     */
-    public int getDockedDividerSize(Context context) {
-        Resources res = context.getResources();
-        int dividerWindowWidth = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.docked_stack_divider_thickness);
-        int dividerInsets = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.docked_stack_divider_insets);
-        return dividerWindowWidth - 2 * dividerInsets;
-    }
-
-    public void requestKeyboardShortcuts(
-            Context context, KeyboardShortcutsReceiver receiver, int deviceId) {
-        mWm.requestAppKeyboardShortcuts(receiver, deviceId);
-    }
-
-    public void getStableInsets(Rect outStableInsets) {
-        if (mWm == null) return;
-
-        try {
-            mIwm.getStableInsets(Display.DEFAULT_DISPLAY, outStableInsets);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    /**
-     * Updates the visibility of recents.
-     */
-    public void setRecentsVisibility(final boolean visible) {
-        mUiOffloadThread.submit(() -> {
-            try {
-                mIwm.setRecentsVisibility(visible);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Unable to reach window manager", e);
-            }
-        });
-    }
-
-    /**
-     * Updates the visibility of the picture-in-picture.
-     */
-    public void setPipVisibility(final boolean visible) {
-        mUiOffloadThread.submit(() -> {
-            try {
-                mIwm.setPipVisibility(visible);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Unable to reach window manager", e);
-            }
-        });
-    }
-
-    public boolean isDreaming() {
-        try {
-            return mDreamManager.isDreaming();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to query dream manager.", e);
-        }
-        return false;
-    }
-
-    public void awakenDreamsAsync() {
-        mUiOffloadThread.submit(() -> {
-            try {
-                mDreamManager.awaken();
-            } catch (RemoteException e) {
-                e.printStackTrace();
-            }
-        });
-    }
-
-    public interface StartActivityFromRecentsResultListener {
-        void onStartActivityResult(boolean succeeded);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java b/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java
deleted file mode 100644
index a246141..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.recents.views;
-
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.annotation.IntDef;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.ColorDrawable;
-import android.util.IntProperty;
-import android.view.animation.Interpolator;
-
-import com.android.internal.policy.DockedDividerUtils;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.shared.recents.utilities.Utilities;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-
-/**
- * The various possible dock states when dragging and dropping a task.
- */
-public class DockState implements DropTarget {
-
-    public static final int DOCK_AREA_BG_COLOR = 0xFFffffff;
-    public static final int DOCK_AREA_GRID_BG_COLOR = 0xFF000000;
-
-    // The rotation to apply to the hint text
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({HORIZONTAL, VERTICAL})
-    public @interface TextOrientation {}
-    private static final int HORIZONTAL = 0;
-    private static final int VERTICAL = 1;
-
-    private static final int DOCK_AREA_ALPHA = 80;
-    public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, 255, HORIZONTAL,
-            null, null, null);
-    public static final DockState LEFT = new DockState(DOCKED_LEFT,
-            SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL,
-            new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1),
-            new RectF(0, 0, 0.5f, 1));
-    public static final DockState TOP = new DockState(DOCKED_TOP,
-            SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
-            new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f),
-            new RectF(0, 0, 1, 0.5f));
-    public static final DockState RIGHT = new DockState(DOCKED_RIGHT,
-            SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL,
-            new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1),
-            new RectF(0.5f, 0, 1, 1));
-    public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM,
-            SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
-            new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1),
-            new RectF(0, 0.5f, 1, 1));
-
-    @Override
-    public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
-            boolean isCurrentTarget) {
-        if (isCurrentTarget) {
-            getMappedRect(expandedTouchDockArea, width, height, mTmpRect);
-            return mTmpRect.contains(x, y);
-        } else {
-            getMappedRect(touchArea, width, height, mTmpRect);
-            updateBoundsWithSystemInsets(mTmpRect, insets);
-            return mTmpRect.contains(x, y);
-        }
-    }
-
-    // Represents the view state of this dock state
-    public static class ViewState {
-        private static final IntProperty<ViewState> HINT_ALPHA =
-                new IntProperty<ViewState>("drawableAlpha") {
-                    @Override
-                    public void setValue(ViewState object, int alpha) {
-                        object.mHintTextAlpha = alpha;
-                        object.dockAreaOverlay.invalidateSelf();
-                    }
-
-                    @Override
-                    public Integer get(ViewState object) {
-                        return object.mHintTextAlpha;
-                    }
-                };
-
-        public final int dockAreaAlpha;
-        public final ColorDrawable dockAreaOverlay;
-        public final int hintTextAlpha;
-        public final int hintTextOrientation;
-
-        private final int mHintTextResId;
-        private String mHintText;
-        private Paint mHintTextPaint;
-        private Point mHintTextBounds = new Point();
-        private int mHintTextAlpha = 255;
-        private AnimatorSet mDockAreaOverlayAnimator;
-        private Rect mTmpRect = new Rect();
-
-        private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation,
-                int hintTextResId) {
-            dockAreaAlpha = areaAlpha;
-            dockAreaOverlay = new ColorDrawable(Recents.getConfiguration().isGridEnabled
-                    ? DOCK_AREA_GRID_BG_COLOR : DOCK_AREA_BG_COLOR);
-            dockAreaOverlay.setAlpha(0);
-            hintTextAlpha = hintAlpha;
-            hintTextOrientation = hintOrientation;
-            mHintTextResId = hintTextResId;
-            mHintTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-            mHintTextPaint.setColor(Color.WHITE);
-        }
-
-        /**
-         * Updates the view state with the given context.
-         */
-        public void update(Context context) {
-            Resources res = context.getResources();
-            mHintText = context.getString(mHintTextResId);
-            mHintTextPaint.setTextSize(res.getDimensionPixelSize(
-                    R.dimen.recents_drag_hint_text_size));
-            mHintTextPaint.getTextBounds(mHintText, 0, mHintText.length(), mTmpRect);
-            mHintTextBounds.set((int) mHintTextPaint.measureText(mHintText), mTmpRect.height());
-        }
-
-        /**
-         * Draws the current view state.
-         */
-        public void draw(Canvas canvas) {
-            // Draw the overlay background
-            if (dockAreaOverlay.getAlpha() > 0) {
-                dockAreaOverlay.draw(canvas);
-            }
-
-            // Draw the hint text
-            if (mHintTextAlpha > 0) {
-                Rect bounds = dockAreaOverlay.getBounds();
-                int x = bounds.left + (bounds.width() - mHintTextBounds.x) / 2;
-                int y = bounds.top + (bounds.height() + mHintTextBounds.y) / 2;
-                mHintTextPaint.setAlpha(mHintTextAlpha);
-                if (hintTextOrientation == VERTICAL) {
-                    canvas.save();
-                    canvas.rotate(-90f, bounds.centerX(), bounds.centerY());
-                }
-                canvas.drawText(mHintText, x, y, mHintTextPaint);
-                if (hintTextOrientation == VERTICAL) {
-                    canvas.restore();
-                }
-            }
-        }
-
-        /**
-         * Creates a new bounds and alpha animation.
-         */
-        public void startAnimation(Rect bounds, int areaAlpha, int hintAlpha, int duration,
-                Interpolator interpolator, boolean animateAlpha, boolean animateBounds) {
-            if (mDockAreaOverlayAnimator != null) {
-                mDockAreaOverlayAnimator.cancel();
-            }
-
-            ObjectAnimator anim;
-            ArrayList<Animator> animators = new ArrayList<>();
-            if (dockAreaOverlay.getAlpha() != areaAlpha) {
-                if (animateAlpha) {
-                    anim = ObjectAnimator.ofInt(dockAreaOverlay,
-                            Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), areaAlpha);
-                    anim.setDuration(duration);
-                    anim.setInterpolator(interpolator);
-                    animators.add(anim);
-                } else {
-                    dockAreaOverlay.setAlpha(areaAlpha);
-                }
-            }
-            if (mHintTextAlpha != hintAlpha) {
-                if (animateAlpha) {
-                    anim = ObjectAnimator.ofInt(this, HINT_ALPHA, mHintTextAlpha,
-                            hintAlpha);
-                    anim.setDuration(150);
-                    anim.setInterpolator(hintAlpha > mHintTextAlpha
-                            ? Interpolators.ALPHA_IN
-                            : Interpolators.ALPHA_OUT);
-                    animators.add(anim);
-                } else {
-                    mHintTextAlpha = hintAlpha;
-                    dockAreaOverlay.invalidateSelf();
-                }
-            }
-            if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) {
-                if (animateBounds) {
-                    PropertyValuesHolder prop = PropertyValuesHolder.ofObject(
-                            Utilities.DRAWABLE_RECT, Utilities.RECT_EVALUATOR,
-                            new Rect(dockAreaOverlay.getBounds()), bounds);
-                    anim = ObjectAnimator.ofPropertyValuesHolder(dockAreaOverlay, prop);
-                    anim.setDuration(duration);
-                    anim.setInterpolator(interpolator);
-                    animators.add(anim);
-                } else {
-                    dockAreaOverlay.setBounds(bounds);
-                }
-            }
-            if (!animators.isEmpty()) {
-                mDockAreaOverlayAnimator = new AnimatorSet();
-                mDockAreaOverlayAnimator.playTogether(animators);
-                mDockAreaOverlayAnimator.start();
-            }
-        }
-    }
-
-    public final int dockSide;
-    public final int createMode;
-    public final ViewState viewState;
-    private final RectF touchArea;
-    private final RectF dockArea;
-    private final RectF expandedTouchDockArea;
-    private static final Rect mTmpRect = new Rect();
-
-    /**
-     * @param createMode used to pass to ActivityManager to dock the task
-     * @param touchArea the area in which touch will initiate this dock state
-     * @param dockArea the visible dock area
-     * @param expandedTouchDockArea the area in which touch will continue to dock after entering
-     *                              the initial touch area.  This is also the new dock area to
-     *                              draw.
-     */
-    DockState(int dockSide, int createMode, int dockAreaAlpha, int hintTextAlpha,
-            @TextOrientation int hintTextOrientation, RectF touchArea, RectF dockArea,
-            RectF expandedTouchDockArea) {
-        this.dockSide = dockSide;
-        this.createMode = createMode;
-        this.viewState = new ViewState(dockAreaAlpha, hintTextAlpha, hintTextOrientation,
-                R.string.recents_drag_hint_message);
-        this.dockArea = dockArea;
-        this.touchArea = touchArea;
-        this.expandedTouchDockArea = expandedTouchDockArea;
-    }
-
-    /**
-     * Updates the dock state with the given context.
-     */
-    public void update(Context context) {
-        viewState.update(context);
-    }
-
-    /**
-     * Returns the docked task bounds with the given {@param width} and {@param height}.
-     */
-    public Rect getPreDockedBounds(int width, int height, Rect insets) {
-        getMappedRect(dockArea, width, height, mTmpRect);
-        return updateBoundsWithSystemInsets(mTmpRect, insets);
-    }
-
-    /**
-     * Returns the expanded docked task bounds with the given {@param width} and
-     * {@param height}.
-     */
-    public Rect getDockedBounds(int width, int height, int dividerSize, Rect insets,
-            Resources res) {
-        // Calculate the docked task bounds
-        boolean isHorizontalDivision =
-                res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
-        int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
-                insets, width, height, dividerSize);
-        Rect newWindowBounds = new Rect();
-        DockedDividerUtils.calculateBoundsForPosition(position, dockSide, newWindowBounds,
-                width, height, dividerSize);
-        return newWindowBounds;
-    }
-
-    /**
-     * Returns the task stack bounds with the given {@param width} and
-     * {@param height}.
-     */
-    public Rect getDockedTaskStackBounds(Rect displayRect, int width, int height,
-            int dividerSize, Rect insets, TaskStackLayoutAlgorithm layoutAlgorithm,
-            Resources res, Rect windowRectOut) {
-        // Calculate the inverse docked task bounds
-        boolean isHorizontalDivision =
-                res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
-        int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
-                insets, width, height, dividerSize);
-        DockedDividerUtils.calculateBoundsForPosition(position,
-                DockedDividerUtils.invertDockSide(dockSide), windowRectOut, width, height,
-                dividerSize);
-
-        // Calculate the task stack bounds from the new window bounds
-        Rect taskStackBounds = new Rect();
-        // If the task stack bounds is specifically under the dock area, then ignore the top
-        // inset
-        int top = dockArea.bottom < 1f
-                ? 0
-                : insets.top;
-        // For now, ignore the left insets since we always dock on the left and show Recents
-        // on the right
-        layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, 0, insets.right,
-                taskStackBounds);
-        return taskStackBounds;
-    }
-
-    /**
-     * Returns the expanded bounds in certain dock sides such that the bounds account for the
-     * system insets (namely the vertical nav bar).  This call modifies and returns the given
-     * {@param bounds}.
-     */
-    private Rect updateBoundsWithSystemInsets(Rect bounds, Rect insets) {
-        if (dockSide == DOCKED_LEFT) {
-            bounds.right += insets.left;
-        } else if (dockSide == DOCKED_RIGHT) {
-            bounds.left -= insets.right;
-        }
-        return bounds;
-    }
-
-    /**
-     * Returns the mapped rect to the given dimensions.
-     */
-    private void getMappedRect(RectF bounds, int width, int height, Rect out) {
-        out.set((int) (bounds.left * width), (int) (bounds.top * height),
-                (int) (bounds.right * width), (int) (bounds.bottom * height));
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
deleted file mode 100644
index 79a774f..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.RadialGradient;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Shader;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsConfiguration;
-
-/**
- * A rounded rectangle drawable which also includes a shadow around. This is mostly copied from
- * frameworks/support/v7/cardview/eclair-mr1/android/support/v7/widget/
- * RoundRectDrawableWithShadow.java revision c42ba8c000d1e6ce85e152dfc17089a0a69e739f with a few
- * modifications to suit our needs in SystemUI.
- */
-class FakeShadowDrawable extends Drawable {
-    // used to calculate content padding
-    final static double COS_45 = Math.cos(Math.toRadians(45));
-
-    final static float SHADOW_MULTIPLIER = 1.5f;
-
-    final float mInsetShadow; // extra shadow to avoid gaps between card and shadow
-
-    Paint mCornerShadowPaint;
-
-    Paint mEdgeShadowPaint;
-
-    final RectF mCardBounds;
-
-    float mCornerRadius;
-
-    Path mCornerShadowPath;
-
-    // updated value with inset
-    float mMaxShadowSize;
-
-    // actual value set by developer
-    float mRawMaxShadowSize;
-
-    // multiplied value to account for shadow offset
-    float mShadowSize;
-
-    // actual value set by developer
-    float mRawShadowSize;
-
-    private boolean mDirty = true;
-
-    private final int mShadowStartColor;
-
-    private final int mShadowEndColor;
-
-    private boolean mAddPaddingForCorners = true;
-
-    /**
-     * If shadow size is set to a value above max shadow, we print a warning
-     */
-    private boolean mPrintedShadowClipWarning = false;
-
-    public FakeShadowDrawable(Resources resources, RecentsConfiguration config) {
-        mShadowStartColor = resources.getColor(R.color.fake_shadow_start_color);
-        mShadowEndColor = resources.getColor(R.color.fake_shadow_end_color);
-        mInsetShadow = resources.getDimension(R.dimen.fake_shadow_inset);
-        setShadowSize(resources.getDimensionPixelSize(R.dimen.fake_shadow_size),
-                resources.getDimensionPixelSize(R.dimen.fake_shadow_size));
-        mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
-        mCornerShadowPaint.setStyle(Paint.Style.FILL);
-        mCornerShadowPaint.setDither(true);
-        mCornerRadius = Recents.getConfiguration().isGridEnabled ?
-                resources.getDimensionPixelSize(
-                    R.dimen.recents_grid_task_view_rounded_corners_radius) :
-                resources.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
-        mCardBounds = new RectF();
-        mEdgeShadowPaint = new Paint(mCornerShadowPaint);
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mCornerShadowPaint.setAlpha(alpha);
-        mEdgeShadowPaint.setAlpha(alpha);
-    }
-
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        super.onBoundsChange(bounds);
-        mDirty = true;
-    }
-
-    void setShadowSize(float shadowSize, float maxShadowSize) {
-        if (shadowSize < 0 || maxShadowSize < 0) {
-            throw new IllegalArgumentException("invalid shadow size");
-        }
-        if (shadowSize > maxShadowSize) {
-            shadowSize = maxShadowSize;
-            if (!mPrintedShadowClipWarning) {
-                Log.w("CardView", "Shadow size is being clipped by the max shadow size. See "
-                        + "{CardView#setMaxCardElevation}.");
-                mPrintedShadowClipWarning = true;
-            }
-        }
-        if (mRawShadowSize == shadowSize && mRawMaxShadowSize == maxShadowSize) {
-            return;
-        }
-        mRawShadowSize = shadowSize;
-        mRawMaxShadowSize = maxShadowSize;
-        mShadowSize = shadowSize * SHADOW_MULTIPLIER + mInsetShadow;
-        mMaxShadowSize = maxShadowSize + mInsetShadow;
-        mDirty = true;
-        invalidateSelf();
-    }
-
-    @Override
-    public boolean getPadding(Rect padding) {
-        int vOffset = (int) Math.ceil(calculateVerticalPadding(mRawMaxShadowSize, mCornerRadius,
-                mAddPaddingForCorners));
-        int hOffset = (int) Math.ceil(calculateHorizontalPadding(mRawMaxShadowSize, mCornerRadius,
-                mAddPaddingForCorners));
-        padding.set(hOffset, vOffset, hOffset, vOffset);
-        return true;
-    }
-
-    static float calculateVerticalPadding(float maxShadowSize, float cornerRadius,
-            boolean addPaddingForCorners) {
-        if (addPaddingForCorners) {
-            return (float) (maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius);
-        } else {
-            return maxShadowSize * SHADOW_MULTIPLIER;
-        }
-    }
-
-    static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius,
-            boolean addPaddingForCorners) {
-        if (addPaddingForCorners) {
-            return (float) (maxShadowSize + (1 - COS_45) * cornerRadius);
-        } else {
-            return maxShadowSize;
-        }
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-        mCornerShadowPaint.setColorFilter(colorFilter);
-        mEdgeShadowPaint.setColorFilter(colorFilter);
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.OPAQUE;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        if (mDirty) {
-            buildComponents(getBounds());
-            mDirty = false;
-        }
-        canvas.translate(0, mRawShadowSize / 4);
-        drawShadow(canvas);
-        canvas.translate(0, -mRawShadowSize / 4);
-    }
-
-    private void drawShadow(Canvas canvas) {
-        final float edgeShadowTop = -mCornerRadius - mShadowSize;
-        final float inset = mCornerRadius + mInsetShadow + mRawShadowSize / 2;
-        final boolean drawHorizontalEdges = mCardBounds.width() - 2 * inset > 0;
-        final boolean drawVerticalEdges = mCardBounds.height() - 2 * inset > 0;
-        // LT
-        int saved = canvas.save();
-        canvas.translate(mCardBounds.left + inset, mCardBounds.top + inset);
-        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
-        if (drawHorizontalEdges) {
-            canvas.drawRect(0, edgeShadowTop,
-                    mCardBounds.width() - 2 * inset, -mCornerRadius,
-                    mEdgeShadowPaint);
-        }
-        canvas.restoreToCount(saved);
-        // RB
-        saved = canvas.save();
-        canvas.translate(mCardBounds.right - inset, mCardBounds.bottom - inset);
-        canvas.rotate(180f);
-        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
-        if (drawHorizontalEdges) {
-            canvas.drawRect(0, edgeShadowTop,
-                    mCardBounds.width() - 2 * inset, -mCornerRadius + mShadowSize,
-                    mEdgeShadowPaint);
-        }
-        canvas.restoreToCount(saved);
-        // LB
-        saved = canvas.save();
-        canvas.translate(mCardBounds.left + inset, mCardBounds.bottom - inset);
-        canvas.rotate(270f);
-        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
-        if (drawVerticalEdges) {
-            canvas.drawRect(0, edgeShadowTop,
-                    mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
-        }
-        canvas.restoreToCount(saved);
-        // RT
-        saved = canvas.save();
-        canvas.translate(mCardBounds.right - inset, mCardBounds.top + inset);
-        canvas.rotate(90f);
-        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
-        if (drawVerticalEdges) {
-            canvas.drawRect(0, edgeShadowTop,
-                    mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
-        }
-        canvas.restoreToCount(saved);
-    }
-
-    private void buildShadowCorners() {
-        RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);
-        RectF outerBounds = new RectF(innerBounds);
-        outerBounds.inset(-mShadowSize, -mShadowSize);
-
-        if (mCornerShadowPath == null) {
-            mCornerShadowPath = new Path();
-        } else {
-            mCornerShadowPath.reset();
-        }
-        mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
-        mCornerShadowPath.moveTo(-mCornerRadius, 0);
-        mCornerShadowPath.rLineTo(-mShadowSize, 0);
-        // outer arc
-        mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false);
-        // inner arc
-        mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false);
-        mCornerShadowPath.close();
-
-        float startRatio = mCornerRadius / (mCornerRadius + mShadowSize);
-        mCornerShadowPaint.setShader(new RadialGradient(0, 0, mCornerRadius + mShadowSize,
-                new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
-                new float[]{0f, startRatio, 1f}
-                , Shader.TileMode.CLAMP));
-
-        // we offset the content shadowSize/2 pixels up to make it more realistic.
-        // this is why edge shadow shader has some extra space
-        // When drawing bottom edge shadow, we use that extra space.
-        mEdgeShadowPaint.setShader(new LinearGradient(0, -mCornerRadius + mShadowSize, 0,
-                -mCornerRadius - mShadowSize,
-                new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
-                new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP));
-    }
-
-    private void buildComponents(Rect bounds) {
-        // Card is offset SHADOW_MULTIPLIER * maxShadowSize to account for the shadow shift.
-        // We could have different top-bottom offsets to avoid extra gap above but in that case
-        // center aligning Views inside the CardView would be problematic.
-        final float verticalOffset = mMaxShadowSize * SHADOW_MULTIPLIER;
-        mCardBounds.set(bounds.left + mMaxShadowSize, bounds.top + verticalOffset,
-                bounds.right - mMaxShadowSize, bounds.bottom - verticalOffset);
-        buildShadowCorners();
-    }
-
-    float getMinWidth() {
-        final float content = 2 *
-                Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow + mRawMaxShadowSize / 2);
-        return content + (mRawMaxShadowSize + mInsetShadow) * 2;
-    }
-
-    float getMinHeight() {
-        final float content = 2 * Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow
-                        + mRawMaxShadowSize * SHADOW_MULTIPLIER / 2);
-        return content + (mRawMaxShadowSize * SHADOW_MULTIPLIER + mInsetShadow) * 2;
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java
deleted file mode 100644
index 1c47430..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.GraphicBuffer;
-import android.graphics.Rect;
-import android.util.Log;
-
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsDebugFlags;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
-import com.android.systemui.shared.recents.view.RecentsTransition;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A helper class to create the transition app animation specs to/from Recents
- */
-public class RecentsTransitionComposer {
-
-    private static final String TAG = "RecentsTransitionComposer";
-
-    private Context mContext;
-    private TaskViewTransform mTmpTransform = new TaskViewTransform();
-
-    public RecentsTransitionComposer(Context context) {
-        mContext = context;
-    }
-
-    /**
-     * Composes a single animation spec for the given {@link TaskView}
-     */
-    private static AppTransitionAnimationSpecCompat composeAnimationSpec(TaskStackView stackView,
-            TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
-        Bitmap b = null;
-        if (addHeaderBitmap) {
-            b = composeHeaderBitmap(taskView, transform);
-            if (b == null) {
-                return null;
-            }
-        }
-
-        Rect taskRect = new Rect();
-        transform.rect.round(taskRect);
-        // Disable in for low ram devices because each task does in Recents does not have fullscreen
-        // height (stackView height) and when transitioning to fullscreen app, the code below would
-        // force the task thumbnail to full stackView height immediately causing the transition
-        // jarring.
-        if (!Recents.getConfiguration().isLowRamDevice && taskView.getTask() !=
-                stackView.getStack().getFrontMostTask()) {
-            taskRect.bottom = taskRect.top + stackView.getMeasuredHeight();
-        }
-        return new AppTransitionAnimationSpecCompat(taskView.getTask().key.id, b, taskRect);
-    }
-
-    /**
-     * Composes the transition spec when docking a task, which includes a full task bitmap.
-     */
-    public List<AppTransitionAnimationSpecCompat> composeDockAnimationSpec(TaskView taskView,
-            Rect bounds) {
-        mTmpTransform.fillIn(taskView);
-        Task task = taskView.getTask();
-        Bitmap buffer = RecentsTransitionComposer.composeTaskBitmap(taskView, mTmpTransform);
-        return Collections.singletonList(new AppTransitionAnimationSpecCompat(task.key.id, buffer,
-                bounds));
-    }
-
-    /**
-     * Composes the animation specs for all the tasks in the target stack.
-     */
-    public List<AppTransitionAnimationSpecCompat> composeAnimationSpecs(final Task task,
-            final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) {
-        // Calculate the offscreen task rect (for tasks that are not backed by views)
-        TaskView taskView = stackView.getChildViewForTask(task);
-        TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
-        Rect offscreenTaskRect = new Rect();
-        stackLayout.getFrontOfStackTransform().rect.round(offscreenTaskRect);
-
-        // If this is a full screen stack, the transition will be towards the single, full screen
-        // task. We only need the transition spec for this task.
-
-        // TODO: Sometimes targetStackId is not initialized after reboot, so we also have to
-        // check for INVALID_STACK_ID (now WINDOWING_MODE_UNDEFINED)
-        if (windowingMode == WINDOWING_MODE_FULLSCREEN
-                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                || activityType == ACTIVITY_TYPE_ASSISTANT
-                || windowingMode == WINDOWING_MODE_UNDEFINED) {
-            List<AppTransitionAnimationSpecCompat> specs = new ArrayList<>();
-            if (taskView == null) {
-                specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
-            } else {
-                mTmpTransform.fillIn(taskView);
-                stackLayout.transformToScreenCoordinates(mTmpTransform, windowRect);
-                AppTransitionAnimationSpecCompat spec = composeAnimationSpec(stackView, taskView,
-                        mTmpTransform, true /* addHeaderBitmap */);
-                if (spec != null) {
-                    specs.add(spec);
-                }
-            }
-            return specs;
-        }
-        return Collections.emptyList();
-    }
-
-    /**
-     * Composes a single animation spec for the given {@link Task}
-     */
-    private static AppTransitionAnimationSpecCompat composeOffscreenAnimationSpec(Task task,
-            Rect taskRect) {
-        return new AppTransitionAnimationSpecCompat(task.key.id, null, taskRect);
-    }
-
-    public static Bitmap composeTaskBitmap(TaskView taskView, TaskViewTransform transform) {
-        float scale = transform.scale;
-        int fromWidth = (int) (transform.rect.width() * scale);
-        int fromHeight = (int) (transform.rect.height() * scale);
-        if (fromWidth == 0 || fromHeight == 0) {
-            Log.e(TAG, "Could not compose thumbnail for task: " + taskView.getTask() +
-                    " at transform: " + transform);
-
-            return RecentsTransition.drawViewIntoHardwareBitmap(1, 1, null, 1f, 0x00ffffff);
-        } else {
-            if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
-                return RecentsTransition.drawViewIntoHardwareBitmap(fromWidth, fromHeight, null, 1f,
-                        0xFFff0000);
-            } else {
-                return RecentsTransition.drawViewIntoHardwareBitmap(fromWidth, fromHeight, taskView,
-                        scale, 0);
-            }
-        }
-    }
-
-    private static Bitmap composeHeaderBitmap(TaskView taskView,
-            TaskViewTransform transform) {
-        float scale = transform.scale;
-        int headerWidth = (int) (transform.rect.width());
-        int headerHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale);
-        if (headerWidth == 0 || headerHeight == 0) {
-            return null;
-        }
-
-        if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
-            return RecentsTransition.drawViewIntoHardwareBitmap(headerWidth, headerHeight, null, 1f,
-                    0xFFff0000);
-        } else {
-            return RecentsTransition.drawViewIntoHardwareBitmap(headerWidth, headerHeight,
-                    taskView.mHeaderView, scale, 0);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
deleted file mode 100644
index 5c925fd..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ /dev/null
@@ -1,1078 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
-
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.IRemoteCallback;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewPropertyAnimator;
-import android.view.Window;
-import android.view.WindowInsets;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.colorextraction.drawable.GradientDrawable;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.Utils;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
-import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
-import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
-import com.android.systemui.recents.events.activity.ShowEmptyViewEvent;
-import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
-import com.android.systemui.recents.events.component.ExpandPipEvent;
-import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
-import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
-import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
-import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
-import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.WindowManagerProxy;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.statusbar.phone.ScrimController;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This view is the the top level layout that contains TaskStacks (which are laid out according
- * to their SpaceNode bounds.
- */
-public class RecentsView extends FrameLayout {
-
-    private static final String TAG = "RecentsView";
-
-    private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200;
-
-    private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134;
-    private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100;
-
-    private static final int BUSY_RECENTS_TASK_COUNT = 3;
-
-    private Handler mHandler;
-    private TaskStackView mTaskStackView;
-    private TextView mStackActionButton;
-    private TextView mEmptyView;
-    private final float mStackButtonShadowRadius;
-    private final PointF mStackButtonShadowDistance;
-    private final int mStackButtonShadowColor;
-
-    private boolean mAwaitingFirstLayout = true;
-
-    @ViewDebug.ExportedProperty(category="recents")
-    Rect mSystemInsets = new Rect();
-    private int mDividerSize;
-
-    private float mBusynessFactor;
-    private GradientDrawable mBackgroundScrim;
-    private ColorDrawable mMultiWindowBackgroundScrim;
-    private ValueAnimator mBackgroundScrimAnimator;
-    private Point mTmpDisplaySize = new Point();
-
-    private final AnimatorUpdateListener mUpdateBackgroundScrimAlpha = (animation) -> {
-        int alpha = (Integer) animation.getAnimatedValue();
-        mBackgroundScrim.setAlpha(alpha);
-        mMultiWindowBackgroundScrim.setAlpha(alpha);
-    };
-
-    private RecentsTransitionComposer mTransitionHelper;
-    @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
-    private RecentsViewTouchHandler mTouchHandler;
-    private final FlingAnimationUtils mFlingAnimationUtils;
-
-    public RecentsView(Context context) {
-        this(context, null);
-    }
-
-    public RecentsView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        setWillNotDraw(false);
-
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        mHandler = new Handler();
-        mTransitionHelper = new RecentsTransitionComposer(getContext());
-        mDividerSize = ssp.getDockedDividerSize(context);
-        mTouchHandler = new RecentsViewTouchHandler(this);
-        mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
-        mBackgroundScrim = new GradientDrawable(context);
-        mMultiWindowBackgroundScrim = new ColorDrawable();
-
-        LayoutInflater inflater = LayoutInflater.from(context);
-        mEmptyView = (TextView) inflater.inflate(R.layout.recents_empty, this, false);
-        addView(mEmptyView);
-
-        if (mStackActionButton != null) {
-            removeView(mStackActionButton);
-        }
-        mStackActionButton = (TextView) inflater.inflate(Recents.getConfiguration()
-                        .isLowRamDevice
-                    ? R.layout.recents_low_ram_stack_action_button
-                    : R.layout.recents_stack_action_button,
-                this, false);
-
-        mStackButtonShadowRadius = mStackActionButton.getShadowRadius();
-        mStackButtonShadowDistance = new PointF(mStackActionButton.getShadowDx(),
-                mStackActionButton.getShadowDy());
-        mStackButtonShadowColor = mStackActionButton.getShadowColor();
-        addView(mStackActionButton);
-
-        reevaluateStyles();
-    }
-
-    public void reevaluateStyles() {
-        int textColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
-        boolean usingDarkText = Color.luminance(textColor) < 0.5f;
-
-        mEmptyView.setTextColor(textColor);
-        mEmptyView.setCompoundDrawableTintList(new ColorStateList(new int[][]{
-                {android.R.attr.state_enabled}}, new int[]{textColor}));
-
-        if (mStackActionButton != null) {
-            mStackActionButton.setTextColor(textColor);
-            // Enable/disable shadow if text color is already dark.
-            if (usingDarkText) {
-                mStackActionButton.setShadowLayer(0, 0, 0, 0);
-            } else {
-                mStackActionButton.setShadowLayer(mStackButtonShadowRadius,
-                        mStackButtonShadowDistance.x, mStackButtonShadowDistance.y,
-                        mStackButtonShadowColor);
-            }
-        }
-
-        // Let's also require dark status and nav bars if the text is dark
-        int systemBarsStyle = usingDarkText ? View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
-                View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0;
-
-        setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
-                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
-                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
-                systemBarsStyle);
-    }
-
-    /**
-     * Called from RecentsActivity when it is relaunched.
-     */
-    public void onReload(TaskStack stack, boolean isResumingFromVisible) {
-        final RecentsConfiguration config = Recents.getConfiguration();
-        final RecentsActivityLaunchState launchState = config.getLaunchState();
-        final boolean isTaskStackEmpty = stack.getTaskCount() == 0;
-
-        if (mTaskStackView == null) {
-            isResumingFromVisible = false;
-            mTaskStackView = new TaskStackView(getContext());
-            mTaskStackView.setSystemInsets(mSystemInsets);
-            addView(mTaskStackView);
-        }
-
-        // Reset the state
-        mAwaitingFirstLayout = !isResumingFromVisible;
-
-        // Update the stack
-        mTaskStackView.onReload(isResumingFromVisible);
-        updateStack(stack, true /* setStackViewTasks */);
-        updateBusyness();
-
-        if (isResumingFromVisible) {
-            // If we are already visible, then restore the background scrim
-            animateBackgroundScrim(getOpaqueScrimAlpha(), DEFAULT_UPDATE_SCRIM_DURATION);
-        } else {
-            // If we are already occluded by the app, then set the final background scrim alpha now.
-            // Otherwise, defer until the enter animation completes to animate the scrim alpha with
-            // the tasks for the home animation.
-            if (launchState.launchedViaDockGesture || launchState.launchedFromApp
-                    || isTaskStackEmpty) {
-                mBackgroundScrim.setAlpha((int) (getOpaqueScrimAlpha() * 255));
-            } else {
-                mBackgroundScrim.setAlpha(0);
-            }
-            mMultiWindowBackgroundScrim.setAlpha(mBackgroundScrim.getAlpha());
-        }
-    }
-
-    /**
-     * Called from RecentsActivity when the task stack is updated.
-     */
-    public void updateStack(TaskStack stack, boolean setStackViewTasks) {
-        if (setStackViewTasks) {
-            mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
-        }
-
-        // Update the top level view's visibilities
-        if (stack.getTaskCount() > 0) {
-            hideEmptyView();
-        } else {
-            showEmptyView(R.string.recents_empty_message);
-        }
-    }
-
-    /**
-     * Animates the scrim opacity based on how many tasks are visible.
-     * Called from {@link RecentsActivity} when tasks are dismissed.
-     */
-    public void updateScrimOpacity() {
-        if (updateBusyness()) {
-            animateBackgroundScrim(getOpaqueScrimAlpha(), DEFAULT_UPDATE_SCRIM_DURATION);
-        }
-    }
-
-    /**
-     * Updates the busyness factor.
-     *
-     * @return True if it changed.
-     */
-    private boolean updateBusyness() {
-        final int taskCount = mTaskStackView.getStack().getTaskCount();
-        final float busyness = Math.min(taskCount, BUSY_RECENTS_TASK_COUNT)
-                / (float) BUSY_RECENTS_TASK_COUNT;
-        if (mBusynessFactor == busyness) {
-            return false;
-        } else {
-            mBusynessFactor = busyness;
-            return true;
-        }
-    }
-
-    /**
-     * Returns the current TaskStack.
-     */
-    public TaskStack getStack() {
-        return mTaskStackView.getStack();
-    }
-
-    /**
-     * Returns the window background scrim.
-     */
-    public void updateBackgroundScrim(Window window, boolean isInMultiWindow) {
-        if (isInMultiWindow) {
-            mBackgroundScrim.setCallback(null);
-            window.setBackgroundDrawable(mMultiWindowBackgroundScrim);
-        } else {
-            mMultiWindowBackgroundScrim.setCallback(null);
-            window.setBackgroundDrawable(mBackgroundScrim);
-        }
-    }
-
-    /** Launches the focused task from the first stack if possible */
-    public boolean launchFocusedTask(int logEvent) {
-        if (mTaskStackView != null) {
-            Task task = mTaskStackView.getFocusedTask();
-            if (task != null) {
-                TaskView taskView = mTaskStackView.getChildViewForTask(task);
-                EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, false));
-
-                if (logEvent != 0) {
-                    MetricsLogger.action(getContext(), logEvent,
-                            task.key.getComponent().toString());
-                }
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /** Launches the task that recents was launched from if possible */
-    public boolean launchPreviousTask() {
-        if (Recents.getConfiguration().getLaunchState().launchedFromPipApp) {
-            // If the app auto-entered PiP on the way to Recents, then just re-expand it
-            EventBus.getDefault().send(new ExpandPipEvent());
-            return true;
-        }
-
-        if (mTaskStackView != null) {
-            Task task = getStack().getLaunchTarget();
-            if (task != null) {
-                TaskView taskView = mTaskStackView.getChildViewForTask(task);
-                EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, false));
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Hides the task stack and shows the empty view.
-     */
-    public void showEmptyView(int msgResId) {
-        mTaskStackView.setVisibility(View.INVISIBLE);
-        mEmptyView.setText(msgResId);
-        mEmptyView.setVisibility(View.VISIBLE);
-        mEmptyView.bringToFront();
-        mStackActionButton.bringToFront();
-    }
-
-    /**
-     * Shows the task stack and hides the empty view.
-     */
-    public void hideEmptyView() {
-        mEmptyView.setVisibility(View.INVISIBLE);
-        mTaskStackView.setVisibility(View.VISIBLE);
-        mTaskStackView.bringToFront();
-        mStackActionButton.bringToFront();
-    }
-
-    /**
-     * Set the color of the scrim.
-     *
-     * @param scrimColors Colors to use.
-     * @param animated Interpolate colors if true.
-     */
-    public void setScrimColors(ColorExtractor.GradientColors scrimColors, boolean animated) {
-        mBackgroundScrim.setColors(scrimColors, animated);
-        int alpha = mMultiWindowBackgroundScrim.getAlpha();
-        mMultiWindowBackgroundScrim.setColor(scrimColors.getMainColor());
-        mMultiWindowBackgroundScrim.setAlpha(alpha);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
-        EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 2);
-        super.onAttachedToWindow();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        EventBus.getDefault().unregister(this);
-        EventBus.getDefault().unregister(mTouchHandler);
-    }
-
-    /**
-     * This is called with the full size of the window since we are handling our own insets.
-     */
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        int height = MeasureSpec.getSize(heightMeasureSpec);
-
-        if (mTaskStackView.getVisibility() != GONE) {
-            mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
-        }
-
-        // Measure the empty view to the full size of the screen
-        if (mEmptyView.getVisibility() != GONE) {
-            measureChild(mEmptyView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
-                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
-        }
-
-        // Measure the stack action button within the constraints of the space above the stack
-        Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect();
-        measureChild(mStackActionButton,
-                MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST),
-                MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST));
-
-        setMeasuredDimension(width, height);
-    }
-
-    /**
-     * This is called with the full size of the window since we are handling our own insets.
-     */
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (mTaskStackView.getVisibility() != GONE) {
-            mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
-        }
-
-        // Layout the empty view
-        if (mEmptyView.getVisibility() != GONE) {
-            int leftRightInsets = mSystemInsets.left + mSystemInsets.right;
-            int topBottomInsets = mSystemInsets.top + mSystemInsets.bottom;
-            int childWidth = mEmptyView.getMeasuredWidth();
-            int childHeight = mEmptyView.getMeasuredHeight();
-            int childLeft = left + mSystemInsets.left +
-                    Math.max(0, (right - left - leftRightInsets - childWidth)) / 2;
-            int childTop = top + mSystemInsets.top +
-                    Math.max(0, (bottom - top - topBottomInsets - childHeight)) / 2;
-            mEmptyView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
-        }
-
-        // Needs to know the screen size since the gradient never scales up or down
-        // even when bounds change.
-        mContext.getDisplay().getRealSize(mTmpDisplaySize);
-        mBackgroundScrim.setScreenSize(mTmpDisplaySize.x, mTmpDisplaySize.y);
-        mBackgroundScrim.setBounds(left, top, right, bottom);
-        mMultiWindowBackgroundScrim.setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
-
-        // Layout the stack action button such that its drawable is start-aligned with the
-        // stack, vertically centered in the available space above the stack
-        Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
-        mStackActionButton.layout(buttonBounds.left, buttonBounds.top, buttonBounds.right,
-                buttonBounds.bottom);
-
-        if (mAwaitingFirstLayout) {
-            mAwaitingFirstLayout = false;
-            // If launched via dragging from the nav bar, then we should translate the whole view
-            // down offscreen
-            RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-            if (launchState.launchedViaDragGesture) {
-                setTranslationY(getMeasuredHeight());
-            } else {
-                setTranslationY(0f);
-            }
-
-            if (Recents.getConfiguration().isLowRamDevice
-                    && mEmptyView.getVisibility() == View.VISIBLE) {
-                animateEmptyView(true /* show */, null /* postAnimationTrigger */);
-            }
-        }
-    }
-
-    @Override
-    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        mSystemInsets.set(insets.getSystemWindowInsets());
-        mTaskStackView.setSystemInsets(mSystemInsets);
-        requestLayout();
-        return insets;
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        return mTouchHandler.onInterceptTouchEvent(ev);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        return mTouchHandler.onTouchEvent(ev);
-    }
-
-    @Override
-    public void onDrawForeground(Canvas canvas) {
-        super.onDrawForeground(canvas);
-
-        ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
-        for (int i = visDockStates.size() - 1; i >= 0; i--) {
-            visDockStates.get(i).viewState.draw(canvas);
-        }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
-        for (int i = visDockStates.size() - 1; i >= 0; i--) {
-            Drawable d = visDockStates.get(i).viewState.dockAreaOverlay;
-            if (d == who) {
-                return true;
-            }
-        }
-        return super.verifyDrawable(who);
-    }
-
-    /**** EventBus Events ****/
-
-    public final void onBusEvent(LaunchTaskEvent event) {
-        launchTaskFromRecents(getStack(), event.task, mTaskStackView, event.taskView,
-                event.screenPinningRequested, event.targetWindowingMode, event.targetActivityType);
-        if (Recents.getConfiguration().isLowRamDevice) {
-            EventBus.getDefault().send(new HideStackActionButtonEvent(false /* translate */));
-        }
-    }
-
-    public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
-        int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
-        // Hide the stack action button
-        EventBus.getDefault().send(new HideStackActionButtonEvent());
-        animateBackgroundScrim(0f, taskViewExitToHomeDuration);
-
-        if (Recents.getConfiguration().isLowRamDevice) {
-            animateEmptyView(false /* show */, event.getAnimationTrigger());
-        }
-    }
-
-    public final void onBusEvent(DragStartEvent event) {
-        updateVisibleDockRegions(Recents.getConfiguration().getDockStatesForCurrentOrientation(),
-                true /* isDefaultDockState */, DockState.NONE.viewState.dockAreaAlpha,
-                DockState.NONE.viewState.hintTextAlpha,
-                true /* animateAlpha */, false /* animateBounds */);
-
-        // Temporarily hide the stack action button without changing visibility
-        if (mStackActionButton != null) {
-            mStackActionButton.animate()
-                    .alpha(0f)
-                    .setDuration(HIDE_STACK_ACTION_BUTTON_DURATION)
-                    .setInterpolator(Interpolators.ALPHA_OUT)
-                    .start();
-        }
-    }
-
-    public final void onBusEvent(DragDropTargetChangedEvent event) {
-        if (event.dropTarget == null || !(event.dropTarget instanceof DockState)) {
-            updateVisibleDockRegions(
-                    Recents.getConfiguration().getDockStatesForCurrentOrientation(),
-                    true /* isDefaultDockState */, DockState.NONE.viewState.dockAreaAlpha,
-                    DockState.NONE.viewState.hintTextAlpha,
-                    true /* animateAlpha */, true /* animateBounds */);
-        } else {
-            final DockState dockState = (DockState) event.dropTarget;
-            updateVisibleDockRegions(new DockState[] {dockState},
-                    false /* isDefaultDockState */, -1, -1, true /* animateAlpha */,
-                    true /* animateBounds */);
-        }
-        if (mStackActionButton != null) {
-            event.addPostAnimationCallback(new Runnable() {
-                @Override
-                public void run() {
-                    // Move the clear all button to its new position
-                    Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
-                    mStackActionButton.setLeftTopRightBottom(buttonBounds.left, buttonBounds.top,
-                            buttonBounds.right, buttonBounds.bottom);
-                }
-            });
-        }
-    }
-
-    public final void onBusEvent(final DragEndEvent event) {
-        // Handle the case where we drop onto a dock region
-        if (event.dropTarget instanceof DockState) {
-            final DockState dockState = (DockState) event.dropTarget;
-
-            // Hide the dock region
-            updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, -1,
-                    false /* animateAlpha */, false /* animateBounds */);
-
-            // We translated the view but we need to animate it back from the current layout-space
-            // rect to its final layout-space rect
-            Utilities.setViewFrameFromTranslation(event.taskView);
-
-            final ActivityOptions options = ActivityOptionsCompat.makeSplitScreenOptions(
-                    dockState.createMode == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
-            if (ActivityManagerWrapper.getInstance().startActivityFromRecents(event.task.key.id,
-                    options)) {
-                final Runnable animStartedListener = () -> {
-                    EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
-                    // Remove the task and don't bother relaying out, as all the tasks
-                    // will be relaid out when the stack changes on the multiwindow
-                    // change event
-                    getStack().removeTask(event.task, null, true /* fromDockGesture */);
-                };
-                final Rect taskRect = getTaskRect(event.taskView);
-                AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(
-                        getHandler()) {
-                    @Override
-                    public List<AppTransitionAnimationSpecCompat> composeSpecs() {
-                        return mTransitionHelper.composeDockAnimationSpec(event.taskView, taskRect);
-                    }
-                };
-                WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
-                        future, animStartedListener, getHandler(), true /* scaleUp */);
-                MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
-                        event.task.getTopComponent().flattenToShortString());
-            } else {
-                EventBus.getDefault().send(new DragEndCancelledEvent(getStack(), event.task,
-                        event.taskView));
-            }
-        } else {
-            // Animate the overlay alpha back to 0
-            updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1,
-                    true /* animateAlpha */, false /* animateBounds */);
-        }
-
-        // Show the stack action button again without changing visibility
-        if (mStackActionButton != null) {
-            mStackActionButton.animate()
-                    .alpha(1f)
-                    .setDuration(SHOW_STACK_ACTION_BUTTON_DURATION)
-                    .setInterpolator(Interpolators.ALPHA_IN)
-                    .start();
-        }
-    }
-
-    public final void onBusEvent(final DragEndCancelledEvent event) {
-        // Animate the overlay alpha back to 0
-        updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1,
-                true /* animateAlpha */, false /* animateBounds */);
-    }
-
-    private Rect getTaskRect(TaskView taskView) {
-        int[] location = taskView.getLocationOnScreen();
-        int viewX = location[0];
-        int viewY = location[1];
-        return new Rect(viewX, viewY,
-                (int) (viewX + taskView.getWidth() * taskView.getScaleX()),
-                (int) (viewY + taskView.getHeight() * taskView.getScaleY()));
-    }
-
-    public final void onBusEvent(DraggingInRecentsEvent event) {
-        if (mTaskStackView.getTaskViews().size() > 0) {
-            setTranslationY(event.distanceFromTop - mTaskStackView.getTaskViews().get(0).getY());
-        }
-    }
-
-    public final void onBusEvent(DraggingInRecentsEndedEvent event) {
-        ViewPropertyAnimator animator = animate();
-        if (event.velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
-            animator.translationY(getHeight());
-            animator.withEndAction(new Runnable() {
-                @Override
-                public void run() {
-                    WindowManagerProxy.getInstance().maximizeDockedStack();
-                }
-            });
-            mFlingAnimationUtils.apply(animator, getTranslationY(), getHeight(), event.velocity);
-        } else {
-            animator.translationY(0f);
-            animator.setListener(null);
-            mFlingAnimationUtils.apply(animator, getTranslationY(), 0, event.velocity);
-        }
-        animator.start();
-    }
-
-    public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
-        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-        if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp
-                && getStack().getTaskCount() > 0) {
-            animateBackgroundScrim(getOpaqueScrimAlpha(),
-                    TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
-        }
-    }
-
-    public final void onBusEvent(AllTaskViewsDismissedEvent event) {
-        EventBus.getDefault().send(new HideStackActionButtonEvent());
-    }
-
-    public final void onBusEvent(DismissAllTaskViewsEvent event) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        if (!ssp.hasDockedTask()) {
-            // Animate the background away only if we are dismissing Recents to home
-            animateBackgroundScrim(0f, DEFAULT_UPDATE_SCRIM_DURATION);
-        }
-    }
-
-    public final void onBusEvent(ShowStackActionButtonEvent event) {
-        showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, event.translate);
-    }
-
-    public final void onBusEvent(HideStackActionButtonEvent event) {
-        hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
-    }
-
-    public final void onBusEvent(MultiWindowStateChangedEvent event) {
-        updateStack(event.stack, false /* setStackViewTasks */);
-    }
-
-    public final void onBusEvent(ShowEmptyViewEvent event) {
-        showEmptyView(R.string.recents_empty_message);
-    }
-
-    /**
-     * Shows the stack action button.
-     */
-    private void showStackActionButton(final int duration, final boolean translate) {
-        final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
-        if (mStackActionButton.getVisibility() == View.INVISIBLE) {
-            mStackActionButton.setVisibility(View.VISIBLE);
-            mStackActionButton.setAlpha(0f);
-            if (translate) {
-                mStackActionButton.setTranslationY(mStackActionButton.getMeasuredHeight() *
-                        (Recents.getConfiguration().isLowRamDevice ? 1 : -0.25f));
-            } else {
-                mStackActionButton.setTranslationY(0f);
-            }
-            postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
-                @Override
-                public void run() {
-                    if (translate) {
-                        mStackActionButton.animate()
-                            .translationY(0f);
-                    }
-                    mStackActionButton.animate()
-                            .alpha(1f)
-                            .setDuration(duration)
-                            .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                            .start();
-                }
-            });
-        }
-        postAnimationTrigger.flushLastDecrementRunnables();
-    }
-
-    /**
-     * Hides the stack action button.
-     */
-    private void hideStackActionButton(int duration, boolean translate) {
-        final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
-        hideStackActionButton(duration, translate, postAnimationTrigger);
-        postAnimationTrigger.flushLastDecrementRunnables();
-    }
-
-    /**
-     * Hides the stack action button.
-     */
-    private void hideStackActionButton(int duration, boolean translate,
-                                       final ReferenceCountedTrigger postAnimationTrigger) {
-        if (mStackActionButton.getVisibility() == View.VISIBLE) {
-            if (translate) {
-                mStackActionButton.animate().translationY(mStackActionButton.getMeasuredHeight()
-                        * (Recents.getConfiguration().isLowRamDevice ? 1 : -0.25f));
-            }
-            mStackActionButton.animate()
-                    .alpha(0f)
-                    .setDuration(duration)
-                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                    .withEndAction(new Runnable() {
-                        @Override
-                        public void run() {
-                            mStackActionButton.setVisibility(View.INVISIBLE);
-                            postAnimationTrigger.decrement();
-                        }
-                    })
-                    .start();
-            postAnimationTrigger.increment();
-        }
-    }
-
-    /**
-     * Animates a translation in the Y direction and fades in/out for empty view to show or hide it.
-     * @param show whether to translate up and fade in the empty view to the center of the screen
-     * @param postAnimationTrigger to keep track of the animation
-     */
-    private void animateEmptyView(boolean show, ReferenceCountedTrigger postAnimationTrigger) {
-        float start = mTaskStackView.getStackAlgorithm().getTaskRect().height() / 4;
-        mEmptyView.setTranslationY(show ? start : 0);
-        mEmptyView.setAlpha(show ? 0f : 1f);
-        ViewPropertyAnimator animator = mEmptyView.animate()
-                .setDuration(150)
-                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                .translationY(show ? 0 : start)
-                .alpha(show ? 1f : 0f);
-
-        if (postAnimationTrigger != null) {
-            animator.setListener(postAnimationTrigger.decrementOnAnimationEnd());
-            postAnimationTrigger.increment();
-        }
-        animator.start();
-    }
-
-    /**
-     * Updates the dock region to match the specified dock state.
-     */
-    private void updateVisibleDockRegions(DockState[] newDockStates,
-            boolean isDefaultDockState, int overrideAreaAlpha, int overrideHintAlpha,
-            boolean animateAlpha, boolean animateBounds) {
-        ArraySet<DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates,
-                new ArraySet<DockState>());
-        ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
-        for (int i = visDockStates.size() - 1; i >= 0; i--) {
-            DockState dockState = visDockStates.get(i);
-            DockState.ViewState viewState = dockState.viewState;
-            if (newDockStates == null || !newDockStatesSet.contains(dockState)) {
-                // This is no longer visible, so hide it
-                viewState.startAnimation(null, 0, 0, TaskStackView.SLOW_SYNC_STACK_DURATION,
-                        Interpolators.FAST_OUT_SLOW_IN, animateAlpha, animateBounds);
-            } else {
-                // This state is now visible, update the bounds and show it
-                int areaAlpha = overrideAreaAlpha != -1
-                        ? overrideAreaAlpha
-                        : viewState.dockAreaAlpha;
-                int hintAlpha = overrideHintAlpha != -1
-                        ? overrideHintAlpha
-                        : viewState.hintTextAlpha;
-                Rect bounds = isDefaultDockState
-                        ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
-                                mSystemInsets)
-                        : dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
-                                mDividerSize, mSystemInsets, getResources());
-                if (viewState.dockAreaOverlay.getCallback() != this) {
-                    viewState.dockAreaOverlay.setCallback(this);
-                    viewState.dockAreaOverlay.setBounds(bounds);
-                }
-                viewState.startAnimation(bounds, areaAlpha, hintAlpha,
-                        TaskStackView.SLOW_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN,
-                        animateAlpha, animateBounds);
-            }
-        }
-    }
-
-    /**
-     * Scrim alpha based on how busy recents is:
-     * Scrim will be {@link ScrimController#GRADIENT_SCRIM_ALPHA} when the stack is empty,
-     * and {@link ScrimController#GRADIENT_SCRIM_ALPHA_BUSY} when it's full.
-     *
-     * @return Alpha from 0 to 1.
-     */
-    private float getOpaqueScrimAlpha() {
-        return MathUtils.map(0, 1, ScrimController.GRADIENT_SCRIM_ALPHA,
-                ScrimController.GRADIENT_SCRIM_ALPHA_BUSY, mBusynessFactor);
-    }
-
-    /**
-     * Animates the background scrim to the given {@param alpha}.
-     */
-    private void animateBackgroundScrim(float alpha, int duration) {
-        Utilities.cancelAnimationWithoutCallbacks(mBackgroundScrimAnimator);
-        // Calculate the absolute alpha to animate from
-        final int fromAlpha = mBackgroundScrim.getAlpha();
-        final int toAlpha = (int) (alpha * 255);
-        mBackgroundScrimAnimator = ValueAnimator.ofInt(fromAlpha, toAlpha);
-        mBackgroundScrimAnimator.setDuration(duration);
-        mBackgroundScrimAnimator.setInterpolator(toAlpha > fromAlpha
-                ? Interpolators.ALPHA_IN
-                : Interpolators.ALPHA_OUT);
-        mBackgroundScrimAnimator.addUpdateListener(mUpdateBackgroundScrimAlpha);
-        mBackgroundScrimAnimator.start();
-    }
-
-    /**
-     * @return the bounds of the stack action button.
-     */
-    Rect getStackActionButtonBoundsFromStackLayout() {
-        Rect actionButtonRect = new Rect(
-                mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect());
-        int left, top;
-        if (Recents.getConfiguration().isLowRamDevice) {
-            Rect windowRect = Recents.getSystemServices().getWindowRect();
-            int spaceLeft = windowRect.width() - mSystemInsets.left - mSystemInsets.right;
-            left = (spaceLeft - mStackActionButton.getMeasuredWidth()) / 2 + mSystemInsets.left;
-            top = windowRect.height() - (mStackActionButton.getMeasuredHeight()
-                    + mSystemInsets.bottom + mStackActionButton.getPaddingBottom() / 2);
-        } else {
-            left = isLayoutRtl()
-                ? actionButtonRect.left - mStackActionButton.getPaddingLeft()
-                : actionButtonRect.right + mStackActionButton.getPaddingRight()
-                        - mStackActionButton.getMeasuredWidth();
-            top = actionButtonRect.top +
-                (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2;
-        }
-        actionButtonRect.set(left, top, left + mStackActionButton.getMeasuredWidth(),
-                top + mStackActionButton.getMeasuredHeight());
-        return actionButtonRect;
-    }
-
-    View getStackActionButton() {
-        return mStackActionButton;
-    }
-
-    /**
-     * Launches the specified {@link Task}.
-     */
-    public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
-            final TaskStackView stackView, final TaskView taskView,
-            final boolean screenPinningRequested, final int windowingMode, final int activityType) {
-
-        final Runnable animStartedListener;
-        final AppTransitionAnimationSpecsFuture transitionFuture;
-        if (taskView != null) {
-
-            // Fetch window rect here already in order not to be blocked on lock contention in WM
-            // when the future calls it.
-            final Rect windowRect = Recents.getSystemServices().getWindowRect();
-            transitionFuture = new AppTransitionAnimationSpecsFuture(stackView.getHandler()) {
-                @Override
-                public List<AppTransitionAnimationSpecCompat> composeSpecs() {
-                    return mTransitionHelper.composeAnimationSpecs(task, stackView, windowingMode,
-                            activityType, windowRect);
-                }
-            };
-            animStartedListener = new Runnable() {
-                private boolean mHandled;
-
-                @Override
-                public void run() {
-                    if (mHandled) {
-                        return;
-                    }
-                    mHandled = true;
-
-                    // If we are launching into another task, cancel the previous task's
-                    // window transition
-                    EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
-                    EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
-                    stackView.cancelAllTaskViewAnimations();
-
-                    if (screenPinningRequested) {
-                        // Request screen pinning after the animation runs
-                        mHandler.postDelayed(() -> {
-                            EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext,
-                                    task.key.id));
-                        }, 350);
-                    }
-
-                    if (!Recents.getConfiguration().isLowRamDevice) {
-                        // Reset the state where we are waiting for the transition to start
-                        EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
-                    }
-                }
-            };
-        } else {
-            // This is only the case if the task is not on screen (scrolled offscreen for example)
-            transitionFuture = null;
-            animStartedListener = new Runnable() {
-                private boolean mHandled;
-
-                @Override
-                public void run() {
-                    if (mHandled) {
-                        return;
-                    }
-                    mHandled = true;
-
-                    // If we are launching into another task, cancel the previous task's
-                    // window transition
-                    EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
-                    EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
-                    stackView.cancelAllTaskViewAnimations();
-
-                    if (!Recents.getConfiguration().isLowRamDevice) {
-                        // Reset the state where we are waiting for the transition to start
-                        EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
-                    }
-                }
-            };
-        }
-
-        EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(true));
-        final ActivityOptions opts = RecentsTransition.createAspectScaleAnimation(mContext,
-                mHandler, true /* scaleUp */, transitionFuture != null ? transitionFuture : null,
-                animStartedListener);
-        if (taskView == null) {
-            // If there is no task view, then we do not need to worry about animating out occluding
-            // task views, and we can launch immediately
-            startTaskActivity(stack, task, taskView, opts, transitionFuture,
-                    windowingMode, activityType);
-        } else {
-            LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView,
-                    screenPinningRequested);
-            EventBus.getDefault().send(launchStartedEvent);
-            startTaskActivity(stack, task, taskView, opts, transitionFuture, windowingMode,
-                    activityType);
-        }
-        ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
-    }
-
-    /**
-     * Starts the activity for the launch task.
-     *
-     * @param taskView this is the {@link TaskView} that we are launching from. This can be null if
-     *                 we are toggling recents and the launch-to task is now offscreen.
-     */
-    private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView,
-            ActivityOptions opts, AppTransitionAnimationSpecsFuture transitionFuture,
-            int windowingMode, int activityType) {
-        ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(task.key, opts,
-                windowingMode, activityType, succeeded -> {
-            if (succeeded) {
-                // Keep track of the index of the task launch
-                int taskIndexFromFront = 0;
-                int taskIndex = stack.indexOfTask(task);
-                if (taskIndex > -1) {
-                    taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
-                }
-                EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
-            } else {
-                Log.e(TAG, mContext.getString(R.string.recents_launch_error_message, task.title));
-
-                // Dismiss the task if we fail to launch it
-                if (taskView != null) {
-                    taskView.dismissTask();
-                }
-
-                // Keep track of failed launches
-                EventBus.getDefault().send(new LaunchTaskFailedEvent());
-            }
-        }, getHandler());
-        if (transitionFuture != null) {
-            mHandler.post(transitionFuture::composeSpecsSynchronous);
-        }
-    }
-
-    @Override
-    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        super.requestDisallowInterceptTouchEvent(disallowIntercept);
-        mTouchHandler.cancelStackActionButtonClick();
-    }
-
-    public void dump(String prefix, PrintWriter writer) {
-        String innerPrefix = prefix + "  ";
-        String id = Integer.toHexString(System.identityHashCode(this));
-
-        writer.print(prefix); writer.print(TAG);
-        writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N");
-        writer.print(" insets="); writer.print(Utilities.dumpRect(mSystemInsets));
-        writer.print(" [0x"); writer.print(id); writer.print("]");
-        writer.println();
-
-        if (getStack() != null) {
-            getStack().dump(innerPrefix, writer);
-        }
-        if (mTaskStackView != null) {
-            mTaskStackView.dump(innerPrefix, writer);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
deleted file mode 100644
index 53a91e5..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.PointerIcon;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewDebug;
-
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
-import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
-import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.model.Task;
-
-import java.util.ArrayList;
-
-/**
- * Handles touch events for a RecentsView.
- */
-public class RecentsViewTouchHandler {
-
-    private RecentsView mRv;
-
-    @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task")
-    private Task mDragTask;
-    @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task_view_")
-    private TaskView mTaskView;
-
-    @ViewDebug.ExportedProperty(category="recents")
-    private Point mTaskViewOffset = new Point();
-    @ViewDebug.ExportedProperty(category="recents")
-    private Point mDownPos = new Point();
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mDragRequested;
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mIsDragging;
-    private float mDragSlop;
-    private int mDeviceId = -1;
-
-    private DropTarget mLastDropTarget;
-    private DividerSnapAlgorithm mDividerSnapAlgorithm;
-    private ArrayList<DropTarget> mDropTargets = new ArrayList<>();
-    private ArrayList<DockState> mVisibleDockStates = new ArrayList<>();
-
-    public RecentsViewTouchHandler(RecentsView rv) {
-        mRv = rv;
-        mDragSlop = ViewConfiguration.get(rv.getContext()).getScaledTouchSlop();
-        updateSnapAlgorithm();
-    }
-
-    private void updateSnapAlgorithm() {
-        Rect insets = new Rect();
-        SystemServicesProxy.getInstance(mRv.getContext()).getStableInsets(insets);
-        mDividerSnapAlgorithm = DividerSnapAlgorithm.create(mRv.getContext(), insets);
-    }
-
-    /**
-     * Registers a new drop target for the current drag only.
-     */
-    public void registerDropTargetForCurrentDrag(DropTarget target) {
-        mDropTargets.add(target);
-    }
-
-    /**
-     * Returns the set of visible dock states for this current drag.
-     */
-    public ArrayList<DockState> getVisibleDockStates() {
-        return mVisibleDockStates;
-    }
-
-    /** Touch preprocessing for handling below */
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        return handleTouchEvent(ev) || mDragRequested;
-    }
-
-    /** Handles touch events once we have intercepted them */
-    public boolean onTouchEvent(MotionEvent ev) {
-        handleTouchEvent(ev);
-        if (ev.getAction() == MotionEvent.ACTION_UP && mRv.getStack().getTaskCount() == 0) {
-            EventBus.getDefault().send(new HideRecentsEvent(false, true));
-        }
-        return true;
-    }
-
-    /**** Events ****/
-
-    public final void onBusEvent(DragStartEvent event) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        mRv.getParent().requestDisallowInterceptTouchEvent(true);
-        mDragRequested = true;
-        // We defer starting the actual drag handling until the user moves past the drag slop
-        mIsDragging = false;
-        mDragTask = event.task;
-        mTaskView = event.taskView;
-        mDropTargets.clear();
-
-        int[] recentsViewLocation = new int[2];
-        mRv.getLocationInWindow(recentsViewLocation);
-        mTaskViewOffset.set(mTaskView.getLeft() - recentsViewLocation[0] + event.tlOffset.x,
-                mTaskView.getTop() - recentsViewLocation[1] + event.tlOffset.y);
-
-        // Change space coordinates relative to the view to RecentsView when user initiates a touch
-        if (event.isUserTouchInitiated) {
-            float x = mDownPos.x - mTaskViewOffset.x;
-            float y = mDownPos.y - mTaskViewOffset.y;
-            mTaskView.setTranslationX(x);
-            mTaskView.setTranslationY(y);
-        }
-
-        mVisibleDockStates.clear();
-        if (ActivityTaskManager.supportsMultiWindow(mRv.getContext()) && !ssp.hasDockedTask()
-                && mDividerSnapAlgorithm.isSplitScreenFeasible()) {
-            Recents.logDockAttempt(mRv.getContext(), event.task.getTopComponent(),
-                    event.task.resizeMode);
-            if (!event.task.isDockable) {
-                EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent());
-            } else {
-                // Add the dock state drop targets (these take priority)
-                DockState[] dockStates = Recents.getConfiguration()
-                        .getDockStatesForCurrentOrientation();
-                for (DockState dockState : dockStates) {
-                    registerDropTargetForCurrentDrag(dockState);
-                    dockState.update(mRv.getContext());
-                    mVisibleDockStates.add(dockState);
-                }
-            }
-        }
-
-        // Request other drop targets to register themselves
-        EventBus.getDefault().send(new DragStartInitializeDropTargetsEvent(event.task,
-                event.taskView, this));
-        if (mDeviceId != -1) {
-            InputDevice device = InputDevice.getDevice(mDeviceId);
-            if (device != null) {
-                device.setPointerType(PointerIcon.TYPE_GRABBING);
-            }
-        }
-    }
-
-    public final void onBusEvent(DragEndEvent event) {
-        if (!mDragTask.isDockable) {
-            EventBus.getDefault().send(new HideIncompatibleAppOverlayEvent());
-        }
-        mDragRequested = false;
-        mDragTask = null;
-        mTaskView = null;
-        mLastDropTarget = null;
-    }
-
-    public final void onBusEvent(ConfigurationChangedEvent event) {
-        if (event.fromDisplayDensityChange || event.fromDeviceOrientationChange) {
-            updateSnapAlgorithm();
-        }
-    }
-
-    void cancelStackActionButtonClick() {
-        mRv.getStackActionButton().setPressed(false);
-    }
-
-    private boolean isWithinStackActionButton(float x, float y) {
-        Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
-        return mRv.getStackActionButton().getVisibility() == View.VISIBLE &&
-                mRv.getStackActionButton().pointInView(x - rect.left, y - rect.top, 0 /* slop */);
-    }
-
-    private void changeStackActionButtonDrawableHotspot(float x, float y) {
-        Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
-        mRv.getStackActionButton().drawableHotspotChanged(x - rect.left, y - rect.top);
-    }
-
-    /**
-     * Handles dragging touch events
-     */
-    private boolean handleTouchEvent(MotionEvent ev) {
-        int action = ev.getActionMasked();
-        boolean consumed = false;
-        float evX = ev.getX();
-        float evY = ev.getY();
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                mDownPos.set((int) evX, (int) evY);
-                mDeviceId = ev.getDeviceId();
-
-                if (isWithinStackActionButton(evX, evY)) {
-                    changeStackActionButtonDrawableHotspot(evX, evY);
-                    mRv.getStackActionButton().setPressed(true);
-                }
-                break;
-            case MotionEvent.ACTION_MOVE: {
-                float x = evX - mTaskViewOffset.x;
-                float y = evY - mTaskViewOffset.y;
-
-                if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
-                    changeStackActionButtonDrawableHotspot(evX, evY);
-                }
-
-                if (mDragRequested) {
-                    if (!mIsDragging) {
-                        mIsDragging = Math.hypot(evX - mDownPos.x, evY - mDownPos.y) > mDragSlop;
-                    }
-                    if (mIsDragging) {
-                        int width = mRv.getMeasuredWidth();
-                        int height = mRv.getMeasuredHeight();
-
-                        DropTarget currentDropTarget = null;
-
-                        // Give priority to the current drop target to retain the touch handling
-                        if (mLastDropTarget != null) {
-                            if (mLastDropTarget.acceptsDrop((int) evX, (int) evY, width, height,
-                                    mRv.mSystemInsets, true /* isCurrentTarget */)) {
-                                currentDropTarget = mLastDropTarget;
-                            }
-                        }
-
-                        // Otherwise, find the next target to handle this event
-                        if (currentDropTarget == null) {
-                            for (DropTarget target : mDropTargets) {
-                                if (target.acceptsDrop((int) evX, (int) evY, width, height,
-                                        mRv.mSystemInsets, false /* isCurrentTarget */)) {
-                                    currentDropTarget = target;
-                                    break;
-                                }
-                            }
-                        }
-                        if (mLastDropTarget != currentDropTarget) {
-                            mLastDropTarget = currentDropTarget;
-                            EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
-                                    currentDropTarget));
-                        }
-                    }
-                    mTaskView.setTranslationX(x);
-                    mTaskView.setTranslationY(y);
-                }
-                break;
-            }
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL: {
-                if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
-                    EventBus.getDefault().send(new DismissAllTaskViewsEvent());
-                    consumed = true;
-                }
-                cancelStackActionButtonClick();
-                if (mDragRequested) {
-                    boolean cancelled = action == MotionEvent.ACTION_CANCEL;
-                    if (cancelled) {
-                        EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask, null));
-                    }
-                    EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView,
-                            !cancelled ? mLastDropTarget : null));
-                    break;
-                }
-                mDeviceId = -1;
-            }
-        }
-        return consumed;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
deleted file mode 100644
index 170e39d..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
-import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-
-/** Manages the scrims for the various system bars. */
-public class SystemBarScrimViews {
-
-    private static final int DEFAULT_ANIMATION_DURATION = 150;
-
-    private Context mContext;
-
-    private View mNavBarScrimView;
-
-    private boolean mHasNavBarScrim;
-    private boolean mShouldAnimateNavBarScrim;
-    private boolean mHasTransposedNavBar;
-    private boolean mHasDockedTasks;
-    private int mNavBarScrimEnterDuration;
-
-    public SystemBarScrimViews(RecentsActivity activity) {
-        mContext = activity;
-        mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
-        mNavBarScrimView.forceHasOverlappingRendering(false);
-        mNavBarScrimEnterDuration = activity.getResources().getInteger(
-                R.integer.recents_nav_bar_scrim_enter_duration);
-        mHasNavBarScrim = Recents.getSystemServices().hasTransposedNavigationBar();
-        mHasDockedTasks = Recents.getSystemServices().hasDockedTask();
-    }
-
-    /**
-     * Updates the nav bar scrim.
-     */
-    public void updateNavBarScrim(boolean animateNavBarScrim, boolean hasStackTasks,
-            AnimationProps animation) {
-        prepareEnterRecentsAnimation(isNavBarScrimRequired(hasStackTasks), animateNavBarScrim);
-        if (animateNavBarScrim && animation != null) {
-            animateNavBarScrimVisibility(true, animation);
-        }
-    }
-
-    /**
-     * Prepares the scrim views for animating when entering Recents. This will be called before
-     * the first draw, unless we are updating the scrim on configuration change.
-     */
-    private void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) {
-        mHasNavBarScrim = hasNavBarScrim;
-        mShouldAnimateNavBarScrim = animateNavBarScrim;
-
-        mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
-                View.VISIBLE : View.INVISIBLE);
-    }
-
-    /**
-     * Animates the nav bar scrim visibility.
-     */
-    private void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) {
-        int toY = 0;
-        if (visible) {
-            mNavBarScrimView.setVisibility(View.VISIBLE);
-            mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
-        } else {
-            toY = mNavBarScrimView.getMeasuredHeight();
-        }
-        if (animation != AnimationProps.IMMEDIATE) {
-            mNavBarScrimView.animate()
-                    .translationY(toY)
-                    .setDuration(animation.getDuration(AnimationProps.BOUNDS))
-                    .setInterpolator(animation.getInterpolator(AnimationProps.BOUNDS))
-                    .start();
-        } else {
-            mNavBarScrimView.setTranslationY(toY);
-        }
-    }
-
-    /**
-     * @return Whether to show the nav bar scrim.
-     */
-    private boolean isNavBarScrimRequired(boolean hasStackTasks) {
-        return hasStackTasks && !mHasTransposedNavBar && !mHasDockedTasks;
-    }
-
-    /**** EventBus events ****/
-
-    /**
-     * Starts animating the scrim views when entering Recents.
-     */
-    public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
-        if (mHasNavBarScrim) {
-            AnimationProps animation = mShouldAnimateNavBarScrim
-                    ? new AnimationProps()
-                            .setDuration(AnimationProps.BOUNDS, mNavBarScrimEnterDuration)
-                            .setInterpolator(AnimationProps.BOUNDS, Interpolators.DECELERATE_QUINT)
-                    : AnimationProps.IMMEDIATE;
-            animateNavBarScrimVisibility(true, animation);
-        }
-    }
-
-    /**
-     * Starts animating the scrim views when leaving Recents (either via launching a task, or
-     * going home).
-     */
-    public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
-        if (mHasNavBarScrim) {
-            AnimationProps animation = createBoundsAnimation(
-                    TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
-            animateNavBarScrimVisibility(false, animation);
-        }
-    }
-
-    public final void onBusEvent(DismissAllTaskViewsEvent event) {
-        if (mHasNavBarScrim) {
-            AnimationProps animation = createBoundsAnimation(
-                    TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
-            animateNavBarScrimVisibility(false, animation);
-        }
-    }
-
-    public final void onBusEvent(ConfigurationChangedEvent event) {
-        if (event.fromDeviceOrientationChange) {
-            mHasNavBarScrim = Recents.getSystemServices().hasTransposedNavigationBar();
-        }
-        animateScrimToCurrentNavBarState(event.hasStackTasks);
-    }
-
-    public final void onBusEvent(MultiWindowStateChangedEvent event) {
-        mHasDockedTasks = event.inMultiWindow;
-        animateScrimToCurrentNavBarState(event.stack.getTaskCount() > 0);
-    }
-
-    public final void onBusEvent(final DragEndEvent event) {
-        // Hide the nav bar scrims once we drop to a dock region
-        if (event.dropTarget instanceof DockState) {
-            animateScrimToCurrentNavBarState(false /* hasStackTasks */);
-        }
-    }
-
-    public final void onBusEvent(final DragEndCancelledEvent event) {
-        // Restore the scrims to the normal state
-        animateScrimToCurrentNavBarState(event.stack.getTaskCount() > 0);
-    }
-
-    /**
-     * Animates the scrim to match the state of the current nav bar.
-     */
-    private void animateScrimToCurrentNavBarState(boolean hasStackTasks) {
-        boolean hasNavBarScrim = isNavBarScrimRequired(hasStackTasks);
-        if (mHasNavBarScrim != hasNavBarScrim) {
-            AnimationProps animation = hasNavBarScrim
-                    ? createBoundsAnimation(DEFAULT_ANIMATION_DURATION)
-                    : AnimationProps.IMMEDIATE;
-            animateNavBarScrimVisibility(hasNavBarScrim, animation);
-        }
-        mHasNavBarScrim = hasNavBarScrim;
-    }
-
-    /**
-     * @return a default animation to aniamte the bounds of the scrim.
-     */
-    private AnimationProps createBoundsAnimation(int duration) {
-        return new AnimationProps()
-                .setDuration(AnimationProps.BOUNDS, duration)
-                .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
deleted file mode 100644
index 67d0978..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ /dev/null
@@ -1,705 +0,0 @@
-/*
- * Copyright (C) 2015 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.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.util.Log;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A helper class to create task view animations for {@link TaskView}s in a {@link TaskStackView},
- * but not the contents of the {@link TaskView}s.
- */
-public class TaskStackAnimationHelper {
-
-    /**
-     * Callbacks from the helper to coordinate view-content animations with view animations.
-     */
-    public interface Callbacks {
-        /**
-         * Callback to prepare for the start animation for the launch target {@link TaskView}.
-         */
-        void onPrepareLaunchTargetForEnterAnimation();
-
-        /**
-         * Callback to start the animation for the launch target {@link TaskView}.
-         */
-        void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
-                boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger);
-
-        /**
-         * Callback to start the animation for the launch target {@link TaskView} when it is
-         * launched from Recents.
-         */
-        void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
-                ReferenceCountedTrigger postAnimationTrigger);
-
-        /**
-         * Callback to start the animation for the front {@link TaskView} if there is no launch
-         * target.
-         */
-        void onStartFrontTaskEnterAnimation(boolean screenPinningEnabled);
-    }
-
-    private static final int DOUBLE_FRAME_OFFSET_MS = 33;
-    private static final int FRAME_OFFSET_MS = 16;
-
-    private static final int ENTER_EXIT_NUM_ANIMATING_TASKS = 5;
-
-    private static final int ENTER_FROM_HOME_ALPHA_DURATION = 100;
-    public static final int ENTER_FROM_HOME_TRANSLATION_DURATION = 300;
-    private static final Interpolator ENTER_FROM_HOME_ALPHA_INTERPOLATOR = Interpolators.LINEAR;
-
-    public static final int EXIT_TO_HOME_TRANSLATION_DURATION = 200;
-    private static final Interpolator EXIT_TO_HOME_TRANSLATION_INTERPOLATOR =
-            new PathInterpolator(0.4f, 0, 0.6f, 1f);
-
-    private static final int DISMISS_TASK_DURATION = 175;
-    private static final int DISMISS_ALL_TASKS_DURATION = 200;
-    private static final Interpolator DISMISS_ALL_TRANSLATION_INTERPOLATOR =
-            new PathInterpolator(0.4f, 0, 1f, 1f);
-
-    private static final Interpolator FOCUS_NEXT_TASK_INTERPOLATOR =
-            new PathInterpolator(0.4f, 0, 0, 1f);
-    private static final Interpolator FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR =
-            new PathInterpolator(0, 0, 0, 1f);
-    private static final Interpolator FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR =
-            Interpolators.LINEAR_OUT_SLOW_IN;
-
-    private static final Interpolator ENTER_WHILE_DOCKING_INTERPOLATOR =
-            Interpolators.LINEAR_OUT_SLOW_IN;
-
-    private final int mEnterAndExitFromHomeTranslationOffset;
-    private TaskStackView mStackView;
-
-    private TaskViewTransform mTmpTransform = new TaskViewTransform();
-    private ArrayList<TaskViewTransform> mTmpCurrentTaskTransforms = new ArrayList<>();
-    private ArrayList<TaskViewTransform> mTmpFinalTaskTransforms = new ArrayList<>();
-
-    public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
-        mStackView = stackView;
-        mEnterAndExitFromHomeTranslationOffset = Recents.getConfiguration().isGridEnabled
-                ? 0 : DOUBLE_FRAME_OFFSET_MS;
-    }
-
-    /**
-     * Prepares the stack views and puts them in their initial animation state while visible, before
-     * the in-app enter animations start (after the window-transition completes).
-     */
-    public void prepareForEnterAnimation() {
-        RecentsConfiguration config = Recents.getConfiguration();
-        RecentsActivityLaunchState launchState = config.getLaunchState();
-        Resources res = mStackView.getResources();
-        Resources appResources = mStackView.getContext().getApplicationContext().getResources();
-
-        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
-        TaskStackViewScroller stackScroller = mStackView.getScroller();
-        TaskStack stack = mStackView.getStack();
-        Task launchTargetTask = stack.getLaunchTarget();
-
-        // Break early if there are no tasks
-        if (stack.getTaskCount() == 0) {
-            return;
-        }
-
-        int offscreenYOffset = stackLayout.mStackRect.height();
-        int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
-                R.dimen.recents_task_stack_animation_affiliate_enter_offset);
-        int launchedWhileDockingOffset = res.getDimensionPixelSize(
-                R.dimen.recents_task_stack_animation_launched_while_docking_offset);
-        boolean isLandscape = appResources.getConfiguration().orientation
-                == Configuration.ORIENTATION_LANDSCAPE;
-
-        float top = 0;
-        final boolean isLowRamDevice = Recents.getConfiguration().isLowRamDevice;
-        if (isLowRamDevice && launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
-            stackLayout.getStackTransform(launchTargetTask, stackScroller.getStackScroll(),
-                    mTmpTransform, null /* frontTransform */);
-            top = mTmpTransform.rect.top;
-        }
-
-        // Prepare each of the task views for their enter animation from front to back
-        List<TaskView> taskViews = mStackView.getTaskViews();
-        for (int i = taskViews.size() - 1; i >= 0; i--) {
-            TaskView tv = taskViews.get(i);
-            Task task = tv.getTask();
-
-            // Get the current transform for the task, which will be used to position it offscreen
-            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
-                    null);
-
-            if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
-                if (task.isLaunchTarget) {
-                    tv.onPrepareLaunchTargetForEnterAnimation();
-                } else if (isLowRamDevice && i >= taskViews.size() -
-                            (TaskStackLowRamLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT + 1)
-                        && !RecentsDebugFlags.Static.DisableRecentsLowRamEnterExitAnimation) {
-                    // Move the last 2nd and 3rd last tasks in-app animation to match the motion of
-                    // the last task's app transition
-                    stackLayout.getStackTransform(task, stackScroller.getStackScroll(),
-                            mTmpTransform, null);
-                    mTmpTransform.rect.offset(0, -top);
-                    mTmpTransform.alpha = 0f;
-                    mStackView.updateTaskViewToTransform(tv, mTmpTransform,
-                            AnimationProps.IMMEDIATE);
-                    stackLayout.getStackTransform(task, stackScroller.getStackScroll(),
-                            mTmpTransform, null);
-                    mTmpTransform.alpha = 1f;
-                    // Duration see {@link
-                    // com.android.server.wm.AppTransition#DEFAULT_APP_TRANSITION_DURATION}
-                    mStackView.updateTaskViewToTransform(tv, mTmpTransform,
-                            new AnimationProps(336, Interpolators.FAST_OUT_SLOW_IN));
-                }
-            } else if (launchState.launchedFromHome) {
-                if (isLowRamDevice) {
-                    mTmpTransform.rect.offset(0, stackLayout.getTaskRect().height() / 4);
-                } else {
-                    // Move the task view off screen (below) so we can animate it in
-                    mTmpTransform.rect.offset(0, offscreenYOffset);
-                }
-                mTmpTransform.alpha = 0f;
-                mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
-            } else if (launchState.launchedViaDockGesture) {
-                int offset = isLandscape
-                        ? launchedWhileDockingOffset
-                        : (int) (offscreenYOffset * 0.9f);
-                mTmpTransform.rect.offset(0, offset);
-                mTmpTransform.alpha = 0f;
-                mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
-            }
-        }
-    }
-
-    /**
-     * Starts the in-app enter animation, which animates the {@link TaskView}s to their final places
-     * depending on how Recents was triggered.
-     */
-    public void startEnterAnimation(final ReferenceCountedTrigger postAnimationTrigger) {
-        RecentsConfiguration config = Recents.getConfiguration();
-        RecentsActivityLaunchState launchState = config.getLaunchState();
-        Resources res = mStackView.getResources();
-        Resources appRes = mStackView.getContext().getApplicationContext().getResources();
-
-        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
-        TaskStackViewScroller stackScroller = mStackView.getScroller();
-        TaskStack stack = mStackView.getStack();
-        Task launchTargetTask = stack.getLaunchTarget();
-
-        // Break early if there are no tasks
-        if (stack.getTaskCount() == 0) {
-            return;
-        }
-
-        final boolean isLowRamDevice = Recents.getConfiguration().isLowRamDevice;
-        int taskViewEnterFromAppDuration = res.getInteger(
-                R.integer.recents_task_enter_from_app_duration);
-        int taskViewEnterFromAffiliatedAppDuration = res.getInteger(
-                R.integer.recents_task_enter_from_affiliated_app_duration);
-        int dockGestureAnimDuration = appRes.getInteger(
-                R.integer.long_press_dock_anim_duration);
-
-        // Since low ram devices have an animation when entering app -> recents, do not allow
-        // toggle until the animation is complete
-        if (launchState.launchedFromApp && !launchState.launchedViaDockGesture && isLowRamDevice) {
-            postAnimationTrigger.addLastDecrementRunnable(() -> EventBus.getDefault()
-                .send(new SetWaitingForTransitionStartEvent(false)));
-        }
-
-        // Create enter animations for each of the views from front to back
-        List<TaskView> taskViews = mStackView.getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = taskViewCount - 1; i >= 0; i--) {
-            int taskIndexFromFront = taskViewCount - i - 1;
-            int taskIndexFromBack = i;
-            final TaskView tv = taskViews.get(i);
-            Task task = tv.getTask();
-
-            // Get the current transform for the task, which will be updated to the final transform
-            // to animate to depending on how recents was invoked
-            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
-                    null);
-
-            if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
-                if (task.isLaunchTarget) {
-                    tv.onStartLaunchTargetEnterAnimation(mTmpTransform,
-                            taskViewEnterFromAppDuration, mStackView.mScreenPinningEnabled,
-                            postAnimationTrigger);
-                }
-
-            } else if (launchState.launchedFromHome) {
-                // Animate the tasks up, but offset the animations to be relative to the front-most
-                // task animation
-                final float startOffsetFraction = (float) (Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS,
-                        taskIndexFromFront) * mEnterAndExitFromHomeTranslationOffset) /
-                        ENTER_FROM_HOME_TRANSLATION_DURATION;
-                AnimationProps taskAnimation = new AnimationProps()
-                        .setInterpolator(AnimationProps.ALPHA, ENTER_FROM_HOME_ALPHA_INTERPOLATOR)
-                        .setListener(postAnimationTrigger.decrementOnAnimationEnd());
-                if (isLowRamDevice) {
-                    taskAnimation.setInterpolator(AnimationProps.BOUNDS,
-                            Interpolators.FAST_OUT_SLOW_IN)
-                            .setDuration(AnimationProps.BOUNDS, 150)
-                            .setDuration(AnimationProps.ALPHA, 150);
-                } else {
-                    taskAnimation.setStartDelay(AnimationProps.ALPHA,
-                                Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
-                                        FRAME_OFFSET_MS)
-                            .setInterpolator(AnimationProps.BOUNDS,
-                                new RecentsEntrancePathInterpolator(0f, 0f, 0.2f, 1f,
-                                        startOffsetFraction))
-                            .setDuration(AnimationProps.BOUNDS, ENTER_FROM_HOME_TRANSLATION_DURATION)
-                            .setDuration(AnimationProps.ALPHA, ENTER_FROM_HOME_ALPHA_DURATION);
-                }
-                postAnimationTrigger.increment();
-                mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
-                if (i == taskViewCount - 1) {
-                    tv.onStartFrontTaskEnterAnimation(mStackView.mScreenPinningEnabled);
-                }
-            } else if (launchState.launchedViaDockGesture) {
-                // Animate the tasks up - add some delay to match the divider animation
-                AnimationProps taskAnimation = new AnimationProps()
-                        .setDuration(AnimationProps.BOUNDS, dockGestureAnimDuration +
-                                (taskIndexFromBack * DOUBLE_FRAME_OFFSET_MS))
-                        .setInterpolator(AnimationProps.BOUNDS,
-                                ENTER_WHILE_DOCKING_INTERPOLATOR)
-                        .setStartDelay(AnimationProps.BOUNDS, 48)
-                        .setListener(postAnimationTrigger.decrementOnAnimationEnd());
-                postAnimationTrigger.increment();
-                mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
-            }
-        }
-    }
-
-    /**
-     * Starts an in-app animation to hide all the task views so that we can transition back home.
-     */
-    public void startExitToHomeAnimation(boolean animated,
-            ReferenceCountedTrigger postAnimationTrigger) {
-        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
-        TaskStack stack = mStackView.getStack();
-
-        // Break early if there are no tasks
-        if (stack.getTaskCount() == 0) {
-            return;
-        }
-
-        int offscreenYOffset = stackLayout.mStackRect.height();
-
-        // Create the animations for each of the tasks
-        List<TaskView> taskViews = mStackView.getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            int taskIndexFromFront = taskViewCount - i - 1;
-            TaskView tv = taskViews.get(i);
-            Task task = tv.getTask();
-
-            if (mStackView.isIgnoredTask(task)) {
-                continue;
-            }
-
-            // Animate the tasks down
-            AnimationProps taskAnimation;
-            if (animated) {
-                int delay = Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS , taskIndexFromFront) *
-                        mEnterAndExitFromHomeTranslationOffset;
-                taskAnimation = new AnimationProps()
-                        .setDuration(AnimationProps.BOUNDS, EXIT_TO_HOME_TRANSLATION_DURATION)
-                        .setListener(postAnimationTrigger.decrementOnAnimationEnd());
-                if (Recents.getConfiguration().isLowRamDevice) {
-                    taskAnimation.setInterpolator(AnimationProps.BOUNDS,
-                            Interpolators.FAST_OUT_SLOW_IN);
-                } else {
-                    taskAnimation.setStartDelay(AnimationProps.BOUNDS, delay)
-                            .setInterpolator(AnimationProps.BOUNDS,
-                                    EXIT_TO_HOME_TRANSLATION_INTERPOLATOR);
-                }
-                postAnimationTrigger.increment();
-            } else {
-                taskAnimation = AnimationProps.IMMEDIATE;
-            }
-
-            mTmpTransform.fillIn(tv);
-            if (Recents.getConfiguration().isLowRamDevice) {
-                taskAnimation.setInterpolator(AnimationProps.ALPHA,
-                                EXIT_TO_HOME_TRANSLATION_INTERPOLATOR)
-                        .setDuration(AnimationProps.ALPHA, EXIT_TO_HOME_TRANSLATION_DURATION);
-                mTmpTransform.rect.offset(0, stackLayout.mTaskStackLowRamLayoutAlgorithm
-                        .getTaskRect().height() / 4);
-                mTmpTransform.alpha = 0f;
-            } else {
-                mTmpTransform.rect.offset(0, offscreenYOffset);
-            }
-            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
-        }
-    }
-
-    /**
-     * Starts the animation for the launching task view, hiding any tasks that might occlude the
-     * window transition for the launching task.
-     */
-    public void startLaunchTaskAnimation(TaskView launchingTaskView, boolean screenPinningRequested,
-            final ReferenceCountedTrigger postAnimationTrigger) {
-        Resources res = mStackView.getResources();
-
-        int taskViewExitToAppDuration = res.getInteger(
-                R.integer.recents_task_exit_to_app_duration);
-        int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
-                R.dimen.recents_task_stack_animation_affiliate_enter_offset);
-
-        Task launchingTask = launchingTaskView.getTask();
-        List<TaskView> taskViews = mStackView.getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            TaskView tv = taskViews.get(i);
-            Task task = tv.getTask();
-
-            if (tv == launchingTaskView) {
-                tv.setClipViewInStack(false);
-                postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
-                    @Override
-                    public void run() {
-                        tv.setClipViewInStack(true);
-                    }
-                });
-                tv.onStartLaunchTargetLaunchAnimation(taskViewExitToAppDuration,
-                        screenPinningRequested, postAnimationTrigger);
-            }
-        }
-    }
-
-    /**
-     * Starts the delete animation for the specified {@link TaskView}.
-     */
-    public void startDeleteTaskAnimation(final TaskView deleteTaskView, boolean gridLayout,
-            final ReferenceCountedTrigger postAnimationTrigger) {
-        if (gridLayout) {
-            startTaskGridDeleteTaskAnimation(deleteTaskView, postAnimationTrigger);
-        } else {
-            startTaskStackDeleteTaskAnimation(deleteTaskView, postAnimationTrigger);
-        }
-    }
-
-    /**
-     * Starts the delete animation for all the {@link TaskView}s.
-     */
-    public void startDeleteAllTasksAnimation(final List<TaskView> taskViews, boolean gridLayout,
-            final ReferenceCountedTrigger postAnimationTrigger) {
-        if (gridLayout) {
-            for (int i = 0; i < taskViews.size(); i++) {
-                startTaskGridDeleteTaskAnimation(taskViews.get(i), postAnimationTrigger);
-            }
-        } else {
-            startTaskStackDeleteAllTasksAnimation(taskViews, postAnimationTrigger);
-        }
-    }
-
-    /**
-     * Starts the animation to focus the next {@link TaskView} when paging through recents.
-     *
-     * @return whether or not this will trigger a scroll in the stack
-     */
-    public boolean startScrollToFocusedTaskAnimation(Task newFocusedTask,
-            boolean requestViewFocus) {
-        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
-        TaskStackViewScroller stackScroller = mStackView.getScroller();
-        TaskStack stack = mStackView.getStack();
-
-        final float curScroll = stackScroller.getStackScroll();
-        final float newScroll = stackScroller.getBoundedStackScroll(
-                stackLayout.getStackScrollForTask(newFocusedTask));
-        boolean willScrollToFront = newScroll > curScroll;
-        boolean willScroll = Float.compare(newScroll, curScroll) != 0;
-
-        // Get the current set of task transforms
-        int taskViewCount = mStackView.getTaskViews().size();
-        ArrayList<Task> stackTasks = stack.getTasks();
-        mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
-
-        // Pick up the newly visible views after the scroll
-        mStackView.bindVisibleTaskViews(newScroll);
-
-        // Update the internal state
-        stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
-        stackScroller.setStackScroll(newScroll, null /* animation */);
-        mStackView.cancelDeferredTaskViewLayoutAnimation();
-
-        // Get the final set of task transforms
-        mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
-                true /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
-
-        // Focus the task view
-        TaskView newFocusedTaskView = mStackView.getChildViewForTask(newFocusedTask);
-        if (newFocusedTaskView == null) {
-            // Log the error if we have no task view, and skip the animation
-            Log.e("TaskStackAnimationHelper", "b/27389156 null-task-view prebind:" + taskViewCount +
-                    " postbind:" + mStackView.getTaskViews().size() + " prescroll:" + curScroll +
-                    " postscroll: " + newScroll);
-            return false;
-        }
-        newFocusedTaskView.setFocusedState(true, requestViewFocus);
-
-        // Setup the end listener to return all the hidden views to the view pool after the
-        // focus animation
-        ReferenceCountedTrigger postAnimTrigger = new ReferenceCountedTrigger();
-        postAnimTrigger.addLastDecrementRunnable(new Runnable() {
-            @Override
-            public void run() {
-                mStackView.bindVisibleTaskViews(newScroll);
-            }
-        });
-
-        List<TaskView> taskViews = mStackView.getTaskViews();
-        taskViewCount = taskViews.size();
-        int newFocusTaskViewIndex = taskViews.indexOf(newFocusedTaskView);
-        for (int i = 0; i < taskViewCount; i++) {
-            TaskView tv = taskViews.get(i);
-            Task task = tv.getTask();
-
-            if (mStackView.isIgnoredTask(task)) {
-                continue;
-            }
-
-            int taskIndex = stackTasks.indexOf(task);
-            TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
-            TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
-
-            // Update the task to the initial state (for the newly picked up tasks)
-            mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
-
-            int duration;
-            Interpolator interpolator;
-            if (willScrollToFront) {
-                duration = calculateStaggeredAnimDuration(i);
-                interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
-            } else {
-                if (i < newFocusTaskViewIndex) {
-                    duration = 150 + ((newFocusTaskViewIndex - i - 1) * 50);
-                    interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
-                } else if (i > newFocusTaskViewIndex) {
-                    duration = Math.max(100, 150 - ((i - newFocusTaskViewIndex - 1) * 50));
-                    interpolator = FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR;
-                } else {
-                    duration = 200;
-                    interpolator = FOCUS_NEXT_TASK_INTERPOLATOR;
-                }
-            }
-
-            AnimationProps anim = new AnimationProps()
-                    .setDuration(AnimationProps.BOUNDS, duration)
-                    .setInterpolator(AnimationProps.BOUNDS, interpolator)
-                    .setListener(postAnimTrigger.decrementOnAnimationEnd());
-            postAnimTrigger.increment();
-            mStackView.updateTaskViewToTransform(tv, toTransform, anim);
-        }
-        return willScroll;
-    }
-
-    /**
-     * Starts the animation to go to the initial stack layout with a task focused.  In addition, the
-     * previous task will be animated in after the scroll completes.
-     */
-    public void startNewStackScrollAnimation(TaskStack newStack,
-            ReferenceCountedTrigger animationTrigger) {
-        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
-        TaskStackViewScroller stackScroller = mStackView.getScroller();
-
-        // Get the current set of task transforms
-        ArrayList<Task> stackTasks = newStack.getTasks();
-        mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
-
-        // Update the stack
-        mStackView.setTasks(newStack, false /* allowNotifyStackChanges */);
-        mStackView.updateLayoutAlgorithm(false /* boundScroll */);
-
-        // Pick up the newly visible views after the scroll
-        final float newScroll = stackLayout.mInitialScrollP;
-        mStackView.bindVisibleTaskViews(newScroll);
-
-        // Update the internal state
-        stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
-        stackLayout.setTaskOverridesForInitialState(newStack, true /* ignoreScrollToFront */);
-        stackScroller.setStackScroll(newScroll);
-        mStackView.cancelDeferredTaskViewLayoutAnimation();
-
-        // Get the final set of task transforms
-        mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
-                false /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
-
-        // Hide the front most task view until the scroll is complete
-        Task frontMostTask = newStack.getFrontMostTask();
-        final TaskView frontMostTaskView = mStackView.getChildViewForTask(frontMostTask);
-        final TaskViewTransform frontMostTransform = mTmpFinalTaskTransforms.get(
-                stackTasks.indexOf(frontMostTask));
-        if (frontMostTaskView != null) {
-            mStackView.updateTaskViewToTransform(frontMostTaskView,
-                    stackLayout.getFrontOfStackTransform(), AnimationProps.IMMEDIATE);
-        }
-
-        // Setup the end listener to return all the hidden views to the view pool after the
-        // focus animation
-        animationTrigger.addLastDecrementRunnable(new Runnable() {
-            @Override
-            public void run() {
-                mStackView.bindVisibleTaskViews(newScroll);
-
-                // Now, animate in the front-most task
-                if (frontMostTaskView != null) {
-                    mStackView.updateTaskViewToTransform(frontMostTaskView, frontMostTransform,
-                            new AnimationProps(75, 250, FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR));
-                }
-            }
-        });
-
-        List<TaskView> taskViews = mStackView.getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            TaskView tv = taskViews.get(i);
-            Task task = tv.getTask();
-
-            if (mStackView.isIgnoredTask(task)) {
-                continue;
-            }
-            if (task == frontMostTask && frontMostTaskView != null) {
-                continue;
-            }
-
-            int taskIndex = stackTasks.indexOf(task);
-            TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
-            TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
-
-            // Update the task to the initial state (for the newly picked up tasks)
-            mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
-
-            int duration = calculateStaggeredAnimDuration(i);
-            Interpolator interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
-
-            AnimationProps anim = new AnimationProps()
-                    .setDuration(AnimationProps.BOUNDS, duration)
-                    .setInterpolator(AnimationProps.BOUNDS, interpolator)
-                    .setListener(animationTrigger.decrementOnAnimationEnd());
-            animationTrigger.increment();
-            mStackView.updateTaskViewToTransform(tv, toTransform, anim);
-        }
-    }
-
-    /**
-     * Caclulates a staggered duration for {@link #startScrollToFocusedTaskAnimation} and
-     * {@link #startNewStackScrollAnimation}.
-     */
-    private int calculateStaggeredAnimDuration(int i) {
-        return Math.max(100, 100 + ((i - 1) * 50));
-    }
-
-    private void startTaskGridDeleteTaskAnimation(final TaskView deleteTaskView,
-            final ReferenceCountedTrigger postAnimationTrigger) {
-        postAnimationTrigger.increment();
-        postAnimationTrigger.addLastDecrementRunnable(() -> {
-            mStackView.getTouchHandler().onChildDismissed(deleteTaskView);
-        });
-        deleteTaskView.animate().setDuration(300).scaleX(0.9f).scaleY(0.9f).alpha(0).setListener(
-                new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        postAnimationTrigger.decrement();
-                    }}).start();
-    }
-
-    private void startTaskStackDeleteTaskAnimation(final TaskView deleteTaskView,
-            final ReferenceCountedTrigger postAnimationTrigger) {
-        TaskStackViewTouchHandler touchHandler = mStackView.getTouchHandler();
-        touchHandler.onBeginManualDrag(deleteTaskView);
-
-        postAnimationTrigger.increment();
-        postAnimationTrigger.addLastDecrementRunnable(() -> {
-            touchHandler.onChildDismissed(deleteTaskView);
-        });
-
-        final float dismissSize = touchHandler.getScaledDismissSize();
-        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
-        animator.setDuration(400);
-        animator.addUpdateListener((animation) -> {
-            float progress = (Float) animation.getAnimatedValue();
-            deleteTaskView.setTranslationX(progress * dismissSize);
-            touchHandler.updateSwipeProgress(deleteTaskView, true, progress);
-        });
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                postAnimationTrigger.decrement();
-            }
-        });
-        animator.start();
-    }
-
-    private void startTaskStackDeleteAllTasksAnimation(final List<TaskView> taskViews,
-            final ReferenceCountedTrigger postAnimationTrigger) {
-        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
-
-        int offscreenXOffset = mStackView.getMeasuredWidth() - stackLayout.getTaskRect().left;
-
-        int taskViewCount = taskViews.size();
-        for (int i = taskViewCount - 1; i >= 0; i--) {
-            TaskView tv = taskViews.get(i);
-            int taskIndexFromFront = taskViewCount - i - 1;
-            int startDelay = taskIndexFromFront * DOUBLE_FRAME_OFFSET_MS;
-
-            // Disabling clipping with the stack while the view is animating away
-            tv.setClipViewInStack(false);
-
-            // Compose the new animation and transform and star the animation
-            AnimationProps taskAnimation = new AnimationProps(startDelay,
-                    DISMISS_ALL_TASKS_DURATION, DISMISS_ALL_TRANSLATION_INTERPOLATOR,
-                    new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            postAnimationTrigger.decrement();
-
-                            // Re-enable clipping with the stack (we will reuse this view)
-                            tv.setClipViewInStack(true);
-                        }
-                    });
-            postAnimationTrigger.increment();
-
-            mTmpTransform.fillIn(tv);
-            mTmpTransform.rect.offset(offscreenXOffset, 0);
-            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
deleted file mode 100644
index d9f79bb..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ /dev/null
@@ -1,1280 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import android.annotation.IntDef;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.view.ViewDebug;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
-import com.android.systemui.recents.misc.FreePathInterpolator;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
-import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
-
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Used to describe a visible range that can be normalized to [0, 1].
- */
-class Range {
-    final float relativeMin;
-    final float relativeMax;
-    float origin;
-    float min;
-    float max;
-
-    public Range(float relMin, float relMax) {
-        min = relativeMin = relMin;
-        max = relativeMax = relMax;
-    }
-
-    /**
-     * Offsets this range to a given absolute position.
-     */
-    public void offset(float x) {
-        this.origin = x;
-        min = x + relativeMin;
-        max = x + relativeMax;
-    }
-
-    /**
-     * Returns x normalized to the range 0 to 1 such that 0 = min, 0.5 = origin and 1 = max
-     *
-     * @param x is an absolute value in the same domain as origin
-     */
-    public float getNormalizedX(float x) {
-        if (x < origin) {
-            return 0.5f + 0.5f * (x - origin) / -relativeMin;
-        } else {
-            return 0.5f + 0.5f * (x - origin) / relativeMax;
-        }
-    }
-
-    /**
-     * Given a normalized {@param x} value in this range, projected onto the full range to get an
-     * absolute value about the given {@param origin}.
-     */
-    public float getAbsoluteX(float normX) {
-        if (normX < 0.5f) {
-            return (normX - 0.5f) / 0.5f * -relativeMin;
-        } else {
-            return (normX - 0.5f) / 0.5f * relativeMax;
-        }
-    }
-
-    /**
-     * Returns whether a value at an absolute x would be within range.
-     */
-    public boolean isInRange(float absX) {
-        return (absX >= Math.floor(min)) && (absX <= Math.ceil(max));
-    }
-}
-
-/**
- * The layout logic for a TaskStackView.  This layout needs to be able to calculate the stack layout
- * without an activity-specific context only with the information passed in.  This layout can have
- * two states focused and unfocused, and in the focused state, there is a task that is displayed
- * more prominently in the stack.
- */
-public class TaskStackLayoutAlgorithm {
-
-    private static final String TAG = "TaskStackLayoutAlgorithm";
-
-    // The distribution of view bounds alpha
-    // XXX: This is a hack because you can currently set the max alpha to be > 1f
-    public static final float OUTLINE_ALPHA_MIN_VALUE = 0f;
-    public static final float OUTLINE_ALPHA_MAX_VALUE = 2f;
-
-    // The medium/maximum dim on the tasks
-    private static final float MED_DIM = 0.15f;
-    private static final float MAX_DIM = 0.25f;
-
-    // The various focus states
-    public static final int STATE_FOCUSED = 1;
-    public static final int STATE_UNFOCUSED = 0;
-
-    // The side that an offset is anchored
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({FROM_TOP, FROM_BOTTOM})
-    public @interface AnchorSide {}
-    private static final int FROM_TOP = 0;
-    private static final int FROM_BOTTOM = 1;
-
-    // The extent that we care about when calculating fractions
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({WIDTH, HEIGHT})
-    public @interface Extent {}
-    private static final int WIDTH = 0;
-    private static final int HEIGHT = 1;
-
-    public interface TaskStackLayoutAlgorithmCallbacks {
-        void onFocusStateChanged(int prevFocusState, int curFocusState);
-    }
-
-    /**
-     * @return True if we should use the grid layout.
-     */
-    boolean useGridLayout() {
-        return Recents.getConfiguration().isGridEnabled;
-    }
-
-    // A report of the visibility state of the stack
-    public static class VisibilityReport {
-        public int numVisibleTasks;
-        public int numVisibleThumbnails;
-
-        public VisibilityReport(int tasks, int thumbnails) {
-            numVisibleTasks = tasks;
-            numVisibleThumbnails = thumbnails;
-        }
-    }
-
-    Context mContext;
-    private TaskStackLayoutAlgorithmCallbacks mCb;
-
-    // The task bounds (untransformed) for layout.  This rect is anchored at mTaskRoot.
-    @ViewDebug.ExportedProperty(category="recents")
-    public Rect mTaskRect = new Rect();
-    // The stack bounds, inset from the top system insets, and runs to the bottom of the screen
-    @ViewDebug.ExportedProperty(category="recents")
-    public Rect mStackRect = new Rect();
-    // This is the current system insets
-    @ViewDebug.ExportedProperty(category="recents")
-    public Rect mSystemInsets = new Rect();
-
-    // The visible ranges when the stack is focused and unfocused
-    private Range mUnfocusedRange;
-    private Range mFocusedRange;
-
-    // This is the bounds of the stack action above the stack rect
-    @ViewDebug.ExportedProperty(category="recents")
-    private Rect mStackActionButtonRect = new Rect();
-    // The base top margin for the stack from the system insets
-    @ViewDebug.ExportedProperty(category="recents")
-    private int mBaseTopMargin;
-    // The base side margin for the stack from the system insets
-    @ViewDebug.ExportedProperty(category="recents")
-    private int mBaseSideMargin;
-    // The base bottom margin for the stack from the system insets
-    @ViewDebug.ExportedProperty(category="recents")
-    private int mBaseBottomMargin;
-    private int mMinMargin;
-
-    // The initial offset that the focused task is from the top
-    @ViewDebug.ExportedProperty(category="recents")
-    private int mInitialTopOffset;
-    private int mBaseInitialTopOffset;
-    // The initial offset that the launch-from task is from the bottom
-    @ViewDebug.ExportedProperty(category="recents")
-    private int mInitialBottomOffset;
-    private int mBaseInitialBottomOffset;
-
-    // The height between the top margin and the top of the focused task
-    @ViewDebug.ExportedProperty(category="recents")
-    private int mFocusedTopPeekHeight;
-    // The height between the bottom margin and the top of task in front of the focused task
-    @ViewDebug.ExportedProperty(category="recents")
-    private int mFocusedBottomPeekHeight;
-
-    // The offset from the bottom of the stack to the bottom of the bounds when the stack is
-    // scrolled to the front
-    @ViewDebug.ExportedProperty(category="recents")
-    private int mStackBottomOffset;
-
-    /** The height, in pixels, of each task view's title bar. */
-    private int mTitleBarHeight;
-
-    // The paths defining the motion of the tasks when the stack is focused and unfocused
-    private Path mUnfocusedCurve;
-    private Path mFocusedCurve;
-    private FreePathInterpolator mUnfocusedCurveInterpolator;
-    private FreePathInterpolator mFocusedCurveInterpolator;
-
-    // The paths defining the distribution of the dim to apply to tasks in the stack when focused
-    // and unfocused
-    private Path mUnfocusedDimCurve;
-    private Path mFocusedDimCurve;
-    private FreePathInterpolator mUnfocusedDimCurveInterpolator;
-    private FreePathInterpolator mFocusedDimCurveInterpolator;
-
-    // The state of the stack focus (0..1), which controls the transition of the stack from the
-    // focused to non-focused state
-    @ViewDebug.ExportedProperty(category="recents")
-    private int mFocusState;
-
-    // The smallest scroll progress, at this value, the back most task will be visible
-    @ViewDebug.ExportedProperty(category="recents")
-    float mMinScrollP;
-    // The largest scroll progress, at this value, the front most task will be visible above the
-    // navigation bar
-    @ViewDebug.ExportedProperty(category="recents")
-    float mMaxScrollP;
-    // The initial progress that the scroller is set when you first enter recents
-    @ViewDebug.ExportedProperty(category="recents")
-    float mInitialScrollP;
-    // The task progress for the front-most task in the stack
-    @ViewDebug.ExportedProperty(category="recents")
-    float mFrontMostTaskP;
-
-    // The last computed task counts
-    @ViewDebug.ExportedProperty(category="recents")
-    int mNumStackTasks;
-
-    // The min/max z translations
-    @ViewDebug.ExportedProperty(category="recents")
-    int mMinTranslationZ;
-    @ViewDebug.ExportedProperty(category="recents")
-    public int mMaxTranslationZ;
-
-    // Optimization, allows for quick lookup of task -> index
-    private SparseIntArray mTaskIndexMap = new SparseIntArray();
-    private SparseArray<Float> mTaskIndexOverrideMap = new SparseArray<>();
-
-    TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
-    TaskStackLowRamLayoutAlgorithm mTaskStackLowRamLayoutAlgorithm;
-
-    // The transform to place TaskViews at the front and back of the stack respectively
-    TaskViewTransform mBackOfStackTransform = new TaskViewTransform();
-    TaskViewTransform mFrontOfStackTransform = new TaskViewTransform();
-
-    public TaskStackLayoutAlgorithm(Context context, TaskStackLayoutAlgorithmCallbacks cb) {
-        mContext = context;
-        mCb = cb;
-        mTaskGridLayoutAlgorithm = new TaskGridLayoutAlgorithm(context);
-        mTaskStackLowRamLayoutAlgorithm = new TaskStackLowRamLayoutAlgorithm(context);
-        reloadOnConfigurationChange(context);
-    }
-
-    /**
-     * Reloads the layout for the current configuration.
-     */
-    public void reloadOnConfigurationChange(Context context) {
-        Resources res = context.getResources();
-        mFocusedRange = new Range(res.getFloat(R.integer.recents_layout_focused_range_min),
-                res.getFloat(R.integer.recents_layout_focused_range_max));
-        mUnfocusedRange = new Range(res.getFloat(R.integer.recents_layout_unfocused_range_min),
-                res.getFloat(R.integer.recents_layout_unfocused_range_max));
-        mFocusState = getInitialFocusState();
-        mFocusedTopPeekHeight = res.getDimensionPixelSize(R.dimen.recents_layout_top_peek_size);
-        mFocusedBottomPeekHeight =
-                res.getDimensionPixelSize(R.dimen.recents_layout_bottom_peek_size);
-        mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_min);
-        mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_max);
-        mBaseInitialTopOffset = getDimensionForDevice(context,
-                R.dimen.recents_layout_initial_top_offset_phone_port,
-                R.dimen.recents_layout_initial_top_offset_phone_land,
-                R.dimen.recents_layout_initial_top_offset_tablet,
-                R.dimen.recents_layout_initial_top_offset_tablet,
-                R.dimen.recents_layout_initial_top_offset_tablet,
-                R.dimen.recents_layout_initial_top_offset_tablet,
-                R.dimen.recents_layout_initial_top_offset_tablet);
-        mBaseInitialBottomOffset = getDimensionForDevice(context,
-                R.dimen.recents_layout_initial_bottom_offset_phone_port,
-                R.dimen.recents_layout_initial_bottom_offset_phone_land,
-                R.dimen.recents_layout_initial_bottom_offset_tablet,
-                R.dimen.recents_layout_initial_bottom_offset_tablet,
-                R.dimen.recents_layout_initial_bottom_offset_tablet,
-                R.dimen.recents_layout_initial_bottom_offset_tablet,
-                R.dimen.recents_layout_initial_bottom_offset_tablet);
-        mTaskGridLayoutAlgorithm.reloadOnConfigurationChange(context);
-        mTaskStackLowRamLayoutAlgorithm.reloadOnConfigurationChange(context);
-        mMinMargin = res.getDimensionPixelSize(R.dimen.recents_layout_min_margin);
-        mBaseTopMargin = getDimensionForDevice(context,
-                R.dimen.recents_layout_top_margin_phone,
-                R.dimen.recents_layout_top_margin_tablet,
-                R.dimen.recents_layout_top_margin_tablet_xlarge,
-                R.dimen.recents_layout_top_margin_tablet);
-        mBaseSideMargin = getDimensionForDevice(context,
-                R.dimen.recents_layout_side_margin_phone,
-                R.dimen.recents_layout_side_margin_tablet,
-                R.dimen.recents_layout_side_margin_tablet_xlarge,
-                R.dimen.recents_layout_side_margin_tablet);
-        mBaseBottomMargin = res.getDimensionPixelSize(R.dimen.recents_layout_bottom_margin);
-        mTitleBarHeight = getDimensionForDevice(mContext,
-                R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height_tablet_land,
-                R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height_tablet_land,
-                R.dimen.recents_grid_task_view_header_height);
-    }
-
-    /**
-     * Resets this layout when the stack view is reset.
-     */
-    public void reset() {
-        mTaskIndexOverrideMap.clear();
-        setFocusState(getInitialFocusState());
-    }
-
-    /**
-     * Sets the system insets.
-     */
-    public boolean setSystemInsets(Rect systemInsets) {
-        boolean changed = !mSystemInsets.equals(systemInsets);
-        mSystemInsets.set(systemInsets);
-        mTaskGridLayoutAlgorithm.setSystemInsets(systemInsets);
-        mTaskStackLowRamLayoutAlgorithm.setSystemInsets(systemInsets);
-        return changed;
-    }
-
-    /**
-     * Sets the focused state.
-     */
-    public void setFocusState(int focusState) {
-        int prevFocusState = mFocusState;
-        mFocusState = focusState;
-        updateFrontBackTransforms();
-        if (mCb != null) {
-            mCb.onFocusStateChanged(prevFocusState, focusState);
-        }
-    }
-
-    /**
-     * Gets the focused state.
-     */
-    public int getFocusState() {
-        return mFocusState;
-    }
-
-    /**
-     * Computes the stack and task rects.  The given task stack bounds already has the top/right
-     * insets and left/right padding already applied.
-     */
-    public void initialize(Rect displayRect, Rect windowRect, Rect taskStackBounds) {
-        Rect lastStackRect = new Rect(mStackRect);
-
-        int topMargin = getScaleForExtent(windowRect, displayRect, mBaseTopMargin, mMinMargin, HEIGHT);
-        int bottomMargin = getScaleForExtent(windowRect, displayRect, mBaseBottomMargin, mMinMargin,
-                HEIGHT);
-        mInitialTopOffset = getScaleForExtent(windowRect, displayRect, mBaseInitialTopOffset,
-                mMinMargin, HEIGHT);
-        mInitialBottomOffset = mBaseInitialBottomOffset;
-
-        // Compute the stack bounds
-        mStackBottomOffset = mSystemInsets.bottom + bottomMargin;
-        mStackRect.set(taskStackBounds);
-        mStackRect.top += topMargin;
-
-        // The stack action button will take the full un-padded header space above the stack
-        mStackActionButtonRect.set(mStackRect.left, mStackRect.top - topMargin,
-                mStackRect.right, mStackRect.top + mFocusedTopPeekHeight);
-
-        // Anchor the task rect top aligned to the stack rect
-        int height = mStackRect.height() - mInitialTopOffset - mStackBottomOffset;
-        mTaskRect.set(mStackRect.left, mStackRect.top, mStackRect.right, mStackRect.top + height);
-
-        if (mTaskRect.width() <= 0 || mTaskRect.height() <= 0) {
-            // Logging for b/36654830
-            Log.e(TAG, "Invalid task rect: taskRect=" + mTaskRect + " stackRect=" + mStackRect
-                    + " displayRect=" + displayRect + " windowRect=" + windowRect
-                    + " taskStackBounds=" + taskStackBounds);
-        }
-
-        // Short circuit here if the stack rects haven't changed so we don't do all the work below
-        if (!lastStackRect.equals(mStackRect)) {
-            // Reinitialize the focused and unfocused curves
-            mUnfocusedCurve = constructUnfocusedCurve();
-            mUnfocusedCurveInterpolator = new FreePathInterpolator(mUnfocusedCurve);
-            mFocusedCurve = constructFocusedCurve();
-            mFocusedCurveInterpolator = new FreePathInterpolator(mFocusedCurve);
-            mUnfocusedDimCurve = constructUnfocusedDimCurve();
-            mUnfocusedDimCurveInterpolator = new FreePathInterpolator(mUnfocusedDimCurve);
-            mFocusedDimCurve = constructFocusedDimCurve();
-            mFocusedDimCurveInterpolator = new FreePathInterpolator(mFocusedDimCurve);
-
-            updateFrontBackTransforms();
-        }
-
-        // Initialize the grid layout
-        mTaskGridLayoutAlgorithm.initialize(windowRect);
-        mTaskStackLowRamLayoutAlgorithm.initialize(windowRect);
-    }
-
-    /**
-     * Computes the minimum and maximum scroll progress values and the progress values for each task
-     * in the stack.
-     */
-    public void update(TaskStack stack, ArraySet<Task.TaskKey> ignoreTasksSet,
-            RecentsActivityLaunchState launchState, float lastScrollPPercent) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-
-        // Clear the progress map
-        mTaskIndexMap.clear();
-
-        // Return early if we have no tasks
-        ArrayList<Task> tasks = stack.getTasks();
-        if (tasks.isEmpty()) {
-            mFrontMostTaskP = 0;
-            mMinScrollP = mMaxScrollP = mInitialScrollP = 0;
-            mNumStackTasks = 0;
-            return;
-        }
-
-        // Filter the set of stack tasks
-        ArrayList<Task> stackTasks = new ArrayList<>();
-        for (int i = 0; i < tasks.size(); i++) {
-            Task task = tasks.get(i);
-            if (ignoreTasksSet.contains(task.key)) {
-                continue;
-            }
-            stackTasks.add(task);
-        }
-        mNumStackTasks = stackTasks.size();
-
-        // Put each of the tasks in the progress map at a fixed index (does not need to actually
-        // map to a scroll position, just by index)
-        int taskCount = stackTasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            Task task = stackTasks.get(i);
-            mTaskIndexMap.put(task.key.id, i);
-        }
-
-        // Calculate the min/max/initial scroll
-        Task launchTask = stack.getLaunchTarget();
-        int launchTaskIndex = launchTask != null
-                ? stack.indexOfTask(launchTask)
-                : mNumStackTasks - 1;
-        if (getInitialFocusState() == STATE_FOCUSED) {
-            int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
-            float maxBottomNormX = getNormalizedXFromFocusedY(maxBottomOffset, FROM_BOTTOM);
-            mFocusedRange.offset(0f);
-            mMinScrollP = 0;
-            mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
-                    Math.max(0, mFocusedRange.getAbsoluteX(maxBottomNormX)));
-            if (launchState.launchedFromHome || launchState.launchedFromPipApp
-                    || launchState.launchedWithNextPipApp) {
-                mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
-            } else {
-                mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
-            }
-        } else if (mNumStackTasks == 1) {
-            // If there is one stack task, ignore the min/max/initial scroll positions
-            mMinScrollP = 0;
-            mMaxScrollP = 0;
-            mInitialScrollP = 0;
-        } else {
-            // Set the max scroll to be the point where the front most task is visible with the
-            // stack bottom offset
-            int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
-            float maxBottomNormX = getNormalizedXFromUnfocusedY(maxBottomOffset, FROM_BOTTOM);
-            mUnfocusedRange.offset(0f);
-            mMinScrollP = Recents.getConfiguration().isLowRamDevice
-                    ? mTaskStackLowRamLayoutAlgorithm.getMinScrollP()
-                    : 0;
-            mMaxScrollP = Recents.getConfiguration().isLowRamDevice
-                    ? mTaskStackLowRamLayoutAlgorithm.getMaxScrollP(taskCount)
-                    : Math.max(mMinScrollP, (mNumStackTasks - 1) -
-                    Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX)));
-            boolean scrollToFront = launchState.launchedFromHome || launchState.launchedFromPipApp
-                    || launchState.launchedWithNextPipApp || launchState.launchedViaDockGesture;
-
-            if (launchState.launchedWithAltTab) {
-                mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
-            } else if (0 <= lastScrollPPercent && lastScrollPPercent <= 1) {
-                mInitialScrollP = Utilities.mapRange(lastScrollPPercent, mMinScrollP, mMaxScrollP);
-            } else if (Recents.getConfiguration().isLowRamDevice) {
-                mInitialScrollP = mTaskStackLowRamLayoutAlgorithm.getInitialScrollP(mNumStackTasks,
-                        scrollToFront);
-            } else if (scrollToFront) {
-                mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
-            } else {
-                // We are overriding the initial two task positions, so set the initial scroll
-                // position to match the second task (aka focused task) position
-                float initialTopNormX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
-                mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP, (mNumStackTasks - 2))
-                        - Math.max(0, mUnfocusedRange.getAbsoluteX(initialTopNormX)));
-            }
-        }
-    }
-
-    /**
-     * Creates task overrides to ensure the initial stack layout if necessary.
-     */
-    public void setTaskOverridesForInitialState(TaskStack stack, boolean ignoreScrollToFront) {
-        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-
-        mTaskIndexOverrideMap.clear();
-
-        boolean scrollToFront = launchState.launchedFromHome ||
-                launchState.launchedFromPipApp ||
-                launchState.launchedWithNextPipApp ||
-                launchState.launchedViaDockGesture;
-        if (getInitialFocusState() == STATE_UNFOCUSED && mNumStackTasks > 1) {
-            if (ignoreScrollToFront || (!launchState.launchedWithAltTab && !scrollToFront)) {
-                // Set the initial scroll to the predefined state (which differs from the stack)
-                float [] initialNormX = null;
-                float minBottomTaskNormX = getNormalizedXFromUnfocusedY(mSystemInsets.bottom +
-                        mInitialBottomOffset, FROM_BOTTOM);
-                float maxBottomTaskNormX = getNormalizedXFromUnfocusedY(mFocusedTopPeekHeight +
-                        mTaskRect.height() - mMinMargin, FROM_TOP);
-                if (mNumStackTasks <= 2) {
-                    // For small stacks, position the tasks so that they are top aligned to under
-                    // the action button, but ensure that it is at least a certain offset from the
-                    // bottom of the stack
-                    initialNormX = new float[] {
-                            Math.min(maxBottomTaskNormX, minBottomTaskNormX),
-                            getNormalizedXFromUnfocusedY(mFocusedTopPeekHeight, FROM_TOP)
-                    };
-                } else {
-                    initialNormX = new float[] {
-                            minBottomTaskNormX,
-                            getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP)
-                    };
-                }
-
-                mUnfocusedRange.offset(0f);
-                List<Task> tasks = stack.getTasks();
-                int taskCount = tasks.size();
-                for (int i = taskCount - 1; i >= 0; i--) {
-                    int indexFromFront = taskCount - i - 1;
-                    if (indexFromFront >= initialNormX.length) {
-                        break;
-                    }
-                    float newTaskProgress = mInitialScrollP +
-                            mUnfocusedRange.getAbsoluteX(initialNormX[indexFromFront]);
-                    mTaskIndexOverrideMap.put(tasks.get(i).key.id, newTaskProgress);
-                }
-            }
-        }
-    }
-
-    /**
-     * Adds and override task progress for the given task when transitioning from focused to
-     * unfocused state.
-     */
-    public void addUnfocusedTaskOverride(Task task, float stackScroll) {
-        if (mFocusState != STATE_UNFOCUSED) {
-            mFocusedRange.offset(stackScroll);
-            mUnfocusedRange.offset(stackScroll);
-            float focusedRangeX = mFocusedRange.getNormalizedX(mTaskIndexMap.get(task.key.id));
-            float focusedY = mFocusedCurveInterpolator.getInterpolation(focusedRangeX);
-            float unfocusedRangeX = mUnfocusedCurveInterpolator.getX(focusedY);
-            float unfocusedTaskProgress = stackScroll + mUnfocusedRange.getAbsoluteX(unfocusedRangeX);
-            if (Float.compare(focusedRangeX, unfocusedRangeX) != 0) {
-                mTaskIndexOverrideMap.put(task.key.id, unfocusedTaskProgress);
-            }
-        }
-    }
-
-    /**
-     * Adds and override task progress for the given task when transitioning from focused to
-     * unfocused state.
-     */
-    public void addUnfocusedTaskOverride(TaskView taskView, float stackScroll) {
-        mFocusedRange.offset(stackScroll);
-        mUnfocusedRange.offset(stackScroll);
-
-        Task task = taskView.getTask();
-        int top = taskView.getTop() - mTaskRect.top;
-        float focusedRangeX = getNormalizedXFromFocusedY(top, FROM_TOP);
-        float unfocusedRangeX = getNormalizedXFromUnfocusedY(top, FROM_TOP);
-        float unfocusedTaskProgress = stackScroll + mUnfocusedRange.getAbsoluteX(unfocusedRangeX);
-        if (Float.compare(focusedRangeX, unfocusedRangeX) != 0) {
-            mTaskIndexOverrideMap.put(task.key.id, unfocusedTaskProgress);
-        }
-    }
-
-    public void clearUnfocusedTaskOverrides() {
-        mTaskIndexOverrideMap.clear();
-    }
-
-    /**
-     * Updates this stack when a scroll happens.
-     *
-     */
-    public float updateFocusStateOnScroll(float lastTargetStackScroll, float targetStackScroll,
-            float lastStackScroll) {
-        if (targetStackScroll == lastStackScroll || Recents.getConfiguration().isLowRamDevice) {
-            return targetStackScroll;
-        }
-
-        float deltaScroll = targetStackScroll - lastStackScroll;
-        float deltaTargetScroll = targetStackScroll - lastTargetStackScroll;
-        float newScroll = targetStackScroll;
-        mUnfocusedRange.offset(targetStackScroll);
-        for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
-            int taskId = mTaskIndexOverrideMap.keyAt(i);
-            float x = mTaskIndexMap.get(taskId);
-            float overrideX = mTaskIndexOverrideMap.get(taskId, 0f);
-            float newOverrideX = overrideX + deltaScroll;
-            if (isInvalidOverrideX(x, overrideX, newOverrideX)) {
-                // Remove the override once we reach the original task index
-                mTaskIndexOverrideMap.removeAt(i);
-            } else if ((overrideX >= x && deltaScroll <= 0f) ||
-                    (overrideX <= x && deltaScroll >= 0f)) {
-                // Scrolling from override x towards x, then lock the task in place
-                mTaskIndexOverrideMap.put(taskId, newOverrideX);
-            } else {
-                // Scrolling override x away from x, we should still move the scroll towards x
-                newScroll = lastStackScroll;
-                newOverrideX = overrideX - deltaTargetScroll;
-                if (isInvalidOverrideX(x, overrideX, newOverrideX)) {
-                    mTaskIndexOverrideMap.removeAt(i);
-                } else{
-                    mTaskIndexOverrideMap.put(taskId, newOverrideX);
-                }
-            }
-        }
-        return newScroll;
-    }
-
-    private boolean isInvalidOverrideX(float x, float overrideX, float newOverrideX) {
-        boolean outOfBounds = mUnfocusedRange.getNormalizedX(newOverrideX) < 0f ||
-                mUnfocusedRange.getNormalizedX(newOverrideX) > 1f;
-        return outOfBounds || (overrideX >= x && x >= newOverrideX) ||
-                (overrideX <= x && x <= newOverrideX);
-    }
-
-    /**
-     * Returns the default focus state.
-     */
-    public int getInitialFocusState() {
-        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-        RecentsDebugFlags debugFlags = Recents.getDebugFlags();
-        if (launchState.launchedWithAltTab) {
-            return STATE_FOCUSED;
-        } else {
-            return STATE_UNFOCUSED;
-        }
-    }
-
-    public Rect getStackActionButtonRect() {
-        return useGridLayout()
-                ? mTaskGridLayoutAlgorithm.getStackActionButtonRect() : mStackActionButtonRect;
-    }
-
-    /**
-     * Returns the TaskViewTransform that would put the task just off the back of the stack.
-     */
-    public TaskViewTransform getBackOfStackTransform() {
-        return mBackOfStackTransform;
-    }
-
-    /**
-     * Returns the TaskViewTransform that would put the task just off the front of the stack.
-     */
-    public TaskViewTransform getFrontOfStackTransform() {
-        return mFrontOfStackTransform;
-    }
-
-    /**
-     * Returns whether this stack layout has been initialized.
-     */
-    public boolean isInitialized() {
-        return !mStackRect.isEmpty();
-    }
-
-    /**
-     * Computes the maximum number of visible tasks and thumbnails when the scroll is at the initial
-     * stack scroll.  Requires that update() is called first.
-     */
-    public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
-        if (useGridLayout()) {
-            return mTaskGridLayoutAlgorithm.computeStackVisibilityReport(tasks);
-        }
-
-        if (Recents.getConfiguration().isLowRamDevice) {
-            return mTaskStackLowRamLayoutAlgorithm.computeStackVisibilityReport(tasks);
-        }
-
-        // Ensure minimum visibility count
-        if (tasks.size() <= 1) {
-            return new VisibilityReport(1, 1);
-        }
-
-        // Otherwise, walk backwards in the stack and count the number of tasks and visible
-        // thumbnails and add that to the total task count
-        TaskViewTransform tmpTransform = new TaskViewTransform();
-        Range currentRange = getInitialFocusState() > 0f ? mFocusedRange : mUnfocusedRange;
-        currentRange.offset(mInitialScrollP);
-        int taskBarHeight = mContext.getResources().getDimensionPixelSize(
-                R.dimen.recents_task_view_header_height);
-        int numVisibleTasks = 0;
-        int numVisibleThumbnails = 0;
-        float prevScreenY = Integer.MAX_VALUE;
-        for (int i = tasks.size() - 1; i >= 0; i--) {
-            Task task = tasks.get(i);
-
-            // Skip invisible
-            float taskProgress = getStackScrollForTask(task);
-            if (!currentRange.isInRange(taskProgress)) {
-                continue;
-            }
-
-            getStackTransform(taskProgress, taskProgress, mInitialScrollP, mFocusState,
-                    tmpTransform, null, false /* ignoreSingleTaskCase */, false /* forceUpdate */);
-            float screenY = tmpTransform.rect.top;
-            boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
-            if (hasVisibleThumbnail) {
-                numVisibleThumbnails++;
-                numVisibleTasks++;
-                prevScreenY = screenY;
-            } else {
-                // Once we hit the next front most task that does not have a visible thumbnail,
-                // walk through remaining visible set
-                for (int j = i; j >= 0; j--) {
-                    taskProgress = getStackScrollForTask(tasks.get(j));
-                    if (!currentRange.isInRange(taskProgress)) {
-                        break;
-                    }
-                    numVisibleTasks++;
-                }
-                break;
-            }
-        }
-        return new VisibilityReport(numVisibleTasks, numVisibleThumbnails);
-    }
-
-    /**
-     * Returns the transform for the given task.  This transform is relative to the mTaskRect, which
-     * is what the view is measured and laid out with.
-     */
-    public TaskViewTransform getStackTransform(Task task, float stackScroll,
-            TaskViewTransform transformOut, TaskViewTransform frontTransform) {
-        return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
-                false /* forceUpdate */, false /* ignoreTaskOverrides */);
-    }
-
-    public TaskViewTransform getStackTransform(Task task, float stackScroll,
-            TaskViewTransform transformOut, TaskViewTransform frontTransform,
-            boolean ignoreTaskOverrides) {
-        return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
-                false /* forceUpdate */, ignoreTaskOverrides);
-    }
-
-    public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState,
-            TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate,
-            boolean ignoreTaskOverrides) {
-        if (useGridLayout()) {
-            int taskIndex = mTaskIndexMap.get(task.key.id);
-            int taskCount = mTaskIndexMap.size();
-            mTaskGridLayoutAlgorithm.getTransform(taskIndex, taskCount, transformOut, this);
-            return transformOut;
-        } else if (Recents.getConfiguration().isLowRamDevice) {
-            if (task == null) {
-                transformOut.reset();
-                return transformOut;
-            }
-            int taskIndex = mTaskIndexMap.get(task.key.id);
-            mTaskStackLowRamLayoutAlgorithm.getTransform(taskIndex, stackScroll, transformOut,
-                    mNumStackTasks, this);
-            return transformOut;
-        } else {
-            // Return early if we have an invalid index
-            int nonOverrideTaskProgress = mTaskIndexMap.get(task.key.id, -1);
-            if (task == null || nonOverrideTaskProgress == -1) {
-                transformOut.reset();
-                return transformOut;
-            }
-            float taskProgress = ignoreTaskOverrides
-                    ? nonOverrideTaskProgress
-                    : getStackScrollForTask(task);
-
-            getStackTransform(taskProgress, nonOverrideTaskProgress, stackScroll, focusState,
-                    transformOut, frontTransform, false /* ignoreSingleTaskCase */, forceUpdate);
-            return transformOut;
-        }
-    }
-
-    /**
-     * Like {@link #getStackTransform}, but in screen coordinates
-     */
-    public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
-            TaskViewTransform transformOut, TaskViewTransform frontTransform,
-            Rect windowOverrideRect) {
-        TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
-                transformOut, frontTransform, true /* forceUpdate */,
-                false /* ignoreTaskOverrides */);
-        return transformToScreenCoordinates(transform, windowOverrideRect);
-    }
-
-    /**
-     * Transforms the given {@param transformOut} to the screen coordinates, overriding the current
-     * window rectangle with {@param windowOverrideRect} if non-null.
-     */
-    TaskViewTransform transformToScreenCoordinates(TaskViewTransform transformOut,
-            Rect windowOverrideRect) {
-        Rect windowRect = windowOverrideRect != null
-                ? windowOverrideRect
-                : Recents.getSystemServices().getWindowRect();
-        transformOut.rect.offset(windowRect.left, windowRect.top);
-        if (useGridLayout()) {
-            // Draw the thumbnail a little lower to perfectly coincide with the view we are
-            // transitioning to, where the header bar has already been drawn.
-            transformOut.rect.offset(0, mTitleBarHeight);
-        }
-        return transformOut;
-    }
-
-    /**
-     * Update/get the transform.
-     *
-     * @param ignoreSingleTaskCase When set, will ensure that the transform computed does not take
-     *                             into account the special single-task case.  This is only used
-     *                             internally to ensure that we can calculate the transform for any
-     *                             position in the stack.
-     */
-    public void getStackTransform(float taskProgress, float nonOverrideTaskProgress,
-            float stackScroll, int focusState, TaskViewTransform transformOut,
-            TaskViewTransform frontTransform, boolean ignoreSingleTaskCase, boolean forceUpdate) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-
-        // Ensure that the task is in range
-        mUnfocusedRange.offset(stackScroll);
-        mFocusedRange.offset(stackScroll);
-        boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress);
-        boolean focusedVisible = mFocusedRange.isInRange(taskProgress);
-
-        // Skip if the task is not visible
-        if (!forceUpdate && !unfocusedVisible && !focusedVisible) {
-            transformOut.reset();
-            return;
-        }
-
-        // Map the absolute task progress to the normalized x at the stack scroll.  We use this to
-        // calculate positions along the curve.
-        mUnfocusedRange.offset(stackScroll);
-        mFocusedRange.offset(stackScroll);
-        float unfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
-        float focusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
-
-        // Map the absolute task progress to the normalized x at the bounded stack scroll.  We use
-        // this to calculate bounded properties, like translationZ and outline alpha.
-        float boundedStackScroll = Utilities.clamp(stackScroll, mMinScrollP, mMaxScrollP);
-        mUnfocusedRange.offset(boundedStackScroll);
-        mFocusedRange.offset(boundedStackScroll);
-        float boundedScrollUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
-        float boundedScrollUnfocusedNonOverrideRangeX =
-                mUnfocusedRange.getNormalizedX(nonOverrideTaskProgress);
-
-        // Map the absolute task progress to the normalized x at the upper bounded stack scroll.
-        // We use this to calculate the dim, which is bounded only on one end.
-        float lowerBoundedStackScroll = Utilities.clamp(stackScroll, -Float.MAX_VALUE, mMaxScrollP);
-        mUnfocusedRange.offset(lowerBoundedStackScroll);
-        mFocusedRange.offset(lowerBoundedStackScroll);
-        float lowerBoundedUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
-        float lowerBoundedFocusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
-
-        int x = (mStackRect.width() - mTaskRect.width()) / 2;
-        int y;
-        float z;
-        float dimAlpha;
-        float viewOutlineAlpha;
-        if (mNumStackTasks == 1 && !ignoreSingleTaskCase) {
-            // When there is exactly one task, then decouple the task from the stack and just move
-            // in screen space
-            float tmpP = (mMinScrollP - stackScroll) / mNumStackTasks;
-            int centerYOffset = (mStackRect.top - mTaskRect.top) +
-                    (mStackRect.height() - mSystemInsets.bottom - mTaskRect.height()) / 2;
-            y = centerYOffset + getYForDeltaP(tmpP, 0);
-            z = mMaxTranslationZ;
-            dimAlpha = 0f;
-            viewOutlineAlpha = OUTLINE_ALPHA_MIN_VALUE +
-                    (OUTLINE_ALPHA_MAX_VALUE - OUTLINE_ALPHA_MIN_VALUE) / 2f;
-
-        } else {
-            // Otherwise, update the task to the stack layout
-            int unfocusedY = (int) ((1f - mUnfocusedCurveInterpolator.getInterpolation(
-                    unfocusedRangeX)) * mStackRect.height());
-            int focusedY = (int) ((1f - mFocusedCurveInterpolator.getInterpolation(
-                    focusedRangeX)) * mStackRect.height());
-            float unfocusedDim = mUnfocusedDimCurveInterpolator.getInterpolation(
-                    lowerBoundedUnfocusedRangeX);
-            float focusedDim = mFocusedDimCurveInterpolator.getInterpolation(
-                    lowerBoundedFocusedRangeX);
-
-            // Special case, because we override the initial task positions differently for small
-            // stacks, we clamp the dim to 0 in the initial position, and then only modulate the
-            // dim when the task is scrolled back towards the top of the screen
-            if (mNumStackTasks <= 2 && nonOverrideTaskProgress == 0f) {
-                if (boundedScrollUnfocusedRangeX >= 0.5f) {
-                    unfocusedDim = 0f;
-                } else {
-                    float offset = mUnfocusedDimCurveInterpolator.getInterpolation(0.5f);
-                    unfocusedDim -= offset;
-                    unfocusedDim *= MAX_DIM / (MAX_DIM - offset);
-                }
-            }
-            y = (mStackRect.top - mTaskRect.top) +
-                    (int) Utilities.mapRange(focusState, unfocusedY, focusedY);
-            z = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedNonOverrideRangeX),
-                    mMinTranslationZ, mMaxTranslationZ);
-            dimAlpha = Utilities.mapRange(focusState, unfocusedDim, focusedDim);
-            viewOutlineAlpha = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedRangeX),
-                    OUTLINE_ALPHA_MIN_VALUE, OUTLINE_ALPHA_MAX_VALUE);
-        }
-
-        // Fill out the transform
-        transformOut.scale = 1f;
-        transformOut.alpha = 1f;
-        transformOut.translationZ = z;
-        transformOut.dimAlpha = dimAlpha;
-        transformOut.viewOutlineAlpha = viewOutlineAlpha;
-        transformOut.rect.set(mTaskRect);
-        transformOut.rect.offset(x, y);
-        Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
-        transformOut.visible = (transformOut.rect.top < mStackRect.bottom) &&
-                (frontTransform == null || transformOut.rect.top != frontTransform.rect.top);
-    }
-
-    /**
-     * Returns the untransformed task view bounds.
-     */
-    public Rect getUntransformedTaskViewBounds() {
-        return new Rect(mTaskRect);
-    }
-
-    /**
-     * Returns the scroll progress to scroll to such that the top of the task is at the top of the
-     * stack.
-     */
-    float getStackScrollForTask(Task t) {
-        Float overrideP = mTaskIndexOverrideMap.get(t.key.id, null);
-        if (Recents.getConfiguration().isLowRamDevice || overrideP == null) {
-            return (float) mTaskIndexMap.get(t.key.id, 0);
-        }
-        return overrideP;
-    }
-
-    /**
-     * Returns the original scroll progress to scroll to such that the top of the task is at the top
-     * of the stack.
-     */
-    float getStackScrollForTaskIgnoreOverrides(Task t) {
-        return (float) mTaskIndexMap.get(t.key.id, 0);
-    }
-
-    /**
-     * Returns the scroll progress to scroll to such that the top of the task at the initial top
-     * offset (which is at the task's brightest point).
-     */
-    float getStackScrollForTaskAtInitialOffset(Task t) {
-        if (Recents.getConfiguration().isLowRamDevice) {
-            RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-            return mTaskStackLowRamLayoutAlgorithm.getInitialScrollP(mNumStackTasks,
-                    launchState.launchedFromHome || launchState.launchedFromPipApp
-                            || launchState.launchedWithNextPipApp);
-        }
-        float normX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
-        mUnfocusedRange.offset(0f);
-        return Utilities.clamp((float) mTaskIndexMap.get(t.key.id, 0) - Math.max(0,
-                mUnfocusedRange.getAbsoluteX(normX)), mMinScrollP, mMaxScrollP);
-    }
-
-    /**
-     * Maps a movement in screen y, relative to {@param downY}, to a movement in along the arc
-     * length of the curve.  We know the curve is mostly flat, so we just map the length of the
-     * screen along the arc-length proportionally (1/arclength).
-     */
-    public float getDeltaPForY(int downY, int y) {
-        if (Recents.getConfiguration().isLowRamDevice) {
-            return mTaskStackLowRamLayoutAlgorithm.scrollToPercentage(downY - y);
-        }
-        float deltaP = (float) (y - downY) / mStackRect.height() *
-                mUnfocusedCurveInterpolator.getArcLength();
-        return -deltaP;
-    }
-
-    /**
-     * This is the inverse of {@link #getDeltaPForY}.  Given a movement along the arc length
-     * of the curve, map back to the screen y.
-     */
-    public int getYForDeltaP(float downScrollP, float p) {
-        if (Recents.getConfiguration().isLowRamDevice) {
-            return mTaskStackLowRamLayoutAlgorithm.percentageToScroll(downScrollP - p);
-        }
-        int y = (int) ((p - downScrollP) * mStackRect.height() *
-                (1f / mUnfocusedCurveInterpolator.getArcLength()));
-        return -y;
-    }
-
-    /**
-     * Returns the task stack bounds in the current orientation.  This rect takes into account the
-     * top and right system insets (but not the bottom inset) and left/right paddings, but _not_
-     * the top/bottom padding or insets.
-     */
-    public void getTaskStackBounds(Rect displayRect, Rect windowRect, int topInset, int leftInset,
-            int rightInset, Rect taskStackBounds) {
-        taskStackBounds.set(windowRect.left + leftInset, windowRect.top + topInset,
-                windowRect.right - rightInset, windowRect.bottom);
-
-        // Ensure that the new width is at most the smaller display edge size
-        int sideMargin = getScaleForExtent(windowRect, displayRect, mBaseSideMargin, mMinMargin,
-                WIDTH);
-        int targetStackWidth = taskStackBounds.width() - 2 * sideMargin;
-        if (Utilities.getAppConfiguration(mContext).orientation
-                == Configuration.ORIENTATION_LANDSCAPE) {
-            // If we are in landscape, calculate the width of the stack in portrait and ensure that
-            // we are not larger than that size
-            Rect portraitDisplayRect = new Rect(0, 0,
-                    Math.min(displayRect.width(), displayRect.height()),
-                    Math.max(displayRect.width(), displayRect.height()));
-            int portraitSideMargin = getScaleForExtent(portraitDisplayRect, portraitDisplayRect,
-                    mBaseSideMargin, mMinMargin, WIDTH);
-            targetStackWidth = Math.min(targetStackWidth,
-                    portraitDisplayRect.width() - 2 * portraitSideMargin);
-        }
-        taskStackBounds.inset((taskStackBounds.width() - targetStackWidth) / 2, 0);
-    }
-
-    /**
-     * Retrieves resources that are constant regardless of the current configuration of the device.
-     */
-    public static int getDimensionForDevice(Context ctx, int phoneResId,
-            int tabletResId, int xlargeTabletResId, int gridLayoutResId) {
-        return getDimensionForDevice(ctx, phoneResId, phoneResId, tabletResId, tabletResId,
-                xlargeTabletResId, xlargeTabletResId, gridLayoutResId);
-    }
-
-    /**
-     * Retrieves resources that are constant regardless of the current configuration of the device.
-     */
-    public static int getDimensionForDevice(Context ctx, int phonePortResId, int phoneLandResId,
-            int tabletPortResId, int tabletLandResId, int xlargeTabletPortResId,
-            int xlargeTabletLandResId, int gridLayoutResId) {
-        RecentsConfiguration config = Recents.getConfiguration();
-        Resources res = ctx.getResources();
-        boolean isLandscape = Utilities.getAppConfiguration(ctx).orientation ==
-                Configuration.ORIENTATION_LANDSCAPE;
-        if (config.isGridEnabled) {
-            return res.getDimensionPixelSize(gridLayoutResId);
-        } else if (config.isXLargeScreen) {
-            return res.getDimensionPixelSize(isLandscape
-                    ? xlargeTabletLandResId
-                    : xlargeTabletPortResId);
-        } else if (config.isLargeScreen) {
-            return res.getDimensionPixelSize(isLandscape
-                    ? tabletLandResId
-                    : tabletPortResId);
-        } else {
-            return res.getDimensionPixelSize(isLandscape
-                    ? phoneLandResId
-                    : phonePortResId);
-        }
-    }
-
-    /**
-     * Returns the normalized x on the unfocused curve given an absolute Y position (relative to the
-     * stack height).
-     */
-    private float getNormalizedXFromUnfocusedY(float y, @AnchorSide int fromSide) {
-        float offset = (fromSide == FROM_TOP)
-                ? mStackRect.height() - y
-                : y;
-        float offsetPct = offset / mStackRect.height();
-        return mUnfocusedCurveInterpolator.getX(offsetPct);
-    }
-
-    /**
-     * Returns the normalized x on the focused curve given an absolute Y position (relative to the
-     * stack height).
-     */
-    private float getNormalizedXFromFocusedY(float y, @AnchorSide int fromSide) {
-        float offset = (fromSide == FROM_TOP)
-                ? mStackRect.height() - y
-                : y;
-        float offsetPct = offset / mStackRect.height();
-        return mFocusedCurveInterpolator.getX(offsetPct);
-    }
-
-    /**
-     * Creates a new path for the focused curve.
-     */
-    private Path constructFocusedCurve() {
-        // Initialize the focused curve. This curve is a piecewise curve composed of several
-        // linear pieces that goes from (0,1) through (0.5, peek height offset),
-        // (0.5, bottom task offsets), and (1,0).
-        float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
-        float bottomPeekHeightPct = (float) (mStackBottomOffset + mFocusedBottomPeekHeight) /
-                mStackRect.height();
-        float minBottomPeekHeightPct = (float) (mFocusedTopPeekHeight + mTaskRect.height() -
-                mMinMargin) / mStackRect.height();
-        Path p = new Path();
-        p.moveTo(0f, 1f);
-        p.lineTo(0.5f, 1f - topPeekHeightPct);
-        p.lineTo(1f - (0.5f / mFocusedRange.relativeMax), Math.max(1f - minBottomPeekHeightPct,
-                bottomPeekHeightPct));
-        p.lineTo(1f, 0f);
-        return p;
-    }
-
-    /**
-     * Creates a new path for the unfocused curve.
-     */
-    private Path constructUnfocusedCurve() {
-        // Initialize the unfocused curve. This curve is a piecewise curve composed of two quadradic
-        // beziers that goes from (0,1) through (0.5, peek height offset) and ends at (1,0).  This
-        // ensures that we match the range, at which 0.5 represents the stack scroll at the current
-        // task progress.  Because the height offset can change depending on a resource, we compute
-        // the control point of the second bezier such that between it and a first known point,
-        // there is a tangent at (0.5, peek height offset).
-        float cpoint1X = 0.4f;
-        float cpoint1Y = 0.975f;
-        float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
-        float slope = ((1f - topPeekHeightPct) - cpoint1Y) / (0.5f - cpoint1X);
-        float b = 1f - slope * cpoint1X;
-        float cpoint2X = 0.65f;
-        float cpoint2Y = slope * cpoint2X + b;
-        Path p = new Path();
-        p.moveTo(0f, 1f);
-        p.cubicTo(0f, 1f, cpoint1X, cpoint1Y, 0.5f, 1f - topPeekHeightPct);
-        p.cubicTo(0.5f, 1f - topPeekHeightPct, cpoint2X, cpoint2Y, 1f, 0f);
-        return p;
-    }
-
-    /**
-     * Creates a new path for the focused dim curve.
-     */
-    private Path constructFocusedDimCurve() {
-        Path p = new Path();
-        // The focused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused
-        // task), then goes back to max dim at the next task
-        p.moveTo(0f, MAX_DIM);
-        p.lineTo(0.5f, 0f);
-        p.lineTo(0.5f + (0.5f / mFocusedRange.relativeMax), MAX_DIM);
-        p.lineTo(1f, MAX_DIM);
-        return p;
-    }
-
-    /**
-     * Creates a new path for the unfocused dim curve.
-     */
-    private Path constructUnfocusedDimCurve() {
-        float focusX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
-        float cpoint2X = focusX + (1f - focusX) / 2;
-        Path p = new Path();
-        // The unfocused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused
-        // task), then goes back to max dim towards the front of the stack
-        p.moveTo(0f, MAX_DIM);
-        p.cubicTo(focusX * 0.5f, MAX_DIM, focusX * 0.75f, MAX_DIM * 0.75f, focusX, 0f);
-        p.cubicTo(cpoint2X, 0f, cpoint2X, MED_DIM, 1f, MED_DIM);
-        return p;
-    }
-
-    /**
-     * Scales the given {@param value} to the scale of the {@param instance} rect relative to the
-     * {@param other} rect in the {@param extent} side.
-     */
-    private int getScaleForExtent(Rect instance, Rect other, int value, int minValue,
-                                  @Extent int extent) {
-        if (extent == WIDTH) {
-            float scale = Utilities.clamp01((float) instance.width() / other.width());
-            return Math.max(minValue, (int) (scale * value));
-        } else if (extent == HEIGHT) {
-            float scale = Utilities.clamp01((float) instance.height() / other.height());
-            return Math.max(minValue, (int) (scale * value));
-        }
-        return value;
-    }
-
-    /**
-     * Updates the current transforms that would put a TaskView at the front and back of the stack.
-     */
-    private void updateFrontBackTransforms() {
-        // Return early if we have not yet initialized
-        if (mStackRect.isEmpty()) {
-            return;
-        }
-        if (Recents.getConfiguration().isLowRamDevice) {
-            mTaskStackLowRamLayoutAlgorithm.getBackOfStackTransform(mBackOfStackTransform, this);
-            mTaskStackLowRamLayoutAlgorithm.getFrontOfStackTransform(mFrontOfStackTransform, this);
-            return;
-        }
-
-        float min = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMin,
-                mFocusedRange.relativeMin);
-        float max = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMax,
-                mFocusedRange.relativeMax);
-        getStackTransform(min, min, 0f, mFocusState, mBackOfStackTransform, null,
-                true /* ignoreSingleTaskCase */, true /* forceUpdate */);
-        getStackTransform(max, max, 0f, mFocusState, mFrontOfStackTransform, null,
-                true /* ignoreSingleTaskCase */, true /* forceUpdate */);
-        mBackOfStackTransform.visible = true;
-        mFrontOfStackTransform.visible = true;
-    }
-
-    /**
-     * Returns the proper task rectangle according to the current grid state.
-     */
-    public Rect getTaskRect() {
-        if (Recents.getConfiguration().isLowRamDevice) {
-            return mTaskStackLowRamLayoutAlgorithm.getTaskRect();
-        }
-        return useGridLayout() ? mTaskGridLayoutAlgorithm.getTaskGridRect() : mTaskRect;
-    }
-
-    public void dump(String prefix, PrintWriter writer) {
-        String innerPrefix = prefix + "  ";
-
-        writer.print(prefix); writer.print(TAG);
-        writer.write(" numStackTasks="); writer.print(mNumStackTasks);
-        writer.println();
-
-        writer.print(innerPrefix);
-        writer.print("insets="); writer.print(Utilities.dumpRect(mSystemInsets));
-        writer.print(" stack="); writer.print(Utilities.dumpRect(mStackRect));
-        writer.print(" task="); writer.print(Utilities.dumpRect(mTaskRect));
-        writer.print(" actionButton="); writer.print(Utilities.dumpRect(mStackActionButtonRect));
-        writer.println();
-
-        writer.print(innerPrefix);
-        writer.print("minScroll="); writer.print(mMinScrollP);
-        writer.print(" maxScroll="); writer.print(mMaxScrollP);
-        writer.print(" initialScroll="); writer.print(mInitialScrollP);
-        writer.println();
-
-        writer.print(innerPrefix);
-        writer.print("focusState="); writer.print(mFocusState);
-        writer.println();
-
-        if (mTaskIndexOverrideMap.size() > 0) {
-            for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
-                int taskId = mTaskIndexOverrideMap.keyAt(i);
-                float x = mTaskIndexMap.get(taskId);
-                float overrideX = mTaskIndexOverrideMap.get(taskId, 0f);
-
-                writer.print(innerPrefix);
-                writer.print("taskId= "); writer.print(taskId);
-                writer.print(" x= "); writer.print(x);
-                writer.print(" overrideX= "); writer.print(overrideX);
-                writer.println();
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
deleted file mode 100644
index 89288d8..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ /dev/null
@@ -1,2286 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.IntDef;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.MutableBoolean;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.ScrollView;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
-import com.android.systemui.recents.RecentsImpl;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
-import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
-import com.android.systemui.recents.events.activity.LaunchMostRecentTaskRequestEvent;
-import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
-import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
-import com.android.systemui.recents.events.activity.PackagesChangedEvent;
-import com.android.systemui.recents.events.activity.ShowEmptyViewEvent;
-import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
-import com.android.systemui.recents.events.component.ActivityPinnedEvent;
-import com.android.systemui.recents.events.component.ExpandPipEvent;
-import com.android.systemui.recents.events.component.HidePipMenuEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
-import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
-import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
-import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
-import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
-import com.android.systemui.recents.events.ui.UserInteractionEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
-import com.android.systemui.recents.misc.DozeTrigger;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.recents.views.grid.GridTaskView;
-import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
-import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
-
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-
-/* The visual representation of a task stack view */
-public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
-        TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
-        TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks,
-        ViewPool.ViewPoolConsumer<TaskView, Task> {
-
-    private static final String TAG = "TaskStackView";
-
-    // The thresholds at which to show/hide the stack action button.
-    private static final float SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
-    private static final float HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
-
-    public static final int DEFAULT_SYNC_STACK_DURATION = 200;
-    public static final int SLOW_SYNC_STACK_DURATION = 250;
-    private static final int DRAG_SCALE_DURATION = 175;
-    static final float DRAG_SCALE_FACTOR = 1.05f;
-
-    private static final int LAUNCH_NEXT_SCROLL_BASE_DURATION = 216;
-    private static final int LAUNCH_NEXT_SCROLL_INCR_DURATION = 32;
-
-    // The actions to perform when resetting to initial state,
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({INITIAL_STATE_UPDATE_NONE, INITIAL_STATE_UPDATE_ALL, INITIAL_STATE_UPDATE_LAYOUT_ONLY})
-    public @interface InitialStateAction {}
-    /** Do not update the stack and layout to the initial state. */
-    private static final int INITIAL_STATE_UPDATE_NONE = 0;
-    /** Update both the stack and layout to the initial state. */
-    private static final int INITIAL_STATE_UPDATE_ALL = 1;
-    /** Update only the layout to the initial state. */
-    private static final int INITIAL_STATE_UPDATE_LAYOUT_ONLY = 2;
-
-    private LayoutInflater mInflater;
-    private TaskStack mStack = new TaskStack();
-    @ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
-    TaskStackLayoutAlgorithm mLayoutAlgorithm;
-    // The stable layout algorithm is only used to calculate the task rect with the stable bounds
-    private TaskStackLayoutAlgorithm mStableLayoutAlgorithm;
-    @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
-    private TaskStackViewScroller mStackScroller;
-    @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
-    private TaskStackViewTouchHandler mTouchHandler;
-    private TaskStackAnimationHelper mAnimationHelper;
-    private ViewPool<TaskView, Task> mViewPool;
-
-    private ArrayList<TaskView> mTaskViews = new ArrayList<>();
-    private ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
-    private ArraySet<Task.TaskKey> mIgnoreTasks = new ArraySet<>();
-    private AnimationProps mDeferredTaskViewLayoutAnimation = null;
-
-    @ViewDebug.ExportedProperty(deepExport=true, prefix="doze_")
-    private DozeTrigger mUIDozeTrigger;
-    @ViewDebug.ExportedProperty(deepExport=true, prefix="focused_task_")
-    private Task mFocusedTask;
-
-    private int mTaskCornerRadiusPx;
-    private int mDividerSize;
-    private int mStartTimerIndicatorDuration;
-
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mTaskViewsClipDirty = true;
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mEnterAnimationComplete = false;
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mStackReloaded = false;
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mFinishedLayoutAfterStackReload = false;
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mLaunchNextAfterFirstMeasure = false;
-    @ViewDebug.ExportedProperty(category="recents")
-    @InitialStateAction
-    private int mInitialState = INITIAL_STATE_UPDATE_ALL;
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mInMeasureLayout = false;
-    @ViewDebug.ExportedProperty(category="recents")
-    boolean mTouchExplorationEnabled;
-    @ViewDebug.ExportedProperty(category="recents")
-    boolean mScreenPinningEnabled;
-
-    // The stable stack bounds are the full bounds that we were measured with from RecentsView
-    @ViewDebug.ExportedProperty(category="recents")
-    private Rect mStableStackBounds = new Rect();
-    // The current stack bounds are dynamic and may change as the user drags and drops
-    @ViewDebug.ExportedProperty(category="recents")
-    private Rect mStackBounds = new Rect();
-    // The current window bounds at the point we were measured
-    @ViewDebug.ExportedProperty(category="recents")
-    private Rect mStableWindowRect = new Rect();
-    // The current window bounds are dynamic and may change as the user drags and drops
-    @ViewDebug.ExportedProperty(category="recents")
-    private Rect mWindowRect = new Rect();
-    // The current display bounds
-    @ViewDebug.ExportedProperty(category="recents")
-    private Rect mDisplayRect = new Rect();
-    // The current display orientation
-    @ViewDebug.ExportedProperty(category="recents")
-    private int mDisplayOrientation = Configuration.ORIENTATION_UNDEFINED;
-
-    private Rect mTmpRect = new Rect();
-    private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
-    private List<TaskView> mTmpTaskViews = new ArrayList<>();
-    private TaskViewTransform mTmpTransform = new TaskViewTransform();
-    private int[] mTmpIntPair = new int[2];
-    private boolean mResetToInitialStateWhenResized;
-    private int mLastWidth;
-    private int mLastHeight;
-    private boolean mStackActionButtonVisible;
-
-    // Percentage of last ScrollP from the min to max scrollP that lives after configuration changes
-    private float mLastScrollPPercent = -1;
-
-    // We keep track of the task view focused by user interaction and draw a frame around it in the
-    // grid layout.
-    private TaskViewFocusFrame mTaskViewFocusFrame;
-
-    private Task mPrefetchingTask;
-    private final float mFastFlingVelocity;
-
-    // A convenience update listener to request updating clipping of tasks
-    private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    if (!mTaskViewsClipDirty) {
-                        mTaskViewsClipDirty = true;
-                        invalidate();
-                    }
-                }
-            };
-
-    private DropTarget mStackDropTarget = new DropTarget() {
-        @Override
-        public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
-                boolean isCurrentTarget) {
-            // This drop target has a fixed bounds and should be checked last, so just fall through
-            // if it is the current target
-            if (!isCurrentTarget) {
-                return mLayoutAlgorithm.mStackRect.contains(x, y);
-            }
-            return false;
-        }
-    };
-
-    public TaskStackView(Context context) {
-        super(context);
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        Resources res = context.getResources();
-
-        // Set the stack first
-        mStack.setCallbacks(this);
-        mViewPool = new ViewPool<>(context, this);
-        mInflater = LayoutInflater.from(context);
-        mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
-        mStableLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
-        mStackScroller = new TaskStackViewScroller(context, this, mLayoutAlgorithm);
-        mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
-        mAnimationHelper = new TaskStackAnimationHelper(context, this);
-        mTaskCornerRadiusPx = Recents.getConfiguration().isGridEnabled ?
-                res.getDimensionPixelSize(R.dimen.recents_grid_task_view_rounded_corners_radius) :
-                res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
-        mFastFlingVelocity = res.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
-        mDividerSize = ssp.getDockedDividerSize(context);
-        mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
-        mDisplayRect = ssp.getDisplayRect();
-        mStackActionButtonVisible = false;
-
-        // Create a frame to draw around the focused task view
-        if (Recents.getConfiguration().isGridEnabled) {
-            mTaskViewFocusFrame = new TaskViewFocusFrame(mContext, this,
-                mLayoutAlgorithm.mTaskGridLayoutAlgorithm);
-            addView(mTaskViewFocusFrame);
-            getViewTreeObserver().addOnGlobalFocusChangeListener(mTaskViewFocusFrame);
-        }
-
-        int taskBarDismissDozeDelaySeconds = getResources().getInteger(
-                R.integer.recents_task_bar_dismiss_delay_seconds);
-        mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
-            @Override
-            public void run() {
-                // Show the task bar dismiss buttons
-                List<TaskView> taskViews = getTaskViews();
-                int taskViewCount = taskViews.size();
-                for (int i = 0; i < taskViewCount; i++) {
-                    TaskView tv = taskViews.get(i);
-                    tv.startNoUserInteractionAnimation();
-                }
-            }
-        });
-        setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
-        super.onAttachedToWindow();
-        readSystemFlags();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        EventBus.getDefault().unregister(this);
-    }
-
-    /**
-     * Called from RecentsActivity when it is relaunched.
-     */
-    void onReload(boolean isResumingFromVisible) {
-        if (!isResumingFromVisible) {
-            // Reset the focused task
-            resetFocusedTask(getFocusedTask());
-        }
-
-        // Reset the state of each of the task views
-        List<TaskView> taskViews = new ArrayList<>();
-        taskViews.addAll(getTaskViews());
-        taskViews.addAll(mViewPool.getViews());
-        for (int i = taskViews.size() - 1; i >= 0; i--) {
-            taskViews.get(i).onReload(isResumingFromVisible);
-        }
-
-        // Reset the stack state
-        readSystemFlags();
-        mTaskViewsClipDirty = true;
-        mUIDozeTrigger.stopDozing();
-        if (!isResumingFromVisible) {
-            mStackScroller.reset();
-            mStableLayoutAlgorithm.reset();
-            mLayoutAlgorithm.reset();
-            mLastScrollPPercent = -1;
-        }
-
-        // Since we always animate to the same place in (the initial state), always reset the stack
-        // to the initial state when resuming
-        mStackReloaded = true;
-        mFinishedLayoutAfterStackReload = false;
-        mLaunchNextAfterFirstMeasure = false;
-        mInitialState = INITIAL_STATE_UPDATE_ALL;
-        requestLayout();
-    }
-
-    /**
-     * Sets the stack tasks of this TaskStackView from the given TaskStack.
-     */
-    public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) {
-        boolean isInitialized = mLayoutAlgorithm.isInitialized();
-
-        // Only notify if we are already initialized, otherwise, everything will pick up all the
-        // new and old tasks when we next layout
-        mStack.setTasks(stack, allowNotifyStackChanges && isInitialized);
-    }
-
-    /** Returns the task stack. */
-    public TaskStack getStack() {
-        return mStack;
-    }
-
-    /**
-     * Updates this TaskStackView to the initial state.
-     */
-    public void updateToInitialState() {
-        mStackScroller.setStackScrollToInitialState();
-        mLayoutAlgorithm.setTaskOverridesForInitialState(mStack, false /* ignoreScrollToFront */);
-    }
-
-    /** Updates the list of task views */
-    void updateTaskViewsList() {
-        mTaskViews.clear();
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View v = getChildAt(i);
-            if (v instanceof TaskView) {
-                mTaskViews.add((TaskView) v);
-            }
-        }
-    }
-
-    /** Gets the list of task views */
-    List<TaskView> getTaskViews() {
-        return mTaskViews;
-    }
-
-    /**
-     * Returns the front most task view.
-     */
-    private TaskView getFrontMostTaskView() {
-        List<TaskView> taskViews = getTaskViews();
-        if (taskViews.isEmpty()) {
-            return null;
-        }
-        return taskViews.get(taskViews.size() - 1);
-    }
-
-    /**
-     * Finds the child view given a specific {@param task}.
-     */
-    public TaskView getChildViewForTask(Task t) {
-        List<TaskView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            TaskView tv = taskViews.get(i);
-            if (tv.getTask() == t) {
-                return tv;
-            }
-        }
-        return null;
-    }
-
-    /** Returns the stack algorithm for this task stack. */
-    public TaskStackLayoutAlgorithm getStackAlgorithm() {
-        return mLayoutAlgorithm;
-    }
-
-    /** Returns the grid algorithm for this task stack. */
-    public TaskGridLayoutAlgorithm getGridAlgorithm() {
-        return mLayoutAlgorithm.mTaskGridLayoutAlgorithm;
-    }
-
-    /**
-     * Returns the touch handler for this task stack.
-     */
-    public TaskStackViewTouchHandler getTouchHandler() {
-        return mTouchHandler;
-    }
-
-    /**
-     * Adds a task to the ignored set.
-     */
-    void addIgnoreTask(Task task) {
-        mIgnoreTasks.add(task.key);
-    }
-
-    /**
-     * Removes a task from the ignored set.
-     */
-    void removeIgnoreTask(Task task) {
-        mIgnoreTasks.remove(task.key);
-    }
-
-    /**
-     * Returns whether the specified {@param task} is ignored.
-     */
-    boolean isIgnoredTask(Task task) {
-        return mIgnoreTasks.contains(task.key);
-    }
-
-    /**
-     * Computes the task transforms at the current stack scroll for all visible tasks. If a valid
-     * target stack scroll is provided (ie. is different than {@param curStackScroll}), then the
-     * visible range includes all tasks at the target stack scroll. This is useful for ensure that
-     * all views necessary for a transition or animation will be visible at the start.
-     *
-     * @param taskTransforms The set of task view transforms to reuse, this list will be sized to
-     *                       match the size of {@param tasks}
-     * @param tasks The set of tasks for which to generate transforms
-     * @param curStackScroll The current stack scroll
-     * @param targetStackScroll The stack scroll that we anticipate we are going to be scrolling to.
-     *                          The range of the union of the visible views at the current and
-     *                          target stack scrolls will be returned.
-     * @param ignoreTasksSet The set of tasks to skip for purposes of calculaing the visible range.
-     *                       Transforms will still be calculated for the ignore tasks.
-     * @return the front and back most visible task indices (there may be non visible tasks in
-     *         between this range)
-     */
-    int[] computeVisibleTaskTransforms(ArrayList<TaskViewTransform> taskTransforms,
-            ArrayList<Task> tasks, float curStackScroll, float targetStackScroll,
-            ArraySet<Task.TaskKey> ignoreTasksSet, boolean ignoreTaskOverrides) {
-        int taskCount = tasks.size();
-        int[] visibleTaskRange = mTmpIntPair;
-        visibleTaskRange[0] = -1;
-        visibleTaskRange[1] = -1;
-        boolean useTargetStackScroll = Float.compare(curStackScroll, targetStackScroll) != 0;
-
-        // We can reuse the task transforms where possible to reduce object allocation
-        matchTaskListSize(tasks, taskTransforms);
-
-        // Update the stack transforms
-        TaskViewTransform frontTransform = null;
-        TaskViewTransform frontTransformAtTarget = null;
-        TaskViewTransform transform = null;
-        TaskViewTransform transformAtTarget = null;
-        for (int i = taskCount - 1; i >= 0; i--) {
-            Task task = tasks.get(i);
-
-            // Calculate the current and (if necessary) the target transform for the task
-            transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
-                    taskTransforms.get(i), frontTransform, ignoreTaskOverrides);
-            if (useTargetStackScroll && !transform.visible) {
-                // If we have a target stack scroll and the task is not currently visible, then we
-                // just update the transform at the new scroll
-                // TODO: Optimize this
-                transformAtTarget = mLayoutAlgorithm.getStackTransform(task, targetStackScroll,
-                    new TaskViewTransform(), frontTransformAtTarget);
-                if (transformAtTarget.visible) {
-                    transform.copyFrom(transformAtTarget);
-                }
-            }
-
-            // For ignore tasks, only calculate the stack transform and skip the calculation of the
-            // visible stack indices
-            if (ignoreTasksSet.contains(task.key)) {
-                continue;
-            }
-
-            frontTransform = transform;
-            frontTransformAtTarget = transformAtTarget;
-            if (transform.visible) {
-                if (visibleTaskRange[0] < 0) {
-                    visibleTaskRange[0] = i;
-                }
-                visibleTaskRange[1] = i;
-            }
-        }
-        return visibleTaskRange;
-    }
-
-    /**
-     * Binds the visible {@link TaskView}s at the given target scroll.
-     */
-    void bindVisibleTaskViews(float targetStackScroll) {
-        bindVisibleTaskViews(targetStackScroll, false /* ignoreTaskOverrides */);
-    }
-
-    /**
-     * Synchronizes the set of children {@link TaskView}s to match the visible set of tasks in the
-     * current {@link TaskStack}. This call does not continue on to update their position to the
-     * computed {@link TaskViewTransform}s of the visible range, but only ensures that they will
-     * be added/removed from the view hierarchy and placed in the correct Z order and initial
-     * position (if not currently on screen).
-     *
-     * @param targetStackScroll If provided, will ensure that the set of visible {@link TaskView}s
-     *                          includes those visible at the current stack scroll, and all at the
-     *                          target stack scroll.
-     * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for
-     *                            tasks at their non-overridden task progress
-     */
-    void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
-        // Get all the task transforms
-        ArrayList<Task> tasks = mStack.getTasks();
-        int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
-                mStackScroller.getStackScroll(), targetStackScroll, mIgnoreTasks,
-                ignoreTaskOverrides);
-
-        // Return all the invisible children to the pool
-        mTmpTaskViewMap.clear();
-        List<TaskView> taskViews = getTaskViews();
-        int lastFocusedTaskIndex = -1;
-        int taskViewCount = taskViews.size();
-        for (int i = taskViewCount - 1; i >= 0; i--) {
-            TaskView tv = taskViews.get(i);
-            Task task = tv.getTask();
-
-            // Skip ignored tasks
-            if (mIgnoreTasks.contains(task.key)) {
-                continue;
-            }
-
-            // It is possible for the set of lingering TaskViews to differ from the stack if the
-            // stack was updated before the relayout.  If the task view is no longer in the stack,
-            // then just return it back to the view pool.
-            int taskIndex = mStack.indexOfTask(task);
-            TaskViewTransform transform = null;
-            if (taskIndex != -1) {
-                transform = mCurrentTaskTransforms.get(taskIndex);
-            }
-
-            if (transform != null && transform.visible) {
-                mTmpTaskViewMap.put(task.key, tv);
-            } else {
-                if (mTouchExplorationEnabled && Utilities.isDescendentAccessibilityFocused(tv)) {
-                    lastFocusedTaskIndex = taskIndex;
-                    resetFocusedTask(task);
-                }
-                mViewPool.returnViewToPool(tv);
-            }
-        }
-
-        // Pick up all the newly visible children
-        for (int i = tasks.size() - 1; i >= 0; i--) {
-            Task task = tasks.get(i);
-            TaskViewTransform transform = mCurrentTaskTransforms.get(i);
-
-            // Skip ignored tasks
-            if (mIgnoreTasks.contains(task.key)) {
-                continue;
-            }
-
-            // Skip the invisible stack tasks
-            if (!transform.visible) {
-                continue;
-            }
-
-            TaskView tv = mTmpTaskViewMap.get(task.key);
-            if (tv == null) {
-                tv = mViewPool.pickUpViewFromPool(task, task);
-                if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
-                    updateTaskViewToTransform(tv, mLayoutAlgorithm.getBackOfStackTransform(),
-                            AnimationProps.IMMEDIATE);
-                } else {
-                    updateTaskViewToTransform(tv, mLayoutAlgorithm.getFrontOfStackTransform(),
-                            AnimationProps.IMMEDIATE);
-                }
-            } else {
-                // Reattach it in the right z order
-                final int taskIndex = mStack.indexOfTask(task);
-                final int insertIndex = findTaskViewInsertIndex(task, taskIndex);
-                if (insertIndex != getTaskViews().indexOf(tv)){
-                    detachViewFromParent(tv);
-                    attachViewToParent(tv, insertIndex, tv.getLayoutParams());
-                    updateTaskViewsList();
-                }
-            }
-        }
-
-        updatePrefetchingTask(tasks, visibleTaskRange[0], visibleTaskRange[1]);
-
-        // Update the focus if the previous focused task was returned to the view pool
-        if (lastFocusedTaskIndex != -1) {
-            int newFocusedTaskIndex = (lastFocusedTaskIndex < visibleTaskRange[1])
-                    ? visibleTaskRange[1]
-                    : visibleTaskRange[0];
-            setFocusedTask(newFocusedTaskIndex, false /* scrollToTask */,
-                    true /* requestViewFocus */);
-            TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
-            if (focusedTaskView != null) {
-                focusedTaskView.requestAccessibilityFocus();
-            }
-        }
-    }
-
-    /**
-     * @see #relayoutTaskViews(AnimationProps, ArrayMap<Task, AnimationProps>, boolean)
-     */
-    public void relayoutTaskViews(AnimationProps animation) {
-        relayoutTaskViews(animation, null /* animationOverrides */,
-                false /* ignoreTaskOverrides */);
-    }
-
-    /**
-     * Relayout the the visible {@link TaskView}s to their current transforms as specified by the
-     * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any
-     * animations that are current running on those task views, and will ensure that the children
-     * {@link TaskView}s will match the set of visible tasks in the stack.  If a {@link Task} has
-     * an animation provided in {@param animationOverrides}, that will be used instead.
-     */
-    private void relayoutTaskViews(AnimationProps animation,
-            ArrayMap<Task, AnimationProps> animationOverrides, boolean ignoreTaskOverrides) {
-        // If we had a deferred animation, cancel that
-        cancelDeferredTaskViewLayoutAnimation();
-
-        // Synchronize the current set of TaskViews
-        bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTaskOverrides);
-
-        // Animate them to their final transforms with the given animation
-        List<TaskView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            TaskView tv = taskViews.get(i);
-            Task task = tv.getTask();
-
-            if (mIgnoreTasks.contains(task.key)) {
-                continue;
-            }
-
-            int taskIndex = mStack.indexOfTask(task);
-            TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
-            if (animationOverrides != null && animationOverrides.containsKey(task)) {
-                animation = animationOverrides.get(task);
-            }
-
-            updateTaskViewToTransform(tv, transform, animation);
-        }
-    }
-
-    /**
-     * Posts an update to synchronize the {@link TaskView}s with the stack on the next frame.
-     */
-    void relayoutTaskViewsOnNextFrame(AnimationProps animation) {
-        mDeferredTaskViewLayoutAnimation = animation;
-        invalidate();
-    }
-
-    /**
-     * Called to update a specific {@link TaskView} to a given {@link TaskViewTransform} with a
-     * given set of {@link AnimationProps} properties.
-     */
-    public void updateTaskViewToTransform(TaskView taskView, TaskViewTransform transform,
-            AnimationProps animation) {
-        if (taskView.isAnimatingTo(transform)) {
-            return;
-        }
-        taskView.cancelTransformAnimation();
-        taskView.updateViewPropertiesToTaskTransform(transform, animation,
-                mRequestUpdateClippingListener);
-    }
-
-    /**
-     * Returns the current task transforms of all tasks, falling back to the stack layout if there
-     * is no {@link TaskView} for the task.
-     */
-    public void getCurrentTaskTransforms(ArrayList<Task> tasks,
-            ArrayList<TaskViewTransform> transformsOut) {
-        matchTaskListSize(tasks, transformsOut);
-        int focusState = mLayoutAlgorithm.getFocusState();
-        for (int i = tasks.size() - 1; i >= 0; i--) {
-            Task task = tasks.get(i);
-            TaskViewTransform transform = transformsOut.get(i);
-            TaskView tv = getChildViewForTask(task);
-            if (tv != null) {
-                transform.fillIn(tv);
-            } else {
-                mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
-                        focusState, transform, null, true /* forceUpdate */,
-                        false /* ignoreTaskOverrides */);
-            }
-            transform.visible = true;
-        }
-    }
-
-    /**
-     * Returns the task transforms for all the tasks in the stack if the stack was at the given
-     * {@param stackScroll} and {@param focusState}.
-     */
-    public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks,
-            boolean ignoreTaskOverrides, ArrayList<TaskViewTransform> transformsOut) {
-        matchTaskListSize(tasks, transformsOut);
-        for (int i = tasks.size() - 1; i >= 0; i--) {
-            Task task = tasks.get(i);
-            TaskViewTransform transform = transformsOut.get(i);
-            mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
-                    true /* forceUpdate */, ignoreTaskOverrides);
-            transform.visible = true;
-        }
-    }
-
-    /**
-     * Cancels the next deferred task view layout.
-     */
-    void cancelDeferredTaskViewLayoutAnimation() {
-        mDeferredTaskViewLayoutAnimation = null;
-    }
-
-    /**
-     * Cancels all {@link TaskView} animations.
-     */
-    void cancelAllTaskViewAnimations() {
-        List<TaskView> taskViews = getTaskViews();
-        for (int i = taskViews.size() - 1; i >= 0; i--) {
-            final TaskView tv = taskViews.get(i);
-            if (!mIgnoreTasks.contains(tv.getTask().key)) {
-                tv.cancelTransformAnimation();
-            }
-        }
-    }
-
-    /**
-     * Updates the clip for each of the task views from back to front.
-     */
-    private void clipTaskViews() {
-        // We never clip task views in grid layout
-        if (Recents.getConfiguration().isGridEnabled) {
-            return;
-        }
-
-        // Update the clip on each task child
-        List<TaskView> taskViews = getTaskViews();
-        TaskView tmpTv = null;
-        TaskView prevVisibleTv = null;
-        int taskViewCount = taskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            TaskView tv = taskViews.get(i);
-            TaskView frontTv = null;
-            int clipBottom = 0;
-
-            if (isIgnoredTask(tv.getTask())) {
-                // For each of the ignore tasks, update the translationZ of its TaskView to be
-                // between the translationZ of the tasks immediately underneath it
-                if (prevVisibleTv != null) {
-                    tv.setTranslationZ(Math.max(tv.getTranslationZ(),
-                            prevVisibleTv.getTranslationZ() + 0.1f));
-                }
-            }
-
-            if (i < (taskViewCount - 1) && tv.shouldClipViewInStack()) {
-                // Find the next view to clip against
-                for (int j = i + 1; j < taskViewCount; j++) {
-                    tmpTv = taskViews.get(j);
-
-                    if (tmpTv.shouldClipViewInStack()) {
-                        frontTv = tmpTv;
-                        break;
-                    }
-                }
-
-                // Clip against the next view, this is just an approximation since we are
-                // stacked and we can make assumptions about the visibility of the this
-                // task relative to the ones in front of it.
-                if (frontTv != null) {
-                    float taskBottom = tv.getBottom();
-                    float frontTaskTop = frontTv.getTop();
-                    if (frontTaskTop < taskBottom) {
-                        // Map the stack view space coordinate (the rects) to view space
-                        clipBottom = (int) (taskBottom - frontTaskTop) - mTaskCornerRadiusPx;
-                    }
-                }
-            }
-            tv.getViewBounds().setClipBottom(clipBottom);
-            tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
-            prevVisibleTv = tv;
-        }
-        mTaskViewsClipDirty = false;
-    }
-
-    public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
-        updateLayoutAlgorithm(boundScrollToNewMinMax, Recents.getConfiguration().getLaunchState());
-    }
-
-    /**
-     * Updates the layout algorithm min and max virtual scroll bounds.
-     */
-   public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
-           RecentsActivityLaunchState launchState) {
-        // Compute the min and max scroll values
-        mLayoutAlgorithm.update(mStack, mIgnoreTasks, launchState, mLastScrollPPercent);
-
-        if (boundScrollToNewMinMax) {
-            mStackScroller.boundScroll();
-        }
-    }
-
-    /**
-     * Updates the stack layout to its stable places.
-     */
-    private void updateLayoutToStableBounds() {
-        mWindowRect.set(mStableWindowRect);
-        mStackBounds.set(mStableStackBounds);
-        mLayoutAlgorithm.setSystemInsets(mStableLayoutAlgorithm.mSystemInsets);
-        mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
-        updateLayoutAlgorithm(true /* boundScroll */);
-    }
-
-    /** Returns the scroller. */
-    public TaskStackViewScroller getScroller() {
-        return mStackScroller;
-    }
-
-    /**
-     * Sets the focused task to the provided (bounded taskIndex).
-     *
-     * @return whether or not the stack will scroll as a part of this focus change
-     */
-    public boolean setFocusedTask(int taskIndex, boolean scrollToTask,
-            final boolean requestViewFocus) {
-        return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
-    }
-
-    /**
-     * Sets the focused task to the provided (bounded focusTaskIndex).
-     *
-     * @return whether or not the stack will scroll as a part of this focus change
-     */
-    public boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
-            boolean requestViewFocus, int timerIndicatorDuration) {
-        // Find the next task to focus
-        int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
-                Utilities.clamp(focusTaskIndex, 0, mStack.getTaskCount() - 1) : -1;
-        final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
-                mStack.getTasks().get(newFocusedTaskIndex) : null;
-
-        // Reset the last focused task state if changed
-        if (mFocusedTask != null) {
-            // Cancel the timer indicator, if applicable
-            if (timerIndicatorDuration > 0) {
-                final TaskView tv = getChildViewForTask(mFocusedTask);
-                if (tv != null) {
-                    tv.getHeaderView().cancelFocusTimerIndicator();
-                }
-            }
-
-            resetFocusedTask(mFocusedTask);
-        }
-
-        boolean willScroll = false;
-        mFocusedTask = newFocusedTask;
-
-        if (newFocusedTask != null) {
-            // Start the timer indicator, if applicable
-            if (timerIndicatorDuration > 0) {
-                final TaskView tv = getChildViewForTask(mFocusedTask);
-                if (tv != null) {
-                    tv.getHeaderView().startFocusTimerIndicator(timerIndicatorDuration);
-                } else {
-                    // The view is null; set a flag for later
-                    mStartTimerIndicatorDuration = timerIndicatorDuration;
-                }
-            }
-
-            if (scrollToTask) {
-                // Cancel any running enter animations at this point when we scroll or change focus
-                if (!mEnterAnimationComplete) {
-                    cancelAllTaskViewAnimations();
-                }
-
-                mLayoutAlgorithm.clearUnfocusedTaskOverrides();
-                willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
-                        requestViewFocus);
-                if (willScroll) {
-                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
-                }
-            } else {
-                // Focus the task view
-                TaskView newFocusedTaskView = getChildViewForTask(newFocusedTask);
-                if (newFocusedTaskView != null) {
-                    newFocusedTaskView.setFocusedState(true, requestViewFocus);
-                }
-            }
-            // Any time a task view gets the focus, we move the focus frame around it.
-            if (mTaskViewFocusFrame != null) {
-                mTaskViewFocusFrame.moveGridTaskViewFocus(getChildViewForTask(newFocusedTask));
-            }
-        }
-        return willScroll;
-    }
-
-    /**
-     * Sets the focused task relative to the currently focused task.
-     *
-     * @param forward whether to go to the next task in the stack (along the curve) or the previous
-     * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
-     *                       if the currently focused task is not a stack task, will set the focus
-     *                       to the first visible stack task
-     * @param animated determines whether to actually draw the highlight along with the change in
-     *                            focus.
-     */
-    public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) {
-        setRelativeFocusedTask(forward, stackTasksOnly, animated, false, 0);
-    }
-
-    /**
-     * Sets the focused task relative to the currently focused task.
-     *
-     * @param forward whether to go to the next task in the stack (along the curve) or the previous
-     * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
-     *                       if the currently focused task is not a stack task, will set the focus
-     *                       to the first visible stack task
-     * @param animated determines whether to actually draw the highlight along with the change in
-     *                            focus.
-     * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
-     *                               happens.
-     * @param timerIndicatorDuration the duration to initialize the auto-advance timer indicator
-     */
-    public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
-                                       boolean cancelWindowAnimations, int timerIndicatorDuration) {
-        Task focusedTask = getFocusedTask();
-        int newIndex = mStack.indexOfTask(focusedTask);
-        if (focusedTask != null) {
-            if (stackTasksOnly) {
-                List<Task> tasks =  mStack.getTasks();
-                // Try the next task if it is a stack task
-                int tmpNewIndex = newIndex + (forward ? -1 : 1);
-                if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
-                    newIndex = tmpNewIndex;
-                }
-            } else {
-                // No restrictions, lets just move to the new task (looping forward/backwards if
-                // necessary)
-                int taskCount = mStack.getTaskCount();
-                newIndex = (newIndex + (forward ? -1 : 1) + taskCount) % taskCount;
-            }
-        } else {
-            // We don't have a focused task
-            float stackScroll = mStackScroller.getStackScroll();
-            ArrayList<Task> tasks = mStack.getTasks();
-            int taskCount = tasks.size();
-            if (useGridLayout()) {
-                // For the grid layout, we directly set focus to the most recently used task
-                // no matter we're moving forwards or backwards.
-                newIndex = taskCount - 1;
-            } else {
-                // For the grid layout we pick a proper task to focus, according to the current
-                // stack scroll.
-                if (forward) {
-                    // Walk backwards and focus the next task smaller than the current stack scroll
-                    for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
-                        float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
-                        if (Float.compare(taskP, stackScroll) <= 0) {
-                            break;
-                        }
-                    }
-                } else {
-                    // Walk forwards and focus the next task larger than the current stack scroll
-                    for (newIndex = 0; newIndex < taskCount; newIndex++) {
-                        float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
-                        if (Float.compare(taskP, stackScroll) >= 0) {
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-        if (newIndex != -1) {
-            boolean willScroll = setFocusedTask(newIndex, true /* scrollToTask */,
-                    true /* requestViewFocus */, timerIndicatorDuration);
-            if (willScroll && cancelWindowAnimations) {
-                // As we iterate to the next/previous task, cancel any current/lagging window
-                // transition animations
-                EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
-            }
-        }
-    }
-
-    /**
-     * Resets the focused task.
-     */
-    public void resetFocusedTask(Task task) {
-        if (task != null) {
-            TaskView tv = getChildViewForTask(task);
-            if (tv != null) {
-                tv.setFocusedState(false, false /* requestViewFocus */);
-            }
-        }
-        if (mTaskViewFocusFrame != null) {
-            mTaskViewFocusFrame.moveGridTaskViewFocus(null);
-        }
-        mFocusedTask = null;
-    }
-
-    /**
-     * Returns the focused task.
-     */
-    public Task getFocusedTask() {
-        return mFocusedTask;
-    }
-
-    /**
-     * Returns the accessibility focused task.
-     */
-    Task getAccessibilityFocusedTask() {
-        List<TaskView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            TaskView tv = taskViews.get(i);
-            if (Utilities.isDescendentAccessibilityFocused(tv)) {
-                return tv.getTask();
-            }
-        }
-        TaskView frontTv = getFrontMostTaskView();
-        if (frontTv != null) {
-            return frontTv.getTask();
-        }
-        return null;
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        List<TaskView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-        if (taskViewCount > 0) {
-            TaskView backMostTask = taskViews.get(0);
-            TaskView frontMostTask = taskViews.get(taskViewCount - 1);
-            event.setFromIndex(mStack.indexOfTask(backMostTask.getTask()));
-            event.setToIndex(mStack.indexOfTask(frontMostTask.getTask()));
-            event.setContentDescription(frontMostTask.getTask().title);
-        }
-        event.setItemCount(mStack.getTaskCount());
-
-        int stackHeight = mLayoutAlgorithm.mStackRect.height();
-        event.setScrollY((int) (mStackScroller.getStackScroll() * stackHeight));
-        event.setMaxScrollY((int) (mLayoutAlgorithm.mMaxScrollP * stackHeight));
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        List<TaskView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-        if (taskViewCount > 1) {
-            // Find the accessibility focused task
-            Task focusedTask = getAccessibilityFocusedTask();
-            info.setScrollable(true);
-            int focusedTaskIndex = mStack.indexOfTask(focusedTask);
-            if (focusedTaskIndex > 0 || !mStackActionButtonVisible) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
-            }
-            if (0 <= focusedTaskIndex && focusedTaskIndex < mStack.getTaskCount() - 1) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
-            }
-        }
-    }
-
-    @Override
-    public CharSequence getAccessibilityClassName() {
-        return ScrollView.class.getName();
-    }
-
-    @Override
-    public boolean performAccessibilityAction(int action, Bundle arguments) {
-        if (super.performAccessibilityAction(action, arguments)) {
-            return true;
-        }
-        Task focusedTask = getAccessibilityFocusedTask();
-        int taskIndex = mStack.indexOfTask(focusedTask);
-        if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
-            switch (action) {
-                case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
-                    setFocusedTask(taskIndex + 1, true /* scrollToTask */, true /* requestViewFocus */,
-                            0);
-                    return true;
-                }
-                case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
-                    setFocusedTask(taskIndex - 1, true /* scrollToTask */, true /* requestViewFocus */,
-                            0);
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        return mTouchHandler.onInterceptTouchEvent(ev);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        return mTouchHandler.onTouchEvent(ev);
-    }
-
-    @Override
-    public boolean onGenericMotionEvent(MotionEvent ev) {
-        return mTouchHandler.onGenericMotionEvent(ev);
-    }
-
-    @Override
-    public void computeScroll() {
-        if (mStackScroller.computeScroll()) {
-            // Notify accessibility
-            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
-            Recents.getTaskLoader().getHighResThumbnailLoader().setFlingingFast(
-                    mStackScroller.getScrollVelocity() > mFastFlingVelocity);
-        }
-        if (mDeferredTaskViewLayoutAnimation != null) {
-            relayoutTaskViews(mDeferredTaskViewLayoutAnimation);
-            mTaskViewsClipDirty = true;
-            mDeferredTaskViewLayoutAnimation = null;
-        }
-        if (mTaskViewsClipDirty) {
-            clipTaskViews();
-        }
-        mLastScrollPPercent = Utilities.clamp(Utilities.unmapRange(mStackScroller.getStackScroll(),
-            mLayoutAlgorithm.mMinScrollP, mLayoutAlgorithm.mMaxScrollP), 0, 1);
-    }
-
-    /**
-     * Computes the maximum number of visible tasks and thumbnails. Requires that
-     * updateLayoutForStack() is called first.
-     */
-    public TaskStackLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
-        return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getTasks());
-    }
-
-    /**
-     * Updates the system insets.
-     */
-    public void setSystemInsets(Rect systemInsets) {
-        boolean changed = false;
-        changed |= mStableLayoutAlgorithm.setSystemInsets(systemInsets);
-        changed |= mLayoutAlgorithm.setSystemInsets(systemInsets);
-        if (changed) {
-            requestLayout();
-        }
-    }
-
-    /**
-     * This is called with the full window width and height to allow stack view children to
-     * perform the full screen transition down.
-     */
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        mInMeasureLayout = true;
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        int height = MeasureSpec.getSize(heightMeasureSpec);
-
-        // Update the stable stack bounds, but only update the current stack bounds if the stable
-        // bounds have changed.  This is because we may get spurious measures while dragging where
-        // our current stack bounds reflect the target drop region.
-        mLayoutAlgorithm.getTaskStackBounds(mDisplayRect, new Rect(0, 0, width, height),
-                mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.left,
-                mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
-        if (!mTmpRect.equals(mStableStackBounds)) {
-            mStableStackBounds.set(mTmpRect);
-            mStackBounds.set(mTmpRect);
-            mStableWindowRect.set(0, 0, width, height);
-            mWindowRect.set(0, 0, width, height);
-        }
-
-        // Compute the rects in the stack algorithm
-        mStableLayoutAlgorithm.initialize(mDisplayRect, mStableWindowRect, mStableStackBounds);
-        mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
-        updateLayoutAlgorithm(false /* boundScroll */);
-
-        // If this is the first layout, then scroll to the front of the stack, then update the
-        // TaskViews with the stack so that we can lay them out
-        boolean resetToInitialState = (width != mLastWidth || height != mLastHeight)
-                && mResetToInitialStateWhenResized;
-        if (!mFinishedLayoutAfterStackReload || mInitialState != INITIAL_STATE_UPDATE_NONE
-                || resetToInitialState) {
-            if (mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY || resetToInitialState) {
-                updateToInitialState();
-                mResetToInitialStateWhenResized = false;
-            }
-            if (mFinishedLayoutAfterStackReload) {
-                mInitialState = INITIAL_STATE_UPDATE_NONE;
-            }
-        }
-        // If we got the launch-next event before the first layout pass, then re-send it after the
-        // initial state has been updated
-        if (mLaunchNextAfterFirstMeasure) {
-            mLaunchNextAfterFirstMeasure = false;
-            EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
-        }
-
-        // Rebind all the views, including the ignore ones
-        bindVisibleTaskViews(mStackScroller.getStackScroll(), false /* ignoreTaskOverrides */);
-
-        // Measure each of the TaskViews
-        mTmpTaskViews.clear();
-        mTmpTaskViews.addAll(getTaskViews());
-        mTmpTaskViews.addAll(mViewPool.getViews());
-        int taskViewCount = mTmpTaskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            measureTaskView(mTmpTaskViews.get(i));
-        }
-        if (mTaskViewFocusFrame != null) {
-            mTaskViewFocusFrame.measure();
-        }
-
-        setMeasuredDimension(width, height);
-        mLastWidth = width;
-        mLastHeight = height;
-        mInMeasureLayout = false;
-    }
-
-    /**
-     * Measures a TaskView.
-     */
-    private void measureTaskView(TaskView tv) {
-        Rect padding = new Rect();
-        if (tv.getBackground() != null) {
-            tv.getBackground().getPadding(padding);
-        }
-        mTmpRect.set(mStableLayoutAlgorithm.getTaskRect());
-        mTmpRect.union(mLayoutAlgorithm.getTaskRect());
-        tv.measure(
-                MeasureSpec.makeMeasureSpec(mTmpRect.width() + padding.left + padding.right,
-                        MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(mTmpRect.height() + padding.top + padding.bottom,
-                        MeasureSpec.EXACTLY));
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        // Layout each of the TaskViews
-        mTmpTaskViews.clear();
-        mTmpTaskViews.addAll(getTaskViews());
-        mTmpTaskViews.addAll(mViewPool.getViews());
-        int taskViewCount = mTmpTaskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            layoutTaskView(changed, mTmpTaskViews.get(i));
-        }
-        if (mTaskViewFocusFrame != null) {
-            mTaskViewFocusFrame.layout();
-        }
-
-        if (changed) {
-            if (mStackScroller.isScrollOutOfBounds()) {
-                mStackScroller.boundScroll();
-            }
-        }
-
-        // Relayout all of the task views including the ignored ones
-        relayoutTaskViews(AnimationProps.IMMEDIATE);
-        clipTaskViews();
-
-        if (!mFinishedLayoutAfterStackReload) {
-            // Prepare the task enter animations (this can be called numerous times)
-            mInitialState = INITIAL_STATE_UPDATE_NONE;
-            onFirstLayout();
-
-            if (mStackReloaded) {
-                mFinishedLayoutAfterStackReload = true;
-                tryStartEnterAnimation();
-            }
-        }
-    }
-
-    /**
-     * Lays out a TaskView.
-     */
-    private void layoutTaskView(boolean changed, TaskView tv) {
-        if (changed) {
-            Rect padding = new Rect();
-            if (tv.getBackground() != null) {
-                tv.getBackground().getPadding(padding);
-            }
-            mTmpRect.set(mStableLayoutAlgorithm.getTaskRect());
-            mTmpRect.union(mLayoutAlgorithm.getTaskRect());
-            tv.cancelTransformAnimation();
-            tv.layout(mTmpRect.left - padding.left, mTmpRect.top - padding.top,
-                    mTmpRect.right + padding.right, mTmpRect.bottom + padding.bottom);
-        } else {
-            // If the layout has not changed, then just lay it out again in-place
-            tv.layout(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
-        }
-    }
-
-    /** Handler for the first layout. */
-    void onFirstLayout() {
-        // Setup the view for the enter animation
-        mAnimationHelper.prepareForEnterAnimation();
-
-        // Set the task focused state without requesting view focus, and leave the focus animations
-        // until after the enter-animation
-        RecentsConfiguration config = Recents.getConfiguration();
-        RecentsActivityLaunchState launchState = config.getLaunchState();
-
-        // We set the initial focused task view iff the following conditions are satisfied:
-        // 1. Recents is showing task views in stack layout.
-        // 2. Recents is launched with ALT + TAB.
-        boolean setFocusOnFirstLayout = !useGridLayout() || launchState.launchedWithAltTab;
-        if (setFocusOnFirstLayout) {
-            int focusedTaskIndex = getInitialFocusTaskIndex(launchState, mStack.getTaskCount(),
-                useGridLayout());
-            if (focusedTaskIndex != -1) {
-                setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
-                        false /* requestViewFocus */);
-            }
-        }
-        updateStackActionButtonVisibility();
-    }
-
-    public boolean isTouchPointInView(float x, float y, TaskView tv) {
-        mTmpRect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
-        mTmpRect.offset((int) tv.getTranslationX(), (int) tv.getTranslationY());
-        return mTmpRect.contains((int) x, (int) y);
-    }
-
-    /**
-     * Returns a non-ignored task in the {@param tasks} list that can be used as an achor when
-     * calculating the scroll position before and after a layout change.
-     */
-    public Task findAnchorTask(List<Task> tasks, MutableBoolean isFrontMostTask) {
-        for (int i = tasks.size() - 1; i >= 0; i--) {
-            Task task = tasks.get(i);
-
-            // Ignore deleting tasks
-            if (isIgnoredTask(task)) {
-                if (i == tasks.size() - 1) {
-                    isFrontMostTask.value = true;
-                }
-                continue;
-            }
-            return task;
-        }
-        return null;
-    }
-
-    /**** TaskStackCallbacks Implementation ****/
-
-    @Override
-    public void onStackTaskAdded(TaskStack stack, Task newTask) {
-        // Update the min/max scroll and animate other task views into their new positions
-        updateLayoutAlgorithm(true /* boundScroll */);
-
-        // Animate all the tasks into place
-        relayoutTaskViews(!mFinishedLayoutAfterStackReload
-                ? AnimationProps.IMMEDIATE
-                : new AnimationProps(DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN));
-    }
-
-    /**
-     * We expect that the {@link TaskView} associated with the removed task is already hidden.
-     */
-    @Override
-    public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
-            AnimationProps animation, boolean fromDockGesture, boolean dismissRecentsIfAllRemoved) {
-        if (mFocusedTask == removedTask) {
-            resetFocusedTask(removedTask);
-        }
-
-        // Remove the view associated with this task, we can't rely on updateTransforms
-        // to work here because the task is no longer in the list
-        TaskView tv = getChildViewForTask(removedTask);
-        if (tv != null) {
-            mViewPool.returnViewToPool(tv);
-        }
-
-        // Remove the task from the ignored set
-        removeIgnoreTask(removedTask);
-
-        // If requested, relayout with the given animation
-        if (animation != null) {
-            updateLayoutAlgorithm(true /* boundScroll */);
-            relayoutTaskViews(animation);
-        }
-
-        // Update the new front most task's action button
-        if (mScreenPinningEnabled && newFrontMostTask != null) {
-            TaskView frontTv = getChildViewForTask(newFrontMostTask);
-            if (frontTv != null) {
-                frontTv.showActionButton(true /* fadeIn */, DEFAULT_SYNC_STACK_DURATION);
-            }
-        }
-
-        // If there are no remaining tasks, then just close recents
-        if (mStack.getTaskCount() == 0) {
-            if (dismissRecentsIfAllRemoved) {
-                EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture
-                        ? R.string.recents_empty_message
-                        : R.string.recents_empty_message_dismissed_all));
-            } else {
-                EventBus.getDefault().send(new ShowEmptyViewEvent());
-            }
-        }
-    }
-
-    @Override
-    public void onStackTasksRemoved(TaskStack stack) {
-        // Reset the focused task
-        resetFocusedTask(getFocusedTask());
-
-        // Return all the views to the pool
-        List<TaskView> taskViews = new ArrayList<>();
-        taskViews.addAll(getTaskViews());
-        for (int i = taskViews.size() - 1; i >= 0; i--) {
-            mViewPool.returnViewToPool(taskViews.get(i));
-        }
-
-        // Remove all the ignore tasks
-        mIgnoreTasks.clear();
-
-        // If there are no remaining tasks, then just close recents
-        EventBus.getDefault().send(new AllTaskViewsDismissedEvent(
-                R.string.recents_empty_message_dismissed_all));
-    }
-
-    @Override
-    public void onStackTasksUpdated(TaskStack stack) {
-        if (!mFinishedLayoutAfterStackReload) {
-            return;
-        }
-
-        // Update the layout and immediately layout
-        updateLayoutAlgorithm(false /* boundScroll */);
-        relayoutTaskViews(AnimationProps.IMMEDIATE);
-
-        // Rebind all the task views.  This will not trigger new resources to be loaded
-        // unless they have actually changed
-        List<TaskView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            TaskView tv = taskViews.get(i);
-            bindTaskView(tv, tv.getTask());
-        }
-    }
-
-    /**** ViewPoolConsumer Implementation ****/
-
-    @Override
-    public TaskView createView(Context context) {
-        if (Recents.getConfiguration().isGridEnabled) {
-            return (GridTaskView) mInflater.inflate(R.layout.recents_grid_task_view, this, false);
-        } else {
-            return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
-        }
-    }
-
-    @Override
-    public void onReturnViewToPool(TaskView tv) {
-        final Task task = tv.getTask();
-
-        // Unbind the task from the task view
-        unbindTaskView(tv, task);
-
-        // Reset the view properties and view state
-        tv.clearAccessibilityFocus();
-        tv.resetViewProperties();
-        tv.setFocusedState(false, false /* requestViewFocus */);
-        tv.setClipViewInStack(false);
-        if (mScreenPinningEnabled) {
-            tv.hideActionButton(false /* fadeOut */, 0 /* duration */, false /* scaleDown */, null);
-        }
-
-        // Detach the view from the hierarchy
-        detachViewFromParent(tv);
-        // Update the task views list after removing the task view
-        updateTaskViewsList();
-    }
-
-    @Override
-    public void onPickUpViewFromPool(TaskView tv, Task task, boolean isNewView) {
-        // Find the index where this task should be placed in the stack
-        int taskIndex = mStack.indexOfTask(task);
-        int insertIndex = findTaskViewInsertIndex(task, taskIndex);
-
-        // Add/attach the view to the hierarchy
-        if (isNewView) {
-            if (mInMeasureLayout) {
-                // If we are measuring the layout, then just add the view normally as it will be
-                // laid out during the layout pass
-                addView(tv, insertIndex);
-            } else {
-                // Otherwise, this is from a bindVisibleTaskViews() call outside the measure/layout
-                // pass, and we should layout the new child ourselves
-                ViewGroup.LayoutParams params = tv.getLayoutParams();
-                if (params == null) {
-                    params = generateDefaultLayoutParams();
-                }
-                addViewInLayout(tv, insertIndex, params, true /* preventRequestLayout */);
-                measureTaskView(tv);
-                layoutTaskView(true /* changed */, tv);
-            }
-        } else {
-            attachViewToParent(tv, insertIndex, tv.getLayoutParams());
-        }
-        // Update the task views list after adding the new task view
-        updateTaskViewsList();
-
-        // Bind the task view to the new task
-        bindTaskView(tv, task);
-
-        // Set the new state for this view, including the callbacks and view clipping
-        tv.setCallbacks(this);
-        tv.setTouchEnabled(true);
-        tv.setClipViewInStack(true);
-        if (mFocusedTask == task) {
-            tv.setFocusedState(true, false /* requestViewFocus */);
-            if (mStartTimerIndicatorDuration > 0) {
-                // The timer indicator couldn't be started before, so start it now
-                tv.getHeaderView().startFocusTimerIndicator(mStartTimerIndicatorDuration);
-                mStartTimerIndicatorDuration = 0;
-            }
-        }
-
-        // Restore the action button visibility if it is the front most task view
-        if (mScreenPinningEnabled && tv.getTask() == mStack.getFrontMostTask()) {
-            tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
-        }
-    }
-
-    @Override
-    public boolean hasPreferredData(TaskView tv, Task preferredData) {
-        return (tv.getTask() == preferredData);
-    }
-
-    private void bindTaskView(TaskView tv, Task task) {
-        // Rebind the task and request that this task's data be filled into the TaskView
-        tv.onTaskBound(task, mTouchExplorationEnabled, mDisplayOrientation, mDisplayRect);
-
-        // If the doze trigger has already fired, then update the state for this task view
-        if (mUIDozeTrigger.isAsleep() ||
-                useGridLayout() || Recents.getConfiguration().isLowRamDevice) {
-            tv.setNoUserInteractionState();
-        }
-
-        if (task == mPrefetchingTask) {
-            task.notifyTaskDataLoaded(task.thumbnail, task.icon);
-        } else {
-            // Load the task data
-            Recents.getTaskLoader().loadTaskData(task);
-        }
-        Recents.getTaskLoader().getHighResThumbnailLoader().onTaskVisible(task);
-    }
-
-    private void unbindTaskView(TaskView tv, Task task) {
-        if (task != mPrefetchingTask) {
-            // Report that this task's data is no longer being used
-            Recents.getTaskLoader().unloadTaskData(task);
-        }
-        Recents.getTaskLoader().getHighResThumbnailLoader().onTaskInvisible(task);
-    }
-
-    private void updatePrefetchingTask(ArrayList<Task> tasks, int frontIndex, int backIndex) {
-        Task t = null;
-        boolean somethingVisible = frontIndex != -1 && backIndex != -1;
-        if (somethingVisible && frontIndex < tasks.size() - 1) {
-            t = tasks.get(frontIndex + 1);
-        }
-        if (mPrefetchingTask != t) {
-            if (mPrefetchingTask != null) {
-                int index = tasks.indexOf(mPrefetchingTask);
-                if (index < backIndex || index > frontIndex) {
-                    Recents.getTaskLoader().unloadTaskData(mPrefetchingTask);
-                }
-            }
-            mPrefetchingTask = t;
-            if (t != null) {
-                Recents.getTaskLoader().loadTaskData(t);
-            }
-        }
-    }
-
-    private void clearPrefetchingTask() {
-        if (mPrefetchingTask != null) {
-            Recents.getTaskLoader().unloadTaskData(mPrefetchingTask);
-        }
-        mPrefetchingTask = null;
-    }
-
-    /**** TaskViewCallbacks Implementation ****/
-
-    @Override
-    public void onTaskViewClipStateChanged(TaskView tv) {
-        if (!mTaskViewsClipDirty) {
-            mTaskViewsClipDirty = true;
-            invalidate();
-        }
-    }
-
-    /**** TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks ****/
-
-    @Override
-    public void onFocusStateChanged(int prevFocusState, int curFocusState) {
-        if (mDeferredTaskViewLayoutAnimation == null) {
-            mUIDozeTrigger.poke();
-            relayoutTaskViewsOnNextFrame(AnimationProps.IMMEDIATE);
-        }
-    }
-
-    /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
-
-    @Override
-    public void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation) {
-        mUIDozeTrigger.poke();
-        if (animation != null) {
-            relayoutTaskViewsOnNextFrame(animation);
-        }
-
-        // In grid layout, the stack action button always remains visible.
-        if (mEnterAnimationComplete && !useGridLayout()) {
-            if (Recents.getConfiguration().isLowRamDevice) {
-                // Show stack button when user drags down to show older tasks on low ram devices
-                if (mStack.getTaskCount() > 0 && !mStackActionButtonVisible
-                        && mTouchHandler.mIsScrolling && curScroll - prevScroll < 0) {
-                    // Going up
-                    EventBus.getDefault().send(
-                            new ShowStackActionButtonEvent(true /* translate */));
-                }
-                return;
-            }
-            if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
-                    curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
-                    mStack.getTaskCount() > 0) {
-                EventBus.getDefault().send(new ShowStackActionButtonEvent(true /* translate */));
-            } else if (prevScroll < HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
-                    curScroll >= HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
-                EventBus.getDefault().send(new HideStackActionButtonEvent());
-            }
-        }
-    }
-
-    /**** EventBus Events ****/
-
-    public final void onBusEvent(PackagesChangedEvent event) {
-        // Compute which components need to be removed
-        ArraySet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
-                event.packageName, event.userId);
-
-        // For other tasks, just remove them directly if they no longer exist
-        ArrayList<Task> tasks = mStack.getTasks();
-        for (int i = tasks.size() - 1; i >= 0; i--) {
-            final Task t = tasks.get(i);
-            if (removedComponents.contains(t.key.getComponent())) {
-                final TaskView tv = getChildViewForTask(t);
-                if (tv != null) {
-                    // For visible children, defer removing the task until after the animation
-                    tv.dismissTask();
-                } else {
-                    // Otherwise, remove the task from the stack immediately
-                    mStack.removeTask(t, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
-                }
-            }
-        }
-    }
-
-    public final void onBusEvent(LaunchTaskEvent event) {
-        // Cancel any doze triggers once a task is launched
-        mUIDozeTrigger.stopDozing();
-    }
-
-    public final void onBusEvent(LaunchMostRecentTaskRequestEvent event) {
-        if (mStack.getTaskCount() > 0) {
-            Task mostRecentTask = mStack.getFrontMostTask();
-            launchTask(mostRecentTask);
-        }
-    }
-
-    public final void onBusEvent(ShowStackActionButtonEvent event) {
-        mStackActionButtonVisible = true;
-    }
-
-    public final void onBusEvent(HideStackActionButtonEvent event) {
-        mStackActionButtonVisible = false;
-    }
-
-    public final void onBusEvent(LaunchNextTaskRequestEvent event) {
-        if (!mFinishedLayoutAfterStackReload) {
-            mLaunchNextAfterFirstMeasure = true;
-            return;
-        }
-
-        if (mStack.getTaskCount() == 0) {
-            if (RecentsImpl.getLastPipTime() != -1) {
-                EventBus.getDefault().send(new ExpandPipEvent());
-                MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
-                        "pip");
-            } else {
-                // If there are no tasks, then just hide recents back to home.
-                EventBus.getDefault().send(new HideRecentsEvent(false, true));
-            }
-            return;
-        }
-
-        if (!Recents.getConfiguration().getLaunchState().launchedFromPipApp
-                && mStack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime())) {
-            // If the launch task is in the pinned stack, then expand the PiP now
-            EventBus.getDefault().send(new ExpandPipEvent());
-            MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK, "pip");
-        } else {
-            final Task launchTask = mStack.getNextLaunchTarget();
-            if (launchTask != null) {
-                // Defer launching the task until the PiP menu has been dismissed (if it is
-                // showing at all)
-                HidePipMenuEvent hideMenuEvent = new HidePipMenuEvent();
-                hideMenuEvent.addPostAnimationCallback(() -> {
-                    launchTask(launchTask);
-                });
-                EventBus.getDefault().send(hideMenuEvent);
-                MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
-                        launchTask.key.getComponent().toString());
-            }
-        }
-    }
-
-    public final void onBusEvent(LaunchTaskStartedEvent event) {
-        mAnimationHelper.startLaunchTaskAnimation(event.taskView, event.screenPinningRequested,
-                event.getAnimationTrigger());
-    }
-
-    public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
-        // Stop any scrolling
-        mTouchHandler.cancelNonDismissTaskAnimations();
-        mStackScroller.stopScroller();
-        mStackScroller.stopBoundScrollAnimation();
-        cancelDeferredTaskViewLayoutAnimation();
-
-        // Start the task animations
-        mAnimationHelper.startExitToHomeAnimation(event.animated, event.getAnimationTrigger());
-
-        // Dismiss the grid task view focus frame
-        if (mTaskViewFocusFrame != null) {
-            mTaskViewFocusFrame.moveGridTaskViewFocus(null);
-        }
-    }
-
-    public final void onBusEvent(DismissFocusedTaskViewEvent event) {
-        if (mFocusedTask != null) {
-            if (mTaskViewFocusFrame != null) {
-                mTaskViewFocusFrame.moveGridTaskViewFocus(null);
-            }
-            TaskView tv = getChildViewForTask(mFocusedTask);
-            if (tv != null) {
-                tv.dismissTask();
-            }
-            resetFocusedTask(mFocusedTask);
-        }
-    }
-
-    public final void onBusEvent(DismissTaskViewEvent event) {
-        // For visible children, defer removing the task until after the animation
-        mAnimationHelper.startDeleteTaskAnimation(
-                event.taskView, useGridLayout(), event.getAnimationTrigger());
-    }
-
-    public final void onBusEvent(final DismissAllTaskViewsEvent event) {
-        // Keep track of the tasks which will have their data removed
-        ArrayList<Task> tasks = new ArrayList<>(mStack.getTasks());
-        mAnimationHelper.startDeleteAllTasksAnimation(
-                getTaskViews(), useGridLayout(), event.getAnimationTrigger());
-        event.addPostAnimationCallback(new Runnable() {
-            @Override
-            public void run() {
-                // Announce for accessibility
-                announceForAccessibility(getContext().getString(
-                        R.string.accessibility_recents_all_items_dismissed));
-
-                // Remove all tasks and delete the task data for all tasks
-                mStack.removeAllTasks(true /* notifyStackChanges */);
-                for (int i = tasks.size() - 1; i >= 0; i--) {
-                    EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));
-                }
-
-                MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS_ALL);
-            }
-        });
-
-    }
-
-    public final void onBusEvent(TaskViewDismissedEvent event) {
-        // Announce for accessibility
-        announceForAccessibility(getContext().getString(
-                R.string.accessibility_recents_item_dismissed, event.task.title));
-
-        if (useGridLayout() && event.animation != null) {
-            event.animation.setListener(new AnimatorListenerAdapter() {
-                public void onAnimationEnd(Animator animator) {
-                    if (mTaskViewFocusFrame != null) {
-                        // Resize the grid layout task view focus frame
-                        mTaskViewFocusFrame.resize();
-                    }
-                }
-            });
-        }
-
-        // Remove the task from the stack
-        mStack.removeTask(event.task, event.animation, false /* fromDockGesture */);
-        EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
-        if (mStack.getTaskCount() > 0 && Recents.getConfiguration().isLowRamDevice) {
-            EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
-        }
-
-        MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
-                event.task.key.getComponent().toString());
-    }
-
-    public final void onBusEvent(FocusNextTaskViewEvent event) {
-        // Stop any scrolling
-        mStackScroller.stopScroller();
-        mStackScroller.stopBoundScrollAnimation();
-
-        setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false, 0);
-    }
-
-    public final void onBusEvent(FocusPreviousTaskViewEvent event) {
-        // Stop any scrolling
-        mStackScroller.stopScroller();
-        mStackScroller.stopBoundScrollAnimation();
-
-        setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
-    }
-
-    public final void onBusEvent(NavigateTaskViewEvent event) {
-        if (useGridLayout()) {
-            final int taskCount = mStack.getTaskCount();
-            final int currentIndex = mStack.indexOfTask(getFocusedTask());
-            final int nextIndex = mLayoutAlgorithm.mTaskGridLayoutAlgorithm.navigateFocus(taskCount,
-                    currentIndex, event.direction);
-            setFocusedTask(nextIndex, false, true);
-        } else {
-            switch (event.direction) {
-                case UP:
-                    EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
-                    break;
-                case DOWN:
-                    EventBus.getDefault().send(new FocusNextTaskViewEvent());
-                    break;
-            }
-        }
-    }
-
-    public final void onBusEvent(UserInteractionEvent event) {
-        // Poke the doze trigger on user interaction
-        mUIDozeTrigger.poke();
-
-        RecentsDebugFlags debugFlags = Recents.getDebugFlags();
-        if (mFocusedTask != null) {
-            TaskView tv = getChildViewForTask(mFocusedTask);
-            if (tv != null) {
-                tv.getHeaderView().cancelFocusTimerIndicator();
-            }
-        }
-    }
-
-    public final void onBusEvent(DragStartEvent event) {
-        // Ensure that the drag task is not animated
-        addIgnoreTask(event.task);
-
-        // Enlarge the dragged view slightly
-        float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
-        mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
-                mTmpTransform, null);
-        mTmpTransform.scale = finalScale;
-        mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
-        mTmpTransform.dimAlpha = 0f;
-        updateTaskViewToTransform(event.taskView, mTmpTransform,
-                new AnimationProps(DRAG_SCALE_DURATION, Interpolators.FAST_OUT_SLOW_IN));
-    }
-
-    public final void onBusEvent(DragDropTargetChangedEvent event) {
-        AnimationProps animation = new AnimationProps(SLOW_SYNC_STACK_DURATION,
-                Interpolators.FAST_OUT_SLOW_IN);
-        boolean ignoreTaskOverrides = false;
-        if (event.dropTarget instanceof DockState) {
-            // Calculate the new task stack bounds that matches the window size that Recents will
-            // have after the drop
-            final DockState dockState = (DockState) event.dropTarget;
-            Rect systemInsets = new Rect(mStableLayoutAlgorithm.mSystemInsets);
-            // When docked, the nav bar insets are consumed and the activity is measured without
-            // insets.  However, the window bounds include the insets, so we need to subtract them
-            // here to make them identical.
-            int height = getMeasuredHeight();
-            height -= systemInsets.bottom;
-            systemInsets.bottom = 0;
-            mStackBounds.set(dockState.getDockedTaskStackBounds(mDisplayRect, getMeasuredWidth(),
-                    height, mDividerSize, systemInsets,
-                    mLayoutAlgorithm, getResources(), mWindowRect));
-            mLayoutAlgorithm.setSystemInsets(systemInsets);
-            mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
-            updateLayoutAlgorithm(true /* boundScroll */);
-            ignoreTaskOverrides = true;
-        } else {
-            // Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging
-            // task view, so add it back to the ignore set after updating the layout
-            removeIgnoreTask(event.task);
-            updateLayoutToStableBounds();
-            addIgnoreTask(event.task);
-        }
-        relayoutTaskViews(animation, null /* animationOverrides */, ignoreTaskOverrides);
-    }
-
-    public final void onBusEvent(final DragEndEvent event) {
-        // We don't handle drops on the dock regions
-        if (event.dropTarget instanceof DockState) {
-            // However, we do need to reset the overrides, since the last state of this task stack
-            // view layout was ignoring task overrides (see DragDropTargetChangedEvent handler)
-            mLayoutAlgorithm.clearUnfocusedTaskOverrides();
-            return;
-        }
-
-        // Restore the task, so that relayout will apply to it below
-        removeIgnoreTask(event.task);
-
-        // Convert the dragging task view back to its final layout-space rect
-        Utilities.setViewFrameFromTranslation(event.taskView);
-
-        // Animate all the tasks into place
-        ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>();
-        animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION,
-                Interpolators.FAST_OUT_SLOW_IN,
-                event.getAnimationTrigger().decrementOnAnimationEnd()));
-        relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION,
-                Interpolators.FAST_OUT_SLOW_IN));
-        event.getAnimationTrigger().increment();
-    }
-
-    public final void onBusEvent(final DragEndCancelledEvent event) {
-        // Restore the pre-drag task stack bounds, including the dragging task view
-        removeIgnoreTask(event.task);
-        updateLayoutToStableBounds();
-
-        // Convert the dragging task view back to its final layout-space rect
-        Utilities.setViewFrameFromTranslation(event.taskView);
-
-        // Animate all the tasks into place
-        ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>();
-        animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION,
-                Interpolators.FAST_OUT_SLOW_IN,
-                event.getAnimationTrigger().decrementOnAnimationEnd()));
-        relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION,
-                Interpolators.FAST_OUT_SLOW_IN));
-        event.getAnimationTrigger().increment();
-    }
-
-    public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
-        mEnterAnimationComplete = true;
-        tryStartEnterAnimation();
-    }
-
-    private void tryStartEnterAnimation() {
-        if (!mStackReloaded || !mFinishedLayoutAfterStackReload || !mEnterAnimationComplete) {
-            return;
-        }
-
-        if (mStack.getTaskCount() > 0) {
-            // Start the task enter animations
-            ReferenceCountedTrigger trigger = new ReferenceCountedTrigger();
-            mAnimationHelper.startEnterAnimation(trigger);
-
-            // Add a runnable to the post animation ref counter to clear all the views
-            trigger.addLastDecrementRunnable(() -> {
-                // Start the dozer to trigger to trigger any UI that shows after a timeout
-                mUIDozeTrigger.startDozing();
-
-                // Update the focused state here -- since we only set the focused task without
-                // requesting view focus in onFirstLayout(), actually request view focus and
-                // animate the focused state if we are alt-tabbing now, after the window enter
-                // animation is completed
-                if (mFocusedTask != null) {
-                    RecentsConfiguration config = Recents.getConfiguration();
-                    RecentsActivityLaunchState launchState = config.getLaunchState();
-                    setFocusedTask(mStack.indexOfTask(mFocusedTask),
-                            false /* scrollToTask */, launchState.launchedWithAltTab);
-                    TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
-                    if (mTouchExplorationEnabled && focusedTaskView != null) {
-                        focusedTaskView.requestAccessibilityFocus();
-                    }
-                }
-            });
-        }
-
-        // This flag is only used to choreograph the enter animation, so we can reset it here
-        mStackReloaded = false;
-    }
-
-    public final void onBusEvent(final MultiWindowStateChangedEvent event) {
-        if (event.inMultiWindow || !event.showDeferredAnimation) {
-            setTasks(event.stack, true /* allowNotifyStackChanges */);
-        } else {
-            // Reset the launch state before handling the multiwindow change
-            RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-            launchState.reset();
-
-            // Defer until the next frame to ensure that we have received all the system insets, and
-            // initial layout updates
-            event.getAnimationTrigger().increment();
-            post(new Runnable() {
-                @Override
-                public void run() {
-                    // Scroll the stack to the front to see the undocked task
-                    mAnimationHelper.startNewStackScrollAnimation(event.stack,
-                            event.getAnimationTrigger());
-                    event.getAnimationTrigger().decrement();
-                }
-            });
-        }
-    }
-
-    public final void onBusEvent(ConfigurationChangedEvent event) {
-        if (event.fromDeviceOrientationChange) {
-            mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
-            mDisplayRect = Recents.getSystemServices().getDisplayRect();
-
-            // Always stop the scroller, otherwise, we may continue setting the stack scroll to the
-            // wrong bounds in the new layout
-            mStackScroller.stopScroller();
-        }
-        reloadOnConfigurationChange();
-
-        // Notify the task views of the configuration change so they can reload their resources
-        if (!event.fromMultiWindow) {
-            mTmpTaskViews.clear();
-            mTmpTaskViews.addAll(getTaskViews());
-            mTmpTaskViews.addAll(mViewPool.getViews());
-            int taskViewCount = mTmpTaskViews.size();
-            for (int i = 0; i < taskViewCount; i++) {
-                mTmpTaskViews.get(i).onConfigurationChanged();
-            }
-        }
-
-        // Update the Clear All button in case we're switching in or out of grid layout.
-        updateStackActionButtonVisibility();
-
-        // Trigger a new layout and update to the initial state if necessary. When entering split
-        // screen, the multi-window configuration change event can happen after the stack is already
-        // reloaded (but pending measure/layout), in this case, do not override the intiial state
-        // and just wait for the upcoming measure/layout pass.
-        if (event.fromMultiWindow && mInitialState == INITIAL_STATE_UPDATE_NONE) {
-            mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY;
-            requestLayout();
-        } else if (event.fromDeviceOrientationChange) {
-            mInitialState = INITIAL_STATE_UPDATE_ALL;
-            requestLayout();
-        }
-    }
-
-    public final void onBusEvent(RecentsGrowingEvent event) {
-        mResetToInitialStateWhenResized = true;
-    }
-
-    public final void onBusEvent(RecentsVisibilityChangedEvent event) {
-        if (!event.visible) {
-            if (mTaskViewFocusFrame != null) {
-                mTaskViewFocusFrame.moveGridTaskViewFocus(null);
-            }
-
-            List<TaskView> taskViews = new ArrayList<>(getTaskViews());
-            for (int i = 0; i < taskViews.size(); i++) {
-                mViewPool.returnViewToPool(taskViews.get(i));
-            }
-            clearPrefetchingTask();
-
-            // We can not reset mEnterAnimationComplete in onReload() because when docking the top
-            // task, we can receive the enter animation callback before onReload(), so reset it
-            // here onces Recents is not visible
-            mEnterAnimationComplete = false;
-        }
-    }
-
-    public final void onBusEvent(ActivityPinnedEvent event) {
-        // If an activity enters PiP while Recents is open, remove the stack task associated with
-        // the new PiP task
-        Task removeTask = mStack.findTaskWithId(event.taskId);
-        if (removeTask != null) {
-            // In this case, we remove the task, but if the last task is removed, don't dismiss
-            // Recents to home
-            mStack.removeTask(removeTask, AnimationProps.IMMEDIATE, false /* fromDockGesture */,
-                    false /* dismissRecentsIfAllRemoved */);
-        }
-        updateLayoutAlgorithm(false /* boundScroll */);
-        updateToInitialState();
-    }
-
-    public void reloadOnConfigurationChange() {
-        mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
-        mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
-    }
-
-    /**
-     * Returns the insert index for the task in the current set of task views. If the given task
-     * is already in the task view list, then this method returns the insert index assuming it
-     * is first removed at the previous index.
-     *
-     * @param task the task we are finding the index for
-     * @param taskIndex the index of the task in the stack
-     */
-    private int findTaskViewInsertIndex(Task task, int taskIndex) {
-        if (taskIndex != -1) {
-            List<TaskView> taskViews = getTaskViews();
-            boolean foundTaskView = false;
-            int taskViewCount = taskViews.size();
-            for (int i = 0; i < taskViewCount; i++) {
-                Task tvTask = taskViews.get(i).getTask();
-                if (tvTask == task) {
-                    foundTaskView = true;
-                } else if (taskIndex < mStack.indexOfTask(tvTask)) {
-                    if (foundTaskView) {
-                        return i - 1;
-                    } else {
-                        return i;
-                    }
-                }
-            }
-        }
-        return -1;
-    }
-
-    private void launchTask(Task task) {
-        // Stop all animations
-        cancelAllTaskViewAnimations();
-
-        float curScroll = mStackScroller.getStackScroll();
-        float targetScroll = mLayoutAlgorithm.getStackScrollForTaskAtInitialOffset(task);
-        float absScrollDiff = Math.abs(targetScroll - curScroll);
-        if (getChildViewForTask(task) == null || absScrollDiff > 0.35f) {
-            int duration = (int) (LAUNCH_NEXT_SCROLL_BASE_DURATION +
-                    absScrollDiff * LAUNCH_NEXT_SCROLL_INCR_DURATION);
-            mStackScroller.animateScroll(targetScroll,
-                    duration, new Runnable() {
-                        @Override
-                        public void run() {
-                            EventBus.getDefault().send(new LaunchTaskEvent(
-                                    getChildViewForTask(task), task, null,
-                                    false /* screenPinningRequested */));
-                        }
-                    });
-        } else {
-            EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(task), task, null,
-                    false /* screenPinningRequested */));
-        }
-    }
-
-    /**
-     * Check whether we should use the grid layout.
-     */
-    public boolean useGridLayout() {
-        return mLayoutAlgorithm.useGridLayout();
-    }
-
-    /**
-     * Reads current system flags related to accessibility and screen pinning.
-     */
-    private void readSystemFlags() {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
-        mScreenPinningEnabled = ActivityManagerWrapper.getInstance().isScreenPinningEnabled()
-                && !ActivityManagerWrapper.getInstance().isLockToAppActive();
-    }
-
-    private void updateStackActionButtonVisibility() {
-        if (Recents.getConfiguration().isLowRamDevice) {
-            return;
-        }
-
-        // Always show the button in grid layout.
-        if (useGridLayout() ||
-                (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
-                        mStack.getTaskCount() > 0)) {
-            EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
-        } else {
-            EventBus.getDefault().send(new HideStackActionButtonEvent());
-        }
-    }
-
-    /**
-     * Returns the task to focus given the current launch state.
-     */
-    private int getInitialFocusTaskIndex(RecentsActivityLaunchState launchState, int numTasks,
-            boolean useGridLayout) {
-        if (launchState.launchedFromApp) {
-            if (useGridLayout) {
-                // If coming from another app to the grid layout, focus the front most task
-                return numTasks - 1;
-            }
-
-            // If coming from another app, focus the next task
-            return Math.max(0, numTasks - 2);
-        } else {
-            // If coming from home, focus the front most task
-            return numTasks - 1;
-        }
-    }
-
-    /**
-     * Updates {@param transforms} to be the same size as {@param tasks}.
-     */
-    private void matchTaskListSize(List<Task> tasks, List<TaskViewTransform> transforms) {
-        // We can reuse the task transforms where possible to reduce object allocation
-        int taskTransformCount = transforms.size();
-        int taskCount = tasks.size();
-        if (taskTransformCount < taskCount) {
-            // If there are less transforms than tasks, then add as many transforms as necessary
-            for (int i = taskTransformCount; i < taskCount; i++) {
-                transforms.add(new TaskViewTransform());
-            }
-        } else if (taskTransformCount > taskCount) {
-            // If there are more transforms than tasks, then just subset the transform list
-            transforms.subList(taskCount, taskTransformCount).clear();
-        }
-    }
-
-    public void dump(String prefix, PrintWriter writer) {
-        String innerPrefix = prefix + "  ";
-        String id = Integer.toHexString(System.identityHashCode(this));
-
-        writer.print(prefix); writer.print(TAG);
-        writer.print(" hasDefRelayout=");
-        writer.print(mDeferredTaskViewLayoutAnimation != null ? "Y" : "N");
-        writer.print(" clipDirty="); writer.print(mTaskViewsClipDirty ? "Y" : "N");
-        writer.print(" awaitingStackReload="); writer.print(mFinishedLayoutAfterStackReload ? "Y" : "N");
-        writer.print(" initialState="); writer.print(mInitialState);
-        writer.print(" inMeasureLayout="); writer.print(mInMeasureLayout ? "Y" : "N");
-        writer.print(" enterAnimCompleted="); writer.print(mEnterAnimationComplete ? "Y" : "N");
-        writer.print(" touchExplorationOn="); writer.print(mTouchExplorationEnabled ? "Y" : "N");
-        writer.print(" screenPinningOn="); writer.print(mScreenPinningEnabled ? "Y" : "N");
-        writer.print(" numIgnoreTasks="); writer.print(mIgnoreTasks.size());
-        writer.print(" numViewPool="); writer.print(mViewPool.getViews().size());
-        writer.print(" stableStackBounds="); writer.print(Utilities.dumpRect(mStableStackBounds));
-        writer.print(" stackBounds="); writer.print(Utilities.dumpRect(mStackBounds));
-        writer.print(" stableWindow="); writer.print(Utilities.dumpRect(mStableWindowRect));
-        writer.print(" window="); writer.print(Utilities.dumpRect(mWindowRect));
-        writer.print(" display="); writer.print(Utilities.dumpRect(mDisplayRect));
-        writer.print(" orientation="); writer.print(mDisplayOrientation);
-        writer.print(" [0x"); writer.print(id); writer.print("]");
-        writer.println();
-
-        if (mFocusedTask != null) {
-            writer.print(innerPrefix);
-            writer.print("Focused task: ");
-            mFocusedTask.dump("", writer);
-        }
-
-        int numTaskViews = mTaskViews.size();
-        for (int i = 0; i < numTaskViews; i++) {
-            mTaskViews.get(i).dump(innerPrefix, writer);
-        }
-
-        mLayoutAlgorithm.dump(innerPrefix, writer);
-        mStackScroller.dump(innerPrefix, writer);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
deleted file mode 100644
index 6b23977..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.util.FloatProperty;
-import android.util.Log;
-import android.util.Property;
-import android.view.ViewConfiguration;
-import android.view.ViewDebug;
-import android.widget.OverScroller;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-
-import java.io.PrintWriter;
-
-/* The scrolling logic for a TaskStackView */
-public class TaskStackViewScroller {
-
-    private static final String TAG = "TaskStackViewScroller";
-    private static final boolean DEBUG = false;
-
-    public interface TaskStackViewScrollerCallbacks {
-        void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation);
-    }
-
-    /**
-     * A Property wrapper around the <code>stackScroll</code> functionality handled by the
-     * {@link #setStackScroll(float)} and
-     * {@link #getStackScroll()} methods.
-     */
-    private static final Property<TaskStackViewScroller, Float> STACK_SCROLL =
-            new FloatProperty<TaskStackViewScroller>("stackScroll") {
-                @Override
-                public void setValue(TaskStackViewScroller object, float value) {
-                    object.setStackScroll(value);
-                }
-
-                @Override
-                public Float get(TaskStackViewScroller object) {
-                    return object.getStackScroll();
-                }
-            };
-
-    Context mContext;
-    TaskStackLayoutAlgorithm mLayoutAlgorithm;
-    TaskStackViewScrollerCallbacks mCb;
-
-    @ViewDebug.ExportedProperty(category="recents")
-    float mStackScrollP;
-    @ViewDebug.ExportedProperty(category="recents")
-    float mLastDeltaP = 0f;
-    float mFlingDownScrollP;
-    int mFlingDownY;
-
-    OverScroller mScroller;
-    ObjectAnimator mScrollAnimator;
-    float mFinalAnimatedScroll;
-
-    final FlingAnimationUtils mFlingAnimationUtils;
-
-    public TaskStackViewScroller(Context context, TaskStackViewScrollerCallbacks cb,
-            TaskStackLayoutAlgorithm layoutAlgorithm) {
-        mContext = context;
-        mCb = cb;
-        mScroller = new OverScroller(context);
-        if (Recents.getConfiguration().isLowRamDevice) {
-            mScroller.setFriction(0.06f);
-        }
-        mLayoutAlgorithm = layoutAlgorithm;
-        mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
-    }
-
-    /** Resets the task scroller. */
-    void reset() {
-        mStackScrollP = 0f;
-        mLastDeltaP = 0f;
-    }
-
-    void resetDeltaScroll() {
-        mLastDeltaP = 0f;
-    }
-
-    /** Gets the current stack scroll */
-    public float getStackScroll() {
-        return mStackScrollP;
-    }
-
-    /**
-     * Sets the current stack scroll immediately.
-     */
-    public void setStackScroll(float s) {
-        setStackScroll(s, AnimationProps.IMMEDIATE);
-    }
-
-    /**
-     * Sets the current stack scroll immediately, and returns the difference between the target
-     * scroll and the actual scroll after accounting for the effect on the focus state.
-     */
-    public float setDeltaStackScroll(float downP, float deltaP) {
-        float targetScroll = downP + deltaP;
-        float newScroll = mLayoutAlgorithm.updateFocusStateOnScroll(downP + mLastDeltaP, targetScroll,
-                mStackScrollP);
-        setStackScroll(newScroll, AnimationProps.IMMEDIATE);
-        mLastDeltaP = deltaP;
-        return newScroll - targetScroll;
-    }
-
-    /**
-     * Sets the current stack scroll, but indicates to the callback the preferred animation to
-     * update to this new scroll.
-     */
-    public void setStackScroll(float newScroll, AnimationProps animation) {
-        float prevScroll = mStackScrollP;
-        mStackScrollP = newScroll;
-        if (mCb != null) {
-            mCb.onStackScrollChanged(prevScroll, mStackScrollP, animation);
-        }
-    }
-
-    /**
-     * Sets the current stack scroll to the initial state when you first enter recents.
-     * @return whether the stack progress changed.
-     */
-    public boolean setStackScrollToInitialState() {
-        float prevScroll = mStackScrollP;
-        setStackScroll(mLayoutAlgorithm.mInitialScrollP);
-        return Float.compare(prevScroll, mStackScrollP) != 0;
-    }
-
-    /**
-     * Starts a fling that is coordinated with the {@link TaskStackViewTouchHandler}.
-     */
-    public void fling(float downScrollP, int downY, int y, int velY, int minY, int maxY,
-            int overscroll) {
-        if (DEBUG) {
-            Log.d(TAG, "fling: " + downScrollP + ", downY: " + downY + ", y: " + y +
-                    ", velY: " + velY + ", minY: " + minY + ", maxY: " + maxY);
-        }
-        mFlingDownScrollP = downScrollP;
-        mFlingDownY = downY;
-        mScroller.fling(0, y, 0, velY, 0, 0, minY, maxY, 0, overscroll);
-    }
-
-    /** Bounds the current scroll if necessary */
-    public boolean boundScroll() {
-        float curScroll = getStackScroll();
-        float newScroll = getBoundedStackScroll(curScroll);
-        if (Float.compare(newScroll, curScroll) != 0) {
-            setStackScroll(newScroll);
-            return true;
-        }
-        return false;
-    }
-
-    /** Returns the bounded stack scroll */
-    float getBoundedStackScroll(float scroll) {
-        return Utilities.clamp(scroll, mLayoutAlgorithm.mMinScrollP, mLayoutAlgorithm.mMaxScrollP);
-    }
-
-    /** Returns the amount that the absolute value of how much the scroll is out of bounds. */
-    float getScrollAmountOutOfBounds(float scroll) {
-        if (scroll < mLayoutAlgorithm.mMinScrollP) {
-            return Math.abs(scroll - mLayoutAlgorithm.mMinScrollP);
-        } else if (scroll > mLayoutAlgorithm.mMaxScrollP) {
-            return Math.abs(scroll - mLayoutAlgorithm.mMaxScrollP);
-        }
-        return 0f;
-    }
-
-    /** Returns whether the specified scroll is out of bounds */
-    boolean isScrollOutOfBounds() {
-        return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0;
-    }
-
-    /**
-     * Scrolls the closest task and snaps into place. Only used in recents for low ram devices.
-     * @param velocity of scroll
-     */
-    void scrollToClosestTask(int velocity) {
-        float stackScroll = getStackScroll();
-
-        // Skip if not in low ram layout and if the scroll is out of min and max bounds
-        if (!Recents.getConfiguration().isLowRamDevice || stackScroll < mLayoutAlgorithm.mMinScrollP
-                || stackScroll > mLayoutAlgorithm.mMaxScrollP) {
-            return;
-        }
-        TaskStackLowRamLayoutAlgorithm algorithm = mLayoutAlgorithm.mTaskStackLowRamLayoutAlgorithm;
-
-        float flingThreshold = ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity();
-        if (Math.abs(velocity) > flingThreshold) {
-            int minY = algorithm.percentageToScroll(mLayoutAlgorithm.mMinScrollP);
-            int maxY = algorithm.percentageToScroll(mLayoutAlgorithm.mMaxScrollP);
-
-            // Calculate the fling and snap to closest task from final y position, computeScroll()
-            // never runs when cancelled with animateScroll() and the overscroll is not calculated
-            // here
-            fling(0 /* downScrollP */, 0 /* downY */, algorithm.percentageToScroll(stackScroll),
-                    -velocity, minY, maxY, 0 /* overscroll */);
-            float pos = algorithm.scrollToPercentage(mScroller.getFinalY());
-
-            float newScrollP = algorithm.getClosestTaskP(pos, mLayoutAlgorithm.mNumStackTasks,
-                    velocity);
-            ValueAnimator animator = ObjectAnimator.ofFloat(stackScroll, newScrollP);
-            mFlingAnimationUtils.apply(animator, algorithm.percentageToScroll(stackScroll),
-                    algorithm.percentageToScroll(newScrollP), velocity);
-            animateScroll(newScrollP, (int) animator.getDuration(), animator.getInterpolator(),
-                    null /* postRunnable */);
-        } else {
-            float newScrollP = algorithm.getClosestTaskP(stackScroll,
-                    mLayoutAlgorithm.mNumStackTasks, velocity);
-            animateScroll(newScrollP, 300, Interpolators.ACCELERATE_DECELERATE,
-                    null /* postRunnable */);
-        }
-    }
-
-    /** Animates the stack scroll into bounds */
-    ObjectAnimator animateBoundScroll() {
-        // TODO: Take duration for snap back
-        float curScroll = getStackScroll();
-        float newScroll = getBoundedStackScroll(curScroll);
-        if (Float.compare(newScroll, curScroll) != 0) {
-            // Start a new scroll animation
-            animateScroll(newScroll, null /* postScrollRunnable */);
-        }
-        return mScrollAnimator;
-    }
-
-    /** Animates the stack scroll */
-    void animateScroll(float newScroll, final Runnable postRunnable) {
-        int duration = mContext.getResources().getInteger(
-                R.integer.recents_animate_task_stack_scroll_duration);
-        animateScroll(newScroll, duration, postRunnable);
-    }
-
-    /** Animates the stack scroll */
-    void animateScroll(float newScroll, int duration, final Runnable postRunnable) {
-        animateScroll(newScroll, duration, Interpolators.LINEAR_OUT_SLOW_IN, postRunnable);
-    }
-
-    /** Animates the stack scroll with time interpolator */
-    void animateScroll(float newScroll, int duration, TimeInterpolator interpolator,
-            final Runnable postRunnable) {
-        ObjectAnimator an = ObjectAnimator.ofFloat(this, STACK_SCROLL, getStackScroll(), newScroll);
-        an.setDuration(duration);
-        an.setInterpolator(interpolator);
-        animateScroll(newScroll, an, postRunnable);
-    }
-
-    /** Animates the stack scroll with animator */
-    private void animateScroll(float newScroll, ObjectAnimator animator,
-            final Runnable postRunnable) {
-        // Finish any current scrolling animations
-        if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
-            setStackScroll(mFinalAnimatedScroll);
-            mScroller.forceFinished(true);
-        }
-        stopScroller();
-        stopBoundScrollAnimation();
-
-        if (Float.compare(mStackScrollP, newScroll) != 0) {
-            mFinalAnimatedScroll = newScroll;
-            mScrollAnimator = animator;
-            mScrollAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (postRunnable != null) {
-                        postRunnable.run();
-                    }
-                    mScrollAnimator.removeAllListeners();
-                }
-            });
-            mScrollAnimator.start();
-        } else {
-            if (postRunnable != null) {
-                postRunnable.run();
-            }
-        }
-    }
-
-    /** Aborts any current stack scrolls */
-    void stopBoundScrollAnimation() {
-        Utilities.cancelAnimationWithoutCallbacks(mScrollAnimator);
-    }
-
-    /**** OverScroller ****/
-
-    /** Called from the view draw, computes the next scroll. */
-    boolean computeScroll() {
-        if (mScroller.computeScrollOffset()) {
-            float deltaP = mLayoutAlgorithm.getDeltaPForY(mFlingDownY, mScroller.getCurrY());
-            mFlingDownScrollP += setDeltaStackScroll(mFlingDownScrollP, deltaP);
-            if (DEBUG) {
-                Log.d(TAG, "computeScroll: " + (mFlingDownScrollP + deltaP));
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /** Returns whether the overscroller is scrolling. */
-    boolean isScrolling() {
-        return !mScroller.isFinished();
-    }
-
-    float getScrollVelocity() {
-        return mScroller.getCurrVelocity();
-    }
-
-    /** Stops the scroller and any current fling. */
-    void stopScroller() {
-        if (!mScroller.isFinished()) {
-            mScroller.abortAnimation();
-        }
-    }
-
-    public void dump(String prefix, PrintWriter writer) {
-        writer.print(prefix); writer.print(TAG);
-        writer.print(" stackScroll:"); writer.print(mStackScrollP);
-        writer.println();
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
deleted file mode 100644
index c91cdfc..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ /dev/null
@@ -1,705 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Path;
-import android.util.ArrayMap;
-import android.util.MutableBoolean;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewDebug;
-import android.view.ViewParent;
-import android.view.animation.Interpolator;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.SwipeHelper;
-import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
-import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
-import com.android.systemui.recents.misc.FreePathInterpolator;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Handles touch events for a TaskStackView.
- */
-class TaskStackViewTouchHandler implements SwipeHelper.Callback {
-
-    private static final int INACTIVE_POINTER_ID = -1;
-    private static final float CHALLENGING_SWIPE_ESCAPE_VELOCITY = 800f; // dp/sec
-    // The min overscroll is the amount of task progress overscroll we want / the max overscroll
-    // curve value below
-    private static final float MAX_OVERSCROLL = 0.7f / 0.3f;
-    private static final Interpolator OVERSCROLL_INTERP;
-    static {
-        Path OVERSCROLL_PATH = new Path();
-        OVERSCROLL_PATH.moveTo(0, 0);
-        OVERSCROLL_PATH.cubicTo(0.2f, 0.175f, 0.25f, 0.3f, 1f, 0.3f);
-        OVERSCROLL_INTERP = new FreePathInterpolator(OVERSCROLL_PATH);
-    }
-
-    Context mContext;
-    TaskStackView mSv;
-    TaskStackViewScroller mScroller;
-    VelocityTracker mVelocityTracker;
-    FlingAnimationUtils mFlingAnimUtils;
-    ValueAnimator mScrollFlingAnimator;
-
-    @ViewDebug.ExportedProperty(category="recents")
-    boolean mIsScrolling;
-    float mDownScrollP;
-    int mDownX, mDownY;
-    int mLastY;
-    int mActivePointerId = INACTIVE_POINTER_ID;
-    int mOverscrollSize;
-    TaskView mActiveTaskView = null;
-
-    int mMinimumVelocity;
-    int mMaximumVelocity;
-    // The scroll touch slop is used to calculate when we start scrolling
-    int mScrollTouchSlop;
-    // Used to calculate when a tap is outside a task view rectangle.
-    final int mWindowTouchSlop;
-
-    private final StackViewScrolledEvent mStackViewScrolledEvent = new StackViewScrolledEvent();
-
-    // The current and final set of task transforms, sized to match the list of tasks in the stack
-    private ArrayList<Task> mCurrentTasks = new ArrayList<>();
-    private ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
-    private ArrayList<TaskViewTransform> mFinalTaskTransforms = new ArrayList<>();
-    private ArrayMap<View, Animator> mSwipeHelperAnimations = new ArrayMap<>();
-    private TaskViewTransform mTmpTransform = new TaskViewTransform();
-    private float mTargetStackScroll;
-
-    SwipeHelper mSwipeHelper;
-    boolean mInterceptedBySwipeHelper;
-
-    public TaskStackViewTouchHandler(Context context, TaskStackView sv,
-            TaskStackViewScroller scroller) {
-        Resources res = context.getResources();
-        ViewConfiguration configuration = ViewConfiguration.get(context);
-        mContext = context;
-        mSv = sv;
-        mScroller = scroller;
-        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
-        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-        mScrollTouchSlop = configuration.getScaledTouchSlop();
-        mWindowTouchSlop = configuration.getScaledWindowTouchSlop();
-        mFlingAnimUtils = new FlingAnimationUtils(context, 0.2f);
-        mOverscrollSize = res.getDimensionPixelSize(R.dimen.recents_fling_overscroll_distance);
-        mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, context) {
-            @Override
-            protected float getSize(View v) {
-                return getScaledDismissSize();
-            }
-
-            @Override
-            protected void prepareDismissAnimation(View v, Animator anim) {
-                mSwipeHelperAnimations.put(v, anim);
-            }
-
-            @Override
-            protected void prepareSnapBackAnimation(View v, Animator anim) {
-                anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-                mSwipeHelperAnimations.put(v, anim);
-            }
-
-            @Override
-            protected float getUnscaledEscapeVelocity() {
-                return CHALLENGING_SWIPE_ESCAPE_VELOCITY;
-            }
-
-            @Override
-            protected long getMaxEscapeAnimDuration() {
-                return 700;
-            }
-        };
-        mSwipeHelper.setDisableHardwareLayers(true);
-    }
-
-    /** Velocity tracker helpers */
-    void initOrResetVelocityTracker() {
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        } else {
-            mVelocityTracker.clear();
-        }
-    }
-    void recycleVelocityTracker() {
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-    }
-
-    /** Touch preprocessing for handling below */
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        // Pass through to swipe helper if we are swiping
-        mInterceptedBySwipeHelper = isSwipingEnabled() && mSwipeHelper.onInterceptTouchEvent(ev);
-        if (mInterceptedBySwipeHelper) {
-            return true;
-        }
-
-        return handleTouchEvent(ev);
-    }
-
-    /** Handles touch events once we have intercepted them */
-    public boolean onTouchEvent(MotionEvent ev) {
-        // Pass through to swipe helper if we are swiping
-        if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
-            return true;
-        }
-
-        handleTouchEvent(ev);
-        return true;
-    }
-
-    /**
-     * Finishes all scroll-fling and non-dismissing animations currently running.
-     */
-    public void cancelNonDismissTaskAnimations() {
-        Utilities.cancelAnimationWithoutCallbacks(mScrollFlingAnimator);
-        if (!mSwipeHelperAnimations.isEmpty()) {
-            // For the non-dismissing tasks, freeze the position into the task overrides
-            List<TaskView> taskViews = mSv.getTaskViews();
-            for (int i = taskViews.size() - 1; i >= 0; i--) {
-                TaskView tv = taskViews.get(i);
-
-                if (mSv.isIgnoredTask(tv.getTask())) {
-                    continue;
-                }
-
-                tv.cancelTransformAnimation();
-                mSv.getStackAlgorithm().addUnfocusedTaskOverride(tv, mTargetStackScroll);
-            }
-            mSv.getStackAlgorithm().setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
-            // Update the scroll to the final scroll position from onBeginDrag()
-            mSv.getScroller().setStackScroll(mTargetStackScroll, null);
-
-            mSwipeHelperAnimations.clear();
-        }
-        mActiveTaskView = null;
-    }
-
-    private boolean handleTouchEvent(MotionEvent ev) {
-        // Short circuit if we have no children
-        if (mSv.getTaskViews().size() == 0) {
-            return false;
-        }
-
-        final TaskStackLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm;
-        int action = ev.getAction();
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_DOWN: {
-                // Stop the current scroll if it is still flinging
-                mScroller.stopScroller();
-                mScroller.stopBoundScrollAnimation();
-                mScroller.resetDeltaScroll();
-                cancelNonDismissTaskAnimations();
-                mSv.cancelDeferredTaskViewLayoutAnimation();
-
-                // Save the touch down info
-                mDownX = (int) ev.getX();
-                mDownY = (int) ev.getY();
-                mLastY = mDownY;
-                mDownScrollP = mScroller.getStackScroll();
-                mActivePointerId = ev.getPointerId(0);
-                mActiveTaskView = findViewAtPoint(mDownX, mDownY);
-
-                // Initialize the velocity tracker
-                initOrResetVelocityTracker();
-                mVelocityTracker.addMovement(ev);
-                break;
-            }
-            case MotionEvent.ACTION_POINTER_DOWN: {
-                final int index = ev.getActionIndex();
-                mActivePointerId = ev.getPointerId(index);
-                mDownX = (int) ev.getX(index);
-                mDownY = (int) ev.getY(index);
-                mLastY = mDownY;
-                mDownScrollP = mScroller.getStackScroll();
-                mScroller.resetDeltaScroll();
-                mVelocityTracker.addMovement(ev);
-                break;
-            }
-            case MotionEvent.ACTION_MOVE: {
-                int activePointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (activePointerIndex == -1) {
-                    break;
-                }
-                int y = (int) ev.getY(activePointerIndex);
-                int x = (int) ev.getX(activePointerIndex);
-                if (!mIsScrolling) {
-                    int yDiff = Math.abs(y - mDownY);
-                    int xDiff = Math.abs(x - mDownX);
-                    if (Math.abs(y - mDownY) > mScrollTouchSlop && yDiff > xDiff) {
-                        mIsScrolling = true;
-                        float stackScroll = mScroller.getStackScroll();
-                        List<TaskView> taskViews = mSv.getTaskViews();
-                        for (int i = taskViews.size() - 1; i >= 0; i--) {
-                            layoutAlgorithm.addUnfocusedTaskOverride(taskViews.get(i).getTask(),
-                                    stackScroll);
-                        }
-                        layoutAlgorithm.setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
-
-                        // Disallow parents from intercepting touch events
-                        final ViewParent parent = mSv.getParent();
-                        if (parent != null) {
-                            parent.requestDisallowInterceptTouchEvent(true);
-                        }
-
-                        MetricsLogger.action(mSv.getContext(), MetricsEvent.OVERVIEW_SCROLL);
-                        mLastY = mDownY = y;
-                    }
-                }
-                if (mIsScrolling) {
-                    // If we just move linearly on the screen, then that would map to 1/arclength
-                    // of the curve, so just move the scroll proportional to that
-                    float deltaP = layoutAlgorithm.getDeltaPForY(mDownY, y);
-
-                    // Modulate the overscroll to prevent users from pulling the stack too far
-                    float minScrollP = layoutAlgorithm.mMinScrollP;
-                    float maxScrollP = layoutAlgorithm.mMaxScrollP;
-                    float curScrollP = mDownScrollP + deltaP;
-                    if (curScrollP < minScrollP || curScrollP > maxScrollP) {
-                        float clampedScrollP = Utilities.clamp(curScrollP, minScrollP, maxScrollP);
-                        float overscrollP = (curScrollP - clampedScrollP);
-                        float maxOverscroll = Recents.getConfiguration().isLowRamDevice
-                                ? layoutAlgorithm.mTaskStackLowRamLayoutAlgorithm.getMaxOverscroll()
-                                : MAX_OVERSCROLL;
-                        float overscrollX = Math.abs(overscrollP) / maxOverscroll;
-                        float interpX = OVERSCROLL_INTERP.getInterpolation(overscrollX);
-                        curScrollP = clampedScrollP + Math.signum(overscrollP) *
-                                (interpX * maxOverscroll);
-                    }
-                    mDownScrollP += mScroller.setDeltaStackScroll(mDownScrollP,
-                            curScrollP - mDownScrollP);
-                    mStackViewScrolledEvent.updateY(y - mLastY);
-                    EventBus.getDefault().send(mStackViewScrolledEvent);
-                }
-
-                mLastY = y;
-                mVelocityTracker.addMovement(ev);
-                break;
-            }
-            case MotionEvent.ACTION_POINTER_UP: {
-                int pointerIndex = ev.getActionIndex();
-                int pointerId = ev.getPointerId(pointerIndex);
-                if (pointerId == mActivePointerId) {
-                    // Select a new active pointer id and reset the motion state
-                    final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
-                    mActivePointerId = ev.getPointerId(newPointerIndex);
-                    mDownX = (int) ev.getX(pointerIndex);
-                    mDownY = (int) ev.getY(pointerIndex);
-                    mLastY = mDownY;
-                    mDownScrollP = mScroller.getStackScroll();
-                }
-                mVelocityTracker.addMovement(ev);
-                break;
-            }
-            case MotionEvent.ACTION_UP: {
-                mVelocityTracker.addMovement(ev);
-                mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                int activePointerIndex = ev.findPointerIndex(mActivePointerId);
-                int y = (int) ev.getY(activePointerIndex);
-                int velocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
-                if (mIsScrolling) {
-                    if (mScroller.isScrollOutOfBounds()) {
-                        mScroller.animateBoundScroll();
-                    } else if (Math.abs(velocity) > mMinimumVelocity &&
-                            !Recents.getConfiguration().isLowRamDevice) {
-                        float minY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
-                                layoutAlgorithm.mMaxScrollP);
-                        float maxY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
-                                layoutAlgorithm.mMinScrollP);
-                        mScroller.fling(mDownScrollP, mDownY, y, velocity, (int) minY, (int) maxY,
-                                mOverscrollSize);
-                        mSv.invalidate();
-                    }
-
-                    // Reset the focused task after the user has scrolled, but we have no scrolling
-                    // in grid layout and therefore we don't want to reset the focus there.
-                    if (!mSv.mTouchExplorationEnabled && !mSv.useGridLayout()) {
-                        if (Recents.getConfiguration().isLowRamDevice) {
-                            mScroller.scrollToClosestTask(velocity);
-                        } else {
-                            mSv.resetFocusedTask(mSv.getFocusedTask());
-                        }
-                    }
-                } else if (mActiveTaskView == null) {
-                    // This tap didn't start on a task.
-                    maybeHideRecentsFromBackgroundTap((int) ev.getX(), (int) ev.getY());
-                }
-
-                mActivePointerId = INACTIVE_POINTER_ID;
-                mIsScrolling = false;
-                recycleVelocityTracker();
-                break;
-            }
-            case MotionEvent.ACTION_CANCEL: {
-                mActivePointerId = INACTIVE_POINTER_ID;
-                mIsScrolling = false;
-                recycleVelocityTracker();
-                break;
-            }
-        }
-        return mIsScrolling;
-    }
-
-    /** Hides recents if the up event at (x, y) is a tap on the background area. */
-    void maybeHideRecentsFromBackgroundTap(int x, int y) {
-        // Ignore the up event if it's too far from its start position. The user might have been
-        // trying to scroll or swipe.
-        int dx = Math.abs(mDownX - x);
-        int dy = Math.abs(mDownY - y);
-        if (dx > mScrollTouchSlop || dy > mScrollTouchSlop) {
-            return;
-        }
-
-        // Shift the tap position toward the center of the task stack and check to see if it would
-        // have hit a view. The user might have tried to tap on a task and missed slightly.
-        int shiftedX = x;
-        if (x > (mSv.getRight() - mSv.getLeft()) / 2) {
-            shiftedX -= mWindowTouchSlop;
-        } else {
-            shiftedX += mWindowTouchSlop;
-        }
-        if (findViewAtPoint(shiftedX, y) != null) {
-            return;
-        }
-
-        // Disallow tapping above and below the stack to dismiss recents
-        if (x > mSv.mLayoutAlgorithm.mStackRect.left && x < mSv.mLayoutAlgorithm.mStackRect.right) {
-            return;
-        }
-
-        // The user intentionally tapped on the background, which is like a tap on the "desktop".
-        // Hide recents and transition to the launcher.
-        EventBus.getDefault().send(new HideRecentsEvent(false, true));
-    }
-
-    /** Handles generic motion events */
-    public boolean onGenericMotionEvent(MotionEvent ev) {
-        if ((ev.getSource() & InputDevice.SOURCE_CLASS_POINTER) ==
-                InputDevice.SOURCE_CLASS_POINTER) {
-            int action = ev.getAction();
-            switch (action & MotionEvent.ACTION_MASK) {
-                case MotionEvent.ACTION_SCROLL:
-                    // Find the front most task and scroll the next task to the front
-                    float vScroll = ev.getAxisValue(MotionEvent.AXIS_VSCROLL);
-                    if (vScroll > 0) {
-                        mSv.setRelativeFocusedTask(true, true /* stackTasksOnly */,
-                                false /* animated */);
-                    } else {
-                        mSv.setRelativeFocusedTask(false, true /* stackTasksOnly */,
-                                false /* animated */);
-                    }
-                    return true;
-            }
-        }
-        return false;
-    }
-
-    /**** SwipeHelper Implementation ****/
-
-    @Override
-    public View getChildAtPosition(MotionEvent ev) {
-        TaskView tv = findViewAtPoint((int) ev.getX(), (int) ev.getY());
-        if (tv != null && canChildBeDismissed(tv)) {
-            return tv;
-        }
-        return null;
-    }
-
-    @Override
-    public boolean canChildBeDismissed(View v) {
-        // Disallow dismissing an already dismissed task
-        TaskView tv = (TaskView) v;
-        Task task = tv.getTask();
-        return !mSwipeHelperAnimations.containsKey(v) &&
-                (mSv.getStack().indexOfTask(task) != -1);
-    }
-
-    /**
-     * Starts a manual drag that goes through the same swipe helper path.
-     */
-    public void onBeginManualDrag(TaskView v) {
-        mActiveTaskView = v;
-        mSwipeHelperAnimations.put(v, null);
-        onBeginDrag(v);
-    }
-
-    @Override
-    public void onBeginDrag(View v) {
-        TaskView tv = (TaskView) v;
-
-        // Disable clipping with the stack while we are swiping
-        tv.setClipViewInStack(false);
-        // Disallow touch events from this task view
-        tv.setTouchEnabled(false);
-        // Disallow parents from intercepting touch events
-        final ViewParent parent = mSv.getParent();
-        if (parent != null) {
-            parent.requestDisallowInterceptTouchEvent(true);
-        }
-
-        // Add this task to the set of tasks we are deleting
-        mSv.addIgnoreTask(tv.getTask());
-
-        // Determine if we are animating the other tasks while dismissing this task
-        mCurrentTasks = new ArrayList<Task>(mSv.getStack().getTasks());
-        MutableBoolean isFrontMostTask = new MutableBoolean(false);
-        Task anchorTask = mSv.findAnchorTask(mCurrentTasks, isFrontMostTask);
-        TaskStackLayoutAlgorithm layoutAlgorithm = mSv.getStackAlgorithm();
-        TaskStackViewScroller stackScroller = mSv.getScroller();
-        if (anchorTask != null) {
-            // Get the current set of task transforms
-            mSv.getCurrentTaskTransforms(mCurrentTasks, mCurrentTaskTransforms);
-
-            // Get the stack scroll of the task to anchor to (since we are removing something, the
-            // front most task will be our anchor task)
-            float prevAnchorTaskScroll = 0;
-            boolean pullStackForward = mCurrentTasks.size() > 0;
-            if (pullStackForward) {
-                if (Recents.getConfiguration().isLowRamDevice) {
-                    float index = layoutAlgorithm.getStackScrollForTask(anchorTask);
-                    prevAnchorTaskScroll = mSv.getStackAlgorithm().mTaskStackLowRamLayoutAlgorithm
-                            .getScrollPForTask((int) index);
-                } else {
-                    prevAnchorTaskScroll = layoutAlgorithm.getStackScrollForTask(anchorTask);
-                }
-            }
-
-            // Calculate where the views would be without the deleting tasks
-            mSv.updateLayoutAlgorithm(false /* boundScroll */);
-
-            float newStackScroll = stackScroller.getStackScroll();
-            if (isFrontMostTask.value) {
-                // Bound the stack scroll to pull tasks forward if necessary
-                newStackScroll = stackScroller.getBoundedStackScroll(newStackScroll);
-            } else if (pullStackForward) {
-                // Otherwise, offset the scroll by the movement of the anchor task
-                float anchorTaskScroll =
-                        layoutAlgorithm.getStackScrollForTaskIgnoreOverrides(anchorTask);
-                if (Recents.getConfiguration().isLowRamDevice) {
-                    float index = layoutAlgorithm.getStackScrollForTask(anchorTask);
-                    anchorTaskScroll = mSv.getStackAlgorithm().mTaskStackLowRamLayoutAlgorithm
-                            .getScrollPForTask((int) index);
-                }
-                float stackScrollOffset = (anchorTaskScroll - prevAnchorTaskScroll);
-                if (layoutAlgorithm.getFocusState() != TaskStackLayoutAlgorithm.STATE_FOCUSED
-                        && !Recents.getConfiguration().isLowRamDevice) {
-                    // If we are focused, we don't want the front task to move, but otherwise, we
-                    // allow the back task to move up, and the front task to move back
-                    stackScrollOffset *= 0.75f;
-                }
-                newStackScroll = stackScroller.getBoundedStackScroll(stackScroller.getStackScroll()
-                        + stackScrollOffset);
-            }
-
-            // Pick up the newly visible views, not including the deleting tasks
-            mSv.bindVisibleTaskViews(newStackScroll, true /* ignoreTaskOverrides */);
-
-            // Get the final set of task transforms (with task removed)
-            mSv.getLayoutTaskTransforms(newStackScroll, TaskStackLayoutAlgorithm.STATE_UNFOCUSED,
-                    mCurrentTasks, true /* ignoreTaskOverrides */, mFinalTaskTransforms);
-
-            // Set the target to scroll towards upon dismissal
-            mTargetStackScroll = newStackScroll;
-
-            /*
-             * Post condition: All views that will be visible as a part of the gesture are retrieved
-             *                 and at their initial positions.  The stack is still at the current
-             *                 scroll, but the layout is updated without the task currently being
-             *                 dismissed.  The final layout is in the unfocused stack state, which
-             *                 will be applied when the current task is dismissed.
-             */
-        }
-    }
-
-    @Override
-    public boolean updateSwipeProgress(View v, boolean dismissable, float swipeProgress) {
-        // Only update the swipe progress for the surrounding tasks if the dismiss animation was not
-        // preempted from a call to cancelNonDismissTaskAnimations
-        if ((mActiveTaskView == v || mSwipeHelperAnimations.containsKey(v)) &&
-                !Recents.getConfiguration().isLowRamDevice) {
-            updateTaskViewTransforms(
-                    Interpolators.FAST_OUT_SLOW_IN.getInterpolation(swipeProgress));
-        }
-        return true;
-    }
-
-    /**
-     * Called after the {@link TaskView} is finished animating away.
-     */
-    @Override
-    public void onChildDismissed(View v) {
-        TaskView tv = (TaskView) v;
-
-        // Re-enable clipping with the stack (we will reuse this view)
-        tv.setClipViewInStack(true);
-        // Re-enable touch events from this task view
-        tv.setTouchEnabled(true);
-        // Update the scroll to the final scroll position before laying out the tasks during dismiss
-        if (mSwipeHelperAnimations.containsKey(v)) {
-            mSv.getScroller().setStackScroll(mTargetStackScroll, null);
-        }
-        // Remove the task view from the stack, ignoring the animation if we've started dragging
-        // again
-        EventBus.getDefault().send(new TaskViewDismissedEvent(tv.getTask(), tv,
-                mSwipeHelperAnimations.containsKey(v)
-                    ? new AnimationProps(TaskStackView.DEFAULT_SYNC_STACK_DURATION,
-                        Interpolators.FAST_OUT_SLOW_IN)
-                    : null));
-        // Only update the final scroll and layout state (set in onBeginDrag()) if the dismiss
-        // animation was not preempted from a call to cancelNonDismissTaskAnimations
-        if (mSwipeHelperAnimations.containsKey(v)) {
-            // Update the focus state to the final focus state
-            mSv.getStackAlgorithm().setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
-            mSv.getStackAlgorithm().clearUnfocusedTaskOverrides();
-            // Stop tracking this deletion animation
-            mSwipeHelperAnimations.remove(v);
-        }
-        // Keep track of deletions by keyboard
-        MetricsLogger.histogram(tv.getContext(), "overview_task_dismissed_source",
-                Constants.Metrics.DismissSourceSwipeGesture);
-    }
-
-    /**
-     * Called after the {@link TaskView} is finished animating back into the list.
-     * onChildDismissed() calls.
-     */
-    @Override
-    public void onChildSnappedBack(View v, float targetLeft) {
-        TaskView tv = (TaskView) v;
-
-        // Re-enable clipping with the stack
-        tv.setClipViewInStack(true);
-        // Re-enable touch events from this task view
-        tv.setTouchEnabled(true);
-
-        // Stop tracking this deleting task, and update the layout to include this task again.  The
-        // stack scroll does not need to be reset, since the scroll has not actually changed in
-        // onBeginDrag().
-        mSv.removeIgnoreTask(tv.getTask());
-        mSv.updateLayoutAlgorithm(false /* boundScroll */);
-        mSv.relayoutTaskViews(AnimationProps.IMMEDIATE);
-        mSwipeHelperAnimations.remove(v);
-    }
-
-    @Override
-    public void onDragCancelled(View v) {
-        // Do nothing
-    }
-
-    @Override
-    public boolean isAntiFalsingNeeded() {
-        return false;
-    }
-
-    @Override
-    public float getFalsingThresholdFactor() {
-        return 0;
-    }
-
-    /**
-     * Interpolates the non-deleting tasks to their final transforms from their current transforms.
-     */
-    private void updateTaskViewTransforms(float dismissFraction) {
-        List<TaskView> taskViews = mSv.getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            TaskView tv = taskViews.get(i);
-            Task task = tv.getTask();
-
-            if (mSv.isIgnoredTask(task)) {
-                continue;
-            }
-
-            int taskIndex = mCurrentTasks.indexOf(task);
-            if (taskIndex == -1) {
-                // If a task was added to the stack view after the start of the dismiss gesture,
-                // just ignore it
-                continue;
-            }
-
-            TaskViewTransform fromTransform = mCurrentTaskTransforms.get(taskIndex);
-            TaskViewTransform toTransform = mFinalTaskTransforms.get(taskIndex);
-
-            mTmpTransform.copyFrom(fromTransform);
-            // We only really need to interpolate the bounds, progress and translation
-            mTmpTransform.rect.set(Utilities.RECTF_EVALUATOR.evaluate(dismissFraction,
-                    fromTransform.rect, toTransform.rect));
-            mTmpTransform.dimAlpha = fromTransform.dimAlpha + (toTransform.dimAlpha -
-                    fromTransform.dimAlpha) * dismissFraction;
-            mTmpTransform.viewOutlineAlpha = fromTransform.viewOutlineAlpha +
-                    (toTransform.viewOutlineAlpha - fromTransform.viewOutlineAlpha) *
-                            dismissFraction;
-            mTmpTransform.translationZ = fromTransform.translationZ +
-                    (toTransform.translationZ - fromTransform.translationZ) * dismissFraction;
-
-            mSv.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
-        }
-    }
-
-    /** Returns the view at the specified coordinates */
-    private TaskView findViewAtPoint(int x, int y) {
-        List<Task> tasks = mSv.getStack().getTasks();
-        int taskCount = tasks.size();
-        for (int i = taskCount - 1; i >= 0; i--) {
-            TaskView tv = mSv.getChildViewForTask(tasks.get(i));
-            if (tv != null && tv.getVisibility() == View.VISIBLE) {
-                if (mSv.isTouchPointInView(x, y, tv)) {
-                    return tv;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns the scaled size used to calculate the dismiss fraction.
-     */
-    public float getScaledDismissSize() {
-        return 1.5f * Math.max(mSv.getWidth(), mSv.getHeight());
-    }
-
-    /**
-     * Returns whether swiping is enabled.
-     */
-    private boolean isSwipingEnabled() {
-        return !mSv.useGridLayout();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
deleted file mode 100644
index f0278a6..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ /dev/null
@@ -1,738 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Outline;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.util.Property;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewOutlineProvider;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.LaunchTaskEvent;
-import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
-import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.recents.view.AnimateableViewBounds;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-/**
- * A {@link TaskView} represents a fixed view of a task. Because the TaskView's layout is directed
- * solely by the {@link TaskStackView}, we make it a fixed size layout which allows relayouts down
- * the view hierarchy, but not upwards from any of its children (the TaskView will relayout itself
- * with the previous bounds if any child requests layout).
- */
-public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks,
-        TaskStackAnimationHelper.Callbacks, View.OnClickListener, View.OnLongClickListener {
-
-    /** The TaskView callbacks */
-    interface TaskViewCallbacks {
-        void onTaskViewClipStateChanged(TaskView tv);
-    }
-
-    /**
-     * The dim overlay is generally calculated from the task progress, but occasionally (like when
-     * launching) needs to be animated independently of the task progress.  This call is only used
-     * when animating the task into Recents, when the header dim is already applied
-     */
-    public static final Property<TaskView, Float> DIM_ALPHA_WITHOUT_HEADER =
-            new FloatProperty<TaskView>("dimAlphaWithoutHeader") {
-                @Override
-                public void setValue(TaskView tv, float dimAlpha) {
-                    tv.setDimAlphaWithoutHeader(dimAlpha);
-                }
-
-                @Override
-                public Float get(TaskView tv) {
-                    return tv.getDimAlpha();
-                }
-            };
-
-    /**
-     * The dim overlay is generally calculated from the task progress, but occasionally (like when
-     * launching) needs to be animated independently of the task progress.
-     */
-    public static final Property<TaskView, Float> DIM_ALPHA =
-            new FloatProperty<TaskView>("dimAlpha") {
-                @Override
-                public void setValue(TaskView tv, float dimAlpha) {
-                    tv.setDimAlpha(dimAlpha);
-                }
-
-                @Override
-                public Float get(TaskView tv) {
-                    return tv.getDimAlpha();
-                }
-            };
-
-    /**
-     * The dim overlay is generally calculated from the task progress, but occasionally (like when
-     * launching) needs to be animated independently of the task progress.
-     */
-    public static final Property<TaskView, Float> VIEW_OUTLINE_ALPHA =
-            new FloatProperty<TaskView>("viewOutlineAlpha") {
-                @Override
-                public void setValue(TaskView tv, float alpha) {
-                    tv.getViewBounds().setAlpha(alpha);
-                }
-
-                @Override
-                public Float get(TaskView tv) {
-                    return tv.getViewBounds().getAlpha();
-                }
-            };
-
-    @ViewDebug.ExportedProperty(category="recents")
-    private float mDimAlpha;
-    private float mActionButtonTranslationZ;
-
-    @ViewDebug.ExportedProperty(deepExport=true, prefix="task_")
-    private Task mTask;
-    private boolean mTaskBound;
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mClipViewInStack = true;
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mTouchExplorationEnabled;
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mIsDisabledInSafeMode;
-    @ViewDebug.ExportedProperty(deepExport=true, prefix="view_bounds_")
-    private AnimateableViewBounds mViewBounds;
-
-    private AnimatorSet mTransformAnimation;
-    private ObjectAnimator mDimAnimator;
-    private ObjectAnimator mOutlineAnimator;
-    private final TaskViewTransform mTargetAnimationTransform = new TaskViewTransform();
-    private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
-
-    @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
-    protected TaskViewThumbnail mThumbnailView;
-    @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
-    protected TaskViewHeader mHeaderView;
-    private View mActionButtonView;
-    private View mIncompatibleAppToastView;
-    private TaskViewCallbacks mCb;
-
-    @ViewDebug.ExportedProperty(category="recents")
-    private Point mDownTouchPos = new Point();
-
-    private Toast mDisabledAppToast;
-
-    public TaskView(Context context) {
-        this(context, null);
-    }
-
-    public TaskView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        RecentsConfiguration config = Recents.getConfiguration();
-        Resources res = context.getResources();
-        mViewBounds = createOutlineProvider();
-        if (config.fakeShadows) {
-            setBackground(new FakeShadowDrawable(res, config));
-        }
-        setOutlineProvider(mViewBounds);
-        setOnLongClickListener(this);
-        setAccessibilityDelegate(new TaskViewAccessibilityDelegate(this));
-    }
-
-    /** Set callback */
-    void setCallbacks(TaskViewCallbacks cb) {
-        mCb = cb;
-    }
-
-    /**
-     * Called from RecentsActivity when it is relaunched.
-     */
-    void onReload(boolean isResumingFromVisible) {
-        resetNoUserInteractionState();
-        if (!isResumingFromVisible) {
-            resetViewProperties();
-        }
-    }
-
-    /** Gets the task */
-    public Task getTask() {
-        return mTask;
-    }
-
-    /* Create an outline provider to clip and outline the view */
-    protected AnimateableViewBounds createOutlineProvider() {
-        return new AnimateableViewBounds(this, mContext.getResources().getDimensionPixelSize(
-            R.dimen.recents_task_view_shadow_rounded_corners_radius));
-    }
-
-    /** Returns the view bounds. */
-    AnimateableViewBounds getViewBounds() {
-        return mViewBounds;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        // Bind the views
-        mHeaderView = findViewById(R.id.task_view_bar);
-        mThumbnailView = findViewById(R.id.task_view_thumbnail);
-        mThumbnailView.updateClipToTaskBar(mHeaderView);
-        mActionButtonView = findViewById(R.id.lock_to_app_fab);
-        mActionButtonView.setOutlineProvider(new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                // Set the outline to match the FAB background
-                outline.setOval(0, 0, mActionButtonView.getWidth(), mActionButtonView.getHeight());
-                outline.setAlpha(0.35f);
-            }
-        });
-        mActionButtonView.setOnClickListener(this);
-        mActionButtonTranslationZ = mActionButtonView.getTranslationZ();
-    }
-
-    /**
-     * Update the task view when the configuration changes.
-     */
-    protected void onConfigurationChanged() {
-        mHeaderView.onConfigurationChanged();
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        if (w > 0 && h > 0) {
-            mHeaderView.onTaskViewSizeChanged(w, h);
-            mThumbnailView.onTaskViewSizeChanged(w, h);
-
-            mActionButtonView.setTranslationX(w - getMeasuredWidth());
-            mActionButtonView.setTranslationY(h - getMeasuredHeight());
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mDownTouchPos.set((int) (ev.getX() * getScaleX()), (int) (ev.getY() * getScaleY()));
-        }
-        return super.onInterceptTouchEvent(ev);
-    }
-
-    @Override
-    protected void measureContents(int width, int height) {
-        int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
-        int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;
-        int widthSpec = MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY);
-        int heightSpec = MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY);
-
-        // Measure the content
-        measureChildren(widthSpec, heightSpec);
-
-        setMeasuredDimension(width, height);
-    }
-
-    void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform,
-            AnimationProps toAnimation, ValueAnimator.AnimatorUpdateListener updateCallback) {
-        RecentsConfiguration config = Recents.getConfiguration();
-        cancelTransformAnimation();
-
-        // Compose the animations for the transform
-        mTmpAnimators.clear();
-        toTransform.applyToTaskView(this, mTmpAnimators, toAnimation, !config.fakeShadows);
-        if (toAnimation.isImmediate()) {
-            if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
-                setDimAlpha(toTransform.dimAlpha);
-            }
-            if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
-                mViewBounds.setAlpha(toTransform.viewOutlineAlpha);
-            }
-            // Manually call back to the animator listener and update callback
-            if (toAnimation.getListener() != null) {
-                toAnimation.getListener().onAnimationEnd(null);
-            }
-            if (updateCallback != null) {
-                updateCallback.onAnimationUpdate(null);
-            }
-        } else {
-            // Both the progress and the update are a function of the bounds movement of the task
-            if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
-                mDimAnimator = ObjectAnimator.ofFloat(this, DIM_ALPHA, getDimAlpha(),
-                        toTransform.dimAlpha);
-                mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, mDimAnimator));
-            }
-            if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
-                mOutlineAnimator = ObjectAnimator.ofFloat(this, VIEW_OUTLINE_ALPHA,
-                        mViewBounds.getAlpha(), toTransform.viewOutlineAlpha);
-                mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, mOutlineAnimator));
-            }
-            if (updateCallback != null) {
-                ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1);
-                updateCallbackAnim.addUpdateListener(updateCallback);
-                mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, updateCallbackAnim));
-            }
-
-            // Create the animator
-            mTransformAnimation = toAnimation.createAnimator(mTmpAnimators);
-            mTransformAnimation.start();
-            mTargetAnimationTransform.copyFrom(toTransform);
-        }
-    }
-
-    /** Resets this view's properties */
-    void resetViewProperties() {
-        cancelTransformAnimation();
-        setDimAlpha(0);
-        setVisibility(View.VISIBLE);
-        getViewBounds().reset();
-        getHeaderView().reset();
-        TaskViewTransform.reset(this);
-
-        mActionButtonView.setScaleX(1f);
-        mActionButtonView.setScaleY(1f);
-        mActionButtonView.setAlpha(0f);
-        mActionButtonView.setTranslationX(0f);
-        mActionButtonView.setTranslationY(0f);
-        mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
-        if (mIncompatibleAppToastView != null) {
-            mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
-        }
-    }
-
-    /**
-     * @return whether we are animating towards {@param transform}
-     */
-    boolean isAnimatingTo(TaskViewTransform transform) {
-        return mTransformAnimation != null && mTransformAnimation.isStarted()
-                && mTargetAnimationTransform.isSame(transform);
-    }
-
-    /**
-     * Cancels any current transform animations.
-     */
-    public void cancelTransformAnimation() {
-        cancelDimAnimationIfExists();
-        Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
-        Utilities.cancelAnimationWithoutCallbacks(mOutlineAnimator);
-    }
-
-    private void cancelDimAnimationIfExists() {
-        if (mDimAnimator != null) {
-            mDimAnimator.cancel();
-        }
-    }
-
-    /** Enables/disables handling touch on this task view. */
-    public void setTouchEnabled(boolean enabled) {
-        setOnClickListener(enabled ? this : null);
-    }
-
-    /** Animates this task view if the user does not interact with the stack after a certain time. */
-    public void startNoUserInteractionAnimation() {
-        mHeaderView.startNoUserInteractionAnimation();
-    }
-
-    /** Mark this task view that the user does has not interacted with the stack after a certain time. */
-    void setNoUserInteractionState() {
-        mHeaderView.setNoUserInteractionState();
-    }
-
-    /** Resets the state tracking that the user has not interacted with the stack after a certain time. */
-    void resetNoUserInteractionState() {
-        mHeaderView.resetNoUserInteractionState();
-    }
-
-    /** Dismisses this task. */
-    void dismissTask() {
-        // Animate out the view and call the callback
-        final TaskView tv = this;
-        DismissTaskViewEvent dismissEvent = new DismissTaskViewEvent(tv);
-        dismissEvent.addPostAnimationCallback(new Runnable() {
-            @Override
-            public void run() {
-                EventBus.getDefault().send(new TaskViewDismissedEvent(mTask, tv,
-                        new AnimationProps(TaskStackView.DEFAULT_SYNC_STACK_DURATION,
-                                Interpolators.FAST_OUT_SLOW_IN)));
-            }
-        });
-        EventBus.getDefault().send(dismissEvent);
-    }
-
-    /**
-     * Returns whether this view should be clipped, or any views below should clip against this
-     * view.
-     */
-    boolean shouldClipViewInStack() {
-        if (getVisibility() != View.VISIBLE || Recents.getConfiguration().isLowRamDevice) {
-            return false;
-        }
-        return mClipViewInStack;
-    }
-
-    /** Sets whether this view should be clipped, or clipped against. */
-    void setClipViewInStack(boolean clip) {
-        if (clip != mClipViewInStack) {
-            mClipViewInStack = clip;
-            if (mCb != null) {
-                mCb.onTaskViewClipStateChanged(this);
-            }
-        }
-    }
-
-    public TaskViewHeader getHeaderView() {
-        return mHeaderView;
-    }
-
-    /**
-     * Sets the current dim.
-     */
-    public void setDimAlpha(float dimAlpha) {
-        mDimAlpha = dimAlpha;
-        mThumbnailView.setDimAlpha(dimAlpha);
-        mHeaderView.setDimAlpha(dimAlpha);
-    }
-
-    /**
-     * Sets the current dim without updating the header's dim.
-     */
-    public void setDimAlphaWithoutHeader(float dimAlpha) {
-        mDimAlpha = dimAlpha;
-        mThumbnailView.setDimAlpha(dimAlpha);
-    }
-
-    /**
-     * Returns the current dim.
-     */
-    public float getDimAlpha() {
-        return mDimAlpha;
-    }
-
-    /**
-     * Explicitly sets the focused state of this task.
-     */
-    public void setFocusedState(boolean isFocused, boolean requestViewFocus) {
-        if (isFocused) {
-            if (requestViewFocus && !isFocused()) {
-                requestFocus();
-            }
-        } else {
-            if (isAccessibilityFocused() && mTouchExplorationEnabled) {
-                clearAccessibilityFocus();
-            }
-        }
-    }
-
-    /**
-     * Shows the action button.
-     * @param fadeIn whether or not to animate the action button in.
-     * @param fadeInDuration the duration of the action button animation, only used if
-     *                       {@param fadeIn} is true.
-     */
-    public void showActionButton(boolean fadeIn, int fadeInDuration) {
-        mActionButtonView.setVisibility(View.VISIBLE);
-
-        if (fadeIn && mActionButtonView.getAlpha() < 1f) {
-            mActionButtonView.animate()
-                    .alpha(1f)
-                    .scaleX(1f)
-                    .scaleY(1f)
-                    .setDuration(fadeInDuration)
-                    .setInterpolator(Interpolators.ALPHA_IN)
-                    .start();
-        } else {
-            mActionButtonView.setScaleX(1f);
-            mActionButtonView.setScaleY(1f);
-            mActionButtonView.setAlpha(1f);
-            mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
-        }
-    }
-
-    /**
-     * Immediately hides the action button.
-     *
-     * @param fadeOut whether or not to animate the action button out.
-     */
-    public void hideActionButton(boolean fadeOut, int fadeOutDuration, boolean scaleDown,
-            final Animator.AnimatorListener animListener) {
-        if (fadeOut && mActionButtonView.getAlpha() > 0f) {
-            if (scaleDown) {
-                float toScale = 0.9f;
-                mActionButtonView.animate()
-                        .scaleX(toScale)
-                        .scaleY(toScale);
-            }
-            mActionButtonView.animate()
-                    .alpha(0f)
-                    .setDuration(fadeOutDuration)
-                    .setInterpolator(Interpolators.ALPHA_OUT)
-                    .withEndAction(new Runnable() {
-                        @Override
-                        public void run() {
-                            if (animListener != null) {
-                                animListener.onAnimationEnd(null);
-                            }
-                            mActionButtonView.setVisibility(View.INVISIBLE);
-                        }
-                    })
-                    .start();
-        } else {
-            mActionButtonView.setAlpha(0f);
-            mActionButtonView.setVisibility(View.INVISIBLE);
-            if (animListener != null) {
-                animListener.onAnimationEnd(null);
-            }
-        }
-    }
-
-    /**** TaskStackAnimationHelper.Callbacks Implementation ****/
-
-    @Override
-    public void onPrepareLaunchTargetForEnterAnimation() {
-        // These values will be animated in when onStartLaunchTargetEnterAnimation() is called
-        setDimAlphaWithoutHeader(0);
-        mActionButtonView.setAlpha(0f);
-        if (mIncompatibleAppToastView != null &&
-                mIncompatibleAppToastView.getVisibility() == View.VISIBLE) {
-            mIncompatibleAppToastView.setAlpha(0f);
-        }
-    }
-
-    @Override
-    public void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
-            boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger) {
-        cancelDimAnimationIfExists();
-
-        // Dim the view after the app window transitions down into recents
-        postAnimationTrigger.increment();
-        AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
-        mDimAnimator = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
-                DIM_ALPHA_WITHOUT_HEADER, getDimAlpha(), transform.dimAlpha));
-        mDimAnimator.addListener(postAnimationTrigger.decrementOnAnimationEnd());
-        mDimAnimator.start();
-
-        if (screenPinningEnabled) {
-            showActionButton(true /* fadeIn */, duration /* fadeInDuration */);
-        }
-
-        if (mIncompatibleAppToastView != null &&
-                mIncompatibleAppToastView.getVisibility() == View.VISIBLE) {
-            mIncompatibleAppToastView.animate()
-                    .alpha(1f)
-                    .setDuration(duration)
-                    .setInterpolator(Interpolators.ALPHA_IN)
-                    .start();
-        }
-    }
-
-    @Override
-    public void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
-            ReferenceCountedTrigger postAnimationTrigger) {
-        Utilities.cancelAnimationWithoutCallbacks(mDimAnimator);
-
-        // Un-dim the view before/while launching the target
-        AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
-        mDimAnimator = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
-                DIM_ALPHA, getDimAlpha(), 0));
-        mDimAnimator.start();
-
-        postAnimationTrigger.increment();
-        hideActionButton(true /* fadeOut */, duration,
-                !screenPinningRequested /* scaleDown */,
-                postAnimationTrigger.decrementOnAnimationEnd());
-    }
-
-    @Override
-    public void onStartFrontTaskEnterAnimation(boolean screenPinningEnabled) {
-        if (screenPinningEnabled) {
-            showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
-        }
-    }
-
-    /**** TaskCallbacks Implementation ****/
-
-    public void onTaskBound(Task t, boolean touchExplorationEnabled, int displayOrientation,
-            Rect displayRect) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        mTouchExplorationEnabled = touchExplorationEnabled;
-        mTask = t;
-        mTaskBound = true;
-        mTask.addCallback(this);
-        mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
-        mThumbnailView.bindToTask(mTask, mIsDisabledInSafeMode, displayOrientation, displayRect);
-        mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
-
-        if (!t.isDockable && ssp.hasDockedTask()) {
-            if (mIncompatibleAppToastView == null) {
-                mIncompatibleAppToastView = Utilities.findViewStubById(this,
-                        R.id.incompatible_app_toast_stub).inflate();
-                TextView msg = findViewById(com.android.internal.R.id.message);
-                msg.setText(R.string.dock_non_resizeble_failed_to_dock_text);
-            }
-            mIncompatibleAppToastView.setVisibility(View.VISIBLE);
-        } else if (mIncompatibleAppToastView != null) {
-            mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
-        }
-    }
-
-    @Override
-    public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
-        if (mTaskBound) {
-            // Update each of the views to the new task data
-            mThumbnailView.onTaskDataLoaded(thumbnailData);
-            mHeaderView.onTaskDataLoaded();
-        }
-    }
-
-    @Override
-    public void onTaskDataUnloaded() {
-        // Unbind each of the views from the task and remove the task callback
-        mTask.removeCallback(this);
-        mThumbnailView.unbindFromTask();
-        mHeaderView.unbindFromTask(mTouchExplorationEnabled);
-        mTaskBound = false;
-    }
-
-    @Override
-    public void onTaskWindowingModeChanged() {
-        // Force rebind the header, the thumbnail does not change due to stack changes
-        mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
-        mHeaderView.onTaskDataLoaded();
-    }
-
-    /**** View.OnClickListener Implementation ****/
-
-    @Override
-     public void onClick(final View v) {
-        if (mIsDisabledInSafeMode) {
-            Context context = getContext();
-            String msg = context.getString(R.string.recents_launch_disabled_message, mTask.title);
-            if (mDisabledAppToast != null) {
-                mDisabledAppToast.cancel();
-            }
-            mDisabledAppToast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
-            mDisabledAppToast.show();
-            return;
-        }
-
-        boolean screenPinningRequested = false;
-        if (v == mActionButtonView) {
-            // Reset the translation of the action button before we animate it out
-            mActionButtonView.setTranslationZ(0f);
-            screenPinningRequested = true;
-        }
-        EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, screenPinningRequested));
-
-        MetricsLogger.action(v.getContext(), MetricsEvent.ACTION_OVERVIEW_SELECT,
-                mTask.key.getComponent().toString());
-    }
-
-    /**** View.OnLongClickListener Implementation ****/
-
-    @Override
-    public boolean onLongClick(View v) {
-        if (!Recents.getConfiguration().dragToSplitEnabled) {
-            return false;
-        }
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        boolean inBounds = false;
-        Rect clipBounds = new Rect(mViewBounds.getClipBounds());
-        if (!clipBounds.isEmpty()) {
-            // If we are clipping the view to the bounds, manually do the hit test.
-            clipBounds.scale(getScaleX());
-            inBounds = clipBounds.contains(mDownTouchPos.x, mDownTouchPos.y);
-        } else {
-            // Otherwise just make sure we're within the view's bounds.
-            inBounds = mDownTouchPos.x <= getWidth() && mDownTouchPos.y <= getHeight();
-        }
-        if (v == this && inBounds && !ssp.hasDockedTask()) {
-            // Start listening for drag events
-            setClipViewInStack(false);
-
-            mDownTouchPos.x += ((1f - getScaleX()) * getWidth()) / 2;
-            mDownTouchPos.y += ((1f - getScaleY()) * getHeight()) / 2;
-
-            EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
-            EventBus.getDefault().send(new DragStartEvent(mTask, this, mDownTouchPos));
-            return true;
-        }
-        return false;
-    }
-
-    /**** Events ****/
-
-    public final void onBusEvent(DragEndEvent event) {
-        if (!(event.dropTarget instanceof DockState)) {
-            event.addPostAnimationCallback(() -> {
-                // Reset the clip state for the drag view after the end animation completes
-                setClipViewInStack(true);
-            });
-        }
-        EventBus.getDefault().unregister(this);
-    }
-
-    public final void onBusEvent(DragEndCancelledEvent event) {
-        // Reset the clip state for the drag view after the cancel animation completes
-        event.addPostAnimationCallback(() -> {
-            setClipViewInStack(true);
-        });
-    }
-
-    public void dump(String prefix, PrintWriter writer) {
-        String innerPrefix = prefix + "  ";
-
-        writer.print(prefix); writer.print("TaskView");
-        writer.print(" mTask="); writer.print(mTask.key.id);
-        writer.println();
-
-        mThumbnailView.dump(innerPrefix, writer);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
deleted file mode 100644
index 5bb5b2d..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.recents.views;
-
-import android.app.ActivityTaskManager;
-import android.content.Context;
-import android.graphics.Point;
-import android.os.Bundle;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-
-public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate {
-    private static final String TAG = "TaskViewAccessibilityDelegate";
-
-    private final TaskView mTaskView;
-
-    protected static final int SPLIT_TASK_TOP = R.id.action_split_task_to_top;
-    protected static final int SPLIT_TASK_LEFT = R.id.action_split_task_to_left;
-    protected static final int SPLIT_TASK_RIGHT = R.id.action_split_task_to_right;
-
-    protected final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
-
-    public TaskViewAccessibilityDelegate(TaskView taskView) {
-        mTaskView = taskView;
-        Context context = taskView.getContext();
-        mActions.put(SPLIT_TASK_TOP, new AccessibilityAction(SPLIT_TASK_TOP,
-                context.getString(R.string.recents_accessibility_split_screen_top)));
-        mActions.put(SPLIT_TASK_LEFT, new AccessibilityAction(SPLIT_TASK_LEFT,
-                context.getString(R.string.recents_accessibility_split_screen_left)));
-        mActions.put(SPLIT_TASK_RIGHT, new AccessibilityAction(SPLIT_TASK_RIGHT,
-                context.getString(R.string.recents_accessibility_split_screen_right)));
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(host, info);
-        if (ActivityTaskManager.supportsSplitScreenMultiWindow(mTaskView.getContext())
-                && !Recents.getSystemServices().hasDockedTask()) {
-            DockState[] dockStates = Recents.getConfiguration()
-                    .getDockStatesForCurrentOrientation();
-            for (DockState dockState: dockStates) {
-                if (dockState == DockState.TOP) {
-                    info.addAction(mActions.get(SPLIT_TASK_TOP));
-                } else if (dockState == DockState.LEFT) {
-                    info.addAction(mActions.get(SPLIT_TASK_LEFT));
-                } else if (dockState == DockState.RIGHT) {
-                    info.addAction(mActions.get(SPLIT_TASK_RIGHT));
-                }
-            }
-        }
-    }
-
-    @Override
-    public boolean performAccessibilityAction(View host, int action, Bundle args) {
-        if (action == SPLIT_TASK_TOP) {
-            simulateDragIntoMultiwindow(DockState.TOP);
-        } else if (action == SPLIT_TASK_LEFT) {
-            simulateDragIntoMultiwindow(DockState.LEFT);
-        } else if (action == SPLIT_TASK_RIGHT) {
-            simulateDragIntoMultiwindow(DockState.RIGHT);
-        } else {
-            return super.performAccessibilityAction(host, action, args);
-        }
-        return true;
-    }
-
-    /** Simulate a user drag event to split the screen to the respected side */
-    private void simulateDragIntoMultiwindow(DockState dockState) {
-        EventBus.getDefault().send(new DragStartEvent(mTaskView.getTask(), mTaskView,
-                new Point(0,0), false /* isUserTouchInitiated */));
-        EventBus.getDefault().send(new DragEndEvent(mTaskView.getTask(), mTaskView, dockState));
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
deleted file mode 100644
index de42914..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ /dev/null
@@ -1,685 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.Nullable;
-import android.app.AppGlobals;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
-import android.os.CountDownTimer;
-import androidx.core.graphics.ColorUtils;
-import android.util.AttributeSet;
-import android.util.IconDrawableFactory;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.LaunchTaskEvent;
-import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-
-/* The task bar view */
-public class TaskViewHeader extends FrameLayout
-        implements View.OnClickListener, View.OnLongClickListener {
-
-    private static IconDrawableFactory sDrawableFactory;
-
-    private static final float HIGHLIGHT_LIGHTNESS_INCREMENT = 0.075f;
-    private static final float OVERLAY_LIGHTNESS_INCREMENT = -0.0625f;
-    private static final int OVERLAY_REVEAL_DURATION = 250;
-    private static final long FOCUS_INDICATOR_INTERVAL_MS = 30;
-
-    /**
-     * A color drawable that draws a slight highlight at the top to help it stand out.
-     */
-    private class HighlightColorDrawable extends Drawable {
-
-        private Paint mHighlightPaint = new Paint();
-        private Paint mBackgroundPaint = new Paint();
-        private int mColor;
-        private float mDimAlpha;
-
-        public HighlightColorDrawable() {
-            mBackgroundPaint.setColor(Color.argb(255, 0, 0, 0));
-            mBackgroundPaint.setAntiAlias(true);
-            mHighlightPaint.setColor(Color.argb(255, 255, 255, 255));
-            mHighlightPaint.setAntiAlias(true);
-        }
-
-        public void setColorAndDim(int color, float dimAlpha) {
-            if (mColor != color || Float.compare(mDimAlpha, dimAlpha) != 0) {
-                mColor = color;
-                mDimAlpha = dimAlpha;
-                if (mShouldDarkenBackgroundColor) {
-                    color = getSecondaryColor(color, false /* useLightOverlayColor */);
-                }
-                mBackgroundPaint.setColor(color);
-
-                ColorUtils.colorToHSL(color, mTmpHSL);
-                // TODO: Consider using the saturation of the color to adjust the lightness as well
-                mTmpHSL[2] = Math.min(1f,
-                        mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
-                mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
-
-                invalidateSelf();
-            }
-        }
-
-        @Override
-        public void setColorFilter(@Nullable ColorFilter colorFilter) {
-            // Do nothing
-        }
-
-        @Override
-        public void setAlpha(int alpha) {
-            // Do nothing
-        }
-
-        @Override
-        public void draw(Canvas canvas) {
-            // Draw the highlight at the top edge (but put the bottom edge just out of view)
-            canvas.drawRoundRect(0, 0, mTaskViewRect.width(),
-                    2 * Math.max(mHighlightHeight, mCornerRadius),
-                    mCornerRadius, mCornerRadius, mHighlightPaint);
-
-            // Draw the background with the rounded corners
-            canvas.drawRoundRect(0, mHighlightHeight, mTaskViewRect.width(),
-                    getHeight() + mCornerRadius,
-                    mCornerRadius, mCornerRadius, mBackgroundPaint);
-        }
-
-        @Override
-        public int getOpacity() {
-            return PixelFormat.OPAQUE;
-        }
-
-        public int getColor() {
-            return mColor;
-        }
-    }
-
-    Task mTask;
-
-    // Header views
-    ImageView mIconView;
-    TextView mTitleView;
-    ImageView mMoveTaskButton;
-    ImageView mDismissButton;
-    FrameLayout mAppOverlayView;
-    ImageView mAppIconView;
-    ImageView mAppInfoView;
-    TextView mAppTitleView;
-    ProgressBar mFocusTimerIndicator;
-
-    // Header drawables
-    @ViewDebug.ExportedProperty(category="recents")
-    Rect mTaskViewRect = new Rect();
-    int mHeaderBarHeight;
-    int mHeaderButtonPadding;
-    int mCornerRadius;
-    int mHighlightHeight;
-    @ViewDebug.ExportedProperty(category="recents")
-    float mDimAlpha;
-    Drawable mLightDismissDrawable;
-    Drawable mDarkDismissDrawable;
-    Drawable mLightFullscreenIcon;
-    Drawable mDarkFullscreenIcon;
-    Drawable mLightInfoIcon;
-    Drawable mDarkInfoIcon;
-    int mTaskBarViewLightTextColor;
-    int mTaskBarViewDarkTextColor;
-    int mDisabledTaskBarBackgroundColor;
-    String mDismissDescFormat;
-    String mAppInfoDescFormat;
-    int mTaskWindowingMode = WINDOWING_MODE_UNDEFINED;
-
-    // Header background
-    private HighlightColorDrawable mBackground;
-    private HighlightColorDrawable mOverlayBackground;
-    private float[] mTmpHSL = new float[3];
-
-    // Header dim, which is only used when task view hardware layers are not used
-    private Paint mDimLayerPaint = new Paint();
-
-    // Whether the background color should be darkened to differentiate from the primary color.
-    // Used in grid layout.
-    private boolean mShouldDarkenBackgroundColor = false;
-
-    private CountDownTimer mFocusTimerCountDown;
-
-    public TaskViewHeader(Context context) {
-        this(context, null);
-    }
-
-    public TaskViewHeader(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        setWillNotDraw(false);
-
-        // Load the dismiss resources
-        Resources res = context.getResources();
-        mLightDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_light);
-        mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
-        mCornerRadius = Recents.getConfiguration().isGridEnabled ?
-                res.getDimensionPixelSize(R.dimen.recents_grid_task_view_rounded_corners_radius) :
-                res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
-        mHighlightHeight = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
-        mTaskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
-        mTaskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
-        mLightFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_light);
-        mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark);
-        mLightInfoIcon = context.getDrawable(R.drawable.recents_info_light);
-        mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark);
-        mDisabledTaskBarBackgroundColor =
-                context.getColor(R.color.recents_task_bar_disabled_background_color);
-        mDismissDescFormat = mContext.getString(
-                R.string.accessibility_recents_item_will_be_dismissed);
-        mAppInfoDescFormat = mContext.getString(R.string.accessibility_recents_item_open_app_info);
-
-        // Configure the background and dim
-        mBackground = new HighlightColorDrawable();
-        mBackground.setColorAndDim(Color.argb(255, 0, 0, 0), 0f);
-        setBackground(mBackground);
-        mOverlayBackground = new HighlightColorDrawable();
-        mDimLayerPaint.setColor(Color.argb(255, 0, 0, 0));
-        mDimLayerPaint.setAntiAlias(true);
-    }
-
-    /**
-     * Resets this header along with the TaskView.
-     */
-    public void reset() {
-        hideAppOverlay(true /* immediate */);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-
-        // Initialize the icon and description views
-        mIconView = findViewById(R.id.icon);
-        mIconView.setOnLongClickListener(this);
-        mTitleView = findViewById(R.id.title);
-        mDismissButton = findViewById(R.id.dismiss_task);
-
-        onConfigurationChanged();
-    }
-
-    /**
-     * Programmatically sets the layout params for a header bar layout.  This is necessary because
-     * we can't get resources based on the current configuration, but instead need to get them
-     * based on the device configuration.
-     */
-    private void updateLayoutParams(View icon, View title, View secondaryButton, View button) {
-        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, mHeaderBarHeight, Gravity.TOP);
-        setLayoutParams(lp);
-        lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.START);
-        icon.setLayoutParams(lp);
-        lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
-        lp.setMarginStart(mHeaderBarHeight);
-        lp.setMarginEnd(mMoveTaskButton != null
-                ? 2 * mHeaderBarHeight
-                : mHeaderBarHeight);
-        title.setLayoutParams(lp);
-        if (secondaryButton != null) {
-            lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
-            lp.setMarginEnd(mHeaderBarHeight);
-            secondaryButton.setLayoutParams(lp);
-            secondaryButton.setPadding(mHeaderButtonPadding, mHeaderButtonPadding,
-                    mHeaderButtonPadding, mHeaderButtonPadding);
-        }
-        lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
-        button.setLayoutParams(lp);
-        button.setPadding(mHeaderButtonPadding, mHeaderButtonPadding, mHeaderButtonPadding,
-                mHeaderButtonPadding);
-    }
-
-    /**
-     * Update the header view when the configuration changes.
-     */
-    public void onConfigurationChanged() {
-        // Update the dimensions of everything in the header. We do this because we need to use
-        // resources for the display, and not the current configuration.
-        Resources res = getResources();
-        int headerBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
-                R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height_tablet_land,
-                R.dimen.recents_task_view_header_height,
-                R.dimen.recents_task_view_header_height_tablet_land,
-                R.dimen.recents_grid_task_view_header_height);
-        int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
-                R.dimen.recents_task_view_header_button_padding,
-                R.dimen.recents_task_view_header_button_padding,
-                R.dimen.recents_task_view_header_button_padding,
-                R.dimen.recents_task_view_header_button_padding_tablet_land,
-                R.dimen.recents_task_view_header_button_padding,
-                R.dimen.recents_task_view_header_button_padding_tablet_land,
-                R.dimen.recents_grid_task_view_header_button_padding);
-        if (headerBarHeight != mHeaderBarHeight || headerButtonPadding != mHeaderButtonPadding) {
-            mHeaderBarHeight = headerBarHeight;
-            mHeaderButtonPadding = headerButtonPadding;
-            updateLayoutParams(mIconView, mTitleView, mMoveTaskButton, mDismissButton);
-            if (mAppOverlayView != null) {
-                updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
-            }
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-
-        // Since we update the position of children based on the width of the parent and this view
-        // recompute these changes with the new view size
-        onTaskViewSizeChanged(mTaskViewRect.width(), mTaskViewRect.height());
-    }
-
-    /**
-     * Called when the task view frame changes, allowing us to move the contents of the header
-     * to match the frame changes.
-     */
-    public void onTaskViewSizeChanged(int width, int height) {
-        mTaskViewRect.set(0, 0, width, height);
-
-        boolean showTitle = true;
-        boolean showMoveIcon = true;
-        boolean showDismissIcon = true;
-        int rightInset = width - getMeasuredWidth();
-
-        mTitleView.setVisibility(showTitle ? View.VISIBLE : View.INVISIBLE);
-        if (mMoveTaskButton != null) {
-            mMoveTaskButton.setVisibility(showMoveIcon ? View.VISIBLE : View.INVISIBLE);
-            mMoveTaskButton.setTranslationX(rightInset);
-        }
-        mDismissButton.setVisibility(showDismissIcon ? View.VISIBLE : View.INVISIBLE);
-        mDismissButton.setTranslationX(rightInset);
-
-        setLeftTopRightBottom(0, 0, width, getMeasuredHeight());
-    }
-
-    @Override
-    public void onDrawForeground(Canvas canvas) {
-        super.onDrawForeground(canvas);
-
-        // Draw the dim layer with the rounded corners
-        canvas.drawRoundRect(0, 0, mTaskViewRect.width(), getHeight() + mCornerRadius,
-                mCornerRadius, mCornerRadius, mDimLayerPaint);
-    }
-
-    /** Starts the focus timer. */
-    public void startFocusTimerIndicator(int duration) {
-        if (mFocusTimerIndicator == null) {
-            return;
-        }
-
-        mFocusTimerIndicator.setVisibility(View.VISIBLE);
-        mFocusTimerIndicator.setMax(duration);
-        mFocusTimerIndicator.setProgress(duration);
-        if (mFocusTimerCountDown != null) {
-            mFocusTimerCountDown.cancel();
-        }
-        mFocusTimerCountDown = new CountDownTimer(duration,
-                FOCUS_INDICATOR_INTERVAL_MS) {
-            public void onTick(long millisUntilFinished) {
-                mFocusTimerIndicator.setProgress((int) millisUntilFinished);
-            }
-
-            public void onFinish() {
-                // Do nothing
-            }
-        }.start();
-    }
-
-    /** Cancels the focus timer. */
-    public void cancelFocusTimerIndicator() {
-        if (mFocusTimerIndicator == null) {
-            return;
-        }
-
-        if (mFocusTimerCountDown != null) {
-            mFocusTimerCountDown.cancel();
-            mFocusTimerIndicator.setProgress(0);
-            mFocusTimerIndicator.setVisibility(View.INVISIBLE);
-        }
-    }
-
-    /** Only exposed for the workaround for b/27815919. */
-    public ImageView getIconView() {
-        return mIconView;
-    }
-
-    /** Returns the secondary color for a primary color. */
-    int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
-        int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
-        return Utilities.getColorWithOverlay(primaryColor, overlayColor, 0.8f);
-    }
-
-    /**
-     * Sets the dim alpha, only used when we are not using hardware layers.
-     * (see RecentsConfiguration.useHardwareLayers)
-     */
-    public void setDimAlpha(float dimAlpha) {
-        if (Float.compare(mDimAlpha, dimAlpha) != 0) {
-            mDimAlpha = dimAlpha;
-            mTitleView.setAlpha(1f - dimAlpha);
-            updateBackgroundColor(mBackground.getColor(), dimAlpha);
-        }
-    }
-
-    /**
-     * Updates the background and highlight colors for this header.
-     */
-    private void updateBackgroundColor(int color, float dimAlpha) {
-        if (mTask != null) {
-            mBackground.setColorAndDim(color, dimAlpha);
-            // TODO: Consider using the saturation of the color to adjust the lightness as well
-            ColorUtils.colorToHSL(color, mTmpHSL);
-            mTmpHSL[2] = Math.min(1f, mTmpHSL[2] + OVERLAY_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
-            mOverlayBackground.setColorAndDim(ColorUtils.HSLToColor(mTmpHSL), dimAlpha);
-            mDimLayerPaint.setAlpha((int) (dimAlpha * 255));
-            invalidate();
-        }
-    }
-
-    /**
-     * Sets whether the background color should be darkened to differentiate from the primary color.
-     */
-    public void setShouldDarkenBackgroundColor(boolean flag) {
-        mShouldDarkenBackgroundColor = flag;
-    }
-
-    /**
-     * Binds the bar view to the task.
-     */
-    public void bindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
-        mTask = t;
-
-        int primaryColor = disabledInSafeMode
-                ? mDisabledTaskBarBackgroundColor
-                : t.colorPrimary;
-        if (mBackground.getColor() != primaryColor) {
-            updateBackgroundColor(primaryColor, mDimAlpha);
-        }
-        if (!mTitleView.getText().toString().equals(t.title)) {
-            mTitleView.setText(t.title);
-        }
-        mTitleView.setContentDescription(t.titleDescription);
-        mTitleView.setTextColor(t.useLightOnPrimaryColor ?
-                mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
-        mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
-                mLightDismissDrawable : mDarkDismissDrawable);
-        mDismissButton.setContentDescription(String.format(mDismissDescFormat, t.titleDescription));
-        mDismissButton.setOnClickListener(this);
-        mDismissButton.setClickable(false);
-        ((RippleDrawable) mDismissButton.getBackground()).setForceSoftware(true);
-
-        // In accessibility, a single click on the focused app info button will show it
-        if (touchExplorationEnabled) {
-            mIconView.setContentDescription(String.format(mAppInfoDescFormat, t.titleDescription));
-            mIconView.setOnClickListener(this);
-            mIconView.setClickable(true);
-        }
-    }
-
-    /**
-     * Called when the bound task's data has loaded and this view should update to reflect the
-     * changes.
-     */
-    public void onTaskDataLoaded() {
-        if (mTask != null && mTask.icon != null) {
-            mIconView.setImageDrawable(mTask.icon);
-        }
-    }
-
-    /** Unbinds the bar view from the task */
-    void unbindFromTask(boolean touchExplorationEnabled) {
-        mTask = null;
-        mIconView.setImageDrawable(null);
-        if (touchExplorationEnabled) {
-            mIconView.setClickable(false);
-        }
-    }
-
-    /** Animates this task bar if the user does not interact with the stack after a certain time. */
-    void startNoUserInteractionAnimation() {
-        int duration = getResources().getInteger(R.integer.recents_task_enter_from_app_duration);
-        mDismissButton.setVisibility(View.VISIBLE);
-        mDismissButton.setClickable(true);
-        if (mDismissButton.getVisibility() == VISIBLE) {
-            mDismissButton.animate()
-                    .alpha(1f)
-                    .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
-                    .setDuration(duration)
-                    .start();
-        } else {
-            mDismissButton.setAlpha(1f);
-        }
-        if (mMoveTaskButton != null) {
-            if (mMoveTaskButton.getVisibility() == VISIBLE) {
-                mMoveTaskButton.setVisibility(View.VISIBLE);
-                mMoveTaskButton.setClickable(true);
-                mMoveTaskButton.animate()
-                        .alpha(1f)
-                        .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
-                        .setDuration(duration)
-                        .start();
-            } else {
-                mMoveTaskButton.setAlpha(1f);
-            }
-        }
-    }
-
-    /**
-     * Mark this task view that the user does has not interacted with the stack after a certain
-     * time.
-     */
-    public void setNoUserInteractionState() {
-        mDismissButton.setVisibility(View.VISIBLE);
-        mDismissButton.animate().cancel();
-        mDismissButton.setAlpha(1f);
-        mDismissButton.setClickable(true);
-        if (mMoveTaskButton != null) {
-            mMoveTaskButton.setVisibility(View.VISIBLE);
-            mMoveTaskButton.animate().cancel();
-            mMoveTaskButton.setAlpha(1f);
-            mMoveTaskButton.setClickable(true);
-        }
-    }
-
-    /**
-     * Resets the state tracking that the user has not interacted with the stack after a certain
-     * time.
-     */
-    void resetNoUserInteractionState() {
-        mDismissButton.setVisibility(View.INVISIBLE);
-        mDismissButton.setAlpha(0f);
-        mDismissButton.setClickable(false);
-        if (mMoveTaskButton != null) {
-            mMoveTaskButton.setVisibility(View.INVISIBLE);
-            mMoveTaskButton.setAlpha(0f);
-            mMoveTaskButton.setClickable(false);
-        }
-    }
-
-    @Override
-    protected int[] onCreateDrawableState(int extraSpace) {
-
-        // Don't forward our state to the drawable - we do it manually in onTaskViewFocusChanged.
-        // This is to prevent layer trashing when the view is pressed.
-        return new int[] {};
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v == mIconView) {
-            // In accessibility, a single click on the focused app info button will show it
-            EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
-        } else if (v == mDismissButton) {
-            TaskView tv = Utilities.findParent(this, TaskView.class);
-            tv.dismissTask();
-
-            // Keep track of deletions by the dismiss button
-            MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
-                    Constants.Metrics.DismissSourceHeaderButton);
-        } else if (v == mMoveTaskButton) {
-            TaskView tv = Utilities.findParent(this, TaskView.class);
-            EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, null, false,
-                    mTaskWindowingMode, ACTIVITY_TYPE_UNDEFINED));
-        } else if (v == mAppInfoView) {
-            EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
-        } else if (v == mAppIconView) {
-            hideAppOverlay(false /* immediate */);
-        }
-    }
-
-    @Override
-    public boolean onLongClick(View v) {
-        if (v == mIconView) {
-            showAppOverlay();
-            return true;
-        } else if (v == mAppIconView) {
-            hideAppOverlay(false /* immediate */);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Shows the application overlay.
-     */
-    private void showAppOverlay() {
-        // Skip early if the task is invalid
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        ComponentName cn = mTask.key.getComponent();
-        int userId = mTask.key.userId;
-        ActivityInfo activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(cn, userId);
-        if (activityInfo == null) {
-            return;
-        }
-
-        // Inflate the overlay if necessary
-        if (mAppOverlayView == null) {
-            mAppOverlayView = (FrameLayout) Utilities.findViewStubById(this,
-                    R.id.app_overlay_stub).inflate();
-            mAppOverlayView.setBackground(mOverlayBackground);
-            mAppIconView = (ImageView) mAppOverlayView.findViewById(R.id.app_icon);
-            mAppIconView.setOnClickListener(this);
-            mAppIconView.setOnLongClickListener(this);
-            mAppInfoView = (ImageView) mAppOverlayView.findViewById(R.id.app_info);
-            mAppInfoView.setOnClickListener(this);
-            mAppTitleView = (TextView) mAppOverlayView.findViewById(R.id.app_title);
-            updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
-        }
-
-        // Update the overlay contents for the current app
-        mAppTitleView.setText(ActivityManagerWrapper.getInstance().getBadgedApplicationLabel(
-                activityInfo.applicationInfo, userId));
-        mAppTitleView.setTextColor(mTask.useLightOnPrimaryColor ?
-                mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
-        mAppIconView.setImageDrawable(getIconDrawableFactory().getBadgedIcon(
-                activityInfo.applicationInfo, userId));
-        mAppInfoView.setImageDrawable(mTask.useLightOnPrimaryColor
-                ? mLightInfoIcon
-                : mDarkInfoIcon);
-        mAppOverlayView.setVisibility(View.VISIBLE);
-
-        int x = mIconView.getLeft() + mIconView.getWidth() / 2;
-        int y = mIconView.getTop() + mIconView.getHeight() / 2;
-        Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y, 0,
-                getWidth());
-        revealAnim.setDuration(OVERLAY_REVEAL_DURATION);
-        revealAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        revealAnim.start();
-    }
-
-    /**
-     * Hide the application overlay.
-     */
-    private void hideAppOverlay(boolean immediate) {
-        // Skip if we haven't even loaded the overlay yet
-        if (mAppOverlayView == null) {
-            return;
-        }
-
-        if (immediate) {
-            mAppOverlayView.setVisibility(View.GONE);
-        } else {
-            int x = mIconView.getLeft() + mIconView.getWidth() / 2;
-            int y = mIconView.getTop() + mIconView.getHeight() / 2;
-            Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y,
-                    getWidth(), 0);
-            revealAnim.setDuration(OVERLAY_REVEAL_DURATION);
-            revealAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-            revealAnim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mAppOverlayView.setVisibility(View.GONE);
-                }
-            });
-            revealAnim.start();
-        }
-    }
-
-    private static IconDrawableFactory getIconDrawableFactory() {
-        if (sDrawableFactory == null) {
-            sDrawableFactory = IconDrawableFactory.newInstance(AppGlobals.getInitialApplication());
-        }
-        return sDrawableFactory;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
deleted file mode 100644
index 4152b05..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.LightingColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Shader;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewDebug;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import java.io.PrintWriter;
-
-
-/**
- * The task thumbnail view.  It implements an image view that allows for animating the dim and
- * alpha of the thumbnail image.
- */
-public class TaskViewThumbnail extends View {
-
-    private static final ColorMatrix TMP_FILTER_COLOR_MATRIX = new ColorMatrix();
-    private static final ColorMatrix TMP_BRIGHTNESS_COLOR_MATRIX = new ColorMatrix();
-
-    private Task mTask;
-
-    private int mDisplayOrientation = Configuration.ORIENTATION_UNDEFINED;
-    private Rect mDisplayRect = new Rect();
-
-    // Drawing
-    @ViewDebug.ExportedProperty(category="recents")
-    protected Rect mTaskViewRect = new Rect();
-    @ViewDebug.ExportedProperty(category="recents")
-    protected Rect mThumbnailRect = new Rect();
-    @ViewDebug.ExportedProperty(category="recents")
-    protected float mThumbnailScale;
-    private float mFullscreenThumbnailScale = 1f;
-    /** The height, in pixels, of the task view's title bar. */
-    private int mTitleBarHeight;
-    private boolean mSizeToFit = false;
-    private boolean mOverlayHeaderOnThumbnailActionBar = true;
-    private ThumbnailData mThumbnailData;
-
-    protected int mCornerRadius;
-    @ViewDebug.ExportedProperty(category="recents")
-    private float mDimAlpha;
-    private Matrix mMatrix = new Matrix();
-    private Paint mDrawPaint = new Paint();
-    protected Paint mLockedPaint = new Paint();
-    protected Paint mBgFillPaint = new Paint();
-    protected BitmapShader mBitmapShader;
-    protected boolean mUserLocked = false;
-    private LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
-
-    // Clip the top of the thumbnail against the opaque header bar that overlaps this view
-    private View mTaskBar;
-
-    // Visibility optimization, if the thumbnail height is less than the height of the header
-    // bar for the task view, then just mark this thumbnail view as invisible
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mInvisible;
-
-    @ViewDebug.ExportedProperty(category="recents")
-    private boolean mDisabledInSafeMode;
-
-    public TaskViewThumbnail(Context context) {
-        this(context, null);
-    }
-
-    public TaskViewThumbnail(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        mDrawPaint.setColorFilter(mLightingColorFilter);
-        mDrawPaint.setFilterBitmap(true);
-        mDrawPaint.setAntiAlias(true);
-        Resources res = getResources();
-        mCornerRadius = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
-        mBgFillPaint.setColor(Color.WHITE);
-        mLockedPaint.setColor(Color.WHITE);
-        mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
-    }
-
-    /**
-     * Called when the task view frame changes, allowing us to move the contents of the header
-     * to match the frame changes.
-     */
-    public void onTaskViewSizeChanged(int width, int height) {
-        // Return early if the bounds have not changed
-        if (mTaskViewRect.width() == width && mTaskViewRect.height() == height) {
-            return;
-        }
-
-        mTaskViewRect.set(0, 0, width, height);
-        setLeftTopRightBottom(0, 0, width, height);
-        updateThumbnailMatrix();
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (mInvisible) {
-            return;
-        }
-
-        int viewWidth = mTaskViewRect.width();
-        int viewHeight = mTaskViewRect.height();
-        int thumbnailWidth = Math.min(viewWidth,
-                (int) (mThumbnailRect.width() * mThumbnailScale));
-        int thumbnailHeight = Math.min(viewHeight,
-                (int) (mThumbnailRect.height() * mThumbnailScale));
-
-        if (mUserLocked) {
-            canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
-                    mLockedPaint);
-        } else if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
-            int topOffset = 0;
-            if (mTaskBar != null && mOverlayHeaderOnThumbnailActionBar) {
-                topOffset = mTaskBar.getHeight() - mCornerRadius;
-            }
-
-            // Draw the background, there will be some small overdraw with the thumbnail
-            if (thumbnailWidth < viewWidth) {
-                // Portrait thumbnail on a landscape task view
-                canvas.drawRoundRect(Math.max(0, thumbnailWidth - mCornerRadius), topOffset,
-                        viewWidth, viewHeight,
-                        mCornerRadius, mCornerRadius, mBgFillPaint);
-            }
-            if (thumbnailHeight < viewHeight) {
-                // Landscape thumbnail on a portrait task view
-                canvas.drawRoundRect(0, Math.max(topOffset, thumbnailHeight - mCornerRadius),
-                        viewWidth, viewHeight,
-                        mCornerRadius, mCornerRadius, mBgFillPaint);
-            }
-
-            // Draw the thumbnail
-            canvas.drawRoundRect(0, topOffset, thumbnailWidth, thumbnailHeight,
-                    mCornerRadius, mCornerRadius, mDrawPaint);
-        } else {
-            canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
-                    mBgFillPaint);
-        }
-    }
-
-    /** Sets the thumbnail to a given bitmap. */
-    void setThumbnail(ThumbnailData thumbnailData) {
-        if (thumbnailData != null && thumbnailData.thumbnail != null) {
-            Bitmap bm = thumbnailData.thumbnail;
-            bm.prepareToDraw();
-            mFullscreenThumbnailScale = thumbnailData.scale;
-            mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
-            mDrawPaint.setShader(mBitmapShader);
-            mThumbnailRect.set(0, 0,
-                    bm.getWidth() - thumbnailData.insets.left - thumbnailData.insets.right,
-                    bm.getHeight() - thumbnailData.insets.top - thumbnailData.insets.bottom);
-            mThumbnailData = thumbnailData;
-            updateThumbnailMatrix();
-            updateThumbnailPaintFilter();
-        } else {
-            mBitmapShader = null;
-            mDrawPaint.setShader(null);
-            mThumbnailRect.setEmpty();
-            mThumbnailData = null;
-        }
-    }
-
-    /** Updates the paint to draw the thumbnail. */
-    void updateThumbnailPaintFilter() {
-        if (mInvisible) {
-            return;
-        }
-        int mul = (int) ((1.0f - mDimAlpha) * 255);
-        if (mBitmapShader != null) {
-            if (mDisabledInSafeMode) {
-                // Brightness: C-new = C-old*(1-amount) + amount
-                TMP_FILTER_COLOR_MATRIX.setSaturation(0);
-                float scale = 1f - mDimAlpha;
-                float[] mat = TMP_BRIGHTNESS_COLOR_MATRIX.getArray();
-                mat[0] = scale;
-                mat[6] = scale;
-                mat[12] = scale;
-                mat[4] = mDimAlpha * 255f;
-                mat[9] = mDimAlpha * 255f;
-                mat[14] = mDimAlpha * 255f;
-                TMP_FILTER_COLOR_MATRIX.preConcat(TMP_BRIGHTNESS_COLOR_MATRIX);
-                ColorMatrixColorFilter filter = new ColorMatrixColorFilter(TMP_FILTER_COLOR_MATRIX);
-                mDrawPaint.setColorFilter(filter);
-                mBgFillPaint.setColorFilter(filter);
-                mLockedPaint.setColorFilter(filter);
-            } else {
-                mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
-                mDrawPaint.setColorFilter(mLightingColorFilter);
-                mDrawPaint.setColor(0xFFffffff);
-                mBgFillPaint.setColorFilter(mLightingColorFilter);
-                mLockedPaint.setColorFilter(mLightingColorFilter);
-            }
-        } else {
-            int grey = mul;
-            mDrawPaint.setColorFilter(null);
-            mDrawPaint.setColor(Color.argb(255, grey, grey, grey));
-        }
-        if (!mInvisible) {
-            invalidate();
-        }
-    }
-
-    /**
-     * Updates the scale of the bitmap relative to this view.
-     */
-    public void updateThumbnailMatrix() {
-        mThumbnailScale = 1f;
-        if (mBitmapShader != null && mThumbnailData != null) {
-            if (mTaskViewRect.isEmpty()) {
-                // If we haven't measured , skip the thumbnail drawing and only draw the background
-                // color
-                mThumbnailScale = 0f;
-            } else if (mSizeToFit) {
-                // Make sure we fill the entire space regardless of the orientation.
-                float viewAspectRatio = (float) mTaskViewRect.width() /
-                        (float) (mTaskViewRect.height() - mTitleBarHeight);
-                float thumbnailAspectRatio =
-                        (float) mThumbnailRect.width() / (float) mThumbnailRect.height();
-                if (viewAspectRatio > thumbnailAspectRatio) {
-                    mThumbnailScale =
-                            (float) mTaskViewRect.width() / (float) mThumbnailRect.width();
-                } else {
-                    mThumbnailScale = (float) (mTaskViewRect.height() - mTitleBarHeight)
-                            / (float) mThumbnailRect.height();
-                }
-            } else {
-                float invThumbnailScale = 1f / mFullscreenThumbnailScale;
-                if (mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT) {
-                    if (mThumbnailData.orientation == Configuration.ORIENTATION_PORTRAIT) {
-                        // If we are in the same orientation as the screenshot, just scale it to the
-                        // width of the task view
-                        mThumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
-                    } else {
-                        // Scale the landscape thumbnail up to app size, then scale that to the task
-                        // view size to match other portrait screenshots
-                        mThumbnailScale = invThumbnailScale *
-                                ((float) mTaskViewRect.width() / mDisplayRect.width());
-                    }
-                } else {
-                    // Otherwise, scale the screenshot to fit 1:1 in the current orientation
-                    mThumbnailScale = invThumbnailScale;
-                }
-            }
-            mMatrix.setTranslate(-mThumbnailData.insets.left * mFullscreenThumbnailScale,
-                    -mThumbnailData.insets.top * mFullscreenThumbnailScale);
-            mMatrix.postScale(mThumbnailScale, mThumbnailScale);
-            mBitmapShader.setLocalMatrix(mMatrix);
-        }
-        if (!mInvisible) {
-            invalidate();
-        }
-    }
-
-    /** Sets whether the thumbnail should be resized to fit the task view in all orientations. */
-    public void setSizeToFit(boolean flag) {
-        mSizeToFit = flag;
-    }
-
-    /**
-     * Sets whether the header should overlap (and hide) the action bar in the thumbnail, or
-     * be stacked just above it.
-     */
-    public void setOverlayHeaderOnThumbnailActionBar(boolean flag) {
-        mOverlayHeaderOnThumbnailActionBar = flag;
-    }
-
-    /** Updates the clip rect based on the given task bar. */
-    void updateClipToTaskBar(View taskBar) {
-        mTaskBar = taskBar;
-        invalidate();
-    }
-
-    /** Updates the visibility of the the thumbnail. */
-    void updateThumbnailVisibility(int clipBottom) {
-        boolean invisible = mTaskBar != null && (getHeight() - clipBottom) <= mTaskBar.getHeight();
-        if (invisible != mInvisible) {
-            mInvisible = invisible;
-            if (!mInvisible) {
-                updateThumbnailPaintFilter();
-            }
-        }
-    }
-
-    /**
-     * Sets the dim alpha, only used when we are not using hardware layers.
-     * (see RecentsConfiguration.useHardwareLayers)
-     */
-    public void setDimAlpha(float dimAlpha) {
-        mDimAlpha = dimAlpha;
-        updateThumbnailPaintFilter();
-    }
-
-    /**
-     * Returns the {@link Paint} used to draw a task screenshot, or {@link #mLockedPaint} if the
-     * thumbnail shouldn't be drawn because it belongs to a locked user.
-     */
-    protected Paint getDrawPaint() {
-        if (mUserLocked) {
-            return mLockedPaint;
-        }
-        return mDrawPaint;
-    }
-
-    /**
-     * Binds the thumbnail view to the task.
-     */
-    void bindToTask(Task t, boolean disabledInSafeMode, int displayOrientation, Rect displayRect) {
-        mTask = t;
-        mDisabledInSafeMode = disabledInSafeMode;
-        mDisplayOrientation = displayOrientation;
-        mDisplayRect.set(displayRect);
-        if (t.colorBackground != 0) {
-            mBgFillPaint.setColor(t.colorBackground);
-        }
-        if (t.colorPrimary != 0) {
-            mLockedPaint.setColor(t.colorPrimary);
-        }
-        mUserLocked = t.isLocked;
-        EventBus.getDefault().register(this);
-    }
-
-    /**
-     * Called when the bound task's data has loaded and this view should update to reflect the
-     * changes.
-     */
-    void onTaskDataLoaded(ThumbnailData thumbnailData) {
-        setThumbnail(thumbnailData);
-    }
-
-    /** Unbinds the thumbnail view from the task */
-    void unbindFromTask() {
-        mTask = null;
-        setThumbnail(null);
-        EventBus.getDefault().unregister(this);
-    }
-
-    public final void onBusEvent(TaskSnapshotChangedEvent event) {
-        if (mTask == null || event.taskId != mTask.key.id || event.thumbnailData == null
-                || event.thumbnailData.thumbnail == null) {
-            return;
-        }
-        setThumbnail(event.thumbnailData);
-    }
-
-    public void dump(String prefix, PrintWriter writer) {
-        writer.print(prefix); writer.print("TaskViewThumbnail");
-        writer.print(" mTaskViewRect="); writer.print(Utilities.dumpRect(mTaskViewRect));
-        writer.print(" mThumbnailRect="); writer.print(Utilities.dumpRect(mThumbnailRect));
-        writer.print(" mThumbnailScale="); writer.print(mThumbnailScale);
-        writer.print(" mDimAlpha="); writer.print(mDimAlpha);
-        writer.println();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
deleted file mode 100644
index 9b717e0..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.util.Property;
-import android.view.View;
-
-import com.android.systemui.shared.recents.utilities.AnimationProps;
-import com.android.systemui.shared.recents.utilities.Utilities;
-
-import java.util.ArrayList;
-
-/**
- * The visual properties for a {@link TaskView}.
- */
-public class TaskViewTransform {
-
-    public static final Property<View, Rect> LTRB =
-            new Property<View, Rect>(Rect.class, "leftTopRightBottom") {
-
-                private Rect mTmpRect = new Rect();
-
-                @Override
-                public void set(View v, Rect ltrb) {
-                    v.setLeftTopRightBottom(ltrb.left, ltrb.top, ltrb.right, ltrb.bottom);
-                }
-
-                @Override
-                public Rect get(View v) {
-                    mTmpRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
-                    return mTmpRect;
-                }
-            };
-
-    public float translationZ = 0;
-    public float scale = 1f;
-    public float alpha = 1f;
-    public float dimAlpha = 0f;
-    public float viewOutlineAlpha = 0f;
-
-    public boolean visible = false;
-
-    // This is a window-space rect used for positioning the task in the stack
-    public RectF rect = new RectF();
-
-    /**
-     * Fills int this transform from the state of the given TaskView.
-     */
-    public void fillIn(TaskView tv) {
-        translationZ = tv.getTranslationZ();
-        scale = tv.getScaleX();
-        alpha = tv.getAlpha();
-        visible = true;
-        dimAlpha = tv.getDimAlpha();
-        viewOutlineAlpha = tv.getViewBounds().getAlpha();
-        rect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
-    }
-
-    /**
-     * Copies the transform state from another {@link TaskViewTransform}.
-     */
-    public void copyFrom(TaskViewTransform other) {
-        translationZ = other.translationZ;
-        scale = other.scale;
-        alpha = other.alpha;
-        visible = other.visible;
-        dimAlpha = other.dimAlpha;
-        viewOutlineAlpha = other.viewOutlineAlpha;
-        rect.set(other.rect);
-    }
-
-    /**
-     * @return whether {@param other} is the same transform as this
-     */
-    public boolean isSame(TaskViewTransform other) {
-        return translationZ == other.translationZ
-                && scale == other.scale
-                && other.alpha == alpha
-                && dimAlpha == other.dimAlpha
-                && visible == other.visible
-                && rect.equals(other.rect);
-    }
-
-    /**
-     * Resets the current transform.
-     */
-    public void reset() {
-        translationZ = 0;
-        scale = 1f;
-        alpha = 1f;
-        dimAlpha = 0f;
-        viewOutlineAlpha = 0f;
-        visible = false;
-        rect.setEmpty();
-    }
-
-    /** Convenience functions to compare against current property values */
-    public boolean hasAlphaChangedFrom(float v) {
-        return (Float.compare(alpha, v) != 0);
-    }
-
-    public boolean hasScaleChangedFrom(float v) {
-        return (Float.compare(scale, v) != 0);
-    }
-
-    public boolean hasTranslationZChangedFrom(float v) {
-        return (Float.compare(translationZ, v) != 0);
-    }
-
-    public boolean hasRectChangedFrom(View v) {
-        return ((int) rect.left != v.getLeft()) || ((int) rect.right != v.getRight()) ||
-                ((int) rect.top != v.getTop()) || ((int) rect.bottom != v.getBottom());
-    }
-
-    /**
-     * Applies this transform to a view.
-     */
-    public void applyToTaskView(TaskView v, ArrayList<Animator> animators,
-            AnimationProps animation, boolean allowShadows) {
-        // Return early if not visible
-        if (!visible) {
-            return;
-        }
-
-        if (animation.isImmediate()) {
-            if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
-                v.setTranslationZ(translationZ);
-            }
-            if (hasScaleChangedFrom(v.getScaleX())) {
-                v.setScaleX(scale);
-                v.setScaleY(scale);
-            }
-            if (hasAlphaChangedFrom(v.getAlpha())) {
-                v.setAlpha(alpha);
-            }
-            if (hasRectChangedFrom(v)) {
-                v.setLeftTopRightBottom((int) rect.left, (int) rect.top, (int) rect.right,
-                        (int) rect.bottom);
-            }
-        } else {
-            if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
-                ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.TRANSLATION_Z,
-                        v.getTranslationZ(), translationZ);
-                animators.add(animation.apply(AnimationProps.TRANSLATION_Z, anim));
-            }
-            if (hasScaleChangedFrom(v.getScaleX())) {
-                ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(v,
-                        PropertyValuesHolder.ofFloat(View.SCALE_X, v.getScaleX(), scale),
-                        PropertyValuesHolder.ofFloat(View.SCALE_Y, v.getScaleX(), scale));
-                animators.add(animation.apply(AnimationProps.SCALE, anim));
-            }
-            if (hasAlphaChangedFrom(v.getAlpha())) {
-                ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.ALPHA, v.getAlpha(), alpha);
-                animators.add(animation.apply(AnimationProps.ALPHA, anim));
-            }
-            if (hasRectChangedFrom(v)) {
-                Rect fromViewRect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
-                Rect toViewRect = new Rect();
-                rect.round(toViewRect);
-                ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(v,
-                        PropertyValuesHolder.ofObject(LTRB, Utilities.RECT_EVALUATOR,
-                                fromViewRect, toViewRect));
-                animators.add(animation.apply(AnimationProps.BOUNDS, anim));
-            }
-        }
-    }
-
-    /** Reset the transform on a view. */
-    public static void reset(TaskView v) {
-        v.setTranslationX(0f);
-        v.setTranslationY(0f);
-        v.setTranslationZ(0f);
-        v.setScaleX(1f);
-        v.setScaleY(1f);
-        v.setAlpha(1f);
-        v.getViewBounds().setClipBottom(0);
-        v.setLeftTopRightBottom(0, 0, 0, 0);
-    }
-
-    @Override
-    public String toString() {
-        return "R: " + rect + " V: " + visible;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
deleted file mode 100644
index 3bdad31..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2016 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.systemui.recents.views.grid;
-
-import android.view.View;
-import com.android.systemui.shared.recents.view.AnimateableViewBounds;
-
-/* An outline provider for grid-based task views. */
-class AnimateableGridViewBounds extends AnimateableViewBounds {
-
-    public AnimateableGridViewBounds(View source, int cornerRadius) {
-        super(source, cornerRadius);
-    }
-
-    @Override
-    protected void updateClipBounds() {
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java
deleted file mode 100644
index 0d51154..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2016 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.systemui.recents.views.grid;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import com.android.systemui.R;
-import com.android.systemui.shared.recents.view.AnimateableViewBounds;
-import com.android.systemui.recents.views.TaskView;
-
-public class GridTaskView extends TaskView {
-
-    /** The height, in pixels, of the header view. */
-    private int mHeaderHeight;
-
-    public GridTaskView(Context context) {
-        this(context, null);
-    }
-
-    public GridTaskView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public GridTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public GridTaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        mHeaderHeight = context.getResources().getDimensionPixelSize(
-                R.dimen.recents_grid_task_view_header_height);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        // Show the full thumbnail and don't overlap with the header.
-        mThumbnailView.setSizeToFit(true);
-        mThumbnailView.setOverlayHeaderOnThumbnailActionBar(false);
-        mThumbnailView.updateThumbnailMatrix();
-        mThumbnailView.setTranslationY(mHeaderHeight);
-        mHeaderView.setShouldDarkenBackgroundColor(true);
-    }
-
-    @Override
-    protected AnimateableViewBounds createOutlineProvider() {
-        return new AnimateableGridViewBounds(this, mContext.getResources().getDimensionPixelSize(
-            R.dimen.recents_task_view_shadow_rounded_corners_radius));
-    }
-
-    @Override
-    protected void onConfigurationChanged() {
-        super.onConfigurationChanged();
-        mHeaderHeight = mContext.getResources().getDimensionPixelSize(
-                R.dimen.recents_grid_task_view_header_height);
-        mThumbnailView.setTranslationY(mHeaderHeight);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
deleted file mode 100644
index ccda4b5..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Copyright (C) 2016 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.systemui.recents.views.grid;
-
-import static com.android.systemui.recents.views.TaskStackLayoutAlgorithm.*;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.view.WindowManager;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
-import com.android.systemui.recents.views.TaskViewTransform;
-
-import java.util.ArrayList;
-
-public class TaskGridLayoutAlgorithm  {
-
-    private final String TAG = "TaskGridLayoutAlgorithm";
-    public static final int MAX_LAYOUT_TASK_COUNT = 8;
-
-    /** The horizontal padding around the whole recents view. */
-    private int mPaddingLeftRight;
-    /** The vertical padding around the whole recents view. */
-    private int mPaddingTopBottom;
-    /** The padding between task views. */
-    private int mPaddingTaskView;
-
-    private Rect mWindowRect;
-    private Point mScreenSize = new Point();
-
-    private Rect mTaskGridRect;
-
-    /** The height, in pixels, of each task view's title bar. */
-    private int mTitleBarHeight;
-
-    /** The aspect ratio of each task thumbnail, without the title bar. */
-    private float mAppAspectRatio;
-    private Rect mSystemInsets = new Rect();
-
-    /** The thickness of the focused task view frame. */
-    private int mFocusedFrameThickness;
-
-    /**
-     * When the amount of tasks is determined, the size and position of every task view can be
-     * decided. Each instance of TaskGridRectInfo store the task view information for a certain
-     * amount of tasks.
-     */
-    class TaskGridRectInfo {
-        Rect size;
-        int[] xOffsets;
-        int[] yOffsets;
-        int tasksPerLine;
-        int lines;
-
-        TaskGridRectInfo(int taskCount) {
-            size = new Rect();
-            xOffsets = new int[taskCount];
-            yOffsets = new int[taskCount];
-
-            int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
-            tasksPerLine = getTasksPerLine(layoutTaskCount);
-            lines = layoutTaskCount < 4 ? 1 : 2;
-
-            // A couple of special cases.
-            boolean landscapeWindow = mWindowRect.width() > mWindowRect.height();
-            boolean landscapeTaskView = mAppAspectRatio > 1;
-            // If we're in portrait but task views are landscape, show more lines of fewer tasks.
-            if (!landscapeWindow && landscapeTaskView) {
-                tasksPerLine = layoutTaskCount < 2 ? 1 : 2;
-                lines = layoutTaskCount < 3 ? 1 : (
-                        layoutTaskCount < 5 ? 2 : (
-                                layoutTaskCount < 7 ? 3 : 4));
-            }
-            // If we're in landscape but task views are portrait, show fewer lines of more tasks.
-            if (landscapeWindow && !landscapeTaskView) {
-                tasksPerLine = layoutTaskCount < 7 ? layoutTaskCount : 6;
-                lines = layoutTaskCount < 7 ? 1 : 2;
-            }
-
-            int taskWidth, taskHeight;
-            int maxTaskWidth = (mWindowRect.width() - 2 * mPaddingLeftRight
-                - (tasksPerLine - 1) * mPaddingTaskView) / tasksPerLine;
-            int maxTaskHeight = (mWindowRect.height() - 2 * mPaddingTopBottom
-                - (lines - 1) * mPaddingTaskView) / lines;
-
-            if (maxTaskHeight >= maxTaskWidth / mAppAspectRatio + mTitleBarHeight) {
-                // Width bound.
-                taskWidth = maxTaskWidth;
-                // Here we should round the height to the nearest integer.
-                taskHeight = (int) (maxTaskWidth / mAppAspectRatio + mTitleBarHeight + 0.5);
-            } else {
-                // Height bound.
-                taskHeight = maxTaskHeight;
-                // Here we should round the width to the nearest integer.
-                taskWidth = (int) ((taskHeight - mTitleBarHeight) * mAppAspectRatio + 0.5);
-            }
-            size.set(0, 0, taskWidth, taskHeight);
-
-            int emptySpaceX = mWindowRect.width() - 2 * mPaddingLeftRight
-                - (tasksPerLine * taskWidth) - (tasksPerLine - 1) * mPaddingTaskView;
-            int emptySpaceY = mWindowRect.height() - 2 * mPaddingTopBottom
-                - (lines * taskHeight) - (lines - 1) * mPaddingTaskView;
-            for (int taskIndex = 0; taskIndex < taskCount; taskIndex++) {
-                // We also need to invert the index in order to display the most recent tasks first.
-                int taskLayoutIndex = taskCount - taskIndex - 1;
-
-                int xIndex = taskLayoutIndex % tasksPerLine;
-                int yIndex = taskLayoutIndex / tasksPerLine;
-                xOffsets[taskIndex] = mWindowRect.left +
-                    emptySpaceX / 2 + mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
-                yOffsets[taskIndex] = mWindowRect.top +
-                    emptySpaceY / 2 + mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
-            }
-        }
-
-        private int getTasksPerLine(int taskCount) {
-            switch(taskCount) {
-                case 0:
-                    return 0;
-                case 1:
-                    return 1;
-                case 2:
-                case 4:
-                    return 2;
-                case 3:
-                case 5:
-                case 6:
-                    return 3;
-                case 7:
-                case 8:
-                    return 4;
-                default:
-                    throw new IllegalArgumentException("Unsupported task count " + taskCount);
-            }
-        }
-    }
-
-    /**
-     * We can find task view sizes and positions from mTaskGridRectInfoList[k - 1] when there
-     * are k tasks.
-     */
-    private TaskGridRectInfo[] mTaskGridRectInfoList;
-
-    public TaskGridLayoutAlgorithm(Context context) {
-        reloadOnConfigurationChange(context);
-    }
-
-    public void reloadOnConfigurationChange(Context context) {
-        Resources res = context.getResources();
-        mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
-        mFocusedFrameThickness = res.getDimensionPixelSize(
-            R.dimen.recents_grid_task_view_focused_frame_thickness);
-
-        mTaskGridRect = new Rect();
-        mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
-
-        WindowManager windowManager = (WindowManager) context
-                .getSystemService(Context.WINDOW_SERVICE);
-        windowManager.getDefaultDisplay().getRealSize(mScreenSize);
-
-        updateAppAspectRatio();
-    }
-
-    /**
-     * Returns the proper task view transform of a certain task view, according to its index and the
-     * amount of task views.
-     * @param taskIndex     The index of the task view whose transform we want. It's never greater
-     *                      than {@link MAX_LAYOUT_TASK_COUNT}.
-     * @param taskCount     The current amount of task views.
-     * @param transformOut  The result transform that this method returns.
-     * @param stackLayout   The base stack layout algorithm.
-     * @return  The expected transform of the (taskIndex)th task view.
-     */
-    public TaskViewTransform getTransform(int taskIndex, int taskCount,
-        TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
-        if (taskCount == 0) {
-            transformOut.reset();
-            return transformOut;
-        }
-
-        TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
-        mTaskGridRect.set(gridInfo.size);
-
-        int x = gridInfo.xOffsets[taskIndex];
-        int y = gridInfo.yOffsets[taskIndex];
-        float z = stackLayout.mMaxTranslationZ;
-
-        // We always set the dim alpha to 0, since we don't want grid task views to dim.
-        float dimAlpha = 0f;
-        // We always set the alpha of the view outline to 1, to make sure the shadow is visible.
-        float viewOutlineAlpha = 1f;
-
-        // We also need to invert the index in order to display the most recent tasks first.
-        int taskLayoutIndex = taskCount - taskIndex - 1;
-        boolean isTaskViewVisible = taskLayoutIndex < MAX_LAYOUT_TASK_COUNT;
-
-        // Fill out the transform
-        transformOut.scale = 1f;
-        transformOut.alpha = isTaskViewVisible ? 1f : 0f;
-        transformOut.translationZ = z;
-        transformOut.dimAlpha = dimAlpha;
-        transformOut.viewOutlineAlpha = viewOutlineAlpha;
-        transformOut.rect.set(mTaskGridRect);
-        transformOut.rect.offset(x, y);
-        Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
-        // We only show the 8 most recent tasks.
-        transformOut.visible = isTaskViewVisible;
-        return transformOut;
-    }
-
-    /**
-     * Return the proper task index to focus for arrow key navigation.
-     * @param taskCount             The amount of tasks.
-     * @param currentFocusedIndex   The index of the currently focused task.
-     * @param direction             The direction we're navigating.
-     * @return  The index of the task that should get the focus.
-     */
-    public int navigateFocus(int taskCount, int currentFocusedIndex, Direction direction) {
-        if (taskCount < 1 || taskCount > MAX_LAYOUT_TASK_COUNT) {
-            return -1;
-        }
-        if (currentFocusedIndex == -1) {
-            return 0;
-        }
-        int newIndex = currentFocusedIndex;
-        final TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
-        final int currentLine = (taskCount - 1 - currentFocusedIndex) / gridInfo.tasksPerLine;
-        switch (direction) {
-            case UP:
-                newIndex += gridInfo.tasksPerLine;
-                newIndex = newIndex >= taskCount ? currentFocusedIndex : newIndex;
-                break;
-            case DOWN:
-                newIndex -= gridInfo.tasksPerLine;
-                newIndex = newIndex < 0 ? currentFocusedIndex : newIndex;
-                break;
-            case LEFT:
-                newIndex++;
-                final int leftMostIndex = (taskCount - 1) - currentLine * gridInfo.tasksPerLine;
-                newIndex = newIndex > leftMostIndex ? currentFocusedIndex : newIndex;
-                break;
-            case RIGHT:
-                newIndex--;
-                int rightMostIndex =
-                    (taskCount - 1) - (currentLine + 1) * gridInfo.tasksPerLine + 1;
-                rightMostIndex = rightMostIndex < 0 ? 0 : rightMostIndex;
-                newIndex = newIndex < rightMostIndex ? currentFocusedIndex : newIndex;
-                break;
-        }
-        return newIndex;
-    }
-
-    public void initialize(Rect windowRect) {
-        mWindowRect = windowRect;
-        // Define paddings in terms of percentage of the total area.
-        mPaddingLeftRight = (int) (0.025f * Math.min(mWindowRect.width(), mWindowRect.height()));
-        mPaddingTopBottom = (int) (0.1 * mWindowRect.height());
-
-        // Pre-calculate the positions and offsets of task views so that we can reuse them directly
-        // in the future.
-        mTaskGridRectInfoList = new TaskGridRectInfo[MAX_LAYOUT_TASK_COUNT];
-        for (int i = 0; i < MAX_LAYOUT_TASK_COUNT; i++) {
-            mTaskGridRectInfoList[i] = new TaskGridRectInfo(i + 1);
-        }
-    }
-
-    public void setSystemInsets(Rect systemInsets) {
-        mSystemInsets = systemInsets;
-        updateAppAspectRatio();
-    }
-
-    private void updateAppAspectRatio() {
-        int usableWidth = mScreenSize.x - mSystemInsets.left - mSystemInsets.right;
-        int usableHeight = mScreenSize.y - mSystemInsets.top - mSystemInsets.bottom;
-        mAppAspectRatio = (float) usableWidth / (float) usableHeight;
-    }
-
-    public Rect getStackActionButtonRect() {
-        Rect buttonRect = new Rect(mWindowRect);
-        buttonRect.right -= mPaddingLeftRight;
-        buttonRect.left += mPaddingLeftRight;
-        buttonRect.bottom = buttonRect.top + mPaddingTopBottom;
-        return buttonRect;
-    }
-
-    public void updateTaskGridRect(int taskCount) {
-        if (taskCount > 0) {
-            TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
-            mTaskGridRect.set(gridInfo.size);
-        }
-    }
-
-    public Rect getTaskGridRect() {
-        return mTaskGridRect;
-    }
-
-    public int getFocusFrameThickness() {
-        return mFocusedFrameThickness;
-    }
-
-    public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
-        int visibleCount = Math.min(TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT, tasks.size());
-        return new VisibilityReport(visibleCount, visibleCount);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
deleted file mode 100644
index fe6bafb..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.recents.views.grid;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-
-import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
-import com.android.systemui.R;
-import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskStackView;
-
-public class TaskViewFocusFrame extends View implements OnGlobalFocusChangeListener {
-
-    private TaskStackView mSv;
-    private TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
-    public TaskViewFocusFrame(Context context) {
-        this(context, null);
-    }
-
-    public TaskViewFocusFrame(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr,
-        int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        setBackground(mContext.getDrawable(
-            R.drawable.recents_grid_task_view_focus_frame_background));
-        setFocusable(false);
-        hide();
-    }
-
-    public TaskViewFocusFrame(Context context, TaskStackView stackView,
-        TaskGridLayoutAlgorithm taskGridLayoutAlgorithm) {
-        this(context);
-        mSv = stackView;
-        mTaskGridLayoutAlgorithm = taskGridLayoutAlgorithm;
-    }
-
-    /**
-     * Measure the width and height of the focus frame according to the current grid task view size.
-     */
-    public void measure() {
-        int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
-        Rect rect = mTaskGridLayoutAlgorithm.getTaskGridRect();
-        measure(
-            MeasureSpec.makeMeasureSpec(rect.width() + thickness * 2, MeasureSpec.EXACTLY),
-            MeasureSpec.makeMeasureSpec(rect.height() + thickness * 2, MeasureSpec.EXACTLY));
-    }
-
-    /**
-     * Layout the focus frame with its size.
-     */
-    public void layout() {
-        layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
-    }
-
-    /**
-     * Update the current size of grid task view and the focus frame.
-     */
-    public void resize() {
-        if (mSv.useGridLayout()) {
-            mTaskGridLayoutAlgorithm.updateTaskGridRect(mSv.getStack().getTaskCount());
-            measure();
-            requestLayout();
-        }
-    }
-
-    /**
-     * Move the task view focus frame to surround the newly focused view. If it's {@code null} or
-     * it's not an instance of GridTaskView, we hide the focus frame.
-     * @param newFocus The newly focused view.
-     */
-    public void moveGridTaskViewFocus(View newFocus) {
-        if (mSv.useGridLayout()) {
-            // The frame only shows up in the grid layout. It shouldn't show up in the stack
-            // layout including when we're in the split screen.
-            if (newFocus instanceof GridTaskView) {
-                // If the focus goes to a GridTaskView, we show the frame and layout it.
-                int[] location = new int[2];
-                newFocus.getLocationInWindow(location);
-                int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
-                setTranslationX(location[0] - thickness);
-                setTranslationY(location[1] - thickness);
-                show();
-            } else {
-                // If focus goes to other views, we hide the frame.
-                hide();
-            }
-        }
-    }
-
-    @Override
-    public void onGlobalFocusChanged(View oldFocus, View newFocus) {
-        if (!mSv.useGridLayout()) {
-            return;
-        }
-        if (newFocus == null) {
-            // We're going to touch mode, unset the focus.
-            moveGridTaskViewFocus(null);
-            return;
-        }
-        if (oldFocus == null) {
-            // We're returning from touch mode, set the focus to the previously focused task.
-            final TaskStack stack = mSv.getStack();
-            final int taskCount = stack.getTaskCount();
-            final int k = stack.indexOfTask(mSv.getFocusedTask());
-            final int taskIndexToFocus = k == -1 ? (taskCount - 1) : (k % taskCount);
-            mSv.setFocusedTask(taskIndexToFocus, false, true);
-        }
-    }
-
-    private void show() {
-        setAlpha(1f);
-    }
-
-    private void hide() {
-        setAlpha(0f);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java
deleted file mode 100644
index 49cac26..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.recents.views.lowram;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.view.ViewConfiguration;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
-import com.android.systemui.recents.views.TaskViewTransform;
-
-import java.util.ArrayList;
-
-import static com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
-
-public class TaskStackLowRamLayoutAlgorithm {
-
-    private static final String TAG = "TaskStackLowRamLayoutAlgorithm";
-    private static final float MAX_OVERSCROLL = 0.2f / 0.3f;
-
-    public static final int MAX_LAYOUT_TASK_COUNT = 9;
-    public static final int NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME = 2;
-    public static final int NUM_TASK_VISIBLE_LAUNCHED_FROM_APP =
-            NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME + 1;
-    private Rect mWindowRect;
-
-    private int mFlingThreshold;
-    private int mPadding;
-    private int mPaddingLeftRight;
-    private int mTopOffset;
-    private int mPaddingEndTopBottom;
-    private Rect mTaskRect = new Rect();
-    private Rect mSystemInsets = new Rect();
-
-    public TaskStackLowRamLayoutAlgorithm(Context context) {
-        reloadOnConfigurationChange(context);
-    }
-
-    public void reloadOnConfigurationChange(Context context) {
-        mPadding = context.getResources()
-                .getDimensionPixelSize(R.dimen.recents_layout_side_margin_phone);
-        mFlingThreshold = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
-    }
-
-    public void initialize(Rect windowRect) {
-        mWindowRect = windowRect;
-        if (mWindowRect.height() > 0) {
-            int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
-            int windowWidth = mWindowRect.width() - mSystemInsets.right - mSystemInsets.left;
-            int width = Math.min(windowWidth, windowHeight) - mPadding * 2;
-            boolean isLandscape = windowWidth > windowHeight;
-            mTaskRect.set(0, 0, width, isLandscape ? width * 2 / 3 : width);
-            mPaddingLeftRight = (windowWidth - mTaskRect.width()) / 2;
-            mPaddingEndTopBottom = (windowHeight - mTaskRect.height()) / 2;
-
-            // Compute the top offset to center tasks in the middle of the screen
-            mTopOffset = (getTotalHeightOfTasks(MAX_LAYOUT_TASK_COUNT) - windowHeight) / 2;
-        }
-    }
-
-    public void setSystemInsets(Rect systemInsets) {
-        mSystemInsets = systemInsets;
-    }
-
-    public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
-        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-        int maxVisible = launchState.launchedFromHome || launchState.launchedFromPipApp
-                    || launchState.launchedWithNextPipApp
-                ? NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME
-                : NUM_TASK_VISIBLE_LAUNCHED_FROM_APP;
-        int visibleCount = Math.min(maxVisible, tasks.size());
-        return new VisibilityReport(visibleCount, visibleCount);
-    }
-
-    public void getFrontOfStackTransform(TaskViewTransform transformOut,
-            TaskStackLayoutAlgorithm stackLayout) {
-        if (mWindowRect == null) {
-            transformOut.reset();
-            return;
-        }
-
-        // Calculate the static task y position 2 tasks after/below the middle/current task
-        int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
-        int bottomOfCurrentTask = (windowHeight + mTaskRect.height()) / 2;
-        int y = bottomOfCurrentTask + mTaskRect.height() + mPadding * 2;
-        fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, true);
-    }
-
-    public void getBackOfStackTransform(TaskViewTransform transformOut,
-            TaskStackLayoutAlgorithm stackLayout) {
-        if (mWindowRect == null) {
-            transformOut.reset();
-            return;
-        }
-
-        // Calculate the static task y position 2 tasks before/above the middle/current task
-        int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
-        int topOfCurrentTask = (windowHeight - mTaskRect.height()) / 2;
-        int y = topOfCurrentTask - (mTaskRect.height() + mPadding) * 2;
-        fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, true);
-    }
-
-    public TaskViewTransform getTransform(int taskIndex, float stackScroll,
-            TaskViewTransform transformOut, int taskCount, TaskStackLayoutAlgorithm stackLayout) {
-        if (taskCount == 0) {
-            transformOut.reset();
-            return transformOut;
-        }
-        boolean visible = true;
-        int y;
-        if (taskCount > 1) {
-            y = getTaskTopFromIndex(taskIndex) - percentageToScroll(stackScroll);
-
-            // Check visibility from the bottom of the task
-            visible = y + mPadding + getTaskRect().height() > 0;
-        } else {
-            int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
-            y = (windowHeight - mTaskRect.height()) / 2 - percentageToScroll(stackScroll);
-        }
-        fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, visible);
-        return transformOut;
-    }
-
-    /**
-     * Finds the closest task to the scroll percentage in the y axis and returns the percentage of
-     * the task to scroll to.
-     * @param scrollP percentage to find nearest to
-     * @param numTasks number of tasks in recents stack
-     * @param velocity speed of fling
-     */
-    public float getClosestTaskP(float scrollP, int numTasks, int velocity) {
-        int y = percentageToScroll(scrollP);
-
-        int lastY = getTaskTopFromIndex(0) - mPaddingEndTopBottom;
-        for (int i = 1; i < numTasks; i++) {
-            int taskY = getTaskTopFromIndex(i) - mPaddingEndTopBottom;
-            int diff = taskY - y;
-            if (diff > 0) {
-                int diffPrev = Math.abs(y - lastY);
-                boolean useNext = diff > diffPrev;
-                if (Math.abs(velocity) > mFlingThreshold) {
-                    useNext = velocity > 0;
-                }
-                return useNext
-                        ? scrollToPercentage(lastY) : scrollToPercentage(taskY);
-            }
-            lastY = taskY;
-        }
-        return scrollToPercentage(lastY);
-    }
-
-    /**
-     * Convert a scroll value to a percentage
-     * @param scroll a scroll value
-     * @return a percentage that represents the scroll from the total height of tasks
-     */
-    public float scrollToPercentage(int scroll) {
-        return (float) scroll / (mTaskRect.height() + mPadding);
-    }
-
-    /**
-     * Converts a percentage to the scroll value from the total height of tasks
-     * @param p a percentage that represents the scroll value
-     * @return a scroll value in pixels
-     */
-    public int percentageToScroll(float p) {
-        return (int) (p * (mTaskRect.height() + mPadding));
-    }
-
-    /**
-     * Get the min scroll progress for low ram layout. This computes the top position of the
-     * first task and reduce by the end padding to center the first task
-     * @return position of max scroll
-     */
-    public float getMinScrollP() {
-        return getScrollPForTask(0);
-    }
-
-    /**
-     * Get the max scroll progress for low ram layout. This computes the top position of the last
-     * task and reduce by the end padding to center the last task
-     * @param taskCount the amount of tasks in the recents stack
-     * @return position of max scroll
-     */
-    public float getMaxScrollP(int taskCount) {
-        return getScrollPForTask(taskCount - 1);
-    }
-
-    /**
-     * Get the initial scroll value whether launched from home or from an app.
-     * @param taskCount the amount of tasks currently in recents
-     * @param fromHome if launching recents from home or not
-     * @return from home it will return max value and from app it will return 2nd last task
-     */
-    public float getInitialScrollP(int taskCount, boolean fromHome) {
-        if (fromHome) {
-            return getMaxScrollP(taskCount);
-        }
-        if (taskCount < 2) {
-            return 0;
-        }
-        return getScrollPForTask(taskCount - 2);
-    }
-
-    /**
-     * Get the scroll progress for any task
-     * @param taskIndex task index to get the scroll progress of
-     * @return scroll progress of task
-     */
-    public float getScrollPForTask(int taskIndex) {
-        return scrollToPercentage(getTaskTopFromIndex(taskIndex) - mPaddingEndTopBottom);
-    }
-
-    public Rect getTaskRect() {
-        return mTaskRect;
-    }
-
-    public float getMaxOverscroll() {
-        return MAX_OVERSCROLL;
-    }
-
-    private int getTaskTopFromIndex(int index) {
-        return getTotalHeightOfTasks(index) - mTopOffset;
-    }
-
-    private int getTotalHeightOfTasks(int taskCount) {
-        return taskCount * mTaskRect.height() + (taskCount + 1) * mPadding;
-    }
-
-    private void fillStackTransform(TaskViewTransform transformOut, int y, int translationZ,
-            boolean visible) {
-        transformOut.scale = 1f;
-        transformOut.alpha = 1f;
-        transformOut.translationZ = translationZ;
-        transformOut.dimAlpha = 0f;
-        transformOut.viewOutlineAlpha = 1f;
-        transformOut.rect.set(getTaskRect());
-        transformOut.rect.offset(mPaddingLeftRight + mSystemInsets.left, y);
-        Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
-        transformOut.visible = visible;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
new file mode 100644
index 0000000..a9896f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2018 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.systemui.screenrecord;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.MediaRecorder;
+import android.media.ThumbnailUtils;
+import android.media.projection.MediaProjection;
+import android.media.projection.MediaProjectionManager;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.IBinder;
+import android.provider.MediaStore;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Surface;
+import android.widget.Toast;
+
+import androidx.core.content.FileProvider;
+
+import com.android.systemui.R;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * A service which records the device screen and optionally microphone input.
+ */
+public class RecordingService extends Service {
+    private static final int NOTIFICATION_ID = 1;
+    private static final String TAG = "RecordingService";
+    private static final String CHANNEL_ID = "screen_record";
+    private static final String EXTRA_RESULT_CODE = "extra_resultCode";
+    private static final String EXTRA_DATA = "extra_data";
+    private static final String EXTRA_PATH = "extra_path";
+    private static final String EXTRA_USE_AUDIO = "extra_useAudio";
+    private static final String EXTRA_SHOW_TAPS = "extra_showTaps";
+    private static final int REQUEST_CODE = 2;
+
+    private static final String ACTION_START = "com.android.systemui.screenrecord.START";
+    private static final String ACTION_STOP = "com.android.systemui.screenrecord.STOP";
+    private static final String ACTION_PAUSE = "com.android.systemui.screenrecord.PAUSE";
+    private static final String ACTION_RESUME = "com.android.systemui.screenrecord.RESUME";
+    private static final String ACTION_CANCEL = "com.android.systemui.screenrecord.CANCEL";
+    private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE";
+    private static final String ACTION_DELETE = "com.android.systemui.screenrecord.DELETE";
+
+    private static final int TOTAL_NUM_TRACKS = 1;
+    private static final String RECORD_DIR = "Captures"; // TODO: use a translatable string
+    private static final int VIDEO_BIT_RATE = 6000000;
+    private static final int VIDEO_FRAME_RATE = 30;
+    private static final int AUDIO_BIT_RATE = 16;
+    private static final int AUDIO_SAMPLE_RATE = 44100;
+    private static final String FILE_PROVIDER = "com.android.systemui.fileprovider";
+
+    private MediaProjectionManager mMediaProjectionManager;
+    private MediaProjection mMediaProjection;
+    private Surface mInputSurface;
+    private VirtualDisplay mVirtualDisplay;
+    private MediaRecorder mMediaRecorder;
+    private Notification.Builder mRecordingNotificationBuilder;
+
+    private boolean mUseAudio;
+    private boolean mShowTaps;
+    private File mTempFile;
+
+    /**
+     * Get an intent to start the recording service.
+     *
+     * @param context    Context from the requesting activity
+     * @param resultCode The result code from {@link android.app.Activity#onActivityResult(int, int,
+     *                   android.content.Intent)}
+     * @param data       The data from {@link android.app.Activity#onActivityResult(int, int,
+     *                   android.content.Intent)}
+     * @param useAudio   True to enable microphone input while recording
+     * @param showTaps   True to make touches visible while recording
+     */
+    public static Intent getStartIntent(Context context, int resultCode, Intent data,
+            boolean useAudio, boolean showTaps) {
+        return new Intent(context, RecordingService.class)
+                .setAction(ACTION_START)
+                .putExtra(EXTRA_RESULT_CODE, resultCode)
+                .putExtra(EXTRA_DATA, data)
+                .putExtra(EXTRA_USE_AUDIO, useAudio)
+                .putExtra(EXTRA_SHOW_TAPS, showTaps);
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.d(TAG, "RecordingService is starting");
+        if (intent == null) {
+            return Service.START_NOT_STICKY;
+        }
+        String action = intent.getAction();
+
+        NotificationManager notificationManager =
+                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+        switch (action) {
+            case ACTION_START:
+                int resultCode = intent.getIntExtra(EXTRA_RESULT_CODE, Activity.RESULT_CANCELED);
+                mUseAudio = intent.getBooleanExtra(EXTRA_USE_AUDIO, false);
+                mShowTaps = intent.getBooleanExtra(EXTRA_SHOW_TAPS, false);
+                Intent data = intent.getParcelableExtra(EXTRA_DATA);
+                if (data != null) {
+                    mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data);
+                    startRecording();
+                }
+                break;
+
+            case ACTION_CANCEL:
+                stopRecording();
+
+                // Delete temp file
+                if (!mTempFile.delete()) {
+                    Log.e(TAG, "Error canceling screen recording!");
+                    Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG)
+                            .show();
+                } else {
+                    Toast.makeText(this, R.string.screenrecord_cancel_success, Toast.LENGTH_LONG)
+                            .show();
+                }
+
+                // Close quick shade
+                sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+                break;
+
+            case ACTION_STOP:
+                stopRecording();
+
+                // Move temp file to user directory
+                File recordDir = new File(
+                        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
+                        RECORD_DIR);
+                recordDir.mkdirs();
+
+                String fileName = new SimpleDateFormat("'screen-'yyyyMMdd-HHmmss'.mp4'")
+                        .format(new Date());
+                Path path = new File(recordDir, fileName).toPath();
+
+                try {
+                    Files.move(mTempFile.toPath(), path);
+                    Notification notification = createSaveNotification(path);
+                    notificationManager.notify(NOTIFICATION_ID, notification);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG)
+                            .show();
+                }
+                break;
+
+            case ACTION_PAUSE:
+                mMediaRecorder.pause();
+                setNotificationActions(true, notificationManager);
+                break;
+
+            case ACTION_RESUME:
+                mMediaRecorder.resume();
+                setNotificationActions(false, notificationManager);
+                break;
+
+            case ACTION_SHARE:
+                File shareFile = new File(intent.getStringExtra(EXTRA_PATH));
+                Uri shareUri = FileProvider.getUriForFile(this, FILE_PROVIDER, shareFile);
+
+                Intent shareIntent = new Intent(Intent.ACTION_SEND)
+                        .setType("video/mp4")
+                        .putExtra(Intent.EXTRA_STREAM, shareUri);
+                String shareLabel = getResources().getString(R.string.screenrecord_share_label);
+
+                // Close quick shade
+                sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+
+                // Remove notification
+                notificationManager.cancel(NOTIFICATION_ID);
+
+                startActivity(Intent.createChooser(shareIntent, shareLabel)
+                                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+                break;
+            case ACTION_DELETE:
+                // Close quick shade
+                sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+
+                File file = new File(intent.getStringExtra(EXTRA_PATH));
+                if (file.delete()) {
+                    Toast.makeText(
+                            this,
+                            R.string.screenrecord_delete_description,
+                            Toast.LENGTH_LONG).show();
+
+                    // Remove notification
+                    notificationManager.cancel(NOTIFICATION_ID);
+                } else {
+                    Log.e(TAG, "Error deleting screen recording!");
+                    Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG)
+                            .show();
+                }
+                break;
+        }
+        return Service.START_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        mMediaProjectionManager =
+                (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
+    }
+
+    /**
+     * Begin the recording session
+     */
+    private void startRecording() {
+        try {
+            mTempFile = File.createTempFile("temp", ".mp4");
+            Log.d(TAG, "Writing video output to: " + mTempFile.getAbsolutePath());
+
+            setTapsVisible(mShowTaps);
+
+            // Set up media recorder
+            mMediaRecorder = new MediaRecorder();
+            if (mUseAudio) {
+                mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+            }
+            mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
+            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+
+            // Set up video
+            DisplayMetrics metrics = getResources().getDisplayMetrics();
+            int screenWidth = metrics.widthPixels;
+            int screenHeight = metrics.heightPixels;
+            mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+            mMediaRecorder.setVideoSize(screenWidth, screenHeight);
+            mMediaRecorder.setVideoFrameRate(VIDEO_FRAME_RATE);
+            mMediaRecorder.setVideoEncodingBitRate(VIDEO_BIT_RATE);
+
+            // Set up audio
+            if (mUseAudio) {
+                mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+                mMediaRecorder.setAudioChannels(TOTAL_NUM_TRACKS);
+                mMediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE);
+                mMediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE);
+            }
+
+            mMediaRecorder.setOutputFile(mTempFile);
+            mMediaRecorder.prepare();
+
+            // Create surface
+            mInputSurface = mMediaRecorder.getSurface();
+            mVirtualDisplay = mMediaProjection.createVirtualDisplay(
+                    "Recording Display",
+                    screenWidth,
+                    screenHeight,
+                    metrics.densityDpi,
+                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+                    mInputSurface,
+                    null,
+                    null);
+
+            mMediaRecorder.start();
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+
+        createRecordingNotification();
+    }
+
+    private void createRecordingNotification() {
+        NotificationChannel channel = new NotificationChannel(
+                CHANNEL_ID,
+                getString(R.string.screenrecord_name),
+                NotificationManager.IMPORTANCE_HIGH);
+        channel.setDescription(getString(R.string.screenrecord_channel_description));
+        channel.enableVibration(true);
+        NotificationManager notificationManager =
+                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        notificationManager.createNotificationChannel(channel);
+
+        mRecordingNotificationBuilder = new Notification.Builder(this, CHANNEL_ID)
+                .setSmallIcon(R.drawable.ic_android)
+                .setContentTitle(getResources().getString(R.string.screenrecord_name))
+                .setUsesChronometer(true)
+                .setOngoing(true);
+        setNotificationActions(false, notificationManager);
+        Notification notification = mRecordingNotificationBuilder.build();
+        startForeground(NOTIFICATION_ID, notification);
+    }
+
+    private void setNotificationActions(boolean isPaused, NotificationManager notificationManager) {
+        String pauseString = getResources()
+                .getString(isPaused ? R.string.screenrecord_resume_label
+                        : R.string.screenrecord_pause_label);
+        Intent pauseIntent = isPaused ? getResumeIntent(this) : getPauseIntent(this);
+
+        mRecordingNotificationBuilder.setActions(
+                new Notification.Action.Builder(
+                        Icon.createWithResource(this, R.drawable.ic_android),
+                        getResources().getString(R.string.screenrecord_stop_label),
+                        PendingIntent
+                                .getService(this, REQUEST_CODE, getStopIntent(this),
+                                        PendingIntent.FLAG_UPDATE_CURRENT))
+                        .build(),
+                new Notification.Action.Builder(
+                        Icon.createWithResource(this, R.drawable.ic_android), pauseString,
+                        PendingIntent.getService(this, REQUEST_CODE, pauseIntent,
+                                PendingIntent.FLAG_UPDATE_CURRENT))
+                        .build(),
+                new Notification.Action.Builder(
+                        Icon.createWithResource(this, R.drawable.ic_android),
+                        getResources().getString(R.string.screenrecord_cancel_label),
+                        PendingIntent
+                                .getService(this, REQUEST_CODE, getCancelIntent(this),
+                                        PendingIntent.FLAG_UPDATE_CURRENT))
+                        .build());
+        notificationManager.notify(NOTIFICATION_ID, mRecordingNotificationBuilder.build());
+    }
+
+    private Notification createSaveNotification(Path path) {
+        Uri saveUri = FileProvider.getUriForFile(this, FILE_PROVIDER, path.toFile());
+        Log.d(TAG, "Screen recording saved to " + path.toString());
+
+        Intent viewIntent = new Intent(Intent.ACTION_VIEW)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION)
+                .setDataAndType(saveUri, "video/mp4");
+
+        Notification.Action shareAction = new Notification.Action.Builder(
+                Icon.createWithResource(this, R.drawable.ic_android),
+                getResources().getString(R.string.screenrecord_share_label),
+                PendingIntent.getService(
+                        this,
+                        REQUEST_CODE,
+                        getShareIntent(this, path.toString()),
+                        PendingIntent.FLAG_UPDATE_CURRENT))
+                .build();
+
+        Notification.Action deleteAction = new Notification.Action.Builder(
+                Icon.createWithResource(this, R.drawable.ic_android),
+                getResources().getString(R.string.screenrecord_delete_label),
+                PendingIntent.getService(
+                        this,
+                        REQUEST_CODE,
+                        getDeleteIntent(this, path.toString()),
+                        PendingIntent.FLAG_UPDATE_CURRENT))
+                .build();
+
+        Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
+                .setSmallIcon(R.drawable.ic_android)
+                .setContentTitle(getResources().getString(R.string.screenrecord_name))
+                .setContentText(getResources().getString(R.string.screenrecord_save_message))
+                .setContentIntent(PendingIntent.getActivity(
+                        this,
+                        REQUEST_CODE,
+                        viewIntent,
+                        Intent.FLAG_GRANT_READ_URI_PERMISSION))
+                .addAction(shareAction)
+                .addAction(deleteAction)
+                .setAutoCancel(true);
+
+        // Add thumbnail if available
+        Bitmap thumbnailBitmap = ThumbnailUtils.createVideoThumbnail(path.toString(),
+                MediaStore.Video.Thumbnails.MINI_KIND);
+        if (thumbnailBitmap != null) {
+            Notification.BigPictureStyle pictureStyle = new Notification.BigPictureStyle()
+                    .bigPicture(thumbnailBitmap)
+                    .bigLargeIcon((Bitmap) null);
+            builder.setLargeIcon(thumbnailBitmap).setStyle(pictureStyle);
+        }
+        return builder.build();
+    }
+
+    private void stopRecording() {
+        setTapsVisible(false);
+        mMediaRecorder.stop();
+        mMediaRecorder.release();
+        mMediaRecorder = null;
+        mMediaProjection.stop();
+        mMediaProjection = null;
+        mInputSurface.release();
+        mVirtualDisplay.release();
+        stopSelf();
+    }
+
+    private void setTapsVisible(boolean turnOn) {
+        int value = turnOn ? 1 : 0;
+        Settings.System.putInt(getApplicationContext().getContentResolver(),
+                Settings.System.SHOW_TOUCHES, value);
+    }
+
+    private static Intent getStopIntent(Context context) {
+        return new Intent(context, RecordingService.class).setAction(ACTION_STOP);
+    }
+
+    private static Intent getPauseIntent(Context context) {
+        return new Intent(context, RecordingService.class).setAction(ACTION_PAUSE);
+    }
+
+    private static Intent getResumeIntent(Context context) {
+        return new Intent(context, RecordingService.class).setAction(ACTION_RESUME);
+    }
+
+    private static Intent getCancelIntent(Context context) {
+        return new Intent(context, RecordingService.class).setAction(ACTION_CANCEL);
+    }
+
+    private static Intent getShareIntent(Context context, String path) {
+        return new Intent(context, RecordingService.class).setAction(ACTION_SHARE)
+                .putExtra(EXTRA_PATH, path);
+    }
+
+    private static Intent getDeleteIntent(Context context, String path) {
+        return new Intent(context, RecordingService.class).setAction(ACTION_DELETE)
+                .putExtra(EXTRA_PATH, path);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
new file mode 100644
index 0000000..27e9fba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 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.systemui.screenrecord;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.media.projection.MediaProjectionManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.Toast;
+
+import com.android.systemui.R;
+
+/**
+ * Activity to select screen recording options
+ */
+public class ScreenRecordDialog extends Activity {
+    private static final String TAG = "ScreenRecord";
+    private static final int REQUEST_CODE_VIDEO_ONLY = 200;
+    private static final int REQUEST_CODE_VIDEO_TAPS = 201;
+    private static final int REQUEST_CODE_PERMISSIONS = 299;
+    private static final int REQUEST_CODE_VIDEO_AUDIO = 300;
+    private static final int REQUEST_CODE_VIDEO_AUDIO_TAPS = 301;
+    private static final int REQUEST_CODE_PERMISSIONS_AUDIO = 399;
+    private boolean mUseAudio;
+    private boolean mShowTaps;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.screen_record_dialog);
+
+        final CheckBox micCheckBox = findViewById(R.id.checkbox_mic);
+        final CheckBox tapsCheckBox = findViewById(R.id.checkbox_taps);
+
+        final Button recordButton = findViewById(R.id.record_button);
+        recordButton.setOnClickListener(v -> {
+            mUseAudio = micCheckBox.isChecked();
+            mShowTaps = tapsCheckBox.isChecked();
+            Log.d(TAG, "Record button clicked: audio " + mUseAudio + ", taps " + mShowTaps);
+
+            if (mUseAudio && checkSelfPermission(Manifest.permission.RECORD_AUDIO)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Log.d(TAG, "Requesting permission for audio");
+                requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO},
+                        REQUEST_CODE_PERMISSIONS_AUDIO);
+            } else {
+                requestScreenCapture();
+            }
+        });
+    }
+
+    private void requestScreenCapture() {
+        MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(
+                Context.MEDIA_PROJECTION_SERVICE);
+        Intent permissionIntent = mediaProjectionManager.createScreenCaptureIntent();
+
+        if (mUseAudio) {
+            startActivityForResult(permissionIntent,
+                    mShowTaps ? REQUEST_CODE_VIDEO_AUDIO_TAPS : REQUEST_CODE_VIDEO_AUDIO);
+        } else {
+            startActivityForResult(permissionIntent,
+                    mShowTaps ? REQUEST_CODE_VIDEO_TAPS : REQUEST_CODE_VIDEO_ONLY);
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        mShowTaps = (requestCode == REQUEST_CODE_VIDEO_TAPS
+                || requestCode == REQUEST_CODE_VIDEO_AUDIO_TAPS);
+        switch (requestCode) {
+            case REQUEST_CODE_VIDEO_TAPS:
+            case REQUEST_CODE_VIDEO_AUDIO_TAPS:
+            case REQUEST_CODE_VIDEO_ONLY:
+            case REQUEST_CODE_VIDEO_AUDIO:
+                if (resultCode == RESULT_OK) {
+                    mUseAudio = (requestCode == REQUEST_CODE_VIDEO_AUDIO
+                            || requestCode == REQUEST_CODE_VIDEO_AUDIO_TAPS);
+                    startForegroundService(
+                            RecordingService.getStartIntent(this, resultCode, data, mUseAudio,
+                                    mShowTaps));
+                } else {
+                    Toast.makeText(this,
+                            getResources().getString(R.string.screenrecord_permission_error),
+                            Toast.LENGTH_SHORT).show();
+                }
+                finish();
+                break;
+            case REQUEST_CODE_PERMISSIONS:
+                int permission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+                if (permission != PackageManager.PERMISSION_GRANTED) {
+                    Toast.makeText(this,
+                            getResources().getString(R.string.screenrecord_permission_error),
+                            Toast.LENGTH_SHORT).show();
+                    finish();
+                } else {
+                    requestScreenCapture();
+                }
+                break;
+            case REQUEST_CODE_PERMISSIONS_AUDIO:
+                int videoPermission = checkSelfPermission(
+                        Manifest.permission.WRITE_EXTERNAL_STORAGE);
+                int audioPermission = checkSelfPermission(Manifest.permission.RECORD_AUDIO);
+                if (videoPermission != PackageManager.PERMISSION_GRANTED
+                        || audioPermission != PackageManager.PERMISSION_GRANTED) {
+                    Toast.makeText(this,
+                            getResources().getString(R.string.screenrecord_permission_error),
+                            Toast.LENGTH_SHORT).show();
+                    finish();
+                } else {
+                    requestScreenCapture();
+                }
+                break;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 8526afd..8a86826 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -128,7 +128,8 @@
                 }
                 return true;
             case MotionEvent.ACTION_UP:
-                if (!isFalseTouch() && mDragDownCallback.onDraggedDown(mStartingChild,
+                if (!mFalsingManager.isUnlockingDisabled() && !isFalseTouch()
+                        && mDragDownCallback.onDraggedDown(mStartingChild,
                         (int) (y - mInitialTouchY))) {
                     if (mStartingChild == null) {
                         cancelExpansion();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index a00eac4..960d221 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -51,6 +51,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
 import com.android.systemui.statusbar.phone.LockIcon;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -66,7 +67,7 @@
 /**
  * Controls the indications and error messages shown on the Keyguard
  */
-public class KeyguardIndicationController {
+public class KeyguardIndicationController implements StateListener {
 
     private static final String TAG = "KeyguardIndication";
     private static final boolean DEBUG_CHARGING_SPEED = false;
@@ -154,6 +155,19 @@
         mContext.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
                 new IntentFilter(Intent.ACTION_TIME_TICK), null,
                 Dependency.get(Dependency.TIME_TICK_HANDLER));
+
+        Dependency.get(StatusBarStateController.class).addListener(this);
+    }
+
+    /**
+     * Used by {@link com.android.systemui.statusbar.phone.StatusBar} to give the indication
+     * controller a chance to unregister itself as a receiver.
+     *
+     * //TODO: This can probably be converted to a fragment and not have to be manually recreated
+     */
+    public void destroy() {
+        mContext.unregisterReceiver(mTickReceiver);
+        Dependency.get(StatusBarStateController.class).removeListener(this);
     }
 
     /**
@@ -518,6 +532,16 @@
         updateAlphas();
     }
 
+    @Override
+    public void onStateChanged(int newState) {
+        // don't care
+    }
+
+    @Override
+    public void onDozingChanged(boolean isDozing) {
+        setDozing(isDozing);
+    }
+
     protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
         public static final int HIDE_DELAY_MS = 5000;
         private int mLastSuccessiveErrorMessage = -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 178c5c5..e3bc5b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.statusbar;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
 
 import android.app.ActivityManager;
@@ -44,11 +45,11 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
-import com.android.systemui.OverviewProxyService;
+import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
+import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
@@ -292,8 +293,16 @@
             Log.wtf(TAG, "mEntryManager was null!", new Throwable());
             return false;
         }
-        return mShowLockscreenNotifications
-                && !getEntryManager().getNotificationData().isAmbient(sbn.getKey());
+        boolean exceedsPriorityThreshold;
+        if (NotificationUtils.useNewInterruptionModel(mContext)) {
+            exceedsPriorityThreshold =
+                    getEntryManager().getNotificationData().getImportance(sbn.getKey())
+                            >= IMPORTANCE_DEFAULT;
+        } else {
+            exceedsPriorityThreshold =
+                    !getEntryManager().getNotificationData().isAmbient(sbn.getKey());
+        }
+        return mShowLockscreenNotifications && exceedsPriorityThreshold;
     }
 
     private void setShowLockscreenNotifications(boolean show) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index f30377e..8c53cc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -35,6 +35,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -47,7 +48,6 @@
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
-import com.android.systemui.InitController;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -122,7 +122,7 @@
 
         @Override
         public boolean onClickHandler(
-                final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
+                View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) {
             getShadeController().wakeUpIfDozing(SystemClock.uptimeMillis(), view);
 
             if (handleRemoteInput(view, pendingIntent)) {
@@ -141,8 +141,12 @@
                 ActivityManager.getService().resumeAppSwitches();
             } catch (RemoteException e) {
             }
-            return mCallback.handleRemoteViewClick(view, pendingIntent, fillInIntent,
-                    () -> super.onClickHandler(view, pendingIntent, fillInIntent));
+            return mCallback.handleRemoteViewClick(pendingIntent, () -> {
+                Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
+                options.second.setLaunchWindowingMode(
+                        WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+                return RemoteViews.startPendingIntent(view, pendingIntent, options);
+            });
         }
 
         private void logActionClick(View view) {
@@ -180,13 +184,6 @@
             return null;
         }
 
-        @Override
-        protected ActivityOptions getActivityOptions(Context context) {
-            ActivityOptions options = super.getActivityOptions(context);
-            options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
-            return options;
-        }
-
         private boolean handleRemoteInput(View view, PendingIntent pendingIntent) {
             if (mCallback.shouldHandleRemoteInput(view, pendingIntent)) {
                 return true;
@@ -661,14 +658,11 @@
          * Performs any special handling for a remote view click. The default behaviour can be
          * called through the defaultHandler parameter.
          *
-         * @param view
          * @param pendingIntent
-         * @param fillInIntent
          * @param defaultHandler
          * @return  true iff the click was handled
          */
-        boolean handleRemoteViewClick(View view, PendingIntent pendingIntent, Intent fillInIntent,
-                ClickHandler defaultHandler);
+        boolean handleRemoteViewClick(PendingIntent pendingIntent, ClickHandler defaultHandler);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index cd3da123..bf33614 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -43,13 +43,13 @@
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.phone.NotificationIconContainer;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.StackScrollState;
 import com.android.systemui.statusbar.notification.stack.ViewState;
+import com.android.systemui.statusbar.phone.NotificationIconContainer;
 
 /**
  * A notification shelf view that is placed inside the notification scroller. It manages the
@@ -58,7 +58,6 @@
 public class NotificationShelf extends ActivatableNotificationView implements
         View.OnLayoutChangeListener {
 
-    public static final boolean SHOW_AMBIENT_ICONS = true;
     private static final boolean USE_ANIMATIONS_WHEN_OPENING =
             SystemProperties.getBoolean("debug.icon_opening_animations", true);
     private static final boolean ICON_ANMATIONS_WHILE_SCROLLING
@@ -176,21 +175,6 @@
         updateInteractiveness();
     }
 
-    public void fadeInTranslating() {
-        mShelfIcons.setTranslationY(-mShelfAppearTranslation);
-        mShelfIcons.setAlpha(0);
-        mShelfIcons.animate()
-                .setInterpolator(Interpolators.DECELERATE_QUINT)
-                .translationY(0)
-                .setDuration(SHELF_IN_TRANSLATION_DURATION)
-                .start();
-        mShelfIcons.animate()
-                .alpha(1)
-                .setInterpolator(Interpolators.LINEAR)
-                .setDuration(SHELF_IN_TRANSLATION_DURATION)
-                .start();
-    }
-
     @Override
     protected View getContentView() {
         return mShelfIcons;
@@ -417,7 +401,7 @@
         float maxTop = row.getTranslationY();
         StatusBarIconView icon = row.getEntry().expandedIcon;
         float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
-        if (shelfIconPosition < maxTop && !mAmbientState.isDark()) {
+        if (shelfIconPosition < maxTop) {
             int top = (int) (maxTop - shelfIconPosition);
             Rect clipRect = new Rect(0, top, icon.getWidth(), Math.max(top, icon.getHeight()));
             icon.setClipBounds(clipRect);
@@ -428,7 +412,7 @@
 
     private void updateContinuousClipping(final ExpandableNotificationRow row) {
         StatusBarIconView icon = row.getEntry().expandedIcon;
-        boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDark();
+        boolean needsContinuousClipping = ViewState.isAnimatingY(icon);
         boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
         if (needsContinuousClipping && !isContinuousClipping) {
             final ViewTreeObserver observer = icon.getViewTreeObserver();
@@ -619,9 +603,7 @@
             iconState.translateContent = false;
         }
         float transitionAmount;
-        if (mAmbientState.getDarkAmount() > 0 && !row.isInShelf()) {
-            transitionAmount = mAmbientState.isFullyDark() ? 1 : 0;
-        } else if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount
+        if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount
                 || iconState.useLinearTransitionAmount) {
             transitionAmount = iconTransitionAmount;
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 1e04377..8b61a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -206,10 +206,6 @@
         mIconScale = SYSTEM_ICON_SCALE;
     }
 
-    public float getIconScaleFullyDark() {
-        return (float) mStatusBarIconDrawingSizeDark / mStatusBarIconDrawingSize;
-    }
-
     public float getIconScale() {
         return mIconScale;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index c35e1da..bc3638e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -21,6 +21,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.util.Log;
+import android.view.Display;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -35,7 +36,6 @@
 import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.recents.Recents;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.statusbar.StatusBarState;
@@ -268,6 +268,14 @@
         buildNavBarWindows();
         buildNavBarContent();
         attachNavBarWindows();
+
+        // Add external navigation bars if more than one displays exist.
+        final Display[] displays = mDisplayManager.getDisplays();
+
+        for (Display display : displays) {
+            // TODO(115978725): Add phone navigationBar for now
+            addExternalNavigationBar(display);
+        }
     }
 
     private void buildNavBarContent() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
index 27a5d4b..f9fa44b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
@@ -79,7 +79,7 @@
 
                 @Override
                 public void onFinish() {
-                    mCarUserManagerHelper.startNewGuestSession(mGuestName);
+                    mCarUserManagerHelper.startGuestSession(mGuestName);
                     cancel();
                 }
             };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 618a4c1..53a7afe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -223,7 +223,7 @@
 
                 if (userRecord.mIsStartGuestSession) {
                     notifyUserSelected(userRecord);
-                    mCarUserManagerHelper.startNewGuestSession(mGuestName);
+                    mCarUserManagerHelper.startGuestSession(mGuestName);
                     return;
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java
deleted file mode 100644
index 9e99fbb..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.statusbar.notification;
-
-import android.app.AppOpsManager;
-import android.content.Context;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.statusbar.NotificationPresenter;
-
-/**
- * This class handles listening to notification updates and passing them along to
- * NotificationPresenter to be displayed to the user.
- */
-public class AppOpsListener implements AppOpsManager.OnOpActiveChangedListener {
-    private static final String TAG = "NotificationListener";
-
-    // Dependencies:
-    private final ForegroundServiceController mFsc =
-            Dependency.get(ForegroundServiceController.class);
-    private final NotificationEntryManager mEntryManager =
-            Dependency.get(NotificationEntryManager.class);
-
-    private final Context mContext;
-    protected NotificationPresenter mPresenter;
-    protected final AppOpsManager mAppOps;
-
-    protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA,
-            AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
-            AppOpsManager.OP_RECORD_AUDIO};
-
-    public AppOpsListener(Context context) {
-        mContext = context;
-        mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-    }
-
-    public void setUpWithPresenter(NotificationPresenter presenter) {
-        mPresenter = presenter;
-        mAppOps.startWatchingActive(OPS, this);
-    }
-
-    public void destroy() {
-        mAppOps.stopWatchingActive(this);
-    }
-
-    @Override
-    public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
-        mFsc.onAppOpChanged(code, uid, packageName, active);
-        Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
-          mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active);
-        });
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index 66ba9e9..1f48c15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -16,8 +16,11 @@
 
 package com.android.systemui.statusbar.notification;
 
+import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
+
 import android.content.Context;
 import android.graphics.Color;
+import android.provider.Settings;
 import android.view.View;
 import android.widget.ImageView;
 
@@ -68,4 +71,10 @@
                 context.getResources().getDisplayMetrics().density);
         return (int) (dimensionPixelSize * factor);
     }
+
+    /** Returns the value of the new interruption model setting. */
+    public static boolean useNewInterruptionModel(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) != 0;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index b5fbde1..5dfd5d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -29,9 +29,11 @@
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.UiOffloadThread;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 
 import java.util.ArrayList;
@@ -42,7 +44,7 @@
  * Handles notification logging, in particular, logging which notifications are visible and which
  * are not.
  */
-public class NotificationLogger {
+public class NotificationLogger implements StateListener {
     private static final String TAG = "NotificationLogger";
 
     /** The minimum delay in ms between reports of notification visibility. */
@@ -63,7 +65,7 @@
     protected IStatusBarService mBarService;
     private long mLastVisibilityReportUptimeMs;
     private NotificationListContainer mListContainer;
-    private Object mDozingLock = new Object();
+    private final Object mDozingLock = new Object();
     private boolean mDozing;
 
     protected final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
@@ -146,6 +148,8 @@
     public NotificationLogger() {
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        // Not expected to be destroyed, don't need to unsubscribe
+        Dependency.get(StatusBarStateController.class).addListener(this);
     }
 
     public void setUpWithContainer(NotificationListContainer listContainer) {
@@ -175,7 +179,7 @@
         mNotificationLocationsChangedListener.onChildLocationsChanged();
     }
 
-    public void setDozing(boolean dozing) {
+    private void setDozing(boolean dozing) {
         synchronized (mDozingLock) {
             mDozing = dozing;
         }
@@ -258,6 +262,16 @@
         return mVisibilityReporter;
     }
 
+    @Override
+    public void onStateChanged(int newState) {
+        // don't care about state change
+    }
+
+    @Override
+    public void onDozingChanged(boolean isDozing) {
+        setDozing(isDozing);
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 22c37fd..965fb13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -82,7 +82,6 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
@@ -1500,9 +1499,7 @@
     }
 
     private void updateIconVisibilities() {
-        boolean visible = isChildInGroup()
-                || (isBelowSpeedBump() && !NotificationShelf.SHOW_AMBIENT_ICONS)
-                || mIconsVisible;
+        boolean visible = isChildInGroup() || mIconsVisible;
         for (NotificationContentView l : mLayouts) {
             l.setIconsVisible(visible);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 7e60c4b..674c8ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -16,17 +16,8 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import java.util.ArrayList;
-
 import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
 
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.AlphaOptimizedImageView;
-import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
@@ -45,6 +36,15 @@
 import android.widget.FrameLayout.LayoutParams;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
+import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+
+import java.util.ArrayList;
+import java.util.List;
 
 public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
         ExpandableNotificationRow.LayoutListener {
@@ -70,7 +70,8 @@
     private MenuItem mInfoItem;
     private MenuItem mAppOpsItem;
     private MenuItem mSnoozeItem;
-    private ArrayList<MenuItem> mMenuItems;
+    private ArrayList<MenuItem> mLeftMenuItems;
+    private ArrayList<MenuItem> mRightMenuItems;
     private OnMenuEventListener mMenuListener;
 
     private ValueAnimator mFadeAnimator;
@@ -107,12 +108,13 @@
         mContext = context;
         mShouldShowMenu = context.getResources().getBoolean(R.bool.config_showNotificationGear);
         mHandler = new Handler(Looper.getMainLooper());
-        mMenuItems = new ArrayList<>();
+        mLeftMenuItems = new ArrayList<>();
+        mRightMenuItems = new ArrayList<>();
     }
 
     @Override
     public ArrayList<MenuItem> getMenuItems(Context context) {
-        return mMenuItems;
+        return mOnLeft ? mLeftMenuItems : mRightMenuItems;
     }
 
     @Override
@@ -231,7 +233,8 @@
         final Resources res = mContext.getResources();
         mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
         mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
-        mMenuItems.clear();
+        mLeftMenuItems.clear();
+        mRightMenuItems.clear();
         // Construct the menu items based on the notification
         if (mParent != null && mParent.getStatusBarNotification() != null) {
             int flags = mParent.getStatusBarNotification().getNotification().flags;
@@ -239,24 +242,19 @@
             if (!isForeground) {
                 // Only show snooze for non-foreground notifications
                 mSnoozeItem = createSnoozeItem(mContext);
-                mMenuItems.add(mSnoozeItem);
+                mLeftMenuItems.add(mSnoozeItem);
+                mRightMenuItems.add(mSnoozeItem);
             }
         }
         mInfoItem = createInfoItem(mContext);
-        mMenuItems.add(mInfoItem);
+        mLeftMenuItems.add(mInfoItem);
+        mRightMenuItems.add(mInfoItem);
 
         mAppOpsItem = createAppOpsItem(mContext);
-        mMenuItems.add(mAppOpsItem);
+        mLeftMenuItems.add(mAppOpsItem);
+        mRightMenuItems.add(mAppOpsItem);
 
-        // Construct the menu views
-        if (mMenuContainer != null) {
-            mMenuContainer.removeAllViews();
-        } else {
-            mMenuContainer = new FrameLayout(mContext);
-        }
-        for (int i = 0; i < mMenuItems.size(); i++) {
-            addMenuView(mMenuItems.get(i), mMenuContainer);
-        }
+        populateMenuViews();
         if (resetState) {
             resetState(false /* notify */);
         } else {
@@ -268,6 +266,18 @@
         }
     }
 
+    private void populateMenuViews() {
+        if (mMenuContainer != null) {
+            mMenuContainer.removeAllViews();
+        } else {
+            mMenuContainer = new FrameLayout(mContext);
+        }
+        List<MenuItem> menuItems = mOnLeft ? mLeftMenuItems : mRightMenuItems;
+        for (int i = 0; i < menuItems.size(); i++) {
+            addMenuView(menuItems.get(i), mMenuContainer);
+        }
+    }
+
     private void resetState(boolean notify) {
         setMenuAlpha(0f);
         mIconsPlaced = false;
@@ -386,10 +396,16 @@
         if (appName == null) {
             return;
         }
+        setAppName(appName, mLeftMenuItems);
+        setAppName(appName, mRightMenuItems);
+    }
+
+    private void setAppName(String appName,
+            ArrayList<MenuItem> menuItems) {
         Resources res = mContext.getResources();
-        final int count = mMenuItems.size();
+        final int count = menuItems.size();
         for (int i = 0; i < count; i++) {
-            MenuItem item = mMenuItems.get(i);
+            MenuItem item = menuItems.get(i);
             String description = String.format(
                     res.getString(R.string.notification_menu_accessibility),
                     appName, item.getContentDescription());
@@ -402,7 +418,9 @@
 
     @Override
     public void onParentHeightUpdate() {
-        if (mParent == null || mMenuItems.size() == 0 || mMenuContainer == null) {
+        if (mParent == null
+                || (mLeftMenuItems.isEmpty() && mRightMenuItems.isEmpty())
+                || mMenuContainer == null) {
             return;
         }
         int parentHeight = mParent.getActualHeight();
@@ -443,13 +461,14 @@
         }
         v.getLocationOnScreen(mIconLocation);
         mParent.getLocationOnScreen(mParentLocation);
-        final int centerX = (int) (mHorizSpaceForIcon / 2);
+        final int centerX = mHorizSpaceForIcon / 2;
         final int centerY = v.getHeight() / 2;
         final int x = mIconLocation[0] - mParentLocation[0] + centerX;
         final int y = mIconLocation[1] - mParentLocation[1] + centerY;
         final int index = mMenuContainer.indexOfChild(v);
         if (mMenuListener != null) {
-            mMenuListener.onMenuClicked(mParent, x, y, mMenuItems.get(index));
+            mMenuListener.onMenuClicked(mParent, x, y,
+                    (mOnLeft ? mLeftMenuItems : mRightMenuItems).get(index));
         }
     }
 
@@ -469,6 +488,11 @@
             // Do nothing
             return;
         }
+        boolean wasOnLeft = mOnLeft;
+        mOnLeft = showOnLeft;
+        if (wasOnLeft != showOnLeft) {
+            populateMenuViews();
+        }
         final int count = mMenuContainer.getChildCount();
         for (int i = 0; i < count; i++) {
             final View v = mMenuContainer.getChildAt(i);
@@ -476,7 +500,6 @@
             final float right = mParent.getWidth() - (mHorizSpaceForIcon * (i + 1));
             v.setX(showOnLeft ? left : right);
         }
-        mOnLeft = showOnLeft;
         mIconsPlaced = true;
     }
 
@@ -603,6 +626,7 @@
     private void addMenuView(MenuItem item, ViewGroup parent) {
         View menuView = item.getMenuView();
         if (menuView != null) {
+            menuView.setAlpha(mAlpha);
             parent.addView(menuView);
             menuView.setOnClickListener(this);
             FrameLayout.LayoutParams lp = (LayoutParams) menuView.getLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 30d1701..c3bf16e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
         .ExpandAnimationParameters;
+import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -27,6 +28,7 @@
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.NotificationManager;
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.content.Intent;
@@ -111,7 +113,6 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationSnooze;
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
-import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
@@ -127,6 +128,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
+import com.android.systemui.tuner.TunerService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -159,6 +161,7 @@
     private int mCurrentStackHeight = Integer.MAX_VALUE;
     private final Paint mBackgroundPaint = new Paint();
     private final boolean mShouldDrawNotificationBackground;
+    private boolean mLowPriorityBeforeSpeedBump;
 
     private float mExpandedHeight;
     private int mOwnScrollY;
@@ -515,6 +518,13 @@
             mDebugPaint.setStyle(Paint.Style.STROKE);
         }
         mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
+
+        TunerService tunerService = Dependency.get(TunerService.class);
+        tunerService.addTunable((key, newValue) -> {
+            if (key.equals(LOW_PRIORITY)) {
+                mLowPriorityBeforeSpeedBump = "1".equals(newValue);
+            }
+        }, LOW_PRIORITY);
     }
 
     @Override
@@ -662,37 +672,29 @@
 
     @ShadeViewRefactor(RefactorComponent.DECORATOR)
     private void drawBackground(Canvas canvas) {
-        final int lockScreenLeft = mSidePaddings;
-        final int lockScreenRight = getWidth() - mSidePaddings;
-        final int lockScreenTop = mCurrentBounds.top;
-        final int lockScreenBottom = mCurrentBounds.bottom;
-        final int darkLeft = getWidth() / 2;
-        final int darkTop = mRegularTopPadding;
+        int lockScreenLeft = mSidePaddings;
+        int lockScreenRight = getWidth() - mSidePaddings;
+        int lockScreenTop = mCurrentBounds.top;
+        int lockScreenBottom = mCurrentBounds.bottom;
+        int darkLeft = getWidth() / 2;
+        int darkTop = mRegularTopPadding;
 
-        if (mAmbientState.hasPulsingNotifications()) {
-            // No divider, we have a notification icon instead
-        } else if (mAmbientState.isFullyDark()) {
-            // Only draw divider on AOD if we actually have notifications
-            if (mFirstVisibleBackgroundChild != null) {
-                canvas.drawRect(darkLeft, darkTop, darkLeft, darkTop, mBackgroundPaint);
-            }
-        } else {
-            float yProgress = 1 - mInterpolatedDarkAmount;
-            float xProgress = mDarkXInterpolator.getInterpolation(
-                    (1 - mLinearDarkAmount) * mBackgroundXFactor);
+        float yProgress = 1 - mInterpolatedDarkAmount;
+        float xProgress = mDarkXInterpolator.getInterpolation(
+                (1 - mLinearDarkAmount) * mBackgroundXFactor);
 
-            mBackgroundAnimationRect.set(
-                    (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
-                    (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
-                    (int) MathUtils.lerp(darkLeft, lockScreenRight, xProgress),
-                    (int) MathUtils.lerp(darkTop, lockScreenBottom, yProgress));
+        mBackgroundAnimationRect.set(
+                (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
+                (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
+                (int) MathUtils.lerp(darkLeft, lockScreenRight, xProgress),
+                (int) MathUtils.lerp(darkTop, lockScreenBottom, yProgress));
 
-            if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
-                canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
-                        mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
-                        mCornerRadius, mCornerRadius, mBackgroundPaint);
-            }
+        if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
+            canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
+                    mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
+                    mCornerRadius, mCornerRadius, mBackgroundPaint);
         }
+
         updateClipping();
     }
 
@@ -1099,14 +1101,15 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void updateClipping() {
-        boolean animatingClipping = mInterpolatedDarkAmount > 0 && mInterpolatedDarkAmount < 1;
         boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
                 && !mHeadsUpAnimatingAway;
         if (mIsClipped != clipped) {
             mIsClipped = clipped;
         }
 
-        if (animatingClipping) {
+        if (mPulsing) {
+            setClipBounds(null);
+        } else if (mAmbientState.isDarkAtAll()) {
             setClipBounds(mBackgroundAnimationRect);
         } else if (clipped) {
             setClipBounds(mRequestedClipBounds);
@@ -4246,13 +4249,9 @@
         boolean nowDarkAtAll = mAmbientState.isDarkAtAll();
         if (nowFullyDark != wasFullyDark) {
             updateContentHeight();
-            DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
-            if (nowFullyDark && dozeParameters.shouldControlScreenOff()) {
-                mShelf.fadeInTranslating();
-            }
-            if (mIconAreaController != null) {
-                mIconAreaController.setFullyDark(nowFullyDark);
-            }
+        }
+        if (mIconAreaController != null) {
+            mIconAreaController.setDarkAmount(interpolatedDarkAmount);
         }
         if (!wasDarkAtAll && nowDarkAtAll) {
             resetExposedMenuView(true /* animate */, true /* animate */);
@@ -5087,8 +5086,16 @@
             }
             ExpandableNotificationRow row = (ExpandableNotificationRow) view;
             currentIndex++;
-            if (!mEntryManager.getNotificationData().isAmbient(
-                    row.getStatusBarNotification().getKey())) {
+            boolean beforeSpeedBump;
+            if (mLowPriorityBeforeSpeedBump) {
+                beforeSpeedBump = !mEntryManager.getNotificationData().isAmbient(
+                        row.getStatusBarNotification().getKey());
+            } else {
+                beforeSpeedBump = mEntryManager.getNotificationData().getImportance(
+                        row.getStatusBarNotification().getKey())
+                        >= NotificationManager.IMPORTANCE_DEFAULT;
+            }
+            if (beforeSpeedBump) {
                 speedBumpIndex = currentIndex;
             }
         }
@@ -5636,6 +5643,11 @@
         public boolean canChildBeDismissed(View v) {
             return NotificationStackScrollLayout.this.canChildBeDismissed(v);
         }
+
+        @Override
+        public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
+            return (isLayoutRtl() ? !isRightOrDown : isRightOrDown) && canChildBeDismissed(v);
+        }
     };
 
     // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index fa63831..d4de8fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -20,7 +20,6 @@
 
 import android.annotation.Nullable;
 import android.app.Fragment;
-import android.app.StatusBarManager;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -35,8 +34,8 @@
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.EncryptionHelper;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.NetworkController;
@@ -55,6 +54,7 @@
     public static final int FADE_IN_DURATION = 320;
     public static final int FADE_IN_DELAY = 50;
     private PhoneStatusBarView mStatusBar;
+    private StatusBarStateController mStatusBarStateController;
     private KeyguardMonitor mKeyguardMonitor;
     private NetworkController mNetworkController;
     private LinearLayout mSystemIconArea;
@@ -78,6 +78,7 @@
         super.onCreate(savedInstanceState);
         mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
         mNetworkController = Dependency.get(NetworkController.class);
+        mStatusBarStateController = Dependency.get(StatusBarStateController.class);
         mStatusBarComponent = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
         mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class);
     }
@@ -207,6 +208,12 @@
                 state |= DISABLE_SYSTEM_INFO;
             }
         }
+
+        if (mStatusBarStateController.isDozing()) {
+            state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
+            state &= ~DISABLE_NOTIFICATION_ICONS;
+        }
+
         return state;
     }
 
@@ -241,7 +248,8 @@
      * don't set the clock GONE otherwise it'll mess up the animation.
      */
     private int clockHiddenMode() {
-        if (!mStatusBar.isClosed() && !mKeyguardMonitor.isShowing()) {
+        if (!mStatusBar.isClosed() && !mKeyguardMonitor.isShowing()
+                && !mStatusBarStateController.isDozing()) {
             return View.INVISIBLE;
         }
         return View.GONE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 25db4f2..94b2cde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -17,23 +17,25 @@
 package com.android.systemui.statusbar.phone;
 
 import android.annotation.NonNull;
-import android.content.Context;
 import android.os.Handler;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 
 /**
  * Controller which handles all the doze animations of the scrims.
  */
-public class DozeScrimController {
+public class DozeScrimController implements StateListener {
     private static final String TAG = "DozeScrimController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final DozeParameters mDozeParameters;
     private final Handler mHandler = new Handler();
-    private final ScrimController mScrimController;
 
     private boolean mDozing;
     private DozeHost.PulseCallback mPulseCallback;
@@ -83,12 +85,13 @@
         }
     };
 
-    public DozeScrimController(ScrimController scrimController, Context context,
-            DozeParameters dozeParameters) {
-        mScrimController = scrimController;
+    public DozeScrimController(DozeParameters dozeParameters) {
         mDozeParameters = dozeParameters;
+        //Never expected to be destroyed
+        Dependency.get(StatusBarStateController.class).addListener(this);
     }
 
+    @VisibleForTesting
     public void setDozing(boolean dozing) {
         if (mDozing == dozing) return;
         mDozing = dozing;
@@ -117,8 +120,6 @@
         // be invoked when we're done so that the caller can drop the pulse wakelock.
         mPulseCallback = callback;
         mPulseReason = reason;
-
-        mScrimController.transitionTo(ScrimState.PULSING, mScrimCallback);
     }
 
     public void pulseOutNow() {
@@ -180,13 +181,21 @@
             mHandler.removeCallbacks(mPulseOutExtended);
             if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
             if (!mDozing) return;
-            mScrimController.transitionTo(ScrimState.AOD,
-                    new ScrimController.Callback() {
-                        @Override
-                        public void onDisplayBlanked() {
-                            pulseFinished();
-                        }
-                    });
+            pulseFinished();
         }
     };
+
+    public ScrimController.Callback getScrimCallback() {
+        return mScrimCallback;
+    }
+
+    @Override
+    public void onStateChanged(int newState) {
+        // don't care
+    }
+
+    @Override
+    public void onDozingChanged(boolean isDozing) {
+        setDozing(isDozing);
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 4a7bc3a..d8280ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -187,6 +187,7 @@
         if (mPulsing) {
             clockYDark -= mPulsingPadding;
         }
+        clockYDark = MathUtils.max(0, clockYDark);
 
         float clockYRegular = getExpandedClockPosition();
         float clockYBouncer = -mKeyguardStatusHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
index 462201c..b3d0bf8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
@@ -35,7 +35,7 @@
     }
 
     /**
-     * Executes an action that requres the screen to be unlocked.
+     * Executes an action that requires the screen to be unlocked.
      *
      * <p>Must be called after {@link #setDismissHandler}.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index e85ff8e..96b7536 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -347,6 +347,7 @@
         mIconManager = new TintedIconManager(findViewById(R.id.statusIcons));
         Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
         onThemeChanged();
+        updateDozeState();
     }
 
     @Override
@@ -514,8 +515,7 @@
     private void updateDozeState() {
         float alpha = 1f - mDarkAmount;
         int visibility = alpha != 0f ? VISIBLE : INVISIBLE;
-        mCarrierLabel.setAlpha(alpha);
-        mCarrierLabel.setVisibility(visibility);
+        mCarrierLabel.setAlpha(alpha * alpha);
         mStatusIconContainer.setAlpha(alpha);
         mStatusIconContainer.setVisibility(visibility);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 59c15f1..4406b14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -19,7 +19,7 @@
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
 
-import static com.android.systemui.OverviewProxyService.OverviewProxyListener;
+import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
 import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
@@ -55,7 +55,8 @@
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.IRotationWatcher.Stub;
+import android.view.Display;
+import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -74,12 +75,12 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.LatencyTracker;
 import com.android.systemui.Dependency;
-import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
+import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.stackdivider.Divider;
@@ -419,8 +420,12 @@
         }
         checkNavBarModes();
         mStatusBar.touchAutoHide();
-        mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
-                true /* nbModeChanged */, mNavigationBarMode);
+
+        // TODO(115978725): Support light bar controller on external nav bars.
+        if (mLightBarController != null) {
+            mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
+                    true /* nbModeChanged */, mNavigationBarMode);
+        }
     }
 
     @Override
@@ -452,8 +457,11 @@
             }
         }
 
-        mLightBarController.onNavigationVisibilityChanged(vis, mask, nbModeChanged,
-                mNavigationBarMode);
+        // TODO(115978725): Support light bar controller on external nav bars.
+        if (mLightBarController != null) {
+            mLightBarController.onNavigationVisibilityChanged(vis, mask, nbModeChanged,
+                    mNavigationBarMode);
+        }
     }
 
     @Override
@@ -840,9 +848,17 @@
     };
 
     public static View create(Context context, FragmentListener listener) {
+        final int displayId = context.getDisplay().getDisplayId();
+        final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+        final int height = isDefaultDisplay
+                ? LayoutParams.MATCH_PARENT
+                : context.getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+                LayoutParams.MATCH_PARENT, height,
+                // TODO(b/117478341): Resolve one status bar/ navigation bar assumption
+                isDefaultDisplay
+                        ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
+                        : WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
@@ -851,9 +867,13 @@
                         | WindowManager.LayoutParams.FLAG_SLIPPERY,
                 PixelFormat.TRANSLUCENT);
         lp.token = new Binder();
-        lp.setTitle("NavigationBar");
+        lp.setTitle("NavigationBar" + displayId);
         lp.accessibilityTitle = context.getString(R.string.nav_bar);
         lp.windowAnimations = 0;
+        if (!isDefaultDisplay) {
+            lp.flags |= LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+            lp.gravity = Gravity.BOTTOM;
+        }
 
         View navigationBarView = LayoutInflater.from(context).inflate(
                 R.layout.navigation_bar_window, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 52134d9..22b6ba6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -34,7 +34,7 @@
 import android.widget.Space;
 
 import com.android.systemui.Dependency;
-import com.android.systemui.OverviewProxyService;
+import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.plugins.PluginManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 2ab5958..6728f08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -57,7 +57,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.DockedStackExistsListener;
 import com.android.systemui.Interpolators;
-import com.android.systemui.OverviewProxyService;
+import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.PluginListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index c08366a..6b12dd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -16,22 +16,24 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.annotation.NonNull;
 import android.app.Notification;
 import android.os.SystemClock;
 import android.service.notification.StatusBarNotification;
-import androidx.annotation.Nullable;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListener;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
@@ -48,7 +50,7 @@
  * A class to handle notifications and their corresponding groups.
  */
 public class NotificationGroupManager implements OnHeadsUpChangedListener,
-        OnAmbientChangedListener {
+        OnAmbientChangedListener, StateListener {
 
     private static final String TAG = "NotificationGroupManager";
     private static final long ALERT_TRANSFER_TIMEOUT = 300;
@@ -62,10 +64,8 @@
     private boolean mIsUpdatingUnchangedGroup;
     private HashMap<String, NotificationData.Entry> mPendingNotifications;
 
-    private final StateListener mStateListener = this::setStatusBarState;
-
     public NotificationGroupManager() {
-        Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).addListener(this);
     }
 
     public void setOnGroupChangeListener(OnGroupChangeListener listener) {
@@ -185,6 +185,7 @@
      * specific alert state logic based off when the state changes.
      * @param isDozing if the device is dozing.
      */
+    @VisibleForTesting
     public void setDozing(boolean isDozing) {
         if (mIsDozing != isDozing) {
             for (NotificationGroup group : mGroupMap.values()) {
@@ -732,6 +733,16 @@
         mPendingNotifications = pendingNotifications;
     }
 
+    @Override
+    public void onStateChanged(int newState) {
+        setStatusBarState(newState);
+    }
+
+    @Override
+    public void onDozingChanged(boolean isDozing) {
+        setDozing(isDozing);
+    }
+
     public static class NotificationGroup {
         public final HashMap<String, NotificationData.Entry> children = new HashMap<>();
         public NotificationData.Entry summary;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 21b98db..8e90f98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -1,29 +1,34 @@
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
+
+import android.app.NotificationManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
-import androidx.annotation.NonNull;
-import androidx.collection.ArrayMap;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+import androidx.collection.ArrayMap;
+
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.widget.ViewClippingUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.tuner.TunerService;
 
 import java.util.ArrayList;
 import java.util.function.Function;
@@ -33,9 +38,24 @@
  * normally reserved for notifications.
  */
 public class NotificationIconAreaController implements DarkReceiver {
+
+    public static final String LOW_PRIORITY = "low_priority";
+
     private final ContrastColorUtil mContrastColorUtil;
     private final NotificationEntryManager mEntryManager;
     private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;
+    private final TunerService.Tunable mTunable = new TunerService.Tunable() {
+        @Override
+        public void onTuningChanged(String key, String newValue) {
+            if (key.equals(LOW_PRIORITY)) {
+                mShowLowPriority = "1".equals(newValue)
+                        || !NotificationUtils.useNewInterruptionModel(mContext);
+                if (mNotificationScrollLayout != null) {
+                    updateStatusBarIcons();
+                }
+            }
+        }
+    };
 
     private int mIconSize;
     private int mIconHPadding;
@@ -49,6 +69,23 @@
     private ViewGroup mNotificationScrollLayout;
     private Context mContext;
     private boolean mFullyDark;
+    private boolean mShowLowPriority;
+
+    /**
+     * Ratio representing being awake or in ambient mode, where 1 is dark and 0 awake.
+     */
+    private float mDarkAmount;
+    /**
+     * Maximum translation to avoid burn in.
+     */
+    private int mBurnInOffset;
+    /**
+     * Height of the keyguard status bar (not the one after unlocking.)
+     */
+    private int mKeyguardStatusBarHeight;
+
+    private final ViewClippingUtil.ClippingParameters mClippingParameters =
+            view -> view instanceof StatusBarWindowView;
 
     public NotificationIconAreaController(Context context, StatusBar statusBar) {
         mStatusBar = statusBar;
@@ -56,6 +93,8 @@
         mContext = context;
         mEntryManager = Dependency.get(NotificationEntryManager.class);
 
+        Dependency.get(TunerService.class).addTunable(mTunable, LOW_PRIORITY);
+
         initializeNotificationAreaViews(context);
     }
 
@@ -104,6 +143,9 @@
         Resources res = context.getResources();
         mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
         mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
+        mBurnInOffset = res.getDimensionPixelSize(R.dimen.default_burn_in_prevention_offset);
+        mKeyguardStatusBarHeight = res
+                .getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard);
     }
 
     /**
@@ -142,10 +184,16 @@
     }
 
     protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
-            boolean showAmbient, boolean hideDismissed, boolean hideRepliedMessages) {
+            boolean showAmbient, boolean showLowPriority, boolean hideDismissed,
+            boolean hideRepliedMessages) {
         if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
             return false;
         }
+        if (!showLowPriority
+                && mEntryManager.getNotificationData().getImportance(entry.key)
+                < NotificationManager.IMPORTANCE_DEFAULT) {
+            return false;
+        }
         if (!StatusBar.isTopLevelChild(entry)) {
             return false;
         }
@@ -181,13 +229,15 @@
 
     private void updateShelfIcons() {
         updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
-                NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */,
-                mFullyDark /* hideRepliedMessages */);
+                true /* showAmbient */, !mFullyDark /* showLowPriority */,
+                false /* hideDismissed */, mFullyDark /* hideRepliedMessages */);
     }
 
     public void updateStatusBarIcons() {
         updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
-                false /* showAmbient */, true /* hideDismissed */, true /* hideRepliedMessages */);
+                false /* showAmbient */, mShowLowPriority /* showLowPriority */,
+                true /* hideDismissed */,
+                true /* hideRepliedMessages */);
     }
 
     /**
@@ -200,8 +250,8 @@
      * @param hideRepliedMessages should messages that have been replied to be hidden
      */
     private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
-            NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed,
-            boolean hideRepliedMessages) {
+            NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority,
+            boolean hideDismissed, boolean hideRepliedMessages) {
         ArrayList<StatusBarIconView> toShow = new ArrayList<>(
                 mNotificationScrollLayout.getChildCount());
 
@@ -210,7 +260,7 @@
             View view = mNotificationScrollLayout.getChildAt(i);
             if (view instanceof ExpandableNotificationRow) {
                 NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
-                if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed,
+                if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed,
                         hideRepliedMessages)) {
                     toShow.add(function.apply(ent));
                 }
@@ -325,9 +375,22 @@
         v.setDecorColor(mIconTint);
     }
 
-    public void setFullyDark(boolean fullyDark) {
-        mFullyDark = fullyDark;
-        updateShelfIcons();
+    /**
+     * Dark amount, from 0 to 1, representing being awake or in AOD.
+     */
+    public void setDarkAmount(float darkAmount) {
+        mDarkAmount = darkAmount;
+        if (darkAmount == 0 || darkAmount == 1) {
+            ViewClippingUtil.setClippingDeactivated(mNotificationIcons, darkAmount != 0,
+                    mClippingParameters);
+        }
+        dozeTimeTick();
+
+        boolean fullyDark = darkAmount == 1f;
+        if (mFullyDark != fullyDark) {
+            mFullyDark = fullyDark;
+            updateShelfIcons();
+        }
     }
 
     public void setDark(boolean dark) {
@@ -342,4 +405,15 @@
     public void setIsolatedIconLocation(Rect iconDrawingRect, boolean requireStateUpdate) {
         mNotificationIcons.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate);
     }
+
+    /**
+     * Moves icons whenever the device wakes up in AOD, to avoid burn in.
+     */
+    public void dozeTimeTick() {
+        int yOffset = (mKeyguardStatusBarHeight - getHeight()) / 2;
+        int translationX = getBurnInOffset(mBurnInOffset, true /* xAxis */);
+        int translationY = getBurnInOffset(mBurnInOffset, false /* xAxis */) + yOffset;
+        mNotificationIcons.setTranslationX(translationX * mDarkAmount);
+        mNotificationIcons.setTranslationY(translationY * mDarkAmount);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 0a724bd..964b2210 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DURATION;
 import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DELAY;
+import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DURATION;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -26,10 +26,11 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.drawable.Icon;
-import androidx.collection.ArrayMap;
 import android.util.AttributeSet;
 import android.view.View;
 
+import androidx.collection.ArrayMap;
+
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -127,7 +128,6 @@
         }
     }.setDuration(CONTENT_FADE_DURATION);
 
-    public static final int MAX_VISIBLE_ICONS_WHEN_DARK = 5;
     public static final int MAX_STATIC_ICONS = 4;
     private static final int MAX_DOTS = 1;
 
@@ -371,8 +371,7 @@
         float translationX = getActualPaddingStart();
         int firstOverflowIndex = -1;
         int childCount = getChildCount();
-        int maxVisibleIcons = mDark ? MAX_VISIBLE_ICONS_WHEN_DARK :
-                    mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
+        int maxVisibleIcons = mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
         float layoutEnd = getLayoutEnd();
         float overflowStart = getMaxOverflowStart();
         mVisualOverflowStart = 0;
@@ -388,9 +387,6 @@
             boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
                     && iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
             boolean noOverflowAfter = i == childCount - 1;
-            float drawingScale = mDark && view instanceof StatusBarIconView
-                    ? ((StatusBarIconView) view).getIconScaleFullyDark()
-                    : 1f;
             if (mOpenedAmount != 0.0f) {
                 noOverflowAfter = noOverflowAfter && !hasAmbient && !forceOverflow;
             }
@@ -406,7 +402,7 @@
                     mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
                 }
             }
-            translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
+            translationX += iconState.iconAppearAmount * view.getWidth();
         }
         mNumDots = 0;
         if (firstOverflowIndex != -1) {
@@ -435,26 +431,6 @@
             mLastVisibleIconState = mIconStates.get(lastChild);
             mFirstVisibleIconState = mIconStates.get(getChildAt(0));
         }
-        boolean center = mDark;
-        if (center && translationX < getLayoutEnd()) {
-            float initialTranslation =
-                    mFirstVisibleIconState == null ? 0 : mFirstVisibleIconState.xTranslation;
-            float contentWidth = getFinalTranslationX() - initialTranslation;
-            float availableSpace = getLayoutEnd() - getActualPaddingStart();
-            float delta = (availableSpace - contentWidth) / 2;
-
-            if (firstOverflowIndex != -1) {
-                // If we have an overflow, only count those half for centering because the dots
-                // don't have a lot of visual weight.
-                float deltaIgnoringOverflow = (getLayoutEnd() - mVisualOverflowStart) / 2;
-                delta = (deltaIgnoringOverflow + delta) / 2;
-            }
-            for (int i = 0; i < childCount; i++) {
-                View view = getChildAt(i);
-                IconState iconState = mIconStates.get(view);
-                iconState.xTranslation += delta;
-            }
-        }
 
         if (isLayoutRtl()) {
             for (int i = 0; i < childCount; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 31facb7..f4c2e27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -582,7 +582,7 @@
         int stackScrollerPadding;
         if (mBarState != StatusBarState.KEYGUARD) {
             stackScrollerPadding = (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
-            +  mQsNotificationTopPadding;
+                    + mQsNotificationTopPadding;
         } else {
             int totalHeight = getHeight();
             int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
@@ -754,7 +754,7 @@
             mQsExpandImmediate = true;
             mNotificationStackScroller.setShouldShowShelfOnly(true);
         }
-        if (isFullyCollapsed()){
+        if (isFullyCollapsed()) {
             expand(true /* animate */);
         } else {
             flingSettings(0 /* velocity */, FLING_EXPAND);
@@ -921,7 +921,7 @@
     }
 
     private boolean flingExpandsQs(float vel) {
-        if (isFalseTouch()) {
+        if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) {
             return false;
         }
         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -1046,11 +1046,11 @@
 
         final boolean stylusButtonClickDrag = action == MotionEvent.ACTION_DOWN
                 && (event.isButtonPressed(MotionEvent.BUTTON_STYLUS_PRIMARY)
-                        || event.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY));
+                || event.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY));
 
         final boolean mouseButtonClickDrag = action == MotionEvent.ACTION_DOWN
                 && (event.isButtonPressed(MotionEvent.BUTTON_SECONDARY)
-                        || event.isButtonPressed(MotionEvent.BUTTON_TERTIARY));
+                || event.isButtonPressed(MotionEvent.BUTTON_TERTIARY));
 
         return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag;
     }
@@ -1321,12 +1321,12 @@
 
     private final ValueAnimator.AnimatorUpdateListener mStatusBarAnimateAlphaListener =
             new ValueAnimator.AnimatorUpdateListener() {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
-            updateHeaderKeyguardAlpha();
-        }
-    };
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
+                    updateHeaderKeyguardAlpha();
+                }
+            };
 
     private void animateKeyguardStatusBarIn(long duration) {
         mKeyguardStatusBar.setVisibility(View.VISIBLE);
@@ -1382,7 +1382,7 @@
             if (keyguardFadingAway) {
                 mKeyguardStatusView.animate()
                         .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay())
-                        .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration()/2)
+                        .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2)
                         .start();
             }
         } else if (mBarState == StatusBarState.SHADE_LOCKED
@@ -1425,8 +1425,8 @@
         updateEmptyShadeView();
         mQsNavbarScrim.setVisibility(mBarState == StatusBarState.SHADE && mQsExpanded
                 && !mStackScrollerOverscrolling && mQsScrimEnabled
-                        ? View.VISIBLE
-                        : View.INVISIBLE);
+                ? View.VISIBLE
+                : View.INVISIBLE);
         if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
             mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
         }
@@ -1459,7 +1459,8 @@
             setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
         }
 
-        if (mQsFullyExpanded && mFalsingManager.shouldEnforceBouncer()) {
+        if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded
+                && mFalsingManager.shouldEnforceBouncer()) {
             mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
                     false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
         }
@@ -2130,8 +2131,7 @@
                     }
                 }, null, true /* dismissShade */, false /* afterKeyguardGone */,
                         true /* deferred */);
-            }
-            else {
+            } else {
                 mKeyguardBottomArea.launchLeftAffordance();
             }
         } else {
@@ -2588,7 +2588,7 @@
         x = Math.min(rightMost, Math.max(leftMost, x));
         setVerticalPanelTranslation(x -
                 (mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2));
-     }
+    }
 
     private void resetVerticalPanelPosition() {
         setVerticalPanelTranslation(0f);
@@ -2716,8 +2716,8 @@
         String packageToLaunch = (resolveInfo == null || resolveInfo.activityInfo == null)
                 ? null : resolveInfo.activityInfo.packageName;
         return packageToLaunch != null &&
-               (keyguardIsShowing || !isForegroundApp(packageToLaunch)) &&
-               !mAffordanceHelper.isSwipingInProgress();
+                (keyguardIsShowing || !isForegroundApp(packageToLaunch))
+                && !mAffordanceHelper.isSwipingInProgress();
     }
 
     /**
@@ -2884,13 +2884,14 @@
     }
 
     public void setStatusAccessibilityImportance(int mode) {
-         mKeyguardStatusView.setImportantForAccessibility(mode);
+        mKeyguardStatusView.setImportantForAccessibility(mode);
     }
 
     /**
      * TODO: this should be removed.
      * It's not correct to pass this view forward because other classes will end up adding
      * children to it. Theme will be out of sync.
+     *
      * @return bottom area view
      */
     public KeyguardBottomAreaView getKeyguardBottomAreaView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index f29b7ca..021b430 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -670,6 +670,10 @@
      * @return whether a fling should expands the panel; contracts otherwise
      */
     protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
+        if (mFalsingManager.isUnlockingDisabled()) {
+            return true;
+        }
+
         if (isFalseTouch(x, y)) {
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 553165b..0e6efc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -57,6 +57,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
+
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.TelephonyIntents;
@@ -391,7 +392,7 @@
             zenDescription = mContext.getString(R.string.interruption_level_priority);
         }
 
-        if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConfig())) {
+        if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConsolidatedPolicy())) {
             if (audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
                 volumeVisible = true;
                 volumeIconId = R.drawable.stat_sys_ringer_vibrate;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index fd5403f..3980126 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -20,8 +20,8 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
 import static com.android.systemui.Interpolators.ALPHA_IN;
 import static com.android.systemui.Interpolators.ALPHA_OUT;
-import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
-import static com.android.systemui.OverviewProxyService.TAG_OPS;
+import static com.android.systemui.recents.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
+import static com.android.systemui.recents.OverviewProxyService.TAG_OPS;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
 
@@ -56,7 +56,7 @@
 import android.view.WindowManagerGlobal;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
-import com.android.systemui.OverviewProxyService;
+import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
index 855592f..c6e98e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
@@ -44,8 +44,8 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.R;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 import com.android.systemui.statusbar.policy.RotationLockController;
 
@@ -400,7 +400,7 @@
         }
     }
 
-    private class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
+    private class TaskStackListenerImpl extends TaskStackChangeListener {
         // Invalidate any rotation suggestion on task change or activity orientation change
         // Note: all callbacks happen on main thread
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index b9ca949..966a346 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -22,6 +22,7 @@
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
@@ -29,7 +30,6 @@
 import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_INVALID;
 import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_LEFT;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
-import static com.android.systemui.statusbar.NotificationMediaManager.DEBUG_MEDIA;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -46,6 +46,7 @@
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
+import android.app.AppOpsManager;
 import android.app.IWallpaperManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
@@ -70,6 +71,8 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
 import android.media.AudioAttributes;
 import android.metrics.LogMaker;
 import android.net.Uri;
@@ -96,6 +99,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.Display;
 import android.view.IWindowManager;
 import android.view.KeyEvent;
@@ -130,14 +134,15 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.EventLogTags;
+import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.InitController;
 import com.android.systemui.Interpolators;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.RecentsComponent;
 import com.android.systemui.SystemUI;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.UiOffloadThread;
+import com.android.systemui.appops.AppOpsController;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.charging.WirelessChargingAnimation;
 import com.android.systemui.classifier.FalsingLog;
@@ -183,7 +188,6 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.AppOpsListener;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -228,7 +232,8 @@
         OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
         ColorExtractor.OnColorsChangedListener, ConfigurationListener,
         StatusBarStateController.StateListener, ShadeController,
-        ActivityLaunchAnimator.Callback, AmbientPulseManager.OnAmbientChangedListener {
+        ActivityLaunchAnimator.Callback, AmbientPulseManager.OnAmbientChangedListener,
+        AppOpsController.Callback {
     public static final boolean MULTIUSER_DEBUG = false;
 
     public static final boolean ENABLE_CHILD_NOTIFICATIONS
@@ -372,7 +377,8 @@
     protected NotificationLogger mNotificationLogger;
     protected NotificationEntryManager mEntryManager;
     protected NotificationViewHierarchyManager mViewHierarchyManager;
-    protected AppOpsListener mAppOpsListener;
+    protected ForegroundServiceController mForegroundServiceController;
+    protected AppOpsController mAppOpsController;
     protected KeyguardViewMediator mKeyguardViewMediator;
     private ZenModeController mZenController;
 
@@ -556,21 +562,63 @@
                 }
             };
 
+    protected DisplayManager mDisplayManager;
+
     private NavigationBarFragment mNavigationBar;
     private View mNavigationBarView;
+
+    /** A displayId - nav bar mapping */
+    private SparseArray<NavigationBarFragment> mExternalNavigationBarMap = new SparseArray<>();
+
+    // TODO(b/115978725): Move it to DisplayNavigationBarController
+    private final DisplayListener mDisplayListener = new DisplayListener() {
+        @Override
+        public void onDisplayAdded(int displayId) {
+            final Display display = mDisplayManager.getDisplay(displayId);
+            addExternalNavigationBar(display);
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            final NavigationBarFragment navBar = mExternalNavigationBarMap.get(displayId);
+            if (navBar != null) {
+                final View navigationView = navBar.getView().getRootView();
+                WindowManagerGlobal.getInstance().removeView(navigationView, true);
+                mExternalNavigationBarMap.remove(displayId);
+            }
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+        }
+    };
+
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
     private boolean mVibrateOnOpening;
     private VibratorHelper mVibratorHelper;
     protected NotificationPresenter mPresenter;
 
     @Override
+    public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
+        mForegroundServiceController.onAppOpChanged(code, uid, packageName, active);
+        Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
+            mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active);
+        });
+    }
+
+    protected static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA,
+            AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+            AppOpsManager.OP_RECORD_AUDIO,
+            AppOpsManager.OP_COARSE_LOCATION,
+            AppOpsManager.OP_FINE_LOCATION};
+
+    @Override
     public void start() {
         mGroupManager = Dependency.get(NotificationGroupManager.class);
         mVisualStabilityManager = Dependency.get(VisualStabilityManager.class);
         mNotificationLogger = Dependency.get(NotificationLogger.class);
         mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
         mNotificationListener =  Dependency.get(NotificationListener.class);
-        mGroupManager = Dependency.get(NotificationGroupManager.class);
         mNetworkController = Dependency.get(NetworkController.class);
         mUserSwitcherController = Dependency.get(UserSwitcherController.class);
         mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
@@ -585,7 +633,8 @@
         mMediaManager = Dependency.get(NotificationMediaManager.class);
         mEntryManager = Dependency.get(NotificationEntryManager.class);
         mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
-        mAppOpsListener = Dependency.get(AppOpsListener.class);
+        mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
+        mAppOpsController = Dependency.get(AppOpsController.class);
         mZenController = Dependency.get(ZenModeController.class);
         mKeyguardViewMediator = getComponent(KeyguardViewMediator.class);
         mColorExtractor = Dependency.get(SysuiColorExtractor.class);
@@ -601,6 +650,9 @@
         mDisplay = mWindowManager.getDefaultDisplay();
         updateDisplaySize();
 
+        // get display service to detect display status
+        mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+
         Resources res = mContext.getResources();
         mVibrateOnOpening = mContext.getResources().getBoolean(
                 R.bool.config_vibrateOnIconAnimation);
@@ -643,6 +695,7 @@
             // If the system process isn't there we're doomed anyway.
         }
 
+        mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
         createAndAddWindows();
 
         // Make sure we always have the most current wallpaper info.
@@ -847,6 +900,7 @@
             }
         });
 
+        // TODO(115978725): Support light bar controller on external nav bars.
         mLightBarController = Dependency.get(LightBarController.class);
         if (mNavigationBar != null) {
             mNavigationBar.setLightBarController(mLightBarController);
@@ -865,8 +919,7 @@
                 mContext.getSystemService(AlarmManager.class));
         mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf,
                 mHeadsUpManager, mNotificationIconAreaController, mScrimController);
-        mDozeScrimController = new DozeScrimController(mScrimController, context,
-                DozeParameters.getInstance(context));
+        mDozeScrimController = new DozeScrimController(DozeParameters.getInstance(context));
 
         mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);
         mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front);
@@ -984,7 +1037,7 @@
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
                 mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
                 mScrimController, this);
-        mAppOpsListener.setUpWithPresenter(mPresenter);
+        mAppOpsController.addCallback(APP_OPS, this);
         mNotificationListener.setUpWithPresenter(mPresenter);
         mNotificationShelf.setOnActivatedListener(mPresenter);
         mRemoteInputManager.getController().addCallback(mStatusBarWindowController);
@@ -1031,6 +1084,33 @@
             }
             mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
         });
+
+        // Add external navigation bars if more than one displays exist.
+        final Display[] displays = mDisplayManager.getDisplays();
+        for (Display display : displays) {
+            addExternalNavigationBar(display);
+        }
+    }
+
+    /**
+     * Add a phone navigation bar on an external display if the display supports system decorations.
+     *
+     * @param display the display to add navigation bar on
+     */
+    protected void addExternalNavigationBar(Display display) {
+        if (display == null || display.getDisplayId() == DEFAULT_DISPLAY
+                || !display.supportsSystemDecorations()) {
+            return;
+        }
+
+        final int displayId = display.getDisplayId();
+        final Context externalDisplayContext = mContext.createDisplayContext(display);
+        NavigationBarFragment.create(externalDisplayContext,
+                (tag, fragment) -> {
+                    final NavigationBarFragment navBar = (NavigationBarFragment) fragment;
+                    navBar.setCurrentSysuiVisibility(mSystemUiVisibility);
+                    mExternalNavigationBarMap.append(displayId, navBar);
+                });
     }
 
     /**
@@ -1080,6 +1160,9 @@
     @Override
     public void onThemeChanged() {
         // Recreate Indication controller because internal references changed
+        if (mKeyguardIndicationController != null) {
+            mKeyguardIndicationController.destroy();
+        }
         mKeyguardIndicationController =
                 SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
                         mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
@@ -1088,7 +1171,6 @@
         mKeyguardIndicationController
                 .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mKeyguardIndicationController.setVisible(mState == StatusBarState.KEYGUARD);
-        mKeyguardIndicationController.setDozing(mDozing);
         if (mStatusBarKeyguardViewManager != null) {
             mStatusBarKeyguardViewManager.onThemeChanged();
         }
@@ -1502,7 +1584,7 @@
     }
 
     public boolean isPulsing() {
-        return mDozeScrimController != null && mDozeScrimController.isPulsing();
+        return mAmbientPulseManager.hasNotifications();
     }
 
     public boolean isLaunchTransitionFadingAway() {
@@ -2049,6 +2131,7 @@
         }
     }
 
+    // TODO(115978725): Support auto hide on external nav bars.
     void touchAutoHide() {
         // update transient bar autohide
         if (mStatusBarMode == MODE_SEMI_TRANSPARENT || (mNavigationBar != null
@@ -2088,6 +2171,7 @@
                 : MODE_OPAQUE;
     }
 
+    // TODO(115978725): Support animations on external nav bars.
     void checkBarModes() {
         if (mDemoMode) return;
         if (mStatusBarView != null) checkBarMode(mStatusBarMode, mStatusBarWindowState,
@@ -2107,6 +2191,7 @@
         transitions.transitionTo(mode, anim);
     }
 
+    // TODO(115978725): Support animations on external nav bars.
     private void finishBarAnimations() {
         if (mStatusBarView != null) {
             mStatusBarView.getBarTransitions().finishAnimations();
@@ -2840,6 +2925,16 @@
             mWindowManager.removeViewImmediate(mNavigationBarView);
             mNavigationBarView = null;
         }
+        mDisplayManager.unregisterDisplayListener(mDisplayListener);
+        if (mExternalNavigationBarMap.size() > 0) {
+            for (int i = 0; i < mExternalNavigationBarMap.size(); i++) {
+                final View navigationWindow = mExternalNavigationBarMap.valueAt(i)
+                        .getView().getRootView();
+                WindowManagerGlobal.getInstance()
+                        .removeView(navigationWindow, true /* immediate */);
+            }
+            mExternalNavigationBarMap.clear();
+        }
         mContext.unregisterReceiver(mBroadcastReceiver);
         mContext.unregisterReceiver(mDemoReceiver);
         mAssistManager.destroy();
@@ -2853,7 +2948,7 @@
         mDeviceProvisionedController.removeCallback(mUserSetupObserver);
         Dependency.get(ConfigurationController.class).removeCallback(this);
         mZenController.removeCallback(this);
-        mAppOpsListener.destroy();
+        mAppOpsController.removeCallback(APP_OPS, this);
     }
 
     private boolean mDemoModeAllowed;
@@ -2908,6 +3003,7 @@
                     "transparent".equals(mode) ? MODE_TRANSPARENT :
                     "warning".equals(mode) ? MODE_WARNING :
                     -1;
+            // TODO(115978725): Support external nav bar transitions
             if (barMode != -1) {
                 boolean animate = true;
                 if (mStatusBarView != null) {
@@ -3141,6 +3237,7 @@
                 mDraggedDownRow = null;
             }
 
+            // TODO(115978725): Support animations on external nav bars.
             // Disable layout transitions in navbar for this transition because the load is just
             // too heavy for the CPU and GPU on any device.
             if (mNavigationBar != null) {
@@ -3231,12 +3328,8 @@
         boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup())
                 || (mDozing && mDozeServiceHost.shouldAnimateScreenOff() && sleepingFromKeyguard);
 
-        mDozeScrimController.setDozing(mDozing);
-        mKeyguardIndicationController.setDozing(mDozing);
         mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation,
                 mDozeServiceHost.wasPassivelyInterrupted());
-        mNotificationLogger.setDozing(mDozing);
-        mGroupManager.setDozing(mDozing);
         updateQsExpansionEnabled();
         Trace.endSection();
     }
@@ -3426,13 +3519,6 @@
         updateQsExpansionEnabled();
         mKeyguardViewMediator.setAodShowing(mDozing);
 
-        //TODO: make these folks listeners of StatusBarStateController.onDozingChanged
-        mStatusBarWindowController.setDozing(mDozing);
-        mStatusBarKeyguardViewManager.setDozing(mDozing);
-        if (mAmbientIndicationContainer instanceof DozeReceiver) {
-            ((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing);
-        }
-
         mEntryManager.updateNotifications();
         updateDozingState();
         updateScrimController();
@@ -3631,7 +3717,6 @@
             mNotificationPanel.setTouchAndAnimationDisabled(false);
             updateVisibleToUser();
             updateIsKeyguard();
-            updateScrimController();
         }
     };
 
@@ -3817,8 +3902,9 @@
         } else if (mBrightnessMirrorVisible) {
             mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
         } else if (isPulsing()) {
-            // Handled in DozeScrimController#setPulsing
-        } else if (mDozing) {
+            mScrimController.transitionTo(ScrimState.PULSING,
+                    mDozeScrimController.getScrimCallback());
+        } else if (mDozing && !wakeAndUnlocking) {
             mScrimController.transitionTo(ScrimState.AOD);
         } else if (mIsKeyguard && !wakeAndUnlocking) {
             mScrimController.transitionTo(mNotificationPanel.isSemiAwake()
@@ -3911,8 +3997,12 @@
                     mNotificationPanel.setPulsing(pulsing);
                     mVisualStabilityManager.setPulsing(pulsing);
                     mIgnoreTouchWhilePulsing = false;
+                    updateScrimController();
                 }
             }, reason);
+            // DozeScrimController is in pulse state, now let's ask ScrimController to start
+            // pulsing and draw the black frame, if necessary.
+            updateScrimController();
         }
 
         @Override
@@ -3944,6 +4034,10 @@
         @Override
         public void dozeTimeTick() {
             mNotificationPanel.dozeTimeTick();
+            mNotificationIconAreaController.dozeTimeTick();
+            if (mAmbientIndicationContainer instanceof DozeReceiver) {
+                ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
+            }
         }
 
         @Override
@@ -4010,7 +4104,8 @@
                 float viewY = screenY - mTmpInt2[1];
                 if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth()
                         && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) {
-                    dispatchDoubleTap(viewX, viewY);
+                    if (mAmbientIndicationContainer instanceof DozeReceiver)
+                    ((DozeReceiver) mAmbientIndicationContainer).onDozeDoubleTap();
                 }
             }
         }
@@ -4025,17 +4120,6 @@
             mScrimController.setAodFrontScrimAlpha(scrimOpacity);
         }
 
-        public void dispatchDoubleTap(float viewX, float viewY) {
-            dispatchTap(mAmbientIndicationContainer, viewX, viewY);
-            dispatchTap(mAmbientIndicationContainer, viewX, viewY);
-        }
-
-        private void dispatchTap(View view, float x, float y) {
-            long now = SystemClock.elapsedRealtime();
-            dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN);
-            dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP);
-        }
-
         private void dispatchTouchEvent(View view, float x, float y, long now, int action) {
             MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */);
             view.dispatchTouchEvent(ev);
@@ -4103,7 +4187,7 @@
 
     protected Display mDisplay;
 
-    protected RecentsComponent mRecents;
+    protected Recents mRecents;
 
     protected NotificationShelf mNotificationShelf;
     protected EmptyShadeView mEmptyShadeView;
@@ -4261,12 +4345,9 @@
         }
     }
 
-    public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
+    public void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone) {
         if (!mDeviceProvisionedController.isDeviceProvisioned()) return;
 
-        final boolean afterKeyguardGone = intent.isActivity()
-                && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
-                mLockscreenUserManager.getCurrentUserId());
         dismissKeyguardThenExecute(() -> {
             new Thread(() -> {
                 try {
@@ -4277,25 +4358,35 @@
                     ActivityManager.getService().resumeAppSwitches();
                 } catch (RemoteException e) {
                 }
-                try {
-                    intent.send(null, 0, null, null, null, null, getActivityOptions(
-                            null /* animationAdapter */));
-                } catch (PendingIntent.CanceledException e) {
-                    // the stack trace isn't very helpful here.
-                    // Just log the exception message.
-                    Log.w(TAG, "Sending intent failed: " + e);
-
-                    // TODO: Dismiss Keyguard.
-                }
-                if (intent.isActivity()) {
-                    mAssistManager.hideAssist();
-                }
+                action.run();
             }).start();
 
             return collapsePanel();
         }, afterKeyguardGone);
     }
 
+    public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
+        final boolean afterKeyguardGone = intent.isActivity()
+                && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
+                mLockscreenUserManager.getCurrentUserId());
+
+        executeActionDismissingKeyguard(() -> {
+            try {
+                intent.send(null, 0, null, null, null, null, getActivityOptions(
+                        null /* animationAdapter */));
+            } catch (PendingIntent.CanceledException e) {
+                // the stack trace isn't very helpful here.
+                // Just log the exception message.
+                Log.w(TAG, "Sending intent failed: " + e);
+
+                // TODO: Dismiss Keyguard.
+            }
+            if (intent.isActivity()) {
+                mAssistManager.hideAssist();
+            }
+        }, afterKeyguardGone);
+    }
+
     public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) {
         ActivityOptions options;
         if (animationAdapter != null) {
@@ -4404,6 +4495,7 @@
     }
     // End Extra BaseStatusBarMethods.
 
+    // TODO(115978725): Handle dimming for external nav bars
     private final Runnable mAutoDim = () -> {
         if (mNavigationBar != null) {
             mNavigationBar.getBarTransitions().setAutoDim(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index c560301..484fe11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -58,7 +59,8 @@
  * which is in turn, reported to this class by the current
  * {@link com.android.keyguard.KeyguardViewBase}.
  */
-public class StatusBarKeyguardViewManager implements RemoteInputController.Callback {
+public class StatusBarKeyguardViewManager implements RemoteInputController.Callback,
+        StatusBarStateController.StateListener {
 
     // When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
     private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3;
@@ -124,7 +126,8 @@
 
     // Dismiss action to be launched when we stop dozing or the keyguard is gone.
     private DismissWithActionRequest mPendingWakeupAction;
-    private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+    private final KeyguardMonitorImpl mKeyguardMonitor =
+            (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
     private final NotificationMediaManager mMediaManager =
             Dependency.get(NotificationMediaManager.class);
 
@@ -148,6 +151,7 @@
         mLockPatternUtils = lockPatternUtils;
         mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
         KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
+        Dependency.get(StatusBarStateController.class).addListener(this);
     }
 
     public void registerStatusBar(StatusBar statusBar,
@@ -202,6 +206,8 @@
     public void show(Bundle options) {
         mShowing = true;
         mStatusBarWindowController.setKeyguardShowing(true);
+        mKeyguardMonitor.notifyKeyguardState(
+                mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded());
         reset(true /* hideBouncerWhenShowing */);
         StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
             StatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
@@ -330,7 +336,7 @@
         updateStates();
     }
 
-    public void setDozing(boolean dozing) {
+    private void setDozing(boolean dozing) {
         if (mDozing != dozing) {
             mDozing = dozing;
             if (dozing || mBouncer.needsFullscreenBouncer() || mOccluded) {
@@ -424,6 +430,8 @@
      */
     public void hide(long startTime, long fadeoutDuration) {
         mShowing = false;
+        mKeyguardMonitor.notifyKeyguardState(
+                mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded());
         launchPendingWakeupAction();
 
         if (KeyguardUpdateMonitor.getInstance(mContext).needsSlowUnlockTransition()) {
@@ -775,6 +783,16 @@
         }
     }
 
+    @Override
+    public void onStateChanged(int newState) {
+        // Nothing
+    }
+
+    @Override
+    public void onDozingChanged(boolean isDozing) {
+        setDozing(isDozing);
+    }
+
     private static class DismissWithActionRequest {
         final OnDismissAction dismissAction;
         final Runnable cancelAction;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 06f9658..a743d41e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -33,7 +33,6 @@
 import android.os.UserHandle;
 import android.view.View;
 import android.view.ViewParent;
-import android.view.ViewTreeObserver;
 
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.ActivityStarter;
@@ -44,7 +43,6 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager.Callback;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -208,8 +206,8 @@
     }
 
     @Override
-    public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
-            Intent fillInIntent, NotificationRemoteInputManager.ClickHandler defaultHandler) {
+    public boolean handleRemoteViewClick(PendingIntent pendingIntent,
+            NotificationRemoteInputManager.ClickHandler defaultHandler) {
         final boolean isActivity = pendingIntent.isActivity();
         if (isActivity) {
             final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 384a6e7..f81ffe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 import java.util.ArrayList;
 import java.util.List;
@@ -88,11 +89,13 @@
         mNetworkController = Dependency.get(NetworkController.class);
         mSecurityController = Dependency.get(SecurityController.class);
 
+        Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
         mNetworkController.addCallback(this);
         mSecurityController.addCallback(this);
     }
 
     public void destroy() {
+        Dependency.get(TunerService.class).removeTunable(this);
         mNetworkController.removeCallback(this);
         mSecurityController.removeCallback(this);
     }
@@ -101,7 +104,8 @@
         boolean vpnVisible = mSecurityController.isVpnEnabled();
         int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
 
-        mIconController.setIcon(mSlotVpn, vpnIconId, null);
+        mIconController.setIcon(mSlotVpn, vpnIconId,
+                mContext.getResources().getString(R.string.accessibility_vpn_on));
         mIconController.setIconVisibility(mSlotVpn, vpnVisible);
     }
 
@@ -136,6 +140,7 @@
             mBlockWifi = blockWifi || mForceBlockWifi;
             // Re-register to get new callbacks.
             mNetworkController.removeCallback(this);
+            mNetworkController.addCallback(this);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 0d37b55..11de941 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -76,7 +76,6 @@
     private final State mCurrentState = new State();
     private OtherwisedCollapsedListener mListener;
 
-    private final StateListener mStateListener = this::setStatusBarState;
     private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
 
     public StatusBarWindowController(Context context) {
@@ -564,6 +563,18 @@
         }
     }
 
+    private final StateListener mStateListener = new StateListener() {
+        @Override
+        public void onStateChanged(int newState) {
+            setStatusBarState(newState);
+        }
+
+        @Override
+        public void onDozingChanged(boolean isDozing) {
+            setDozing(isDozing);
+        }
+    };
+
     /**
      * Custom listener to pipe data back to plugins about whether or not the status bar would be
      * collapsed if not for the plugin.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 298a93e..6fa73ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -34,7 +34,6 @@
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.TypedValue;
-import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
@@ -49,7 +48,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.Dependency;
-import com.android.systemui.OverviewProxyService;
+import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
 import com.android.systemui.shared.system.NavigationBarCompat;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index 4ee8059..ad19729 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.app.NotificationManager;
 import android.content.ComponentName;
 import android.net.Uri;
 import android.service.notification.Condition;
@@ -29,6 +30,8 @@
     int getZen();
     ZenRule getManualRule();
     ZenModeConfig getConfig();
+    /** Gets consolidated zen policy that will apply when DND is on in priority only mode */
+    NotificationManager.Policy getConsolidatedPolicy();
     long getNextAlarm();
     boolean isZenAvailable();
     ComponentName getEffectsSuppressor();
@@ -45,6 +48,8 @@
         default void onEffectsSupressorChanged() {}
         default void onManualRuleChanged(ZenRule rule) {}
         default void onConfigChanged(ZenModeConfig config) {}
+        /** Called when the consolidated zen policy changes */
+        default void onConsolidatedPolicyChanged(NotificationManager.Policy policy) {}
     }
 
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 8d2552f..89ccff0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -69,6 +69,7 @@
     private ZenModeConfig mConfig;
     private int mZenMode;
     private long mZenUpdateTime;
+    private NotificationManager.Policy mConsolidatedNotificationPolicy;
 
     public ZenModeControllerImpl(Context context, Handler handler) {
         super(context);
@@ -91,6 +92,7 @@
         updateZenMode(mModeSetting.getValue());
         mConfigSetting.setListening(true);
         updateZenModeConfig();
+        updateConsolidatedNotificationPolicy();
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         mSetupObserver = new SetupObserver(handler);
         mSetupObserver.register();
@@ -153,6 +155,11 @@
     }
 
     @Override
+    public NotificationManager.Policy getConsolidatedPolicy() {
+        return mConsolidatedNotificationPolicy;
+    }
+
+    @Override
     public long getNextAlarm() {
         final AlarmManager.AlarmClockInfo info = mAlarmManager.getNextAlarmClock(mUserId);
         return info != null ? info.getTriggerTime() : 0;
@@ -217,6 +224,12 @@
         }
     }
 
+    private void fireConsolidatedPolicyChanged(NotificationManager.Policy policy) {
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onConsolidatedPolicyChanged(policy));
+        }
+    }
+
     @VisibleForTesting
     protected void fireConfigChanged(ZenModeConfig config) {
         synchronized (mCallbacksLock) {
@@ -231,6 +244,16 @@
     }
 
     @VisibleForTesting
+    protected void updateConsolidatedNotificationPolicy() {
+        final NotificationManager.Policy policy = mNoMan.getConsolidatedNotificationPolicy();
+        if (!Objects.equals(policy, mConsolidatedNotificationPolicy)) {
+            mConsolidatedNotificationPolicy = policy;
+            fireConsolidatedPolicyChanged(policy);
+        }
+    }
+
+
+    @VisibleForTesting
     protected void updateZenModeConfig() {
         final ZenModeConfig config = mNoMan.getZenModeConfig();
         if (Objects.equals(config, mConfig)) return;
@@ -238,9 +261,19 @@
         mConfig = config;
         mZenUpdateTime = System.currentTimeMillis();
         fireConfigChanged(config);
+
         final ZenRule newRule = config != null ? config.manualRule : null;
-        if (Objects.equals(oldRule, newRule)) return;
-        fireManualRuleChanged(newRule);
+        if (!Objects.equals(oldRule, newRule)) {
+            fireManualRuleChanged(newRule);
+        }
+
+        final NotificationManager.Policy consolidatedPolicy =
+                mNoMan.getConsolidatedNotificationPolicy();
+        if (!Objects.equals(consolidatedPolicy, mConsolidatedNotificationPolicy)) {
+            mConsolidatedNotificationPolicy = consolidatedPolicy;
+            fireConsolidatedPolicyChanged(consolidatedPolicy);
+        }
+
     }
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -260,6 +293,7 @@
         pw.println("ZenModeControllerImpl:");
         pw.println("  mZenMode=" + mZenMode);
         pw.println("  mConfig=" + mConfig);
+        pw.println("  mConsolidatedNotificationPolicy=" + mConsolidatedNotificationPolicy);
         pw.println("  mZenUpdateTime=" + DateFormat.format("MM-dd HH:mm:ss", mZenUpdateTime));
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index 0826054..ecb830c 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -29,6 +29,8 @@
 import android.util.ArraySet;
 import android.view.View;
 
+import com.android.internal.util.ArrayUtils;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.PluginEnablerImpl;
 import com.android.systemui.shared.plugins.PluginEnabler;
@@ -77,6 +79,7 @@
     }
 
     private void loadPrefs() {
+        PluginManager manager = Dependency.get(PluginManager.class);
         PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(getContext());
         screen.setOrderingAsAdded(false);
         Context prefContext = getPreferenceManager().getContext();
@@ -103,6 +106,10 @@
                 PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES);
         apps.forEach(app -> {
             if (!plugins.containsKey(app.packageName)) return;
+            if (ArrayUtils.contains(manager.getWhitelistedPlugins(), app.packageName)) {
+                // Don't manage whitelisted plugins, they are part of the OS.
+                return;
+            }
             SwitchPreference pref = new PluginPreference(prefContext, app, mPluginEnabler);
             pref.setSummary("Plugins: " + toString(plugins.get(app.packageName)));
             screen.addPreference(pref);
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 4102e63..ed2ad79 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -23,7 +23,7 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.hardware.usb.IUsbManager;
+import android.debug.IAdbManager;
 import android.hardware.usb.UsbManager;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -153,12 +153,12 @@
         boolean allow = (which == AlertDialog.BUTTON_POSITIVE);
         boolean alwaysAllow = allow && mAlwaysAllow.isChecked();
         try {
-            IBinder b = ServiceManager.getService(USB_SERVICE);
-            IUsbManager service = IUsbManager.Stub.asInterface(b);
+            IBinder b = ServiceManager.getService(ADB_SERVICE);
+            IAdbManager service = IAdbManager.Stub.asInterface(b);
             if (allow) {
-                service.allowUsbDebugging(alwaysAllow, mKey);
+                service.allowDebugging(alwaysAllow, mKey);
             } else {
-                service.denyUsbDebugging();
+                service.denyDebugging();
             }
         } catch (Exception e) {
             Log.e(TAG, "Unable to notify Usb service", e);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index 3744105..9077b6b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -539,9 +539,9 @@
       if (value != volumeItem.progress) {
         volumeItem.listItem.setProgress(value);
         volumeItem.progress = value;
-        if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
-          show(Events.SHOW_REASON_VOLUME_CHANGED);
-        }
+      }
+      if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
+        show(Events.SHOW_REASON_VOLUME_CHANGED);
       }
     }
 
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index aac37a2..b32bf99 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -39,7 +39,7 @@
     telephony-common \
     android.test.base \
     android.car \
-    android.car.user
+    android.car.userlib
 
 LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui:com.android.keyguard
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 62ca3f3..9cbe415 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -21,10 +21,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
 import android.graphics.Color;
@@ -34,7 +35,6 @@
 import android.testing.TestableLooper.RunWithLooper;
 import android.text.TextPaint;
 import android.view.LayoutInflater;
-import android.view.ViewGroup;
 import android.widget.TextClock;
 
 import com.android.systemui.SysuiTestCase;
@@ -68,6 +68,7 @@
         mKeyguardClockSwitch =
                 (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
         MockitoAnnotations.initMocks(this);
+        when(mClockView.getPaint()).thenReturn(mock(TextPaint.class));
     }
 
     @Test
@@ -91,8 +92,6 @@
         ClockPlugin plugin = mock(ClockPlugin.class);
         TextClock pluginView = new TextClock(getContext());
         when(plugin.getView()).thenReturn(pluginView);
-        TextPaint paint = mock(TextPaint.class);
-        doReturn(paint).when(mClockView).getPaint();
         PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
 
         listener.onPluginConnected(plugin, null);
@@ -102,16 +101,38 @@
     }
 
     @Test
+    public void onPluginConnected_nullView() {
+        ClockPlugin plugin = mock(ClockPlugin.class);
+        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
+        listener.onPluginConnected(plugin, null);
+        verify(mClockView, never()).setVisibility(GONE);
+    }
+
+    @Test
+    public void onPluginConnected_showSecondPluginClock() {
+        // GIVEN a plugin has already connected
+        ClockPlugin plugin1 = mock(ClockPlugin.class);
+        when(plugin1.getView()).thenReturn(new TextClock(getContext()));
+        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
+        listener.onPluginConnected(plugin1, null);
+        // WHEN a second plugin is connected
+        ClockPlugin plugin2 = mock(ClockPlugin.class);
+        when(plugin2.getView()).thenReturn(new TextClock(getContext()));
+        listener.onPluginConnected(plugin2, null);
+        // THEN only the view from the second plugin should be a child of KeyguardClockSwitch.
+        assertThat(plugin2.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+        assertThat(plugin1.getView().getParent()).isNull();
+    }
+
+    @Test
     public void onPluginDisconnected_showDefaultClock() {
         ClockPlugin plugin = mock(ClockPlugin.class);
         TextClock pluginView = new TextClock(getContext());
         when(plugin.getView()).thenReturn(pluginView);
         mClockView.setVisibility(GONE);
-        mKeyguardClockSwitch.addView(plugin.getView(), -1,
-                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                        ViewGroup.LayoutParams.WRAP_CONTENT));
         PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
 
+        listener.onPluginConnected(plugin, null);
         listener.onPluginDisconnected(plugin);
 
         verify(mClockView).setVisibility(VISIBLE);
@@ -119,6 +140,50 @@
     }
 
     @Test
+    public void onPluginDisconnected_nullView() {
+        ClockPlugin plugin = mock(ClockPlugin.class);
+        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
+        listener.onPluginConnected(plugin, null);
+        listener.onPluginDisconnected(plugin);
+        verify(mClockView, never()).setVisibility(GONE);
+    }
+
+    @Test
+    public void onPluginDisconnected_firstOfTwoDisconnected() {
+        // GIVEN two plugins are connected
+        ClockPlugin plugin1 = mock(ClockPlugin.class);
+        when(plugin1.getView()).thenReturn(new TextClock(getContext()));
+        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
+        listener.onPluginConnected(plugin1, null);
+        ClockPlugin plugin2 = mock(ClockPlugin.class);
+        when(plugin2.getView()).thenReturn(new TextClock(getContext()));
+        listener.onPluginConnected(plugin2, null);
+        // WHEN the first plugin is disconnected
+        listener.onPluginDisconnected(plugin1);
+        // THEN the view from the second plugin is still a child of KeyguardClockSwitch.
+        assertThat(plugin2.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+        assertThat(plugin1.getView().getParent()).isNull();
+    }
+
+    @Test
+    public void onPluginDisconnected_secondOfTwoDisconnected() {
+        // GIVEN two plugins are connected
+        ClockPlugin plugin1 = mock(ClockPlugin.class);
+        when(plugin1.getView()).thenReturn(new TextClock(getContext()));
+        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
+        listener.onPluginConnected(plugin1, null);
+        ClockPlugin plugin2 = mock(ClockPlugin.class);
+        when(plugin2.getView()).thenReturn(new TextClock(getContext()));
+        listener.onPluginConnected(plugin2, null);
+        // WHEN the second plugin is disconnected
+        listener.onPluginDisconnected(plugin2);
+        // THEN the default clock should be shown.
+        verify(mClockView).setVisibility(VISIBLE);
+        assertThat(plugin1.getView().getParent()).isNull();
+        assertThat(plugin2.getView().getParent()).isNull();
+    }
+
+    @Test
     public void setTextColor_defaultClockSetTextColor() {
         mKeyguardClockSwitch.setTextColor(Color.YELLOW);
 
@@ -130,8 +195,6 @@
         ClockPlugin plugin = mock(ClockPlugin.class);
         TextClock pluginView = new TextClock(getContext());
         when(plugin.getView()).thenReturn(pluginView);
-        TextPaint paint = mock(TextPaint.class);
-        doReturn(paint).when(mClockView).getPaint();
         PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
         listener.onPluginConnected(plugin, null);
 
@@ -156,8 +219,6 @@
         ClockPlugin plugin = mock(ClockPlugin.class);
         TextClock pluginView = new TextClock(getContext());
         when(plugin.getView()).thenReturn(pluginView);
-        TextPaint paint = mock(TextPaint.class);
-        doReturn(paint).when(mClockView).getPaint();
         Style style = mock(Style.class);
         PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
         listener.onPluginConnected(plugin, null);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 4ec30fd..b98ce39 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -73,17 +73,6 @@
     }
 
     @Test
-    public void hasHeader_readsSliceData() {
-        ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
-        mKeyguardSliceView.onChanged(builder.build());
-        Assert.assertFalse("View should not have a header", mKeyguardSliceView.hasHeader());
-
-        builder.setHeader(new ListBuilder.HeaderBuilder().setTitle("header title!"));
-        mKeyguardSliceView.onChanged(builder.build());
-        Assert.assertTrue("View should have a header", mKeyguardSliceView.hasHeader());
-    }
-
-    @Test
     public void refresh_replacesSliceContentAndNotifiesListener() {
         AtomicBoolean notified = new AtomicBoolean();
         mKeyguardSliceView.setContentChangeListener(()-> notified.set(true));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/InitControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/InitControllerTest.java
new file mode 100644
index 0000000..4d33d43
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/InitControllerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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.systemui;
+
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class InitControllerTest extends SysuiTestCase {
+
+    private InitController mInitController = new InitController();
+
+    @Test
+    public void testInitControllerExecutesTasks() {
+        boolean[] runs = {false, false, false};
+        mInitController.addPostInitTask(() -> {
+            runs[0] = true;
+        });
+        mInitController.addPostInitTask(() -> {
+            runs[1] = true;
+        });
+        mInitController.addPostInitTask(() -> {
+            runs[2] = true;
+        });
+        assertFalse(runs[0] || runs[1] || runs[2]);
+
+        mInitController.executePostInitTasks();
+        assertTrue(runs[0] && runs[1] && runs[2]);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testInitControllerThrowsWhenTasksAreAddedAfterExecution() {
+        boolean[] runs = {false, false, false};
+        mInitController.addPostInitTask(() -> {
+            runs[0] = true;
+        });
+        mInitController.addPostInitTask(() -> {
+            runs[1] = true;
+        });
+
+        mInitController.executePostInitTasks();
+
+        // Throws
+        mInitController.addPostInitTask(() -> {
+            runs[2] = true;
+        });
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
new file mode 100644
index 0000000..b699163
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 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.systemui.appops;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.AppOpsManager;
+import android.os.UserHandle;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationPresenter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class AppOpsControllerTest extends SysuiTestCase {
+    private static final String TEST_PACKAGE_NAME = "test";
+    private static final int TEST_UID = 0;
+    private static final int TEST_UID_OTHER = 500000;
+
+    @Mock private NotificationPresenter mPresenter;
+    @Mock private AppOpsManager mAppOpsManager;
+    @Mock private AppOpsController.Callback mCallback;
+
+    private AppOpsControllerImpl mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
+
+        mController = new AppOpsControllerImpl(mContext, Dependency.get(Dependency.BG_LOOPER));
+    }
+
+    @Test
+    public void testOnlyListenForFewOps() {
+        mController.setListening(true);
+        verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController);
+    }
+
+    @Test
+    public void testStopListening() {
+        mController.setListening(false);
+        verify(mAppOpsManager, times(1)).stopWatchingActive(mController);
+    }
+
+    @Test
+    public void addCallback_includedCode() {
+        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+        verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
+                TEST_UID, TEST_PACKAGE_NAME, true);
+    }
+
+    @Test
+    public void addCallback_notIncludedCode() {
+        mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+        verify(mCallback, never()).onActiveStateChanged(
+                anyInt(), anyInt(), anyString(), anyBoolean());
+    }
+
+    @Test
+    public void removeCallback_sameCode() {
+        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mController.removeCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+        verify(mCallback, never()).onActiveStateChanged(
+                anyInt(), anyInt(), anyString(), anyBoolean());
+    }
+
+    @Test
+    public void addCallback_notSameCode() {
+        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mController.removeCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+        verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
+                TEST_UID, TEST_PACKAGE_NAME, true);
+    }
+
+    @Test
+    public void getActiveItems_sameDetails() {
+        mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+                TEST_UID, TEST_PACKAGE_NAME, true);
+        mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+                TEST_UID, TEST_PACKAGE_NAME, true);
+        assertEquals(1, mController.getActiveAppOps().size());
+    }
+
+    @Test
+    public void getActiveItems_differentDetails() {
+        mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+                TEST_UID, TEST_PACKAGE_NAME, true);
+        mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
+                TEST_UID, TEST_PACKAGE_NAME, true);
+        assertEquals(2, mController.getActiveAppOps().size());
+    }
+
+    @Test public void getActiveItemsForUser() {
+        mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+                TEST_UID, TEST_PACKAGE_NAME, true);
+        mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
+                TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        assertEquals(1,
+                mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
+        assertEquals(1,
+                mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
new file mode 100644
index 0000000..7204d31
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 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.systemui.privacy
+
+import android.support.test.filters.SmallTest
+import android.support.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class PrivacyDialogBuilderTest : SysuiTestCase() {
+
+    companion object {
+        val MILLIS_IN_MINUTE: Long = 1000 * 60
+        val NOW = 4 * MILLIS_IN_MINUTE
+    }
+
+    @Test
+    fun testGenerateText_multipleApps() {
+        val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+                "Bar", context), 2 * MILLIS_IN_MINUTE)
+        val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
+                "Bar", context), 3 * MILLIS_IN_MINUTE)
+        val foo0 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+                "Foo", context), 0)
+        val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+                "Baz", context), 1 * MILLIS_IN_MINUTE)
+
+        val items = listOf(bar2, foo0, baz1, bar3)
+
+        val textBuilder = PrivacyDialogBuilder(context, items)
+
+        val textList = textBuilder.generateText(NOW)
+        assertEquals(2, textList.size)
+        assertEquals("Bar, Foo, Baz are using your camera", textList[0])
+        assertEquals("Bar is using your location for the last 1 min", textList[1])
+    }
+
+    @Test
+    fun testGenerateText_singleApp() {
+        val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+                "Bar", context), 0)
+        val bar1 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
+                "Bar", context), 0)
+
+        val items = listOf(bar2, bar1)
+
+        val textBuilder = PrivacyDialogBuilder(context, items)
+        val textList = textBuilder.generateText(NOW)
+        assertEquals(1, textList.size)
+        assertEquals("Bar is using your camera, location", textList[0])
+    }
+
+    @Test
+    fun testGenerateText_singleApp_singleType() {
+        val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+                "Bar", context), 2 * MILLIS_IN_MINUTE)
+        val items = listOf(bar2)
+        val textBuilder = PrivacyDialogBuilder(context, items)
+        val textList = textBuilder.generateText(NOW)
+        assertEquals(1, textList.size)
+        assertEquals("Bar is using your camera for the last 2 min", textList[0])
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
deleted file mode 120000
index 0ea3e91..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
deleted file mode 120000
index b1a0963..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java
deleted file mode 100644
index 2160f9a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.recents;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-
-import static com.android.systemui.recents.RecentsImpl.RECENTS_ACTIVITY;
-import static com.android.systemui.recents.RecentsImpl.RECENTS_PACKAGE;
-
-import static org.junit.Assert.fail;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityTaskManager;
-import android.app.IActivityTaskManager;
-import android.os.SystemClock;
-import android.support.test.filters.MediumTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@MediumTest
-public class RecentsTest extends SysuiTestCase {
-
-    @Test
-    public void testRecentsActivityType() throws Exception {
-        // Clear the state
-        final IActivityTaskManager atm = ActivityTaskManager.getService();
-        atm.removeStacksWithActivityTypes(new int[] { ACTIVITY_TYPE_RECENTS });
-
-        // Toggle recents, use a shell command because it is not exported
-        runShellCommand("am start -n " + RECENTS_PACKAGE + "/" + RECENTS_ACTIVITY);
-
-        // Verify that an activity was launched with the right activity type
-        int retryCount = 0;
-        while (retryCount < 10) {
-            List<RunningTaskInfo> tasks = atm.getTasks(Integer.MAX_VALUE);
-            for (RunningTaskInfo info : tasks) {
-                if (info.configuration.windowConfiguration.getActivityType()
-                        == ACTIVITY_TYPE_RECENTS) {
-                    // Found a recents activity with the right activity type
-                    return;
-                }
-            }
-            SystemClock.sleep(50);
-            retryCount++;
-        }
-        fail("Expected Recents activity with ACTIVITY_TYPE_RECENTS");
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index 5bf6040..5cc3b3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -122,7 +123,7 @@
         waitForIdleSync(mPluginInstanceManager.mPluginHandler);
         waitForIdleSync(mPluginInstanceManager.mMainHandler);
 
-        verify(mMockListener, Mockito.never()).onPluginConnected(any(), any());
+        verify(mMockListener, never()).onPluginConnected(any(), any());
     }
 
     @Test
@@ -162,7 +163,7 @@
         waitForIdleSync(mPluginInstanceManager.mMainHandler);
 
         // Plugin shouldn't be connected because it is the wrong version.
-        verify(mMockListener, Mockito.never()).onPluginConnected(any(), any());
+        verify(mMockListener, never()).onPluginConnected(any(), any());
         verify(nm).notifyAsUser(eq(TestPlugin.class.getName()), eq(SystemMessage.NOTE_PLUGIN),
                 any(), eq(UserHandle.ALL));
     }
@@ -200,7 +201,7 @@
         waitForIdleSync(mPluginInstanceManager.mMainHandler);;
 
         // Non-debuggable build should receive no plugins.
-        verify(mMockListener, Mockito.never()).onPluginConnected(any(), any());
+        verify(mMockListener, never()).onPluginConnected(any(), any());
     }
 
     @Test
@@ -229,7 +230,7 @@
         // Start with an unrelated class.
         boolean result = mPluginInstanceManager.checkAndDisable(Activity.class.getName());
         assertFalse(result);
-        verify(mMockPm, Mockito.never()).setComponentEnabledSetting(
+        verify(mMockPm, never()).setComponentEnabledSetting(
                 ArgumentCaptor.forClass(ComponentName.class).capture(),
                 ArgumentCaptor.forClass(int.class).capture(),
                 ArgumentCaptor.forClass(int.class).capture());
@@ -255,6 +256,21 @@
                 ArgumentCaptor.forClass(int.class).capture());
     }
 
+    @Test
+    public void testDisableWhitelisted() throws Exception {
+        mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
+                mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
+                mMockManager, false, new String[] {WHITELISTED_PACKAGE});
+        createPlugin(); // Get into valid created state.
+
+        mPluginInstanceManager.disableAll();
+
+        verify(mMockPm, never()).setComponentEnabledSetting(
+                ArgumentCaptor.forClass(ComponentName.class).capture(),
+                ArgumentCaptor.forClass(int.class).capture(),
+                ArgumentCaptor.forClass(int.class).capture());
+    }
+
     private void setupFakePmQuery() throws Exception {
         List<ResolveInfo> list = new ArrayList<>();
         ResolveInfo info = new ResolveInfo();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java
deleted file mode 100644
index b405a5c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2018 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.systemui.statusbar.notification;
-
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.app.AppOpsManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationPresenter;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class AppOpsListenerTest extends SysuiTestCase {
-    private static final String TEST_PACKAGE_NAME = "test";
-    private static final int TEST_UID = 0;
-
-    @Mock private NotificationPresenter mPresenter;
-    @Mock private AppOpsManager mAppOpsManager;
-
-    // Dependency mocks:
-    @Mock private NotificationEntryManager mEntryManager;
-    @Mock private ForegroundServiceController mFsc;
-
-    private AppOpsListener mListener;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
-        mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
-        getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
-        mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
-                Handler.createAsync(Looper.myLooper()));
-
-        mListener = new AppOpsListener(mContext);
-    }
-
-    @Test
-    public void testOnlyListenForFewOps() {
-        mListener.setUpWithPresenter(mPresenter);
-
-        verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsListener.OPS, mListener);
-    }
-
-    @Test
-    public void testStopListening() {
-        mListener.destroy();
-        verify(mAppOpsManager, times(1)).stopWatchingActive(mListener);
-    }
-
-    @Test
-    public void testInformEntryMgrOnAppOpsChange() {
-        mListener.setUpWithPresenter(mPresenter);
-        mListener.onOpActiveChanged(
-                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-        TestableLooper.get(this).processAllMessages();
-        verify(mEntryManager, times(1)).updateNotificationsForAppOp(
-                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-    }
-
-    @Test
-    public void testInformFscOnAppOpsChange() {
-        mListener.setUpWithPresenter(mPresenter);
-        mListener.onOpActiveChanged(
-                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-        TestableLooper.get(this).processAllMessages();
-        verify(mFsc, times(1)).onAppOpChanged(
-                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
index fdb66cc..0d2d345 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
@@ -167,7 +167,7 @@
         CountDownLatch countDownLatch = new CountDownLatch(1);
         NotificationInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0,
                 new ArrayMap() /* cachedContentViews */, mRow, false /* redactAmbient */,
-                true /* isNewView */, new RemoteViews.OnClickHandler(),
+                true /* isNewView */, (v, p, r) -> true,
                 new NotificationInflater.InflationCallback() {
                     @Override
                     public void handleInflationException(StatusBarNotification notification,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index d2fe82f..fdd89de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -147,7 +147,6 @@
         doNothing().when(mGroupManager).collapseAllGroups();
         doNothing().when(mExpandHelper).cancelImmediately();
         doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
-        doNothing().when(notificationShelf).fadeInTranslating();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index b5f67c0..098fa62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -15,22 +15,13 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.mockitoSession;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -38,49 +29,34 @@
 
 import android.animation.Animator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.graphics.Rect;
 import android.os.Handler;
-import android.os.IPowerManager;
-import android.os.Looper;
-import android.os.PowerManager;
 import android.service.notification.StatusBarNotification;
-import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.testing.TestableLooper.RunWithLooper;
+import android.testing.UiThreadTest;
 import android.view.MotionEvent;
-import android.view.VelocityTracker;
 import android.view.View;
-import android.view.MotionEvent;
 
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationMenuRow;
 
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoSession;
-import org.mockito.invocation.InvocationOnMock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.mockito.stubbing.Answer;
 
-import java.util.ArrayList;
-
 /**
  * Tests for {@link NotificationSwipeHelper}.
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
+@UiThreadTest
 public class NotificationSwipeHelperTest extends SysuiTestCase {
 
     private NotificationSwipeHelper mSwipeHelper;
@@ -96,7 +72,6 @@
     @Rule public MockitoRule mockito = MockitoJUnit.rule();
 
     @Before
-    @UiThreadTest
     public void setUp() throws Exception {
         mCallback = mock(NotificationSwipeHelper.NotificationCallback.class);
         mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
index 203ebe6..fe36b6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
@@ -16,14 +16,10 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
-import android.os.Debug;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -43,41 +39,26 @@
 public class DozeScrimControllerTest extends SysuiTestCase {
 
     @Mock
-    private ScrimController mScrimController;
-    @Mock
     private DozeParameters mDozeParameters;
     private DozeScrimController mDozeScrimController;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        // Make sure callbacks will be invoked to complete the lifecycle.
-        doAnswer(invocationOnMock -> {
-            ScrimController.Callback callback = invocationOnMock.getArgument(1);
-            callback.onStart();
-            callback.onDisplayBlanked();
-            callback.onFinished();
-            return null;
-        }).when(mScrimController).transitionTo(any(ScrimState.class),
-                any(ScrimController.Callback.class));
-
-        mDozeScrimController = new DozeScrimController(mScrimController, getContext(),
-                mDozeParameters);
+        mDozeScrimController = new DozeScrimController(mDozeParameters);
         mDozeScrimController.setDozing(true);
     }
 
     @Test
-    public void changesScrimControllerState() {
-        mDozeScrimController.pulse(mock(DozeHost.PulseCallback.class), 0);
-        verify(mScrimController).transitionTo(eq(ScrimState.PULSING),
-                any(ScrimController.Callback.class));
-    }
-
-    @Test
     public void callsPulseCallback() {
         DozeHost.PulseCallback callback = mock(DozeHost.PulseCallback.class);
         mDozeScrimController.pulse(callback, 0);
 
+        // Manually simulate a scrim lifecycle
+        mDozeScrimController.getScrimCallback().onStart();
+        mDozeScrimController.getScrimCallback().onDisplayBlanked();
+        mDozeScrimController.getScrimCallback().onFinished();
+
         verify(callback).onPulseStarted();
         mDozeScrimController.pulseOutNow();
         verify(callback).onPulseFinished();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
new file mode 100644
index 0000000..f8ad298
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2018 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.systemui.statusbar.phone;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
+
+    private static final int SCREEN_HEIGHT = 2000;
+    private static final int EMPTY_MARGIN = 0;
+    private static final int EMPTY_HEIGHT = 0;
+    private static final boolean SECURE_LOCKED = false;
+    private static final boolean PULSING_NO = false;
+    private static final float ZERO_DRAG = 0.f;
+    private static final float OPAQUE = 1.f;
+    private static final float TRANSPARENT = 0.f;
+
+    private KeyguardClockPositionAlgorithm mClockPositionAlgorithm;
+    private KeyguardClockPositionAlgorithm.Result mClockPosition;
+    private int mNotificationStackHeight;
+    private float mPanelExpansion;
+    private int mKeyguardStatusHeight;
+    private float mDark;
+
+    @Before
+    public void setUp() {
+        mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm();
+        mClockPosition = new KeyguardClockPositionAlgorithm.Result();
+    }
+
+    @Test
+    public void clockPositionMiddleOfScreenOnAOD() {
+        // GIVEN on AOD and both stack scroll and clock have 0 height
+        givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the clock Y position is the middle of the screen (SCREEN_HEIGHT / 2).
+        assertThat(mClockPosition.clockY).isEqualTo(1000);
+        // AND the clock is opaque and positioned on the left.
+        assertThat(mClockPosition.clockX).isEqualTo(0);
+        assertThat(mClockPosition.clockAlpha).isEqualTo(OPAQUE);
+    }
+
+    @Test
+    public void clockPositionAdjustsForKeyguardStatusOnAOD() {
+        // GIVEN on AOD with a clock of height 100
+        givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = 100;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the clock Y position adjusts for the clock height (SCREEN_HEIGHT / 2 - 100).
+        assertThat(mClockPosition.clockY).isEqualTo(900);
+        // AND the clock is opaque and positioned on the left.
+        assertThat(mClockPosition.clockX).isEqualTo(0);
+        assertThat(mClockPosition.clockAlpha).isEqualTo(OPAQUE);
+    }
+
+    @Test
+    public void clockPositionLargeClockOnAOD() {
+        // GIVEN on AOD with a full screen clock
+        givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = SCREEN_HEIGHT;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the clock Y position doesn't overflow the screen.
+        assertThat(mClockPosition.clockY).isEqualTo(0);
+        // AND the clock is opaque and positioned on the left.
+        assertThat(mClockPosition.clockX).isEqualTo(0);
+        assertThat(mClockPosition.clockAlpha).isEqualTo(OPAQUE);
+    }
+
+    @Test
+    public void clockPositionMiddleOfScreenOnLockScreen() {
+        // GIVEN on lock screen with stack scroll and clock of 0 height
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the clock Y position is the middle of the screen (SCREEN_HEIGHT / 2).
+        assertThat(mClockPosition.clockY).isEqualTo(1000);
+        // AND the clock is opaque and positioned on the left.
+        assertThat(mClockPosition.clockX).isEqualTo(0);
+        assertThat(mClockPosition.clockAlpha).isEqualTo(OPAQUE);
+    }
+
+    @Test
+    public void clockPositionWithStackScrollExpandOnLockScreen() {
+        // GIVEN on lock screen with stack scroll of height 500
+        givenLockScreen();
+        mNotificationStackHeight = 500;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the clock Y position adjusts for stack scroll height ( (SCREEN_HEIGHT - 500 ) / 2).
+        assertThat(mClockPosition.clockY).isEqualTo(750);
+        // AND the clock is opaque and positioned on the left.
+        assertThat(mClockPosition.clockX).isEqualTo(0);
+        assertThat(mClockPosition.clockAlpha).isEqualTo(OPAQUE);
+    }
+
+    @Test
+    public void clockPositionWithPartialDragOnLockScreen() {
+        // GIVEN dragging up on lock screen
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        mPanelExpansion = 0.5f;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the clock Y position adjusts with drag gesture.
+        assertThat(mClockPosition.clockY).isLessThan(1000);
+        // AND the clock is positioned on the left and not fully opaque.
+        assertThat(mClockPosition.clockX).isEqualTo(0);
+        assertThat(mClockPosition.clockAlpha).isLessThan(OPAQUE);
+    }
+
+    @Test
+    public void clockPositionWithFullDragOnLockScreen() {
+        // GIVEN the lock screen is dragged up
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        mPanelExpansion = 0.f;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the clock is transparent.
+        assertThat(mClockPosition.clockAlpha).isEqualTo(TRANSPARENT);
+    }
+
+    @Test
+    public void largeClockOnLockScreenIsTransparent() {
+        // GIVEN on lock screen with a full screen clock
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = SCREEN_HEIGHT;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the clock is transparent
+        assertThat(mClockPosition.clockAlpha).isEqualTo(TRANSPARENT);
+    }
+
+    private void givenAOD() {
+        mPanelExpansion = 1.f;
+        mDark = 1.f;
+    }
+
+    private void givenLockScreen() {
+        mPanelExpansion = 1.f;
+        mDark = 0.f;
+    }
+
+    private void positionClock() {
+        mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
+                mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mDark, SECURE_LOCKED,
+                PULSING_NO, ZERO_DRAG);
+        mClockPositionAlgorithm.run(mClockPosition);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 17df800..003d058 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -27,7 +27,7 @@
 
 import com.android.systemui.Dependency;
 
-import com.android.systemui.OverviewProxyService;
+import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.stackdivider.Divider;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 939245f..882f261 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -67,6 +67,8 @@
 import com.android.systemui.InitController;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.appops.AppOpsControllerImpl;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -84,7 +86,6 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.AppOpsListener;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -157,7 +158,7 @@
         mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
         mDependency.injectTestDependency(NotificationListener.class, mNotificationListener);
         mDependency.injectTestDependency(KeyguardMonitor.class, mock(KeyguardMonitorImpl.class));
-        mDependency.injectTestDependency(AppOpsListener.class, mock(AppOpsListener.class));
+        mDependency.injectTestDependency(AppOpsController.class, mock(AppOpsControllerImpl.class));
         mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController);
         mDependency.injectTestDependency(DeviceProvisionedController.class,
                 mDeviceProvisionedController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
index 5f54bce..6d1e6ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
@@ -62,6 +62,11 @@
     }
 
     @Override
+    public String[] getWhitelistedPlugins() {
+        return new String[0];
+    }
+
+    @Override
     public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
         return null;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
index 86c43c9..75df4e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.app.NotificationManager;
 import android.content.ComponentName;
 import android.net.Uri;
 import android.service.notification.ZenModeConfig;
@@ -49,6 +50,11 @@
     }
 
     @Override
+    public NotificationManager.Policy getConsolidatedPolicy() {
+        return null;
+    }
+
+    @Override
     public long getNextAlarm() {
         return 0;
     }
diff --git a/packages/VpnDialogs/res/values-de/strings.xml b/packages/VpnDialogs/res/values-de/strings.xml
index d901104..0f1e009 100644
--- a/packages/VpnDialogs/res/values-de/strings.xml
+++ b/packages/VpnDialogs/res/values-de/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Verbindungsanfrage"</string>
-    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> möchte eine VPN-Verbindung herstellen, über die der Netzwerkverkehr überwacht werden kann. Lass die Verbindung nur zu, wenn die App vertrauenswürdig ist. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; wird oben am Display angezeigt, wenn VPN aktiv ist."</string>
+    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> möchte eine VPN-Verbindung herstellen, über die der Netzwerkverkehr überwacht werden kann. Lass die Verbindung nur zu, wenn die App vertrauenswürdig ist. Wenn VPN aktiv ist, wird oben im Display &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; angezeigt."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN ist verbunden"</string>
     <string name="session" msgid="6470628549473641030">"Sitzung:"</string>
     <string name="duration" msgid="3584782459928719435">"Dauer:"</string>
diff --git a/packages/VpnDialogs/res/values-hi/strings.xml b/packages/VpnDialogs/res/values-hi/strings.xml
index b866e5c..5560a85 100644
--- a/packages/VpnDialogs/res/values-hi/strings.xml
+++ b/packages/VpnDialogs/res/values-hi/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"कनेक्शन अनुरोध"</string>
-    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> वीपीएन कनेक्‍शन सेट अप करना चाहता है, जिससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. इसकी मंज़ूरी तभी दें जब आपको इस पर भरोसा हो. वीपीएन चालू होने पर आपकी स्क्रीन के सबसे ऊपर &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; दिखाई देता है."</string>
+    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> वीपीएन कनेक्‍शन सेट अप करना चाहता है, जिससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. इसकी मंज़ूरी तभी दें जब आपको इस पर भरोसा हो. वीपीएन चालू होने पर &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; आपकी स्क्रीन के सबसे ऊपर दिखाई देता है."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN कनेक्‍ट है"</string>
     <string name="session" msgid="6470628549473641030">"सत्र:"</string>
     <string name="duration" msgid="3584782459928719435">"अवधि:"</string>
diff --git a/packages/VpnDialogs/res/values-mr/strings.xml b/packages/VpnDialogs/res/values-mr/strings.xml
index 129b7b1..318f854 100644
--- a/packages/VpnDialogs/res/values-mr/strings.xml
+++ b/packages/VpnDialogs/res/values-mr/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"कनेक्‍शन विनंती"</string>
-    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> नेटवर्क रहदारीचे परीक्षण करण्‍यासाठी त्यास अनुमती देणारे VPN कनेक्‍शन सेट करू इच्‍छितो. आपल्याला स्त्रोत विश्वसनीय वाटत असेल तरच स्वीकार करा. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN सक्रिय असताना आपल्‍या स्क्रीनच्या शीर्षावर दिसते."</string>
+    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> नेटवर्क रहदारीचे परीक्षण करण्‍यासाठी त्यास अनुमती देणारे VPN कनेक्‍शन सेट करू इच्‍छितो. तुम्हाला स्त्रोत विश्वसनीय वाटत असेल तरच स्वीकार करा. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN सक्रिय असताना आपल्‍या स्क्रीनच्या शीर्षावर दिसते."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN कनेक्‍ट केले"</string>
     <string name="session" msgid="6470628549473641030">"सत्र:"</string>
     <string name="duration" msgid="3584782459928719435">"कालावधी:"</string>
diff --git a/packages/VpnDialogs/res/values-vi/strings.xml b/packages/VpnDialogs/res/values-vi/strings.xml
index fa5e114..097c9ae 100644
--- a/packages/VpnDialogs/res/values-vi/strings.xml
+++ b/packages/VpnDialogs/res/values-vi/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Yêu cầu kết nối"</string>
-    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Chỉ chấp nhận nếu bạn tin tưởng nguồn. Biểu tượng &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; xuất hiện ở đầu màn hình của bạn khi VPN đang hoạt động."</string>
+    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Chỉ chấp nhận nếu bạn tin tưởng nguồn. &lt;br /&gt; &lt;br /&gt; Biểu tượng &lt;img src=vpn_icon /&gt; xuất hiện ở đầu màn hình của bạn khi VPN đang hoạt động."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN được kết nối"</string>
     <string name="session" msgid="6470628549473641030">"Phiên"</string>
     <string name="duration" msgid="3584782459928719435">"Thời lượng:"</string>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-af/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-af/strings.xml
new file mode 100644
index 0000000..09a3ca8
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-af/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Eksperiment met swewende navigasiebalk"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-am/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-am/strings.xml
new file mode 100644
index 0000000..10a5d9d
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-am/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"ተንሳፋፊ የአሰሳ አሞሌ ሙከራ"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ar/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ar/strings.xml
new file mode 100644
index 0000000..123e1be
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ar/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"تجربة شريط التنقُّل العائم"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-az/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-az/strings.xml
new file mode 100644
index 0000000..bc48759
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-az/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Dəyişkən Naviqasiya Paneli Təcrübəsi"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..f83754c
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Eksperiment sa plutajućom trakom za navigaciju"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-be/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-be/strings.xml
new file mode 100644
index 0000000..957be53
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-be/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Эксперымент з плаваючай панэллю навігацыі"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-bg/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-bg/strings.xml
new file mode 100644
index 0000000..1fe6d36
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-bg/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Експеримент с плаваща лента за навигация"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-bs/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-bs/strings.xml
new file mode 100644
index 0000000..621cf59
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-bs/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Eksperiment s plutajućom trakom za navigaciju"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ca/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ca/strings.xml
new file mode 100644
index 0000000..e747d06
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ca/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Experiment amb barra de navegació flotant"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-cs/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-cs/strings.xml
new file mode 100644
index 0000000..3b5a4d8
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-cs/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Plovoucí navigační panel (experiment)"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-da/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-da/strings.xml
new file mode 100644
index 0000000..0e114df
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-da/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Test med svævende navigationslinje"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-de/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-de/strings.xml
new file mode 100644
index 0000000..320e275
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-de/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Experiment mit unverankerter Navigationsleiste"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-el/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-el/strings.xml
new file mode 100644
index 0000000..2d46403
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-el/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Πείραμα κινούμενης γραμμής πλοήγησης"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rAU/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..17227fc
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rAU/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Floating Navigation Bar Experiment"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..17227fc
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rCA/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Floating Navigation Bar Experiment"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rGB/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..17227fc
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rGB/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Floating Navigation Bar Experiment"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rIN/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..17227fc
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rIN/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Floating Navigation Bar Experiment"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rXC/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..d379ec1
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-en-rXC/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‎‎‏‎‎‎Floating Navigation Bar Experiment‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-es-rUS/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..34e70dc
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-es-rUS/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Experimento de barra de navegación flotante"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-es/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-es/strings.xml
new file mode 100644
index 0000000..34e70dc
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-es/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Experimento de barra de navegación flotante"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-et/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-et/strings.xml
new file mode 100644
index 0000000..c20f38e
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-et/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Hõljuva navigeerimisriba katse"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-eu/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-eu/strings.xml
new file mode 100644
index 0000000..4623a88
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-eu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Nabigazio-barra gainerakorraren esperimentua"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-fa/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-fa/strings.xml
new file mode 100644
index 0000000..f7dbb53
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-fa/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"آزمایش نوار پیمایش شناور"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-fi/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-fi/strings.xml
new file mode 100644
index 0000000..397052d
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-fi/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Kelluvan navigointipalkin kokeilu"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..a831068
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-fr-rCA/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Expérience de barre de navigation flottante"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-fr/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-fr/strings.xml
new file mode 100644
index 0000000..032ca9d
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-fr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Test relatif à la barre de navigation flottante"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-gl/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-gl/strings.xml
new file mode 100644
index 0000000..34e70dc
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-gl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Experimento de barra de navegación flotante"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-hi/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-hi/strings.xml
new file mode 100644
index 0000000..5f8b1bc
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-hi/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"फ़्लोट करता हुआ नेविगेशन बार प्रयोग"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-hr/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-hr/strings.xml
new file mode 100644
index 0000000..8570caa
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-hr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Eksperiment s plutajućom navigacijskom trakom"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-hu/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-hu/strings.xml
new file mode 100644
index 0000000..56e02a8
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-hu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Kísérleti lebegő navigációs sáv"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-hy/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-hy/strings.xml
new file mode 100644
index 0000000..6623812
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-hy/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Նավարկման լողացող գոտու փորձարկում"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-in/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-in/strings.xml
new file mode 100644
index 0000000..457db1c
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-in/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Eksperimen Menu Navigasi Mengambang"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-is/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-is/strings.xml
new file mode 100644
index 0000000..c5b2c23
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-is/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Tilraun með fljótandi yfirlitsstiku"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-it/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-it/strings.xml
new file mode 100644
index 0000000..ef858fb
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-it/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Esperimento Barra di navigazione floating"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-iw/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-iw/strings.xml
new file mode 100644
index 0000000..866ab78
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-iw/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"ניסוי של סרגל ניווט צף"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ja/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ja/strings.xml
new file mode 100644
index 0000000..49264ca
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ja/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"フローティング ナビゲーション バー テスト"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ka/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ka/strings.xml
new file mode 100644
index 0000000..440535e
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ka/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"ნავიგაციის მოლივლივე ზოლის ექსპერიმენტი"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-kk/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-kk/strings.xml
new file mode 100644
index 0000000..6316555
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-kk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Қалқымалы навигация жолағы (эксперимент)"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-km/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-km/strings.xml
new file mode 100644
index 0000000..4888b2b
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-km/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"ការពិសោធ​នៃ​របាររុករក​ដែល​អណ្ដែត"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ko/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ko/strings.xml
new file mode 100644
index 0000000..89d221c
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ko/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"플로팅 탐색 메뉴 실험"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ky/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ky/strings.xml
new file mode 100644
index 0000000..bfd96da
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ky/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Калкыма чабыттоо тилкесин колдонуу"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-lo/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-lo/strings.xml
new file mode 100644
index 0000000..4b14abd
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-lo/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"ການທົດລອງແຖບການນຳທາງແບບລອຍ"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-lt/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-lt/strings.xml
new file mode 100644
index 0000000..bb45ec2
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-lt/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Slankiosios naršymo juostos eksperimentas"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-lv/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-lv/strings.xml
new file mode 100644
index 0000000..e537508
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-lv/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Peldošas navigācijas joslas eksperiments"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-mk/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-mk/strings.xml
new file mode 100644
index 0000000..f1a442d
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-mk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Експеримент со лебдечка лента за навигација"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-mn/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-mn/strings.xml
new file mode 100644
index 0000000..1fd974d
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-mn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Хөвөгч навигацийн самбарын туршилт"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-mr/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-mr/strings.xml
new file mode 100644
index 0000000..54387fe
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-mr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"फ्लोटिंग नेव्हिगेशन बार प्रयोग"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ms/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ms/strings.xml
new file mode 100644
index 0000000..20471d3a
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ms/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Percubaan Bar Navigasi Terapung"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-my/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-my/strings.xml
new file mode 100644
index 0000000..7d94bdb
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-my/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"မျောနေသော လမ်းညွှန်ဘား စမ်းသပ်မှု"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-nb/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-nb/strings.xml
new file mode 100644
index 0000000..505e28a
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-nb/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Eksperiment med flytende navigasjonsrad"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-nl/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-nl/strings.xml
new file mode 100644
index 0000000..69242a7
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-nl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Experiment voor zwevende navigatiebalk"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-pl/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-pl/strings.xml
new file mode 100644
index 0000000..a18afc3
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-pl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Eksperyment z pływającym paskiem nawigacyjnym"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..7f58e75
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-pt-rBR/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Experimento de barra de navegação flutuante"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..faee8c4
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-pt-rPT/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Experiência de barra de navegação flutuante"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-pt/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-pt/strings.xml
new file mode 100644
index 0000000..7f58e75
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-pt/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Experimento de barra de navegação flutuante"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ro/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ro/strings.xml
new file mode 100644
index 0000000..b7debfc
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ro/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Experiment cu bară de navigare flotantă"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ru/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ru/strings.xml
new file mode 100644
index 0000000..8314c9d
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ru/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Плавающая панель навигации (эксперимент)"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sk/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sk/strings.xml
new file mode 100644
index 0000000..6cf1794
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Plávajúci navigačný panel (experiment)"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sl/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sl/strings.xml
new file mode 100644
index 0000000..4b1417f
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Preizkus s plavajočo vrstico za krmarjenje"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sq/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sq/strings.xml
new file mode 100644
index 0000000..254101f
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sq/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Eksperimenti i shiritit pluskues të navigimit"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sr/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sr/strings.xml
new file mode 100644
index 0000000..4ee3f2e
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Експеримент са плутајућом траком за навигацију"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sv/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sv/strings.xml
new file mode 100644
index 0000000..0c1cbf2
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sv/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Experimentellt flytande navigeringsfält"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sw/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sw/strings.xml
new file mode 100644
index 0000000..5ecbf906
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-sw/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Jaribio la Sehemu ya Viungo Muhimu Inayoweza Kusogezwa"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-th/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-th/strings.xml
new file mode 100644
index 0000000..763d1a4
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-th/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"การทดสอบแถบนำทางแบบลอย"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-tl/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-tl/strings.xml
new file mode 100644
index 0000000..f3d5981
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-tl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Eksperimentong Floating na Navigation Bar"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-tr/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-tr/strings.xml
new file mode 100644
index 0000000..08ecdee
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-tr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Kayan Gezinme Çubuğu Denemesi"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-uk/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-uk/strings.xml
new file mode 100644
index 0000000..497bfe6
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-uk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Експеримент із плаваючою панеллю навігації"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ur/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ur/strings.xml
new file mode 100644
index 0000000..aa7810e
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-ur/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"فلوٹنگ نیویگیشن بار کا تجربہ"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-uz/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-uz/strings.xml
new file mode 100644
index 0000000..0f95716
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-uz/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Erkin harakatlanuvchi navigatsiya paneli tajribasi"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-vi/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-vi/strings.xml
new file mode 100644
index 0000000..b0c2b06
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-vi/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Thử nghiệm thanh điều hướng nổi"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..3edd382
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-zh-rCN/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"浮动导航栏实验"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..3f038ea
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-zh-rHK/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"懸浮導覽列實驗"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..51dc364
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-zh-rTW/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"浮動導覽列實驗"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-zu/strings.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-zu/strings.xml
new file mode 100644
index 0000000..ea193ea
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values-zu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="7290469683147348228">"Ukuhlolwa kwebha entantayo yokuzula"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-af/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-af/strings.xml
new file mode 100644
index 0000000..21a0003
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-af/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperiment met dun navigasiebalk"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-am/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-am/strings.xml
new file mode 100644
index 0000000..6a7d644
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-am/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ቀጭን የአሰሳ አሞሌ ሙከራ"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ar/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ar/strings.xml
new file mode 100644
index 0000000..2cebf40
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ar/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"تجربة شريط التنقُّل النحيف"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-az/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-az/strings.xml
new file mode 100644
index 0000000..266c051
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-az/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Sabit Naviqasiya Paneli Təcrübəsi"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..dc57a3f
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperiment sa tankom trakom za navigaciju"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-be/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-be/strings.xml
new file mode 100644
index 0000000..8b53bcd
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-be/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Эксперымент з тонкай панэллю навігацыі"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bg/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bg/strings.xml
new file mode 100644
index 0000000..4bf000e
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bg/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Експеримент с тънка лента за навигация"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bs/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bs/strings.xml
new file mode 100644
index 0000000..9814209
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bs/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperiment s tankom trakom za navigaciju"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ca/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ca/strings.xml
new file mode 100644
index 0000000..4a9b598
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ca/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experiment amb barra de navegació fina"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-cs/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-cs/strings.xml
new file mode 100644
index 0000000..d923a3c
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-cs/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Úzký navigační panel (experiment)"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-da/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-da/strings.xml
new file mode 100644
index 0000000..12bb2f2
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-da/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Test med smal navigationslinje"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-de/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-de/strings.xml
new file mode 100644
index 0000000..960a7d9
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-de/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experiment mit schmaler Navigationsleiste"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-el/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-el/strings.xml
new file mode 100644
index 0000000..119b7e6
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-el/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Πείραμα λεπτής γραμμής πλοήγησης"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rAU/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..5ebf403
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rAU/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Slim Navigation Bar Experiment"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..5ebf403
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rCA/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Slim Navigation Bar Experiment"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rGB/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..5ebf403
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rGB/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Slim Navigation Bar Experiment"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rIN/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..5ebf403
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rIN/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Slim Navigation Bar Experiment"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rXC/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..79884ce
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rXC/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‎Slim Navigation Bar Experiment‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es-rUS/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..00b0444
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es-rUS/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experimento de barra de navegación delgada"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es/strings.xml
new file mode 100644
index 0000000..58bdaa2
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experimento de barra navegación delgada"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-et/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-et/strings.xml
new file mode 100644
index 0000000..4c5023c
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-et/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Kitsa navigeerimisriba katse"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-eu/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-eu/strings.xml
new file mode 100644
index 0000000..fc16eeb
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-eu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Nabigazio-barra finaren esperimentua"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fa/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fa/strings.xml
new file mode 100644
index 0000000..e4246d7
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fa/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"آزمایش نوار پیمایش باریک"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fi/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fi/strings.xml
new file mode 100644
index 0000000..9385a47
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fi/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Ohuen navigointipalkin kokeilu"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..d22848b
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr-rCA/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Expérience de barre de navigation mince"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr/strings.xml
new file mode 100644
index 0000000..cfa1db2
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Test relatif à la barre de navigation fine"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gl/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gl/strings.xml
new file mode 100644
index 0000000..d2194ce
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experimento de barra de navegación estreita"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hi/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hi/strings.xml
new file mode 100644
index 0000000..d51cee3
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hi/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"स्लिम नेविगेशन बार प्रयोग"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hr/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hr/strings.xml
new file mode 100644
index 0000000..410c58e
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperiment s tankom navigacijskom trakom"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hu/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hu/strings.xml
new file mode 100644
index 0000000..d7eafed
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Kísérleti keskeny navigációs sáv"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hy/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hy/strings.xml
new file mode 100644
index 0000000..b0200ca
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hy/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Նավարկման նեղ գոտու փորձարկում"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-in/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-in/strings.xml
new file mode 100644
index 0000000..3e0bded
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-in/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperimen Menu Navigasi Ramping"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-is/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-is/strings.xml
new file mode 100644
index 0000000..03ccaf3
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-is/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Tilraun með þunna yfirlitsstiku"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-it/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-it/strings.xml
new file mode 100644
index 0000000..f7c5d253
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-it/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Esperimento Barra di navigazione sottile"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-iw/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-iw/strings.xml
new file mode 100644
index 0000000..0d0ec2c
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-iw/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ניסוי של סרגל ניווט דק"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ja/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ja/strings.xml
new file mode 100644
index 0000000..a3d6874
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ja/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"スリム ナビゲーション バー テスト"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ka/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ka/strings.xml
new file mode 100644
index 0000000..ffddf3b
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ka/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ნავიგაციის მჭიდრო ზოლის ექსპერიმენტი"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kk/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kk/strings.xml
new file mode 100644
index 0000000..f34ac08
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Жіңішке навигация жолағы (эксперимент)"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-km/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-km/strings.xml
new file mode 100644
index 0000000..114a782
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-km/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ការពិសោធ​នៃ​របាររុករក​ស្ដើង"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ko/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ko/strings.xml
new file mode 100644
index 0000000..fca02c3
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ko/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"슬림한 탐색 메뉴 실험"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ky/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ky/strings.xml
new file mode 100644
index 0000000..6b15d51
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ky/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Жука чабыттоо тилкесин колдонуу"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lo/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lo/strings.xml
new file mode 100644
index 0000000..6ec48ca
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lo/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ການທົດລອງແຖບການນຳທາງແບບບາງ"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lt/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lt/strings.xml
new file mode 100644
index 0000000..1df54aa
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lt/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Plonos naršymo juostos eksperimentas"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lv/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lv/strings.xml
new file mode 100644
index 0000000..5c6c565
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lv/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Plānas navigācijas joslas eksperiments"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mk/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mk/strings.xml
new file mode 100644
index 0000000..3517813
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Експеримент со тенка лента за навигација"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mn/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mn/strings.xml
new file mode 100644
index 0000000..a2282c4
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Нимгэн навигацийн самбарын туршилт"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mr/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mr/strings.xml
new file mode 100644
index 0000000..c714370
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"स्लिम नॅव्हिगेशन बार प्रयोग"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ms/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ms/strings.xml
new file mode 100644
index 0000000..68f831e
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ms/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Percubaan Bar Navigasi Langsing"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-my/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-my/strings.xml
new file mode 100644
index 0000000..84db279
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-my/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ပါးလွှာသော လမ်းညွှန်ဘား စမ်းသပ်မှု"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nb/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nb/strings.xml
new file mode 100644
index 0000000..e1ff863
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nb/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperiment med tynn navigasjonsrad"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nl/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nl/strings.xml
new file mode 100644
index 0000000..01190bc
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experiment voor smalle navigatiebalk"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pl/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pl/strings.xml
new file mode 100644
index 0000000..1742aad
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperyment z wąskim paskiem nawigacyjnym"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..22194b7
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rBR/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experimento de barra de navegação fina"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..f6c0309
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rPT/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experiência de barra de navegação fina"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt/strings.xml
new file mode 100644
index 0000000..22194b7
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experimento de barra de navegação fina"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ro/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ro/strings.xml
new file mode 100644
index 0000000..e1655f2
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ro/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experiment cu bară de navigare subțire"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ru/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ru/strings.xml
new file mode 100644
index 0000000..cac66dc
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ru/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Узкая панель навигации (эксперимент)"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml
new file mode 100644
index 0000000..6a1ce41
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Úzky navigačný panel (experiment)"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sl/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sl/strings.xml
new file mode 100644
index 0000000..beab7b6
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Preizkus z vitko vrstico za krmarjenje"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sq/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sq/strings.xml
new file mode 100644
index 0000000..b7a28d5
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sq/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperimenti i shiritit të hollë të navigimit"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sr/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sr/strings.xml
new file mode 100644
index 0000000..048f649
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Експеримент са танком траком за навигацију"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sv/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sv/strings.xml
new file mode 100644
index 0000000..b94438f
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sv/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Experimentellt tunt navigeringsfält"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sw/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sw/strings.xml
new file mode 100644
index 0000000..3a5a73c
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sw/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Jaribio la Sehemu ya Viungo Muhimu Inayoweza Kupunguzwa"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-th/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-th/strings.xml
new file mode 100644
index 0000000..945297b
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-th/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"การทดสอบแถบนำทางแบบบาง"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tl/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tl/strings.xml
new file mode 100644
index 0000000..0c8087c
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Eksperimentong Slim na Navigation Bar"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tr/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tr/strings.xml
new file mode 100644
index 0000000..a3ca754
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"İnce Gezinme Çubuğu Denemesi"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uk/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uk/strings.xml
new file mode 100644
index 0000000..656c4a9
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Експеримент із тонкою панеллю навігації"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ur/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ur/strings.xml
new file mode 100644
index 0000000..bcd6bc3
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ur/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"سلم نیویگیشن بار کا تجربہ"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uz/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uz/strings.xml
new file mode 100644
index 0000000..0d40981
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uz/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Ingichka navigatsiya paneli tajribasi"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-vi/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-vi/strings.xml
new file mode 100644
index 0000000..dad56b4
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-vi/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Thử nghiệm thanh điều hướng mỏng"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..b2602e0
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rCN/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"精简导航栏实验"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..d5259d3
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rHK/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"精簡導覽列實驗"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..6586a57
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rTW/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"細長導覽列實驗"</string>
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zu/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zu/strings.xml
new file mode 100644
index 0000000..12d04d3
--- /dev/null
+++ b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2018, 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"Ukuhlolwa kwebha yokuzula encane"</string>
+</resources>
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index da1fee3..a947ea1 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -2832,7 +2832,7 @@
     // ACTION: Logs the end to end time taken by all provisioning tasks.
     PROVISIONING_TOTAL_TASK_TIME_MS = 627;
 
-    // OPEN: Settings > Privacy
+    // OPEN: Settings > Security
     // CATEGORY: SETTINGS
     // OS: O
     ENTERPRISE_PRIVACY_SETTINGS = 628;
@@ -6569,6 +6569,21 @@
     // OS: Q
     LOCK_SCREEN_NOTIFICATION_CONTENT = 1584;
 
+    // ConfirmDeviceCredentials > BiometricPrompt
+    // CATEGORY: SETTINGS
+    // OS: Q
+    BIOMETRIC_FRAGMENT = 1585;
+
+    // OPEN: Biometric Enrollment (android.settings.BIOMETRIC_ENROLL action intent)
+    // CATEGORY: SETTINGS
+    // OS: Q
+    BIOMETRIC_ENROLL_ACTIVITY = 1586;
+
+    // OPEN: Settings > Privacy
+    // CATEGORY: SETTINGS
+    // OS: Q
+    TOP_LEVEL_PRIVACY = 1587;
+
     // ---- End Q Constants, all Q constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index ebd9e77..6eba914 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -457,13 +457,12 @@
         final int interrogatingPid = Binder.getCallingPid();
         callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
                 interrogatingPid, interrogatingTid);
-        final int callingUid = Binder.getCallingUid();
         final long identityToken = Binder.clearCallingIdentity();
         try {
             connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId,
                     viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags,
                     interrogatingPid, interrogatingTid, spec);
-            return mSecurityPolicy.computeValidReportedPackages(callingUid,
+            return mSecurityPolicy.computeValidReportedPackages(
                     connection.getPackageName(), connection.getUid());
         } catch (RemoteException re) {
             if (DEBUG) {
@@ -514,13 +513,12 @@
         final int interrogatingPid = Binder.getCallingPid();
         callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
                 interrogatingPid, interrogatingTid);
-        final int callingUid = Binder.getCallingUid();
         final long identityToken = Binder.clearCallingIdentity();
         try {
             connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId,
                     text, partialInteractiveRegion, interactionId, callback, mFetchFlags,
                     interrogatingPid, interrogatingTid, spec);
-            return mSecurityPolicy.computeValidReportedPackages(callingUid,
+            return mSecurityPolicy.computeValidReportedPackages(
                     connection.getPackageName(), connection.getUid());
         } catch (RemoteException re) {
             if (DEBUG) {
@@ -571,13 +569,12 @@
         final int interrogatingPid = Binder.getCallingPid();
         callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
                 interrogatingPid, interrogatingTid);
-        final int callingUid = Binder.getCallingUid();
         final long identityToken = Binder.clearCallingIdentity();
         try {
             connection.getRemote().findAccessibilityNodeInfoByAccessibilityId(
                     accessibilityNodeId, partialInteractiveRegion, interactionId, callback,
                     mFetchFlags | flags, interrogatingPid, interrogatingTid, spec, arguments);
-            return mSecurityPolicy.computeValidReportedPackages(callingUid,
+            return mSecurityPolicy.computeValidReportedPackages(
                     connection.getPackageName(), connection.getUid());
         } catch (RemoteException re) {
             if (DEBUG) {
@@ -628,13 +625,12 @@
         final int interrogatingPid = Binder.getCallingPid();
         callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
                 interrogatingPid, interrogatingTid);
-        final int callingUid = Binder.getCallingUid();
         final long identityToken = Binder.clearCallingIdentity();
         try {
             connection.getRemote().findFocus(accessibilityNodeId, focusType,
                     partialInteractiveRegion, interactionId, callback, mFetchFlags,
                     interrogatingPid, interrogatingTid, spec);
-            return mSecurityPolicy.computeValidReportedPackages(callingUid,
+            return mSecurityPolicy.computeValidReportedPackages(
                     connection.getPackageName(), connection.getUid());
         } catch (RemoteException re) {
             if (DEBUG) {
@@ -684,13 +680,12 @@
         final int interrogatingPid = Binder.getCallingPid();
         callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
                 interrogatingPid, interrogatingTid);
-        final int callingUid = Binder.getCallingUid();
         final long identityToken = Binder.clearCallingIdentity();
         try {
             connection.getRemote().focusSearch(accessibilityNodeId, direction,
                     partialInteractiveRegion, interactionId, callback, mFetchFlags,
                     interrogatingPid, interrogatingTid, spec);
-            return mSecurityPolicy.computeValidReportedPackages(callingUid,
+            return mSecurityPolicy.computeValidReportedPackages(
                     connection.getPackageName(), connection.getUid());
         } catch (RemoteException re) {
             if (DEBUG) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index fbceade..5c189ce 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -348,11 +348,24 @@
     }
 
     boolean getBindInstantServiceAllowed(int userId) {
-        return  mSecurityPolicy.getBindInstantServiceAllowed(userId);
+        final UserState userState = getUserState(userId);
+        if (userState == null) return false;
+        return userState.getBindInstantServiceAllowed();
     }
 
     void setBindInstantServiceAllowed(int userId, boolean allowed) {
-        mSecurityPolicy.setBindInstantServiceAllowed(userId, allowed);
+        UserState userState;
+        synchronized (mLock) {
+            userState = getUserState(userId);
+            if (userState == null) {
+                if (!allowed) {
+                    return;
+                }
+                userState = new UserState(userId);
+                mUserStates.put(userId, userState);
+            }
+        }
+        userState.setBindInstantServiceAllowed(allowed);
     }
 
     private void registerBroadcastReceivers() {
@@ -1316,7 +1329,7 @@
                 | PackageManager.MATCH_DIRECT_BOOT_AWARE
                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
-        if (userState.mBindInstantServiceAllowed) {
+        if (userState.getBindInstantServiceAllowed()) {
             flags |= PackageManager.MATCH_INSTANT;
         }
 
@@ -1659,22 +1672,23 @@
                 client -> client.setState(clientState)));
     }
 
-    private void scheduleNotifyClientsOfServicesStateChange(UserState userState) {
+    private void scheduleNotifyClientsOfServicesStateChangeLocked(UserState userState) {
+        updateRecommendedUiTimeoutLocked(userState);
         mMainHandler.sendMessage(obtainMessage(
                 AccessibilityManagerService::sendServicesStateChanged,
-                this, userState.mUserClients));
+                this, userState.mUserClients, getRecommendedTimeoutMillisLocked(userState)));
     }
 
     private void sendServicesStateChanged(
-            RemoteCallbackList<IAccessibilityManagerClient> userClients) {
-        notifyClientsOfServicesStateChange(mGlobalClients);
-        notifyClientsOfServicesStateChange(userClients);
+            RemoteCallbackList<IAccessibilityManagerClient> userClients, long uiTimeout) {
+        notifyClientsOfServicesStateChange(mGlobalClients, uiTimeout);
+        notifyClientsOfServicesStateChange(userClients, uiTimeout);
     }
 
     private void notifyClientsOfServicesStateChange(
-            RemoteCallbackList<IAccessibilityManagerClient> clients) {
+            RemoteCallbackList<IAccessibilityManagerClient> clients, long uiTimeout) {
         clients.broadcast(ignoreRemoteException(
-                client -> client.notifyServicesStateChanged()));
+                client -> client.notifyServicesStateChanged(uiTimeout)));
     }
 
     private void scheduleUpdateInputFilter(UserState userState) {
@@ -1688,24 +1702,6 @@
                 this, userState));
     }
 
-    private void scheduleSetAllClientsMinimumUiTimeout(UserState userState) {
-        mMainHandler.sendMessage(obtainMessage(
-                AccessibilityManagerService::sendMinimumUiTimeoutChanged,
-                this, userState.mUserClients, userState.mMinimumUiTimeout));
-    }
-
-    private void sendMinimumUiTimeoutChanged(
-            RemoteCallbackList<IAccessibilityManagerClient> userClients, int uiTimeout) {
-        notifyClientsOfServicesMinimumUiTimeoutChange(mGlobalClients, uiTimeout);
-        notifyClientsOfServicesMinimumUiTimeoutChange(userClients, uiTimeout);
-    }
-
-    private void notifyClientsOfServicesMinimumUiTimeoutChange(
-            RemoteCallbackList<IAccessibilityManagerClient> clients, int uiTimeout) {
-        clients.broadcast(ignoreRemoteException(
-                client -> client.setMinimumUiTimeout(uiTimeout)));
-    }
-
     private void updateInputFilter(UserState userState) {
         if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) return;
 
@@ -1839,7 +1835,6 @@
         scheduleUpdateClientsIfNeededLocked(userState);
         updateRelevantEventsLocked(userState);
         updateAccessibilityButtonTargetsLocked(userState);
-        updateMinimumUiTimeoutLocked(userState);
     }
 
     private void updateAccessibilityFocusBehaviorLocked(UserState userState) {
@@ -1958,7 +1953,7 @@
         somethingChanged |= readAutoclickEnabledSettingLocked(userState);
         somethingChanged |= readAccessibilityShortcutSettingLocked(userState);
         somethingChanged |= readAccessibilityButtonSettingsLocked(userState);
-        somethingChanged |= readUserMinimumUiTimeoutSettingsLocked(userState);
+        somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
         return somethingChanged;
     }
 
@@ -2068,7 +2063,9 @@
                 && componentNameToEnable.equals(userState.mServiceToEnableWithShortcut)) {
             return false;
         }
+
         userState.mServiceToEnableWithShortcut = componentNameToEnable;
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
         return true;
     }
 
@@ -2103,17 +2100,20 @@
         return true;
     }
 
-    private boolean readUserMinimumUiTimeoutSettingsLocked(UserState userState) {
-        final boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED, 0,
-                userState.mUserId) == 1;
-        final int timeout = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS, 0,
+    private boolean readUserRecommendedUiTimeoutSettingsLocked(UserState userState) {
+        final int nonInteractiveUiTimeout = Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS, 0,
                 userState.mUserId);
-        if (enabled != userState.mUserMinimumUiTimeoutEnabled
-                || timeout != userState.mUserMinimumUiTimeout) {
-            userState.mUserMinimumUiTimeoutEnabled = enabled;
-            userState.mUserMinimumUiTimeout = timeout;
+        final int interactiveUiTimeout = Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, 0,
+                userState.mUserId);
+        if (nonInteractiveUiTimeout != userState.mUserNonInteractiveUiTimeout
+                || interactiveUiTimeout != userState.mUserInteractiveUiTimeout) {
+            userState.mUserNonInteractiveUiTimeout = nonInteractiveUiTimeout;
+            userState.mUserInteractiveUiTimeout = interactiveUiTimeout;
+            scheduleNotifyClientsOfServicesStateChangeLocked(userState);
             return true;
         }
         return false;
@@ -2285,25 +2285,33 @@
         }
     }
 
-    private void updateMinimumUiTimeoutLocked(UserState userState) {
-        int newUiTimeout = 0;
-        if (userState.mUserMinimumUiTimeoutEnabled) {
-            newUiTimeout = userState.mUserMinimumUiTimeout;
-        } else {
+    private void updateRecommendedUiTimeoutLocked(UserState userState) {
+        int newNonInteractiveUiTimeout = userState.mUserNonInteractiveUiTimeout;
+        int newInteractiveUiTimeout = userState.mUserInteractiveUiTimeout;
+        // read from a11y services if user does not specify value
+        if (newNonInteractiveUiTimeout == 0 || newInteractiveUiTimeout == 0) {
+            int serviceNonInteractiveUiTimeout = 0;
+            int serviceInteractiveUiTimeout = 0;
             final List<AccessibilityServiceConnection> services = userState.mBoundServices;
-            final int numServices = services.size();
-            for (int i = 0; i < numServices; i++) {
-                final int serviceUiTimeout = services.get(i).getServiceInfo()
-                        .getMinimumUiTimeoutMillis();
-                if (newUiTimeout < serviceUiTimeout) {
-                    newUiTimeout = serviceUiTimeout;
+            for (int i = 0; i < services.size(); i++) {
+                int timeout = services.get(i).getServiceInfo().getInteractiveUiTimeoutMillis();
+                if (serviceInteractiveUiTimeout < timeout) {
+                    serviceInteractiveUiTimeout = timeout;
+                }
+                timeout = services.get(i).getServiceInfo().getNonInteractiveUiTimeoutMillis();
+                if (serviceNonInteractiveUiTimeout < timeout) {
+                    serviceNonInteractiveUiTimeout = timeout;
                 }
             }
+            if (newNonInteractiveUiTimeout == 0) {
+                newNonInteractiveUiTimeout = serviceNonInteractiveUiTimeout;
+            }
+            if (newInteractiveUiTimeout == 0) {
+                newInteractiveUiTimeout = serviceInteractiveUiTimeout;
+            }
         }
-        if (newUiTimeout != userState.mMinimumUiTimeout) {
-            userState.mMinimumUiTimeout = newUiTimeout;
-            scheduleSetAllClientsMinimumUiTimeout(userState);
-        }
+        userState.mNonInteractiveUiTimeout = newNonInteractiveUiTimeout;
+        userState.mInteractiveUiTimeout = newInteractiveUiTimeout;
     }
 
     @GuardedBy("mLock")
@@ -2343,10 +2351,10 @@
     @Override
     public void performAccessibilityShortcut() {
         if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
-                && (mContext.checkCallingPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+                && (mContext.checkCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
                 != PackageManager.PERMISSION_GRANTED)) {
             throw new SecurityException(
-                    "performAccessibilityShortcut requires the WRITE_SECURE_SETTINGS permission");
+                    "performAccessibilityShortcut requires the MANAGE_ACCESSIBILITY permission");
         }
         final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
@@ -2381,6 +2389,19 @@
         }
     };
 
+    @Override
+    public String getAccessibilityShortcutService() {
+        if (mContext.checkCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "getAccessibilityShortcutService requires the MANAGE_ACCESSIBILITY permission");
+        }
+        synchronized(mLock) {
+            final UserState userState = getUserStateLocked(mCurrentUserId);
+            return userState.mServiceToEnableWithShortcut.flattenToString();
+        }
+    }
+
     /**
      * Enables accessibility service specified by {@param componentName} for the {@param userId}.
      */
@@ -2445,19 +2466,24 @@
     }
 
     /**
-     * Get the minimum timeout for changes to the UI needed by this user. Controls should remain
-     * on the screen for at least this long to give users time to react.
+     * Get the recommended timeout of interactive controls and non-interactive controls.
      *
-     * @return The minimum timeout for the current user in milliseconds.
+     * @return A long for pair of {@code int}s. First integer for interactive one, and second
+     * integer for non-interactive one.
      */
     @Override
-    public int getMinimumUiTimeout() {
+    public long getRecommendedTimeoutMillis() {
         synchronized(mLock) {
             final UserState userState = getCurrentUserStateLocked();
-            return userState.mMinimumUiTimeout;
+            return getRecommendedTimeoutMillisLocked(userState);
         }
     }
 
+    private long getRecommendedTimeoutMillisLocked(UserState userState) {
+        return IntPair.of(userState.mInteractiveUiTimeout,
+                userState.mNonInteractiveUiTimeout);
+    }
+
     @Override
     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
@@ -2475,7 +2501,8 @@
                 pw.append(", navBarMagnificationEnabled="
                         + userState.mIsNavBarMagnificationEnabled);
                 pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled);
-                pw.append(", minimumUiTimeout=" + userState.mMinimumUiTimeout);
+                pw.append(", nonInteractiveUiTimeout=" + userState.mNonInteractiveUiTimeout);
+                pw.append(", interactiveUiTimeout=" + userState.mInteractiveUiTimeout);
                 if (mUiAutomationManager.isUiAutomationRunningLocked()) {
                     pw.append(", ");
                     mUiAutomationManager.dumpUiAutomationService(fd, pw, args);
@@ -2791,7 +2818,7 @@
         AccessibilityManagerService.UserState userState = getUserStateLocked(mCurrentUserId);
         onUserStateChangedLocked(userState);
         if (serviceInfoChanged) {
-            scheduleNotifyClientsOfServicesStateChange(userState);
+            scheduleNotifyClientsOfServicesStateChangeLocked(userState);
         }
     }
 
@@ -3143,9 +3170,15 @@
             return packageNames[0];
         }
 
-        String[] computeValidReportedPackages(int callingUid,
-                String targetPackage, int targetUid) {
-            if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
+        /**
+         * Get a list of package names an app may report, including any widget packages it owns.
+         *
+         * @param targetPackage The known valid target package
+         * @param targetUid The uid of the target app
+         * @return
+         */
+        String[] computeValidReportedPackages(String targetPackage, int targetUid) {
+            if (UserHandle.getAppId(targetUid) == Process.SYSTEM_UID) {
                 // Empty array means any package is Okay
                 return EmptyArray.STRING;
             }
@@ -3169,32 +3202,6 @@
             return uidPackages;
         }
 
-        private boolean getBindInstantServiceAllowed(int userId) {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
-                    "getBindInstantServiceAllowed");
-            UserState state = mUserStates.get(userId);
-            return (state != null) && state.mBindInstantServiceAllowed;
-        }
-
-        private void setBindInstantServiceAllowed(int userId, boolean allowed) {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
-                    "setBindInstantServiceAllowed");
-            UserState state = mUserStates.get(userId);
-            if (state == null) {
-                if (!allowed) {
-                    return;
-                }
-                state = new UserState(userId);
-                mUserStates.put(userId, state);
-            }
-            if (state.mBindInstantServiceAllowed != allowed) {
-                state.mBindInstantServiceAllowed = allowed;
-                onUserStateChangedLocked(state);
-            }
-        }
-
         public void clearWindowsLocked() {
             List<WindowInfo> windows = Collections.emptyList();
             final int activeWindowId = mActiveWindowId;
@@ -3786,7 +3793,8 @@
         public ComponentName mServiceToEnableWithShortcut;
 
         public int mLastSentClientState = -1;
-        public int mMinimumUiTimeout = 0;
+        public int mNonInteractiveUiTimeout = 0;
+        public int mInteractiveUiTimeout = 0;
 
         private int mSoftKeyboardShowMode = 0;
 
@@ -3801,10 +3809,10 @@
         public boolean mIsPerformGesturesEnabled;
         public boolean mIsFilterKeyEventsEnabled;
         public boolean mAccessibilityFocusOnlyInActiveWindow;
-        public boolean mUserMinimumUiTimeoutEnabled;
-        public int mUserMinimumUiTimeout;
+        public int mUserNonInteractiveUiTimeout;
+        public int mUserInteractiveUiTimeout;
 
-        public boolean mBindInstantServiceAllowed;
+        private boolean mBindInstantServiceAllowed;
 
         public UserState(int userId) {
             mUserId = userId;
@@ -3842,8 +3850,9 @@
             // Clear event management state.
             mLastSentClientState = -1;
 
-            // clear minimum ui timeout
-            mMinimumUiTimeout = 0;
+            // clear UI timeout
+            mNonInteractiveUiTimeout = 0;
+            mInteractiveUiTimeout = 0;
 
             // Clear state persisted in settings.
             mEnabledServices.clear();
@@ -3854,8 +3863,8 @@
             mServiceAssignedToAccessibilityButton = null;
             mIsNavBarMagnificationAssignedToAccessibilityButton = false;
             mIsAutoclickEnabled = false;
-            mUserMinimumUiTimeoutEnabled = false;
-            mUserMinimumUiTimeout = 0;
+            mUserNonInteractiveUiTimeout = 0;
+            mUserInteractiveUiTimeout = 0;
         }
 
         public void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
@@ -3863,7 +3872,7 @@
                 serviceConnection.onAdded();
                 mBoundServices.add(serviceConnection);
                 mComponentNameToServiceMap.put(serviceConnection.mComponentName, serviceConnection);
-                scheduleNotifyClientsOfServicesStateChange(this);
+                scheduleNotifyClientsOfServicesStateChangeLocked(this);
             }
         }
 
@@ -3889,7 +3898,7 @@
                 AccessibilityServiceConnection boundClient = mBoundServices.get(i);
                 mComponentNameToServiceMap.put(boundClient.mComponentName, boundClient);
             }
-            scheduleNotifyClientsOfServicesStateChange(this);
+            scheduleNotifyClientsOfServicesStateChangeLocked(this);
         }
 
         /**
@@ -4036,6 +4045,24 @@
                     Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0)
                     & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
         }
+
+        public boolean getBindInstantServiceAllowed() {
+            synchronized (mLock) {
+                return mBindInstantServiceAllowed;
+            }
+        }
+
+        public void setBindInstantServiceAllowed(boolean allowed) {
+            synchronized (mLock) {
+                mContext.enforceCallingOrSelfPermission(
+                        Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
+                        "setBindInstantServiceAllowed");
+                if (allowed) {
+                    mBindInstantServiceAllowed = allowed;
+                    onUserStateChangedLocked(this);
+                }
+            }
+        }
     }
 
     private final class AccessibilityContentObserver extends ContentObserver {
@@ -4082,11 +4109,11 @@
         private final Uri mAccessibilityButtonComponentIdUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
 
-        private final Uri mUserMinimumUiTimeoutEnabledUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_ENABLED);
+        private final Uri mUserNonInteractiveUiTimeoutUri = Settings.Secure.getUriFor(
+                Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS);
 
-        private final Uri mUserMinimumUiTimeoutUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_MINIMUM_UI_TIMEOUT_MS);
+        private final Uri mUserInteractiveUiTimeoutUri = Settings.Secure.getUriFor(
+                Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS);
 
         public AccessibilityContentObserver(Handler handler) {
             super(handler);
@@ -4123,9 +4150,9 @@
             contentResolver.registerContentObserver(
                     mAccessibilityButtonComponentIdUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
-                    mUserMinimumUiTimeoutEnabledUri, false, this, UserHandle.USER_ALL);
+                    mUserNonInteractiveUiTimeoutUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
-                    mUserMinimumUiTimeoutUri, false, this, UserHandle.USER_ALL);
+                    mUserInteractiveUiTimeoutUri, false, this, UserHandle.USER_ALL);
         }
 
         @Override
@@ -4176,11 +4203,9 @@
                     if (readAccessibilityButtonSettingsLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
-                } else if (mUserMinimumUiTimeoutEnabledUri.equals(uri)
-                        || mUserMinimumUiTimeoutUri.equals(uri)) {
-                    if (readUserMinimumUiTimeoutSettingsLocked(userState)) {
-                        updateMinimumUiTimeoutLocked(userState);
-                    }
+                } else if (mUserNonInteractiveUiTimeoutUri.equals(uri)
+                        || mUserInteractiveUiTimeoutUri.equals(uri)) {
+                    readUserRecommendedUiTimeoutSettingsLocked(userState);
                 }
             }
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 2396ded..86132a8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -92,7 +92,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE;
-            if (userState.mBindInstantServiceAllowed) {
+            if (userState.getBindInstantServiceAllowed()) {
                 flags |= Context.BIND_ALLOW_INSTANT;
             }
             if (mService == null && mContext.bindServiceAsUser(
diff --git a/services/art-profile b/services/art-profile
index 742ca1c..bdd49de 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -2271,6 +2271,7 @@
 HPLcom/android/server/wm/DisplayContent;->prepareSurfaces()V
 HPLcom/android/server/wm/DisplayContent;->resetAnimationBackgroundAnimator()V
 HPLcom/android/server/wm/DisplayContent;->skipTraverseChild(Lcom/android/server/wm/WindowContainer;)Z
+HPLcom/android/server/wm/DisplayContent;->updateOrientationFromAppTokens(Z)Z
 HPLcom/android/server/wm/DisplayContent;->updateTouchExcludeRegion()V
 HPLcom/android/server/wm/DockedStackDividerController;->isResizing()Z
 HPLcom/android/server/wm/DragDropController;->dragDropActiveLocked()Z
@@ -2451,7 +2452,6 @@
 HPLcom/android/server/wm/WindowManagerService;->resetPriorityAfterLockedSection()V
 HPLcom/android/server/wm/WindowManagerService;->scheduleAnimationLocked()V
 HPLcom/android/server/wm/WindowManagerService;->traceStateLocked(Ljava/lang/String;)V
-HPLcom/android/server/wm/WindowManagerService;->updateOrientationFromAppTokensLocked(IZ)Z
 HPLcom/android/server/wm/WindowManagerService;->windowForClientLocked(Lcom/android/server/wm/Session;Landroid/os/IBinder;Z)Lcom/android/server/wm/WindowState;
 HPLcom/android/server/wm/WindowManagerThreadPriorityBooster;->boost()V
 HPLcom/android/server/wm/WindowManagerThreadPriorityBooster;->reset()V
@@ -18137,6 +18137,7 @@
 PLcom/android/server/wm/DisplayContent;->updateBounds()V
 PLcom/android/server/wm/DisplayContent;->updateDisplayAndOrientation(I)Landroid/view/DisplayInfo;
 PLcom/android/server/wm/DisplayContent;->updateDisplayInfo()V
+PLcom/android/server/wm/DisplayContent;->updateOrientationFromAppTokens()Z
 PLcom/android/server/wm/DisplayContent;->updateRotationUnchecked()Z
 PLcom/android/server/wm/DisplayContent;->updateRotationUnchecked(Z)Z
 PLcom/android/server/wm/DisplayContent;->updateStackBoundsAfterConfigChange(Ljava/util/List;)V
@@ -18906,7 +18907,6 @@
 PLcom/android/server/wm/WindowManagerService;->updateNonSystemOverlayWindowsVisibilityIfNeeded(Lcom/android/server/wm/WindowState;Z)V
 PLcom/android/server/wm/WindowManagerService;->updateOrientationFromAppTokens(Landroid/content/res/Configuration;Landroid/os/IBinder;I)Landroid/content/res/Configuration;
 PLcom/android/server/wm/WindowManagerService;->updateOrientationFromAppTokens(Landroid/content/res/Configuration;Landroid/os/IBinder;IZ)Landroid/content/res/Configuration;
-PLcom/android/server/wm/WindowManagerService;->updateOrientationFromAppTokensLocked(I)Z
 PLcom/android/server/wm/WindowManagerService;->updateOrientationFromAppTokensLocked(Landroid/content/res/Configuration;Landroid/os/IBinder;IZ)Landroid/content/res/Configuration;
 PLcom/android/server/wm/WindowManagerService;->updatePointerIcon(Landroid/view/IWindow;)V
 PLcom/android/server/wm/WindowManagerService;->updateRotation(ZZ)V
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index d3842b7..4205ac7 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -21,14 +21,11 @@
 
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sFullScreenMode;
-import static com.android.server.autofill.Helper.sPartitionMaxCount;
-import static com.android.server.autofill.Helper.sVisibleDatasetsMaxCount;
 import static com.android.server.autofill.Helper.sVerbose;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityThread;
 import android.content.BroadcastReceiver;
@@ -38,13 +35,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.graphics.Rect;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcelable;
 import android.os.RemoteCallback;
@@ -53,7 +47,6 @@
 import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.service.autofill.FillEventHistory;
 import android.service.autofill.UserData;
@@ -63,7 +56,6 @@
 import android.util.LocalLog;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillManagerInternal;
@@ -73,14 +65,12 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
+import com.android.server.AbstractMasterSystemService;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
-import com.android.server.SystemService;
 import com.android.server.autofill.ui.AutoFillUI;
 
 import java.io.FileDescriptor;
@@ -98,10 +88,13 @@
  * {@link AutofillManagerServiceImpl} per user; the real work is done by
  * {@link AutofillManagerServiceImpl} itself.
  */
-public final class AutofillManagerService extends SystemService {
+public final class AutofillManagerService
+        extends AbstractMasterSystemService<AutofillManagerServiceImpl> {
 
     private static final String TAG = "AutofillManagerService";
 
+    private static final Object sLock = AutofillManagerService.class;
+
     static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
 
     private static final char COMPAT_PACKAGE_DELIMITER = ':';
@@ -109,27 +102,28 @@
     private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
     private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
 
-    private final Context mContext;
+
+    /**
+     * Maximum number of partitions that can be allowed in a session.
+     *
+     * <p>Can be modified using {@code cmd autofill set max_partitions} or through
+     * {@link android.provider.Settings.Global#AUTOFILL_MAX_PARTITIONS_SIZE}.
+     */
+    @GuardedBy("sLock")
+    private static int sPartitionMaxCount = AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE;
+
+    /**
+     * Maximum number of visible datasets in the dataset picker UI, or {@code 0} to use default
+     * value from resources.
+     *
+     * <p>Can be modified using {@code cmd autofill set max_visible_datasets} or through
+     * {@link android.provider.Settings.Global#AUTOFILL_MAX_VISIBLE_DATASETS}.
+     */
+    @GuardedBy("sLock")
+    private static int sVisibleDatasetsMaxCount = 0;
+
     private final AutoFillUI mUi;
 
-    private final Object mLock = new Object();
-
-    /**
-     * Cache of {@link AutofillManagerServiceImpl} per user id.
-     * <p>
-     * It has to be mapped by user id because the same current user could have simultaneous sessions
-     * associated to different user profiles (for example, in a multi-window environment or when
-     * device has work profiles).
-     */
-    @GuardedBy("mLock")
-    private SparseArray<AutofillManagerServiceImpl> mServicesCache = new SparseArray<>();
-
-    /**
-     * Users disabled due to {@link UserManager} restrictions.
-     */
-    @GuardedBy("mLock")
-    private final SparseBooleanArray mDisabledUsers = new SparseBooleanArray();
-
     private final LocalLog mRequestsHistory = new LocalLog(20);
     private final LocalLog mUiLatencyHistory = new LocalLog(20);
     private final LocalLog mWtfHistory = new LocalLog(50);
@@ -148,22 +142,19 @@
                 // beneath it is brought back to top. Ideally, we should just hide the UI and
                 // bring it back when the activity resumes.
                 synchronized (mLock) {
-                    for (int i = 0; i < mServicesCache.size(); i++) {
-                        mServicesCache.valueAt(i).destroyFinishedSessionsLocked();
-                    }
+                    visitServicesLocked((s) -> s.destroyFinishedSessionsLocked());
                 }
-
                 mUi.hideAll(null);
             }
         }
     };
 
+    // TODO(b/117779333): move to superclass / create super-class for ShellCommand
     @GuardedBy("mLock")
     private boolean mAllowInstantService;
 
     public AutofillManagerService(Context context) {
-        super(context);
-        mContext = context;
+        super(context, UserManager.DISALLOW_AUTOFILL);
         mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
 
         setLogLevelFromSettings();
@@ -172,199 +163,91 @@
 
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        mContext.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
+        context.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
+    }
 
-        // Hookup with UserManager to disable service when necessary.
-        final UserManager um = context.getSystemService(UserManager.class);
-        final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
-        final List<UserInfo> users = um.getUsers();
-        for (int i = 0; i < users.size(); i++) {
-            final int userId = users.get(i).id;
-            final boolean disabled = umi.getUserRestriction(userId, UserManager.DISALLOW_AUTOFILL);
-            if (disabled) {
-                Slog.i(TAG, "Disabling Autofill for user " + userId);
-                mDisabledUsers.put(userId, disabled);
-            }
+    @Override // from MasterSystemService
+    protected String getServiceSettingsProperty() {
+        return Settings.Secure.AUTOFILL_SERVICE;
+    }
+
+    @Override // from MasterSystemService
+    protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
+            @NonNull ContentObserver observer) {
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, observer,
+                UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.AUTOFILL_LOGGING_LEVEL), false, observer,
+                UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE), false, observer,
+                UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, observer,
+                UserHandle.USER_ALL);
+    }
+
+    @Override // from MasterSystemService
+    protected void onSettingsChanged(int userId, @NonNull String property) {
+        switch (property) {
+            case Settings.Global.AUTOFILL_LOGGING_LEVEL:
+                setLogLevelFromSettings();
+                break;
+            case Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE:
+                setMaxPartitionsFromSettings();
+                break;
+            case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
+                setMaxVisibleDatasetsFromSettings();
+                break;
+            default:
+                Slog.w(TAG, "Unexpected property (" + property + "); updating cache instead");
+                // fall through
+            case Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
+                synchronized (mLock) {
+                    updateCachedServiceLocked(userId);
+                }
         }
-        umi.addUserRestrictionsListener((userId, newRestrictions, prevRestrictions) -> {
-            final boolean disabledNow =
-                    newRestrictions.getBoolean(UserManager.DISALLOW_AUTOFILL, false);
-            synchronized (mLock) {
-                final boolean disabledBefore = mDisabledUsers.get(userId);
-                if (disabledBefore == disabledNow) {
-                    // Nothing changed, do nothing.
-                    if (sDebug) {
-                        Slog.d(TAG, "Autofill restriction did not change for user " + userId);
-                        return;
-                    }
-                }
-                Slog.i(TAG, "Updating Autofill for user " + userId + ": disabled=" + disabledNow);
-                mDisabledUsers.put(userId, disabledNow);
-                updateCachedServiceLocked(userId, disabledNow);
-            }
-        });
-        startTrackingPackageChanges();
     }
 
-    private void startTrackingPackageChanges() {
-        PackageMonitor monitor = new PackageMonitor() {
-            @Override
-            public void onSomePackagesChanged() {
-                synchronized (mLock) {
-                    updateCachedServiceLocked(getChangingUserId());
-                }
-            }
-
-            @Override
-            public void onPackageUpdateFinished(String packageName, int uid) {
-                synchronized (mLock) {
-                    final String activePackageName = getActiveAutofillServicePackageName();
-                    if (packageName.equals(activePackageName)) {
-                        removeCachedServiceLocked(getChangingUserId());
-                    } else {
-                        handlePackageUpdateLocked(packageName);
-                    }
-                }
-            }
-
-            @Override
-            public void onPackageRemoved(String packageName, int uid) {
-                synchronized (mLock) {
-                    final int userId = getChangingUserId();
-                    final AutofillManagerServiceImpl userState = peekServiceForUserLocked(userId);
-                    if (userState != null) {
-                        final ComponentName componentName = userState.getServiceComponentName();
-                        if (componentName != null) {
-                            if (packageName.equals(componentName.getPackageName())) {
-                                handleActiveAutofillServiceRemoved(userId);
-                            }
-                        }
-                    }
-                }
-            }
-
-            @Override
-            public boolean onHandleForceStop(Intent intent, String[] packages,
-                    int uid, boolean doit) {
-                synchronized (mLock) {
-                    final String activePackageName = getActiveAutofillServicePackageName();
-                    for (String pkg : packages) {
-                        if (pkg.equals(activePackageName)) {
-                            if (!doit) {
-                                return true;
-                            }
-                            removeCachedServiceLocked(getChangingUserId());
-                        } else {
-                          handlePackageUpdateLocked(pkg);
-                        }
-                    }
-                }
-                return false;
-            }
-
-            private void handleActiveAutofillServiceRemoved(int userId) {
-                removeCachedServiceLocked(userId);
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        Settings.Secure.AUTOFILL_SERVICE, null, userId);
-            }
-
-            private String getActiveAutofillServicePackageName() {
-                final int userId = getChangingUserId();
-                final AutofillManagerServiceImpl userState = peekServiceForUserLocked(userId);
-                if (userState == null) {
-                    return null;
-                }
-                final ComponentName serviceComponent = userState.getServiceComponentName();
-                if (serviceComponent == null) {
-                    return null;
-                }
-                return serviceComponent.getPackageName();
-            }
-
-            @GuardedBy("mLock")
-            private void handlePackageUpdateLocked(String packageName) {
-                final int size = mServicesCache.size();
-                for (int i = 0; i < size; i++) {
-                    mServicesCache.valueAt(i).handlePackageUpdateLocked(packageName);
-                }
-            }
-        };
-
-        // package changes
-        monitor.register(mContext, null,  UserHandle.ALL, true);
+    @Override // from MasterSystemService
+    protected AutofillManagerServiceImpl newServiceLocked(int resolvedUserId, boolean disabled) {
+        return new AutofillManagerServiceImpl(this, mLock, mRequestsHistory,
+                mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi, mAutofillCompatState,
+                disabled);
     }
 
-    @Override
+    @Override // MasterSystemService
+    protected AutofillManagerServiceImpl removeCachedServiceLocked(int userId) {
+        final AutofillManagerServiceImpl service = super.removeCachedServiceLocked(userId);
+        if (service != null) {
+            service.destroyLocked();
+            mAutofillCompatState.removeCompatibilityModeRequests(userId);
+        }
+        return service;
+    }
+
+    @Override // from MasterSystemService
+    protected void onServiceEnabledLocked(@NonNull AutofillManagerServiceImpl service, int userId) {
+        addCompatibilityModeRequestsLocked(service, userId);
+    }
+
+    @Override // from SystemService
     public void onStart() {
         publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
         publishLocalService(AutofillManagerInternal.class, mLocalService);
     }
 
-    @Override
-    public void onBootPhase(int phase) {
-        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
-            new SettingsObserver(BackgroundThread.getHandler());
-        }
-    }
-
-    @Override
-    public void onUnlockUser(int userId) {
-        synchronized (mLock) {
-            updateCachedServiceLocked(userId);
-        }
-    }
-
-    @Override
+    @Override // from SystemService
     public void onSwitchUser(int userHandle) {
         if (sDebug) Slog.d(TAG, "Hiding UI when user switched");
         mUi.hideAll(null);
     }
 
-    @Override
-    public void onCleanupUser(int userId) {
-        synchronized (mLock) {
-            removeCachedServiceLocked(userId);
-        }
-    }
-
-    /**
-     * Gets the service instance for an user.
-     *
-     * @return service instance.
-     */
-    @GuardedBy("mLock")
-    @NonNull
-    AutofillManagerServiceImpl getServiceForUserLocked(int userId) {
-        final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
-                Binder.getCallingUid(), userId, false, false, null, null);
-        AutofillManagerServiceImpl service = mServicesCache.get(resolvedUserId);
-        if (service == null) {
-            service = new AutofillManagerServiceImpl(mContext, mLock, mRequestsHistory,
-                    mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi,
-                    mAutofillCompatState, mDisabledUsers.get(resolvedUserId));
-            mServicesCache.put(userId, service);
-            addCompatibilityModeRequestsLocked(service, userId);
-        }
-        return service;
-    }
-
-    /**
-     * Peeks the service instance for a user.
-     *
-     * @return service instance or {@code null} if not already present
-     */
-    @GuardedBy("mLock")
-    @Nullable
-    AutofillManagerServiceImpl peekServiceForUserLocked(int userId) {
-        final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
-                Binder.getCallingUid(), userId, false, false, null, null);
-        return mServicesCache.get(resolvedUserId);
-    }
-
     // Called by Shell command.
     void destroySessions(int userId, IResultReceiver receiver) {
         Slog.i(TAG, "destroySessions() for userId " + userId);
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         synchronized (mLock) {
             if (userId != UserHandle.USER_ALL) {
@@ -373,10 +256,7 @@
                     service.destroySessionsLocked();
                 }
             } else {
-                final int size = mServicesCache.size();
-                for (int i = 0; i < size; i++) {
-                    mServicesCache.valueAt(i).destroySessionsLocked();
-                }
+                visitServicesLocked((s) -> s.destroySessionsLocked());
             }
         }
 
@@ -390,7 +270,7 @@
     // Called by Shell command.
     void listSessions(int userId, IResultReceiver receiver) {
         Slog.i(TAG, "listSessions() for userId " + userId);
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         final Bundle resultData = new Bundle();
         final ArrayList<String> sessions = new ArrayList<>();
@@ -402,10 +282,7 @@
                     service.listSessionsLocked(sessions);
                 }
             } else {
-                final int size = mServicesCache.size();
-                for (int i = 0; i < size; i++) {
-                    mServicesCache.valueAt(i).listSessionsLocked(sessions);
-                }
+                visitServicesLocked((s) -> s.listSessionsLocked(sessions));
             }
         }
 
@@ -420,25 +297,22 @@
     // Called by Shell command.
     void reset() {
         Slog.i(TAG, "reset()");
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         synchronized (mLock) {
-            final int size = mServicesCache.size();
-            for (int i = 0; i < size; i++) {
-                mServicesCache.valueAt(i).destroyLocked();
-            }
-            mServicesCache.clear();
+            visitServicesLocked((s) -> s.destroyLocked());
+            clearCacheLocked();
         }
     }
 
     // Called by Shell command.
     void setLogLevel(int level) {
         Slog.i(TAG, "setLogLevel(): " + level);
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         final long token = Binder.clearCallingIdentity();
         try {
-            Settings.Global.putInt(mContext.getContentResolver(),
+            Settings.Global.putInt(getContext().getContentResolver(),
                     Settings.Global.AUTOFILL_LOGGING_LEVEL, level);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -447,7 +321,7 @@
 
     private void setLogLevelFromSettings() {
         final int level = Settings.Global.getInt(
-                mContext.getContentResolver(),
+                getContext().getContentResolver(),
                 Settings.Global.AUTOFILL_LOGGING_LEVEL, AutofillManager.DEFAULT_LOGGING_LEVEL);
         boolean debug = false;
         boolean verbose = false;
@@ -465,14 +339,13 @@
                     + ", verbose=" + verbose);
         }
         synchronized (mLock) {
-            setDebugLocked(debug);
-            setVerboseLocked(verbose);
+            setLoggingLevelsLocked(debug, verbose);
         }
     }
 
     // Called by Shell command.
     int getLogLevel() {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         synchronized (mLock) {
             if (sVerbose) return AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
@@ -483,7 +356,7 @@
 
     // Called by Shell command.
     int getMaxPartitions() {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         synchronized (mLock) {
             return sPartitionMaxCount;
@@ -492,12 +365,12 @@
 
     // Called by Shell command.
     void setMaxPartitions(int max) {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxPartitions(): " + max);
 
         final long token = Binder.clearCallingIdentity();
         try {
-            Settings.Global.putInt(mContext.getContentResolver(),
+            Settings.Global.putInt(getContext().getContentResolver(),
                     Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE, max);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -505,33 +378,33 @@
     }
 
     private void setMaxPartitionsFromSettings() {
-        final int max = Settings.Global.getInt(mContext.getContentResolver(),
+        final int max = Settings.Global.getInt(getContext().getContentResolver(),
                 Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
                 AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE);
         if (sDebug) Slog.d(TAG, "setMaxPartitionsFromSettings(): " + max);
 
-        synchronized (mLock) {
+        synchronized (sLock) {
             sPartitionMaxCount = max;
         }
     }
 
     // Called by Shell command.
     int getMaxVisibleDatasets() {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
-        synchronized (mLock) {
+        synchronized (sLock) {
             return sVisibleDatasetsMaxCount;
         }
     }
 
     // Called by Shell command.
     void setMaxVisibleDatasets(int max) {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxVisibleDatasets(): " + max);
 
         final long token = Binder.clearCallingIdentity();
         try {
-            Settings.Global.putInt(mContext.getContentResolver(),
+            Settings.Global.putInt(getContext().getContentResolver(),
                     Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, max);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -539,11 +412,11 @@
     }
 
     private void setMaxVisibleDatasetsFromSettings() {
-        final int max = Settings.Global.getInt(mContext.getContentResolver(),
+        final int max = Settings.Global.getInt(getContext().getContentResolver(),
                 Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, 0);
 
         if (sDebug) Slog.d(TAG, "setMaxVisibleDatasetsFromSettings(): " + max);
-        synchronized (mLock) {
+        synchronized (sLock) {
             sVisibleDatasetsMaxCount = max;
         }
     }
@@ -551,10 +424,10 @@
     // Called by Shell command.
     void getScore(@Nullable String algorithmName, @NonNull String value1,
             @NonNull String value2, @NonNull RemoteCallback callback) {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         final FieldClassificationStrategy strategy =
-                new FieldClassificationStrategy(mContext, UserHandle.USER_CURRENT);
+                new FieldClassificationStrategy(getContext(), UserHandle.USER_CURRENT);
 
         strategy.getScores(callback, algorithmName, null,
                 Arrays.asList(AutofillValue.forText(value1)), new String[] { value2 });
@@ -562,19 +435,19 @@
 
     // Called by Shell command.
     Boolean getFullScreenMode() {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         return sFullScreenMode;
     }
 
     // Called by Shell command.
     void setFullScreenMode(@Nullable Boolean mode) {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         sFullScreenMode = mode;
     }
 
     // Called by Shell command.
     boolean getAllowInstantService() {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         synchronized (mLock) {
             return mAllowInstantService;
         }
@@ -582,59 +455,21 @@
 
     // Called by Shell command.
     void setAllowInstantService(boolean mode) {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setAllowInstantService(): " + mode);
         synchronized (mLock) {
             mAllowInstantService = mode;
         }
     }
 
-    private void setDebugLocked(boolean debug) {
+    private void setLoggingLevelsLocked(boolean debug, boolean verbose) {
         com.android.server.autofill.Helper.sDebug = debug;
         android.view.autofill.Helper.sDebug = debug;
-    }
+        this.debug = debug;
 
-    private void setVerboseLocked(boolean verbose) {
         com.android.server.autofill.Helper.sVerbose = verbose;
         android.view.autofill.Helper.sVerbose = verbose;
-    }
-
-    /**
-     * Removes a cached service for a given user.
-     */
-    @GuardedBy("mLock")
-    private void removeCachedServiceLocked(int userId) {
-        final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
-        if (service != null) {
-            mServicesCache.delete(userId);
-            service.destroyLocked();
-            mAutofillCompatState.removeCompatibilityModeRequests(userId);
-        }
-    }
-
-    /**
-     * Updates a cached service for a given user.
-     */
-    @GuardedBy("mLock")
-    private void updateCachedServiceLocked(int userId) {
-        updateCachedServiceLocked(userId, mDisabledUsers.get(userId));
-    }
-
-    /**
-     * Updates a cached service for a given user.
-     */
-    @GuardedBy("mLock")
-    private void updateCachedServiceLocked(int userId, boolean disabled) {
-        AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
-        if (service != null) {
-            service.destroySessionsLocked();
-            service.updateLocked(disabled);
-            if (!service.isEnabledLocked()) {
-                removeCachedServiceLocked(userId);
-            } else {
-                addCompatibilityModeRequestsLocked(service, userId);
-            }
-        }
+        this.verbose = verbose;
     }
 
     private void addCompatibilityModeRequestsLocked(@NonNull AutofillManagerServiceImpl service
@@ -664,7 +499,7 @@
 
     private String getWhitelistedCompatModePackagesFromSettings() {
         return Settings.Global.getString(
-                mContext.getContentResolver(),
+                getContext().getContentResolver(),
                 Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES);
     }
 
@@ -758,6 +593,24 @@
         return compatPackages;
     }
 
+    /**
+     * Gets the maximum number of partitions / fill requests.
+     */
+    public static int getPartitionMaxCount() {
+        synchronized (sLock) {
+            return sPartitionMaxCount;
+        }
+    }
+
+    /**
+     * Gets the maxium number of datasets visible in the UI.
+     */
+    public static int getVisibleDatasetsMaxCount() {
+        synchronized (sLock) {
+            return sVisibleDatasetsMaxCount;
+        }
+    }
+
     private final class LocalService extends AutofillManagerInternal {
         @Override
         public void onBackKeyPressed() {
@@ -889,20 +742,24 @@
         }
 
         private void dump(String prefix, PrintWriter pw) {
-             if (mUserSpecs == null) {
-                 pw.println("N/A");
-                 return;
-             }
-             pw.println();
-             final String prefix2 = prefix + "  ";
-             for (int i = 0; i < mUserSpecs.size(); i++) {
-                 final int user = mUserSpecs.keyAt(i);
-                 pw.print(prefix); pw.print("User: "); pw.println(user);
-                 final ArrayMap<String, PackageCompatState> perUser = mUserSpecs.valueAt(i);
-                 for (int j = 0; j < perUser.size(); j++) {
-                     final String packageName = perUser.keyAt(j);
-                     final PackageCompatState state = perUser.valueAt(j);
-                     pw.print(prefix2); pw.print(packageName); pw.print(": "); pw.println(state);
+            synchronized (mLock) {
+                if (mUserSpecs == null) {
+                    pw.println("N/A");
+                    return;
+                }
+                pw.println();
+                final String prefix2 = prefix + "  ";
+                for (int i = 0; i < mUserSpecs.size(); i++) {
+                    final int user = mUserSpecs.keyAt(i);
+                    pw.print(prefix);
+                    pw.print("User: ");
+                    pw.println(user);
+                    final ArrayMap<String, PackageCompatState> perUser = mUserSpecs.valueAt(i);
+                    for (int j = 0; j < perUser.size(); j++) {
+                        final String packageName = perUser.keyAt(j);
+                        final PackageCompatState state = perUser.valueAt(j);
+                        pw.print(prefix2); pw.print(packageName); pw.print(": "); pw.println(state);
+                    }
                 }
             }
         }
@@ -971,7 +828,7 @@
             Preconditions.checkArgument(userId == UserHandle.getUserId(getCallingUid()), "userId");
 
             try {
-                mContext.getPackageManager().getPackageInfoAsUser(packageName, 0, userId);
+                getContext().getPackageManager().getPackageInfoAsUser(packageName, 0, userId);
             } catch (PackageManager.NameNotFoundException e) {
                 throw new IllegalArgumentException(packageName + " is not a valid package", e);
             }
@@ -1139,7 +996,7 @@
 
             boolean restored = false;
             synchronized (mLock) {
-                final AutofillManagerServiceImpl service = mServicesCache.get(userId);
+                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
                     restored = service.restoreSession(sessionId, getCallingUid(), activityToken,
                             appCallback);
@@ -1216,7 +1073,7 @@
         public void isServiceSupported(int userId, @NonNull IResultReceiver receiver) {
             boolean supported = false;
             synchronized (mLock) {
-                supported = !mDisabledUsers.get(userId);
+                supported = !isDisabledLocked(userId);
             }
             send(receiver, supported);
         }
@@ -1253,7 +1110,7 @@
 
         @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
 
             boolean showHistory = true;
             boolean uiOnly = false;
@@ -1280,102 +1137,48 @@
                 return;
             }
 
-            boolean oldDebug = sDebug;
             final String prefix = "  ";
-            final String prefix2 = "    ";
+            boolean realDebug = sDebug;
+            boolean realVerbose = sVerbose;
             try {
+                sDebug = sVerbose = true;
                 synchronized (mLock) {
-                    oldDebug = sDebug;
-                    setDebugLocked(true);
-                    pw.print("Debug mode: "); pw.println(oldDebug);
-                    pw.print("Verbose mode: "); pw.println(sVerbose);
-                    pw.print("Disabled users: "); pw.println(mDisabledUsers);
+                    pw.print("sDebug: "); pw.print(realDebug);
+                    pw.print(" sVerbose: "); pw.println(realVerbose);
+                    // Dump per-user services
+                    dumpLocked("", pw);
                     pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount);
                     pw.print("Max visible datasets: "); pw.println(sVisibleDatasetsMaxCount);
                     if (sFullScreenMode != null) {
                         pw.print("Overridden full-screen mode: "); pw.println(sFullScreenMode);
                     }
                     pw.println("User data constraints: "); UserData.dumpConstraints(prefix, pw);
-                    final int size = mServicesCache.size();
-                    pw.print("Cached services: ");
-                    if (size == 0) {
-                        pw.println("none");
-                    } else {
-                        pw.println(size);
-                        for (int i = 0; i < size; i++) {
-                            pw.print("\nService at index "); pw.println(i);
-                            final AutofillManagerServiceImpl impl = mServicesCache.valueAt(i);
-                            impl.dumpLocked(prefix, pw);
-                        }
-                    }
                     mUi.dump(pw);
                     pw.print("Autofill Compat State: ");
-                    mAutofillCompatState.dump(prefix2, pw);
-                    pw.print(prefix2); pw.print("from settings: ");
+                    mAutofillCompatState.dump(prefix, pw);
+                    pw.print("from settings: ");
                     pw.println(getWhitelistedCompatModePackagesFromSettings());
                     pw.print("Allow instant service: "); pw.println(mAllowInstantService);
-                }
-                if (showHistory) {
-                    pw.println(); pw.println("Requests history:"); pw.println();
-                    mRequestsHistory.reverseDump(fd, pw, args);
-                    pw.println(); pw.println("UI latency history:"); pw.println();
-                    mUiLatencyHistory.reverseDump(fd, pw, args);
-                    pw.println(); pw.println("WTF history:"); pw.println();
-                    mWtfHistory.reverseDump(fd, pw, args);
+                    if (showHistory) {
+                        pw.println(); pw.println("Requests history:"); pw.println();
+                        mRequestsHistory.reverseDump(fd, pw, args);
+                        pw.println(); pw.println("UI latency history:"); pw.println();
+                        mUiLatencyHistory.reverseDump(fd, pw, args);
+                        pw.println(); pw.println("WTF history:"); pw.println();
+                        mWtfHistory.reverseDump(fd, pw, args);
+                    }
                 }
             } finally {
-                setDebugLocked(oldDebug);
+                sDebug = realDebug;
+                sVerbose = realVerbose;
             }
         }
 
         @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
-            (new AutofillManagerServiceShellCommand(AutofillManagerService.this)).exec(
+            new AutofillManagerServiceShellCommand(AutofillManagerService.this).exec(
                     this, in, out, err, args, callback, resultReceiver);
         }
     }
-
-    private final class SettingsObserver extends ContentObserver {
-        SettingsObserver(Handler handler) {
-            super(handler);
-            ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.AUTOFILL_SERVICE), false, this, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.AUTOFILL_LOGGING_LEVEL), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, this,
-                    UserHandle.USER_ALL);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri, int userId) {
-            if (sVerbose) Slog.v(TAG, "onChange(): uri=" + uri + ", userId=" + userId);
-            switch (uri.getLastPathSegment()) {
-                case Settings.Global.AUTOFILL_LOGGING_LEVEL:
-                    setLogLevelFromSettings();
-                    break;
-                case Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE:
-                    setMaxPartitionsFromSettings();
-                    break;
-                case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
-                    setMaxVisibleDatasetsFromSettings();
-                    break;
-                default:
-                synchronized (mLock) {
-                    updateCachedServiceLocked(userId);
-                }
-            }
-        }
-    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 14d68cb..4810355 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -25,19 +25,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
 import android.app.ActivityManagerInternal;
-import android.app.AppGlobals;
+import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.metrics.LogMaker;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -49,7 +44,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings;
 import android.service.autofill.AutofillService;
 import android.service.autofill.AutofillServiceInfo;
@@ -60,7 +54,6 @@
 import android.service.autofill.FillResponse;
 import android.service.autofill.IAutoFillService;
 import android.service.autofill.UserData;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
@@ -77,6 +70,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.AbstractPerUserSystemService;
 import com.android.server.LocalServices;
 import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
 import com.android.server.autofill.ui.AutoFillUI;
@@ -91,7 +85,8 @@
  * app's {@link IAutoFillService} implementation.
  *
  */
-final class AutofillManagerServiceImpl {
+final class AutofillManagerServiceImpl
+        extends AbstractPerUserSystemService<AutofillManagerServiceImpl> {
 
     private static final String TAG = "AutofillManagerServiceImpl";
     private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
@@ -99,9 +94,6 @@
     /** Minimum interval to prune abandoned sessions */
     private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;
 
-    private final int mUserId;
-    private final Context mContext;
-    private final Object mLock;
     private final AutoFillUI mUi;
     private final MetricsLogger mMetricsLogger = new MetricsLogger();
 
@@ -132,23 +124,11 @@
     private ArrayMap<ComponentName, Long> mDisabledActivities;
 
     /**
-     * Whether service was disabled for user due to {@link UserManager} restrictions.
-     */
-    @GuardedBy("mLock")
-    private boolean mDisabled;
-
-    /**
      * Data used for field classification.
      */
     @GuardedBy("mLock")
     private UserData mUserData;
 
-    /**
-     * Caches whether the setup completed for the current user.
-     */
-    @GuardedBy("mLock")
-    private boolean mSetupComplete;
-
     private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
 
     /**
@@ -170,116 +150,27 @@
     /** When was {@link PruneTask} last executed? */
     private long mLastPrune = 0;
 
-    AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
+    AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog requestsHistory,
             LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
             AutofillCompatState autofillCompatState, boolean disabled) {
-        mContext = context;
-        mLock = lock;
+        super(master, lock, userId);
+
         mRequestsHistory = requestsHistory;
         mUiLatencyHistory = uiLatencyHistory;
         mWtfHistory = wtfHistory;
-        mUserId = userId;
         mUi = ui;
-        mFieldClassificationStrategy = new FieldClassificationStrategy(context, userId);
+        mFieldClassificationStrategy = new FieldClassificationStrategy(getContext(), userId);
         mAutofillCompatState = autofillCompatState;
         updateLocked(disabled);
     }
 
     @GuardedBy("mLock")
-    private int getServiceUidLocked() {
-        if (mInfo == null) {
-            Slog.w(TAG,  "getServiceUidLocked(): no mInfo");
-            return -1;
-        }
-        return mInfo.getServiceInfo().applicationInfo.uid;
-    }
-
-
-    @Nullable
-    String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
-        return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
-    }
-
-    @Nullable
-    String getServicePackageName() {
-        final ComponentName serviceComponent = getServiceComponentName();
-        if (serviceComponent != null) {
-            return serviceComponent.getPackageName();
-        }
-        return null;
-    }
-
-    @Nullable
-    ComponentName getServiceComponentName() {
-        synchronized (mLock) {
-            if (mInfo == null) {
-                return null;
-            }
-            return mInfo.getServiceInfo().getComponentName();
-        }
-    }
-
-    int getTargedSdkLocked() {
-        if (mInfo == null) {
-            return 0;
-        }
-        return mInfo.getServiceInfo().applicationInfo.targetSdkVersion;
-    }
-
-    private boolean isSetupCompletedLocked() {
-        final String setupComplete = Settings.Secure.getStringForUser(
-                mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId);
-        return "1".equals(setupComplete);
-    }
-
-    private String getComponentNameFromSettings() {
-        return Settings.Secure.getStringForUser(
-                mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
-    }
-
-    @GuardedBy("mLock")
-    void updateLocked(boolean disabled) {
-        final boolean wasEnabled = isEnabledLocked();
-        if (sVerbose) {
-            Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
-                    + ", mSetupComplete= " + mSetupComplete
-                    + ", disabled=" + disabled + ", mDisabled=" + mDisabled);
-        }
-        mSetupComplete = isSetupCompletedLocked();
-        mDisabled = disabled;
-        ComponentName serviceComponent = null;
-        ServiceInfo serviceInfo = null;
-        final String componentName = getComponentNameFromSettings();
-        if (!TextUtils.isEmpty(componentName)) {
-            try {
-                serviceComponent = ComponentName.unflattenFromString(componentName);
-                serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
-                        0, mUserId);
-                if (serviceInfo == null) {
-                    Slog.e(TAG, "Bad AutofillService name: " + componentName);
-                }
-            } catch (RuntimeException | RemoteException e) {
-                Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e);
-                serviceInfo = null;
-            }
-        }
-        try {
-            if (serviceInfo != null) {
-                mInfo = new AutofillServiceInfo(mContext, serviceComponent, mUserId);
-                if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo);
-            } else {
-                mInfo = null;
-                if (sDebug) {
-                    Slog.d(TAG, "Reset component for user " + mUserId + " (" + componentName + ")");
-                }
-            }
-        } catch (Exception e) {
-            Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e);
-            mInfo = null;
-        }
-        final boolean isEnabled = isEnabledLocked();
-        if (wasEnabled != isEnabled) {
-            if (!isEnabled) {
+    @Override // from PerUserSystemService
+    protected boolean updateLocked(boolean disabled) {
+        destroySessionsLocked();
+        final boolean enabledChanged = super.updateLocked(disabled);
+        if (enabledChanged) {
+            if (!isEnabledLocked()) {
                 final int sessionCount = mSessions.size();
                 for (int i = sessionCount - 1; i >= 0; i--) {
                     final Session session = mSessions.valueAt(i);
@@ -288,6 +179,19 @@
             }
             sendStateToClients(false);
         }
+        return enabledChanged;
+    }
+
+    @Override // from PerUserSystemService
+    protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
+            throws NameNotFoundException {
+        mInfo = new AutofillServiceInfo(getContext(), serviceComponent, mUserId);
+        return mInfo.getServiceInfo();
+    }
+
+    @Nullable
+    String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
+        return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
     }
 
     @GuardedBy("mLock")
@@ -469,7 +373,7 @@
             if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) {
                 mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF,
                         componentName.getPackageName());
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                Settings.Secure.putStringForUser(getContext().getContentResolver(),
                         Settings.Secure.AUTOFILL_SERVICE, null, mUserId);
                 destroySessionsLocked();
             } else {
@@ -501,7 +405,7 @@
 
         assertCallerLocked(componentName, compatMode);
 
-        final Session newSession = new Session(this, mUi, mContext, mHandler, mUserId, mLock,
+        final Session newSession = new Session(this, mUi, getContext(), mHandler, mUserId, mLock,
                 sessionId, taskId, uid, activityToken, appCallbackToken, hasCallback,
                 mUiLatencyHistory, mWtfHistory, mInfo.getServiceInfo().getComponentName(),
                 componentName, compatMode, bindInstantServiceAllowed, flags);
@@ -515,7 +419,7 @@
      */
     private void assertCallerLocked(@NonNull ComponentName componentName, boolean compatMode) {
         final String packageName = componentName.getPackageName();
-        final PackageManager pm = mContext.getPackageManager();
+        final PackageManager pm = getContext().getPackageManager();
         final int callingUid = Binder.getCallingUid();
         final int packageUid;
         try {
@@ -651,7 +555,8 @@
     }
 
     @GuardedBy("mLock")
-    void handlePackageUpdateLocked(String packageName) {
+    @Override // from PerUserSystemService
+    protected void handlePackageUpdateLocked(@NonNull String packageName) {
         final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
         if (serviceInfo != null && serviceInfo.packageName.equals(packageName)) {
             resetExtServiceLocked();
@@ -691,29 +596,6 @@
     }
 
     /**
-     * Gets the user-visibile name of the service this service binds to, or {@code null} if the
-     * service is disabled.
-     */
-    @Nullable
-    @GuardedBy("mLock")
-    public CharSequence getServiceLabelLocked() {
-        return mInfo == null ? null : mInfo.getServiceInfo().loadSafeLabel(
-                mContext.getPackageManager(), 0 /* do not ellipsize */,
-                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
-    }
-
-    /**
-     * Gets the icon of the service this service binds to, or {@code null} if the service is
-     * disabled.
-     */
-    @NonNull
-    @Nullable
-    @GuardedBy("mLock")
-    Drawable getServiceIconLocked() {
-        return mInfo == null ? null : mInfo.getServiceInfo().loadIcon(mContext.getPackageManager());
-    }
-
-    /**
      * Initializes the last fill selection after an autofill service returned a new
      * {@link FillResponse}.
      */
@@ -941,11 +823,13 @@
         return true;
     }
 
+    @Override
     @GuardedBy("mLock")
-    void dumpLocked(String prefix, PrintWriter pw) {
+    protected void dumpLocked(String prefix, PrintWriter pw) {
+        super.dumpLocked(prefix, pw);
+
         final String prefix2 = prefix + "  ";
 
-        pw.print(prefix); pw.print("User: "); pw.println(mUserId);
         pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked());
         pw.print(prefix); pw.print("Autofill Service Info: ");
         if (mInfo == null) {
@@ -958,9 +842,8 @@
         }
         pw.print(prefix); pw.print("Component from settings: ");
             pw.println(getComponentNameFromSettings());
-        pw.print(prefix); pw.print("Default component: ");
-            pw.println(mContext.getString(R.string.config_defaultAutofillService));
-        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+        pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
+                .getString(R.string.config_defaultAutofillService));
         pw.print(prefix); pw.print("Field classification enabled: ");
             pw.println(isFieldClassificationEnabledLocked());
         pw.print(prefix); pw.print("Compat pkgs: ");
@@ -970,7 +853,6 @@
         } else {
             pw.println(compatPkgs);
         }
-        pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
 
         pw.print(prefix); pw.print("Disabled apps: ");
@@ -1158,11 +1040,6 @@
         return true;
     }
 
-    @GuardedBy("mLock")
-    boolean isEnabledLocked() {
-        return mSetupComplete && mInfo != null && !mDisabled;
-    }
-
     /**
      * Called by {@link Session} when service asked to disable autofill for an app.
      */
@@ -1270,7 +1147,7 @@
     // Called by internally, no need to check UID.
     boolean isFieldClassificationEnabledLocked() {
         return Settings.Secure.getIntForUser(
-                mContext.getContentResolver(),
+                getContext().getContentResolver(),
                 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 1,
                 mUserId) == 1;
     }
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 420c2be..3c0da7d 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -28,20 +28,21 @@
 import android.util.Slog;
 import android.view.WindowManager;
 import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ArrayUtils;
 
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.LinkedList;
 
 public final class Helper {
 
     private static final String TAG = "AutofillHelper";
 
+    // TODO(b/117779333): get rid of sDebug / sVerbose and always use the service variables instead
+
     /**
      * Defines a logging flag that can be dynamically changed at runtime using
      * {@code cmd autofill set log_level debug} or through
@@ -57,23 +58,6 @@
     public static boolean sVerbose = false;
 
     /**
-     * Maximum number of partitions that can be allowed in a session.
-     *
-     * <p>Can be modified using {@code cmd autofill set max_partitions} or through
-     * {@link android.provider.Settings.Global#AUTOFILL_MAX_PARTITIONS_SIZE}.
-     */
-    static int sPartitionMaxCount = AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE;
-
-    /**
-     * Maximum number of visible datasets in the dataset picker UI, or {@code 0} to use default
-     * value from resources.
-     *
-     * <p>Can be modified using {@code cmd autofill set max_visible_datasets} or through
-     * {@link android.provider.Settings.Global#AUTOFILL_MAX_VISIBLE_DATASETS}.
-     */
-    public static int sVisibleDatasetsMaxCount = 0;
-
-    /**
      * When non-null, overrides whether the UI should be shown on full-screen mode.
      *
      * <p>Note: access to this variable is not synchronized because it's "final" on real usage -
@@ -162,7 +146,7 @@
 
     private static ViewNode findViewNode(@NonNull AssistStructure structure,
             @NonNull ViewNodeFilter filter) {
-        final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
+        final ArrayDeque<ViewNode> nodesToProcess = new ArrayDeque<>();
         final int numWindowNodes = structure.getWindowNodeCount();
         for (int i = 0; i < numWindowNodes; i++) {
             nodesToProcess.add(structure.getWindowNodeAt(i).getRootViewNode());
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index d1b09ca..9aa9d7c 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -18,7 +18,6 @@
 
 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
 
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
 
@@ -26,17 +25,11 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.IntentSender;
-import android.content.ServiceConnection;
-import android.os.Build;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
 import android.os.ICancellationSignal;
+import android.os.IInterface;
 import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
 import android.service.autofill.AutofillService;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
@@ -47,59 +40,17 @@
 import android.text.format.DateUtils;
 import android.util.Slog;
 
-import com.android.internal.annotations.GuardedBy;
-import com.android.server.FgThread;
+import com.android.server.AbstractRemoteService;
 
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
+final class RemoteFillService extends AbstractRemoteService {
 
-/**
- * This class represents a remote fill service. It abstracts away the binding
- * and unbinding from the remote implementation.
- *
- * <p>Clients can call methods of this class without worrying about when and
- * how to bind/unbind/timeout. All state of this class is modified on a handler
- * thread.
- */
-final class RemoteFillService implements DeathRecipient {
-    private static final String LOG_TAG = "RemoteFillService";
-    // How long after the last interaction with the service we would unbind
     private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
-
-    // How long after we make a remote request to a fill service we timeout
     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
-    private static final int MSG_UNBIND = 3;
-
-    private final Context mContext;
-
-    private final ComponentName mComponentName;
-
-    private final Intent mIntent;
-
     private final FillServiceCallbacks mCallbacks;
-
-    private final int mUserId;
-
-    private final ServiceConnection mServiceConnection = new RemoteServiceConnection();
-
-    private final Handler mHandler;
-
-    private final boolean mBindInstantServiceAllowed;
-
     private IAutoFillService mAutoFillService;
 
-    private boolean mBinding;
-
-    private boolean mDestroyed;
-
-    private boolean mServiceDied;
-
-    private boolean mCompleted;
-
-    private PendingRequest mPendingRequest;
-
-    public interface FillServiceCallbacks {
+    public interface FillServiceCallbacks extends VultureCallback {
         void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
                 @NonNull String servicePackageName, int requestFlags);
         void onFillRequestFailure(int requestId, @Nullable CharSequence message);
@@ -109,48 +60,42 @@
         // TODO(b/80093094): add timeout here too?
         void onSaveRequestFailure(@Nullable CharSequence message,
                 @NonNull String servicePackageName);
-        void onServiceDied(RemoteFillService service);
     }
 
-    public RemoteFillService(Context context, ComponentName componentName,
-            int userId, FillServiceCallbacks callbacks, boolean bindInstantServiceAllowed) {
-        mContext = context;
+    RemoteFillService(Context context, ComponentName componentName, int userId,
+            FillServiceCallbacks callbacks, boolean bindInstantServiceAllowed) {
+        super(context, AutofillService.SERVICE_INTERFACE, componentName, userId, callbacks,
+                bindInstantServiceAllowed, sVerbose);
         mCallbacks = callbacks;
-        mComponentName = componentName;
-        mIntent = new Intent(AutofillService.SERVICE_INTERFACE).setComponent(mComponentName);
-        mUserId = userId;
-        mHandler = new Handler(FgThread.getHandler().getLooper());
-        mBindInstantServiceAllowed = bindInstantServiceAllowed;
-    }
-
-    public void destroy() {
-        mHandler.sendMessage(obtainMessage(RemoteFillService::handleDestroy, this));
-    }
-
-    private void handleDestroy() {
-        if (checkIfDestroyed()) return;
-        if (mPendingRequest != null) {
-            mPendingRequest.cancel();
-            mPendingRequest = null;
-        }
-        ensureUnbound();
-        mDestroyed = true;
     }
 
     @Override
-    public void binderDied() {
-        mHandler.sendMessage(obtainMessage(
-                RemoteFillService::handleBinderDied, this));
+    protected void onConnectedStateChanged(boolean state) {
+        if (mAutoFillService == null) {
+            Slog.w(mTag, "onConnectedStateChanged(): null service");
+            return;
+        }
+        try {
+            mAutoFillService.onConnectedStateChanged(state);
+        } catch (Exception e) {
+            Slog.w(mTag, "Exception calling onConnectedStateChanged(): " + e);
+        }
     }
 
-    private void handleBinderDied() {
-        if (checkIfDestroyed()) return;
-        if (mAutoFillService != null) {
-            mAutoFillService.asBinder().unlinkToDeath(this, 0);
-        }
-        mAutoFillService = null;
-        mServiceDied = true;
-        mCallbacks.onServiceDied(this);
+    @Override
+    protected IInterface getServiceInterface(IBinder service) {
+        mAutoFillService = IAutoFillService.Stub.asInterface(service);
+        return mAutoFillService;
+    }
+
+    @Override
+    protected long getTimeoutIdleBindMillis() {
+        return TIMEOUT_IDLE_BIND_MILLIS;
+    }
+
+    @Override
+    protected long getRemoteRequestMillis() {
+        return TIMEOUT_REMOTE_REQUEST_MILLIS;
     }
 
     /**
@@ -162,8 +107,9 @@
      * @return the id of the canceled request, or {@link FillRequest#INVALID_REQUEST_ID} if no
      *         {@link PendingFillRequest} was canceled.
      */
+    // TODO(b/117779333): move this logic to super class (and make mPendingRequest private)
     public int cancelCurrentRequest() {
-        if (mDestroyed) {
+        if (isDestroyed()) {
             return INVALID_REQUEST_ID;
         }
 
@@ -190,118 +136,6 @@
         scheduleRequest(new PendingSaveRequest(request, this));
     }
 
-    private void scheduleRequest(PendingRequest pendingRequest) {
-        mHandler.sendMessage(obtainMessage(
-                RemoteFillService::handlePendingRequest, this, pendingRequest));
-    }
-
-    // Note: we are dumping without a lock held so this is a bit racy but
-    // adding a lock to a class that offloads to a handler thread would
-    // mean adding a lock adding overhead to normal runtime operation.
-    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
-        String tab = "  ";
-        pw.append(prefix).append("service:").println();
-        pw.append(prefix).append(tab).append("userId=")
-                .append(String.valueOf(mUserId)).println();
-        pw.append(prefix).append(tab).append("componentName=")
-                .append(mComponentName.flattenToString()).println();
-        pw.append(prefix).append(tab).append("destroyed=")
-                .append(String.valueOf(mDestroyed)).println();
-        pw.append(prefix).append(tab).append("bound=")
-                .append(String.valueOf(isBound())).println();
-        pw.append(prefix).append(tab).append("hasPendingRequest=")
-                .append(String.valueOf(mPendingRequest != null)).println();
-        pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed);
-        pw.println();
-    }
-
-    private void cancelScheduledUnbind() {
-        mHandler.removeMessages(MSG_UNBIND);
-    }
-
-    private void scheduleUnbind() {
-        cancelScheduledUnbind();
-        mHandler.sendMessageDelayed(
-                obtainMessage(RemoteFillService::handleUnbind, this)
-                        .setWhat(MSG_UNBIND),
-                TIMEOUT_IDLE_BIND_MILLIS);
-    }
-
-    private void handleUnbind() {
-        if (checkIfDestroyed()) return;
-        ensureUnbound();
-    }
-
-    private void handlePendingRequest(PendingRequest pendingRequest) {
-        if (checkIfDestroyed()) return;
-        if (mCompleted) {
-            return;
-        }
-        if (!isBound()) {
-            if (mPendingRequest != null) {
-                mPendingRequest.cancel();
-            }
-            mPendingRequest = pendingRequest;
-            ensureBound();
-        } else {
-            if (sVerbose) Slog.v(LOG_TAG, "[user: " + mUserId + "] handlePendingRequest()");
-            pendingRequest.run();
-            if (pendingRequest.isFinal()) {
-                mCompleted = true;
-            }
-        }
-    }
-
-    private boolean isBound() {
-        return mAutoFillService != null;
-    }
-
-    private void ensureBound() {
-        if (isBound() || mBinding) {
-            return;
-        }
-        if (sVerbose) Slog.v(LOG_TAG, "[user: " + mUserId + "] ensureBound()");
-        mBinding = true;
-
-        int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
-        if (mBindInstantServiceAllowed) {
-            flags |= Context.BIND_ALLOW_INSTANT;
-        }
-
-        final boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, flags,
-                new UserHandle(mUserId));
-
-        if (!willBind) {
-            Slog.w(LOG_TAG, "[user: " + mUserId + "] could not bind to " + mIntent + " using flags "
-                    + flags);
-            mBinding = false;
-
-            if (!mServiceDied) {
-                handleBinderDied();
-            }
-        }
-    }
-
-    private void ensureUnbound() {
-        if (!isBound() && !mBinding) {
-            return;
-        }
-        if (sVerbose) Slog.v(LOG_TAG, "[user: " + mUserId + "] ensureUnbound()");
-        mBinding = false;
-        if (isBound()) {
-            try {
-                mAutoFillService.onConnectedStateChanged(false);
-            } catch (Exception e) {
-                Slog.w(LOG_TAG, "Exception calling onDisconnected(): " + e);
-            }
-            if (mAutoFillService != null) {
-                mAutoFillService.asBinder().unlinkToDeath(this, 0);
-                mAutoFillService = null;
-            }
-        }
-        mContext.unbindService(mServiceConnection);
-    }
-
     private void dispatchOnFillRequestSuccess(@NonNull PendingFillRequest pendingRequest,
             @Nullable FillResponse response, int requestFlags) {
         mHandler.post(() -> {
@@ -334,12 +168,12 @@
             try {
                 cancellationSignal.cancel();
             } catch (RemoteException e) {
-                Slog.w(LOG_TAG, "Error calling cancellation signal: " + e);
+                Slog.w(mTag, "Error calling cancellation signal: " + e);
             }
         });
     }
 
-    private void dispatchOnSaveRequestSuccess(PendingRequest pendingRequest,
+    private void dispatchOnSaveRequestSuccess(PendingSaveRequest pendingRequest,
             IntentSender intentSender) {
         mHandler.post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
@@ -348,7 +182,7 @@
         });
     }
 
-    private void dispatchOnSaveRequestFailure(PendingRequest pendingRequest,
+    private void dispatchOnSaveRequestFailure(PendingSaveRequest pendingRequest,
             @Nullable CharSequence message) {
         mHandler.post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
@@ -357,162 +191,7 @@
         });
     }
 
-    private boolean handleResponseCallbackCommon(PendingRequest pendingRequest) {
-        if (mDestroyed) {
-            return false;
-        }
-        if (mPendingRequest == pendingRequest) {
-            mPendingRequest = null;
-        }
-        if (mPendingRequest == null) {
-            scheduleUnbind();
-        }
-        return true;
-    }
-
-    private class RemoteServiceConnection implements ServiceConnection {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            if (mDestroyed || !mBinding) {
-                // This is abnormal. Unbinding the connection has been requested already.
-                Slog.wtf(LOG_TAG, "onServiceConnected was dispatched after unbindService.");
-                return;
-            }
-            mBinding = false;
-            mAutoFillService = IAutoFillService.Stub.asInterface(service);
-            try {
-                service.linkToDeath(RemoteFillService.this, 0);
-            } catch (RemoteException re) {
-                handleBinderDied();
-                return;
-            }
-            try {
-                mAutoFillService.onConnectedStateChanged(true);
-            } catch (RemoteException e) {
-                Slog.w(LOG_TAG, "Exception calling onConnected(): " + e);
-            }
-
-            if (mPendingRequest != null) {
-                PendingRequest pendingRequest = mPendingRequest;
-                mPendingRequest = null;
-                handlePendingRequest(pendingRequest);
-            }
-
-            mServiceDied = false;
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            mBinding = true;
-            mAutoFillService = null;
-        }
-    }
-
-    private boolean checkIfDestroyed() {
-        if (mDestroyed) {
-            if (sVerbose) {
-                Slog.v(LOG_TAG, "Not handling operation as service for "
-                        + mComponentName + " is already destroyed");
-            }
-        }
-        return mDestroyed;
-    }
-
-    private static abstract class PendingRequest implements Runnable {
-        protected final Object mLock = new Object();
-        private final WeakReference<RemoteFillService> mWeakService;
-
-        private final Runnable mTimeoutTrigger;
-        private final Handler mServiceHandler;
-
-        @GuardedBy("mLock")
-        private boolean mCancelled;
-
-        @GuardedBy("mLock")
-        private boolean mCompleted;
-
-        PendingRequest(RemoteFillService service) {
-            mWeakService = new WeakReference<>(service);
-            mServiceHandler = service.mHandler;
-            mTimeoutTrigger = () -> {
-                synchronized (mLock) {
-                    if (mCancelled) {
-                        return;
-                    }
-                    mCompleted = true;
-                }
-
-                Slog.w(LOG_TAG, getClass().getSimpleName() + " timed out");
-                final RemoteFillService remoteService = mWeakService.get();
-                if (remoteService != null) {
-                    Slog.w(LOG_TAG, getClass().getSimpleName() + " timed out after "
-                            + TIMEOUT_REMOTE_REQUEST_MILLIS + " ms");
-                    onTimeout(remoteService);
-                }
-            };
-            mServiceHandler.postAtTime(mTimeoutTrigger,
-                    SystemClock.uptimeMillis() + TIMEOUT_REMOTE_REQUEST_MILLIS);
-        }
-
-        protected RemoteFillService getService() {
-            return mWeakService.get();
-        }
-
-        /**
-         * Sub-classes must call this method when the remote service finishes, i.e., when it
-         * called {@code onFill...} or {@code onSave...}.
-         *
-         * @return {@code false} in the service is already finished, {@code true} otherwise.
-         */
-        protected final boolean finish() {
-            synchronized (mLock) {
-                if (mCompleted || mCancelled) {
-                    return false;
-                }
-                mCompleted = true;
-            }
-            mServiceHandler.removeCallbacks(mTimeoutTrigger);
-            return true;
-        }
-
-        @GuardedBy("mLock")
-        protected boolean isCancelledLocked() {
-            return mCancelled;
-        }
-
-        /**
-         * Cancels the service.
-         *
-         * @return {@code false} if service is already canceled, {@code true} otherwise.
-         */
-        boolean cancel() {
-            synchronized (mLock) {
-                if (mCancelled || mCompleted) {
-                    return false;
-                }
-                mCancelled = true;
-            }
-
-            mServiceHandler.removeCallbacks(mTimeoutTrigger);
-            return true;
-        }
-
-        /**
-         * Called by the self-destructure timeout when the AutofilllService didn't reply to the
-         * request on time.
-         */
-        abstract void onTimeout(RemoteFillService remoteService);
-
-        /**
-         * @return whether this request leads to a final state where no
-         * other requests can be made.
-         */
-        boolean isFinal() {
-            return false;
-        }
-    }
-
-    private static final class PendingFillRequest extends PendingRequest {
+    private static final class PendingFillRequest extends PendingRequest<RemoteFillService> {
         private final FillRequest mRequest;
         private final IFillCallback mCallback;
         private ICancellationSignal mCancellation;
@@ -534,7 +213,7 @@
                             try {
                                 cancellation.cancel();
                             } catch (RemoteException e) {
-                                Slog.e(LOG_TAG, "Error requesting a cancellation", e);
+                                Slog.e(mTag, "Error requesting a cancellation", e);
                             }
                         }
                     }
@@ -565,7 +244,7 @@
         }
 
         @Override
-        void onTimeout(RemoteFillService remoteService) {
+        protected void onTimeout(RemoteFillService remoteService) {
             // NOTE: Must make these 2 calls asynchronously, because the cancellation signal is
             // handled by the service, which could block.
             final ICancellationSignal cancellation;
@@ -582,17 +261,17 @@
         public void run() {
             synchronized (mLock) {
                 if (isCancelledLocked()) {
-                    if (sDebug) Slog.d(LOG_TAG, "run() called after canceled: " + mRequest);
+                    if (sDebug) Slog.d(mTag, "run() called after canceled: " + mRequest);
                     return;
                 }
             }
             final RemoteFillService remoteService = getService();
             if (remoteService != null) {
-                if (sVerbose) Slog.v(LOG_TAG, "calling onFillRequest() for id=" + mRequest.getId());
+                if (sVerbose) Slog.v(mTag, "calling onFillRequest() for id=" + mRequest.getId());
                 try {
                     remoteService.mAutoFillService.onFillRequest(mRequest, mCallback);
                 } catch (RemoteException e) {
-                    Slog.e(LOG_TAG, "Error calling on fill request", e);
+                    Slog.e(mTag, "Error calling on fill request", e);
 
                     remoteService.dispatchOnFillRequestFailure(PendingFillRequest.this, null);
                 }
@@ -611,14 +290,14 @@
                 try {
                     cancellation.cancel();
                 } catch (RemoteException e) {
-                    Slog.e(LOG_TAG, "Error cancelling a fill request", e);
+                    Slog.e(mTag, "Error cancelling a fill request", e);
                 }
             }
             return true;
         }
     }
 
-    private static final class PendingSaveRequest extends PendingRequest {
+    private static final class PendingSaveRequest extends PendingRequest<RemoteFillService> {
         private final SaveRequest mRequest;
         private final ISaveCallback mCallback;
 
@@ -653,7 +332,7 @@
         }
 
         @Override
-        void onTimeout(RemoteFillService remoteService) {
+        protected void onTimeout(RemoteFillService remoteService) {
             remoteService.dispatchOnSaveRequestFailure(PendingSaveRequest.this, null);
         }
 
@@ -661,11 +340,11 @@
         public void run() {
             final RemoteFillService remoteService = getService();
             if (remoteService != null) {
-                if (sVerbose) Slog.v(LOG_TAG, "calling onSaveRequest()");
+                if (sVerbose) Slog.v(mTag, "calling onSaveRequest()");
                 try {
                     remoteService.mAutoFillService.onSaveRequest(mRequest, mCallback);
                 } catch (RemoteException e) {
-                    Slog.e(LOG_TAG, "Error calling on save request", e);
+                    Slog.e(mTag, "Error calling on save request", e);
 
                     remoteService.dispatchOnSaveRequestFailure(PendingSaveRequest.this, null);
                 }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f85749a..09f915e 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -27,7 +27,6 @@
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.autofill.Helper.getNumericValue;
 import static com.android.server.autofill.Helper.sDebug;
-import static com.android.server.autofill.Helper.sPartitionMaxCount;
 import static com.android.server.autofill.Helper.sVerbose;
 import static com.android.server.autofill.Helper.toArray;
 import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
@@ -65,7 +64,6 @@
 import android.service.autofill.Dataset;
 import android.service.autofill.FieldClassification;
 import android.service.autofill.FieldClassification.Match;
-import android.text.TextUtils;
 import android.service.autofill.FillContext;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
@@ -75,10 +73,10 @@
 import android.service.autofill.SaveRequest;
 import android.service.autofill.UserData;
 import android.service.autofill.ValueFinder;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LocalLog;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -94,6 +92,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.AbstractRemoteService;
 import com.android.server.autofill.ui.AutoFillUI;
 import com.android.server.autofill.ui.PendingUi;
 
@@ -903,9 +902,9 @@
                 this, authenticationId, intent, fillInIntent));
     }
 
-    // FillServiceCallbacks
+    // VultureCallback
     @Override
-    public void onServiceDied(RemoteFillService service) {
+    public void onServiceDied(AbstractRemoteService service) {
         Slog.w(TAG, "removing session because service died");
         forceRemoveSelfLocked();
     }
@@ -2095,9 +2094,9 @@
         }
 
         final int numResponses = mResponses.size();
-        if (numResponses >= sPartitionMaxCount) {
+        if (numResponses >= AutofillManagerService.getPartitionMaxCount()) {
             Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
-                    + " reached maximum of " + sPartitionMaxCount);
+                    + " reached maximum of " + AutofillManagerService.getPartitionMaxCount());
             return false;
         }
 
@@ -2289,7 +2288,7 @@
                 requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);
 
                 // Remove the UI if the ViewState has changed.
-                if (mCurrentViewId != viewState.id) {
+                if (!Objects.equals(mCurrentViewId, viewState.id)) {
                     mUi.hideFillUi(this);
                     mCurrentViewId = viewState.id;
                 }
@@ -2298,7 +2297,7 @@
                 viewState.update(value, virtualBounds, flags);
                 break;
             case ACTION_VIEW_EXITED:
-                if (mCurrentViewId == viewState.id) {
+                if (Objects.equals(mCurrentViewId, viewState.id)) {
                     if (sVerbose) Slog.d(TAG, "Exiting view " + id);
                     mUi.hideFillUi(this);
                     mCurrentViewId = null;
@@ -3077,7 +3076,9 @@
 
     private void wtf(@Nullable Exception e, String fmt, Object...args) {
         final String message = String.format(fmt, args);
-        mWtfHistory.log(message);
+        synchronized (mLock) {
+            mWtfHistory.log(message);
+        }
 
         if (e != null) {
             Slog.wtf(TAG, message, e);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 742d494..d1fe970c 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -19,25 +19,22 @@
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sFullScreenMode;
 import static com.android.server.autofill.Helper.sVerbose;
-import static com.android.server.autofill.Helper.sVisibleDatasetsMaxCount;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.PendingIntent;
 import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.view.ContextThemeWrapper;
-import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.PackageManager;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.service.autofill.Dataset;
 import android.service.autofill.Dataset.DatasetFieldFilter;
 import android.service.autofill.FillResponse;
 import android.text.TextUtils;
 import android.util.Slog;
 import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -60,6 +57,7 @@
 
 import com.android.internal.R;
 import com.android.server.UiThread;
+import com.android.server.autofill.AutofillManagerService;
 import com.android.server.autofill.Helper;
 
 import java.io.PrintWriter;
@@ -193,8 +191,8 @@
             }
         });
 
-        if (sVisibleDatasetsMaxCount > 0) {
-            mVisibleDatasetsMaxCount = sVisibleDatasetsMaxCount;
+        if (AutofillManagerService.getVisibleDatasetsMaxCount() > 0) {
+            mVisibleDatasetsMaxCount = AutofillManagerService.getVisibleDatasetsMaxCount();
             if (sVerbose) {
                 Slog.v(TAG, "overriding maximum visible datasets to " + mVisibleDatasetsMaxCount);
             }
@@ -203,15 +201,11 @@
                     .getInteger(com.android.internal.R.integer.autofill_max_visible_datasets);
         }
 
-        final RemoteViews.OnClickHandler interceptionHandler = new RemoteViews.OnClickHandler() {
-            @Override
-            public boolean onClickHandler(View view, PendingIntent pendingIntent,
-                    Intent fillInIntent) {
-                if (pendingIntent != null) {
-                    mCallback.startIntentSender(pendingIntent.getIntentSender());
-                }
-                return true;
+        final RemoteViews.OnClickHandler interceptionHandler = (view, pendingIntent, r) -> {
+            if (pendingIntent != null) {
+                mCallback.startIntentSender(pendingIntent.getIntentSender());
             }
+            return true;
         };
 
         if (response.getAuthentication() != null) {
@@ -369,13 +363,9 @@
      * Creates a remoteview interceptor used to block clicks.
      */
     private RemoteViews.OnClickHandler newClickBlocker() {
-        return new RemoteViews.OnClickHandler() {
-            @Override
-            public boolean onClickHandler(View view, PendingIntent pendingIntent,
-                    Intent fillInIntent) {
-                if (sVerbose) Slog.v(TAG, "Ignoring click on " + view);
-                return true;
-            }
+        return (view, pendingIntent, response) -> {
+            if (sVerbose) Slog.v(TAG, "Ignoring click on " + view);
+            return true;
         };
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 89b442e..1e30c8a 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -304,39 +304,36 @@
             }
         }
 
-        final RemoteViews.OnClickHandler handler = new RemoteViews.OnClickHandler() {
-            @Override
-            public boolean onClickHandler(View view, PendingIntent pendingIntent,
-                    Intent intent) {
-                final LogMaker log =
-                        newLogMaker(MetricsEvent.AUTOFILL_SAVE_LINK_TAPPED, type);
-                // We need to hide the Save UI before launching the pending intent, and
-                // restore back it once the activity is finished, and that's achieved by
-                // adding a custom extra in the activity intent.
-                final boolean isValid = isValidLink(pendingIntent, intent);
-                if (!isValid) {
-                    log.setType(MetricsEvent.TYPE_UNKNOWN);
-                    mMetricsLogger.write(log);
-                    return false;
-                }
-                if (sVerbose) Slog.v(TAG, "Intercepting custom description intent");
-                final IBinder token = mPendingUi.getToken();
-                intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token);
-                try {
-                    mPendingUi.client.startIntentSender(pendingIntent.getIntentSender(),
-                            intent);
-                    mPendingUi.setState(PendingUi.STATE_PENDING);
-                    if (sDebug) Slog.d(TAG, "hiding UI until restored with token " + token);
-                    hide();
-                    log.setType(MetricsEvent.TYPE_OPEN);
-                    mMetricsLogger.write(log);
-                    return true;
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "error triggering pending intent: " + intent);
-                    log.setType(MetricsEvent.TYPE_FAILURE);
-                    mMetricsLogger.write(log);
-                    return false;
-                }
+        final RemoteViews.OnClickHandler handler = (view, pendingIntent, response) -> {
+            Intent intent = response.getLaunchOptions(view).first;
+            final LogMaker log =
+                    newLogMaker(MetricsEvent.AUTOFILL_SAVE_LINK_TAPPED, type);
+            // We need to hide the Save UI before launching the pending intent, and
+            // restore back it once the activity is finished, and that's achieved by
+            // adding a custom extra in the activity intent.
+            final boolean isValid = isValidLink(pendingIntent, intent);
+            if (!isValid) {
+                log.setType(MetricsEvent.TYPE_UNKNOWN);
+                mMetricsLogger.write(log);
+                return false;
+            }
+            if (sVerbose) Slog.v(TAG, "Intercepting custom description intent");
+            final IBinder token = mPendingUi.getToken();
+            intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token);
+            try {
+                mPendingUi.client.startIntentSender(pendingIntent.getIntentSender(),
+                        intent);
+                mPendingUi.setState(PendingUi.STATE_PENDING);
+                if (sDebug) Slog.d(TAG, "hiding UI until restored with token " + token);
+                hide();
+                log.setType(MetricsEvent.TYPE_OPEN);
+                mMetricsLogger.write(log);
+                return true;
+            } catch (RemoteException e) {
+                Slog.w(TAG, "error triggering pending intent: " + intent);
+                log.setType(MetricsEvent.TYPE_FAILURE);
+                mMetricsLogger.write(log);
+                return false;
             }
         };
 
diff --git a/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java b/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java
new file mode 100644
index 0000000..9cab1ed
--- /dev/null
+++ b/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 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.server.intelligence;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.service.intelligence.IntelligenceService;
+import android.service.intelligence.InteractionContext;
+import android.service.intelligence.InteractionSessionId;
+import android.util.Slog;
+import android.view.intelligence.ContentCaptureEvent;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+import com.android.server.AbstractRemoteService;
+import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks {
+
+    private static final String TAG = "ContentCaptureSession";
+
+    private final Object mLock;
+    private final IBinder mActivityToken;
+
+    private final IntelligencePerUserService mService;
+    private final RemoteIntelligenceService mRemoteService;
+    private final InteractionContext mInterationContext;
+    private final InteractionSessionId mId;
+
+    ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock,
+            @NonNull IBinder activityToken, @NonNull IntelligencePerUserService service,
+            @NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
+            int taskId, int displayId, @NonNull InteractionSessionId sessionId, int flags,
+            boolean bindInstantServiceAllowed, boolean verbose) {
+        mLock = lock;
+        mActivityToken = activityToken;
+        mService = service;
+        mId = Preconditions.checkNotNull(sessionId);
+        mRemoteService = new RemoteIntelligenceService(context,
+                IntelligenceService.SERVICE_INTERFACE, serviceComponentName, userId, this,
+                bindInstantServiceAllowed, verbose);
+        mInterationContext = new InteractionContext(appComponentName, taskId, displayId, flags);
+    }
+
+    /**
+     * Notifies the {@link IntelligenceService} that the service started.
+     */
+    @GuardedBy("mLock")
+    public void notifySessionStartedLocked() {
+        mRemoteService.onSessionLifecycleRequest(mInterationContext, mId);
+    }
+
+    /**
+     * Notifies the {@link IntelligenceService} of a batch of events.
+     */
+    public void sendEventsLocked(List<ContentCaptureEvent> events) {
+        mRemoteService.onContentCaptureEventsRequest(mId, events);
+    }
+
+    /**
+     * Cleans up the session and remove itself from the service.
+     *
+     * @param notifyRemoteService whether it should trigger a {@link
+     * IntelligenceService#onDestroyInteractionSession(InteractionSessionId)}
+     * request.
+     */
+    @GuardedBy("mLock")
+    public void removeSelfLocked(boolean notifyRemoteService) {
+        try {
+            if (notifyRemoteService) {
+                mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
+            }
+        } finally {
+            mService.removeSessionLocked(mId);
+        }
+    }
+
+    @Override // from RemoteScreenObservationServiceCallbacks
+    public void onServiceDied(AbstractRemoteService service) {
+        // TODO(b/111276913): implement (remove session from PerUserSession?)
+        if (mService.isDebug()) {
+            Slog.d(TAG, "onServiceDied() for " + mId);
+        }
+        synchronized (mLock) {
+            removeSelfLocked(/* notifyRemoteService= */ false);
+        }
+    }
+
+    @Override // from RemoteScreenObservationServiceCallbacks
+    public void onFailureOrTimeout(boolean timedOut) {
+        // TODO(b/111276913): log metrics on whether timed out or not
+        if (mService.isDebug()) {
+            Slog.d(TAG, "onFailureOrTimeout(" + mId + "): timed out=" + timedOut);
+        }
+        synchronized (mLock) {
+            removeSelfLocked(/* notifyRemoteService= */ false);
+        }
+    }
+
+    @GuardedBy("mLock")
+    public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+        pw.print(prefix); pw.print("id: ");  mId.dump(pw); pw.println();
+        pw.print(prefix); pw.print("context: ");  mInterationContext.dump(pw); pw.println();
+        pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
+    }
+
+    @Override
+    public String toString() {
+        return "ContentCaptureSession[id=" + mId.getValue() + ", act=" + mActivityToken + "]";
+    }
+}
diff --git a/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java
new file mode 100644
index 0000000..43d4a44
--- /dev/null
+++ b/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 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.server.intelligence;
+
+import static android.content.Context.INTELLIGENCE_MANAGER_SERVICE;
+
+import android.annotation.NonNull;
+import android.app.ActivityManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.UserManager;
+import android.service.intelligence.InteractionSessionId;
+import android.view.intelligence.ContentCaptureEvent;
+import android.view.intelligence.IIntelligenceManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.AbstractMasterSystemService;
+import com.android.server.LocalServices;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * A service used to observe the contents of the screen.
+ *
+ * <p>The data collected by this service can be analyzed and combined with other sources to provide
+ * contextual data in other areas of the system such as Autofill.
+ */
+public final class IntelligenceManagerService
+        extends AbstractMasterSystemService<IntelligencePerUserService> {
+
+    private static final String TAG = "IntelligenceManagerService";
+
+    @GuardedBy("mLock")
+    private ActivityManagerInternal mAm;
+
+    public IntelligenceManagerService(Context context) {
+        super(context, UserManager.DISALLOW_INTELLIGENCE_CAPTURE);
+    }
+
+    @Override // from MasterSystemService
+    protected String getServiceSettingsProperty() {
+        // TODO(b/111276913): STOPSHIP temporary settings, until it's set by resourcs + cmd
+        return "intel_service";
+    }
+
+    @Override // from MasterSystemService
+    protected IntelligencePerUserService newServiceLocked(int resolvedUserId,
+            boolean disabled) {
+        return new IntelligencePerUserService(this, mLock, resolvedUserId);
+    }
+
+    @Override // from SystemService
+    public void onStart() {
+        publishBinderService(INTELLIGENCE_MANAGER_SERVICE,
+                new IntelligenceManagerServiceStub());
+    }
+
+    private ActivityManagerInternal getAmInternal() {
+        synchronized (mLock) {
+            if (mAm == null) {
+                mAm = LocalServices.getService(ActivityManagerInternal.class);
+            }
+        }
+        return mAm;
+    }
+
+    final class IntelligenceManagerServiceStub extends IIntelligenceManager.Stub {
+
+        @Override
+        public void startSession(int userId, @NonNull IBinder activityToken,
+                @NonNull ComponentName componentName, @NonNull InteractionSessionId sessionId,
+                int flags, @NonNull IResultReceiver result) {
+            Preconditions.checkNotNull(activityToken);
+            Preconditions.checkNotNull(componentName);
+            Preconditions.checkNotNull(sessionId);
+
+            // TODO(b/111276913): refactor getTaskIdForActivity() to also return ComponentName,
+            // so we don't pass it on startSession (same for Autofill)
+            final int taskId = getAmInternal().getTaskIdForActivity(activityToken, false);
+
+            // TODO(b/111276913): get from AM as well
+            final int displayId = 0;
+
+            synchronized (mLock) {
+                final IntelligencePerUserService service = getServiceForUserLocked(userId);
+                service.startSessionLocked(activityToken, componentName, taskId, displayId,
+                        sessionId, flags, result);
+            }
+        }
+
+        @Override
+        public void sendEvents(int userId, @NonNull InteractionSessionId sessionId,
+                @NonNull List<ContentCaptureEvent> events) {
+            Preconditions.checkNotNull(sessionId);
+            Preconditions.checkNotNull(events);
+
+            synchronized (mLock) {
+                final IntelligencePerUserService service = getServiceForUserLocked(userId);
+                service.sendEventsLocked(sessionId, events);
+            }
+        }
+
+        @Override
+        public void finishSession(int userId, @NonNull InteractionSessionId sessionId) {
+            Preconditions.checkNotNull(sessionId);
+
+            synchronized (mLock) {
+                final IntelligencePerUserService service = getServiceForUserLocked(userId);
+                service.finishSessionLocked(sessionId);
+            }
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
+
+            synchronized (mLock) {
+                dumpLocked("", pw);
+            }
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java
new file mode 100644
index 0000000..584b872
--- /dev/null
+++ b/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2018 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.server.intelligence;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.service.intelligence.InteractionSessionId;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.intelligence.ContentCaptureEvent;
+import android.view.intelligence.IntelligenceManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
+import com.android.server.AbstractPerUserSystemService;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Per-user instance of {@link IntelligenceManagerService}.
+ */
+final class IntelligencePerUserService
+        extends AbstractPerUserSystemService<IntelligencePerUserService> {
+
+    private static final String TAG = "IntelligencePerUserService";
+
+    @GuardedBy("mLock")
+    private final ArrayMap<InteractionSessionId, ContentCaptureSession> mSessions =
+            new ArrayMap<>();
+
+    // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
+
+    protected IntelligencePerUserService(
+            IntelligenceManagerService master, Object lock, int userId) {
+        super(master, lock, userId);
+    }
+
+    @Override // from PerUserSystemService
+    protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
+            throws NameNotFoundException {
+
+        ServiceInfo si;
+        try {
+            // TODO(b/111276913): must check that either the service is from a system component,
+            // or it matches a service set by shell cmd (so it can be used on CTS tests and when
+            // OEMs are implementing the real service
+            si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                    PackageManager.GET_META_DATA, mUserId);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not get service for " + serviceComponent + ": " + e);
+            return null;
+        }
+        if (!Manifest.permission.BIND_INTELLIGENCE_SERVICE.equals(si.permission)) {
+            Slog.w(TAG, "IntelligenceService from '" + si.packageName
+                    + "' does not require permission "
+                    + Manifest.permission.BIND_INTELLIGENCE_SERVICE);
+            throw new SecurityException("Service does not require permission "
+                    + Manifest.permission.BIND_INTELLIGENCE_SERVICE);
+        }
+        return si;
+    }
+
+    // TODO(b/111276913): log metrics
+    @GuardedBy("mLock")
+    public void startSessionLocked(@NonNull IBinder activityToken,
+            @NonNull ComponentName componentName, int taskId, int displayId,
+            @NonNull InteractionSessionId sessionId, int flags,
+            @NonNull IResultReceiver resultReceiver) {
+        final ComponentName serviceComponentName = getServiceComponentName();
+        if (serviceComponentName == null) {
+            // TODO(b/111276913): this happens when the system service is starting, we should
+            // probably handle it in a more elegant way (like waiting for boot_complete or
+            // something like that
+            Slog.w(TAG, "startSession(" + activityToken + "): hold your horses");
+            return;
+        }
+
+        ContentCaptureSession session = mSessions.get(sessionId);
+        if (session != null) {
+            if (mMaster.debug) {
+                Slog.d(TAG, "startSession(): reusing session " + sessionId + " for "
+                        + componentName);
+            }
+            // TODO(b/111276913): check if local ids match and decide what to do if they don't
+            // TODO(b/111276913): should we call session.notifySessionStartedLocked() again??
+            // if not, move notifySessionStartedLocked() into session constructor
+            sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
+            return;
+        }
+
+        // TODO(b/117779333): get from mMaster once it's moved to superclass
+        final boolean bindInstantServiceAllowed = false;
+
+        session = new ContentCaptureSession(getContext(), mUserId, mLock, activityToken,
+                this, serviceComponentName, componentName, taskId, displayId, sessionId, flags,
+                bindInstantServiceAllowed, mMaster.verbose);
+        if (mMaster.verbose) {
+            Slog.v(TAG, "startSession(): new session for " + componentName + " and id "
+                    + sessionId);
+        }
+        mSessions.put(sessionId, session);
+        session.notifySessionStartedLocked();
+        sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
+    }
+
+    // TODO(b/111276913): log metrics
+    @GuardedBy("mLock")
+    public void finishSessionLocked(@NonNull InteractionSessionId sessionId) {
+        final ContentCaptureSession session = mSessions.get(sessionId);
+        if (session == null) {
+            Slog.w(TAG, "finishSession(): no session with id" + sessionId);
+            return;
+        }
+        if (mMaster.verbose) {
+            Slog.v(TAG, "finishSession(): " + session);
+        }
+        session.removeSelfLocked(true);
+    }
+
+    @GuardedBy("mLock")
+    public void sendEventsLocked(@NonNull InteractionSessionId sessionId,
+            @NonNull List<ContentCaptureEvent> events) {
+        final ContentCaptureSession session = mSessions.get(sessionId);
+        if (session == null) {
+            Slog.w(TAG, "sendEvents(): no session for " + sessionId);
+            return;
+        }
+        if (mMaster.verbose) {
+            Slog.v(TAG, "sendEvents(): id=" + sessionId + "; events =" + events.size());
+        }
+        session.sendEventsLocked(events);
+    }
+
+    @GuardedBy("mLock")
+    public void removeSessionLocked(@NonNull InteractionSessionId sessionId) {
+        mSessions.remove(sessionId);
+    }
+
+    @Override
+    protected void dumpLocked(String prefix, PrintWriter pw) {
+        super.dumpLocked(prefix, pw);
+        if (mSessions.isEmpty()) {
+            pw.print(prefix); pw.println("no sessions");
+        } else {
+            final int size = mSessions.size();
+            pw.print(prefix); pw.print("number sessions: "); pw.println(size);
+            final String prefix2 = prefix + "  ";
+            for (int i = 0; i < size; i++) {
+                pw.print(prefix); pw.print("session@"); pw.println(i);
+                final ContentCaptureSession session = mSessions.valueAt(i);
+                session.dumpLocked(prefix2, pw);
+            }
+        }
+    }
+
+    private static void sendToClient(@NonNull IResultReceiver resultReceiver, int value) {
+        try {
+            resultReceiver.send(value, null);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Error async reporting result to client: " + e);
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java
new file mode 100644
index 0000000..9d241fb
--- /dev/null
+++ b/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2018 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.server.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.service.intelligence.IIntelligenceService;
+import android.service.intelligence.InteractionContext;
+import android.service.intelligence.InteractionSessionId;
+import android.text.format.DateUtils;
+import android.util.Slog;
+import android.view.intelligence.ContentCaptureEvent;
+
+import com.android.server.AbstractRemoteService;
+
+import java.util.List;
+
+final class RemoteIntelligenceService extends AbstractRemoteService {
+
+    private static final String TAG = "RemoteIntelligenceService";
+
+    private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
+    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
+
+    private final RemoteIntelligenceServiceCallbacks mCallbacks;
+    private IIntelligenceService mService;
+
+    RemoteIntelligenceService(Context context, String serviceInterface,
+            ComponentName componentName, int userId,
+            RemoteIntelligenceServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
+            boolean verbose) {
+        super(context, serviceInterface, componentName, userId, callbacks,
+                bindInstantServiceAllowed, verbose);
+        mCallbacks = callbacks;
+    }
+
+    @Override // from RemoteService
+    protected IInterface getServiceInterface(@NonNull IBinder service) {
+        mService = IIntelligenceService.Stub.asInterface(service);
+        return mService;
+    }
+
+    // TODO(b/111276913): modify super class to allow permanent binding when value is 0 or negative
+    @Override // from RemoteService
+    protected long getTimeoutIdleBindMillis() {
+        // TODO(b/111276913): read from Settings so it can be changed in the field
+        return TIMEOUT_IDLE_BIND_MILLIS;
+    }
+
+    @Override // from RemoteService
+    protected long getRemoteRequestMillis() {
+        // TODO(b/111276913): read from Settings so it can be changed in the field
+        return TIMEOUT_REMOTE_REQUEST_MILLIS;
+    }
+
+    /**
+     * Called by {@link ContentCaptureSession} to generate a call to the
+     * {@link RemoteIntelligenceService} to indicate the session was created (when {@code context}
+     * is not {@code null} or destroyed (when {@code context} is {@code null}).
+     */
+    public void onSessionLifecycleRequest(@Nullable InteractionContext context,
+            @NonNull InteractionSessionId sessionId) {
+        cancelScheduledUnbind();
+        scheduleRequest(new PendingSessionLifecycleRequest(this, context, sessionId));
+    }
+
+    /**
+     * Called by {@link ContentCaptureSession} to send a batch of events to the service.
+     */
+    public void onContentCaptureEventsRequest(@NonNull InteractionSessionId sessionId,
+            @NonNull List<ContentCaptureEvent> events) {
+        cancelScheduledUnbind();
+        scheduleRequest(new PendingOnContentCaptureEventsRequest(this, sessionId, events));
+    }
+
+
+    private abstract static class MyPendingRequest
+            extends PendingRequest<RemoteIntelligenceService> {
+        protected final InteractionSessionId mSessionId;
+
+        private MyPendingRequest(@NonNull RemoteIntelligenceService service,
+                @NonNull InteractionSessionId sessionId) {
+            super(service);
+            mSessionId = sessionId;
+        }
+
+        @Override // from PendingRequest
+        protected final void onTimeout(RemoteIntelligenceService remoteService) {
+            Slog.w(TAG, "timed out handling " + getClass().getSimpleName() + " for "
+                    + mSessionId);
+            remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ true);
+        }
+
+        @Override // from PendingRequest
+        public final void run() {
+            final RemoteIntelligenceService remoteService = getService();
+            if (remoteService != null) {
+                try {
+                    myRun(remoteService);
+                    // We don't expect the service to call us back, so we finish right away.
+                    finish();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "exception handling " + getClass().getSimpleName() + " for "
+                            + mSessionId + ": " + e);
+                    remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ false);
+                }
+            }
+        }
+
+        protected abstract void myRun(@NonNull RemoteIntelligenceService service)
+                throws RemoteException;
+
+    }
+
+    private static final class PendingSessionLifecycleRequest extends MyPendingRequest {
+
+        private final InteractionContext mContext;
+
+        protected PendingSessionLifecycleRequest(@NonNull RemoteIntelligenceService service,
+                @Nullable InteractionContext context, @NonNull InteractionSessionId sessionId) {
+            super(service, sessionId);
+            mContext = context;
+        }
+
+        @Override // from MyPendingRequest
+        public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
+            remoteService.mService.onSessionLifecycle(mContext, mSessionId);
+        }
+    }
+
+    private static final class PendingOnContentCaptureEventsRequest extends MyPendingRequest {
+
+        private final List<ContentCaptureEvent> mEvents;
+
+        protected PendingOnContentCaptureEventsRequest(@NonNull RemoteIntelligenceService service,
+                @NonNull InteractionSessionId sessionId,
+                @NonNull List<ContentCaptureEvent> events) {
+            super(service, sessionId);
+            mEvents = events;
+        }
+
+        @Override // from MyPendingRequest
+        public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
+            remoteService.mService.onContentCaptureEvents(mSessionId, mEvents);
+        }
+    }
+
+    public interface RemoteIntelligenceServiceCallbacks extends VultureCallback {
+        // To keep it simple, we use the same callback for all failures / timeouts.
+        void onFailureOrTimeout(boolean timedOut);
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index eb31e78..1ad83ec 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -591,15 +591,15 @@
             if (DEBUG) {
                 Slog.i(TAG, "Backup enable apparently not migrated");
             }
-            final ContentResolver r = sInstance.mContext.getContentResolver();
-            final int enableState = Settings.Secure.getIntForUser(r,
+            ContentResolver resolver = sInstance.getContext().getContentResolver();
+            int enableState = Settings.Secure.getIntForUser(resolver,
                     Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
             if (enableState >= 0) {
                 if (DEBUG) {
                     Slog.i(TAG, "Migrating enable state " + (enableState != 0));
                 }
                 writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
-                Settings.Secure.putStringForUser(r,
+                Settings.Secure.putStringForUser(resolver,
                         Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
             } else {
                 if (DEBUG) {
@@ -2790,8 +2790,8 @@
             Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
                     + e.getMessage());
 
-            final ContentResolver r = sInstance.mContext.getContentResolver();
-            Settings.Secure.putStringForUser(r,
+            ContentResolver resolver = sInstance.getContext().getContentResolver();
+            Settings.Secure.putStringForUser(resolver,
                     Settings.Secure.BACKUP_ENABLED, null, userId);
             enableFile.delete();
             stage.delete();
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 818154b..32fd7e0 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -16,7 +16,10 @@
 
 package com.android.server.backup;
 
+import static com.android.server.backup.BackupManagerService.TAG;
+
 import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
 import android.app.backup.BackupManager;
 import android.app.backup.IBackupManager;
 import android.app.backup.IBackupManagerMonitor;
@@ -39,45 +42,52 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.Slog;
-
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
-
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
 
-
 /**
  * A proxy to BackupManagerService implementation.
  *
- * This is an external interface to the BackupManagerService which is being accessed via published
- * binder (see BackupManagerService$Lifecycle). This lets us turn down the heavy implementation
- * object on the fly without disturbing binders that have been cached somewhere in the system.
+ * <p>This is an external interface to the BackupManagerService which is being accessed via
+ * published binder (see BackupManagerService$Lifecycle). This lets us turn down the heavy
+ * implementation object on the fly without disturbing binders that have been cached somewhere in
+ * the system.
  *
- * This is where it is decided whether backup subsystem is available. It can be disabled with the
- * following two methods:
+ * <p>Trampoline determines whether the backup service is available. It can be disabled in the
+ * following two ways:
  *
  * <ul>
- * <li> Temporarily - create a file named Trampoline.BACKUP_SUPPRESS_FILENAME, or
- * <li> Product level - set Trampoline.BACKUP_DISABLE_PROPERTY system property to true.
+ *   <li>Temporary - create the file {@link #BACKUP_SUPPRESS_FILENAME}, or
+ *   <li>Permanent - set the system property {@link #BACKUP_DISABLE_PROPERTY} to true.
  * </ul>
+ *
+ * Temporary disabling is controlled by {@link #setBackupServiceActive(int, boolean)} through
+ * privileged callers (currently {@link DevicePolicyManager}). This is called on {@link
+ * UserHandle#USER_SYSTEM} and disables backup for all users.
+ *
+ * <p>Creation of the backup service is done when {@link UserHandle#USER_SYSTEM} is unlocked. The
+ * system user is unlocked before any other users.
  */
 public class Trampoline extends IBackupManager.Stub {
-    static final String TAG = "BackupManagerService";
-    static final boolean DEBUG_TRAMPOLINE = false;
+    // When this file is present, the backup service is inactive.
+    private static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress";
 
-    // When this file is present, the backup service is inactive
-    static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress";
+    // Product-level suppression of backup/restore.
+    private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
 
-    // Product-level suppression of backup/restore
-    static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
+    private final Context mContext;
 
-    final Context mContext;
-    final File mSuppressFile;   // existence testing & creating synchronized on 'this'
-    final boolean mGlobalDisable;
-    volatile BackupManagerService mService;
+    @GuardedBy("mStateLock")
+    private final File mSuppressFile;
 
+    private final boolean mGlobalDisable;
+    private final Object mStateLock = new Object();
+
+    private volatile BackupManagerService mService;
     private HandlerThread mHandlerThread;
 
     public Trampoline(Context context) {
@@ -100,78 +110,100 @@
                 BACKUP_SUPPRESS_FILENAME);
     }
 
+    protected Context getContext() {
+        return mContext;
+    }
+
     protected BackupManagerService createBackupManagerService() {
         return BackupManagerService.create(mContext, this, mHandlerThread);
     }
 
-    // internal control API
-    public void initialize(final int whichUser) {
-        // Note that only the owner user is currently involved in backup/restore
-        // TODO: http://b/22388012
-        if (whichUser == UserHandle.USER_SYSTEM) {
-            // Does this product support backup/restore at all?
-            if (mGlobalDisable) {
-                Slog.i(TAG, "Backup/restore not supported");
-                return;
-            }
+    /**
+     * Initialize {@link BackupManagerService} if the backup service is not disabled. Only the
+     * system user can initialize the service.
+     */
+    /* package */ void initializeService(int userId) {
+        if (mGlobalDisable) {
+            Slog.i(TAG, "Backup service not supported");
+            return;
+        }
 
-            synchronized (this) {
-                if (!mSuppressFile.exists()) {
-                    mService = createBackupManagerService();
-                } else {
-                    Slog.i(TAG, "Backup inactive in user " + whichUser);
-                }
+        if (userId != UserHandle.USER_SYSTEM) {
+            Slog.i(TAG, "Cannot initialize backup service for non-system user: " + userId);
+            return;
+        }
+
+        synchronized (mStateLock) {
+            if (!mSuppressFile.exists()) {
+                mService = createBackupManagerService();
+            } else {
+                Slog.i(TAG, "Backup service inactive");
             }
         }
     }
 
+    /**
+     * Called from {@link BackupManagerService$Lifecycle} when the system user is unlocked. Attempts
+     * to initialize {@link BackupManagerService} and set backup state for the system user.
+     *
+     * @see BackupManagerService#unlockSystemUser()
+     */
     void unlockSystemUser() {
         mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
         mHandlerThread.start();
 
         Handler h = new Handler(mHandlerThread.getLooper());
-        h.post(() -> {
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
-            initialize(UserHandle.USER_SYSTEM);
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        h.post(
+                () -> {
+                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
+                    initializeService(UserHandle.USER_SYSTEM);
+                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
-            BackupManagerService svc = mService;
-            Slog.i(TAG, "Unlocking system user; mService=" + mService);
-            if (svc != null) {
-                svc.unlockSystemUser();
-            }
-        });
+                    BackupManagerService service = mService;
+                    if (service != null) {
+                        Slog.i(TAG, "Unlocking system user");
+                        service.unlockSystemUser();
+                    }
+                });
     }
 
-    public void setBackupServiceActive(final int userHandle, boolean makeActive) {
-        // Only the DPM should be changing the active state of backup
-        final int caller = binderGetCallingUid();
-        if (caller != Process.SYSTEM_UID
-                && caller != Process.ROOT_UID) {
+    /**
+     * Only privileged callers should be changing the backup state. This method only acts on {@link
+     * UserHandle#USER_SYSTEM} and is a no-op if passed non-system users. Deactivating backup in the
+     * system user also deactivates backup in all users.
+     */
+    public void setBackupServiceActive(int userId, boolean makeActive) {
+        int caller = binderGetCallingUid();
+        if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
             throw new SecurityException("No permission to configure backup activity");
         }
 
         if (mGlobalDisable) {
-            Slog.i(TAG, "Backup/restore not supported");
+            Slog.i(TAG, "Backup service not supported");
             return;
         }
-        // TODO: http://b/22388012
-        if (userHandle == UserHandle.USER_SYSTEM) {
-            synchronized (this) {
-                if (makeActive != isBackupServiceActive(userHandle)) {
-                    Slog.i(TAG, "Making backup "
-                            + (makeActive ? "" : "in") + "active in user " + userHandle);
-                    if (makeActive) {
-                        mService = createBackupManagerService();
-                        mSuppressFile.delete();
-                    } else {
-                        mService = null;
-                        try {
-                            mSuppressFile.createNewFile();
-                        } catch (IOException e) {
-                            Slog.e(TAG, "Unable to persist backup service inactivity");
-                        }
-                    }
+
+        if (userId != UserHandle.USER_SYSTEM) {
+            Slog.i(TAG, "Cannot set backup service activity for non-system user: " + userId);
+            return;
+        }
+
+        if (makeActive == isBackupServiceActive(userId)) {
+            Slog.i(TAG, "No change in backup service activity");
+            return;
+        }
+
+        synchronized (mStateLock) {
+            Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
+            if (makeActive) {
+                mService = createBackupManagerService();
+                mSuppressFile.delete();
+            } else {
+                mService = null;
+                try {
+                    mSuppressFile.createNewFile();
+                } catch (IOException e) {
+                    Slog.e(TAG, "Unable to persist backup service inactivity");
                 }
             }
         }
@@ -182,14 +214,15 @@
     /**
      * Querying activity state of backup service. Calling this method before initialize yields
      * undefined result.
-     * @param userHandle The user in which the activity state of backup service is queried.
+     *
+     * @param userId The user in which the activity state of backup service is queried.
      * @return true if the service is active.
      */
     @Override
-    public boolean isBackupServiceActive(final int userHandle) {
+    public boolean isBackupServiceActive(int userId) {
         // TODO: http://b/22388012
-        if (userHandle == UserHandle.USER_SYSTEM) {
-            synchronized (this) {
+        if (userId == UserHandle.USER_SYSTEM) {
+            synchronized (mStateLock) {
                 return mService != null;
             }
         }
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/ChunkEncryptor.java b/services/backup/java/com/android/server/backup/encryption/chunking/ChunkEncryptor.java
new file mode 100644
index 0000000..812cfbd
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/ChunkEncryptor.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 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.server.backup.encryption.chunking;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+/** Encrypts chunks of a file using AES/GCM. */
+public class ChunkEncryptor {
+    private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+    private static final int GCM_NONCE_LENGTH_BYTES = 12;
+    private static final int GCM_TAG_LENGTH_BYTES = 16;
+
+    private final SecretKey mSecretKey;
+    private final SecureRandom mSecureRandom;
+
+    /**
+     * A new instance using {@code mSecretKey} to encrypt chunks and {@code mSecureRandom} to
+     * generate nonces.
+     */
+    public ChunkEncryptor(SecretKey secretKey, SecureRandom secureRandom) {
+        this.mSecretKey = secretKey;
+        this.mSecureRandom = secureRandom;
+    }
+
+    /**
+     * Transforms {@code plaintext} into an {@link EncryptedChunk}.
+     *
+     * @param plaintextHash The hash of the plaintext to encrypt, to attach as the key of the chunk.
+     * @param plaintext Bytes to encrypt.
+     * @throws InvalidKeyException If the given secret key is not a valid AES key for decryption.
+     * @throws IllegalBlockSizeException If the input data cannot be encrypted using
+     *     AES/GCM/NoPadding. This should never be the case.
+     */
+    public EncryptedChunk encrypt(ChunkHash plaintextHash, byte[] plaintext)
+            throws InvalidKeyException, IllegalBlockSizeException {
+        byte[] nonce = generateNonce();
+        Cipher cipher;
+        try {
+            cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+            cipher.init(
+                    Cipher.ENCRYPT_MODE,
+                    mSecretKey,
+                    new GCMParameterSpec(GCM_TAG_LENGTH_BYTES * 8, nonce));
+        } catch (NoSuchAlgorithmException
+                | NoSuchPaddingException
+                | InvalidAlgorithmParameterException e) {
+            // This can not happen - AES/GCM/NoPadding is supported.
+            throw new AssertionError(e);
+        }
+        byte[] encryptedBytes;
+        try {
+            encryptedBytes = cipher.doFinal(plaintext);
+        } catch (BadPaddingException e) {
+            // This can not happen - BadPaddingException can only be thrown in decrypt mode.
+            throw new AssertionError("Impossible: threw BadPaddingException in encrypt mode.");
+        }
+
+        return EncryptedChunk.create(/*key=*/ plaintextHash, nonce, encryptedBytes);
+    }
+
+    private byte[] generateNonce() {
+        byte[] nonce = new byte[GCM_NONCE_LENGTH_BYTES];
+        mSecureRandom.nextBytes(nonce);
+        return nonce;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/ChunkHasher.java b/services/backup/java/com/android/server/backup/encryption/chunking/ChunkHasher.java
new file mode 100644
index 0000000..145b7bf
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/ChunkHasher.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 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.server.backup.encryption.chunking;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+
+/** Computes the SHA-256 HMAC of a chunk of bytes. */
+public class ChunkHasher {
+    private static final String MAC_ALGORITHM = "HmacSHA256";
+
+    private final SecretKey mSecretKey;
+
+    /** Constructs a new hasher which computes the HMAC using the given secret key. */
+    public ChunkHasher(SecretKey secretKey) {
+        this.mSecretKey = secretKey;
+    }
+
+    /** Returns the SHA-256 over the given bytes. */
+    public ChunkHash computeHash(byte[] plaintext) throws InvalidKeyException {
+        try {
+            Mac mac = Mac.getInstance(MAC_ALGORITHM);
+            mac.init(mSecretKey);
+            return new ChunkHash(mac.doFinal(plaintext));
+        } catch (NoSuchAlgorithmException e) {
+            // This can not happen - AES/GCM/NoPadding is available as part of the framework.
+            throw new AssertionError(e);
+        }
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/Chunker.java b/services/backup/java/com/android/server/backup/encryption/chunking/Chunker.java
new file mode 100644
index 0000000..b91913e
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/Chunker.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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.server.backup.encryption.chunking;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+
+/** Splits an input stream into chunks, which are to be encrypted separately. */
+public interface Chunker {
+    /**
+     * Splits the input stream into chunks.
+     *
+     * @param inputStream The input stream.
+     * @param chunkConsumer A function that processes each chunk as it is produced.
+     * @throws IOException If there is a problem reading the input stream.
+     * @throws GeneralSecurityException if the consumer function throws an error.
+     */
+    void chunkify(InputStream inputStream, ChunkConsumer chunkConsumer)
+            throws IOException, GeneralSecurityException;
+
+    /** Function that consumes chunks. */
+    interface ChunkConsumer {
+        /**
+         * Invoked for each chunk.
+         *
+         * @param chunk Plaintext bytes of chunk.
+         * @throws GeneralSecurityException if there is an issue encrypting the chunk.
+         */
+        void accept(byte[] chunk) throws GeneralSecurityException;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/RawBackupWriter.java b/services/backup/java/com/android/server/backup/encryption/chunking/RawBackupWriter.java
new file mode 100644
index 0000000..839dc7c
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/RawBackupWriter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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.server.backup.encryption.chunking;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** Writes data straight to an output stream. */
+public class RawBackupWriter implements BackupWriter {
+    private final OutputStream outputStream;
+    private long bytesWritten;
+
+    /** Constructs a new writer which writes bytes to the given output stream. */
+    public RawBackupWriter(OutputStream outputStream) {
+        this.outputStream = outputStream;
+    }
+
+    @Override
+    public void writeBytes(byte[] bytes) throws IOException {
+        outputStream.write(bytes);
+        bytesWritten += bytes.length;
+    }
+
+    @Override
+    public void writeChunk(long start, int length) throws IOException {
+        throw new UnsupportedOperationException("RawBackupWriter cannot write existing chunks");
+    }
+
+    @Override
+    public long getBytesWritten() {
+        return bytesWritten;
+    }
+
+    @Override
+    public void flush() throws IOException {
+        outputStream.flush();
+    }
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 2fa2941..888ad1d 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -55,7 +55,7 @@
     srcs: [":services.core.unboosted"],
     tools: ["lockedregioncodeinjection"],
     cmd: "$(location lockedregioncodeinjection) " +
-        "  --targets \"Lcom/android/server/am/ActivityManagerService;,Lcom/android/server/wm/WindowHashMap;\" " +
+        "  --targets \"Lcom/android/server/am/ActivityManagerService;,Lcom/android/server/wm/WindowManagerGlobalLock;\" " +
         "  --pre \"com/android/server/am/ActivityManagerService.boostPriorityForLockedSection,com/android/server/wm/WindowManagerService.boostPriorityForLockedSection\" " +
         "  --post \"com/android/server/am/ActivityManagerService.resetPriorityAfterLockedSection,com/android/server/wm/WindowManagerService.resetPriorityAfterLockedSection\" " +
         "  -o $(out) " +
diff --git a/services/core/java/com/android/server/AbstractMasterSystemService.java b/services/core/java/com/android/server/AbstractMasterSystemService.java
new file mode 100644
index 0000000..c955daf
--- /dev/null
+++ b/services/core/java/com/android/server/AbstractMasterSystemService.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2018 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.server;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.UserManagerInternal;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Base class for {@link SystemService SystemServices} that support multi user.
+ *
+ * <p>Subclasses of this service are just a facade for the service binder calls - the "real" work
+ * is done by the {@link AbstractPerUserSystemService} subclasses, which are automatically managed
+ * through an user -> service cache.
+ *
+ * <p>It also takes care of other plumbing tasks such as:
+ *
+ * <ul>
+ *   <li>Disabling the service when {@link UserManager} restrictions change.
+ *   <li>Refreshing the service when its underlying
+ *   {@link #getServiceSettingsProperty() Settings property} changed.
+ *   <li>Calling the service when other Settings properties changed.
+ * </ul>
+ *
+ * <p>See {@code com.android.server.autofill.AutofillManagerService} for a concrete
+ * (no pun intended) example of how to use it.
+ *
+ * @param <S> "real" service class.
+ *
+ * @hide
+ */
+// TODO(b/117779333): improve javadoc above instead of using Autofill as an example
+public abstract class AbstractMasterSystemService<S extends AbstractPerUserSystemService<S>>
+        extends SystemService {
+
+    /**
+     * Log tag
+     */
+    protected final String mTag = getClass().getSimpleName();
+
+    /**
+     * Lock used to synchronize access to internal state; should be acquired before calling a
+     * method whose name ends with {@code locked}.
+     */
+    protected final Object mLock = new Object();
+
+    /**
+     * Whether the service should log debug statements.
+     */
+    public boolean verbose = false;
+
+    /**
+     * Whether the service should log verbose statements.
+     */
+    public boolean debug = false;
+
+    /**
+     * Users disabled due to {@link UserManager} restrictions, or {@code null} if the service cannot
+     * be disabled through {@link UserManager}.
+     */
+    @GuardedBy("mLock")
+    @Nullable
+    private final SparseBooleanArray mDisabledUsers;
+
+    /**
+     * Cache of services per user id.
+     */
+    @GuardedBy("mLock")
+    private final SparseArray<S> mServicesCache = new SparseArray<>();
+
+    /**
+     * Default constructor.
+     *
+     * @param context system context.
+     * @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that
+     *        disables the service.
+     */
+    protected AbstractMasterSystemService(@NonNull Context context,
+            @Nullable String disallowProperty) {
+        super(context);
+
+        if (disallowProperty == null) {
+            mDisabledUsers = null;
+        } else {
+            mDisabledUsers = new SparseBooleanArray();
+            // Hookup with UserManager to disable service when necessary.
+            final UserManager um = context.getSystemService(UserManager.class);
+            final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+            final List<UserInfo> users = um.getUsers();
+            for (int i = 0; i < users.size(); i++) {
+                final int userId = users.get(i).id;
+                final boolean disabled = umi.getUserRestriction(userId, disallowProperty);
+                if (disabled) {
+                    Slog.i(mTag, "Disabling for user " + userId);
+                    mDisabledUsers.put(userId, disabled);
+                }
+            }
+            umi.addUserRestrictionsListener((userId, newRestrictions, prevRestrictions) -> {
+                final boolean disabledNow =
+                        newRestrictions.getBoolean(disallowProperty, false);
+                synchronized (mLock) {
+                    final boolean disabledBefore = mDisabledUsers.get(userId);
+                    if (disabledBefore == disabledNow) {
+                        // Nothing changed, do nothing.
+                        if (debug) {
+                            Slog.d(mTag, "Restriction did not change for user " + userId);
+                            return;
+                        }
+                    }
+                    Slog.i(mTag, "Updating for user " + userId + ": disabled=" + disabledNow);
+                    mDisabledUsers.put(userId, disabledNow);
+                    updateCachedServiceLocked(userId, disabledNow);
+                }
+            });
+        }
+        startTrackingPackageChanges();
+    }
+
+    @Override // from SystemService
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+            new SettingsObserver(BackgroundThread.getHandler());
+        }
+    }
+
+    @Override // from SystemService
+    public void onUnlockUser(int userId) {
+        synchronized (mLock) {
+            updateCachedServiceLocked(userId);
+        }
+    }
+
+    @Override // from SystemService
+    public void onCleanupUser(int userId) {
+        synchronized (mLock) {
+            removeCachedServiceLocked(userId);
+        }
+    }
+
+    /**
+     * Creates a new service that will be added to the cache.
+     *
+     * @param resolvedUserId the resolved user id for the service.
+     * @param disabled whether the service is currently disabled (due to {@link UserManager}
+     * restrictions).
+     *
+     * @return a new instance.
+     */
+    protected abstract S newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled);
+
+    /**
+     * Register the service for extra Settings changes (i.e., other than
+     * {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
+     * {@link #getServiceSettingsProperty()}, which are automatically handled).
+     *
+     * <p> Example:
+     *
+     * <pre><code>
+     * resolver.registerContentObserver(Settings.Global.getUriFor(
+     *     Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, observer,
+     *     UserHandle.USER_ALL);
+     * </code></pre>
+     *
+     * <p><b>NOTE: </p>it doesn't need to register for
+     * {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
+     * {@link #getServiceSettingsProperty()}.
+     *
+     */
+    @SuppressWarnings("unused")
+    protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
+            @NonNull ContentObserver observer) {
+    }
+
+    /**
+     * Callback for Settings changes that were registered though
+     * {@link #registerForExtraSettingsChanges(ContentResolver, ContentObserver)}.
+     *
+     * @param userId user associated with the change
+     * @param property Settings property changed.
+     */
+    protected void onSettingsChanged(@UserIdInt int userId, @NonNull String property) {
+    }
+
+    /**
+     * Gets the service instance for an user, creating an instance if not present in the cache.
+     */
+    @GuardedBy("mLock")
+    @NonNull
+    protected S getServiceForUserLocked(@UserIdInt int userId) {
+        final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, false, null, null);
+        S service = mServicesCache.get(resolvedUserId);
+        if (service == null) {
+            final boolean disabled = isDisabledLocked(userId);
+            service = newServiceLocked(resolvedUserId, disabled);
+            if (!disabled) {
+                onServiceEnabledLocked(service, resolvedUserId);
+            }
+            mServicesCache.put(userId, service);
+        }
+        return service;
+    }
+
+    /**
+     * Gets the <b>existing</b> service instance for a user, returning {@code null} if not already
+     * present in the cache.
+     */
+    @GuardedBy("mLock")
+    @Nullable
+    protected S peekServiceForUserLocked(int userId) {
+        final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, false, null, null);
+        return mServicesCache.get(resolvedUserId);
+    }
+
+    /**
+     * Updates a cached service for a given user.
+     */
+    @GuardedBy("mLock")
+    protected void updateCachedServiceLocked(int userId) {
+        updateCachedServiceLocked(userId, isDisabledLocked(userId));
+    }
+
+    /**
+     * Checks whether the service is disabled (through {@link UserManager} restrictions) for the
+     * given user.
+     */
+    protected boolean isDisabledLocked(int userId) {
+        return mDisabledUsers == null ? false : mDisabledUsers.get(userId);
+    }
+
+    /**
+     * Updates a cached service for a given user.
+     *
+     * @param userId user handle.
+     * @param disabled whether the user is disabled.
+     * @return service for the user.
+     */
+    @GuardedBy("mLock")
+    protected S updateCachedServiceLocked(int userId, boolean disabled) {
+        final S service = getServiceForUserLocked(userId);
+        if (service != null) {
+            service.updateLocked(disabled);
+            if (!service.isEnabledLocked()) {
+                removeCachedServiceLocked(userId);
+            } else {
+                onServiceEnabledLocked(service, userId);
+            }
+        }
+        return service;
+    }
+
+    /**
+     * Gets the Settings property that defines the name of the component name used to bind this
+     * service to an external service, or {@code null} when the service is not defined by such
+     * property (for example, if it's a system service defined by framework resources).
+     */
+    @Nullable
+    protected String getServiceSettingsProperty() {
+        return null;
+    }
+
+    /**
+     * Callback called after a new service was added to the cache, or an existing service that was
+     * previously disabled gets enabled.
+     *
+     * <p>By default doesn't do anything, but can be overridden by subclasses.
+     */
+    @SuppressWarnings("unused")
+    protected void onServiceEnabledLocked(S service, @UserIdInt int userId) {
+    }
+
+    /**
+     * Removes a cached service for a given user.
+     *
+     * @return the removed service;
+     */
+    @GuardedBy("mLock")
+    @NonNull
+    protected S removeCachedServiceLocked(@UserIdInt int userId) {
+        final S service = peekServiceForUserLocked(userId);
+        if (service != null) {
+            mServicesCache.delete(userId);
+        }
+        return service;
+    }
+
+    /**
+     * Visits all services in the cache.
+     */
+    @GuardedBy("mLock")
+    protected void visitServicesLocked(@NonNull Visitor<S> visitor) {
+        final int size = mServicesCache.size();
+        for (int i = 0; i < size; i++) {
+            visitor.visit(mServicesCache.valueAt(i));
+        }
+    }
+
+    /**
+     * Clear the cache by removing all services.
+     */
+    @GuardedBy("mLock")
+    protected void clearCacheLocked() {
+        mServicesCache.clear();
+    }
+
+    // TODO(b/117779333): support proto
+    protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+        boolean realDebug = debug;
+        boolean realVerbose = verbose;
+
+        try {
+            // Temporarily turn on full logging;
+            debug = verbose = true;
+            final int size = mServicesCache.size();
+            pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
+            pw.print(" Verbose: "); pw.println(realVerbose);
+            pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
+            pw.print(prefix); pw.print("Settings property: "); pw.println(
+                    getServiceSettingsProperty());
+            pw.print(prefix); pw.print("Cached services: ");
+            if (size == 0) {
+                pw.println("none");
+            } else {
+                pw.println(size);
+                final String prefix2 = "    ";
+                for (int i = 0; i < size; i++) {
+                    pw.print(prefix); pw.print("Service at "); pw.print(i); pw.println(": ");
+                    final S service = mServicesCache.valueAt(i);
+                    service.dumpLocked(prefix2, pw);
+                    pw.println();
+                }
+            }
+        } finally {
+            debug = realDebug;
+            verbose = realVerbose;
+        }
+    }
+
+    private void startTrackingPackageChanges() {
+        PackageMonitor monitor = new PackageMonitor() {
+            @Override
+            public void onSomePackagesChanged() {
+                synchronized (mLock) {
+                    updateCachedServiceLocked(getChangingUserId());
+                }
+            }
+
+            @Override
+            public void onPackageUpdateFinished(String packageName, int uid) {
+                synchronized (mLock) {
+                    final String activePackageName = getActiveServicePackageName();
+                    if (packageName.equals(activePackageName)) {
+                        removeCachedServiceLocked(getChangingUserId());
+                    } else {
+                        handlePackageUpdateLocked(packageName);
+                    }
+                }
+            }
+
+            @Override
+            public void onPackageRemoved(String packageName, int uid) {
+                synchronized (mLock) {
+                    final int userId = getChangingUserId();
+                    final S service = peekServiceForUserLocked(userId);
+                    if (service != null) {
+                        final ComponentName componentName = service.getServiceComponentName();
+                        if (componentName != null) {
+                            if (packageName.equals(componentName.getPackageName())) {
+                                handleActiveServiceRemoved(userId);
+                            }
+                        }
+                    }
+                }
+            }
+
+            @Override
+            public boolean onHandleForceStop(Intent intent, String[] packages,
+                    int uid, boolean doit) {
+                synchronized (mLock) {
+                    final String activePackageName = getActiveServicePackageName();
+                    for (String pkg : packages) {
+                        if (pkg.equals(activePackageName)) {
+                            if (!doit) {
+                                return true;
+                            }
+                            removeCachedServiceLocked(getChangingUserId());
+                        } else {
+                            handlePackageUpdateLocked(pkg);
+                        }
+                    }
+                }
+                return false;
+            }
+
+            private void handleActiveServiceRemoved(@UserIdInt int userId) {
+                removeCachedServiceLocked(userId);
+                final String serviceSettingsProperty = getServiceSettingsProperty();
+                if (serviceSettingsProperty != null) {
+                    Settings.Secure.putStringForUser(getContext().getContentResolver(),
+                            serviceSettingsProperty, null, userId);
+                }
+            }
+
+            private String getActiveServicePackageName() {
+                final int userId = getChangingUserId();
+                final S service = peekServiceForUserLocked(userId);
+                if (service == null) {
+                    return null;
+                }
+                final ComponentName serviceComponent = service.getServiceComponentName();
+                if (serviceComponent == null) {
+                    return null;
+                }
+                return serviceComponent.getPackageName();
+            }
+
+            @GuardedBy("mLock")
+            private void handlePackageUpdateLocked(String packageName) {
+                visitServicesLocked((s) -> s.handlePackageUpdateLocked(packageName));
+            }
+        };
+
+        // package changes
+        monitor.register(getContext(), null,  UserHandle.ALL, true);
+    }
+
+    /**
+     * Visitor pattern.
+     *
+     * @param <S> visited class.
+     */
+    public interface Visitor<S> {
+        /**
+         * Visits a service.
+         *
+         * @param service the service to be visited.
+         */
+        void visit(@NonNull S service);
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+            ContentResolver resolver = getContext().getContentResolver();
+            final String serviceProperty = getServiceSettingsProperty();
+            if (serviceProperty != null) {
+                resolver.registerContentObserver(Settings.Secure.getUriFor(
+                        serviceProperty), false, this, UserHandle.USER_ALL);
+            }
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL);
+            registerForExtraSettingsChanges(resolver, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
+            if (verbose) Slog.v(mTag, "onChange(): uri=" + uri + ", userId=" + userId);
+            final String property = uri.getLastPathSegment();
+            if (property.equals(getServiceSettingsProperty())
+                    || property.equals(Settings.Secure.USER_SETUP_COMPLETE)) {
+                synchronized (mLock) {
+                    updateCachedServiceLocked(userId);
+                }
+            } else {
+                onSettingsChanged(userId, property);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/AbstractPerUserSystemService.java
new file mode 100644
index 0000000..201abe6
--- /dev/null
+++ b/services/core/java/com/android/server/AbstractPerUserSystemService.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2018 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.server;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+
+/**
+ * Companion for {@link AbstractMasterSystemService}, it's the base class for the "real" service
+ * implementation.
+ *
+ * @param <S> itself
+ *
+ * @hide
+ */
+public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S>> {
+
+    protected final @UserIdInt int mUserId;
+    protected final Object mLock;
+    protected final String mTag = getClass().getSimpleName();
+
+    protected final AbstractMasterSystemService<S> mMaster;
+
+    /**
+     * Whether service was disabled for user due to {@link UserManager} restrictions.
+     */
+    @GuardedBy("mLock")
+    private boolean mDisabled;
+
+    /**
+     * Caches whether the setup completed for the current user.
+     */
+    @GuardedBy("mLock")
+    private boolean mSetupComplete;
+
+    @GuardedBy("mLock")
+    private ServiceInfo mServiceInfo;
+
+    protected AbstractPerUserSystemService(@NonNull AbstractMasterSystemService<S> master,
+            @NonNull Object lock, @UserIdInt int userId) {
+        mMaster = master;
+        mLock = lock;
+        mUserId = userId;
+    }
+
+    /**
+     * Creates a new {@link ServiceInfo} for the given service name.
+     *
+     * @throws NameNotFoundException if the service does not exist.
+     * @throws SecurityException if the service does not have the proper permissions to be bound to.
+     */
+    protected abstract ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
+            throws NameNotFoundException;
+
+    /**
+     * Callback called when an app has been updated.
+     *
+     * @param packageName package of the app being updated.
+     */
+    protected void handlePackageUpdateLocked(@NonNull String packageName) {
+    }
+
+    /**
+     * Gets whether the service is enabled and ready.
+     */
+    @GuardedBy("mLock")
+    protected boolean isEnabledLocked() {
+        return mSetupComplete && mServiceInfo != null && !mDisabled;
+    }
+
+    /**
+     * Updates the state of this service.
+     *
+     * <p>Typically called when the service {@link Settings} property or {@link UserManager}
+     * restriction changed, which includes the initial creation of the service.
+     *
+     * <p>Subclasses can extend this method to provide extra initialization.
+     *
+     * @param disabled whether the service is disabled (due to {@link UserManager} restrictions).
+     *
+     * @return whether the disabled state changed.
+     */
+    @GuardedBy("mLock")
+    @CallSuper
+    protected boolean updateLocked(boolean disabled) {
+
+        final boolean wasEnabled = isEnabledLocked();
+        if (mMaster.verbose) {
+            Slog.v(mTag, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
+                    + ", mSetupComplete=" + mSetupComplete
+                    + ", disabled=" + disabled + ", mDisabled=" + mDisabled);
+        }
+
+        mSetupComplete = isSetupCompletedLocked();
+        mDisabled = disabled;
+        ComponentName serviceComponent = null;
+        ServiceInfo serviceInfo = null;
+        final String componentName = getComponentNameFromSettings();
+        if (!TextUtils.isEmpty(componentName)) {
+            try {
+                serviceComponent = ComponentName.unflattenFromString(componentName);
+                serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                        0, mUserId);
+                if (serviceInfo == null) {
+                    Slog.e(mTag, "Bad service name: " + componentName);
+                }
+            } catch (RuntimeException | RemoteException e) {
+                Slog.e(mTag, "Error getting service info for '" + componentName + "': " + e);
+                serviceInfo = null;
+            }
+        }
+        try {
+            if (serviceInfo != null) {
+                mServiceInfo = newServiceInfo(serviceComponent);
+                if (mMaster.debug) {
+                    Slog.d(mTag, "Set component for user " + mUserId + " as " + mServiceInfo);
+                }
+            } else {
+                mServiceInfo = null;
+                if (mMaster.debug) {
+                    Slog.d(mTag, "Reset component for user " + mUserId + ":" + componentName);
+                }
+            }
+        } catch (Exception e) {
+            Slog.e(mTag, "Bad ServiceInfo for '" + componentName + "': " + e);
+            mServiceInfo = null;
+        }
+        return wasEnabled != isEnabledLocked();
+    }
+
+    /**
+     * Gets this UID of the remote service this service binds to, or {@code -1} if the service is
+     * disabled.
+     */
+    @GuardedBy("mLock")
+    protected final int getServiceUidLocked() {
+        if (mServiceInfo == null) {
+            Slog.w(mTag, "getServiceUidLocked(): no mServiceInfo");
+            return Process.INVALID_UID;
+        }
+        return mServiceInfo.applicationInfo.uid;
+    }
+
+    /**
+     * Gets this name of the remote service this service binds to as defined by {@link Settings}.
+     */
+    @Nullable
+    protected final String getComponentNameFromSettings() {
+        final String property = mMaster.getServiceSettingsProperty();
+        return property == null ? null : Settings.Secure
+                .getStringForUser(getContext().getContentResolver(), property, mUserId);
+    }
+
+    /**
+     * Gets the {@link ComponentName} of the remote service this service binds to, or {@code null}
+     * if the service is disabled.
+     */
+    @Nullable
+    public final ComponentName getServiceComponentName() {
+        synchronized (mLock) {
+            return mServiceInfo == null ? null : mServiceInfo.getComponentName();
+        }
+    }
+    /**
+     * Gets the name of the of the app this service binds to, or {@code null} if the service is
+     * disabled.
+     */
+    @Nullable
+    public final String getServicePackageName() {
+        final ComponentName serviceComponent = getServiceComponentName();
+        return serviceComponent == null ? null : serviceComponent.getPackageName();
+    }
+
+    /**
+     * Gets the user-visibile name of the service this service binds to, or {@code null} if the
+     * service is disabled.
+     */
+    @Nullable
+    @GuardedBy("mLock")
+    public final CharSequence getServiceLabelLocked() {
+        return mServiceInfo == null ? null : mServiceInfo.loadSafeLabel(
+                getContext().getPackageManager(), 0 /* do not ellipsize */,
+                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
+    }
+
+    /**
+     * Gets the icon the service this service binds to, or {@code null} if the service is disabled.
+     */
+    @Nullable
+    @GuardedBy("mLock")
+    public final Drawable getServiceIconLocked() {
+        return mServiceInfo == null ? null
+                : mServiceInfo.loadIcon(getContext().getPackageManager());
+    }
+
+    /**
+     * Whether the service should log debug statements.
+     */
+    public final boolean isDebug() {
+        return mMaster.debug;
+    }
+
+    /**
+     * Whether the service should log verbose statements.
+     */
+    public final boolean isVerbose() {
+        return mMaster.verbose;
+    }
+
+    /**
+     * Gets the target SDK level of the service this service binds to,
+     * or {@code 0} if the service is disabled.
+     */
+    public final int getTargedSdkLocked() {
+        return mServiceInfo == null ? 0 : mServiceInfo.applicationInfo.targetSdkVersion;
+    }
+
+    /**
+     * Gets whether the device already finished setup.
+     */
+    protected final boolean isSetupCompletedLocked() {
+        final String setupComplete = Settings.Secure.getStringForUser(
+                getContext().getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId);
+        return "1".equals(setupComplete);
+    }
+
+    /**
+     * Gets the context associated with this service.
+     */
+    protected final Context getContext() {
+        return mMaster.getContext();
+    }
+
+    // TODO(b/117779333): support proto
+    @GuardedBy("mLock")
+    protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+        pw.print(prefix); pw.print("User: "); pw.println(mUserId);
+        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+        pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
+        pw.print(prefix); pw.print("Service name: "); pw.println(getComponentNameFromSettings());
+    }
+}
diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/AbstractRemoteService.java
new file mode 100644
index 0000000..181d7fd
--- /dev/null
+++ b/services/core/java/com/android/server/AbstractRemoteService.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2018 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.server;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+
+/**
+ * Base class representing a remote service.
+ *
+ * <p>It abstracts away the binding and unbinding from the remote implementation, so clients can
+ * call its methods without worrying about when and how to bind/unbind/timeout.
+ *
+ * <p>All state of this class is modified on a handler thread.
+ *
+ * <p>See {@code com.android.server.autofill.RemoteFillService} for a concrete
+ * (no pun intended) example of how to use it.
+ *
+ * @hide
+ */
+//TODO(b/117779333): improve javadoc above instead of using Autofill as an example
+public abstract class AbstractRemoteService implements DeathRecipient {
+
+    private static final int MSG_UNBIND = 1;
+
+    protected static final int LAST_PRIVATE_MSG = MSG_UNBIND;
+
+    // TODO(b/117779333): convert all booleans into an integer / flags
+    public final boolean mVerbose;
+
+    protected final String mTag = getClass().getSimpleName();
+    protected final Handler mHandler;
+    protected final ComponentName mComponentName;
+
+    protected PendingRequest<? extends AbstractRemoteService> mPendingRequest;
+
+    private final Context mContext;
+    private final Intent mIntent;
+    private final VultureCallback mVultureCallback;
+    private final int mUserId;
+    private final ServiceConnection mServiceConnection = new RemoteServiceConnection();
+    private final boolean mBindInstantServiceAllowed;
+    private IInterface mServiceInterface;
+
+    private boolean mBinding;
+    private boolean mDestroyed;
+    private boolean mServiceDied;
+    private boolean mCompleted;
+
+    /**
+     * Callback called when the service dies.
+     */
+    public interface VultureCallback {
+        /**
+         * Called when the service dies.
+         *
+         * @param service service that died!
+         */
+        void onServiceDied(AbstractRemoteService service);
+    }
+
+    public AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface,
+            @NonNull ComponentName componentName, int userId, @NonNull VultureCallback callback,
+            boolean bindInstantServiceAllowed, boolean verbose) {
+        mContext = context;
+        mVultureCallback = callback;
+        mVerbose = verbose;
+        mComponentName = componentName;
+        mIntent = new Intent(serviceInterface).setComponent(mComponentName);
+        mUserId = userId;
+        mHandler = new Handler(FgThread.getHandler().getLooper());
+        mBindInstantServiceAllowed = bindInstantServiceAllowed;
+    }
+
+    /**
+     * Destroys this service.
+     */
+    public final void destroy() {
+        mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleDestroy, this));
+    }
+
+    /**
+     * Checks whether this service is destroyed.
+     */
+    public final boolean isDestroyed() {
+        return mDestroyed;
+    }
+
+    /**
+     * Callback called when the system connected / disconnected to the service.
+     *
+     * @param state {@code true} when connected, {@code false} when disconnected.
+     */
+    protected void onConnectedStateChanged(boolean state) {
+    }
+
+    /**
+     * Gets the base Binder interface from the service.
+     */
+    @NonNull
+    protected abstract IInterface getServiceInterface(@NonNull IBinder service);
+
+    /**
+     * Defines How long after the last interaction with the service we would unbind.
+     */
+    protected abstract long getTimeoutIdleBindMillis();
+
+    /**
+     * Defines how long after we make a remote request to a fill service we timeout.
+     */
+    protected abstract long getRemoteRequestMillis();
+
+    private void handleDestroy() {
+        if (checkIfDestroyed()) return;
+        if (mPendingRequest != null) {
+            mPendingRequest.cancel();
+            mPendingRequest = null;
+        }
+        ensureUnbound();
+        mDestroyed = true;
+    }
+
+    @Override // from DeathRecipient
+    public void binderDied() {
+        mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleBinderDied, this));
+    }
+
+    private void handleBinderDied() {
+        if (checkIfDestroyed()) return;
+        if (mServiceInterface != null) {
+            mServiceInterface.asBinder().unlinkToDeath(this, 0);
+        }
+        mServiceInterface = null;
+        mServiceDied = true;
+        mVultureCallback.onServiceDied(this);
+    }
+
+    // Note: we are dumping without a lock held so this is a bit racy but
+    // adding a lock to a class that offloads to a handler thread would
+    // mean adding a lock adding overhead to normal runtime operation.
+    /**
+     * Dump it!
+     */
+    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        String tab = "  ";
+        pw.append(prefix).append("service:").println();
+        pw.append(prefix).append(tab).append("userId=")
+                .append(String.valueOf(mUserId)).println();
+        pw.append(prefix).append(tab).append("componentName=")
+                .append(mComponentName.flattenToString()).println();
+        pw.append(prefix).append(tab).append("destroyed=")
+                .append(String.valueOf(mDestroyed)).println();
+        pw.append(prefix).append(tab).append("bound=")
+                .append(String.valueOf(isBound())).println();
+        pw.append(prefix).append(tab).append("hasPendingRequest=")
+                .append(String.valueOf(mPendingRequest != null)).println();
+        pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed);
+        pw.append(prefix).append("idleTimeout=")
+            .append(Long.toString(getTimeoutIdleBindMillis() / 1000)).append("s").println();
+        pw.append(prefix).append("requestTimeout=")
+            .append(Long.toString(getRemoteRequestMillis() / 1000)).append("s").println();
+        pw.println();
+    }
+
+    protected void scheduleRequest(PendingRequest<? extends AbstractRemoteService> pendingRequest) {
+        mHandler.sendMessage(obtainMessage(
+                AbstractRemoteService::handlePendingRequest, this, pendingRequest));
+    }
+
+    protected void cancelScheduledUnbind() {
+        mHandler.removeMessages(MSG_UNBIND);
+    }
+
+    protected void scheduleUnbind() {
+        cancelScheduledUnbind();
+        mHandler.sendMessageDelayed(obtainMessage(AbstractRemoteService::handleUnbind, this)
+                .setWhat(MSG_UNBIND), getTimeoutIdleBindMillis());
+    }
+
+    private void handleUnbind() {
+        if (checkIfDestroyed()) return;
+
+        ensureUnbound();
+    }
+
+    private void handlePendingRequest(
+            PendingRequest<? extends AbstractRemoteService> pendingRequest) {
+        if (checkIfDestroyed() || mCompleted) return;
+
+        if (!isBound()) {
+            if (mPendingRequest != null) {
+                mPendingRequest.cancel();
+            }
+            mPendingRequest = pendingRequest;
+            ensureBound();
+        } else {
+            if (mVerbose) Slog.v(mTag, "handlePendingRequest(): " + pendingRequest);
+            pendingRequest.run();
+            if (pendingRequest.isFinal()) {
+                mCompleted = true;
+            }
+        }
+    }
+
+    private boolean isBound() {
+        return mServiceInterface != null;
+    }
+
+    private void ensureBound() {
+        if (isBound() || mBinding) return;
+
+        if (mVerbose) Slog.v(mTag, "ensureBound()");
+        mBinding = true;
+
+        int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
+        if (mBindInstantServiceAllowed) {
+            flags |= Context.BIND_ALLOW_INSTANT;
+        }
+
+        final boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, flags,
+                new UserHandle(mUserId));
+
+        if (!willBind) {
+            Slog.w(mTag, "could not bind to " + mIntent + " using flags " + flags);
+            mBinding = false;
+
+            if (!mServiceDied) {
+                handleBinderDied();
+            }
+        }
+    }
+
+    private void ensureUnbound() {
+        if (!isBound() && !mBinding) return;
+
+        if (mVerbose) Slog.v(mTag, "ensureUnbound()");
+        mBinding = false;
+        if (isBound()) {
+            onConnectedStateChanged(false);
+            if (mServiceInterface != null) {
+                mServiceInterface.asBinder().unlinkToDeath(this, 0);
+                mServiceInterface = null;
+            }
+        }
+        mContext.unbindService(mServiceConnection);
+    }
+
+    private class RemoteServiceConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (mDestroyed || !mBinding) {
+                // This is abnormal. Unbinding the connection has been requested already.
+                Slog.wtf(mTag, "onServiceConnected() was dispatched after unbindService.");
+                return;
+            }
+            mBinding = false;
+            mServiceInterface = getServiceInterface(service);
+            try {
+                service.linkToDeath(AbstractRemoteService.this, 0);
+            } catch (RemoteException re) {
+                handleBinderDied();
+                return;
+            }
+            onConnectedStateChanged(true);
+
+            if (mPendingRequest != null) {
+                final PendingRequest<? extends AbstractRemoteService> pendingRequest =
+                        mPendingRequest;
+                mPendingRequest = null;
+                handlePendingRequest(pendingRequest);
+            }
+
+            mServiceDied = false;
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            mBinding = true;
+            mServiceInterface = null;
+        }
+    }
+
+    private boolean checkIfDestroyed() {
+        if (mDestroyed) {
+            if (mVerbose) {
+                Slog.v(mTag, "Not handling operation as service for " + mComponentName
+                        + " is already destroyed");
+            }
+        }
+        return mDestroyed;
+    }
+
+    protected boolean handleResponseCallbackCommon(
+            PendingRequest<? extends AbstractRemoteService> pendingRequest) {
+        if (isDestroyed()) return false;
+
+        if (mPendingRequest == pendingRequest) {
+            mPendingRequest = null;
+        }
+        if (mPendingRequest == null) {
+            scheduleUnbind();
+        }
+        return true;
+    }
+
+    /**
+     * Base class for the requests serviced by the remote service.
+     *
+     * @param <S> the remote service class
+     */
+    public abstract static class PendingRequest<S extends AbstractRemoteService>
+            implements Runnable {
+        protected final String mTag = getClass().getSimpleName();
+        protected final Object mLock = new Object();
+
+        private final WeakReference<S> mWeakService;
+        private final Runnable mTimeoutTrigger;
+        private final Handler mServiceHandler;
+
+        @GuardedBy("mLock")
+        private boolean mCancelled;
+
+        @GuardedBy("mLock")
+        private boolean mCompleted;
+
+        protected PendingRequest(S service) {
+            mWeakService = new WeakReference<>(service);
+            mServiceHandler = service.mHandler;
+            mTimeoutTrigger = () -> {
+                synchronized (mLock) {
+                    if (mCancelled) {
+                        return;
+                    }
+                    mCompleted = true;
+                }
+
+                final S remoteService = mWeakService.get();
+                if (remoteService != null) {
+                    Slog.w(mTag, "timed out after " + service.getRemoteRequestMillis() + " ms");
+                    onTimeout(remoteService);
+                } else {
+                    Slog.w(mTag, "timed out (no service)");
+                }
+            };
+            mServiceHandler.postAtTime(mTimeoutTrigger,
+                    SystemClock.uptimeMillis() + service.getRemoteRequestMillis());
+        }
+
+        /**
+         * Gets a reference to the remote service.
+         */
+        protected final S getService() {
+            return mWeakService.get();
+        }
+
+        /**
+         * Subclasses must call this method when the remote service finishes, i.e., when the service
+         * finishes processing a request.
+         *
+         * @return {@code false} in the service is already finished, {@code true} otherwise.
+         */
+        protected final boolean finish() {
+            synchronized (mLock) {
+                if (mCompleted || mCancelled) {
+                    return false;
+                }
+                mCompleted = true;
+            }
+            mServiceHandler.removeCallbacks(mTimeoutTrigger);
+            return true;
+        }
+
+        /**
+         * Checks whether this request was cancelled.
+         */
+        @GuardedBy("mLock")
+        protected final boolean isCancelledLocked() {
+            return mCancelled;
+        }
+
+        /**
+         * Cancels the service.
+         *
+         * @return {@code false} if service is already canceled, {@code true} otherwise.
+         */
+        public boolean cancel() {
+            synchronized (mLock) {
+                if (mCancelled || mCompleted) {
+                    return false;
+                }
+                mCancelled = true;
+            }
+
+            mServiceHandler.removeCallbacks(mTimeoutTrigger);
+            return true;
+        }
+
+        /**
+         * Called by the self-destruct timeout when the remote service didn't reply to the
+         * request on time.
+         */
+        protected abstract void onTimeout(S remoteService);
+
+        /**
+         * Checks whether this request leads to a final state where no other requests can be made.
+         */
+        protected boolean isFinal() {
+            return false;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 38b9647..854c03f 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1462,6 +1462,10 @@
         TimeZone.setDefault(null);
 
         if (timeZoneWasChanged) {
+            // Don't wait for broadcasts to update our midnight alarm
+            mClockReceiver.scheduleDateChangedEvent();
+
+            // And now let everyone else know
             Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
                     | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index c2aec29..32667b8 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -26,10 +26,14 @@
 import static android.app.AppOpsManager._NUM_UID_STATE;
 
 import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.AppOpsManager.HistoricalOpEntry;
+import android.app.AppOpsManager.HistoricalPackageOps;
 import android.app.AppOpsManagerInternal;
 import android.app.AppOpsManagerInternal.CheckOpsDelegate;
 import android.content.ContentResolver;
@@ -38,6 +42,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.media.AudioAttributes;
@@ -929,6 +934,116 @@
     }
 
     @Override
+    public @Nullable ParceledListSlice getAllHistoricalPackagesOps(@Nullable String[] opNames,
+            long beginTimeMillis, long endTimeMillis) {
+        Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis,
+                "beginTimeMillis must be non negative and lesser than endTimeMillis");
+
+        mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+                Binder.getCallingPid(), Binder.getCallingUid(), "getAllHistoricalPackagesOps");
+
+        ArrayList<HistoricalPackageOps> historicalPackageOpsList = null;
+
+        final int uidStateCount = mUidStates.size();
+        for (int i = 0; i < uidStateCount; i++) {
+            final UidState uidState = mUidStates.valueAt(i);
+            if (uidState.pkgOps == null || uidState.pkgOps.isEmpty()) {
+                continue;
+            }
+            final ArrayMap<String, Ops> packages = uidState.pkgOps;
+            final int packageCount = packages.size();
+            for (int j = 0; j < packageCount; j++) {
+                final Ops pkgOps = packages.valueAt(j);
+                final AppOpsManager.HistoricalPackageOps historicalPackageOps =
+                        createHistoricalPackageOps(uidState.uid, pkgOps, opNames,
+                                beginTimeMillis, endTimeMillis);
+                if (historicalPackageOps != null) {
+                    if (historicalPackageOpsList == null) {
+                        historicalPackageOpsList = new ArrayList<>();
+                    }
+                    historicalPackageOpsList.add(historicalPackageOps);
+                }
+            }
+        }
+
+        if (historicalPackageOpsList == null) {
+            return null;
+        }
+
+        return new ParceledListSlice<>(historicalPackageOpsList);
+    }
+
+    private static @Nullable HistoricalPackageOps createHistoricalPackageOps(int uid,
+            @Nullable Ops pkgOps, @Nullable String[] opNames, long beginTimeMillis,
+            long endTimeMillis) {
+        // TODO: Implement historical data collection
+        if (pkgOps == null) {
+            return null;
+        }
+
+        final HistoricalPackageOps historicalPackageOps = new HistoricalPackageOps(uid,
+                pkgOps.packageName);
+
+        if (opNames == null) {
+            opNames = AppOpsManager.getOpStrs();
+        }
+        for (String opName : opNames) {
+            addHistoricOpEntry(AppOpsManager.strOpToOp(opName), pkgOps, historicalPackageOps);
+        }
+
+        return historicalPackageOps;
+    }
+
+    @Override
+    public @Nullable HistoricalPackageOps getHistoricalPackagesOps(int uid,
+            @NonNull String packageName, @Nullable String[] opNames,
+            long beginTimeMillis, long endTimeMillis) {
+        Preconditions.checkNotNull(packageName,
+                "packageName cannot be null");
+        Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis,
+                "beginTimeMillis must be non negative and lesser than endTimeMillis");
+
+        mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+                Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalPackagesOps");
+
+        final String resolvedPackageName = resolvePackageName(uid, packageName);
+        if (resolvedPackageName == null) {
+            return null;
+        }
+
+        // TODO: Implement historical data collection
+        final Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* edit */,
+                false /* uidMismatchExpected */);
+        return createHistoricalPackageOps(uid, pkgOps, opNames, beginTimeMillis, endTimeMillis);
+    }
+
+    private static void addHistoricOpEntry(int opCode, @NonNull Ops ops,
+            @NonNull HistoricalPackageOps outHistoricalPackageOps) {
+        final Op op = ops.get(opCode);
+        if (op == null) {
+            return;
+        }
+
+        final HistoricalOpEntry historicalOpEntry = new HistoricalOpEntry(opCode);
+
+        // TODO: Keep per UID state duration
+        for (int uidState = 0; uidState < AppOpsManager._NUM_UID_STATE; uidState++) {
+            final int acceptCount;
+            final int rejectCount;
+            if (op.rejectTime[uidState] == 0) {
+                acceptCount = 1;
+                rejectCount = 0;
+            } else {
+                acceptCount = 0;
+                rejectCount = 1;
+            }
+            historicalOpEntry.addEntry(uidState, acceptCount, rejectCount, 0);
+        }
+
+        outHistoricalPackageOps.addEntry(historicalOpEntry);
+    }
+
+    @Override
     public List<AppOpsManager.PackageOps> getUidOps(int uid, int[] ops) {
         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
@@ -1095,13 +1210,29 @@
 
     @Override
     public void setMode(int code, int uid, String packageName, int mode) {
+        setMode(code, uid, packageName, mode, true, false);
+    }
+
+    /**
+     * Sets the mode for a certain op and uid.
+     *
+     * @param code The op code to set
+     * @param uid The UID for which to set
+     * @param packageName The package for which to set
+     * @param mode The new mode to set
+     * @param verifyUid Iff {@code true}, check that the package name belongs to the uid
+     * @param isPrivileged Whether the package is privileged. (Only used if {@code verifyUid ==
+     *                     false})
+     */
+    private void setMode(int code, int uid, @NonNull String packageName, int mode,
+            boolean verifyUid, boolean isPrivileged) {
         enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
         verifyIncomingOp(code);
         ArraySet<ModeCallback> repCbs = null;
         code = AppOpsManager.opToSwitch(code);
         synchronized (this) {
             UidState uidState = getUidStateLocked(uid, false);
-            Op op = getOpLocked(code, uid, packageName, true);
+            Op op = getOpLocked(code, uid, packageName, true, verifyUid, isPrivileged);
             if (op != null) {
                 if (op.mode != mode) {
                     op.mode = mode;
@@ -1460,7 +1591,7 @@
                     && uidState.opModes.indexOfKey(code) >= 0) {
                 return uidState.opModes.get(code);
             }
-            Op op = getOpLocked(code, uid, resolvedPackageName, false);
+            Op op = getOpLocked(code, uid, resolvedPackageName, false, true, false);
             if (op == null) {
                 return AppOpsManager.opToDefaultMode(code);
             }
@@ -1803,7 +1934,7 @@
         }
         ClientState client = (ClientState) token;
         synchronized (this) {
-            Op op = getOpLocked(code, uid, resolvedPackageName, true);
+            Op op = getOpLocked(code, uid, resolvedPackageName, true, true, false);
             if (op == null) {
                 return;
             }
@@ -2020,7 +2151,8 @@
                     try {
                         ApplicationInfo appInfo = ActivityThread.getPackageManager()
                                 .getApplicationInfo(packageName,
-                                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+                                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                                         UserHandle.getUserId(uid));
                         if (appInfo != null) {
                             pkgUid = appInfo.uid;
@@ -2056,6 +2188,43 @@
         return ops;
     }
 
+    /**
+     * Get the state of all ops for a package, <b>don't verify that package belongs to uid</b>.
+     *
+     * <p>Usually callers should use {@link #getOpLocked} and not call this directly.
+     *
+     * @param uid The uid the of the package
+     * @param packageName The package name for which to get the state for
+     * @param edit Iff {@code true} create the {@link Ops} object if not yet created
+     * @param isPrivileged Whether the package is privileged or not
+     *
+     * @return The {@link Ops state} of all ops for the package
+     */
+    private @Nullable Ops getOpsRawNoVerifyLocked(int uid, @NonNull String packageName,
+            boolean edit, boolean isPrivileged) {
+        UidState uidState = getUidStateLocked(uid, edit);
+        if (uidState == null) {
+            return null;
+        }
+
+        if (uidState.pkgOps == null) {
+            if (!edit) {
+                return null;
+            }
+            uidState.pkgOps = new ArrayMap<>();
+        }
+
+        Ops ops = uidState.pkgOps.get(packageName);
+        if (ops == null) {
+            if (!edit) {
+                return null;
+            }
+            ops = new Ops(packageName, uidState, isPrivileged);
+            uidState.pkgOps.put(packageName, ops);
+        }
+        return ops;
+    }
+
     private void scheduleWriteLocked() {
         if (!mWriteScheduled) {
             mWriteScheduled = true;
@@ -2072,9 +2241,29 @@
         }
     }
 
-    private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
-        Ops ops = getOpsRawLocked(uid, packageName, edit,
-                false /* uidMismatchExpected */);
+    /**
+     * Get the state of an op for a uid.
+     *
+     * @param code The code of the op
+     * @param uid The uid the of the package
+     * @param packageName The package name for which to get the state for
+     * @param edit Iff {@code true} create the {@link Op} object if not yet created
+     * @param verifyUid Iff {@code true} check that the package belongs to the uid
+     * @param isPrivileged Whether the package is privileged or not (only used if {@code verifyUid
+     *                     == false})
+     *
+     * @return The {@link Op state} of the op
+     */
+    private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, boolean edit,
+            boolean verifyUid, boolean isPrivileged) {
+        Ops ops;
+
+        if (verifyUid) {
+            ops = getOpsRawLocked(uid, packageName, edit, false /* uidMismatchExpected */);
+        }  else {
+            ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged);
+        }
+
         if (ops == null) {
             return null;
         }
@@ -3832,5 +4021,11 @@
                 mProfileOwners = owners;
             }
         }
+
+        @Override
+        public void setMode(int code, int uid, @NonNull String packageName, int mode,
+                boolean isPrivileged) {
+            AppOpsService.this.setMode(code, uid, packageName, mode, false, isPrivileged);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 872261a..dd96075 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -49,6 +49,7 @@
     private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
             = "persist.sys.binder_calls_detailed_tracking";
 
+
     /** Listens for flag changes. */
     private static class SettingsObserver extends ContentObserver {
         private static final String SETTINGS_ENABLED_KEY = "enabled";
@@ -101,7 +102,14 @@
             final boolean enabled =
                     mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT);
             if (mEnabled != enabled) {
-                Binder.setObserver(enabled ? mBinderCallsStats : null);
+                if (enabled) {
+                    Binder.setObserver(mBinderCallsStats);
+                    Binder.setProxyTransactListener(
+                            new Binder.PropagateWorkSourceTransactListener());
+                } else {
+                    Binder.setObserver(null);
+                    Binder.setProxyTransactListener(null);
+                }
                 mEnabled = enabled;
                 mBinderCallsStats.reset();
             }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1c8d99a..74c8023 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -215,7 +215,8 @@
     private static final String REQUEST_ARG = "requests";
 
     private static final boolean DBG = true;
-    private static final boolean VDBG = false;
+    private static final boolean DDBG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
 
     private static final boolean LOGD_BLOCKED_NETWORKINFO = true;
 
@@ -1143,7 +1144,7 @@
         }
         synchronized (mVpns) {
             final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
-            if (vpn != null && vpn.isBlockingUid(uid)) {
+            if (vpn != null && vpn.getLockdown() && vpn.isBlockingUid(uid)) {
                 return true;
             }
         }
@@ -1736,7 +1737,7 @@
             // list all state depending on the return value of this function has to be recomputed.
             // TODO: add a trigger when the always-on VPN sets its blocked UIDs to reevaluate and
             // send the necessary onBlockedStatusChanged callbacks.
-            if (vpn != null && vpn.isBlockingUid(uid)) {
+            if (vpn != null && vpn.getLockdown() && vpn.isBlockingUid(uid)) {
                 return true;
             }
         }
@@ -2056,7 +2057,7 @@
         }
 
         try {
-            if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
+            if (VDBG || DDBG) log("Setting MTU size: " + iface + ", " + mtu);
             mNMS.setMtu(iface, mtu);
         } catch (Exception e) {
             Slog.e(TAG, "exception in setMtu()" + e);
@@ -2092,7 +2093,7 @@
         if (tcpBufferSizes.equals(mCurrentTcpBufferSizes)) return;
 
         try {
-            if (VDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
+            if (VDBG || DDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
 
             final String prefix = "/sys/kernel/ipv4/tcp_";
             FileUtils.stringToFile(prefix + "rmem_min", values[0]);
@@ -2931,7 +2932,7 @@
             if (nai != null) {
                 boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
                 nai.removeRequest(nri.request.requestId);
-                if (VDBG) {
+                if (VDBG || DDBG) {
                     log(" Removing from current network " + nai.name() +
                             ", leaving " + nai.numNetworkRequests() + " requests.");
                 }
@@ -3154,7 +3155,7 @@
     }
 
     private void handlePromptUnvalidated(Network network) {
-        if (VDBG) log("handlePromptUnvalidated " + network);
+        if (VDBG || DDBG) log("handlePromptUnvalidated " + network);
         NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
 
         // Only prompt if the network is unvalidated and was explicitly selected by the user, and if
@@ -4843,7 +4844,7 @@
         // do this twice, adding non-next-hop routes first, then routes they are dependent on
         for (RouteInfo route : routeDiff.added) {
             if (route.hasGateway()) continue;
-            if (VDBG) log("Adding Route [" + route + "] to network " + netId);
+            if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
                 mNMS.addRoute(netId, route);
             } catch (Exception e) {
@@ -4854,7 +4855,7 @@
         }
         for (RouteInfo route : routeDiff.added) {
             if (route.hasGateway() == false) continue;
-            if (VDBG) log("Adding Route [" + route + "] to network " + netId);
+            if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
                 mNMS.addRoute(netId, route);
             } catch (Exception e) {
@@ -4865,7 +4866,7 @@
         }
 
         for (RouteInfo route : routeDiff.removed) {
-            if (VDBG) log("Removing Route [" + route + "] from network " + netId);
+            if (VDBG || DDBG) log("Removing Route [" + route + "] from network " + netId);
             try {
                 mNMS.removeRoute(netId, route);
             } catch (Exception e) {
@@ -5066,7 +5067,7 @@
         }
         // newLp is already a defensive copy.
         newLp.ensureDirectlyConnectedRoutes();
-        if (VDBG) {
+        if (VDBG || DDBG) {
             log("Update of LinkProperties for " + nai.name() +
                     "; created=" + nai.created +
                     "; everConnected=" + nai.everConnected);
@@ -5090,7 +5091,9 @@
     }
 
     private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) {
-        if (VDBG) log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
+        if (VDBG || DDBG){
+            log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
+        }
         for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
             nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, 0,
                     networkRequest);
@@ -5168,6 +5171,7 @@
                 break;
             }
             case ConnectivityManager.CALLBACK_BLK_CHANGED: {
+                maybeLogBlockedStatusChanged(nri, networkAgent.network, arg1 != 0);
                 msg.arg1 = arg1;
                 break;
             }
@@ -5302,7 +5306,7 @@
         final boolean wasBackgroundNetwork = newNetwork.isBackgroundNetwork();
         final int score = newNetwork.getCurrentScore();
 
-        if (VDBG) log("rematching " + newNetwork.name());
+        if (VDBG || DDBG) log("rematching " + newNetwork.name());
 
         // Find and migrate to this Network any NetworkRequests for
         // which this network is now the best.
@@ -5333,7 +5337,7 @@
             if (satisfies) {
                 // next check if it's better than any current network we're using for
                 // this request
-                if (VDBG) {
+                if (VDBG || DDBG) {
                     log("currentScore = " +
                             (currentNetwork != null ? currentNetwork.getCurrentScore() : 0) +
                             ", newScore = " + score);
@@ -5341,12 +5345,14 @@
                 if (currentNetwork == null || currentNetwork.getCurrentScore() < score) {
                     if (VDBG) log("rematch for " + newNetwork.name());
                     if (currentNetwork != null) {
-                        if (VDBG) log("   accepting network in place of " + currentNetwork.name());
+                        if (VDBG || DDBG){
+                            log("   accepting network in place of " + currentNetwork.name());
+                        }
                         currentNetwork.removeRequest(nri.request.requestId);
                         currentNetwork.lingerRequest(nri.request, now, mLingerDelayMs);
                         affectedNetworks.add(currentNetwork);
                     } else {
-                        if (VDBG) log("   accepting network in place of null");
+                        if (VDBG || DDBG) log("   accepting network in place of null");
                     }
                     newNetwork.unlingerRequest(nri.request);
                     setNetworkForRequest(nri.request.requestId, newNetwork);
@@ -5699,7 +5705,7 @@
     }
 
     private void updateNetworkScore(NetworkAgentInfo nai, int score) {
-        if (VDBG) log("updateNetworkScore for " + nai.name() + " to " + score);
+        if (VDBG || DDBG) log("updateNetworkScore for " + nai.name() + " to " + score);
         if (score < 0) {
             loge("updateNetworkScore for " + nai.name() + " got a negative score (" + score +
                     ").  Bumping score to min of 0");
@@ -5845,7 +5851,7 @@
     }
 
     protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType, int arg1) {
-        if (VDBG) {
+        if (VDBG || DDBG) {
             String notification = ConnectivityManager.getCallbackName(notifyType);
             log("notifyType " + notification + " for " + networkAgent.name());
         }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 93bdcbb..90ad09e 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -541,7 +541,7 @@
         }
     }
 
-    private void ensureFallbackFusedProviderPresentLocked(ArrayList<String> pkgs) {
+    private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) {
         PackageManager pm = mContext.getPackageManager();
         String systemPackageName = mContext.getPackageName();
         ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs);
@@ -646,16 +646,14 @@
         that matches the signature of at least one package on this list.
         */
         Resources resources = mContext.getResources();
-        ArrayList<String> providerPackageNames = new ArrayList<>();
         String[] pkgs = resources.getStringArray(
                 com.android.internal.R.array.config_locationProviderPackageNames);
         if (D) {
             Log.d(TAG, "certificates for location providers pulled from: " +
                     Arrays.toString(pkgs));
         }
-        if (pkgs != null) providerPackageNames.addAll(Arrays.asList(pkgs));
 
-        ensureFallbackFusedProviderPresentLocked(providerPackageNames);
+        ensureFallbackFusedProviderPresentLocked(pkgs);
 
         // bind to network provider
         LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
@@ -664,8 +662,7 @@
                 NETWORK_LOCATION_SERVICE_ACTION,
                 com.android.internal.R.bool.config_enableNetworkLocationOverlay,
                 com.android.internal.R.string.config_networkLocationProviderPackageName,
-                com.android.internal.R.array.config_locationProviderPackageNames,
-                mLocationHandler);
+                com.android.internal.R.array.config_locationProviderPackageNames);
         if (networkProvider != null) {
             mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
             mProxyProviders.add(networkProvider);
@@ -681,8 +678,7 @@
                 FUSED_LOCATION_SERVICE_ACTION,
                 com.android.internal.R.bool.config_enableFusedLocationOverlay,
                 com.android.internal.R.string.config_fusedLocationProviderPackageName,
-                com.android.internal.R.array.config_locationProviderPackageNames,
-                mLocationHandler);
+                com.android.internal.R.array.config_locationProviderPackageNames);
         if (fusedLocationProvider != null) {
             addProviderLocked(fusedLocationProvider);
             mProxyProviders.add(fusedLocationProvider);
@@ -697,8 +693,7 @@
         mGeocodeProvider = GeocoderProxy.createAndBind(mContext,
                 com.android.internal.R.bool.config_enableGeocoderOverlay,
                 com.android.internal.R.string.config_geocoderProviderPackageName,
-                com.android.internal.R.array.config_locationProviderPackageNames,
-                mLocationHandler);
+                com.android.internal.R.array.config_locationProviderPackageNames);
         if (mGeocodeProvider == null) {
             Slog.e(TAG, "no geocoder provider found");
         }
@@ -708,7 +703,6 @@
                 mContext, com.android.internal.R.bool.config_enableGeofenceOverlay,
                 com.android.internal.R.string.config_geofenceProviderPackageName,
                 com.android.internal.R.array.config_locationProviderPackageNames,
-                mLocationHandler,
                 mGpsGeofenceProxy,
                 null);
         if (provider == null) {
@@ -725,7 +719,6 @@
         }
         ActivityRecognitionProxy proxy = ActivityRecognitionProxy.createAndBind(
                 mContext,
-                mLocationHandler,
                 activityRecognitionHardwareIsSupported,
                 activityRecognitionHardware,
                 com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay,
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index f15cd2a..b6fa157 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -17,7 +17,6 @@
 package com.android.server;
 
 import android.Manifest;
-import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -337,6 +336,7 @@
             mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
             if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
+                Slog.e(TAG, callingPkg + " is not allowed to call sendMessage()");
                 return;
             }
             contentUri = adjustUriForUserAndGrantPermission(contentUri,
@@ -355,6 +355,7 @@
                     "Download MMS message");
             if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(),
                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
+                Slog.e(TAG, callingPkg + " is not allowed to call downloadMessage()");
                 return;
             }
             contentUri = adjustUriForUserAndGrantPermission(contentUri,
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index f510d83..92d8d73 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -61,6 +61,7 @@
 import android.net.INetworkManagementEventObserver;
 import android.net.ITetheringStatsProvider;
 import android.net.InterfaceConfiguration;
+import android.net.InterfaceConfigurationParcel;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.Network;
@@ -69,6 +70,7 @@
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
 import android.net.UidRange;
+import android.net.UidRangeParcel;
 import android.net.util.NetdService;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
@@ -969,55 +971,29 @@
     public String[] listInterfaces() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            return NativeDaemonEvent.filterMessageList(
-                    mConnector.executeForList("interface", "list"), InterfaceListResult);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            final List<String> result = mNetdService.interfaceGetList();
+            return result.toArray(EMPTY_STRING_ARRAY);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
     @Override
     public InterfaceConfiguration getInterfaceConfig(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-
-        final NativeDaemonEvent event;
+        final InterfaceConfigurationParcel result;
         try {
-            event = mConnector.execute("interface", "getcfg", iface);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            result = mNetdService.interfaceGetCfg(iface);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
 
-        event.checkCode(InterfaceGetCfgResult);
-
-        // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz flag1 flag2 flag3
-        final StringTokenizer st = new StringTokenizer(event.getMessage());
-
-        InterfaceConfiguration cfg;
         try {
-            cfg = new InterfaceConfiguration();
-            cfg.setHardwareAddress(st.nextToken(" "));
-            InetAddress addr = null;
-            int prefixLength = 0;
-            try {
-                addr = NetworkUtils.numericToInetAddress(st.nextToken());
-            } catch (IllegalArgumentException iae) {
-                Slog.e(TAG, "Failed to parse ipaddr", iae);
-            }
-
-            try {
-                prefixLength = Integer.parseInt(st.nextToken());
-            } catch (NumberFormatException nfe) {
-                Slog.e(TAG, "Failed to parse prefixLength", nfe);
-            }
-
-            cfg.setLinkAddress(new LinkAddress(addr, prefixLength));
-            while (st.hasMoreTokens()) {
-                cfg.setFlag(st.nextToken());
-            }
-        } catch (NoSuchElementException nsee) {
-            throw new IllegalStateException("Invalid response from daemon: " + event);
+            final InterfaceConfiguration cfg = InterfaceConfiguration.fromParcel(result);
+            return cfg;
+        } catch (IllegalArgumentException iae) {
+            throw new IllegalStateException("Invalid InterfaceConfigurationParcel", iae);
         }
-        return cfg;
     }
 
     @Override
@@ -1028,17 +1004,12 @@
             throw new IllegalStateException("Null LinkAddress given");
         }
 
-        final Command cmd = new Command("interface", "setcfg", iface,
-                linkAddr.getAddress().getHostAddress(),
-                linkAddr.getPrefixLength());
-        for (String flag : cfg.getFlags()) {
-            cmd.appendArg(flag);
-        }
+        final InterfaceConfigurationParcel cfgParcel = cfg.toParcel(iface);
 
         try {
-            mConnector.execute(cmd);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.interfaceSetCfg(cfgParcel);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1062,10 +1033,9 @@
     public void setInterfaceIpv6PrivacyExtensions(String iface, boolean enable) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute(
-                    "interface", "ipv6privacyextensions", iface, enable ? "enable" : "disable");
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.interfaceSetIPv6PrivacyExtensions(iface, enable);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1075,9 +1045,9 @@
     public void clearInterfaceAddresses(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("interface", "clearaddrs", iface);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.interfaceClearAddrs(iface);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1085,9 +1055,9 @@
     public void enableIpv6(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("interface", "ipv6", iface, "enable");
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.interfaceSetEnableIPv6(iface, true);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1104,9 +1074,9 @@
     public void disableIpv6(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("interface", "ipv6", iface, "disable");
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.interfaceSetEnableIPv6(iface, false);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1188,11 +1158,10 @@
     public void setMtu(String iface, int mtu) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
-        final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("interface", "setmtu", iface, mtu);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.interfaceSetMtu(iface, mtu);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1743,7 +1712,6 @@
     public void setAllowOnlyVpnForUids(boolean add, UidRange[] uidRanges)
             throws ServiceSpecificException {
         mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
-
         try {
             mNetdService.networkRejectNonSecureVpn(add, uidRanges);
         } catch (ServiceSpecificException e) {
@@ -1934,10 +1902,11 @@
     public void setFirewallEnabled(boolean enabled) {
         enforceSystemUid();
         try {
-            mConnector.execute("firewall", "enable", enabled ? "whitelist" : "blacklist");
+            mNetdService.firewallSetFirewallType(
+                    enabled ? INetd.FIREWALL_WHITELIST : INetd.FIREWALL_BLACKLIST);
             mFirewallEnabled = enabled;
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1951,11 +1920,11 @@
     public void setFirewallInterfaceRule(String iface, boolean allow) {
         enforceSystemUid();
         Preconditions.checkState(mFirewallEnabled);
-        final String rule = allow ? "allow" : "deny";
         try {
-            mConnector.execute("firewall", "set_interface_rule", iface, rule);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.firewallSetInterfaceRule(iface,
+                    allow ? INetd.FIREWALL_RULE_ALLOW : INetd.FIREWALL_RULE_DENY);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1966,7 +1935,7 @@
         int[] exemptUids;
 
         int numUids = 0;
-
+        if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName);
         if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {
             // Close all sockets on all non-system UIDs...
             ranges = new UidRange[] {
@@ -2036,26 +2005,15 @@
                 setFirewallChainState(chain, enable);
             }
 
-            final String operation = enable ? "enable_chain" : "disable_chain";
-            final String chainName;
-            switch(chain) {
-                case FIREWALL_CHAIN_STANDBY:
-                    chainName = FIREWALL_CHAIN_NAME_STANDBY;
-                    break;
-                case FIREWALL_CHAIN_DOZABLE:
-                    chainName = FIREWALL_CHAIN_NAME_DOZABLE;
-                    break;
-                case FIREWALL_CHAIN_POWERSAVE:
-                    chainName = FIREWALL_CHAIN_NAME_POWERSAVE;
-                    break;
-                default:
-                    throw new IllegalArgumentException("Bad child chain: " + chain);
+            final String chainName = getFirewallChainName(chain);
+            if (chain == FIREWALL_CHAIN_NONE) {
+                throw new IllegalArgumentException("Bad child chain: " + chainName);
             }
 
             try {
-                mConnector.execute("firewall", operation, chainName);
-            } catch (NativeDaemonConnectorException e) {
-                throw e.rethrowAsParcelableException();
+                mNetdService.firewallEnableChildChain(chain, enable);
+            } catch (RemoteException | ServiceSpecificException e) {
+                throw new IllegalStateException(e);
             }
 
             // Close any sockets that were opened by the affected UIDs. This has to be done after
@@ -2063,12 +2021,24 @@
             // the connection and race with the iptables commands that enable the firewall. All
             // whitelist and blacklist chains allow RSTs through.
             if (enable) {
-                if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName);
                 closeSocketsForFirewallChainLocked(chain, chainName);
             }
         }
     }
 
+    private String getFirewallChainName(int chain) {
+        switch (chain) {
+            case FIREWALL_CHAIN_STANDBY:
+                return FIREWALL_CHAIN_NAME_STANDBY;
+            case FIREWALL_CHAIN_DOZABLE:
+                return FIREWALL_CHAIN_NAME_DOZABLE;
+            case FIREWALL_CHAIN_POWERSAVE:
+                return FIREWALL_CHAIN_NAME_POWERSAVE;
+            default:
+                throw new IllegalArgumentException("Bad child chain: " + chain);
+        }
+    }
+
     private int getFirewallType(int chain) {
         switch (chain) {
             case FIREWALL_CHAIN_STANDBY:
@@ -2141,11 +2111,11 @@
 
     private void setFirewallUidRuleLocked(int chain, int uid, int rule) {
         if (updateFirewallUidRuleLocked(chain, uid, rule)) {
+            final int ruleType = getFirewallRuleType(chain, rule);
             try {
-                mConnector.execute("firewall", "set_uid_rule", getFirewallChainName(chain), uid,
-                        getFirewallRuleName(chain, rule));
-            } catch (NativeDaemonConnectorException e) {
-                throw e.rethrowAsParcelableException();
+                mNetdService.firewallSetUidRule(chain, uid, ruleType);
+            } catch (RemoteException | ServiceSpecificException e) {
+                throw new IllegalStateException(e);
             }
         }
     }
@@ -2212,19 +2182,12 @@
         }
     }
 
-    public @NonNull String getFirewallChainName(int chain) {
-        switch (chain) {
-            case FIREWALL_CHAIN_STANDBY:
-                return FIREWALL_CHAIN_NAME_STANDBY;
-            case FIREWALL_CHAIN_DOZABLE:
-                return FIREWALL_CHAIN_NAME_DOZABLE;
-            case FIREWALL_CHAIN_POWERSAVE:
-                return FIREWALL_CHAIN_NAME_POWERSAVE;
-            case FIREWALL_CHAIN_NONE:
-                return FIREWALL_CHAIN_NAME_NONE;
-            default:
-                throw new IllegalArgumentException("Unknown chain:" + chain);
+    private int getFirewallRuleType(int chain, int rule) {
+        if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
+            return getFirewallType(chain) == FIREWALL_TYPE_WHITELIST
+                    ? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW;
         }
+        return rule;
     }
 
     private static void enforceSystemUid() {
@@ -2554,18 +2517,18 @@
 
     @Override
     public void addInterfaceToLocalNetwork(String iface, List<RouteInfo> routes) {
-        modifyInterfaceInNetwork(MODIFY_OPERATION_ADD, INetd.NETID_LOCAL, iface);
+        modifyInterfaceInNetwork(MODIFY_OPERATION_ADD, INetd.LOCAL_NET_ID, iface);
 
         for (RouteInfo route : routes) {
             if (!route.isDefaultRoute()) {
-                modifyRoute(MODIFY_OPERATION_ADD, INetd.NETID_LOCAL, route);
+                modifyRoute(MODIFY_OPERATION_ADD, INetd.LOCAL_NET_ID, route);
             }
         }
     }
 
     @Override
     public void removeInterfaceFromLocalNetwork(String iface) {
-        modifyInterfaceInNetwork(MODIFY_OPERATION_REMOVE, INetd.NETID_LOCAL, iface);
+        modifyInterfaceInNetwork(MODIFY_OPERATION_REMOVE, INetd.LOCAL_NET_ID, iface);
     }
 
     @Override
@@ -2574,7 +2537,7 @@
 
         for (RouteInfo route : routes) {
             try {
-                modifyRoute(MODIFY_OPERATION_REMOVE, INetd.NETID_LOCAL, route);
+                modifyRoute(MODIFY_OPERATION_REMOVE, INetd.LOCAL_NET_ID, route);
             } catch (IllegalStateException e) {
                 failures++;
             }
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 42c836e..574f54a 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -16,8 +16,10 @@
 
 package com.android.server;
 
-import android.annotation.NonNull;
+import android.annotation.MainThread;
 import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -30,17 +32,21 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.Signature;
 import android.content.res.Resources;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -50,268 +56,54 @@
  * Handles run-time package changes.
  */
 public class ServiceWatcher implements ServiceConnection {
+
+    private static final String TAG = "ServiceWatcher";
     private static final boolean D = false;
+
     public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
     public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
 
-    private final String mTag;
-    private final Context mContext;
-    private final PackageManager mPm;
-    private final List<HashSet<Signature>> mSignatureSets;
-    private final String mAction;
-
     /**
-     * If mServicePackageName is not null, only this package will be searched for the service that
-     * implements mAction. When null, all packages in the system that matches one of the signature
-     * in mSignatureSets are searched.
+     * The runner that runs on the binder retrieved from {@link ServiceWatcher}.
      */
-    private final String mServicePackageName;
-    private final Runnable mNewServiceWork;
-    private final Handler mHandler;
-
-    private final Object mLock = new Object();
-
-    @GuardedBy("mLock")
-    private int mCurrentUserId = UserHandle.USER_SYSTEM;
-
-    @GuardedBy("mLock")
-    private IBinder mBoundService;
-    @GuardedBy("mLock")
-    private ComponentName mBoundComponent;
-    @GuardedBy("mLock")
-    private String mBoundPackageName;
-    @GuardedBy("mLock")
-    private int mBoundVersion = Integer.MIN_VALUE;
-    @GuardedBy("mLock")
-    private int mBoundUserId = UserHandle.USER_NULL;
+    public interface BinderRunner {
+        /**
+         * Runs on the retrieved binder.
+         *
+         * @param binder the binder retrieved from the {@link ServiceWatcher}.
+         */
+        void run(IBinder binder);
+    }
 
     public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
-            List<String> initialPackageNames) {
+            String... packageNames) {
         PackageManager pm = context.getPackageManager();
-        ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>();
-        for (int i = 0, size = initialPackageNames.size(); i < size; i++) {
-            String pkg = initialPackageNames.get(i);
+
+        ArrayList<HashSet<Signature>> signatureSets = new ArrayList<>(packageNames.length);
+        for (String packageName : packageNames) {
             try {
-                HashSet<Signature> set = new HashSet<Signature>();
-                Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.MATCH_SYSTEM_ONLY
-                        | PackageManager.GET_SIGNATURES).signatures;
-                set.addAll(Arrays.asList(sigs));
-                sigSets.add(set);
+                Signature[] signatures = pm.getPackageInfo(packageName,
+                        PackageManager.MATCH_SYSTEM_ONLY
+                                | PackageManager.GET_SIGNATURES).signatures;
+
+                HashSet<Signature> set = new HashSet<>();
+                Collections.addAll(set, signatures);
+                signatureSets.add(set);
             } catch (NameNotFoundException e) {
-                Log.w("ServiceWatcher", pkg + " not found");
+                Log.w(TAG, packageName + " not found");
             }
         }
-        return sigSets;
+        return signatureSets;
     }
 
-    public ServiceWatcher(Context context, String logTag, String action,
-            int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId, Runnable newServiceWork,
-            Handler handler) {
-        mContext = context;
-        mTag = logTag;
-        mAction = action;
-        mPm = mContext.getPackageManager();
-        mNewServiceWork = newServiceWork;
-        mHandler = handler;
-        Resources resources = context.getResources();
-
-        // Whether to enable service overlay.
-        boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
-        ArrayList<String> initialPackageNames = new ArrayList<String>();
-        if (enableOverlay) {
-            // A list of package names used to create the signatures.
-            String[] pkgs = resources.getStringArray(initialPackageNamesResId);
-            if (pkgs != null) initialPackageNames.addAll(Arrays.asList(pkgs));
-            mServicePackageName = null;
-            if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
-        } else {
-            // The default package name that is searched for service implementation when overlay is
-            // disabled.
-            String servicePackageName = resources.getString(defaultServicePackageNameResId);
-            if (servicePackageName != null) initialPackageNames.add(servicePackageName);
-            mServicePackageName = servicePackageName;
-            if (D) Log.d(mTag, "Overlay disabled, default package=" + servicePackageName);
-        }
-        mSignatureSets = getSignatureSets(context, initialPackageNames);
-    }
-
-    /**
-     * Start this watcher, including binding to the current best match and
-     * re-binding to any better matches down the road.
-     * <p>
-     * Note that if there are no matching encryption-aware services, we may not
-     * bind to a real service until after the current user is unlocked.
-     *
-     * @returns {@code true} if a potential service implementation was found.
-     */
-    public boolean start() {
-        if (isServiceMissing()) return false;
-
-        synchronized (mLock) {
-            bindBestPackageLocked(mServicePackageName, false);
-        }
-
-        // listen for user change
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
-        intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
-        mContext.registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                final String action = intent.getAction();
-                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                        UserHandle.USER_NULL);
-                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                    switchUser(userId);
-                } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
-                    unlockUser(userId);
-                }
-            }
-        }, UserHandle.ALL, intentFilter, null, mHandler);
-
-        // listen for relevant package changes if service overlay is enabled.
-        if (mServicePackageName == null) {
-            mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
-        }
-
-        return true;
-    }
-
-    /**
-     * Check if any instance of this service is present on the device,
-     * regardless of it being encryption-aware or not.
-     */
-    private boolean isServiceMissing() {
-        final Intent intent = new Intent(mAction);
-        final int flags = PackageManager.MATCH_DIRECT_BOOT_AWARE
-                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
-        return mPm.queryIntentServicesAsUser(intent, flags, mCurrentUserId).isEmpty();
-    }
-
-    /**
-     * Searches and binds to the best package, or do nothing if the best package
-     * is already bound, unless force rebinding is requested.
-     *
-     * @param justCheckThisPackage Only consider this package, or consider all
-     *            packages if it is {@code null}.
-     * @param forceRebind Force a rebinding to the best package if it's already
-     *            bound.
-     * @returns {@code true} if a valid package was found to bind to.
-     */
-    @GuardedBy("mLock")
-    private boolean bindBestPackageLocked(String justCheckThisPackage, boolean forceRebind) {
-        Intent intent = new Intent(mAction);
-        if (justCheckThisPackage != null) {
-            intent.setPackage(justCheckThisPackage);
-        }
-        final List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent,
-                PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
-                mCurrentUserId);
-        int bestVersion = Integer.MIN_VALUE;
-        ComponentName bestComponent = null;
-        boolean bestIsMultiuser = false;
-        if (rInfos != null) {
-            for (ResolveInfo rInfo : rInfos) {
-                final ComponentName component = rInfo.serviceInfo.getComponentName();
-                final String packageName = component.getPackageName();
-
-                // check signature
-                try {
-                    PackageInfo pInfo;
-                    pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES
-                            | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
-                    if (!isSignatureMatch(pInfo.signatures)) {
-                        Log.w(mTag, packageName + " resolves service " + mAction
-                                + ", but has wrong signature, ignoring");
-                        continue;
-                    }
-                } catch (NameNotFoundException e) {
-                    Log.wtf(mTag, e);
-                    continue;
-                }
-
-                // check metadata
-                int version = Integer.MIN_VALUE;
-                boolean isMultiuser = false;
-                if (rInfo.serviceInfo.metaData != null) {
-                    version = rInfo.serviceInfo.metaData.getInt(
-                            EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
-                    isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
-                }
-
-                if (version > bestVersion) {
-                    bestVersion = version;
-                    bestComponent = component;
-                    bestIsMultiuser = isMultiuser;
-                }
-            }
-
-            if (D) {
-                Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
-                        (justCheckThisPackage == null ? ""
-                                : "(" + justCheckThisPackage + ") "), rInfos.size(),
-                        (bestComponent == null ? "no new best component"
-                                : "new best component: " + bestComponent)));
-            }
-        } else {
-            if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction);
-        }
-
-        if (bestComponent == null) {
-            Slog.w(mTag, "Odd, no component found for service " + mAction);
-            unbindLocked();
-            return false;
-        }
-
-        final int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId;
-        final boolean alreadyBound = Objects.equals(bestComponent, mBoundComponent)
-                && bestVersion == mBoundVersion && userId == mBoundUserId;
-        if (forceRebind || !alreadyBound) {
-            unbindLocked();
-            bindToPackageLocked(bestComponent, bestVersion, userId);
-        }
-        return true;
-    }
-
-    @GuardedBy("mLock")
-    private void unbindLocked() {
-        ComponentName component;
-        component = mBoundComponent;
-        mBoundComponent = null;
-        mBoundPackageName = null;
-        mBoundVersion = Integer.MIN_VALUE;
-        mBoundUserId = UserHandle.USER_NULL;
-        if (component != null) {
-            if (D) Log.d(mTag, "unbinding " + component);
-            mBoundService = null;
-            mContext.unbindService(this);
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void bindToPackageLocked(ComponentName component, int version, int userId) {
-        Intent intent = new Intent(mAction);
-        intent.setComponent(component);
-        mBoundComponent = component;
-        mBoundPackageName = component.getPackageName();
-        mBoundVersion = version;
-        mBoundUserId = userId;
-        if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")");
-        mContext.bindServiceAsUser(intent, this,
-                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
-                new UserHandle(userId));
-    }
-
+    /** Checks if signatures match. */
     public static boolean isSignatureMatch(Signature[] signatures,
             List<HashSet<Signature>> sigSets) {
         if (signatures == null) return false;
 
         // build hashset of input to test against
-        HashSet<Signature> inputSet = new HashSet<Signature>();
-        for (Signature s : signatures) {
-            inputSet.add(s);
-        }
+        HashSet<Signature> inputSet = new HashSet<>();
+        Collections.addAll(inputSet, signatures);
 
         // test input against each of the signature sets
         for (HashSet<Signature> referenceSet : sigSets) {
@@ -322,124 +114,318 @@
         return false;
     }
 
-    private boolean isSignatureMatch(Signature[] signatures) {
-        return isSignatureMatch(signatures, mSignatureSets);
+    private final Context mContext;
+    private final String mTag;
+    private final String mAction;
+    private final String mServicePackageName;
+    private final List<HashSet<Signature>> mSignatureSets;
+
+    private final Handler mHandler;
+
+    // this lock is held to ensure the service binder is not exposed (via runOnBinder) until after
+    // the new service initialization work has completed
+    private final Object mBindLock = new Object();
+
+    // read/write from handler thread
+    private int mCurrentUserId;
+
+    // read from any thread, write from handler thread
+    private volatile ComponentName mBestComponent;
+    private volatile int mBestVersion;
+    private volatile int mBestUserId;
+    private volatile IBinder mBestService;
+
+    public ServiceWatcher(Context context, String logTag, String action,
+            int overlaySwitchResId, int defaultServicePackageNameResId,
+            int initialPackageNamesResId, Handler handler) {
+        Resources resources = context.getResources();
+
+        mContext = context;
+        mTag = logTag;
+        mAction = action;
+
+        boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
+        if (enableOverlay) {
+            String[] pkgs = resources.getStringArray(initialPackageNamesResId);
+            mServicePackageName = null;
+            mSignatureSets = getSignatureSets(context, pkgs);
+            if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
+        } else {
+            mServicePackageName = resources.getString(defaultServicePackageNameResId);
+            mSignatureSets = getSignatureSets(context, mServicePackageName);
+            if (D) Log.d(mTag, "Overlay disabled, default package=" + mServicePackageName);
+        }
+
+        mHandler = handler;
+
+        mBestComponent = null;
+        mBestVersion = Integer.MIN_VALUE;
+        mBestUserId = UserHandle.USER_NULL;
+
+        mBestService = null;
     }
 
-    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
-        /**
-         * Called when package has been reinstalled
-         */
-        @Override
-        public void onPackageUpdateFinished(String packageName, int uid) {
-            synchronized (mLock) {
-                final boolean forceRebind = Objects.equals(packageName, mBoundPackageName);
-                bindBestPackageLocked(null, forceRebind);
-            }
-        }
+    // called on handler thread
+    @GuardedBy("mBindLock")
+    protected void onBind() {
 
-        @Override
-        public void onPackageAdded(String packageName, int uid) {
-            synchronized (mLock) {
-                final boolean forceRebind = Objects.equals(packageName, mBoundPackageName);
-                bindBestPackageLocked(null, forceRebind);
-            }
-        }
+    }
 
-        @Override
-        public void onPackageRemoved(String packageName, int uid) {
-            synchronized (mLock) {
-                final boolean forceRebind = Objects.equals(packageName, mBoundPackageName);
-                bindBestPackageLocked(null, forceRebind);
-            }
-        }
+    // called on handler thread
+    @GuardedBy("mBindLock")
+    protected void onUnbind() {
 
-        @Override
-        public boolean onPackageChanged(String packageName, int uid, String[] components) {
-            synchronized (mLock) {
-                final boolean forceRebind = Objects.equals(packageName, mBoundPackageName);
-                bindBestPackageLocked(null, forceRebind);
-            }
-            return super.onPackageChanged(packageName, uid, components);
-        }
-    };
+    }
 
-    @Override
-    public void onServiceConnected(ComponentName component, IBinder binder) {
-        synchronized (mLock) {
-            if (component.equals(mBoundComponent)) {
-                if (D) Log.d(mTag, component + " connected");
-                mBoundService = binder;
-                if (mHandler !=null && mNewServiceWork != null) {
-                    mHandler.post(mNewServiceWork);
+    /**
+     * Start this watcher, including binding to the current best match and
+     * re-binding to any better matches down the road.
+     * <p>
+     * Note that if there are no matching encryption-aware services, we may not
+     * bind to a real service until after the current user is unlocked.
+     *
+     * @return {@code true} if a potential service implementation was found.
+     */
+    public final boolean start() {
+        // if we have to return false, do it before registering anything
+        if (isServiceMissing()) return false;
+
+        // listen for relevant package changes if service overlay is enabled on handler
+        if (mServicePackageName == null) {
+            new PackageMonitor() {
+                @Override
+                public void onPackageUpdateFinished(String packageName, int uid) {
+                    bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
                 }
-            } else {
-                Log.w(mTag, "unexpected onServiceConnected: " + component);
+
+                @Override
+                public void onPackageAdded(String packageName, int uid) {
+                    bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+                }
+
+                @Override
+                public void onPackageRemoved(String packageName, int uid) {
+                        bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+                }
+
+                @Override
+                public boolean onPackageChanged(String packageName, int uid, String[] components) {
+                        bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+                    return super.onPackageChanged(packageName, uid, components);
+                }
+            }.register(mContext, UserHandle.ALL, true, mHandler);
+        }
+
+        // listen for user change on handler
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+        intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
+        mContext.registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String action = intent.getAction();
+                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                        UserHandle.USER_NULL);
+                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                    mCurrentUserId = userId;
+                    bindBestPackage(false);
+                } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+                    if (userId == mCurrentUserId) {
+                        bindBestPackage(false);
+                    }
+                }
+            }
+        }, UserHandle.ALL, intentFilter, null, mHandler);
+
+        mCurrentUserId = ActivityManager.getCurrentUser();
+
+        mHandler.post(() -> bindBestPackage(false));
+        return true;
+    }
+
+    /** Returns thje name of the currently connected package or null. */
+    @Nullable
+    public String getCurrentPackageName() {
+        ComponentName bestComponent = mBestComponent;
+        return bestComponent == null ? null : bestComponent.getPackageName();
+    }
+
+    public int getCurrentPackageVersion() {
+        return mBestVersion;
+    }
+
+    /**
+     * Runs the given BinderRunner if currently connected. Returns true if it was run, and false
+     * otherwise. All invocations to runOnBinder are run serially.
+     */
+    public final void runOnBinder(BinderRunner runner) {
+        synchronized (mBindLock) {
+            IBinder service = mBestService;
+            if (service != null) {
+                try {
+                    runner.run(service);
+                } catch (Exception e) {
+                    // remote exceptions cannot be allowed to crash system server
+                    Log.e(TAG, "exception while while running " + runner + " on " + service
+                            + " from " + mBestComponent.toShortString(), e);
+                }
             }
         }
     }
 
+    private boolean isServiceMissing() {
+        return mContext.getPackageManager().queryIntentServicesAsUser(new Intent(mAction),
+                PackageManager.MATCH_DIRECT_BOOT_AWARE
+                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                UserHandle.USER_SYSTEM).isEmpty();
+    }
+
+    /**
+     * Searches and binds to the best package, or do nothing if the best package
+     * is already bound, unless force rebinding is requested.
+     *
+     * @param forceRebind          Force a rebinding to the best package if it's already
+     *                             bound.
+     */
+    @WorkerThread
+    private void bindBestPackage(boolean forceRebind) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+        Intent intent = new Intent(mAction);
+        if (mServicePackageName != null) {
+            intent.setPackage(mServicePackageName);
+        }
+
+        List<ResolveInfo> rInfos = mContext.getPackageManager().queryIntentServicesAsUser(intent,
+                PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AUTO,
+                mCurrentUserId);
+        if (rInfos == null) {
+            rInfos = Collections.emptyList();
+        }
+
+        ComponentName bestComponent = null;
+        int bestVersion = Integer.MIN_VALUE;
+        boolean bestIsMultiuser = false;
+
+        for (ResolveInfo rInfo : rInfos) {
+            ComponentName component = rInfo.serviceInfo.getComponentName();
+            String packageName = component.getPackageName();
+
+            // check signature
+            try {
+                PackageInfo pInfo = mContext.getPackageManager().getPackageInfo(packageName,
+                        PackageManager.GET_SIGNATURES
+                                | PackageManager.MATCH_DIRECT_BOOT_AUTO);
+                if (!isSignatureMatch(pInfo.signatures, mSignatureSets)) {
+                    Log.w(mTag, packageName + " resolves service " + mAction
+                            + ", but has wrong signature, ignoring");
+                    continue;
+                }
+            } catch (NameNotFoundException e) {
+                Log.wtf(mTag, e);
+                continue;
+            }
+
+            // check metadata
+            Bundle metadata = rInfo.serviceInfo.metaData;
+            int version = Integer.MIN_VALUE;
+            boolean isMultiuser = false;
+            if (metadata != null) {
+                version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
+                isMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false);
+            }
+
+            if (version > bestVersion) {
+                bestComponent = component;
+                bestVersion = version;
+                bestIsMultiuser = isMultiuser;
+            }
+        }
+
+        if (D) {
+            Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
+                    (mServicePackageName == null ? ""
+                            : "(" + mServicePackageName + ") "), rInfos.size(),
+                    (bestComponent == null ? "no new best component"
+                            : "new best component: " + bestComponent)));
+        }
+
+        if (bestComponent == null) {
+            Slog.w(mTag, "Odd, no component found for service " + mAction);
+            unbind();
+            return;
+        }
+
+        int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId;
+        boolean alreadyBound = Objects.equals(bestComponent, mBestComponent)
+                && bestVersion == mBestVersion && userId == mBestUserId;
+        if (forceRebind || !alreadyBound) {
+            unbind();
+            bind(bestComponent, bestVersion, userId);
+        }
+    }
+
+    @WorkerThread
+    private void bind(ComponentName component, int version, int userId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+        Intent intent = new Intent(mAction);
+        intent.setComponent(component);
+
+        mBestComponent = component;
+        mBestVersion = version;
+        mBestUserId = userId;
+
+        if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")");
+        mContext.bindServiceAsUser(intent, this,
+                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
+                UserHandle.of(userId));
+    }
+
+    @WorkerThread
+    private void unbind() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+        if (mBestComponent != null) {
+            if (D) Log.d(mTag, "unbinding " + mBestComponent);
+            mContext.unbindService(this);
+        }
+
+        mBestComponent = null;
+        mBestVersion = Integer.MIN_VALUE;
+        mBestUserId = UserHandle.USER_NULL;
+    }
+
+    @MainThread
     @Override
-    public void onServiceDisconnected(ComponentName component) {
-        synchronized (mLock) {
+    public final void onServiceConnected(ComponentName component, IBinder binder) {
+        mHandler.post(() -> {
+            if (D) Log.d(mTag, component + " connected");
+
+            // hold the lock so that mBestService cannot be used by runOnBinder until complete
+            synchronized (mBindLock) {
+                mBestService = binder;
+                onBind();
+            }
+        });
+    }
+
+    @MainThread
+    @Override
+    public final void onServiceDisconnected(ComponentName component) {
+        mHandler.post(() -> {
             if (D) Log.d(mTag, component + " disconnected");
 
-            if (component.equals(mBoundComponent)) {
-                mBoundService = null;
+            mBestService = null;
+            synchronized (mBindLock) {
+                onUnbind();
             }
-        }
+        });
     }
 
-    public @Nullable String getBestPackageName() {
-        synchronized (mLock) {
-            return mBoundPackageName;
-        }
-    }
-
-    public int getBestVersion() {
-        synchronized (mLock) {
-            return mBoundVersion;
-        }
-    }
-
-    /**
-     * The runner that runs on the binder retrieved from {@link ServiceWatcher}.
-     */
-    public interface BinderRunner {
-        /**
-         * Runs on the retrieved binder.
-         * @param binder the binder retrieved from the {@link ServiceWatcher}.
-         */
-        public void run(@NonNull IBinder binder);
-    }
-
-    /**
-     * Retrieves the binder from {@link ServiceWatcher} and runs it.
-     * @return whether a valid service exists.
-     */
-    public boolean runOnBinder(@NonNull BinderRunner runner) {
-        synchronized (mLock) {
-            if (mBoundService == null) {
-                return false;
-            } else {
-                runner.run(mBoundService);
-                return true;
-            }
-        }
-    }
-
-    public void switchUser(int userId) {
-        synchronized (mLock) {
-            mCurrentUserId = userId;
-            bindBestPackageLocked(mServicePackageName, false);
-        }
-    }
-
-    public void unlockUser(int userId) {
-        synchronized (mLock) {
-            if (userId == mCurrentUserId) {
-                bindBestPackageLocked(mServicePackageName, false);
-            }
-        }
+    @Override
+    public String toString() {
+        ComponentName bestComponent = mBestComponent;
+        return bestComponent == null ? "null" : bestComponent.toShortString() + "@" + mBestVersion;
     }
 }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 858dced..54c7d17 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -112,7 +112,6 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.AppFuseMount;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.FuseUnavailableMountException;
@@ -323,12 +322,6 @@
     @GuardedBy("mPackagesLock")
     private final SparseArray<ArraySet<String>> mPackages = new SparseArray<>();
 
-    @GuardedBy("mPackagesLock")
-    private final ArrayMap<String, Integer> mAppIds = new ArrayMap<>();
-
-    @GuardedBy("mPackagesLock")
-    private final SparseArray<String> mSandboxIds = new SparseArray<>();
-
     /**
      * List of volumes visible to any user.
      * TODO: may be have a map of userId -> volumes?
@@ -876,16 +869,16 @@
             try {
                 mVold.reset();
 
-                pushPackagesInfo();
                 // Tell vold about all existing and started users
                 for (UserInfo user : users) {
                     mVold.onUserAdded(user.id, user.serialNumber);
                 }
                 for (int userId : systemUnlockedUsers) {
-                    mVold.onUserStarted(userId, getPackagesArrayForUser(userId));
+                    sendUserStartedCallback(userId);
                     mStoraged.onUserStarted(userId);
                 }
                 mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
+                mStorageManagerInternal.onReset(mVold);
             } catch (Exception e) {
                 Slog.wtf(TAG, e);
             }
@@ -899,7 +892,7 @@
         // staging area is ready so it's ready for zygote-forked apps to
         // bind mount against.
         try {
-            mVold.onUserStarted(userId, getPackagesArrayForUser(userId));
+            sendUserStartedCallback(userId);
             mStoraged.onUserStarted(userId);
         } catch (Exception e) {
             Slog.wtf(TAG, e);
@@ -932,11 +925,52 @@
             Slog.wtf(TAG, e);
         }
 
+        synchronized (mPackagesLock) {
+            mPackages.delete(userId);
+        }
+
         synchronized (mLock) {
             mSystemUnlockedUsers = ArrayUtils.removeInt(mSystemUnlockedUsers, userId);
         }
     }
 
+    private void sendUserStartedCallback(int userId) throws Exception {
+        if (!ENABLE_ISOLATED_STORAGE) {
+            mVold.onUserStarted(userId, EmptyArray.STRING, EmptyArray.INT, EmptyArray.STRING);
+        }
+
+        final String[] packages;
+        final int[] appIds;
+        final String[] sandboxIds;
+        final SparseArray<String> sharedUserIds = mPmInternal.getAppsWithSharedUserIds();
+        final List<ApplicationInfo> appInfos =
+                mContext.getPackageManager().getInstalledApplicationsAsUser(
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+        synchronized (mPackagesLock) {
+            final ArraySet<String> userPackages = new ArraySet<>();
+            final ArrayMap<String, Integer> packageToAppId = new ArrayMap<>();
+            for (int i = appInfos.size() - 1; i >= 0; --i) {
+                final ApplicationInfo appInfo = appInfos.get(i);
+                if (appInfo.isInstantApp()) {
+                    continue;
+                }
+                userPackages.add(appInfo.packageName);
+                packageToAppId.put(appInfo.packageName, UserHandle.getAppId(appInfo.uid));
+            }
+            mPackages.put(userId, userPackages);
+
+            packages = new String[userPackages.size()];
+            appIds = new int[userPackages.size()];
+            sandboxIds = new String[userPackages.size()];
+            for (int i = userPackages.size() - 1; i >= 0; --i) {
+                packages[i] = userPackages.valueAt(i);
+                appIds[i] = packageToAppId.get(packages[i]);
+                sandboxIds[i] = getSandboxId(packages[i], sharedUserIds.get(appIds[i]));
+            }
+        }
+        mVold.onUserStarted(userId, packages, appIds, sandboxIds);
+    }
+
     @Override
     public void onAwakeStateChanged(boolean isAwake) {
         // Ignored
@@ -1454,111 +1488,12 @@
     }
 
     private void start() {
-        collectPackagesInfo();
         connect();
     }
 
-    @VisibleForTesting
-    void collectPackagesInfo() {
-        if (!ENABLE_ISOLATED_STORAGE) return;
-
-        resetPackageData();
-        final SparseArray<String> sharedUserIds = mPmInternal.getAppsWithSharedUserIds();
-        final int[] userIds = mUmInternal.getUserIds();
-        for (int userId : userIds) {
-            final List<ApplicationInfo> appInfos
-                    = mContext.getPackageManager().getInstalledApplicationsAsUser(
-                            PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
-            synchronized (mPackagesLock) {
-                final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId);
-                for (int i = appInfos.size() - 1; i >= 0; --i) {
-                    if (appInfos.get(i).isInstantApp()) {
-                        continue;
-                    }
-                    final String packageName = appInfos.get(i).packageName;
-                    userPackages.add(packageName);
-
-                    final int appId = UserHandle.getAppId(appInfos.get(i).uid);
-                    mAppIds.put(packageName, appId);
-                    mSandboxIds.put(appId, getSandboxId(packageName, sharedUserIds.get(appId)));
-                }
-            }
-        }
-    }
-
-    private void resetPackageData() {
-        synchronized (mPackagesLock) {
-            mPackages.clear();
-            mAppIds.clear();
-            mSandboxIds.clear();
-        }
-    }
-
     private static String getSandboxId(String packageName, String sharedUserId) {
         return sharedUserId == null ? packageName : SHARED_SANDBOX_ID_PREFIX + sharedUserId;
     }
-    private void pushPackagesInfo() throws RemoteException {
-        if (!ENABLE_ISOLATED_STORAGE) return;
-
-        // Arrays to fill up from {@link #mAppIds}
-        final String[] allPackageNames;
-        final int[] appIdsForPackages;
-
-        // Arrays to fill up from {@link #mSandboxIds}
-        final int[] allAppIds;
-        final String[] sandboxIdsForApps;
-        synchronized (mPackagesLock) {
-            allPackageNames = new String[mAppIds.size()];
-            appIdsForPackages = new int[mAppIds.size()];
-            for (int i = mAppIds.size() - 1; i >= 0; --i) {
-                allPackageNames[i] = mAppIds.keyAt(i);
-                appIdsForPackages[i] = mAppIds.valueAt(i);
-            }
-
-            allAppIds = new int[mSandboxIds.size()];
-            sandboxIdsForApps = new String[mSandboxIds.size()];
-            for (int i = mSandboxIds.size() - 1; i >= 0; --i) {
-                allAppIds[i] = mSandboxIds.keyAt(i);
-                sandboxIdsForApps[i] = mSandboxIds.valueAt(i);
-            }
-        }
-        mVold.addAppIds(allPackageNames, appIdsForPackages);
-        mVold.addSandboxIds(allAppIds, sandboxIdsForApps);
-    }
-
-    @GuardedBy("mPackagesLock")
-    private ArraySet<String> getAvailablePackagesForUserPL(int userId) {
-        ArraySet<String> userPackages = mPackages.get(userId);
-        if (userPackages == null) {
-            userPackages = new ArraySet<>();
-            mPackages.put(userId, userPackages);
-        }
-        return userPackages;
-    }
-
-    private String[] getPackagesArrayForUser(int userId) {
-        if (!ENABLE_ISOLATED_STORAGE) return EmptyArray.STRING;
-
-        final ArraySet<String> userPackages;
-        synchronized (mPackagesLock) {
-            userPackages = getAvailablePackagesForUserPL(userId);
-            if (!userPackages.isEmpty()) {
-                return userPackages.toArray(new String[0]);
-            }
-        }
-        final List<ApplicationInfo> appInfos =
-                mContext.getPackageManager().getInstalledApplicationsAsUser(
-                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
-        synchronized (mPackagesLock) {
-            for (int i = appInfos.size() - 1; i >= 0; --i) {
-                if (appInfos.get(i).isInstantApp()) {
-                    continue;
-                }
-                userPackages.add(appInfos.get(i).packageName);
-            }
-            return userPackages.toArray(new String[0]);
-        }
-    }
 
     private void connect() {
         IBinder binder = ServiceManager.getService("storaged");
@@ -3122,15 +3057,8 @@
             throw new SecurityException("Shady looking path " + path);
         }
 
-        final int uid = mPmInternal.getPackageUid(packageName,
-                PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
-        final String sandboxId;
-        synchronized (mPackagesLock) {
-            sandboxId = mSandboxIds.get(UserHandle.getAppId(uid));
-        }
-        if (uid < 0 || sandboxId == null) {
-            throw new IllegalArgumentException("Unknown package " + packageName);
-        }
+        final String sharedUserId = mPmInternal.getSharedUserIdForPackage(packageName);
+        final String sandboxId = getSandboxId(packageName, sharedUserId);
 
         final Matcher m = PATTERN_TRANSLATE.matcher(path);
         if (m.matches()) {
@@ -3139,7 +3067,9 @@
 
             // Does path belong to any packages belonging to this UID? If so,
             // they get to go straight through to legacy paths.
-            final String[] pkgs = mContext.getPackageManager().getPackagesForUid(uid);
+            final String[] pkgs = (sharedUserId == null)
+                    ? new String[] {packageName}
+                    : mPmInternal.getPackagesForSharedUserId(sharedUserId, userId);
             for (String pkg : pkgs) {
                 if (devicePath.startsWith("Android/data/" + pkg + "/") ||
                         devicePath.startsWith("Android/media/" + pkg + "/") ||
@@ -3706,6 +3636,10 @@
         private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies =
                 new CopyOnWriteArrayList<>();
 
+        @GuardedBy("mResetListeners")
+        private final List<StorageManagerInternal.ResetListener> mResetListeners =
+                new ArrayList<>();
+
         @Override
         public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) {
             // No locking - CopyOnWriteArrayList
@@ -3737,6 +3671,21 @@
             return mountMode;
         }
 
+        @Override
+        public void addResetListener(StorageManagerInternal.ResetListener listener) {
+            synchronized (mResetListeners) {
+                mResetListeners.add(listener);
+            }
+        }
+
+        public void onReset(IVold vold) {
+            synchronized (mResetListeners) {
+                for (StorageManagerInternal.ResetListener listener : mResetListeners) {
+                    listener.onReset(vold);
+                }
+            }
+        }
+
         public boolean hasExternalStorage(int uid, String packageName) {
             // No need to check for system uid. This avoids a deadlock between
             // PackageManagerService and AppOpsService.
@@ -3758,16 +3707,14 @@
                 int userId) {
             final String sandboxId;
             synchronized (mPackagesLock) {
-                final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId);
+                final ArraySet<String> userPackages = mPackages.get(userId);
                 // If userPackages is empty, it means the user is not started yet, so no need to
                 // do anything now.
-                if (userPackages.isEmpty() || userPackages.contains(packageName)) {
+                if (userPackages == null || userPackages.contains(packageName)) {
                     return;
                 }
                 userPackages.add(packageName);
-                mAppIds.put(packageName, appId);
                 sandboxId = getSandboxId(packageName, sharedUserId);
-                mSandboxIds.put(appId, sandboxId);
             }
 
             try {
@@ -3778,34 +3725,21 @@
         }
 
         @Override
-        public void destroySandboxForApp(String packageName, int userId) {
+        public void destroySandboxForApp(String packageName, String sharedUserId, int userId) {
             if (!ENABLE_ISOLATED_STORAGE) {
                 return;
             }
-            final int appId;
-            final String sandboxId;
+            final String sandboxId = getSandboxId(packageName, sharedUserId);
             synchronized (mPackagesLock) {
-                final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId);
-                userPackages.remove(packageName);
-                appId = mAppIds.get(packageName);
-                sandboxId = mSandboxIds.get(appId);
-
-                // If the package is not uninstalled in any other users, remove appId and sandboxId
-                // corresponding to it from the internal state.
-                boolean installedInAnyUser = false;
-                for (int i = mPackages.size() - 1; i >= 0; --i) {
-                    if (mPackages.valueAt(i).contains(packageName)) {
-                        installedInAnyUser = true;
-                        break;
-                    }
-                }
-                if (!installedInAnyUser) {
-                    mAppIds.remove(packageName);
-                    mSandboxIds.remove(appId);
+                final ArraySet<String> userPackages = mPackages.get(userId);
+                // If the userPackages is null, it means the user is not started but we still
+                // need to delete the sandbox data though.
+                if (userPackages != null) {
+                    userPackages.remove(packageName);
                 }
             }
             try {
-                mVold.destroySandboxForApp(packageName, appId, sandboxId, userId);
+                mVold.destroySandboxForApp(packageName, sandboxId, userId);
             } catch (Exception e) {
                 Slog.wtf(TAG, e);
             }
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 793a177..bbb1d13 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -1193,6 +1193,8 @@
                 if (i >= timings.length) {
                     if (repeatIndex >= 0) {
                         i = repeatIndex;
+                        // prevent infinite loop
+                        repeatIndex = -1;
                     } else {
                         break;
                     }
@@ -1258,8 +1260,6 @@
 
     private final class VibratorShellCommand extends ShellCommand {
 
-        private static final long MAX_VIBRATION_MS = 200;
-
         private final IBinder mToken;
 
         private VibratorShellCommand(IBinder token) {
@@ -1270,8 +1270,13 @@
         public int onCommand(String cmd) {
             if ("vibrate".equals(cmd)) {
                 return runVibrate();
+            } else if ("waveform".equals(cmd)) {
+                return runWaveform();
             } else if ("prebaked".equals(cmd)) {
                 return runPrebaked();
+            } else if ("cancel".equals(cmd)) {
+                cancelVibrate(mToken);
+                return 0;
             }
             return handleDefaultCommands(cmd);
         }
@@ -1303,9 +1308,6 @@
                 }
 
                 final long duration = Long.parseLong(getNextArgRequired());
-                if (duration > MAX_VIBRATION_MS) {
-                    throw new IllegalArgumentException("maximum duration is " + MAX_VIBRATION_MS);
-                }
                 String description = getNextArg();
                 if (description == null) {
                     description = "Shell command";
@@ -1321,6 +1323,62 @@
             }
         }
 
+        private int runWaveform() {
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runWaveform");
+            try {
+                if (checkDoNotDisturb()) {
+                    return 0;
+                }
+
+                String description = "Shell command";
+                int repeat = -1;
+                ArrayList<Integer> amplitudesList = null;
+
+                String opt;
+                while ((opt = getNextOption()) != null) {
+                    switch (opt) {
+                        case "-d":
+                            description = getNextArgRequired();
+                            break;
+                        case "-r":
+                            repeat = Integer.parseInt(getNextArgRequired());
+                            break;
+                        case "-a":
+                            if (amplitudesList == null) {
+                                amplitudesList = new ArrayList<Integer>();
+                            }
+                            break;
+                    }
+                }
+
+                ArrayList<Long> timingsList = new ArrayList<Long>();
+
+                String arg;
+                while ((arg = getNextArg()) != null) {
+                    if (amplitudesList != null && amplitudesList.size() < timingsList.size()) {
+                        amplitudesList.add(Integer.parseInt(arg));
+                    } else {
+                        timingsList.add(Long.parseLong(arg));
+                    }
+                }
+
+                VibrationEffect effect;
+                long[] timings = timingsList.stream().mapToLong(Long::longValue).toArray();
+                if (amplitudesList == null) {
+                    effect = VibrationEffect.createWaveform(timings, repeat);
+                } else {
+                    int[] amplitudes =
+                            amplitudesList.stream().mapToInt(Integer::intValue).toArray();
+                    effect = VibrationEffect.createWaveform(timings, amplitudes, repeat);
+                }
+                vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
+                        "Shell Command", mToken);
+                return 0;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            }
+        }
+
         private int runPrebaked() {
             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runPrebaked");
             try {
@@ -1355,9 +1413,19 @@
                 pw.println("  vibrate duration [description]");
                 pw.println("    Vibrates for duration milliseconds; ignored when device is on DND ");
                 pw.println("    (Do Not Disturb) mode.");
+                pw.println("  waveform [-d description] [-r index] [-a] duration [amplitude] ...");
+                pw.println("    Vibrates for durations and amplitudes in list;");
+                pw.println("    ignored when device is on DND (Do Not Disturb) mode.");
+                pw.println("    If -r is provided, the waveform loops back to the specified");
+                pw.println("    index (e.g. 0 loops from the beginning)");
+                pw.println("    If -a is provided, the command accepts duration-amplitude pairs;");
+                pw.println("    otherwise, it accepts durations only and alternates off/on");
+                pw.println("    Duration is in milliseconds; amplitude is a scale of 1-255.");
                 pw.println("  prebaked effect-id [description]");
                 pw.println("    Vibrates with prebaked effect; ignored when device is on DND ");
                 pw.println("    (Do Not Disturb) mode.");
+                pw.println("  cancel");
+                pw.println("    Cancels any active vibration");
                 pw.println("");
             }
         }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 9cc550d..d1b56e9 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -43,6 +43,7 @@
 
 import com.android.internal.os.ZygoteConnectionConstants;
 import com.android.server.am.ActivityManagerService;
+import com.android.server.wm.SurfaceAnimationThread;
 
 import java.io.File;
 import java.io.FileWriter;
@@ -280,6 +281,12 @@
         // And the display thread.
         mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
                 "display thread", DEFAULT_TIMEOUT));
+        // And the animation thread.
+        mHandlerCheckers.add(new HandlerChecker(AnimationThread.getHandler(),
+                "animation thread", DEFAULT_TIMEOUT));
+        // And the surface animation thread.
+        mHandlerCheckers.add(new HandlerChecker(SurfaceAnimationThread.getHandler(),
+                "surface animation thread", DEFAULT_TIMEOUT));
 
         // Initialize monitor for Binder threads.
         addMonitor(new BinderThreadMonitor());
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 9c60b8c..e879efd 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -241,9 +241,6 @@
         private final HashMap<Account, AtomicReference<String>> previousNameCache =
                 new HashMap<Account, AtomicReference<String>>();
 
-        private int debugDbInsertionPoint = -1;
-        private SQLiteStatement statementForLogging; // TODO Move to AccountsDb
-
         UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
             this.userId = userId;
             synchronized (dbLock) {
@@ -1299,7 +1296,6 @@
                 File preNDbFile = new File(mInjector.getPreNDatabaseName(userId));
                 File deDbFile = new File(mInjector.getDeDatabaseName(userId));
                 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
-                initializeDebugDbSizeAndCompileSqlStatementForLogging(accounts);
                 mUsers.append(userId, accounts);
                 purgeOldGrants(accounts);
                 validateAccounts = true;
@@ -1400,7 +1396,7 @@
         if (accounts != null) {
             synchronized (accounts.dbLock) {
                 synchronized (accounts.cacheLock) {
-                    accounts.statementForLogging.close();
+                    accounts.accountsDb.closeDebugStatement();
                     accounts.accountsDb.close();
                 }
             }
@@ -5124,41 +5120,36 @@
 
             @Override
             public void run() {
-                SQLiteStatement logStatement = userAccount.statementForLogging;
-                logStatement.bindLong(1, accountId);
-                logStatement.bindString(2, action);
-                logStatement.bindString(3, mDateFormat.format(new Date()));
-                logStatement.bindLong(4, callingUid);
-                logStatement.bindString(5, tableName);
-                logStatement.bindLong(6, userDebugDbInsertionPoint);
-                try {
-                    logStatement.execute();
-                } catch (IllegalStateException e) {
-                    // Guard against crash, DB can already be closed
-                    // since this statement is executed on a handler thread
-                    Slog.w(TAG, "Failed to insert a log record. accountId=" + accountId
-                            + " action=" + action + " tableName=" + tableName + " Error: " + e);
-                } finally {
-                    logStatement.clearBindings();
+                synchronized (userAccount.accountsDb.mDebugStatementLock) {
+                    SQLiteStatement logStatement = userAccount.accountsDb.getStatementForLogging();
+                    if (logStatement == null) {
+                        return; // Can't log.
+                    }
+                    logStatement.bindLong(1, accountId);
+                    logStatement.bindString(2, action);
+                    logStatement.bindString(3, mDateFormat.format(new Date()));
+                    logStatement.bindLong(4, callingUid);
+                    logStatement.bindString(5, tableName);
+                    logStatement.bindLong(6, userDebugDbInsertionPoint);
+                    try {
+                        logStatement.execute();
+                    } catch (IllegalStateException e) {
+                        // Guard against crash, DB can already be closed
+                        // since this statement is executed on a handler thread
+                        Slog.w(TAG, "Failed to insert a log record. accountId=" + accountId
+                                + " action=" + action + " tableName=" + tableName + " Error: " + e);
+                    } finally {
+                        logStatement.clearBindings();
+                    }
                 }
             }
         }
-
-        LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
-                callingUid, userAccount.debugDbInsertionPoint);
-        userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
-                % AccountsDb.MAX_DEBUG_DB_SIZE;
-        mHandler.post(logTask);
-    }
-
-    /*
-     * This should only be called once to compile the sql statement for logging
-     * and to find the insertion point.
-     */
-    private void initializeDebugDbSizeAndCompileSqlStatementForLogging(UserAccounts userAccount) {
-        userAccount.debugDbInsertionPoint = userAccount.accountsDb
-                .calculateDebugTableInsertionPoint();
-        userAccount.statementForLogging = userAccount.accountsDb.compileSqlStatementForLogging();
+        long insertionPoint = userAccount.accountsDb.reserveDebugDbInsertionPoint();
+        if (insertionPoint != -1) {
+            LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
+                    callingUid, insertionPoint);
+            mHandler.post(logTask);
+        }
     }
 
     public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
diff --git a/services/core/java/com/android/server/accounts/AccountsDb.java b/services/core/java/com/android/server/accounts/AccountsDb.java
index 0c3d268..712edcc 100644
--- a/services/core/java/com/android/server/accounts/AccountsDb.java
+++ b/services/core/java/com/android/server/accounts/AccountsDb.java
@@ -17,11 +17,13 @@
 package com.android.server.accounts;
 
 import android.accounts.Account;
+import android.annotation.Nullable;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteStatement;
 import android.os.FileUtils;
@@ -183,6 +185,10 @@
     private final Context mContext;
     private final File mPreNDatabaseFile;
 
+    final Object mDebugStatementLock = new Object();
+    private volatile long mDebugDbInsertionPoint = -1;
+    private volatile SQLiteStatement mDebugStatementForLogging; // not thread safe.
+
     AccountsDb(DeDatabaseHelper deDatabase, Context context, File preNDatabaseFile) {
         mDeDatabase = deDatabase;
         mContext = context;
@@ -1278,31 +1284,72 @@
      * Finds the row key where the next insertion should take place. Returns number of rows
      * if it is less {@link #MAX_DEBUG_DB_SIZE}, otherwise finds the lowest number available.
      */
-    int calculateDebugTableInsertionPoint() {
-        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
-        String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + TABLE_DEBUG;
-        int size = (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
-        if (size < MAX_DEBUG_DB_SIZE) {
-            return size;
-        }
+    long calculateDebugTableInsertionPoint() {
+        try {
+            SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+            String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + TABLE_DEBUG;
+            int size = (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
+            if (size < MAX_DEBUG_DB_SIZE) {
+                return size;
+            }
 
-        // This query finds the smallest timestamp value (and if 2 records have
-        // same timestamp, the choose the lower id).
-        queryCountDebugDbRows = "SELECT " + DEBUG_TABLE_KEY +
-                " FROM " + TABLE_DEBUG +
-                " ORDER BY "  + DEBUG_TABLE_TIMESTAMP + "," + DEBUG_TABLE_KEY +
-                " LIMIT 1";
-        return (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
+            // This query finds the smallest timestamp value (and if 2 records have
+            // same timestamp, the choose the lower id).
+            queryCountDebugDbRows =
+                    "SELECT " + DEBUG_TABLE_KEY
+                    + " FROM " + TABLE_DEBUG
+                    + " ORDER BY "  + DEBUG_TABLE_TIMESTAMP + ","
+                    + DEBUG_TABLE_KEY
+                    + " LIMIT 1";
+            return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
+        } catch (SQLiteException e) {
+            Log.e(TAG, "Failed to open debug table" + e);
+            return -1;
+        }
     }
 
     SQLiteStatement compileSqlStatementForLogging() {
-        // TODO b/31708085 Fix debug logging - it eagerly opens database for write without a need
         SQLiteDatabase db = mDeDatabase.getWritableDatabase();
         String sql = "INSERT OR REPLACE INTO " + AccountsDb.TABLE_DEBUG
                 + " VALUES (?,?,?,?,?,?)";
         return db.compileStatement(sql);
     }
 
+    /**
+     * Returns statement for logging or {@code null} on database open failure.
+     * Returned value must be guarded by {link #debugStatementLock}
+     */
+    @Nullable SQLiteStatement getStatementForLogging() {
+        if (mDebugStatementForLogging != null) {
+            return mDebugStatementForLogging;
+        }
+        try {
+            mDebugStatementForLogging =  compileSqlStatementForLogging();
+            return mDebugStatementForLogging;
+        } catch (SQLiteException e) {
+            Log.e(TAG, "Failed to open debug table" + e);
+            return null;
+        }
+    }
+
+    void closeDebugStatement() {
+        synchronized (mDebugStatementLock) {
+            if (mDebugStatementForLogging != null) {
+                mDebugStatementForLogging.close();
+                mDebugStatementForLogging = null;
+            }
+        }
+    }
+
+    long reserveDebugDbInsertionPoint() {
+        if (mDebugDbInsertionPoint == -1) {
+            mDebugDbInsertionPoint = calculateDebugTableInsertionPoint();
+            return mDebugDbInsertionPoint;
+        }
+        mDebugDbInsertionPoint = (mDebugDbInsertionPoint + 1) % MAX_DEBUG_DB_SIZE;
+        return mDebugDbInsertionPoint;
+    }
+
     void dumpDebugTable(PrintWriter pw) {
         SQLiteDatabase db = mDeDatabase.getReadableDatabase();
         Cursor cursor = db.query(TABLE_DEBUG, null,
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
new file mode 100644
index 0000000..ccead6c
--- /dev/null
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2012 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.server.adb;
+
+import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
+
+import android.app.ActivityManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.adb.AdbDebuggingManagerProto;
+import android.util.Base64;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.server.FgThread;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+/**
+ * Provides communication to the Android Debug Bridge daemon to allow, deny, or clear public keysi
+ * that are authorized to connect to the ADB service itself.
+ */
+public class AdbDebuggingManager {
+    private static final String TAG = "AdbDebuggingManager";
+    private static final boolean DEBUG = false;
+
+    private static final String ADBD_SOCKET = "adbd";
+    private static final String ADB_DIRECTORY = "misc/adb";
+    private static final String ADB_KEYS_FILE = "adb_keys";
+    private static final int BUFFER_SIZE = 4096;
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private AdbDebuggingThread mThread;
+    private boolean mAdbEnabled = false;
+    private String mFingerprints;
+
+    public AdbDebuggingManager(Context context) {
+        mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
+        mContext = context;
+    }
+
+    class AdbDebuggingThread extends Thread {
+        private boolean mStopped;
+        private LocalSocket mSocket;
+        private OutputStream mOutputStream;
+        private InputStream mInputStream;
+
+        AdbDebuggingThread() {
+            super(TAG);
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) Slog.d(TAG, "Entering thread");
+            while (true) {
+                synchronized (this) {
+                    if (mStopped) {
+                        if (DEBUG) Slog.d(TAG, "Exiting thread");
+                        return;
+                    }
+                    try {
+                        openSocketLocked();
+                    } catch (Exception e) {
+                        /* Don't loop too fast if adbd dies, before init restarts it */
+                        SystemClock.sleep(1000);
+                    }
+                }
+                try {
+                    listenToSocket();
+                } catch (Exception e) {
+                    /* Don't loop too fast if adbd dies, before init restarts it */
+                    SystemClock.sleep(1000);
+                }
+            }
+        }
+
+        private void openSocketLocked() throws IOException {
+            try {
+                LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET,
+                        LocalSocketAddress.Namespace.RESERVED);
+                mInputStream = null;
+
+                if (DEBUG) Slog.d(TAG, "Creating socket");
+                mSocket = new LocalSocket();
+                mSocket.connect(address);
+
+                mOutputStream = mSocket.getOutputStream();
+                mInputStream = mSocket.getInputStream();
+            } catch (IOException ioe) {
+                closeSocketLocked();
+                throw ioe;
+            }
+        }
+
+        private void listenToSocket() throws IOException {
+            try {
+                byte[] buffer = new byte[BUFFER_SIZE];
+                while (true) {
+                    int count = mInputStream.read(buffer);
+                    if (count < 0) {
+                        break;
+                    }
+
+                    if (buffer[0] == 'P' && buffer[1] == 'K') {
+                        String key = new String(Arrays.copyOfRange(buffer, 2, count));
+                        Slog.d(TAG, "Received public key: " + key);
+                        Message msg = mHandler.obtainMessage(
+                                AdbDebuggingHandler.MESSAGE_ADB_CONFIRM);
+                        msg.obj = key;
+                        mHandler.sendMessage(msg);
+                    } else {
+                        Slog.e(TAG, "Wrong message: "
+                                + (new String(Arrays.copyOfRange(buffer, 0, 2))));
+                        break;
+                    }
+                }
+            } finally {
+                synchronized (this) {
+                    closeSocketLocked();
+                }
+            }
+        }
+
+        private void closeSocketLocked() {
+            if (DEBUG) Slog.d(TAG, "Closing socket");
+            try {
+                if (mOutputStream != null) {
+                    mOutputStream.close();
+                    mOutputStream = null;
+                }
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed closing output stream: " + e);
+            }
+
+            try {
+                if (mSocket != null) {
+                    mSocket.close();
+                    mSocket = null;
+                }
+            } catch (IOException ex) {
+                Slog.e(TAG, "Failed closing socket: " + ex);
+            }
+        }
+
+        /** Call to stop listening on the socket and exit the thread. */
+        void stopListening() {
+            synchronized (this) {
+                mStopped = true;
+                closeSocketLocked();
+            }
+        }
+
+        void sendResponse(String msg) {
+            synchronized (this) {
+                if (!mStopped && mOutputStream != null) {
+                    try {
+                        mOutputStream.write(msg.getBytes());
+                    } catch (IOException ex) {
+                        Slog.e(TAG, "Failed to write response:", ex);
+                    }
+                }
+            }
+        }
+    }
+
+    class AdbDebuggingHandler extends Handler {
+        private static final int MESSAGE_ADB_ENABLED = 1;
+        private static final int MESSAGE_ADB_DISABLED = 2;
+        private static final int MESSAGE_ADB_ALLOW = 3;
+        private static final int MESSAGE_ADB_DENY = 4;
+        private static final int MESSAGE_ADB_CONFIRM = 5;
+        private static final int MESSAGE_ADB_CLEAR = 6;
+
+        AdbDebuggingHandler(Looper looper) {
+            super(looper);
+        }
+
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_ADB_ENABLED:
+                    if (mAdbEnabled) {
+                        break;
+                    }
+
+                    mAdbEnabled = true;
+
+                    mThread = new AdbDebuggingThread();
+                    mThread.start();
+
+                    break;
+
+                case MESSAGE_ADB_DISABLED:
+                    if (!mAdbEnabled) {
+                        break;
+                    }
+
+                    mAdbEnabled = false;
+
+                    if (mThread != null) {
+                        mThread.stopListening();
+                        mThread = null;
+                    }
+
+                    break;
+
+                case MESSAGE_ADB_ALLOW: {
+                    String key = (String) msg.obj;
+                    String fingerprints = getFingerprints(key);
+
+                    if (!fingerprints.equals(mFingerprints)) {
+                        Slog.e(TAG, "Fingerprints do not match. Got "
+                                + fingerprints + ", expected " + mFingerprints);
+                        break;
+                    }
+
+                    if (msg.arg1 == 1) {
+                        writeKey(key);
+                    }
+
+                    if (mThread != null) {
+                        mThread.sendResponse("OK");
+                    }
+                    break;
+                }
+
+                case MESSAGE_ADB_DENY:
+                    if (mThread != null) {
+                        mThread.sendResponse("NO");
+                    }
+                    break;
+
+                case MESSAGE_ADB_CONFIRM: {
+                    if ("trigger_restart_min_framework".equals(
+                            SystemProperties.get("vold.decrypt"))) {
+                        Slog.d(TAG, "Deferring adb confirmation until after vold decrypt");
+                        if (mThread != null) {
+                            mThread.sendResponse("NO");
+                        }
+                        break;
+                    }
+                    String key = (String) msg.obj;
+                    String fingerprints = getFingerprints(key);
+                    if ("".equals(fingerprints)) {
+                        if (mThread != null) {
+                            mThread.sendResponse("NO");
+                        }
+                        break;
+                    }
+                    mFingerprints = fingerprints;
+                    startConfirmation(key, mFingerprints);
+                    break;
+                }
+
+                case MESSAGE_ADB_CLEAR:
+                    deleteKeyFile();
+                    break;
+            }
+        }
+    }
+
+    private String getFingerprints(String key) {
+        String hex = "0123456789ABCDEF";
+        StringBuilder sb = new StringBuilder();
+        MessageDigest digester;
+
+        if (key == null) {
+            return "";
+        }
+
+        try {
+            digester = MessageDigest.getInstance("MD5");
+        } catch (Exception ex) {
+            Slog.e(TAG, "Error getting digester", ex);
+            return "";
+        }
+
+        byte[] base64_data = key.split("\\s+")[0].getBytes();
+        byte[] digest;
+        try {
+            digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "error doing base64 decoding", e);
+            return "";
+        }
+        for (int i = 0; i < digest.length; i++) {
+            sb.append(hex.charAt((digest[i] >> 4) & 0xf));
+            sb.append(hex.charAt(digest[i] & 0xf));
+            if (i < digest.length - 1) {
+                sb.append(":");
+            }
+        }
+        return sb.toString();
+    }
+
+    private void startConfirmation(String key, String fingerprints) {
+        int currentUserId = ActivityManager.getCurrentUser();
+        UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
+        String componentString;
+        if (userInfo.isAdmin()) {
+            componentString = Resources.getSystem().getString(
+                    com.android.internal.R.string.config_customAdbPublicKeyConfirmationComponent);
+        } else {
+            // If the current foreground user is not the admin user we send a different
+            // notification specific to secondary users.
+            componentString = Resources.getSystem().getString(
+                    R.string.config_customAdbPublicKeyConfirmationSecondaryUserComponent);
+        }
+        ComponentName componentName = ComponentName.unflattenFromString(componentString);
+        if (startConfirmationActivity(componentName, userInfo.getUserHandle(), key, fingerprints)
+                || startConfirmationService(componentName, userInfo.getUserHandle(),
+                        key, fingerprints)) {
+            return;
+        }
+        Slog.e(TAG, "unable to start customAdbPublicKeyConfirmation[SecondaryUser]Component "
+                + componentString + " as an Activity or a Service");
+    }
+
+    /**
+     * @return true if the componentName led to an Activity that was started.
+     */
+    private boolean startConfirmationActivity(ComponentName componentName, UserHandle userHandle,
+            String key, String fingerprints) {
+        PackageManager packageManager = mContext.getPackageManager();
+        Intent intent = createConfirmationIntent(componentName, key, fingerprints);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
+            try {
+                mContext.startActivityAsUser(intent, userHandle);
+                return true;
+            } catch (ActivityNotFoundException e) {
+                Slog.e(TAG, "unable to start adb whitelist activity: " + componentName, e);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return true if the componentName led to a Service that was started.
+     */
+    private boolean startConfirmationService(ComponentName componentName, UserHandle userHandle,
+            String key, String fingerprints) {
+        Intent intent = createConfirmationIntent(componentName, key, fingerprints);
+        try {
+            if (mContext.startServiceAsUser(intent, userHandle) != null) {
+                return true;
+            }
+        } catch (SecurityException e) {
+            Slog.e(TAG, "unable to start adb whitelist service: " + componentName, e);
+        }
+        return false;
+    }
+
+    private Intent createConfirmationIntent(ComponentName componentName, String key,
+            String fingerprints) {
+        Intent intent = new Intent();
+        intent.setClassName(componentName.getPackageName(), componentName.getClassName());
+        intent.putExtra("key", key);
+        intent.putExtra("fingerprints", fingerprints);
+        return intent;
+    }
+
+    private File getUserKeyFile() {
+        File dataDir = Environment.getDataDirectory();
+        File adbDir = new File(dataDir, ADB_DIRECTORY);
+
+        if (!adbDir.exists()) {
+            Slog.e(TAG, "ADB data directory does not exist");
+            return null;
+        }
+
+        return new File(adbDir, ADB_KEYS_FILE);
+    }
+
+    private void writeKey(String key) {
+        try {
+            File keyFile = getUserKeyFile();
+
+            if (keyFile == null) {
+                return;
+            }
+
+            if (!keyFile.exists()) {
+                keyFile.createNewFile();
+                FileUtils.setPermissions(keyFile.toString(),
+                        FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1);
+            }
+
+            FileOutputStream fo = new FileOutputStream(keyFile, true);
+            fo.write(key.getBytes());
+            fo.write('\n');
+            fo.close();
+        } catch (IOException ex) {
+            Slog.e(TAG, "Error writing key:" + ex);
+        }
+    }
+
+    private void deleteKeyFile() {
+        File keyFile = getUserKeyFile();
+        if (keyFile != null) {
+            keyFile.delete();
+        }
+    }
+
+    /**
+     * When {@code enabled} is {@code true}, this allows ADB debugging and starts the ADB hanler
+     * thread. When {@code enabled} is {@code false}, this disallows ADB debugging and shuts
+     * down the handler thread.
+     */
+    public void setAdbEnabled(boolean enabled) {
+        mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED
+                                          : AdbDebuggingHandler.MESSAGE_ADB_DISABLED);
+    }
+
+    /**
+     * Allows the debugging from the endpoint identified by {@code publicKey} either once or
+     * always if {@code alwaysAllow} is {@code true}.
+     */
+    public void allowDebugging(boolean alwaysAllow, String publicKey) {
+        Message msg = mHandler.obtainMessage(AdbDebuggingHandler.MESSAGE_ADB_ALLOW);
+        msg.arg1 = alwaysAllow ? 1 : 0;
+        msg.obj = publicKey;
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Denies debugging connection from the device that last requested to connect.
+     */
+    public void denyDebugging() {
+        mHandler.sendEmptyMessage(AdbDebuggingHandler.MESSAGE_ADB_DENY);
+    }
+
+    /**
+     * Clears all previously accepted ADB debugging public keys. Any subsequent request will need
+     * to pass through {@link #allowUsbDebugging(boolean, String)} again.
+     */
+    public void clearDebuggingKeys() {
+        mHandler.sendEmptyMessage(AdbDebuggingHandler.MESSAGE_ADB_CLEAR);
+    }
+
+    /**
+     * Dump the USB debugging state.
+     */
+    public void dump(DualDumpOutputStream dump, String idName, long id) {
+        long token = dump.start(idName, id);
+
+        dump.write("connected_to_adb", AdbDebuggingManagerProto.CONNECTED_TO_ADB, mThread != null);
+        writeStringIfNotNull(dump, "last_key_received", AdbDebuggingManagerProto.LAST_KEY_RECEVIED,
+                mFingerprints);
+
+        try {
+            dump.write("user_keys", AdbDebuggingManagerProto.USER_KEYS,
+                    FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
+        } catch (IOException e) {
+            Slog.e(TAG, "Cannot read user keys", e);
+        }
+
+        try {
+            dump.write("system_keys", AdbDebuggingManagerProto.SYSTEM_KEYS,
+                    FileUtils.readTextFile(new File("/adb_keys"), 0, null));
+        } catch (IOException e) {
+            Slog.e(TAG, "Cannot read system keys", e);
+        }
+
+        dump.end(token);
+    }
+}
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
new file mode 100644
index 0000000..e5ab8fc
--- /dev/null
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2018 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.server.adb;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.debug.AdbManagerInternal;
+import android.debug.IAdbManager;
+import android.debug.IAdbTransport;
+import android.hardware.usb.UsbManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.service.adb.AdbServiceDumpProto;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
+
+/**
+ * The Android Debug Bridge (ADB) service. This controls the availability of ADB and authorization
+ * of devices allowed to connect to ADB.
+ */
+public class AdbService extends IAdbManager.Stub {
+    /**
+     * Manages the service lifecycle for {@code AdbService} in {@code SystemServer}.
+     */
+    public static class Lifecycle extends SystemService {
+        private AdbService mAdbService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mAdbService = new AdbService(getContext());
+            publishBinderService(Context.ADB_SERVICE, mAdbService);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+                mAdbService.systemReady();
+            } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+                mAdbService.bootCompleted();
+            }
+        }
+    }
+
+    private class AdbManagerInternalImpl extends AdbManagerInternal {
+        @Override
+        public void registerTransport(IAdbTransport transport) {
+            mTransports.put(transport.asBinder(), transport);
+        }
+
+        @Override
+        public void unregisterTransport(IAdbTransport transport) {
+            mTransports.remove(transport.asBinder());
+        }
+
+        @Override
+        public boolean isAdbEnabled() {
+            return mAdbEnabled;
+        }
+    }
+
+    private final class AdbHandler extends Handler {
+        AdbHandler(Looper looper) {
+            super(looper);
+            try {
+                /*
+                 * Use the normal bootmode persistent prop to maintain state of adb across
+                 * all boot modes.
+                 */
+                mAdbEnabled = containsFunction(
+                        SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, ""),
+                        UsbManager.USB_FUNCTION_ADB);
+
+                // register observer to listen for settings changes
+                mContentResolver.registerContentObserver(
+                        Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
+                        false, new AdbSettingsObserver());
+            } catch (Exception e) {
+                Slog.e(TAG, "Error initializing AdbHandler", e);
+            }
+        }
+
+        private boolean containsFunction(String functions, String function) {
+            int index = functions.indexOf(function);
+            if (index < 0) return false;
+            if (index > 0 && functions.charAt(index - 1) != ',') return false;
+            int charAfter = index + function.length();
+            if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
+            return true;
+        }
+
+        public void sendMessage(int what, boolean arg) {
+            removeMessages(what);
+            Message m = Message.obtain(this, what);
+            m.arg1 = (arg ? 1 : 0);
+            sendMessage(m);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_ENABLE_ADB:
+                    setAdbEnabled(msg.arg1 == 1);
+                    break;
+                case MSG_BOOT_COMPLETED:
+                    if (mDebuggingManager != null) {
+                        mDebuggingManager.setAdbEnabled(mAdbEnabled);
+                    }
+                    break;
+            }
+        }
+    }
+
+    private class AdbSettingsObserver extends ContentObserver {
+        AdbSettingsObserver() {
+            super(null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            boolean enable = (Settings.Global.getInt(mContentResolver,
+                    Settings.Global.ADB_ENABLED, 0) > 0);
+            mHandler.sendMessage(MSG_ENABLE_ADB, enable);
+        }
+    }
+
+    private static final String TAG = "AdbService";
+    private static final boolean DEBUG = false;
+
+    private static final int MSG_ENABLE_ADB = 1;
+    private static final int MSG_BOOT_COMPLETED = 2;
+
+    /**
+     * The persistent property which stores whether adb is enabled or not.
+     * May also contain vendor-specific default functions for testing purposes.
+     */
+    private static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
+
+    private final Context mContext;
+    private final ContentResolver mContentResolver;
+    private final AdbService.AdbHandler mHandler;
+    private final ArrayMap<IBinder, IAdbTransport> mTransports = new ArrayMap<>();
+
+    private boolean mAdbEnabled;
+    private AdbDebuggingManager mDebuggingManager;
+
+    private AdbService(Context context) {
+        mContext = context;
+        mContentResolver = context.getContentResolver();
+
+        boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
+        boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
+        if (secureAdbEnabled && !dataEncrypted) {
+            mDebuggingManager = new AdbDebuggingManager(context);
+        }
+
+        mHandler = new AdbHandler(FgThread.get().getLooper());
+
+        LocalServices.addService(AdbManagerInternal.class, new AdbManagerInternalImpl());
+    }
+
+    /**
+     * Called in response to {@code SystemService.PHASE_ACTIVITY_MANAGER_READY} from {@code
+     * SystemServer}.
+     */
+    public void systemReady() {
+        if (DEBUG) Slog.d(TAG, "systemReady");
+
+        // make sure the ADB_ENABLED setting value matches the current state
+        try {
+            Settings.Global.putInt(mContentResolver,
+                    Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+        } catch (SecurityException e) {
+            // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed.
+            Slog.d(TAG, "ADB_ENABLED is restricted.");
+        }
+    }
+
+    /**
+     * Callend in response to {@code SystemService.PHASE_BOOT_COMPLETED} from {@code SystemServer}.
+     */
+    public void bootCompleted() {
+        if (DEBUG) Slog.d(TAG, "boot completed");
+        mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
+    }
+
+    @Override
+    public void allowDebugging(boolean alwaysAllow, String publicKey) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.allowDebugging(alwaysAllow, publicKey);
+        }
+    }
+
+    @Override
+    public void denyDebugging() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.denyDebugging();
+        }
+    }
+
+    @Override
+    public void clearDebuggingKeys() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.clearDebuggingKeys();
+        } else {
+            throw new RuntimeException("Cannot clear ADB debugging keys, "
+                    + "AdbDebuggingManager not enabled");
+        }
+    }
+
+    private void setAdbEnabled(boolean enable) {
+        if (DEBUG) Slog.d(TAG, "setAdbEnabled(" + enable + "), mAdbEnabled=" + mAdbEnabled);
+
+        if (enable == mAdbEnabled) {
+            return;
+        }
+        mAdbEnabled = enable;
+
+        for (IAdbTransport transport : mTransports.values()) {
+            try {
+                transport.onAdbEnabled(enable);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Unable to send onAdbEnabled to transport " + transport.toString());
+            }
+        }
+
+        if (mDebuggingManager != null) {
+            mDebuggingManager.setAdbEnabled(enable);
+        }
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+
+        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            ArraySet<String> argsSet = new ArraySet<>();
+            Collections.addAll(argsSet, args);
+
+            boolean dumpAsProto = false;
+            if (argsSet.contains("--proto")) {
+                dumpAsProto = true;
+            }
+
+            if (argsSet.size() == 0 || argsSet.contains("-a") || dumpAsProto) {
+                DualDumpOutputStream dump;
+                if (dumpAsProto) {
+                    dump = new DualDumpOutputStream(new ProtoOutputStream(fd));
+                } else {
+                    pw.println("ADB MANAGER STATE (dumpsys adb):");
+
+                    dump = new DualDumpOutputStream(new IndentingPrintWriter(pw, "  "));
+                }
+
+                if (mDebuggingManager != null) {
+                    mDebuggingManager.dump(dump, "debugging_manager",
+                            AdbServiceDumpProto.DEBUGGING_MANAGER);
+                }
+
+                dump.flush();
+            } else {
+                pw.println("Dump current ADB state");
+                pw.println("  No commands available");
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 405a2d0..2d3912b 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -95,6 +95,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.webkit.WebViewZygote;
 import com.android.server.uri.NeededUriGrants;
+import com.android.server.wm.ActivityServiceConnectionsHolder;
 
 public final class ActiveServices {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActiveServices" : TAG_AM;
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
deleted file mode 100644
index ede13ef..0000000
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ /dev/null
@@ -1,1283 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.FLAG_PRIVATE;
-import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
-
-import static com.android.server.am.ActivityDisplayProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityDisplayProto.FOCUSED_STACK_ID;
-import static com.android.server.am.ActivityDisplayProto.ID;
-import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
-import static com.android.server.am.ActivityDisplayProto.STACKS;
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.am.ActivityStackSupervisor.TAG_STATES;
-import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.app.WindowConfiguration;
-import android.graphics.Point;
-import android.os.UserHandle;
-import android.util.IntArray;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-import android.view.Display;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.server.wm.ConfigurationContainer;
-import com.android.server.wm.DisplayWindowController;
-import com.android.server.wm.WindowContainerListener;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-/**
- * Exactly one of these classes per Display in the system. Capable of holding zero or more
- * attached {@link ActivityStack}s.
- */
-class ActivityDisplay extends ConfigurationContainer<ActivityStack>
-        implements WindowContainerListener {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_ATM;
-    private static final String TAG_STACK = TAG + POSTFIX_STACK;
-
-    static final int POSITION_TOP = Integer.MAX_VALUE;
-    static final int POSITION_BOTTOM = Integer.MIN_VALUE;
-
-
-    /**
-     * Counter for next free stack ID to use for dynamic activity stacks. Unique across displays.
-     */
-    private static int sNextFreeStackId = 0;
-
-    private ActivityStackSupervisor mSupervisor;
-    /** Actual Display this object tracks. */
-    int mDisplayId;
-    Display mDisplay;
-
-    /**
-     * All of the stacks on this display. Order matters, topmost stack is in front of all other
-     * stacks, bottommost behind. Accessed directly by ActivityManager package classes. Any calls
-     * changing the list should also call {@link #onStackOrderChanged()}.
-     */
-    private final ArrayList<ActivityStack> mStacks = new ArrayList<>();
-    private ArrayList<OnStackOrderChangedListener> mStackOrderChangedCallbacks = new ArrayList<>();
-
-    /** Array of all UIDs that are present on the display. */
-    private IntArray mDisplayAccessUIDs = new IntArray();
-
-    /** All tokens used to put activities on this stack to sleep (including mOffToken) */
-    final ArrayList<ActivityTaskManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>();
-    /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
-    ActivityTaskManagerInternal.SleepToken mOffToken;
-
-    private boolean mSleeping;
-
-    /**
-     * The display is removed from the system and we are just waiting for all activities on it to be
-     * finished before removing this object.
-     */
-    private boolean mRemoved;
-
-    /**
-     * A focusable stack that is purposely to be positioned at the top. Although the stack may not
-     * have the topmost index, it is used as a preferred candidate to prevent being unable to resume
-     * target stack properly when there are other focusable always-on-top stacks.
-     */
-    private ActivityStack mPreferredTopFocusableStack;
-
-    /**
-     * If this is the same as {@link #getFocusedStack} then the activity on the top of the focused
-     * stack has been resumed. If stacks are changing position this will hold the old stack until
-     * the new stack becomes resumed after which it will be set to current focused stack.
-     */
-    private ActivityStack mLastFocusedStack;
-
-    // Cached reference to some special stacks we tend to get a lot so we don't need to loop
-    // through the list to find them.
-    private ActivityStack mHomeStack = null;
-    private ActivityStack mRecentsStack = null;
-    private ActivityStack mPinnedStack = null;
-    private ActivityStack mSplitScreenPrimaryStack = null;
-
-    // Used in updating the display size
-    private Point mTmpDisplaySize = new Point();
-
-    private DisplayWindowController mWindowContainerController;
-
-    private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
-
-    ActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
-        mSupervisor = supervisor;
-        mDisplayId = display.getDisplayId();
-        mDisplay = display;
-        mWindowContainerController = createWindowContainerController();
-        updateBounds();
-    }
-
-    protected DisplayWindowController createWindowContainerController() {
-        return new DisplayWindowController(mDisplay, this);
-    }
-
-    DisplayWindowController getWindowContainerController() {
-        return mWindowContainerController;
-    }
-
-    void updateBounds() {
-        mDisplay.getSize(mTmpDisplaySize);
-        setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
-    }
-
-    void addChild(ActivityStack stack, int position) {
-        if (position == POSITION_BOTTOM) {
-            position = 0;
-        } else if (position == POSITION_TOP) {
-            position = mStacks.size();
-        }
-        if (DEBUG_STACK) Slog.v(TAG_STACK, "addChild: attaching " + stack
-                + " to displayId=" + mDisplayId + " position=" + position);
-        addStackReferenceIfNeeded(stack);
-        positionChildAt(stack, position);
-        mSupervisor.mService.updateSleepIfNeededLocked();
-    }
-
-    void removeChild(ActivityStack stack) {
-        if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack
-                + " from displayId=" + mDisplayId);
-        mStacks.remove(stack);
-        if (mPreferredTopFocusableStack == stack) {
-            mPreferredTopFocusableStack = null;
-        }
-        removeStackReferenceIfNeeded(stack);
-        releaseSelfIfNeeded();
-        mSupervisor.mService.updateSleepIfNeededLocked();
-        onStackOrderChanged();
-    }
-
-    void positionChildAtTop(ActivityStack stack, boolean includingParents) {
-        positionChildAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
-    }
-
-    void positionChildAtTop(ActivityStack stack, boolean includingParents,
-            String updateLastFocusedStackReason) {
-        positionChildAt(stack, mStacks.size(), includingParents, updateLastFocusedStackReason);
-    }
-
-    void positionChildAtBottom(ActivityStack stack) {
-        positionChildAtBottom(stack, null /* updateLastFocusedStackReason */);
-    }
-
-    void positionChildAtBottom(ActivityStack stack, String updateLastFocusedStackReason) {
-        positionChildAt(stack, 0, false /* includingParents */, updateLastFocusedStackReason);
-    }
-
-    private void positionChildAt(ActivityStack stack, int position) {
-        positionChildAt(stack, position, false /* includingParents */,
-                null /* updateLastFocusedStackReason */);
-    }
-
-    private void positionChildAt(ActivityStack stack, int position, boolean includingParents,
-            String updateLastFocusedStackReason) {
-        // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
-        //       the position internally, also update the logic here
-        final ActivityStack prevFocusedStack = updateLastFocusedStackReason != null
-                ? getFocusedStack() : null;
-        final boolean wasContained = mStacks.remove(stack);
-        final int insertPosition = getTopInsertPosition(stack, position);
-        mStacks.add(insertPosition, stack);
-
-        // The insert position may be adjusted to non-top when there is always-on-top stack. Since
-        // the original position is preferred to be top, the stack should have higher priority when
-        // we are looking for top focusable stack. The condition {@code wasContained} restricts the
-        // preferred stack is set only when moving an existing stack to top instead of adding a new
-        // stack that may be too early (e.g. in the middle of launching or reparenting).
-        if (wasContained && position >= mStacks.size() - 1 && stack.isFocusableAndVisible()) {
-            mPreferredTopFocusableStack = stack;
-        } else if (mPreferredTopFocusableStack == stack) {
-            mPreferredTopFocusableStack = null;
-        }
-
-        if (updateLastFocusedStackReason != null) {
-            final ActivityStack currentFocusedStack = getFocusedStack();
-            if (currentFocusedStack != prevFocusedStack) {
-                mLastFocusedStack = prevFocusedStack;
-                EventLogTags.writeAmFocusedStack(mSupervisor.mCurrentUser, mDisplayId,
-                        currentFocusedStack == null ? -1 : currentFocusedStack.getStackId(),
-                        mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(),
-                        updateLastFocusedStackReason);
-            }
-        }
-
-        // Since positionChildAt() is called during the creation process of pinned stacks,
-        // ActivityStack#getWindowContainerController() can be null. In this special case,
-        // since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
-        // we don't have to call WindowContainerController#positionChildAt() here.
-        if (stack.getWindowContainerController() != null) {
-            mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
-                    insertPosition, includingParents);
-        }
-        onStackOrderChanged();
-    }
-
-    private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
-        int position = mStacks.size();
-        if (stack.inPinnedWindowingMode()) {
-            // Stack in pinned windowing mode is z-ordered on-top of all other stacks so okay to
-            // just return the candidate position.
-            return Math.min(position, candidatePosition);
-        }
-        while (position > 0) {
-            final ActivityStack targetStack = mStacks.get(position - 1);
-            if (!targetStack.isAlwaysOnTop()) {
-                // We reached a stack that isn't always-on-top.
-                break;
-            }
-            if (stack.isAlwaysOnTop() && !targetStack.inPinnedWindowingMode()) {
-                // Always on-top non-pinned windowing mode stacks can go anywhere below pinned stack.
-                break;
-            }
-            position--;
-        }
-        return Math.min(position, candidatePosition);
-    }
-
-    <T extends ActivityStack> T getStack(int stackId) {
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final ActivityStack stack = mStacks.get(i);
-            if (stack.mStackId == stackId) {
-                return (T) stack;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @return the topmost stack on the display that is compatible with the input windowing mode and
-     * activity type. {@code null} means no compatible stack on the display.
-     * @see ConfigurationContainer#isCompatible(int, int)
-     */
-    <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
-        if (activityType == ACTIVITY_TYPE_HOME) {
-            return (T) mHomeStack;
-        } else if (activityType == ACTIVITY_TYPE_RECENTS) {
-            return (T) mRecentsStack;
-        }
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            return (T) mPinnedStack;
-        } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            return (T) mSplitScreenPrimaryStack;
-        }
-
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final ActivityStack stack = mStacks.get(i);
-            if (stack.isCompatible(windowingMode, activityType)) {
-                return (T) stack;
-            }
-        }
-        return null;
-    }
-
-    private boolean alwaysCreateStack(int windowingMode, int activityType) {
-        // Always create a stack for fullscreen, freeform, and split-screen-secondary windowing
-        // modes so that we can manage visual ordering and return types correctly.
-        return activityType == ACTIVITY_TYPE_STANDARD
-                && (windowingMode == WINDOWING_MODE_FULLSCREEN
-                || windowingMode == WINDOWING_MODE_FREEFORM
-                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-    }
-
-    /**
-     * Returns an existing stack compatible with the windowing mode and activity type or creates one
-     * if a compatible stack doesn't exist.
-     * @see #getStack(int, int)
-     * @see #createStack(int, int, boolean)
-     */
-    <T extends ActivityStack> T getOrCreateStack(int windowingMode, int activityType,
-            boolean onTop) {
-        if (!alwaysCreateStack(windowingMode, activityType)) {
-            T stack = getStack(windowingMode, activityType);
-            if (stack != null) {
-                return stack;
-            }
-        }
-        return createStack(windowingMode, activityType, onTop);
-    }
-
-    /**
-     * Returns an existing stack compatible with the input params or creates one
-     * if a compatible stack doesn't exist.
-     * @see #getOrCreateStack(int, int, boolean)
-     */
-    <T extends ActivityStack> T getOrCreateStack(@Nullable ActivityRecord r,
-            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, int activityType,
-            boolean onTop) {
-        // First preference is the windowing mode in the activity options if set.
-        int windowingMode = (options != null)
-                ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
-        // Validate that our desired windowingMode will work under the current conditions.
-        // UNDEFINED windowing mode is a valid result and means that the new stack will inherit
-        // it's display's windowing mode.
-        windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
-        return getOrCreateStack(windowingMode, activityType, onTop);
-    }
-
-    private int getNextStackId() {
-        return sNextFreeStackId++;
-    }
-
-    /**
-     * Creates a stack matching the input windowing mode and activity type on this display.
-     * @param windowingMode The windowing mode the stack should be created in. If
-     *                      {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will
-     *                      inherit it's parent's windowing mode.
-     * @param activityType The activityType the stack should be created in. If
-     *                     {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
-     *                     be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
-     * @param onTop If true the stack will be created at the top of the display, else at the bottom.
-     * @return The newly created stack.
-     */
-    <T extends ActivityStack> T createStack(int windowingMode, int activityType, boolean onTop) {
-
-        if (activityType == ACTIVITY_TYPE_UNDEFINED) {
-            // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
-            // anything else should be passing it in anyways...
-            activityType = ACTIVITY_TYPE_STANDARD;
-        }
-
-        if (activityType != ACTIVITY_TYPE_STANDARD) {
-            // For now there can be only one stack of a particular non-standard activity type on a
-            // display. So, get that ignoring whatever windowing mode it is currently in.
-            T stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
-            if (stack != null) {
-                throw new IllegalArgumentException("Stack=" + stack + " of activityType="
-                        + activityType + " already on display=" + this + ". Can't have multiple.");
-            }
-        }
-
-        final ActivityTaskManagerService service = mSupervisor.mService;
-        if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
-                service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
-                service.mSupportsPictureInPicture, activityType)) {
-            throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
-                    + windowingMode);
-        }
-
-        final int stackId = getNextStackId();
-        return createStackUnchecked(windowingMode, activityType, stackId, onTop);
-    }
-
-    @VisibleForTesting
-    <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
-            int stackId, boolean onTop) {
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
-        }
-        return (T) new ActivityStack(
-                        this, stackId, mSupervisor, windowingMode, activityType, onTop);
-    }
-
-    /**
-     * Get the preferred focusable stack in priority. If the preferred stack does not exist, find a
-     * focusable and visible stack from the top of stacks in this display.
-     */
-    ActivityStack getFocusedStack() {
-        if (mPreferredTopFocusableStack != null) {
-            return mPreferredTopFocusableStack;
-        }
-
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final ActivityStack stack = mStacks.get(i);
-            if (stack.isFocusableAndVisible()) {
-                return stack;
-            }
-        }
-
-        return null;
-    }
-
-    ActivityStack getNextFocusableStack() {
-        return getNextFocusableStack(null /* currentFocus */, false /* ignoreCurrent */);
-    }
-
-    ActivityStack getNextFocusableStack(ActivityStack currentFocus, boolean ignoreCurrent) {
-        final int currentWindowingMode = currentFocus != null
-                ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
-
-        ActivityStack candidate = null;
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final ActivityStack stack = mStacks.get(i);
-            if (ignoreCurrent && stack == currentFocus) {
-                continue;
-            }
-            if (!stack.isFocusableAndVisible()) {
-                continue;
-            }
-
-            if (currentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                    && candidate == null && stack.inSplitScreenPrimaryWindowingMode()) {
-                // If the currently focused stack is in split-screen secondary we save off the
-                // top primary split-screen stack as a candidate for focus because we might
-                // prefer focus to move to an other stack to avoid primary split-screen stack
-                // overlapping with a fullscreen stack when a fullscreen stack is higher in z
-                // than the next split-screen stack. Assistant stack, I am looking at you...
-                // We only move the focus to the primary-split screen stack if there isn't a
-                // better alternative.
-                candidate = stack;
-                continue;
-            }
-            if (candidate != null && stack.inSplitScreenSecondaryWindowingMode()) {
-                // Use the candidate stack since we are now at the secondary split-screen.
-                return candidate;
-            }
-            return stack;
-        }
-        return candidate;
-    }
-
-    ActivityRecord getResumedActivity() {
-        final ActivityStack focusedStack = getFocusedStack();
-        if (focusedStack == null) {
-            return null;
-        }
-        // TODO(b/111541062): Move this into ActivityStack#getResumedActivity()
-        // Check if the focused stack has the resumed activity
-        ActivityRecord resumedActivity = focusedStack.getResumedActivity();
-        if (resumedActivity == null || resumedActivity.app == null) {
-            // If there is no registered resumed activity in the stack or it is not running -
-            // try to use previously resumed one.
-            resumedActivity = focusedStack.mPausingActivity;
-            if (resumedActivity == null || resumedActivity.app == null) {
-                // If previously resumed activity doesn't work either - find the topmost running
-                // activity that can be focused.
-                resumedActivity = focusedStack.topRunningActivityLocked(true /* focusableOnly */);
-            }
-        }
-        return resumedActivity;
-    }
-
-    ActivityStack getLastFocusedStack() {
-        return mLastFocusedStack;
-    }
-
-    boolean allResumedActivitiesComplete() {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityRecord r = mStacks.get(stackNdx).getResumedActivity();
-            if (r != null && !r.isState(RESUMED)) {
-                return false;
-            }
-        }
-        final ActivityStack currentFocusedStack = getFocusedStack();
-        if (DEBUG_STACK) {
-            Slog.d(TAG_STACK, "allResumedActivitiesComplete: mLastFocusedStack changing from="
-                    + mLastFocusedStack + " to=" + currentFocusedStack);
-        }
-        mLastFocusedStack = currentFocusedStack;
-        return true;
-    }
-
-    /**
-     * Pause all activities in either all of the stacks or just the back stacks.
-     * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
-     * @param resuming The resuming activity.
-     * @param dontWait The resuming activity isn't going to wait for all activities to be paused
-     *                 before resuming.
-     * @return true if any activity was paused as a result of this call.
-     */
-    boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
-        boolean someActivityPaused = false;
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            // TODO(b/111541062): Check if resumed activity on this display instead
-            if (!mSupervisor.isTopDisplayFocusedStack(stack)
-                    && stack.getResumedActivity() != null) {
-                if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
-                        " mResumedActivity=" + stack.getResumedActivity());
-                someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
-                        dontWait);
-            }
-        }
-        return someActivityPaused;
-    }
-
-    /**
-     * Find task for putting the Activity in.
-     */
-    void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplay,
-            FindTaskResult result) {
-        mTmpFindTaskResult.clear();
-        for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = getChildAt(stackNdx);
-            if (!r.hasCompatibleActivityType(stack)) {
-                if (DEBUG_TASKS) {
-                    Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
-                }
-                continue;
-            }
-
-            stack.findTaskLocked(r, mTmpFindTaskResult);
-            // It is possible to have tasks in multiple stacks with the same root affinity, so
-            // we should keep looking after finding an affinity match to see if there is a
-            // better match in another stack. Also, task affinity isn't a good enough reason
-            // to target a display which isn't the source of the intent, so skip any affinity
-            // matches not on the specified display.
-            if (mTmpFindTaskResult.mRecord != null) {
-                if (mTmpFindTaskResult.mIdealMatch) {
-                    result.setTo(mTmpFindTaskResult);
-                    return;
-                } else if (isPreferredDisplay) {
-                    // Note: since the traversing through the stacks is top down, the floating
-                    // tasks should always have lower priority than any affinity-matching tasks
-                    // in the fullscreen stacks
-                    result.setTo(mTmpFindTaskResult);
-                }
-            }
-        }
-    }
-
-    /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
-     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
-     */
-    void removeStacksInWindowingModes(int... windowingModes) {
-        if (windowingModes == null || windowingModes.length == 0) {
-            return;
-        }
-
-        for (int j = windowingModes.length - 1 ; j >= 0; --j) {
-            final int windowingMode = windowingModes[j];
-            for (int i = mStacks.size() - 1; i >= 0; --i) {
-                final ActivityStack stack = mStacks.get(i);
-                if (!stack.isActivityTypeStandardOrUndefined()) {
-                    continue;
-                }
-                if (stack.getWindowingMode() != windowingMode) {
-                    continue;
-                }
-                mSupervisor.removeStack(stack);
-            }
-        }
-    }
-
-    void removeStacksWithActivityTypes(int... activityTypes) {
-        if (activityTypes == null || activityTypes.length == 0) {
-            return;
-        }
-
-        for (int j = activityTypes.length - 1 ; j >= 0; --j) {
-            final int activityType = activityTypes[j];
-            for (int i = mStacks.size() - 1; i >= 0; --i) {
-                final ActivityStack stack = mStacks.get(i);
-                if (stack.getActivityType() == activityType) {
-                    mSupervisor.removeStack(stack);
-                }
-            }
-        }
-    }
-
-    void onStackWindowingModeChanged(ActivityStack stack) {
-        removeStackReferenceIfNeeded(stack);
-        addStackReferenceIfNeeded(stack);
-    }
-
-    private void addStackReferenceIfNeeded(ActivityStack stack) {
-        final int activityType = stack.getActivityType();
-        final int windowingMode = stack.getWindowingMode();
-
-        if (activityType == ACTIVITY_TYPE_HOME) {
-            if (mHomeStack != null && mHomeStack != stack) {
-                throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
-                        + mHomeStack + " already exist on display=" + this + " stack=" + stack);
-            }
-            mHomeStack = stack;
-        } else if (activityType == ACTIVITY_TYPE_RECENTS) {
-            if (mRecentsStack != null && mRecentsStack != stack) {
-                throw new IllegalArgumentException("addStackReferenceIfNeeded: recents stack="
-                        + mRecentsStack + " already exist on display=" + this + " stack=" + stack);
-            }
-            mRecentsStack = stack;
-        }
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            if (mPinnedStack != null && mPinnedStack != stack) {
-                throw new IllegalArgumentException("addStackReferenceIfNeeded: pinned stack="
-                        + mPinnedStack + " already exist on display=" + this
-                        + " stack=" + stack);
-            }
-            mPinnedStack = stack;
-        } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            if (mSplitScreenPrimaryStack != null && mSplitScreenPrimaryStack != stack) {
-                throw new IllegalArgumentException("addStackReferenceIfNeeded:"
-                        + " split-screen-primary" + " stack=" + mSplitScreenPrimaryStack
-                        + " already exist on display=" + this + " stack=" + stack);
-            }
-            mSplitScreenPrimaryStack = stack;
-            onSplitScreenModeActivated();
-        }
-    }
-
-    private void removeStackReferenceIfNeeded(ActivityStack stack) {
-        if (stack == mHomeStack) {
-            mHomeStack = null;
-        } else if (stack == mRecentsStack) {
-            mRecentsStack = null;
-        } else if (stack == mPinnedStack) {
-            mPinnedStack = null;
-        } else if (stack == mSplitScreenPrimaryStack) {
-            mSplitScreenPrimaryStack = null;
-            // Inform the reset of the system that split-screen mode was dismissed so things like
-            // resizing all the other stacks can take place.
-            onSplitScreenModeDismissed();
-        }
-    }
-
-    private void onSplitScreenModeDismissed() {
-        mSupervisor.mWindowManager.deferSurfaceLayout();
-        try {
-            // Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
-            for (int i = mStacks.size() - 1; i >= 0; --i) {
-                final ActivityStack otherStack = mStacks.get(i);
-                if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
-                    continue;
-                }
-                otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED, false /* animate */,
-                        false /* showRecents */, false /* enteringSplitScreenMode */,
-                        true /* deferEnsuringVisibility */);
-            }
-        } finally {
-            final ActivityStack topFullscreenStack =
-                    getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
-            if (topFullscreenStack != null && mHomeStack != null && !isTopStack(mHomeStack)) {
-                // Whenever split-screen is dismissed we want the home stack directly behind the
-                // current top fullscreen stack so it shows up when the top stack is finished.
-                // TODO: Would be better to use ActivityDisplay.positionChildAt() for this, however
-                // ActivityDisplay doesn't have a direct controller to WM side yet. We can switch
-                // once we have that.
-                mHomeStack.moveToFront("onSplitScreenModeDismissed");
-                topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
-            }
-            mSupervisor.mWindowManager.continueSurfaceLayout();
-        }
-    }
-
-    private void onSplitScreenModeActivated() {
-        mSupervisor.mWindowManager.deferSurfaceLayout();
-        try {
-            // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
-            for (int i = mStacks.size() - 1; i >= 0; --i) {
-                final ActivityStack otherStack = mStacks.get(i);
-                if (otherStack == mSplitScreenPrimaryStack
-                        || !otherStack.affectedBySplitScreenResize()) {
-                    continue;
-                }
-                otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
-                        false /* animate */, false /* showRecents */,
-                        true /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */);
-            }
-        } finally {
-            mSupervisor.mWindowManager.continueSurfaceLayout();
-        }
-    }
-
-    /**
-     * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
-     * @param windowingMode The windowing mode we are checking support for.
-     * @param supportsMultiWindow If we should consider support for multi-window mode in general.
-     * @param supportsSplitScreen If we should consider support for split-screen multi-window.
-     * @param supportsFreeform If we should consider support for freeform multi-window.
-     * @param supportsPip If we should consider support for picture-in-picture mutli-window.
-     * @param activityType The activity type under consideration.
-     * @return true if the windowing mode is supported.
-     */
-    private boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
-            boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
-            int activityType) {
-
-        if (windowingMode == WINDOWING_MODE_UNDEFINED
-                || windowingMode == WINDOWING_MODE_FULLSCREEN) {
-            return true;
-        }
-        if (!supportsMultiWindow) {
-            return false;
-        }
-
-        final int displayWindowingMode = getWindowingMode();
-        if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
-            return supportsSplitScreen
-                    && WindowConfiguration.supportSplitScreenWindowingMode(activityType)
-                    // Freeform windows and split-screen windows don't mix well, so prevent
-                    // split windowing modes on freeform displays.
-                    && displayWindowingMode != WINDOWING_MODE_FREEFORM;
-        }
-
-        if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
-            return false;
-        }
-
-        if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Resolves the windowing mode that an {@link ActivityRecord} would be in if started on this
-     * display with the provided parameters.
-     *
-     * @param r The ActivityRecord in question.
-     * @param options Options to start with.
-     * @param task The task within-which the activity would start.
-     * @param activityType The type of activity to start.
-     * @return The resolved (not UNDEFINED) windowing-mode that the activity would be in.
-     */
-    int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
-            @Nullable TaskRecord task, int activityType) {
-
-        // First preference if the windowing mode in the activity options if set.
-        int windowingMode = (options != null)
-                ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
-
-        // If windowing mode is unset, then next preference is the candidate task, then the
-        // activity record.
-        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
-            if (task != null) {
-                windowingMode = task.getWindowingMode();
-            }
-            if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) {
-                windowingMode = r.getWindowingMode();
-            }
-            if (windowingMode == WINDOWING_MODE_UNDEFINED) {
-                // Use the display's windowing mode.
-                windowingMode = getWindowingMode();
-            }
-        }
-        windowingMode = validateWindowingMode(windowingMode, r, task, activityType);
-        return windowingMode != WINDOWING_MODE_UNDEFINED
-                ? windowingMode : WINDOWING_MODE_FULLSCREEN;
-    }
-
-    /**
-     * Check that the requested windowing-mode is appropriate for the specified task and/or activity
-     * on this display.
-     *
-     * @param windowingMode The windowing-mode to validate.
-     * @param r The {@link ActivityRecord} to check against.
-     * @param task The {@link TaskRecord} to check against.
-     * @param activityType An activity type.
-     * @return The provided windowingMode or the closest valid mode which is appropriate.
-     */
-    int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r,
-        @Nullable TaskRecord task, int activityType) {
-        // Make sure the windowing mode we are trying to use makes sense for what is supported.
-        final ActivityTaskManagerService service = mSupervisor.mService;
-        boolean supportsMultiWindow = service.mSupportsMultiWindow;
-        boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow;
-        boolean supportsFreeform = service.mSupportsFreeformWindowManagement;
-        boolean supportsPip = service.mSupportsPictureInPicture;
-        if (supportsMultiWindow) {
-            if (task != null) {
-                supportsMultiWindow = task.isResizeable();
-                supportsSplitScreen = task.supportsSplitScreenWindowingMode();
-                // TODO: Do we need to check for freeform and Pip support here?
-            } else if (r != null) {
-                supportsMultiWindow = r.isResizeable();
-                supportsSplitScreen = r.supportsSplitScreenWindowingMode();
-                supportsFreeform = r.supportsFreeform();
-                supportsPip = r.supportsPictureInPicture();
-            }
-        }
-
-        final boolean inSplitScreenMode = hasSplitScreenPrimaryStack();
-        if (!inSplitScreenMode
-                && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
-            // Switch to the display's windowing mode if we are not in split-screen mode and we are
-            // trying to launch in split-screen secondary.
-            windowingMode = WINDOWING_MODE_UNDEFINED;
-        } else if (inSplitScreenMode && (windowingMode == WINDOWING_MODE_FULLSCREEN
-                        || windowingMode == WINDOWING_MODE_UNDEFINED)
-                && supportsSplitScreen) {
-            windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-        }
-
-        if (windowingMode != WINDOWING_MODE_UNDEFINED
-                && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
-                        supportsFreeform, supportsPip, activityType)) {
-            return windowingMode;
-        }
-        return WINDOWING_MODE_UNDEFINED;
-    }
-
-    /**
-     * Get the topmost stack on the display. It may be different from focused stack, because
-     * some stacks are not focusable (e.g. PiP).
-     */
-    ActivityStack getTopStack() {
-        return mStacks.isEmpty() ? null : mStacks.get(mStacks.size() - 1);
-    }
-
-    boolean isTopStack(ActivityStack stack) {
-        return stack == getTopStack();
-    }
-
-    boolean isTopNotPinnedStack(ActivityStack stack) {
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final ActivityStack current = mStacks.get(i);
-            if (!current.inPinnedWindowingMode()) {
-                return current == stack;
-            }
-        }
-        return false;
-    }
-
-    ActivityStack getTopStackInWindowingMode(int windowingMode) {
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final ActivityStack current = mStacks.get(i);
-            if (windowingMode == current.getWindowingMode()) {
-                return current;
-            }
-        }
-        return null;
-    }
-
-    ActivityRecord topRunningActivity() {
-        return topRunningActivity(false /* considerKeyguardState */);
-    }
-
-    /**
-     * Returns the top running activity in the focused stack. In the case the focused stack has no
-     * such activity, the next focusable stack on this display is returned.
-     *
-     * @param considerKeyguardState Indicates whether the locked state should be considered. if
-     *                              {@code true} and the keyguard is locked, only activities that
-     *                              can be shown on top of the keyguard will be considered.
-     * @return The top running activity. {@code null} if none is available.
-     */
-    ActivityRecord topRunningActivity(boolean considerKeyguardState) {
-        ActivityRecord topRunning = null;
-        final ActivityStack focusedStack = getFocusedStack();
-        if (focusedStack != null) {
-            topRunning = focusedStack.topRunningActivityLocked();
-        }
-
-        // Look in other focusable stacks.
-        if (topRunning == null) {
-            for (int i = mStacks.size() - 1; i >= 0; --i) {
-                final ActivityStack stack = mStacks.get(i);
-                // Only consider focusable stacks other than the current focused one.
-                if (stack == focusedStack || !stack.isFocusable()) {
-                    continue;
-                }
-                topRunning = stack.topRunningActivityLocked();
-                if (topRunning != null) {
-                    break;
-                }
-            }
-        }
-
-        // This activity can be considered the top running activity if we are not considering
-        // the locked state, the keyguard isn't locked, or we can show when locked.
-        if (topRunning != null && considerKeyguardState
-                && mSupervisor.getKeyguardController().isKeyguardLocked()
-                && !topRunning.canShowWhenLocked()) {
-            return null;
-        }
-
-        return topRunning;
-    }
-
-    int getIndexOf(ActivityStack stack) {
-        return mStacks.indexOf(stack);
-    }
-
-    void onLockTaskPackagesUpdated() {
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            mStacks.get(i).onLockTaskPackagesUpdated();
-        }
-    }
-
-    /** We are in the process of exiting split-screen mode. */
-    void onExitingSplitScreenMode() {
-        // Remove reference to the primary-split-screen stack so it no longer has any effect on the
-        // display. For example, we want to be able to create fullscreen stack for standard activity
-        // types when exiting split-screen mode.
-        mSplitScreenPrimaryStack = null;
-    }
-
-    ActivityStack getSplitScreenPrimaryStack() {
-        return mSplitScreenPrimaryStack;
-    }
-
-    boolean hasSplitScreenPrimaryStack() {
-        return mSplitScreenPrimaryStack != null;
-    }
-
-    PinnedActivityStack getPinnedStack() {
-        return (PinnedActivityStack) mPinnedStack;
-    }
-
-    boolean hasPinnedStack() {
-        return mPinnedStack != null;
-    }
-
-    @Override
-    public String toString() {
-        return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
-    }
-
-    @Override
-    protected int getChildCount() {
-        return mStacks.size();
-    }
-
-    @Override
-    protected ActivityStack getChildAt(int index) {
-        return mStacks.get(index);
-    }
-
-    @Override
-    protected ConfigurationContainer getParent() {
-        return mSupervisor;
-    }
-
-    boolean isPrivate() {
-        return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
-    }
-
-    boolean isUidPresent(int uid) {
-        for (ActivityStack stack : mStacks) {
-            if (stack.isUidPresent(uid)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * @see #mRemoved
-     */
-    boolean isRemoved() {
-        return mRemoved;
-    }
-
-    void remove() {
-        final boolean destroyContentOnRemoval = shouldDestroyContentOnRemove();
-
-        // Stacks could be reparented from the removed display to other display. While
-        // reparenting the last stack of the removed display, the remove display is ready to be
-        // released (no more ActivityStack). But, we cannot release it at that moment or the
-        // related WindowContainer and WindowContainerController will also be removed. So, we
-        // set display as removed after reparenting stack finished.
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final ActivityStack stack = mStacks.get(i);
-            // Always finish non-standard type stacks.
-            if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) {
-                stack.finishAllActivitiesLocked(true /* immediately */);
-            } else {
-                // If default display is in split-window mode, set windowing mode of the stack to
-                // split-screen secondary. Otherwise, set the windowing mode to undefined by
-                // default to let stack inherited the windowing mode from the new display.
-                int windowingMode = mSupervisor.getDefaultDisplay().hasSplitScreenPrimaryStack()
-                        ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_UNDEFINED;
-                mSupervisor.moveStackToDisplayLocked(stack.mStackId, DEFAULT_DISPLAY, true);
-                stack.setWindowingMode(windowingMode);
-            }
-        }
-        mRemoved = true;
-
-        releaseSelfIfNeeded();
-
-        mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId);
-    }
-
-    private void releaseSelfIfNeeded() {
-        if (mStacks.isEmpty() && mRemoved) {
-            mWindowContainerController.removeContainer();
-            mWindowContainerController = null;
-            mSupervisor.removeChild(this);
-        }
-    }
-
-    /** Update and get all UIDs that are present on the display and have access to it. */
-    IntArray getPresentUIDs() {
-        mDisplayAccessUIDs.clear();
-        for (ActivityStack stack : mStacks) {
-            stack.getPresentUIDs(mDisplayAccessUIDs);
-        }
-        return mDisplayAccessUIDs;
-    }
-
-    /**
-     * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
-     */
-    boolean supportsSystemDecorations() {
-        return mDisplay.supportsSystemDecorations()
-                // TODO (b/111363427): Remove this and set the new FLAG_SHOULD_SHOW_LAUNCHER flag
-                // (b/114338689) whenever vr 2d display id is set.
-                || mDisplayId == mSupervisor.mService.mVr2dDisplayId;
-    }
-
-    private boolean shouldDestroyContentOnRemove() {
-        return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
-    }
-
-    boolean shouldSleep() {
-        return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
-                && (mSupervisor.mService.mRunningVoice == null);
-    }
-
-    void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
-        mWindowContainerController.setFocusedApp(r.appToken, moveFocusNow);
-    }
-
-    /**
-     * @return the stack currently above the {@param stack}.  Can be null if the {@param stack} is
-     *         already top-most.
-     */
-    ActivityStack getStackAbove(ActivityStack stack) {
-        final int stackIndex = mStacks.indexOf(stack) + 1;
-        return (stackIndex < mStacks.size()) ? mStacks.get(stackIndex) : null;
-    }
-
-    /**
-     * Adjusts the {@param stack} behind the last visible stack in the display if necessary.
-     * Generally used in conjunction with {@link #moveStackBehindStack}.
-     */
-    void moveStackBehindBottomMostVisibleStack(ActivityStack stack) {
-        if (stack.shouldBeVisible(null)) {
-            // Skip if the stack is already visible
-            return;
-        }
-
-        // Move the stack to the bottom to not affect the following visibility checks
-        positionChildAtBottom(stack);
-
-        // Find the next position where the stack should be placed
-        final int numStacks = mStacks.size();
-        for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
-            final ActivityStack s = mStacks.get(stackNdx);
-            if (s == stack) {
-                continue;
-            }
-            final int winMode = s.getWindowingMode();
-            final boolean isValidWindowingMode = winMode == WINDOWING_MODE_FULLSCREEN ||
-                    winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-            if (s.shouldBeVisible(null) && isValidWindowingMode) {
-                // Move the provided stack to behind this stack
-                positionChildAt(stack, Math.max(0, stackNdx - 1));
-                break;
-            }
-        }
-    }
-
-    /**
-     * Moves the {@param stack} behind the given {@param behindStack} if possible. If
-     * {@param behindStack} is not currently in the display, then then the stack is moved to the
-     * back. Generally used in conjunction with {@link #moveStackBehindBottomMostVisibleStack}.
-     */
-    void moveStackBehindStack(ActivityStack stack, ActivityStack behindStack) {
-        if (behindStack == null || behindStack == stack) {
-            return;
-        }
-
-        // Note that positionChildAt will first remove the given stack before inserting into the
-        // list, so we need to adjust the insertion index to account for the removed index
-        // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
-        //       position internally
-        final int stackIndex = mStacks.indexOf(stack);
-        final int behindStackIndex = mStacks.indexOf(behindStack);
-        final int insertIndex = stackIndex <= behindStackIndex
-                ? behindStackIndex - 1 : behindStackIndex;
-        positionChildAt(stack, Math.max(0, insertIndex));
-    }
-
-    void moveHomeStackToFront(String reason) {
-        if (mHomeStack != null) {
-            mHomeStack.moveToFront(reason);
-        }
-    }
-
-    /** Returns true if the focus activity was adjusted to the home stack top activity. */
-    boolean moveHomeActivityToTop(String reason) {
-        final ActivityRecord top = getHomeActivity();
-        if (top == null) {
-            return false;
-        }
-        top.moveFocusableActivityToTop(reason);
-        return true;
-    }
-
-    @Nullable
-    ActivityStack getHomeStack() {
-        return mHomeStack;
-    }
-
-    @Nullable
-    ActivityRecord getHomeActivity() {
-        return getHomeActivityForUser(mSupervisor.mCurrentUser);
-    }
-
-    @Nullable
-    ActivityRecord getHomeActivityForUser(int userId) {
-        if (mHomeStack == null) {
-            return null;
-        }
-
-        final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
-        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = tasks.get(taskNdx);
-            if (!task.isActivityTypeHome()) {
-                continue;
-            }
-
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (r.isActivityTypeHome()
-                        && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
-                    return r;
-                }
-            }
-        }
-        return null;
-    }
-
-    boolean isSleeping() {
-        return mSleeping;
-    }
-
-    void setIsSleeping(boolean asleep) {
-        mSleeping = asleep;
-    }
-
-    /**
-     * Adds a listener to be notified whenever the stack order in the display changes. Currently
-     * only used by the {@link RecentsAnimation} to determine whether to interrupt and cancel the
-     * current animation when the system state changes.
-     */
-    void registerStackOrderChangedListener(OnStackOrderChangedListener listener) {
-        if (!mStackOrderChangedCallbacks.contains(listener)) {
-            mStackOrderChangedCallbacks.add(listener);
-        }
-    }
-
-    /**
-     * Removes a previously registered stack order change listener.
-     */
-    void unregisterStackOrderChangedListener(OnStackOrderChangedListener listener) {
-        mStackOrderChangedCallbacks.remove(listener);
-    }
-
-    private void onStackOrderChanged() {
-        for (int i = mStackOrderChangedCallbacks.size() - 1; i >= 0; i--) {
-            mStackOrderChangedCallbacks.get(i).onStackOrderChanged();
-        }
-    }
-
-    /**
-     * See {@link DisplayWindowController#deferUpdateImeTarget()}
-     */
-    public void deferUpdateImeTarget() {
-        mWindowContainerController.deferUpdateImeTarget();
-    }
-
-    /**
-     * See {@link DisplayWindowController#deferUpdateImeTarget()}
-     */
-    public void continueUpdateImeTarget() {
-        mWindowContainerController.continueUpdateImeTarget();
-    }
-
-    public void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size());
-        final String myPrefix = prefix + " ";
-        if (mHomeStack != null) {
-            pw.println(myPrefix + "mHomeStack=" + mHomeStack);
-        }
-        if (mRecentsStack != null) {
-            pw.println(myPrefix + "mRecentsStack=" + mRecentsStack);
-        }
-        if (mPinnedStack != null) {
-            pw.println(myPrefix + "mPinnedStack=" + mPinnedStack);
-        }
-        if (mSplitScreenPrimaryStack != null) {
-            pw.println(myPrefix + "mSplitScreenPrimaryStack=" + mSplitScreenPrimaryStack);
-        }
-        if (mPreferredTopFocusableStack != null) {
-            pw.println(myPrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
-        }
-        if (mLastFocusedStack != null) {
-            pw.println(myPrefix + "mLastFocusedStack=" + mLastFocusedStack);
-        }
-    }
-
-    public void dumpStacks(PrintWriter pw) {
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            pw.print(mStacks.get(i).mStackId);
-            if (i > 0) {
-                pw.print(",");
-            }
-        }
-    }
-
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
-        proto.write(ID, mDisplayId);
-        final ActivityStack focusedStack = getFocusedStack();
-        if (focusedStack != null) {
-            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
-            final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
-            if (focusedActivity != null) {
-                focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
-            }
-        } else {
-            proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
-        }
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            stack.writeToProto(proto, STACKS);
-        }
-        proto.end(token);
-    }
-
-    /**
-     * Callback for when the order of the stacks in the display changes.
-     */
-    interface OnStackOrderChangedListener {
-        void onStackOrderChanged();
-    }
-}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1b74ed0..c52df2d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -127,29 +127,28 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityTaskManagerService.DUMP_ACTIVITIES_CMD;
-import static com.android.server.am.ActivityTaskManagerService.DUMP_ACTIVITIES_SHORT_CMD;
-import static com.android.server.am.ActivityTaskManagerService.DUMP_CONTAINERS_CMD;
-import static com.android.server.am.ActivityTaskManagerService.DUMP_LASTANR_CMD;
-import static com.android.server.am.ActivityTaskManagerService.DUMP_LASTANR_TRACES_CMD;
-import static com.android.server.am.ActivityTaskManagerService.DUMP_RECENTS_CMD;
-import static com.android.server.am.ActivityTaskManagerService.DUMP_RECENTS_SHORT_CMD;
-import static com.android.server.am.ActivityTaskManagerService.DUMP_STARTER_CMD;
-import static com.android.server.am.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.am.ActivityTaskManagerService.relaunchReasonToString;
 import static com.android.server.am.MemoryStatUtil.MEMORY_STAT_INTERESTING_NATIVE_PROCESSES;
 import static com.android.server.am.MemoryStatUtil.hasMemcg;
 import static com.android.server.am.MemoryStatUtil.readCmdlineFromProcfs;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerService.DUMP_ACTIVITIES_CMD;
+import static com.android.server.wm.ActivityTaskManagerService.DUMP_ACTIVITIES_SHORT_CMD;
+import static com.android.server.wm.ActivityTaskManagerService.DUMP_CONTAINERS_CMD;
+import static com.android.server.wm.ActivityTaskManagerService.DUMP_LASTANR_CMD;
+import static com.android.server.wm.ActivityTaskManagerService.DUMP_LASTANR_TRACES_CMD;
+import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_CMD;
+import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_SHORT_CMD;
+import static com.android.server.wm.ActivityTaskManagerService.DUMP_STARTER_CMD;
+import static com.android.server.wm.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.ActivityTaskManagerService.relaunchReasonToString;
 
 import android.Manifest;
 import android.Manifest.permission;
@@ -353,8 +352,11 @@
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.utils.PriorityDump;
 import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.ActivityServiceConnectionsHolder;
 import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerService;
 import com.android.server.wm.WindowManagerService;
+import com.android.server.wm.WindowProcessController;
 
 import dalvik.system.VMRuntime;
 
@@ -538,14 +540,23 @@
 
     BroadcastQueue mFgBroadcastQueue;
     BroadcastQueue mBgBroadcastQueue;
+    BroadcastQueue mOffloadBroadcastQueue;
     // Convenient for easy iteration over the queues. Foreground is first
     // so that dispatch of foreground broadcasts gets precedence.
-    final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[2];
+    final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[3];
 
     BroadcastStats mLastBroadcastStats;
     BroadcastStats mCurBroadcastStats;
 
     BroadcastQueue broadcastQueueForIntent(Intent intent) {
+        if (isOnOffloadQueue(intent.getFlags())) {
+            if (DEBUG_BROADCAST_BACKGROUND) {
+                Slog.i(TAG_BROADCAST,
+                        "Broadcast intent " + intent + " on offload queue");
+            }
+            return mOffloadBroadcastQueue;
+        }
+
         final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
         if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST,
                 "Broadcast intent " + intent + " on "
@@ -559,7 +570,8 @@
     String mDeviceOwnerName;
 
     final UserController mUserController;
-    final PendingIntentController mPendingIntentController;
+    @VisibleForTesting
+    public final PendingIntentController mPendingIntentController;
 
     final AppErrors mAppErrors;
 
@@ -1122,9 +1134,46 @@
     boolean mOrigWaitForDebugger = false;
     boolean mAlwaysFinishActivities = false;
 
-    String mProfileApp = null;
-    ProcessRecord mProfileProc = null;
-    ProfilerInfo mProfilerInfo = null;
+    class ProfileData {
+        private String mProfileApp = null;
+        private ProcessRecord mProfileProc = null;
+        private ProfilerInfo mProfilerInfo = null;
+
+        void setProfileApp(String profileApp) {
+            mProfileApp = profileApp;
+            if (mAtmInternal != null) {
+                mAtmInternal.setProfileApp(profileApp);
+            }
+        }
+
+        String getProfileApp() {
+            return mProfileApp;
+        }
+
+        void setProfileProc(ProcessRecord profileProc) {
+            mProfileProc = profileProc;
+            if (mAtmInternal != null) {
+                mAtmInternal.setProfileProc(
+                        profileProc.getWindowProcessController());
+            }
+        }
+
+        ProcessRecord getProfileProc() {
+            return mProfileProc;
+        }
+
+        void setProfilerInfo(ProfilerInfo profilerInfo) {
+            mProfilerInfo = profilerInfo;
+            if (mAtmInternal != null) {
+                mAtmInternal.setProfilerInfo(profilerInfo);
+            }
+        }
+
+        ProfilerInfo getProfilerInfo() {
+            return mProfilerInfo;
+        }
+    }
+    final ProfileData mProfileData = new ProfileData();
 
     /**
      * Stores a map of process name -> agent string. When a process is started and mAgentAppMap
@@ -1268,10 +1317,14 @@
      */
     int mBootPhase;
 
-    WindowManagerService mWindowManager;
-    ActivityTaskManagerService mActivityTaskManager;
-    ActivityTaskManagerInternal mAtmInternal;
-    UriGrantsManagerInternal mUgmInternal;
+    @VisibleForTesting
+    public WindowManagerService mWindowManager;
+    @VisibleForTesting
+    public ActivityTaskManagerService mActivityTaskManager;
+    @VisibleForTesting
+    public ActivityTaskManagerInternal mAtmInternal;
+    @VisibleForTesting
+    public UriGrantsManagerInternal mUgmInternal;
     final ActivityThread mSystemThread;
 
     private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -1341,7 +1394,8 @@
      */
     private boolean mUserIsMonkey;
 
-    final ServiceThread mHandlerThread;
+    @VisibleForTesting
+    public final ServiceThread mHandlerThread;
     final MainHandler mHandler;
     final Handler mUiHandler;
     final ServiceThread mProcStartHandlerThread;
@@ -1361,14 +1415,6 @@
 
     private static String sTheRealBuildSerial = Build.UNKNOWN;
 
-    /**
-     * Current global configuration information. Contains general settings for the entire system,
-     * also corresponds to the merged configuration of the default display.
-     */
-    Configuration getGlobalConfiguration() {
-        return mActivityTaskManager.getGlobalConfiguration();
-    }
-
     final class UiHandler extends Handler {
         public UiHandler() {
             super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -2111,7 +2157,7 @@
      * given to initialize the dependency members.
      */
     @VisibleForTesting
-    ActivityManagerService(Injector injector, ServiceThread handlerThread) {
+    public ActivityManagerService(Injector injector, ServiceThread handlerThread) {
         final boolean hasHandlerThread = handlerThread != null;
         mInjector = injector;
         mContext = mInjector.getContext();
@@ -2174,8 +2220,11 @@
                 "foreground", BROADCAST_FG_TIMEOUT, false);
         mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
                 "background", BROADCAST_BG_TIMEOUT, true);
+        mOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
+                "offload", BROADCAST_BG_TIMEOUT, true);
         mBroadcastQueues[0] = mFgBroadcastQueue;
         mBroadcastQueues[1] = mBgBroadcastQueue;
+        mBroadcastQueues[2] = mOffloadBroadcastQueue;
 
         mServices = new ActiveServices(this);
         mProviderMap = new ProviderMap(this);
@@ -2213,8 +2262,7 @@
         mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
 
         mActivityTaskManager = atm;
-        mActivityTaskManager.setActivityManagerService(this, mHandlerThread.getLooper(),
-                mIntentFirewall, mPendingIntentController);
+        mActivityTaskManager.setActivityManagerService(mIntentFirewall, mPendingIntentController);
         mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
 
         mProcessCpuThread = new Thread("CpuTracker") {
@@ -3117,7 +3165,7 @@
             }
         }
 
-        if (mProfileProc == app) {
+        if (mProfileData.getProfileProc() == app) {
             clearProfilerLocked();
         }
 
@@ -3552,7 +3600,7 @@
 
                 if (appInfo != null) {
                     forceStopPackageLocked(packageName, appInfo.uid, "clear data");
-                    mActivityTaskManager.getRecentTasks().removeTasksByPackageName(packageName, resolvedUserId);
+                    mAtmInternal.removeRecentTasksByPackageName(packageName, resolvedUserId);
                 }
             }
 
@@ -3840,7 +3888,7 @@
 
         final int callingPid = Binder.getCallingPid();
         final int callingUid = Binder.getCallingUid();
-        final int userId = UserHandle.getUserId(callingUid);
+        final int callingUserId = UserHandle.getUserId(callingUid);
         final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
                 callingUid) == PackageManager.PERMISSION_GRANTED;
         // Check REAL_GET_TASKS to see if they are allowed to access other uids
@@ -3858,11 +3906,17 @@
                     oomAdj = proc != null ? proc.setAdj : 0;
                 }
             }
-            if (!allUids || (!allUsers && (proc == null
-                    || UserHandle.getUserId(proc.uid) != userId))) {
-                // The caller is not allow to get information about this other process...
-                // just leave it empty.
-                continue;
+            final int targetUid = (proc != null) ? proc.uid : -1;
+            final int targetUserId = (proc != null) ? UserHandle.getUserId(targetUid) : -1;
+
+            if (callingUid != targetUid) {
+                if (!allUids) {
+                    continue; // Not allowed to see other UIDs.
+                }
+
+                if (!allUsers && (targetUserId != callingUserId)) {
+                    continue; // Not allowed to see other users.
+                }
             }
             if (proc != null && proc.lastMemInfoTime >= lastNow && proc.lastMemInfo != null) {
                 // It hasn't been long enough that we want to take another sample; return
@@ -4398,16 +4452,18 @@
 
             ProfilerInfo profilerInfo = null;
             String preBindAgent = null;
-            if (mProfileApp != null && mProfileApp.equals(processName)) {
-                mProfileProc = app;
-                if (mProfilerInfo != null) {
+            if (mProfileData.getProfileApp() != null
+                    && mProfileData.getProfileApp().equals(processName)) {
+                mProfileData.setProfileProc(app);
+                if (mProfileData.getProfilerInfo() != null) {
                     // Send a profiler info object to the app if either a file is given, or
                     // an agent should be loaded at bind-time.
-                    boolean needsInfo = mProfilerInfo.profileFile != null
-                            || mProfilerInfo.attachAgentDuringBind;
-                    profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
-                    if (mProfilerInfo.agent != null) {
-                        preBindAgent = mProfilerInfo.agent;
+                    boolean needsInfo = mProfileData.getProfilerInfo().profileFile != null
+                            || mProfileData.getProfilerInfo().attachAgentDuringBind;
+                    profilerInfo = needsInfo
+                            ? new ProfilerInfo(mProfileData.getProfilerInfo()) : null;
+                    if (mProfileData.getProfilerInfo().agent != null) {
+                        preBindAgent = mProfileData.getProfilerInfo().agent;
                     }
                 }
             } else if (instr != null && instr.mProfileFile != null) {
@@ -4431,7 +4487,8 @@
 
             if (profilerInfo != null && profilerInfo.profileFd != null) {
                 profilerInfo.profileFd = profilerInfo.profileFd.dup();
-                if (TextUtils.equals(mProfileApp, processName) && mProfilerInfo != null) {
+                if (TextUtils.equals(mProfileData.getProfileApp(), processName)
+                        && mProfileData.getProfilerInfo() != null) {
                     clearProfilerLocked();
                 }
             }
@@ -5135,6 +5192,14 @@
 
     @Override
     public boolean isAppForeground(int uid) {
+        int callerUid = Binder.getCallingUid();
+        if (UserHandle.isCore(callerUid) || callerUid == uid) {
+            return isAppForegroundInternal(uid);
+        }
+        return false;
+    }
+
+    private boolean isAppForegroundInternal(int uid) {
         synchronized (this) {
             UidRecord uidRec = mActiveUids.get(uid);
             if (uidRec == null || uidRec.idle) {
@@ -5545,7 +5610,8 @@
         return pi;
     }
 
-    void grantEphemeralAccessLocked(int userId, Intent intent,
+    @VisibleForTesting
+    public void grantEphemeralAccessLocked(int userId, Intent intent,
             int targetAppId, int ephemeralAppId) {
         getPackageManagerInternalLocked().
                 grantEphemeralAccess(userId, intent, targetAppId, ephemeralAppId);
@@ -5815,16 +5881,7 @@
 
     @Override
     public void updateLockTaskPackages(int userId, String[] packages) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != 0 && callingUid != SYSTEM_UID) {
-            enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
-                    "updateLockTaskPackages()");
-        }
-        synchronized (this) {
-            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" +
-                    Arrays.toString(packages));
-            mActivityTaskManager.getLockTaskController().updateLockTaskPackages(userId, packages);
-        }
+        mActivityTaskManager.updateLockTaskPackages(userId, packages);
     }
 
     @Override
@@ -6507,6 +6564,7 @@
 
         // Wait for the provider to be published...
         final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
+        boolean timedOut = false;
         synchronized (cpr) {
             while (cpr.provider == null) {
                 if (cpr.launchingApp == null) {
@@ -6530,12 +6588,8 @@
                     }
                     cpr.wait(wait);
                     if (cpr.provider == null) {
-                        Slog.wtf(TAG, "Timeout waiting for provider "
-                                + cpi.applicationInfo.packageName + "/"
-                                + cpi.applicationInfo.uid + " for provider "
-                                + name
-                                + " providerRunning=" + providerRunning);
-                        return null;
+                        timedOut = true;
+                        break;
                     }
                 } catch (InterruptedException ex) {
                 } finally {
@@ -6545,7 +6599,43 @@
                 }
             }
         }
-        return cpr != null ? cpr.newHolder(conn) : null;
+        if (timedOut) {
+            // Note we do it afer releasing the lock.
+            String callerName = "unknown";
+            synchronized (this) {
+                final ProcessRecord record = mProcessList.getLRURecordForAppLocked(caller);
+                if (record != null) {
+                    callerName = record.processName;
+                }
+            }
+
+            Slog.wtf(TAG, "Timeout waiting for provider "
+                    + cpi.applicationInfo.packageName + "/"
+                    + cpi.applicationInfo.uid + " for provider "
+                    + name
+                    + " providerRunning=" + providerRunning
+                    + " caller=" + callerName + "/" + Binder.getCallingUid());
+            return null;
+        }
+
+        return cpr.newHolder(conn);
+    }
+
+    private static final class StartActivityRunnable implements Runnable {
+        private final Context mContext;
+        private final Intent mIntent;
+        private final UserHandle mUserHandle;
+
+        StartActivityRunnable(Context context, Intent intent, UserHandle userHandle) {
+            this.mContext = context;
+            this.mIntent = intent;
+            this.mUserHandle = userHandle;
+        }
+
+        @Override
+        public void run() {
+            mContext.startActivityAsUser(mIntent, mUserHandle);
+        }
     }
 
     private boolean requestTargetProviderPermissionsReviewIfNeededLocked(ProviderInfo cpi,
@@ -6574,12 +6664,7 @@
             }
 
             final UserHandle userHandle = new UserHandle(userId);
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mContext.startActivityAsUser(intent, userHandle);
-                }
-            });
+            mHandler.post(new StartActivityRunnable(mContext, intent, userHandle));
 
             return false;
         }
@@ -6592,11 +6677,13 @@
      * PackageManager could be unavailable at construction time and therefore needs to be accessed
      * on demand.
      */
-    IPackageManager getPackageManager() {
+    @VisibleForTesting
+    public IPackageManager getPackageManager() {
         return AppGlobals.getPackageManager();
     }
 
-    PackageManagerInternal getPackageManagerInternalLocked() {
+    @VisibleForTesting
+    public PackageManagerInternal getPackageManagerInternalLocked() {
         if (mPackageManagerInt == null) {
             mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
         }
@@ -7272,20 +7359,12 @@
         mBatteryStatsService.shutdown();
         synchronized (this) {
             mProcessStats.shutdownLocked();
-            mActivityTaskManager.notifyTaskPersisterLocked(null, true);
         }
 
         return timedout;
     }
 
     @Override
-    public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
-            int secondaryDisplayShowing) {
-        mActivityTaskManager.setLockScreenShown(
-                keyguardShowing, aodShowing, secondaryDisplayShowing);
-    }
-
-    @Override
     public void notifyLockedProfile(@UserIdInt int userId) {
         mAtmInternal.notifyLockedProfile(userId, mUserController.getCurrentUserId());
     }
@@ -7410,17 +7489,17 @@
                     throw new SecurityException("Process not debuggable: " + app.packageName);
                 }
             }
-            mProfileApp = processName;
+            mProfileData.setProfileApp(processName);
 
-            if (mProfilerInfo != null) {
-                if (mProfilerInfo.profileFd != null) {
+            if (mProfileData.getProfilerInfo() != null) {
+                if (mProfileData.getProfilerInfo().profileFd != null) {
                     try {
-                        mProfilerInfo.profileFd.close();
+                        mProfileData.getProfilerInfo().profileFd.close();
                     } catch (IOException e) {
                     }
                 }
             }
-            mProfilerInfo = new ProfilerInfo(profilerInfo);
+            mProfileData.setProfilerInfo(new ProfilerInfo(profilerInfo));
             mProfileType = 0;
         }
     }
@@ -7669,7 +7748,7 @@
      *
      * @return {@code true} if this succeeded.
      */
-    static boolean scheduleAsRegularPriority(int tid, boolean suppressLogs) {
+    public static boolean scheduleAsRegularPriority(int tid, boolean suppressLogs) {
         try {
             Process.setThreadScheduler(tid, Process.SCHED_OTHER, 0);
             return true;
@@ -7693,7 +7772,7 @@
      *
      * @return {@code true} if this succeeded.
      */
-    static boolean scheduleAsFifoPriority(int tid, boolean suppressLogs) {
+    public static boolean scheduleAsFifoPriority(int tid, boolean suppressLogs) {
         try {
             Process.setThreadScheduler(tid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
             return true;
@@ -8374,7 +8453,7 @@
                     throw e.rethrowAsRuntimeException();
                 }
             }
-            mAtmInternal.startHomeActivity(currentUserId, "systemReady");
+            mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
 
             mAtmInternal.showSystemReadyErrorDialogsIfNeeded();
 
@@ -9153,6 +9232,11 @@
                 pw.println("-------------------------------------------------------------------------------");
             }
             dumpBinderProxies(pw);
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpLmkLocked(pw);
         }
     }
 
@@ -9343,6 +9427,10 @@
                 synchronized (this) {
                     dumpOomLocked(fd, pw, args, opti, true);
                 }
+            } else if ("lmk".equals(cmd)) {
+                synchronized (this) {
+                    dumpLmkLocked(pw);
+                }
             } else if ("permissions".equals(cmd) || "perm".equals(cmd)) {
                 synchronized (this) {
                     dumpPermissionsLocked(fd, pw, args, opti, true, null);
@@ -9928,20 +10016,25 @@
                 pw.println("  mTrackAllocationApp=" + mTrackAllocationApp);
             }
         }
-        if (mProfileApp != null || mProfileProc != null || (mProfilerInfo != null &&
-                (mProfilerInfo.profileFile != null || mProfilerInfo.profileFd != null))) {
-            if (dumpPackage == null || dumpPackage.equals(mProfileApp)) {
+        if (mProfileData.getProfileApp() != null || mProfileData.getProfileProc() != null
+                || (mProfileData.getProfilerInfo() != null &&
+                (mProfileData.getProfilerInfo().profileFile != null
+                        || mProfileData.getProfilerInfo().profileFd != null))) {
+            if (dumpPackage == null || dumpPackage.equals(mProfileData.getProfileApp())) {
                 if (needSep) {
                     pw.println();
                     needSep = false;
                 }
-                pw.println("  mProfileApp=" + mProfileApp + " mProfileProc=" + mProfileProc);
-                if (mProfilerInfo != null) {
-                    pw.println("  mProfileFile=" + mProfilerInfo.profileFile + " mProfileFd=" +
-                            mProfilerInfo.profileFd);
-                    pw.println("  mSamplingInterval=" + mProfilerInfo.samplingInterval +
-                            " mAutoStopProfiler=" + mProfilerInfo.autoStopProfiler +
-                            " mStreamingOutput=" + mProfilerInfo.streamingOutput);
+                pw.println("  mProfileApp=" + mProfileData.getProfileApp()
+                        + " mProfileProc=" + mProfileData.getProfileProc());
+                if (mProfileData.getProfilerInfo() != null) {
+                    pw.println("  mProfileFile=" + mProfileData.getProfilerInfo().profileFile
+                            + " mProfileFd=" + mProfileData.getProfilerInfo().profileFd);
+                    pw.println("  mSamplingInterval="
+                            + mProfileData.getProfilerInfo().samplingInterval +
+                            " mAutoStopProfiler="
+                            + mProfileData.getProfilerInfo().autoStopProfiler +
+                            " mStreamingOutput=" + mProfileData.getProfilerInfo().streamingOutput);
                     pw.println("  mProfileType=" + mProfileType);
                 }
             }
@@ -10221,19 +10314,26 @@
 
         if (mTrackAllocationApp != null) {
             if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
-                proto.write(ActivityManagerServiceDumpProcessesProto.TRACK_ALLOCATION_APP, mTrackAllocationApp);
+                proto.write(ActivityManagerServiceDumpProcessesProto.TRACK_ALLOCATION_APP,
+                        mTrackAllocationApp);
             }
         }
 
-        if (mProfileApp != null || mProfileProc != null || (mProfilerInfo != null &&
-                (mProfilerInfo.profileFile != null || mProfilerInfo.profileFd != null))) {
-            if (dumpPackage == null || dumpPackage.equals(mProfileApp)) {
+        if (mProfileData.getProfileApp() != null || mProfileData.getProfileProc() != null
+                || (mProfileData.getProfilerInfo() != null &&
+                (mProfileData.getProfilerInfo().profileFile != null
+                        || mProfileData.getProfilerInfo().profileFd != null))) {
+            if (dumpPackage == null || dumpPackage.equals(mProfileData.getProfileApp())) {
                 final long token = proto.start(ActivityManagerServiceDumpProcessesProto.PROFILE);
-                proto.write(ActivityManagerServiceDumpProcessesProto.Profile.APP_NAME, mProfileApp);
-                mProfileProc.writeToProto(proto,ActivityManagerServiceDumpProcessesProto.Profile.PROC);
-                if (mProfilerInfo != null) {
-                    mProfilerInfo.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.Profile.INFO);
-                    proto.write(ActivityManagerServiceDumpProcessesProto.Profile.TYPE, mProfileType);
+                proto.write(ActivityManagerServiceDumpProcessesProto.Profile.APP_NAME,
+                        mProfileData.getProfileApp());
+                mProfileData.getProfileProc().writeToProto(proto,
+                        ActivityManagerServiceDumpProcessesProto.Profile.PROC);
+                if (mProfileData.getProfilerInfo() != null) {
+                    mProfileData.getProfilerInfo().writeToProto(proto,
+                            ActivityManagerServiceDumpProcessesProto.Profile.INFO);
+                    proto.write(ActivityManagerServiceDumpProcessesProto.Profile.TYPE,
+                            mProfileType);
                 }
                 proto.end(token);
             }
@@ -10372,15 +10472,42 @@
         dumpProcessesToGc(pw, needSep, null);
 
         pw.println();
-        pw.println("  mHomeProcess: " + mActivityTaskManager.mHomeProcess);
-        pw.println("  mPreviousProcess: " + mActivityTaskManager.mPreviousProcess);
-        if (mActivityTaskManager.mHeavyWeightProcess != null) {
-            pw.println("  mHeavyWeightProcess: " + mActivityTaskManager.mHeavyWeightProcess);
-        }
+        mAtmInternal.dumpForOom(pw);
 
         return true;
     }
 
+    private boolean reportLmkKillAtOrBelow(PrintWriter pw, int oom_adj) {
+        Integer cnt = ProcessList.getLmkdKillCount(0, oom_adj);
+        if (cnt != null) {
+            pw.println("    kills at or below oom_adj " + oom_adj + ": " + cnt);
+            return true;
+        }
+        return false;
+    }
+
+    boolean dumpLmkLocked(PrintWriter pw) {
+        pw.println("ACTIVITY MANAGER LMK KILLS (dumpsys activity lmk)");
+        Integer cnt = ProcessList.getLmkdKillCount(ProcessList.UNKNOWN_ADJ,
+                ProcessList.UNKNOWN_ADJ);
+        if (cnt == null) {
+            return false;
+        }
+        pw.println("  Total number of kills: " + cnt);
+
+        return reportLmkKillAtOrBelow(pw, ProcessList.CACHED_APP_MAX_ADJ) &&
+            reportLmkKillAtOrBelow(pw, ProcessList.CACHED_APP_MIN_ADJ) &&
+            reportLmkKillAtOrBelow(pw, ProcessList.SERVICE_B_ADJ) &&
+            reportLmkKillAtOrBelow(pw, ProcessList.PREVIOUS_APP_ADJ) &&
+            reportLmkKillAtOrBelow(pw, ProcessList.HOME_APP_ADJ) &&
+            reportLmkKillAtOrBelow(pw, ProcessList.SERVICE_ADJ) &&
+            reportLmkKillAtOrBelow(pw, ProcessList.HEAVY_WEIGHT_APP_ADJ) &&
+            reportLmkKillAtOrBelow(pw, ProcessList.BACKUP_APP_ADJ) &&
+            reportLmkKillAtOrBelow(pw, ProcessList.PERCEPTIBLE_APP_ADJ) &&
+            reportLmkKillAtOrBelow(pw, ProcessList.VISIBLE_APP_ADJ) &&
+            reportLmkKillAtOrBelow(pw, ProcessList.FOREGROUND_APP_ADJ);
+    }
+
     /**
      * There are three ways to call this:
      *  - no provider specified: dump all the providers
@@ -10403,17 +10530,17 @@
         return mProviderMap.dumpProviderProto(fd, pw, name, args);
     }
 
-    static class ItemMatcher {
+    public static class ItemMatcher {
         ArrayList<ComponentName> components;
         ArrayList<String> strings;
         ArrayList<Integer> objects;
         boolean all;
 
-        ItemMatcher() {
+        public ItemMatcher() {
             all = true;
         }
 
-        void build(String name) {
+        public void build(String name) {
             ComponentName componentName = ComponentName.unflattenFromString(name);
             if (componentName != null) {
                 if (components == null) {
@@ -10442,7 +10569,7 @@
             }
         }
 
-        int build(String[] args, int opti) {
+        public int build(String[] args, int opti) {
             for (; opti<args.length; opti++) {
                 String name = args[opti];
                 if ("--".equals(name)) {
@@ -10453,7 +10580,7 @@
             return opti;
         }
 
-        boolean match(Object object, ComponentName comp) {
+        public boolean match(Object object, ComponentName comp) {
             if (all) {
                 return true;
             }
@@ -13355,7 +13482,8 @@
 
     boolean isPendingBroadcastProcessLocked(int pid) {
         return mFgBroadcastQueue.isPendingBroadcastProcessLocked(pid)
-                || mBgBroadcastQueue.isPendingBroadcastProcessLocked(pid);
+                || mBgBroadcastQueue.isPendingBroadcastProcessLocked(pid)
+                || mOffloadBroadcastQueue.isPendingBroadcastProcessLocked(pid);
     }
 
     void skipPendingBroadcastLocked(int pid) {
@@ -13959,16 +14087,14 @@
                                     forceStopPackageLocked(list[i], -1, false, true, true,
                                             false, false, userId, "storage unmount");
                                 }
-                                mActivityTaskManager.getRecentTasks().cleanupLocked(
-                                        UserHandle.USER_ALL);
+                                mAtmInternal.cleanupRecentTasksForUser(UserHandle.USER_ALL);
                                 sendPackageBroadcastLocked(
                                         ApplicationThreadConstants.EXTERNAL_STORAGE_UNAVAILABLE,
                                         list, userId);
                             }
                             break;
                         case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
-                            mActivityTaskManager.getRecentTasks().cleanupLocked(
-                                    UserHandle.USER_ALL);
+                            mAtmInternal.cleanupRecentTasksForUser(UserHandle.USER_ALL);
                             break;
                         case Intent.ACTION_PACKAGE_REMOVED:
                         case Intent.ACTION_PACKAGE_CHANGED:
@@ -14001,7 +14127,7 @@
                                         mUgmInternal.removeUriPermissionsForPackage(ssp, userId,
                                                 true, false);
 
-                                        mActivityTaskManager.getRecentTasks().removeTasksByPackageName(ssp, userId);
+                                        mAtmInternal.removeRecentTasksByPackageName(ssp, userId);
 
                                         mServices.forceStopPackageLocked(ssp, userId);
                                         mAtmInternal.onPackageUninstalled(ssp);
@@ -14031,10 +14157,8 @@
                             final int userHandle = intent.getIntExtra(
                                     Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
 
-                            synchronized(ActivityManagerService.this) {
-                                mActivityTaskManager.getRecentTasks().onPackagesSuspendedChanged(
-                                        packageNames, suspended, userHandle);
-                            }
+                            mAtmInternal.onPackagesSuspendedChanged(packageNames, suspended,
+                                    userHandle);
                             break;
                     }
                     break;
@@ -14601,10 +14725,16 @@
         try {
             boolean doNext = false;
             BroadcastRecord r;
+            BroadcastQueue queue;
 
             synchronized(this) {
-                BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
-                        ? mFgBroadcastQueue : mBgBroadcastQueue;
+                if (isOnOffloadQueue(flags)) {
+                    queue = mOffloadBroadcastQueue;
+                } else {
+                    queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
+                            ? mFgBroadcastQueue : mBgBroadcastQueue;
+                }
+
                 r = queue.getMatchingOrderedReceiver(who);
                 if (r != null) {
                     doNext = r.queue.finishReceiverLocked(r, resultCode,
@@ -15434,7 +15564,7 @@
             }
         }
 
-        if (wpc == mActivityTaskManager.mHomeProcess) {
+        if (wpc.isHomeProcess()) {
             if (adj > ProcessList.HOME_APP_ADJ) {
                 // This process is hosting what we currently consider to be the
                 // home app, so we don't want to let it go into the background.
@@ -15455,7 +15585,7 @@
             }
         }
 
-        if (wpc == mActivityTaskManager.mPreviousProcess && app.hasActivities()) {
+        if (wpc.isPreviousProcess() && app.hasActivities()) {
             if (adj > ProcessList.PREVIOUS_APP_ADJ) {
                 // This was the previous process that showed UI to the user.
                 // We want to try to keep it around more aggressively, to give
@@ -15532,7 +15662,7 @@
                                 "Raise procstate to started service: " + app);
                     }
                 }
-                if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess) {
+                if (app.hasShownUi && !wpc.isHomeProcess()) {
                     // If this process has shown some UI, let it immediately
                     // go to the LRU list because it may be pretty heavy with
                     // UI stuff.  We'll tag it with a label just to help
@@ -15611,7 +15741,7 @@
                         if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
                             // Not doing bind OOM management, so treat
                             // this guy more like a started service.
-                            if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess) {
+                            if (app.hasShownUi && !wpc.isHomeProcess()) {
                                 // If this process has shown some UI, let it immediately
                                 // go to the LRU list because it may be pretty heavy with
                                 // UI stuff.  We'll tag it with a label just to help
@@ -15644,7 +15774,7 @@
                             // about letting this process get into the LRU
                             // list to be killed and restarted if needed for
                             // memory.
-                            if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess
+                            if (app.hasShownUi && !wpc.isHomeProcess()
                                     && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                                 if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
                                     adjType = "cch-bound-ui-services";
@@ -15850,7 +15980,7 @@
                 }
                 String adjType = null;
                 if (adj > clientAdj) {
-                    if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess
+                    if (app.hasShownUi && !wpc.isHomeProcess()
                             && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                         adjType = "cch-ui-provider";
                     } else {
@@ -16078,6 +16208,60 @@
         return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
     }
 
+    private static final class RecordPssRunnable implements Runnable {
+        private final ActivityManagerService mService;
+        private final ProcessRecord mProc;
+        private final File mHeapdumpFile;
+
+        RecordPssRunnable(ActivityManagerService service, ProcessRecord proc, File heapdumpFile) {
+            this.mService = service;
+            this.mProc = proc;
+            this.mHeapdumpFile = heapdumpFile;
+        }
+
+        @Override
+        public void run() {
+            mService.revokeUriPermission(ActivityThread.currentActivityThread()
+                            .getApplicationThread(),
+                    null, DumpHeapActivity.JAVA_URI,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION
+                            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                    UserHandle.myUserId());
+            ParcelFileDescriptor fd = null;
+            try {
+                mHeapdumpFile.delete();
+                fd = ParcelFileDescriptor.open(mHeapdumpFile,
+                        ParcelFileDescriptor.MODE_CREATE
+                        | ParcelFileDescriptor.MODE_TRUNCATE
+                        | ParcelFileDescriptor.MODE_WRITE_ONLY
+                        | ParcelFileDescriptor.MODE_APPEND);
+                IApplicationThread thread = mProc.thread;
+                if (thread != null) {
+                    try {
+                        if (DEBUG_PSS) {
+                            Slog.d(TAG_PSS, "Requesting dump heap from "
+                                    + mProc + " to " + mHeapdumpFile);
+                        }
+                        thread.dumpHeap(/* managed= */ true,
+                                /* mallocInfo= */ false, /* runGc= */ false,
+                                mHeapdumpFile.toString(), fd,
+                                /* finishCallback= */ null);
+                    } catch (RemoteException e) {
+                    }
+                }
+            } catch (FileNotFoundException e) {
+                e.printStackTrace();
+            } finally {
+                if (fd != null) {
+                    try {
+                        fd.close();
+                    } catch (IOException e) {
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Record new PSS sample for a process.
      */
@@ -16138,48 +16322,8 @@
                     mMemWatchDumpFile = heapdumpFile.toString();
                     mMemWatchDumpPid = proc.pid;
                     mMemWatchDumpUid = proc.uid;
-                    BackgroundThread.getHandler().post(new Runnable() {
-                        @Override
-                        public void run() {
-                            revokeUriPermission(ActivityThread.currentActivityThread()
-                                            .getApplicationThread(),
-                                    null, DumpHeapActivity.JAVA_URI,
-                                    Intent.FLAG_GRANT_READ_URI_PERMISSION
-                                            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                                    UserHandle.myUserId());
-                            ParcelFileDescriptor fd = null;
-                            try {
-                                heapdumpFile.delete();
-                                fd = ParcelFileDescriptor.open(heapdumpFile,
-                                        ParcelFileDescriptor.MODE_CREATE |
-                                                ParcelFileDescriptor.MODE_TRUNCATE |
-                                                ParcelFileDescriptor.MODE_WRITE_ONLY |
-                                                ParcelFileDescriptor.MODE_APPEND);
-                                IApplicationThread thread = myProc.thread;
-                                if (thread != null) {
-                                    try {
-                                        if (DEBUG_PSS) Slog.d(TAG_PSS,
-                                                "Requesting dump heap from "
-                                                + myProc + " to " + heapdumpFile);
-                                        thread.dumpHeap(/* managed= */ true,
-                                                /* mallocInfo= */ false, /* runGc= */ false,
-                                                heapdumpFile.toString(), fd,
-                                                /* finishCallback= */ null);
-                                    } catch (RemoteException e) {
-                                    }
-                                }
-                            } catch (FileNotFoundException e) {
-                                e.printStackTrace();
-                            } finally {
-                                if (fd != null) {
-                                    try {
-                                        fd.close();
-                                    } catch (IOException e) {
-                                    }
-                                }
-                            }
-                        }
-                    });
+                    BackgroundThread.getHandler().post(
+                            new RecordPssRunnable(this, myProc, DumpHeapProvider.getJavaFile()));
                 } else {
                     Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check
                             + ", but debugging not enabled");
@@ -16518,8 +16662,7 @@
                     if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
                         // do nothing if we already switched to RT
                         if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-                            mActivityTaskManager.onTopProcChangedLocked(
-                                    app.getWindowProcessController());
+                            app.getWindowProcessController().onTopProcChanged();
                             if (mUseFifoUiScheduling) {
                                 // Switch UI pipeline for app to SCHED_FIFO
                                 app.savedPriority = Process.getThreadPriority(app.pid);
@@ -16551,8 +16694,7 @@
                         }
                     } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
                             curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-                        mActivityTaskManager.onTopProcChangedLocked(
-                                app.getWindowProcessController());
+                        app.getWindowProcessController().onTopProcChanged();
                         if (mUseFifoUiScheduling) {
                             try {
                                 // Reset UI pipeline to SCHED_OTHER
@@ -17019,6 +17161,22 @@
         return success;
     }
 
+    private static final class ProcStatsRunnable implements Runnable {
+        private final ActivityManagerService mService;
+        private final ProcessStatsService mProcessStats;
+
+        ProcStatsRunnable(ActivityManagerService service, ProcessStatsService mProcessStats) {
+            this.mService = service;
+            this.mProcessStats = mProcessStats;
+        }
+
+        @Override public void run() {
+            synchronized (mService) {
+                mProcessStats.writeStateAsyncLocked();
+            }
+        }
+    }
+
     @GuardedBy("this")
     final void updateOomAdjLocked() {
         mOomAdjProfiler.oomAdjStarted();
@@ -17247,8 +17405,6 @@
             }
         }
 
-        mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
-
         incrementProcStateSeqAndNotifyAppsLocked();
 
         mNumServiceProcs = mNewNumServiceProcs;
@@ -17313,8 +17469,8 @@
             }
             int factor = numTrimming/3;
             int minFactor = 2;
-            if (mActivityTaskManager.mHomeProcess != null) minFactor++;
-            if (mActivityTaskManager.mPreviousProcess != null) minFactor++;
+            if (mAtmInternal.getHomeProcess() != null) minFactor++;
+            if (mAtmInternal.getPreviousProcess() != null) minFactor++;
             if (factor < minFactor) factor = minFactor;
             int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
             for (int i=N-1; i>=0; i--) {
@@ -17507,15 +17663,12 @@
         }
 
         if (mProcessStats.shouldWriteNowLocked(now)) {
-            mHandler.post(new Runnable() {
-                @Override public void run() {
-                    synchronized (ActivityManagerService.this) {
-                        mProcessStats.writeStateAsyncLocked();
-                    }
-                }
-            });
+            mHandler.post(new ProcStatsRunnable(ActivityManagerService.this, mProcessStats));
         }
 
+        // Run this after making sure all procstates are updated.
+        mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+
         if (DEBUG_OOM_ADJ) {
             final long duration = SystemClock.uptimeMillis() - now;
             if (false) {
@@ -17937,8 +18090,8 @@
     }
 
     private void stopProfilerLocked(ProcessRecord proc, int profileType) {
-        if (proc == null || proc == mProfileProc) {
-            proc = mProfileProc;
+        if (proc == null || proc == mProfileData.getProfileProc()) {
+            proc = mProfileData.getProfileProc();
             profileType = mProfileType;
             clearProfilerLocked();
         }
@@ -17953,15 +18106,16 @@
     }
 
     void clearProfilerLocked() {
-        if (mProfilerInfo !=null && mProfilerInfo.profileFd != null) {
+        if (mProfileData.getProfilerInfo() != null
+                && mProfileData.getProfilerInfo().profileFd != null) {
             try {
-                mProfilerInfo.profileFd.close();
+                mProfileData.getProfilerInfo().profileFd.close();
             } catch (IOException e) {
             }
         }
-        mProfileApp = null;
-        mProfileProc = null;
-        mProfilerInfo = null;
+        mProfileData.setProfileApp(null);
+        mProfileData.setProfileProc(null);
+        mProfileData.setProfilerInfo(null);
     }
 
     public boolean profileControl(String process, int userId, boolean start,
@@ -17993,7 +18147,7 @@
                 if (start) {
                     stopProfilerLocked(null, 0);
                     setProfileApp(proc.info, proc.processName, profilerInfo);
-                    mProfileProc = proc;
+                    mProfileData.setProfileProc(proc);
                     mProfileType = profileType;
                     ParcelFileDescriptor fd = profilerInfo.profileFd;
                     try {
@@ -18005,10 +18159,10 @@
                     proc.thread.profilerControl(start, profilerInfo, profileType);
                     fd = null;
                     try {
-                        mProfilerInfo.profileFd.close();
+                        mProfileData.getProfilerInfo().profileFd.close();
                     } catch (IOException e) {
                     }
-                    mProfilerInfo.profileFd = null;
+                    mProfileData.getProfilerInfo().profileFd = null;
 
                     if (proc.pid == MY_PID) {
                         // When profiling the system server itself, avoid closing the file
@@ -18390,7 +18544,7 @@
     }
 
     @VisibleForTesting
-    final class LocalService extends ActivityManagerInternal {
+    public final class LocalService extends ActivityManagerInternal {
         @Override
         public String checkContentProviderAccess(String authority, int userId) {
             return ActivityManagerService.this.checkContentProviderAccess(authority, userId);
@@ -18612,7 +18766,8 @@
                                     memoryStat.rssInBytes,
                                     memoryStat.cacheInBytes,
                                     memoryStat.swapInBytes,
-                                    memoryStat.rssHighWatermarkInBytes);
+                                    memoryStat.rssHighWatermarkInBytes,
+                                    memoryStat.startTimeNanos);
                     processMemoryStates.add(processMemoryState);
                 }
             }
@@ -18635,7 +18790,7 @@
                 ProcessMemoryState processMemoryState = new ProcessMemoryState(uid, processName,
                         oomScore, memoryStat.pgfault, memoryStat.pgmajfault,
                         memoryStat.rssInBytes, memoryStat.cacheInBytes, memoryStat.swapInBytes,
-                        memoryStat.rssHighWatermarkInBytes);
+                        memoryStat.rssHighWatermarkInBytes, memoryStat.startTimeNanos);
                 processMemoryStates.add(processMemoryState);
             }
             return processMemoryStates;
@@ -19476,4 +19631,8 @@
             }
         }
     }
+
+    private boolean isOnOffloadQueue(int flags) {
+        return ((flags & Intent.FLAG_RECEIVER_OFFLOAD) != 0);
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 96601a2..fe402ea 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2736,7 +2736,7 @@
     int runWrite(PrintWriter pw) {
         mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
                 "registerUidObserver()");
-        mInternal.mActivityTaskManager.getRecentTasks().flush();
+        mInternal.mAtmInternal.flushRecentTasks();
         pw.println("All tasks persisted.");
         return 0;
     }
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
deleted file mode 100644
index a0dd878..0000000
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ /dev/null
@@ -1,926 +0,0 @@
-package com.android.server.am;
-
-import static android.app.ActivityManager.START_SUCCESS;
-import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManager.processStateAmToProto;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_ACTIVITY_START;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_BIND_APPLICATION_DELAY_MS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_CALLING_PACKAGE_NAME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_CANCELLED;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DELAY_MS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_IS_EPHEMERAL;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_PROCESS_RUNNING;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_REPORTED_DRAWN;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_REPORTED_DRAWN_MS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_FLAGS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_FULLSCREEN;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_VISIBLE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_LAUNCH_MODE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_PROCESS_NAME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_REAL_ACTIVITY;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_PACKAGE_NAME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_UID;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_UID_HAS_ANY_VISIBLE_WINDOW;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_UID_PROC_STATE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CLASS_NAME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_COMING_FROM_PENDING_INTENT;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_LAUNCH_TOKEN;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INTENT_ACTION;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_CUR_PROC_STATE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_CLIENT_ACTIVITIES;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_FOREGROUND_ACTIVITIES;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_FOREGROUND_SERVICES;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_OVERLAY_UI;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_TOP_UI;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_MILLIS_SINCE_FG_INTERACTION;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_MILLIS_SINCE_LAST_INTERACTION_EVENT;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_MILLIS_SINCE_UNIMPORTANT;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_PENDING_UI_CLEAN;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_PROCESS_NAME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_PROC_STATE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_PACKAGE_NAME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_SHORT_COMPONENT_NAME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID_PROC_STATE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_WHITELIST_TAG;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_FILTER;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_REASON;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_HOT_LAUNCH;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.EventLogTags.AM_ACTIVITY_LAUNCH_TIME;
-import static com.android.server.am.MemoryStatUtil.MemoryStat;
-import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
-import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.dex.ArtManagerInternal;
-import android.content.pm.dex.PackageOptimizationInfo;
-import android.metrics.LogMaker;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.util.StatsLog;
-import android.util.TimeUtils;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.SomeArgs;
-import com.android.server.LocalServices;
-
-/**
- * Listens to activity launches, transitions, visibility changes and window drawn callbacks to
- * determine app launch times and draw delays. Source of truth for activity metrics and provides
- * data for Tron, logcat, event logs and {@link android.app.WaitResult}.
- *
- * Tests:
- * atest CtsActivityManagerDeviceTestCases:ActivityMetricsLoggerTests
- */
-class ActivityMetricsLogger {
-
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityMetricsLogger" : TAG_ATM;
-
-    // Window modes we are interested in logging. If we ever introduce a new type, we need to add
-    // a value here and increase the {@link #TRON_WINDOW_STATE_VARZ_STRINGS} array.
-    private static final int WINDOW_STATE_STANDARD = 0;
-    private static final int WINDOW_STATE_SIDE_BY_SIDE = 1;
-    private static final int WINDOW_STATE_FREEFORM = 2;
-    private static final int WINDOW_STATE_ASSISTANT = 3;
-    private static final int WINDOW_STATE_INVALID = -1;
-
-    private static final long INVALID_START_TIME = -1;
-    private static final int INVALID_DELAY = -1;
-    private static final int INVALID_TRANSITION_TYPE = -1;
-
-    private static final int MSG_CHECK_VISIBILITY = 0;
-
-    // Preallocated strings we are sending to tron, so we don't have to allocate a new one every
-    // time we log.
-    private static final String[] TRON_WINDOW_STATE_VARZ_STRINGS = {
-            "window_time_0", "window_time_1", "window_time_2", "window_time_3"};
-
-    private int mWindowState = WINDOW_STATE_STANDARD;
-    private long mLastLogTimeSecs;
-    private final ActivityStackSupervisor mSupervisor;
-    private final Context mContext;
-    private final MetricsLogger mMetricsLogger = new MetricsLogger();
-
-    private long mCurrentTransitionStartTime = INVALID_START_TIME;
-    private long mLastTransitionStartTime = INVALID_START_TIME;
-
-    private int mCurrentTransitionDeviceUptime;
-    private int mCurrentTransitionDelayMs;
-    private boolean mLoggedTransitionStarting;
-
-    private final SparseArray<WindowingModeTransitionInfo> mWindowingModeTransitionInfo =
-            new SparseArray<>();
-    private final SparseArray<WindowingModeTransitionInfo> mLastWindowingModeTransitionInfo =
-            new SparseArray<>();
-    private final H mHandler;
-
-    private ArtManagerInternal mArtManagerInternal;
-    private final StringBuilder mStringBuilder = new StringBuilder();
-
-    private final class H extends Handler {
-
-        public H(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_CHECK_VISIBILITY:
-                    final SomeArgs args = (SomeArgs) msg.obj;
-                    checkVisibility((TaskRecord) args.arg1, (ActivityRecord) args.arg2);
-                    break;
-            }
-        }
-    }
-
-    private final class WindowingModeTransitionInfo {
-        private ActivityRecord launchedActivity;
-        private int startResult;
-        private boolean currentTransitionProcessRunning;
-        /** Elapsed time from when we launch an activity to when its windows are drawn. */
-        private int windowsDrawnDelayMs;
-        private int startingWindowDelayMs = INVALID_DELAY;
-        private int bindApplicationDelayMs = INVALID_DELAY;
-        private int reason = APP_TRANSITION_TIMEOUT;
-        private boolean loggedWindowsDrawn;
-        private boolean loggedStartingWindowDrawn;
-        private boolean launchTraceActive;
-    }
-
-    final class WindowingModeTransitionInfoSnapshot {
-        final private ApplicationInfo applicationInfo;
-        final private WindowProcessController processRecord;
-        final String packageName;
-        final String launchedActivityName;
-        final private String launchedActivityLaunchedFromPackage;
-        final private String launchedActivityLaunchToken;
-        final private String launchedActivityAppRecordRequiredAbi;
-        final String launchedActivityShortComponentName;
-        final private String processName;
-        final private int reason;
-        final private int startingWindowDelayMs;
-        final private int bindApplicationDelayMs;
-        final int windowsDrawnDelayMs;
-        final int type;
-        final int userId;
-        /**
-         * Elapsed time from when we launch an activity to when the app reported it was
-         * fully drawn. If this is not reported then the value is set to INVALID_DELAY.
-         */
-        final int windowsFullyDrawnDelayMs;
-        final int activityRecordIdHashCode;
-
-        private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info) {
-            this(info, info.launchedActivity);
-        }
-
-        private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info,
-                ActivityRecord launchedActivity) {
-            this(info, launchedActivity, INVALID_DELAY);
-        }
-
-        private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info,
-                ActivityRecord launchedActivity, int windowsFullyDrawnDelayMs) {
-            applicationInfo = launchedActivity.appInfo;
-            packageName = launchedActivity.packageName;
-            launchedActivityName = launchedActivity.info.name;
-            launchedActivityLaunchedFromPackage = launchedActivity.launchedFromPackage;
-            launchedActivityLaunchToken = launchedActivity.info.launchToken;
-            launchedActivityAppRecordRequiredAbi = launchedActivity.app == null
-                    ? null
-                    : launchedActivity.app.getRequiredAbi();
-            reason = info.reason;
-            startingWindowDelayMs = info.startingWindowDelayMs;
-            bindApplicationDelayMs = info.bindApplicationDelayMs;
-            windowsDrawnDelayMs = info.windowsDrawnDelayMs;
-            type = getTransitionType(info);
-            processRecord = findProcessForActivity(launchedActivity);
-            processName = launchedActivity.processName;
-            userId = launchedActivity.userId;
-            launchedActivityShortComponentName = launchedActivity.shortComponentName;
-            activityRecordIdHashCode = System.identityHashCode(launchedActivity);
-            this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
-        }
-    }
-
-    ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context, Looper looper) {
-        mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
-        mSupervisor = supervisor;
-        mContext = context;
-        mHandler = new H(looper);
-    }
-
-    void logWindowState() {
-        final long now = SystemClock.elapsedRealtime() / 1000;
-        if (mWindowState != WINDOW_STATE_INVALID) {
-            // We log even if the window state hasn't changed, because the user might remain in
-            // home/fullscreen move forever and we would like to track this kind of behavior
-            // too.
-            MetricsLogger.count(mContext, TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState],
-                    (int) (now - mLastLogTimeSecs));
-        }
-        mLastLogTimeSecs = now;
-
-        mWindowState = WINDOW_STATE_INVALID;
-        ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
-        if (stack == null) {
-            return;
-        }
-
-        if (stack.isActivityTypeAssistant()) {
-            mWindowState = WINDOW_STATE_ASSISTANT;
-            return;
-        }
-
-        int windowingMode = stack.getWindowingMode();
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            stack = mSupervisor.findStackBehind(stack);
-            windowingMode = stack.getWindowingMode();
-        }
-        switch (windowingMode) {
-            case WINDOWING_MODE_FULLSCREEN:
-                mWindowState = WINDOW_STATE_STANDARD;
-                break;
-            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-            case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
-                mWindowState = WINDOW_STATE_SIDE_BY_SIDE;
-                break;
-            case WINDOWING_MODE_FREEFORM:
-                mWindowState = WINDOW_STATE_FREEFORM;
-                break;
-            default:
-                if (windowingMode != WINDOWING_MODE_UNDEFINED) {
-                    throw new IllegalStateException("Unknown windowing mode for stack=" + stack
-                            + " windowingMode=" + windowingMode);
-                }
-        }
-    }
-
-    /**
-     * Notifies the tracker at the earliest possible point when we are starting to launch an
-     * activity.
-     */
-    void notifyActivityLaunching() {
-        if (!isAnyTransitionActive()) {
-            if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunching");
-            mCurrentTransitionStartTime = SystemClock.uptimeMillis();
-            mLastTransitionStartTime = mCurrentTransitionStartTime;
-        }
-    }
-
-    /**
-     * Notifies the tracker that the activity is actually launching.
-     *
-     * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the
-     *                   launch
-     * @param launchedActivity the activity that is being launched
-     */
-    void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity) {
-        final WindowProcessController processRecord = findProcessForActivity(launchedActivity);
-        final boolean processRunning = processRecord != null;
-
-        // We consider this a "process switch" if the process of the activity that gets launched
-        // didn't have an activity that was in started state. In this case, we assume that lot
-        // of caches might be purged so the time until it produces the first frame is very
-        // interesting.
-        final boolean processSwitch = processRecord == null
-                || !processRecord.hasStartedActivity(launchedActivity);
-
-        notifyActivityLaunched(resultCode, launchedActivity, processRunning, processSwitch);
-    }
-
-    /**
-     * Notifies the tracker the the activity is actually launching.
-     *
-     * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the
-     *                   launch
-     * @param launchedActivity the activity being launched
-     * @param processRunning whether the process that will contains the activity is already running
-     * @param processSwitch whether the process that will contain the activity didn't have any
-     *                      activity that was stopped, i.e. the started activity is "switching"
-     *                      processes
-     */
-    private void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity,
-            boolean processRunning, boolean processSwitch) {
-
-        if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched"
-                + " resultCode=" + resultCode
-                + " launchedActivity=" + launchedActivity
-                + " processRunning=" + processRunning
-                + " processSwitch=" + processSwitch);
-
-        final int windowingMode = launchedActivity != null
-                ? launchedActivity.getWindowingMode()
-                : WINDOWING_MODE_UNDEFINED;
-        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
-        if (mCurrentTransitionStartTime == INVALID_START_TIME) {
-            // No transition is active ignore this launch.
-            return;
-        }
-
-        if (launchedActivity != null && launchedActivity.nowVisible) {
-            // Launched activity is already visible. We cannot measure windows drawn delay.
-            reset(true /* abort */, info);
-            return;
-        }
-
-        if (launchedActivity != null && info != null) {
-            // If we are already in an existing transition, only update the activity name, but not
-            // the other attributes.
-            info.launchedActivity = launchedActivity;
-            return;
-        }
-
-        final boolean otherWindowModesLaunching =
-                mWindowingModeTransitionInfo.size() > 0 && info == null;
-        if ((!isLoggableResultCode(resultCode) || launchedActivity == null || !processSwitch
-                || windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) {
-            // Failed to launch or it was not a process switch, so we don't care about the timing.
-            reset(true /* abort */, info);
-            return;
-        } else if (otherWindowModesLaunching) {
-            // Don't log this windowing mode but continue with the other windowing modes.
-            return;
-        }
-
-        if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched successful");
-
-        final WindowingModeTransitionInfo newInfo = new WindowingModeTransitionInfo();
-        newInfo.launchedActivity = launchedActivity;
-        newInfo.currentTransitionProcessRunning = processRunning;
-        newInfo.startResult = resultCode;
-        mWindowingModeTransitionInfo.put(windowingMode, newInfo);
-        mLastWindowingModeTransitionInfo.put(windowingMode, newInfo);
-        mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000);
-        startTraces(newInfo);
-    }
-
-    /**
-     * @return True if we should start logging an event for an activity start that returned
-     *         {@code resultCode} and that we'll indeed get a windows drawn event.
-     */
-    private boolean isLoggableResultCode(int resultCode) {
-        return resultCode == START_SUCCESS || resultCode == START_TASK_TO_FRONT;
-    }
-
-    /**
-     * Notifies the tracker that all windows of the app have been drawn.
-     */
-    WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(int windowingMode, long timestamp) {
-        if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode);
-
-        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
-        if (info == null || info.loggedWindowsDrawn) {
-            return null;
-        }
-        info.windowsDrawnDelayMs = calculateDelay(timestamp);
-        info.loggedWindowsDrawn = true;
-        final WindowingModeTransitionInfoSnapshot infoSnapshot =
-                new WindowingModeTransitionInfoSnapshot(info);
-        if (allWindowsDrawn() && mLoggedTransitionStarting) {
-            reset(false /* abort */, info);
-        }
-        return infoSnapshot;
-    }
-
-    /**
-     * Notifies the tracker that the starting window was drawn.
-     */
-    void notifyStartingWindowDrawn(int windowingMode, long timestamp) {
-        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
-        if (info == null || info.loggedStartingWindowDrawn) {
-            return;
-        }
-        info.loggedStartingWindowDrawn = true;
-        info.startingWindowDelayMs = calculateDelay(timestamp);
-    }
-
-    /**
-     * Notifies the tracker that the app transition is starting.
-     *
-     * @param windowingModeToReason A map from windowing mode to a reason integer, which must be on
-     *                              of ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
-     */
-    void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestamp) {
-        if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
-            return;
-        }
-        if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
-        mCurrentTransitionDelayMs = calculateDelay(timestamp);
-        mLoggedTransitionStarting = true;
-        for (int index = windowingModeToReason.size() - 1; index >= 0; index--) {
-            final int windowingMode = windowingModeToReason.keyAt(index);
-            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
-                    windowingMode);
-            if (info == null) {
-                continue;
-            }
-            info.reason = windowingModeToReason.valueAt(index);
-        }
-        if (allWindowsDrawn()) {
-            reset(false /* abort */, null /* WindowingModeTransitionInfo */);
-        }
-    }
-
-    /**
-     * Notifies the tracker that the visibility of an app is changing.
-     *
-     * @param activityRecord the app that is changing its visibility
-     */
-    void notifyVisibilityChanged(ActivityRecord activityRecord) {
-        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
-                activityRecord.getWindowingMode());
-        if (info == null) {
-            return;
-        }
-        if (info.launchedActivity != activityRecord) {
-            return;
-        }
-        final TaskRecord t = activityRecord.getTask();
-        final SomeArgs args = SomeArgs.obtain();
-        args.arg1 = t;
-        args.arg2 = activityRecord;
-        mHandler.obtainMessage(MSG_CHECK_VISIBILITY, args).sendToTarget();
-    }
-
-    private void checkVisibility(TaskRecord t, ActivityRecord r) {
-        synchronized (mSupervisor.mService.mGlobalLock) {
-
-            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
-                    r.getWindowingMode());
-
-            // If we have an active transition that's waiting on a certain activity that will be
-            // invisible now, we'll never get onWindowsDrawn, so abort the transition if necessary.
-            if (info != null && !t.isVisible()) {
-                if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible"
-                        + " activity=" + r);
-                logAppTransitionCancel(info);
-                mWindowingModeTransitionInfo.remove(r.getWindowingMode());
-                if (mWindowingModeTransitionInfo.size() == 0) {
-                    reset(true /* abort */, info);
-                }
-            }
-        }
-    }
-
-    /**
-     * Notifies the tracker that we called immediately before we call bindApplication on the client.
-     *
-     * @param appInfo The client into which we'll call bindApplication.
-     */
-    void notifyBindApplication(ApplicationInfo appInfo) {
-        for (int i = mWindowingModeTransitionInfo.size() - 1; i >= 0; i--) {
-            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(i);
-
-            // App isn't attached to record yet, so match with info.
-            if (info.launchedActivity.appInfo == appInfo) {
-                info.bindApplicationDelayMs = calculateCurrentDelay();
-            }
-        }
-    }
-
-    private boolean allWindowsDrawn() {
-        for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) {
-            if (!mWindowingModeTransitionInfo.valueAt(index).loggedWindowsDrawn) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private boolean isAnyTransitionActive() {
-        return mCurrentTransitionStartTime != INVALID_START_TIME
-                && mWindowingModeTransitionInfo.size() > 0;
-    }
-
-    private void reset(boolean abort, WindowingModeTransitionInfo info) {
-        if (DEBUG_METRICS) Slog.i(TAG, "reset abort=" + abort);
-        if (!abort && isAnyTransitionActive()) {
-            logAppTransitionMultiEvents();
-        }
-        stopLaunchTrace(info);
-        mCurrentTransitionStartTime = INVALID_START_TIME;
-        mCurrentTransitionDelayMs = INVALID_DELAY;
-        mLoggedTransitionStarting = false;
-        mWindowingModeTransitionInfo.clear();
-    }
-
-    private int calculateCurrentDelay() {
-        // Shouldn't take more than 25 days to launch an app, so int is fine here.
-        return (int) (SystemClock.uptimeMillis() - mCurrentTransitionStartTime);
-    }
-
-    private int calculateDelay(long timestamp) {
-        // Shouldn't take more than 25 days to launch an app, so int is fine here.
-        return (int) (timestamp - mCurrentTransitionStartTime);
-    }
-
-    private void logAppTransitionCancel(WindowingModeTransitionInfo info) {
-        final int type = getTransitionType(info);
-        if (type == INVALID_TRANSITION_TYPE) {
-            return;
-        }
-        final LogMaker builder = new LogMaker(APP_TRANSITION_CANCELLED);
-        builder.setPackageName(info.launchedActivity.packageName);
-        builder.setType(type);
-        builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name);
-        mMetricsLogger.write(builder);
-        StatsLog.write(
-                StatsLog.APP_START_CANCELED,
-                info.launchedActivity.appInfo.uid,
-                info.launchedActivity.packageName,
-                convertAppStartTransitionType(type),
-                info.launchedActivity.info.name);
-    }
-
-    private void logAppTransitionMultiEvents() {
-        if (DEBUG_METRICS) Slog.i(TAG, "logging transition events");
-        for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) {
-            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(index);
-            final int type = getTransitionType(info);
-            if (type == INVALID_TRANSITION_TYPE) {
-                return;
-            }
-
-            // Take a snapshot of the transition info before sending it to the handler for logging.
-            // This will avoid any races with other operations that modify the ActivityRecord.
-            final WindowingModeTransitionInfoSnapshot infoSnapshot =
-                    new WindowingModeTransitionInfoSnapshot(info);
-            final int currentTransitionDeviceUptime = mCurrentTransitionDeviceUptime;
-            final int currentTransitionDelayMs = mCurrentTransitionDelayMs;
-            BackgroundThread.getHandler().post(() -> logAppTransition(
-                    currentTransitionDeviceUptime, currentTransitionDelayMs, infoSnapshot));
-            BackgroundThread.getHandler().post(() -> logAppDisplayed(infoSnapshot));
-
-            info.launchedActivity.info.launchToken = null;
-        }
-    }
-
-    // This gets called on a background thread without holding the activity manager lock.
-    private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs,
-            WindowingModeTransitionInfoSnapshot info) {
-        final LogMaker builder = new LogMaker(APP_TRANSITION);
-        builder.setPackageName(info.packageName);
-        builder.setType(info.type);
-        builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivityName);
-        final boolean isInstantApp = info.applicationInfo.isInstantApp();
-        if (info.launchedActivityLaunchedFromPackage != null) {
-            builder.addTaggedData(APP_TRANSITION_CALLING_PACKAGE_NAME,
-                    info.launchedActivityLaunchedFromPackage);
-        }
-        String launchToken = info.launchedActivityLaunchToken;
-        if (launchToken != null) {
-            builder.addTaggedData(FIELD_INSTANT_APP_LAUNCH_TOKEN, launchToken);
-        }
-        builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
-        builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
-                currentTransitionDeviceUptime);
-        builder.addTaggedData(APP_TRANSITION_DELAY_MS, currentTransitionDelayMs);
-        builder.setSubtype(info.reason);
-        if (info.startingWindowDelayMs != INVALID_DELAY) {
-            builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
-                    info.startingWindowDelayMs);
-        }
-        if (info.bindApplicationDelayMs != INVALID_DELAY) {
-            builder.addTaggedData(APP_TRANSITION_BIND_APPLICATION_DELAY_MS,
-                    info.bindApplicationDelayMs);
-        }
-        builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs);
-        final ArtManagerInternal artManagerInternal = getArtManagerInternal();
-        final PackageOptimizationInfo packageOptimizationInfo =
-                (artManagerInternal == null) || (info.launchedActivityAppRecordRequiredAbi == null)
-                ? PackageOptimizationInfo.createWithNoInfo()
-                : artManagerInternal.getPackageOptimizationInfo(
-                        info.applicationInfo,
-                        info.launchedActivityAppRecordRequiredAbi);
-        builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_REASON,
-                packageOptimizationInfo.getCompilationReason());
-        builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_FILTER,
-                packageOptimizationInfo.getCompilationFilter());
-        mMetricsLogger.write(builder);
-        StatsLog.write(
-                StatsLog.APP_START_OCCURRED,
-                info.applicationInfo.uid,
-                info.packageName,
-                convertAppStartTransitionType(info.type),
-                info.launchedActivityName,
-                info.launchedActivityLaunchedFromPackage,
-                isInstantApp,
-                currentTransitionDeviceUptime * 1000,
-                info.reason,
-                currentTransitionDelayMs,
-                info.startingWindowDelayMs,
-                info.bindApplicationDelayMs,
-                info.windowsDrawnDelayMs,
-                launchToken,
-                packageOptimizationInfo.getCompilationReason(),
-                packageOptimizationInfo.getCompilationFilter());
-        logAppStartMemoryStateCapture(info);
-    }
-
-    private void logAppDisplayed(WindowingModeTransitionInfoSnapshot info) {
-        if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) {
-            return;
-        }
-
-        EventLog.writeEvent(AM_ACTIVITY_LAUNCH_TIME,
-                info.userId, info.activityRecordIdHashCode, info.launchedActivityShortComponentName,
-                info.windowsDrawnDelayMs);
-
-        StringBuilder sb = mStringBuilder;
-        sb.setLength(0);
-        sb.append("Displayed ");
-        sb.append(info.launchedActivityShortComponentName);
-        sb.append(": ");
-        TimeUtils.formatDuration(info.windowsDrawnDelayMs, sb);
-        Log.i(TAG, sb.toString());
-    }
-
-    private int convertAppStartTransitionType(int tronType) {
-        if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
-            return StatsLog.APP_START_OCCURRED__TYPE__COLD;
-        }
-        if (tronType == TYPE_TRANSITION_WARM_LAUNCH) {
-            return StatsLog.APP_START_OCCURRED__TYPE__WARM;
-        }
-        if (tronType == TYPE_TRANSITION_HOT_LAUNCH) {
-            return StatsLog.APP_START_OCCURRED__TYPE__HOT;
-        }
-        return StatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
-     }
-
-    WindowingModeTransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r,
-            boolean restoredFromBundle) {
-        final WindowingModeTransitionInfo info = mLastWindowingModeTransitionInfo.get(
-                r.getWindowingMode());
-        if (info == null) {
-            return null;
-        }
-
-        // Record the handling of the reportFullyDrawn callback in the trace system. This is not
-        // actually used to trace this function, but instead the logical task that this function
-        // fullfils (handling reportFullyDrawn() callbacks).
-        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
-                "ActivityManager:ReportingFullyDrawn " + info.launchedActivity.packageName);
-
-        final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
-        builder.setPackageName(r.packageName);
-        builder.addTaggedData(FIELD_CLASS_NAME, r.info.name);
-        long startupTimeMs = SystemClock.uptimeMillis() - mLastTransitionStartTime;
-        builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS, startupTimeMs);
-        builder.setType(restoredFromBundle
-                ? TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE
-                : TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE);
-        builder.addTaggedData(APP_TRANSITION_PROCESS_RUNNING,
-                info.currentTransitionProcessRunning ? 1 : 0);
-        mMetricsLogger.write(builder);
-        StatsLog.write(
-                StatsLog.APP_START_FULLY_DRAWN,
-                info.launchedActivity.appInfo.uid,
-                info.launchedActivity.packageName,
-                restoredFromBundle
-                        ? StatsLog.APP_START_FULLY_DRAWN__TYPE__WITH_BUNDLE
-                        : StatsLog.APP_START_FULLY_DRAWN__TYPE__WITHOUT_BUNDLE,
-                info.launchedActivity.info.name,
-                info.currentTransitionProcessRunning,
-                startupTimeMs);
-
-        // Ends the trace started at the beginning of this function. This is located here to allow
-        // the trace slice to have a noticable duration.
-        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
-        final WindowingModeTransitionInfoSnapshot infoSnapshot =
-                new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
-        BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
-        return infoSnapshot;
-    }
-
-    private void logAppFullyDrawn(WindowingModeTransitionInfoSnapshot info) {
-        if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) {
-            return;
-        }
-
-        StringBuilder sb = mStringBuilder;
-        sb.setLength(0);
-        sb.append("Fully drawn ");
-        sb.append(info.launchedActivityShortComponentName);
-        sb.append(": ");
-        TimeUtils.formatDuration(info.windowsFullyDrawnDelayMs, sb);
-        Log.i(TAG, sb.toString());
-    }
-
-    void logActivityStart(Intent intent, WindowProcessController callerApp, ActivityRecord r,
-            int callingUid, String callingPackage, int callingUidProcState,
-            boolean callingUidHasAnyVisibleWindow,
-            int realCallingUid, int realCallingUidProcState,
-            boolean realCallingUidHasAnyVisibleWindow,
-            int targetUid, String targetPackage, int targetUidProcState,
-            boolean targetUidHasAnyVisibleWindow, String targetWhitelistTag,
-            boolean comingFromPendingIntent) {
-
-        final long nowElapsed = SystemClock.elapsedRealtime();
-        final long nowUptime = SystemClock.uptimeMillis();
-        final LogMaker builder = new LogMaker(ACTION_ACTIVITY_START);
-        builder.setTimestamp(System.currentTimeMillis());
-        builder.addTaggedData(FIELD_CALLING_UID, callingUid);
-        builder.addTaggedData(FIELD_CALLING_PACKAGE_NAME, callingPackage);
-        builder.addTaggedData(FIELD_CALLING_UID_PROC_STATE,
-                processStateAmToProto(callingUidProcState));
-        builder.addTaggedData(FIELD_CALLING_UID_HAS_ANY_VISIBLE_WINDOW,
-                callingUidHasAnyVisibleWindow ? 1 : 0);
-        builder.addTaggedData(FIELD_REAL_CALLING_UID, realCallingUid);
-        builder.addTaggedData(FIELD_REAL_CALLING_UID_PROC_STATE,
-                processStateAmToProto(realCallingUidProcState));
-        builder.addTaggedData(FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW,
-                realCallingUidHasAnyVisibleWindow ? 1 : 0);
-        builder.addTaggedData(FIELD_TARGET_UID, targetUid);
-        builder.addTaggedData(FIELD_TARGET_PACKAGE_NAME, targetPackage);
-        builder.addTaggedData(FIELD_TARGET_UID_PROC_STATE,
-                processStateAmToProto(targetUidProcState));
-        builder.addTaggedData(FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW,
-                targetUidHasAnyVisibleWindow ? 1 : 0);
-        builder.addTaggedData(FIELD_TARGET_WHITELIST_TAG, targetWhitelistTag);
-        builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);
-        builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0);
-        builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
-        if (callerApp != null) {
-            builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.mName);
-            builder.addTaggedData(FIELD_PROCESS_RECORD_CUR_PROC_STATE,
-                    processStateAmToProto(callerApp.getCurrentProcState()));
-            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_CLIENT_ACTIVITIES,
-                    callerApp.hasClientActivities() ? 1 : 0);
-            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_FOREGROUND_SERVICES,
-                    callerApp.hasForegroundServices() ? 1 : 0);
-            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_FOREGROUND_ACTIVITIES,
-                    callerApp.hasForegroundActivities() ? 1 : 0);
-            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_TOP_UI, callerApp.hasTopUi() ? 1 : 0);
-            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_OVERLAY_UI,
-                    callerApp.hasOverlayUi() ? 1 : 0);
-            builder.addTaggedData(FIELD_PROCESS_RECORD_PENDING_UI_CLEAN,
-                    callerApp.hasPendingUiClean() ? 1 : 0);
-            if (callerApp.getInteractionEventTime() != 0) {
-                builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_LAST_INTERACTION_EVENT,
-                        (nowElapsed - callerApp.getInteractionEventTime()));
-            }
-            if (callerApp.getFgInteractionTime() != 0) {
-                builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_FG_INTERACTION,
-                        (nowElapsed - callerApp.getFgInteractionTime()));
-            }
-            if (callerApp.getWhenUnimportant() != 0) {
-                builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_UNIMPORTANT,
-                        (nowUptime - callerApp.getWhenUnimportant()));
-            }
-        }
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY, r.realActivity.toShortString());
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0);
-        if (r.lastVisibleTime != 0) {
-            builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE,
-                    (nowUptime - r.lastVisibleTime));
-        }
-        if (r.resultTo != null) {
-            builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME, r.resultTo.packageName);
-            builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME,
-                    r.resultTo.shortComponentName);
-        }
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD,
-                r.visibleIgnoringKeyguard ? 1 : 0);
-        if (r.lastLaunchTime != 0) {
-            builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH,
-                    (nowUptime - r.lastLaunchTime));
-        }
-        mMetricsLogger.write(builder);
-    }
-
-    private int getTransitionType(WindowingModeTransitionInfo info) {
-        if (info.currentTransitionProcessRunning) {
-            if (info.startResult == START_SUCCESS) {
-                return TYPE_TRANSITION_WARM_LAUNCH;
-            } else if (info.startResult == START_TASK_TO_FRONT) {
-                return TYPE_TRANSITION_HOT_LAUNCH;
-            }
-        } else if (info.startResult == START_SUCCESS) {
-            return TYPE_TRANSITION_COLD_LAUNCH;
-        }
-        return INVALID_TRANSITION_TYPE;
-    }
-
-    private void logAppStartMemoryStateCapture(WindowingModeTransitionInfoSnapshot info) {
-        if (info.processRecord == null) {
-            if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture processRecord null");
-            return;
-        }
-
-        final int pid = info.processRecord.getPid();
-        final int uid = info.applicationInfo.uid;
-        final MemoryStat memoryStat = readMemoryStatFromFilesystem(uid, pid);
-        if (memoryStat == null) {
-            if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture memoryStat null");
-            return;
-        }
-
-        StatsLog.write(
-                StatsLog.APP_START_MEMORY_STATE_CAPTURED,
-                uid,
-                info.processName,
-                info.launchedActivityName,
-                memoryStat.pgfault,
-                memoryStat.pgmajfault,
-                memoryStat.rssInBytes,
-                memoryStat.cacheInBytes,
-                memoryStat.swapInBytes);
-    }
-
-    private WindowProcessController findProcessForActivity(ActivityRecord launchedActivity) {
-        return launchedActivity != null
-                ? mSupervisor.mService.mProcessNames.get(
-                        launchedActivity.processName, launchedActivity.appInfo.uid)
-                : null;
-    }
-
-    private ArtManagerInternal getArtManagerInternal() {
-        if (mArtManagerInternal == null) {
-            // Note that this may be null.
-            // ArtManagerInternal is registered during PackageManagerService
-            // initialization which happens after ActivityManagerService.
-            mArtManagerInternal = LocalServices.getService(ArtManagerInternal.class);
-        }
-        return mArtManagerInternal;
-    }
-
-    /**
-     * Starts traces for app launch.
-     *
-     * @param info
-     * */
-    private void startTraces(WindowingModeTransitionInfo info) {
-        if (info == null) {
-            return;
-        }
-        int transitionType = getTransitionType(info);
-        if (!info.launchTraceActive && transitionType == TYPE_TRANSITION_WARM_LAUNCH
-                || transitionType == TYPE_TRANSITION_COLD_LAUNCH) {
-            Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
-                    + info.launchedActivity.packageName, 0);
-            info.launchTraceActive = true;
-        }
-    }
-
-    private void stopLaunchTrace(WindowingModeTransitionInfo info) {
-        if (info == null) {
-            return;
-        }
-        if (info.launchTraceActive) {
-            Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
-                    + info.launchedActivity.packageName, 0);
-            info.launchTraceActive = false;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
deleted file mode 100644
index 865c774..0000000
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ /dev/null
@@ -1,3055 +0,0 @@
-/*
- * Copyright (C) 2006 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.server.am;
-
-import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
-import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
-import static android.app.ActivityOptions.ANIM_CUSTOM;
-import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
-import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
-import static android.app.ActivityOptions.ANIM_SCALE_UP;
-import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
-import static android.app.WaitResult.INVALID_DELAY;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.activityTypeToString;
-import static android.content.Intent.ACTION_MAIN;
-import static android.content.Intent.CATEGORY_HOME;
-import static android.content.Intent.CATEGORY_LAUNCHER;
-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_SCREEN_LAYOUT;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
-import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
-import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
-import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
-import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
-import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE;
-import static android.content.pm.ActivityInfo.FLAG_MULTIPROCESS;
-import static android.content.pm.ActivityInfo.FLAG_NO_HISTORY;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
-import static android.content.pm.ActivityInfo.FLAG_STATE_NOT_NEEDED;
-import static android.content.pm.ActivityInfo.FLAG_TURN_SCREEN_ON;
-import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
-import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS;
-import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
-import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
-import static android.content.res.Configuration.EMPTY;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
-import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET;
-import static android.os.Build.VERSION_CODES.HONEYCOMB;
-import static android.os.Build.VERSION_CODES.O;
-import static android.os.Process.SYSTEM_UID;
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
-import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
-import static com.android.server.am.ActivityRecordProto.PROC_ID;
-import static com.android.server.am.ActivityRecordProto.STATE;
-import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
-import static com.android.server.am.ActivityRecordProto.VISIBLE;
-import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.am.ActivityStack.LAUNCH_TICK;
-import static com.android.server.am.ActivityStack.LAUNCH_TICK_MSG;
-import static com.android.server.am.ActivityStack.PAUSE_TIMEOUT_MSG;
-import static com.android.server.am.ActivityStack.STOP_TIMEOUT_MSG;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
-import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
-import static com.android.server.am.TaskPersister.DEBUG;
-import static com.android.server.am.TaskPersister.IMAGE_EXTENSION;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static com.android.server.wm.IdentifierProto.HASH_CODE;
-import static com.android.server.wm.IdentifierProto.TITLE;
-import static com.android.server.wm.IdentifierProto.USER_ID;
-
-import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.END_TAG;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
-
-import android.annotation.NonNull;
-import android.app.ActivityManager.TaskDescription;
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.app.PictureInPictureParams;
-import android.app.ResultInfo;
-import android.app.servertransaction.ActivityConfigurationChangeItem;
-import android.app.servertransaction.ActivityLifecycleItem;
-import android.app.servertransaction.ActivityRelaunchItem;
-import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.ClientTransactionItem;
-import android.app.servertransaction.MoveToDisplayItem;
-import android.app.servertransaction.MultiWindowModeChangeItem;
-import android.app.servertransaction.NewIntentItem;
-import android.app.servertransaction.PauseActivityItem;
-import android.app.servertransaction.PipModeChangeItem;
-import android.app.servertransaction.ResumeActivityItem;
-import android.app.servertransaction.WindowVisibilityItem;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.GraphicBuffer;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.storage.StorageManager;
-import android.service.voice.IVoiceInteractionSession;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.MergedConfiguration;
-import android.util.Slog;
-import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
-import android.view.AppTransitionAnimationSpec;
-import android.view.IAppTransitionAnimationSpecsFuture;
-import android.view.IApplicationToken;
-import android.view.RemoteAnimationDefinition;
-import android.view.WindowManager.LayoutParams;
-
-import com.android.internal.R;
-import com.android.internal.app.ResolverActivity;
-import com.android.internal.content.ReferrerIntent;
-import com.android.internal.util.XmlUtils;
-import com.android.server.AttributeCache;
-import com.android.server.AttributeCache.Entry;
-import com.android.server.am.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot;
-import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.uri.UriPermissionOwner;
-import com.android.server.wm.AppWindowContainerController;
-import com.android.server.wm.AppWindowContainerListener;
-import com.android.server.wm.ConfigurationContainer;
-import com.android.server.wm.TaskWindowContainerController;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * An entry in the history stack, representing an activity.
- */
-final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
-    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
-    private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
-    private static final String TAG_STATES = TAG + POSTFIX_STATES;
-    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
-    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
-    private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
-    // TODO(b/67864419): Remove once recents component is overridden
-    private static final String LEGACY_RECENTS_PACKAGE_NAME = "com.android.systemui.recents";
-
-    private static final boolean SHOW_ACTIVITY_START_TIME = true;
-
-    private static final String ATTR_ID = "id";
-    private static final String TAG_INTENT = "intent";
-    private static final String ATTR_USERID = "user_id";
-    private static final String TAG_PERSISTABLEBUNDLE = "persistable_bundle";
-    private static final String ATTR_LAUNCHEDFROMUID = "launched_from_uid";
-    private static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package";
-    private static final String ATTR_RESOLVEDTYPE = "resolved_type";
-    private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
-    static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
-
-    final ActivityTaskManagerService service; // owner
-    final IApplicationToken.Stub appToken; // window manager token
-    AppWindowContainerController mWindowContainerController;
-    final ActivityInfo info; // all about me
-    // TODO: This is duplicated state already contained in info.applicationInfo - remove
-    ApplicationInfo appInfo; // information about activity's app
-    final int launchedFromPid; // always the pid who started the activity.
-    final int launchedFromUid; // always the uid who started the activity.
-    final String launchedFromPackage; // always the package who started the activity.
-    final int userId;          // Which user is this running for?
-    final Intent intent;    // the original intent that generated us
-    final ComponentName realActivity;  // the intent component, or target of an alias.
-    final String shortComponentName; // the short component name of the intent
-    final String resolvedType; // as per original caller;
-    final String packageName; // the package implementing intent's component
-    final String processName; // process where this component wants to run
-    final String taskAffinity; // as per ActivityInfo.taskAffinity
-    final boolean stateNotNeeded; // As per ActivityInfo.flags
-    boolean fullscreen; // The activity is opaque and fills the entire space of this task.
-    // TODO: See if it possible to combine this with the fullscreen field.
-    final boolean hasWallpaper; // Has a wallpaper window as a background.
-    final boolean noDisplay;  // activity is not displayed?
-    private final boolean componentSpecified;  // did caller specify an explicit component?
-    final boolean rootVoiceInteraction;  // was this the root activity of a voice interaction?
-
-    private CharSequence nonLocalizedLabel;  // the label information from the package mgr.
-    private int labelRes;           // the label information from the package mgr.
-    private int icon;               // resource identifier of activity's icon.
-    private int logo;               // resource identifier of activity's logo.
-    private int theme;              // resource identifier of activity's theme.
-    private int realTheme;          // actual theme resource we will use, never 0.
-    private int windowFlags;        // custom window flags for preview window.
-    private TaskRecord task;        // the task this is in.
-    private long createTime = System.currentTimeMillis();
-    long lastVisibleTime;   // last time this activity became visible
-    long cpuTimeAtResume;   // the cpu time of host process at the time of resuming activity
-    long pauseTime;         // last time we started pausing the activity
-    long launchTickTime;    // base time for launch tick messages
-    // Last configuration reported to the activity in the client process.
-    private MergedConfiguration mLastReportedConfiguration;
-    private int mLastReportedDisplayId;
-    private boolean mLastReportedMultiWindowMode;
-    private boolean mLastReportedPictureInPictureMode;
-    CompatibilityInfo compat;// last used compatibility mode
-    ActivityRecord resultTo; // who started this entry, so will get our reply
-    final String resultWho; // additional identifier for use by resultTo.
-    final int requestCode;  // code given by requester (resultTo)
-    ArrayList<ResultInfo> results; // pending ActivityResult objs we have received
-    HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
-    ArrayList<ReferrerIntent> newIntents; // any pending new intents for single-top mode
-    ActivityOptions pendingOptions; // most recently given options
-    ActivityOptions returningOptions; // options that are coming back via convertToTranslucent
-    AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
-    ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections.
-    UriPermissionOwner uriPermissions; // current special URI access perms.
-    WindowProcessController app;      // if non-null, hosting application
-    private ActivityState mState;    // current state we are in
-    Bundle  icicle;         // last saved activity state
-    PersistableBundle persistentState; // last persistently saved activity state
-    // TODO: See if this is still needed.
-    boolean frontOfTask;    // is this the root activity of its task?
-    boolean launchFailed;   // set if a launched failed, to abort on 2nd try
-    boolean haveState;      // have we gotten the last activity state?
-    boolean stopped;        // is activity pause finished?
-    boolean delayedResume;  // not yet resumed because of stopped app switches?
-    boolean finishing;      // activity in pending finish list?
-    boolean deferRelaunchUntilPaused;   // relaunch of activity is being deferred until pause is
-                                        // completed
-    boolean preserveWindowOnDeferredRelaunch; // activity windows are preserved on deferred relaunch
-    int configChangeFlags;  // which config values have changed
-    private boolean keysPaused;     // has key dispatching been paused for it?
-    int launchMode;         // the launch mode activity attribute.
-    int lockTaskLaunchMode; // the lockTaskMode manifest attribute, subject to override
-    boolean visible;        // does this activity's window need to be shown?
-    boolean visibleIgnoringKeyguard; // is this activity visible, ignoring the fact that Keyguard
-                                     // might hide this activity?
-    private boolean mDeferHidingClient; // If true we told WM to defer reporting to the client
-                                        // process that it is hidden.
-    boolean sleeping;       // have we told the activity to sleep?
-    boolean nowVisible;     // is this activity's window visible?
-    boolean mClientVisibilityDeferred;// was the visibility change message to client deferred?
-    boolean idle;           // has the activity gone idle?
-    boolean hasBeenLaunched;// has this activity ever been launched?
-    boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
-    boolean immersive;      // immersive mode (don't interrupt if possible)
-    boolean forceNewConfig; // force re-create with new config next time
-    boolean supportsEnterPipOnTaskSwitch;  // This flag is set by the system to indicate that the
-        // activity can enter picture in picture while pausing (only when switching to another task)
-    PictureInPictureParams pictureInPictureArgs = new PictureInPictureParams.Builder().build();
-        // The PiP params used when deferring the entering of picture-in-picture.
-    int launchCount;        // count of launches since last state
-    long lastLaunchTime;    // time of last launch of this activity
-    ComponentName requestedVrComponent; // the requested component for handling VR mode.
-
-    String stringName;      // for caching of toString().
-
-    private boolean inHistory;  // are we in the history stack?
-    final ActivityStackSupervisor mStackSupervisor;
-
-    static final int STARTING_WINDOW_NOT_SHOWN = 0;
-    static final int STARTING_WINDOW_SHOWN = 1;
-    static final int STARTING_WINDOW_REMOVED = 2;
-    int mStartingWindowState = STARTING_WINDOW_NOT_SHOWN;
-    boolean mTaskOverlay = false; // Task is always on-top of other activities in the task.
-
-    // Marking the reason why this activity is being relaunched. Mainly used to track that this
-    // activity is being relaunched to fulfill a resize request due to compatibility issues, e.g. in
-    // pre-NYC apps that don't have a sense of being resized.
-    int mRelaunchReason = RELAUNCH_REASON_NONE;
-
-    TaskDescription taskDescription; // the recents information for this activity
-    boolean mLaunchTaskBehind; // this activity is actively being launched with
-        // ActivityOptions.setLaunchTaskBehind, will be cleared once launch is completed.
-
-    // These configurations are collected from application's resources based on size-sensitive
-    // qualifiers. For example, layout-w800dp will be added to mHorizontalSizeConfigurations as 800
-    // and drawable-sw400dp will be added to both as 400.
-    private int[] mVerticalSizeConfigurations;
-    private int[] mHorizontalSizeConfigurations;
-    private int[] mSmallestSizeConfigurations;
-
-    boolean pendingVoiceInteractionStart;   // Waiting for activity-invoked voice session
-    IVoiceInteractionSession voiceSession;  // Voice interaction session for this activity
-
-    // A hint to override the window specified rotation animation, or -1
-    // to use the window specified value. We use this so that
-    // we can select the right animation in the cases of starting
-    // windows, where the app hasn't had time to set a value
-    // on the window.
-    int mRotationAnimationHint = -1;
-
-    private boolean mShowWhenLocked;
-    private boolean mTurnScreenOn;
-
-    /**
-     * Temp configs used in {@link #ensureActivityConfiguration(int, boolean)}
-     */
-    private final Configuration mTmpConfig = new Configuration();
-    private final Rect mTmpBounds = new Rect();
-
-    private static String startingWindowStateToString(int state) {
-        switch (state) {
-            case STARTING_WINDOW_NOT_SHOWN:
-                return "STARTING_WINDOW_NOT_SHOWN";
-            case STARTING_WINDOW_SHOWN:
-                return "STARTING_WINDOW_SHOWN";
-            case STARTING_WINDOW_REMOVED:
-                return "STARTING_WINDOW_REMOVED";
-            default:
-                return "unknown state=" + state;
-        }
-    }
-
-    void dump(PrintWriter pw, String prefix) {
-        final long now = SystemClock.uptimeMillis();
-        pw.print(prefix); pw.print("packageName="); pw.print(packageName);
-                pw.print(" processName="); pw.println(processName);
-        pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
-                pw.print(" launchedFromPackage="); pw.print(launchedFromPackage);
-                pw.print(" userId="); pw.println(userId);
-        pw.print(prefix); pw.print("app="); pw.println(app);
-        pw.print(prefix); pw.println(intent.toInsecureStringWithClip());
-        pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask);
-                pw.print(" task="); pw.println(task);
-        pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity);
-        pw.print(prefix); pw.print("realActivity=");
-                pw.println(realActivity.flattenToShortString());
-        if (appInfo != null) {
-            pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
-            if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
-                pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
-            }
-            pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
-            if (appInfo.splitSourceDirs != null) {
-                pw.print(prefix); pw.print("splitDir=");
-                        pw.println(Arrays.toString(appInfo.splitSourceDirs));
-            }
-        }
-        pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
-                pw.print(" componentSpecified="); pw.print(componentSpecified);
-                pw.print(" mActivityType="); pw.println(
-                        activityTypeToString(getActivityType()));
-        if (rootVoiceInteraction) {
-            pw.print(prefix); pw.print("rootVoiceInteraction="); pw.println(rootVoiceInteraction);
-        }
-        pw.print(prefix); pw.print("compat="); pw.print(compat);
-                pw.print(" labelRes=0x"); pw.print(Integer.toHexString(labelRes));
-                pw.print(" icon=0x"); pw.print(Integer.toHexString(icon));
-                pw.print(" theme=0x"); pw.println(Integer.toHexString(theme));
-        pw.println(prefix + "mLastReportedConfigurations:");
-        mLastReportedConfiguration.dump(pw, prefix + " ");
-
-        pw.print(prefix); pw.print("CurrentConfiguration="); pw.println(getConfiguration());
-        if (!getOverrideConfiguration().equals(EMPTY)) {
-            pw.println(prefix + "OverrideConfiguration=" + getOverrideConfiguration());
-        }
-        if (!matchParentBounds()) {
-            pw.println(prefix + "bounds=" + getBounds());
-        }
-        if (resultTo != null || resultWho != null) {
-            pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
-                    pw.print(" resultWho="); pw.print(resultWho);
-                    pw.print(" resultCode="); pw.println(requestCode);
-        }
-        if (taskDescription != null) {
-            final String iconFilename = taskDescription.getIconFilename();
-            if (iconFilename != null || taskDescription.getLabel() != null ||
-                    taskDescription.getPrimaryColor() != 0) {
-                pw.print(prefix); pw.print("taskDescription:");
-                        pw.print(" label=\""); pw.print(taskDescription.getLabel());
-                                pw.print("\"");
-                        pw.print(" icon="); pw.print(taskDescription.getInMemoryIcon() != null
-                                ? taskDescription.getInMemoryIcon().getByteCount() + " bytes"
-                                : "null");
-                        pw.print(" iconResource="); pw.print(taskDescription.getIconResource());
-                        pw.print(" iconFilename="); pw.print(taskDescription.getIconFilename());
-                        pw.print(" primaryColor=");
-                        pw.println(Integer.toHexString(taskDescription.getPrimaryColor()));
-                        pw.print(prefix + " backgroundColor=");
-                        pw.println(Integer.toHexString(taskDescription.getBackgroundColor()));
-                        pw.print(prefix + " statusBarColor=");
-                        pw.println(Integer.toHexString(taskDescription.getStatusBarColor()));
-                        pw.print(prefix + " navigationBarColor=");
-                        pw.println(Integer.toHexString(taskDescription.getNavigationBarColor()));
-            }
-        }
-        if (results != null) {
-            pw.print(prefix); pw.print("results="); pw.println(results);
-        }
-        if (pendingResults != null && pendingResults.size() > 0) {
-            pw.print(prefix); pw.println("Pending Results:");
-            for (WeakReference<PendingIntentRecord> wpir : pendingResults) {
-                PendingIntentRecord pir = wpir != null ? wpir.get() : null;
-                pw.print(prefix); pw.print("  - ");
-                if (pir == null) {
-                    pw.println("null");
-                } else {
-                    pw.println(pir);
-                    pir.dump(pw, prefix + "    ");
-                }
-            }
-        }
-        if (newIntents != null && newIntents.size() > 0) {
-            pw.print(prefix); pw.println("Pending New Intents:");
-            for (int i=0; i<newIntents.size(); i++) {
-                Intent intent = newIntents.get(i);
-                pw.print(prefix); pw.print("  - ");
-                if (intent == null) {
-                    pw.println("null");
-                } else {
-                    pw.println(intent.toShortString(false, true, false, true));
-                }
-            }
-        }
-        if (pendingOptions != null) {
-            pw.print(prefix); pw.print("pendingOptions="); pw.println(pendingOptions);
-        }
-        if (appTimeTracker != null) {
-            appTimeTracker.dumpWithHeader(pw, prefix, false);
-        }
-        if (uriPermissions != null) {
-            uriPermissions.dump(pw, prefix);
-        }
-        pw.print(prefix); pw.print("launchFailed="); pw.print(launchFailed);
-                pw.print(" launchCount="); pw.print(launchCount);
-                pw.print(" lastLaunchTime=");
-                if (lastLaunchTime == 0) pw.print("0");
-                else TimeUtils.formatDuration(lastLaunchTime, now, pw);
-                pw.println();
-        pw.print(prefix); pw.print("haveState="); pw.print(haveState);
-                pw.print(" icicle="); pw.println(icicle);
-        pw.print(prefix); pw.print("state="); pw.print(mState);
-                pw.print(" stopped="); pw.print(stopped);
-                pw.print(" delayedResume="); pw.print(delayedResume);
-                pw.print(" finishing="); pw.println(finishing);
-        pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
-                pw.print(" inHistory="); pw.print(inHistory);
-                pw.print(" visible="); pw.print(visible);
-                pw.print(" sleeping="); pw.print(sleeping);
-                pw.print(" idle="); pw.print(idle);
-                pw.print(" mStartingWindowState=");
-                pw.println(startingWindowStateToString(mStartingWindowState));
-        pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen);
-                pw.print(" noDisplay="); pw.print(noDisplay);
-                pw.print(" immersive="); pw.print(immersive);
-                pw.print(" launchMode="); pw.println(launchMode);
-        pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
-                pw.print(" forceNewConfig="); pw.println(forceNewConfig);
-        pw.print(prefix); pw.print("mActivityType=");
-                pw.println(activityTypeToString(getActivityType()));
-        if (requestedVrComponent != null) {
-            pw.print(prefix);
-            pw.print("requestedVrComponent=");
-            pw.println(requestedVrComponent);
-        }
-        final boolean waitingVisible =
-                mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this);
-        if (lastVisibleTime != 0 || waitingVisible || nowVisible) {
-            pw.print(prefix); pw.print("waitingVisible="); pw.print(waitingVisible);
-                    pw.print(" nowVisible="); pw.print(nowVisible);
-                    pw.print(" lastVisibleTime=");
-                    if (lastVisibleTime == 0) pw.print("0");
-                    else TimeUtils.formatDuration(lastVisibleTime, now, pw);
-                    pw.println();
-        }
-        if (mDeferHidingClient) {
-            pw.println(prefix + "mDeferHidingClient=" + mDeferHidingClient);
-        }
-        if (deferRelaunchUntilPaused || configChangeFlags != 0) {
-            pw.print(prefix); pw.print("deferRelaunchUntilPaused="); pw.print(deferRelaunchUntilPaused);
-                    pw.print(" configChangeFlags=");
-                    pw.println(Integer.toHexString(configChangeFlags));
-        }
-        if (mServiceConnectionsHolder != null) {
-            pw.print(prefix); pw.print("connections="); pw.println(mServiceConnectionsHolder);
-        }
-        if (info != null) {
-            pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
-            pw.println(prefix + "mLastReportedMultiWindowMode=" + mLastReportedMultiWindowMode
-                    + " mLastReportedPictureInPictureMode=" + mLastReportedPictureInPictureMode);
-            if (info.supportsPictureInPicture()) {
-                pw.println(prefix + "supportsPictureInPicture=" + info.supportsPictureInPicture());
-                pw.println(prefix + "supportsEnterPipOnTaskSwitch: "
-                        + supportsEnterPipOnTaskSwitch);
-            }
-            if (info.maxAspectRatio != 0) {
-                pw.println(prefix + "maxAspectRatio=" + info.maxAspectRatio);
-            }
-        }
-    }
-
-    void updateApplicationInfo(ApplicationInfo aInfo) {
-        appInfo = aInfo;
-        info.applicationInfo = aInfo;
-    }
-
-    private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) {
-        return crossesSizeThreshold(mHorizontalSizeConfigurations, firstDp, secondDp);
-    }
-
-    private boolean crossesVerticalSizeThreshold(int firstDp, int secondDp) {
-        return crossesSizeThreshold(mVerticalSizeConfigurations, firstDp, secondDp);
-    }
-
-    private boolean crossesSmallestSizeThreshold(int firstDp, int secondDp) {
-        return crossesSizeThreshold(mSmallestSizeConfigurations, firstDp, secondDp);
-    }
-
-    /**
-     * The purpose of this method is to decide whether the activity needs to be relaunched upon
-     * changing its size. In most cases the activities don't need to be relaunched, if the resize
-     * is small, all the activity content has to do is relayout itself within new bounds. There are
-     * cases however, where the activity's content would be completely changed in the new size and
-     * the full relaunch is required.
-     *
-     * The activity will report to us vertical and horizontal thresholds after which a relaunch is
-     * required. These thresholds are collected from the application resource qualifiers. For
-     * example, if application has layout-w600dp resource directory, then it needs a relaunch when
-     * we resize from width of 650dp to 550dp, as it crosses the 600dp threshold. However, if
-     * it resizes width from 620dp to 700dp, it won't be relaunched as it stays on the same side
-     * of the threshold.
-     */
-    private static boolean crossesSizeThreshold(int[] thresholds, int firstDp,
-            int secondDp) {
-        if (thresholds == null) {
-            return false;
-        }
-        for (int i = thresholds.length - 1; i >= 0; i--) {
-            final int threshold = thresholds[i];
-            if ((firstDp < threshold && secondDp >= threshold)
-                    || (firstDp >= threshold && secondDp < threshold)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    void setSizeConfigurations(int[] horizontalSizeConfiguration,
-            int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
-        mHorizontalSizeConfigurations = horizontalSizeConfiguration;
-        mVerticalSizeConfigurations = verticalSizeConfigurations;
-        mSmallestSizeConfigurations = smallestSizeConfigurations;
-    }
-
-    private void scheduleActivityMovedToDisplay(int displayId, Configuration config) {
-        if (!attachedToProcess()) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.w(TAG,
-                    "Can't report activity moved to display - client not running, activityRecord="
-                            + this + ", displayId=" + displayId);
-            return;
-        }
-        try {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                    "Reporting activity moved to display" + ", activityRecord=" + this
-                            + ", displayId=" + displayId + ", config=" + config);
-
-            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                    MoveToDisplayItem.obtain(displayId, config));
-        } catch (RemoteException e) {
-            // If process died, whatever.
-        }
-    }
-
-    private void scheduleConfigurationChanged(Configuration config) {
-        if (!attachedToProcess()) {
-            if (DEBUG_CONFIGURATION) Slog.w(TAG,
-                    "Can't report activity configuration update - client not running"
-                            + ", activityRecord=" + this);
-            return;
-        }
-        try {
-            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
-                    + config);
-
-            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                    ActivityConfigurationChangeItem.obtain(config));
-        } catch (RemoteException e) {
-            // If process died, whatever.
-        }
-    }
-
-    void updateMultiWindowMode() {
-        if (task == null || task.getStack() == null || !attachedToProcess()) {
-            return;
-        }
-
-        if (task.getStack().deferScheduleMultiWindowModeChanged()) {
-            // Don't do anything if we are currently deferring multi-window mode change.
-            return;
-        }
-
-        // An activity is considered to be in multi-window mode if its task isn't fullscreen.
-        final boolean inMultiWindowMode = inMultiWindowMode();
-        if (inMultiWindowMode != mLastReportedMultiWindowMode) {
-            mLastReportedMultiWindowMode = inMultiWindowMode;
-            scheduleMultiWindowModeChanged(getConfiguration());
-        }
-    }
-
-    private void scheduleMultiWindowModeChanged(Configuration overrideConfig) {
-        try {
-            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                    MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode, overrideConfig));
-        } catch (Exception e) {
-            // If process died, I don't care.
-        }
-    }
-
-    void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) {
-        if (task == null || task.getStack() == null || !attachedToProcess()) {
-            return;
-        }
-
-        final boolean inPictureInPictureMode = inPinnedWindowingMode() && targetStackBounds != null;
-        if (inPictureInPictureMode != mLastReportedPictureInPictureMode || forceUpdate) {
-            // Picture-in-picture mode changes also trigger a multi-window mode change as well, so
-            // update that here in order. Set the last reported MW state to the same as the PiP
-            // state since we haven't yet actually resized the task (these callbacks need to
-            // preceed the configuration change from the resiez.
-            // TODO(110009072): Once we move these callbacks to the client, remove all logic related
-            // to forcing the update of the picture-in-picture mode as a part of the PiP animation.
-            mLastReportedPictureInPictureMode = inPictureInPictureMode;
-            mLastReportedMultiWindowMode = inPictureInPictureMode;
-            final Configuration newConfig = task.computeNewOverrideConfigurationForBounds(
-                    targetStackBounds, null);
-            schedulePictureInPictureModeChanged(newConfig);
-            scheduleMultiWindowModeChanged(newConfig);
-        }
-    }
-
-    private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
-        try {
-            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                    PipModeChangeItem.obtain(mLastReportedPictureInPictureMode,
-                            overrideConfig));
-        } catch (Exception e) {
-            // If process died, no one cares.
-        }
-    }
-
-    @Override
-    protected int getChildCount() {
-        // {@link ActivityRecord} is a leaf node and has no children.
-        return 0;
-    }
-
-    @Override
-    protected ConfigurationContainer getChildAt(int index) {
-        return null;
-    }
-
-    @Override
-    protected ConfigurationContainer getParent() {
-        return getTask();
-    }
-
-    TaskRecord getTask() {
-        return task;
-    }
-
-    /**
-     * Sets reference to the {@link TaskRecord} the {@link ActivityRecord} will treat as its parent.
-     * Note that this does not actually add the {@link ActivityRecord} as a {@link TaskRecord}
-     * children. However, this method will clean up references to this {@link ActivityRecord} in
-     * {@link ActivityStack}.
-     * @param task The new parent {@link TaskRecord}.
-     */
-    void setTask(TaskRecord task) {
-        setTask(task /* task */, false /* reparenting */);
-    }
-
-    /**
-     * This method should only be called by {@link TaskRecord#removeActivity(ActivityRecord)}.
-     * @param task          The new parent task.
-     * @param reparenting   Whether we're in the middle of reparenting.
-     */
-    void setTask(TaskRecord task, boolean reparenting) {
-        // Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}.
-        if (task != null && task == getTask()) {
-            return;
-        }
-
-        final ActivityStack oldStack = getStack();
-        final ActivityStack newStack = task != null ? task.getStack() : null;
-
-        // Inform old stack (if present) of activity removal and new stack (if set) of activity
-        // addition.
-        if (oldStack != newStack) {
-            if (!reparenting && oldStack != null) {
-                oldStack.onActivityRemovedFromStack(this);
-            }
-
-            if (newStack != null) {
-                newStack.onActivityAddedToStack(this);
-            }
-        }
-
-        this.task = task;
-
-        if (!reparenting) {
-            onParentChanged();
-        }
-    }
-
-    /**
-     * See {@link AppWindowContainerController#setWillCloseOrEnterPip(boolean)}
-     */
-    void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
-        getWindowContainerController().setWillCloseOrEnterPip(willCloseOrEnterPip);
-    }
-
-    static class Token extends IApplicationToken.Stub {
-        private final WeakReference<ActivityRecord> weakActivity;
-        private final String name;
-
-        Token(ActivityRecord activity, Intent intent) {
-            weakActivity = new WeakReference<>(activity);
-            name = intent.getComponent().flattenToShortString();
-        }
-
-        private static ActivityRecord tokenToActivityRecordLocked(Token token) {
-            if (token == null) {
-                return null;
-            }
-            ActivityRecord r = token.weakActivity.get();
-            if (r == null || r.getStack() == null) {
-                return null;
-            }
-            return r;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder(128);
-            sb.append("Token{");
-            sb.append(Integer.toHexString(System.identityHashCode(this)));
-            sb.append(' ');
-            sb.append(weakActivity.get());
-            sb.append('}');
-            return sb.toString();
-        }
-
-        @Override
-        public String getName() {
-            return name;
-        }
-    }
-
-    static ActivityRecord forTokenLocked(IBinder token) {
-        try {
-            return Token.tokenToActivityRecordLocked((Token)token);
-        } catch (ClassCastException e) {
-            Slog.w(TAG, "Bad activity token: " + token, e);
-            return null;
-        }
-    }
-
-    boolean isResolverActivity() {
-        return ResolverActivity.class.getName().equals(realActivity.getClassName());
-    }
-
-    boolean isResolverOrChildActivity() {
-        if (!"android".equals(packageName)) {
-            return false;
-        }
-        try {
-            return ResolverActivity.class.isAssignableFrom(
-                    Object.class.getClassLoader().loadClass(realActivity.getClassName()));
-        } catch (ClassNotFoundException e) {
-            return false;
-        }
-    }
-
-    ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
-            int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage, Intent _intent,
-            String _resolvedType, ActivityInfo aInfo, Configuration _configuration,
-            ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified,
-            boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
-            ActivityOptions options, ActivityRecord sourceRecord) {
-        service = _service;
-        appToken = new Token(this, _intent);
-        info = aInfo;
-        launchedFromPid = _launchedFromPid;
-        launchedFromUid = _launchedFromUid;
-        launchedFromPackage = _launchedFromPackage;
-        userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
-        intent = _intent;
-        shortComponentName = _intent.getComponent().flattenToShortString();
-        resolvedType = _resolvedType;
-        componentSpecified = _componentSpecified;
-        rootVoiceInteraction = _rootVoiceInteraction;
-        mLastReportedConfiguration = new MergedConfiguration(_configuration);
-        resultTo = _resultTo;
-        resultWho = _resultWho;
-        requestCode = _reqCode;
-        setState(INITIALIZING, "ActivityRecord ctor");
-        frontOfTask = false;
-        launchFailed = false;
-        stopped = false;
-        delayedResume = false;
-        finishing = false;
-        deferRelaunchUntilPaused = false;
-        keysPaused = false;
-        inHistory = false;
-        visible = false;
-        nowVisible = false;
-        idle = false;
-        hasBeenLaunched = false;
-        mStackSupervisor = supervisor;
-
-        // This starts out true, since the initial state of an activity is that we have everything,
-        // and we shouldn't never consider it lacking in state to be removed if it dies.
-        haveState = true;
-
-        // If the class name in the intent doesn't match that of the target, this is
-        // probably an alias. We have to create a new ComponentName object to keep track
-        // of the real activity name, so that FLAG_ACTIVITY_CLEAR_TOP is handled properly.
-        if (aInfo.targetActivity == null
-                || (aInfo.targetActivity.equals(_intent.getComponent().getClassName())
-                && (aInfo.launchMode == LAUNCH_MULTIPLE
-                || aInfo.launchMode == LAUNCH_SINGLE_TOP))) {
-            realActivity = _intent.getComponent();
-        } else {
-            realActivity = new ComponentName(aInfo.packageName, aInfo.targetActivity);
-        }
-        taskAffinity = aInfo.taskAffinity;
-        stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
-        appInfo = aInfo.applicationInfo;
-        nonLocalizedLabel = aInfo.nonLocalizedLabel;
-        labelRes = aInfo.labelRes;
-        if (nonLocalizedLabel == null && labelRes == 0) {
-            ApplicationInfo app = aInfo.applicationInfo;
-            nonLocalizedLabel = app.nonLocalizedLabel;
-            labelRes = app.labelRes;
-        }
-        icon = aInfo.getIconResource();
-        logo = aInfo.getLogoResource();
-        theme = aInfo.getThemeResource();
-        realTheme = theme;
-        if (realTheme == 0) {
-            realTheme = aInfo.applicationInfo.targetSdkVersion < HONEYCOMB
-                    ? android.R.style.Theme : android.R.style.Theme_Holo;
-        }
-        if ((aInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
-            windowFlags |= LayoutParams.FLAG_HARDWARE_ACCELERATED;
-        }
-        if ((aInfo.flags & FLAG_MULTIPROCESS) != 0 && _caller != null
-                && (aInfo.applicationInfo.uid == SYSTEM_UID
-                    || aInfo.applicationInfo.uid == _caller.mInfo.uid)) {
-            processName = _caller.mName;
-        } else {
-            processName = aInfo.processName;
-        }
-
-        if ((aInfo.flags & FLAG_EXCLUDE_FROM_RECENTS) != 0) {
-            intent.addFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-        }
-
-        packageName = aInfo.applicationInfo.packageName;
-        launchMode = aInfo.launchMode;
-
-        Entry ent = AttributeCache.instance().get(packageName,
-                realTheme, com.android.internal.R.styleable.Window, userId);
-
-        if (ent != null) {
-            fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array);
-            hasWallpaper = ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
-            noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
-        } else {
-            hasWallpaper = false;
-            noDisplay = false;
-        }
-
-        setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);
-
-        immersive = (aInfo.flags & FLAG_IMMERSIVE) != 0;
-
-        requestedVrComponent = (aInfo.requestedVrComponent == null) ?
-                null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
-
-        mShowWhenLocked = (aInfo.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
-        mTurnScreenOn = (aInfo.flags & FLAG_TURN_SCREEN_ON) != 0;
-
-        mRotationAnimationHint = aInfo.rotationAnimation;
-        lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
-        if (appInfo.isPrivilegedApp() && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
-                || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
-            lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
-        }
-
-        if (options != null) {
-            pendingOptions = options;
-            mLaunchTaskBehind = options.getLaunchTaskBehind();
-
-            final int rotationAnimation = pendingOptions.getRotationAnimationHint();
-            // Only override manifest supplied option if set.
-            if (rotationAnimation >= 0) {
-                mRotationAnimationHint = rotationAnimation;
-            }
-            final PendingIntent usageReport = pendingOptions.getUsageTimeReport();
-            if (usageReport != null) {
-                appTimeTracker = new AppTimeTracker(usageReport);
-            }
-            final boolean useLockTask = pendingOptions.getLockTaskMode();
-            if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
-                lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
-            }
-        }
-    }
-
-    void setProcess(WindowProcessController proc) {
-        app = proc;
-        final ActivityRecord root = task != null ? task.getRootActivity() : null;
-        if (root == this) {
-            task.setRootProcess(proc);
-        }
-    }
-
-    boolean hasProcess() {
-        return app != null;
-    }
-
-    boolean attachedToProcess() {
-        return hasProcess() && app.hasThread();
-    }
-
-    AppWindowContainerController getWindowContainerController() {
-        return mWindowContainerController;
-    }
-
-    void createWindowContainer() {
-        if (mWindowContainerController != null) {
-            throw new IllegalArgumentException("Window container=" + mWindowContainerController
-                    + " already created for r=" + this);
-        }
-
-        inHistory = true;
-
-        final TaskWindowContainerController taskController = task.getWindowContainerController();
-
-        // TODO(b/36505427): Maybe this call should be moved inside updateOverrideConfiguration()
-        task.updateOverrideConfigurationFromLaunchBounds();
-        // Make sure override configuration is up-to-date before using to create window controller.
-        updateOverrideConfiguration();
-
-        mWindowContainerController = new AppWindowContainerController(taskController, appToken,
-                this, Integer.MAX_VALUE /* add on top */, info.screenOrientation, fullscreen,
-                (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
-                task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
-                appInfo.targetSdkVersion, mRotationAnimationHint,
-                ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
-
-        task.addActivityToTop(this);
-
-        // When an activity is started directly into a split-screen fullscreen stack, we need to
-        // update the initial multi-window modes so that the callbacks are scheduled correctly when
-        // the user leaves that mode.
-        mLastReportedMultiWindowMode = inMultiWindowMode();
-        mLastReportedPictureInPictureMode = inPinnedWindowingMode();
-    }
-
-    void removeWindowContainer() {
-        // Do not try to remove a window container if we have already removed it.
-        if (mWindowContainerController == null) {
-            return;
-        }
-
-        // Resume key dispatching if it is currently paused before we remove the container.
-        resumeKeyDispatchingLocked();
-
-        mWindowContainerController.removeContainer(getDisplayId());
-        mWindowContainerController = null;
-    }
-
-    /**
-     * Reparents this activity into {@param newTask} at the provided {@param position}.  The caller
-     * should ensure that the {@param newTask} is not already the parent of this activity.
-     */
-    void reparent(TaskRecord newTask, int position, String reason) {
-        final TaskRecord prevTask = task;
-        if (prevTask == newTask) {
-            throw new IllegalArgumentException(reason + ": task=" + newTask
-                    + " is already the parent of r=" + this);
-        }
-
-        // TODO: Ensure that we do not directly reparent activities across stacks, as that may leave
-        //       the stacks in strange states. For now, we should use Task.reparent() to ensure that
-        //       the stack is left in an OK state.
-        if (prevTask != null && newTask != null && prevTask.getStack() != newTask.getStack()) {
-            throw new IllegalArgumentException(reason + ": task=" + newTask
-                    + " is in a different stack (" + newTask.getStackId() + ") than the parent of"
-                    + " r=" + this + " (" + prevTask.getStackId() + ")");
-        }
-
-        // Must reparent first in window manager
-        mWindowContainerController.reparent(newTask.getWindowContainerController(), position);
-
-        // Reparenting prevents informing the parent stack of activity removal in the case that
-        // the new stack has the same parent. we must manually signal here if this is not the case.
-        final ActivityStack prevStack = prevTask.getStack();
-
-        if (prevStack != newTask.getStack()) {
-            prevStack.onActivityRemovedFromStack(this);
-        }
-        // Remove the activity from the old task and add it to the new task.
-        prevTask.removeActivity(this, true /* reparenting */);
-
-        newTask.addActivityAtIndex(position, this);
-    }
-
-    private boolean isHomeIntent(Intent intent) {
-        return ACTION_MAIN.equals(intent.getAction())
-                && intent.hasCategory(CATEGORY_HOME)
-                && intent.getCategories().size() == 1
-                && intent.getData() == null
-                && intent.getType() == null;
-    }
-
-    static boolean isMainIntent(Intent intent) {
-        return ACTION_MAIN.equals(intent.getAction())
-                && intent.hasCategory(CATEGORY_LAUNCHER)
-                && intent.getCategories().size() == 1
-                && intent.getData() == null
-                && intent.getType() == null;
-    }
-
-    private boolean canLaunchHomeActivity(int uid, ActivityRecord sourceRecord) {
-        if (uid == Process.myUid() || uid == 0) {
-            // System process can launch home activity.
-            return true;
-        }
-        // Allow the recents component to launch the home activity.
-        final RecentTasks recentTasks = mStackSupervisor.mService.getRecentTasks();
-        if (recentTasks != null && recentTasks.isCallerRecents(uid)) {
-            return true;
-        }
-        // Resolver activity can launch home activity.
-        return sourceRecord != null && sourceRecord.isResolverActivity();
-    }
-
-    /**
-     * @return whether the given package name can launch an assist activity.
-     */
-    private boolean canLaunchAssistActivity(String packageName) {
-        final ComponentName assistComponent =
-                service.mActiveVoiceInteractionServiceComponent;
-        if (assistComponent != null) {
-            return assistComponent.getPackageName().equals(packageName);
-        }
-        return false;
-    }
-
-    private void setActivityType(boolean componentSpecified, int launchedFromUid, Intent intent,
-            ActivityOptions options, ActivityRecord sourceRecord) {
-        int activityType = ACTIVITY_TYPE_UNDEFINED;
-        if ((!componentSpecified || canLaunchHomeActivity(launchedFromUid, sourceRecord))
-                && isHomeIntent(intent) && !isResolverActivity()) {
-            // This sure looks like a home activity!
-            activityType = ACTIVITY_TYPE_HOME;
-
-            if (info.resizeMode == RESIZE_MODE_FORCE_RESIZEABLE
-                    || info.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
-                // We only allow home activities to be resizeable if they explicitly requested it.
-                info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
-            }
-        } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME)
-                || service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
-            activityType = ACTIVITY_TYPE_RECENTS;
-        } else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
-                && canLaunchAssistActivity(launchedFromPackage)) {
-            activityType = ACTIVITY_TYPE_ASSISTANT;
-        }
-        setActivityType(activityType);
-    }
-
-    void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) {
-        if (launchMode != LAUNCH_SINGLE_INSTANCE && launchMode != LAUNCH_SINGLE_TASK) {
-            task.setTaskToAffiliateWith(taskToAffiliateWith);
-        }
-    }
-
-    /**
-     * @return Stack value from current task, null if there is no task.
-     */
-    <T extends ActivityStack> T getStack() {
-        return task != null ? (T) task.getStack() : null;
-    }
-
-    int getStackId() {
-        return getStack() != null ? getStack().mStackId : INVALID_STACK_ID;
-    }
-
-    ActivityDisplay getDisplay() {
-        final ActivityStack stack = getStack();
-        return stack != null ? stack.getDisplay() : null;
-    }
-
-    boolean changeWindowTranslucency(boolean toOpaque) {
-        if (fullscreen == toOpaque) {
-            return false;
-        }
-
-        // Keep track of the number of fullscreen activities in this task.
-        task.numFullscreen += toOpaque ? +1 : -1;
-
-        fullscreen = toOpaque;
-        return true;
-    }
-
-    void takeFromHistory() {
-        if (inHistory) {
-            inHistory = false;
-            if (task != null && !finishing) {
-                task = null;
-            }
-            clearOptionsLocked();
-        }
-    }
-
-    boolean isInHistory() {
-        return inHistory;
-    }
-
-    boolean isInStackLocked() {
-        final ActivityStack stack = getStack();
-        return stack != null && stack.isInStackLocked(this) != null;
-    }
-
-    boolean isPersistable() {
-        return (info.persistableMode == PERSIST_ROOT_ONLY ||
-                info.persistableMode == PERSIST_ACROSS_REBOOTS) &&
-                (intent == null || (intent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0);
-    }
-
-    boolean isFocusable() {
-        return mStackSupervisor.isFocusable(this, isAlwaysFocusable());
-    }
-
-    boolean isResizeable() {
-        return ActivityInfo.isResizeableMode(info.resizeMode) || info.supportsPictureInPicture();
-    }
-
-    /**
-     * @return whether this activity is non-resizeable or forced to be resizeable
-     */
-    boolean isNonResizableOrForcedResizable() {
-        return info.resizeMode != RESIZE_MODE_RESIZEABLE
-                && info.resizeMode != RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-    }
-
-    /**
-     * @return whether this activity supports PiP multi-window and can be put in the pinned stack.
-     */
-    boolean supportsPictureInPicture() {
-        return service.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined()
-                && info.supportsPictureInPicture();
-    }
-
-    /**
-     * @return whether this activity supports split-screen multi-window and can be put in the docked
-     *         stack.
-     */
-    @Override
-    public boolean supportsSplitScreenWindowingMode() {
-        // An activity can not be docked even if it is considered resizeable because it only
-        // supports picture-in-picture mode but has a non-resizeable resizeMode
-        return super.supportsSplitScreenWindowingMode()
-                && service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
-    }
-
-    /**
-     * @return whether this activity supports freeform multi-window and can be put in the freeform
-     *         stack.
-     */
-    boolean supportsFreeform() {
-        return service.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
-    }
-
-    /**
-     * @return whether this activity supports non-PiP multi-window.
-     */
-    private boolean supportsResizeableMultiWindow() {
-        return service.mSupportsMultiWindow && !isActivityTypeHome()
-                && (ActivityInfo.isResizeableMode(info.resizeMode)
-                        || service.mForceResizableActivities);
-    }
-
-    /**
-     * Check whether this activity can be launched on the specified display.
-     *
-     * @param displayId Target display id.
-     * @return {@code true} if either it is the default display or this activity can be put on a
-     *         secondary screen.
-     */
-    boolean canBeLaunchedOnDisplay(int displayId) {
-        return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
-                launchedFromUid, info);
-    }
-
-    /**
-     * @param beforeStopping Whether this check is for an auto-enter-pip operation, that is to say
-     *         the activity has requested to enter PiP when it would otherwise be stopped.
-     *
-     * @return whether this activity is currently allowed to enter PIP.
-     */
-    boolean checkEnterPictureInPictureState(String caller, boolean beforeStopping) {
-        if (!supportsPictureInPicture()) {
-            return false;
-        }
-
-        // Check app-ops and see if PiP is supported for this package
-        if (!checkEnterPictureInPictureAppOpsState()) {
-            return false;
-        }
-
-        // Check to see if we are in VR mode, and disallow PiP if so
-        if (service.shouldDisableNonVrUiLocked()) {
-            return false;
-        }
-
-        boolean isKeyguardLocked = service.isKeyguardLocked();
-        boolean isCurrentAppLocked =
-                service.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
-        final ActivityDisplay display = getDisplay();
-        boolean hasPinnedStack = display != null && display.hasPinnedStack();
-        // Don't return early if !isNotLocked, since we want to throw an exception if the activity
-        // is in an incorrect state
-        boolean isNotLockedOrOnKeyguard = !isKeyguardLocked && !isCurrentAppLocked;
-
-        // We don't allow auto-PiP when something else is already pipped.
-        if (beforeStopping && hasPinnedStack) {
-            return false;
-        }
-
-        switch (mState) {
-            case RESUMED:
-                // When visible, allow entering PiP if the app is not locked.  If it is over the
-                // keyguard, then we will prompt to unlock in the caller before entering PiP.
-                return !isCurrentAppLocked &&
-                        (supportsEnterPipOnTaskSwitch || !beforeStopping);
-            case PAUSING:
-            case PAUSED:
-                // When pausing, then only allow enter PiP as in the resume state, and in addition,
-                // require that there is not an existing PiP activity and that the current system
-                // state supports entering PiP
-                return isNotLockedOrOnKeyguard && !hasPinnedStack
-                        && supportsEnterPipOnTaskSwitch;
-            case STOPPING:
-                // When stopping in a valid state, then only allow enter PiP as in the pause state.
-                // Otherwise, fall through to throw an exception if the caller is trying to enter
-                // PiP in an invalid stopping state.
-                if (supportsEnterPipOnTaskSwitch) {
-                    return isNotLockedOrOnKeyguard && !hasPinnedStack;
-                }
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * @return Whether AppOps allows this package to enter picture-in-picture.
-     */
-    private boolean checkEnterPictureInPictureAppOpsState() {
-        return service.getAppOpsService().checkOperation(
-                OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) == MODE_ALLOWED;
-    }
-
-    boolean isAlwaysFocusable() {
-        return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0;
-    }
-
-    /** Move activity with its stack to front and make the stack focused. */
-    boolean moveFocusableActivityToTop(String reason) {
-        if (!isFocusable()) {
-            if (DEBUG_FOCUS) {
-                Slog.d(TAG_FOCUS, "moveActivityStackToFront: unfocusable activity=" + this);
-            }
-            return false;
-        }
-
-        final TaskRecord task = getTask();
-        final ActivityStack stack = getStack();
-        if (stack == null) {
-            Slog.w(TAG, "moveActivityStackToFront: invalid task or stack: activity="
-                    + this + " task=" + task);
-            return false;
-        }
-
-        if (mStackSupervisor.getTopResumedActivity() == this) {
-            if (DEBUG_FOCUS) {
-                Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this);
-            }
-            return false;
-        }
-
-        if (DEBUG_FOCUS) {
-            Slog.d(TAG_FOCUS, "moveActivityStackToFront: activity=" + this);
-        }
-
-        stack.moveToFront(reason, task);
-        // Report top activity change to tracking services and WM
-        if (mStackSupervisor.getTopResumedActivity() == this) {
-            // TODO(b/111361570): Support multiple focused apps in WM
-            service.setResumedActivityUncheckLocked(this, reason);
-        }
-        return true;
-    }
-
-    /**
-     * @return true if the activity contains windows that have
-     *         {@link LayoutParams#FLAG_DISMISS_KEYGUARD} set
-     */
-    boolean hasDismissKeyguardWindows() {
-        return service.mWindowManager.containsDismissKeyguardWindow(appToken);
-    }
-
-    void makeFinishingLocked() {
-        if (finishing) {
-            return;
-        }
-        finishing = true;
-        if (stopped) {
-            clearOptionsLocked();
-        }
-
-        if (service != null) {
-            service.getTaskChangeNotificationController().notifyTaskStackChanged();
-        }
-    }
-
-    UriPermissionOwner getUriPermissionsLocked() {
-        if (uriPermissions == null) {
-            uriPermissions = new UriPermissionOwner(service.mUgmInternal, this);
-        }
-        return uriPermissions;
-    }
-
-    void addResultLocked(ActivityRecord from, String resultWho,
-            int requestCode, int resultCode,
-            Intent resultData) {
-        ActivityResult r = new ActivityResult(from, resultWho,
-                requestCode, resultCode, resultData);
-        if (results == null) {
-            results = new ArrayList<ResultInfo>();
-        }
-        results.add(r);
-    }
-
-    void removeResultsLocked(ActivityRecord from, String resultWho,
-            int requestCode) {
-        if (results != null) {
-            for (int i=results.size()-1; i>=0; i--) {
-                ActivityResult r = (ActivityResult)results.get(i);
-                if (r.mFrom != from) continue;
-                if (r.mResultWho == null) {
-                    if (resultWho != null) continue;
-                } else {
-                    if (!r.mResultWho.equals(resultWho)) continue;
-                }
-                if (r.mRequestCode != requestCode) continue;
-
-                results.remove(i);
-            }
-        }
-    }
-
-    private void addNewIntentLocked(ReferrerIntent intent) {
-        if (newIntents == null) {
-            newIntents = new ArrayList<>();
-        }
-        newIntents.add(intent);
-    }
-
-    final boolean isSleeping() {
-        final ActivityStack stack = getStack();
-        return stack != null ? stack.shouldSleepActivities() : service.isSleepingLocked();
-    }
-
-    /**
-     * Deliver a new Intent to an existing activity, so that its onNewIntent()
-     * method will be called at the proper time.
-     */
-    final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
-        // The activity now gets access to the data associated with this Intent.
-        service.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
-                intent, getUriPermissionsLocked(), userId);
-        final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
-        boolean unsent = true;
-        final boolean isTopActivityWhileSleeping = isTopRunningActivity() && isSleeping();
-
-        // We want to immediately deliver the intent to the activity if:
-        // - It is currently resumed or paused. i.e. it is currently visible to the user and we want
-        //   the user to see the visual effects caused by the intent delivery now.
-        // - The device is sleeping and it is the top activity behind the lock screen (b/6700897).
-        if ((mState == RESUMED || mState == PAUSED || isTopActivityWhileSleeping)
-                && attachedToProcess()) {
-            try {
-                ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
-                ar.add(rintent);
-                service.getLifecycleManager().scheduleTransaction(
-                        app.getThread(), appToken, NewIntentItem.obtain(ar, mState == PAUSED));
-                unsent = false;
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
-            } catch (NullPointerException e) {
-                Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
-            }
-        }
-        if (unsent) {
-            addNewIntentLocked(rintent);
-        }
-    }
-
-    void updateOptionsLocked(ActivityOptions options) {
-        if (options != null) {
-            if (pendingOptions != null) {
-                pendingOptions.abort();
-            }
-            pendingOptions = options;
-        }
-    }
-
-    void applyOptionsLocked() {
-        if (pendingOptions != null
-                && pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) {
-            final int animationType = pendingOptions.getAnimationType();
-            switch (animationType) {
-                case ANIM_CUSTOM:
-                    service.mWindowManager.overridePendingAppTransition(
-                            pendingOptions.getPackageName(),
-                            pendingOptions.getCustomEnterResId(),
-                            pendingOptions.getCustomExitResId(),
-                            pendingOptions.getOnAnimationStartListener());
-                    break;
-                case ANIM_CLIP_REVEAL:
-                    service.mWindowManager.overridePendingAppTransitionClipReveal(
-                            pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getWidth(), pendingOptions.getHeight());
-                    if (intent.getSourceBounds() == null) {
-                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                pendingOptions.getStartY(),
-                                pendingOptions.getStartX()+pendingOptions.getWidth(),
-                                pendingOptions.getStartY()+pendingOptions.getHeight()));
-                    }
-                    break;
-                case ANIM_SCALE_UP:
-                    service.mWindowManager.overridePendingAppTransitionScaleUp(
-                            pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getWidth(), pendingOptions.getHeight());
-                    if (intent.getSourceBounds() == null) {
-                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                pendingOptions.getStartY(),
-                                pendingOptions.getStartX()+pendingOptions.getWidth(),
-                                pendingOptions.getStartY()+pendingOptions.getHeight()));
-                    }
-                    break;
-                case ANIM_THUMBNAIL_SCALE_UP:
-                case ANIM_THUMBNAIL_SCALE_DOWN:
-                    final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
-                    final GraphicBuffer buffer = pendingOptions.getThumbnail();
-                    service.mWindowManager.overridePendingAppTransitionThumb(buffer,
-                            pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getOnAnimationStartListener(),
-                            scaleUp);
-                    if (intent.getSourceBounds() == null && buffer != null) {
-                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                pendingOptions.getStartY(),
-                                pendingOptions.getStartX() + buffer.getWidth(),
-                                pendingOptions.getStartY() + buffer.getHeight()));
-                    }
-                    break;
-                case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
-                case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
-                    final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
-                    final IAppTransitionAnimationSpecsFuture specsFuture =
-                            pendingOptions.getSpecsFuture();
-                    if (specsFuture != null) {
-                        service.mWindowManager.overridePendingAppTransitionMultiThumbFuture(
-                                specsFuture, pendingOptions.getOnAnimationStartListener(),
-                                animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
-                    } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
-                            && specs != null) {
-                        service.mWindowManager.overridePendingAppTransitionMultiThumb(
-                                specs, pendingOptions.getOnAnimationStartListener(),
-                                pendingOptions.getAnimationFinishedListener(), false);
-                    } else {
-                        service.mWindowManager.overridePendingAppTransitionAspectScaledThumb(
-                                pendingOptions.getThumbnail(),
-                                pendingOptions.getStartX(), pendingOptions.getStartY(),
-                                pendingOptions.getWidth(), pendingOptions.getHeight(),
-                                pendingOptions.getOnAnimationStartListener(),
-                                (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
-                        if (intent.getSourceBounds() == null) {
-                            intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                    pendingOptions.getStartY(),
-                                    pendingOptions.getStartX() + pendingOptions.getWidth(),
-                                    pendingOptions.getStartY() + pendingOptions.getHeight()));
-                        }
-                    }
-                    break;
-                case ANIM_OPEN_CROSS_PROFILE_APPS:
-                    service.mWindowManager.overridePendingAppTransitionStartCrossProfileApps();
-                    break;
-                case ANIM_REMOTE_ANIMATION:
-                    service.mWindowManager.overridePendingAppTransitionRemote(
-                            pendingOptions.getRemoteAnimationAdapter());
-                    break;
-                default:
-                    Slog.e(TAG, "applyOptionsLocked: Unknown animationType=" + animationType);
-                    break;
-            }
-
-            if (task == null) {
-                clearOptionsLocked(false /* withAbort */);
-            } else {
-                // This will clear the options for all the ActivityRecords for this Task.
-                task.clearAllPendingOptions();
-            }
-        }
-    }
-
-    ActivityOptions getOptionsForTargetActivityLocked() {
-        return pendingOptions != null ? pendingOptions.forTargetActivity() : null;
-    }
-
-    void clearOptionsLocked() {
-        clearOptionsLocked(true /* withAbort */);
-    }
-
-    void clearOptionsLocked(boolean withAbort) {
-        if (withAbort && pendingOptions != null) {
-            pendingOptions.abort();
-        }
-        pendingOptions = null;
-    }
-
-    ActivityOptions takeOptionsLocked() {
-        ActivityOptions opts = pendingOptions;
-        pendingOptions = null;
-        return opts;
-    }
-
-    void removeUriPermissionsLocked() {
-        if (uriPermissions != null) {
-            uriPermissions.removeUriPermissions();
-            uriPermissions = null;
-        }
-    }
-
-    void pauseKeyDispatchingLocked() {
-        if (!keysPaused) {
-            keysPaused = true;
-
-            if (mWindowContainerController != null) {
-                mWindowContainerController.pauseKeyDispatching();
-            }
-        }
-    }
-
-    void resumeKeyDispatchingLocked() {
-        if (keysPaused) {
-            keysPaused = false;
-
-            if (mWindowContainerController != null) {
-                mWindowContainerController.resumeKeyDispatching();
-            }
-        }
-    }
-
-    private void updateTaskDescription(CharSequence description) {
-        task.lastDescription = description;
-    }
-
-    void setDeferHidingClient(boolean deferHidingClient) {
-        if (mDeferHidingClient == deferHidingClient) {
-            return;
-        }
-        mDeferHidingClient = deferHidingClient;
-        if (!mDeferHidingClient && !visible) {
-            // Hiding the client is no longer deferred and the app isn't visible still, go ahead and
-            // update the visibility.
-            setVisibility(false);
-        }
-    }
-
-    void setVisibility(boolean visible) {
-        mWindowContainerController.setVisibility(visible, mDeferHidingClient);
-        mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
-    }
-
-    // TODO: Look into merging with #setVisibility()
-    void setVisible(boolean newVisible) {
-        visible = newVisible;
-        mDeferHidingClient = !visible && mDeferHidingClient;
-        setVisibility(visible);
-        mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
-    }
-
-    void setState(ActivityState state, String reason) {
-        if (DEBUG_STATES) Slog.v(TAG_STATES, "State movement: " + this + " from:" + getState()
-                        + " to:" + state + " reason:" + reason);
-
-        if (state == mState) {
-            // No need to do anything if state doesn't change.
-            if (DEBUG_STATES) Slog.v(TAG_STATES, "State unchanged from:" + state);
-            return;
-        }
-
-        mState = state;
-
-        final TaskRecord parent = getTask();
-
-        if (parent != null) {
-            parent.onActivityStateChanged(this, state, reason);
-        }
-
-        // The WindowManager interprets the app stopping signal as
-        // an indication that the Surface will eventually be destroyed.
-        // This however isn't necessarily true if we are going to sleep.
-        if (state == STOPPING && !isSleeping()) {
-            mWindowContainerController.notifyAppStopping();
-        }
-    }
-
-    ActivityState getState() {
-        return mState;
-    }
-
-    /**
-     * Returns {@code true} if the Activity is in the specified state.
-     */
-    boolean isState(ActivityState state) {
-        return state == mState;
-    }
-
-    /**
-     * Returns {@code true} if the Activity is in one of the specified states.
-     */
-    boolean isState(ActivityState state1, ActivityState state2) {
-        return state1 == mState || state2 == mState;
-    }
-
-    /**
-     * Returns {@code true} if the Activity is in one of the specified states.
-     */
-    boolean isState(ActivityState state1, ActivityState state2, ActivityState state3) {
-        return state1 == mState || state2 == mState || state3 == mState;
-    }
-
-    /**
-     * Returns {@code true} if the Activity is in one of the specified states.
-     */
-    boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
-            ActivityState state4) {
-        return state1 == mState || state2 == mState || state3 == mState || state4 == mState;
-    }
-
-    void notifyAppResumed(boolean wasStopped) {
-        mWindowContainerController.notifyAppResumed(wasStopped);
-    }
-
-    void notifyUnknownVisibilityLaunched() {
-
-        // No display activities never add a window, so there is no point in waiting them for
-        // relayout.
-        if (!noDisplay) {
-            mWindowContainerController.notifyUnknownVisibilityLaunched();
-        }
-    }
-
-    /**
-     * @return true if the input activity should be made visible, ignoring any effect Keyguard
-     * might have on the visibility
-     *
-     * @see {@link ActivityStack#checkKeyguardVisibility}
-     */
-    boolean shouldBeVisibleIgnoringKeyguard(boolean behindFullscreenActivity) {
-        if (!okToShowLocked()) {
-            return false;
-        }
-
-        return !behindFullscreenActivity || mLaunchTaskBehind;
-    }
-
-    void makeVisibleIfNeeded(ActivityRecord starting, boolean reportToClient) {
-        // This activity is not currently visible, but is running. Tell it to become visible.
-        if (mState == RESUMED || this == starting) {
-            if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY,
-                    "Not making visible, r=" + this + " state=" + mState + " starting=" + starting);
-            return;
-        }
-
-        // If this activity is paused, tell it to now show its window.
-        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                "Making visible and scheduling visibility: " + this);
-        final ActivityStack stack = getStack();
-        try {
-            if (stack.mTranslucentActivityWaiting != null) {
-                updateOptionsLocked(returningOptions);
-                stack.mUndrawnActivitiesBelowTopTranslucent.add(this);
-            }
-            setVisible(true);
-            sleeping = false;
-            app.postPendingUiCleanMsg(true);
-            if (reportToClient) {
-                makeClientVisible();
-            } else {
-                mClientVisibilityDeferred = true;
-            }
-            // The activity may be waiting for stop, but that is no longer appropriate for it.
-            mStackSupervisor.mStoppingActivities.remove(this);
-            mStackSupervisor.mGoingToSleepActivities.remove(this);
-        } catch (Exception e) {
-            // Just skip on any failure; we'll make it visible when it next restarts.
-            Slog.w(TAG, "Exception thrown making visible: " + intent.getComponent(), e);
-        }
-        handleAlreadyVisible();
-    }
-
-    /** Send visibility change message to the client and pause if needed. */
-    void makeClientVisible() {
-        mClientVisibilityDeferred = false;
-        try {
-            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                    WindowVisibilityItem.obtain(true /* showWindow */));
-            if (shouldPauseWhenBecomingVisible()) {
-                // An activity must be in the {@link PAUSING} state for the system to validate
-                // the move to {@link PAUSED}.
-                setState(PAUSING, "makeVisibleIfNeeded");
-                service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                        PauseActivityItem.obtain(finishing, false /* userLeaving */,
-                                configChangeFlags, false /* dontReport */));
-            }
-        } catch (Exception e) {
-            Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e);
-        }
-    }
-
-    /** Check if activity should be moved to PAUSED state when it becomes visible. */
-    private boolean shouldPauseWhenBecomingVisible() {
-        // If the activity is stopped or stopping, cycle to the paused state. We avoid doing
-        // this when there is an activity waiting to become translucent as the extra binder
-        // calls will lead to noticeable jank. A later call to
-        // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to the proper
-        // paused state. We also avoid doing this for the activity the stack supervisor
-        // considers the resumed activity, as normal means will bring the activity from STOPPED
-        // to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
-        if (!isState(STOPPED, STOPPING) || getStack().mTranslucentActivityWaiting != null
-                || isResumedActivityOnDisplay()) {
-            return false;
-        }
-
-        // Check if position in task allows to become paused
-        final int positionInTask = task.mActivities.indexOf(this);
-        if (positionInTask == -1) {
-            throw new IllegalStateException("Activity not found in its task");
-        }
-        if (positionInTask == task.mActivities.size() - 1) {
-            // It's the topmost activity in the task - should become paused now
-            return true;
-        }
-        // Check if activity above is finishing now and this one becomes the topmost in task.
-        final ActivityRecord activityAbove = task.mActivities.get(positionInTask + 1);
-        if (activityAbove.finishing && results == null) {
-            // We will only allow pausing if activity above wasn't launched for result. Otherwise it
-            // will cause this activity to resume before getting result.
-            return true;
-        }
-        return false;
-    }
-
-    boolean handleAlreadyVisible() {
-        stopFreezingScreenLocked(false);
-        try {
-            if (returningOptions != null) {
-                app.getThread().scheduleOnNewActivityOptions(appToken, returningOptions.toBundle());
-            }
-        } catch(RemoteException e) {
-        }
-        return mState == RESUMED;
-    }
-
-    static void activityResumedLocked(IBinder token) {
-        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        if (DEBUG_SAVED_STATE) Slog.i(TAG_STATES, "Resumed activity; dropping state of: " + r);
-        if (r != null) {
-            r.icicle = null;
-            r.haveState = false;
-        }
-    }
-
-    /**
-     * Once we know that we have asked an application to put an activity in the resumed state
-     * (either by launching it or explicitly telling it), this function updates the rest of our
-     * state to match that fact.
-     */
-    void completeResumeLocked() {
-        final boolean wasVisible = visible;
-        setVisible(true);
-        if (!wasVisible) {
-            // Visibility has changed, so take a note of it so we call the TaskStackChangedListener
-            mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
-        }
-        idle = false;
-        results = null;
-        newIntents = null;
-        stopped = false;
-
-        if (isActivityTypeHome()) {
-            WindowProcessController app = task.mActivities.get(0).app;
-            if (hasProcess() && app != service.mHomeProcess) {
-                service.mHomeProcess = app;
-            }
-        }
-
-        if (nowVisible) {
-            // We won't get a call to reportActivityVisibleLocked() so dismiss lockscreen now.
-            mStackSupervisor.reportActivityVisibleLocked(this);
-        }
-
-        // Schedule an idle timeout in case the app doesn't do it for us.
-        mStackSupervisor.scheduleIdleTimeoutLocked(this);
-
-        mStackSupervisor.reportResumedActivityLocked(this);
-
-        resumeKeyDispatchingLocked();
-        final ActivityStack stack = getStack();
-        mStackSupervisor.mNoAnimActivities.clear();
-
-        // Mark the point when the activity is resuming
-        // TODO: To be more accurate, the mark should be before the onCreate,
-        //       not after the onResume. But for subsequent starts, onResume is fine.
-        if (hasProcess()) {
-            cpuTimeAtResume = app.getCpuTime();
-        } else {
-            cpuTimeAtResume = 0; // Couldn't get the cpu time of process
-        }
-
-        returningOptions = null;
-
-        if (canTurnScreenOn()) {
-            mStackSupervisor.wakeUp("turnScreenOnFlag");
-        } else {
-            // If the screen is going to turn on because the caller explicitly requested it and
-            // the keyguard is not showing don't attempt to sleep. Otherwise the Activity will
-            // pause and then resume again later, which will result in a double life-cycle event.
-            stack.checkReadyForSleep();
-        }
-    }
-
-    final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
-            CharSequence description) {
-        final ActivityStack stack = getStack();
-        if (mState != STOPPING) {
-            Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this);
-            stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
-            return;
-        }
-        if (newPersistentState != null) {
-            persistentState = newPersistentState;
-            service.notifyTaskPersisterLocked(task, false);
-        }
-        if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + icicle);
-
-        if (newIcicle != null) {
-            // If icicle is null, this is happening due to a timeout, so we haven't really saved
-            // the state.
-            icicle = newIcicle;
-            haveState = true;
-            launchCount = 0;
-            updateTaskDescription(description);
-        }
-        if (!stopped) {
-            if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPED: " + this + " (stop complete)");
-            stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
-            stopped = true;
-            setState(STOPPED, "activityStoppedLocked");
-
-            mWindowContainerController.notifyAppStopped();
-
-            if (finishing) {
-                clearOptionsLocked();
-            } else {
-                if (deferRelaunchUntilPaused) {
-                    stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config");
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-                } else {
-                    mStackSupervisor.updatePreviousProcessLocked(this);
-                }
-            }
-        }
-    }
-
-    void startLaunchTickingLocked() {
-        if (Build.IS_USER) {
-            return;
-        }
-        if (launchTickTime == 0) {
-            launchTickTime = SystemClock.uptimeMillis();
-            continueLaunchTickingLocked();
-        }
-    }
-
-    boolean continueLaunchTickingLocked() {
-        if (launchTickTime == 0) {
-            return false;
-        }
-
-        final ActivityStack stack = getStack();
-        if (stack == null) {
-            return false;
-        }
-
-        Message msg = stack.mHandler.obtainMessage(LAUNCH_TICK_MSG, this);
-        stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
-        stack.mHandler.sendMessageDelayed(msg, LAUNCH_TICK);
-        return true;
-    }
-
-    void finishLaunchTickingLocked() {
-        launchTickTime = 0;
-        final ActivityStack stack = getStack();
-        if (stack != null) {
-            stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
-        }
-    }
-
-    // IApplicationToken
-
-    public boolean mayFreezeScreenLocked(WindowProcessController app) {
-        // Only freeze the screen if this activity is currently attached to
-        // an application, and that application is not blocked or unresponding.
-        // In any other case, we can't count on getting the screen unfrozen,
-        // so it is best to leave as-is.
-        return hasProcess() && !app.isCrashing() && !app.isNotResponding();
-    }
-
-    public void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
-        if (mayFreezeScreenLocked(app)) {
-            mWindowContainerController.startFreezingScreen(configChanges);
-        }
-    }
-
-    public void stopFreezingScreenLocked(boolean force) {
-        if (force || frozenBeforeDestroy) {
-            frozenBeforeDestroy = false;
-            mWindowContainerController.stopFreezingScreen(force);
-        }
-    }
-
-    public void reportFullyDrawnLocked(boolean restoredFromBundle) {
-        final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
-                .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
-        if (info != null) {
-            mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
-                    info.windowsFullyDrawnDelayMs);
-        }
-    }
-    @Override
-    public void onStartingWindowDrawn(long timestamp) {
-        synchronized (service.mGlobalLock) {
-            mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
-                    getWindowingMode(), timestamp);
-        }
-    }
-
-    @Override
-    public void onWindowsDrawn(long timestamp) {
-        synchronized (service.mGlobalLock) {
-            final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
-                    .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp);
-            final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
-            mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
-                    windowsDrawnDelayMs);
-            mStackSupervisor.sendWaitingVisibleReportLocked(this);
-            finishLaunchTickingLocked();
-            if (task != null) {
-                task.hasBeenVisible = true;
-            }
-        }
-    }
-
-    @Override
-    public void onWindowsVisible() {
-        synchronized (service.mGlobalLock) {
-            mStackSupervisor.reportActivityVisibleLocked(this);
-            if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this);
-            if (!nowVisible) {
-                nowVisible = true;
-                lastVisibleTime = SystemClock.uptimeMillis();
-                if (idle || mStackSupervisor.isStoppingNoHistoryActivity()) {
-                    // If this activity was already idle or there is an activity that must be
-                    // stopped immediately after visible, then we now need to make sure we perform
-                    // the full stop of any activities that are waiting to do so. This is because
-                    // we won't do that while they are still waiting for this one to become visible.
-                    final int size = mStackSupervisor.mActivitiesWaitingForVisibleActivity.size();
-                    if (size > 0) {
-                        for (int i = 0; i < size; i++) {
-                            final ActivityRecord r =
-                                    mStackSupervisor.mActivitiesWaitingForVisibleActivity.get(i);
-                            if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "Was waiting for visible: " + r);
-                        }
-                        mStackSupervisor.mActivitiesWaitingForVisibleActivity.clear();
-                        mStackSupervisor.scheduleIdleLocked();
-                    }
-                } else {
-                    // Instead of doing the full stop routine here, let's just hide any activities
-                    // we now can, and let them stop when the normal idle happens.
-                    mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */,
-                            false /* remove */, true /* processPausingActivities */);
-                }
-                service.scheduleAppGcsLocked();
-            }
-        }
-    }
-
-    @Override
-    public void onWindowsGone() {
-        synchronized (service.mGlobalLock) {
-            if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
-            nowVisible = false;
-        }
-    }
-
-    @Override
-    public boolean keyDispatchingTimedOut(String reason, int windowPid) {
-        ActivityRecord anrActivity;
-        WindowProcessController anrApp;
-        boolean windowFromSameProcessAsActivity;
-        synchronized (service.mGlobalLock) {
-            anrActivity = getWaitingHistoryRecordLocked();
-            anrApp = app;
-            windowFromSameProcessAsActivity =
-                    !hasProcess() || app.getPid() == windowPid || windowPid == -1;
-        }
-
-        if (windowFromSameProcessAsActivity) {
-            return service.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
-                    anrActivity.shortComponentName, anrActivity.appInfo, shortComponentName,
-                    app, false, reason);
-        } else {
-            // In this case another process added windows using this activity token. So, we call the
-            // generic service input dispatch timed out method so that the right process is blamed.
-            return service.mAmInternal.inputDispatchingTimedOut(
-                    windowPid, false /* aboveSystem */, reason) < 0;
-        }
-    }
-
-    private ActivityRecord getWaitingHistoryRecordLocked() {
-        // First find the real culprit...  if this activity is waiting for
-        // another activity to start or has stopped, then the key dispatching
-        // timeout should not be caused by this.
-        if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this) || stopped) {
-            final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
-            // Try to use the one which is closest to top.
-            ActivityRecord r = stack.getResumedActivity();
-            if (r == null) {
-                r = stack.mPausingActivity;
-            }
-            if (r != null) {
-                return r;
-            }
-        }
-        return this;
-    }
-
-    /** Checks whether the activity should be shown for current user. */
-    public boolean okToShowLocked() {
-        // We cannot show activities when the device is locked and the application is not
-        // encryption aware.
-        if (!StorageManager.isUserKeyUnlocked(userId)
-                && !info.applicationInfo.isEncryptionAware()) {
-            return false;
-        }
-
-        return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0
-                || (mStackSupervisor.isCurrentProfileLocked(userId)
-                && service.mAmInternal.isUserRunning(userId, 0 /* flags */));
-    }
-
-    /**
-     * This method will return true if the activity is either visible, is becoming visible, is
-     * currently pausing, or is resumed.
-     */
-    public boolean isInterestingToUserLocked() {
-        return visible || nowVisible || mState == PAUSING || mState == RESUMED;
-    }
-
-    void setSleeping(boolean _sleeping) {
-        setSleeping(_sleeping, false);
-    }
-
-    void setSleeping(boolean _sleeping, boolean force) {
-        if (!force && sleeping == _sleeping) {
-            return;
-        }
-        if (attachedToProcess()) {
-            try {
-                app.getThread().scheduleSleeping(appToken, _sleeping);
-                if (_sleeping && !mStackSupervisor.mGoingToSleepActivities.contains(this)) {
-                    mStackSupervisor.mGoingToSleepActivities.add(this);
-                }
-                sleeping = _sleeping;
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Exception thrown when sleeping: " + intent.getComponent(), e);
-            }
-        }
-    }
-
-    static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
-        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        if (r == null) {
-            return INVALID_TASK_ID;
-        }
-        final TaskRecord task = r.task;
-        final int activityNdx = task.mActivities.indexOf(r);
-        if (activityNdx < 0 || (onlyRoot && activityNdx > task.findEffectiveRootIndex())) {
-            return INVALID_TASK_ID;
-        }
-        return task.taskId;
-    }
-
-    static ActivityRecord isInStackLocked(IBinder token) {
-        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        return (r != null) ? r.getStack().isInStackLocked(r) : null;
-    }
-
-    static ActivityStack getStackLocked(IBinder token) {
-        final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-        if (r != null) {
-            return r.getStack();
-        }
-        return null;
-    }
-
-    /**
-     * @return display id to which this record is attached,
-     *         {@link android.view.Display#INVALID_DISPLAY} if not attached.
-     */
-    int getDisplayId() {
-        final ActivityStack stack = getStack();
-        if (stack == null) {
-            return INVALID_DISPLAY;
-        }
-        return stack.mDisplayId;
-    }
-
-    final boolean isDestroyable() {
-        if (finishing || !hasProcess()) {
-            // This would be redundant.
-            return false;
-        }
-        final ActivityStack stack = getStack();
-        if (stack == null || this == stack.getResumedActivity() || this == stack.mPausingActivity
-                || !haveState || !stopped) {
-            // We're not ready for this kind of thing.
-            return false;
-        }
-        if (visible) {
-            // The user would notice this!
-            return false;
-        }
-        return true;
-    }
-
-    private static String createImageFilename(long createTime, int taskId) {
-        return String.valueOf(taskId) + ACTIVITY_ICON_SUFFIX + createTime +
-                IMAGE_EXTENSION;
-    }
-
-    void setTaskDescription(TaskDescription _taskDescription) {
-        Bitmap icon;
-        if (_taskDescription.getIconFilename() == null &&
-                (icon = _taskDescription.getIcon()) != null) {
-            final String iconFilename = createImageFilename(createTime, task.taskId);
-            final File iconFile = new File(TaskPersister.getUserImagesDir(task.userId),
-                    iconFilename);
-            final String iconFilePath = iconFile.getAbsolutePath();
-            service.getRecentTasks().saveImage(icon, iconFilePath);
-            _taskDescription.setIconFilename(iconFilePath);
-        }
-        taskDescription = _taskDescription;
-    }
-
-    void setVoiceSessionLocked(IVoiceInteractionSession session) {
-        voiceSession = session;
-        pendingVoiceInteractionStart = false;
-    }
-
-    void clearVoiceSessionLocked() {
-        voiceSession = null;
-        pendingVoiceInteractionStart = false;
-    }
-
-    void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
-        showStartingWindow(prev, newTask, taskSwitch, false /* fromRecents */);
-    }
-
-    void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
-            boolean fromRecents) {
-        if (mWindowContainerController == null) {
-            return;
-        }
-        if (mTaskOverlay) {
-            // We don't show starting window for overlay activities.
-            return;
-        }
-        if (pendingOptions != null
-                && pendingOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
-            // Don't show starting window when using shared element transition.
-            return;
-        }
-
-        final CompatibilityInfo compatInfo =
-                service.compatibilityInfoForPackageLocked(info.applicationInfo);
-        final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
-                compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
-                prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
-                allowTaskSnapshot(),
-                mState.ordinal() >= RESUMED.ordinal() && mState.ordinal() <= STOPPED.ordinal(),
-                fromRecents);
-        if (shown) {
-            mStartingWindowState = STARTING_WINDOW_SHOWN;
-        }
-    }
-
-    void removeOrphanedStartingWindow(boolean behindFullscreenActivity) {
-        if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) {
-            if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
-            mStartingWindowState = STARTING_WINDOW_REMOVED;
-            mWindowContainerController.removeStartingWindow();
-        }
-    }
-
-    int getRequestedOrientation() {
-        return mWindowContainerController.getOrientation();
-    }
-
-    void setRequestedOrientation(int requestedOrientation) {
-        final int displayId = getDisplayId();
-        final Configuration displayConfig =
-                mStackSupervisor.getDisplayOverrideConfiguration(displayId);
-
-        final Configuration config = mWindowContainerController.setOrientation(requestedOrientation,
-                displayId, displayConfig, mayFreezeScreenLocked(app));
-        if (config != null) {
-            frozenBeforeDestroy = true;
-            if (!service.updateDisplayOverrideConfigurationLocked(config, this,
-                    false /* deferResume */, displayId)) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-            }
-        }
-        service.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
-                task.taskId, requestedOrientation);
-    }
-
-    void setDisablePreviewScreenshots(boolean disable) {
-        mWindowContainerController.setDisablePreviewScreenshots(disable);
-    }
-
-    /**
-     * Set the last reported global configuration to the client. Should be called whenever a new
-     * global configuration is sent to the client for this activity.
-     */
-    void setLastReportedGlobalConfiguration(@NonNull Configuration config) {
-        mLastReportedConfiguration.setGlobalConfiguration(config);
-    }
-
-    /**
-     * Set the last reported configuration to the client. Should be called whenever
-     * a new merged configuration is sent to the client for this activity.
-     */
-    void setLastReportedConfiguration(@NonNull MergedConfiguration config) {
-        setLastReportedConfiguration(config.getGlobalConfiguration(),
-            config.getOverrideConfiguration());
-    }
-
-    private void setLastReportedConfiguration(Configuration global, Configuration override) {
-        mLastReportedConfiguration.setConfiguration(global, override);
-    }
-
-    // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
-    private void updateOverrideConfiguration() {
-        mTmpConfig.unset();
-        computeBounds(mTmpBounds);
-
-        if (mTmpBounds.equals(getOverrideBounds())) {
-            return;
-        }
-
-        setBounds(mTmpBounds);
-
-        final Rect updatedBounds = getOverrideBounds();
-
-        // Bounds changed...update configuration to match.
-        if (!matchParentBounds()) {
-            task.computeOverrideConfiguration(mTmpConfig, updatedBounds, null /* insetBounds */,
-                    false /* overrideWidth */, false /* overrideHeight */);
-        }
-
-        onOverrideConfigurationChanged(mTmpConfig);
-    }
-
-    /** Returns true if the configuration is compatible with this activity. */
-    boolean isConfigurationCompatible(Configuration config) {
-        final int orientation = mWindowContainerController != null
-                ? mWindowContainerController.getOrientation() : info.screenOrientation;
-        if (isFixedOrientationPortrait(orientation)
-                && config.orientation != ORIENTATION_PORTRAIT) {
-            return false;
-        }
-        if (isFixedOrientationLandscape(orientation)
-                && config.orientation != ORIENTATION_LANDSCAPE) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Computes the bounds to fit the Activity within the bounds of the {@link Configuration}.
-     */
-    // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
-    private void computeBounds(Rect outBounds) {
-        outBounds.setEmpty();
-        final float maxAspectRatio = info.maxAspectRatio;
-        final ActivityStack stack = getStack();
-        if (task == null || stack == null || task.inMultiWindowMode() || maxAspectRatio == 0
-                || isInVrUiMode(getConfiguration())) {
-            // We don't set override configuration if that activity task isn't fullscreen. I.e. the
-            // activity is in multi-window mode. Or, there isn't a max aspect ratio specified for
-            // the activity. This is indicated by an empty {@link outBounds}. We also don't set it
-            // if we are in VR mode.
-            return;
-        }
-
-        // We must base this on the parent configuration, because we set our override
-        // configuration's appBounds based on the result of this method. If we used our own
-        // configuration, it would be influenced by past invocations.
-        final Rect appBounds = getParent().getWindowConfiguration().getAppBounds();
-        final int containingAppWidth = appBounds.width();
-        final int containingAppHeight = appBounds.height();
-        int maxActivityWidth = containingAppWidth;
-        int maxActivityHeight = containingAppHeight;
-
-        if (containingAppWidth < containingAppHeight) {
-            // Width is the shorter side, so we use that to figure-out what the max. height
-            // should be given the aspect ratio.
-            maxActivityHeight = (int) ((maxActivityWidth * maxAspectRatio) + 0.5f);
-        } else {
-            // Height is the shorter side, so we use that to figure-out what the max. width
-            // should be given the aspect ratio.
-            maxActivityWidth = (int) ((maxActivityHeight * maxAspectRatio) + 0.5f);
-        }
-
-        if (containingAppWidth <= maxActivityWidth && containingAppHeight <= maxActivityHeight) {
-            // The display matches or is less than the activity aspect ratio, so nothing else to do.
-            // Return the existing bounds. If this method is running for the first time,
-            // {@link #getOverrideBounds()} will be empty (representing no override). If the method has run
-            // before, then effect of {@link #getOverrideBounds()} will already have been applied to the
-            // value returned from {@link getConfiguration}. Refer to
-            // {@link TaskRecord#computeOverrideConfiguration}.
-            outBounds.set(getOverrideBounds());
-            return;
-        }
-
-        // Compute configuration based on max supported width and height.
-        // Also account for the left / top insets (e.g. from display cutouts), which will be clipped
-        // away later in StackWindowController.adjustConfigurationForBounds(). Otherwise, the app
-        // bounds would end up too small.
-        outBounds.set(0, 0, maxActivityWidth + appBounds.left, maxActivityHeight + appBounds.top);
-
-        if (service.mWindowManager.getNavBarPosition() == NAV_BAR_LEFT) {
-            // Position the activity frame on the opposite side of the nav bar.
-            outBounds.left = appBounds.right - maxActivityWidth;
-            outBounds.right = appBounds.right;
-        }
-    }
-
-    /**
-     * @return {@code true} if this activity was reparented to another display but
-     *         {@link #ensureActivityConfiguration} is not called.
-     */
-    boolean shouldUpdateConfigForDisplayChanged() {
-        return mLastReportedDisplayId != getDisplayId();
-    }
-
-    boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
-        return ensureActivityConfiguration(globalChanges, preserveWindow,
-                false /* ignoreStopState */);
-    }
-
-    /**
-     * Make sure the given activity matches the current configuration. Ensures the HistoryRecord
-     * is updated with the correct configuration and all other bookkeeping is handled.
-     *
-     * @param globalChanges The changes to the global configuration.
-     * @param preserveWindow If the activity window should be preserved on screen if the activity
-     *                       is relaunched.
-     * @param ignoreStopState If we should try to relaunch the activity even if it is in the stopped
-     *                        state. This is useful for the case where we know the activity will be
-     *                        visible soon and we want to ensure its configuration before we make it
-     *                        visible.
-     * @return True if the activity was relaunched and false if it wasn't relaunched because we
-     *         can't or the app handles the specific configuration that is changing.
-     */
-    boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
-            boolean ignoreStopState) {
-        final ActivityStack stack = getStack();
-        if (stack.mConfigWillChange) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Skipping config check (will change): " + this);
-            return true;
-        }
-
-        // We don't worry about activities that are finishing.
-        if (finishing) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration doesn't matter in finishing " + this);
-            stopFreezingScreenLocked(false);
-            return true;
-        }
-
-        if (!ignoreStopState && (mState == STOPPING || mState == STOPPED)) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Skipping config check stopped or stopping: " + this);
-            return true;
-        }
-
-        // TODO: We should add ActivityRecord.shouldBeVisible() that checks if the activity should
-        // be visible based on the stack, task, and lockscreen state and use that here instead. The
-        // method should be based on the logic in ActivityStack.ensureActivitiesVisibleLocked().
-        // Skip updating configuration for activity is a stack that shouldn't be visible.
-        if (!stack.shouldBeVisible(null /* starting */)) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Skipping config check invisible stack: " + this);
-            return true;
-        }
-
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                "Ensuring correct configuration: " + this);
-
-        final int newDisplayId = getDisplayId();
-        final boolean displayChanged = mLastReportedDisplayId != newDisplayId;
-        if (displayChanged) {
-            mLastReportedDisplayId = newDisplayId;
-        }
-        // TODO(b/36505427): Is there a better place to do this?
-        updateOverrideConfiguration();
-
-        // Short circuit: if the two full configurations are equal (the common case), then there is
-        // nothing to do.  We test the full configuration instead of the global and merged override
-        // configurations because there are cases (like moving a task to the pinned stack) where
-        // the combine configurations are equal, but would otherwise differ in the override config
-        mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
-        if (getConfiguration().equals(mTmpConfig) && !forceNewConfig && !displayChanged) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration & display unchanged in " + this);
-            return true;
-        }
-
-        // Okay we now are going to make this activity have the new config.
-        // But then we need to figure out how it needs to deal with that.
-
-        // Find changes between last reported merged configuration and the current one. This is used
-        // to decide whether to relaunch an activity or just report a configuration change.
-        final int changes = getConfigurationChanges(mTmpConfig);
-
-        // Update last reported values.
-        final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
-
-        setLastReportedConfiguration(service.getGlobalConfiguration(), newMergedOverrideConfig);
-
-        if (mState == INITIALIZING) {
-            // No need to relaunch or schedule new config for activity that hasn't been launched
-            // yet. We do, however, return after applying the config to activity record, so that
-            // it will use it for launch transaction.
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Skipping config check for initializing activity: " + this);
-            return true;
-        }
-
-        if (changes == 0 && !forceNewConfig) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration no differences in " + this);
-            // There are no significant differences, so we won't relaunch but should still deliver
-            // the new configuration to the client process.
-            if (displayChanged) {
-                scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
-            } else {
-                scheduleConfigurationChanged(newMergedOverrideConfig);
-            }
-            return true;
-        }
-
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                "Configuration changes for " + this + ", allChanges="
-                        + Configuration.configurationDiffToString(changes));
-
-        // If the activity isn't currently running, just leave the new configuration and it will
-        // pick that up next time it starts.
-        if (!attachedToProcess()) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration doesn't matter not running " + this);
-            stopFreezingScreenLocked(false);
-            forceNewConfig = false;
-            return true;
-        }
-
-        // Figure out how to handle the changes between the configurations.
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                "Checking to restart " + info.name + ": changed=0x"
-                        + Integer.toHexString(changes) + ", handles=0x"
-                        + Integer.toHexString(info.getRealConfigChanged())
-                        + ", mLastReportedConfiguration=" + mLastReportedConfiguration);
-
-        if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
-            // Aha, the activity isn't handling the change, so DIE DIE DIE.
-            configChangeFlags |= changes;
-            startFreezingScreenLocked(app, globalChanges);
-            forceNewConfig = false;
-            preserveWindow &= isResizeOnlyChange(changes);
-            final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
-            if (hasResizeChange) {
-                final boolean isDragResizing =
-                        getTask().getWindowContainerController().isDragResizing();
-                mRelaunchReason = isDragResizing ? RELAUNCH_REASON_FREE_RESIZE
-                        : RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-            } else {
-                mRelaunchReason = RELAUNCH_REASON_NONE;
-            }
-            if (!attachedToProcess()) {
-                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Config is destroying non-running " + this);
-                stack.destroyActivityLocked(this, true, "config");
-            } else if (mState == PAUSING) {
-                // A little annoying: we are waiting for this activity to finish pausing. Let's not
-                // do anything now, but just flag that it needs to be restarted when done pausing.
-                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Config is skipping already pausing " + this);
-                deferRelaunchUntilPaused = true;
-                preserveWindowOnDeferredRelaunch = preserveWindow;
-                return true;
-            } else if (mState == RESUMED) {
-                // Try to optimize this case: the configuration is changing and we need to restart
-                // the top, resumed activity. Instead of doing the normal handshaking, just say
-                // "restart!".
-                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Config is relaunching resumed " + this);
-
-                if (DEBUG_STATES && !visible) {
-                    Slog.v(TAG_STATES, "Config is relaunching resumed invisible activity " + this
-                            + " called by " + Debug.getCallers(4));
-                }
-
-                relaunchActivityLocked(true /* andResume */, preserveWindow);
-            } else {
-                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Config is relaunching non-resumed " + this);
-                relaunchActivityLocked(false /* andResume */, preserveWindow);
-            }
-
-            // All done...  tell the caller we weren't able to keep this activity around.
-            return false;
-        }
-
-        // Default case: the activity can handle this new configuration, so hand it over.
-        // NOTE: We only forward the override configuration as the system level configuration
-        // changes is always sent to all processes when they happen so it can just use whatever
-        // system level configuration it last got.
-        if (displayChanged) {
-            scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
-        } else {
-            scheduleConfigurationChanged(newMergedOverrideConfig);
-        }
-        stopFreezingScreenLocked(false);
-
-        return true;
-    }
-
-    /**
-     * When assessing a configuration change, decide if the changes flags and the new configurations
-     * should cause the Activity to relaunch.
-     *
-     * @param changes the changes due to the given configuration.
-     * @param changesConfig the configuration that was used to calculate the given changes via a
-     *        call to getConfigurationChanges.
-     */
-    private boolean shouldRelaunchLocked(int changes, Configuration changesConfig) {
-        int configChanged = info.getRealConfigChanged();
-        boolean onlyVrUiModeChanged = onlyVrUiModeChanged(changes, changesConfig);
-
-        // Override for apps targeting pre-O sdks
-        // If a device is in VR mode, and we're transitioning into VR ui mode, add ignore ui mode
-        // to the config change.
-        // For O and later, apps will be required to add configChanges="uimode" to their manifest.
-        if (appInfo.targetSdkVersion < O
-                && requestedVrComponent != null
-                && onlyVrUiModeChanged) {
-            configChanged |= CONFIG_UI_MODE;
-        }
-
-        return (changes&(~configChanged)) != 0;
-    }
-
-    /**
-     * Returns true if the configuration change is solely due to the UI mode switching into or out
-     * of UI_MODE_TYPE_VR_HEADSET.
-     */
-    private boolean onlyVrUiModeChanged(int changes, Configuration lastReportedConfig) {
-        final Configuration currentConfig = getConfiguration();
-        return changes == CONFIG_UI_MODE && (isInVrUiMode(currentConfig)
-            != isInVrUiMode(lastReportedConfig));
-    }
-
-    private int getConfigurationChanges(Configuration lastReportedConfig) {
-        // Determine what has changed.  May be nothing, if this is a config that has come back from
-        // the app after going idle.  In that case we just want to leave the official config object
-        // now in the activity and do nothing else.
-        final Configuration currentConfig = getConfiguration();
-        int changes = lastReportedConfig.diff(currentConfig);
-        // We don't want to use size changes if they don't cross boundaries that are important to
-        // the app.
-        if ((changes & CONFIG_SCREEN_SIZE) != 0) {
-            final boolean crosses = crossesHorizontalSizeThreshold(lastReportedConfig.screenWidthDp,
-                    currentConfig.screenWidthDp)
-                    || crossesVerticalSizeThreshold(lastReportedConfig.screenHeightDp,
-                    currentConfig.screenHeightDp);
-            if (!crosses) {
-                changes &= ~CONFIG_SCREEN_SIZE;
-            }
-        }
-        if ((changes & CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
-            final int oldSmallest = lastReportedConfig.smallestScreenWidthDp;
-            final int newSmallest = currentConfig.smallestScreenWidthDp;
-            if (!crossesSmallestSizeThreshold(oldSmallest, newSmallest)) {
-                changes &= ~CONFIG_SMALLEST_SCREEN_SIZE;
-            }
-        }
-        // We don't want window configuration to cause relaunches.
-        if ((changes & CONFIG_WINDOW_CONFIGURATION) != 0) {
-            changes &= ~CONFIG_WINDOW_CONFIGURATION;
-        }
-
-        return changes;
-    }
-
-    private static boolean isResizeOnlyChange(int change) {
-        return (change & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_ORIENTATION
-                | CONFIG_SCREEN_LAYOUT)) == 0;
-    }
-
-    private static boolean hasResizeChange(int change) {
-        return (change & (CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_ORIENTATION
-                | CONFIG_SCREEN_LAYOUT)) != 0;
-    }
-
-    void relaunchActivityLocked(boolean andResume, boolean preserveWindow) {
-        if (service.mSuppressResizeConfigChanges && preserveWindow) {
-            configChangeFlags = 0;
-            return;
-        }
-
-        List<ResultInfo> pendingResults = null;
-        List<ReferrerIntent> pendingNewIntents = null;
-        if (andResume) {
-            pendingResults = results;
-            pendingNewIntents = newIntents;
-        }
-        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
-                "Relaunching: " + this + " with results=" + pendingResults
-                        + " newIntents=" + pendingNewIntents + " andResume=" + andResume
-                        + " preserveWindow=" + preserveWindow);
-        EventLog.writeEvent(andResume ? AM_RELAUNCH_RESUME_ACTIVITY
-                        : AM_RELAUNCH_ACTIVITY, userId, System.identityHashCode(this),
-                task.taskId, shortComponentName);
-
-        startFreezingScreenLocked(app, 0);
-
-        try {
-            if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH,
-                    "Moving to " + (andResume ? "RESUMED" : "PAUSED") + " Relaunching " + this
-                            + " callers=" + Debug.getCallers(6));
-            forceNewConfig = false;
-            mStackSupervisor.activityRelaunchingLocked(this);
-            final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
-                    pendingNewIntents, configChangeFlags,
-                    new MergedConfiguration(service.getGlobalConfiguration(),
-                            getMergedOverrideConfiguration()),
-                    preserveWindow);
-            final ActivityLifecycleItem lifecycleItem;
-            if (andResume) {
-                lifecycleItem = ResumeActivityItem.obtain(service.isNextTransitionForward());
-            } else {
-                lifecycleItem = PauseActivityItem.obtain();
-            }
-            final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), appToken);
-            transaction.addCallback(callbackItem);
-            transaction.setLifecycleStateRequest(lifecycleItem);
-            service.getLifecycleManager().scheduleTransaction(transaction);
-            // Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
-            // request resume if this activity is currently resumed, which implies we aren't
-            // sleeping.
-        } catch (RemoteException e) {
-            if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, "Relaunch failed", e);
-        }
-
-        if (andResume) {
-            if (DEBUG_STATES) {
-                Slog.d(TAG_STATES, "Resumed after relaunch " + this);
-            }
-            results = null;
-            newIntents = null;
-            service.getAppWarningsLocked().onResumeActivity(this);
-        } else {
-            final ActivityStack stack = getStack();
-            if (stack != null) {
-                stack.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
-            }
-            setState(PAUSED, "relaunchActivityLocked");
-        }
-
-        configChangeFlags = 0;
-        deferRelaunchUntilPaused = false;
-        preserveWindowOnDeferredRelaunch = false;
-    }
-
-    private boolean isProcessRunning() {
-        WindowProcessController proc = app;
-        if (proc == null) {
-            proc = service.mProcessNames.get(processName, info.applicationInfo.uid);
-        }
-        return proc != null && proc.hasThread();
-    }
-
-    /**
-     * @return Whether a task snapshot starting window may be shown.
-     */
-    private boolean allowTaskSnapshot() {
-        if (newIntents == null) {
-            return true;
-        }
-
-        // Restrict task snapshot starting window to launcher start, or there is no intent at all
-        // (eg. task being brought to front). If the intent is something else, likely the app is
-        // going to show some specific page or view, instead of what's left last time.
-        for (int i = newIntents.size() - 1; i >= 0; i--) {
-            final Intent intent = newIntents.get(i);
-            if (intent != null && !ActivityRecord.isMainIntent(intent)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Returns {@code true} if the associated activity has the no history flag set on it.
-     * {@code false} otherwise.
-     */
-    boolean isNoHistory() {
-        return (intent.getFlags() & FLAG_ACTIVITY_NO_HISTORY) != 0
-                || (info.flags & FLAG_NO_HISTORY) != 0;
-    }
-
-    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
-        out.attribute(null, ATTR_ID, String.valueOf(createTime));
-        out.attribute(null, ATTR_LAUNCHEDFROMUID, String.valueOf(launchedFromUid));
-        if (launchedFromPackage != null) {
-            out.attribute(null, ATTR_LAUNCHEDFROMPACKAGE, launchedFromPackage);
-        }
-        if (resolvedType != null) {
-            out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType);
-        }
-        out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified));
-        out.attribute(null, ATTR_USERID, String.valueOf(userId));
-
-        if (taskDescription != null) {
-            taskDescription.saveToXml(out);
-        }
-
-        out.startTag(null, TAG_INTENT);
-        intent.saveToXml(out);
-        out.endTag(null, TAG_INTENT);
-
-        if (isPersistable() && persistentState != null) {
-            out.startTag(null, TAG_PERSISTABLEBUNDLE);
-            persistentState.saveToXml(out);
-            out.endTag(null, TAG_PERSISTABLEBUNDLE);
-        }
-    }
-
-    static ActivityRecord restoreFromXml(XmlPullParser in,
-            ActivityStackSupervisor stackSupervisor) throws IOException, XmlPullParserException {
-        Intent intent = null;
-        PersistableBundle persistentState = null;
-        int launchedFromUid = 0;
-        String launchedFromPackage = null;
-        String resolvedType = null;
-        boolean componentSpecified = false;
-        int userId = 0;
-        long createTime = -1;
-        final int outerDepth = in.getDepth();
-        TaskDescription taskDescription = new TaskDescription();
-
-        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
-            final String attrName = in.getAttributeName(attrNdx);
-            final String attrValue = in.getAttributeValue(attrNdx);
-            if (DEBUG) Slog.d(TaskPersister.TAG,
-                        "ActivityRecord: attribute name=" + attrName + " value=" + attrValue);
-            if (ATTR_ID.equals(attrName)) {
-                createTime = Long.parseLong(attrValue);
-            } else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) {
-                launchedFromUid = Integer.parseInt(attrValue);
-            } else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) {
-                launchedFromPackage = attrValue;
-            } else if (ATTR_RESOLVEDTYPE.equals(attrName)) {
-                resolvedType = attrValue;
-            } else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) {
-                componentSpecified = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_USERID.equals(attrName)) {
-                userId = Integer.parseInt(attrValue);
-            } else if (attrName.startsWith(ATTR_TASKDESCRIPTION_PREFIX)) {
-                taskDescription.restoreFromXml(attrName, attrValue);
-            } else {
-                Log.d(TAG, "Unknown ActivityRecord attribute=" + attrName);
-            }
-        }
-
-        int event;
-        while (((event = in.next()) != END_DOCUMENT) &&
-                (event != END_TAG || in.getDepth() >= outerDepth)) {
-            if (event == START_TAG) {
-                final String name = in.getName();
-                if (DEBUG)
-                        Slog.d(TaskPersister.TAG, "ActivityRecord: START_TAG name=" + name);
-                if (TAG_INTENT.equals(name)) {
-                    intent = Intent.restoreFromXml(in);
-                    if (DEBUG)
-                            Slog.d(TaskPersister.TAG, "ActivityRecord: intent=" + intent);
-                } else if (TAG_PERSISTABLEBUNDLE.equals(name)) {
-                    persistentState = PersistableBundle.restoreFromXml(in);
-                    if (DEBUG) Slog.d(TaskPersister.TAG,
-                            "ActivityRecord: persistentState=" + persistentState);
-                } else {
-                    Slog.w(TAG, "restoreActivity: unexpected name=" + name);
-                    XmlUtils.skipCurrentTag(in);
-                }
-            }
-        }
-
-        if (intent == null) {
-            throw new XmlPullParserException("restoreActivity error intent=" + intent);
-        }
-
-        final ActivityTaskManagerService service = stackSupervisor.mService;
-        final ActivityInfo aInfo = stackSupervisor.resolveActivity(intent, resolvedType, 0, null,
-                userId, Binder.getCallingUid());
-        if (aInfo == null) {
-            throw new XmlPullParserException("restoreActivity resolver error. Intent=" + intent +
-                    " resolvedType=" + resolvedType);
-        }
-        final ActivityRecord r = new ActivityRecord(service, null /* caller */,
-                0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, intent, resolvedType,
-                aInfo, service.getConfiguration(), null /* resultTo */, null /* resultWho */,
-                0 /* reqCode */, componentSpecified, false /* rootVoiceInteraction */,
-                stackSupervisor, null /* options */, null /* sourceRecord */);
-
-        r.persistentState = persistentState;
-        r.taskDescription = taskDescription;
-        r.createTime = createTime;
-
-        return r;
-    }
-
-    private static boolean isInVrUiMode(Configuration config) {
-        return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET;
-    }
-
-    int getUid() {
-        return info.applicationInfo.uid;
-    }
-
-    void setShowWhenLocked(boolean showWhenLocked) {
-        mShowWhenLocked = showWhenLocked;
-        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0 /* configChanges */,
-                false /* preserveWindows */);
-    }
-
-    /**
-     * @return true if the activity windowing mode is not
-     *         {@link android.app.WindowConfiguration#WINDOWING_MODE_PINNED} and activity contains
-     *         windows that have {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the activity
-     *         has set {@link #mShowWhenLocked}.
-     *         Multi-windowing mode will be exited if true is returned.
-     */
-    boolean canShowWhenLocked() {
-        return !inPinnedWindowingMode() && (mShowWhenLocked
-                || service.mWindowManager.containsShowWhenLockedWindow(appToken));
-    }
-
-    void setTurnScreenOn(boolean turnScreenOn) {
-        mTurnScreenOn = turnScreenOn;
-    }
-
-    /**
-     * Determines whether this ActivityRecord can turn the screen on. It checks whether the flag
-     * {@link #mTurnScreenOn} is set and checks whether the ActivityRecord should be visible
-     * depending on Keyguard state
-     *
-     * @return true if the screen can be turned on, false otherwise.
-     */
-    boolean canTurnScreenOn() {
-        final ActivityStack stack = getStack();
-        return mTurnScreenOn && stack != null &&
-                stack.checkKeyguardVisibility(this, true /* shouldBeVisible */, true /* isTop */);
-    }
-
-    boolean getTurnScreenOnFlag() {
-        return mTurnScreenOn;
-    }
-
-    boolean isTopRunningActivity() {
-        return mStackSupervisor.topRunningActivityLocked() == this;
-    }
-
-    /**
-     * @return {@code true} if this is the resumed activity on its current display, {@code false}
-     * otherwise.
-     */
-    boolean isResumedActivityOnDisplay() {
-        final ActivityDisplay display = getDisplay();
-        return display != null && this == display.getResumedActivity();
-    }
-
-    void registerRemoteAnimations(RemoteAnimationDefinition definition) {
-        mWindowContainerController.registerRemoteAnimations(definition);
-    }
-
-    @Override
-    public String toString() {
-        if (stringName != null) {
-            return stringName + " t" + (task == null ? INVALID_TASK_ID : task.taskId) +
-                    (finishing ? " f}" : "}");
-        }
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("ActivityRecord{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(" u");
-        sb.append(userId);
-        sb.append(' ');
-        sb.append(intent.getComponent().flattenToShortString());
-        stringName = sb.toString();
-        return toString();
-    }
-
-    void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-        proto.write(HASH_CODE, System.identityHashCode(this));
-        proto.write(USER_ID, userId);
-        proto.write(TITLE, intent.getComponent().flattenToShortString());
-        proto.end(token);
-    }
-
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
-        writeIdentifierToProto(proto, IDENTIFIER);
-        proto.write(STATE, mState.toString());
-        proto.write(VISIBLE, visible);
-        proto.write(FRONT_OF_TASK, frontOfTask);
-        if (hasProcess()) {
-            proto.write(PROC_ID, app.getPid());
-        }
-        proto.write(TRANSLUCENT, !fullscreen);
-        proto.end(token);
-    }
-}
diff --git a/services/core/java/com/android/server/am/ActivityResult.java b/services/core/java/com/android/server/am/ActivityResult.java
deleted file mode 100644
index 395918e..0000000
--- a/services/core/java/com/android/server/am/ActivityResult.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2006 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.server.am;
-
-import android.app.ResultInfo;
-import android.content.Intent;
-
-/**
- * Pending result information to send back to an activity.
- */
-final class ActivityResult extends ResultInfo {
-    final ActivityRecord mFrom;
-    
-    public ActivityResult(ActivityRecord from, String resultWho,
-            int requestCode, int resultCode, Intent data) {
-        super(resultWho, requestCode, resultCode, data);
-        mFrom = from;
-    }
-}
diff --git a/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java
deleted file mode 100644
index b1ced29..0000000
--- a/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-
-import java.io.PrintWriter;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.function.Consumer;
-
-/**
- * Class for tracking the connections to services on the AM side that activities on the
- * WM side (in the future) bind with for things like oom score adjustment. Would normally be one
- * instance of this per activity for tracking all services connected to that activity. AM will
- * sometimes query this to bump the OOM score for the processes with services connected to visible
- * activities.
- */
-public class ActivityServiceConnectionsHolder<T> {
-
-    private final ActivityTaskManagerService mService;
-
-    /** The activity the owns this service connection object. */
-    private final ActivityRecord mActivity;
-
-    /**
-     * The service connection object bounded with the owning activity. They represent
-     * ConnectionRecord on the AM side, however we don't need to know their object representation
-     * on the WM side since we don't perform operations on the object. Mainly here for communication
-     * and booking with the AM side.
-     */
-    private HashSet<T> mConnections;
-
-    ActivityServiceConnectionsHolder(ActivityTaskManagerService service, ActivityRecord activity) {
-        mService = service;
-        mActivity = activity;
-    }
-
-    /** Adds a connection record that the activity has bound to a specific service. */
-    public void addConnection(T c) {
-        synchronized (mService.mGlobalLock) {
-            if (mConnections == null) {
-                mConnections = new HashSet<>();
-            }
-            mConnections.add(c);
-        }
-    }
-
-    /** Removed a connection record between the activity and a specific service. */
-    public void removeConnection(T c) {
-        synchronized (mService.mGlobalLock) {
-            if (mConnections == null) {
-                return;
-            }
-            mConnections.remove(c);
-        }
-    }
-
-    public boolean isActivityVisible() {
-        synchronized (mService.mGlobalLock) {
-            return mActivity.visible || mActivity.isState(RESUMED, PAUSING);
-        }
-    }
-
-    public int getActivityPid() {
-        synchronized (mService.mGlobalLock) {
-            return mActivity.hasProcess() ? mActivity.app.getPid() : -1;
-        }
-    }
-
-    public void forEachConnection(Consumer<T> consumer) {
-        synchronized (mService.mGlobalLock) {
-            if (mConnections == null || mConnections.isEmpty()) {
-                return;
-            }
-            final Iterator<T> it = mConnections.iterator();
-            while (it.hasNext()) {
-                T c = it.next();
-                consumer.accept(c);
-            }
-        }
-    }
-
-    /** Removes the connection between the activity and all services that were connected to it. */
-    void disconnectActivityFromServices() {
-        if (mConnections == null || mConnections.isEmpty()) {
-            return;
-        }
-        mService.mH.post(() -> mService.mAmInternal.disconnectActivityFromServices(this));
-    }
-
-    public void dump(PrintWriter pw, String prefix) {
-        synchronized (mService.mGlobalLock) {
-            pw.println(prefix + "activity=" + mActivity);
-        }
-    }
-
-}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
deleted file mode 100644
index 026c5cc..0000000
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ /dev/null
@@ -1,5455 +0,0 @@
-/*
- * Copyright (C) 2010 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.server.am;
-
-import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.windowingModeToString;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
-import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-
-import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
-import static com.android.server.am.ActivityDisplay.POSITION_TOP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_APP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_APP;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
-import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.am.ActivityStackProto.BOUNDS;
-import static com.android.server.am.ActivityStackProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityStackProto.DISPLAY_ID;
-import static com.android.server.am.ActivityStackProto.FULLSCREEN;
-import static com.android.server.am.ActivityStackProto.ID;
-import static com.android.server.am.ActivityStackProto.RESUMED_ACTIVITY;
-import static com.android.server.am.ActivityStackProto.TASKS;
-import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-
-import static com.android.server.am.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
-import static java.lang.Integer.MAX_VALUE;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityOptions;
-import android.app.AppGlobals;
-import android.app.IActivityController;
-import android.app.ResultInfo;
-import android.app.WindowConfiguration.ActivityType;
-import android.app.WindowConfiguration.WindowingMode;
-import android.app.servertransaction.ActivityResultItem;
-import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.DestroyActivityItem;
-import android.app.servertransaction.NewIntentItem;
-import android.app.servertransaction.PauseActivityItem;
-import android.app.servertransaction.ResumeActivityItem;
-import android.app.servertransaction.StopActivityItem;
-import android.app.servertransaction.WindowVisibilityItem;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.service.voice.IVoiceInteractionSession;
-import android.util.ArraySet;
-import android.util.EventLog;
-import android.util.IntArray;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.proto.ProtoOutputStream;
-import android.view.Display;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.Watchdog;
-import com.android.server.am.ActivityManagerService.ItemMatcher;
-import com.android.server.wm.ConfigurationContainer;
-import com.android.server.wm.StackWindowController;
-import com.android.server.wm.StackWindowListener;
-import com.android.server.wm.WindowManagerService;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * State and management of a single stack of activities.
- */
-class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
-        implements StackWindowListener {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
-    private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
-    private static final String TAG_APP = TAG + POSTFIX_APP;
-    private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
-    private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS;
-    private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
-    private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
-    private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
-    private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
-    private static final String TAG_STACK = TAG + POSTFIX_STACK;
-    private static final String TAG_STATES = TAG + POSTFIX_STATES;
-    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
-    private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
-    private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
-    private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
-    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
-
-    // Ticks during which we check progress while waiting for an app to launch.
-    static final int LAUNCH_TICK = 500;
-
-    // How long we wait until giving up on the last activity to pause.  This
-    // is short because it directly impacts the responsiveness of starting the
-    // next activity.
-    private static final int PAUSE_TIMEOUT = 500;
-
-    // How long we wait for the activity to tell us it has stopped before
-    // giving up.  This is a good amount of time because we really need this
-    // from the application in order to get its saved state. Once the stop
-    // is complete we may start destroying client resources triggering
-    // crashes if the UI thread was hung. We put this timeout one second behind
-    // the ANR timeout so these situations will generate ANR instead of
-    // Surface lost or other errors.
-    private static final int STOP_TIMEOUT = 11 * 1000;
-
-    // How long we wait until giving up on an activity telling us it has
-    // finished destroying itself.
-    private static final int DESTROY_TIMEOUT = 10 * 1000;
-
-    // Set to false to disable the preview that is shown while a new activity
-    // is being started.
-    private static final boolean SHOW_APP_STARTING_PREVIEW = true;
-
-    // How long to wait for all background Activities to redraw following a call to
-    // convertToTranslucent().
-    private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
-
-    // How many activities have to be scheduled to stop to force a stop pass.
-    private static final int MAX_STOPPING_TO_FORCE = 3;
-
-    @Override
-    protected int getChildCount() {
-        return mTaskHistory.size();
-    }
-
-    @Override
-    protected TaskRecord getChildAt(int index) {
-        return mTaskHistory.get(index);
-    }
-
-    @Override
-    protected ConfigurationContainer getParent() {
-        return getDisplay();
-    }
-
-    @Override
-    protected void onParentChanged() {
-        super.onParentChanged();
-        mStackSupervisor.updateUIDsPresentOnDisplay();
-    }
-
-    enum ActivityState {
-        INITIALIZING,
-        RESUMED,
-        PAUSING,
-        PAUSED,
-        STOPPING,
-        STOPPED,
-        FINISHING,
-        DESTROYING,
-        DESTROYED
-    }
-
-    @VisibleForTesting
-    /* The various modes for the method {@link #removeTask}. */
-    // Task is being completely removed from all stacks in the system.
-    protected static final int REMOVE_TASK_MODE_DESTROYING = 0;
-    // Task is being removed from this stack so we can add it to another stack. In the case we are
-    // moving we don't want to perform some operations on the task like removing it from window
-    // manager or recents.
-    static final int REMOVE_TASK_MODE_MOVING = 1;
-    // Similar to {@link #REMOVE_TASK_MODE_MOVING} and the task will be added to the top of its new
-    // stack and the new stack will be on top of all stacks.
-    static final int REMOVE_TASK_MODE_MOVING_TO_TOP = 2;
-
-    // The height/width divide used when fitting a task within a bounds with method
-    // {@link #fitWithinBounds}.
-    // We always want the task to to be visible in the bounds without affecting its size when
-    // fitting. To make sure this is the case, we don't adjust the task left or top side pass
-    // the input bounds right or bottom side minus the width or height divided by this value.
-    private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
-
-    final ActivityTaskManagerService mService;
-    private final WindowManagerService mWindowManager;
-    T mWindowContainerController;
-
-    /**
-     * The back history of all previous (and possibly still
-     * running) activities.  It contains #TaskRecord objects.
-     */
-    private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>();
-
-    /**
-     * List of running activities, sorted by recent usage.
-     * The first entry in the list is the least recently used.
-     * It contains HistoryRecord objects.
-     */
-    final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<>();
-
-    /**
-     * When we are in the process of pausing an activity, before starting the
-     * next one, this variable holds the activity that is currently being paused.
-     */
-    ActivityRecord mPausingActivity = null;
-
-    /**
-     * This is the last activity that we put into the paused state.  This is
-     * used to determine if we need to do an activity transition while sleeping,
-     * when we normally hold the top activity paused.
-     */
-    ActivityRecord mLastPausedActivity = null;
-
-    /**
-     * Activities that specify No History must be removed once the user navigates away from them.
-     * If the device goes to sleep with such an activity in the paused state then we save it here
-     * and finish it later if another activity replaces it on wakeup.
-     */
-    ActivityRecord mLastNoHistoryActivity = null;
-
-    /**
-     * Current activity that is resumed, or null if there is none.
-     */
-    ActivityRecord mResumedActivity = null;
-
-    // The topmost Activity passed to convertToTranslucent(). When non-null it means we are
-    // waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they
-    // are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the
-    // Activity in mTranslucentActivityWaiting is notified via
-    // Activity.onTranslucentConversionComplete(false). If a timeout occurs prior to the last
-    // background activity being drawn then the same call will be made with a true value.
-    ActivityRecord mTranslucentActivityWaiting = null;
-    ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent = new ArrayList<>();
-
-    /**
-     * Set when we know we are going to be calling updateConfiguration()
-     * soon, so want to skip intermediate config checks.
-     */
-    boolean mConfigWillChange;
-
-    /**
-     * When set, will force the stack to report as invisible.
-     */
-    boolean mForceHidden = false;
-
-    private boolean mUpdateBoundsDeferred;
-    private boolean mUpdateBoundsDeferredCalled;
-    private final Rect mDeferredBounds = new Rect();
-    private final Rect mDeferredTaskBounds = new Rect();
-    private final Rect mDeferredTaskInsetBounds = new Rect();
-
-    int mCurrentUser;
-
-    final int mStackId;
-    /** The attached Display's unique identifier, or -1 if detached */
-    int mDisplayId;
-
-    /** Stores the override windowing-mode from before a transient mode change (eg. split) */
-    private int mRestoreOverrideWindowingMode = WINDOWING_MODE_UNDEFINED;
-
-    private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
-    private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
-    private final Rect mTmpRect2 = new Rect();
-    private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
-
-    /** List for processing through a set of activities */
-    private final ArrayList<ActivityRecord> mTmpActivities = new ArrayList<>();
-
-    /** Run all ActivityStacks through this */
-    protected final ActivityStackSupervisor mStackSupervisor;
-
-    private boolean mTopActivityOccludesKeyguard;
-    private ActivityRecord mTopDismissingKeyguardActivity;
-
-    static final int PAUSE_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
-    static final int DESTROY_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 2;
-    static final int LAUNCH_TICK_MSG = FIRST_ACTIVITY_STACK_MSG + 3;
-    static final int STOP_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 4;
-    static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5;
-    static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6;
-
-    private static class ScheduleDestroyArgs {
-        final WindowProcessController mOwner;
-        final String mReason;
-        ScheduleDestroyArgs(WindowProcessController owner, String reason) {
-            mOwner = owner;
-            mReason = reason;
-        }
-    }
-
-    final Handler mHandler;
-
-    private class ActivityStackHandler extends Handler {
-
-        ActivityStackHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case PAUSE_TIMEOUT_MSG: {
-                    ActivityRecord r = (ActivityRecord)msg.obj;
-                    // We don't at this point know if the activity is fullscreen,
-                    // so we need to be conservative and assume it isn't.
-                    Slog.w(TAG, "Activity pause timeout for " + r);
-                    synchronized (mService.mGlobalLock) {
-                        if (r.hasProcess()) {
-                            mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
-                        }
-                        activityPausedLocked(r.appToken, true);
-                    }
-                } break;
-                case LAUNCH_TICK_MSG: {
-                    ActivityRecord r = (ActivityRecord)msg.obj;
-                    synchronized (mService.mGlobalLock) {
-                        if (r.continueLaunchTickingLocked()) {
-                            mService.logAppTooSlow(r.app, r.launchTickTime, "launching " + r);
-                        }
-                    }
-                } break;
-                case DESTROY_TIMEOUT_MSG: {
-                    ActivityRecord r = (ActivityRecord)msg.obj;
-                    // We don't at this point know if the activity is fullscreen,
-                    // so we need to be conservative and assume it isn't.
-                    Slog.w(TAG, "Activity destroy timeout for " + r);
-                    synchronized (mService.mGlobalLock) {
-                        activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout");
-                    }
-                } break;
-                case STOP_TIMEOUT_MSG: {
-                    ActivityRecord r = (ActivityRecord)msg.obj;
-                    // We don't at this point know if the activity is fullscreen,
-                    // so we need to be conservative and assume it isn't.
-                    Slog.w(TAG, "Activity stop timeout for " + r);
-                    synchronized (mService.mGlobalLock) {
-                        if (r.isInHistory()) {
-                            r.activityStoppedLocked(null /* icicle */,
-                                    null /* persistentState */, null /* description */);
-                        }
-                    }
-                } break;
-                case DESTROY_ACTIVITIES_MSG: {
-                    ScheduleDestroyArgs args = (ScheduleDestroyArgs)msg.obj;
-                    synchronized (mService.mGlobalLock) {
-                        destroyActivitiesLocked(args.mOwner, args.mReason);
-                    }
-                } break;
-                case TRANSLUCENT_TIMEOUT_MSG: {
-                    synchronized (mService.mGlobalLock) {
-                        notifyActivityDrawnLocked(null);
-                    }
-                } break;
-            }
-        }
-    }
-
-    int numActivities() {
-        int count = 0;
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            count += mTaskHistory.get(taskNdx).mActivities.size();
-        }
-        return count;
-    }
-
-    ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
-            int windowingMode, int activityType, boolean onTop) {
-        mStackSupervisor = supervisor;
-        mService = supervisor.mService;
-        mHandler = new ActivityStackHandler(supervisor.mLooper);
-        mWindowManager = mService.mWindowManager;
-        mStackId = stackId;
-        mCurrentUser = mService.mAmInternal.getCurrentUserId();
-        mTmpRect2.setEmpty();
-        // Set display id before setting activity and window type to make sure it won't affect
-        // stacks on a wrong display.
-        mDisplayId = display.mDisplayId;
-        setActivityType(activityType);
-        setWindowingMode(windowingMode);
-        mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
-                mTmpRect2);
-        postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
-    }
-
-    T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
-        return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
-                mStackSupervisor.mWindowManager);
-    }
-
-    T getWindowContainerController() {
-        return mWindowContainerController;
-    }
-
-    /**
-     * This should be called when an activity in a child task changes state. This should only
-     * be called from
-     * {@link TaskRecord#onActivityStateChanged(ActivityRecord, ActivityState, String)}.
-     * @param record The {@link ActivityRecord} whose state has changed.
-     * @param state The new state.
-     * @param reason The reason for the change.
-     */
-    void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) {
-        if (record == mResumedActivity && state != RESUMED) {
-            setResumedActivity(null, reason + " - onActivityStateChanged");
-        }
-
-        if (state == RESUMED) {
-            if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
-                    + reason);
-            setResumedActivity(record, reason + " - onActivityStateChanged");
-            if (record == mStackSupervisor.getTopResumedActivity()) {
-                // TODO(b/111361570): Support multiple focused apps in WM
-                mService.setResumedActivityUncheckLocked(record, reason);
-            }
-            mStackSupervisor.mRecentTasks.add(record.getTask());
-        }
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newParentConfig) {
-        final int prevWindowingMode = getWindowingMode();
-        final boolean prevIsAlwaysOnTop = isAlwaysOnTop();
-        super.onConfigurationChanged(newParentConfig);
-        final ActivityDisplay display = getDisplay();
-        if (display == null) {
-          return;
-        }
-        if (prevWindowingMode != getWindowingMode()) {
-            display.onStackWindowingModeChanged(this);
-        }
-        if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
-            // Since always on top is only on when the stack is freeform or pinned, the state
-            // can be toggled when the windowing mode changes. We must make sure the stack is
-            // placed properly when always on top state changes.
-            display.positionChildAtTop(this, false /* includingParents */);
-        }
-    }
-
-    @Override
-    public void setWindowingMode(int windowingMode) {
-        setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
-                false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */);
-    }
-
-    /**
-     * A transient windowing mode is one which activities enter into temporarily. Examples of this
-     * are Split window modes and pip. Non-transient modes are modes that displays can adopt.
-     *
-     * @param windowingMode the windowingMode to test for transient-ness.
-     * @return {@code true} if the windowing mode is transient, {@code false} otherwise.
-     */
-    private static boolean isTransientWindowingMode(int windowingMode) {
-        // TODO(b/114842032): add PIP if/when it uses mode transitions instead of task reparenting
-        return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-    }
-
-    /**
-     * Specialization of {@link #setWindowingMode(int)} for this subclass.
-     *
-     * @param preferredWindowingMode the preferred windowing mode. This may not be honored depending
-     *         on the state of things. For example, WINDOWING_MODE_UNDEFINED will resolve to the
-     *         previous non-transient mode if this stack is currently in a transient mode.
-     * @param animate Can be used to prevent animation.
-     * @param showRecents Controls whether recents is shown on the other side of a split while
-     *         entering split mode.
-     * @param enteringSplitScreenMode {@code true} if entering split mode.
-     * @param deferEnsuringVisibility Whether visibility updates are deferred. This is set when
-     *         many operations (which can effect visibility) are being performed in bulk.
-     */
-    void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents,
-            boolean enteringSplitScreenMode, boolean deferEnsuringVisibility) {
-        final boolean creating = mWindowContainerController == null;
-        final int currentMode = getWindowingMode();
-        final int currentOverrideMode = getOverrideWindowingMode();
-        final ActivityDisplay display = getDisplay();
-        final TaskRecord topTask = topTask();
-        final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
-        int windowingMode = preferredWindowingMode;
-        if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED
-                && isTransientWindowingMode(currentMode)) {
-            // Leaving a transient mode. Interpret UNDEFINED as "restore"
-            windowingMode = mRestoreOverrideWindowingMode;
-        }
-        mTmpOptions.setLaunchWindowingMode(windowingMode);
-
-        // Need to make sure windowing mode is supported. If we in the process of creating the stack
-        // no need to resolve the windowing mode again as it is already resolved to the right mode.
-        if (!creating) {
-            windowingMode = display.validateWindowingMode(windowingMode,
-                    null /* ActivityRecord */, topTask, getActivityType());
-        }
-        if (splitScreenStack == this
-                && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
-            // Resolution to split-screen secondary for the primary split-screen stack means
-            // we want to leave split-screen mode.
-            windowingMode = mRestoreOverrideWindowingMode;
-        }
-
-        final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryStack();
-
-        // Don't send non-resizeable notifications if the windowing mode changed was a side effect
-        // of us entering split-screen mode.
-        final boolean sendNonResizeableNotification = !enteringSplitScreenMode;
-        // Take any required action due to us not supporting the preferred windowing mode.
-        if (alreadyInSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
-                && sendNonResizeableNotification && isActivityTypeStandardOrUndefined()) {
-            final boolean preferredSplitScreen =
-                    preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                    || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-            if (preferredSplitScreen || creating) {
-                // Looks like we can't launch in split screen mode or the stack we are launching
-                // doesn't support split-screen mode, go ahead an dismiss split-screen and display a
-                // warning toast about it.
-                mService.getTaskChangeNotificationController().notifyActivityDismissingDockedStack();
-                display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_UNDEFINED,
-                        false /* animate */, false /* showRecents */,
-                        false /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */);
-            }
-        }
-
-        if (currentMode == windowingMode) {
-            // You are already in the window mode, so we can skip most of the work below. However,
-            // it's possible that we have inherited the current windowing mode from a parent. So,
-            // fulfill this method's contract by setting the override mode directly.
-            getOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
-            return;
-        }
-
-        final WindowManagerService wm = mService.mWindowManager;
-        final ActivityRecord topActivity = getTopActivity();
-
-        // For now, assume that the Stack's windowing mode is what will actually be used
-        // by it's activities. In the future, there may be situations where this doesn't
-        // happen; so at that point, this message will need to handle that.
-        int likelyResolvedMode = windowingMode;
-        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
-            final ConfigurationContainer parent = getParent();
-            likelyResolvedMode = parent != null ? parent.getWindowingMode()
-                    : WINDOWING_MODE_FULLSCREEN;
-        }
-        if (sendNonResizeableNotification && likelyResolvedMode != WINDOWING_MODE_FULLSCREEN
-                && topActivity != null && topActivity.isNonResizableOrForcedResizable()
-                && !topActivity.noDisplay) {
-            // Inform the user that they are starting an app that may not work correctly in
-            // multi-window mode.
-            final String packageName = topActivity.appInfo.packageName;
-            mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
-                    topTask.taskId, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN, packageName);
-        }
-
-        wm.deferSurfaceLayout();
-        try {
-            if (!animate && topActivity != null) {
-                mStackSupervisor.mNoAnimActivities.add(topActivity);
-            }
-            super.setWindowingMode(windowingMode);
-            // setWindowingMode triggers an onConfigurationChanged cascade which can result in a
-            // different resolved windowing mode (usually when preferredWindowingMode is UNDEFINED).
-            windowingMode = getWindowingMode();
-
-            if (creating) {
-                // Nothing else to do if we don't have a window container yet. E.g. call from ctor.
-                return;
-            }
-
-            if (windowingMode == WINDOWING_MODE_PINNED || currentMode == WINDOWING_MODE_PINNED) {
-                // TODO: Need to remove use of PinnedActivityStack for this to be supported.
-                // NOTE: Need to ASS.scheduleUpdatePictureInPictureModeIfNeeded() in
-                // setWindowModeUnchecked() when this support is added. See TaskRecord.reparent()
-                throw new IllegalArgumentException(
-                        "Changing pinned windowing mode not currently supported");
-            }
-
-            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && splitScreenStack != null) {
-                // We already have a split-screen stack in this display, so just move the tasks over.
-                // TODO: Figure-out how to do all the stuff in
-                // AMS.setTaskWindowingModeSplitScreenPrimary
-                throw new IllegalArgumentException("Setting primary split-screen windowing mode"
-                        + " while there is already one isn't currently supported");
-                //return;
-            }
-            if (isTransientWindowingMode(windowingMode) && !isTransientWindowingMode(currentMode)) {
-                mRestoreOverrideWindowingMode = currentOverrideMode;
-            }
-
-            mTmpRect2.setEmpty();
-            if (windowingMode != WINDOWING_MODE_FULLSCREEN) {
-                mWindowContainerController.getRawBounds(mTmpRect2);
-            }
-
-            if (!Objects.equals(getOverrideBounds(), mTmpRect2)) {
-                resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
-            }
-        } finally {
-            if (showRecents && !alreadyInSplitScreenMode && mDisplayId == DEFAULT_DISPLAY
-                    && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                // Make sure recents stack exist when creating a dock stack as it normally needs to
-                // be on the other side of the docked stack and we make visibility decisions based
-                // on that.
-                // TODO: This is only here to help out with the case where recents stack doesn't
-                // exist yet. For that case the initial size of the split-screen stack will be the
-                // the one where the home stack is visible since recents isn't visible yet, but the
-                // divider will be off. I think we should just make the initial bounds that of home
-                // so that the divider matches and remove this logic.
-                // TODO: This is currently only called when entering split-screen while in another
-                // task, and from the tests
-                // TODO (b/78247419): Check if launcher and overview are same then move home stack
-                // instead of recents stack. Then fix the rotation animation from fullscreen to
-                // minimized mode
-                final ActivityStack recentStack = display.getOrCreateStack(
-                        WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS,
-                        true /* onTop */);
-                recentStack.moveToFront("setWindowingMode");
-                // If task moved to docked stack - show recents if needed.
-                mService.mWindowManager.showRecentApps();
-            }
-            wm.continueSurfaceLayout();
-        }
-
-        if (!deferEnsuringVisibility) {
-            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-        }
-    }
-
-    @Override
-    public boolean isCompatible(int windowingMode, int activityType) {
-        // TODO: Should we just move this to ConfigurationContainer?
-        if (activityType == ACTIVITY_TYPE_UNDEFINED) {
-            // Undefined activity types end up in a standard stack once the stack is created on a
-            // display, so they should be considered compatible.
-            activityType = ACTIVITY_TYPE_STANDARD;
-        }
-        return super.isCompatible(windowingMode, activityType);
-    }
-
-    /** Adds the stack to specified display and calls WindowManager to do the same. */
-    void reparent(ActivityDisplay activityDisplay, boolean onTop) {
-        // TODO: We should probably resolve the windowing mode for the stack on the new display here
-        // so that it end up in a compatible mode in the new display. e.g. split-screen secondary.
-        removeFromDisplay();
-        // Reparent the window container before we try to update the position when adding it to
-        // the new display below
-        mTmpRect2.setEmpty();
-        mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop);
-        postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
-        adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */);
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-        // Update visibility of activities before notifying WM. This way it won't try to resize
-        // windows that are no longer visible.
-        mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
-                !PRESERVE_WINDOWS);
-    }
-
-    /**
-     * Updates internal state after adding to new display.
-     * @param activityDisplay New display to which this stack was attached.
-     * @param bounds Updated bounds.
-     */
-    private void postAddToDisplay(ActivityDisplay activityDisplay, Rect bounds, boolean onTop) {
-        mDisplayId = activityDisplay.mDisplayId;
-        setBounds(bounds);
-        onParentChanged();
-
-        activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
-        if (inSplitScreenPrimaryWindowingMode()) {
-            // If we created a docked stack we want to resize it so it resizes all other stacks
-            // in the system.
-            mStackSupervisor.resizeDockedStackLocked(
-                    getOverrideBounds(), null, null, null, null, PRESERVE_WINDOWS);
-        }
-    }
-
-    /**
-     * Updates the inner state of the stack to remove it from its current parent, so it can be
-     * either destroyed completely or re-parented.
-     */
-    private void removeFromDisplay() {
-        final ActivityDisplay display = getDisplay();
-        if (display != null) {
-            display.removeChild(this);
-        }
-        mDisplayId = INVALID_DISPLAY;
-    }
-
-    /** Removes the stack completely. Also calls WindowManager to do the same on its side. */
-    void remove() {
-        removeFromDisplay();
-        mWindowContainerController.removeContainer();
-        mWindowContainerController = null;
-        onParentChanged();
-    }
-
-    ActivityDisplay getDisplay() {
-        return mStackSupervisor.getActivityDisplay(mDisplayId);
-    }
-
-    /**
-     * @see #getStackDockedModeBounds(Rect, Rect, Rect, boolean)
-     */
-    void getStackDockedModeBounds(Rect currentTempTaskBounds, Rect outStackBounds,
-            Rect outTempTaskBounds, boolean ignoreVisibility) {
-        mWindowContainerController.getStackDockedModeBounds(currentTempTaskBounds,
-                outStackBounds, outTempTaskBounds, ignoreVisibility);
-    }
-
-    void prepareFreezingTaskBounds() {
-        mWindowContainerController.prepareFreezingTaskBounds();
-    }
-
-    void getWindowContainerBounds(Rect outBounds) {
-        if (mWindowContainerController != null) {
-            mWindowContainerController.getBounds(outBounds);
-            return;
-        }
-        outBounds.setEmpty();
-    }
-
-    void getBoundsForNewConfiguration(Rect outBounds) {
-        mWindowContainerController.getBoundsForNewConfiguration(outBounds);
-    }
-
-    void positionChildWindowContainerAtTop(TaskRecord child) {
-        mWindowContainerController.positionChildAtTop(child.getWindowContainerController(),
-                true /* includingParents */);
-    }
-
-    void positionChildWindowContainerAtBottom(TaskRecord child) {
-        mWindowContainerController.positionChildAtBottom(child.getWindowContainerController(),
-                true /* includingParents */);
-    }
-
-    /**
-     * Returns whether to defer the scheduling of the multi-window mode.
-     */
-    boolean deferScheduleMultiWindowModeChanged() {
-        return false;
-    }
-
-    /**
-     * Defers updating the bounds of the stack. If the stack was resized/repositioned while
-     * deferring, the bounds will update in {@link #continueUpdateBounds()}.
-     */
-    void deferUpdateBounds() {
-        if (!mUpdateBoundsDeferred) {
-            mUpdateBoundsDeferred = true;
-            mUpdateBoundsDeferredCalled = false;
-        }
-    }
-
-    /**
-     * Continues updating bounds after updates have been deferred. If there was a resize attempt
-     * between {@link #deferUpdateBounds()} and {@link #continueUpdateBounds()}, the stack will
-     * be resized to that bounds.
-     */
-    void continueUpdateBounds() {
-        final boolean wasDeferred = mUpdateBoundsDeferred;
-        mUpdateBoundsDeferred = false;
-        if (wasDeferred && mUpdateBoundsDeferredCalled) {
-            resize(mDeferredBounds.isEmpty() ? null : mDeferredBounds,
-                    mDeferredTaskBounds.isEmpty() ? null : mDeferredTaskBounds,
-                    mDeferredTaskInsetBounds.isEmpty() ? null : mDeferredTaskInsetBounds);
-        }
-    }
-
-    boolean updateBoundsAllowed(Rect bounds, Rect tempTaskBounds,
-            Rect tempTaskInsetBounds) {
-        if (!mUpdateBoundsDeferred) {
-            return true;
-        }
-        if (bounds != null) {
-            mDeferredBounds.set(bounds);
-        } else {
-            mDeferredBounds.setEmpty();
-        }
-        if (tempTaskBounds != null) {
-            mDeferredTaskBounds.set(tempTaskBounds);
-        } else {
-            mDeferredTaskBounds.setEmpty();
-        }
-        if (tempTaskInsetBounds != null) {
-            mDeferredTaskInsetBounds.set(tempTaskInsetBounds);
-        } else {
-            mDeferredTaskInsetBounds.setEmpty();
-        }
-        mUpdateBoundsDeferredCalled = true;
-        return false;
-    }
-
-    @Override
-    public int setBounds(Rect bounds) {
-        return super.setBounds(!inMultiWindowMode() ? null : bounds);
-    }
-
-    ActivityRecord topRunningActivityLocked() {
-        return topRunningActivityLocked(false /* focusableOnly */);
-    }
-
-    void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
-        outActivities.clear();
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            mTaskHistory.get(taskNdx).getAllRunningVisibleActivitiesLocked(outActivities);
-        }
-    }
-
-    ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked();
-            if (r != null && (!focusableOnly || r.isFocusable())) {
-                return r;
-            }
-        }
-        return null;
-    }
-
-    ActivityRecord topRunningNonOverlayTaskActivity() {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (!r.finishing && !r.mTaskOverlay) {
-                    return r;
-                }
-            }
-        }
-        return null;
-    }
-
-    ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = activities.get(activityNdx);
-                if (!r.finishing && !r.delayedResume && r != notTop && r.okToShowLocked()) {
-                    return r;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * This is a simplified version of topRunningActivityLocked that provides a number of
-     * optional skip-over modes.  It is intended for use with the ActivityController hook only.
-     *
-     * @param token If non-null, any history records matching this token will be skipped.
-     * @param taskId If non-zero, we'll attempt to skip over records with the same task ID.
-     *
-     * @return Returns the HistoryRecord of the next activity on the stack.
-     */
-    final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            TaskRecord task = mTaskHistory.get(taskNdx);
-            if (task.taskId == taskId) {
-                continue;
-            }
-            ArrayList<ActivityRecord> activities = task.mActivities;
-            for (int i = activities.size() - 1; i >= 0; --i) {
-                final ActivityRecord r = activities.get(i);
-                // Note: the taskId check depends on real taskId fields being non-zero
-                if (!r.finishing && (token != r.appToken) && r.okToShowLocked()) {
-                    return r;
-                }
-            }
-        }
-        return null;
-    }
-
-    ActivityRecord getTopActivity() {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ActivityRecord r = mTaskHistory.get(taskNdx).getTopActivity();
-            if (r != null) {
-                return r;
-            }
-        }
-        return null;
-    }
-
-    final TaskRecord topTask() {
-        final int size = mTaskHistory.size();
-        if (size > 0) {
-            return mTaskHistory.get(size - 1);
-        }
-        return null;
-    }
-
-    private TaskRecord bottomTask() {
-        if (mTaskHistory.isEmpty()) {
-            return null;
-        }
-        return mTaskHistory.get(0);
-    }
-
-    TaskRecord taskForIdLocked(int id) {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            if (task.taskId == id) {
-                return task;
-            }
-        }
-        return null;
-    }
-
-    ActivityRecord isInStackLocked(IBinder token) {
-        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        return isInStackLocked(r);
-    }
-
-    ActivityRecord isInStackLocked(ActivityRecord r) {
-        if (r == null) {
-            return null;
-        }
-        final TaskRecord task = r.getTask();
-        final ActivityStack stack = r.getStack();
-        if (stack != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) {
-            if (stack != this) Slog.w(TAG,
-                    "Illegal state! task does not point to stack it is in.");
-            return r;
-        }
-        return null;
-    }
-
-    boolean isInStackLocked(TaskRecord task) {
-        return mTaskHistory.contains(task);
-    }
-
-    /** Checks if there are tasks with specific UID in the stack. */
-    boolean isUidPresent(int uid) {
-        for (TaskRecord task : mTaskHistory) {
-            for (ActivityRecord r : task.mActivities) {
-                if (r.getUid() == uid) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /** Get all UIDs that are present in the stack. */
-    void getPresentUIDs(IntArray presentUIDs) {
-        for (TaskRecord task : mTaskHistory) {
-            for (ActivityRecord r : task.mActivities) {
-                presentUIDs.add(r.getUid());
-            }
-        }
-    }
-
-    final void removeActivitiesFromLRUListLocked(TaskRecord task) {
-        for (ActivityRecord r : task.mActivities) {
-            mLRUActivities.remove(r);
-        }
-    }
-
-    final boolean updateLRUListLocked(ActivityRecord r) {
-        final boolean hadit = mLRUActivities.remove(r);
-        mLRUActivities.add(r);
-        return hadit;
-    }
-
-    final boolean isHomeOrRecentsStack() {
-        return isActivityTypeHome() || isActivityTypeRecents();
-    }
-
-    final boolean isOnHomeDisplay() {
-        return mDisplayId == DEFAULT_DISPLAY;
-    }
-
-    private boolean returnsToHomeStack() {
-        return !inMultiWindowMode()
-                && !mTaskHistory.isEmpty()
-                && mTaskHistory.get(0).returnsToHomeStack();
-    }
-
-    void moveToFront(String reason) {
-        moveToFront(reason, null);
-    }
-
-    /**
-     * @param reason The reason for moving the stack to the front.
-     * @param task If non-null, the task will be moved to the top of the stack.
-     * */
-    void moveToFront(String reason, TaskRecord task) {
-        if (!isAttached()) {
-            return;
-        }
-
-        final ActivityDisplay display = getDisplay();
-
-        if (inSplitScreenSecondaryWindowingMode()) {
-            // If the stack is in split-screen seconardy mode, we need to make sure we move the
-            // primary split-screen stack forward in the case it is currently behind a fullscreen
-            // stack so both halves of the split-screen appear on-top and the fullscreen stack isn't
-            // cutting between them.
-            // TODO(b/70677280): This is a workaround until we can fix as part of b/70677280.
-            final ActivityStack topFullScreenStack =
-                    display.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
-            if (topFullScreenStack != null) {
-                final ActivityStack primarySplitScreenStack = display.getSplitScreenPrimaryStack();
-                if (display.getIndexOf(topFullScreenStack)
-                        > display.getIndexOf(primarySplitScreenStack)) {
-                    primarySplitScreenStack.moveToFront(reason + " splitScreenToTop");
-                }
-            }
-        }
-
-        if (!isActivityTypeHome() && returnsToHomeStack()) {
-            // Make sure the home stack is behind this stack since that is where we should return to
-            // when this stack is no longer visible.
-            display.moveHomeStackToFront(reason + " returnToHome");
-        }
-
-        final boolean movingTask = task != null;
-        display.positionChildAtTop(this, !movingTask /* includingParents */, reason);
-        if (movingTask) {
-            // This also moves the entire hierarchy branch to top, including parents
-            insertTaskAtTop(task, null /* starting */);
-        }
-    }
-
-    /**
-     * @param reason The reason for moving the stack to the back.
-     * @param task If non-null, the task will be moved to the bottom of the stack.
-     **/
-    void moveToBack(String reason, TaskRecord task) {
-        if (!isAttached()) {
-            return;
-        }
-
-        /**
-         * The intent behind moving a primary split screen stack to the back is usually to hide
-         * behind the home stack. Exit split screen in this case.
-         */
-        if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            setWindowingMode(WINDOWING_MODE_UNDEFINED);
-        }
-
-        getDisplay().positionChildAtBottom(this, reason);
-        if (task != null) {
-            // TODO(b/111541062): We probably don't want to change display z-order to bottom just
-            // because one of its stacks moved to bottom.
-            insertTaskAtBottom(task);
-        }
-    }
-
-    boolean isFocusable() {
-        final ActivityRecord r = topRunningActivityLocked();
-        return mStackSupervisor.isFocusable(this, r != null && r.isFocusable());
-    }
-
-    boolean isFocusableAndVisible() {
-        return isFocusable() && shouldBeVisible(null /* starting */);
-    }
-
-    final boolean isAttached() {
-        return getParent() != null;
-    }
-
-    /**
-     * Returns the top activity in any existing task matching the given Intent in the input result.
-     * Returns null if no such task is found.
-     */
-    void findTaskLocked(ActivityRecord target, FindTaskResult result) {
-        Intent intent = target.intent;
-        ActivityInfo info = target.info;
-        ComponentName cls = intent.getComponent();
-        if (info.targetActivity != null) {
-            cls = new ComponentName(info.packageName, info.targetActivity);
-        }
-        final int userId = UserHandle.getUserId(info.applicationInfo.uid);
-        boolean isDocument = intent != null & intent.isDocument();
-        // If documentData is non-null then it must match the existing task data.
-        Uri documentData = isDocument ? intent.getData() : null;
-
-        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + target + " in " + this);
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            if (task.voiceSession != null) {
-                // We never match voice sessions; those always run independently.
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": voice session");
-                continue;
-            }
-            if (task.userId != userId) {
-                // Looking for a different task.
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": different user");
-                continue;
-            }
-
-            // Overlays should not be considered as the task's logical top activity.
-            final ActivityRecord r = task.getTopActivity(false /* includeOverlays */);
-            if (r == null || r.finishing || r.userId != userId ||
-                    r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
-                continue;
-            }
-            if (!r.hasCompatibleActivityType(target)) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch activity type");
-                continue;
-            }
-
-            final Intent taskIntent = task.intent;
-            final Intent affinityIntent = task.affinityIntent;
-            final boolean taskIsDocument;
-            final Uri taskDocumentData;
-            if (taskIntent != null && taskIntent.isDocument()) {
-                taskIsDocument = true;
-                taskDocumentData = taskIntent.getData();
-            } else if (affinityIntent != null && affinityIntent.isDocument()) {
-                taskIsDocument = true;
-                taskDocumentData = affinityIntent.getData();
-            } else {
-                taskIsDocument = false;
-                taskDocumentData = null;
-            }
-
-            if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls="
-                    + taskIntent.getComponent().flattenToShortString()
-                    + "/aff=" + r.getTask().rootAffinity + " to new cls="
-                    + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
-            // TODO Refactor to remove duplications. Check if logic can be simplified.
-            if (taskIntent != null && taskIntent.getComponent() != null &&
-                    taskIntent.getComponent().compareTo(cls) == 0 &&
-                    Objects.equals(documentData, taskDocumentData)) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
-                //dump();
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS,
-                        "For Intent " + intent + " bringing to top: " + r.intent);
-                result.mRecord = r;
-                result.mIdealMatch = true;
-                break;
-            } else if (affinityIntent != null && affinityIntent.getComponent() != null &&
-                    affinityIntent.getComponent().compareTo(cls) == 0 &&
-                    Objects.equals(documentData, taskDocumentData)) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
-                //dump();
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS,
-                        "For Intent " + intent + " bringing to top: " + r.intent);
-                result.mRecord = r;
-                result.mIdealMatch = true;
-                break;
-            } else if (!isDocument && !taskIsDocument
-                    && result.mRecord == null && task.rootAffinity != null) {
-                if (task.rootAffinity.equals(target.taskAffinity)) {
-                    if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!");
-                    // It is possible for multiple tasks to have the same root affinity especially
-                    // if they are in separate stacks. We save off this candidate, but keep looking
-                    // to see if there is a better candidate.
-                    result.mRecord = r;
-                    result.mIdealMatch = false;
-                }
-            } else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
-        }
-    }
-
-    /**
-     * Returns the first activity (starting from the top of the stack) that
-     * is the same as the given activity.  Returns null if no such activity
-     * is found.
-     */
-    ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
-                                      boolean compareIntentFilters) {
-        ComponentName cls = intent.getComponent();
-        if (info.targetActivity != null) {
-            cls = new ComponentName(info.packageName, info.targetActivity);
-        }
-        final int userId = UserHandle.getUserId(info.applicationInfo.uid);
-
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = activities.get(activityNdx);
-                if (!r.okToShowLocked()) {
-                    continue;
-                }
-                if (!r.finishing && r.userId == userId) {
-                    if (compareIntentFilters) {
-                        if (r.intent.filterEquals(intent)) {
-                            return r;
-                        }
-                    } else {
-                        if (r.intent.getComponent().equals(cls)) {
-                            return r;
-                        }
-                    }
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /*
-     * Move the activities around in the stack to bring a user to the foreground.
-     */
-    final void switchUserLocked(int userId) {
-        if (mCurrentUser == userId) {
-            return;
-        }
-        mCurrentUser = userId;
-
-        // Move userId's tasks to the top.
-        int index = mTaskHistory.size();
-        for (int i = 0; i < index; ) {
-            final TaskRecord task = mTaskHistory.get(i);
-
-            if (task.okToShowLocked()) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() +
-                        " moving " + task + " to top");
-                mTaskHistory.remove(i);
-                mTaskHistory.add(task);
-                --index;
-                // Use same value for i.
-            } else {
-                ++i;
-            }
-        }
-    }
-
-    void minimalResumeActivityLocked(ActivityRecord r) {
-        if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + r + " (starting new instance)"
-                + " callers=" + Debug.getCallers(5));
-        r.setState(RESUMED, "minimalResumeActivityLocked");
-        r.completeResumeLocked();
-        if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE,
-                "Launch completed; removing icicle of " + r.icicle);
-    }
-
-    private void clearLaunchTime(ActivityRecord r) {
-        // Make sure that there is no activity waiting for this to launch.
-        if (!mStackSupervisor.mWaitingActivityLaunched.isEmpty()) {
-            mStackSupervisor.removeTimeoutsForActivityLocked(r);
-            mStackSupervisor.scheduleIdleTimeoutLocked(r);
-        }
-    }
-
-    void awakeFromSleepingLocked() {
-        // Ensure activities are no longer sleeping.
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                activities.get(activityNdx).setSleeping(false);
-            }
-        }
-        if (mPausingActivity != null) {
-            Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
-            activityPausedLocked(mPausingActivity.appToken, true);
-        }
-    }
-
-    void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
-        final String packageName = aInfo.packageName;
-        final int userId = UserHandle.getUserId(aInfo.uid);
-
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final List<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord ar = activities.get(activityNdx);
-
-                if ((userId == ar.userId) && packageName.equals(ar.packageName)) {
-                    ar.updateApplicationInfo(aInfo);
-                }
-            }
-        }
-    }
-
-    void checkReadyForSleep() {
-        if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
-            mStackSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
-        }
-    }
-
-    /**
-     * Tries to put the activities in the stack to sleep.
-     *
-     * If the stack is not in a state where its activities can be put to sleep, this function will
-     * start any necessary actions to move the stack into such a state. It is expected that this
-     * function get called again when those actions complete.
-     *
-     * @param shuttingDown true when the called because the device is shutting down.
-     * @return true if the stack finished going to sleep, false if the stack only started the
-     * process of going to sleep (checkReadyForSleep will be called when that process finishes).
-     */
-    boolean goToSleepIfPossible(boolean shuttingDown) {
-        boolean shouldSleep = true;
-
-        if (mResumedActivity != null) {
-            // Still have something resumed; can't sleep until it is paused.
-            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep needs to pause " + mResumedActivity);
-            if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
-                    "Sleep => pause with userLeaving=false");
-
-            startPausingLocked(false, true, null, false);
-            shouldSleep = false ;
-        } else if (mPausingActivity != null) {
-            // Still waiting for something to pause; can't sleep yet.
-            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still waiting to pause " + mPausingActivity);
-            shouldSleep = false;
-        }
-
-        if (!shuttingDown) {
-            if (containsActivityFromStack(mStackSupervisor.mStoppingActivities)) {
-                // Still need to tell some activities to stop; can't sleep yet.
-                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
-                        + mStackSupervisor.mStoppingActivities.size() + " activities");
-
-                mStackSupervisor.scheduleIdleLocked();
-                shouldSleep = false;
-            }
-
-            if (containsActivityFromStack(mStackSupervisor.mGoingToSleepActivities)) {
-                // Still need to tell some activities to sleep; can't sleep yet.
-                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to sleep "
-                        + mStackSupervisor.mGoingToSleepActivities.size() + " activities");
-                shouldSleep = false;
-            }
-        }
-
-        if (shouldSleep) {
-            goToSleep();
-        }
-
-        return shouldSleep;
-    }
-
-    void goToSleep() {
-        // Ensure visibility without updating configuration, as activities are about to sleep.
-        ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
-                !PRESERVE_WINDOWS);
-
-        // Make sure any paused or stopped but visible activities are now sleeping.
-        // This ensures that the activity's onStop() is called.
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (r.isState(STOPPING, STOPPED, PAUSED, PAUSING)) {
-                    r.setSleeping(true);
-                }
-            }
-        }
-    }
-
-    private boolean containsActivityFromStack(List<ActivityRecord> rs) {
-        for (ActivityRecord r : rs) {
-            if (r.getStack() == this) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Schedule a pause timeout in case the app doesn't respond. We don't give it much time because
-     * this directly impacts the responsiveness seen by the user.
-     */
-    private void schedulePauseTimeout(ActivityRecord r) {
-        final Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
-        msg.obj = r;
-        r.pauseTime = SystemClock.uptimeMillis();
-        mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
-        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
-    }
-
-    /**
-     * Start pausing the currently resumed activity.  It is an error to call this if there
-     * is already an activity being paused or there is no resumed activity.
-     *
-     * @param userLeaving True if this should result in an onUserLeaving to the current activity.
-     * @param uiSleeping True if this is happening with the user interface going to sleep (the
-     * screen turning off).
-     * @param resuming The activity we are currently trying to resume or null if this is not being
-     *                 called as part of resuming the top activity, so we shouldn't try to instigate
-     *                 a resume here if not null.
-     * @param pauseImmediately True if the caller does not want to wait for the activity callback to
-     *                         complete pausing.
-     * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
-     * it to tell us when it is done.
-     */
-    final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
-            ActivityRecord resuming, boolean pauseImmediately) {
-        if (mPausingActivity != null) {
-            Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
-                    + " state=" + mPausingActivity.getState());
-            if (!shouldSleepActivities()) {
-                // Avoid recursion among check for sleep and complete pause during sleeping.
-                // Because activity will be paused immediately after resume, just let pause
-                // be completed by the order of activity paused from clients.
-                completePauseLocked(false, resuming);
-            }
-        }
-        ActivityRecord prev = mResumedActivity;
-
-        if (prev == null) {
-            if (resuming == null) {
-                Slog.wtf(TAG, "Trying to pause when nothing is resumed");
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-            }
-            return false;
-        }
-
-        if (prev == resuming) {
-            Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
-            return false;
-        }
-
-        if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSING: " + prev);
-        else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev);
-        mPausingActivity = prev;
-        mLastPausedActivity = prev;
-        mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
-                || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
-        prev.setState(PAUSING, "startPausingLocked");
-        prev.getTask().touchActiveTime();
-        clearLaunchTime(prev);
-
-        mService.updateCpuStats();
-
-        if (prev.attachedToProcess()) {
-            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
-            try {
-                EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
-                        prev.shortComponentName, "userLeaving=" + userLeaving);
-                mService.updateUsageStats(prev, false);
-
-                mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
-                        prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
-                                prev.configChangeFlags, pauseImmediately));
-            } catch (Exception e) {
-                // Ignore exception, if process died other code will cleanup.
-                Slog.w(TAG, "Exception thrown during pause", e);
-                mPausingActivity = null;
-                mLastPausedActivity = null;
-                mLastNoHistoryActivity = null;
-            }
-        } else {
-            mPausingActivity = null;
-            mLastPausedActivity = null;
-            mLastNoHistoryActivity = null;
-        }
-
-        // If we are not going to sleep, we want to ensure the device is
-        // awake until the next activity is started.
-        if (!uiSleeping && !mService.isSleepingOrShuttingDownLocked()) {
-            mStackSupervisor.acquireLaunchWakelock();
-        }
-
-        if (mPausingActivity != null) {
-            // Have the window manager pause its key dispatching until the new
-            // activity has started.  If we're pausing the activity just because
-            // the screen is being turned off and the UI is sleeping, don't interrupt
-            // key dispatch; the same activity will pick it up again on wakeup.
-            if (!uiSleeping) {
-                prev.pauseKeyDispatchingLocked();
-            } else if (DEBUG_PAUSE) {
-                 Slog.v(TAG_PAUSE, "Key dispatch not paused for screen off");
-            }
-
-            if (pauseImmediately) {
-                // If the caller said they don't want to wait for the pause, then complete
-                // the pause now.
-                completePauseLocked(false, resuming);
-                return false;
-
-            } else {
-                schedulePauseTimeout(prev);
-                return true;
-            }
-
-        } else {
-            // This activity failed to schedule the
-            // pause, so just treat it as being paused now.
-            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
-            if (resuming == null) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-            }
-            return false;
-        }
-    }
-
-    final void activityPausedLocked(IBinder token, boolean timeout) {
-        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,
-            "Activity paused: token=" + token + ", timeout=" + timeout);
-
-        final ActivityRecord r = isInStackLocked(token);
-        if (r != null) {
-            mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
-            if (mPausingActivity == r) {
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r
-                        + (timeout ? " (due to timeout)" : " (pause complete)"));
-                mService.mWindowManager.deferSurfaceLayout();
-                try {
-                    completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
-                } finally {
-                    mService.mWindowManager.continueSurfaceLayout();
-                }
-                return;
-            } else {
-                EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
-                        r.userId, System.identityHashCode(r), r.shortComponentName,
-                        mPausingActivity != null
-                            ? mPausingActivity.shortComponentName : "(none)");
-                if (r.isState(PAUSING)) {
-                    r.setState(PAUSED, "activityPausedLocked");
-                    if (r.finishing) {
-                        if (DEBUG_PAUSE) Slog.v(TAG,
-                                "Executing finish of failed to pause activity: " + r);
-                        finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false,
-                                "activityPausedLocked");
-                    }
-                }
-            }
-        }
-        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-    }
-
-    private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
-        ActivityRecord prev = mPausingActivity;
-        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev);
-
-        if (prev != null) {
-            prev.setWillCloseOrEnterPip(false);
-            final boolean wasStopping = prev.isState(STOPPING);
-            prev.setState(PAUSED, "completePausedLocked");
-            if (prev.finishing) {
-                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
-                prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
-                        "completedPausedLocked");
-            } else if (prev.hasProcess()) {
-                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
-                        + " wasStopping=" + wasStopping + " visible=" + prev.visible);
-                if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(prev)) {
-                    if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG_PAUSE,
-                            "Complete pause, no longer waiting: " + prev);
-                }
-                if (prev.deferRelaunchUntilPaused) {
-                    // Complete the deferred relaunch that was waiting for pause to complete.
-                    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev);
-                    prev.relaunchActivityLocked(false /* andResume */,
-                            prev.preserveWindowOnDeferredRelaunch);
-                } else if (wasStopping) {
-                    // We are also stopping, the stop request must have gone soon after the pause.
-                    // We can't clobber it, because the stop confirmation will not be handled.
-                    // We don't need to schedule another stop, we only need to let it happen.
-                    prev.setState(STOPPING, "completePausedLocked");
-                } else if (!prev.visible || shouldSleepOrShutDownActivities()) {
-                    // Clear out any deferred client hide we might currently have.
-                    prev.setDeferHidingClient(false);
-                    // If we were visible then resumeTopActivities will release resources before
-                    // stopping.
-                    addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */);
-                }
-            } else {
-                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev);
-                prev = null;
-            }
-            // It is possible the activity was freezing the screen before it was paused.
-            // In that case go ahead and remove the freeze this activity has on the screen
-            // since it is no longer visible.
-            if (prev != null) {
-                prev.stopFreezingScreenLocked(true /*force*/);
-            }
-            mPausingActivity = null;
-        }
-
-        if (resumeNext) {
-            final ActivityStack topStack = mStackSupervisor.getTopDisplayFocusedStack();
-            if (!topStack.shouldSleepOrShutDownActivities()) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(topStack, prev, null);
-            } else {
-                checkReadyForSleep();
-                ActivityRecord top = topStack.topRunningActivityLocked();
-                if (top == null || (prev != null && top != prev)) {
-                    // If there are no more activities available to run, do resume anyway to start
-                    // something. Also if the top activity on the stack is not the just paused
-                    // activity, we need to go ahead and resume it to ensure we complete an
-                    // in-flight app switch.
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-                }
-            }
-        }
-
-        if (prev != null) {
-            prev.resumeKeyDispatchingLocked();
-
-            if (prev.hasProcess() && prev.cpuTimeAtResume > 0) {
-                final long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume;
-                if (diff > 0) {
-                    final Runnable r = PooledLambda.obtainRunnable(
-                            ActivityManagerInternal::updateForegroundTimeIfOnBattery,
-                            mService.mAmInternal, prev.info.packageName,
-                            prev.info.applicationInfo.uid,
-                            diff);
-                    mService.mH.post(r);
-                }
-            }
-            prev.cpuTimeAtResume = 0; // reset it
-        }
-
-        // Notify when the task stack has changed, but only if visibilities changed (not just
-        // focus). Also if there is an active pinned stack - we always want to notify it about
-        // task stack changes, because its positioning may depend on it.
-        if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
-                || getDisplay().hasPinnedStack()) {
-            mService.getTaskChangeNotificationController().notifyTaskStackChanged();
-            mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
-        }
-
-        mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);
-    }
-
-    private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
-        if (!mStackSupervisor.mStoppingActivities.contains(r)) {
-            mStackSupervisor.mStoppingActivities.add(r);
-
-            // Some activity is waiting for another activity to become visible before it's being
-            // stopped, which means that we also want to wait with stopping this one to avoid
-            // flickers.
-            if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.isEmpty()
-                    && !mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) {
-                if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "adding to waiting visible activity=" + r
-                        + " existing=" + mStackSupervisor.mActivitiesWaitingForVisibleActivity);
-                mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(r);
-            }
-        }
-
-        // If we already have a few activities waiting to stop, then give up
-        // on things going idle and start clearing them out. Or if r is the
-        // last of activity of the last task the stack will be empty and must
-        // be cleared immediately.
-        boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE
-                || (r.frontOfTask && mTaskHistory.size() <= 1);
-        if (scheduleIdle || forceIdle) {
-            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle="
-                    + forceIdle + "immediate=" + !idleDelayed);
-            if (!idleDelayed) {
-                mStackSupervisor.scheduleIdleLocked();
-            } else {
-                mStackSupervisor.scheduleIdleTimeoutLocked(r);
-            }
-        } else {
-            checkReadyForSleep();
-        }
-    }
-
-    /**
-     * Returns true if the stack is translucent and can have other contents visible behind it if
-     * needed. A stack is considered translucent if it don't contain a visible or
-     * starting (about to be visible) activity that is fullscreen (opaque).
-     * @param starting The currently starting activity or null if there is none.
-     */
-    @VisibleForTesting
-    boolean isStackTranslucent(ActivityRecord starting) {
-        if (!isAttached() || mForceHidden) {
-            return true;
-        }
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-
-                if (r.finishing) {
-                    // We don't factor in finishing activities when determining translucency since
-                    // they will be gone soon.
-                    continue;
-                }
-
-                if (!r.visibleIgnoringKeyguard && r != starting) {
-                    // Also ignore invisible activities that are not the currently starting
-                    // activity (about to be visible).
-                    continue;
-                }
-
-                if (r.fullscreen || r.hasWallpaper) {
-                    // Stack isn't translucent if it has at least one fullscreen activity
-                    // that is visible.
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    boolean isTopStackOnDisplay() {
-        final ActivityDisplay display = getDisplay();
-        return display != null && display.isTopStack(this);
-    }
-
-    /**
-     * @return {@code true} if this is the focused stack on its current display, {@code false}
-     * otherwise.
-     */
-    boolean isFocusedStackOnDisplay() {
-        final ActivityDisplay display = getDisplay();
-        return display != null && this == display.getFocusedStack();
-    }
-
-    boolean isTopActivityVisible() {
-        final ActivityRecord topActivity = getTopActivity();
-        return topActivity != null && topActivity.visible;
-    }
-
-    /**
-     * Returns true if the stack should be visible.
-     *
-     * @param starting The currently starting activity or null if there is none.
-     */
-    boolean shouldBeVisible(ActivityRecord starting) {
-        if (!isAttached() || mForceHidden) {
-            return false;
-        }
-
-        final ActivityRecord top = topRunningActivityLocked();
-        if (top == null && isInStackLocked(starting) == null && !isTopStackOnDisplay()) {
-            // Shouldn't be visible if you don't have any running activities, not starting one, and
-            // not the top stack on display.
-            return false;
-        }
-
-        final ActivityDisplay display = getDisplay();
-        boolean gotSplitScreenStack = false;
-        boolean gotOpaqueSplitScreenPrimary = false;
-        boolean gotOpaqueSplitScreenSecondary = false;
-        final int windowingMode = getWindowingMode();
-        final boolean isAssistantType = isActivityTypeAssistant();
-        for (int i = display.getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack other = display.getChildAt(i);
-            if (other == this) {
-                // Should be visible if there is no other stack occluding it.
-                return true;
-            }
-
-            final int otherWindowingMode = other.getWindowingMode();
-
-            if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
-                // In this case the home stack isn't resizeable even though we are in split-screen
-                // mode. We still want the primary splitscreen stack to be visible as there will be
-                // a slight hint of it in the status bar area above the non-resizeable home
-                // activity. In addition, if the fullscreen assistant is over primary splitscreen
-                // stack, the stack should still be visible in the background as long as the recents
-                // animation is running.
-                final int activityType = other.getActivityType();
-                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                    if (activityType == ACTIVITY_TYPE_HOME
-                            || (activityType == ACTIVITY_TYPE_ASSISTANT
-                                    && mWindowManager.getRecentsAnimationController() != null)) {
-                       return true;
-                    }
-                }
-                if (other.isStackTranslucent(starting)) {
-                    // Can be visible behind a translucent fullscreen stack.
-                    continue;
-                }
-                return false;
-            } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                    && !gotOpaqueSplitScreenPrimary) {
-                gotSplitScreenStack = true;
-                gotOpaqueSplitScreenPrimary =
-                        !other.isStackTranslucent(starting);
-                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                        && gotOpaqueSplitScreenPrimary) {
-                    // Can not be visible behind another opaque stack in split-screen-primary mode.
-                    return false;
-                }
-            } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                    && !gotOpaqueSplitScreenSecondary) {
-                gotSplitScreenStack = true;
-                gotOpaqueSplitScreenSecondary =
-                        !other.isStackTranslucent(starting);
-                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                        && gotOpaqueSplitScreenSecondary) {
-                    // Can not be visible behind another opaque stack in split-screen-secondary mode.
-                    return false;
-                }
-            }
-            if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
-                // Can not be visible if we are in split-screen windowing mode and both halves of
-                // the screen are opaque.
-                return false;
-            }
-            if (isAssistantType && gotSplitScreenStack) {
-                // Assistant stack can't be visible behind split-screen. In addition to this not
-                // making sense, it also works around an issue here we boost the z-order of the
-                // assistant window surfaces in window manager whenever it is visible.
-                return false;
-            }
-        }
-
-        // Well, nothing is stopping you from being visible...
-        return true;
-    }
-
-    final int rankTaskLayers(int baseLayer) {
-        int layer = 0;
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            ActivityRecord r = task.topRunningActivityLocked();
-            if (r == null || r.finishing || !r.visible) {
-                task.mLayerRank = -1;
-            } else {
-                task.mLayerRank = baseLayer + layer++;
-            }
-        }
-        return layer;
-    }
-
-    /**
-     * Make sure that all activities that need to be visible in the stack (that is, they
-     * currently can be seen by the user) actually are and update their configuration.
-     */
-    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
-            boolean preserveWindows) {
-        ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
-                true /* notifyClients */);
-    }
-
-    /**
-     * Ensure visibility with an option to also update the configuration of visible activities.
-     * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
-     * @see ActivityStackSupervisor#ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
-     */
-    // TODO: Should be re-worked based on the fact that each task as a stack in most cases.
-    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients) {
-        mTopActivityOccludesKeyguard = false;
-        mTopDismissingKeyguardActivity = null;
-        mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
-        try {
-            ActivityRecord top = topRunningActivityLocked();
-            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + top
-                    + " configChanges=0x" + Integer.toHexString(configChanges));
-            if (top != null) {
-                checkTranslucentActivityWaiting(top);
-            }
-
-            // If the top activity is not fullscreen, then we need to
-            // make sure any activities under it are now visible.
-            boolean aboveTop = top != null;
-            final boolean stackShouldBeVisible = shouldBeVisible(starting);
-            boolean behindFullscreenActivity = !stackShouldBeVisible;
-            boolean resumeNextActivity = mStackSupervisor.isTopDisplayFocusedStack(this)
-                    && (isInStackLocked(starting) == null);
-            final boolean isTopNotPinnedStack =
-                    isAttached() && getDisplay().isTopNotPinnedStack(this);
-            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-                final TaskRecord task = mTaskHistory.get(taskNdx);
-                final ArrayList<ActivityRecord> activities = task.mActivities;
-                for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                    final ActivityRecord r = activities.get(activityNdx);
-                    if (r.finishing) {
-                        continue;
-                    }
-                    final boolean isTop = r == top;
-                    if (aboveTop && !isTop) {
-                        continue;
-                    }
-                    aboveTop = false;
-
-                    // Check whether activity should be visible without Keyguard influence
-                    final boolean visibleIgnoringKeyguard = r.shouldBeVisibleIgnoringKeyguard(
-                            behindFullscreenActivity);
-                    r.visibleIgnoringKeyguard = visibleIgnoringKeyguard;
-
-                    // Now check whether it's really visible depending on Keyguard state.
-                    final boolean reallyVisible = checkKeyguardVisibility(r,
-                            visibleIgnoringKeyguard, isTop && isTopNotPinnedStack);
-                    if (visibleIgnoringKeyguard) {
-                        behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible,
-                                behindFullscreenActivity, r);
-                    }
-                    if (reallyVisible) {
-                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r
-                                + " finishing=" + r.finishing + " state=" + r.getState());
-                        // First: if this is not the current activity being started, make
-                        // sure it matches the current configuration.
-                        if (r != starting && notifyClients) {
-                            r.ensureActivityConfiguration(0 /* globalChanges */, preserveWindows,
-                                    true /* ignoreStopState */);
-                        }
-
-                        if (!r.attachedToProcess()) {
-                            if (makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
-                                    resumeNextActivity, r)) {
-                                if (activityNdx >= activities.size()) {
-                                    // Record may be removed if its process needs to restart.
-                                    activityNdx = activities.size() - 1;
-                                } else {
-                                    resumeNextActivity = false;
-                                }
-                            }
-                        } else if (r.visible) {
-                            // If this activity is already visible, then there is nothing to do here.
-                            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                                    "Skipping: already visible at " + r);
-
-                            if (r.mClientVisibilityDeferred && notifyClients) {
-                                r.makeClientVisible();
-                            }
-
-                            if (r.handleAlreadyVisible()) {
-                                resumeNextActivity = false;
-                            }
-                        } else {
-                            r.makeVisibleIfNeeded(starting, notifyClients);
-                        }
-                        // Aggregate current change flags.
-                        configChanges |= r.configChangeFlags;
-                    } else {
-                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
-                                + " finishing=" + r.finishing + " state=" + r.getState()
-                                + " stackShouldBeVisible=" + stackShouldBeVisible
-                                + " behindFullscreenActivity=" + behindFullscreenActivity
-                                + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
-                        makeInvisible(r);
-                    }
-                }
-                final int windowingMode = getWindowingMode();
-                if (windowingMode == WINDOWING_MODE_FREEFORM) {
-                    // The visibility of tasks and the activities they contain in freeform stack are
-                    // determined individually unlike other stacks where the visibility or fullscreen
-                    // status of an activity in a previous task affects other.
-                    behindFullscreenActivity = !stackShouldBeVisible;
-                } else if (isActivityTypeHome()) {
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + task
-                            + " stackShouldBeVisible=" + stackShouldBeVisible
-                            + " behindFullscreenActivity=" + behindFullscreenActivity);
-                    // No other task in the home stack should be visible behind the home activity.
-                    // Home activities is usually a translucent activity with the wallpaper behind
-                    // them. However, when they don't have the wallpaper behind them, we want to
-                    // show activities in the next application stack behind them vs. another
-                    // task in the home stack like recents.
-                    behindFullscreenActivity = true;
-                }
-            }
-
-            if (mTranslucentActivityWaiting != null &&
-                    mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
-                // Nothing is getting drawn or everything was already visible, don't wait for timeout.
-                notifyActivityDrawnLocked(null);
-            }
-        } finally {
-            mStackSupervisor.getKeyguardController().endActivityVisibilityUpdate();
-        }
-    }
-
-    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            mTaskHistory.get(taskNdx).addStartingWindowsForVisibleActivities(taskSwitch);
-        }
-    }
-
-    /**
-     * @return true if the top visible activity wants to occlude the Keyguard, false otherwise
-     */
-    boolean topActivityOccludesKeyguard() {
-        return mTopActivityOccludesKeyguard;
-    }
-
-    /**
-     * Returns true if this stack should be resized to match the bounds specified by
-     * {@link ActivityOptions#setLaunchBounds} when launching an activity into the stack.
-     */
-    boolean resizeStackWithLaunchBounds() {
-        return inPinnedWindowingMode();
-    }
-
-    @Override
-    public boolean supportsSplitScreenWindowingMode() {
-        final TaskRecord topTask = topTask();
-        return super.supportsSplitScreenWindowingMode()
-                && (topTask == null || topTask.supportsSplitScreenWindowingMode());
-    }
-
-    /** @return True if the resizing of the primary-split-screen stack affects this stack size. */
-    boolean affectedBySplitScreenResize() {
-        if (!supportsSplitScreenWindowingMode()) {
-            return false;
-        }
-        final int windowingMode = getWindowingMode();
-        return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
-    }
-
-    /**
-     * @return the top most visible activity that wants to dismiss Keyguard
-     */
-    ActivityRecord getTopDismissingKeyguardActivity() {
-        return mTopDismissingKeyguardActivity;
-    }
-
-    /**
-     * Checks whether {@param r} should be visible depending on Keyguard state and updates
-     * {@link #mTopActivityOccludesKeyguard} and {@link #mTopDismissingKeyguardActivity} if
-     * necessary.
-     *
-     * @return true if {@param r} is visible taken Keyguard state into account, false otherwise
-     */
-    boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible, boolean isTop) {
-        final int displayId = mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY;
-        final boolean keyguardOrAodShowing = mStackSupervisor.getKeyguardController()
-                .isKeyguardOrAodShowing(displayId);
-        final boolean keyguardLocked = mStackSupervisor.getKeyguardController().isKeyguardLocked();
-        final boolean showWhenLocked = r.canShowWhenLocked();
-        final boolean dismissKeyguard = r.hasDismissKeyguardWindows();
-        if (shouldBeVisible) {
-            if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
-                mTopDismissingKeyguardActivity = r;
-            }
-
-            // Only the top activity may control occluded, as we can't occlude the Keyguard if the
-            // top app doesn't want to occlude it.
-            if (isTop) {
-                mTopActivityOccludesKeyguard |= showWhenLocked;
-            }
-
-            final boolean canShowWithKeyguard = canShowWithInsecureKeyguard()
-                    && mStackSupervisor.getKeyguardController().canDismissKeyguard();
-            if (canShowWithKeyguard) {
-                return true;
-            }
-        }
-        if (keyguardOrAodShowing) {
-            // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
-            // right away and AOD isn't visible.
-            return shouldBeVisible && mStackSupervisor.getKeyguardController()
-                    .canShowActivityWhileKeyguardShowing(r, dismissKeyguard);
-        } else if (keyguardLocked) {
-            return shouldBeVisible && mStackSupervisor.getKeyguardController().canShowWhileOccluded(
-                    dismissKeyguard, showWhenLocked);
-        } else {
-            return shouldBeVisible;
-        }
-    }
-
-    /**
-     * Check if the display to which this stack is attached has
-     * {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
-     */
-    private boolean canShowWithInsecureKeyguard() {
-        final ActivityDisplay activityDisplay = getDisplay();
-        if (activityDisplay == null) {
-            throw new IllegalStateException("Stack is not attached to any display, stackId="
-                    + mStackId);
-        }
-
-        final int flags = activityDisplay.mDisplay.getFlags();
-        return (flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0;
-    }
-
-    private void checkTranslucentActivityWaiting(ActivityRecord top) {
-        if (mTranslucentActivityWaiting != top) {
-            mUndrawnActivitiesBelowTopTranslucent.clear();
-            if (mTranslucentActivityWaiting != null) {
-                // Call the callback with a timeout indication.
-                notifyActivityDrawnLocked(null);
-                mTranslucentActivityWaiting = null;
-            }
-            mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
-        }
-    }
-
-    private boolean makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges,
-            boolean isTop, boolean andResume, ActivityRecord r) {
-        // We need to make sure the app is running if it's the top, or it is just made visible from
-        // invisible. If the app is already visible, it must have died while it was visible. In this
-        // case, we'll show the dead window but will not restart the app. Otherwise we could end up
-        // thrashing.
-        if (isTop || !r.visible) {
-            // This activity needs to be visible, but isn't even running...
-            // get it started and resume if no other stack in this stack is resumed.
-            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Start and freeze screen for " + r);
-            if (r != starting) {
-                r.startFreezingScreenLocked(r.app, configChanges);
-            }
-            if (!r.visible || r.mLaunchTaskBehind) {
-                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r);
-                r.setVisible(true);
-            }
-            if (r != starting) {
-                mStackSupervisor.startSpecificActivityLocked(r, andResume, false);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // TODO: Should probably be moved into ActivityRecord.
-    private void makeInvisible(ActivityRecord r) {
-        if (!r.visible) {
-            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
-            return;
-        }
-        // Now for any activities that aren't visible to the user, make sure they no longer are
-        // keeping the screen frozen.
-        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.getState());
-        try {
-            final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState(
-                    "makeInvisible", true /* beforeStopping */);
-            // Defer telling the client it is hidden if it can enter Pip and isn't current paused,
-            // stopped or stopping. This gives it a chance to enter Pip in onPause().
-            // TODO: There is still a question surrounding activities in multi-window mode that want
-            // to enter Pip after they are paused, but are still visible. I they should be okay to
-            // enter Pip in those cases, but not "auto-Pip" which is what this condition covers and
-            // the current contract for "auto-Pip" is that the app should enter it before onPause
-            // returns. Just need to confirm this reasoning makes sense.
-            final boolean deferHidingClient = canEnterPictureInPicture
-                    && !r.isState(STOPPING, STOPPED, PAUSED);
-            r.setDeferHidingClient(deferHidingClient);
-            r.setVisible(false);
-
-            switch (r.getState()) {
-                case STOPPING:
-                case STOPPED:
-                    if (r.attachedToProcess()) {
-                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                                "Scheduling invisibility: " + r);
-                        mService.getLifecycleManager().scheduleTransaction(r.app.getThread(),
-                                r.appToken, WindowVisibilityItem.obtain(false /* showWindow */));
-                    }
-
-                    // Reset the flag indicating that an app can enter picture-in-picture once the
-                    // activity is hidden
-                    r.supportsEnterPipOnTaskSwitch = false;
-                    break;
-
-                case INITIALIZING:
-                case RESUMED:
-                case PAUSING:
-                case PAUSED:
-                    addToStopping(r, true /* scheduleIdle */,
-                            canEnterPictureInPicture /* idleDelayed */);
-                    break;
-
-                default:
-                    break;
-            }
-        } catch (Exception e) {
-            // Just skip on any failure; we'll make it visible when it next restarts.
-            Slog.w(TAG, "Exception thrown making hidden: " + r.intent.getComponent(), e);
-        }
-    }
-
-    private boolean updateBehindFullscreen(boolean stackInvisible, boolean behindFullscreenActivity,
-            ActivityRecord r) {
-        if (r.fullscreen) {
-            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
-                        + " stackInvisible=" + stackInvisible
-                        + " behindFullscreenActivity=" + behindFullscreenActivity);
-            // At this point, nothing else needs to be shown in this task.
-            behindFullscreenActivity = true;
-        }
-        return behindFullscreenActivity;
-    }
-
-    void convertActivityToTranslucent(ActivityRecord r) {
-        mTranslucentActivityWaiting = r;
-        mUndrawnActivitiesBelowTopTranslucent.clear();
-        mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
-    }
-
-    void clearOtherAppTimeTrackers(AppTimeTracker except) {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if ( r.appTimeTracker != except) {
-                    r.appTimeTracker = null;
-                }
-            }
-        }
-    }
-
-    /**
-     * Called as activities below the top translucent activity are redrawn. When the last one is
-     * redrawn notify the top activity by calling
-     * {@link Activity#onTranslucentConversionComplete}.
-     *
-     * @param r The most recent background activity to be drawn. Or, if r is null then a timeout
-     * occurred and the activity will be notified immediately.
-     */
-    void notifyActivityDrawnLocked(ActivityRecord r) {
-        if ((r == null)
-                || (mUndrawnActivitiesBelowTopTranslucent.remove(r) &&
-                        mUndrawnActivitiesBelowTopTranslucent.isEmpty())) {
-            // The last undrawn activity below the top has just been drawn. If there is an
-            // opaque activity at the top, notify it that it can become translucent safely now.
-            final ActivityRecord waitingActivity = mTranslucentActivityWaiting;
-            mTranslucentActivityWaiting = null;
-            mUndrawnActivitiesBelowTopTranslucent.clear();
-            mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
-
-            if (waitingActivity != null) {
-                mWindowManager.setWindowOpaque(waitingActivity.appToken, false);
-                if (waitingActivity.attachedToProcess()) {
-                    try {
-                        waitingActivity.app.getThread().scheduleTranslucentConversionComplete(
-                                waitingActivity.appToken, r != null);
-                    } catch (RemoteException e) {
-                    }
-                }
-            }
-        }
-    }
-
-    /** If any activities below the top running one are in the INITIALIZING state and they have a
-     * starting window displayed then remove that starting window. It is possible that the activity
-     * in this state will never resumed in which case that starting window will be orphaned. */
-    void cancelInitializingActivities() {
-        final ActivityRecord topActivity = topRunningActivityLocked();
-        boolean aboveTop = true;
-        // We don't want to clear starting window for activities that aren't behind fullscreen
-        // activities as we need to display their starting window until they are done initializing.
-        boolean behindFullscreenActivity = false;
-
-        if (!shouldBeVisible(null)) {
-            // The stack is not visible, so no activity in it should be displaying a starting
-            // window. Mark all activities below top and behind fullscreen.
-            aboveTop = false;
-            behindFullscreenActivity = true;
-        }
-
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (aboveTop) {
-                    if (r == topActivity) {
-                        aboveTop = false;
-                    }
-                    behindFullscreenActivity |= r.fullscreen;
-                    continue;
-                }
-
-                r.removeOrphanedStartingWindow(behindFullscreenActivity);
-                behindFullscreenActivity |= r.fullscreen;
-            }
-        }
-    }
-
-    /**
-     * Ensure that the top activity in the stack is resumed.
-     *
-     * @param prev The previously resumed activity, for when in the process
-     * of pausing; can be null to call from elsewhere.
-     * @param options Activity options.
-     *
-     * @return Returns true if something is being resumed, or false if
-     * nothing happened.
-     *
-     * NOTE: It is not safe to call this method directly as it can cause an activity in a
-     *       non-focused stack to be resumed.
-     *       Use {@link ActivityStackSupervisor#resumeFocusedStacksTopActivitiesLocked} to resume the
-     *       right activity for the current system state.
-     */
-    @GuardedBy("mService")
-    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
-        if (mStackSupervisor.inResumeTopActivity) {
-            // Don't even start recursing.
-            return false;
-        }
-
-        boolean result = false;
-        try {
-            // Protect against recursion.
-            mStackSupervisor.inResumeTopActivity = true;
-            result = resumeTopActivityInnerLocked(prev, options);
-
-            // When resuming the top activity, it may be necessary to pause the top activity (for
-            // example, returning to the lock screen. We suppress the normal pause logic in
-            // {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the
-            // end. We call the {@link ActivityStackSupervisor#checkReadyForSleepLocked} again here
-            // to ensure any necessary pause logic occurs. In the case where the Activity will be
-            // shown regardless of the lock screen, the call to
-            // {@link ActivityStackSupervisor#checkReadyForSleepLocked} is skipped.
-            final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
-            if (next == null || !next.canTurnScreenOn()) {
-                checkReadyForSleep();
-            }
-        } finally {
-            mStackSupervisor.inResumeTopActivity = false;
-        }
-
-        return result;
-    }
-
-    /**
-     * Returns the currently resumed activity.
-     */
-    protected ActivityRecord getResumedActivity() {
-        return mResumedActivity;
-    }
-
-    private void setResumedActivity(ActivityRecord r, String reason) {
-        if (mResumedActivity == r) {
-            return;
-        }
-
-        if (DEBUG_STACK) Slog.d(TAG_STACK, "setResumedActivity stack:" + this + " + from: "
-                + mResumedActivity + " to:" + r + " reason:" + reason);
-        mResumedActivity = r;
-    }
-
-    @GuardedBy("mService")
-    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
-        if (!mService.isBooting() && !mService.isBooted()) {
-            // Not ready yet!
-            return false;
-        }
-
-        // Find the next top-most activity to resume in this stack that is not finishing and is
-        // focusable. If it is not focusable, we will fall into the case below to resume the
-        // top activity in the next focusable task.
-        final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
-
-        final boolean hasRunningActivity = next != null;
-
-        // TODO: Maybe this entire condition can get removed?
-        if (hasRunningActivity && !isAttached()) {
-            return false;
-        }
-
-        mStackSupervisor.cancelInitializingActivities();
-
-        // Remember how we'll process this pause/resume situation, and ensure
-        // that the state is reset however we wind up proceeding.
-        boolean userLeaving = mStackSupervisor.mUserLeaving;
-        mStackSupervisor.mUserLeaving = false;
-
-        if (!hasRunningActivity) {
-            // There are no activities left in the stack, let's look somewhere else.
-            return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities");
-        }
-
-        next.delayedResume = false;
-        final ActivityDisplay display = getDisplay();
-
-        // If the top activity is the resumed one, nothing to do.
-        if (mResumedActivity == next && next.isState(RESUMED)
-                && display.allResumedActivitiesComplete()) {
-            // Make sure we have executed any pending transitions, since there
-            // should be nothing left to do at this point.
-            executeAppTransition(options);
-            if (DEBUG_STATES) Slog.d(TAG_STATES,
-                    "resumeTopActivityLocked: Top activity resumed " + next);
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-            return false;
-        }
-
-        // If we are sleeping, and there is no resumed activity, and the top
-        // activity is paused, well that is the state we want.
-        if (shouldSleepOrShutDownActivities()
-                && mLastPausedActivity == next
-                && mStackSupervisor.allPausedActivitiesComplete()) {
-            // If the current top activity may be able to occlude keyguard but the occluded state
-            // has not been set, update visibility and check again if we should continue to resume.
-            boolean nothingToResume = true;
-            if (!mService.mShuttingDown && !mTopActivityOccludesKeyguard
-                    && next.canShowWhenLocked()) {
-                ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
-                        !PRESERVE_WINDOWS);
-                nothingToResume = shouldSleepActivities();
-            }
-            if (nothingToResume) {
-                // Make sure we have executed any pending transitions, since there
-                // should be nothing left to do at this point.
-                executeAppTransition(options);
-                if (DEBUG_STATES) Slog.d(TAG_STATES,
-                        "resumeTopActivityLocked: Going to sleep and all paused");
-                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-                return false;
-            }
-        }
-
-        // Make sure that the user who owns this activity is started.  If not,
-        // we will just leave it as is because someone should be bringing
-        // another user's activities to the top of the stack.
-        if (!mService.mAmInternal.hasStartedUserState(next.userId)) {
-            Slog.w(TAG, "Skipping resume of top activity " + next
-                    + ": user " + next.userId + " is stopped");
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-            return false;
-        }
-
-        // The activity may be waiting for stop, but that is no longer
-        // appropriate for it.
-        mStackSupervisor.mStoppingActivities.remove(next);
-        mStackSupervisor.mGoingToSleepActivities.remove(next);
-        next.sleeping = false;
-        mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(next);
-
-        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
-
-        // If we are currently pausing an activity, then don't do anything until that is done.
-        if (!mStackSupervisor.allPausedActivitiesComplete()) {
-            if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
-                    "resumeTopActivityLocked: Skip resume: some activity pausing.");
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-            return false;
-        }
-
-        mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);
-
-        boolean lastResumedCanPip = false;
-        ActivityRecord lastResumed = null;
-        final ActivityStack lastFocusedStack = display.getLastFocusedStack();
-        if (lastFocusedStack != null && lastFocusedStack != this) {
-            // So, why aren't we using prev here??? See the param comment on the method. prev doesn't
-            // represent the last resumed activity. However, the last focus stack does if it isn't null.
-            lastResumed = lastFocusedStack.mResumedActivity;
-            if (userLeaving && inMultiWindowMode() && lastFocusedStack.shouldBeVisible(next)) {
-                // The user isn't leaving if this stack is the multi-window mode and the last
-                // focused stack should still be visible.
-                if(DEBUG_USER_LEAVING) Slog.i(TAG_USER_LEAVING, "Overriding userLeaving to false"
-                        + " next=" + next + " lastResumed=" + lastResumed);
-                userLeaving = false;
-            }
-            lastResumedCanPip = lastResumed != null && lastResumed.checkEnterPictureInPictureState(
-                    "resumeTopActivity", userLeaving /* beforeStopping */);
-        }
-        // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous activity
-        // to be paused, while at the same time resuming the new resume activity only if the
-        // previous activity can't go into Pip since we want to give Pip activities a chance to
-        // enter Pip before resuming the next activity.
-        final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0
-                && !lastResumedCanPip;
-
-        boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
-        if (mResumedActivity != null) {
-            if (DEBUG_STATES) Slog.d(TAG_STATES,
-                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
-            pausing |= startPausingLocked(userLeaving, false, next, false);
-        }
-        if (pausing && !resumeWhilePausing) {
-            if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG_STATES,
-                    "resumeTopActivityLocked: Skip resume: need to start pausing");
-            // At this point we want to put the upcoming activity's process
-            // at the top of the LRU list, since we know we will be needing it
-            // very soon and it would be a waste to let it get killed if it
-            // happens to be sitting towards the end.
-            if (next.attachedToProcess()) {
-                next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
-                        true /* updateLru */, true /* activityChange */, false /* updateOomAdj */);
-            }
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-            if (lastResumed != null) {
-                lastResumed.setWillCloseOrEnterPip(true);
-            }
-            return true;
-        } else if (mResumedActivity == next && next.isState(RESUMED)
-                && display.allResumedActivitiesComplete()) {
-            // It is possible for the activity to be resumed when we paused back stacks above if the
-            // next activity doesn't have to wait for pause to complete.
-            // So, nothing else to-do except:
-            // Make sure we have executed any pending transitions, since there
-            // should be nothing left to do at this point.
-            executeAppTransition(options);
-            if (DEBUG_STATES) Slog.d(TAG_STATES,
-                    "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-            return true;
-        }
-
-        // If the most recent activity was noHistory but was only stopped rather
-        // than stopped+finished because the device went to sleep, we need to make
-        // sure to finish it as we're making a new activity topmost.
-        if (shouldSleepActivities() && mLastNoHistoryActivity != null &&
-                !mLastNoHistoryActivity.finishing) {
-            if (DEBUG_STATES) Slog.d(TAG_STATES,
-                    "no-history finish of " + mLastNoHistoryActivity + " on new resume");
-            requestFinishActivityLocked(mLastNoHistoryActivity.appToken, Activity.RESULT_CANCELED,
-                    null, "resume-no-history", false);
-            mLastNoHistoryActivity = null;
-        }
-
-        if (prev != null && prev != next) {
-            if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(prev)
-                    && next != null && !next.nowVisible) {
-                mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev);
-                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
-                        "Resuming top, waiting visible to hide: " + prev);
-            } else {
-                // The next activity is already visible, so hide the previous
-                // activity's windows right now so we can show the new one ASAP.
-                // We only do this if the previous is finishing, which should mean
-                // it is on top of the one being resumed so hiding it quickly
-                // is good.  Otherwise, we want to do the normal route of allowing
-                // the resumed activity to be shown so we can decide if the
-                // previous should actually be hidden depending on whether the
-                // new one is found to be full-screen or not.
-                if (prev.finishing) {
-                    prev.setVisibility(false);
-                    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
-                            "Not waiting for visible to hide: " + prev + ", waitingVisible="
-                            + mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(prev)
-                            + ", nowVisible=" + next.nowVisible);
-                } else {
-                    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
-                            "Previous already visible but still waiting to hide: " + prev
-                            + ", waitingVisible="
-                            + mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(prev)
-                            + ", nowVisible=" + next.nowVisible);
-                }
-            }
-        }
-
-        // Launching this app's activity, make sure the app is no longer
-        // considered stopped.
-        try {
-            AppGlobals.getPackageManager().setPackageStoppedState(
-                    next.packageName, false, next.userId); /* TODO: Verify if correct userid */
-        } catch (RemoteException e1) {
-        } catch (IllegalArgumentException e) {
-            Slog.w(TAG, "Failed trying to unstop package "
-                    + next.packageName + ": " + e);
-        }
-
-        // We are starting up the next activity, so tell the window manager
-        // that the previous one will be hidden soon.  This way it can know
-        // to ignore it when computing the desired screen orientation.
-        boolean anim = true;
-        if (prev != null) {
-            if (prev.finishing) {
-                if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
-                        "Prepare close transition: prev=" + prev);
-                if (mStackSupervisor.mNoAnimActivities.contains(prev)) {
-                    anim = false;
-                    mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
-                } else {
-                    mWindowManager.prepareAppTransition(prev.getTask() == next.getTask()
-                            ? TRANSIT_ACTIVITY_CLOSE
-                            : TRANSIT_TASK_CLOSE, false);
-                }
-                prev.setVisibility(false);
-            } else {
-                if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
-                        "Prepare open transition: prev=" + prev);
-                if (mStackSupervisor.mNoAnimActivities.contains(next)) {
-                    anim = false;
-                    mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
-                } else {
-                    mWindowManager.prepareAppTransition(prev.getTask() == next.getTask()
-                            ? TRANSIT_ACTIVITY_OPEN
-                            : next.mLaunchTaskBehind
-                                    ? TRANSIT_TASK_OPEN_BEHIND
-                                    : TRANSIT_TASK_OPEN, false);
-                }
-            }
-        } else {
-            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
-            if (mStackSupervisor.mNoAnimActivities.contains(next)) {
-                anim = false;
-                mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
-            } else {
-                mWindowManager.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false);
-            }
-        }
-
-        if (anim) {
-            next.applyOptionsLocked();
-        } else {
-            next.clearOptionsLocked();
-        }
-
-        mStackSupervisor.mNoAnimActivities.clear();
-
-        if (next.attachedToProcess()) {
-            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
-                    + " stopped=" + next.stopped + " visible=" + next.visible);
-
-            // If the previous activity is translucent, force a visibility update of
-            // the next activity, so that it's added to WM's opening app list, and
-            // transition animation can be set up properly.
-            // For example, pressing Home button with a translucent activity in focus.
-            // Launcher is already visible in this case. If we don't add it to opening
-            // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
-            // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
-            final boolean lastActivityTranslucent = lastFocusedStack != null
-                    && (lastFocusedStack.inMultiWindowMode()
-                    || (lastFocusedStack.mLastPausedActivity != null
-                    && !lastFocusedStack.mLastPausedActivity.fullscreen));
-
-            // The contained logic must be synchronized, since we are both changing the visibility
-            // and updating the {@link Configuration}. {@link ActivityRecord#setVisibility} will
-            // ultimately cause the client code to schedule a layout. Since layouts retrieve the
-            // current {@link Configuration}, we must ensure that the below code updates it before
-            // the layout can occur.
-            synchronized(mWindowManager.getWindowManagerLock()) {
-                // This activity is now becoming visible.
-                if (!next.visible || next.stopped || lastActivityTranslucent) {
-                    next.setVisibility(true);
-                }
-
-                // schedule launch ticks to collect information about slow apps.
-                next.startLaunchTickingLocked();
-
-                ActivityRecord lastResumedActivity =
-                        lastFocusedStack == null ? null : lastFocusedStack.mResumedActivity;
-                final ActivityState lastState = next.getState();
-
-                mService.updateCpuStats();
-
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next
-                        + " (in existing)");
-
-                next.setState(RESUMED, "resumeTopActivityInnerLocked");
-
-                next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
-                        true /* updateLru */, true /* activityChange */, true /* updateOomAdj */);
-                updateLRUListLocked(next);
-
-                // Have the window manager re-evaluate the orientation of
-                // the screen based on the new activity order.
-                boolean notUpdated = true;
-
-                if (isFocusedStackOnDisplay()) {
-                    // We have special rotation behavior when here is some active activity that
-                    // requests specific orientation or Keyguard is locked. Make sure all activity
-                    // visibilities are set correctly as well as the transition is updated if needed
-                    // to get the correct rotation behavior. Otherwise the following call to update
-                    // the orientation may cause incorrect configurations delivered to client as a
-                    // result of invisible window resize.
-                    // TODO: Remove this once visibilities are set correctly immediately when
-                    // starting an activity.
-                    notUpdated = !mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
-                            true /* markFrozenIfConfigChanged */, false /* deferResume */);
-                }
-
-                if (notUpdated) {
-                    // The configuration update wasn't able to keep the existing
-                    // instance of the activity, and instead started a new one.
-                    // We should be all done, but let's just make sure our activity
-                    // is still at the top and schedule another run if something
-                    // weird happened.
-                    ActivityRecord nextNext = topRunningActivityLocked();
-                    if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
-                            "Activity config changed during resume: " + next
-                                    + ", new next: " + nextNext);
-                    if (nextNext != next) {
-                        // Do over!
-                        mStackSupervisor.scheduleResumeTopActivities();
-                    }
-                    if (!next.visible || next.stopped) {
-                        next.setVisibility(true);
-                    }
-                    next.completeResumeLocked();
-                    if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-                    return true;
-                }
-
-                try {
-                    final ClientTransaction transaction =
-                            ClientTransaction.obtain(next.app.getThread(), next.appToken);
-                    // Deliver all pending results.
-                    ArrayList<ResultInfo> a = next.results;
-                    if (a != null) {
-                        final int N = a.size();
-                        if (!next.finishing && N > 0) {
-                            if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
-                                    "Delivering results to " + next + ": " + a);
-                            transaction.addCallback(ActivityResultItem.obtain(a));
-                        }
-                    }
-
-                    if (next.newIntents != null) {
-                        transaction.addCallback(NewIntentItem.obtain(next.newIntents,
-                                false /* andPause */));
-                    }
-
-                    // Well the app will no longer be stopped.
-                    // Clear app token stopped state in window manager if needed.
-                    next.notifyAppResumed(next.stopped);
-
-                    EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
-                            System.identityHashCode(next), next.getTask().taskId,
-                            next.shortComponentName);
-
-                    next.sleeping = false;
-                    mService.getAppWarningsLocked().onResumeActivity(next);
-                    next.app.setPendingUiCleanAndForceProcessStateUpTo(mService.mTopProcessState);
-                    next.clearOptionsLocked();
-                    transaction.setLifecycleStateRequest(
-                            ResumeActivityItem.obtain(next.app.getReportedProcState(),
-                                    mService.isNextTransitionForward()));
-                    mService.getLifecycleManager().scheduleTransaction(transaction);
-
-                    if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
-                            + next);
-                } catch (Exception e) {
-                    // Whoops, need to restart this activity!
-                    if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to "
-                            + lastState + ": " + next);
-                    next.setState(lastState, "resumeTopActivityInnerLocked");
-
-                    // lastResumedActivity being non-null implies there is a lastStack present.
-                    if (lastResumedActivity != null) {
-                        lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
-                    }
-
-                    Slog.i(TAG, "Restarting because process died: " + next);
-                    if (!next.hasBeenLaunched) {
-                        next.hasBeenLaunched = true;
-                    } else  if (SHOW_APP_STARTING_PREVIEW && lastFocusedStack != null
-                            && lastFocusedStack.isTopStackOnDisplay()) {
-                        next.showStartingWindow(null /* prev */, false /* newTask */,
-                                false /* taskSwitch */);
-                    }
-                    mStackSupervisor.startSpecificActivityLocked(next, true, false);
-                    if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-                    return true;
-                }
-            }
-
-            // From this point on, if something goes wrong there is no way
-            // to recover the activity.
-            try {
-                next.completeResumeLocked();
-            } catch (Exception e) {
-                // If any exception gets thrown, toss away this
-                // activity and try the next one.
-                Slog.w(TAG, "Exception thrown during resume of " + next, e);
-                requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
-                        "resume-exception", true);
-                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-                return true;
-            }
-        } else {
-            // Whoops, need to restart this activity!
-            if (!next.hasBeenLaunched) {
-                next.hasBeenLaunched = true;
-            } else {
-                if (SHOW_APP_STARTING_PREVIEW) {
-                    next.showStartingWindow(null /* prev */, false /* newTask */,
-                            false /* taskSwich */);
-                }
-                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
-            }
-            if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Restarting " + next);
-            mStackSupervisor.startSpecificActivityLocked(next, true, true);
-        }
-
-        if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-        return true;
-    }
-
-    private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev,
-            ActivityOptions options, String reason) {
-        final ActivityStack nextFocusedStack = adjustFocusToNextFocusableStack(reason);
-        if (nextFocusedStack != null) {
-            // Try to move focus to the next visible stack with a running activity if this
-            // stack is not covering the entire screen or is on a secondary display (with no home
-            // stack).
-            return mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(nextFocusedStack, prev,
-                    null /* targetOptions */);
-        }
-
-        // Let's just start up the Launcher...
-        ActivityOptions.abort(options);
-        if (DEBUG_STATES) Slog.d(TAG_STATES,
-                "resumeTopActivityInNextFocusableStack: " + reason + ", go home");
-        if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-        return mStackSupervisor.resumeHomeActivity(prev, reason, mDisplayId);
-    }
-
-    /** Returns the position the input task should be placed in this stack. */
-    int getAdjustedPositionForTask(TaskRecord task, int suggestedPosition,
-            ActivityRecord starting) {
-
-        int maxPosition = mTaskHistory.size();
-        if ((starting != null && starting.okToShowLocked())
-                || (starting == null && task.okToShowLocked())) {
-            // If the task or starting activity can be shown, then whatever position is okay.
-            return Math.min(suggestedPosition, maxPosition);
-        }
-
-        // The task can't be shown, put non-current user tasks below current user tasks.
-        while (maxPosition > 0) {
-            final TaskRecord tmpTask = mTaskHistory.get(maxPosition - 1);
-            if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId)
-                    || tmpTask.topRunningActivityLocked() == null) {
-                break;
-            }
-            maxPosition--;
-        }
-
-        return  Math.min(suggestedPosition, maxPosition);
-    }
-
-    /**
-     * Used from {@link ActivityStack#positionTask(TaskRecord, int)}.
-     * @see ActivityTaskManagerService#positionTaskInStack(int, int, int).
-     */
-    private void insertTaskAtPosition(TaskRecord task, int position) {
-        if (position >= mTaskHistory.size()) {
-            insertTaskAtTop(task, null);
-            return;
-        } else if (position <= 0) {
-            insertTaskAtBottom(task);
-            return;
-        }
-        position = getAdjustedPositionForTask(task, position, null /* starting */);
-        mTaskHistory.remove(task);
-        mTaskHistory.add(position, task);
-        mWindowContainerController.positionChildAt(task.getWindowContainerController(), position);
-        updateTaskMovement(task, true);
-    }
-
-    private void insertTaskAtTop(TaskRecord task, ActivityRecord starting) {
-        // TODO: Better place to put all the code below...may be addTask...
-        mTaskHistory.remove(task);
-        // Now put task at top.
-        final int position = getAdjustedPositionForTask(task, mTaskHistory.size(), starting);
-        mTaskHistory.add(position, task);
-        updateTaskMovement(task, true);
-        positionChildWindowContainerAtTop(task);
-    }
-
-    private void insertTaskAtBottom(TaskRecord task) {
-        // Unlike insertTaskAtPosition, this will also position parents of the windowcontroller.
-        mTaskHistory.remove(task);
-        final int position = getAdjustedPositionForTask(task, 0, null);
-        mTaskHistory.add(position, task);
-        updateTaskMovement(task, true);
-        positionChildWindowContainerAtBottom(task);
-    }
-
-    void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
-            boolean newTask, boolean keepCurTransition, ActivityOptions options) {
-        TaskRecord rTask = r.getTask();
-        final int taskId = rTask.taskId;
-        // mLaunchTaskBehind tasks get placed at the back of the task stack.
-        if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
-            // Last activity in task had been removed or ActivityManagerService is reusing task.
-            // Insert or replace.
-            // Might not even be in.
-            insertTaskAtTop(rTask, r);
-        }
-        TaskRecord task = null;
-        if (!newTask) {
-            // If starting in an existing task, find where that is...
-            boolean startIt = true;
-            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-                task = mTaskHistory.get(taskNdx);
-                if (task.getTopActivity() == null) {
-                    // All activities in task are finishing.
-                    continue;
-                }
-                if (task == rTask) {
-                    // Here it is!  Now, if this is not yet visible to the
-                    // user, then just add it without starting; it will
-                    // get started when the user navigates back to it.
-                    if (!startIt) {
-                        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
-                                + task, new RuntimeException("here").fillInStackTrace());
-                        r.createWindowContainer();
-                        ActivityOptions.abort(options);
-                        return;
-                    }
-                    break;
-                } else if (task.numFullscreen > 0) {
-                    startIt = false;
-                }
-            }
-        }
-
-        // Place a new activity at top of stack, so it is next to interact with the user.
-
-        // If we are not placing the new activity frontmost, we do not want to deliver the
-        // onUserLeaving callback to the actual frontmost activity
-        final TaskRecord activityTask = r.getTask();
-        if (task == activityTask && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
-            mStackSupervisor.mUserLeaving = false;
-            if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
-                    "startActivity() behind front, mUserLeaving=false");
-        }
-
-        task = activityTask;
-
-        // Slot the activity into the history stack and proceed
-        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
-                new RuntimeException("here").fillInStackTrace());
-        // TODO: Need to investigate if it is okay for the controller to already be created by the
-        // time we get to this point. I think it is, but need to double check.
-        // Use test in b/34179495 to trace the call path.
-        if (r.getWindowContainerController() == null) {
-            r.createWindowContainer();
-        }
-        task.setFrontOfTask();
-
-        if (!isHomeOrRecentsStack() || numActivities() > 0) {
-            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
-                    "Prepare open transition: starting " + r);
-            if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
-                mWindowManager.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
-                mStackSupervisor.mNoAnimActivities.add(r);
-            } else {
-                int transit = TRANSIT_ACTIVITY_OPEN;
-                if (newTask) {
-                    if (r.mLaunchTaskBehind) {
-                        transit = TRANSIT_TASK_OPEN_BEHIND;
-                    } else {
-                        // If a new task is being launched, then mark the existing top activity as
-                        // supporting picture-in-picture while pausing only if the starting activity
-                        // would not be considered an overlay on top of the current activity
-                        // (eg. not fullscreen, or the assistant)
-                        if (canEnterPipOnTaskSwitch(focusedTopActivity,
-                                null /* toFrontTask */, r, options)) {
-                            focusedTopActivity.supportsEnterPipOnTaskSwitch = true;
-                        }
-                        transit = TRANSIT_TASK_OPEN;
-                    }
-                }
-                mWindowManager.prepareAppTransition(transit, keepCurTransition);
-                mStackSupervisor.mNoAnimActivities.remove(r);
-            }
-            boolean doShow = true;
-            if (newTask) {
-                // Even though this activity is starting fresh, we still need
-                // to reset it to make sure we apply affinities to move any
-                // existing activities from other tasks in to it.
-                // If the caller has requested that the target task be
-                // reset, then do so.
-                if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
-                    resetTaskIfNeededLocked(r, r);
-                    doShow = topRunningNonDelayedActivityLocked(null) == r;
-                }
-            } else if (options != null && options.getAnimationType()
-                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
-                doShow = false;
-            }
-            if (r.mLaunchTaskBehind) {
-                // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
-                // tell WindowManager that r is visible even though it is at the back of the stack.
-                r.setVisibility(true);
-                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
-                // Figure out if we are transitioning from another activity that is
-                // "has the same starting icon" as the next one.  This allows the
-                // window manager to keep the previous window it had previously
-                // created, if it still had one.
-                TaskRecord prevTask = r.getTask();
-                ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked();
-                if (prev != null) {
-                    // We don't want to reuse the previous starting preview if:
-                    // (1) The current activity is in a different task.
-                    if (prev.getTask() != prevTask) {
-                        prev = null;
-                    }
-                    // (2) The current activity is already displayed.
-                    else if (prev.nowVisible) {
-                        prev = null;
-                    }
-                }
-                r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));
-            }
-        } else {
-            // If this is the first activity, don't do any fancy animations,
-            // because there is nothing for it to animate on top of.
-            ActivityOptions.abort(options);
-        }
-    }
-
-    /**
-     * @return Whether the switch to another task can trigger the currently running activity to
-     * enter PiP while it is pausing (if supported). Only one of {@param toFrontTask} or
-     * {@param toFrontActivity} should be set.
-     */
-    private boolean canEnterPipOnTaskSwitch(ActivityRecord pipCandidate,
-            TaskRecord toFrontTask, ActivityRecord toFrontActivity, ActivityOptions opts) {
-        if (opts != null && opts.disallowEnterPictureInPictureWhileLaunching()) {
-            // Ensure the caller has requested not to trigger auto-enter PiP
-            return false;
-        }
-        if (pipCandidate == null || pipCandidate.inPinnedWindowingMode()) {
-            // Ensure that we do not trigger entering PiP an activity on the pinned stack
-            return false;
-        }
-        final ActivityStack targetStack = toFrontTask != null
-                ? toFrontTask.getStack() : toFrontActivity.getStack();
-        if (targetStack != null && targetStack.isActivityTypeAssistant()) {
-            // Ensure the task/activity being brought forward is not the assistant
-            return false;
-        }
-        return true;
-    }
-
-    private boolean isTaskSwitch(ActivityRecord r,
-            ActivityRecord topFocusedActivity) {
-        return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask();
-    }
-
-    /**
-     * Perform a reset of the given task, if needed as part of launching it.
-     * Returns the new HistoryRecord at the top of the task.
-     */
-    /**
-     * Helper method for #resetTaskIfNeededLocked.
-     * We are inside of the task being reset...  we'll either finish this activity, push it out
-     * for another task, or leave it as-is.
-     * @param task The task containing the Activity (taskTop) that might be reset.
-     * @param forceReset
-     * @return An ActivityOptions that needs to be processed.
-     */
-    private ActivityOptions resetTargetTaskIfNeededLocked(TaskRecord task, boolean forceReset) {
-        ActivityOptions topOptions = null;
-
-        int replyChainEnd = -1;
-        boolean canMoveOptions = true;
-
-        // We only do this for activities that are not the root of the task (since if we finish
-        // the root, we may no longer have the task!).
-        final ArrayList<ActivityRecord> activities = task.mActivities;
-        final int numActivities = activities.size();
-        final int rootActivityNdx = task.findEffectiveRootIndex();
-        for (int i = numActivities - 1; i > rootActivityNdx; --i ) {
-            ActivityRecord target = activities.get(i);
-            if (target.frontOfTask)
-                break;
-
-            final int flags = target.info.flags;
-            final boolean finishOnTaskLaunch =
-                    (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
-            final boolean allowTaskReparenting =
-                    (flags & ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
-            final boolean clearWhenTaskReset =
-                    (target.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0;
-
-            if (!finishOnTaskLaunch
-                    && !clearWhenTaskReset
-                    && target.resultTo != null) {
-                // If this activity is sending a reply to a previous
-                // activity, we can't do anything with it now until
-                // we reach the start of the reply chain.
-                // XXX note that we are assuming the result is always
-                // to the previous activity, which is almost always
-                // the case but we really shouldn't count on.
-                if (replyChainEnd < 0) {
-                    replyChainEnd = i;
-                }
-            } else if (!finishOnTaskLaunch
-                    && !clearWhenTaskReset
-                    && allowTaskReparenting
-                    && target.taskAffinity != null
-                    && !target.taskAffinity.equals(task.affinity)) {
-                // If this activity has an affinity for another
-                // task, then we need to move it out of here.  We will
-                // move it as far out of the way as possible, to the
-                // bottom of the activity stack.  This also keeps it
-                // correctly ordered with any activities we previously
-                // moved.
-                final TaskRecord targetTask;
-                final ActivityRecord bottom =
-                        !mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ?
-                                mTaskHistory.get(0).mActivities.get(0) : null;
-                if (bottom != null && target.taskAffinity != null
-                        && target.taskAffinity.equals(bottom.getTask().affinity)) {
-                    // If the activity currently at the bottom has the
-                    // same task affinity as the one we are moving,
-                    // then merge it into the same task.
-                    targetTask = bottom.getTask();
-                    if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
-                            + " out to bottom task " + targetTask);
-                } else {
-                    targetTask = createTaskRecord(
-                            mStackSupervisor.getNextTaskIdForUserLocked(target.userId),
-                            target.info, null, null, null, false);
-                    targetTask.affinityIntent = target.intent;
-                    if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
-                            + " out to new task " + targetTask);
-                }
-
-                boolean noOptions = canMoveOptions;
-                final int start = replyChainEnd < 0 ? i : replyChainEnd;
-                for (int srcPos = start; srcPos >= i; --srcPos) {
-                    final ActivityRecord p = activities.get(srcPos);
-                    if (p.finishing) {
-                        continue;
-                    }
-
-                    canMoveOptions = false;
-                    if (noOptions && topOptions == null) {
-                        topOptions = p.takeOptionsLocked();
-                        if (topOptions != null) {
-                            noOptions = false;
-                        }
-                    }
-                    if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
-                            "Removing activity " + p + " from task=" + task + " adding to task="
-                            + targetTask + " Callers=" + Debug.getCallers(4));
-                    if (DEBUG_TASKS) Slog.v(TAG_TASKS,
-                            "Pushing next activity " + p + " out to target's task " + target);
-                    p.reparent(targetTask, 0 /* position - bottom */, "resetTargetTaskIfNeeded");
-                }
-
-                positionChildWindowContainerAtBottom(targetTask);
-                replyChainEnd = -1;
-            } else if (forceReset || finishOnTaskLaunch || clearWhenTaskReset) {
-                // If the activity should just be removed -- either
-                // because it asks for it, or the task should be
-                // cleared -- then finish it and anything that is
-                // part of its reply chain.
-                int end;
-                if (clearWhenTaskReset) {
-                    // In this case, we want to finish this activity
-                    // and everything above it, so be sneaky and pretend
-                    // like these are all in the reply chain.
-                    end = activities.size() - 1;
-                } else if (replyChainEnd < 0) {
-                    end = i;
-                } else {
-                    end = replyChainEnd;
-                }
-                boolean noOptions = canMoveOptions;
-                for (int srcPos = i; srcPos <= end; srcPos++) {
-                    ActivityRecord p = activities.get(srcPos);
-                    if (p.finishing) {
-                        continue;
-                    }
-                    canMoveOptions = false;
-                    if (noOptions && topOptions == null) {
-                        topOptions = p.takeOptionsLocked();
-                        if (topOptions != null) {
-                            noOptions = false;
-                        }
-                    }
-                    if (DEBUG_TASKS) Slog.w(TAG_TASKS,
-                            "resetTaskIntendedTask: calling finishActivity on " + p);
-                    if (finishActivityLocked(
-                            p, Activity.RESULT_CANCELED, null, "reset-task", false)) {
-                        end--;
-                        srcPos--;
-                    }
-                }
-                replyChainEnd = -1;
-            } else {
-                // If we were in the middle of a chain, well the
-                // activity that started it all doesn't want anything
-                // special, so leave it all as-is.
-                replyChainEnd = -1;
-            }
-        }
-
-        return topOptions;
-    }
-
-    /**
-     * Helper method for #resetTaskIfNeededLocked. Processes all of the activities in a given
-     * TaskRecord looking for an affinity with the task of resetTaskIfNeededLocked.taskTop.
-     * @param affinityTask The task we are looking for an affinity to.
-     * @param task Task that resetTaskIfNeededLocked.taskTop belongs to.
-     * @param topTaskIsHigher True if #task has already been processed by resetTaskIfNeededLocked.
-     * @param forceReset Flag passed in to resetTaskIfNeededLocked.
-     */
-    private int resetAffinityTaskIfNeededLocked(TaskRecord affinityTask, TaskRecord task,
-            boolean topTaskIsHigher, boolean forceReset, int taskInsertionPoint) {
-        int replyChainEnd = -1;
-        final int taskId = task.taskId;
-        final String taskAffinity = task.affinity;
-
-        final ArrayList<ActivityRecord> activities = affinityTask.mActivities;
-        final int numActivities = activities.size();
-        final int rootActivityNdx = affinityTask.findEffectiveRootIndex();
-
-        // Do not operate on or below the effective root Activity.
-        for (int i = numActivities - 1; i > rootActivityNdx; --i) {
-            ActivityRecord target = activities.get(i);
-            if (target.frontOfTask)
-                break;
-
-            final int flags = target.info.flags;
-            boolean finishOnTaskLaunch = (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
-            boolean allowTaskReparenting = (flags & ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
-
-            if (target.resultTo != null) {
-                // If this activity is sending a reply to a previous
-                // activity, we can't do anything with it now until
-                // we reach the start of the reply chain.
-                // XXX note that we are assuming the result is always
-                // to the previous activity, which is almost always
-                // the case but we really shouldn't count on.
-                if (replyChainEnd < 0) {
-                    replyChainEnd = i;
-                }
-            } else if (topTaskIsHigher
-                    && allowTaskReparenting
-                    && taskAffinity != null
-                    && taskAffinity.equals(target.taskAffinity)) {
-                // This activity has an affinity for our task. Either remove it if we are
-                // clearing or move it over to our task.  Note that
-                // we currently punt on the case where we are resetting a
-                // task that is not at the top but who has activities above
-                // with an affinity to it...  this is really not a normal
-                // case, and we will need to later pull that task to the front
-                // and usually at that point we will do the reset and pick
-                // up those remaining activities.  (This only happens if
-                // someone starts an activity in a new task from an activity
-                // in a task that is not currently on top.)
-                if (forceReset || finishOnTaskLaunch) {
-                    final int start = replyChainEnd >= 0 ? replyChainEnd : i;
-                    if (DEBUG_TASKS) Slog.v(TAG_TASKS,
-                            "Finishing task at index " + start + " to " + i);
-                    for (int srcPos = start; srcPos >= i; --srcPos) {
-                        final ActivityRecord p = activities.get(srcPos);
-                        if (p.finishing) {
-                            continue;
-                        }
-                        finishActivityLocked(
-                                p, Activity.RESULT_CANCELED, null, "move-affinity", false);
-                    }
-                } else {
-                    if (taskInsertionPoint < 0) {
-                        taskInsertionPoint = task.mActivities.size();
-
-                    }
-
-                    final int start = replyChainEnd >= 0 ? replyChainEnd : i;
-                    if (DEBUG_TASKS) Slog.v(TAG_TASKS,
-                            "Reparenting from task=" + affinityTask + ":" + start + "-" + i
-                            + " to task=" + task + ":" + taskInsertionPoint);
-                    for (int srcPos = start; srcPos >= i; --srcPos) {
-                        final ActivityRecord p = activities.get(srcPos);
-                        p.reparent(task, taskInsertionPoint, "resetAffinityTaskIfNeededLocked");
-
-                        if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
-                                "Removing and adding activity " + p + " to stack at " + task
-                                + " callers=" + Debug.getCallers(3));
-                        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Pulling activity " + p
-                                + " from " + srcPos + " in to resetting task " + task);
-                    }
-                    positionChildWindowContainerAtTop(task);
-
-                    // Now we've moved it in to place...  but what if this is
-                    // a singleTop activity and we have put it on top of another
-                    // instance of the same activity?  Then we drop the instance
-                    // below so it remains singleTop.
-                    if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
-                        ArrayList<ActivityRecord> taskActivities = task.mActivities;
-                        int targetNdx = taskActivities.indexOf(target);
-                        if (targetNdx > 0) {
-                            ActivityRecord p = taskActivities.get(targetNdx - 1);
-                            if (p.intent.getComponent().equals(target.intent.getComponent())) {
-                                finishActivityLocked(p, Activity.RESULT_CANCELED, null, "replace",
-                                        false);
-                            }
-                        }
-                    }
-                }
-
-                replyChainEnd = -1;
-            }
-        }
-        return taskInsertionPoint;
-    }
-
-    final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
-            ActivityRecord newActivity) {
-        final boolean forceReset =
-                (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
-        final TaskRecord task = taskTop.getTask();
-
-        /** False until we evaluate the TaskRecord associated with taskTop. Switches to true
-         * for remaining tasks. Used for later tasks to reparent to task. */
-        boolean taskFound = false;
-
-        /** If ActivityOptions are moved out and need to be aborted or moved to taskTop. */
-        ActivityOptions topOptions = null;
-
-        // Preserve the location for reparenting in the new task.
-        int reparentInsertionPoint = -1;
-
-        for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
-            final TaskRecord targetTask = mTaskHistory.get(i);
-
-            if (targetTask == task) {
-                topOptions = resetTargetTaskIfNeededLocked(task, forceReset);
-                taskFound = true;
-            } else {
-                reparentInsertionPoint = resetAffinityTaskIfNeededLocked(targetTask, task,
-                        taskFound, forceReset, reparentInsertionPoint);
-            }
-        }
-
-        int taskNdx = mTaskHistory.indexOf(task);
-        if (taskNdx >= 0) {
-            do {
-                taskTop = mTaskHistory.get(taskNdx--).getTopActivity();
-            } while (taskTop == null && taskNdx >= 0);
-        }
-
-        if (topOptions != null) {
-            // If we got some ActivityOptions from an activity on top that
-            // was removed from the task, propagate them to the new real top.
-            if (taskTop != null) {
-                taskTop.updateOptionsLocked(topOptions);
-            } else {
-                topOptions.abort();
-            }
-        }
-
-        return taskTop;
-    }
-
-    void sendActivityResultLocked(int callingUid, ActivityRecord r,
-            String resultWho, int requestCode, int resultCode, Intent data) {
-
-        if (callingUid > 0) {
-            mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
-                    data, r.getUriPermissionsLocked(), r.userId);
-        }
-
-        if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
-                + " : who=" + resultWho + " req=" + requestCode
-                + " res=" + resultCode + " data=" + data);
-        if (mResumedActivity == r && r.attachedToProcess()) {
-            try {
-                ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
-                list.add(new ResultInfo(resultWho, requestCode,
-                        resultCode, data));
-                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
-                        ActivityResultItem.obtain(list));
-                return;
-            } catch (Exception e) {
-                Slog.w(TAG, "Exception thrown sending result to " + r, e);
-            }
-        }
-
-        r.addResultLocked(null, resultWho, requestCode, resultCode, data);
-    }
-
-    /** Returns true if the task is one of the task finishing on-top of the top running task. */
-    private boolean isATopFinishingTask(TaskRecord task) {
-        for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
-            final TaskRecord current = mTaskHistory.get(i);
-            final ActivityRecord r = current.topRunningActivityLocked();
-            if (r != null) {
-                // We got a top running activity, so there isn't a top finishing task...
-                return false;
-            }
-            if (current == task) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void adjustFocusedActivityStack(ActivityRecord r, String reason) {
-        if (!mStackSupervisor.isTopDisplayFocusedStack(this) ||
-                ((mResumedActivity != r) && (mResumedActivity != null))) {
-            return;
-        }
-
-        final ActivityRecord next = topRunningActivityLocked();
-        final String myReason = reason + " adjustFocus";
-
-        if (next == r) {
-            final ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
-            if (top != null) {
-                top.moveFocusableActivityToTop(myReason);
-            }
-            return;
-        }
-
-        if (next != null && isFocusable()) {
-            // Keep focus in stack if we have a top running activity and are focusable.
-            return;
-        }
-
-        // Task is not guaranteed to be non-null. For example, destroying the
-        // {@link ActivityRecord} will disassociate the task from the activity.
-        final TaskRecord task = r.getTask();
-
-        if (task == null) {
-            throw new IllegalStateException("activity no longer associated with task:" + r);
-        }
-
-        // Move focus to next focusable stack if possible.
-        final ActivityStack nextFocusableStack = adjustFocusToNextFocusableStack(myReason);
-        if (nextFocusableStack != null) {
-            final ActivityRecord top = nextFocusableStack.topRunningActivityLocked();
-            if (top != null && top == mStackSupervisor.getTopResumedActivity()) {
-                // TODO(b/111361570): Remove this and update focused app per-display in
-                // WindowManager every time an activity becomes resumed in
-                // ActivityTaskManagerService#setResumedActivityUncheckLocked().
-                mService.setResumedActivityUncheckLocked(top, reason);
-            }
-            return;
-        }
-
-        // Whatever...go home.
-        getDisplay().moveHomeActivityToTop(myReason);
-    }
-
-    /**
-     * Find next proper focusable stack and make it focused.
-     * @return The stack that now got the focus, {@code null} if none found.
-     */
-    ActivityStack adjustFocusToNextFocusableStack(String reason) {
-        return adjustFocusToNextFocusableStack(reason, false /* allowFocusSelf */);
-    }
-
-    /**
-     * Find next proper focusable stack and make it focused.
-     * @param allowFocusSelf Is the focus allowed to remain on the same stack.
-     * @return The stack that now got the focus, {@code null} if none found.
-     */
-    private ActivityStack adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) {
-        final ActivityStack stack =
-                mStackSupervisor.getNextFocusableStackLocked(this, !allowFocusSelf);
-        final String myReason = reason + " adjustFocusToNextFocusableStack";
-        if (stack == null) {
-            return null;
-        }
-
-        final ActivityRecord top = stack.topRunningActivityLocked();
-
-        if (stack.isActivityTypeHome() && (top == null || !top.visible)) {
-            // If we will be focusing on the home stack next and its current top activity isn't
-            // visible, then use the move the home stack task to top to make the activity visible.
-            stack.getDisplay().moveHomeActivityToTop(reason);
-            return stack;
-        }
-
-        stack.moveToFront(myReason);
-        return stack;
-    }
-
-    final void stopActivityLocked(ActivityRecord r) {
-        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + r);
-        if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
-                || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
-            if (!r.finishing) {
-                if (!shouldSleepActivities()) {
-                    if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r);
-                    if (requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
-                            "stop-no-history", false)) {
-                        // If {@link requestFinishActivityLocked} returns {@code true},
-                        // {@link adjustFocusedActivityStack} would have been already called.
-                        r.resumeKeyDispatchingLocked();
-                        return;
-                    }
-                } else {
-                    if (DEBUG_STATES) Slog.d(TAG_STATES, "Not finishing noHistory " + r
-                            + " on stop because we're just sleeping");
-                }
-            }
-        }
-
-        if (r.attachedToProcess()) {
-            adjustFocusedActivityStack(r, "stopActivity");
-            r.resumeKeyDispatchingLocked();
-            try {
-                r.stopped = false;
-                if (DEBUG_STATES) Slog.v(TAG_STATES,
-                        "Moving to STOPPING: " + r + " (stop requested)");
-                r.setState(STOPPING, "stopActivityLocked");
-                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                        "Stopping visible=" + r.visible + " for " + r);
-                if (!r.visible) {
-                    r.setVisible(false);
-                }
-                EventLogTags.writeAmStopActivity(
-                        r.userId, System.identityHashCode(r), r.shortComponentName);
-                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
-                        StopActivityItem.obtain(r.visible, r.configChangeFlags));
-                if (shouldSleepOrShutDownActivities()) {
-                    r.setSleeping(true);
-                }
-                Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
-                mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
-            } catch (Exception e) {
-                // Maybe just ignore exceptions here...  if the process
-                // has crashed, our death notification will clean things
-                // up.
-                Slog.w(TAG, "Exception thrown during pause", e);
-                // Just in case, assume it to be stopped.
-                r.stopped = true;
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r);
-                r.setState(STOPPED, "stopActivityLocked");
-                if (r.deferRelaunchUntilPaused) {
-                    destroyActivityLocked(r, true, "stop-except");
-                }
-            }
-        }
-    }
-
-    /**
-     * @return Returns true if the activity is being finished, false if for
-     * some reason it is being left as-is.
-     */
-    final boolean requestFinishActivityLocked(IBinder token, int resultCode,
-            Intent resultData, String reason, boolean oomAdj) {
-        ActivityRecord r = isInStackLocked(token);
-        if (DEBUG_RESULTS || DEBUG_STATES) Slog.v(TAG_STATES,
-                "Finishing activity token=" + token + " r="
-                + ", result=" + resultCode + ", data=" + resultData
-                + ", reason=" + reason);
-        if (r == null) {
-            return false;
-        }
-
-        finishActivityLocked(r, resultCode, resultData, reason, oomAdj);
-        return true;
-    }
-
-    final void finishSubActivityLocked(ActivityRecord self, String resultWho, int requestCode) {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = activities.get(activityNdx);
-                if (r.resultTo == self && r.requestCode == requestCode) {
-                    if ((r.resultWho == null && resultWho == null) ||
-                        (r.resultWho != null && r.resultWho.equals(resultWho))) {
-                        finishActivityLocked(r, Activity.RESULT_CANCELED, null, "request-sub",
-                                false);
-                    }
-                }
-            }
-        }
-        mService.updateOomAdj();
-    }
-
-    /**
-     * Finish the topmost activity that belongs to the crashed app. We may also finish the activity
-     * that requested launch of the crashed one to prevent launch-crash loop.
-     * @param app The app that crashed.
-     * @param reason Reason to perform this action.
-     * @return The task that was finished in this stack, {@code null} if top running activity does
-     *         not belong to the crashed app.
-     */
-    final TaskRecord finishTopCrashedActivityLocked(WindowProcessController app, String reason) {
-        ActivityRecord r = topRunningActivityLocked();
-        TaskRecord finishedTask = null;
-        if (r == null || r.app != app) {
-            return null;
-        }
-        Slog.w(TAG, "  Force finishing activity "
-                + r.intent.getComponent().flattenToShortString());
-        finishedTask = r.getTask();
-        int taskNdx = mTaskHistory.indexOf(finishedTask);
-        final TaskRecord task = finishedTask;
-        int activityNdx = task.mActivities.indexOf(r);
-        mWindowManager.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE,
-                false /* alwaysKeepCurrent */);
-        finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
-        finishedTask = task;
-        // Also terminate any activities below it that aren't yet
-        // stopped, to avoid a situation where one will get
-        // re-start our crashing activity once it gets resumed again.
-        --activityNdx;
-        if (activityNdx < 0) {
-            do {
-                --taskNdx;
-                if (taskNdx < 0) {
-                    break;
-                }
-                activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
-            } while (activityNdx < 0);
-        }
-        if (activityNdx >= 0) {
-            r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
-            if (r.isState(RESUMED, PAUSING, PAUSED)) {
-                if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
-                    Slog.w(TAG, "  Force finishing activity "
-                            + r.intent.getComponent().flattenToShortString());
-                    finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
-                }
-            }
-        }
-        return finishedTask;
-    }
-
-    final void finishVoiceTask(IVoiceInteractionSession session) {
-        IBinder sessionBinder = session.asBinder();
-        boolean didOne = false;
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            TaskRecord tr = mTaskHistory.get(taskNdx);
-            if (tr.voiceSession != null && tr.voiceSession.asBinder() == sessionBinder) {
-                for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-                    ActivityRecord r = tr.mActivities.get(activityNdx);
-                    if (!r.finishing) {
-                        finishActivityLocked(r, Activity.RESULT_CANCELED, null, "finish-voice",
-                                false);
-                        didOne = true;
-                    }
-                }
-            } else {
-                // Check if any of the activities are using voice
-                for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-                    ActivityRecord r = tr.mActivities.get(activityNdx);
-                    if (r.voiceSession != null && r.voiceSession.asBinder() == sessionBinder) {
-                        // Inform of cancellation
-                        r.clearVoiceSessionLocked();
-                        try {
-                            r.app.getThread().scheduleLocalVoiceInteractionStarted(
-                                    r.appToken, null);
-                        } catch (RemoteException re) {
-                            // Ok
-                        }
-                        mService.finishRunningVoiceLocked();
-                        break;
-                    }
-                }
-            }
-        }
-
-        if (didOne) {
-            mService.updateOomAdj();
-        }
-    }
-
-    final boolean finishActivityAffinityLocked(ActivityRecord r) {
-        ArrayList<ActivityRecord> activities = r.getTask().mActivities;
-        for (int index = activities.indexOf(r); index >= 0; --index) {
-            ActivityRecord cur = activities.get(index);
-            if (!Objects.equals(cur.taskAffinity, r.taskAffinity)) {
-                break;
-            }
-            finishActivityLocked(cur, Activity.RESULT_CANCELED, null, "request-affinity", true);
-        }
-        return true;
-    }
-
-    private void finishActivityResultsLocked(ActivityRecord r, int resultCode, Intent resultData) {
-        // send the result
-        ActivityRecord resultTo = r.resultTo;
-        if (resultTo != null) {
-            if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Adding result to " + resultTo
-                    + " who=" + r.resultWho + " req=" + r.requestCode
-                    + " res=" + resultCode + " data=" + resultData);
-            if (resultTo.userId != r.userId) {
-                if (resultData != null) {
-                    resultData.prepareToLeaveUser(r.userId);
-                }
-            }
-            if (r.info.applicationInfo.uid > 0) {
-                mService.mUgmInternal.grantUriPermissionFromIntent(r.info.applicationInfo.uid,
-                        resultTo.packageName, resultData,
-                        resultTo.getUriPermissionsLocked(), resultTo.userId);
-            }
-            resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData);
-            r.resultTo = null;
-        }
-        else if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "No result destination from " + r);
-
-        // Make sure this HistoryRecord is not holding on to other resources,
-        // because clients have remote IPC references to this object so we
-        // can't assume that will go away and want to avoid circular IPC refs.
-        r.results = null;
-        r.pendingResults = null;
-        r.newIntents = null;
-        r.icicle = null;
-    }
-
-    /**
-     * See {@link #finishActivityLocked(ActivityRecord, int, Intent, String, boolean, boolean)}
-     */
-    final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
-            String reason, boolean oomAdj) {
-        return finishActivityLocked(r, resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY);
-    }
-
-    /**
-     * @return Returns true if this activity has been removed from the history
-     * list, or false if it is still in the list and will be removed later.
-     */
-    final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
-            String reason, boolean oomAdj, boolean pauseImmediately) {
-        if (r.finishing) {
-            Slog.w(TAG, "Duplicate finish request for " + r);
-            return false;
-        }
-
-        mWindowManager.deferSurfaceLayout();
-        try {
-            r.makeFinishingLocked();
-            final TaskRecord task = r.getTask();
-            EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
-                    r.userId, System.identityHashCode(r),
-                    task.taskId, r.shortComponentName, reason);
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-            final int index = activities.indexOf(r);
-            if (index < (activities.size() - 1)) {
-                task.setFrontOfTask();
-                if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
-                    // If the caller asked that this activity (and all above it)
-                    // be cleared when the task is reset, don't lose that information,
-                    // but propagate it up to the next activity.
-                    ActivityRecord next = activities.get(index+1);
-                    next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
-                }
-            }
-
-            r.pauseKeyDispatchingLocked();
-
-            adjustFocusedActivityStack(r, "finishActivity");
-
-            finishActivityResultsLocked(r, resultCode, resultData);
-
-            final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
-            final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
-            if (mResumedActivity == r) {
-                if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
-                        "Prepare close transition: finishing " + r);
-                if (endTask) {
-                    mService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
-                            task.taskId);
-                }
-                mWindowManager.prepareAppTransition(transit, false);
-
-                // Tell window manager to prepare for this one to be removed.
-                r.setVisibility(false);
-
-                if (mPausingActivity == null) {
-                    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish needs to pause: " + r);
-                    if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
-                            "finish() => pause with userLeaving=false");
-                    startPausingLocked(false, false, null, pauseImmediately);
-                }
-
-                if (endTask) {
-                    mService.getLockTaskController().clearLockedTask(task);
-                }
-            } else if (!r.isState(PAUSING)) {
-                // If the activity is PAUSING, we will complete the finish once
-                // it is done pausing; else we can just directly finish it here.
-                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + r);
-                if (r.visible) {
-                    prepareActivityHideTransitionAnimation(r, transit);
-                }
-
-                final int finishMode = (r.visible || r.nowVisible) ? FINISH_AFTER_VISIBLE
-                        : FINISH_AFTER_PAUSE;
-                final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj,
-                        "finishActivityLocked") == null;
-
-                // The following code is an optimization. When the last non-task overlay activity
-                // is removed from the task, we remove the entire task from the stack. However,
-                // since that is done after the scheduled destroy callback from the activity, that
-                // call to change the visibility of the task overlay activities would be out of
-                // sync with the activitiy visibility being set for this finishing activity above.
-                // In this case, we can set the visibility of all the task overlay activities when
-                // we detect the last one is finishing to keep them in sync.
-                if (task.onlyHasTaskOverlayActivities(true /* excludeFinishing */)) {
-                    for (ActivityRecord taskOverlay : task.mActivities) {
-                        if (!taskOverlay.mTaskOverlay) {
-                            continue;
-                        }
-                        prepareActivityHideTransitionAnimation(taskOverlay, transit);
-                    }
-                }
-                return removedActivity;
-            } else {
-                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + r);
-            }
-
-            return false;
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-        }
-    }
-
-    private void prepareActivityHideTransitionAnimation(ActivityRecord r, int transit) {
-        mWindowManager.prepareAppTransition(transit, false);
-        r.setVisibility(false);
-        mWindowManager.executeAppTransition();
-        if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) {
-            mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(r);
-        }
-    }
-
-    static final int FINISH_IMMEDIATELY = 0;
-    static final int FINISH_AFTER_PAUSE = 1;
-    static final int FINISH_AFTER_VISIBLE = 2;
-
-    final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj,
-            String reason) {
-        // First things first: if this activity is currently visible,
-        // and the resumed activity is not yet visible, then hold off on
-        // finishing until the resumed one becomes visible.
-
-        // The activity that we are finishing may be over the lock screen. In this case, we do not
-        // want to consider activities that cannot be shown on the lock screen as running and should
-        // proceed with finishing the activity if there is no valid next top running activity.
-        final ActivityDisplay display = getDisplay();
-        final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */);
-
-        if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
-                && next != null && !next.nowVisible) {
-            if (!mStackSupervisor.mStoppingActivities.contains(r)) {
-                addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */);
-            }
-            if (DEBUG_STATES) Slog.v(TAG_STATES,
-                    "Moving to STOPPING: "+ r + " (finish requested)");
-            r.setState(STOPPING, "finishCurrentActivityLocked");
-            if (oomAdj) {
-                mService.updateOomAdj();
-            }
-            return r;
-        }
-
-        // make sure the record is cleaned out of other places.
-        mStackSupervisor.mStoppingActivities.remove(r);
-        mStackSupervisor.mGoingToSleepActivities.remove(r);
-        mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(r);
-        final ActivityState prevState = r.getState();
-        if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + r);
-
-        r.setState(FINISHING, "finishCurrentActivityLocked");
-        final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE
-                && prevState == PAUSED && (r.getStack() != display.getFocusedStack()
-                        || (next == null && display.topRunningActivity() == null));
-
-        if (mode == FINISH_IMMEDIATELY
-                || (prevState == PAUSED
-                    && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
-                || finishingInNonFocusedStackOrNoRunning
-                || prevState == STOPPING
-                || prevState == STOPPED
-                || prevState == ActivityState.INITIALIZING) {
-            r.makeFinishingLocked();
-            boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
-
-            if (finishingInNonFocusedStackOrNoRunning) {
-                // Finishing activity that was in paused state and it was in not currently focused
-                // stack, need to make something visible in its place. Also if the display does not
-                // have running activity, the configuration may need to be updated for restoring
-                // original orientation of the display.
-                mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
-                        false /* markFrozenIfConfigChanged */, true /* deferResume */);
-            }
-            if (activityRemoved) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-            }
-            if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
-                    "destroyActivityLocked: finishCurrentActivityLocked r=" + r +
-                    " destroy returned removed=" + activityRemoved);
-            return activityRemoved ? null : r;
-        }
-
-        // Need to go through the full pause cycle to get this
-        // activity into the stopped state and then finish it.
-        if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r);
-        mStackSupervisor.mFinishingActivities.add(r);
-        r.resumeKeyDispatchingLocked();
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-        return r;
-    }
-
-    void finishAllActivitiesLocked(boolean immediately) {
-        boolean noActivitiesInStack = true;
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                noActivitiesInStack = false;
-                if (r.finishing && !immediately) {
-                    continue;
-                }
-                Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r + " immediately");
-                finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false,
-                        "finishAllActivitiesLocked");
-            }
-        }
-        if (noActivitiesInStack) {
-            remove();
-        }
-    }
-
-    /** @return true if the stack behind this one is a standard activity type. */
-    boolean inFrontOfStandardStack() {
-        final ActivityDisplay display = getDisplay();
-        if (display == null) {
-            return false;
-        }
-        final int index = display.getIndexOf(this);
-        if (index == 0) {
-            return false;
-        }
-        final ActivityStack stackBehind = display.getChildAt(index - 1);
-        return stackBehind.isActivityTypeStandard();
-    }
-
-    boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) {
-        // Basic case: for simple app-centric recents, we need to recreate
-        // the task if the affinity has changed.
-        if (srec == null || srec.getTask().affinity == null ||
-                !srec.getTask().affinity.equals(destAffinity)) {
-            return true;
-        }
-        // Document-centric case: an app may be split in to multiple documents;
-        // they need to re-create their task if this current activity is the root
-        // of a document, unless simply finishing it will return them to the the
-        // correct app behind.
-        final TaskRecord task = srec.getTask();
-        if (srec.frontOfTask && task.getBaseIntent() != null && task.getBaseIntent().isDocument()) {
-            // Okay, this activity is at the root of its task.  What to do, what to do...
-            if (!inFrontOfStandardStack()) {
-                // Finishing won't return to an application, so we need to recreate.
-                return true;
-            }
-            // We now need to get the task below it to determine what to do.
-            int taskIdx = mTaskHistory.indexOf(task);
-            if (taskIdx <= 0) {
-                Slog.w(TAG, "shouldUpRecreateTask: task not in history for " + srec);
-                return false;
-            }
-            final TaskRecord prevTask = mTaskHistory.get(taskIdx);
-            if (!task.affinity.equals(prevTask.affinity)) {
-                // These are different apps, so need to recreate.
-                return true;
-            }
-        }
-        return false;
-    }
-
-    final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode,
-            Intent resultData) {
-        final TaskRecord task = srec.getTask();
-        final ArrayList<ActivityRecord> activities = task.mActivities;
-        final int start = activities.indexOf(srec);
-        if (!mTaskHistory.contains(task) || (start < 0)) {
-            return false;
-        }
-        int finishTo = start - 1;
-        ActivityRecord parent = finishTo < 0 ? null : activities.get(finishTo);
-        boolean foundParentInTask = false;
-        final ComponentName dest = destIntent.getComponent();
-        if (start > 0 && dest != null) {
-            for (int i = finishTo; i >= 0; i--) {
-                ActivityRecord r = activities.get(i);
-                if (r.info.packageName.equals(dest.getPackageName()) &&
-                        r.info.name.equals(dest.getClassName())) {
-                    finishTo = i;
-                    parent = r;
-                    foundParentInTask = true;
-                    break;
-                }
-            }
-        }
-
-        // TODO: There is a dup. of this block of code in ActivityTaskManagerService.finishActivity
-        // We should consolidate.
-        IActivityController controller = mService.mController;
-        if (controller != null) {
-            ActivityRecord next = topRunningActivityLocked(srec.appToken, 0);
-            if (next != null) {
-                // ask watcher if this is allowed
-                boolean resumeOK = true;
-                try {
-                    resumeOK = controller.activityResuming(next.packageName);
-                } catch (RemoteException e) {
-                    mService.mController = null;
-                    Watchdog.getInstance().setActivityController(null);
-                }
-
-                if (!resumeOK) {
-                    return false;
-                }
-            }
-        }
-        final long origId = Binder.clearCallingIdentity();
-        for (int i = start; i > finishTo; i--) {
-            ActivityRecord r = activities.get(i);
-            requestFinishActivityLocked(r.appToken, resultCode, resultData, "navigate-up", true);
-            // Only return the supplied result for the first activity finished
-            resultCode = Activity.RESULT_CANCELED;
-            resultData = null;
-        }
-
-        if (parent != null && foundParentInTask) {
-            final int parentLaunchMode = parent.info.launchMode;
-            final int destIntentFlags = destIntent.getFlags();
-            if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
-                    parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
-                    parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
-                    (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
-                parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent,
-                        srec.packageName);
-            } else {
-                try {
-                    ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
-                            destIntent.getComponent(), ActivityManagerService.STOCK_PM_FLAGS,
-                            srec.userId);
-                    // TODO(b/64750076): Check if calling pid should really be -1.
-                    final int res = mService.getActivityStartController()
-                            .obtainStarter(destIntent, "navigateUpTo")
-                            .setCaller(srec.app.getThread())
-                            .setActivityInfo(aInfo)
-                            .setResultTo(parent.appToken)
-                            .setCallingPid(-1)
-                            .setCallingUid(parent.launchedFromUid)
-                            .setCallingPackage(parent.launchedFromPackage)
-                            .setRealCallingPid(-1)
-                            .setRealCallingUid(parent.launchedFromUid)
-                            .setComponentSpecified(true)
-                            .execute();
-                    foundParentInTask = res == ActivityManager.START_SUCCESS;
-                } catch (RemoteException e) {
-                    foundParentInTask = false;
-                }
-                requestFinishActivityLocked(parent.appToken, resultCode,
-                        resultData, "navigate-top", true);
-            }
-        }
-        Binder.restoreCallingIdentity(origId);
-        return foundParentInTask;
-    }
-
-    /**
-     * Remove any state associated with the {@link ActivityRecord}. This should be called whenever
-     * an activity moves away from the stack.
-     */
-    void onActivityRemovedFromStack(ActivityRecord r) {
-        removeTimeoutsForActivityLocked(r);
-
-        if (mResumedActivity != null && mResumedActivity == r) {
-            setResumedActivity(null, "onActivityRemovedFromStack");
-        }
-        if (mPausingActivity != null && mPausingActivity == r) {
-            mPausingActivity = null;
-        }
-    }
-
-    void onActivityAddedToStack(ActivityRecord r) {
-        if(r.getState() == RESUMED) {
-            setResumedActivity(r, "onActivityAddedToStack");
-        }
-    }
-
-    /**
-     * Perform the common clean-up of an activity record.  This is called both
-     * as part of destroyActivityLocked() (when destroying the client-side
-     * representation) and cleaning things up as a result of its hosting
-     * processing going away, in which case there is no remaining client-side
-     * state to destroy so only the cleanup here is needed.
-     *
-     * Note: Call before #removeActivityFromHistoryLocked.
-     */
-    private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean setState) {
-        onActivityRemovedFromStack(r);
-
-        r.deferRelaunchUntilPaused = false;
-        r.frozenBeforeDestroy = false;
-
-        if (setState) {
-            if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (cleaning up)");
-            r.setState(DESTROYED, "cleanupActivityLocked");
-            if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + r);
-            r.app = null;
-        }
-
-        // Inform supervisor the activity has been removed.
-        mStackSupervisor.cleanupActivity(r);
-
-
-        // Remove any pending results.
-        if (r.finishing && r.pendingResults != null) {
-            for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
-                PendingIntentRecord rec = apr.get();
-                if (rec != null) {
-                    mService.mPendingIntentController.cancelIntentSender(rec, false);
-                }
-            }
-            r.pendingResults = null;
-        }
-
-        if (cleanServices) {
-            cleanUpActivityServicesLocked(r);
-        }
-
-        // Get rid of any pending idle timeouts.
-        removeTimeoutsForActivityLocked(r);
-        // Clean-up activities are no longer relaunching (e.g. app process died). Notify window
-        // manager so it can update its bookkeeping.
-        mWindowManager.notifyAppRelaunchesCleared(r.appToken);
-    }
-
-    private void removeTimeoutsForActivityLocked(ActivityRecord r) {
-        mStackSupervisor.removeTimeoutsForActivityLocked(r);
-        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
-        mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
-        mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
-        r.finishLaunchTickingLocked();
-    }
-
-    private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) {
-        finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null);
-        r.makeFinishingLocked();
-        if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
-                "Removing activity " + r + " from stack callers=" + Debug.getCallers(5));
-
-        r.takeFromHistory();
-        removeTimeoutsForActivityLocked(r);
-        if (DEBUG_STATES) Slog.v(TAG_STATES,
-                "Moving to DESTROYED: " + r + " (removed from history)");
-        r.setState(DESTROYED, "removeActivityFromHistoryLocked");
-        if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r);
-        r.app = null;
-        r.removeWindowContainer();
-        final TaskRecord task = r.getTask();
-        final boolean lastActivity = task != null ? task.removeActivity(r) : false;
-        // If we are removing the last activity in the task, not including task overlay activities,
-        // then fall through into the block below to remove the entire task itself
-        final boolean onlyHasTaskOverlays = task != null
-                ? task.onlyHasTaskOverlayActivities(false /* excludingFinishing */) : false;
-
-        if (lastActivity || onlyHasTaskOverlays) {
-            if (DEBUG_STACK) {
-                Slog.i(TAG_STACK,
-                        "removeActivityFromHistoryLocked: last activity removed from " + this
-                                + " onlyHasTaskOverlays=" + onlyHasTaskOverlays);
-            }
-
-            // The following block can be executed multiple times if there is more than one overlay.
-            // {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup
-            // of the task by id and exiting early if not found.
-            if (onlyHasTaskOverlays) {
-                // When destroying a task, tell the supervisor to remove it so that any activity it
-                // has can be cleaned up correctly. This is currently the only place where we remove
-                // a task with the DESTROYING mode, so instead of passing the onlyHasTaskOverlays
-                // state into removeTask(), we just clear the task here before the other residual
-                // work.
-                // TODO: If the callers to removeTask() changes such that we have multiple places
-                //       where we are destroying the task, move this back into removeTask()
-                mStackSupervisor.removeTaskByIdLocked(task.taskId, false /* killProcess */,
-                        !REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY, reason);
-            }
-
-            // We must keep the task around until all activities are destroyed. The following
-            // statement will only execute once since overlays are also considered activities.
-            if (lastActivity) {
-                removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING);
-            }
-        }
-        cleanUpActivityServicesLocked(r);
-        r.removeUriPermissionsLocked();
-    }
-
-    /**
-     * Perform clean-up of service connections in an activity record.
-     */
-    private void cleanUpActivityServicesLocked(ActivityRecord r) {
-        if (r.mServiceConnectionsHolder == null) {
-            return;
-        }
-        // Throw away any services that have been bound by this activity.
-        r.mServiceConnectionsHolder.disconnectActivityFromServices();
-    }
-
-    final void scheduleDestroyActivities(WindowProcessController owner, String reason) {
-        Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
-        msg.obj = new ScheduleDestroyArgs(owner, reason);
-        mHandler.sendMessage(msg);
-    }
-
-    private void destroyActivitiesLocked(WindowProcessController owner, String reason) {
-        boolean lastIsOpaque = false;
-        boolean activityRemoved = false;
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (r.finishing) {
-                    continue;
-                }
-                if (r.fullscreen) {
-                    lastIsOpaque = true;
-                }
-                if (owner != null && r.app != owner) {
-                    continue;
-                }
-                if (!lastIsOpaque) {
-                    continue;
-                }
-                if (r.isDestroyable()) {
-                    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Destroying " + r
-                            + " in state " + r.getState()
-                            + " resumed=" + mResumedActivity
-                            + " pausing=" + mPausingActivity + " for reason " + reason);
-                    if (destroyActivityLocked(r, true, reason)) {
-                        activityRemoved = true;
-                    }
-                }
-            }
-        }
-        if (activityRemoved) {
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-        }
-    }
-
-    final boolean safelyDestroyActivityLocked(ActivityRecord r, String reason) {
-        if (r.isDestroyable()) {
-            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
-                    "Destroying " + r + " in state " + r.getState() + " resumed=" + mResumedActivity
-                    + " pausing=" + mPausingActivity + " for reason " + reason);
-            return destroyActivityLocked(r, true, reason);
-        }
-        return false;
-    }
-
-    final int releaseSomeActivitiesLocked(WindowProcessController app, ArraySet<TaskRecord> tasks,
-            String reason) {
-        // Iterate over tasks starting at the back (oldest) first.
-        if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + app);
-        int maxTasks = tasks.size() / 4;
-        if (maxTasks < 1) {
-            maxTasks = 1;
-        }
-        int numReleased = 0;
-        for (int taskNdx = 0; taskNdx < mTaskHistory.size() && maxTasks > 0; taskNdx++) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            if (!tasks.contains(task)) {
-                continue;
-            }
-            if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Looking for activities to release in " + task);
-            int curNum = 0;
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-            for (int actNdx = 0; actNdx < activities.size(); actNdx++) {
-                final ActivityRecord activity = activities.get(actNdx);
-                if (activity.app == app && activity.isDestroyable()) {
-                    if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + activity
-                            + " in state " + activity.getState() + " resumed=" + mResumedActivity
-                            + " pausing=" + mPausingActivity + " for reason " + reason);
-                    destroyActivityLocked(activity, true, reason);
-                    if (activities.get(actNdx) != activity) {
-                        // Was removed from list, back up so we don't miss the next one.
-                        actNdx--;
-                    }
-                    curNum++;
-                }
-            }
-            if (curNum > 0) {
-                numReleased += curNum;
-                maxTasks--;
-                if (mTaskHistory.get(taskNdx) != task) {
-                    // The entire task got removed, back up so we don't miss the next one.
-                    taskNdx--;
-                }
-            }
-        }
-        if (DEBUG_RELEASE) Slog.d(TAG_RELEASE,
-                "Done releasing: did " + numReleased + " activities");
-        return numReleased;
-    }
-
-    /**
-     * Destroy the current CLIENT SIDE instance of an activity.  This may be
-     * called both when actually finishing an activity, or when performing
-     * a configuration switch where we destroy the current client-side object
-     * but then create a new client-side object for this same HistoryRecord.
-     */
-    final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
-        if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
-                "Removing activity from " + reason + ": token=" + r
-                        + ", app=" + (r.hasProcess() ? r.app.mName : "(null)"));
-
-        if (r.isState(DESTROYING, DESTROYED)) {
-            if (DEBUG_STATES) Slog.v(TAG_STATES, "activity " + r + " already destroying."
-                    + "skipping request with reason:" + reason);
-            return false;
-        }
-
-        EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
-                r.userId, System.identityHashCode(r),
-                r.getTask().taskId, r.shortComponentName, reason);
-
-        boolean removedFromHistory = false;
-
-        cleanUpActivityLocked(r, false, false);
-
-        final boolean hadApp = r.hasProcess();
-
-        if (hadApp) {
-            if (removeFromApp) {
-                r.app.removeActivity(r);
-                if (!r.app.hasActivities()) {
-                    mService.clearHeavyWeightProcessIfEquals(r.app);
-                }
-                if (!r.app.hasActivities()) {
-                    // Update any services we are bound to that might care about whether
-                    // their client may have activities.
-                    // No longer have activities, so update LRU list and oom adj.
-                    r.app.updateProcessInfo(true, true, false, true);
-                }
-            }
-
-            boolean skipDestroy = false;
-
-            try {
-                if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
-                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
-                        DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
-            } catch (Exception e) {
-                // We can just ignore exceptions here...  if the process
-                // has crashed, our death notification will clean things
-                // up.
-                //Slog.w(TAG, "Exception thrown during finish", e);
-                if (r.finishing) {
-                    removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy");
-                    removedFromHistory = true;
-                    skipDestroy = true;
-                }
-            }
-
-            r.nowVisible = false;
-
-            // If the activity is finishing, we need to wait on removing it
-            // from the list to give it a chance to do its cleanup.  During
-            // that time it may make calls back with its token so we need to
-            // be able to find it on the list and so we don't want to remove
-            // it from the list yet.  Otherwise, we can just immediately put
-            // it in the destroyed state since we are not removing it from the
-            // list.
-            if (r.finishing && !skipDestroy) {
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYING: " + r
-                        + " (destroy requested)");
-                r.setState(DESTROYING,
-                        "destroyActivityLocked. finishing and not skipping destroy");
-                Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
-                mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
-            } else {
-                if (DEBUG_STATES) Slog.v(TAG_STATES,
-                        "Moving to DESTROYED: " + r + " (destroy skipped)");
-                r.setState(DESTROYED,
-                        "destroyActivityLocked. not finishing or skipping destroy");
-                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + r);
-                r.app = null;
-            }
-        } else {
-            // remove this record from the history.
-            if (r.finishing) {
-                removeActivityFromHistoryLocked(r, reason + " hadNoApp");
-                removedFromHistory = true;
-            } else {
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (no app)");
-                r.setState(DESTROYED, "destroyActivityLocked. not finishing and had no app");
-                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + r);
-                r.app = null;
-            }
-        }
-
-        r.configChangeFlags = 0;
-
-        if (!mLRUActivities.remove(r) && hadApp) {
-            Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
-        }
-
-        return removedFromHistory;
-    }
-
-    final void activityDestroyedLocked(IBinder token, String reason) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            activityDestroyedLocked(ActivityRecord.forTokenLocked(token), reason);
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    /**
-     * This method is to only be called from the client via binder when the activity is destroyed
-     * AND finished.
-     */
-    final void activityDestroyedLocked(ActivityRecord record, String reason) {
-        if (record != null) {
-            mHandler.removeMessages(DESTROY_TIMEOUT_MSG, record);
-        }
-
-        if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + record);
-
-        if (isInStackLocked(record) != null) {
-            if (record.isState(DESTROYING, DESTROYED)) {
-                cleanUpActivityLocked(record, true, false);
-                removeActivityFromHistoryLocked(record, reason);
-            }
-        }
-
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-    }
-
-    private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
-            WindowProcessController app, String listName) {
-        int i = list.size();
-        if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
-            "Removing app " + app + " from list " + listName + " with " + i + " entries");
-        while (i > 0) {
-            i--;
-            ActivityRecord r = list.get(i);
-            if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Record #" + i + " " + r);
-            if (r.app == app) {
-                if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "---> REMOVING this entry!");
-                list.remove(i);
-                removeTimeoutsForActivityLocked(r);
-            }
-        }
-    }
-
-    private boolean removeHistoryRecordsForAppLocked(WindowProcessController app) {
-        removeHistoryRecordsForAppLocked(mLRUActivities, app, "mLRUActivities");
-        removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
-                "mStoppingActivities");
-        removeHistoryRecordsForAppLocked(mStackSupervisor.mGoingToSleepActivities, app,
-                "mGoingToSleepActivities");
-        removeHistoryRecordsForAppLocked(mStackSupervisor.mActivitiesWaitingForVisibleActivity, app,
-                "mActivitiesWaitingForVisibleActivity");
-        removeHistoryRecordsForAppLocked(mStackSupervisor.mFinishingActivities, app,
-                "mFinishingActivities");
-
-        boolean hasVisibleActivities = false;
-
-        // Clean out the history list.
-        int i = numActivities();
-        if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
-                "Removing app " + app + " from history with " + i + " entries");
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            mTmpActivities.clear();
-            mTmpActivities.addAll(activities);
-
-            while (!mTmpActivities.isEmpty()) {
-                final int targetIndex = mTmpActivities.size() - 1;
-                final ActivityRecord r = mTmpActivities.remove(targetIndex);
-                if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
-                        "Record #" + targetIndex + " " + r + ": app=" + r.app);
-
-                if (r.app == app) {
-                    if (r.visible) {
-                        hasVisibleActivities = true;
-                    }
-                    final boolean remove;
-                    if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
-                            || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
-                            && r.launchCount < 3 && !r.finishing) {
-                        // If the process crashed during a resize, always try to relaunch it, unless
-                        // it has failed more than twice. Skip activities that's already finishing
-                        // cleanly by itself.
-                        remove = false;
-                    } else if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
-                        // Don't currently have state for the activity, or
-                        // it is finishing -- always remove it.
-                        remove = true;
-                    } else if (!r.visible && r.launchCount > 2 &&
-                            r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
-                        // We have launched this activity too many times since it was
-                        // able to run, so give up and remove it.
-                        // (Note if the activity is visible, we don't remove the record.
-                        // We leave the dead window on the screen but the process will
-                        // not be restarted unless user explicitly tap on it.)
-                        remove = true;
-                    } else {
-                        // The process may be gone, but the activity lives on!
-                        remove = false;
-                    }
-                    if (remove) {
-                        if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE,
-                                "Removing activity " + r + " from stack at " + i
-                                + ": haveState=" + r.haveState
-                                + " stateNotNeeded=" + r.stateNotNeeded
-                                + " finishing=" + r.finishing
-                                + " state=" + r.getState() + " callers=" + Debug.getCallers(5));
-                        if (!r.finishing) {
-                            Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
-                            EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
-                                    r.userId, System.identityHashCode(r),
-                                    r.getTask().taskId, r.shortComponentName,
-                                    "proc died without state saved");
-                            if (r.getState() == RESUMED) {
-                                mService.updateUsageStats(r, false);
-                            }
-                        }
-                    } else {
-                        // We have the current state for this activity, so
-                        // it can be restarted later when needed.
-                        if (DEBUG_ALL) Slog.v(TAG, "Keeping entry, setting app to null");
-                        if (DEBUG_APP) Slog.v(TAG_APP,
-                                "Clearing app during removeHistory for activity " + r);
-                        r.app = null;
-                        // Set nowVisible to previous visible state. If the app was visible while
-                        // it died, we leave the dead window on screen so it's basically visible.
-                        // This is needed when user later tap on the dead window, we need to stop
-                        // other apps when user transfers focus to the restarted activity.
-                        r.nowVisible = r.visible;
-                        if (!r.haveState) {
-                            if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE,
-                                    "App died, clearing saved state of " + r);
-                            r.icicle = null;
-                        }
-                    }
-                    cleanUpActivityLocked(r, true, true);
-                    if (remove) {
-                        removeActivityFromHistoryLocked(r, "appDied");
-                    }
-                }
-            }
-        }
-
-        return hasVisibleActivities;
-    }
-
-    private void updateTransitLocked(int transit, ActivityOptions options) {
-        if (options != null) {
-            ActivityRecord r = topRunningActivityLocked();
-            if (r != null && !r.isState(RESUMED)) {
-                r.updateOptionsLocked(options);
-            } else {
-                ActivityOptions.abort(options);
-            }
-        }
-        mWindowManager.prepareAppTransition(transit, false);
-    }
-
-    private void updateTaskMovement(TaskRecord task, boolean toFront) {
-        if (task.isPersistable) {
-            task.mLastTimeMoved = System.currentTimeMillis();
-            // Sign is used to keep tasks sorted when persisted. Tasks sent to the bottom most
-            // recently will be most negative, tasks sent to the bottom before that will be less
-            // negative. Similarly for recent tasks moved to the top which will be most positive.
-            if (!toFront) {
-                task.mLastTimeMoved *= -1;
-            }
-        }
-        mStackSupervisor.invalidateTaskLayers();
-    }
-
-    final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
-            AppTimeTracker timeTracker, String reason) {
-        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
-
-        final ActivityStack topStack = getDisplay().getTopStack();
-        final ActivityRecord topActivity = topStack != null ? topStack.getTopActivity() : null;
-        final int numTasks = mTaskHistory.size();
-        final int index = mTaskHistory.indexOf(tr);
-        if (numTasks == 0 || index < 0)  {
-            // nothing to do!
-            if (noAnimation) {
-                ActivityOptions.abort(options);
-            } else {
-                updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
-            }
-            return;
-        }
-
-        if (timeTracker != null) {
-            // The caller wants a time tracker associated with this task.
-            for (int i = tr.mActivities.size() - 1; i >= 0; i--) {
-                tr.mActivities.get(i).appTimeTracker = timeTracker;
-            }
-        }
-
-        try {
-            // Defer updating the IME target since the new IME target will try to get computed
-            // before updating all closing and opening apps, which can cause the ime target to
-            // get calculated incorrectly.
-            getDisplay().deferUpdateImeTarget();
-
-            // Shift all activities with this task up to the top
-            // of the stack, keeping them in the same internal order.
-            insertTaskAtTop(tr, null);
-
-            // Don't refocus if invisible to current user
-            final ActivityRecord top = tr.getTopActivity();
-            if (top == null || !top.okToShowLocked()) {
-                if (top != null) {
-                    mStackSupervisor.mRecentTasks.add(top.getTask());
-                }
-                ActivityOptions.abort(options);
-                return;
-            }
-
-            // Set focus to the top running activity of this stack.
-            final ActivityRecord r = topRunningActivityLocked();
-            if (r != null) {
-                r.moveFocusableActivityToTop(reason);
-            }
-
-            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
-            if (noAnimation) {
-                mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
-                if (r != null) {
-                    mStackSupervisor.mNoAnimActivities.add(r);
-                }
-                ActivityOptions.abort(options);
-            } else {
-                updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
-            }
-            // If a new task is moved to the front, then mark the existing top activity as
-            // supporting
-
-            // picture-in-picture while paused only if the task would not be considered an oerlay
-            // on top
-            // of the current activity (eg. not fullscreen, or the assistant)
-            if (canEnterPipOnTaskSwitch(topActivity, tr, null /* toFrontActivity */,
-                    options)) {
-                topActivity.supportsEnterPipOnTaskSwitch = true;
-            }
-
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-            EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
-
-            mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.taskId);
-        } finally {
-            getDisplay().continueUpdateImeTarget();
-        }
-    }
-
-    /**
-     * Worker method for rearranging history stack. Implements the function of moving all
-     * activities for a specific task (gathering them if disjoint) into a single group at the
-     * bottom of the stack.
-     *
-     * If a watcher is installed, the action is preflighted and the watcher has an opportunity
-     * to premeptively cancel the move.
-     *
-     * @param taskId The taskId to collect and move to the bottom.
-     * @return Returns true if the move completed, false if not.
-     */
-    final boolean moveTaskToBackLocked(int taskId) {
-        final TaskRecord tr = taskForIdLocked(taskId);
-        if (tr == null) {
-            Slog.i(TAG, "moveTaskToBack: bad taskId=" + taskId);
-            return false;
-        }
-        Slog.i(TAG, "moveTaskToBack: " + tr);
-
-        // In LockTask mode, moving a locked task to the back of the stack may expose unlocked
-        // ones. Therefore we need to check if this operation is allowed.
-        if (!mService.getLockTaskController().canMoveTaskToBack(tr)) {
-            return false;
-        }
-
-        // If we have a watcher, preflight the move before committing to it.  First check
-        // for *other* available tasks, but if none are available, then try again allowing the
-        // current task to be selected.
-        if (isTopStackOnDisplay() && mService.mController != null) {
-            ActivityRecord next = topRunningActivityLocked(null, taskId);
-            if (next == null) {
-                next = topRunningActivityLocked(null, 0);
-            }
-            if (next != null) {
-                // ask watcher if this is allowed
-                boolean moveOK = true;
-                try {
-                    moveOK = mService.mController.activityResuming(next.packageName);
-                } catch (RemoteException e) {
-                    mService.mController = null;
-                    Watchdog.getInstance().setActivityController(null);
-                }
-                if (!moveOK) {
-                    return false;
-                }
-            }
-        }
-
-        if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task=" + taskId);
-
-        mTaskHistory.remove(tr);
-        mTaskHistory.add(0, tr);
-        updateTaskMovement(tr, false);
-
-        mWindowManager.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
-        moveToBack("moveTaskToBackLocked", tr);
-
-        if (inPinnedWindowingMode()) {
-            mStackSupervisor.removeStack(this);
-            return true;
-        }
-
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-        return true;
-    }
-
-    static void logStartActivity(int tag, ActivityRecord r, TaskRecord task) {
-        final Uri data = r.intent.getData();
-        final String strData = data != null ? data.toSafeString() : null;
-
-        EventLog.writeEvent(tag,
-                r.userId, System.identityHashCode(r), task.taskId,
-                r.shortComponentName, r.intent.getAction(),
-                r.intent.getType(), strData, r.intent.getFlags());
-    }
-
-    /**
-     * Ensures all visible activities at or below the input activity have the right configuration.
-     */
-    void ensureVisibleActivitiesConfigurationLocked(ActivityRecord start, boolean preserveWindow) {
-        if (start == null || !start.visible) {
-            return;
-        }
-
-        final TaskRecord startTask = start.getTask();
-        boolean behindFullscreen = false;
-        boolean updatedConfig = false;
-
-        for (int taskIndex = mTaskHistory.indexOf(startTask); taskIndex >= 0; --taskIndex) {
-            final TaskRecord task = mTaskHistory.get(taskIndex);
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-            int activityIndex =
-                    (start.getTask() == task) ? activities.indexOf(start) : activities.size() - 1;
-            for (; activityIndex >= 0; --activityIndex) {
-                final ActivityRecord r = activities.get(activityIndex);
-                updatedConfig |= r.ensureActivityConfiguration(0 /* globalChanges */,
-                        preserveWindow);
-                if (r.fullscreen) {
-                    behindFullscreen = true;
-                    break;
-                }
-            }
-            if (behindFullscreen) {
-                break;
-            }
-        }
-        if (updatedConfig) {
-            // Ensure the resumed state of the focus activity if we updated the configuration of
-            // any activity.
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-        }
-    }
-
-    // TODO: Figure-out a way to consolidate with resize() method below.
-    @Override
-    public void requestResize(Rect bounds) {
-        mService.resizeStack(mStackId, bounds,
-                true /* allowResizeInDockedMode */, false /* preserveWindows */,
-                false /* animate */, -1 /* animationDuration */);
-    }
-
-    // TODO: Can only be called from special methods in ActivityStackSupervisor.
-    // Need to consolidate those calls points into this resize method so anyone can call directly.
-    void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds) {
-        if (!updateBoundsAllowed(bounds, tempTaskBounds, tempTaskInsetBounds)) {
-            return;
-        }
-
-        // Update override configurations of all tasks in the stack.
-        final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
-        final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds;
-
-        mTmpBounds.clear();
-        mTmpInsetBounds.clear();
-
-        synchronized (mWindowManager.getWindowManagerLock()) {
-            for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
-                final TaskRecord task = mTaskHistory.get(i);
-                if (task.isResizeable()) {
-                    if (inFreeformWindowingMode()) {
-                        // TODO(b/71028874): Can be removed since each freeform task is its own
-                        //                   stack.
-                        // For freeform stack we don't adjust the size of the tasks to match that
-                        // of the stack, but we do try to make sure the tasks are still contained
-                        // with the bounds of the stack.
-                        if (task.getOverrideBounds() != null) {
-                            mTmpRect2.set(task.getOverrideBounds());
-                            fitWithinBounds(mTmpRect2, bounds);
-                            task.updateOverrideConfiguration(mTmpRect2);
-                        }
-                    } else {
-                        task.updateOverrideConfiguration(taskBounds, insetBounds);
-                    }
-                }
-
-                mTmpBounds.put(task.taskId, task.getOverrideBounds());
-                if (tempTaskInsetBounds != null) {
-                    mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
-                }
-            }
-
-            mWindowContainerController.resize(bounds, mTmpBounds, mTmpInsetBounds);
-            setBounds(bounds);
-        }
-    }
-
-    void onPipAnimationEndResize() {
-        mWindowContainerController.onPipAnimationEndResize();
-    }
-
-
-    /**
-     * Adjust bounds to stay within stack bounds.
-     *
-     * Since bounds might be outside of stack bounds, this method tries to move the bounds in a way
-     * that keep them unchanged, but be contained within the stack bounds.
-     *
-     * @param bounds Bounds to be adjusted.
-     * @param stackBounds Bounds within which the other bounds should remain.
-     */
-    private static void fitWithinBounds(Rect bounds, Rect stackBounds) {
-        if (stackBounds == null || stackBounds.isEmpty() || stackBounds.contains(bounds)) {
-            return;
-        }
-
-        if (bounds.left < stackBounds.left || bounds.right > stackBounds.right) {
-            final int maxRight = stackBounds.right
-                    - (stackBounds.width() / FIT_WITHIN_BOUNDS_DIVIDER);
-            int horizontalDiff = stackBounds.left - bounds.left;
-            if ((horizontalDiff < 0 && bounds.left >= maxRight)
-                    || (bounds.left + horizontalDiff >= maxRight)) {
-                horizontalDiff = maxRight - bounds.left;
-            }
-            bounds.left += horizontalDiff;
-            bounds.right += horizontalDiff;
-        }
-
-        if (bounds.top < stackBounds.top || bounds.bottom > stackBounds.bottom) {
-            final int maxBottom = stackBounds.bottom
-                    - (stackBounds.height() / FIT_WITHIN_BOUNDS_DIVIDER);
-            int verticalDiff = stackBounds.top - bounds.top;
-            if ((verticalDiff < 0 && bounds.top >= maxBottom)
-                    || (bounds.top + verticalDiff >= maxBottom)) {
-                verticalDiff = maxBottom - bounds.top;
-            }
-            bounds.top += verticalDiff;
-            bounds.bottom += verticalDiff;
-        }
-    }
-
-    boolean willActivityBeVisibleLocked(IBinder token) {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (r.appToken == token) {
-                    return true;
-                }
-                if (r.fullscreen && !r.finishing) {
-                    return false;
-                }
-            }
-        }
-        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        if (r == null) {
-            return false;
-        }
-        if (r.finishing) Slog.e(TAG, "willActivityBeVisibleLocked: Returning false,"
-                + " would have returned true for r=" + r);
-        return !r.finishing;
-    }
-
-    void closeSystemDialogsLocked() {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
-                    finishActivityLocked(r, Activity.RESULT_CANCELED, null, "close-sys", true);
-                }
-            }
-        }
-    }
-
-    boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses,
-            boolean doit, boolean evenPersistent, int userId) {
-        boolean didSomething = false;
-        TaskRecord lastTask = null;
-        ComponentName homeActivity = null;
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            mTmpActivities.clear();
-            mTmpActivities.addAll(activities);
-
-            while (!mTmpActivities.isEmpty()) {
-                ActivityRecord r = mTmpActivities.remove(0);
-                final boolean sameComponent =
-                        (r.packageName.equals(packageName) && (filterByClasses == null
-                                || filterByClasses.contains(r.realActivity.getClassName())))
-                        || (packageName == null && r.userId == userId);
-                if ((userId == UserHandle.USER_ALL || r.userId == userId)
-                        && (sameComponent || r.getTask() == lastTask)
-                        && (r.app == null || evenPersistent || !r.app.isPersistent())) {
-                    if (!doit) {
-                        if (r.finishing) {
-                            // If this activity is just finishing, then it is not
-                            // interesting as far as something to stop.
-                            continue;
-                        }
-                        return true;
-                    }
-                    if (r.isActivityTypeHome()) {
-                        if (homeActivity != null && homeActivity.equals(r.realActivity)) {
-                            Slog.i(TAG, "Skip force-stop again " + r);
-                            continue;
-                        } else {
-                            homeActivity = r.realActivity;
-                        }
-                    }
-                    didSomething = true;
-                    Slog.i(TAG, "  Force finishing activity " + r);
-                    if (sameComponent) {
-                        if (r.hasProcess()) {
-                            r.app.setRemoved(true);
-                        }
-                        r.app = null;
-                    }
-                    lastTask = r.getTask();
-                    finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop",
-                            true);
-                }
-            }
-        }
-        return didSomething;
-    }
-
-    /**
-     * @return The set of running tasks through {@param tasksOut} that are available to the caller.
-     *         If {@param ignoreActivityType} or {@param ignoreWindowingMode} are not undefined,
-     *         then skip running tasks that match those types.
-     */
-    void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType,
-            @WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) {
-        boolean focusedStack = mStackSupervisor.getTopDisplayFocusedStack() == this;
-        boolean topTask = true;
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            if (task.getTopActivity() == null) {
-                // Skip if there are no activities in the task
-                continue;
-            }
-            if (!allowed && !task.isActivityTypeHome() && task.effectiveUid != callingUid) {
-                // Skip if the caller can't fetch this task
-                continue;
-            }
-            if (ignoreActivityType != ACTIVITY_TYPE_UNDEFINED
-                    && task.getActivityType() == ignoreActivityType) {
-                // Skip ignored activity type
-                continue;
-            }
-            if (ignoreWindowingMode != WINDOWING_MODE_UNDEFINED
-                    && task.getWindowingMode() == ignoreWindowingMode) {
-                // Skip ignored windowing mode
-                continue;
-            }
-            if (focusedStack && topTask) {
-                // For the focused stack top task, update the last stack active time so that it can
-                // be used to determine the order of the tasks (it may not be set for newly created
-                // tasks)
-                task.lastActiveTime = SystemClock.elapsedRealtime();
-                topTask = false;
-            }
-            tasksOut.add(task);
-        }
-    }
-
-    void unhandledBackLocked() {
-        final int top = mTaskHistory.size() - 1;
-        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Performing unhandledBack(): top activity at " + top);
-        if (top >= 0) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
-            int activityTop = activities.size() - 1;
-            if (activityTop >= 0) {
-                finishActivityLocked(activities.get(activityTop), Activity.RESULT_CANCELED, null,
-                        "unhandled-back", true);
-            }
-        }
-    }
-
-    /**
-     * Reset local parameters because an app's activity died.
-     * @param app The app of the activity that died.
-     * @return result from removeHistoryRecordsForAppLocked.
-     */
-    boolean handleAppDiedLocked(WindowProcessController app) {
-        if (mPausingActivity != null && mPausingActivity.app == app) {
-            if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG_PAUSE,
-                    "App died while pausing: " + mPausingActivity);
-            mPausingActivity = null;
-        }
-        if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
-            mLastPausedActivity = null;
-            mLastNoHistoryActivity = null;
-        }
-
-        return removeHistoryRecordsForAppLocked(app);
-    }
-
-    void handleAppCrashLocked(WindowProcessController app) {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (r.app == app) {
-                    Slog.w(TAG, "  Force finishing activity "
-                            + r.intent.getComponent().flattenToShortString());
-                    // Force the destroy to skip right to removal.
-                    r.app = null;
-                    mWindowManager.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE,
-                            false /* alwaysKeepCurrent */);
-                    finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false,
-                            "handleAppCrashedLocked");
-                }
-            }
-        }
-    }
-
-    boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
-            boolean dumpClient, String dumpPackage, boolean needSep) {
-
-        if (mTaskHistory.isEmpty()) {
-            return false;
-        }
-        final String prefix = "    ";
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            if (needSep) {
-                pw.println("");
-            }
-            pw.println(prefix + "Task id #" + task.taskId);
-            pw.println(prefix + "mBounds=" + task.getOverrideBounds());
-            pw.println(prefix + "mMinWidth=" + task.mMinWidth);
-            pw.println(prefix + "mMinHeight=" + task.mMinHeight);
-            pw.println(prefix + "mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
-            pw.println(prefix + "* " + task);
-            task.dump(pw, prefix + "  ");
-            ActivityStackSupervisor.dumpHistoryList(fd, pw, mTaskHistory.get(taskNdx).mActivities,
-                    prefix, "Hist", true, !dumpAll, dumpClient, dumpPackage, false, null, task);
-        }
-        return true;
-    }
-
-    ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
-        ArrayList<ActivityRecord> activities = new ArrayList<>();
-
-        if ("all".equals(name)) {
-            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-                activities.addAll(mTaskHistory.get(taskNdx).mActivities);
-            }
-        } else if ("top".equals(name)) {
-            final int top = mTaskHistory.size() - 1;
-            if (top >= 0) {
-                final ArrayList<ActivityRecord> list = mTaskHistory.get(top).mActivities;
-                int listTop = list.size() - 1;
-                if (listTop >= 0) {
-                    activities.add(list.get(listTop));
-                }
-            }
-        } else {
-            ItemMatcher matcher = new ItemMatcher();
-            matcher.build(name);
-
-            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-                for (ActivityRecord r1 : mTaskHistory.get(taskNdx).mActivities) {
-                    if (matcher.match(r1, r1.intent.getComponent())) {
-                        activities.add(r1);
-                    }
-                }
-            }
-        }
-
-        return activities;
-    }
-
-    ActivityRecord restartPackage(String packageName) {
-        ActivityRecord starting = topRunningActivityLocked();
-
-        // All activities that came from the package must be
-        // restarted as if there was a config change.
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord a = activities.get(activityNdx);
-                if (a.info.packageName.equals(packageName)) {
-                    a.forceNewConfig = true;
-                    if (starting != null && a == starting && a.visible) {
-                        a.startFreezingScreenLocked(starting.app,
-                                CONFIG_SCREEN_LAYOUT);
-                    }
-                }
-            }
-        }
-
-        return starting;
-    }
-
-    /**
-     * Removes the input task from this stack.
-     * @param task to remove.
-     * @param reason for removal.
-     * @param mode task removal mode. Either {@link #REMOVE_TASK_MODE_DESTROYING},
-     *             {@link #REMOVE_TASK_MODE_MOVING}, {@link #REMOVE_TASK_MODE_MOVING_TO_TOP}.
-     */
-    void removeTask(TaskRecord task, String reason, int mode) {
-        for (ActivityRecord record : task.mActivities) {
-            onActivityRemovedFromStack(record);
-        }
-
-        final boolean removed = mTaskHistory.remove(task);
-
-        if (removed) {
-            EventLog.writeEvent(EventLogTags.AM_REMOVE_TASK, task.taskId, getStackId());
-        }
-
-        removeActivitiesFromLRUListLocked(task);
-        updateTaskMovement(task, true);
-
-        if (mode == REMOVE_TASK_MODE_DESTROYING && task.mActivities.isEmpty()) {
-            // TODO: VI what about activity?
-            final boolean isVoiceSession = task.voiceSession != null;
-            if (isVoiceSession) {
-                try {
-                    task.voiceSession.taskFinished(task.intent, task.taskId);
-                } catch (RemoteException e) {
-                }
-            }
-            if (task.autoRemoveFromRecents() || isVoiceSession) {
-                // Task creator asked to remove this when done, or this task was a voice
-                // interaction, so it should not remain on the recent tasks list.
-                mStackSupervisor.mRecentTasks.remove(task);
-            }
-
-            task.removeWindowContainer();
-        }
-
-        if (mTaskHistory.isEmpty()) {
-            if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this);
-            // We only need to adjust focused stack if this stack is in focus and we are not in the
-            // process of moving the task to the top of the stack that will be focused.
-            if (mode != REMOVE_TASK_MODE_MOVING_TO_TOP
-                    && mStackSupervisor.isTopDisplayFocusedStack(this)) {
-                String myReason = reason + " leftTaskHistoryEmpty";
-                if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) {
-                    getDisplay().moveHomeStackToFront(myReason);
-                }
-            }
-            if (isAttached()) {
-                getDisplay().positionChildAtBottom(this);
-            }
-            if (!isActivityTypeHome() || getDisplay().isRemoved()) {
-                remove();
-            }
-        }
-
-        task.setStack(null);
-
-        // Notify if a task from the pinned stack is being removed (or moved depending on the mode)
-        if (inPinnedWindowingMode()) {
-            mService.getTaskChangeNotificationController().notifyActivityUnpinned();
-        }
-    }
-
-    TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            boolean toTop) {
-        return createTaskRecord(taskId, info, intent, voiceSession, voiceInteractor, toTop,
-                null /*activity*/, null /*source*/, null /*options*/);
-    }
-
-    TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            boolean toTop, ActivityRecord activity, ActivityRecord source,
-            ActivityOptions options) {
-        final TaskRecord task = TaskRecord.create(
-                mService, taskId, info, intent, voiceSession, voiceInteractor);
-        // add the task to stack first, mTaskPositioner might need the stack association
-        addTask(task, toTop, "createTaskRecord");
-        final int displayId = mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY;
-        final boolean isLockscreenShown = mService.mStackSupervisor.getKeyguardController()
-                .isKeyguardOrAodShowing(displayId);
-        if (!mStackSupervisor.getLaunchParamsController()
-                .layoutTask(task, info.windowLayout, activity, source, options)
-                && !matchParentBounds() && task.isResizeable() && !isLockscreenShown) {
-            task.updateOverrideConfiguration(getOverrideBounds());
-        }
-        task.createWindowContainer(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
-        return task;
-    }
-
-    ArrayList<TaskRecord> getAllTasks() {
-        return new ArrayList<>(mTaskHistory);
-    }
-
-    void addTask(final TaskRecord task, final boolean toTop, String reason) {
-        addTask(task, toTop ? MAX_VALUE : 0, true /* schedulePictureInPictureModeChange */, reason);
-        if (toTop) {
-            // TODO: figure-out a way to remove this call.
-            positionChildWindowContainerAtTop(task);
-        }
-    }
-
-    // TODO: This shouldn't allow automatic reparenting. Remove the call to preAddTask and deal
-    // with the fall-out...
-    void addTask(final TaskRecord task, int position, boolean schedulePictureInPictureModeChange,
-            String reason) {
-        // TODO: Is this remove really needed? Need to look into the call path for the other addTask
-        mTaskHistory.remove(task);
-        position = getAdjustedPositionForTask(task, position, null /* starting */);
-        final boolean toTop = position >= mTaskHistory.size();
-        final ActivityStack prevStack = preAddTask(task, reason, toTop);
-
-        mTaskHistory.add(position, task);
-        task.setStack(this);
-
-        updateTaskMovement(task, toTop);
-
-        postAddTask(task, prevStack, schedulePictureInPictureModeChange);
-    }
-
-    void positionChildAt(TaskRecord task, int index) {
-
-        if (task.getStack() != this) {
-            throw new IllegalArgumentException("AS.positionChildAt: task=" + task
-                    + " is not a child of stack=" + this + " current parent=" + task.getStack());
-        }
-
-        task.updateOverrideConfigurationForStack(this);
-
-        final ActivityRecord topRunningActivity = task.topRunningActivityLocked();
-        final boolean wasResumed = topRunningActivity == task.getStack().mResumedActivity;
-        insertTaskAtPosition(task, index);
-        task.setStack(this);
-        postAddTask(task, null /* prevStack */, true /* schedulePictureInPictureModeChange */);
-
-        if (wasResumed) {
-            if (mResumedActivity != null) {
-                Log.wtf(TAG, "mResumedActivity was already set when moving mResumedActivity from"
-                        + " other stack to this stack mResumedActivity=" + mResumedActivity
-                        + " other mResumedActivity=" + topRunningActivity);
-            }
-            topRunningActivity.setState(RESUMED, "positionChildAt");
-        }
-
-        // The task might have already been running and its visibility needs to be synchronized with
-        // the visibility of the stack / windows.
-        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-    }
-
-    private ActivityStack preAddTask(TaskRecord task, String reason, boolean toTop) {
-        final ActivityStack prevStack = task.getStack();
-        if (prevStack != null && prevStack != this) {
-            prevStack.removeTask(task, reason,
-                    toTop ? REMOVE_TASK_MODE_MOVING_TO_TOP : REMOVE_TASK_MODE_MOVING);
-        }
-        return prevStack;
-    }
-
-    /**
-     * @param schedulePictureInPictureModeChange specifies whether or not to schedule the PiP mode
-     *            change. Callers may set this to false if they are explicitly scheduling PiP mode
-     *            changes themselves, like during the PiP animation
-     */
-    private void postAddTask(TaskRecord task, ActivityStack prevStack,
-            boolean schedulePictureInPictureModeChange) {
-        if (schedulePictureInPictureModeChange && prevStack != null) {
-            mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, prevStack);
-        } else if (task.voiceSession != null) {
-            try {
-                task.voiceSession.taskStarted(task.intent, task.taskId);
-            } catch (RemoteException e) {
-            }
-        }
-    }
-
-    public void setAlwaysOnTop(boolean alwaysOnTop) {
-        if (isAlwaysOnTop() == alwaysOnTop) {
-            return;
-        }
-        super.setAlwaysOnTop(alwaysOnTop);
-        final ActivityDisplay display = getDisplay();
-        // positionChildAtTop() must be called even when always on top gets turned off because we
-        // need to make sure that the stack is moved from among always on top windows to below other
-        // always on top windows. Since the position the stack should be inserted into is calculated
-        // properly in {@link ActivityDisplay#getTopInsertPosition()} in both cases, we can just
-        // request that the stack is put at top here.
-        display.positionChildAtTop(this, false /* includingParents */);
-    }
-
-    /** NOTE: Should only be called from {@link TaskRecord#reparent}. */
-    void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
-            boolean setPause, String reason) {
-        if (!moveToFront) {
-            return;
-        }
-
-        final ActivityState origState = r.getState();
-        // If the activity owns the last resumed activity, transfer that together,
-        // so that we don't resume the same activity again in the new stack.
-        // Apps may depend on onResume()/onPause() being called in pairs.
-        if (setResume) {
-            r.setState(RESUMED, "moveToFrontAndResumeStateIfNeeded");
-            updateLRUListLocked(r);
-        }
-        // If the activity was previously pausing, then ensure we transfer that as well
-        if (setPause) {
-            mPausingActivity = r;
-            schedulePauseTimeout(r);
-        }
-        // Move the stack in which we are placing the activity to the front.
-        moveToFront(reason);
-        // If the original state is resumed, there is no state change to update focused app.
-        // So here makes sure the activity focus is set if it is the top.
-        if (origState == RESUMED && r == mStackSupervisor.getTopResumedActivity()) {
-            // TODO(b/111361570): Support multiple focused apps in WM
-            mService.setResumedActivityUncheckLocked(r, reason);
-        }
-    }
-
-    public int getStackId() {
-        return mStackId;
-    }
-
-    @Override
-    public String toString() {
-        return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
-                + " stackId=" + mStackId + " type=" + activityTypeToString(getActivityType())
-                + " mode=" + windowingModeToString(getWindowingMode())
-                + " visible=" + shouldBeVisible(null /* starting */)
-                + " translucent=" + isStackTranslucent(null /* starting */)
-                + ", "
-                + mTaskHistory.size() + " tasks}";
-    }
-
-    void onLockTaskPackagesUpdated() {
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            mTaskHistory.get(taskNdx).setLockTaskAuth();
-        }
-    }
-
-    void executeAppTransition(ActivityOptions options) {
-        mWindowManager.executeAppTransition();
-        ActivityOptions.abort(options);
-    }
-
-    boolean shouldSleepActivities() {
-        final ActivityDisplay display = getDisplay();
-
-        // Do not sleep activities in this stack if we're marked as focused and the keyguard
-        // is in the process of going away.
-        if (isFocusedStackOnDisplay()
-                && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
-            return false;
-        }
-
-        return display != null ? display.isSleeping() : mService.isSleepingLocked();
-    }
-
-    boolean shouldSleepOrShutDownActivities() {
-        return shouldSleepActivities() || mService.mShuttingDown;
-    }
-
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
-        proto.write(ID, mStackId);
-        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            task.writeToProto(proto, TASKS);
-        }
-        if (mResumedActivity != null) {
-            mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
-        }
-        proto.write(DISPLAY_ID, mDisplayId);
-        if (!matchParentBounds()) {
-            final Rect bounds = getOverrideBounds();
-            bounds.writeToProto(proto, BOUNDS);
-        }
-
-        // TODO: Remove, no longer needed with windowingMode.
-        proto.write(FULLSCREEN, matchParentBounds());
-        proto.end(token);
-    }
-}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
deleted file mode 100644
index 257a004..0000000
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ /dev/null
@@ -1,4846 +0,0 @@
-/*
- * Copyright (C) 2013 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.server.am;
-
-import static android.Manifest.permission.ACTIVITY_EMBEDDING;
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
-import static android.Manifest.permission.START_ANY_ACTIVITY;
-import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
-import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
-import static android.app.ActivityManager.START_FLAG_DEBUG;
-import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
-import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
-import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
-import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
-import static android.app.WaitResult.INVALID_DELAY;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.windowingModeToString;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
-import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY;
-import static android.content.pm.PackageManager.PERMISSION_DENIED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.graphics.Rect.copyOrNull;
-import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
-import static android.os.Process.SYSTEM_UID;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.Display.TYPE_VIRTUAL;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
-import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
-import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
-import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
-import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
-import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
-import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_IDLE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityTaskManagerService.ANIMATE;
-import static com.android.server.am.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
-import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
-import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
-import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
-
-import static java.lang.Integer.MAX_VALUE;
-
-import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackInfo;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityOptions;
-import android.app.AppOpsManager;
-import android.app.IApplicationThread;
-import android.app.ProfilerInfo;
-import android.app.ResultInfo;
-import android.app.WaitResult;
-import android.app.WindowConfiguration.ActivityType;
-import android.app.WindowConfiguration.WindowingMode;
-import android.app.servertransaction.ActivityLifecycleItem;
-import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.LaunchActivityItem;
-import android.app.servertransaction.PauseActivityItem;
-import android.app.servertransaction.ResumeActivityItem;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
-import android.hardware.display.DisplayManagerInternal;
-import android.hardware.power.V1_0.PowerHint;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.os.WorkSource;
-import android.provider.MediaStore;
-import android.service.voice.IVoiceInteractionSession;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.DisplayMetrics;
-import android.util.EventLog;
-import android.util.IntArray;
-import android.util.MergedConfiguration;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
-import android.view.Display;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.ReferrerIntent;
-import com.android.internal.os.TransferPipe;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.LocalServices;
-import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
-import com.android.server.wm.ConfigurationContainer;
-import com.android.server.wm.DisplayWindowController;
-import com.android.server.wm.PinnedStackWindowController;
-import com.android.server.wm.RootWindowContainerController;
-import com.android.server.wm.RootWindowContainerListener;
-import com.android.server.wm.WindowManagerService;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
-        RecentTasks.Callbacks, RootWindowContainerListener {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_ATM;
-    private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
-    private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
-    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
-    private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
-    private static final String TAG_STACK = TAG + POSTFIX_STACK;
-    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
-    static final String TAG_STATES = TAG + POSTFIX_STATES;
-    static final String TAG_TASKS = TAG + POSTFIX_TASKS;
-
-    /** How long we wait until giving up on the last activity telling us it is idle. */
-    static final int IDLE_TIMEOUT = 10 * 1000;
-
-    /** How long we can hold the sleep wake lock before giving up. */
-    static final int SLEEP_TIMEOUT = 5 * 1000;
-
-    // How long we can hold the launch wake lock before giving up.
-    static final int LAUNCH_TIMEOUT = 10 * 1000;
-
-    static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG;
-    static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_STACK_MSG + 1;
-    static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
-    static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
-    static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
-    static final int HANDLE_DISPLAY_ADDED = FIRST_SUPERVISOR_STACK_MSG + 5;
-    static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6;
-    static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7;
-    static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
-    static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
-    static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
-
-    private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
-
-    // Used to indicate if an object (e.g. stack) that we are trying to get
-    // should be created if it doesn't exist already.
-    static final boolean CREATE_IF_NEEDED = true;
-
-    // Used to indicate that windows of activities should be preserved during the resize.
-    static final boolean PRESERVE_WINDOWS = true;
-
-    // Used to indicate if an object (e.g. task) should be moved/created
-    // at the top of its container (e.g. stack).
-    static final boolean ON_TOP = true;
-
-    // Don't execute any calls to resume.
-    static final boolean DEFER_RESUME = true;
-
-    // Used to indicate that a task is removed it should also be removed from recents.
-    static final boolean REMOVE_FROM_RECENTS = true;
-
-    // Used to indicate that pausing an activity should occur immediately without waiting for
-    // the activity callback indicating that it has completed pausing
-    static final boolean PAUSE_IMMEDIATELY = true;
-
-    /** True if the docked stack is currently being resized. */
-    private boolean mDockedStackResizing;
-
-    /**
-     * True if there are pending docked bounds that need to be applied after
-     * {@link #mDockedStackResizing} is reset to false.
-     */
-    private boolean mHasPendingDockedBounds;
-    private Rect mPendingDockedBounds;
-    private Rect mPendingTempDockedTaskBounds;
-    private Rect mPendingTempDockedTaskInsetBounds;
-    private Rect mPendingTempOtherTaskBounds;
-    private Rect mPendingTempOtherTaskInsetBounds;
-
-    /**
-     * The modes which affect which tasks are returned when calling
-     * {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}.
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            MATCH_TASK_IN_STACKS_ONLY,
-            MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
-            MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
-    })
-    public @interface AnyTaskForIdMatchTaskMode {}
-    // Match only tasks in the current stacks
-    static final int MATCH_TASK_IN_STACKS_ONLY = 0;
-    // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks
-    static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1;
-    // Match either tasks in the current stacks, or in the recent tasks, restoring it to the
-    // provided stack id
-    static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2;
-
-    // Activity actions an app cannot start if it uses a permission which is not granted.
-    private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
-            new ArrayMap<>();
-
-    static {
-        ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE,
-                Manifest.permission.CAMERA);
-        ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_VIDEO_CAPTURE,
-                Manifest.permission.CAMERA);
-        ACTION_TO_RUNTIME_PERMISSION.put(Intent.ACTION_CALL,
-                Manifest.permission.CALL_PHONE);
-    }
-
-    /** Action restriction: launching the activity is not restricted. */
-    private static final int ACTIVITY_RESTRICTION_NONE = 0;
-    /** Action restriction: launching the activity is restricted by a permission. */
-    private static final int ACTIVITY_RESTRICTION_PERMISSION = 1;
-    /** Action restriction: launching the activity is restricted by an app op. */
-    private static final int ACTIVITY_RESTRICTION_APPOP = 2;
-
-    // For debugging to make sure the caller when acquiring/releasing our
-    // wake lock is the system process.
-    static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
-    /** The number of distinct task ids that can be assigned to the tasks of a single user */
-    private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
-
-    ActivityTaskManagerService mService;
-
-    /** The historial list of recent tasks including inactive tasks */
-    RecentTasks mRecentTasks;
-
-    /** Helper class to abstract out logic for fetching the set of currently running tasks */
-    private RunningTasks mRunningTasks;
-
-    final ActivityStackSupervisorHandler mHandler;
-    final Looper mLooper;
-
-    /** Short cut */
-    WindowManagerService mWindowManager;
-    DisplayManager mDisplayManager;
-
-    private LaunchParamsController mLaunchParamsController;
-
-    /**
-     * Maps the task identifier that activities are currently being started in to the userId of the
-     * task. Each time a new task is created, the entry for the userId of the task is incremented
-     */
-    private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20);
-
-    /** The current user */
-    int mCurrentUser;
-
-    /** List of activities that are waiting for a new activity to become visible before completing
-     * whatever operation they are supposed to do. */
-    // TODO: Remove mActivitiesWaitingForVisibleActivity list and just remove activity from
-    // mStoppingActivities when something else comes up.
-    final ArrayList<ActivityRecord> mActivitiesWaitingForVisibleActivity = new ArrayList<>();
-
-    /** List of processes waiting to find out when a specific activity becomes visible. */
-    private final ArrayList<WaitInfo> mWaitingForActivityVisible = new ArrayList<>();
-
-    /** List of processes waiting to find out about the next launched activity. */
-    final ArrayList<WaitResult> mWaitingActivityLaunched = new ArrayList<>();
-
-    /** List of activities that are ready to be stopped, but waiting for the next activity to
-     * settle down before doing so. */
-    final ArrayList<ActivityRecord> mStoppingActivities = new ArrayList<>();
-
-    /** List of activities that are ready to be finished, but waiting for the previous activity to
-     * settle down before doing so.  It contains ActivityRecord objects. */
-    final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<>();
-
-    /** List of activities that are in the process of going to sleep. */
-    final ArrayList<ActivityRecord> mGoingToSleepActivities = new ArrayList<>();
-
-    /** List of activities whose multi-window mode changed that we need to report to the
-     * application */
-    final ArrayList<ActivityRecord> mMultiWindowModeChangedActivities = new ArrayList<>();
-
-    /** List of activities whose picture-in-picture mode changed that we need to report to the
-     * application */
-    final ArrayList<ActivityRecord> mPipModeChangedActivities = new ArrayList<>();
-
-    /**
-     * Animations that for the current transition have requested not to
-     * be considered for the transition animation.
-     */
-    final ArrayList<ActivityRecord> mNoAnimActivities = new ArrayList<>();
-
-    /** The target stack bounds for the picture-in-picture mode changed that we need to report to
-     * the application */
-    Rect mPipModeChangedTargetStackBounds;
-
-    /** Used on user changes */
-    final ArrayList<UserState> mStartingUsers = new ArrayList<>();
-
-    /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
-     * is being brought in front of us. */
-    boolean mUserLeaving = false;
-
-    /** Set when a power hint has started, but not ended. */
-    private boolean mPowerHintSent;
-
-    /**
-     * We don't want to allow the device to go to sleep while in the process
-     * of launching an activity.  This is primarily to allow alarm intent
-     * receivers to launch an activity and get that to run before the device
-     * goes back to sleep.
-     */
-    PowerManager.WakeLock mLaunchingActivity;
-
-    /**
-     * Set when the system is going to sleep, until we have
-     * successfully paused the current activity and released our wake lock.
-     * At that point the system is allowed to actually sleep.
-     */
-    PowerManager.WakeLock mGoingToSleep;
-
-    /**
-     * A list of tokens that cause the top activity to be put to sleep.
-     * They are used by components that may hide and block interaction with underlying
-     * activities.
-     */
-    final ArrayList<SleepToken> mSleepTokens = new ArrayList<>();
-
-    /** Stack id of the front stack when user switched, indexed by userId. */
-    SparseIntArray mUserStackInFront = new SparseIntArray(2);
-
-    /** Reference to default display so we can quickly look it up. */
-    private ActivityDisplay mDefaultDisplay;
-
-    /**
-     * List of displays which contain activities, sorted by z-order.
-     * The last entry in the list is the topmost.
-     */
-    private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>();
-
-    private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
-
-    private DisplayManagerInternal mDisplayManagerInternal;
-
-    /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
-    boolean inResumeTopActivity;
-
-    /**
-     * Temporary rect used during docked stack resize calculation so we don't need to create a new
-     * object each time.
-     */
-    private final Rect tempRect = new Rect();
-    private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
-
-    // The default minimal size that will be used if the activity doesn't specify its minimal size.
-    // It will be calculated when the default display gets added.
-    int mDefaultMinSizeOfResizeableTaskDp = -1;
-
-    // Whether tasks have moved and we need to rank the tasks before next OOM scoring
-    private boolean mTaskLayersChanged = true;
-
-    private ActivityMetricsLogger mActivityMetricsLogger;
-
-    private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
-
-    @Override
-    protected int getChildCount() {
-        return mActivityDisplays.size();
-    }
-
-    @Override
-    protected ActivityDisplay getChildAt(int index) {
-        return mActivityDisplays.get(index);
-    }
-
-    @Override
-    protected ConfigurationContainer getParent() {
-        return null;
-    }
-
-    Configuration getDisplayOverrideConfiguration(int displayId) {
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (activityDisplay == null) {
-            throw new IllegalArgumentException("No display found with id: " + displayId);
-        }
-
-        return activityDisplay.getOverrideConfiguration();
-    }
-
-    void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (activityDisplay == null) {
-            throw new IllegalArgumentException("No display found with id: " + displayId);
-        }
-
-        activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
-    }
-
-    /** Check if placing task or activity on specified display is allowed. */
-    boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
-            ActivityInfo activityInfo) {
-        if (displayId == DEFAULT_DISPLAY) {
-            // No restrictions for the default display.
-            return true;
-        }
-        if (!mService.mSupportsMultiDisplay) {
-            // Can't launch on secondary displays if feature is not supported.
-            return false;
-        }
-        if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
-            // Can't place activities to a display that has restricted launch rules.
-            // In this case the request should be made by explicitly adding target display id and
-            // by caller with corresponding permissions. See #isCallerAllowedToLaunchOnDisplay().
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Check if configuration of specified display matches current global config.
-     * Used to check if we can put a non-resizeable activity on a secondary display and it will get
-     * the same config as on the default display.
-     * @param displayId Id of the display to check.
-     * @return {@code true} if configuration matches.
-     */
-    private boolean displayConfigMatchesGlobal(int displayId) {
-        if (displayId == DEFAULT_DISPLAY) {
-            return true;
-        }
-        if (displayId == INVALID_DISPLAY) {
-            return false;
-        }
-        final ActivityDisplay targetDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (targetDisplay == null) {
-            throw new IllegalArgumentException("No display found with id: " + displayId);
-        }
-        return getConfiguration().equals(targetDisplay.getConfiguration());
-    }
-
-    static class FindTaskResult {
-        ActivityRecord mRecord;
-        boolean mIdealMatch;
-
-        void clear() {
-            mRecord = null;
-            mIdealMatch = false;
-        }
-
-        void setTo(FindTaskResult result) {
-            mRecord = result.mRecord;
-            mIdealMatch = result.mIdealMatch;
-        }
-    }
-
-    private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
-
-    /**
-     * Used to keep track whether app visibilities got changed since the last pause. Useful to
-     * determine whether to invoke the task stack change listener after pausing.
-     */
-    boolean mAppVisibilitiesChangedSinceLastPause;
-
-    /**
-     * Set of tasks that are in resizing mode during an app transition to fill the "void".
-     */
-    private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
-
-
-    /**
-     * If set to {@code false} all calls to resize the docked stack {@link #resizeDockedStackLocked}
-     * will be ignored. Useful for the case where the caller is handling resizing of other stack and
-     * moving tasks around and doesn't want dock stack to be resized due to an automatic trigger
-     * like the docked stack going empty.
-     */
-    private boolean mAllowDockedStackResize = true;
-
-    /**
-     * Is dock currently minimized.
-     */
-    boolean mIsDockMinimized;
-
-    private KeyguardController mKeyguardController;
-
-    private PowerManager mPowerManager;
-    private int mDeferResumeCount;
-
-    private boolean mInitialized;
-
-    private RootWindowContainerController mWindowContainerController;
-
-    /**
-     * Description of a request to start a new activity, which has been held
-     * due to app switches being disabled.
-     */
-    static class PendingActivityLaunch {
-        final ActivityRecord r;
-        final ActivityRecord sourceRecord;
-        final int startFlags;
-        final ActivityStack stack;
-        final WindowProcessController callerApp;
-
-        PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
-                int _startFlags, ActivityStack _stack, WindowProcessController app) {
-            r = _r;
-            sourceRecord = _sourceRecord;
-            startFlags = _startFlags;
-            stack = _stack;
-            callerApp = app;
-        }
-
-        void sendErrorResult(String message) {
-            try {
-                if (callerApp.hasThread()) {
-                    callerApp.getThread().scheduleCrash(message);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Exception scheduling crash of failed "
-                        + "activity launcher sourceRecord=" + sourceRecord, e);
-            }
-        }
-    }
-
-    public ActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
-        mService = service;
-        mLooper = looper;
-        mHandler = new ActivityStackSupervisorHandler(looper);
-    }
-
-    @VisibleForTesting
-    void setService(ActivityTaskManagerService service) {
-        mService = service;
-    }
-
-    @VisibleForTesting
-    void setWindowContainerController(RootWindowContainerController controller) {
-        mWindowContainerController = controller;
-    }
-
-    public void initialize() {
-        if (mInitialized) {
-            return;
-        }
-
-        mInitialized = true;
-        mRunningTasks = createRunningTasks();
-        mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, mHandler.getLooper());
-        mKeyguardController = new KeyguardController(mService, this);
-
-        mLaunchParamsController = new LaunchParamsController(mService);
-        mLaunchParamsController.registerDefaultModifiers(this);
-    }
-
-
-    public ActivityMetricsLogger getActivityMetricsLogger() {
-        return mActivityMetricsLogger;
-    }
-
-    public KeyguardController getKeyguardController() {
-        return mKeyguardController;
-    }
-
-    void setRecentTasks(RecentTasks recentTasks) {
-        mRecentTasks = recentTasks;
-        mRecentTasks.registerCallback(this);
-    }
-
-    @VisibleForTesting
-    RunningTasks createRunningTasks() {
-        return new RunningTasks();
-    }
-
-    /**
-     * At the time when the constructor runs, the power manager has not yet been
-     * initialized.  So we initialize our wakelocks afterwards.
-     */
-    void initPowerManagement() {
-        mPowerManager = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
-        mGoingToSleep = mPowerManager
-                .newWakeLock(PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
-        mLaunchingActivity = mPowerManager.newWakeLock(PARTIAL_WAKE_LOCK, "*launch*");
-        mLaunchingActivity.setReferenceCounted(false);
-    }
-
-    void setWindowManager(WindowManagerService wm) {
-        mWindowManager = wm;
-        getKeyguardController().setWindowManager(wm);
-        setWindowContainerController(new RootWindowContainerController(this));
-
-        mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
-        mDisplayManager.registerDisplayListener(this, null);
-        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
-
-        final Display[] displays = mDisplayManager.getDisplays();
-        for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
-            final Display display = displays[displayNdx];
-            final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
-            if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
-                mDefaultDisplay = activityDisplay;
-            }
-            addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
-        }
-        calculateDefaultMinimalSizeOfResizeableTasks();
-
-        final ActivityDisplay defaultDisplay = getDefaultDisplay();
-
-        defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
-        positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
-    }
-
-    /** Change the z-order of the given display. */
-    private void positionChildAt(ActivityDisplay display, int position) {
-        if (position >= mActivityDisplays.size()) {
-            position = mActivityDisplays.size() - 1;
-        } else if (position < 0) {
-            position = 0;
-        }
-
-        if (mActivityDisplays.isEmpty()) {
-            mActivityDisplays.add(display);
-        } else if (mActivityDisplays.get(position) != display) {
-            mActivityDisplays.remove(display);
-            mActivityDisplays.add(position, display);
-        }
-    }
-
-    @Override
-    public void onChildPositionChanged(DisplayWindowController childController, int position) {
-        // Assume AM lock is held from positionChildAt of controller in each hierarchy.
-        final ActivityDisplay display = getActivityDisplay(childController.getDisplayId());
-        if (display != null) {
-            positionChildAt(display, position);
-        }
-    }
-
-    ActivityStack getTopDisplayFocusedStack() {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack();
-            if (focusedStack != null) {
-                return focusedStack;
-            }
-        }
-        return null;
-    }
-
-    ActivityRecord getTopResumedActivity() {
-        final ActivityStack focusedStack = getTopDisplayFocusedStack();
-        if (focusedStack == null) {
-            return null;
-        }
-        final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
-        if (resumedActivity != null && resumedActivity.app != null) {
-            return resumedActivity;
-        }
-        // The top focused stack might not have a resumed activity yet - look on all displays in
-        // focus order.
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
-            if (resumedActivityOnDisplay != null) {
-                return resumedActivityOnDisplay;
-            }
-        }
-        return null;
-    }
-
-    boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
-        if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) {
-            return false;
-        }
-
-        return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
-    }
-
-    boolean isTopDisplayFocusedStack(ActivityStack stack) {
-        return stack != null && stack == getTopDisplayFocusedStack();
-    }
-
-    void moveRecentsStackToFront(String reason) {
-        final ActivityStack recentsStack = getDefaultDisplay().getStack(
-                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
-        if (recentsStack != null) {
-            recentsStack.moveToFront(reason);
-        }
-    }
-
-    boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
-        if (!mService.isBooting() && !mService.isBooted()) {
-            // Not ready yet!
-            return false;
-        }
-
-        if (displayId == INVALID_DISPLAY) {
-            displayId = DEFAULT_DISPLAY;
-        }
-
-        final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
-        final String myReason = reason + " resumeHomeActivity";
-
-        // Only resume home activity if isn't finishing.
-        if (r != null && !r.finishing) {
-            r.moveFocusableActivityToTop(myReason);
-            return resumeFocusedStacksTopActivitiesLocked(r.getStack(), prev, null);
-        }
-        return mService.startHomeActivityLocked(mCurrentUser, myReason, displayId);
-    }
-
-    boolean canStartHomeOnDisplay(ActivityInfo homeActivity, int displayId) {
-        if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
-                && displayId == mService.mVr2dDisplayId)) {
-            // No restrictions to default display or vr 2d display.
-            return true;
-        }
-
-        final ActivityDisplay display = getActivityDisplay(displayId);
-        if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
-            // Can't launch home on display that doesn't support system decorations.
-            return false;
-        }
-
-        final boolean supportMultipleInstance = homeActivity.launchMode != LAUNCH_SINGLE_TASK
-                && homeActivity.launchMode != LAUNCH_SINGLE_INSTANCE;
-        if (!supportMultipleInstance) {
-            // Can't launch home on other displays if it requested to be single instance.
-            return false;
-        }
-
-        return true;
-    }
-
-    TaskRecord anyTaskForIdLocked(int id) {
-        return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE);
-    }
-
-    TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode) {
-        return anyTaskForIdLocked(id, matchMode, null, !ON_TOP);
-    }
-
-    /**
-     * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
-     * @param id Id of the task we would like returned.
-     * @param matchMode The mode to match the given task id in.
-     * @param aOptions The activity options to use for restoration. Can be null.
-     * @param onTop If the stack for the task should be the topmost on the display.
-     */
-    TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode,
-            @Nullable ActivityOptions aOptions, boolean onTop) {
-        // If options are set, ensure that we are attempting to actually restore a task
-        if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
-            throw new IllegalArgumentException("Should not specify activity options for non-restore"
-                    + " lookup");
-        }
-
-        int numDisplays = mActivityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final TaskRecord task = stack.taskForIdLocked(id);
-                if (task == null) {
-                    continue;
-                }
-                if (aOptions != null) {
-                    // Resolve the stack the task should be placed in now based on options
-                    // and reparent if needed.
-                    final ActivityStack launchStack = getLaunchStack(null, aOptions, task, onTop);
-                    if (launchStack != null && stack != launchStack) {
-                        final int reparentMode = onTop
-                                ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
-                        task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
-                                "anyTaskForIdLocked");
-                    }
-                }
-                return task;
-            }
-        }
-
-        // If we are matching stack tasks only, return now
-        if (matchMode == MATCH_TASK_IN_STACKS_ONLY) {
-            return null;
-        }
-
-        // Otherwise, check the recent tasks and return if we find it there and we are not restoring
-        // the task from recents
-        if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
-        final TaskRecord task = mRecentTasks.getTask(id);
-
-        if (task == null) {
-            if (DEBUG_RECENTS) {
-                Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
-            }
-
-            return null;
-        }
-
-        if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
-            return task;
-        }
-
-        // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
-        if (!restoreRecentTaskLocked(task, aOptions, onTop)) {
-            if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
-                    "Couldn't restore task id=" + id + " found in recents");
-            return null;
-        }
-        if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
-        return task;
-    }
-
-    ActivityRecord isInAnyStackLocked(IBinder token) {
-        int numDisplays = mActivityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord r = stack.isInStackLocked(token);
-                if (r != null) {
-                    return r;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Detects whether we should show a lock screen in front of this task for a locked user.
-     * <p>
-     * We'll do this if either of the following holds:
-     * <ul>
-     *   <li>The top activity explicitly belongs to {@param userId}.</li>
-     *   <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
-     * </ul>
-     *
-     * @return {@code true} if the top activity looks like it belongs to {@param userId}.
-     */
-    private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) {
-        // To handle the case that work app is in the task but just is not the top one.
-        final ActivityRecord activityRecord = task.getTopActivity();
-        final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
-
-        return (activityRecord != null && activityRecord.userId == userId)
-                || (resultTo != null && resultTo.userId == userId);
-    }
-
-    /**
-     * Find all visible task stacks containing {@param userId} and intercept them with an activity
-     * to block out the contents and possibly start a credential-confirming intent.
-     *
-     * @param userId user handle for the locked managed profile.
-     */
-    void lockAllProfileTasks(@UserIdInt int userId) {
-        mWindowManager.deferSurfaceLayout();
-        try {
-            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getChildAt(stackNdx);
-                    final List<TaskRecord> tasks = stack.getAllTasks();
-                    for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
-                        final TaskRecord task = tasks.get(taskNdx);
-
-                        // Check the task for a top activity belonging to userId, or returning a
-                        // result to an activity belonging to userId. Example case: a document
-                        // picker for personal files, opened by a work app, should still get locked.
-                        if (taskTopActivityIsUser(task, userId)) {
-                            mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
-                                    task.taskId, userId);
-                        }
-                    }
-                }
-            }
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-        }
-    }
-
-    void setNextTaskIdForUserLocked(int taskId, int userId) {
-        final int currentTaskId = mCurTaskIdForUser.get(userId, -1);
-        if (taskId > currentTaskId) {
-            mCurTaskIdForUser.put(userId, taskId);
-        }
-    }
-
-    static int nextTaskIdForUser(int taskId, int userId) {
-        int nextTaskId = taskId + 1;
-        if (nextTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) {
-            // Wrap around as there will be smaller task ids that are available now.
-            nextTaskId -= MAX_TASK_IDS_PER_USER;
-        }
-        return nextTaskId;
-    }
-
-    int getNextTaskIdForUserLocked(int userId) {
-        final int currentTaskId = mCurTaskIdForUser.get(userId, userId * MAX_TASK_IDS_PER_USER);
-        // for a userId u, a taskId can only be in the range
-        // [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER
-        // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
-        int candidateTaskId = nextTaskIdForUser(currentTaskId, userId);
-        while (mRecentTasks.containsTaskId(candidateTaskId, userId)
-                || anyTaskForIdLocked(
-                        candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
-            candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
-            if (candidateTaskId == currentTaskId) {
-                // Something wrong!
-                // All MAX_TASK_IDS_PER_USER task ids are taken up by running tasks for this user
-                throw new IllegalStateException("Cannot get an available task id."
-                        + " Reached limit of " + MAX_TASK_IDS_PER_USER
-                        + " running tasks per user.");
-            }
-        }
-        mCurTaskIdForUser.put(userId, candidateTaskId);
-        return candidateTaskId;
-    }
-
-    boolean attachApplicationLocked(WindowProcessController app) throws RemoteException {
-        final String processName = app.mName;
-        boolean didSomething = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (!isTopDisplayFocusedStack(stack)) {
-                    continue;
-                }
-                stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
-                final ActivityRecord top = stack.topRunningActivityLocked();
-                final int size = mTmpActivityList.size();
-                for (int i = 0; i < size; i++) {
-                    final ActivityRecord activity = mTmpActivityList.get(i);
-                    if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
-                            && processName.equals(activity.processName)) {
-                        try {
-                            if (realStartActivityLocked(activity, app,
-                                    top == activity /* andResume */, true /* checkConfig */)) {
-                                didSomething = true;
-                            }
-                        } catch (RemoteException e) {
-                            Slog.w(TAG, "Exception in new application when starting activity "
-                                    + top.intent.getComponent().flattenToShortString(), e);
-                            throw e;
-                        }
-                    }
-                }
-            }
-        }
-        if (!didSomething) {
-            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        }
-        return didSomething;
-    }
-
-    boolean allResumedActivitiesIdle() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (!isTopDisplayFocusedStack(stack) || stack.numActivities() == 0) {
-                    continue;
-                }
-                final ActivityRecord resumedActivity = stack.getResumedActivity();
-                if (resumedActivity == null || !resumedActivity.idle) {
-                    if (DEBUG_STATES) Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
-                             + stack.mStackId + " " + resumedActivity + " not idle");
-                    return false;
-                }
-            }
-        }
-        // Send launch end powerhint when idle
-        sendPowerHintForLaunchEndIfNeeded();
-        return true;
-    }
-
-    private boolean allResumedActivitiesVisible() {
-        boolean foundResumed = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord r = stack.getResumedActivity();
-                if (r != null) {
-                    if (!r.nowVisible || mActivitiesWaitingForVisibleActivity.contains(r)) {
-                        return false;
-                    }
-                    foundResumed = true;
-                }
-            }
-        }
-        return foundResumed;
-    }
-
-    /**
-     * Pause all activities in either all of the stacks or just the back stacks.
-     * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
-     * @param resuming The resuming activity.
-     * @param dontWait The resuming activity isn't going to wait for all activities to be paused
-     *                 before resuming.
-     * @return true if any activity was paused as a result of this call.
-     */
-    boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
-        boolean someActivityPaused = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            someActivityPaused |= mActivityDisplays.get(displayNdx)
-                    .pauseBackStacks(userLeaving, resuming, dontWait);
-        }
-        return someActivityPaused;
-    }
-
-    boolean allPausedActivitiesComplete() {
-        boolean pausing = true;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord r = stack.mPausingActivity;
-                if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
-                    if (DEBUG_STATES) {
-                        Slog.d(TAG_STATES,
-                                "allPausedActivitiesComplete: r=" + r + " state=" + r.getState());
-                        pausing = false;
-                    } else {
-                        return false;
-                    }
-                }
-            }
-        }
-        return pausing;
-    }
-
-    void cancelInitializingActivities() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.cancelInitializingActivities();
-            }
-        }
-    }
-
-    void waitActivityVisible(ComponentName name, WaitResult result, long startTimeMs) {
-        final WaitInfo waitInfo = new WaitInfo(name, result, startTimeMs);
-        mWaitingForActivityVisible.add(waitInfo);
-    }
-
-    void cleanupActivity(ActivityRecord r) {
-        // Make sure this record is no longer in the pending finishes list.
-        // This could happen, for example, if we are trimming activities
-        // down to the max limit while they are still waiting to finish.
-        mFinishingActivities.remove(r);
-        mActivitiesWaitingForVisibleActivity.remove(r);
-
-        for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
-            if (mWaitingForActivityVisible.get(i).matches(r.realActivity)) {
-                mWaitingForActivityVisible.remove(i);
-            }
-        }
-    }
-
-    void reportActivityVisibleLocked(ActivityRecord r) {
-        sendWaitingVisibleReportLocked(r);
-    }
-
-    void sendWaitingVisibleReportLocked(ActivityRecord r) {
-        boolean changed = false;
-        for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
-            final WaitInfo w = mWaitingForActivityVisible.get(i);
-            if (w.matches(r.realActivity)) {
-                final WaitResult result = w.getResult();
-                changed = true;
-                result.timeout = false;
-                result.who = w.getComponent();
-                result.totalTime = SystemClock.uptimeMillis() - w.getStartTime();
-                mWaitingForActivityVisible.remove(w);
-            }
-        }
-        if (changed) {
-            mService.mGlobalLock.notifyAll();
-        }
-    }
-
-    void reportWaitingActivityLaunchedIfNeeded(ActivityRecord r, int result) {
-        if (mWaitingActivityLaunched.isEmpty()) {
-            return;
-        }
-
-        if (result != START_DELIVERED_TO_TOP && result != START_TASK_TO_FRONT) {
-            return;
-        }
-
-        boolean changed = false;
-
-        for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
-            WaitResult w = mWaitingActivityLaunched.remove(i);
-            if (w.who == null) {
-                changed = true;
-                w.result = result;
-
-                // Unlike START_TASK_TO_FRONT, When an intent is delivered to top, there
-                // will be no followup launch signals. Assign the result and launched component.
-                if (result == START_DELIVERED_TO_TOP) {
-                    w.who = r.realActivity;
-                }
-            }
-        }
-
-        if (changed) {
-            mService.mGlobalLock.notifyAll();
-        }
-    }
-
-    void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime) {
-        boolean changed = false;
-        for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
-            WaitResult w = mWaitingActivityLaunched.remove(i);
-            if (w.who == null) {
-                changed = true;
-                w.timeout = timeout;
-                if (r != null) {
-                    w.who = new ComponentName(r.info.packageName, r.info.name);
-                }
-                w.totalTime = totalTime;
-                // Do not modify w.result.
-            }
-        }
-        if (changed) {
-            mService.mGlobalLock.notifyAll();
-        }
-    }
-
-    ActivityRecord topRunningActivityLocked() {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
-            if (topActivity != null) {
-                return topActivity;
-            }
-        }
-        return null;
-    }
-
-    @VisibleForTesting
-    void getRunningTasks(int maxNum, List<RunningTaskInfo> list,
-            @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode,
-            int callingUid, boolean allowed) {
-        mRunningTasks.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode,
-                mActivityDisplays, callingUid, allowed);
-    }
-
-    ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
-            ProfilerInfo profilerInfo) {
-        final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
-        if (aInfo != null) {
-            // Store the found target back into the intent, because now that
-            // we have it we never want to do this again.  For example, if the
-            // user navigates back to this point in the history, we should
-            // always restart the exact same activity.
-            intent.setComponent(new ComponentName(
-                    aInfo.applicationInfo.packageName, aInfo.name));
-
-            // Don't debug things in the system process
-            if (!aInfo.processName.equals("system")) {
-                if ((startFlags & (START_FLAG_DEBUG | START_FLAG_NATIVE_DEBUGGING
-                        | START_FLAG_TRACK_ALLOCATION)) != 0 || profilerInfo != null) {
-                    /**
-                     * Assume safe to call into AMS synchronously because the call that set these
-                     * flags should have originated from AMS which will already have its lock held.
-                     * @see ActivityManagerService#startActivityAndWait(IApplicationThread, String,
-                     * Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle, int)
-                     * TODO(b/80414790): Investigate a better way of untangling this.
-                     */
-                    mService.mAmInternal.setDebugFlagsForStartingActivity(
-                            aInfo, startFlags, profilerInfo);
-                }
-            }
-            final String intentLaunchToken = intent.getLaunchToken();
-            if (aInfo.launchToken == null && intentLaunchToken != null) {
-                aInfo.launchToken = intentLaunchToken;
-            }
-        }
-        return aInfo;
-    }
-
-    ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
-            int filterCallingUid) {
-        synchronized (mService.mGlobalLock) {
-            try {
-                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
-                int modifiedFlags = flags
-                        | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
-                if (intent.isWebIntent()
-                            || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
-                    modifiedFlags |= PackageManager.MATCH_INSTANT;
-                }
-
-                // In order to allow cross-profile lookup, we clear the calling identity here.
-                // Note the binder identity won't affect the result, but filterCallingUid will.
-
-                // Cross-user/profile call check are done at the entry points
-                // (e.g. AMS.startActivityAsUser).
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    return mService.getPackageManagerInternalLocked().resolveIntent(
-                            intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
-            } finally {
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-            }
-        }
-    }
-
-    ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
-            ProfilerInfo profilerInfo, int userId, int filterCallingUid) {
-        final ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId, 0, filterCallingUid);
-        return resolveActivity(intent, rInfo, startFlags, profilerInfo);
-    }
-
-    private boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
-            boolean andResume, boolean checkConfig) throws RemoteException {
-
-        if (!allPausedActivitiesComplete()) {
-            // While there are activities pausing we skipping starting any new activities until
-            // pauses are complete. NOTE: that we also do this for activities that are starting in
-            // the paused state because they will first be resumed then paused on the client side.
-            if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
-                    "realStartActivityLocked: Skipping start of r=" + r
-                    + " some activities pausing...");
-            return false;
-        }
-
-        final TaskRecord task = r.getTask();
-        final ActivityStack stack = task.getStack();
-
-        beginDeferResume();
-
-        try {
-            r.startFreezingScreenLocked(proc, 0);
-
-            // schedule launch ticks to collect information about slow apps.
-            r.startLaunchTickingLocked();
-
-            r.setProcess(proc);
-
-            if (getKeyguardController().isKeyguardLocked()) {
-                r.notifyUnknownVisibilityLaunched();
-            }
-
-            // Have the window manager re-evaluate the orientation of the screen based on the new
-            // activity order.  Note that as a result of this, it can call back into the activity
-            // manager with a new orientation.  We don't care about that, because the activity is
-            // not currently running so we are just restarting it anyway.
-            if (checkConfig) {
-                // Deferring resume here because we're going to launch new activity shortly.
-                // We don't want to perform a redundant launch of the same record while ensuring
-                // configurations and trying to resume top activity of focused stack.
-                ensureVisibilityAndConfig(r, r.getDisplayId(),
-                        false /* markFrozenIfConfigChanged */, true /* deferResume */);
-            }
-
-            if (r.getStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
-                    true /* isTop */)) {
-                // We only set the visibility to true if the activity is allowed to be visible
-                // based on
-                // keyguard state. This avoids setting this into motion in window manager that is
-                // later cancelled due to later calls to ensure visible activities that set
-                // visibility back to false.
-                r.setVisibility(true);
-            }
-
-            final int applicationInfoUid =
-                    (r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
-            if ((r.userId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
-                Slog.wtf(TAG,
-                        "User ID for activity changing for " + r
-                                + " appInfo.uid=" + r.appInfo.uid
-                                + " info.ai.uid=" + applicationInfoUid
-                                + " old=" + r.app + " new=" + proc);
-            }
-
-            proc.clearWaitingToKill();
-            r.launchCount++;
-            r.lastLaunchTime = SystemClock.uptimeMillis();
-
-            if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
-
-            proc.addActivityIfNeeded(r);
-            proc.updateProcessInfo(false, true, true, true);
-
-            final LockTaskController lockTaskController = mService.getLockTaskController();
-            if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
-                    || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV
-                    || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED
-                            && lockTaskController.getLockTaskModeState()
-                                    == LOCK_TASK_MODE_LOCKED)) {
-                lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
-            }
-
-            try {
-                if (!proc.hasThread()) {
-                    throw new RemoteException();
-                }
-                List<ResultInfo> results = null;
-                List<ReferrerIntent> newIntents = null;
-                if (andResume) {
-                    // We don't need to deliver new intents and/or set results if activity is going
-                    // to pause immediately after launch.
-                    results = r.results;
-                    newIntents = r.newIntents;
-                }
-                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
-                        "Launching: " + r + " icicle=" + r.icicle + " with results=" + results
-                                + " newIntents=" + newIntents + " andResume=" + andResume);
-                EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.userId,
-                        System.identityHashCode(r), task.taskId, r.shortComponentName);
-                if (r.isActivityTypeHome()) {
-                    // Home process is the root process of the task.
-                    mService.mHomeProcess = task.mActivities.get(0).app;
-                }
-                mService.getPackageManagerInternalLocked().notifyPackageUse(
-                        r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
-                r.sleeping = false;
-                r.forceNewConfig = false;
-                mService.getAppWarningsLocked().onStartActivity(r);
-                r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
-                ProfilerInfo profilerInfo = proc.onStartActivity(mService.mTopProcessState);
-
-                // Because we could be starting an Activity in the system process this may not go
-                // across a Binder interface which would create a new Configuration. Consequently
-                // we have to always create a new Configuration here.
-
-                final MergedConfiguration mergedConfiguration = new MergedConfiguration(
-                        proc.getConfiguration(), r.getMergedOverrideConfiguration());
-                r.setLastReportedConfiguration(mergedConfiguration);
-
-                logIfTransactionTooLarge(r.intent, r.icicle);
-
-
-                // Create activity launch transaction.
-                final ClientTransaction clientTransaction = ClientTransaction.obtain(
-                        proc.getThread(), r.appToken);
-                clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
-                        System.identityHashCode(r), r.info,
-                        // TODO: Have this take the merged configuration instead of separate global
-                        // and override configs.
-                        mergedConfiguration.getGlobalConfiguration(),
-                        mergedConfiguration.getOverrideConfiguration(), r.compat,
-                        r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
-                        r.icicle, r.persistentState, results, newIntents,
-                        mService.isNextTransitionForward(), profilerInfo));
-
-                // Set desired final state.
-                final ActivityLifecycleItem lifecycleItem;
-                if (andResume) {
-                    lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
-                } else {
-                    lifecycleItem = PauseActivityItem.obtain();
-                }
-                clientTransaction.setLifecycleStateRequest(lifecycleItem);
-
-                // Schedule transaction.
-                mService.getLifecycleManager().scheduleTransaction(clientTransaction);
-
-                if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
-                        && mService.mHasHeavyWeightFeature) {
-                    // This may be a heavy-weight process! Note that the package manager will ensure
-                    // that only activity can run in the main process of the .apk, which is the only
-                    // thing that will be considered heavy-weight.
-                    if (proc.mName.equals(proc.mInfo.packageName)) {
-                        if (mService.mHeavyWeightProcess != null
-                                && mService.mHeavyWeightProcess != proc) {
-                            Slog.w(TAG, "Starting new heavy weight process " + proc
-                                    + " when already running "
-                                    + mService.mHeavyWeightProcess);
-                        }
-                        mService.setHeavyWeightProcess(r);
-                    }
-                }
-
-            } catch (RemoteException e) {
-                if (r.launchFailed) {
-                    // This is the second time we failed -- finish activity and give up.
-                    Slog.e(TAG, "Second failure launching "
-                            + r.intent.getComponent().flattenToShortString() + ", giving up", e);
-                    proc.appDied();
-                    stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
-                            "2nd-crash", false);
-                    return false;
-                }
-
-                // This is the first time we failed -- restart process and
-                // retry.
-                r.launchFailed = true;
-                proc.removeActivity(r);
-                throw e;
-            }
-        } finally {
-            endDeferResume();
-        }
-
-        r.launchFailed = false;
-        if (stack.updateLRUListLocked(r)) {
-            Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list");
-        }
-
-        // TODO(lifecycler): Resume or pause requests are done as part of launch transaction,
-        // so updating the state should be done accordingly.
-        if (andResume && readyToResume()) {
-            // As part of the process of launching, ActivityThread also performs
-            // a resume.
-            stack.minimalResumeActivityLocked(r);
-        } else {
-            // This activity is not starting in the resumed state... which should look like we asked
-            // it to pause+stop (but remain visible), and it has done so and reported back the
-            // current icicle and other state.
-            if (DEBUG_STATES) Slog.v(TAG_STATES,
-                    "Moving to PAUSED: " + r + " (starting in paused state)");
-            r.setState(PAUSED, "realStartActivityLocked");
-        }
-
-        // Launch the new version setup screen if needed.  We do this -after-
-        // launching the initial activity (that is, home), so that it can have
-        // a chance to initialize itself while in the background, making the
-        // switch back to it faster and look better.
-        if (isTopDisplayFocusedStack(stack)) {
-            mService.getActivityStartController().startSetupActivity();
-        }
-
-        // Update any services we are bound to that might care about whether
-        // their client may have activities.
-        if (r.app != null) {
-            r.app.updateServiceConnectionActivities();
-        }
-
-        return true;
-    }
-
-    /**
-     * Ensure all activities visibility, update orientation and configuration.
-     *
-     * @param starting The currently starting activity or {@code null} if there is none.
-     * @param displayId The id of the display where operation is executed.
-     * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
-     *                                  {@code true} if config changed.
-     * @param deferResume Whether to defer resume while updating config.
-     * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
-     *         because of configuration update.
-     */
-    boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
-            boolean markFrozenIfConfigChanged, boolean deferResume) {
-        // First ensure visibility without updating the config just yet. We need this to know what
-        // activities are affecting configuration now.
-        // Passing null here for 'starting' param value, so that visibility of actual starting
-        // activity will be properly updated.
-        ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
-                false /* preserveWindows */, false /* notifyClients */);
-
-        if (displayId == INVALID_DISPLAY) {
-            // The caller didn't provide a valid display id, skip updating config.
-            return true;
-        }
-
-        // Force-update the orientation from the WindowManager, since we need the true configuration
-        // to send to the client now.
-        final Configuration config = mWindowManager.updateOrientationFromAppTokens(
-                getDisplayOverrideConfiguration(displayId),
-                starting != null && starting.mayFreezeScreenLocked(starting.app)
-                        ? starting.appToken : null,
-                displayId, true /* forceUpdate */);
-        if (starting != null && markFrozenIfConfigChanged && config != null) {
-            starting.frozenBeforeDestroy = true;
-        }
-
-        // Update the configuration of the activities on the display.
-        return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
-                displayId);
-    }
-
-    private void logIfTransactionTooLarge(Intent intent, Bundle icicle) {
-        int extrasSize = 0;
-        if (intent != null) {
-            final Bundle extras = intent.getExtras();
-            if (extras != null) {
-                extrasSize = extras.getSize();
-            }
-        }
-        int icicleSize = (icicle == null ? 0 : icicle.getSize());
-        if (extrasSize + icicleSize > 200000) {
-            Slog.e(TAG, "Transaction too large, intent: " + intent + ", extras size: " + extrasSize
-                    + ", icicle size: " + icicleSize);
-        }
-    }
-
-    void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
-        // Is this activity's application already running?
-        final WindowProcessController wpc =
-                mService.getProcessController(r.processName, r.info.applicationInfo.uid);
-
-        if (wpc != null && wpc.hasThread()) {
-            try {
-                if ((r.info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
-                        || !"android".equals(r.info.packageName)) {
-                    // Don't add this if it is a platform component that is marked to run in
-                    // multiple processes, because this is actually part of the framework so doesn't
-                    // make sense to track as a separate apk in the process.
-                    wpc.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode);
-                }
-                realStartActivityLocked(r, wpc, andResume, checkConfig);
-                return;
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Exception when starting activity "
-                        + r.intent.getComponent().flattenToShortString(), e);
-            }
-
-            // If a dead object exception was thrown -- fall through to
-            // restart the application.
-        }
-
-        // Post message to start process to avoid possible deadlock of calling into AMS with the
-        // ATMS lock held.
-        final Message msg = PooledLambda.obtainMessage(
-                ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
-                r.info.applicationInfo, true, "activity", r.intent.getComponent());
-        mService.mH.sendMessage(msg);
-    }
-
-    void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
-        boolean sendHint = forceSend;
-
-        if (!sendHint) {
-            // Send power hint if we don't know what we're launching yet
-            sendHint = targetActivity == null || targetActivity.app == null;
-        }
-
-        if (!sendHint) { // targetActivity != null
-            // Send power hint when the activity's process is different than the current resumed
-            // activity on all displays, or if there are no resumed activities in the system.
-            boolean noResumedActivities = true;
-            boolean allFocusedProcessesDiffer = true;
-            for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
-                final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
-                final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
-                final WindowProcessController resumedActivityProcess =
-                    resumedActivity == null ? null : resumedActivity.app;
-
-                noResumedActivities &= resumedActivityProcess == null;
-                if (resumedActivityProcess != null) {
-                    allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
-                }
-            }
-            sendHint = noResumedActivities || allFocusedProcessesDiffer;
-        }
-
-        if (sendHint && mService.mPowerManagerInternal != null) {
-            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
-            mPowerHintSent = true;
-        }
-    }
-
-    void sendPowerHintForLaunchEndIfNeeded() {
-        // Trigger launch power hint if activity is launched
-        if (mPowerHintSent && mService.mPowerManagerInternal != null) {
-            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
-            mPowerHintSent = false;
-        }
-    }
-
-    boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
-            int requestCode, int callingPid, int callingUid, String callingPackage,
-            boolean ignoreTargetSecurity, boolean launchingInTask,
-            WindowProcessController callerApp, ActivityRecord resultRecord,
-            ActivityStack resultStack) {
-        final boolean isCallerRecents = mService.getRecentTasks() != null
-                && mService.getRecentTasks().isCallerRecents(callingUid);
-        final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
-                callingUid);
-        if (startAnyPerm == PERMISSION_GRANTED || (isCallerRecents && launchingInTask)) {
-            // If the caller has START_ANY_ACTIVITY, ignore all checks below. In addition, if the
-            // caller is the recents component and we are specifically starting an activity in an
-            // existing task, then also allow the activity to be fully relaunched.
-            return true;
-        }
-        final int componentRestriction = getComponentRestrictionForCallingPackage(
-                aInfo, callingPackage, callingPid, callingUid, ignoreTargetSecurity);
-        final int actionRestriction = getActionRestrictionForCallingPackage(
-                intent.getAction(), callingPackage, callingPid, callingUid);
-        if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
-                || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
-            if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(-1,
-                        resultRecord, resultWho, requestCode,
-                        Activity.RESULT_CANCELED, null);
-            }
-            final String msg;
-            if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
-                msg = "Permission Denial: starting " + intent.toString()
-                        + " from " + callerApp + " (pid=" + callingPid
-                        + ", uid=" + callingUid + ")" + " with revoked permission "
-                        + ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction());
-            } else if (!aInfo.exported) {
-                msg = "Permission Denial: starting " + intent.toString()
-                        + " from " + callerApp + " (pid=" + callingPid
-                        + ", uid=" + callingUid + ")"
-                        + " not exported from uid " + aInfo.applicationInfo.uid;
-            } else {
-                msg = "Permission Denial: starting " + intent.toString()
-                        + " from " + callerApp + " (pid=" + callingPid
-                        + ", uid=" + callingUid + ")"
-                        + " requires " + aInfo.permission;
-            }
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-
-        if (actionRestriction == ACTIVITY_RESTRICTION_APPOP) {
-            final String message = "Appop Denial: starting " + intent.toString()
-                    + " from " + callerApp + " (pid=" + callingPid
-                    + ", uid=" + callingUid + ")"
-                    + " requires " + AppOpsManager.permissionToOp(
-                            ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction()));
-            Slog.w(TAG, message);
-            return false;
-        } else if (componentRestriction == ACTIVITY_RESTRICTION_APPOP) {
-            final String message = "Appop Denial: starting " + intent.toString()
-                    + " from " + callerApp + " (pid=" + callingPid
-                    + ", uid=" + callingUid + ")"
-                    + " requires appop " + AppOpsManager.permissionToOp(aInfo.permission);
-            Slog.w(TAG, message);
-            return false;
-        }
-
-        return true;
-    }
-
-    /** Check if caller is allowed to launch activities on specified display. */
-    boolean isCallerAllowedToLaunchOnDisplay(int callingPid, int callingUid, int launchDisplayId,
-            ActivityInfo aInfo) {
-        if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check: displayId=" + launchDisplayId
-                + " callingPid=" + callingPid + " callingUid=" + callingUid);
-
-        if (callingPid == -1 && callingUid == -1) {
-            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check: no caller info, skip check");
-            return true;
-        }
-
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(launchDisplayId);
-        if (activityDisplay == null || activityDisplay.isRemoved()) {
-            Slog.w(TAG, "Launch on display check: display not found");
-            return false;
-        }
-
-        // Check if the caller has enough privileges to embed activities and launch to private
-        // displays.
-        final int startAnyPerm = mService.checkPermission(INTERNAL_SYSTEM_WINDOW, callingPid,
-                callingUid);
-        if (startAnyPerm == PERMISSION_GRANTED) {
-            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
-                    + " allow launch any on display");
-            return true;
-        }
-
-        // Check if caller is already present on display
-        final boolean uidPresentOnDisplay = activityDisplay.isUidPresent(callingUid);
-
-        final int displayOwnerUid = activityDisplay.mDisplay.getOwnerUid();
-        if (activityDisplay.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID
-                && displayOwnerUid != aInfo.applicationInfo.uid) {
-            // Limit launching on virtual displays, because their contents can be read from Surface
-            // by apps that created them.
-            if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
-                if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
-                        + " disallow launch on virtual display for not-embedded activity.");
-                return false;
-            }
-            // Check if the caller is allowed to embed activities from other apps.
-            if (mService.checkPermission(ACTIVITY_EMBEDDING, callingPid, callingUid)
-                    == PERMISSION_DENIED && !uidPresentOnDisplay) {
-                if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
-                        + " disallow activity embedding without permission.");
-                return false;
-            }
-        }
-
-        if (!activityDisplay.isPrivate()) {
-            // Anyone can launch on a public display.
-            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
-                    + " allow launch on public display");
-            return true;
-        }
-
-        // Check if the caller is the owner of the display.
-        if (displayOwnerUid == callingUid) {
-            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
-                    + " allow launch for owner of the display");
-            return true;
-        }
-
-        if (uidPresentOnDisplay) {
-            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
-                    + " allow launch for caller present on the display");
-            return true;
-        }
-
-        Slog.w(TAG, "Launch on display check: denied");
-        return false;
-    }
-
-    /** Update lists of UIDs that are present on displays and have access to them. */
-    void updateUIDsPresentOnDisplay() {
-        mDisplayAccessUIDs.clear();
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
-            // Only bother calculating the whitelist for private displays
-            if (activityDisplay.isPrivate()) {
-                mDisplayAccessUIDs.append(
-                        activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
-            }
-        }
-        // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
-        mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
-    }
-
-    UserInfo getUserInfo(int userId) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return UserManager.get(mService.mContext).getUserInfo(userId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    private int getComponentRestrictionForCallingPackage(ActivityInfo activityInfo,
-            String callingPackage, int callingPid, int callingUid, boolean ignoreTargetSecurity) {
-        if (!ignoreTargetSecurity && mService.checkComponentPermission(activityInfo.permission,
-                callingPid, callingUid, activityInfo.applicationInfo.uid, activityInfo.exported)
-                == PERMISSION_DENIED) {
-            return ACTIVITY_RESTRICTION_PERMISSION;
-        }
-
-        if (activityInfo.permission == null) {
-            return ACTIVITY_RESTRICTION_NONE;
-        }
-
-        final int opCode = AppOpsManager.permissionToOpCode(activityInfo.permission);
-        if (opCode == AppOpsManager.OP_NONE) {
-            return ACTIVITY_RESTRICTION_NONE;
-        }
-
-        if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage)
-                != AppOpsManager.MODE_ALLOWED) {
-            if (!ignoreTargetSecurity) {
-                return ACTIVITY_RESTRICTION_APPOP;
-            }
-        }
-
-        return ACTIVITY_RESTRICTION_NONE;
-    }
-
-    private int getActionRestrictionForCallingPackage(String action,
-            String callingPackage, int callingPid, int callingUid) {
-        if (action == null) {
-            return ACTIVITY_RESTRICTION_NONE;
-        }
-
-        String permission = ACTION_TO_RUNTIME_PERMISSION.get(action);
-        if (permission == null) {
-            return ACTIVITY_RESTRICTION_NONE;
-        }
-
-        final PackageInfo packageInfo;
-        try {
-            packageInfo = mService.mContext.getPackageManager()
-                    .getPackageInfo(callingPackage, PackageManager.GET_PERMISSIONS);
-        } catch (PackageManager.NameNotFoundException e) {
-            Slog.i(TAG, "Cannot find package info for " + callingPackage);
-            return ACTIVITY_RESTRICTION_NONE;
-        }
-
-        if (!ArrayUtils.contains(packageInfo.requestedPermissions, permission)) {
-            return ACTIVITY_RESTRICTION_NONE;
-        }
-
-        if (mService.checkPermission(permission, callingPid, callingUid) == PERMISSION_DENIED) {
-            return ACTIVITY_RESTRICTION_PERMISSION;
-        }
-
-        final int opCode = AppOpsManager.permissionToOpCode(permission);
-        if (opCode == AppOpsManager.OP_NONE) {
-            return ACTIVITY_RESTRICTION_NONE;
-        }
-
-        if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage)
-                != AppOpsManager.MODE_ALLOWED) {
-            return ACTIVITY_RESTRICTION_APPOP;
-        }
-
-        return ACTIVITY_RESTRICTION_NONE;
-    }
-
-    void setLaunchSource(int uid) {
-        mLaunchingActivity.setWorkSource(new WorkSource(uid));
-    }
-
-    void acquireLaunchWakelock() {
-        if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
-            throw new IllegalStateException("Calling must be system uid");
-        }
-        mLaunchingActivity.acquire();
-        if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
-            // To be safe, don't allow the wake lock to be held for too long.
-            mHandler.sendEmptyMessageDelayed(LAUNCH_TIMEOUT_MSG, LAUNCH_TIMEOUT);
-        }
-    }
-
-    /**
-     * Called when the frontmost task is idle.
-     * @return the state of mService.mAm.mBooting before this was called.
-     */
-    @GuardedBy("mService")
-    private boolean checkFinishBootingLocked() {
-        final boolean booting = mService.isBooting();
-        boolean enableScreen = false;
-        mService.setBooting(false);
-        if (!mService.isBooted()) {
-            mService.setBooted(true);
-            enableScreen = true;
-        }
-        if (booting || enableScreen) {
-            mService.postFinishBooting(booting, enableScreen);
-        }
-        return booting;
-    }
-
-    // Checked.
-    @GuardedBy("mService")
-    final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
-            boolean processPausingActivities, Configuration config) {
-        if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);
-
-        ArrayList<ActivityRecord> finishes = null;
-        ArrayList<UserState> startingUsers = null;
-        int NS = 0;
-        int NF = 0;
-        boolean booting = false;
-        boolean activityRemoved = false;
-
-        ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        if (r != null) {
-            if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternalLocked: Callers="
-                    + Debug.getCallers(4));
-            mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
-            r.finishLaunchTickingLocked();
-            if (fromTimeout) {
-                reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY);
-            }
-
-            // This is a hack to semi-deal with a race condition
-            // in the client where it can be constructed with a
-            // newer configuration from when we asked it to launch.
-            // We'll update with whatever configuration it now says
-            // it used to launch.
-            if (config != null) {
-                r.setLastReportedGlobalConfiguration(config);
-            }
-
-            // We are now idle.  If someone is waiting for a thumbnail from
-            // us, we can now deliver.
-            r.idle = true;
-
-            //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
-            if (isTopDisplayFocusedStack(r.getStack()) || fromTimeout) {
-                booting = checkFinishBootingLocked();
-            }
-
-            // When activity is idle, we consider the relaunch must be successful, so let's clear
-            // the flag.
-            r.mRelaunchReason = RELAUNCH_REASON_NONE;
-        }
-
-        if (allResumedActivitiesIdle()) {
-            if (r != null) {
-                mService.scheduleAppGcsLocked();
-            }
-
-            if (mLaunchingActivity.isHeld()) {
-                mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
-                if (VALIDATE_WAKE_LOCK_CALLER &&
-                        Binder.getCallingUid() != Process.myUid()) {
-                    throw new IllegalStateException("Calling must be system uid");
-                }
-                mLaunchingActivity.release();
-            }
-            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        }
-
-        // Atomically retrieve all of the other things to do.
-        final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
-                true /* remove */, processPausingActivities);
-        NS = stops != null ? stops.size() : 0;
-        if ((NF = mFinishingActivities.size()) > 0) {
-            finishes = new ArrayList<>(mFinishingActivities);
-            mFinishingActivities.clear();
-        }
-
-        if (mStartingUsers.size() > 0) {
-            startingUsers = new ArrayList<>(mStartingUsers);
-            mStartingUsers.clear();
-        }
-
-        // Stop any activities that are scheduled to do so but have been
-        // waiting for the next one to start.
-        for (int i = 0; i < NS; i++) {
-            r = stops.get(i);
-            final ActivityStack stack = r.getStack();
-            if (stack != null) {
-                if (r.finishing) {
-                    stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
-                            "activityIdleInternalLocked");
-                } else {
-                    stack.stopActivityLocked(r);
-                }
-            }
-        }
-
-        // Finish any activities that are scheduled to do so but have been
-        // waiting for the next one to start.
-        for (int i = 0; i < NF; i++) {
-            r = finishes.get(i);
-            final ActivityStack stack = r.getStack();
-            if (stack != null) {
-                activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
-            }
-        }
-
-        if (!booting) {
-            // Complete user switch
-            if (startingUsers != null) {
-                for (int i = 0; i < startingUsers.size(); i++) {
-                    mService.mAmInternal.finishUserSwitch(startingUsers.get(i));
-                }
-            }
-        }
-
-        mService.mAmInternal.trimApplications();
-        //dump();
-        //mWindowManager.dump();
-
-        if (activityRemoved) {
-            resumeFocusedStacksTopActivitiesLocked();
-        }
-
-        return r;
-    }
-
-    boolean handleAppDiedLocked(WindowProcessController app) {
-        boolean hasVisibleActivities = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                hasVisibleActivities |= stack.handleAppDiedLocked(app);
-            }
-        }
-        return hasVisibleActivities;
-    }
-
-    void closeSystemDialogsLocked() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.closeSystemDialogsLocked();
-            }
-        }
-    }
-
-    void removeUserLocked(int userId) {
-        mUserStackInFront.delete(userId);
-    }
-
-    /**
-     * Update the last used stack id for non-current user (current user's last
-     * used stack is the focused stack)
-     */
-    void updateUserStackLocked(int userId, ActivityStack stack) {
-        if (userId != mCurrentUser) {
-            mUserStackInFront.put(userId, stack != null ? stack.getStackId()
-                    : getDefaultDisplay().getHomeStack().mStackId);
-        }
-    }
-
-    /**
-     * @return true if some activity was finished (or would have finished if doit were true).
-     */
-    boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses,
-            boolean doit, boolean evenPersistent, int userId) {
-        boolean didSomething = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (stack.finishDisabledPackageActivitiesLocked(
-                        packageName, filterByClasses, doit, evenPersistent, userId)) {
-                    didSomething = true;
-                }
-            }
-        }
-        return didSomething;
-    }
-
-    void updatePreviousProcessLocked(ActivityRecord r) {
-        // Now that this process has stopped, we may want to consider
-        // it to be the previous app to try to keep around in case
-        // the user wants to return to it.
-
-        // First, found out what is currently the foreground app, so that
-        // we don't blow away the previous app if this activity is being
-        // hosted by the process that is actually still the foreground.
-        WindowProcessController fgApp = null;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (isTopDisplayFocusedStack(stack)) {
-                    final ActivityRecord resumedActivity = stack.getResumedActivity();
-                    if (resumedActivity != null) {
-                        fgApp = resumedActivity.app;
-                    } else if (stack.mPausingActivity != null) {
-                        fgApp = stack.mPausingActivity.app;
-                    }
-                    break;
-                }
-            }
-        }
-
-        // Now set this one as the previous process, only if that really
-        // makes sense to.
-        if (r.hasProcess() && fgApp != null && r.app != fgApp
-                && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
-                && r.app != mService.mHomeProcess) {
-            mService.mPreviousProcess = r.app;
-            mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
-        }
-    }
-
-    boolean resumeFocusedStacksTopActivitiesLocked() {
-        return resumeFocusedStacksTopActivitiesLocked(null, null, null);
-    }
-
-    boolean resumeFocusedStacksTopActivitiesLocked(
-            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
-
-        if (!readyToResume()) {
-            return false;
-        }
-
-        if (targetStack != null && (targetStack.isTopStackOnDisplay()
-                || getTopDisplayFocusedStack() == targetStack)) {
-            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
-        }
-
-        // Resume all top activities in focused stacks on all displays.
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final ActivityStack focusedStack = display.getFocusedStack();
-            if (focusedStack == null) {
-                continue;
-            }
-            final ActivityRecord r = focusedStack.topRunningActivityLocked();
-            if (r == null || !r.isState(RESUMED)) {
-                focusedStack.resumeTopActivityUncheckedLocked(null, null);
-            } else if (r.isState(RESUMED)) {
-                // Kick off any lingering app transitions form the MoveTaskToFront operation.
-                focusedStack.executeAppTransition(targetOptions);
-            }
-        }
-
-        return false;
-    }
-
-    void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.updateActivityApplicationInfoLocked(aInfo);
-            }
-        }
-    }
-
-    /**
-     * Finish the topmost activities in all stacks that belong to the crashed app.
-     * @param app The app that crashed.
-     * @param reason Reason to perform this action.
-     * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
-     */
-    int finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
-        TaskRecord finishedTask = null;
-        ActivityStack focusedStack = getTopDisplayFocusedStack();
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            // It is possible that request to finish activity might also remove its task and stack,
-            // so we need to be careful with indexes in the loop and check child count every time.
-            for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason);
-                if (stack == focusedStack || finishedTask == null) {
-                    finishedTask = t;
-                }
-            }
-        }
-        return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
-    }
-
-    void finishVoiceTask(IVoiceInteractionSession session) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final int numStacks = display.getChildCount();
-            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.finishVoiceTask(session);
-            }
-        }
-    }
-
-    /**
-     * This doesn't just find a task, it also moves the task to front.
-     */
-    void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
-            boolean forceNonResizeable) {
-        ActivityStack currentStack = task.getStack();
-        if (currentStack == null) {
-            Slog.e(TAG, "findTaskToMoveToFront: can't move task="
-                    + task + " to front. Stack is null");
-            return;
-        }
-
-        if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
-            mUserLeaving = true;
-        }
-
-        reason = reason + " findTaskToMoveToFront";
-        boolean reparented = false;
-        if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
-            final Rect bounds = options.getLaunchBounds();
-            task.updateOverrideConfiguration(bounds);
-
-            ActivityStack stack = getLaunchStack(null, options, task, ON_TOP);
-
-            if (stack != currentStack) {
-                moveHomeStackToFrontIfNeeded(flags, stack.getDisplay(), reason);
-                task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
-                        reason);
-                currentStack = stack;
-                reparented = true;
-                // task.reparent() should already placed the task on top,
-                // still need moveTaskToFrontLocked() below for any transition settings.
-            }
-            if (stack.resizeStackWithLaunchBounds()) {
-                resizeStackLocked(stack, bounds, null /* tempTaskBounds */,
-                        null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
-                        true /* allowResizeInDockedMode */, !DEFER_RESUME);
-            } else {
-                // WM resizeTask must be done after the task is moved to the correct stack,
-                // because Task's setBounds() also updates dim layer's bounds, but that has
-                // dependency on the stack.
-                task.resizeWindowContainer();
-            }
-        }
-
-        if (!reparented) {
-            moveHomeStackToFrontIfNeeded(flags, currentStack.getDisplay(), reason);
-        }
-
-        final ActivityRecord r = task.getTopActivity();
-        currentStack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
-                r == null ? null : r.appTimeTracker, reason);
-
-        if (DEBUG_STACK) Slog.d(TAG_STACK,
-                "findTaskToMoveToFront: moved to front of stack=" + currentStack);
-
-        handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY,
-                currentStack, forceNonResizeable);
-    }
-
-    private void moveHomeStackToFrontIfNeeded(int flags, ActivityDisplay display, String reason) {
-        final ActivityStack focusedStack = display.getFocusedStack();
-
-        if ((display.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
-                && (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0)
-                || (focusedStack != null && focusedStack.isActivityTypeRecents())) {
-            // We move home stack to front when we are on a fullscreen display and caller has
-            // requested the home activity to move with it. Or the previous stack is recents.
-            display.moveHomeStackToFront(reason);
-        }
-    }
-
-    boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) {
-        // We use the launch bounds in the activity options is the device supports freeform
-        // window management or is launching into the pinned stack.
-        if (options == null || options.getLaunchBounds() == null) {
-            return false;
-        }
-        return (mService.mSupportsPictureInPicture
-                && options.getLaunchWindowingMode() == WINDOWING_MODE_PINNED)
-                || mService.mSupportsFreeformWindowManagement;
-    }
-
-    LaunchParamsController getLaunchParamsController() {
-        return mLaunchParamsController;
-    }
-
-    protected <T extends ActivityStack> T getStack(int stackId) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final T stack = mActivityDisplays.get(i).getStack(stackId);
-            if (stack != null) {
-                return stack;
-            }
-        }
-        return null;
-    }
-
-    /** @see ActivityDisplay#getStack(int, int) */
-    private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
-            if (stack != null) {
-                return stack;
-            }
-        }
-        return null;
-    }
-
-    int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
-            @Nullable TaskRecord task) {
-        // Preference is given to the activity type for the activity then the task since the type
-        // once set shouldn't change.
-        int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
-        if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
-            activityType = task.getActivityType();
-        }
-        if (activityType != ACTIVITY_TYPE_UNDEFINED) {
-            return activityType;
-        }
-        if (options != null) {
-            activityType = options.getLaunchActivityType();
-        }
-        return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD;
-    }
-
-    <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
-            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
-        return getLaunchStack(r, options, candidateTask, onTop, INVALID_DISPLAY);
-    }
-
-    /**
-     * Returns the right stack to use for launching factoring in all the input parameters.
-     *
-     * @param r The activity we are trying to launch. Can be null.
-     * @param options The activity options used to the launch. Can be null.
-     * @param candidateTask The possible task the activity might be launched in. Can be null.
-     *
-     * @return The stack to use for the launch or INVALID_STACK_ID.
-     */
-    <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
-            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
-            int candidateDisplayId) {
-        int taskId = INVALID_TASK_ID;
-        int displayId = INVALID_DISPLAY;
-        //Rect bounds = null;
-
-        // We give preference to the launch preference in activity options.
-        if (options != null) {
-            taskId = options.getLaunchTaskId();
-            displayId = options.getLaunchDisplayId();
-            // TODO: Need to work this into the equation...
-            //bounds = options.getLaunchBounds();
-        }
-
-        // First preference for stack goes to the task Id set in the activity options. Use the stack
-        // associated with that if possible.
-        if (taskId != INVALID_TASK_ID) {
-            // Temporarily set the task id to invalid in case in re-entry.
-            options.setLaunchTaskId(INVALID_TASK_ID);
-            final TaskRecord task = anyTaskForIdLocked(taskId,
-                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
-            options.setLaunchTaskId(taskId);
-            if (task != null) {
-                return task.getStack();
-            }
-        }
-
-        final int activityType = resolveActivityType(r, options, candidateTask);
-        T stack = null;
-
-        // Next preference for stack goes to the display Id set in the activity options or the
-        // candidate display.
-        if (displayId == INVALID_DISPLAY) {
-            displayId = candidateDisplayId;
-        }
-        if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
-            if (r != null) {
-                stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options);
-                if (stack != null) {
-                    return stack;
-                }
-            }
-            final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
-            if (display != null) {
-                stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
-                if (stack != null) {
-                    return stack;
-                }
-            }
-        }
-
-        // Give preference to the stack and display of the input task and activity if they match the
-        // mode we want to launch into.
-        stack = null;
-        ActivityDisplay display = null;
-        if (candidateTask != null) {
-            stack = candidateTask.getStack();
-        }
-        if (stack == null && r != null) {
-            stack = r.getStack();
-        }
-        if (stack != null) {
-            display = stack.getDisplay();
-            if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
-                final int windowingMode =
-                        display.resolveWindowingMode(r, options, candidateTask, activityType);
-                if (stack.isCompatible(windowingMode, activityType)) {
-                    return stack;
-                }
-                if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
-                        && display.getSplitScreenPrimaryStack() == stack
-                        && candidateTask == stack.topTask()) {
-                    // This is a special case when we try to launch an activity that is currently on
-                    // top of split-screen primary stack, but is targeting split-screen secondary.
-                    // In this case we don't want to move it to another stack.
-                    // TODO(b/78788972): Remove after differentiating between preferred and required
-                    // launch options.
-                    return stack;
-                }
-            }
-        }
-
-        if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
-            display = getDefaultDisplay();
-        }
-
-        return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
-    }
-
-    /** @return true if activity record is null or can be launched on provided display. */
-    private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) {
-        if (r == null) {
-            return true;
-        }
-        return r.canBeLaunchedOnDisplay(displayId);
-    }
-
-    /**
-     * Get a topmost stack on the display, that is a valid launch stack for specified activity.
-     * If there is no such stack, new dynamic stack can be created.
-     * @param displayId Target display.
-     * @param r Activity that should be launched there.
-     * @param candidateTask The possible task the activity might be put in.
-     * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
-     */
-    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
-            @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options) {
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (activityDisplay == null) {
-            throw new IllegalArgumentException(
-                    "Display with displayId=" + displayId + " not found.");
-        }
-
-        if (!r.canBeLaunchedOnDisplay(displayId)) {
-            return null;
-        }
-
-        // If {@code r} is already in target display and its task is the same as the candidate task,
-        // the intention should be getting a launch stack for the reusable activity, so we can use
-        // the existing stack.
-        if (r.getDisplayId() == displayId && r.getTask() == candidateTask) {
-            return candidateTask.getStack();
-        }
-
-        // Return the topmost valid stack on the display.
-        for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = activityDisplay.getChildAt(i);
-            if (isValidLaunchStack(stack, displayId, r)) {
-                return stack;
-            }
-        }
-
-        // If there is no valid stack on the external display - check if new dynamic stack will do.
-        if (displayId != DEFAULT_DISPLAY) {
-            return activityDisplay.createStack(
-                    options != null ? options.getLaunchWindowingMode() : r.getWindowingMode(),
-                    options != null ? options.getLaunchActivityType() : r.getActivityType(),
-                    true /*onTop*/);
-        }
-
-        Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
-        return null;
-    }
-
-    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
-            @Nullable ActivityOptions options) {
-        return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options);
-    }
-
-    // TODO: Can probably be consolidated into getLaunchStack()...
-    private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) {
-        switch (stack.getActivityType()) {
-            case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
-            case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
-            case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
-        }
-        // There is a 1-to-1 relationship between stack and task when not in
-        // primary split-windowing mode.
-        if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            return false;
-        } else {
-            return r.supportsSplitScreenWindowingMode();
-        }
-    }
-
-    /**
-     * Get next focusable stack in the system. This will search through the stack on the same
-     * display as the current focused stack, looking for a focusable and visible stack, different
-     * from the target stack. If no valid candidates will be found, it will then go through all
-     * displays and stacks in last-focused order.
-     *
-     * @param currentFocus The stack that previously had focus.
-     * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
-     *                     candidate.
-     * @return Next focusable {@link ActivityStack}, {@code null} if not found.
-     */
-    ActivityStack getNextFocusableStackLocked(@NonNull ActivityStack currentFocus,
-            boolean ignoreCurrent) {
-        // First look for next focusable stack on the same display
-        final ActivityDisplay preferredDisplay = currentFocus.getDisplay();
-        final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
-                currentFocus, ignoreCurrent);
-        if (preferredFocusableStack != null) {
-            return preferredFocusableStack;
-        }
-        if (preferredDisplay.supportsSystemDecorations()) {
-            // Stop looking for focusable stack on other displays because the preferred display
-            // supports system decorations. Home activity would be launched on the same display if
-            // no focusable stack found.
-            return null;
-        }
-
-        // Now look through all displays
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            if (display == preferredDisplay) {
-                // We've already checked this one
-                continue;
-            }
-            final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
-                    ignoreCurrent);
-            if (nextFocusableStack != null) {
-                return nextFocusableStack;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Get next valid stack for launching provided activity in the system. This will search across
-     * displays and stacks in last-focused order for a focusable and visible stack, except those
-     * that are on a currently focused display.
-     *
-     * @param r The activity that is being launched.
-     * @param currentFocus The display that previously had focus and thus needs to be ignored when
-     *                     searching for the next candidate.
-     * @return Next valid {@link ActivityStack}, null if not found.
-     */
-    ActivityStack getNextValidLaunchStackLocked(@NonNull ActivityRecord r, int currentFocus) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            if (display.mDisplayId == currentFocus) {
-                continue;
-            }
-            final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r,
-                    null /* options */);
-            if (stack != null) {
-                return stack;
-            }
-        }
-        return null;
-    }
-
-    ActivityRecord getDefaultDisplayHomeActivity() {
-        return getDefaultDisplayHomeActivityForUser(mCurrentUser);
-    }
-
-    ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
-        getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
-        return null;
-    }
-
-    void resizeStackLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
-            Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
-            boolean deferResume) {
-
-        if (stack.inSplitScreenPrimaryWindowingMode()) {
-            resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
-                    preserveWindows, deferResume);
-            return;
-        }
-
-        final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack();
-        if (!allowResizeInDockedMode
-                && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
-            // If the docked stack exists, don't resize non-floating stacks independently of the
-            // size computed from the docked stack size (otherwise they will be out of sync)
-            return;
-        }
-
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
-        mWindowManager.deferSurfaceLayout();
-        try {
-            if (stack.affectedBySplitScreenResize()) {
-                if (bounds == null && stack.inSplitScreenWindowingMode()) {
-                    // null bounds = fullscreen windowing mode...at least for now.
-                    stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-                } else if (splitScreenActive) {
-                    // If we are in split-screen mode and this stack support split-screen, then
-                    // it should be split-screen secondary mode. i.e. adjacent to the docked stack.
-                    stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-                }
-            }
-            stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
-            if (!deferResume) {
-                stack.ensureVisibleActivitiesConfigurationLocked(
-                        stack.topRunningActivityLocked(), preserveWindows);
-            }
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
-    void deferUpdateRecentsHomeStackBounds() {
-        deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
-        deferUpdateBounds(ACTIVITY_TYPE_HOME);
-    }
-
-    void deferUpdateBounds(int activityType) {
-        final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
-        if (stack != null) {
-            stack.deferUpdateBounds();
-        }
-    }
-
-    void continueUpdateRecentsHomeStackBounds() {
-        continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
-        continueUpdateBounds(ACTIVITY_TYPE_HOME);
-    }
-
-    void continueUpdateBounds(int activityType) {
-        final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
-        if (stack != null) {
-            stack.continueUpdateBounds();
-        }
-    }
-
-    void notifyAppTransitionDone() {
-        continueUpdateRecentsHomeStackBounds();
-        for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
-            final int taskId = mResizingTasksDuringAnimation.valueAt(i);
-            final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY);
-            if (task != null) {
-                task.setTaskDockedResizing(false);
-            }
-        }
-        mResizingTasksDuringAnimation.clear();
-    }
-
-    /**
-     * TODO: This should just change the windowing mode and resize vs. actually moving task around.
-     * Can do that once we are no longer using static stack ids.
-     */
-    private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
-            int toDisplayId, boolean onTop) {
-
-        mWindowManager.deferSurfaceLayout();
-        try {
-            final int windowingMode = fromStack.getWindowingMode();
-            final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED;
-            final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId);
-
-            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                // Tell the display we are exiting split-screen mode.
-                toDisplay.onExitingSplitScreenMode();
-                // We are moving all tasks from the docked stack to the fullscreen stack,
-                // which is dismissing the docked stack, so resize all other stacks to
-                // fullscreen here already so we don't end up with resize trashing.
-                for (int i = toDisplay.getChildCount() - 1; i >= 0; --i) {
-                    final ActivityStack otherStack = toDisplay.getChildAt(i);
-                    if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
-                        continue;
-                    }
-                    otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
-                }
-
-                // Also disable docked stack resizing since we have manually adjusted the
-                // size of other stacks above and we don't want to trigger a docked stack
-                // resize when we remove task from it below and it is detached from the
-                // display because it no longer contains any tasks.
-                mAllowDockedStackResize = false;
-            }
-
-            // If we are moving from the pinned stack, then the animation takes care of updating
-            // the picture-in-picture mode.
-            final boolean schedulePictureInPictureModeChange = inPinnedWindowingMode;
-            final ArrayList<TaskRecord> tasks = fromStack.getAllTasks();
-
-            if (!tasks.isEmpty()) {
-                mTmpOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
-                final int size = tasks.size();
-                for (int i = 0; i < size; ++i) {
-                    final TaskRecord task = tasks.get(i);
-                    final ActivityStack toStack = toDisplay.getOrCreateStack(
-                                null, mTmpOptions, task, task.getActivityType(), onTop);
-
-                    if (onTop) {
-                        final boolean isTopTask = i == (size - 1);
-                        // Defer resume until all the tasks have been moved to the fullscreen stack
-                        task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
-                                isTopTask /* animate */, DEFER_RESUME,
-                                schedulePictureInPictureModeChange,
-                                "moveTasksToFullscreenStack - onTop");
-                        MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext,
-                                task.effectiveUid, task.realActivity.flattenToString());
-                    } else {
-                        // Position the tasks in the fullscreen stack in order at the bottom of the
-                        // stack. Also defer resume until all the tasks have been moved to the
-                        // fullscreen stack.
-                        task.reparent(toStack, ON_TOP,
-                                REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
-                                schedulePictureInPictureModeChange,
-                                "moveTasksToFullscreenStack - NOT_onTop");
-                    }
-                }
-            }
-
-            ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-            resumeFocusedStacksTopActivitiesLocked();
-        } finally {
-            mAllowDockedStackResize = true;
-            mWindowManager.continueSurfaceLayout();
-        }
-    }
-
-    void moveTasksToFullscreenStackLocked(ActivityStack fromStack, boolean onTop) {
-        moveTasksToFullscreenStackLocked(fromStack, DEFAULT_DISPLAY, onTop);
-    }
-
-    void moveTasksToFullscreenStackLocked(ActivityStack fromStack, int toDisplayId, boolean onTop) {
-        mWindowManager.inSurfaceTransaction(() ->
-                moveTasksToFullscreenStackInSurfaceTransaction(fromStack, toDisplayId, onTop));
-    }
-
-    void setSplitScreenResizing(boolean resizing) {
-        if (resizing == mDockedStackResizing) {
-            return;
-        }
-
-        mDockedStackResizing = resizing;
-        mWindowManager.setDockedStackResizing(resizing);
-
-        if (!resizing && mHasPendingDockedBounds) {
-            resizeDockedStackLocked(mPendingDockedBounds, mPendingTempDockedTaskBounds,
-                    mPendingTempDockedTaskInsetBounds, mPendingTempOtherTaskBounds,
-                    mPendingTempOtherTaskInsetBounds, PRESERVE_WINDOWS);
-
-            mHasPendingDockedBounds = false;
-            mPendingDockedBounds = null;
-            mPendingTempDockedTaskBounds = null;
-            mPendingTempDockedTaskInsetBounds = null;
-            mPendingTempOtherTaskBounds = null;
-            mPendingTempOtherTaskInsetBounds = null;
-        }
-    }
-
-    void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
-            Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
-            boolean preserveWindows) {
-        resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
-                tempOtherTaskBounds, tempOtherTaskInsetBounds, preserveWindows,
-                false /* deferResume */);
-    }
-
-    private void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
-            Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
-            boolean preserveWindows, boolean deferResume) {
-
-        if (!mAllowDockedStackResize) {
-            // Docked stack resize currently disabled.
-            return;
-        }
-
-        final ActivityStack stack = getDefaultDisplay().getSplitScreenPrimaryStack();
-        if (stack == null) {
-            Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
-            return;
-        }
-
-        if (mDockedStackResizing) {
-            mHasPendingDockedBounds = true;
-            mPendingDockedBounds = copyOrNull(dockedBounds);
-            mPendingTempDockedTaskBounds = copyOrNull(tempDockedTaskBounds);
-            mPendingTempDockedTaskInsetBounds = copyOrNull(tempDockedTaskInsetBounds);
-            mPendingTempOtherTaskBounds = copyOrNull(tempOtherTaskBounds);
-            mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds);
-        }
-
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
-        mWindowManager.deferSurfaceLayout();
-        try {
-            // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
-            mAllowDockedStackResize = false;
-            ActivityRecord r = stack.topRunningActivityLocked();
-            stack.resize(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds);
-
-            // TODO: Checking for isAttached might not be needed as if the user passes in null
-            // dockedBounds then they want the docked stack to be dismissed.
-            if (stack.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
-                    || (dockedBounds == null && !stack.isAttached())) {
-                // The dock stack either was dismissed or went fullscreen, which is kinda the same.
-                // In this case we make all other static stacks fullscreen and move all
-                // docked stack tasks to the fullscreen stack.
-                moveTasksToFullscreenStackLocked(stack, ON_TOP);
-
-                // stack shouldn't contain anymore activities, so nothing to resume.
-                r = null;
-            } else {
-                // Docked stacks occupy a dedicated region on screen so the size of all other
-                // static stacks need to be adjusted so they don't overlap with the docked stack.
-                // We get the bounds to use from window manager which has been adjusted for any
-                // screen controls and is also the same for all stacks.
-                final ActivityDisplay display = getDefaultDisplay();
-                final Rect otherTaskRect = new Rect();
-                for (int i = display.getChildCount() - 1; i >= 0; --i) {
-                    final ActivityStack current = display.getChildAt(i);
-                    if (current.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                        continue;
-                    }
-                    if (!current.affectedBySplitScreenResize()) {
-                        continue;
-                    }
-                    if (mDockedStackResizing && !current.isTopActivityVisible()) {
-                        // Non-visible stacks get resized once we're done with the resize
-                        // interaction.
-                        continue;
-                    }
-                    // Need to set windowing mode here before we try to get the dock bounds.
-                    current.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-                    current.getStackDockedModeBounds(
-                            tempOtherTaskBounds /* currentTempTaskBounds */,
-                            tempRect /* outStackBounds */,
-                            otherTaskRect /* outTempTaskBounds */, true /* ignoreVisibility */);
-
-                    resizeStackLocked(current, !tempRect.isEmpty() ? tempRect : null,
-                            !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
-                            tempOtherTaskInsetBounds, preserveWindows,
-                            true /* allowResizeInDockedMode */, deferResume);
-                }
-            }
-            if (!deferResume) {
-                stack.ensureVisibleActivitiesConfigurationLocked(r, preserveWindows);
-            }
-        } finally {
-            mAllowDockedStackResize = true;
-            mWindowManager.continueSurfaceLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
-    void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
-        // TODO(multi-display): Pinned stack display should be passed in.
-        final PinnedActivityStack stack = getDefaultDisplay().getPinnedStack();
-        if (stack == null) {
-            Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
-            return;
-        }
-
-        // It is possible for the bounds animation from the WM to call this but be delayed by
-        // another AM call that is holding the AMS lock. In such a case, the pinnedBounds may be
-        // incorrect if AMS.resizeStackWithBoundsFromWindowManager() is already called while waiting
-        // for the AMS lock to be freed. So check and make sure these bounds are still good.
-        final PinnedStackWindowController stackController = stack.getWindowContainerController();
-        if (stackController.pinnedStackResizeDisallowed()) {
-            return;
-        }
-
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack");
-        mWindowManager.deferSurfaceLayout();
-        try {
-            ActivityRecord r = stack.topRunningActivityLocked();
-            Rect insetBounds = null;
-            if (tempPinnedTaskBounds != null && stack.isAnimatingBoundsToFullscreen()) {
-                // Use 0,0 as the position for the inset rect because we are headed for fullscreen.
-                insetBounds = tempRect;
-                insetBounds.top = 0;
-                insetBounds.left = 0;
-                insetBounds.right = tempPinnedTaskBounds.width();
-                insetBounds.bottom = tempPinnedTaskBounds.height();
-            }
-            if (pinnedBounds != null && tempPinnedTaskBounds == null) {
-                // We have finished the animation into PiP, and are resizing the tasks to match the
-                // stack bounds, while layouts are deferred, update any task state as a part of
-                // transitioning it from fullscreen into a floating state.
-                stack.onPipAnimationEndResize();
-            }
-            stack.resize(pinnedBounds, tempPinnedTaskBounds, insetBounds);
-            stack.ensureVisibleActivitiesConfigurationLocked(r, false);
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
-    private void removeStackInSurfaceTransaction(ActivityStack stack) {
-        final ArrayList<TaskRecord> tasks = stack.getAllTasks();
-        if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
-            /**
-             * Workaround: Force-stop all the activities in the pinned stack before we reparent them
-             * to the fullscreen stack.  This is to guarantee that when we are removing a stack,
-             * that the client receives onStop() before it is reparented.  We do this by detaching
-             * the stack from the display so that it will be considered invisible when
-             * ensureActivitiesVisibleLocked() is called, and all of its activitys will be marked
-             * invisible as well and added to the stopping list.  After which we process the
-             * stopping list by handling the idle.
-             */
-            final PinnedActivityStack pinnedStack = (PinnedActivityStack) stack;
-            pinnedStack.mForceHidden = true;
-            pinnedStack.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-            pinnedStack.mForceHidden = false;
-            activityIdleInternalLocked(null, false /* fromTimeout */,
-                    true /* processPausingActivites */, null /* configuration */);
-
-            // Move all the tasks to the bottom of the fullscreen stack
-            moveTasksToFullscreenStackLocked(pinnedStack, !ON_TOP);
-        } else {
-            for (int i = tasks.size() - 1; i >= 0; i--) {
-                removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
-                        REMOVE_FROM_RECENTS, "remove-stack");
-            }
-        }
-    }
-
-    /**
-     * Removes the stack associated with the given {@param stack}. If the {@param stack} is the
-     * pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
-     * instead moved back onto the fullscreen stack.
-     */
-    void removeStack(ActivityStack stack) {
-        mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stack));
-    }
-
-    /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
-     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
-     */
-    void removeStacksInWindowingModes(int... windowingModes) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes);
-        }
-    }
-
-    void removeStacksWithActivityTypes(int... activityTypes) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes);
-        }
-    }
-
-    /**
-     * See {@link #removeTaskByIdLocked(int, boolean, boolean, boolean)}
-     */
-    boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
-            String reason) {
-        return removeTaskByIdLocked(taskId, killProcess, removeFromRecents, !PAUSE_IMMEDIATELY,
-                reason);
-    }
-
-    /**
-     * Removes the task with the specified task id.
-     *
-     * @param taskId Identifier of the task to be removed.
-     * @param killProcess Kill any process associated with the task if possible.
-     * @param removeFromRecents Whether to also remove the task from recents.
-     * @param pauseImmediately Pauses all task activities immediately without waiting for the
-     *                         pause-complete callback from the activity.
-     * @return Returns true if the given task was found and removed.
-     */
-    boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
-            boolean pauseImmediately, String reason) {
-        final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-        if (tr != null) {
-            tr.removeTaskActivitiesLocked(pauseImmediately, reason);
-            cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
-            mService.getLockTaskController().clearLockedTask(tr);
-            if (tr.isPersistable) {
-                mService.notifyTaskPersisterLocked(null, true);
-            }
-            return true;
-        }
-        Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
-        return false;
-    }
-
-    void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
-        if (removeFromRecents) {
-            mRecentTasks.remove(tr);
-        }
-        ComponentName component = tr.getBaseIntent().getComponent();
-        if (component == null) {
-            Slog.w(TAG, "No component for base intent of task: " + tr);
-            return;
-        }
-
-        // Find any running services associated with this app and stop if needed.
-        final Message msg = PooledLambda.obtainMessage(ActivityManagerInternal::cleanUpServices,
-                mService.mAmInternal, tr.userId, component, new Intent(tr.getBaseIntent()));
-        mService.mH.sendMessage(msg);
-
-        if (!killProcess) {
-            return;
-        }
-
-        // Determine if the process(es) for this task should be killed.
-        final String pkg = component.getPackageName();
-        ArrayList<Object> procsToKill = new ArrayList<>();
-        ArrayMap<String, SparseArray<WindowProcessController>> pmap =
-                mService.mProcessNames.getMap();
-        for (int i = 0; i < pmap.size(); i++) {
-
-            SparseArray<WindowProcessController> uids = pmap.valueAt(i);
-            for (int j = 0; j < uids.size(); j++) {
-                WindowProcessController proc = uids.valueAt(j);
-                if (proc.mUserId != tr.userId) {
-                    // Don't kill process for a different user.
-                    continue;
-                }
-                if (proc == mService.mHomeProcess) {
-                    // Don't kill the home process along with tasks from the same package.
-                    continue;
-                }
-                if (!proc.mPkgList.contains(pkg)) {
-                    // Don't kill process that is not associated with this task.
-                    continue;
-                }
-
-                if (!proc.shouldKillProcessForRemovedTask(tr)) {
-                    // Don't kill process(es) that has an activity in a different task that is also
-                    // in recents, or has an activity not stopped.
-                    return;
-                }
-
-                if (proc.hasForegroundServices()) {
-                    // Don't kill process(es) with foreground service.
-                    return;
-                }
-
-                // Add process to kill list.
-                procsToKill.add(proc);
-            }
-        }
-
-        // Kill the running processes. Post on handle since we don't want to hold the service lock
-        // while calling into AM.
-        final Message m = PooledLambda.obtainMessage(
-                ActivityManagerInternal::killProcessesForRemovedTask, mService.mAmInternal,
-                procsToKill);
-        mService.mH.sendMessage(m);
-    }
-
-    /**
-     * Called to restore the state of the task into the stack that it's supposed to go into.
-     *
-     * @param task The recent task to be restored.
-     * @param aOptions The activity options to use for restoration.
-     * @param onTop If the stack for the task should be the topmost on the display.
-     * @return true if the task has been restored successfully.
-     */
-    boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions, boolean onTop) {
-        final ActivityStack stack = getLaunchStack(null, aOptions, task, onTop);
-        final ActivityStack currentStack = task.getStack();
-        if (currentStack != null) {
-            // Task has already been restored once. See if we need to do anything more
-            if (currentStack == stack) {
-                // Nothing else to do since it is already restored in the right stack.
-                return true;
-            }
-            // Remove current stack association, so we can re-associate the task with the
-            // right stack below.
-            currentStack.removeTask(task, "restoreRecentTaskLocked", REMOVE_TASK_MODE_MOVING);
-        }
-
-        stack.addTask(task, onTop, "restoreRecentTask");
-        // TODO: move call for creation here and other place into Stack.addTask()
-        task.createWindowContainer(onTop, true /* showForAllUsers */);
-        if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
-                "Added restored task=" + task + " to stack=" + stack);
-        final ArrayList<ActivityRecord> activities = task.mActivities;
-        for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-            activities.get(activityNdx).createWindowContainer();
-        }
-        return true;
-    }
-
-    @Override
-    public void onRecentTaskAdded(TaskRecord task) {
-        task.touchActiveTime();
-    }
-
-    @Override
-    public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
-        if (wasTrimmed) {
-            // Task was trimmed from the recent tasks list -- remove the active task record as well
-            // since the user won't really be able to go back to it
-            removeTaskByIdLocked(task.taskId, killProcess, false /* removeFromRecents */,
-                    !PAUSE_IMMEDIATELY, "recent-task-trimmed");
-        }
-        task.removedFromRecents();
-    }
-
-    /**
-     * Move stack with all its existing content to specified display.
-     * @param stackId Id of stack to move.
-     * @param displayId Id of display to move stack to.
-     * @param onTop Indicates whether container should be place on top or on bottom.
-     */
-    void moveStackToDisplayLocked(int stackId, int displayId, boolean onTop) {
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (activityDisplay == null) {
-            throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown displayId="
-                    + displayId);
-        }
-        final ActivityStack stack = getStack(stackId);
-        if (stack == null) {
-            throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown stackId="
-                    + stackId);
-        }
-
-        final ActivityDisplay currentDisplay = stack.getDisplay();
-        if (currentDisplay == null) {
-            throw new IllegalStateException("moveStackToDisplayLocked: Stack with stack=" + stack
-                    + " is not attached to any display.");
-        }
-
-        if (currentDisplay.mDisplayId == displayId) {
-            throw new IllegalArgumentException("Trying to move stack=" + stack
-                    + " to its current displayId=" + displayId);
-        }
-
-        stack.reparent(activityDisplay, onTop);
-        // TODO(multi-display): resize stacks properly if moved from split-screen.
-    }
-
-    /**
-     * Returns the reparent target stack, creating the stack if necessary.  This call also enforces
-     * the various checks on tasks that are going to be reparented from one stack to another.
-     */
-    // TODO: Look into changing users to this method to ActivityDisplay.resolveWindowingMode()
-    ActivityStack getReparentTargetStack(TaskRecord task, ActivityStack stack, boolean toTop) {
-        final ActivityStack prevStack = task.getStack();
-        final int stackId = stack.mStackId;
-        final boolean inMultiWindowMode = stack.inMultiWindowMode();
-
-        // Check that we aren't reparenting to the same stack that the task is already in
-        if (prevStack != null && prevStack.mStackId == stackId) {
-            Slog.w(TAG, "Can not reparent to same stack, task=" + task
-                    + " already in stackId=" + stackId);
-            return prevStack;
-        }
-
-        // Ensure that we aren't trying to move into a multi-window stack without multi-window
-        // support
-        if (inMultiWindowMode && !mService.mSupportsMultiWindow) {
-            throw new IllegalArgumentException("Device doesn't support multi-window, can not"
-                    + " reparent task=" + task + " to stack=" + stack);
-        }
-
-        // Ensure that we're not moving a task to a dynamic stack if device doesn't support
-        // multi-display.
-        if (stack.mDisplayId != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
-            throw new IllegalArgumentException("Device doesn't support multi-display, can not"
-                    + " reparent task=" + task + " to stackId=" + stackId);
-        }
-
-        // Ensure that we aren't trying to move into a freeform stack without freeform support
-        if (stack.getWindowingMode() == WINDOWING_MODE_FREEFORM
-                && !mService.mSupportsFreeformWindowManagement) {
-            throw new IllegalArgumentException("Device doesn't support freeform, can not reparent"
-                    + " task=" + task);
-        }
-
-        // Leave the task in its current stack or a fullscreen stack if it isn't resizeable and the
-        // preferred stack is in multi-window mode.
-        if (inMultiWindowMode && !task.isResizeable()) {
-            Slog.w(TAG, "Can not move unresizeable task=" + task + " to multi-window stack=" + stack
-                    + " Moving to a fullscreen stack instead.");
-            if (prevStack != null) {
-                return prevStack;
-            }
-            stack = stack.getDisplay().createStack(
-                    WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop);
-        }
-        return stack;
-    }
-
-    boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) {
-        final ActivityStack stack = getStack(stackId);
-        if (stack == null) {
-            throw new IllegalArgumentException(
-                    "moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
-        }
-
-        final ActivityRecord r = stack.topRunningActivityLocked();
-        if (r == null) {
-            Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: No top running activity"
-                    + " in stack=" + stack);
-            return false;
-        }
-
-        if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
-            Slog.w(TAG,
-                    "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
-                            + " r=" + r);
-            return false;
-        }
-
-        moveActivityToPinnedStackLocked(r, null /* sourceBounds */, 0f /* aspectRatio */,
-                "moveTopActivityToPinnedStack");
-        return true;
-    }
-
-    void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
-            String reason) {
-
-        mWindowManager.deferSurfaceLayout();
-
-        final ActivityDisplay display = r.getStack().getDisplay();
-        PinnedActivityStack stack = display.getPinnedStack();
-
-        // This will clear the pinned stack by moving an existing task to the full screen stack,
-        // ensuring only one task is present.
-        if (stack != null) {
-            moveTasksToFullscreenStackLocked(stack, !ON_TOP);
-        }
-
-        // Need to make sure the pinned stack exist so we can resize it below...
-        stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
-
-        // Calculate the target bounds here before the task is reparented back into pinned windowing
-        // mode (which will reset the saved bounds)
-        final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
-
-        try {
-            final TaskRecord task = r.getTask();
-            // Resize the pinned stack to match the current size of the task the activity we are
-            // going to be moving is currently contained in. We do this to have the right starting
-            // animation bounds for the pinned stack to the desired bounds the caller wants.
-            resizeStackLocked(stack, task.getOverrideBounds(), null /* tempTaskBounds */,
-                    null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
-                    true /* allowResizeInDockedMode */, !DEFER_RESUME);
-
-            if (task.mActivities.size() == 1) {
-                // Defer resume until below, and do not schedule PiP changes until we animate below
-                task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
-                        false /* schedulePictureInPictureModeChange */, reason);
-            } else {
-                // There are multiple activities in the task and moving the top activity should
-                // reveal/leave the other activities in their original task.
-
-                // Currently, we don't support reparenting activities across tasks in two different
-                // stacks, so instead, just create a new task in the same stack, reparent the
-                // activity into that task, and then reparent the whole task to the new stack. This
-                // ensures that all the necessary work to migrate states in the old and new stacks
-                // is also done.
-                final TaskRecord newTask = task.getStack().createTaskRecord(
-                        getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true);
-                r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
-
-                // Defer resume until below, and do not schedule PiP changes until we animate below
-                newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
-                        DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
-            }
-
-            // Reset the state that indicates it can enter PiP while pausing after we've moved it
-            // to the pinned stack
-            r.supportsEnterPipOnTaskSwitch = false;
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-        }
-
-        stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
-                true /* fromFullscreen */);
-
-        // Update the visibility of all activities after the they have been reparented to the new
-        // stack.  This MUST run after the animation above is scheduled to ensure that the windows
-        // drawn signal is scheduled after the bounds animation start call on the bounds animator
-        // thread.
-        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        resumeFocusedStacksTopActivitiesLocked();
-
-        mService.getTaskChangeNotificationController().notifyActivityPinned(r);
-    }
-
-    ActivityRecord findTaskLocked(ActivityRecord r, int preferredDisplayId) {
-        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
-        mTmpFindTaskResult.clear();
-
-        // Looking up task on preferred display first
-        final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId);
-        if (preferredDisplay != null) {
-            preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult);
-            if (mTmpFindTaskResult.mIdealMatch) {
-                return mTmpFindTaskResult.mRecord;
-            }
-        }
-
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            if (display.mDisplayId == preferredDisplayId) {
-                continue;
-            }
-
-            display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult);
-            if (mTmpFindTaskResult.mIdealMatch) {
-                return mTmpFindTaskResult.mRecord;
-            }
-        }
-
-        if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
-        return mTmpFindTaskResult.mRecord;
-    }
-
-    ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
-            boolean compareIntentFilters) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord ar = stack.findActivityLocked(
-                        intent, info, compareIntentFilters);
-                if (ar != null) {
-                    return ar;
-                }
-            }
-        }
-        return null;
-    }
-
-    boolean hasAwakeDisplay() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            if (!display.shouldSleep()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    void goingToSleepLocked() {
-        scheduleSleepTimeout();
-        if (!mGoingToSleep.isHeld()) {
-            mGoingToSleep.acquire();
-            if (mLaunchingActivity.isHeld()) {
-                if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
-                    throw new IllegalStateException("Calling must be system uid");
-                }
-                mLaunchingActivity.release();
-                mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
-            }
-        }
-
-        applySleepTokensLocked(false /* applyToStacks */);
-
-        checkReadyForSleepLocked(true /* allowDelay */);
-    }
-
-    void prepareForShutdownLocked() {
-        for (int i = 0; i < mActivityDisplays.size(); i++) {
-            createSleepTokenLocked("shutdown", mActivityDisplays.get(i).mDisplayId);
-        }
-    }
-
-    boolean shutdownLocked(int timeout) {
-        goingToSleepLocked();
-
-        boolean timedout = false;
-        final long endTime = System.currentTimeMillis() + timeout;
-        while (true) {
-            if (!putStacksToSleepLocked(true /* allowDelay */, true /* shuttingDown */)) {
-                long timeRemaining = endTime - System.currentTimeMillis();
-                if (timeRemaining > 0) {
-                    try {
-                        mService.mGlobalLock.wait(timeRemaining);
-                    } catch (InterruptedException e) {
-                    }
-                } else {
-                    Slog.w(TAG, "Activity manager shutdown timed out");
-                    timedout = true;
-                    break;
-                }
-            } else {
-                break;
-            }
-        }
-
-        // Force checkReadyForSleep to complete.
-        checkReadyForSleepLocked(false /* allowDelay */);
-
-        return timedout;
-    }
-
-    void comeOutOfSleepIfNeededLocked() {
-        removeSleepTimeouts();
-        if (mGoingToSleep.isHeld()) {
-            mGoingToSleep.release();
-        }
-    }
-
-    void applySleepTokensLocked(boolean applyToStacks) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            // Set the sleeping state of the display.
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final boolean displayShouldSleep = display.shouldSleep();
-            if (displayShouldSleep == display.isSleeping()) {
-                continue;
-            }
-            display.setIsSleeping(displayShouldSleep);
-
-            if (!applyToStacks) {
-                continue;
-            }
-
-            // Set the sleeping state of the stacks on the display.
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (displayShouldSleep) {
-                    stack.goToSleepIfPossible(false /* shuttingDown */);
-                } else {
-                    stack.awakeFromSleepingLocked();
-                    if (stack.isFocusedStackOnDisplay() && !getKeyguardController()
-                            .isKeyguardOrAodShowing(display.mDisplayId)) {
-                        // If the keyguard is unlocked - resume immediately.
-                        // It is possible that the display will not be awake at the time we
-                        // process the keyguard going away, which can happen before the sleep token
-                        // is released. As a result, it is important we resume the activity here.
-                        resumeFocusedStacksTopActivitiesLocked();
-                    }
-                }
-            }
-
-            if (displayShouldSleep || mGoingToSleepActivities.isEmpty()) {
-                continue;
-            }
-            // The display is awake now, so clean up the going to sleep list.
-            for (Iterator<ActivityRecord> it = mGoingToSleepActivities.iterator(); it.hasNext(); ) {
-                final ActivityRecord r = it.next();
-                if (r.getDisplayId() == display.mDisplayId) {
-                    it.remove();
-                }
-            }
-        }
-    }
-
-    void activitySleptLocked(ActivityRecord r) {
-        mGoingToSleepActivities.remove(r);
-        final ActivityStack s = r.getStack();
-        if (s != null) {
-            s.checkReadyForSleep();
-        } else {
-            checkReadyForSleepLocked(true);
-        }
-    }
-
-    void checkReadyForSleepLocked(boolean allowDelay) {
-        if (!mService.isSleepingOrShuttingDownLocked()) {
-            // Do not care.
-            return;
-        }
-
-        if (!putStacksToSleepLocked(allowDelay, false /* shuttingDown */)) {
-            return;
-        }
-
-        // Send launch end powerhint before going sleep
-        sendPowerHintForLaunchEndIfNeeded();
-
-        removeSleepTimeouts();
-
-        if (mGoingToSleep.isHeld()) {
-            mGoingToSleep.release();
-        }
-        if (mService.mShuttingDown) {
-            mService.mGlobalLock.notifyAll();
-        }
-    }
-
-    // Tries to put all activity stacks to sleep. Returns true if all stacks were
-    // successfully put to sleep.
-    private boolean putStacksToSleepLocked(boolean allowDelay, boolean shuttingDown) {
-        boolean allSleep = true;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (allowDelay) {
-                    allSleep &= stack.goToSleepIfPossible(shuttingDown);
-                } else {
-                    stack.goToSleep();
-                }
-            }
-        }
-        return allSleep;
-    }
-
-    boolean reportResumedActivityLocked(ActivityRecord r) {
-        // A resumed activity cannot be stopping. remove from list
-        mStoppingActivities.remove(r);
-
-        final ActivityStack stack = r.getStack();
-        if (isTopDisplayFocusedStack(stack)) {
-            mService.updateUsageStats(r, true);
-        }
-        if (stack.getDisplay().allResumedActivitiesComplete()) {
-            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-            mWindowManager.executeAppTransition();
-            return true;
-        }
-        return false;
-    }
-
-    void handleAppCrashLocked(WindowProcessController app) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.handleAppCrashLocked(app);
-            }
-        }
-    }
-
-    // Called when WindowManager has finished animating the launchingBehind activity to the back.
-    private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
-        final TaskRecord task = r.getTask();
-        final ActivityStack stack = task.getStack();
-
-        r.mLaunchTaskBehind = false;
-        mRecentTasks.add(task);
-        mService.getTaskChangeNotificationController().notifyTaskStackChanged();
-        r.setVisibility(false);
-
-        // When launching tasks behind, update the last active time of the top task after the new
-        // task has been shown briefly
-        final ActivityRecord top = stack.getTopActivity();
-        if (top != null) {
-            top.getTask().touchActiveTime();
-        }
-    }
-
-    void scheduleLaunchTaskBehindComplete(IBinder token) {
-        mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
-    }
-
-    /**
-     * Make sure that all activities that need to be visible in the system actually are and update
-     * their configuration.
-     */
-    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
-            boolean preserveWindows) {
-        ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
-                true /* notifyClients */);
-    }
-
-    /**
-     * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
-     */
-    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients) {
-        getKeyguardController().beginActivityVisibilityUpdate();
-        try {
-            // First the front stacks. In case any are not fullscreen and are in front of home.
-            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getChildAt(stackNdx);
-                    stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
-                            notifyClients);
-                }
-            }
-        } finally {
-            getKeyguardController().endActivityVisibilityUpdate();
-        }
-    }
-
-    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.addStartingWindowsForVisibleActivities(taskSwitch);
-            }
-        }
-    }
-
-    void invalidateTaskLayers() {
-        mTaskLayersChanged = true;
-    }
-
-    void rankTaskLayersIfNeeded() {
-        if (!mTaskLayersChanged) {
-            return;
-        }
-        mTaskLayersChanged = false;
-        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            int baseLayer = 0;
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                baseLayer += stack.rankTaskLayers(baseLayer);
-            }
-        }
-    }
-
-    void clearOtherAppTimeTrackers(AppTimeTracker except) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.clearOtherAppTimeTrackers(except);
-            }
-        }
-    }
-
-    void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.scheduleDestroyActivities(app, reason);
-            }
-        }
-    }
-
-    void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
-        // Tasks is non-null only if two or more tasks are found.
-        ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
-        if (tasks == null) {
-            if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
-            return;
-        }
-        // If we have activities in multiple tasks that are in a position to be destroyed,
-        // let's iterate through the tasks and release the oldest one.
-        final int numDisplays = mActivityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final int stackCount = display.getChildCount();
-            // Step through all stacks starting from behind, to hit the oldest things first.
-            for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                // Try to release activities in this stack; if we manage to, we are done.
-                if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
-                    return;
-                }
-            }
-        }
-    }
-
-    boolean switchUserLocked(int userId, UserState uss) {
-        final int focusStackId = getTopDisplayFocusedStack().getStackId();
-        // We dismiss the docked stack whenever we switch users.
-        final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
-        if (dockedStack != null) {
-            moveTasksToFullscreenStackLocked(dockedStack, dockedStack.isFocusedStackOnDisplay());
-        }
-        // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
-        // also cause all tasks to be moved to the fullscreen stack at a position that is
-        // appropriate.
-        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
-
-        mUserStackInFront.put(mCurrentUser, focusStackId);
-        final int restoreStackId =
-                mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
-        mCurrentUser = userId;
-
-        mStartingUsers.add(uss);
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.switchUserLocked(userId);
-                TaskRecord task = stack.topTask();
-                if (task != null) {
-                    stack.positionChildWindowContainerAtTop(task);
-                }
-            }
-        }
-
-        ActivityStack stack = getStack(restoreStackId);
-        if (stack == null) {
-            stack = getDefaultDisplay().getHomeStack();
-        }
-        final boolean homeInFront = stack.isActivityTypeHome();
-        if (stack.isOnHomeDisplay()) {
-            stack.moveToFront("switchUserOnHomeDisplay");
-        } else {
-            // Stack was moved to another display while user was swapped out.
-            resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
-        }
-        return homeInFront;
-    }
-
-    /** Checks whether the userid is a profile of the current user. */
-    boolean isCurrentProfileLocked(int userId) {
-        if (userId == mCurrentUser) return true;
-        return mService.mAmInternal.isCurrentProfile(userId);
-    }
-
-    /**
-     * Returns whether a stopping activity is present that should be stopped after visible, rather
-     * than idle.
-     * @return {@code true} if such activity is present. {@code false} otherwise.
-     */
-    boolean isStoppingNoHistoryActivity() {
-        // Activities that are marked as nohistory should be stopped immediately after the resumed
-        // activity has become visible.
-        for (ActivityRecord record : mStoppingActivities) {
-            if (record.isNoHistory()) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    final ArrayList<ActivityRecord> processStoppingActivitiesLocked(ActivityRecord idleActivity,
-            boolean remove, boolean processPausingActivities) {
-        ArrayList<ActivityRecord> stops = null;
-
-        final boolean nowVisible = allResumedActivitiesVisible();
-        for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-            ActivityRecord s = mStoppingActivities.get(activityNdx);
-            boolean waitingVisible = mActivitiesWaitingForVisibleActivity.contains(s);
-            if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
-                    + " waitingVisible=" + waitingVisible + " finishing=" + s.finishing);
-            if (waitingVisible && nowVisible) {
-                mActivitiesWaitingForVisibleActivity.remove(s);
-                waitingVisible = false;
-                if (s.finishing) {
-                    // If this activity is finishing, it is sitting on top of
-                    // everyone else but we now know it is no longer needed...
-                    // so get rid of it.  Otherwise, we need to go through the
-                    // normal flow and hide it once we determine that it is
-                    // hidden by the activities in front of it.
-                    if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s);
-                    s.setVisibility(false);
-                }
-            }
-            if (remove) {
-                final ActivityStack stack = s.getStack();
-                final boolean shouldSleepOrShutDown = stack != null
-                        ? stack.shouldSleepOrShutDownActivities()
-                        : mService.isSleepingOrShuttingDownLocked();
-                if (!waitingVisible || shouldSleepOrShutDown) {
-                    if (!processPausingActivities && s.isState(PAUSING)) {
-                        // Defer processing pausing activities in this iteration and reschedule
-                        // a delayed idle to reprocess it again
-                        removeTimeoutsForActivityLocked(idleActivity);
-                        scheduleIdleTimeoutLocked(idleActivity);
-                        continue;
-                    }
-
-                    if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
-                    if (stops == null) {
-                        stops = new ArrayList<>();
-                    }
-                    stops.add(s);
-
-                    // Make sure to remove it in all cases in case we entered this block with
-                    // shouldSleepOrShutDown
-                    mActivitiesWaitingForVisibleActivity.remove(s);
-                    mStoppingActivities.remove(activityNdx);
-                }
-            }
-        }
-
-        return stops;
-    }
-
-    void validateTopActivitiesLocked() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord r = stack.topRunningActivityLocked();
-                final ActivityState state = r == null ? DESTROYED : r.getState();
-                if (isTopDisplayFocusedStack(stack)) {
-                    if (r == null) Slog.e(TAG,
-                            "validateTop...: null top activity, stack=" + stack);
-                    else {
-                        final ActivityRecord pausing = stack.mPausingActivity;
-                        if (pausing != null && pausing == r) Slog.e(TAG,
-                                "validateTop...: top stack has pausing activity r=" + r
-                                + " state=" + state);
-                        if (state != INITIALIZING && state != RESUMED) Slog.e(TAG,
-                                "validateTop...: activity in front not resumed r=" + r
-                                + " state=" + state);
-                    }
-                } else {
-                    final ActivityRecord resumed = stack.getResumedActivity();
-                    if (resumed != null && resumed == r) Slog.e(TAG,
-                            "validateTop...: back stack has resumed activity r=" + r
-                            + " state=" + state);
-                    if (r != null && (state == INITIALIZING || state == RESUMED)) Slog.e(TAG,
-                            "validateTop...: activity in back resumed r=" + r + " state=" + state);
-                }
-            }
-        }
-    }
-
-    public void dumpDisplays(PrintWriter pw) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            pw.print("[id:" + display.mDisplayId + " stacks:");
-            display.dumpStacks(pw);
-            pw.print("]");
-        }
-    }
-
-    public void dump(PrintWriter pw, String prefix) {
-        pw.println();
-        pw.println("ActivityStackSupervisor state:");
-        pw.print(prefix);
-        pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
-        pw.print(prefix);
-        pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
-        pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            display.dump(pw, prefix);
-        }
-        if (!mWaitingForActivityVisible.isEmpty()) {
-            pw.print(prefix); pw.println("mWaitingForActivityVisible=");
-            for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) {
-                pw.print(prefix); pw.print(prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
-            }
-        }
-        pw.print(prefix); pw.print("isHomeRecentsComponent=");
-        pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
-
-        getKeyguardController().dump(pw, prefix);
-        mService.getLockTaskController().dump(pw, prefix);
-    }
-
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
-        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
-            final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
-            activityDisplay.writeToProto(proto, DISPLAYS);
-        }
-        getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
-        // TODO(b/111541062): Update tests to look for resumed activities on all displays
-        final ActivityStack focusedStack = getTopDisplayFocusedStack();
-        if (focusedStack != null) {
-            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
-            final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
-            if (focusedActivity != null) {
-                focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
-            }
-        } else {
-            proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
-        }
-        proto.write(IS_HOME_RECENTS_COMPONENT,
-                mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
-        mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES);
-        proto.end(token);
-    }
-
-    /**
-     * Dump all connected displays' configurations.
-     * @param prefix Prefix to apply to each line of the dump.
-     */
-    void dumpDisplayConfigs(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.println("Display override configurations:");
-        final int displayCount = mActivityDisplays.size();
-        for (int i = 0; i < displayCount; i++) {
-            final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
-            pw.print(prefix); pw.print("  "); pw.print(activityDisplay.mDisplayId); pw.print(": ");
-                    pw.println(activityDisplay.getOverrideConfiguration());
-        }
-    }
-
-    /**
-     * Dumps the activities matching the given {@param name} in the either the focused stack
-     * or all visible stacks if {@param dumpVisibleStacks} is true.
-     */
-    ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacksOnly,
-            boolean dumpFocusedStackOnly) {
-        if (dumpFocusedStackOnly) {
-            return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
-        } else {
-            ArrayList<ActivityRecord> activities = new ArrayList<>();
-            int numDisplays = mActivityDisplays.size();
-            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getChildAt(stackNdx);
-                    if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
-                        activities.addAll(stack.getDumpActivitiesLocked(name));
-                    }
-                }
-            }
-            return activities;
-        }
-    }
-
-    static boolean printThisActivity(PrintWriter pw, ActivityRecord activity, String dumpPackage,
-            boolean needSep, String prefix) {
-        if (activity != null) {
-            if (dumpPackage == null || dumpPackage.equals(activity.packageName)) {
-                if (needSep) {
-                    pw.println();
-                }
-                pw.print(prefix);
-                pw.println(activity);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
-            boolean dumpClient, String dumpPackage) {
-        boolean printed = false;
-        boolean needSep = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
-            pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
-                    pw.println(" (activities from top to bottom):");
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                pw.println();
-                pw.println("  Stack #" + stack.mStackId
-                        + ": type=" + activityTypeToString(stack.getActivityType())
-                        + " mode=" + windowingModeToString(stack.getWindowingMode()));
-                pw.println("  isSleeping=" + stack.shouldSleepActivities());
-                pw.println("  mBounds=" + stack.getOverrideBounds());
-
-                printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
-                        needSep);
-
-                printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, "    ", "Run", false,
-                        !dumpAll, false, dumpPackage, true,
-                        "    Running activities (most recent first):", null);
-
-                needSep = printed;
-                boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
-                        "    mPausingActivity: ");
-                if (pr) {
-                    printed = true;
-                    needSep = false;
-                }
-                pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep,
-                        "    mResumedActivity: ");
-                if (pr) {
-                    printed = true;
-                    needSep = false;
-                }
-                if (dumpAll) {
-                    pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
-                            "    mLastPausedActivity: ");
-                    if (pr) {
-                        printed = true;
-                        needSep = true;
-                    }
-                    printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
-                            needSep, "    mLastNoHistoryActivity: ");
-                }
-                needSep = printed;
-            }
-            printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
-                    " ResumedActivity:");
-        }
-
-        printed |= dumpHistoryList(fd, pw, mFinishingActivities, "  ", "Fin", false, !dumpAll,
-                false, dumpPackage, true, "  Activities waiting to finish:", null);
-        printed |= dumpHistoryList(fd, pw, mStoppingActivities, "  ", "Stop", false, !dumpAll,
-                false, dumpPackage, true, "  Activities waiting to stop:", null);
-        printed |= dumpHistoryList(fd, pw, mActivitiesWaitingForVisibleActivity, "  ", "Wait",
-                false, !dumpAll, false, dumpPackage, true,
-                "  Activities waiting for another to become visible:", null);
-        printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, "  ", "Sleep", false, !dumpAll,
-                false, dumpPackage, true, "  Activities waiting to sleep:", null);
-
-        return printed;
-    }
-
-    static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
-            String prefix, String label, boolean complete, boolean brief, boolean client,
-            String dumpPackage, boolean needNL, String header, TaskRecord lastTask) {
-        String innerPrefix = null;
-        String[] args = null;
-        boolean printed = false;
-        for (int i=list.size()-1; i>=0; i--) {
-            final ActivityRecord r = list.get(i);
-            if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
-                continue;
-            }
-            if (innerPrefix == null) {
-                innerPrefix = prefix + "      ";
-                args = new String[0];
-            }
-            printed = true;
-            final boolean full = !brief && (complete || !r.isInHistory());
-            if (needNL) {
-                pw.println("");
-                needNL = false;
-            }
-            if (header != null) {
-                pw.println(header);
-                header = null;
-            }
-            if (lastTask != r.getTask()) {
-                lastTask = r.getTask();
-                pw.print(prefix);
-                pw.print(full ? "* " : "  ");
-                pw.println(lastTask);
-                if (full) {
-                    lastTask.dump(pw, prefix + "  ");
-                } else if (complete) {
-                    // Complete + brief == give a summary.  Isn't that obvious?!?
-                    if (lastTask.intent != null) {
-                        pw.print(prefix); pw.print("  ");
-                                pw.println(lastTask.intent.toInsecureStringWithClip());
-                    }
-                }
-            }
-            pw.print(prefix); pw.print(full ? "  * " : "    "); pw.print(label);
-            pw.print(" #"); pw.print(i); pw.print(": ");
-            pw.println(r);
-            if (full) {
-                r.dump(pw, innerPrefix);
-            } else if (complete) {
-                // Complete + brief == give a summary.  Isn't that obvious?!?
-                pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
-                if (r.app != null) {
-                    pw.print(innerPrefix); pw.println(r.app);
-                }
-            }
-            if (client && r.attachedToProcess()) {
-                // flush anything that is already in the PrintWriter since the thread is going
-                // to write to the file descriptor directly
-                pw.flush();
-                try {
-                    TransferPipe tp = new TransferPipe();
-                    try {
-                        r.app.getThread().dumpActivity(
-                                tp.getWriteFd(), r.appToken, innerPrefix, args);
-                        // Short timeout, since blocking here can deadlock with the application.
-                        tp.go(fd, 2000);
-                    } finally {
-                        tp.kill();
-                    }
-                } catch (IOException e) {
-                    pw.println(innerPrefix + "Failure while dumping the activity: " + e);
-                } catch (RemoteException e) {
-                    pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
-                }
-                needNL = true;
-            }
-        }
-        return printed;
-    }
-
-    void scheduleIdleTimeoutLocked(ActivityRecord next) {
-        if (DEBUG_IDLE) Slog.d(TAG_IDLE,
-                "scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
-        Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
-        mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
-    }
-
-    final void scheduleIdleLocked() {
-        mHandler.sendEmptyMessage(IDLE_NOW_MSG);
-    }
-
-    void removeTimeoutsForActivityLocked(ActivityRecord r) {
-        if (DEBUG_IDLE) Slog.d(TAG_IDLE, "removeTimeoutsForActivity: Callers="
-                + Debug.getCallers(4));
-        mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
-    }
-
-    final void scheduleResumeTopActivities() {
-        if (!mHandler.hasMessages(RESUME_TOP_ACTIVITY_MSG)) {
-            mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
-        }
-    }
-
-    void removeSleepTimeouts() {
-        mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
-    }
-
-    final void scheduleSleepTimeout() {
-        removeSleepTimeouts();
-        mHandler.sendEmptyMessageDelayed(SLEEP_TIMEOUT_MSG, SLEEP_TIMEOUT);
-    }
-
-    @Override
-    public void onDisplayAdded(int displayId) {
-        if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
-        mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_ADDED, displayId, 0));
-    }
-
-    @Override
-    public void onDisplayRemoved(int displayId) {
-        if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
-        mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_REMOVED, displayId, 0));
-    }
-
-    @Override
-    public void onDisplayChanged(int displayId) {
-        if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
-        mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_CHANGED, displayId, 0));
-    }
-
-    private void handleDisplayAdded(int displayId) {
-        synchronized (mService.mGlobalLock) {
-            getActivityDisplayOrCreateLocked(displayId);
-            mService.startHomeActivityLocked(mCurrentUser, "displayAdded", displayId);
-        }
-    }
-
-    /** Check if display with specified id is added to the list. */
-    boolean isDisplayAdded(int displayId) {
-        return getActivityDisplayOrCreateLocked(displayId) != null;
-    }
-
-    // TODO: Look into consolidating with getActivityDisplayOrCreateLocked()
-    ActivityDisplay getActivityDisplay(int displayId) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
-            if (activityDisplay.mDisplayId == displayId) {
-                return activityDisplay;
-            }
-        }
-        return null;
-    }
-
-    // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
-    ActivityDisplay getDefaultDisplay() {
-        return mDefaultDisplay;
-    }
-
-    /**
-     * Get an existing instance of {@link ActivityDisplay} or create new if there is a
-     * corresponding record in display manager.
-     */
-    // TODO: Look into consolidating with getActivityDisplay()
-    ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
-        ActivityDisplay activityDisplay = getActivityDisplay(displayId);
-        if (activityDisplay != null) {
-            return activityDisplay;
-        }
-        if (mDisplayManager == null) {
-            // The system isn't fully initialized yet.
-            return null;
-        }
-        final Display display = mDisplayManager.getDisplay(displayId);
-        if (display == null) {
-            // The display is not registered in DisplayManager.
-            return null;
-        }
-        // The display hasn't been added to ActivityManager yet, create a new record now.
-        activityDisplay = new ActivityDisplay(this, display);
-        addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
-        mWindowManager.onDisplayAdded(displayId);
-        return activityDisplay;
-    }
-
-    @VisibleForTesting
-    void addChild(ActivityDisplay activityDisplay, int position) {
-        positionChildAt(activityDisplay, position);
-        mWindowContainerController.positionChildAt(
-                activityDisplay.getWindowContainerController(), position);
-    }
-
-    void removeChild(ActivityDisplay activityDisplay) {
-        // The caller must tell the controller of {@link ActivityDisplay} to release its container
-        // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}).
-        mActivityDisplays.remove(activityDisplay);
-    }
-
-    private void calculateDefaultMinimalSizeOfResizeableTasks() {
-        final Resources res = mService.mContext.getResources();
-        final float minimalSize = res.getDimension(
-                com.android.internal.R.dimen.default_minimal_size_resizable_task);
-        final DisplayMetrics dm = res.getDisplayMetrics();
-
-        mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
-    }
-
-    private void handleDisplayRemoved(int displayId) {
-        if (displayId == DEFAULT_DISPLAY) {
-            throw new IllegalArgumentException("Can't remove the primary display.");
-        }
-
-        synchronized (mService.mGlobalLock) {
-            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
-            if (activityDisplay == null) {
-                return;
-            }
-
-            activityDisplay.remove();
-
-            releaseSleepTokens(activityDisplay);
-        }
-    }
-
-    private void handleDisplayChanged(int displayId) {
-        synchronized (mService.mGlobalLock) {
-            ActivityDisplay activityDisplay = getActivityDisplay(displayId);
-            // TODO: The following code block should be moved into {@link ActivityDisplay}.
-            if (activityDisplay != null) {
-                // The window policy is responsible for stopping activities on the default display
-                if (displayId != Display.DEFAULT_DISPLAY) {
-                    int displayState = activityDisplay.mDisplay.getState();
-                    if (displayState == Display.STATE_OFF && activityDisplay.mOffToken == null) {
-                        activityDisplay.mOffToken =
-                                mService.acquireSleepToken("Display-off", displayId);
-                    } else if (displayState == Display.STATE_ON
-                            && activityDisplay.mOffToken != null) {
-                        activityDisplay.mOffToken.release();
-                        activityDisplay.mOffToken = null;
-                    }
-                }
-
-                activityDisplay.updateBounds();
-            }
-            mWindowManager.onDisplayChanged(displayId);
-        }
-    }
-
-    SleepToken createSleepTokenLocked(String tag, int displayId) {
-        final ActivityDisplay display = getActivityDisplay(displayId);
-        if (display == null) {
-            throw new IllegalArgumentException("Invalid display: " + displayId);
-        }
-
-        final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
-        mSleepTokens.add(token);
-        display.mAllSleepTokens.add(token);
-        return token;
-    }
-
-    private void removeSleepTokenLocked(SleepTokenImpl token) {
-        mSleepTokens.remove(token);
-
-        final ActivityDisplay display = getActivityDisplay(token.mDisplayId);
-        if (display != null) {
-            display.mAllSleepTokens.remove(token);
-            if (display.mAllSleepTokens.isEmpty()) {
-                mService.updateSleepIfNeededLocked();
-            }
-        }
-    }
-
-    private void releaseSleepTokens(ActivityDisplay display) {
-        if (display.mAllSleepTokens.isEmpty()) {
-            return;
-        }
-        for (SleepToken token : display.mAllSleepTokens) {
-            mSleepTokens.remove(token);
-        }
-        display.mAllSleepTokens.clear();
-
-        mService.updateSleepIfNeededLocked();
-    }
-
-    private StackInfo getStackInfo(ActivityStack stack) {
-        final int displayId = stack.mDisplayId;
-        final ActivityDisplay display = getActivityDisplay(displayId);
-        StackInfo info = new StackInfo();
-        stack.getWindowContainerBounds(info.bounds);
-        info.displayId = displayId;
-        info.stackId = stack.mStackId;
-        info.userId = stack.mCurrentUser;
-        info.visible = stack.shouldBeVisible(null);
-        // A stack might be not attached to a display.
-        info.position = display != null ? display.getIndexOf(stack) : 0;
-        info.configuration.setTo(stack.getConfiguration());
-
-        ArrayList<TaskRecord> tasks = stack.getAllTasks();
-        final int numTasks = tasks.size();
-        int[] taskIds = new int[numTasks];
-        String[] taskNames = new String[numTasks];
-        Rect[] taskBounds = new Rect[numTasks];
-        int[] taskUserIds = new int[numTasks];
-        for (int i = 0; i < numTasks; ++i) {
-            final TaskRecord task = tasks.get(i);
-            taskIds[i] = task.taskId;
-            taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
-                    : task.realActivity != null ? task.realActivity.flattenToString()
-                    : task.getTopActivity() != null ? task.getTopActivity().packageName
-                    : "unknown";
-            taskBounds[i] = new Rect();
-            task.getWindowContainerBounds(taskBounds[i]);
-            taskUserIds[i] = task.userId;
-        }
-        info.taskIds = taskIds;
-        info.taskNames = taskNames;
-        info.taskBounds = taskBounds;
-        info.taskUserIds = taskUserIds;
-
-        final ActivityRecord top = stack.topRunningActivityLocked();
-        info.topActivity = top != null ? top.intent.getComponent() : null;
-        return info;
-    }
-
-    StackInfo getStackInfo(int stackId) {
-        ActivityStack stack = getStack(stackId);
-        if (stack != null) {
-            return getStackInfo(stack);
-        }
-        return null;
-    }
-
-    StackInfo getStackInfo(int windowingMode, int activityType) {
-        final ActivityStack stack = getStack(windowingMode, activityType);
-        return (stack != null) ? getStackInfo(stack) : null;
-    }
-
-    ArrayList<StackInfo> getAllStackInfosLocked() {
-        ArrayList<StackInfo> list = new ArrayList<>();
-        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                list.add(getStackInfo(stack));
-            }
-        }
-        return list;
-    }
-
-    void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
-            int preferredDisplayId, ActivityStack actualStack) {
-        handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayId,
-                actualStack, false /* forceNonResizable */);
-    }
-
-    void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
-            int preferredDisplayId, ActivityStack actualStack, boolean forceNonResizable) {
-        final boolean isSecondaryDisplayPreferred =
-                (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
-        final boolean inSplitScreenMode = actualStack != null
-                && actualStack.getDisplay().hasSplitScreenPrimaryStack();
-        if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
-                && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
-            return;
-        }
-
-        // Handle incorrect launch/move to secondary display if needed.
-        if (isSecondaryDisplayPreferred) {
-            final int actualDisplayId = task.getStack().mDisplayId;
-            if (!task.canBeLaunchedOnDisplay(actualDisplayId)) {
-                throw new IllegalStateException("Task resolved to incompatible display");
-            }
-            if (preferredDisplayId != actualDisplayId) {
-                Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplayId);
-                // Display a warning toast that we failed to put a task on a secondary display.
-                mService.getTaskChangeNotificationController()
-                        .notifyActivityLaunchOnSecondaryDisplayFailed();
-                return;
-            } else if (!forceNonResizable && handleForcedResizableTask(task,
-                    FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY)) {
-                return;
-            }
-        }
-
-        if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
-            // Display a warning toast that we tried to put an app that doesn't support split-screen
-            // in split-screen.
-            mService.getTaskChangeNotificationController().notifyActivityDismissingDockedStack();
-
-            // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
-            // we need to move it to top of fullscreen stack, otherwise it will be covered.
-
-            final ActivityStack dockedStack =
-                    task.getStack().getDisplay().getSplitScreenPrimaryStack();
-            if (dockedStack != null) {
-                moveTasksToFullscreenStackLocked(dockedStack, actualStack == dockedStack);
-            }
-            return;
-        }
-
-        handleForcedResizableTask(task, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN);
-    }
-
-    /**
-     * @return {@code true} if the top activity of the task is forced to be resizable and the user
-     *         was notified about activity being forced resized.
-     */
-    private boolean handleForcedResizableTask(TaskRecord task, int reason) {
-        final ActivityRecord topActivity = task.getTopActivity();
-        if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
-                && !topActivity.noDisplay) {
-            final String packageName = topActivity.appInfo.packageName;
-            mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
-                    task.taskId, reason, packageName);
-            return true;
-        }
-        return false;
-    }
-
-    void activityRelaunchedLocked(IBinder token) {
-        mWindowManager.notifyAppRelaunchingFinished(token);
-        final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-        if (r != null) {
-            if (r.getStack().shouldSleepOrShutDownActivities()) {
-                r.setSleeping(true, true);
-            }
-        }
-    }
-
-    void activityRelaunchingLocked(ActivityRecord r) {
-        mWindowManager.notifyAppRelaunching(r.appToken);
-    }
-
-    void logStackState() {
-        mActivityMetricsLogger.logWindowState();
-    }
-
-    void scheduleUpdateMultiWindowMode(TaskRecord task) {
-        // If the stack is animating in a way where we will be forcing a multi-mode change at the
-        // end, then ensure that we defer all in between multi-window mode changes
-        if (task.getStack().deferScheduleMultiWindowModeChanged()) {
-            return;
-        }
-
-        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
-            final ActivityRecord r = task.mActivities.get(i);
-            if (r.attachedToProcess()) {
-                mMultiWindowModeChangedActivities.add(r);
-            }
-        }
-
-        if (!mHandler.hasMessages(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG)) {
-            mHandler.sendEmptyMessage(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG);
-        }
-    }
-
-    void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, ActivityStack prevStack) {
-        final ActivityStack stack = task.getStack();
-        if (prevStack == null || prevStack == stack
-                || (!prevStack.inPinnedWindowingMode() && !stack.inPinnedWindowingMode())) {
-            return;
-        }
-
-        scheduleUpdatePictureInPictureModeIfNeeded(task, stack.getOverrideBounds());
-    }
-
-    void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
-        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
-            final ActivityRecord r = task.mActivities.get(i);
-            if (r.attachedToProcess()) {
-                mPipModeChangedActivities.add(r);
-                // If we are scheduling pip change, then remove this activity from multi-window
-                // change list as the processing of pip change will make sure multi-window changed
-                // message is processed in the right order relative to pip changed.
-                mMultiWindowModeChangedActivities.remove(r);
-            }
-        }
-        mPipModeChangedTargetStackBounds = targetStackBounds;
-
-        if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
-            mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
-        }
-    }
-
-    void updatePictureInPictureMode(TaskRecord task, Rect targetStackBounds, boolean forceUpdate) {
-        mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
-        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
-            final ActivityRecord r = task.mActivities.get(i);
-            if (r.attachedToProcess()) {
-                r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
-            }
-        }
-    }
-
-    void setDockedStackMinimized(boolean minimized) {
-        // Get currently focused stack before setting mIsDockMinimized. We do this because if
-        // split-screen is active, primary stack will not be focusable (see #isFocusable) while
-        // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
-        final ActivityStack current = getTopDisplayFocusedStack();
-        mIsDockMinimized = minimized;
-        if (mIsDockMinimized) {
-            if (current.inSplitScreenPrimaryWindowingMode()) {
-                // The primary split-screen stack can't be focused while it is minimize, so move
-                // focus to something else.
-                current.adjustFocusToNextFocusableStack("setDockedStackMinimized");
-            }
-        }
-    }
-
-    void wakeUp(String reason) {
-        mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.am:TURN_ON:" + reason);
-    }
-
-    /**
-     * Begin deferring resume to avoid duplicate resumes in one pass.
-     */
-    private void beginDeferResume() {
-        mDeferResumeCount++;
-    }
-
-    /**
-     * End deferring resume and determine if resume can be called.
-     */
-    private void endDeferResume() {
-        mDeferResumeCount--;
-    }
-
-    /**
-     * @return True if resume can be called.
-     */
-    private boolean readyToResume() {
-        return mDeferResumeCount == 0;
-    }
-
-    private final class ActivityStackSupervisorHandler extends Handler {
-
-        public ActivityStackSupervisorHandler(Looper looper) {
-            super(looper);
-        }
-
-        void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) {
-            synchronized (mService.mGlobalLock) {
-                activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */,
-                        processPausingActivities, null);
-            }
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case REPORT_MULTI_WINDOW_MODE_CHANGED_MSG: {
-                    synchronized (mService.mGlobalLock) {
-                        for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
-                            final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
-                            r.updateMultiWindowMode();
-                        }
-                    }
-                } break;
-                case REPORT_PIP_MODE_CHANGED_MSG: {
-                    synchronized (mService.mGlobalLock) {
-                        for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
-                            final ActivityRecord r = mPipModeChangedActivities.remove(i);
-                            r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
-                                    false /* forceUpdate */);
-                        }
-                    }
-                } break;
-                case IDLE_TIMEOUT_MSG: {
-                    if (DEBUG_IDLE) Slog.d(TAG_IDLE,
-                            "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
-                    // We don't at this point know if the activity is fullscreen,
-                    // so we need to be conservative and assume it isn't.
-                    activityIdleInternal((ActivityRecord) msg.obj,
-                            true /* processPausingActivities */);
-                } break;
-                case IDLE_NOW_MSG: {
-                    if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
-                    activityIdleInternal((ActivityRecord) msg.obj,
-                            false /* processPausingActivities */);
-                } break;
-                case RESUME_TOP_ACTIVITY_MSG: {
-                    synchronized (mService.mGlobalLock) {
-                        resumeFocusedStacksTopActivitiesLocked();
-                    }
-                } break;
-                case SLEEP_TIMEOUT_MSG: {
-                    synchronized (mService.mGlobalLock) {
-                        if (mService.isSleepingOrShuttingDownLocked()) {
-                            Slog.w(TAG, "Sleep timeout!  Sleeping now.");
-                            checkReadyForSleepLocked(false /* allowDelay */);
-                        }
-                    }
-                } break;
-                case LAUNCH_TIMEOUT_MSG: {
-                    synchronized (mService.mGlobalLock) {
-                        if (mLaunchingActivity.isHeld()) {
-                            Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
-                            if (VALIDATE_WAKE_LOCK_CALLER
-                                    && Binder.getCallingUid() != Process.myUid()) {
-                                throw new IllegalStateException("Calling must be system uid");
-                            }
-                            mLaunchingActivity.release();
-                        }
-                    }
-                } break;
-                case HANDLE_DISPLAY_ADDED: {
-                    handleDisplayAdded(msg.arg1);
-                } break;
-                case HANDLE_DISPLAY_CHANGED: {
-                    handleDisplayChanged(msg.arg1);
-                } break;
-                case HANDLE_DISPLAY_REMOVED: {
-                    handleDisplayRemoved(msg.arg1);
-                } break;
-                case LAUNCH_TASK_BEHIND_COMPLETE: {
-                    synchronized (mService.mGlobalLock) {
-                        ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
-                        if (r != null) {
-                            handleLaunchTaskBehindCompleteLocked(r);
-                        }
-                    }
-                } break;
-
-            }
-        }
-    }
-
-    ActivityStack findStackBehind(ActivityStack stack) {
-        final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
-        if (display != null) {
-            for (int i = display.getChildCount() - 1; i >= 0; i--) {
-                if (display.getChildAt(i) == stack && i > 0) {
-                    return display.getChildAt(i - 1);
-                }
-            }
-        }
-        throw new IllegalStateException("Failed to find a stack behind stack=" + stack
-                + " in=" + display);
-    }
-
-    /**
-     * Puts a task into resizing mode during the next app transition.
-     *
-     * @param task The task to put into resizing mode
-     */
-    void setResizingDuringAnimation(TaskRecord task) {
-        mResizingTasksDuringAnimation.add(task.taskId);
-        task.setTaskDockedResizing(true);
-    }
-
-    int startActivityFromRecents(int callingPid, int callingUid, int taskId,
-            SafeActivityOptions options) {
-        TaskRecord task = null;
-        final String callingPackage;
-        final Intent intent;
-        final int userId;
-        int activityType = ACTIVITY_TYPE_UNDEFINED;
-        int windowingMode = WINDOWING_MODE_UNDEFINED;
-        final ActivityOptions activityOptions = options != null
-                ? options.getOptions(this)
-                : null;
-        if (activityOptions != null) {
-            activityType = activityOptions.getLaunchActivityType();
-            windowingMode = activityOptions.getLaunchWindowingMode();
-        }
-        if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
-            throw new IllegalArgumentException("startActivityFromRecents: Task "
-                    + taskId + " can't be launch in the home/recents stack.");
-        }
-
-        mWindowManager.deferSurfaceLayout();
-        try {
-            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                mWindowManager.setDockedStackCreateState(
-                        activityOptions.getSplitScreenCreateMode(), null /* initialBounds */);
-
-                // Defer updating the stack in which recents is until the app transition is done, to
-                // not run into issues where we still need to draw the task in recents but the
-                // docked stack is already created.
-                deferUpdateRecentsHomeStackBounds();
-                mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
-            }
-
-            task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
-                    activityOptions, ON_TOP);
-            if (task == null) {
-                continueUpdateRecentsHomeStackBounds();
-                mWindowManager.executeAppTransition();
-                throw new IllegalArgumentException(
-                        "startActivityFromRecents: Task " + taskId + " not found.");
-            }
-
-            if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                // We always want to return to the home activity instead of the recents activity
-                // from whatever is started from the recents activity, so move the home stack
-                // forward.
-                // TODO (b/115289124): Multi-display supports for recents.
-                getDefaultDisplay().moveHomeStackToFront("startActivityFromRecents");
-            }
-
-            // If the user must confirm credentials (e.g. when first launching a work app and the
-            // Work Challenge is present) let startActivityInPackage handle the intercepting.
-            if (!mService.mAmInternal.shouldConfirmCredentials(task.userId)
-                    && task.getRootActivity() != null) {
-                final ActivityRecord targetActivity = task.getTopActivity();
-
-                sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity);
-                mActivityMetricsLogger.notifyActivityLaunching();
-                try {
-                    mService.moveTaskToFrontLocked(task.taskId, 0, options,
-                            true /* fromRecents */);
-                } finally {
-                    mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
-                            targetActivity);
-                }
-
-                mService.getActivityStartController().postStartActivityProcessingForLastStarter(
-                        task.getTopActivity(), ActivityManager.START_TASK_TO_FRONT,
-                        task.getStack());
-                return ActivityManager.START_TASK_TO_FRONT;
-            }
-            callingPackage = task.mCallingPackage;
-            intent = task.intent;
-            intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
-            userId = task.userId;
-            return mService.getActivityStartController().startActivityInPackage(
-                    task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null,
-                    null, 0, 0, options, userId, task, "startActivityFromRecents",
-                    false /* validateIncomingUser */, null /* originatingPendingIntent */);
-        } finally {
-            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && task != null) {
-                // If we are launching the task in the docked stack, put it into resizing mode so
-                // the window renders full-screen with the background filling the void. Also only
-                // call this at the end to make sure that tasks exists on the window manager side.
-                setResizingDuringAnimation(task);
-
-                final ActivityDisplay display = task.getStack().getDisplay();
-                final ActivityStack topSecondaryStack =
-                        display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-                if (topSecondaryStack.isActivityTypeHome()) {
-                    // If the home activity is the top split-screen secondary stack, then the
-                    // primary split-screen stack is in the minimized mode which means it can't
-                    // receive input keys, so we should move the focused app to the home app so that
-                    // window manager can correctly calculate the focus window that can receive
-                    // input keys.
-                    display.moveHomeStackToFront(
-                            "startActivityFromRecents: homeVisibleInSplitScreen");
-
-                    // Immediately update the minimized docked stack mode, the upcoming animation
-                    // for the docked activity (WMS.overridePendingAppTransitionMultiThumbFuture)
-                    // will do the animation to the target bounds
-                    mWindowManager.checkSplitScreenMinimizedChanged(false /* animate */);
-                }
-            }
-            mWindowManager.continueSurfaceLayout();
-        }
-    }
-
-    /**
-     * @return a list of activities which are the top ones in each visible stack. The first
-     * entry will be the focused activity.
-     */
-    List<IBinder> getTopVisibleActivities() {
-        final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
-        final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
-        // Traverse all displays.
-        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            // Traverse all stacks on a display.
-            for (int j = display.getChildCount() - 1; j >= 0; --j) {
-                final ActivityStack stack = display.getChildAt(j);
-                // Get top activity from a visible stack and add it to the list.
-                if (stack.shouldBeVisible(null /* starting */)) {
-                    final ActivityRecord top = stack.getTopActivity();
-                    if (top != null) {
-                        if (stack == topFocusedStack) {
-                            topActivityTokens.add(0, top.appToken);
-                        } else {
-                            topActivityTokens.add(top.appToken);
-                        }
-                    }
-                }
-            }
-        }
-        return topActivityTokens;
-    }
-
-    /**
-     * Internal container to store a match qualifier alongside a WaitResult.
-     */
-    static class WaitInfo {
-        private final ComponentName mTargetComponent;
-        private final WaitResult mResult;
-        /** Time stamp when we started to wait for {@link WaitResult}. */
-        private final long mStartTimeMs;
-
-        WaitInfo(ComponentName targetComponent, WaitResult result, long startTimeMs) {
-            this.mTargetComponent = targetComponent;
-            this.mResult = result;
-            this.mStartTimeMs = startTimeMs;
-        }
-
-        public boolean matches(ComponentName targetComponent) {
-            return mTargetComponent == null || mTargetComponent.equals(targetComponent);
-        }
-
-        public WaitResult getResult() {
-            return mResult;
-        }
-
-        public long getStartTime() {
-            return mStartTimeMs;
-        }
-
-        public ComponentName getComponent() {
-            return mTargetComponent;
-        }
-
-        public void dump(PrintWriter pw, String prefix) {
-            pw.println(prefix + "WaitInfo:");
-            pw.println(prefix + "  mTargetComponent=" + mTargetComponent);
-            pw.println(prefix + "  mResult=");
-            mResult.dump(pw, prefix);
-        }
-    }
-
-    private final class SleepTokenImpl extends SleepToken {
-        private final String mTag;
-        private final long mAcquireTime;
-        private final int mDisplayId;
-
-        public SleepTokenImpl(String tag, int displayId) {
-            mTag = tag;
-            mDisplayId = displayId;
-            mAcquireTime = SystemClock.uptimeMillis();
-        }
-
-        @Override
-        public void release() {
-            synchronized (mService.mGlobalLock) {
-                removeSleepTokenLocked(this);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "{\"" + mTag + "\", display " + mDisplayId
-                    + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
-        }
-    }
-
-}
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
deleted file mode 100644
index 20d5ab2..0000000
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.ActivityManager.START_SUCCESS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
-import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
-import android.app.ActivityOptions;
-import android.app.IApplicationThread;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Binder;
-import android.os.FactoryTest;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-import android.view.RemoteAnimationAdapter;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
-import com.android.server.am.ActivityStarter.DefaultFactory;
-import com.android.server.am.ActivityStarter.Factory;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Controller for delegating activity launches.
- *
- * This class' main objective is to take external activity start requests and prepare them into
- * a series of discrete activity launches that can be handled by an {@link ActivityStarter}. It is
- * also responsible for handling logic that happens around an activity launch, but doesn't
- * necessarily influence the activity start. Examples include power hint management, processing
- * through the pending activity list, and recording home activity launches.
- */
-public class ActivityStartController {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStartController" : TAG_ATM;
-
-    private static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 1;
-
-    private final ActivityTaskManagerService mService;
-    private final ActivityStackSupervisor mSupervisor;
-
-    /** Last home activity record we attempted to start. */
-    private ActivityRecord mLastHomeActivityStartRecord;
-
-    /** Temporary array to capture start activity results */
-    private ActivityRecord[] tmpOutRecord = new ActivityRecord[1];
-
-    /** The result of the last home activity we attempted to start. */
-    private int mLastHomeActivityStartResult;
-
-    /** A list of activities that are waiting to launch. */
-    private final ArrayList<ActivityStackSupervisor.PendingActivityLaunch>
-            mPendingActivityLaunches = new ArrayList<>();
-
-    private final Factory mFactory;
-
-    private final Handler mHandler;
-
-    private final PendingRemoteAnimationRegistry mPendingRemoteAnimationRegistry;
-
-    boolean mCheckedForSetup = false;
-
-    private final class StartHandler extends Handler {
-        public StartHandler(Looper looper) {
-            super(looper, null, true);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch(msg.what) {
-                case DO_PENDING_ACTIVITY_LAUNCHES_MSG:
-                    synchronized (mService.mGlobalLock) {
-                        doPendingActivityLaunches(true);
-                    }
-                    break;
-            }
-        }
-    }
-
-    /**
-     * TODO(b/64750076): Capture information necessary for dump and
-     * {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object
-     * around
-     */
-    private ActivityStarter mLastStarter;
-
-    ActivityStartController(ActivityTaskManagerService service) {
-        this(service, service.mStackSupervisor,
-                new DefaultFactory(service, service.mStackSupervisor,
-                    new ActivityStartInterceptor(service, service.mStackSupervisor)));
-    }
-
-    @VisibleForTesting
-    ActivityStartController(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
-            Factory factory) {
-        mService = service;
-        mSupervisor = supervisor;
-        mHandler = new StartHandler(mService.mH.getLooper());
-        mFactory = factory;
-        mFactory.setController(this);
-        mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service,
-                service.mH);
-    }
-
-    /**
-     * @return A starter to configure and execute starting an activity. It is valid until after
-     *         {@link ActivityStarter#execute} is invoked. At that point, the starter should be
-     *         considered invalid and no longer modified or used.
-     */
-    ActivityStarter obtainStarter(Intent intent, String reason) {
-        return mFactory.obtain().setIntent(intent).setReason(reason);
-    }
-
-    void onExecutionComplete(ActivityStarter starter) {
-        if (mLastStarter == null) {
-            mLastStarter = mFactory.obtain();
-        }
-
-        mLastStarter.set(starter);
-        mFactory.recycle(starter);
-    }
-
-    /**
-     * TODO(b/64750076): usage of this doesn't seem right. We're making decisions based off the
-     * last starter for an arbitrary task record. Re-evaluate whether we can remove.
-     */
-    void postStartActivityProcessingForLastStarter(ActivityRecord r, int result,
-            ActivityStack targetStack) {
-        if (mLastStarter == null) {
-            return;
-        }
-
-        mLastStarter.postStartActivityProcessing(r, result, targetStack);
-    }
-
-    void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
-        if (!mSupervisor.canStartHomeOnDisplay(aInfo, displayId)) {
-            return;
-        }
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
-        options.setLaunchDisplayId(displayId);
-        mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
-                .setOutActivity(tmpOutRecord)
-                .setCallingUid(0)
-                .setActivityInfo(aInfo)
-                .setActivityOptions(options.toBundle())
-                .execute();
-        mLastHomeActivityStartRecord = tmpOutRecord[0];
-        if (mSupervisor.inResumeTopActivity) {
-            // If we are in resume section already, home activity will be initialized, but not
-            // resumed (to avoid recursive resume) and will stay that way until something pokes it
-            // again. We need to schedule another resume.
-            mSupervisor.scheduleResumeTopActivities();
-        }
-    }
-
-    /**
-     * Starts the "new version setup screen" if appropriate.
-     */
-    void startSetupActivity() {
-        // Only do this once per boot.
-        if (mCheckedForSetup) {
-            return;
-        }
-
-        // We will show this screen if the current one is a different
-        // version than the last one shown, and we are not running in
-        // low-level factory test mode.
-        final ContentResolver resolver = mService.mContext.getContentResolver();
-        if (mService.mFactoryTest != FACTORY_TEST_LOW_LEVEL
-                && Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
-            mCheckedForSetup = true;
-
-            // See if we should be showing the platform update setup UI.
-            final Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
-            final List<ResolveInfo> ris =
-                    mService.mContext.getPackageManager().queryIntentActivities(intent,
-                            PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA
-                                    | ActivityManagerService.STOCK_PM_FLAGS);
-            if (!ris.isEmpty()) {
-                final ResolveInfo ri = ris.get(0);
-                String vers = ri.activityInfo.metaData != null
-                        ? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION)
-                        : null;
-                if (vers == null && ri.activityInfo.applicationInfo.metaData != null) {
-                    vers = ri.activityInfo.applicationInfo.metaData.getString(
-                            Intent.METADATA_SETUP_VERSION);
-                }
-                String lastVers = Settings.Secure.getString(
-                        resolver, Settings.Secure.LAST_SETUP_SHOWN);
-                if (vers != null && !vers.equals(lastVers)) {
-                    intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
-                    intent.setComponent(new ComponentName(
-                            ri.activityInfo.packageName, ri.activityInfo.name));
-                    obtainStarter(intent, "startSetupActivity")
-                            .setCallingUid(0)
-                            .setActivityInfo(ri.activityInfo)
-                            .execute();
-                }
-            }
-        }
-    }
-
-    /**
-     * If {@code validateIncomingUser} is true, check {@code targetUserId} against the real calling
-     * user ID inferred from {@code realCallingUid}, then return the resolved user-id, taking into
-     * account "current user", etc.
-     *
-     * If {@code validateIncomingUser} is false, it skips the above check, but instead
-     * ensures {@code targetUserId} is a real user ID and not a special user ID such as
-     * {@link android.os.UserHandle#USER_ALL}, etc.
-     */
-    int checkTargetUser(int targetUserId, boolean validateIncomingUser,
-            int realCallingPid, int realCallingUid, String reason) {
-        if (validateIncomingUser) {
-            return mService.handleIncomingUser(
-                    realCallingPid, realCallingUid, targetUserId, reason);
-        } else {
-            mService.mAmInternal.ensureNotSpecialUser(targetUserId);
-            return targetUserId;
-        }
-    }
-
-    final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
-            String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
-            int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent) {
-
-        userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
-                reason);
-
-        // TODO: Switch to user app stacks here.
-        return obtainStarter(intent, reason)
-                .setCallingUid(uid)
-                .setRealCallingPid(realCallingPid)
-                .setRealCallingUid(realCallingUid)
-                .setCallingPackage(callingPackage)
-                .setResolvedType(resolvedType)
-                .setResultTo(resultTo)
-                .setResultWho(resultWho)
-                .setRequestCode(requestCode)
-                .setStartFlags(startFlags)
-                .setActivityOptions(options)
-                .setMayWait(userId)
-                .setInTask(inTask)
-                .setOriginatingPendingIntent(originatingPendingIntent)
-                .execute();
-    }
-
-    /**
-     * Start intents as a package.
-     *
-     * @param uid Make a call as if this UID did.
-     * @param callingPackage Make a call as if this package did.
-     * @param intents Intents to start.
-     * @param userId Start the intents on this user.
-     * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
-     * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
-     *        null if not originated by PendingIntent
-     */
-    final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
-            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent) {
-
-        final String reason = "startActivityInPackage";
-
-        userId = checkTargetUser(userId, validateIncomingUser, Binder.getCallingPid(),
-                Binder.getCallingUid(), reason);
-
-        // TODO: Switch to user app stacks here.
-        return startActivities(null, uid, callingPackage, intents, resolvedTypes, resultTo, options,
-                userId, reason, originatingPendingIntent);
-    }
-
-    int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
-            Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
-            int userId, String reason, PendingIntentRecord originatingPendingIntent) {
-        if (intents == null) {
-            throw new NullPointerException("intents is null");
-        }
-        if (resolvedTypes == null) {
-            throw new NullPointerException("resolvedTypes is null");
-        }
-        if (intents.length != resolvedTypes.length) {
-            throw new IllegalArgumentException("intents are length different than resolvedTypes");
-        }
-
-        final int realCallingPid = Binder.getCallingPid();
-        final int realCallingUid = Binder.getCallingUid();
-
-        int callingPid;
-        if (callingUid >= 0) {
-            callingPid = -1;
-        } else if (caller == null) {
-            callingPid = realCallingPid;
-            callingUid = realCallingUid;
-        } else {
-            callingPid = callingUid = -1;
-        }
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mService.mGlobalLock) {
-                ActivityRecord[] outActivity = new ActivityRecord[1];
-                for (int i=0; i < intents.length; i++) {
-                    Intent intent = intents[i];
-                    if (intent == null) {
-                        continue;
-                    }
-
-                    // Refuse possible leaked file descriptors
-                    if (intent != null && intent.hasFileDescriptors()) {
-                        throw new IllegalArgumentException("File descriptors passed in Intent");
-                    }
-
-                    boolean componentSpecified = intent.getComponent() != null;
-
-                    // Don't modify the client's object!
-                    intent = new Intent(intent);
-
-                    // Collect information about the target of the Intent.
-                    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0,
-                            null, userId, ActivityStarter.computeResolveFilterUid(
-                                    callingUid, realCallingUid, UserHandle.USER_NULL));
-                    // TODO: New, check if this is correct
-                    aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
-
-                    if (aInfo != null &&
-                            (aInfo.applicationInfo.privateFlags
-                                    & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)  != 0) {
-                        throw new IllegalArgumentException(
-                                "FLAG_CANT_SAVE_STATE not supported here");
-                    }
-
-                    final boolean top = i == intents.length - 1;
-                    final SafeActivityOptions checkedOptions = top
-                            ? options
-                            : null;
-                    final int res = obtainStarter(intent, reason)
-                            .setCaller(caller)
-                            .setResolvedType(resolvedTypes[i])
-                            .setActivityInfo(aInfo)
-                            .setResultTo(resultTo)
-                            .setRequestCode(-1)
-                            .setCallingPid(callingPid)
-                            .setCallingUid(callingUid)
-                            .setCallingPackage(callingPackage)
-                            .setRealCallingPid(realCallingPid)
-                            .setRealCallingUid(realCallingUid)
-                            .setActivityOptions(checkedOptions)
-                            .setComponentSpecified(componentSpecified)
-                            .setOutActivity(outActivity)
-
-                            // Top activity decides on animation being run, so we allow only for the
-                            // top one as otherwise an activity below might consume it.
-                            .setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
-                            .setOriginatingPendingIntent(originatingPendingIntent)
-                            .execute();
-
-                    if (res < 0) {
-                        return res;
-                    }
-
-                    resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-
-        return START_SUCCESS;
-    }
-
-    void schedulePendingActivityLaunches(long delayMs) {
-        mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
-        Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
-        mHandler.sendMessageDelayed(msg, delayMs);
-    }
-
-    void doPendingActivityLaunches(boolean doResume) {
-        while (!mPendingActivityLaunches.isEmpty()) {
-            final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
-            final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
-            final ActivityStarter starter = obtainStarter(null /* intent */,
-                    "pendingActivityLaunch");
-            try {
-                starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
-                        resume, pal.r.pendingOptions, null, null /* outRecords */);
-            } catch (Exception e) {
-                Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
-                pal.sendErrorResult(e.getMessage());
-            }
-        }
-    }
-
-    void addPendingActivityLaunch(PendingActivityLaunch launch) {
-        mPendingActivityLaunches.add(launch);
-    }
-
-    boolean clearPendingActivityLaunches(String packageName) {
-        final int pendingLaunches = mPendingActivityLaunches.size();
-
-        for (int palNdx = pendingLaunches - 1; palNdx >= 0; --palNdx) {
-            final PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
-            final ActivityRecord r = pal.r;
-            if (r != null && r.packageName.equals(packageName)) {
-                mPendingActivityLaunches.remove(palNdx);
-            }
-        }
-        return mPendingActivityLaunches.size() < pendingLaunches;
-    }
-
-    void registerRemoteAnimationForNextActivityStart(String packageName,
-            RemoteAnimationAdapter adapter) {
-        mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);
-    }
-
-    PendingRemoteAnimationRegistry getPendingRemoteAnimationRegistry() {
-        return mPendingRemoteAnimationRegistry;
-    }
-
-    void dump(PrintWriter pw, String prefix, String dumpPackage) {
-        pw.print(prefix);
-        pw.print("mLastHomeActivityStartResult=");
-        pw.println(mLastHomeActivityStartResult);
-
-        if (mLastHomeActivityStartRecord != null) {
-            pw.print(prefix);
-            pw.println("mLastHomeActivityStartRecord:");
-            mLastHomeActivityStartRecord.dump(pw, prefix + "  ");
-        }
-
-        final boolean dumpPackagePresent = dumpPackage != null;
-
-        if (mLastStarter != null) {
-            final boolean dump = !dumpPackagePresent
-                    || mLastStarter.relatedToPackage(dumpPackage)
-                    || (mLastHomeActivityStartRecord != null
-                            && dumpPackage.equals(mLastHomeActivityStartRecord.packageName));
-
-            if (dump) {
-                pw.print(prefix);
-                mLastStarter.dump(pw, prefix + "  ");
-
-                if (dumpPackagePresent) {
-                    return;
-                }
-            }
-        }
-
-        if (dumpPackagePresent) {
-            pw.print(prefix);
-            pw.println("(nothing)");
-        }
-    }
-
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
-        for (PendingActivityLaunch activity: mPendingActivityLaunches) {
-            activity.r.writeIdentifierToProto(proto, fieldId);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
deleted file mode 100644
index e51824f..0000000
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright (C) 2016 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.server.am;
-
-import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
-import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
-import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
-import static android.app.PendingIntent.FLAG_IMMUTABLE;
-import static android.app.PendingIntent.FLAG_ONE_SHOT;
-import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION;
-import static android.app.admin.DevicePolicyManager.POLICY_SUSPEND_PACKAGES;
-import static android.content.Context.KEYGUARD_SERVICE;
-import static android.content.Intent.EXTRA_INTENT;
-import static android.content.Intent.EXTRA_PACKAGE_NAME;
-import static android.content.Intent.EXTRA_TASK_ID;
-import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
-import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
-
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-
-import android.app.ActivityOptions;
-import android.app.KeyguardManager;
-import android.app.admin.DevicePolicyManagerInternal;
-import android.content.Context;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.ResolveInfo;
-import android.content.pm.SuspendDialogInfo;
-import android.content.pm.UserInfo;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.HarmfulAppWarningActivity;
-import com.android.internal.app.SuspendedAppActivity;
-import com.android.internal.app.UnlaunchableAppActivity;
-import com.android.server.LocalServices;
-
-/**
- * A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked}
- * It's initialized via setStates and interception occurs via the intercept method.
- *
- * Note that this class is instantiated when {@link ActivityManagerService} gets created so there
- * is no guarantee that other system services are already present.
- */
-class ActivityStartInterceptor {
-
-    private final ActivityTaskManagerService mService;
-    private final ActivityStackSupervisor mSupervisor;
-    private final Context mServiceContext;
-
-    // UserManager cannot be final as it's not ready when this class is instantiated during boot
-    private UserManager mUserManager;
-
-    /*
-     * Per-intent states loaded from ActivityStarter than shouldn't be changed by any
-     * interception routines.
-     */
-    private int mRealCallingPid;
-    private int mRealCallingUid;
-    private int mUserId;
-    private int mStartFlags;
-    private String mCallingPackage;
-
-    /*
-     * Per-intent states that were load from ActivityStarter and are subject to modifications
-     * by the interception routines. After calling {@link #intercept} the caller should assign
-     * these values back to {@link ActivityStarter#startActivityLocked}'s local variables if
-     * {@link #intercept} returns true.
-     */
-    Intent mIntent;
-    int mCallingPid;
-    int mCallingUid;
-    ResolveInfo mRInfo;
-    ActivityInfo mAInfo;
-    String mResolvedType;
-    TaskRecord mInTask;
-    ActivityOptions mActivityOptions;
-
-    ActivityStartInterceptor(
-            ActivityTaskManagerService service, ActivityStackSupervisor supervisor) {
-        this(service, supervisor, service.mContext);
-    }
-
-    @VisibleForTesting
-    ActivityStartInterceptor(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
-            Context context) {
-        mService = service;
-        mSupervisor = supervisor;
-        mServiceContext = context;
-    }
-
-    /**
-     * Effectively initialize the class before intercepting the start intent. The values set in this
-     * method should not be changed during intercept.
-     */
-    void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags,
-            String callingPackage) {
-        mRealCallingPid = realCallingPid;
-        mRealCallingUid = realCallingUid;
-        mUserId = userId;
-        mStartFlags = startFlags;
-        mCallingPackage = callingPackage;
-    }
-
-    private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
-        Bundle activityOptions = deferCrossProfileAppsAnimationIfNecessary();
-        final IIntentSender target = mService.getIntentSenderLocked(
-                INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/,
-                null /*resultCode*/, 0 /*requestCode*/,
-                new Intent[] { mIntent }, new String[] { mResolvedType },
-                flags, activityOptions);
-        return new IntentSender(target);
-    }
-
-    /**
-     * Intercept the launch intent based on various signals. If an interception happened the
-     * internal variables get assigned and need to be read explicitly by the caller.
-     *
-     * @return true if an interception occurred
-     */
-    boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
-            TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions) {
-        mUserManager = UserManager.get(mServiceContext);
-
-        mIntent = intent;
-        mCallingPid = callingPid;
-        mCallingUid = callingUid;
-        mRInfo = rInfo;
-        mAInfo = aInfo;
-        mResolvedType = resolvedType;
-        mInTask = inTask;
-        mActivityOptions = activityOptions;
-
-        if (interceptSuspendedPackageIfNeeded()) {
-            // Skip the rest of interceptions as the package is suspended by device admin so
-            // no user action can undo this.
-            return true;
-        }
-        if (interceptQuietProfileIfNeeded()) {
-            // If work profile is turned off, skip the work challenge since the profile can only
-            // be unlocked when profile's user is running.
-            return true;
-        }
-        if (interceptHarmfulAppIfNeeded()) {
-            // If the app has a "harmful app" warning associated with it, we should ask to uninstall
-            // before issuing the work challenge.
-            return true;
-        }
-        return interceptWorkProfileChallengeIfNeeded();
-    }
-
-    /**
-     * If the activity option is the {@link ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} one,
-     * defer the animation until the original intent is started.
-     *
-     * @return the activity option used to start the original intent.
-     */
-    private Bundle deferCrossProfileAppsAnimationIfNecessary() {
-        if (mActivityOptions != null
-                && mActivityOptions.getAnimationType() == ANIM_OPEN_CROSS_PROFILE_APPS) {
-            mActivityOptions = null;
-            return ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle();
-        }
-        return null;
-    }
-
-    private boolean interceptQuietProfileIfNeeded() {
-        // Do not intercept if the user has not turned off the profile
-        if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) {
-            return false;
-        }
-
-        IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
-                FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT);
-
-        mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId, target);
-        mCallingPid = mRealCallingPid;
-        mCallingUid = mRealCallingUid;
-        mResolvedType = null;
-
-        final UserInfo parent = mUserManager.getProfileParent(mUserId);
-        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid);
-        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
-        return true;
-    }
-
-    private boolean interceptSuspendedByAdminPackage() {
-        DevicePolicyManagerInternal devicePolicyManager = LocalServices
-                .getService(DevicePolicyManagerInternal.class);
-        if (devicePolicyManager == null) {
-            return false;
-        }
-        mIntent = devicePolicyManager.createShowAdminSupportIntent(mUserId, true);
-        mIntent.putExtra(EXTRA_RESTRICTION, POLICY_SUSPEND_PACKAGES);
-
-        mCallingPid = mRealCallingPid;
-        mCallingUid = mRealCallingUid;
-        mResolvedType = null;
-
-        final UserInfo parent = mUserManager.getProfileParent(mUserId);
-        if (parent != null) {
-            mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0,
-                    mRealCallingUid);
-        } else {
-            mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0,
-                    mRealCallingUid);
-        }
-        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
-        return true;
-    }
-
-    private boolean interceptSuspendedPackageIfNeeded() {
-        // Do not intercept if the package is not suspended
-        if (mAInfo == null || mAInfo.applicationInfo == null ||
-                (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
-            return false;
-        }
-        final PackageManagerInternal pmi = mService.getPackageManagerInternalLocked();
-        if (pmi == null) {
-            return false;
-        }
-        final String suspendedPackage = mAInfo.applicationInfo.packageName;
-        final String suspendingPackage = pmi.getSuspendingPackage(suspendedPackage, mUserId);
-        if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
-            return interceptSuspendedByAdminPackage();
-        }
-        final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage, mUserId);
-        mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage,
-                suspendingPackage, dialogInfo, mUserId);
-        mCallingPid = mRealCallingPid;
-        mCallingUid = mRealCallingUid;
-        mResolvedType = null;
-        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
-        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
-        return true;
-    }
-
-    private boolean interceptWorkProfileChallengeIfNeeded() {
-        final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
-        if (interceptingIntent == null) {
-            return false;
-        }
-        mIntent = interceptingIntent;
-        mCallingPid = mRealCallingPid;
-        mCallingUid = mRealCallingUid;
-        mResolvedType = null;
-        // If we are intercepting and there was a task, convert it into an extra for the
-        // ConfirmCredentials intent and unassign it, as otherwise the task will move to
-        // front even if ConfirmCredentials is cancelled.
-        if (mInTask != null) {
-            mIntent.putExtra(EXTRA_TASK_ID, mInTask.taskId);
-            mInTask = null;
-        }
-        if (mActivityOptions == null) {
-            mActivityOptions = ActivityOptions.makeBasic();
-        }
-
-        ActivityRecord homeActivityRecord = mSupervisor.getDefaultDisplayHomeActivity();
-        if (homeActivityRecord != null && homeActivityRecord.getTask() != null) {
-            // Showing credential confirmation activity in home task to avoid stopping multi-windowed
-            // mode after showing the full-screen credential confirmation activity.
-            mActivityOptions.setLaunchTaskId(homeActivityRecord.getTask().taskId);
-        }
-
-        final UserInfo parent = mUserManager.getProfileParent(mUserId);
-        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid);
-        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
-        return true;
-    }
-
-    /**
-     * Creates an intent to intercept the current activity start with Confirm Credentials if needed.
-     *
-     * @return The intercepting intent if needed.
-     */
-    private Intent interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId) {
-        if (!mService.mAmInternal.shouldConfirmCredentials(userId)) {
-            return null;
-        }
-        // TODO(b/28935539): should allow certain activities to bypass work challenge
-        final IntentSender target = createIntentSenderForOriginalIntent(Binder.getCallingUid(),
-                FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
-        final KeyguardManager km = (KeyguardManager) mServiceContext
-                .getSystemService(KEYGUARD_SERVICE);
-        final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
-        if (newIntent == null) {
-            return null;
-        }
-        newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
-                FLAG_ACTIVITY_TASK_ON_HOME);
-        newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
-        newIntent.putExtra(EXTRA_INTENT, target);
-        return newIntent;
-    }
-
-    private boolean interceptHarmfulAppIfNeeded() {
-        CharSequence harmfulAppWarning;
-        try {
-            harmfulAppWarning = mService.getPackageManager()
-                    .getHarmfulAppWarning(mAInfo.packageName, mUserId);
-        } catch (RemoteException ex) {
-            return false;
-        }
-
-        if (harmfulAppWarning == null) {
-            return false;
-        }
-
-        final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
-                FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
-
-        mIntent = HarmfulAppWarningActivity.createHarmfulAppWarningIntent(mServiceContext,
-                mAInfo.packageName, target, harmfulAppWarning);
-
-        mCallingPid = mRealCallingPid;
-        mCallingUid = mRealCallingUid;
-        mResolvedType = null;
-
-        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
-        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
-        return true;
-    }
-}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
deleted file mode 100644
index fa227a2..0000000
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ /dev/null
@@ -1,2702 +0,0 @@
-/*
- * Copyright (C) 2016 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.server.am;
-
-import static android.app.Activity.RESULT_CANCELED;
-import static android.app.ActivityManager.START_ABORTED;
-import static android.app.ActivityManager.START_CANCELED;
-import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
-import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
-import static android.app.ActivityManager.START_FLAG_ONLY_IF_NEEDED;
-import static android.app.ActivityManager.START_RETURN_INTENT_TO_CALLER;
-import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
-import static android.app.ActivityManager.START_SUCCESS;
-import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
-import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
-import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
-import static android.content.Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP;
-import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
-import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
-import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
-import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
-import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
-import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityTaskManagerService.ANIMATE;
-import static com.android.server.am.EventLogTags.AM_NEW_INTENT;
-import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
-import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.IApplicationThread;
-import android.app.PendingIntent;
-import android.app.ProfilerInfo;
-import android.app.WaitResult;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.AuxiliaryResolveInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.service.voice.IVoiceInteractionSession;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Pools.SynchronizedPool;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.HeavyWeightSwitcherActivity;
-import com.android.internal.app.IVoiceInteractor;
-import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
-import com.android.server.am.LaunchParamsController.LaunchParams;
-import com.android.server.pm.InstantAppResolver;
-
-import java.io.PrintWriter;
-import java.text.DateFormat;
-import java.util.Date;
-
-/**
- * Controller for interpreting how and then launching an activity.
- *
- * This class collects all the logic for determining how an intent and flags should be turned into
- * an activity and associated task and stack.
- */
-class ActivityStarter {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStarter" : TAG_ATM;
-    private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
-    private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
-    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
-    private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
-    private static final int INVALID_LAUNCH_MODE = -1;
-
-    private final ActivityTaskManagerService mService;
-    private final ActivityStackSupervisor mSupervisor;
-    private final ActivityStartInterceptor mInterceptor;
-    private final ActivityStartController mController;
-
-    // Share state variable among methods when starting an activity.
-    private ActivityRecord mStartActivity;
-    private Intent mIntent;
-    private int mCallingUid;
-    private ActivityOptions mOptions;
-
-    private int mLaunchMode;
-    private boolean mLaunchTaskBehind;
-    private int mLaunchFlags;
-
-    private LaunchParams mLaunchParams = new LaunchParams();
-
-    private ActivityRecord mNotTop;
-    private boolean mDoResume;
-    private int mStartFlags;
-    private ActivityRecord mSourceRecord;
-
-    // The display to launch the activity onto, barring any strong reason to do otherwise.
-    private int mPreferredDisplayId;
-
-    private TaskRecord mInTask;
-    private boolean mAddingToTask;
-    private TaskRecord mReuseTask;
-
-    private ActivityInfo mNewTaskInfo;
-    private Intent mNewTaskIntent;
-    private ActivityStack mSourceStack;
-    private ActivityStack mTargetStack;
-    private boolean mMovedToFront;
-    private boolean mNoAnimation;
-    private boolean mKeepCurTransition;
-    private boolean mAvoidMoveToFront;
-
-    // We must track when we deliver the new intent since multiple code paths invoke
-    // {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used
-    // inside {@link #deliverNewIntent} to suppress duplicate requests and ensure the intent is
-    // delivered at most once.
-    private boolean mIntentDelivered;
-
-    private IVoiceInteractionSession mVoiceSession;
-    private IVoiceInteractor mVoiceInteractor;
-
-    // Last activity record we attempted to start
-    private final ActivityRecord[] mLastStartActivityRecord = new ActivityRecord[1];
-    // The result of the last activity we attempted to start.
-    private int mLastStartActivityResult;
-    // Time in milli seconds we attempted to start the last activity.
-    private long mLastStartActivityTimeMs;
-    // The reason we were trying to start the last activity
-    private String mLastStartReason;
-
-    /*
-     * Request details provided through setter methods. Should be reset after {@link #execute()}
-     * to avoid unnecessarily retaining parameters. Note that the request is ignored when
-     * {@link #startResolvedActivity} is invoked directly.
-     */
-    private Request mRequest = new Request();
-
-    /**
-     * An interface that to provide {@link ActivityStarter} instances to the controller. This is
-     * used by tests to inject their own starter implementations for verification purposes.
-     */
-    @VisibleForTesting
-    interface Factory {
-        /**
-         * Sets the {@link ActivityStartController} to be passed to {@link ActivityStarter}.
-         */
-        void setController(ActivityStartController controller);
-
-        /**
-         * Generates an {@link ActivityStarter} that is ready to handle a new start request.
-         * @param controller The {@link ActivityStartController} which the starter who will own
-         *                   this instance.
-         * @return an {@link ActivityStarter}
-         */
-        ActivityStarter obtain();
-
-        /**
-         * Recycles a starter for reuse.
-         */
-        void recycle(ActivityStarter starter);
-    }
-
-    /**
-     * Default implementation of {@link StarterFactory}.
-     */
-    static class DefaultFactory implements Factory {
-        /**
-         * The maximum count of starters that should be active at one time:
-         * 1. last ran starter (for logging and post activity processing)
-         * 2. current running starter
-         * 3. starter from re-entry in (2)
-         */
-        private final int MAX_STARTER_COUNT = 3;
-
-        private ActivityStartController mController;
-        private ActivityTaskManagerService mService;
-        private ActivityStackSupervisor mSupervisor;
-        private ActivityStartInterceptor mInterceptor;
-
-        private SynchronizedPool<ActivityStarter> mStarterPool =
-                new SynchronizedPool<>(MAX_STARTER_COUNT);
-
-        DefaultFactory(ActivityTaskManagerService service,
-                ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
-            mService = service;
-            mSupervisor = supervisor;
-            mInterceptor = interceptor;
-        }
-
-        @Override
-        public void setController(ActivityStartController controller) {
-            mController = controller;
-        }
-
-        @Override
-        public ActivityStarter obtain() {
-            ActivityStarter starter = mStarterPool.acquire();
-
-            if (starter == null) {
-                starter = new ActivityStarter(mController, mService, mSupervisor, mInterceptor);
-            }
-
-            return starter;
-        }
-
-        @Override
-        public void recycle(ActivityStarter starter) {
-            starter.reset(true /* clearRequest*/);
-            mStarterPool.release(starter);
-        }
-    }
-
-    /**
-     * Container for capturing initial start request details. This information is NOT reset until
-     * the {@link ActivityStarter} is recycled, allowing for multiple invocations with the same
-     * parameters.
-     *
-     * TODO(b/64750076): Investigate consolidating member variables of {@link ActivityStarter} with
-     * the request object. Note that some member variables are referenced in
-     * {@link #dump(PrintWriter, String)} and therefore cannot be cleared immediately after
-     * execution.
-     */
-    private static class Request {
-        private static final int DEFAULT_CALLING_UID = -1;
-        private static final int DEFAULT_CALLING_PID = 0;
-
-        IApplicationThread caller;
-        Intent intent;
-        Intent ephemeralIntent;
-        String resolvedType;
-        ActivityInfo activityInfo;
-        ResolveInfo resolveInfo;
-        IVoiceInteractionSession voiceSession;
-        IVoiceInteractor voiceInteractor;
-        IBinder resultTo;
-        String resultWho;
-        int requestCode;
-        int callingPid = DEFAULT_CALLING_UID;
-        int callingUid = DEFAULT_CALLING_PID;
-        String callingPackage;
-        int realCallingPid;
-        int realCallingUid;
-        int startFlags;
-        SafeActivityOptions activityOptions;
-        boolean ignoreTargetSecurity;
-        boolean componentSpecified;
-        boolean avoidMoveToFront;
-        ActivityRecord[] outActivity;
-        TaskRecord inTask;
-        String reason;
-        ProfilerInfo profilerInfo;
-        Configuration globalConfig;
-        int userId;
-        WaitResult waitResult;
-        int filterCallingUid;
-        PendingIntentRecord originatingPendingIntent;
-
-        /**
-         * If set to {@code true}, allows this activity start to look into
-         * {@link PendingRemoteAnimationRegistry}
-         */
-        boolean allowPendingRemoteAnimationRegistryLookup;
-
-        /**
-         * Indicates that we should wait for the result of the start request. This flag is set when
-         * {@link ActivityStarter#setMayWait(int)} is called.
-         * {@see ActivityStarter#startActivityMayWait}.
-         */
-        boolean mayWait;
-
-        /**
-         * Ensure constructed request matches reset instance.
-         */
-        Request() {
-            reset();
-        }
-
-        /**
-         * Sets values back to the initial state, clearing any held references.
-         */
-        void reset() {
-            caller = null;
-            intent = null;
-            ephemeralIntent = null;
-            resolvedType = null;
-            activityInfo = null;
-            resolveInfo = null;
-            voiceSession = null;
-            voiceInteractor = null;
-            resultTo = null;
-            resultWho = null;
-            requestCode = 0;
-            callingPid = DEFAULT_CALLING_PID;
-            callingUid = DEFAULT_CALLING_UID;
-            callingPackage = null;
-            realCallingPid = 0;
-            realCallingUid = 0;
-            startFlags = 0;
-            activityOptions = null;
-            ignoreTargetSecurity = false;
-            componentSpecified = false;
-            outActivity = null;
-            inTask = null;
-            reason = null;
-            profilerInfo = null;
-            globalConfig = null;
-            userId = 0;
-            waitResult = null;
-            mayWait = false;
-            avoidMoveToFront = false;
-            allowPendingRemoteAnimationRegistryLookup = true;
-            filterCallingUid = UserHandle.USER_NULL;
-            originatingPendingIntent = null;
-        }
-
-        /**
-         * Adopts all values from passed in request.
-         */
-        void set(Request request) {
-            caller = request.caller;
-            intent = request.intent;
-            ephemeralIntent = request.ephemeralIntent;
-            resolvedType = request.resolvedType;
-            activityInfo = request.activityInfo;
-            resolveInfo = request.resolveInfo;
-            voiceSession = request.voiceSession;
-            voiceInteractor = request.voiceInteractor;
-            resultTo = request.resultTo;
-            resultWho = request.resultWho;
-            requestCode = request.requestCode;
-            callingPid = request.callingPid;
-            callingUid = request.callingUid;
-            callingPackage = request.callingPackage;
-            realCallingPid = request.realCallingPid;
-            realCallingUid = request.realCallingUid;
-            startFlags = request.startFlags;
-            activityOptions = request.activityOptions;
-            ignoreTargetSecurity = request.ignoreTargetSecurity;
-            componentSpecified = request.componentSpecified;
-            outActivity = request.outActivity;
-            inTask = request.inTask;
-            reason = request.reason;
-            profilerInfo = request.profilerInfo;
-            globalConfig = request.globalConfig;
-            userId = request.userId;
-            waitResult = request.waitResult;
-            mayWait = request.mayWait;
-            avoidMoveToFront = request.avoidMoveToFront;
-            allowPendingRemoteAnimationRegistryLookup
-                    = request.allowPendingRemoteAnimationRegistryLookup;
-            filterCallingUid = request.filterCallingUid;
-            originatingPendingIntent = request.originatingPendingIntent;
-        }
-    }
-
-    ActivityStarter(ActivityStartController controller, ActivityTaskManagerService service,
-            ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
-        mController = controller;
-        mService = service;
-        mSupervisor = supervisor;
-        mInterceptor = interceptor;
-        reset(true);
-    }
-
-    /**
-     * Effectively duplicates the starter passed in. All state and request values will be
-     * mirrored.
-     * @param starter
-     */
-    void set(ActivityStarter starter) {
-        mStartActivity = starter.mStartActivity;
-        mIntent = starter.mIntent;
-        mCallingUid = starter.mCallingUid;
-        mOptions = starter.mOptions;
-
-        mLaunchTaskBehind = starter.mLaunchTaskBehind;
-        mLaunchFlags = starter.mLaunchFlags;
-        mLaunchMode = starter.mLaunchMode;
-
-        mLaunchParams.set(starter.mLaunchParams);
-
-        mNotTop = starter.mNotTop;
-        mDoResume = starter.mDoResume;
-        mStartFlags = starter.mStartFlags;
-        mSourceRecord = starter.mSourceRecord;
-        mPreferredDisplayId = starter.mPreferredDisplayId;
-
-        mInTask = starter.mInTask;
-        mAddingToTask = starter.mAddingToTask;
-        mReuseTask = starter.mReuseTask;
-
-        mNewTaskInfo = starter.mNewTaskInfo;
-        mNewTaskIntent = starter.mNewTaskIntent;
-        mSourceStack = starter.mSourceStack;
-
-        mTargetStack = starter.mTargetStack;
-        mMovedToFront = starter.mMovedToFront;
-        mNoAnimation = starter.mNoAnimation;
-        mKeepCurTransition = starter.mKeepCurTransition;
-        mAvoidMoveToFront = starter.mAvoidMoveToFront;
-
-        mVoiceSession = starter.mVoiceSession;
-        mVoiceInteractor = starter.mVoiceInteractor;
-
-        mIntentDelivered = starter.mIntentDelivered;
-
-        mRequest.set(starter.mRequest);
-    }
-
-    ActivityRecord getStartActivity() {
-        return mStartActivity;
-    }
-
-    boolean relatedToPackage(String packageName) {
-        return (mLastStartActivityRecord[0] != null
-                && packageName.equals(mLastStartActivityRecord[0].packageName))
-                || (mStartActivity != null && packageName.equals(mStartActivity.packageName));
-    }
-
-    /**
-     * Starts an activity based on the request parameters provided earlier.
-     * @return The starter result.
-     */
-    int execute() {
-        try {
-            // TODO(b/64750076): Look into passing request directly to these methods to allow
-            // for transactional diffs and preprocessing.
-            if (mRequest.mayWait) {
-                return startActivityMayWait(mRequest.caller, mRequest.callingUid,
-                        mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,
-                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
-                        mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
-                        mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
-                        mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
-                        mRequest.inTask, mRequest.reason,
-                        mRequest.allowPendingRemoteAnimationRegistryLookup,
-                        mRequest.originatingPendingIntent);
-            } else {
-                return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
-                        mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
-                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
-                        mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,
-                        mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,
-                        mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,
-                        mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
-                        mRequest.outActivity, mRequest.inTask, mRequest.reason,
-                        mRequest.allowPendingRemoteAnimationRegistryLookup,
-                        mRequest.originatingPendingIntent);
-            }
-        } finally {
-            onExecutionComplete();
-        }
-    }
-
-    /**
-     * Starts an activity based on the provided {@link ActivityRecord} and environment parameters.
-     * Note that this method is called internally as well as part of {@link #startActivity}.
-     *
-     * @return The start result.
-     */
-    int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
-            ActivityRecord[] outActivity) {
-        try {
-            return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
-                    doResume, options, inTask, outActivity);
-        } finally {
-            onExecutionComplete();
-        }
-    }
-
-    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
-            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
-            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
-            SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
-            ActivityRecord[] outActivity, TaskRecord inTask, String reason,
-            boolean allowPendingRemoteAnimationRegistryLookup,
-            PendingIntentRecord originatingPendingIntent) {
-
-        if (TextUtils.isEmpty(reason)) {
-            throw new IllegalArgumentException("Need to specify a reason.");
-        }
-        mLastStartReason = reason;
-        mLastStartActivityTimeMs = System.currentTimeMillis();
-        mLastStartActivityRecord[0] = null;
-
-        mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType,
-                aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
-                callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
-                options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
-                inTask, allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent);
-
-        if (outActivity != null) {
-            // mLastStartActivityRecord[0] is set in the call to startActivity above.
-            outActivity[0] = mLastStartActivityRecord[0];
-        }
-
-        return getExternalResult(mLastStartActivityResult);
-    }
-
-    static int getExternalResult(int result) {
-        // Aborted results are treated as successes externally, but we must track them internally.
-        return result != START_ABORTED ? result : START_SUCCESS;
-    }
-
-    /**
-     * Called when execution is complete. Sets state indicating completion and proceeds with
-     * recycling if appropriate.
-     */
-    private void onExecutionComplete() {
-        mController.onExecutionComplete(this);
-    }
-
-    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
-            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
-            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
-            SafeActivityOptions options,
-            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
-            TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
-            PendingIntentRecord originatingPendingIntent) {
-        int err = ActivityManager.START_SUCCESS;
-        // Pull the optional Ephemeral Installer-only bundle out of the options early.
-        final Bundle verificationBundle
-                = options != null ? options.popAppVerificationBundle() : null;
-
-        WindowProcessController callerApp = null;
-        if (caller != null) {
-            callerApp = mService.getProcessController(caller);
-            if (callerApp != null) {
-                callingPid = callerApp.getPid();
-                callingUid = callerApp.mInfo.uid;
-            } else {
-                Slog.w(TAG, "Unable to find app for caller " + caller
-                        + " (pid=" + callingPid + ") when starting: "
-                        + intent.toString());
-                err = ActivityManager.START_PERMISSION_DENIED;
-            }
-        }
-
-        final int userId = aInfo != null && aInfo.applicationInfo != null
-                ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
-
-        if (err == ActivityManager.START_SUCCESS) {
-            Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
-                    + "} from uid " + callingUid);
-        }
-
-        ActivityRecord sourceRecord = null;
-        ActivityRecord resultRecord = null;
-        if (resultTo != null) {
-            sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
-            if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
-                    "Will send result to " + resultTo + " " + sourceRecord);
-            if (sourceRecord != null) {
-                if (requestCode >= 0 && !sourceRecord.finishing) {
-                    resultRecord = sourceRecord;
-                }
-            }
-        }
-
-        final int launchFlags = intent.getFlags();
-
-        if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
-            // Transfer the result target from the source activity to the new
-            // one being started, including any failures.
-            if (requestCode >= 0) {
-                SafeActivityOptions.abort(options);
-                return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
-            }
-            resultRecord = sourceRecord.resultTo;
-            if (resultRecord != null && !resultRecord.isInStackLocked()) {
-                resultRecord = null;
-            }
-            resultWho = sourceRecord.resultWho;
-            requestCode = sourceRecord.requestCode;
-            sourceRecord.resultTo = null;
-            if (resultRecord != null) {
-                resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
-            }
-            if (sourceRecord.launchedFromUid == callingUid) {
-                // The new activity is being launched from the same uid as the previous
-                // activity in the flow, and asking to forward its result back to the
-                // previous.  In this case the activity is serving as a trampoline between
-                // the two, so we also want to update its launchedFromPackage to be the
-                // same as the previous activity.  Note that this is safe, since we know
-                // these two packages come from the same uid; the caller could just as
-                // well have supplied that same package name itself.  This specifially
-                // deals with the case of an intent picker/chooser being launched in the app
-                // flow to redirect to an activity picked by the user, where we want the final
-                // activity to consider it to have been launched by the previous app activity.
-                callingPackage = sourceRecord.launchedFromPackage;
-            }
-        }
-
-        if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
-            // We couldn't find a class that can handle the given Intent.
-            // That's the end of that!
-            err = ActivityManager.START_INTENT_NOT_RESOLVED;
-        }
-
-        if (err == ActivityManager.START_SUCCESS && aInfo == null) {
-            // We couldn't find the specific class specified in the Intent.
-            // Also the end of the line.
-            err = ActivityManager.START_CLASS_NOT_FOUND;
-        }
-
-        if (err == ActivityManager.START_SUCCESS && sourceRecord != null
-                && sourceRecord.getTask().voiceSession != null) {
-            // If this activity is being launched as part of a voice session, we need
-            // to ensure that it is safe to do so.  If the upcoming activity will also
-            // be part of the voice session, we can only launch it if it has explicitly
-            // said it supports the VOICE category, or it is a part of the calling app.
-            if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0
-                    && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
-                try {
-                    intent.addCategory(Intent.CATEGORY_VOICE);
-                    if (!mService.getPackageManager().activitySupportsIntent(
-                            intent.getComponent(), intent, resolvedType)) {
-                        Slog.w(TAG,
-                                "Activity being started in current voice task does not support voice: "
-                                        + intent);
-                        err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
-                    }
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failure checking voice capabilities", e);
-                    err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
-                }
-            }
-        }
-
-        if (err == ActivityManager.START_SUCCESS && voiceSession != null) {
-            // If the caller is starting a new voice session, just make sure the target
-            // is actually allowing it to run this way.
-            try {
-                if (!mService.getPackageManager().activitySupportsIntent(intent.getComponent(),
-                        intent, resolvedType)) {
-                    Slog.w(TAG,
-                            "Activity being started in new voice task does not support: "
-                                    + intent);
-                    err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
-                }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failure checking voice capabilities", e);
-                err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
-            }
-        }
-
-        final ActivityStack resultStack = resultRecord == null ? null : resultRecord.getStack();
-
-        if (err != START_SUCCESS) {
-            if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(
-                        -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
-            }
-            SafeActivityOptions.abort(options);
-            return err;
-        }
-
-        boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
-                requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
-                inTask != null, callerApp, resultRecord, resultStack);
-        abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
-                callingPid, resolvedType, aInfo.applicationInfo);
-
-        // Merge the two options bundles, while realCallerOptions takes precedence.
-        ActivityOptions checkedOptions = options != null
-                ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
-        if (allowPendingRemoteAnimationRegistryLookup) {
-            checkedOptions = mService.getActivityStartController()
-                    .getPendingRemoteAnimationRegistry()
-                    .overrideOptionsIfNeeded(callingPackage, checkedOptions);
-        }
-        if (mService.mController != null) {
-            try {
-                // The Intent we give to the watcher has the extra data
-                // stripped off, since it can contain private information.
-                Intent watchIntent = intent.cloneFilter();
-                abort |= !mService.mController.activityStarting(watchIntent,
-                        aInfo.applicationInfo.packageName);
-            } catch (RemoteException e) {
-                mService.mController = null;
-            }
-        }
-
-        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
-        if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid,
-                callingUid, checkedOptions)) {
-            // activity start was intercepted, e.g. because the target user is currently in quiet
-            // mode (turn off work) or the target application is suspended
-            intent = mInterceptor.mIntent;
-            rInfo = mInterceptor.mRInfo;
-            aInfo = mInterceptor.mAInfo;
-            resolvedType = mInterceptor.mResolvedType;
-            inTask = mInterceptor.mInTask;
-            callingPid = mInterceptor.mCallingPid;
-            callingUid = mInterceptor.mCallingUid;
-            checkedOptions = mInterceptor.mActivityOptions;
-        }
-
-        if (abort) {
-            if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
-                        RESULT_CANCELED, null);
-            }
-            // We pretend to the caller that it was really started, but
-            // they will just get a cancel result.
-            ActivityOptions.abort(checkedOptions);
-            return START_ABORTED;
-        }
-
-        // If permissions need a review before any of the app components can run, we
-        // launch the review activity and pass a pending intent to start the activity
-        // we are to launching now after the review is completed.
-        if (aInfo != null) {
-            if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
-                    aInfo.packageName, userId)) {
-                IIntentSender target = mService.getIntentSenderLocked(
-                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                        callingUid, userId, null, null, 0, new Intent[]{intent},
-                        new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
-                                | PendingIntent.FLAG_ONE_SHOT, null);
-
-                final int flags = intent.getFlags();
-                Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
-                newIntent.setFlags(flags
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
-                newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
-                if (resultRecord != null) {
-                    newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true);
-                }
-                intent = newIntent;
-
-                resolvedType = null;
-                callingUid = realCallingUid;
-                callingPid = realCallingPid;
-
-                rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, 0,
-                        computeResolveFilterUid(
-                                callingUid, realCallingUid, mRequest.filterCallingUid));
-                aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
-                        null /*profilerInfo*/);
-
-                if (DEBUG_PERMISSIONS_REVIEW) {
-                    final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
-                    Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
-                            true, false) + "} from uid " + callingUid + " on display "
-                            + (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId));
-                }
-            }
-        }
-
-        // If we have an ephemeral app, abort the process of launching the resolved intent.
-        // Instead, launch the ephemeral installer. Once the installer is finished, it
-        // starts either the intent we resolved here [on install error] or the ephemeral
-        // app [on install success].
-        if (rInfo != null && rInfo.auxiliaryInfo != null) {
-            intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent,
-                    callingPackage, verificationBundle, resolvedType, userId);
-            resolvedType = null;
-            callingUid = realCallingUid;
-            callingPid = realCallingPid;
-
-            aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
-        }
-
-        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
-                callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
-                resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
-                mSupervisor, checkedOptions, sourceRecord);
-        if (outActivity != null) {
-            outActivity[0] = r;
-        }
-
-        if (r.appTimeTracker == null && sourceRecord != null) {
-            // If the caller didn't specify an explicit time tracker, we want to continue
-            // tracking under any it has.
-            r.appTimeTracker = sourceRecord.appTimeTracker;
-        }
-
-        final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
-
-        // If we are starting an activity that is not from the same uid as the currently resumed
-        // one, check whether app switches are allowed.
-        if (voiceSession == null && (stack.getResumedActivity() == null
-                || stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {
-            if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
-                    realCallingPid, realCallingUid, "Activity start")) {
-                mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
-                        sourceRecord, startFlags, stack, callerApp));
-                ActivityOptions.abort(checkedOptions);
-                return ActivityManager.START_SWITCHES_CANCELED;
-            }
-        }
-
-        mService.onStartActivitySetDidAppSwitch();
-        mController.doPendingActivityLaunches(false);
-
-        maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r,
-                originatingPendingIntent);
-
-        return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
-                true /* doResume */, checkedOptions, inTask, outActivity);
-    }
-
-    private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,
-            Intent intent, WindowProcessController callerApp, ActivityRecord r,
-            PendingIntentRecord originatingPendingIntent) {
-        boolean callerAppHasForegroundActivity =
-                callerApp != null && callerApp.hasForegroundActivities();
-        if (!mService.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity
-                || r == null) {
-            // skip logging in this case
-            return;
-        }
-
-        try {
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "logActivityStart");
-            final int callingUidProcState = mService.getUidStateLocked(callingUid);
-            final boolean callingUidHasAnyVisibleWindow =
-                    mService.mWindowManager.isAnyWindowVisibleForUid(callingUid);
-            final int realCallingUidProcState = (callingUid == realCallingUid)
-                    ? callingUidProcState
-                    : mService.getUidStateLocked(realCallingUid);
-            final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
-                    ? callingUidHasAnyVisibleWindow
-                    : mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid);
-            final String targetPackage = r.packageName;
-            final int targetUid = (r.appInfo != null) ? r.appInfo.uid : -1;
-            final int targetUidProcState = mService.getUidStateLocked(targetUid);
-            final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
-                    ? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid)
-                    : false;
-            final String targetWhitelistTag = (targetUid != -1)
-                    ? mService.getPendingTempWhitelistTagForUidLocked(targetUid)
-                    : null;
-
-            mSupervisor.getActivityMetricsLogger().logActivityStart(intent, callerApp, r,
-                    callingUid, callingPackage, callingUidProcState,
-                    callingUidHasAnyVisibleWindow,
-                    realCallingUid, realCallingUidProcState,
-                    realCallingUidHasAnyVisibleWindow,
-                    targetUid, targetPackage, targetUidProcState,
-                    targetUidHasAnyVisibleWindow, targetWhitelistTag,
-                    (originatingPendingIntent != null));
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
-    /**
-     * Creates a launch intent for the given auxiliary resolution data.
-     */
-    private @NonNull Intent createLaunchIntent(@Nullable AuxiliaryResolveInfo auxiliaryResponse,
-            Intent originalIntent, String callingPackage, Bundle verificationBundle,
-            String resolvedType, int userId) {
-        if (auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo) {
-            // request phase two resolution
-            mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo(
-                    auxiliaryResponse, originalIntent, resolvedType, callingPackage,
-                    verificationBundle, userId);
-        }
-        return InstantAppResolver.buildEphemeralInstallerIntent(
-                originalIntent,
-                InstantAppResolver.sanitizeIntent(originalIntent),
-                auxiliaryResponse == null ? null : auxiliaryResponse.failureIntent,
-                callingPackage,
-                verificationBundle,
-                resolvedType,
-                userId,
-                auxiliaryResponse == null ? null : auxiliaryResponse.installFailureActivity,
-                auxiliaryResponse == null ? null : auxiliaryResponse.token,
-                auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo,
-                auxiliaryResponse == null ? null : auxiliaryResponse.filters);
-    }
-
-    void postStartActivityProcessing(ActivityRecord r, int result,
-            ActivityStack startedActivityStack) {
-        if (ActivityManager.isStartResultFatalError(result)) {
-            return;
-        }
-
-        // We're waiting for an activity launch to finish, but that activity simply
-        // brought another activity to front. We must also handle the case where the task is already
-        // in the front as a result of the trampoline activity being in the same task (it will be
-        // considered focused as the trampoline will be finished). Let startActivityMayWait() know
-        // about this, so it waits for the new activity to become visible instead.
-        mSupervisor.reportWaitingActivityLaunchedIfNeeded(r, result);
-
-        if (startedActivityStack == null) {
-            return;
-        }
-
-        final int clearTaskFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK;
-        boolean clearedTask = (mLaunchFlags & clearTaskFlags) == clearTaskFlags
-                && mReuseTask != null;
-        if (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP || clearedTask) {
-            // The activity was already running so it wasn't started, but either brought to the
-            // front or the new intent was delivered to it since it was already in front. Notify
-            // anyone interested in this piece of information.
-            switch (startedActivityStack.getWindowingMode()) {
-                case WINDOWING_MODE_PINNED:
-                    mService.getTaskChangeNotificationController().notifyPinnedActivityRestartAttempt(
-                            clearedTask);
-                    break;
-                case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-                    final ActivityStack homeStack =
-                            startedActivityStack.getDisplay().getHomeStack();
-                    if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) {
-                        mService.mWindowManager.showRecentApps();
-                    }
-                    break;
-            }
-        }
-    }
-
-    private int startActivityMayWait(IApplicationThread caller, int callingUid,
-            String callingPackage, Intent intent, String resolvedType,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            IBinder resultTo, String resultWho, int requestCode, int startFlags,
-            ProfilerInfo profilerInfo, WaitResult outResult,
-            Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
-            int userId, TaskRecord inTask, String reason,
-            boolean allowPendingRemoteAnimationRegistryLookup,
-            PendingIntentRecord originatingPendingIntent) {
-        // Refuse possible leaked file descriptors
-        if (intent != null && intent.hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-        mSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
-        boolean componentSpecified = intent.getComponent() != null;
-
-        final int realCallingPid = Binder.getCallingPid();
-        final int realCallingUid = Binder.getCallingUid();
-
-        int callingPid;
-        if (callingUid >= 0) {
-            callingPid = -1;
-        } else if (caller == null) {
-            callingPid = realCallingPid;
-            callingUid = realCallingUid;
-        } else {
-            callingPid = callingUid = -1;
-        }
-
-        // Save a copy in case ephemeral needs it
-        final Intent ephemeralIntent = new Intent(intent);
-        // Don't modify the client's object!
-        intent = new Intent(intent);
-        if (componentSpecified
-                && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
-                && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
-                && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
-                && mService.getPackageManagerInternalLocked()
-                        .isInstantAppInstallerComponent(intent.getComponent())) {
-            // intercept intents targeted directly to the ephemeral installer the
-            // ephemeral installer should never be started with a raw Intent; instead
-            // adjust the intent so it looks like a "normal" instant app launch
-            intent.setComponent(null /*component*/);
-            componentSpecified = false;
-        }
-
-        ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
-                0 /* matchFlags */,
-                        computeResolveFilterUid(
-                                callingUid, realCallingUid, mRequest.filterCallingUid));
-        if (rInfo == null) {
-            UserInfo userInfo = mSupervisor.getUserInfo(userId);
-            if (userInfo != null && userInfo.isManagedProfile()) {
-                // Special case for managed profiles, if attempting to launch non-cryto aware
-                // app in a locked managed profile from an unlocked parent allow it to resolve
-                // as user will be sent via confirm credentials to unlock the profile.
-                UserManager userManager = UserManager.get(mService.mContext);
-                boolean profileLockedAndParentUnlockingOrUnlocked = false;
-                long token = Binder.clearCallingIdentity();
-                try {
-                    UserInfo parent = userManager.getProfileParent(userId);
-                    profileLockedAndParentUnlockingOrUnlocked = (parent != null)
-                            && userManager.isUserUnlockingOrUnlocked(parent.id)
-                            && !userManager.isUserUnlockingOrUnlocked(userId);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
-                if (profileLockedAndParentUnlockingOrUnlocked) {
-                    rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
-                            PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                            computeResolveFilterUid(
-                                    callingUid, realCallingUid, mRequest.filterCallingUid));
-                }
-            }
-        }
-        // Collect information about the target of the Intent.
-        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
-
-        synchronized (mService.mGlobalLock) {
-            final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
-            stack.mConfigWillChange = globalConfig != null
-                    && mService.getGlobalConfiguration().diff(globalConfig) != 0;
-            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Starting activity when config will change = " + stack.mConfigWillChange);
-
-            final long origId = Binder.clearCallingIdentity();
-
-            if (aInfo != null &&
-                    (aInfo.applicationInfo.privateFlags
-                            & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 &&
-                    mService.mHasHeavyWeightFeature) {
-                // This may be a heavy-weight process!  Check to see if we already
-                // have another, different heavy-weight process running.
-                if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
-                    final WindowProcessController heavy = mService.mHeavyWeightProcess;
-                    if (heavy != null && (heavy.mInfo.uid != aInfo.applicationInfo.uid
-                            || !heavy.mName.equals(aInfo.processName))) {
-                        int appCallingUid = callingUid;
-                        if (caller != null) {
-                            WindowProcessController callerApp =
-                                    mService.getProcessController(caller);
-                            if (callerApp != null) {
-                                appCallingUid = callerApp.mInfo.uid;
-                            } else {
-                                Slog.w(TAG, "Unable to find app for caller " + caller
-                                        + " (pid=" + callingPid + ") when starting: "
-                                        + intent.toString());
-                                SafeActivityOptions.abort(options);
-                                return ActivityManager.START_PERMISSION_DENIED;
-                            }
-                        }
-
-                        IIntentSender target = mService.getIntentSenderLocked(
-                                ActivityManager.INTENT_SENDER_ACTIVITY, "android",
-                                appCallingUid, userId, null, null, 0, new Intent[] { intent },
-                                new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
-                                        | PendingIntent.FLAG_ONE_SHOT, null);
-
-                        Intent newIntent = new Intent();
-                        if (requestCode >= 0) {
-                            // Caller is requesting a result.
-                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
-                        }
-                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
-                                new IntentSender(target));
-                        heavy.updateIntentForHeavyWeightActivity(newIntent);
-                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
-                                aInfo.packageName);
-                        newIntent.setFlags(intent.getFlags());
-                        newIntent.setClassName("android",
-                                HeavyWeightSwitcherActivity.class.getName());
-                        intent = newIntent;
-                        resolvedType = null;
-                        caller = null;
-                        callingUid = Binder.getCallingUid();
-                        callingPid = Binder.getCallingPid();
-                        componentSpecified = true;
-                        rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId,
-                                0 /* matchFlags */, computeResolveFilterUid(
-                                        callingUid, realCallingUid, mRequest.filterCallingUid));
-                        aInfo = rInfo != null ? rInfo.activityInfo : null;
-                        if (aInfo != null) {
-                            aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
-                        }
-                    }
-                }
-            }
-
-            final ActivityRecord[] outRecord = new ActivityRecord[1];
-            int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
-                    voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
-                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
-                    ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
-                    allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent);
-
-            Binder.restoreCallingIdentity(origId);
-
-            if (stack.mConfigWillChange) {
-                // If the caller also wants to switch to a new configuration,
-                // do so now.  This allows a clean switch, as we are waiting
-                // for the current activity to pause (so we will not destroy
-                // it), and have not yet started the next activity.
-                mService.mAmInternal.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
-                        "updateConfiguration()");
-                stack.mConfigWillChange = false;
-                if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Updating to new configuration after starting activity.");
-                mService.updateConfigurationLocked(globalConfig, null, false);
-            }
-
-            // Notify ActivityMetricsLogger that the activity has launched. ActivityMetricsLogger
-            // will then wait for the windows to be drawn and populate WaitResult.
-            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]);
-            if (outResult != null) {
-                outResult.result = res;
-
-                final ActivityRecord r = outRecord[0];
-
-                switch(res) {
-                    case START_SUCCESS: {
-                        mSupervisor.mWaitingActivityLaunched.add(outResult);
-                        do {
-                            try {
-                                mService.mGlobalLock.wait();
-                            } catch (InterruptedException e) {
-                            }
-                        } while (outResult.result != START_TASK_TO_FRONT
-                                && !outResult.timeout && outResult.who == null);
-                        if (outResult.result == START_TASK_TO_FRONT) {
-                            res = START_TASK_TO_FRONT;
-                        }
-                        break;
-                    }
-                    case START_DELIVERED_TO_TOP: {
-                        outResult.timeout = false;
-                        outResult.who = r.realActivity;
-                        outResult.totalTime = 0;
-                        break;
-                    }
-                    case START_TASK_TO_FRONT: {
-                        // ActivityRecord may represent a different activity, but it should not be
-                        // in the resumed state.
-                        if (r.nowVisible && r.isState(RESUMED)) {
-                            outResult.timeout = false;
-                            outResult.who = r.realActivity;
-                            outResult.totalTime = 0;
-                        } else {
-                            final long startTimeMs = SystemClock.uptimeMillis();
-                            mSupervisor.waitActivityVisible(r.realActivity, outResult, startTimeMs);
-                            // Note: the timeout variable is not currently not ever set.
-                            do {
-                                try {
-                                    mService.mGlobalLock.wait();
-                                } catch (InterruptedException e) {
-                                }
-                            } while (!outResult.timeout && outResult.who == null);
-                        }
-                        break;
-                    }
-                }
-            }
-
-            return res;
-        }
-    }
-
-    /**
-     * Compute the logical UID based on which the package manager would filter
-     * app components i.e. based on which the instant app policy would be applied
-     * because it is the logical calling UID.
-     *
-     * @param customCallingUid The UID on whose behalf to make the call.
-     * @param actualCallingUid The UID actually making the call.
-     * @param filterCallingUid The UID to be used to filter for instant apps.
-     * @return The logical UID making the call.
-     */
-    static int computeResolveFilterUid(int customCallingUid, int actualCallingUid,
-            int filterCallingUid) {
-        return filterCallingUid != UserHandle.USER_NULL
-                ? filterCallingUid
-                : (customCallingUid >= 0 ? customCallingUid : actualCallingUid);
-    }
-
-    private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
-                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
-                ActivityRecord[] outActivity) {
-        int result = START_CANCELED;
-        final ActivityStack startedActivityStack;
-        try {
-            mService.mWindowManager.deferSurfaceLayout();
-            result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
-                    startFlags, doResume, options, inTask, outActivity);
-        } finally {
-            final ActivityStack currentStack = r.getStack();
-            startedActivityStack = currentStack != null ? currentStack : mTargetStack;
-
-            if (ActivityManager.isStartResultSuccessful(result)) {
-                if (startedActivityStack != null) {
-                    // If there is no state change (e.g. a resumed activity is reparented to
-                    // top of another display) to trigger a visibility/configuration checking,
-                    // we have to update the configuration for changing to different display.
-                    final ActivityRecord currentTop =
-                            startedActivityStack.topRunningActivityLocked();
-                    if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
-                        mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(),
-                                true /* markFrozenIfConfigChanged */, false /* deferResume */);
-                    }
-                }
-            } else {
-                // If we are not able to proceed, disassociate the activity from the task.
-                // Leaving an activity in an incomplete state can lead to issues, such as
-                // performing operations without a window container.
-                final ActivityStack stack = mStartActivity.getStack();
-                if (stack != null) {
-                    stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
-                            null /* intentResultData */, "startActivity", true /* oomAdj */);
-                }
-            }
-            mService.mWindowManager.continueSurfaceLayout();
-        }
-
-        postStartActivityProcessing(r, result, startedActivityStack);
-
-        return result;
-    }
-
-    // Note: This method should only be called from {@link startActivity}.
-    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
-            ActivityRecord[] outActivity) {
-
-        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
-                voiceInteractor);
-        final int preferredWindowingMode = mLaunchParams.mWindowingMode;
-
-        // Do not start home activity if it cannot be launched on preferred display. We are not
-        // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
-        // fallback to launch on other displays.
-        if (r.isActivityTypeHome()
-                && !mSupervisor.canStartHomeOnDisplay(r.info, mPreferredDisplayId)) {
-            Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId);
-            return START_CANCELED;
-        }
-
-        computeLaunchingTaskFlags();
-
-        computeSourceStack();
-
-        mIntent.setFlags(mLaunchFlags);
-
-        ActivityRecord reusedActivity = getReusableIntentActivity();
-
-        if (reusedActivity != null) {
-            // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
-            // still needs to be a lock task mode violation since the task gets cleared out and
-            // the device would otherwise leave the locked task.
-            if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(),
-                    (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
-                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
-                Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
-                return START_RETURN_LOCK_TASK_MODE_VIOLATION;
-            }
-
-            // True if we are clearing top and resetting of a standard (default) launch mode
-            // ({@code LAUNCH_MULTIPLE}) activity. The existing activity will be finished.
-            final boolean clearTopAndResetStandardLaunchMode =
-                    (mLaunchFlags & (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED))
-                            == (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
-                    && mLaunchMode == LAUNCH_MULTIPLE;
-
-            // If mStartActivity does not have a task associated with it, associate it with the
-            // reused activity's task. Do not do so if we're clearing top and resetting for a
-            // standard launchMode activity.
-            if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) {
-                mStartActivity.setTask(reusedActivity.getTask());
-            }
-
-            if (reusedActivity.getTask().intent == null) {
-                // This task was started because of movement of the activity based on affinity...
-                // Now that we are actually launching it, we can assign the base intent.
-                reusedActivity.getTask().setIntent(mStartActivity);
-            }
-
-            // This code path leads to delivering a new intent, we want to make sure we schedule it
-            // as the first operation, in case the activity will be resumed as a result of later
-            // operations.
-            if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
-                    || isDocumentLaunchesIntoExisting(mLaunchFlags)
-                    || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
-                final TaskRecord task = reusedActivity.getTask();
-
-                // In this situation we want to remove all activities from the task up to the one
-                // being started. In most cases this means we are resetting the task to its initial
-                // state.
-                final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
-                        mLaunchFlags);
-
-                // The above code can remove {@code reusedActivity} from the task, leading to the
-                // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
-                // task reference is needed in the call below to
-                // {@link setTargetStackAndMoveToFrontIfNeeded}.
-                if (reusedActivity.getTask() == null) {
-                    reusedActivity.setTask(task);
-                }
-
-                if (top != null) {
-                    if (top.frontOfTask) {
-                        // Activity aliases may mean we use different intents for the top activity,
-                        // so make sure the task now has the identity of the new intent.
-                        top.getTask().setIntent(mStartActivity);
-                    }
-                    deliverNewIntent(top);
-                }
-            }
-
-            mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);
-
-            reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
-
-            final ActivityRecord outResult =
-                    outActivity != null && outActivity.length > 0 ? outActivity[0] : null;
-
-            // When there is a reused activity and the current result is a trampoline activity,
-            // set the reused activity as the result.
-            if (outResult != null && (outResult.finishing || outResult.noDisplay)) {
-                outActivity[0] = reusedActivity;
-            }
-
-            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
-                // We don't need to start a new activity, and the client said not to do anything
-                // if that is the case, so this is it!  And for paranoia, make sure we have
-                // correctly resumed the top activity.
-                resumeTargetStackIfNeeded();
-                return START_RETURN_INTENT_TO_CALLER;
-            }
-
-            if (reusedActivity != null) {
-                setTaskFromIntentActivity(reusedActivity);
-
-                if (!mAddingToTask && mReuseTask == null) {
-                    // We didn't do anything...  but it was needed (a.k.a., client don't use that
-                    // intent!)  And for paranoia, make sure we have correctly resumed the top activity.
-
-                    resumeTargetStackIfNeeded();
-                    if (outActivity != null && outActivity.length > 0) {
-                        outActivity[0] = reusedActivity;
-                    }
-
-                    return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
-                }
-            }
-        }
-
-        if (mStartActivity.packageName == null) {
-            final ActivityStack sourceStack = mStartActivity.resultTo != null
-                    ? mStartActivity.resultTo.getStack() : null;
-            if (sourceStack != null) {
-                sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
-                        mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
-                        null /* data */);
-            }
-            ActivityOptions.abort(mOptions);
-            return START_CLASS_NOT_FOUND;
-        }
-
-        // If the activity being launched is the same as the one currently at the top, then
-        // we need to check if it should only be launched once.
-        final ActivityStack topStack = mSupervisor.getTopDisplayFocusedStack();
-        final ActivityRecord topFocused = topStack.getTopActivity();
-        final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
-        final boolean dontStart = top != null && mStartActivity.resultTo == null
-                && top.realActivity.equals(mStartActivity.realActivity)
-                && top.userId == mStartActivity.userId
-                && top.attachedToProcess()
-                && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
-                || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
-                // This allows home activity to automatically launch on secondary display when
-                // display added, if home was the top activity on default display, instead of
-                // sending new intent to the home activity on default display.
-                && (!top.isActivityTypeHome() || top.getDisplayId() == mPreferredDisplayId);
-        if (dontStart) {
-            // For paranoia, make sure we have correctly resumed the top activity.
-            topStack.mLastPausedActivity = null;
-            if (mDoResume) {
-                mSupervisor.resumeFocusedStacksTopActivitiesLocked();
-            }
-            ActivityOptions.abort(mOptions);
-            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
-                // We don't need to start a new activity, and the client said not to do
-                // anything if that is the case, so this is it!
-                return START_RETURN_INTENT_TO_CALLER;
-            }
-
-            deliverNewIntent(top);
-
-            // Don't use mStartActivity.task to show the toast. We're not starting a new activity
-            // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
-            mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
-                    mPreferredDisplayId, topStack);
-
-            return START_DELIVERED_TO_TOP;
-        }
-
-        boolean newTask = false;
-        final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
-                ? mSourceRecord.getTask() : null;
-
-        // Should this be considered a new task?
-        int result = START_SUCCESS;
-        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
-                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
-            newTask = true;
-            result = setTaskFromReuseOrCreateNewTask(taskToAffiliate);
-        } else if (mSourceRecord != null) {
-            result = setTaskFromSourceRecord();
-        } else if (mInTask != null) {
-            result = setTaskFromInTask();
-        } else {
-            // This not being started from an existing activity, and not part of a new task...
-            // just put it in the top task, though these days this case should never happen.
-            setTaskToCurrentTopOrCreateNewTask();
-        }
-        if (result != START_SUCCESS) {
-            return result;
-        }
-
-        mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
-                mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
-        mService.getPackageManagerInternalLocked().grantEphemeralAccess(
-                mStartActivity.userId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
-                UserHandle.getAppId(mCallingUid));
-        if (newTask) {
-            EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
-                    mStartActivity.getTask().taskId);
-        }
-        ActivityStack.logStartActivity(
-                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask());
-        mTargetStack.mLastPausedActivity = null;
-
-        mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity);
-
-        mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
-                mOptions);
-        if (mDoResume) {
-            final ActivityRecord topTaskActivity =
-                    mStartActivity.getTask().topRunningActivityLocked();
-            if (!mTargetStack.isFocusable()
-                    || (topTaskActivity != null && topTaskActivity.mTaskOverlay
-                    && mStartActivity != topTaskActivity)) {
-                // If the activity is not focusable, we can't resume it, but still would like to
-                // make sure it becomes visible as it starts (this will also trigger entry
-                // animation). An example of this are PIP activities.
-                // Also, we don't want to resume activities in a task that currently has an overlay
-                // as the starting activity just needs to be in the visible paused state until the
-                // over is removed.
-                mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                // Go ahead and tell window manager to execute app transition for this activity
-                // since the app transition will not be triggered through the resume channel.
-                mService.mWindowManager.executeAppTransition();
-            } else {
-                // If the target stack was not previously focusable (previous top running activity
-                // on that stack was not visible) then any prior calls to move the stack to the
-                // will not update the focused stack.  If starting the new activity now allows the
-                // task stack to be focusable, then ensure that we now update the focused stack
-                // accordingly.
-                if (mTargetStack.isFocusable()
-                        && !mSupervisor.isTopDisplayFocusedStack(mTargetStack)) {
-                    mTargetStack.moveToFront("startActivityUnchecked");
-                }
-                mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, mStartActivity,
-                        mOptions);
-            }
-        } else if (mStartActivity != null) {
-            mSupervisor.mRecentTasks.add(mStartActivity.getTask());
-        }
-        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
-
-        mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
-                mPreferredDisplayId, mTargetStack);
-
-        return START_SUCCESS;
-    }
-
-    /**
-     * Resets the {@link ActivityStarter} state.
-     * @param clearRequest whether the request should be reset to default values.
-     */
-    void reset(boolean clearRequest) {
-        mStartActivity = null;
-        mIntent = null;
-        mCallingUid = -1;
-        mOptions = null;
-
-        mLaunchTaskBehind = false;
-        mLaunchFlags = 0;
-        mLaunchMode = INVALID_LAUNCH_MODE;
-
-        mLaunchParams.reset();
-
-        mNotTop = null;
-        mDoResume = false;
-        mStartFlags = 0;
-        mSourceRecord = null;
-        mPreferredDisplayId = INVALID_DISPLAY;
-
-        mInTask = null;
-        mAddingToTask = false;
-        mReuseTask = null;
-
-        mNewTaskInfo = null;
-        mNewTaskIntent = null;
-        mSourceStack = null;
-
-        mTargetStack = null;
-        mMovedToFront = false;
-        mNoAnimation = false;
-        mKeepCurTransition = false;
-        mAvoidMoveToFront = false;
-
-        mVoiceSession = null;
-        mVoiceInteractor = null;
-
-        mIntentDelivered = false;
-
-        if (clearRequest) {
-            mRequest.reset();
-        }
-    }
-
-    private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
-            boolean doResume, int startFlags, ActivityRecord sourceRecord,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
-        reset(false /* clearRequest */);
-
-        mStartActivity = r;
-        mIntent = r.intent;
-        mOptions = options;
-        mCallingUid = r.launchedFromUid;
-        mSourceRecord = sourceRecord;
-        mVoiceSession = voiceSession;
-        mVoiceInteractor = voiceInteractor;
-
-        mLaunchParams.reset();
-
-        mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r,
-                sourceRecord, options, mLaunchParams);
-
-        if (mLaunchParams.hasPreferredDisplay()) {
-            mPreferredDisplayId = mLaunchParams.mPreferredDisplayId;
-        } else {
-            mPreferredDisplayId = DEFAULT_DISPLAY;
-        }
-
-        mLaunchMode = r.launchMode;
-
-        mLaunchFlags = adjustLaunchFlagsToDocumentMode(
-                r, LAUNCH_SINGLE_INSTANCE == mLaunchMode,
-                LAUNCH_SINGLE_TASK == mLaunchMode, mIntent.getFlags());
-        mLaunchTaskBehind = r.mLaunchTaskBehind
-                && !isLaunchModeOneOf(LAUNCH_SINGLE_TASK, LAUNCH_SINGLE_INSTANCE)
-                && (mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
-
-        sendNewTaskResultRequestIfNeeded();
-
-        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
-            mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
-        }
-
-        // If we are actually going to launch in to a new task, there are some cases where
-        // we further want to do multiple task.
-        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
-            if (mLaunchTaskBehind
-                    || r.info.documentLaunchMode == DOCUMENT_LAUNCH_ALWAYS) {
-                mLaunchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK;
-            }
-        }
-
-        // We'll invoke onUserLeaving before onPause only if the launching
-        // activity did not explicitly state that this is an automated launch.
-        mSupervisor.mUserLeaving = (mLaunchFlags & FLAG_ACTIVITY_NO_USER_ACTION) == 0;
-        if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
-                "startActivity() => mUserLeaving=" + mSupervisor.mUserLeaving);
-
-        // If the caller has asked not to resume at this point, we make note
-        // of this in the record so that we can skip it when trying to find
-        // the top running activity.
-        mDoResume = doResume;
-        if (!doResume || !r.okToShowLocked()) {
-            r.delayedResume = true;
-            mDoResume = false;
-        }
-
-        if (mOptions != null) {
-            if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
-                r.mTaskOverlay = true;
-                if (!mOptions.canTaskOverlayResume()) {
-                    final TaskRecord task = mSupervisor.anyTaskForIdLocked(
-                            mOptions.getLaunchTaskId());
-                    final ActivityRecord top = task != null ? task.getTopActivity() : null;
-                    if (top != null && !top.isState(RESUMED)) {
-
-                        // The caller specifies that we'd like to be avoided to be moved to the
-                        // front, so be it!
-                        mDoResume = false;
-                        mAvoidMoveToFront = true;
-                    }
-                }
-            } else if (mOptions.getAvoidMoveToFront()) {
-                mDoResume = false;
-                mAvoidMoveToFront = true;
-            }
-        }
-
-        mNotTop = (mLaunchFlags & FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
-
-        mInTask = inTask;
-        // In some flows in to this function, we retrieve the task record and hold on to it
-        // without a lock before calling back in to here...  so the task at this point may
-        // not actually be in recents.  Check for that, and if it isn't in recents just
-        // consider it invalid.
-        if (inTask != null && !inTask.inRecents) {
-            Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
-            mInTask = null;
-        }
-
-        mStartFlags = startFlags;
-        // If the onlyIfNeeded flag is set, then we can do this if the activity being launched
-        // is the same as the one making the call...  or, as a special case, if we do not know
-        // the caller then we count the current top activity as the caller.
-        if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
-            ActivityRecord checkedCaller = sourceRecord;
-            if (checkedCaller == null) {
-                checkedCaller = mSupervisor.getTopDisplayFocusedStack()
-                        .topRunningNonDelayedActivityLocked(mNotTop);
-            }
-            if (!checkedCaller.realActivity.equals(r.realActivity)) {
-                // Caller is not the same as launcher, so always needed.
-                mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED;
-            }
-        }
-
-        mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
-    }
-
-    private void sendNewTaskResultRequestIfNeeded() {
-        final ActivityStack sourceStack = mStartActivity.resultTo != null
-                ? mStartActivity.resultTo.getStack() : null;
-        if (sourceStack != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
-            // For whatever reason this activity is being launched into a new task...
-            // yet the caller has requested a result back.  Well, that is pretty messed up,
-            // so instead immediately send back a cancel and let the new task continue launched
-            // as normal without a dependency on its originator.
-            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
-            sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
-                    mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
-                    null /* data */);
-            mStartActivity.resultTo = null;
-        }
-    }
-
-    private void computeLaunchingTaskFlags() {
-        // If the caller is not coming from another activity, but has given us an explicit task into
-        // which they would like us to launch the new activity, then let's see about doing that.
-        if (mSourceRecord == null && mInTask != null && mInTask.getStack() != null) {
-            final Intent baseIntent = mInTask.getBaseIntent();
-            final ActivityRecord root = mInTask.getRootActivity();
-            if (baseIntent == null) {
-                ActivityOptions.abort(mOptions);
-                throw new IllegalArgumentException("Launching into task without base intent: "
-                        + mInTask);
-            }
-
-            // If this task is empty, then we are adding the first activity -- it
-            // determines the root, and must be launching as a NEW_TASK.
-            if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
-                if (!baseIntent.getComponent().equals(mStartActivity.intent.getComponent())) {
-                    ActivityOptions.abort(mOptions);
-                    throw new IllegalArgumentException("Trying to launch singleInstance/Task "
-                            + mStartActivity + " into different task " + mInTask);
-                }
-                if (root != null) {
-                    ActivityOptions.abort(mOptions);
-                    throw new IllegalArgumentException("Caller with mInTask " + mInTask
-                            + " has root " + root + " but target is singleInstance/Task");
-                }
-            }
-
-            // If task is empty, then adopt the interesting intent launch flags in to the
-            // activity being started.
-            if (root == null) {
-                final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK
-                        | FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS;
-                mLaunchFlags = (mLaunchFlags & ~flagsOfInterest)
-                        | (baseIntent.getFlags() & flagsOfInterest);
-                mIntent.setFlags(mLaunchFlags);
-                mInTask.setIntent(mStartActivity);
-                mAddingToTask = true;
-
-                // If the task is not empty and the caller is asking to start it as the root of
-                // a new task, then we don't actually want to start this on the task. We will
-                // bring the task to the front, and possibly give it a new intent.
-            } else if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
-                mAddingToTask = false;
-
-            } else {
-                mAddingToTask = true;
-            }
-
-            mReuseTask = mInTask;
-        } else {
-            mInTask = null;
-            // Launch ResolverActivity in the source task, so that it stays in the task bounds
-            // when in freeform workspace.
-            // Also put noDisplay activities in the source task. These by itself can be placed
-            // in any task/stack, however it could launch other activities like ResolverActivity,
-            // and we want those to stay in the original task.
-            if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
-                    && mSourceRecord.inFreeformWindowingMode())  {
-                mAddingToTask = true;
-            }
-        }
-
-        if (mInTask == null) {
-            if (mSourceRecord == null) {
-                // This activity is not being started from another...  in this
-                // case we -always- start a new task.
-                if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
-                    Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
-                            "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
-                    mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
-                }
-            } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
-                // The original activity who is starting us is running as a single
-                // instance...  this new activity it is starting must go on its
-                // own task.
-                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
-            } else if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
-                // The activity being started is a single instance...  it always
-                // gets launched into its own task.
-                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
-            }
-        }
-    }
-
-    private void computeSourceStack() {
-        if (mSourceRecord == null) {
-            mSourceStack = null;
-            return;
-        }
-        if (!mSourceRecord.finishing) {
-            mSourceStack = mSourceRecord.getStack();
-            return;
-        }
-
-        // If the source is finishing, we can't further count it as our source. This is because the
-        // task it is associated with may now be empty and on its way out, so we don't want to
-        // blindly throw it in to that task.  Instead we will take the NEW_TASK flow and try to find
-        // a task for it. But save the task information so it can be used when creating the new task.
-        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) {
-            Slog.w(TAG, "startActivity called from finishing " + mSourceRecord
-                    + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
-            mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
-            mNewTaskInfo = mSourceRecord.info;
-
-            // It is not guaranteed that the source record will have a task associated with it. For,
-            // example, if this method is being called for processing a pending activity launch, it
-            // is possible that the activity has been removed from the task after the launch was
-            // enqueued.
-            final TaskRecord sourceTask = mSourceRecord.getTask();
-            mNewTaskIntent = sourceTask != null ? sourceTask.intent : null;
-        }
-        mSourceRecord = null;
-        mSourceStack = null;
-    }
-
-    /**
-     * Decide whether the new activity should be inserted into an existing task. Returns null
-     * if not or an ActivityRecord with the task into which the new activity should be added.
-     */
-    private ActivityRecord getReusableIntentActivity() {
-        // We may want to try to place the new activity in to an existing task.  We always
-        // do this if the target activity is singleTask or singleInstance; we will also do
-        // this if NEW_TASK has been requested, and there is not an additional qualifier telling
-        // us to still place it in a new task: multi task, always doc mode, or being asked to
-        // launch this as a new task behind the current one.
-        boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
-                (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
-                || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);
-        // If bring to front is requested, and no result is requested and we have not been given
-        // an explicit task to launch in to, and we can find a task that was started with this
-        // same component, then instead of launching bring that one to the front.
-        putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
-        ActivityRecord intentActivity = null;
-        if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
-            final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
-            intentActivity = task != null ? task.getTopActivity() : null;
-        } else if (putIntoExistingTask) {
-            if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
-                // There can be one and only one instance of single instance activity in the
-                // history, and it is always in its own unique task, so we do a special search.
-               intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
-                       mStartActivity.isActivityTypeHome());
-            } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
-                // For the launch adjacent case we only want to put the activity in an existing
-                // task if the activity already exists in the history.
-                intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
-                        !(LAUNCH_SINGLE_TASK == mLaunchMode));
-            } else {
-                // Otherwise find the best task to put the activity in.
-                intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
-            }
-        }
-
-        if (mStartActivity.isActivityTypeHome() && intentActivity != null
-                && intentActivity.getDisplayId() != mPreferredDisplayId) {
-            // Do not reuse home activity on other displays.
-            intentActivity = null;
-        }
-
-        return intentActivity;
-    }
-
-    /**
-     * Figure out which task and activity to bring to front when we have found an existing matching
-     * activity record in history. May also clear the task if needed.
-     * @param intentActivity Existing matching activity.
-     * @return {@link ActivityRecord} brought to front.
-     */
-    private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) {
-        mTargetStack = intentActivity.getStack();
-        mTargetStack.mLastPausedActivity = null;
-        // If the target task is not in the front, then we need to bring it to the front...
-        // except...  well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
-        // the same behavior as if a new instance was being started, which means not bringing it
-        // to the front if the caller is not itself in the front.
-        final boolean differentTopTask;
-        if (mPreferredDisplayId == mTargetStack.mDisplayId) {
-            final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack();
-            final ActivityRecord curTop = (focusStack == null)
-                    ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
-            final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
-            differentTopTask = topTask != null
-                    && (topTask != intentActivity.getTask() || topTask != focusStack.topTask());
-        } else {
-            // The existing task should always be different from those in other displays.
-            differentTopTask = true;
-        }
-
-        if (differentTopTask && !mAvoidMoveToFront) {
-            mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
-            if (mSourceRecord == null || (mSourceStack.getTopActivity() != null &&
-                    mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) {
-                // We really do want to push this one into the user's face, right now.
-                if (mLaunchTaskBehind && mSourceRecord != null) {
-                    intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
-                }
-
-                // If the launch flags carry both NEW_TASK and CLEAR_TASK, the task's activities
-                // will be cleared soon by ActivityStarter in setTaskFromIntentActivity().
-                // So no point resuming any of the activities here, it just wastes one extra
-                // resuming, plus enter AND exit transitions.
-                // Here we only want to bring the target stack forward. Transition will be applied
-                // to the new activity that's started after the old ones are gone.
-                final boolean willClearTask =
-                        (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
-                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
-                if (!willClearTask) {
-                    final ActivityStack launchStack = getLaunchStack(
-                            mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions);
-                    final TaskRecord intentTask = intentActivity.getTask();
-                    if (launchStack == null || launchStack == mTargetStack) {
-                        // We only want to move to the front, if we aren't going to launch on a
-                        // different stack. If we launch on a different stack, we will put the
-                        // task on top there.
-                        mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
-                                mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
-                        mMovedToFront = true;
-                    } else if (launchStack.inSplitScreenWindowingMode()) {
-                        if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
-                            // If we want to launch adjacent and mTargetStack is not the computed
-                            // launch stack - move task to top of computed stack.
-                            intentTask.reparent(launchStack, ON_TOP,
-                                    REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
-                                    "launchToSide");
-                        } else {
-                            // TODO: This should be reevaluated in MW v2.
-                            // We choose to move task to front instead of launching it adjacent
-                            // when specific stack was requested explicitly and it appeared to be
-                            // adjacent stack, but FLAG_ACTIVITY_LAUNCH_ADJACENT was not set.
-                            mTargetStack.moveTaskToFrontLocked(intentTask,
-                                    mNoAnimation, mOptions, mStartActivity.appTimeTracker,
-                                    "bringToFrontInsteadOfAdjacentLaunch");
-                        }
-                        mMovedToFront = launchStack != launchStack.getDisplay()
-                                .getTopStackInWindowingMode(launchStack.getWindowingMode());
-                    } else if (launchStack.mDisplayId != mTargetStack.mDisplayId) {
-                        // Target and computed stacks are on different displays and we've
-                        // found a matching task - move the existing instance to that display and
-                        // move it to front.
-                        intentActivity.getTask().reparent(launchStack, ON_TOP,
-                                REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
-                                "reparentToDisplay");
-                        mMovedToFront = true;
-                    } else if (launchStack.isActivityTypeHome()
-                            && !mTargetStack.isActivityTypeHome()) {
-                        // It is possible for the home activity to be in another stack initially.
-                        // For example, the activity may have been initially started with an intent
-                        // which placed it in the fullscreen stack. To ensure the proper handling of
-                        // the activity based on home stack assumptions, we must move it over.
-                        intentActivity.getTask().reparent(launchStack, ON_TOP,
-                                REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
-                                "reparentingHome");
-                        mMovedToFront = true;
-                    }
-                    mOptions = null;
-
-                    // We are moving a task to the front, use starting window to hide initial drawn
-                    // delay.
-                    intentActivity.showStartingWindow(null /* prev */, false /* newTask */,
-                            true /* taskSwitch */);
-                }
-            }
-        }
-        // Need to update mTargetStack because if task was moved out of it, the original stack may
-        // be destroyed.
-        mTargetStack = intentActivity.getStack();
-        if (!mMovedToFront && mDoResume) {
-            if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack
-                    + " from " + intentActivity);
-            mTargetStack.moveToFront("intentActivityFound");
-        }
-
-        mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
-                WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack);
-
-        // If the caller has requested that the target task be reset, then do so.
-        if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
-            return mTargetStack.resetTaskIfNeededLocked(intentActivity, mStartActivity);
-        }
-        return intentActivity;
-    }
-
-    private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
-        if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
-                == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
-            // The caller has requested to completely replace any existing task with its new
-            // activity. Well that should not be too hard...
-            // Note: we must persist the {@link TaskRecord} first as intentActivity could be
-            // removed from calling performClearTaskLocked (For example, if it is being brought out
-            // of history or if it is finished immediately), thus disassociating the task. Also note
-            // that mReuseTask is reset as a result of {@link TaskRecord#performClearTaskLocked}
-            // launching another activity.
-            // TODO(b/36119896):  We shouldn't trigger activity launches in this path since we are
-            // already launching one.
-            final TaskRecord task = intentActivity.getTask();
-            task.performClearTaskLocked();
-            mReuseTask = task;
-            mReuseTask.setIntent(mStartActivity);
-        } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
-                || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
-            ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
-                    mLaunchFlags);
-            if (top == null) {
-                // A special case: we need to start the activity because it is not currently
-                // running, and the caller has asked to clear the current task to have this
-                // activity at the top.
-                mAddingToTask = true;
-
-                // We are no longer placing the activity in the task we previously thought we were.
-                mStartActivity.setTask(null);
-                // Now pretend like this activity is being started by the top of its task, so it
-                // is put in the right place.
-                mSourceRecord = intentActivity;
-                final TaskRecord task = mSourceRecord.getTask();
-                if (task != null && task.getStack() == null) {
-                    // Target stack got cleared when we all activities were removed above.
-                    // Go ahead and reset it.
-                    mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
-                            mLaunchFlags, mOptions);
-                    mTargetStack.addTask(task,
-                            !mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
-                }
-            }
-        } else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) {
-            // In this case the top activity on the task is the same as the one being launched,
-            // so we take that as a request to bring the task to the foreground. If the top
-            // activity in the task is the root activity, deliver this new intent to it if it
-            // desires.
-            if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
-                        || LAUNCH_SINGLE_TOP == mLaunchMode)
-                    && intentActivity.realActivity.equals(mStartActivity.realActivity)) {
-                if (intentActivity.frontOfTask) {
-                    intentActivity.getTask().setIntent(mStartActivity);
-                }
-                deliverNewIntent(intentActivity);
-            } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
-                // In this case we are launching the root activity of the task, but with a
-                // different intent. We should start a new instance on top.
-                mAddingToTask = true;
-                mSourceRecord = intentActivity;
-            }
-        } else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
-            // In this case an activity is being launched in to an existing task, without
-            // resetting that task. This is typically the situation of launching an activity
-            // from a notification or shortcut. We want to place the new activity on top of the
-            // current task.
-            mAddingToTask = true;
-            mSourceRecord = intentActivity;
-        } else if (!intentActivity.getTask().rootWasReset) {
-            // In this case we are launching into an existing task that has not yet been started
-            // from its front door. The current task has been brought to the front. Ideally,
-            // we'd probably like to place this new task at the bottom of its stack, but that's
-            // a little hard to do with the current organization of the code so for now we'll
-            // just drop it.
-            intentActivity.getTask().setIntent(mStartActivity);
-        }
-    }
-
-    private void resumeTargetStackIfNeeded() {
-        if (mDoResume) {
-            mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, null, mOptions);
-        } else {
-            ActivityOptions.abort(mOptions);
-        }
-        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
-    }
-
-    private int setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) {
-        mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);
-
-        // Do no move the target stack to front yet, as we might bail if
-        // isLockTaskModeViolation fails below.
-
-        if (mReuseTask == null) {
-            final TaskRecord task = mTargetStack.createTaskRecord(
-                    mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
-                    mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
-                    mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
-                    mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord,
-                    mOptions);
-            addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
-            updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);
-
-            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
-                    + " in new task " + mStartActivity.getTask());
-        } else {
-            addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
-        }
-
-        if (taskToAffiliate != null) {
-            mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
-        }
-
-        if (mService.getLockTaskController().isLockTaskModeViolation(mStartActivity.getTask())) {
-            Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
-            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
-        }
-
-        if (mDoResume) {
-            mTargetStack.moveToFront("reuseOrNewTask");
-        }
-        return START_SUCCESS;
-    }
-
-    private void deliverNewIntent(ActivityRecord activity) {
-        if (mIntentDelivered) {
-            return;
-        }
-
-        ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTask());
-        activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
-                mStartActivity.launchedFromPackage);
-        mIntentDelivered = true;
-    }
-
-    private int setTaskFromSourceRecord() {
-        if (mService.getLockTaskController().isLockTaskModeViolation(mSourceRecord.getTask())) {
-            Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
-            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
-        }
-
-        final TaskRecord sourceTask = mSourceRecord.getTask();
-        final ActivityStack sourceStack = mSourceRecord.getStack();
-        // We only want to allow changing stack in two cases:
-        // 1. If the target task is not the top one. Otherwise we would move the launching task to
-        //    the other side, rather than show two side by side.
-        // 2. If activity is not allowed on target display.
-        final int targetDisplayId = mTargetStack != null ? mTargetStack.mDisplayId
-                : sourceStack.mDisplayId;
-        final boolean moveStackAllowed = sourceStack.topTask() != sourceTask
-                || !mStartActivity.canBeLaunchedOnDisplay(targetDisplayId);
-        if (moveStackAllowed) {
-            mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.getTask(),
-                    mOptions);
-            // If target stack is not found now - we can't just rely on the source stack, as it may
-            // be not suitable. Let's check other displays.
-            if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) {
-                // Can't use target display, lets find a stack on the source display.
-                mTargetStack = mSupervisor.getValidLaunchStackOnDisplay(
-                        sourceStack.mDisplayId, mStartActivity, mOptions);
-            }
-            if (mTargetStack == null) {
-                // There are no suitable stacks on the target and source display(s). Look on all
-                // displays.
-                mTargetStack = mSupervisor.getNextValidLaunchStackLocked(
-                        mStartActivity, -1 /* currentFocus */);
-            }
-        }
-
-        if (mTargetStack == null) {
-            mTargetStack = sourceStack;
-        } else if (mTargetStack != sourceStack) {
-            sourceTask.reparent(mTargetStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
-                    DEFER_RESUME, "launchToSide");
-        }
-
-        final TaskRecord topTask = mTargetStack.topTask();
-        if (topTask != sourceTask && !mAvoidMoveToFront) {
-            mTargetStack.moveTaskToFrontLocked(sourceTask, mNoAnimation, mOptions,
-                    mStartActivity.appTimeTracker, "sourceTaskToFront");
-        } else if (mDoResume) {
-            mTargetStack.moveToFront("sourceStackToFront");
-        }
-
-        if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0) {
-            // In this case, we are adding the activity to an existing task, but the caller has
-            // asked to clear that task if the activity is already running.
-            ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
-            mKeepCurTransition = true;
-            if (top != null) {
-                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
-                deliverNewIntent(top);
-                // For paranoia, make sure we have correctly resumed the top activity.
-                mTargetStack.mLastPausedActivity = null;
-                if (mDoResume) {
-                    mSupervisor.resumeFocusedStacksTopActivitiesLocked();
-                }
-                ActivityOptions.abort(mOptions);
-                return START_DELIVERED_TO_TOP;
-            }
-        } else if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
-            // In this case, we are launching an activity in our own task that may already be
-            // running somewhere in the history, and we want to shuffle it to the front of the
-            // stack if so.
-            final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity);
-            if (top != null) {
-                final TaskRecord task = top.getTask();
-                task.moveActivityToFrontLocked(top);
-                top.updateOptionsLocked(mOptions);
-                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
-                deliverNewIntent(top);
-                mTargetStack.mLastPausedActivity = null;
-                if (mDoResume) {
-                    mSupervisor.resumeFocusedStacksTopActivitiesLocked();
-                }
-                return START_DELIVERED_TO_TOP;
-            }
-        }
-
-        // An existing activity is starting this new activity, so we want to keep the new one in
-        // the same task as the one that is starting it.
-        addOrReparentStartingActivity(sourceTask, "setTaskFromSourceRecord");
-        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
-                + " in existing task " + mStartActivity.getTask() + " from source " + mSourceRecord);
-        return START_SUCCESS;
-    }
-
-    private int setTaskFromInTask() {
-        // The caller is asking that the new activity be started in an explicit
-        // task it has provided to us.
-        if (mService.getLockTaskController().isLockTaskModeViolation(mInTask)) {
-            Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
-            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
-        }
-
-        mTargetStack = mInTask.getStack();
-
-        // Check whether we should actually launch the new activity in to the task,
-        // or just reuse the current activity on top.
-        ActivityRecord top = mInTask.getTopActivity();
-        if (top != null && top.realActivity.equals(mStartActivity.realActivity)
-                && top.userId == mStartActivity.userId) {
-            if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
-                    || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK)) {
-                mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions,
-                        mStartActivity.appTimeTracker, "inTaskToFront");
-                if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
-                    // We don't need to start a new activity, and the client said not to do
-                    // anything if that is the case, so this is it!
-                    return START_RETURN_INTENT_TO_CALLER;
-                }
-                deliverNewIntent(top);
-                return START_DELIVERED_TO_TOP;
-            }
-        }
-
-        if (!mAddingToTask) {
-            mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions,
-                    mStartActivity.appTimeTracker, "inTaskToFront");
-            // We don't actually want to have this activity added to the task, so just
-            // stop here but still tell the caller that we consumed the intent.
-            ActivityOptions.abort(mOptions);
-            return START_TASK_TO_FRONT;
-        }
-
-        if (!mLaunchParams.mBounds.isEmpty()) {
-            // TODO: Shouldn't we already know what stack to use by the time we get here?
-            ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
-            if (stack != mInTask.getStack()) {
-                mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
-                        DEFER_RESUME, "inTaskToFront");
-                mTargetStack = mInTask.getStack();
-            }
-
-            updateBounds(mInTask, mLaunchParams.mBounds);
-        }
-
-        mTargetStack.moveTaskToFrontLocked(
-                mInTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "inTaskToFront");
-
-        addOrReparentStartingActivity(mInTask, "setTaskFromInTask");
-        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
-                + " in explicit task " + mStartActivity.getTask());
-
-        return START_SUCCESS;
-    }
-
-    @VisibleForTesting
-    void updateBounds(TaskRecord task, Rect bounds) {
-        if (bounds.isEmpty()) {
-            return;
-        }
-
-        final ActivityStack stack = task.getStack();
-        if (stack != null && stack.resizeStackWithLaunchBounds()) {
-            mService.resizeStack(
-                    stack.mStackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
-        } else {
-            task.updateOverrideConfiguration(bounds);
-        }
-    }
-
-    private void setTaskToCurrentTopOrCreateNewTask() {
-        mTargetStack = computeStackFocus(mStartActivity, false, mLaunchFlags, mOptions);
-        if (mDoResume) {
-            mTargetStack.moveToFront("addingToTopTask");
-        }
-        final ActivityRecord prev = mTargetStack.getTopActivity();
-        final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord(
-                mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info,
-                mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions);
-        addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask");
-        mTargetStack.positionChildWindowContainerAtTop(task);
-        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
-                + " in new guessed " + mStartActivity.getTask());
-    }
-
-    private void addOrReparentStartingActivity(TaskRecord parent, String reason) {
-        if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
-            parent.addActivityToTop(mStartActivity);
-        } else {
-            mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason);
-        }
-    }
-
-    private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance,
-            boolean launchSingleTask, int launchFlags) {
-        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
-                (launchSingleInstance || launchSingleTask)) {
-            // We have a conflict between the Intent and the Activity manifest, manifest wins.
-            Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
-                    "\"singleInstance\" or \"singleTask\"");
-            launchFlags &=
-                    ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
-        } else {
-            switch (r.info.documentLaunchMode) {
-                case ActivityInfo.DOCUMENT_LAUNCH_NONE:
-                    break;
-                case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
-                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-                    break;
-                case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
-                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-                    break;
-                case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
-                    launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
-                    break;
-            }
-        }
-        return launchFlags;
-    }
-
-    private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags,
-            ActivityOptions aOptions) {
-        final TaskRecord task = r.getTask();
-        ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
-        if (stack != null) {
-            return stack;
-        }
-
-        final ActivityStack currentStack = task != null ? task.getStack() : null;
-        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
-        if (currentStack != null) {
-            if (focusedStack != currentStack) {
-                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                        "computeStackFocus: Setting " + "focused stack to r=" + r
-                                + " task=" + task);
-            } else {
-                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                        "computeStackFocus: Focused stack already=" + focusedStack);
-            }
-            return currentStack;
-        }
-
-        if (canLaunchIntoFocusedStack(r, newTask)) {
-            if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                    "computeStackFocus: Have a focused stack=" + focusedStack);
-            return focusedStack;
-        }
-
-        if (mPreferredDisplayId != DEFAULT_DISPLAY) {
-            // Try to put the activity in a stack on a secondary display.
-            stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r, aOptions);
-            if (stack == null) {
-                // If source display is not suitable - look for topmost valid stack in the system.
-                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                        "computeStackFocus: Can't launch on mPreferredDisplayId="
-                                + mPreferredDisplayId + ", looking on all displays.");
-                stack = mSupervisor.getNextValidLaunchStackLocked(r, mPreferredDisplayId);
-            }
-        }
-        if (stack == null) {
-            stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
-        }
-        if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
-                + r + " stackId=" + stack.mStackId);
-        return stack;
-    }
-
-    /** Check if provided activity record can launch in currently focused stack. */
-    // TODO: This method can probably be consolidated into getLaunchStack() below.
-    private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
-        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
-        final boolean canUseFocusedStack;
-        if (focusedStack.isActivityTypeAssistant()) {
-            canUseFocusedStack = r.isActivityTypeAssistant();
-        } else {
-            switch (focusedStack.getWindowingMode()) {
-                case WINDOWING_MODE_FULLSCREEN:
-                    // The fullscreen stack can contain any task regardless of if the task is
-                    // resizeable or not. So, we let the task go in the fullscreen task if it is the
-                    // focus stack.
-                    canUseFocusedStack = true;
-                    break;
-                case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-                case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
-                    // Any activity which supports split screen can go in the docked stack.
-                    canUseFocusedStack = r.supportsSplitScreenWindowingMode();
-                    break;
-                case WINDOWING_MODE_FREEFORM:
-                    // Any activity which supports freeform can go in the freeform stack.
-                    canUseFocusedStack = r.supportsFreeform();
-                    break;
-                default:
-                    // Dynamic stacks behave similarly to the fullscreen stack and can contain any
-                    // resizeable task.
-                    canUseFocusedStack = !focusedStack.isOnHomeDisplay()
-                            && r.canBeLaunchedOnDisplay(focusedStack.mDisplayId);
-            }
-        }
-        return canUseFocusedStack && !newTask
-                // Using the focus stack isn't important enough to override the preferred display.
-                && (mPreferredDisplayId == focusedStack.mDisplayId);
-    }
-
-    private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task,
-            ActivityOptions aOptions) {
-        // We are reusing a task, keep the stack!
-        if (mReuseTask != null) {
-            return mReuseTask.getStack();
-        }
-
-        if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0)
-                 || mPreferredDisplayId != DEFAULT_DISPLAY) {
-            // We don't pass in the default display id into the get launch stack call so it can do a
-            // full resolution.
-            final int candidateDisplay =
-                    mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY;
-            return mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP, candidateDisplay);
-        }
-        // Otherwise handle adjacent launch.
-
-        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
-        // The parent activity doesn't want to launch the activity on top of itself, but
-        // instead tries to put it onto other side in side-by-side mode.
-        final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
-
-        if (parentStack != focusedStack) {
-            // If task's parent stack is not focused - use it during adjacent launch.
-            return parentStack;
-        } else {
-            if (focusedStack != null && task == focusedStack.topTask()) {
-                // If task is already on top of focused stack - use it. We don't want to move the
-                // existing focused task to adjacent stack, just deliver new intent in this case.
-                return focusedStack;
-            }
-
-            if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
-                // If parent was in docked stack, the natural place to launch another activity
-                // will be fullscreen, so it can appear alongside the docked window.
-                final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
-                return parentStack.getDisplay().getOrCreateStack(
-                        WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
-            } else {
-                // If the parent is not in the docked stack, we check if there is docked window
-                // and if yes, we will launch into that stack. If not, we just put the new
-                // activity into parent's stack, because we can't find a better place.
-                final ActivityStack dockedStack =
-                        mSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
-                if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
-                    // There is a docked stack, but it isn't visible, so we can't launch into that.
-                    return mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
-                } else {
-                    return dockedStack;
-                }
-            }
-        }
-    }
-
-    private boolean isLaunchModeOneOf(int mode1, int mode2) {
-        return mode1 == mLaunchMode || mode2 == mLaunchMode;
-    }
-
-    static boolean isDocumentLaunchesIntoExisting(int flags) {
-        return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
-                (flags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0;
-    }
-
-    ActivityStarter setIntent(Intent intent) {
-        mRequest.intent = intent;
-        return this;
-    }
-
-    @VisibleForTesting
-    Intent getIntent() {
-        return mRequest.intent;
-    }
-
-    ActivityStarter setReason(String reason) {
-        mRequest.reason = reason;
-        return this;
-    }
-
-    ActivityStarter setCaller(IApplicationThread caller) {
-        mRequest.caller = caller;
-        return this;
-    }
-
-    ActivityStarter setEphemeralIntent(Intent intent) {
-        mRequest.ephemeralIntent = intent;
-        return this;
-    }
-
-
-    ActivityStarter setResolvedType(String type) {
-        mRequest.resolvedType = type;
-        return this;
-    }
-
-    ActivityStarter setActivityInfo(ActivityInfo info) {
-        mRequest.activityInfo = info;
-        return this;
-    }
-
-    ActivityStarter setResolveInfo(ResolveInfo info) {
-        mRequest.resolveInfo = info;
-        return this;
-    }
-
-    ActivityStarter setVoiceSession(IVoiceInteractionSession voiceSession) {
-        mRequest.voiceSession = voiceSession;
-        return this;
-    }
-
-    ActivityStarter setVoiceInteractor(IVoiceInteractor voiceInteractor) {
-        mRequest.voiceInteractor = voiceInteractor;
-        return this;
-    }
-
-    ActivityStarter setResultTo(IBinder resultTo) {
-        mRequest.resultTo = resultTo;
-        return this;
-    }
-
-    ActivityStarter setResultWho(String resultWho) {
-        mRequest.resultWho = resultWho;
-        return this;
-    }
-
-    ActivityStarter setRequestCode(int requestCode) {
-        mRequest.requestCode = requestCode;
-        return this;
-    }
-
-    ActivityStarter setCallingPid(int pid) {
-        mRequest.callingPid = pid;
-        return this;
-    }
-
-    ActivityStarter setCallingUid(int uid) {
-        mRequest.callingUid = uid;
-        return this;
-    }
-
-    ActivityStarter setCallingPackage(String callingPackage) {
-        mRequest.callingPackage = callingPackage;
-        return this;
-    }
-
-    ActivityStarter setRealCallingPid(int pid) {
-        mRequest.realCallingPid = pid;
-        return this;
-    }
-
-    ActivityStarter setRealCallingUid(int uid) {
-        mRequest.realCallingUid = uid;
-        return this;
-    }
-
-    ActivityStarter setStartFlags(int startFlags) {
-        mRequest.startFlags = startFlags;
-        return this;
-    }
-
-    ActivityStarter setActivityOptions(SafeActivityOptions options) {
-        mRequest.activityOptions = options;
-        return this;
-    }
-
-    ActivityStarter setActivityOptions(Bundle bOptions) {
-        return setActivityOptions(SafeActivityOptions.fromBundle(bOptions));
-    }
-
-    ActivityStarter setIgnoreTargetSecurity(boolean ignoreTargetSecurity) {
-        mRequest.ignoreTargetSecurity = ignoreTargetSecurity;
-        return this;
-    }
-
-    ActivityStarter setFilterCallingUid(int filterCallingUid) {
-        mRequest.filterCallingUid = filterCallingUid;
-        return this;
-    }
-
-    ActivityStarter setComponentSpecified(boolean componentSpecified) {
-        mRequest.componentSpecified = componentSpecified;
-        return this;
-    }
-
-    ActivityStarter setOutActivity(ActivityRecord[] outActivity) {
-        mRequest.outActivity = outActivity;
-        return this;
-    }
-
-    ActivityStarter setInTask(TaskRecord inTask) {
-        mRequest.inTask = inTask;
-        return this;
-    }
-
-    ActivityStarter setWaitResult(WaitResult result) {
-        mRequest.waitResult = result;
-        return this;
-    }
-
-    ActivityStarter setProfilerInfo(ProfilerInfo info) {
-        mRequest.profilerInfo = info;
-        return this;
-    }
-
-    ActivityStarter setGlobalConfiguration(Configuration config) {
-        mRequest.globalConfig = config;
-        return this;
-    }
-
-    ActivityStarter setUserId(int userId) {
-        mRequest.userId = userId;
-        return this;
-    }
-
-    ActivityStarter setMayWait(int userId) {
-        mRequest.mayWait = true;
-        mRequest.userId = userId;
-
-        return this;
-    }
-
-    ActivityStarter setAllowPendingRemoteAnimationRegistryLookup(boolean allowLookup) {
-        mRequest.allowPendingRemoteAnimationRegistryLookup = allowLookup;
-        return this;
-    }
-
-    ActivityStarter setOriginatingPendingIntent(PendingIntentRecord originatingPendingIntent) {
-        mRequest.originatingPendingIntent = originatingPendingIntent;
-        return this;
-    }
-
-    void dump(PrintWriter pw, String prefix) {
-        prefix = prefix + "  ";
-        pw.print(prefix);
-        pw.print("mCurrentUser=");
-        pw.println(mSupervisor.mCurrentUser);
-        pw.print(prefix);
-        pw.print("mLastStartReason=");
-        pw.println(mLastStartReason);
-        pw.print(prefix);
-        pw.print("mLastStartActivityTimeMs=");
-        pw.println(DateFormat.getDateTimeInstance().format(new Date(mLastStartActivityTimeMs)));
-        pw.print(prefix);
-        pw.print("mLastStartActivityResult=");
-        pw.println(mLastStartActivityResult);
-        ActivityRecord r = mLastStartActivityRecord[0];
-        if (r != null) {
-            pw.print(prefix);
-            pw.println("mLastStartActivityRecord:");
-            r.dump(pw, prefix + "  ");
-        }
-        if (mStartActivity != null) {
-            pw.print(prefix);
-            pw.println("mStartActivity:");
-            mStartActivity.dump(pw, prefix + "  ");
-        }
-        if (mIntent != null) {
-            pw.print(prefix);
-            pw.print("mIntent=");
-            pw.println(mIntent);
-        }
-        if (mOptions != null) {
-            pw.print(prefix);
-            pw.print("mOptions=");
-            pw.println(mOptions);
-        }
-        pw.print(prefix);
-        pw.print("mLaunchSingleTop=");
-        pw.print(LAUNCH_SINGLE_TOP == mLaunchMode);
-        pw.print(" mLaunchSingleInstance=");
-        pw.print(LAUNCH_SINGLE_INSTANCE == mLaunchMode);
-        pw.print(" mLaunchSingleTask=");
-        pw.println(LAUNCH_SINGLE_TASK == mLaunchMode);
-        pw.print(prefix);
-        pw.print("mLaunchFlags=0x");
-        pw.print(Integer.toHexString(mLaunchFlags));
-        pw.print(" mDoResume=");
-        pw.print(mDoResume);
-        pw.print(" mAddingToTask=");
-        pw.println(mAddingToTask);
-    }
-}
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityTaskManagerDebugConfig.java
deleted file mode 100644
index cf72738..0000000
--- a/services/core/java/com/android/server/am/ActivityTaskManagerDebugConfig.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-/**
- * Common class for the various debug {@link android.util.Log} output configuration relating to
- * activities.
- */
-public class ActivityTaskManagerDebugConfig {
-    // All output logs relating to acitvities use the {@link #TAG_ATM} string for tagging their log
-    // output. This makes it easy to identify the origin of the log message when sifting
-    // through a large amount of log output from multiple sources. However, it also makes trying
-    // to figure-out the origin of a log message while debugging the activity manager a little
-    // painful. By setting this constant to true, log messages from the activity manager package
-    // will be tagged with their class names instead fot the generic tag.
-    static final boolean TAG_WITH_CLASS_NAME = false;
-
-    // While debugging it is sometimes useful to have the category name of the log appended to the
-    // base log tag to make sifting through logs with the same base tag easier. By setting this
-    // constant to true, the category name of the log point will be appended to the log tag.
-    private static final boolean APPEND_CATEGORY_NAME = false;
-
-    // Default log tag for the activities.
-    static final String TAG_ATM = "ActivityTaskManager";
-
-    // Enable all debug log categories.
-    static final boolean DEBUG_ALL = false;
-
-    // Enable all debug log categories for activities.
-    private static final boolean DEBUG_ALL_ACTIVITIES = DEBUG_ALL || false;
-
-    static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_CONFIGURATION = DEBUG_ALL || false;
-    static final boolean DEBUG_CONTAINERS = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_FOCUS = false;
-    static final boolean DEBUG_IMMERSIVE = DEBUG_ALL || false;
-    static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false;
-    static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
-    static final boolean DEBUG_RECENTS = DEBUG_ALL || false;
-    static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false;
-    static final boolean DEBUG_SAVED_STATE = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_STACK = DEBUG_ALL || false;
-    static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
-    static final boolean DEBUG_TASKS = DEBUG_ALL || false;
-    static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
-    static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
-    static final boolean DEBUG_APP = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_IDLE = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_RELEASE = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
-    static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
-    static final boolean DEBUG_RESULTS = DEBUG_ALL || false;
-    static final boolean DEBUG_CLEANUP = DEBUG_ALL || false;
-    static final boolean DEBUG_METRICS = DEBUG_ALL || false;
-
-    static final String POSTFIX_APP = APPEND_CATEGORY_NAME ? "_App" : "";
-    static final String POSTFIX_IDLE = APPEND_CATEGORY_NAME ? "_Idle" : "";
-    static final String POSTFIX_RELEASE = APPEND_CATEGORY_NAME ? "_Release" : "";
-    static final String POSTFIX_USER_LEAVING = APPEND_CATEGORY_NAME ? "_UserLeaving" : "";
-    static final String POSTFIX_ADD_REMOVE = APPEND_CATEGORY_NAME ? "_AddRemove" : "";
-    static final String POSTFIX_CONFIGURATION = APPEND_CATEGORY_NAME ? "_Configuration" : "";
-    static final String POSTFIX_CONTAINERS = APPEND_CATEGORY_NAME ? "_Containers" : "";
-    static final String POSTFIX_FOCUS = APPEND_CATEGORY_NAME ? "_Focus" : "";
-    static final String POSTFIX_IMMERSIVE = APPEND_CATEGORY_NAME ? "_Immersive" : "";
-    static final String POSTFIX_LOCKTASK = APPEND_CATEGORY_NAME ? "_LockTask" : "";
-    static final String POSTFIX_PAUSE = APPEND_CATEGORY_NAME ? "_Pause" : "";
-    static final String POSTFIX_RECENTS = APPEND_CATEGORY_NAME ? "_Recents" : "";
-    static final String POSTFIX_SAVED_STATE = APPEND_CATEGORY_NAME ? "_SavedState" : "";
-    static final String POSTFIX_STACK = APPEND_CATEGORY_NAME ? "_Stack" : "";
-    static final String POSTFIX_STATES = APPEND_CATEGORY_NAME ? "_States" : "";
-    static final String POSTFIX_SWITCH = APPEND_CATEGORY_NAME ? "_Switch" : "";
-    static final String POSTFIX_TASKS = APPEND_CATEGORY_NAME ? "_Tasks" : "";
-    static final String POSTFIX_TRANSITION = APPEND_CATEGORY_NAME ? "_Transition" : "";
-    static final String POSTFIX_VISIBILITY = APPEND_CATEGORY_NAME ? "_Visibility" : "";
-    static final String POSTFIX_RESULTS = APPEND_CATEGORY_NAME ? "_Results" : "";
-}
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
deleted file mode 100644
index 02707fb..0000000
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ /dev/null
@@ -1,6693 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import static android.Manifest.permission.BIND_VOICE_INTERACTION;
-import static android.Manifest.permission.CHANGE_CONFIGURATION;
-import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
-import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
-import static android.Manifest.permission.READ_FRAME_BUFFER;
-import static android.Manifest.permission.REMOVE_TASKS;
-import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.Manifest.permission.STOP_APP_SWITCHES;
-import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
-import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
-import static android.content.pm.ApplicationInfo.FLAG_FACTORY_TEST;
-import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
-import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
-import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
-import static android.content.pm.PackageManager.FEATURE_PC;
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
-import static android.os.Build.VERSION_CODES.N;
-import static android.os.FactoryTest.FACTORY_TEST_HIGH_LEVEL;
-import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
-import static android.os.FactoryTest.FACTORY_TEST_OFF;
-import static android.os.Process.FIRST_APPLICATION_UID;
-import static android.os.Process.SYSTEM_UID;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
-import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
-import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
-import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
-import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
-import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
-import static android.provider.Settings.System.FONT_SCALE;
-import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-
-import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
-import static com.android.server.am.ActivityManagerService.MY_PID;
-import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
-import static com.android.server.am.ActivityManagerService.dumpStackTraces;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONTROLLER;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.GOING_TO_SLEEP;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.HOME_PROC;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.LAUNCHING_ACTIVITY;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto
-        .PREVIOUS_PROC_VISIBLE_TIME_MS;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage
-        .MODE;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage
-        .PACKAGE;
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_IMMERSIVE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_IMMERSIVE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
-import static com.android.server.am.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
-import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
-import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
-import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
-import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.app.ActivityThread;
-import android.app.AlertDialog;
-import android.app.AppGlobals;
-import android.app.Dialog;
-import android.app.IActivityController;
-import android.app.IActivityTaskManager;
-import android.app.IApplicationThread;
-import android.app.IAssistDataReceiver;
-import android.app.INotificationManager;
-import android.app.ITaskStackListener;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.PictureInPictureParams;
-import android.app.ProfilerInfo;
-import android.app.RemoteAction;
-import android.app.WaitResult;
-import android.app.WindowConfiguration;
-import android.app.admin.DevicePolicyCache;
-import android.app.assist.AssistContent;
-import android.app.assist.AssistStructure;
-import android.app.usage.UsageStatsManagerInternal;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ConfigurationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.ResolveInfo;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.metrics.LogMaker;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.FactoryTest;
-import android.os.FileUtils;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IUserManager;
-import android.os.LocaleList;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.os.PowerManager;
-import android.os.PowerManagerInternal;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.StrictMode;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.os.UpdateLock;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.os.WorkSource;
-import android.os.storage.IStorageManager;
-import android.os.storage.StorageManager;
-import android.provider.Settings;
-import android.service.voice.IVoiceInteractionSession;
-import android.service.voice.VoiceInteractionManagerInternal;
-import android.telecom.TelecomManager;
-import android.text.TextUtils;
-import android.text.format.Time;
-import android.util.ArrayMap;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.util.StatsLog;
-import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
-import android.view.IRecentsAnimationRunner;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationDefinition;
-import android.view.WindowManager;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.AssistUtils;
-import com.android.internal.app.IAppOpsService;
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.app.ProcessMap;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.TransferPipe;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
-import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.policy.KeyguardDismissCallback;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.AppOpsService;
-import com.android.server.AttributeCache;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-import com.android.server.SystemServiceManager;
-import com.android.server.Watchdog;
-import com.android.server.firewall.IntentFirewall;
-import com.android.server.pm.UserManagerService;
-import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.vr.VrManagerInternal;
-import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.server.wm.PinnedStackWindowController;
-import com.android.server.wm.WindowManagerService;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.ref.WeakReference;
-import java.text.DateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * System service for managing activities and their containers (task, stacks, displays,... ).
- *
- * {@hide}
- */
-public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
-    private static final String TAG_STACK = TAG + POSTFIX_STACK;
-    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
-    private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
-    private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
-    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
-    private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
-    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
-
-    // How long we wait until we timeout on key dispatching.
-    public static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
-    // How long we wait until we timeout on key dispatching during instrumentation.
-    static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;
-
-    /** Used to indicate that an app transition should be animated. */
-    static final boolean ANIMATE = true;
-
-    /** Hardware-reported OpenGLES version. */
-    final int GL_ES_VERSION;
-
-    public static final String DUMP_ACTIVITIES_CMD = "activities" ;
-    public static final String DUMP_ACTIVITIES_SHORT_CMD = "a" ;
-    public static final String DUMP_LASTANR_CMD = "lastanr" ;
-    public static final String DUMP_LASTANR_TRACES_CMD = "lastanr-traces" ;
-    public static final String DUMP_STARTER_CMD = "starter" ;
-    public static final String DUMP_CONTAINERS_CMD = "containers" ;
-    public static final String DUMP_RECENTS_CMD = "recents" ;
-    public static final String DUMP_RECENTS_SHORT_CMD = "r" ;
-
-    /** This activity is not being relaunched, or being relaunched for a non-resize reason. */
-    public static final int RELAUNCH_REASON_NONE = 0;
-    /** This activity is being relaunched due to windowing mode change. */
-    public static final int RELAUNCH_REASON_WINDOWING_MODE_RESIZE = 1;
-    /** This activity is being relaunched due to a free-resize operation. */
-    public static final int RELAUNCH_REASON_FREE_RESIZE = 2;
-
-    Context mContext;
-
-    /**
-     * This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can
-     * change at runtime. Use mContext for non-UI purposes.
-     */
-    final Context mUiContext;
-    final ActivityThread mSystemThread;
-    H mH;
-    UiHandler mUiHandler;
-    ActivityManagerInternal mAmInternal;
-    UriGrantsManagerInternal mUgmInternal;
-    private PackageManagerInternal mPmInternal;
-    private ActivityTaskManagerInternal mInternal;
-    PowerManagerInternal mPowerManagerInternal;
-    private UsageStatsManagerInternal mUsageStatsInternal;
-
-    PendingIntentController mPendingIntentController;
-    IntentFirewall mIntentFirewall;
-
-    /* Global service lock used by the package the owns this service. */
-    Object mGlobalLock;
-    ActivityStackSupervisor mStackSupervisor;
-    WindowManagerService mWindowManager;
-    private UserManagerService mUserManager;
-    private AppOpsService mAppOpsService;
-    /** All active uids in the system. */
-    private final SparseArray<Integer> mActiveUids = new SparseArray<>();
-    private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>();
-    /** All processes currently running that might have a window organized by name. */
-    final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>();
-    /** All processes we currently have running mapped by pid */
-    final SparseArray<WindowProcessController> mPidMap = new SparseArray<>();
-    /** This is the process holding what we currently consider to be the "home" activity. */
-    WindowProcessController mHomeProcess;
-    /** The currently running heavy-weight process, if any. */
-    WindowProcessController mHeavyWeightProcess = null;
-    boolean mHasHeavyWeightFeature;
-    /**
-     * This is the process holding the activity the user last visited that is in a different process
-     * from the one they are currently in.
-     */
-    WindowProcessController mPreviousProcess;
-    /** The time at which the previous process was last visible. */
-    long mPreviousProcessVisibleTime;
-
-    /** List of intents that were used to start the most recent tasks. */
-    private RecentTasks mRecentTasks;
-    /** State of external calls telling us if the device is awake or asleep. */
-    private boolean mKeyguardShown = false;
-
-    // Wrapper around VoiceInteractionServiceManager
-    private AssistUtils mAssistUtils;
-
-    // VoiceInteraction session ID that changes for each new request except when
-    // being called for multi-window assist in a single session.
-    private int mViSessionId = 1000;
-
-    // How long to wait in getAssistContextExtras for the activity and foreground services
-    // to respond with the result.
-    private static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
-
-    // How long top wait when going through the modern assist (which doesn't need to block
-    // on getting this result before starting to launch its UI).
-    private static final int PENDING_ASSIST_EXTRAS_LONG_TIMEOUT = 2000;
-
-    // How long to wait in getAutofillAssistStructure() for the activity to respond with the result.
-    private static final int PENDING_AUTOFILL_ASSIST_STRUCTURE_TIMEOUT = 2000;
-
-    private final ArrayList<PendingAssistExtras> mPendingAssistExtras = new ArrayList<>();
-
-    // Keeps track of the active voice interaction service component, notified from
-    // VoiceInteractionManagerService
-    ComponentName mActiveVoiceInteractionServiceComponent;
-
-    private VrController mVrController;
-    KeyguardController mKeyguardController;
-    private final ClientLifecycleManager mLifecycleManager;
-    private TaskChangeNotificationController mTaskChangeNotificationController;
-    /** The controller for all operations related to locktask. */
-    private LockTaskController mLockTaskController;
-    private ActivityStartController mActivityStartController;
-
-    boolean mSuppressResizeConfigChanges;
-
-    private final UpdateConfigurationResult mTmpUpdateConfigurationResult =
-            new UpdateConfigurationResult();
-
-    static final class UpdateConfigurationResult {
-        // Configuration changes that were updated.
-        int changes;
-        // If the activity was relaunched to match the new configuration.
-        boolean activityRelaunched;
-
-        void reset() {
-            changes = 0;
-            activityRelaunched = false;
-        }
-    }
-
-    /** Current sequencing integer of the configuration, for skipping old configurations. */
-    private int mConfigurationSeq;
-    // To cache the list of supported system locales
-    private String[] mSupportedSystemLocales = null;
-
-    /**
-     * Temp object used when global and/or display override configuration is updated. It is also
-     * sent to outer world instead of {@link #getGlobalConfiguration} because we don't trust
-     * anyone...
-     */
-    private Configuration mTempConfig = new Configuration();
-
-    /** Temporary to avoid allocations. */
-    final StringBuilder mStringBuilder = new StringBuilder(256);
-
-    // Amount of time after a call to stopAppSwitches() during which we will
-    // prevent further untrusted switches from happening.
-    private static final long APP_SWITCH_DELAY_TIME = 5 * 1000;
-
-    /**
-     * The time at which we will allow normal application switches again,
-     * after a call to {@link #stopAppSwitches()}.
-     */
-    private long mAppSwitchesAllowedTime;
-    /**
-     * This is set to true after the first switch after mAppSwitchesAllowedTime
-     * is set; any switches after that will clear the time.
-     */
-    private boolean mDidAppSwitch;
-
-    IActivityController mController = null;
-    boolean mControllerIsAMonkey = false;
-
-    final int mFactoryTest;
-
-    /** Used to control how we initialize the service. */
-    ComponentName mTopComponent;
-    String mTopAction = Intent.ACTION_MAIN;
-    String mTopData;
-
-    /**
-     * Dump of the activity state at the time of the last ANR. Cleared after
-     * {@link WindowManagerService#LAST_ANR_LIFETIME_DURATION_MSECS}
-     */
-    String mLastANRState;
-
-    /**
-     * Used to retain an update lock when the foreground activity is in
-     * immersive mode.
-     */
-    private final UpdateLock mUpdateLock = new UpdateLock("immersive");
-
-    /**
-     * Packages that are being allowed to perform unrestricted app switches.  Mapping is
-     * User -> Type -> uid.
-     */
-    final SparseArray<ArrayMap<String, Integer>> mAllowAppSwitchUids = new SparseArray<>();
-
-    /** The dimensions of the thumbnails in the Recents UI. */
-    private int mThumbnailWidth;
-    private int mThumbnailHeight;
-    private float mFullscreenThumbnailScale;
-
-    /**
-     * Flag that indicates if multi-window is enabled.
-     *
-     * For any particular form of multi-window to be enabled, generic multi-window must be enabled
-     * in {@link com.android.internal.R.bool#config_supportsMultiWindow} config or
-     * {@link Settings.Global#DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES} development option set.
-     * At least one of the forms of multi-window must be enabled in order for this flag to be
-     * initialized to 'true'.
-     *
-     * @see #mSupportsSplitScreenMultiWindow
-     * @see #mSupportsFreeformWindowManagement
-     * @see #mSupportsPictureInPicture
-     * @see #mSupportsMultiDisplay
-     */
-    boolean mSupportsMultiWindow;
-    boolean mSupportsSplitScreenMultiWindow;
-    boolean mSupportsFreeformWindowManagement;
-    boolean mSupportsPictureInPicture;
-    boolean mSupportsMultiDisplay;
-    boolean mForceResizableActivities;
-
-    final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
-
-    // VR Vr2d Display Id.
-    int mVr2dDisplayId = INVALID_DISPLAY;
-
-    /**
-     * Set while we are wanting to sleep, to prevent any
-     * activities from being started/resumed.
-     *
-     * TODO(b/33594039): Clarify the actual state transitions represented by mSleeping.
-     *
-     * Currently mSleeping is set to true when transitioning into the sleep state, and remains true
-     * while in the sleep state until there is a pending transition out of sleep, in which case
-     * mSleeping is set to false, and remains false while awake.
-     *
-     * Whether mSleeping can quickly toggled between true/false without the device actually
-     * display changing states is undefined.
-     */
-    private boolean mSleeping = false;
-
-    /**
-     * The process state used for processes that are running the top activities.
-     * This changes between TOP and TOP_SLEEPING to following mSleeping.
-     */
-    int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
-
-    // Whether we should show our dialogs (ANR, crash, etc) or just perform their default action
-    // automatically. Important for devices without direct input devices.
-    private boolean mShowDialogs = true;
-
-    /** Set if we are shutting down the system, similar to sleeping. */
-    boolean mShuttingDown = false;
-
-    /**
-     * We want to hold a wake lock while running a voice interaction session, since
-     * this may happen with the screen off and we need to keep the CPU running to
-     * be able to continue to interact with the user.
-     */
-    PowerManager.WakeLock mVoiceWakeLock;
-
-    /**
-     * Set while we are running a voice interaction. This overrides sleeping while it is active.
-     */
-    IVoiceInteractionSession mRunningVoice;
-
-    /**
-     * The last resumed activity. This is identical to the current resumed activity most
-     * of the time but could be different when we're pausing one activity before we resume
-     * another activity.
-     */
-    ActivityRecord mLastResumedActivity;
-
-    /**
-     * The activity that is currently being traced as the active resumed activity.
-     *
-     * @see #updateResumedAppTrace
-     */
-    private @Nullable ActivityRecord mTracedResumedActivity;
-
-    /** If non-null, we are tracking the time the user spends in the currently focused app. */
-    AppTimeTracker mCurAppTimeTracker;
-
-    private AppWarnings mAppWarnings;
-
-    /**
-     * Packages that the user has asked to have run in screen size
-     * compatibility mode instead of filling the screen.
-     */
-    CompatModePackages mCompatModePackages;
-
-    private FontScaleSettingObserver mFontScaleSettingObserver;
-
-    private final class FontScaleSettingObserver extends ContentObserver {
-        private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE);
-        private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS);
-
-        public FontScaleSettingObserver() {
-            super(mH);
-            final ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(mFontScaleUri, false, this, UserHandle.USER_ALL);
-            resolver.registerContentObserver(mHideErrorDialogsUri, false, this,
-                    UserHandle.USER_ALL);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
-            if (mFontScaleUri.equals(uri)) {
-                updateFontScaleIfNeeded(userId);
-            } else if (mHideErrorDialogsUri.equals(uri)) {
-                synchronized (mGlobalLock) {
-                    updateShouldShowDialogsLocked(getGlobalConfiguration());
-                }
-            }
-        }
-    }
-
-    ActivityTaskManagerService(Context context) {
-        mContext = context;
-        mFactoryTest = FactoryTest.getMode();
-        mSystemThread = ActivityThread.currentActivityThread();
-        mUiContext = mSystemThread.getSystemUiContext();
-        mLifecycleManager = new ClientLifecycleManager();
-        GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
-    }
-
-    void onSystemReady() {
-        mHasHeavyWeightFeature = mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_CANT_SAVE_STATE);
-        mAssistUtils = new AssistUtils(mContext);
-        mVrController.onSystemReady();
-        mRecentTasks.onSystemReadyLocked();
-    }
-
-    void onInitPowerManagement() {
-        mStackSupervisor.initPowerManagement();
-        final PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
-        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
-        mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
-        mVoiceWakeLock.setReferenceCounted(false);
-    }
-
-    void installSystemProviders() {
-        mFontScaleSettingObserver = new FontScaleSettingObserver();
-    }
-
-    void retrieveSettings(ContentResolver resolver) {
-        final boolean freeformWindowManagement =
-                mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
-                        || Settings.Global.getInt(
-                        resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
-
-        final boolean supportsMultiWindow = ActivityTaskManager.supportsMultiWindow(mContext);
-        final boolean supportsPictureInPicture = supportsMultiWindow &&
-                mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
-        final boolean supportsSplitScreenMultiWindow =
-                ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
-        final boolean supportsMultiDisplay = mContext.getPackageManager()
-                .hasSystemFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
-        final boolean alwaysFinishActivities =
-                Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
-        final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
-        final boolean forceResizable = Settings.Global.getInt(
-                resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
-        final boolean isPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
-
-        // Transfer any global setting for forcing RTL layout, into a System Property
-        SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
-
-        final Configuration configuration = new Configuration();
-        Settings.System.getConfiguration(resolver, configuration);
-        if (forceRtl) {
-            // This will take care of setting the correct layout direction flags
-            configuration.setLayoutDirection(configuration.locale);
-        }
-
-        synchronized (mGlobalLock) {
-            mForceResizableActivities = forceResizable;
-            final boolean multiWindowFormEnabled = freeformWindowManagement
-                    || supportsSplitScreenMultiWindow
-                    || supportsPictureInPicture
-                    || supportsMultiDisplay;
-            if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) {
-                mSupportsMultiWindow = true;
-                mSupportsFreeformWindowManagement = freeformWindowManagement;
-                mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
-                mSupportsPictureInPicture = supportsPictureInPicture;
-                mSupportsMultiDisplay = supportsMultiDisplay;
-            } else {
-                mSupportsMultiWindow = false;
-                mSupportsFreeformWindowManagement = false;
-                mSupportsSplitScreenMultiWindow = false;
-                mSupportsPictureInPicture = false;
-                mSupportsMultiDisplay = false;
-            }
-            mWindowManager.setForceResizableTasks(mForceResizableActivities);
-            mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
-            mWindowManager.setSupportsFreeformWindowManagement(mSupportsFreeformWindowManagement);
-            mWindowManager.setIsPc(isPc);
-            // This happens before any activities are started, so we can change global configuration
-            // in-place.
-            updateConfigurationLocked(configuration, null, true);
-            final Configuration globalConfig = getGlobalConfiguration();
-            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Initial config: " + globalConfig);
-
-            // Load resources only after the current configuration has been set.
-            final Resources res = mContext.getResources();
-            mThumbnailWidth = res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.thumbnail_width);
-            mThumbnailHeight = res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.thumbnail_height);
-
-            if ((globalConfig.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
-                mFullscreenThumbnailScale = (float) res
-                        .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
-                        (float) globalConfig.screenWidthDp;
-            } else {
-                mFullscreenThumbnailScale = res.getFraction(
-                        com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
-            }
-        }
-    }
-
-    // TODO: Will be converted to WM lock once transition is complete.
-    void setActivityManagerService(Object globalLock, Looper looper,
-            IntentFirewall intentFirewall, PendingIntentController intentController) {
-        mGlobalLock = globalLock;
-        mH = new H(looper);
-        mUiHandler = new UiHandler();
-        mIntentFirewall = intentFirewall;
-        final File systemDir = SystemServiceManager.ensureSystemDir();
-        mAppWarnings = new AppWarnings(this, mUiContext, mH, mUiHandler, systemDir);
-        mCompatModePackages = new CompatModePackages(this, systemDir, mH);
-        mPendingIntentController = intentController;
-
-        mTempConfig.setToDefaults();
-        mTempConfig.setLocales(LocaleList.getDefault());
-        mConfigurationSeq = mTempConfig.seq = 1;
-        mStackSupervisor = createStackSupervisor();
-        mStackSupervisor.onConfigurationChanged(mTempConfig);
-
-        mTaskChangeNotificationController =
-                new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
-        mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mH);
-        mActivityStartController = new ActivityStartController(this);
-        mRecentTasks = createRecentTasks();
-        mStackSupervisor.setRecentTasks(mRecentTasks);
-        mVrController = new VrController(mGlobalLock);
-        mKeyguardController = mStackSupervisor.getKeyguardController();
-    }
-
-    void onActivityManagerInternalAdded() {
-        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
-        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
-    }
-
-    int increaseConfigurationSeqLocked() {
-        mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
-        return mConfigurationSeq;
-    }
-
-    protected ActivityStackSupervisor createStackSupervisor() {
-        final ActivityStackSupervisor supervisor = new ActivityStackSupervisor(this, mH.getLooper());
-        supervisor.initialize();
-        return supervisor;
-    }
-
-    void setWindowManager(WindowManagerService wm) {
-        mWindowManager = wm;
-        mLockTaskController.setWindowManager(wm);
-        mStackSupervisor.setWindowManager(wm);
-    }
-
-    void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
-        mUsageStatsInternal = usageStatsManager;
-    }
-
-    UserManagerService getUserManager() {
-        if (mUserManager == null) {
-            IBinder b = ServiceManager.getService(Context.USER_SERVICE);
-            mUserManager = (UserManagerService) IUserManager.Stub.asInterface(b);
-        }
-        return mUserManager;
-    }
-
-    AppOpsService getAppOpsService() {
-        if (mAppOpsService == null) {
-            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
-            mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b);
-        }
-        return mAppOpsService;
-    }
-
-    boolean hasUserRestriction(String restriction, int userId) {
-        return getUserManager().hasUserRestriction(restriction, userId);
-    }
-
-    protected RecentTasks createRecentTasks() {
-        return new RecentTasks(this, mStackSupervisor);
-    }
-
-    RecentTasks getRecentTasks() {
-        return mRecentTasks;
-    }
-
-    ClientLifecycleManager getLifecycleManager() {
-        return mLifecycleManager;
-    }
-
-    ActivityStartController getActivityStartController() {
-        return mActivityStartController;
-    }
-
-    TaskChangeNotificationController getTaskChangeNotificationController() {
-        return mTaskChangeNotificationController;
-    }
-
-    LockTaskController getLockTaskController() {
-        return mLockTaskController;
-    }
-
-    /**
-     * Return the global configuration used by the process corresponding to the input pid. This is
-     * usually the global configuration with some overrides specific to that process.
-     */
-    Configuration getGlobalConfigurationForCallingPid() {
-        final int pid = Binder.getCallingPid();
-        if (pid == MY_PID || pid < 0) {
-            return getGlobalConfiguration();
-        }
-        synchronized (mGlobalLock) {
-            final WindowProcessController app = mPidMap.get(pid);
-            return app != null ? app.getConfiguration() : getGlobalConfiguration();
-        }
-    }
-
-    /**
-     * Return the device configuration info used by the process corresponding to the input pid.
-     * The value is consistent with the global configuration for the process.
-     */
-    @Override
-    public ConfigurationInfo getDeviceConfigurationInfo() {
-        ConfigurationInfo config = new ConfigurationInfo();
-        synchronized (mGlobalLock) {
-            final Configuration globalConfig = getGlobalConfigurationForCallingPid();
-            config.reqTouchScreen = globalConfig.touchscreen;
-            config.reqKeyboardType = globalConfig.keyboard;
-            config.reqNavigation = globalConfig.navigation;
-            if (globalConfig.navigation == Configuration.NAVIGATION_DPAD
-                    || globalConfig.navigation == Configuration.NAVIGATION_TRACKBALL) {
-                config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
-            }
-            if (globalConfig.keyboard != Configuration.KEYBOARD_UNDEFINED
-                    && globalConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
-                config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
-            }
-            config.reqGlEsVersion = GL_ES_VERSION;
-        }
-        return config;
-    }
-
-    private void start() {
-        mInternal = new LocalService();
-        LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
-    }
-
-    public static final class Lifecycle extends SystemService {
-        private final ActivityTaskManagerService mService;
-
-        public Lifecycle(Context context) {
-            super(context);
-            mService = new ActivityTaskManagerService(context);
-        }
-
-        @Override
-        public void onStart() {
-            publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
-            mService.start();
-        }
-
-        public ActivityTaskManagerService getService() {
-            return mService;
-        }
-    }
-
-    @Override
-    public final int startActivity(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
-        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
-                resultWho, requestCode, startFlags, profilerInfo, bOptions,
-                UserHandle.getCallingUserId());
-    }
-
-    @Override
-    public final int startActivities(IApplicationThread caller, String callingPackage,
-            Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
-            int userId) {
-        final String reason = "startActivities";
-        enforceNotIsolatedCaller(reason);
-        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
-        // TODO: Switch to user app stacks here.
-        return getActivityStartController().startActivities(caller, -1, callingPackage, intents,
-                resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId, reason,
-                null /* originatingPendingIntent */);
-    }
-
-    @Override
-    public int startActivityAsUser(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
-        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
-                resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
-                true /*validateIncomingUser*/);
-    }
-
-    int startActivityAsUser(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
-            boolean validateIncomingUser) {
-        enforceNotIsolatedCaller("startActivityAsUser");
-
-        userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
-                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
-
-        // TODO: Switch to user app stacks here.
-        return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
-                .setCaller(caller)
-                .setCallingPackage(callingPackage)
-                .setResolvedType(resolvedType)
-                .setResultTo(resultTo)
-                .setResultWho(resultWho)
-                .setRequestCode(requestCode)
-                .setStartFlags(startFlags)
-                .setProfilerInfo(profilerInfo)
-                .setActivityOptions(bOptions)
-                .setMayWait(userId)
-                .execute();
-
-    }
-
-    @Override
-    public int startActivityIntentSender(IApplicationThread caller, IIntentSender target,
-            IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) {
-        enforceNotIsolatedCaller("startActivityIntentSender");
-        // Refuse possible leaked file descriptors
-        if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-
-        if (!(target instanceof PendingIntentRecord)) {
-            throw new IllegalArgumentException("Bad PendingIntent object");
-        }
-
-        PendingIntentRecord pir = (PendingIntentRecord)target;
-
-        synchronized (mGlobalLock) {
-            // If this is coming from the currently resumed activity, it is
-            // effectively saying that app switches are allowed at this point.
-            final ActivityStack stack = getTopDisplayFocusedStack();
-            if (stack.mResumedActivity != null &&
-                    stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
-                mAppSwitchesAllowedTime = 0;
-            }
-        }
-        return pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null,
-                resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
-    }
-
-    @Override
-    public boolean startNextMatchingActivity(IBinder callingActivity, Intent intent,
-            Bundle bOptions) {
-        // Refuse possible leaked file descriptors
-        if (intent != null && intent.hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-        SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
-
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(callingActivity);
-            if (r == null) {
-                SafeActivityOptions.abort(options);
-                return false;
-            }
-            if (!r.attachedToProcess()) {
-                // The caller is not running...  d'oh!
-                SafeActivityOptions.abort(options);
-                return false;
-            }
-            intent = new Intent(intent);
-            // The caller is not allowed to change the data.
-            intent.setDataAndType(r.intent.getData(), r.intent.getType());
-            // And we are resetting to find the next component...
-            intent.setComponent(null);
-
-            final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
-
-            ActivityInfo aInfo = null;
-            try {
-                List<ResolveInfo> resolves =
-                        AppGlobals.getPackageManager().queryIntentActivities(
-                                intent, r.resolvedType,
-                                PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS,
-                                UserHandle.getCallingUserId()).getList();
-
-                // Look for the original activity in the list...
-                final int N = resolves != null ? resolves.size() : 0;
-                for (int i=0; i<N; i++) {
-                    ResolveInfo rInfo = resolves.get(i);
-                    if (rInfo.activityInfo.packageName.equals(r.packageName)
-                            && rInfo.activityInfo.name.equals(r.info.name)) {
-                        // We found the current one...  the next matching is
-                        // after it.
-                        i++;
-                        if (i<N) {
-                            aInfo = resolves.get(i).activityInfo;
-                        }
-                        if (debug) {
-                            Slog.v(TAG, "Next matching activity: found current " + r.packageName
-                                    + "/" + r.info.name);
-                            Slog.v(TAG, "Next matching activity: next is " + ((aInfo == null)
-                                    ? "null" : aInfo.packageName + "/" + aInfo.name));
-                        }
-                        break;
-                    }
-                }
-            } catch (RemoteException e) {
-            }
-
-            if (aInfo == null) {
-                // Nobody who is next!
-                SafeActivityOptions.abort(options);
-                if (debug) Slog.d(TAG, "Next matching activity: nothing found");
-                return false;
-            }
-
-            intent.setComponent(new ComponentName(
-                    aInfo.applicationInfo.packageName, aInfo.name));
-            intent.setFlags(intent.getFlags()&~(
-                    Intent.FLAG_ACTIVITY_FORWARD_RESULT|
-                            Intent.FLAG_ACTIVITY_CLEAR_TOP|
-                            Intent.FLAG_ACTIVITY_MULTIPLE_TASK|
-                            FLAG_ACTIVITY_NEW_TASK));
-
-            // Okay now we need to start the new activity, replacing the currently running activity.
-            // This is a little tricky because we want to start the new one as if the current one is
-            // finished, but not finish the current one first so that there is no flicker.
-            // And thus...
-            final boolean wasFinishing = r.finishing;
-            r.finishing = true;
-
-            // Propagate reply information over to the new activity.
-            final ActivityRecord resultTo = r.resultTo;
-            final String resultWho = r.resultWho;
-            final int requestCode = r.requestCode;
-            r.resultTo = null;
-            if (resultTo != null) {
-                resultTo.removeResultsLocked(r, resultWho, requestCode);
-            }
-
-            final long origId = Binder.clearCallingIdentity();
-            // TODO(b/64750076): Check if calling pid should really be -1.
-            final int res = getActivityStartController()
-                    .obtainStarter(intent, "startNextMatchingActivity")
-                    .setCaller(r.app.getThread())
-                    .setResolvedType(r.resolvedType)
-                    .setActivityInfo(aInfo)
-                    .setResultTo(resultTo != null ? resultTo.appToken : null)
-                    .setResultWho(resultWho)
-                    .setRequestCode(requestCode)
-                    .setCallingPid(-1)
-                    .setCallingUid(r.launchedFromUid)
-                    .setCallingPackage(r.launchedFromPackage)
-                    .setRealCallingPid(-1)
-                    .setRealCallingUid(r.launchedFromUid)
-                    .setActivityOptions(options)
-                    .execute();
-            Binder.restoreCallingIdentity(origId);
-
-            r.finishing = wasFinishing;
-            if (res != ActivityManager.START_SUCCESS) {
-                return false;
-            }
-            return true;
-        }
-    }
-
-    @Override
-    public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
-        final WaitResult res = new WaitResult();
-        synchronized (mGlobalLock) {
-            enforceNotIsolatedCaller("startActivityAndWait");
-            userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
-                    userId, "startActivityAndWait");
-            // TODO: Switch to user app stacks here.
-            getActivityStartController().obtainStarter(intent, "startActivityAndWait")
-                    .setCaller(caller)
-                    .setCallingPackage(callingPackage)
-                    .setResolvedType(resolvedType)
-                    .setResultTo(resultTo)
-                    .setResultWho(resultWho)
-                    .setRequestCode(requestCode)
-                    .setStartFlags(startFlags)
-                    .setActivityOptions(bOptions)
-                    .setMayWait(userId)
-                    .setProfilerInfo(profilerInfo)
-                    .setWaitResult(res)
-                    .execute();
-        }
-        return res;
-    }
-
-    @Override
-    public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, Configuration config, Bundle bOptions, int userId) {
-        synchronized (mGlobalLock) {
-            enforceNotIsolatedCaller("startActivityWithConfig");
-            userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
-                    "startActivityWithConfig");
-            // TODO: Switch to user app stacks here.
-            return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
-                    .setCaller(caller)
-                    .setCallingPackage(callingPackage)
-                    .setResolvedType(resolvedType)
-                    .setResultTo(resultTo)
-                    .setResultWho(resultWho)
-                    .setRequestCode(requestCode)
-                    .setStartFlags(startFlags)
-                    .setGlobalConfiguration(config)
-                    .setActivityOptions(bOptions)
-                    .setMayWait(userId)
-                    .execute();
-        }
-    }
-
-    @Override
-    public final int startActivityAsCaller(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, boolean ignoreTargetSecurity,
-            int userId) {
-
-        // This is very dangerous -- it allows you to perform a start activity (including
-        // permission grants) as any app that may launch one of your own activities.  So
-        // we will only allow this to be done from activities that are part of the core framework,
-        // and then only when they are running as the system.
-        final ActivityRecord sourceRecord;
-        final int targetUid;
-        final String targetPackage;
-        final boolean isResolver;
-        synchronized (mGlobalLock) {
-            if (resultTo == null) {
-                throw new SecurityException("Must be called from an activity");
-            }
-            sourceRecord = mStackSupervisor.isInAnyStackLocked(resultTo);
-            if (sourceRecord == null) {
-                throw new SecurityException("Called with bad activity token: " + resultTo);
-            }
-            if (!sourceRecord.info.packageName.equals("android")) {
-                throw new SecurityException(
-                        "Must be called from an activity that is declared in the android package");
-            }
-            if (sourceRecord.app == null) {
-                throw new SecurityException("Called without a process attached to activity");
-            }
-            if (UserHandle.getAppId(sourceRecord.app.mUid) != SYSTEM_UID) {
-                // This is still okay, as long as this activity is running under the
-                // uid of the original calling activity.
-                if (sourceRecord.app.mUid != sourceRecord.launchedFromUid) {
-                    throw new SecurityException(
-                            "Calling activity in uid " + sourceRecord.app.mUid
-                                    + " must be system uid or original calling uid "
-                                    + sourceRecord.launchedFromUid);
-                }
-            }
-            if (ignoreTargetSecurity) {
-                if (intent.getComponent() == null) {
-                    throw new SecurityException(
-                            "Component must be specified with ignoreTargetSecurity");
-                }
-                if (intent.getSelector() != null) {
-                    throw new SecurityException(
-                            "Selector not allowed with ignoreTargetSecurity");
-                }
-            }
-            targetUid = sourceRecord.launchedFromUid;
-            targetPackage = sourceRecord.launchedFromPackage;
-            isResolver = sourceRecord.isResolverOrChildActivity();
-        }
-
-        if (userId == UserHandle.USER_NULL) {
-            userId = UserHandle.getUserId(sourceRecord.app.mUid);
-        }
-
-        // TODO: Switch to user app stacks here.
-        try {
-            return getActivityStartController().obtainStarter(intent, "startActivityAsCaller")
-                    .setCallingUid(targetUid)
-                    .setCallingPackage(targetPackage)
-                    .setResolvedType(resolvedType)
-                    .setResultTo(resultTo)
-                    .setResultWho(resultWho)
-                    .setRequestCode(requestCode)
-                    .setStartFlags(startFlags)
-                    .setActivityOptions(bOptions)
-                    .setMayWait(userId)
-                    .setIgnoreTargetSecurity(ignoreTargetSecurity)
-                    .setFilterCallingUid(isResolver ? 0 /* system */ : targetUid)
-                    .execute();
-        } catch (SecurityException e) {
-            // XXX need to figure out how to propagate to original app.
-            // A SecurityException here is generally actually a fault of the original
-            // calling activity (such as a fairly granting permissions), so propagate it
-            // back to them.
-            /*
-            StringBuilder msg = new StringBuilder();
-            msg.append("While launching");
-            msg.append(intent.toString());
-            msg.append(": ");
-            msg.append(e.getMessage());
-            */
-            throw e;
-        }
-    }
-
-    int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
-        return mAmInternal.handleIncomingUser(callingPid, callingUid, userId, false /* allowAll */,
-                ALLOW_FULL_ONLY, name, null /* callerPackage */);
-    }
-
-    @Override
-    public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
-            Intent intent, String resolvedType, IVoiceInteractionSession session,
-            IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
-            Bundle bOptions, int userId) {
-        mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
-        if (session == null || interactor == null) {
-            throw new NullPointerException("null session or interactor");
-        }
-        userId = handleIncomingUser(callingPid, callingUid, userId, "startVoiceActivity");
-        // TODO: Switch to user app stacks here.
-        return getActivityStartController().obtainStarter(intent, "startVoiceActivity")
-                .setCallingUid(callingUid)
-                .setCallingPackage(callingPackage)
-                .setResolvedType(resolvedType)
-                .setVoiceSession(session)
-                .setVoiceInteractor(interactor)
-                .setStartFlags(startFlags)
-                .setProfilerInfo(profilerInfo)
-                .setActivityOptions(bOptions)
-                .setMayWait(userId)
-                .execute();
-    }
-
-    @Override
-    public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
-            Intent intent, String resolvedType, Bundle bOptions, int userId) {
-        mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
-        userId = handleIncomingUser(callingPid, callingUid, userId, "startAssistantActivity");
-
-        return getActivityStartController().obtainStarter(intent, "startAssistantActivity")
-                .setCallingUid(callingUid)
-                .setCallingPackage(callingPackage)
-                .setResolvedType(resolvedType)
-                .setActivityOptions(bOptions)
-                .setMayWait(userId)
-                .execute();
-    }
-
-    @Override
-    public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
-            IRecentsAnimationRunner recentsAnimationRunner) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
-        final int callingPid = Binder.getCallingPid();
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
-                final int recentsUid = mRecentTasks.getRecentsComponentUid();
-
-                // Start a new recents animation
-                final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
-                        getActivityStartController(), mWindowManager, callingPid);
-                anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
-                        recentsUid, assistDataReceiver);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public final int startActivityFromRecents(int taskId, Bundle bOptions) {
-        enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
-                "startActivityFromRecents()");
-
-        final int callingPid = Binder.getCallingPid();
-        final int callingUid = Binder.getCallingUid();
-        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                return mStackSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
-                        safeOptions);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    /**
-     * This is the internal entry point for handling Activity.finish().
-     *
-     * @param token The Binder token referencing the Activity we want to finish.
-     * @param resultCode Result code, if any, from this Activity.
-     * @param resultData Result data (Intent), if any, from this Activity.
-     * @param finishTask Whether to finish the task associated with this Activity.
-     *
-     * @return Returns true if the activity successfully finished, or false if it is still running.
-     */
-    @Override
-    public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
-            int finishTask) {
-        // Refuse possible leaked file descriptors
-        if (resultData != null && resultData.hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-
-        synchronized (mGlobalLock) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return true;
-            }
-            // Keep track of the root activity of the task before we finish it
-            TaskRecord tr = r.getTask();
-            ActivityRecord rootR = tr.getRootActivity();
-            if (rootR == null) {
-                Slog.w(TAG, "Finishing task with all activities already finished");
-            }
-            // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can
-            // finish.
-            if (getLockTaskController().activityBlockedFromFinish(r)) {
-                return false;
-            }
-
-            // TODO: There is a dup. of this block of code in ActivityStack.navigateUpToLocked
-            // We should consolidate.
-            if (mController != null) {
-                // Find the first activity that is not finishing.
-                ActivityRecord next = r.getStack().topRunningActivityLocked(token, 0);
-                if (next != null) {
-                    // ask watcher if this is allowed
-                    boolean resumeOK = true;
-                    try {
-                        resumeOK = mController.activityResuming(next.packageName);
-                    } catch (RemoteException e) {
-                        mController = null;
-                        Watchdog.getInstance().setActivityController(null);
-                    }
-
-                    if (!resumeOK) {
-                        Slog.i(TAG, "Not finishing activity because controller resumed");
-                        return false;
-                    }
-                }
-            }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                boolean res;
-                final boolean finishWithRootActivity =
-                        finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
-                if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
-                        || (finishWithRootActivity && r == rootR)) {
-                    // If requested, remove the task that is associated to this activity only if it
-                    // was the root activity in the task. The result code and data is ignored
-                    // because we don't support returning them across task boundaries. Also, to
-                    // keep backwards compatibility we remove the task from recents when finishing
-                    // task with root activity.
-                    res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
-                            finishWithRootActivity, "finish-activity");
-                    if (!res) {
-                        Slog.i(TAG, "Removing task failed to finish activity");
-                    }
-                    // Explicitly dismissing the activity so reset its relaunch flag.
-                    r.mRelaunchReason = RELAUNCH_REASON_NONE;
-                } else {
-                    res = tr.getStack().requestFinishActivityLocked(token, resultCode,
-                            resultData, "app-request", true);
-                    if (!res) {
-                        Slog.i(TAG, "Failed to finish by app-request");
-                    }
-                }
-                return res;
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public boolean finishActivityAffinity(IBinder token) {
-        synchronized (mGlobalLock) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r == null) {
-                    return false;
-                }
-
-                // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
-                // can finish.
-                final TaskRecord task = r.getTask();
-                if (getLockTaskController().activityBlockedFromFinish(r)) {
-                    return false;
-                }
-                return task.getStack().finishActivityAffinityLocked(r);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            WindowProcessController proc = null;
-            synchronized (mGlobalLock) {
-                ActivityStack stack = ActivityRecord.getStackLocked(token);
-                if (stack == null) {
-                    return;
-                }
-                final ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token,
-                        false /* fromTimeout */, false /* processPausingActivities */, config);
-                if (r != null) {
-                    proc = r.app;
-                }
-                if (stopProfiling && proc != null) {
-                    proc.clearProfilerIfNeeded();
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public final void activityResumed(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        synchronized (mGlobalLock) {
-            ActivityRecord.activityResumedLocked(token);
-            mWindowManager.notifyAppResumedFinished(token);
-        }
-        Binder.restoreCallingIdentity(origId);
-    }
-
-    @Override
-    public final void activityPaused(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        synchronized (mGlobalLock) {
-            ActivityStack stack = ActivityRecord.getStackLocked(token);
-            if (stack != null) {
-                stack.activityPausedLocked(token, false);
-            }
-        }
-        Binder.restoreCallingIdentity(origId);
-    }
-
-    @Override
-    public final void activityStopped(IBinder token, Bundle icicle,
-            PersistableBundle persistentState, CharSequence description) {
-        if (DEBUG_ALL) Slog.v(TAG, "Activity stopped: token=" + token);
-
-        // Refuse possible leaked file descriptors
-        if (icicle != null && icicle.hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Bundle");
-        }
-
-        final long origId = Binder.clearCallingIdentity();
-
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r != null) {
-                r.activityStoppedLocked(icicle, persistentState, description);
-            }
-        }
-
-        mAmInternal.trimApplications();
-
-        Binder.restoreCallingIdentity(origId);
-    }
-
-    @Override
-    public final void activityDestroyed(IBinder token) {
-        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
-        synchronized (mGlobalLock) {
-            ActivityStack stack = ActivityRecord.getStackLocked(token);
-            if (stack != null) {
-                stack.activityDestroyedLocked(token, "activityDestroyed");
-            }
-        }
-    }
-
-    @Override
-    public final void activityRelaunched(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        synchronized (mGlobalLock) {
-            mStackSupervisor.activityRelaunchedLocked(token);
-        }
-        Binder.restoreCallingIdentity(origId);
-    }
-
-    public final void activitySlept(IBinder token) {
-        if (DEBUG_ALL) Slog.v(TAG, "Activity slept: token=" + token);
-
-        final long origId = Binder.clearCallingIdentity();
-
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r != null) {
-                mStackSupervisor.activitySleptLocked(r);
-            }
-        }
-
-        Binder.restoreCallingIdentity(origId);
-    }
-
-    @Override
-    public void setRequestedOrientation(IBinder token, int requestedOrientation) {
-        synchronized (mGlobalLock) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return;
-            }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                r.setRequestedOrientation(requestedOrientation);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public int getRequestedOrientation(IBinder token) {
-        synchronized (mGlobalLock) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-            }
-            return r.getRequestedOrientation();
-        }
-    }
-
-    @Override
-    public void setImmersive(IBinder token, boolean immersive) {
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                throw new IllegalArgumentException();
-            }
-            r.immersive = immersive;
-
-            // update associated state if we're frontmost
-            if (r.isResumedActivityOnDisplay()) {
-                if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
-                applyUpdateLockStateLocked(r);
-            }
-        }
-    }
-
-    void applyUpdateLockStateLocked(ActivityRecord r) {
-        // Modifications to the UpdateLock state are done on our handler, outside
-        // the activity manager's locks.  The new state is determined based on the
-        // state *now* of the relevant activity record.  The object is passed to
-        // the handler solely for logging detail, not to be consulted/modified.
-        final boolean nextState = r != null && r.immersive;
-        mH.post(() -> {
-            if (mUpdateLock.isHeld() != nextState) {
-                if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE,
-                        "Applying new update lock state '" + nextState + "' for " + r);
-                if (nextState) {
-                    mUpdateLock.acquire();
-                } else {
-                    mUpdateLock.release();
-                }
-            }
-        });
-    }
-
-    @Override
-    public boolean isImmersive(IBinder token) {
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                throw new IllegalArgumentException();
-            }
-            return r.immersive;
-        }
-    }
-
-    @Override
-    public boolean isTopActivityImmersive() {
-        enforceNotIsolatedCaller("isTopActivityImmersive");
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
-            return (r != null) ? r.immersive : false;
-        }
-    }
-
-    @Override
-    public void overridePendingTransition(IBinder token, String packageName,
-            int enterAnim, int exitAnim) {
-        synchronized (mGlobalLock) {
-            ActivityRecord self = ActivityRecord.isInStackLocked(token);
-            if (self == null) {
-                return;
-            }
-
-            final long origId = Binder.clearCallingIdentity();
-
-            if (self.isState(
-                    ActivityStack.ActivityState.RESUMED, ActivityStack.ActivityState.PAUSING)) {
-                mWindowManager.overridePendingAppTransition(packageName,
-                        enterAnim, exitAnim, null);
-            }
-
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public int getFrontActivityScreenCompatMode() {
-        enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
-            if (r == null) {
-                return ActivityManager.COMPAT_MODE_UNKNOWN;
-            }
-            return mCompatModePackages.computeCompatModeLocked(r.info.applicationInfo);
-        }
-    }
-
-    @Override
-    public void setFrontActivityScreenCompatMode(int mode) {
-        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
-                "setFrontActivityScreenCompatMode");
-        ApplicationInfo ai;
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
-            if (r == null) {
-                Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
-                return;
-            }
-            ai = r.info.applicationInfo;
-            mCompatModePackages.setPackageScreenCompatModeLocked(ai, mode);
-        }
-    }
-
-    @Override
-    public int getLaunchedFromUid(IBinder activityToken) {
-        ActivityRecord srec;
-        synchronized (mGlobalLock) {
-            srec = ActivityRecord.forTokenLocked(activityToken);
-        }
-        if (srec == null) {
-            return -1;
-        }
-        return srec.launchedFromUid;
-    }
-
-    @Override
-    public String getLaunchedFromPackage(IBinder activityToken) {
-        ActivityRecord srec;
-        synchronized (mGlobalLock) {
-            srec = ActivityRecord.forTokenLocked(activityToken);
-        }
-        if (srec == null) {
-            return null;
-        }
-        return srec.launchedFromPackage;
-    }
-
-    @Override
-    public boolean convertFromTranslucent(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r == null) {
-                    return false;
-                }
-                final boolean translucentChanged = r.changeWindowTranslucency(true);
-                if (translucentChanged) {
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                }
-                mWindowManager.setAppFullscreen(token, true);
-                return translucentChanged;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public boolean convertToTranslucent(IBinder token, Bundle options) {
-        SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r == null) {
-                    return false;
-                }
-                final TaskRecord task = r.getTask();
-                int index = task.mActivities.lastIndexOf(r);
-                if (index > 0) {
-                    ActivityRecord under = task.mActivities.get(index - 1);
-                    under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
-                }
-                final boolean translucentChanged = r.changeWindowTranslucency(false);
-                if (translucentChanged) {
-                    r.getStack().convertActivityToTranslucent(r);
-                }
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                mWindowManager.setAppFullscreen(token, false);
-                return translucentChanged;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public void notifyActivityDrawn(IBinder token) {
-        if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
-        synchronized (mGlobalLock) {
-            ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token);
-            if (r != null) {
-                r.getStack().notifyActivityDrawnLocked(r);
-            }
-        }
-    }
-
-    @Override
-    public void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) {
-        synchronized (mGlobalLock) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return;
-            }
-            r.reportFullyDrawnLocked(restoredFromBundle);
-        }
-    }
-
-    @Override
-    public int getActivityDisplayId(IBinder activityToken) throws RemoteException {
-        synchronized (mGlobalLock) {
-            final ActivityStack stack = ActivityRecord.getStackLocked(activityToken);
-            if (stack != null && stack.mDisplayId != INVALID_DISPLAY) {
-                return stack.mDisplayId;
-            }
-            return DEFAULT_DISPLAY;
-        }
-    }
-
-    @Override
-    public ActivityManager.StackInfo getFocusedStackInfo() throws RemoteException {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                ActivityStack focusedStack = getTopDisplayFocusedStack();
-                if (focusedStack != null) {
-                    return mStackSupervisor.getStackInfo(focusedStack.mStackId);
-                }
-                return null;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void setFocusedStack(int stackId) {
-        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
-        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
-                if (stack == null) {
-                    Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
-                    return;
-                }
-                final ActivityRecord r = stack.topRunningActivityLocked();
-                if (r != null && r.moveFocusableActivityToTop("setFocusedStack")) {
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-    }
-
-    @Override
-    public void setFocusedTask(int taskId) {
-        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
-        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task == null) {
-                    return;
-                }
-                final ActivityRecord r = task.topRunningActivityLocked();
-                if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) {
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-    }
-
-    @Override
-    public boolean removeTask(int taskId) {
-        enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS,
-                        "remove-task");
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void removeAllVisibleRecentTasks() {
-        enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                getRecentTasks().removeAllVisibleTasks();
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public boolean shouldUpRecreateTask(IBinder token, String destAffinity) {
-        synchronized (mGlobalLock) {
-            final ActivityRecord srec = ActivityRecord.forTokenLocked(token);
-            if (srec != null) {
-                return srec.getStack().shouldUpRecreateTaskLocked(srec, destAffinity);
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
-            Intent resultData) {
-
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            if (r != null) {
-                return r.getStack().navigateUpToLocked(r, destIntent, resultCode, resultData);
-            }
-            return false;
-        }
-    }
-
-    /**
-     * Attempts to move a task backwards in z-order (the order of activities within the task is
-     * unchanged).
-     *
-     * There are several possible results of this call:
-     * - if the task is locked, then we will show the lock toast
-     * - if there is a task behind the provided task, then that task is made visible and resumed as
-     *   this task is moved to the back
-     * - otherwise, if there are no other tasks in the stack:
-     *     - if this task is in the pinned stack, then we remove the stack completely, which will
-     *       have the effect of moving the task to the top or bottom of the fullscreen stack
-     *       (depending on whether it is visible)
-     *     - otherwise, we simply return home and hide this task
-     *
-     * @param token A reference to the activity we wish to move
-     * @param nonRoot If false then this only works if the activity is the root
-     *                of a task; if true it will work for any activity in a task.
-     * @return Returns true if the move completed, false if not.
-     */
-    @Override
-    public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
-        enforceNotIsolatedCaller("moveActivityTaskToBack");
-        synchronized (mGlobalLock) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task != null) {
-                    return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public Rect getTaskBounds(int taskId) {
-        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()");
-        long ident = Binder.clearCallingIdentity();
-        Rect rect = new Rect();
-        try {
-            synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
-                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-                if (task == null) {
-                    Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
-                    return rect;
-                }
-                if (task.getStack() != null) {
-                    // Return the bounds from window manager since it will be adjusted for various
-                    // things like the presense of a docked stack for tasks that aren't resizeable.
-                    task.getWindowContainerBounds(rect);
-                } else {
-                    // Task isn't in window manager yet since it isn't associated with a stack.
-                    // Return the persist value from activity manager
-                    if (!task.matchParentBounds()) {
-                        rect.set(task.getBounds());
-                    } else if (task.mLastNonFullscreenBounds != null) {
-                        rect.set(task.mLastNonFullscreenBounds);
-                    }
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-        return rect;
-    }
-
-    @Override
-    public ActivityManager.TaskDescription getTaskDescription(int id) {
-        synchronized (mGlobalLock) {
-            enforceCallerIsRecentsOrHasPermission(
-                    MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
-            final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
-                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-            if (tr != null) {
-                return tr.lastTaskDescription;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
-        if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
-                    toTop, ANIMATE, null /* initialBounds */, true /* showRecents */);
-            return;
-        }
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task == null) {
-                    Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
-                    return;
-                }
-
-                if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId
-                        + " to windowingMode=" + windowingMode + " toTop=" + toTop);
-
-                if (!task.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
-                            + " non-standard task " + taskId + " to windowing mode="
-                            + windowingMode);
-                }
-
-                final ActivityStack stack = task.getStack();
-                if (toTop) {
-                    stack.moveToFront("setTaskWindowingMode", task);
-                }
-                stack.setWindowingMode(windowingMode);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public String getCallingPackage(IBinder token) {
-        synchronized (mGlobalLock) {
-            ActivityRecord r = getCallingRecordLocked(token);
-            return r != null ? r.info.packageName : null;
-        }
-    }
-
-    @Override
-    public ComponentName getCallingActivity(IBinder token) {
-        synchronized (mGlobalLock) {
-            ActivityRecord r = getCallingRecordLocked(token);
-            return r != null ? r.intent.getComponent() : null;
-        }
-    }
-
-    private ActivityRecord getCallingRecordLocked(IBinder token) {
-        ActivityRecord r = ActivityRecord.isInStackLocked(token);
-        if (r == null) {
-            return null;
-        }
-        return r.resultTo;
-    }
-
-    @Override
-    public void unhandledBack() {
-        mAmInternal.enforceCallingPermission(android.Manifest.permission.FORCE_BACK, "unhandledBack()");
-
-        synchronized (mGlobalLock) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                getTopDisplayFocusedStack().unhandledBackLocked();
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    /**
-     * TODO: Add mController hook
-     */
-    @Override
-    public void moveTaskToFront(int taskId, int flags, Bundle bOptions) {
-        mAmInternal.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()");
-
-        if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
-        synchronized (mGlobalLock) {
-            moveTaskToFrontLocked(taskId, flags, SafeActivityOptions.fromBundle(bOptions),
-                    false /* fromRecents */);
-        }
-    }
-
-    void moveTaskToFrontLocked(int taskId, int flags, SafeActivityOptions options,
-            boolean fromRecents) {
-
-        if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
-                Binder.getCallingUid(), -1, -1, "Task to front")) {
-            SafeActivityOptions.abort(options);
-            return;
-        }
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-            if (task == null) {
-                Slog.d(TAG, "Could not find task for id: "+ taskId);
-                SafeActivityOptions.abort(options);
-                return;
-            }
-            if (getLockTaskController().isLockTaskModeViolation(task)) {
-                Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
-                SafeActivityOptions.abort(options);
-                return;
-            }
-            ActivityOptions realOptions = options != null
-                    ? options.getOptions(mStackSupervisor)
-                    : null;
-            mStackSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
-                    false /* forceNonResizable */);
-
-            final ActivityRecord topActivity = task.getTopActivity();
-            if (topActivity != null) {
-
-                // We are reshowing a task, use a starting window to hide the initial draw delay
-                // so the transition can start earlier.
-                topActivity.showStartingWindow(null /* prev */, false /* newTask */,
-                        true /* taskSwitch */, fromRecents);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
-            int callingPid, int callingUid, String name) {
-        if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
-            return true;
-        }
-
-        if (getRecentTasks().isCallerRecents(sourceUid)) {
-            return true;
-        }
-
-        int perm = checkComponentPermission(STOP_APP_SWITCHES, sourcePid, sourceUid, -1, true);
-        if (perm == PackageManager.PERMISSION_GRANTED) {
-            return true;
-        }
-        if (checkAllowAppSwitchUid(sourceUid)) {
-            return true;
-        }
-
-        // If the actual IPC caller is different from the logical source, then
-        // also see if they are allowed to control app switches.
-        if (callingUid != -1 && callingUid != sourceUid) {
-            perm = checkComponentPermission(STOP_APP_SWITCHES, callingPid, callingUid, -1, true);
-            if (perm == PackageManager.PERMISSION_GRANTED) {
-                return true;
-            }
-            if (checkAllowAppSwitchUid(callingUid)) {
-                return true;
-            }
-        }
-
-        Slog.w(TAG, name + " request from " + sourceUid + " stopped");
-        return false;
-    }
-
-    private boolean checkAllowAppSwitchUid(int uid) {
-        ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(UserHandle.getUserId(uid));
-        if (types != null) {
-            for (int i = types.size() - 1; i >= 0; i--) {
-                if (types.valueAt(i).intValue() == uid) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void setActivityController(IActivityController controller, boolean imAMonkey) {
-        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
-                "setActivityController()");
-        synchronized (mGlobalLock) {
-            mController = controller;
-            mControllerIsAMonkey = imAMonkey;
-            Watchdog.getInstance().setActivityController(controller);
-        }
-    }
-
-    boolean isControllerAMonkey() {
-        synchronized (mGlobalLock) {
-            return mController != null && mControllerIsAMonkey;
-        }
-    }
-
-    @Override
-    public int getTaskForActivity(IBinder token, boolean onlyRoot) {
-        synchronized (mGlobalLock) {
-            return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
-        }
-    }
-
-    @Override
-    public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
-        return getFilteredTasks(maxNum, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED);
-    }
-
-    @Override
-    public List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum,
-            @WindowConfiguration.ActivityType int ignoreActivityType,
-            @WindowConfiguration.WindowingMode int ignoreWindowingMode) {
-        final int callingUid = Binder.getCallingUid();
-        ArrayList<ActivityManager.RunningTaskInfo> list = new ArrayList<>();
-
-        synchronized (mGlobalLock) {
-            if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum);
-
-            final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
-                    callingUid);
-            mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
-                    ignoreWindowingMode, callingUid, allowed);
-        }
-
-        return list;
-    }
-
-    @Override
-    public final void finishSubActivity(IBinder token, String resultWho, int requestCode) {
-        synchronized (mGlobalLock) {
-            final long origId = Binder.clearCallingIdentity();
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r != null) {
-                r.getStack().finishSubActivityLocked(r, resultWho, requestCode);
-            }
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public boolean willActivityBeVisible(IBinder token) {
-        synchronized (mGlobalLock) {
-            ActivityStack stack = ActivityRecord.getStackLocked(token);
-            if (stack != null) {
-                return stack.willActivityBeVisibleLocked(token);
-            }
-            return false;
-        }
-    }
-
-    @Override
-    public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task == null) {
-                    Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
-                    return;
-                }
-
-                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
-                        + " to stackId=" + stackId + " toTop=" + toTop);
-
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
-                if (stack == null) {
-                    throw new IllegalStateException(
-                            "moveTaskToStack: No stack for stackId=" + stackId);
-                }
-                if (!stack.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
-                            + taskId + " to stack " + stackId);
-                }
-                if (stack.inSplitScreenPrimaryWindowingMode()) {
-                    mWindowManager.setDockedStackCreateState(
-                            SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
-                }
-                task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
-                        "moveTaskToStack");
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode,
-            boolean preserveWindows, boolean animate, int animationDuration) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
-
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                if (animate) {
-                    final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
-                    if (stack == null) {
-                        Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
-                        return;
-                    }
-                    if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
-                        throw new IllegalArgumentException("Stack: " + stackId
-                                + " doesn't support animated resize.");
-                    }
-                    stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
-                            animationDuration, false /* fromFullscreen */);
-                } else {
-                    final ActivityStack stack = mStackSupervisor.getStack(stackId);
-                    if (stack == null) {
-                        Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
-                        return;
-                    }
-                    mStackSupervisor.resizeStackLocked(stack, destBounds,
-                            null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
-                            preserveWindows, allowResizeInDockedMode, !DEFER_RESUME);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    /**
-     * Moves the specified task to the primary-split-screen stack.
-     *
-     * @param taskId Id of task to move.
-     * @param createMode The mode the primary split screen stack should be created in if it doesn't
-     *                   exist already. See
-     *                   {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
-     *                   and
-     *                   {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
-     * @param toTop If the task and stack should be moved to the top.
-     * @param animate Whether we should play an animation for the moving the task.
-     * @param initialBounds If the primary stack gets created, it will use these bounds for the
-     *                      stack. Pass {@code null} to use default bounds.
-     * @param showRecents If the recents activity should be shown on the other side of the task
-     *                    going into split-screen mode.
-     */
-    @Override
-    public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
-            boolean toTop, boolean animate, Rect initialBounds, boolean showRecents) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "setTaskWindowingModeSplitScreenPrimary()");
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task == null) {
-                    Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
-                    return false;
-                }
-                if (DEBUG_STACK) Slog.d(TAG_STACK,
-                        "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
-                                + " to createMode=" + createMode + " toTop=" + toTop);
-                if (!task.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
-                            + " non-standard task " + taskId + " to split-screen windowing mode");
-                }
-
-                mWindowManager.setDockedStackCreateState(createMode, initialBounds);
-                final int windowingMode = task.getWindowingMode();
-                final ActivityStack stack = task.getStack();
-                if (toTop) {
-                    stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
-                }
-                stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
-                        false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */);
-                return windowingMode != task.getWindowingMode();
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
-     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
-     */
-    @Override
-    public void removeStacksInWindowingModes(int[] windowingModes) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "removeStacksInWindowingModes()");
-
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mStackSupervisor.removeStacksInWindowingModes(windowingModes);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void removeStacksWithActivityTypes(int[] activityTypes) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "removeStacksWithActivityTypes()");
-
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mStackSupervisor.removeStacksWithActivityTypes(activityTypes);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
-            int userId) {
-        final int callingUid = Binder.getCallingUid();
-        userId = handleIncomingUser(Binder.getCallingPid(), callingUid, userId, "getRecentTasks");
-        final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
-                callingUid);
-        final boolean detailed = checkGetTasksPermission(
-                android.Manifest.permission.GET_DETAILED_TASKS, Binder.getCallingPid(),
-                UserHandle.getAppId(callingUid))
-                == PackageManager.PERMISSION_GRANTED;
-
-        synchronized (mGlobalLock) {
-            return mRecentTasks.getRecentTasks(maxNum, flags, allowed, detailed, userId,
-                    callingUid);
-        }
-    }
-
-    @Override
-    public List<ActivityManager.StackInfo> getAllStackInfos() {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                return mStackSupervisor.getAllStackInfosLocked();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                return mStackSupervisor.getStackInfo(windowingMode, activityType);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
-        final long callingUid = Binder.getCallingUid();
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                // Cancel the recents animation synchronously (do not hold the WM lock)
-                mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
-                        ? REORDER_MOVE_TO_ORIGINAL_POSITION
-                        : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public void startLockTaskModeByToken(IBinder token) {
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            if (r == null) {
-                return;
-            }
-            startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */);
-        }
-    }
-
-    @Override
-    public void startSystemLockTaskMode(int taskId) throws RemoteException {
-        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
-        // This makes inner call to look as if it was initiated by system.
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-
-                // When starting lock task mode the stack must be in front and focused
-                task.getStack().moveToFront("startSystemLockTaskMode");
-                startLockTaskModeLocked(task, true /* isSystemCaller */);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void stopLockTaskModeByToken(IBinder token) {
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            if (r == null) {
-                return;
-            }
-            stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */);
-        }
-    }
-
-    /**
-     * This API should be called by SystemUI only when user perform certain action to dismiss
-     * lock task mode. We should only dismiss pinned lock task mode in this case.
-     */
-    @Override
-    public void stopSystemLockTaskMode() throws RemoteException {
-        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
-        stopLockTaskModeInternal(null, true /* isSystemCaller */);
-    }
-
-    private void startLockTaskModeLocked(@Nullable TaskRecord task, boolean isSystemCaller) {
-        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task);
-        if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
-            return;
-        }
-
-        final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
-        if (stack == null || task != stack.topTask()) {
-            throw new IllegalArgumentException("Invalid task, not in foreground");
-        }
-
-        // {@code isSystemCaller} is used to distinguish whether this request is initiated by the
-        // system or a specific app.
-        // * System-initiated requests will only start the pinned mode (screen pinning)
-        // * App-initiated requests
-        //   - will put the device in fully locked mode (LockTask), if the app is whitelisted
-        //   - will start the pinned mode, otherwise
-        final int callingUid = Binder.getCallingUid();
-        long ident = Binder.clearCallingIdentity();
-        try {
-            // When a task is locked, dismiss the pinned stack if it exists
-            mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
-
-            getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    private void stopLockTaskModeInternal(@Nullable TaskRecord task, boolean isSystemCaller) {
-        final int callingUid = Binder.getCallingUid();
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                getLockTaskController().stopLockTaskMode(task, isSystemCaller, callingUid);
-            }
-            // Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
-            // task and jumping straight into a call in the case of emergency call back.
-            TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
-            if (tm != null) {
-                tm.showInCallScreen(false);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public boolean isInLockTaskMode() {
-        return getLockTaskModeState() != LOCK_TASK_MODE_NONE;
-    }
-
-    @Override
-    public int getLockTaskModeState() {
-        synchronized (mGlobalLock) {
-            return getLockTaskController().getLockTaskModeState();
-        }
-    }
-
-    @Override
-    public void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) {
-        synchronized (mGlobalLock) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r != null) {
-                r.setTaskDescription(td);
-                final TaskRecord task = r.getTask();
-                task.updateTaskDescription();
-                mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td);
-            }
-        }
-    }
-
-    @Override
-    public Bundle getActivityOptions(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r != null) {
-                    final ActivityOptions activityOptions = r.takeOptionsLocked();
-                    return activityOptions == null ? null : activityOptions.toBundle();
-                }
-                return null;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public List<IBinder> getAppTasks(String callingPackage) {
-        int callingUid = Binder.getCallingUid();
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                return mRecentTasks.getAppTasksList(callingUid, callingPackage);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void finishVoiceTask(IVoiceInteractionSession session) {
-        synchronized (mGlobalLock) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                // TODO: VI Consider treating local voice interactions and voice tasks
-                // differently here
-                mStackSupervisor.finishVoiceTask(session);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-
-    }
-
-    @Override
-    public boolean isTopOfTask(IBinder token) {
-        synchronized (mGlobalLock) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            return r != null && r.getTask().getTopActivity() == r;
-        }
-    }
-
-    @Override
-    public void notifyLaunchTaskBehindComplete(IBinder token) {
-        mStackSupervisor.scheduleLaunchTaskBehindComplete(token);
-    }
-
-    @Override
-    public void notifyEnterAnimationComplete(IBinder token) {
-        mH.post(() -> {
-            synchronized (mGlobalLock) {
-                ActivityRecord r = ActivityRecord.forTokenLocked(token);
-                if (r != null && r.attachedToProcess()) {
-                    try {
-                        r.app.getThread().scheduleEnterAnimationComplete(r.appToken);
-                    } catch (RemoteException e) {
-                    }
-                }
-            }
-
-        });
-    }
-
-    /** Called from an app when assist data is ready. */
-    @Override
-    public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
-            AssistContent content, Uri referrer) {
-        PendingAssistExtras pae = (PendingAssistExtras) token;
-        synchronized (pae) {
-            pae.result = extras;
-            pae.structure = structure;
-            pae.content = content;
-            if (referrer != null) {
-                pae.extras.putParcelable(Intent.EXTRA_REFERRER, referrer);
-            }
-            if (structure != null) {
-                structure.setHomeActivity(pae.isHome);
-            }
-            pae.haveResult = true;
-            pae.notifyAll();
-            if (pae.intent == null && pae.receiver == null) {
-                // Caller is just waiting for the result.
-                return;
-            }
-        }
-        // We are now ready to launch the assist activity.
-        IAssistDataReceiver sendReceiver = null;
-        Bundle sendBundle = null;
-        synchronized (mGlobalLock) {
-            buildAssistBundleLocked(pae, extras);
-            boolean exists = mPendingAssistExtras.remove(pae);
-            mUiHandler.removeCallbacks(pae);
-            if (!exists) {
-                // Timed out.
-                return;
-            }
-
-            if ((sendReceiver = pae.receiver) != null) {
-                // Caller wants result sent back to them.
-                sendBundle = new Bundle();
-                sendBundle.putBundle(ASSIST_KEY_DATA, pae.extras);
-                sendBundle.putParcelable(ASSIST_KEY_STRUCTURE, pae.structure);
-                sendBundle.putParcelable(ASSIST_KEY_CONTENT, pae.content);
-                sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
-            }
-        }
-        if (sendReceiver != null) {
-            try {
-                sendReceiver.onHandleAssistData(sendBundle);
-            } catch (RemoteException e) {
-            }
-            return;
-        }
-
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            if (TextUtils.equals(pae.intent.getAction(),
-                    android.service.voice.VoiceInteractionService.SERVICE_INTERFACE)) {
-                pae.intent.putExtras(pae.extras);
-                mContext.startServiceAsUser(pae.intent, new UserHandle(pae.userHandle));
-            } else {
-                pae.intent.replaceExtras(pae.extras);
-                pae.intent.setFlags(FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_SINGLE_TOP
-                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-                mInternal.closeSystemDialogs("assist");
-
-                try {
-                    mContext.startActivityAsUser(pae.intent, new UserHandle(pae.userHandle));
-                } catch (ActivityNotFoundException e) {
-                    Slog.w(TAG, "No activity to handle assist action.", e);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public int addAppTask(IBinder activityToken, Intent intent,
-            ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException {
-        final int callingUid = Binder.getCallingUid();
-        final long callingIdent = Binder.clearCallingIdentity();
-
-        try {
-            synchronized (mGlobalLock) {
-                ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
-                if (r == null) {
-                    throw new IllegalArgumentException("Activity does not exist; token="
-                            + activityToken);
-                }
-                ComponentName comp = intent.getComponent();
-                if (comp == null) {
-                    throw new IllegalArgumentException("Intent " + intent
-                            + " must specify explicit component");
-                }
-                if (thumbnail.getWidth() != mThumbnailWidth
-                        || thumbnail.getHeight() != mThumbnailHeight) {
-                    throw new IllegalArgumentException("Bad thumbnail size: got "
-                            + thumbnail.getWidth() + "x" + thumbnail.getHeight() + ", require "
-                            + mThumbnailWidth + "x" + mThumbnailHeight);
-                }
-                if (intent.getSelector() != null) {
-                    intent.setSelector(null);
-                }
-                if (intent.getSourceBounds() != null) {
-                    intent.setSourceBounds(null);
-                }
-                if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0) {
-                    if ((intent.getFlags()&Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS) == 0) {
-                        // The caller has added this as an auto-remove task...  that makes no
-                        // sense, so turn off auto-remove.
-                        intent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
-                    }
-                }
-                final ActivityInfo ainfo = AppGlobals.getPackageManager().getActivityInfo(comp,
-                        STOCK_PM_FLAGS, UserHandle.getUserId(callingUid));
-                if (ainfo.applicationInfo.uid != callingUid) {
-                    throw new SecurityException(
-                            "Can't add task for another application: target uid="
-                                    + ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
-                }
-
-                final ActivityStack stack = r.getStack();
-                final TaskRecord task = stack.createTaskRecord(
-                        mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent,
-                        null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
-                if (!mRecentTasks.addToBottom(task)) {
-                    // The app has too many tasks already and we can't add any more
-                    stack.removeTask(task, "addAppTask", REMOVE_TASK_MODE_DESTROYING);
-                    return INVALID_TASK_ID;
-                }
-                task.lastTaskDescription.copyFrom(description);
-
-                // TODO: Send the thumbnail to WM to store it.
-
-                return task.taskId;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingIdent);
-        }
-    }
-
-    @Override
-    public Point getAppTaskThumbnailSize() {
-        synchronized (mGlobalLock) {
-            return new Point(mThumbnailWidth, mThumbnailHeight);
-        }
-    }
-
-    @Override
-    public void setTaskResizeable(int taskId, int resizeableMode) {
-        synchronized (mGlobalLock) {
-            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
-                    taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-            if (task == null) {
-                Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
-                return;
-            }
-            task.setResizeMode(resizeableMode);
-        }
-    }
-
-    @Override
-    public void resizeTask(int taskId, Rect bounds, int resizeMode) {
-        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task == null) {
-                    Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
-                    return;
-                }
-                // Place the task in the right stack if it isn't there already based on
-                // the requested bounds.
-                // The stack transition logic is:
-                // - a null bounds on a freeform task moves that task to fullscreen
-                // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
-                //   that task to freeform
-                // - otherwise the task is not moved
-                ActivityStack stack = task.getStack();
-                if (!task.getWindowConfiguration().canResizeTask()) {
-                    throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
-                }
-                if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
-                    stack = stack.getDisplay().getOrCreateStack(
-                            WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
-                } else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
-                    stack = stack.getDisplay().getOrCreateStack(
-                            WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP);
-                }
-
-                // Reparent the task to the right stack if necessary
-                boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
-                if (stack != task.getStack()) {
-                    // Defer resume until the task is resized below
-                    task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
-                            DEFER_RESUME, "resizeTask");
-                    preserveWindow = false;
-                }
-
-                // After reparenting (which only resizes the task to the stack bounds), resize the
-                // task to the actual bounds provided
-                task.resize(bounds, resizeMode, preserveWindow, !DEFER_RESUME);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public boolean releaseActivityInstance(IBinder token) {
-        synchronized (mGlobalLock) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r == null) {
-                    return false;
-                }
-                return r.getStack().safelyDestroyActivityLocked(r, "app-req");
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public void releaseSomeActivities(IApplicationThread appInt) {
-        synchronized (mGlobalLock) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                final WindowProcessController app = getProcessController(appInt);
-                mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
-            int secondaryDisplayShowing) {
-        if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires permission "
-                    + android.Manifest.permission.DEVICE_POWER);
-        }
-
-        synchronized (mGlobalLock) {
-            long ident = Binder.clearCallingIdentity();
-            if (mKeyguardShown != keyguardShowing) {
-                mKeyguardShown = keyguardShowing;
-                final Message msg = PooledLambda.obtainMessage(
-                        ActivityManagerInternal::reportCurKeyguardUsageEvent, mAmInternal,
-                        keyguardShowing);
-                mH.sendMessage(msg);
-            }
-            try {
-                mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
-                        secondaryDisplayShowing);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-
-        mH.post(() -> {
-            for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
-                mScreenObservers.get(i).onKeyguardStateChanged(keyguardShowing);
-            }
-        });
-    }
-
-    void onScreenAwakeChanged(boolean isAwake) {
-        mH.post(() -> {
-            for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
-                mScreenObservers.get(i).onAwakeStateChanged(isAwake);
-            }
-        });
-    }
-
-    @Override
-    public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
-        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
-                userId, "getTaskDescriptionIcon");
-
-        final File passedIconFile = new File(filePath);
-        final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
-                passedIconFile.getName());
-        if (!legitIconFile.getPath().equals(filePath)
-                || !filePath.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
-            throw new IllegalArgumentException("Bad file path: " + filePath
-                    + " passed for userId " + userId);
-        }
-        return mRecentTasks.getTaskDescriptionIcon(filePath);
-    }
-
-    @Override
-    public void startInPlaceAnimationOnFrontMostApplication(Bundle opts) {
-        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(opts);
-        final ActivityOptions activityOptions = safeOptions != null
-                ? safeOptions.getOptions(mStackSupervisor)
-                : null;
-        if (activityOptions == null
-                || activityOptions.getAnimationType() != ActivityOptions.ANIM_CUSTOM_IN_PLACE
-                || activityOptions.getCustomInPlaceResId() == 0) {
-            throw new IllegalArgumentException("Expected in-place ActivityOption " +
-                    "with valid animation");
-        }
-        mWindowManager.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
-        mWindowManager.overridePendingAppTransitionInPlace(activityOptions.getPackageName(),
-                activityOptions.getCustomInPlaceResId());
-        mWindowManager.executeAppTransition();
-    }
-
-    @Override
-    public void removeStack(int stackId) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
-                if (stack == null) {
-                    Slog.w(TAG, "removeStack: No stack with id=" + stackId);
-                    return;
-                }
-                if (!stack.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException(
-                            "Removing non-standard stack is not allowed.");
-                }
-                mStackSupervisor.removeStack(stack);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void moveStackToDisplay(int stackId, int displayId) {
-        mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
-
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveStackToDisplay: moving stackId=" + stackId
-                        + " to displayId=" + displayId);
-                mStackSupervisor.moveStackToDisplayLocked(stackId, displayId, ON_TOP);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void exitFreeformMode(IBinder token) {
-        synchronized (mGlobalLock) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-                if (r == null) {
-                    throw new IllegalArgumentException(
-                            "exitFreeformMode: No activity record matching token=" + token);
-                }
-
-                final ActivityStack stack = r.getStack();
-                if (stack == null || !stack.inFreeformWindowingMode()) {
-                    throw new IllegalStateException(
-                            "exitFreeformMode: You can only go fullscreen from freeform.");
-                }
-
-                stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    /** Sets the task stack listener that gets callbacks when a task stack changes. */
-    @Override
-    public void registerTaskStackListener(ITaskStackListener listener) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "registerTaskStackListener()");
-        mTaskChangeNotificationController.registerTaskStackListener(listener);
-    }
-
-    /** Unregister a task stack listener so that it stops receiving callbacks. */
-    @Override
-    public void unregisterTaskStackListener(ITaskStackListener listener) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "unregisterTaskStackListener()");
-        mTaskChangeNotificationController.unregisterTaskStackListener(listener);
-    }
-
-    @Override
-    public boolean requestAssistContextExtras(int requestType, IAssistDataReceiver receiver,
-            Bundle receiverExtras, IBinder activityToken, boolean focused, boolean newSessionId) {
-        return enqueueAssistContext(requestType, null, null, receiver, receiverExtras,
-                activityToken, focused, newSessionId, UserHandle.getCallingUserId(), null,
-                PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) != null;
-    }
-
-    @Override
-    public boolean requestAutofillData(IAssistDataReceiver receiver, Bundle receiverExtras,
-            IBinder activityToken, int flags) {
-        return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_AUTOFILL, null, null,
-                receiver, receiverExtras, activityToken, true, true, UserHandle.getCallingUserId(),
-                null, PENDING_AUTOFILL_ASSIST_STRUCTURE_TIMEOUT, flags) != null;
-    }
-
-    @Override
-    public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle,
-            Bundle args) {
-        return enqueueAssistContext(requestType, intent, hint, null, null, null,
-                true /* focused */, true /* newSessionId */, userHandle, args,
-                PENDING_ASSIST_EXTRAS_TIMEOUT, 0) != null;
-    }
-
-    @Override
-    public Bundle getAssistContextExtras(int requestType) {
-        PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null,
-                null, null, true /* focused */, true /* newSessionId */,
-                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT, 0);
-        if (pae == null) {
-            return null;
-        }
-        synchronized (pae) {
-            while (!pae.haveResult) {
-                try {
-                    pae.wait();
-                } catch (InterruptedException e) {
-                }
-            }
-        }
-        synchronized (mGlobalLock) {
-            buildAssistBundleLocked(pae, pae.result);
-            mPendingAssistExtras.remove(pae);
-            mUiHandler.removeCallbacks(pae);
-        }
-        return pae.extras;
-    }
-
-    /**
-     * Binder IPC calls go through the public entry point.
-     * This can be called with or without the global lock held.
-     */
-    private static int checkCallingPermission(String permission) {
-        return checkPermission(
-                permission, Binder.getCallingPid(), UserHandle.getAppId(Binder.getCallingUid()));
-    }
-
-    /** This can be called with or without the global lock held. */
-    private void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
-        if (!getRecentTasks().isCallerRecents(Binder.getCallingUid())) {
-            mAmInternal.enforceCallingPermission(permission, func);
-        }
-    }
-
-    @VisibleForTesting
-    int checkGetTasksPermission(String permission, int pid, int uid) {
-        return checkPermission(permission, pid, uid);
-    }
-
-    static int checkPermission(String permission, int pid, int uid) {
-        if (permission == null) {
-            return PackageManager.PERMISSION_DENIED;
-        }
-        return checkComponentPermission(permission, pid, uid, -1, true);
-    }
-
-    public static int checkComponentPermission(String permission, int pid, int uid,
-            int owningUid, boolean exported) {
-        return ActivityManagerService.checkComponentPermission(
-                permission, pid, uid, owningUid, exported);
-    }
-
-    boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
-        if (getRecentTasks().isCallerRecents(callingUid)) {
-            // Always allow the recents component to get tasks
-            return true;
-        }
-
-        boolean allowed = checkGetTasksPermission(android.Manifest.permission.REAL_GET_TASKS,
-                callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
-        if (!allowed) {
-            if (checkGetTasksPermission(android.Manifest.permission.GET_TASKS,
-                    callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) {
-                // Temporary compatibility: some existing apps on the system image may
-                // still be requesting the old permission and not switched to the new
-                // one; if so, we'll still allow them full access.  This means we need
-                // to see if they are holding the old permission and are a system app.
-                try {
-                    if (AppGlobals.getPackageManager().isUidPrivileged(callingUid)) {
-                        allowed = true;
-                        if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
-                                + " is using old GET_TASKS but privileged; allowing");
-                    }
-                } catch (RemoteException e) {
-                }
-            }
-            if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
-                    + " does not hold REAL_GET_TASKS; limiting output");
-        }
-        return allowed;
-    }
-
-    private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
-            IAssistDataReceiver receiver, Bundle receiverExtras, IBinder activityToken,
-            boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
-            int flags) {
-        mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
-                "enqueueAssistContext()");
-
-        synchronized (mGlobalLock) {
-            ActivityRecord activity = getTopDisplayFocusedStack().getTopActivity();
-            if (activity == null) {
-                Slog.w(TAG, "getAssistContextExtras failed: no top activity");
-                return null;
-            }
-            if (!activity.attachedToProcess()) {
-                Slog.w(TAG, "getAssistContextExtras failed: no process for " + activity);
-                return null;
-            }
-            if (focused) {
-                if (activityToken != null) {
-                    ActivityRecord caller = ActivityRecord.forTokenLocked(activityToken);
-                    if (activity != caller) {
-                        Slog.w(TAG, "enqueueAssistContext failed: caller " + caller
-                                + " is not current top " + activity);
-                        return null;
-                    }
-                }
-            } else {
-                activity = ActivityRecord.forTokenLocked(activityToken);
-                if (activity == null) {
-                    Slog.w(TAG, "enqueueAssistContext failed: activity for token=" + activityToken
-                            + " couldn't be found");
-                    return null;
-                }
-                if (!activity.attachedToProcess()) {
-                    Slog.w(TAG, "enqueueAssistContext failed: no process for " + activity);
-                    return null;
-                }
-            }
-
-            PendingAssistExtras pae;
-            Bundle extras = new Bundle();
-            if (args != null) {
-                extras.putAll(args);
-            }
-            extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName);
-            extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.mUid);
-
-            pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras,
-                    userHandle);
-            pae.isHome = activity.isActivityTypeHome();
-
-            // Increment the sessionId if necessary
-            if (newSessionId) {
-                mViSessionId++;
-            }
-            try {
-                activity.app.getThread().requestAssistContextExtras(activity.appToken, pae,
-                        requestType, mViSessionId, flags);
-                mPendingAssistExtras.add(pae);
-                mUiHandler.postDelayed(pae, timeout);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "getAssistContextExtras failed: crash calling " + activity);
-                return null;
-            }
-            return pae;
-        }
-    }
-
-    private void buildAssistBundleLocked(PendingAssistExtras pae, Bundle result) {
-        if (result != null) {
-            pae.extras.putBundle(Intent.EXTRA_ASSIST_CONTEXT, result);
-        }
-        if (pae.hint != null) {
-            pae.extras.putBoolean(pae.hint, true);
-        }
-    }
-
-    private void pendingAssistExtrasTimedOut(PendingAssistExtras pae) {
-        IAssistDataReceiver receiver;
-        synchronized (mGlobalLock) {
-            mPendingAssistExtras.remove(pae);
-            receiver = pae.receiver;
-        }
-        if (receiver != null) {
-            // Caller wants result sent back to them.
-            Bundle sendBundle = new Bundle();
-            // At least return the receiver extras
-            sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
-            try {
-                pae.receiver.onHandleAssistData(sendBundle);
-            } catch (RemoteException e) {
-            }
-        }
-    }
-
-    public class PendingAssistExtras extends Binder implements Runnable {
-        public final ActivityRecord activity;
-        public boolean isHome;
-        public final Bundle extras;
-        public final Intent intent;
-        public final String hint;
-        public final IAssistDataReceiver receiver;
-        public final int userHandle;
-        public boolean haveResult = false;
-        public Bundle result = null;
-        public AssistStructure structure = null;
-        public AssistContent content = null;
-        public Bundle receiverExtras;
-
-        public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent,
-                String _hint, IAssistDataReceiver _receiver, Bundle _receiverExtras,
-                int _userHandle) {
-            activity = _activity;
-            extras = _extras;
-            intent = _intent;
-            hint = _hint;
-            receiver = _receiver;
-            receiverExtras = _receiverExtras;
-            userHandle = _userHandle;
-        }
-
-        @Override
-        public void run() {
-            Slog.w(TAG, "getAssistContextExtras failed: timeout retrieving from " + activity);
-            synchronized (this) {
-                haveResult = true;
-                notifyAll();
-            }
-            pendingAssistExtrasTimedOut(this);
-        }
-    }
-
-    @Override
-    public boolean isAssistDataAllowedOnCurrentActivity() {
-        int userId;
-        synchronized (mGlobalLock) {
-            final ActivityStack focusedStack = getTopDisplayFocusedStack();
-            if (focusedStack == null || focusedStack.isActivityTypeAssistant()) {
-                return false;
-            }
-
-            final ActivityRecord activity = focusedStack.getTopActivity();
-            if (activity == null) {
-                return false;
-            }
-            userId = activity.userId;
-        }
-        return !DevicePolicyCache.getInstance().getScreenCaptureDisabled(userId);
-    }
-
-    @Override
-    public boolean showAssistFromActivity(IBinder token, Bundle args) {
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                ActivityRecord caller = ActivityRecord.forTokenLocked(token);
-                ActivityRecord top = getTopDisplayFocusedStack().getTopActivity();
-                if (top != caller) {
-                    Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
-                            + " is not current top " + top);
-                    return false;
-                }
-                if (!top.nowVisible) {
-                    Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
-                            + " is not visible");
-                    return false;
-                }
-            }
-            return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION, null,
-                    token);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public boolean isRootVoiceInteraction(IBinder token) {
-        synchronized (mGlobalLock) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return false;
-            }
-            return r.rootVoiceInteraction;
-        }
-    }
-
-    private void onLocalVoiceInteractionStartedLocked(IBinder activity,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
-        ActivityRecord activityToCallback = ActivityRecord.forTokenLocked(activity);
-        if (activityToCallback == null) return;
-        activityToCallback.setVoiceSessionLocked(voiceSession);
-
-        // Inform the activity
-        try {
-            activityToCallback.app.getThread().scheduleLocalVoiceInteractionStarted(activity,
-                    voiceInteractor);
-            long token = Binder.clearCallingIdentity();
-            try {
-                startRunningVoiceLocked(voiceSession, activityToCallback.appInfo.uid);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            // TODO: VI Should we cache the activity so that it's easier to find later
-            // rather than scan through all the stacks and activities?
-        } catch (RemoteException re) {
-            activityToCallback.clearVoiceSessionLocked();
-            // TODO: VI Should this terminate the voice session?
-        }
-    }
-
-    private void startRunningVoiceLocked(IVoiceInteractionSession session, int targetUid) {
-        Slog.d(TAG, "<<<  startRunningVoiceLocked()");
-        mVoiceWakeLock.setWorkSource(new WorkSource(targetUid));
-        if (mRunningVoice == null || mRunningVoice.asBinder() != session.asBinder()) {
-            boolean wasRunningVoice = mRunningVoice != null;
-            mRunningVoice = session;
-            if (!wasRunningVoice) {
-                mVoiceWakeLock.acquire();
-                updateSleepIfNeededLocked();
-            }
-        }
-    }
-
-    void finishRunningVoiceLocked() {
-        if (mRunningVoice != null) {
-            mRunningVoice = null;
-            mVoiceWakeLock.release();
-            updateSleepIfNeededLocked();
-        }
-    }
-
-    @Override
-    public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake) {
-        synchronized (mGlobalLock) {
-            if (mRunningVoice != null && mRunningVoice.asBinder() == session.asBinder()) {
-                if (keepAwake) {
-                    mVoiceWakeLock.acquire();
-                } else {
-                    mVoiceWakeLock.release();
-                }
-            }
-        }
-    }
-
-    @Override
-    public ComponentName getActivityClassForToken(IBinder token) {
-        synchronized (mGlobalLock) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return null;
-            }
-            return r.intent.getComponent();
-        }
-    }
-
-    @Override
-    public String getPackageForToken(IBinder token) {
-        synchronized (mGlobalLock) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return null;
-            }
-            return r.packageName;
-        }
-    }
-
-    @Override
-    public void showLockTaskEscapeMessage(IBinder token) {
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            if (r == null) {
-                return;
-            }
-            getLockTaskController().showLockTaskToast();
-        }
-    }
-
-    @Override
-    public void keyguardGoingAway(int flags) {
-        enforceNotIsolatedCaller("keyguardGoingAway");
-        final long token = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                mKeyguardController.keyguardGoingAway(flags);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Try to place task to provided position. The final position might be different depending on
-     * current user and stacks state. The task will be moved to target stack if it's currently in
-     * different stack.
-     */
-    @Override
-    public void positionTaskInStack(int taskId, int stackId, int position) {
-        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
-        synchronized (mGlobalLock) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task="
-                        + taskId + " in stackId=" + stackId + " at position=" + position);
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task == null) {
-                    throw new IllegalArgumentException("positionTaskInStack: no task for id="
-                            + taskId);
-                }
-
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
-
-                if (stack == null) {
-                    throw new IllegalArgumentException("positionTaskInStack: no stack for id="
-                            + stackId);
-                }
-                if (!stack.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException("positionTaskInStack: Attempt to change"
-                            + " the position of task " + taskId + " in/to non-standard stack");
-                }
-
-                // TODO: Have the callers of this API call a separate reparent method if that is
-                // what they intended to do vs. having this method also do reparenting.
-                if (task.getStack() == stack) {
-                    // Change position in current stack.
-                    stack.positionChildAt(task, position);
-                } else {
-                    // Reparent to new stack.
-                    task.reparent(stack, position, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
-                            !DEFER_RESUME, "positionTaskInStack");
-                }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
-            int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
-        if (DEBUG_CONFIGURATION) Slog.v(TAG, "Report configuration: " + token + " "
-                + horizontalSizeConfiguration + " " + verticalSizeConfigurations);
-        synchronized (mGlobalLock) {
-            ActivityRecord record = ActivityRecord.isInStackLocked(token);
-            if (record == null) {
-                throw new IllegalArgumentException("reportSizeConfigurations: ActivityRecord not "
-                        + "found for: " + token);
-            }
-            record.setSizeConfigurations(horizontalSizeConfiguration,
-                    verticalSizeConfigurations, smallestSizeConfigurations);
-        }
-    }
-
-    /**
-     * Dismisses split-screen multi-window mode.
-     * @param toTop If true the current primary split-screen stack will be placed or left on top.
-     */
-    @Override
-    public void dismissSplitScreenMode(boolean toTop) {
-        enforceCallerIsRecentsOrHasPermission(
-                MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityStack stack =
-                        mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
-                if (stack == null) {
-                    Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
-                    return;
-                }
-
-                if (toTop) {
-                    // Caller wants the current split-screen primary stack to be the top stack after
-                    // it goes fullscreen, so move it to the front.
-                    stack.moveToFront("dismissSplitScreenMode");
-                } else if (mStackSupervisor.isTopDisplayFocusedStack(stack)) {
-                    // In this case the current split-screen primary stack shouldn't be the top
-                    // stack after it goes fullscreen, but it current has focus, so we move the
-                    // focus to the top-most split-screen secondary stack next to it.
-                    final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
-                            WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-                    if (otherStack != null) {
-                        otherStack.moveToFront("dismissSplitScreenMode_other");
-                    }
-                }
-
-                stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    /**
-     * Dismisses Pip
-     * @param animate True if the dismissal should be animated.
-     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
-     *                          default animation duration should be used.
-     */
-    @Override
-    public void dismissPip(boolean animate, int animationDuration) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final PinnedActivityStack stack =
-                        mStackSupervisor.getDefaultDisplay().getPinnedStack();
-                if (stack == null) {
-                    Slog.w(TAG, "dismissPip: pinned stack not found.");
-                    return;
-                }
-                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
-                    throw new IllegalArgumentException("Stack: " + stack
-                            + " doesn't support animated resize.");
-                }
-                if (animate) {
-                    stack.animateResizePinnedStack(null /* sourceHintBounds */,
-                            null /* destBounds */, animationDuration, false /* fromFullscreen */);
-                } else {
-                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, true /* onTop */);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
-        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()");
-        synchronized (mGlobalLock) {
-            mSuppressResizeConfigChanges = suppress;
-        }
-    }
-
-    /**
-     * NOTE: For the pinned stack, this method is usually called after the bounds animation has
-     *       animated the stack to the fullscreen, but can also be called if we are relaunching an
-     *       activity and clearing the task at the same time.
-     */
-    @Override
-    // TODO: API should just be about changing windowing modes...
-    public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "moveTasksToFullscreenStack()");
-        synchronized (mGlobalLock) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
-                if (stack != null){
-                    if (!stack.isActivityTypeStandardOrUndefined()) {
-                        throw new IllegalArgumentException(
-                                "You can't move tasks from non-standard stacks.");
-                    }
-                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, onTop);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    /**
-     * Moves the top activity in the input stackId to the pinned stack.
-     *
-     * @param stackId Id of stack to move the top activity to pinned stack.
-     * @param bounds Bounds to use for pinned stack.
-     *
-     * @return True if the top activity of the input stack was successfully moved to the pinned
-     *          stack.
-     */
-    @Override
-    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "moveTopActivityToPinnedStack()");
-        synchronized (mGlobalLock) {
-            if (!mSupportsPictureInPicture) {
-                throw new IllegalStateException("moveTopActivityToPinnedStack:"
-                        + "Device doesn't support picture-in-picture mode");
-            }
-
-            long ident = Binder.clearCallingIdentity();
-            try {
-                return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public boolean isInMultiWindowMode(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r == null) {
-                    return false;
-                }
-                // An activity is consider to be in multi-window mode if its task isn't fullscreen.
-                return r.inMultiWindowMode();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public boolean isInPictureInPictureMode(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                return isInPictureInPictureMode(ActivityRecord.forTokenLocked(token));
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    private boolean isInPictureInPictureMode(ActivityRecord r) {
-        if (r == null || r.getStack() == null || !r.inPinnedWindowingMode()
-                || r.getStack().isInStackLocked(r) == null) {
-            return false;
-        }
-
-        // If we are animating to fullscreen then we have already dispatched the PIP mode
-        // changed, so we should reflect that check here as well.
-        final PinnedActivityStack stack = r.getStack();
-        final PinnedStackWindowController windowController = stack.getWindowContainerController();
-        return !windowController.isAnimatingBoundsToFullscreen();
-    }
-
-    @Override
-    public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
-                        "enterPictureInPictureMode", token, params);
-
-                // If the activity is already in picture in picture mode, then just return early
-                if (isInPictureInPictureMode(r)) {
-                    return true;
-                }
-
-                // Activity supports picture-in-picture, now check that we can enter PiP at this
-                // point, if it is
-                if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode",
-                        false /* beforeStopping */)) {
-                    return false;
-                }
-
-                final Runnable enterPipRunnable = () -> {
-                    synchronized (mGlobalLock) {
-                        // Only update the saved args from the args that are set
-                        r.pictureInPictureArgs.copyOnlySet(params);
-                        final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
-                        final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
-                        // Adjust the source bounds by the insets for the transition down
-                        final Rect sourceBounds = new Rect(
-                                r.pictureInPictureArgs.getSourceRectHint());
-                        mStackSupervisor.moveActivityToPinnedStackLocked(
-                                r, sourceBounds, aspectRatio, "enterPictureInPictureMode");
-                        final PinnedActivityStack stack = r.getStack();
-                        stack.setPictureInPictureAspectRatio(aspectRatio);
-                        stack.setPictureInPictureActions(actions);
-                        MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
-                                r.shortComponentName, r.supportsEnterPipOnTaskSwitch);
-                        logPictureInPictureArgs(params);
-                    }
-                };
-
-                if (isKeyguardLocked()) {
-                    // If the keyguard is showing or occluded, then try and dismiss it before
-                    // entering picture-in-picture (this will prompt the user to authenticate if the
-                    // device is currently locked).
-                    dismissKeyguard(token, new KeyguardDismissCallback() {
-                        @Override
-                        public void onDismissSucceeded() {
-                            mH.post(enterPipRunnable);
-                        }
-                    }, null /* message */);
-                } else {
-                    // Enter picture in picture immediately otherwise
-                    enterPipRunnable.run();
-                }
-                return true;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
-                        "setPictureInPictureParams", token, params);
-
-                // Only update the saved args from the args that are set
-                r.pictureInPictureArgs.copyOnlySet(params);
-                if (r.inPinnedWindowingMode()) {
-                    // If the activity is already in picture-in-picture, update the pinned stack now
-                    // if it is not already expanding to fullscreen. Otherwise, the arguments will
-                    // be used the next time the activity enters PiP
-                    final PinnedActivityStack stack = r.getStack();
-                    if (!stack.isAnimatingBoundsToFullscreen()) {
-                        stack.setPictureInPictureAspectRatio(
-                                r.pictureInPictureArgs.getAspectRatio());
-                        stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
-                    }
-                }
-                logPictureInPictureArgs(params);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public int getMaxNumPictureInPictureActions(IBinder token) {
-        // Currently, this is a static constant, but later, we may change this to be dependent on
-        // the context of the activity
-        return 3;
-    }
-
-    private void logPictureInPictureArgs(PictureInPictureParams params) {
-        if (params.hasSetActions()) {
-            MetricsLogger.histogram(mContext, "tron_varz_picture_in_picture_actions_count",
-                    params.getActions().size());
-        }
-        if (params.hasSetAspectRatio()) {
-            LogMaker lm = new LogMaker(MetricsEvent.ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED);
-            lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, params.getAspectRatio());
-            MetricsLogger.action(lm);
-        }
-    }
-
-    /**
-     * Checks the state of the system and the activity associated with the given {@param token} to
-     * verify that picture-in-picture is supported for that activity.
-     *
-     * @return the activity record for the given {@param token} if all the checks pass.
-     */
-    private ActivityRecord ensureValidPictureInPictureActivityParamsLocked(String caller,
-            IBinder token, PictureInPictureParams params) {
-        if (!mSupportsPictureInPicture) {
-            throw new IllegalStateException(caller
-                    + ": Device doesn't support picture-in-picture mode.");
-        }
-
-        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        if (r == null) {
-            throw new IllegalStateException(caller
-                    + ": Can't find activity for token=" + token);
-        }
-
-        if (!r.supportsPictureInPicture()) {
-            throw new IllegalStateException(caller
-                    + ": Current activity does not support picture-in-picture.");
-        }
-
-        if (params.hasSetAspectRatio()
-                && !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
-                params.getAspectRatio())) {
-            final float minAspectRatio = mContext.getResources().getFloat(
-                    com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
-            final float maxAspectRatio = mContext.getResources().getFloat(
-                    com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
-            throw new IllegalArgumentException(String.format(caller
-                            + ": Aspect ratio is too extreme (must be between %f and %f).",
-                    minAspectRatio, maxAspectRatio));
-        }
-
-        // Truncate the number of actions if necessary
-        params.truncateActions(getMaxNumPictureInPictureActions(token));
-
-        return r;
-    }
-
-    @Override
-    public IBinder getUriPermissionOwnerForActivity(IBinder activityToken) {
-        enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
-        synchronized (mGlobalLock) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
-            if (r == null) {
-                throw new IllegalArgumentException("Activity does not exist; token="
-                        + activityToken);
-            }
-            return r.getUriPermissionsLocked().getExternalToken();
-        }
-    }
-
-    @Override
-    public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
-            Rect tempDockedTaskInsetBounds,
-            Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds,
-                        tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds,
-                        PRESERVE_WINDOWS);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void setSplitScreenResizing(boolean resizing) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.setSplitScreenResizing(resizing);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    /**
-     * Check that we have the features required for VR-related API calls, and throw an exception if
-     * not.
-     */
-    void enforceSystemHasVrFeature() {
-        if (!mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
-            throw new UnsupportedOperationException("VR mode not supported on this device!");
-        }
-    }
-
-    @Override
-    public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
-        enforceSystemHasVrFeature();
-
-        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
-
-        ActivityRecord r;
-        synchronized (mGlobalLock) {
-            r = ActivityRecord.isInStackLocked(token);
-        }
-
-        if (r == null) {
-            throw new IllegalArgumentException();
-        }
-
-        int err;
-        if ((err = vrService.hasVrPackage(packageName, r.userId)) !=
-                VrManagerInternal.NO_ERROR) {
-            return err;
-        }
-
-        // Clear the binder calling uid since this path may call moveToTask().
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                r.requestedVrComponent = (enabled) ? packageName : null;
-
-                // Update associated state if this activity is currently focused
-                if (r.isResumedActivityOnDisplay()) {
-                    applyUpdateVrModeLocked(r);
-                }
-                return 0;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-    }
-
-    @Override
-    public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
-        Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
-        synchronized (mGlobalLock) {
-            ActivityRecord activity = getTopDisplayFocusedStack().getTopActivity();
-            if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
-                throw new SecurityException("Only focused activity can call startVoiceInteraction");
-            }
-            if (mRunningVoice != null || activity.getTask().voiceSession != null
-                    || activity.voiceSession != null) {
-                Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction");
-                return;
-            }
-            if (activity.pendingVoiceInteractionStart) {
-                Slog.w(TAG, "Pending start of voice interaction already.");
-                return;
-            }
-            activity.pendingVoiceInteractionStart = true;
-        }
-        LocalServices.getService(VoiceInteractionManagerInternal.class)
-                .startLocalVoiceInteraction(callingActivity, options);
-    }
-
-    @Override
-    public void stopLocalVoiceInteraction(IBinder callingActivity) {
-        LocalServices.getService(VoiceInteractionManagerInternal.class)
-                .stopLocalVoiceInteraction(callingActivity);
-    }
-
-    @Override
-    public boolean supportsLocalVoiceInteraction() {
-        return LocalServices.getService(VoiceInteractionManagerInternal.class)
-                .supportsLocalVoiceInteraction();
-    }
-
-    /** Notifies all listeners when the pinned stack animation starts. */
-    @Override
-    public void notifyPinnedStackAnimationStarted() {
-        mTaskChangeNotificationController.notifyPinnedStackAnimationStarted();
-    }
-
-    /** Notifies all listeners when the pinned stack animation ends. */
-    @Override
-    public void notifyPinnedStackAnimationEnded() {
-        mTaskChangeNotificationController.notifyPinnedStackAnimationEnded();
-    }
-
-    @Override
-    public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.resizePinnedStackLocked(pinnedBounds, tempPinnedTaskBounds);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public boolean updateDisplayOverrideConfiguration(Configuration values, int displayId) {
-        mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateDisplayOverrideConfiguration()");
-
-        synchronized (mGlobalLock) {
-            // Check if display is initialized in AM.
-            if (!mStackSupervisor.isDisplayAdded(displayId)) {
-                // Call might come when display is not yet added or has already been removed.
-                if (DEBUG_CONFIGURATION) {
-                    Slog.w(TAG, "Trying to update display configuration for non-existing displayId="
-                            + displayId);
-                }
-                return false;
-            }
-
-            if (values == null && mWindowManager != null) {
-                // sentinel: fetch the current configuration from the window manager
-                values = mWindowManager.computeNewConfiguration(displayId);
-            }
-
-            if (mWindowManager != null) {
-                final Message msg = PooledLambda.obtainMessage(
-                        ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal, displayId);
-                mH.sendMessage(msg);
-            }
-
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                if (values != null) {
-                    Settings.System.clearConfiguration(values);
-                }
-                updateDisplayOverrideConfigurationLocked(values, null /* starting */,
-                        false /* deferResume */, displayId, mTmpUpdateConfigurationResult);
-                return mTmpUpdateConfigurationResult.changes != 0;
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public boolean updateConfiguration(Configuration values) {
-        mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
-
-        synchronized (mGlobalLock) {
-            if (values == null && mWindowManager != null) {
-                // sentinel: fetch the current configuration from the window manager
-                values = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
-            }
-
-            if (mWindowManager != null) {
-                final Message msg = PooledLambda.obtainMessage(
-                        ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal,
-                        DEFAULT_DISPLAY);
-                mH.sendMessage(msg);
-            }
-
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                if (values != null) {
-                    Settings.System.clearConfiguration(values);
-                }
-                updateConfigurationLocked(values, null, false, false /* persistent */,
-                        UserHandle.USER_NULL, false /* deferResume */,
-                        mTmpUpdateConfigurationResult);
-                return mTmpUpdateConfigurationResult.changes != 0;
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
-            CharSequence message) {
-        if (message != null) {
-            mAmInternal.enforceCallingPermission(
-                    Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard()");
-        }
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                mKeyguardController.dismissKeyguard(token, callback, message);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-    }
-
-    @Override
-    public void cancelTaskWindowTransition(int taskId) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "cancelTaskWindowTransition()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
-                        MATCH_TASK_IN_STACKS_ONLY);
-                if (task == null) {
-                    Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
-                    return;
-                }
-                task.cancelWindowTransition();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
-        enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            final TaskRecord task;
-            synchronized (mGlobalLock) {
-                task = mStackSupervisor.anyTaskForIdLocked(taskId,
-                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-                if (task == null) {
-                    Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
-                    return null;
-                }
-            }
-            // Don't call this while holding the lock as this operation might hit the disk.
-            return task.getSnapshot(reducedResolution);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void setDisablePreviewScreenshots(IBinder token, boolean disable) {
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                Slog.w(TAG, "setDisablePreviewScreenshots: Unable to find activity for token="
-                        + token);
-                return;
-            }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                r.setDisablePreviewScreenshots(disable);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    /** Return the user id of the last resumed activity. */
-    @Override
-    public @UserIdInt
-    int getLastResumedActivityUserId() {
-        mAmInternal.enforceCallingPermission(
-                Manifest.permission.INTERACT_ACROSS_USERS_FULL, "getLastResumedActivityUserId()");
-        synchronized (mGlobalLock) {
-            if (mLastResumedActivity == null) {
-                return getCurrentUserId();
-            }
-            return mLastResumedActivity.userId;
-        }
-    }
-
-    @Override
-    public void updateLockTaskFeatures(int userId, int flags) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != 0 && callingUid != SYSTEM_UID) {
-            mAmInternal.enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
-                    "updateLockTaskFeatures()");
-        }
-        synchronized (mGlobalLock) {
-            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowing features " + userId + ":0x" +
-                    Integer.toHexString(flags));
-            getLockTaskController().updateLockTaskFeatures(userId, flags);
-        }
-    }
-
-    @Override
-    public void setShowWhenLocked(IBinder token, boolean showWhenLocked) {
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return;
-            }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                r.setShowWhenLocked(showWhenLocked);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public void setTurnScreenOn(IBinder token, boolean turnScreenOn) {
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return;
-            }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                r.setTurnScreenOn(turnScreenOn);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
-        mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
-                "registerRemoteAnimations");
-        definition.setCallingPid(Binder.getCallingPid());
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return;
-            }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                r.registerRemoteAnimations(definition);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public void registerRemoteAnimationForNextActivityStart(String packageName,
-            RemoteAnimationAdapter adapter) {
-        mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
-                "registerRemoteAnimationForNextActivityStart");
-        adapter.setCallingPid(Binder.getCallingPid());
-        synchronized (mGlobalLock) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                getActivityStartController().registerRemoteAnimationForNextActivityStart(
-                        packageName, adapter);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
-    @Override
-    public void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) {
-        synchronized (mGlobalLock) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                mAppWarnings.alwaysShowUnsupportedCompileSdkWarning(activity);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public void setVrThread(int tid) {
-        enforceSystemHasVrFeature();
-        synchronized (mGlobalLock) {
-            final int pid = Binder.getCallingPid();
-            final WindowProcessController wpc = mPidMap.get(pid);
-            mVrController.setVrThreadLocked(tid, pid, wpc);
-        }
-    }
-
-    @Override
-    public void setPersistentVrThread(int tid) {
-        if (checkCallingPermission(Manifest.permission.RESTRICTED_VR_ACCESS)
-                != PERMISSION_GRANTED) {
-            final String msg = "Permission Denial: setPersistentVrThread() from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid()
-                    + " requires " + Manifest.permission.RESTRICTED_VR_ACCESS;
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-        enforceSystemHasVrFeature();
-        synchronized (mGlobalLock) {
-            final int pid = Binder.getCallingPid();
-            final WindowProcessController proc = mPidMap.get(pid);
-            mVrController.setPersistentVrThreadLocked(tid, pid, proc);
-        }
-    }
-
-    @Override
-    public void stopAppSwitches() {
-        enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "stopAppSwitches");
-        synchronized (mGlobalLock) {
-            mAppSwitchesAllowedTime = SystemClock.uptimeMillis() + APP_SWITCH_DELAY_TIME;
-            mDidAppSwitch = false;
-            getActivityStartController().schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
-        }
-    }
-
-    @Override
-    public void resumeAppSwitches() {
-        enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
-        synchronized (mGlobalLock) {
-            // Note that we don't execute any pending app switches... we will
-            // let those wait until either the timeout, or the next start
-            // activity request.
-            mAppSwitchesAllowedTime = 0;
-        }
-    }
-
-    void onStartActivitySetDidAppSwitch() {
-        if (mDidAppSwitch) {
-            // This is the second allowed switch since we stopped switches, so now just generally
-            // allow switches. Use case:
-            // - user presses home (switches disabled, switch to home, mDidAppSwitch now true);
-            // - user taps a home icon (coming from home so allowed, we hit here and now allow
-            // anyone to switch again).
-            mAppSwitchesAllowedTime = 0;
-        } else {
-            mDidAppSwitch = true;
-        }
-    }
-
-    /** @return whether the system should disable UI modes incompatible with VR mode. */
-    boolean shouldDisableNonVrUiLocked() {
-        return mVrController.shouldDisableNonVrUiLocked();
-    }
-
-    private void applyUpdateVrModeLocked(ActivityRecord r) {
-        // VR apps are expected to run in a main display. If an app is turning on VR for
-        // itself, but lives in a dynamic stack, then make sure that it is moved to the main
-        // fullscreen stack before enabling VR Mode.
-        // TODO: The goal of this code is to keep the VR app on the main display. When the
-        // stack implementation changes in the future, keep in mind that the use of the fullscreen
-        // stack is a means to move the activity to the main display and a moveActivityToDisplay()
-        // option would be a better choice here.
-        if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
-            Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
-                    + " to main stack for VR");
-            final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
-                    WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
-            moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
-        }
-        mH.post(() -> {
-            if (!mVrController.onVrModeChanged(r)) {
-                return;
-            }
-            synchronized (mGlobalLock) {
-                final boolean disableNonVrUi = mVrController.shouldDisableNonVrUiLocked();
-                mWindowManager.disableNonVrUi(disableNonVrUi);
-                if (disableNonVrUi) {
-                    // If we are in a VR mode where Picture-in-Picture mode is unsupported,
-                    // then remove the pinned stack.
-                    mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
-                }
-            }
-        });
-    }
-
-    @Override
-    public int getPackageScreenCompatMode(String packageName) {
-        enforceNotIsolatedCaller("getPackageScreenCompatMode");
-        synchronized (mGlobalLock) {
-            return mCompatModePackages.getPackageScreenCompatModeLocked(packageName);
-        }
-    }
-
-    @Override
-    public void setPackageScreenCompatMode(String packageName, int mode) {
-        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
-                "setPackageScreenCompatMode");
-        synchronized (mGlobalLock) {
-            mCompatModePackages.setPackageScreenCompatModeLocked(packageName, mode);
-        }
-    }
-
-    @Override
-    public boolean getPackageAskScreenCompat(String packageName) {
-        enforceNotIsolatedCaller("getPackageAskScreenCompat");
-        synchronized (mGlobalLock) {
-            return mCompatModePackages.getPackageAskCompatModeLocked(packageName);
-        }
-    }
-
-    @Override
-    public void setPackageAskScreenCompat(String packageName, boolean ask) {
-        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
-                "setPackageAskScreenCompat");
-        synchronized (mGlobalLock) {
-            mCompatModePackages.setPackageAskCompatModeLocked(packageName, ask);
-        }
-    }
-
-    public static String relaunchReasonToString(int relaunchReason) {
-        switch (relaunchReason) {
-            case RELAUNCH_REASON_WINDOWING_MODE_RESIZE:
-                return "window_resize";
-            case RELAUNCH_REASON_FREE_RESIZE:
-                return "free_resize";
-            default:
-                return null;
-        }
-    }
-
-    ActivityStack getTopDisplayFocusedStack() {
-        return mStackSupervisor.getTopDisplayFocusedStack();
-    }
-
-    /** Pokes the task persister. */
-    void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
-        mRecentTasks.notifyTaskPersisterLocked(task, flush);
-    }
-
-    void onTopProcChangedLocked(WindowProcessController proc) {
-        mVrController.onTopProcChangedLocked(proc);
-    }
-
-    boolean isKeyguardLocked() {
-        return mKeyguardController.isKeyguardLocked();
-    }
-
-    boolean isNextTransitionForward() {
-        int transit = mWindowManager.getPendingAppTransition();
-        return transit == TRANSIT_ACTIVITY_OPEN
-                || transit == TRANSIT_TASK_OPEN
-                || transit == TRANSIT_TASK_TO_FRONT;
-    }
-
-    void dumpLastANRLocked(PrintWriter pw) {
-        pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
-        if (mLastANRState == null) {
-            pw.println("  <no ANR has occurred since boot>");
-        } else {
-            pw.println(mLastANRState);
-        }
-    }
-
-    void dumpLastANRTracesLocked(PrintWriter pw) {
-        pw.println("ACTIVITY MANAGER LAST ANR TRACES (dumpsys activity lastanr-traces)");
-
-        final File[] files = new File(ANR_TRACE_DIR).listFiles();
-        if (ArrayUtils.isEmpty(files)) {
-            pw.println("  <no ANR has occurred since boot>");
-            return;
-        }
-        // Find the latest file.
-        File latest = null;
-        for (File f : files) {
-            if ((latest == null) || (latest.lastModified() < f.lastModified())) {
-                latest = f;
-            }
-        }
-        pw.print("File: ");
-        pw.print(latest.getName());
-        pw.println();
-        try (BufferedReader in = new BufferedReader(new FileReader(latest))) {
-            String line;
-            while ((line = in.readLine()) != null) {
-                pw.println(line);
-            }
-        } catch (IOException e) {
-            pw.print("Unable to read: ");
-            pw.print(e);
-            pw.println();
-        }
-    }
-
-    void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
-        dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage,
-                "ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)");
-    }
-
-    void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) {
-        pw.println(header);
-
-        boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient,
-                dumpPackage);
-        boolean needSep = printedAnything;
-
-        boolean printed = ActivityStackSupervisor.printThisActivity(pw,
-                mStackSupervisor.getTopResumedActivity(),  dumpPackage, needSep,
-                "  ResumedActivity: ");
-        if (printed) {
-            printedAnything = true;
-            needSep = false;
-        }
-
-        if (dumpPackage == null) {
-            if (needSep) {
-                pw.println();
-            }
-            printedAnything = true;
-            mStackSupervisor.dump(pw, "  ");
-        }
-
-        if (!printedAnything) {
-            pw.println("  (nothing)");
-        }
-    }
-
-    void dumpActivityContainersLocked(PrintWriter pw) {
-        pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
-        mStackSupervisor.dumpChildrenNames(pw, " ");
-        pw.println(" ");
-    }
-
-    void dumpActivityStarterLocked(PrintWriter pw, String dumpPackage) {
-        pw.println("ACTIVITY MANAGER STARTER (dumpsys activity starter)");
-        getActivityStartController().dump(pw, "", dumpPackage);
-    }
-
-    /**
-     * There are three things that cmd can be:
-     *  - a flattened component name that matches an existing activity
-     *  - the cmd arg isn't the flattened component name of an existing activity:
-     *    dump all activity whose component contains the cmd as a substring
-     *  - A hex number of the ActivityRecord object instance.
-     *
-     *  @param dumpVisibleStacksOnly dump activity with {@param name} only if in a visible stack
-     *  @param dumpFocusedStackOnly dump activity with {@param name} only if in the focused stack
-     */
-    protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
-            int opti, boolean dumpAll, boolean dumpVisibleStacksOnly, boolean dumpFocusedStackOnly) {
-        ArrayList<ActivityRecord> activities;
-
-        synchronized (mGlobalLock) {
-            activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
-                    dumpFocusedStackOnly);
-        }
-
-        if (activities.size() <= 0) {
-            return false;
-        }
-
-        String[] newArgs = new String[args.length - opti];
-        System.arraycopy(args, opti, newArgs, 0, args.length - opti);
-
-        TaskRecord lastTask = null;
-        boolean needSep = false;
-        for (int i = activities.size() - 1; i >= 0; i--) {
-            ActivityRecord r = activities.get(i);
-            if (needSep) {
-                pw.println();
-            }
-            needSep = true;
-            synchronized (mGlobalLock) {
-                final TaskRecord task = r.getTask();
-                if (lastTask != task) {
-                    lastTask = task;
-                    pw.print("TASK "); pw.print(lastTask.affinity);
-                    pw.print(" id="); pw.print(lastTask.taskId);
-                    pw.print(" userId="); pw.println(lastTask.userId);
-                    if (dumpAll) {
-                        lastTask.dump(pw, "  ");
-                    }
-                }
-            }
-            dumpActivity("  ", fd, pw, activities.get(i), newArgs, dumpAll);
-        }
-        return true;
-    }
-
-    /**
-     * Invokes IApplicationThread.dumpActivity() on the thread of the specified activity if
-     * there is a thread associated with the activity.
-     */
-    private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
-            final ActivityRecord r, String[] args, boolean dumpAll) {
-        String innerPrefix = prefix + "  ";
-        synchronized (mGlobalLock) {
-            pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName);
-            pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r)));
-            pw.print(" pid=");
-            if (r.hasProcess()) pw.println(r.app.getPid());
-            else pw.println("(not running)");
-            if (dumpAll) {
-                r.dump(pw, innerPrefix);
-            }
-        }
-        if (r.attachedToProcess()) {
-            // flush anything that is already in the PrintWriter since the thread is going
-            // to write to the file descriptor directly
-            pw.flush();
-            try {
-                TransferPipe tp = new TransferPipe();
-                try {
-                    r.app.getThread().dumpActivity(tp.getWriteFd(),
-                            r.appToken, innerPrefix, args);
-                    tp.go(fd);
-                } finally {
-                    tp.kill();
-                }
-            } catch (IOException e) {
-                pw.println(innerPrefix + "Failure while dumping the activity: " + e);
-            } catch (RemoteException e) {
-                pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
-            }
-        }
-    }
-
-    void writeSleepStateToProto(ProtoOutputStream proto) {
-        for (ActivityTaskManagerInternal.SleepToken st : mStackSupervisor.mSleepTokens) {
-            proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS,
-                    st.toString());
-        }
-
-        if (mRunningVoice != null) {
-            final long vrToken = proto.start(
-                    ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
-            proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION,
-                    mRunningVoice.toString());
-            mVoiceWakeLock.writeToProto(
-                    proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK);
-            proto.end(vrToken);
-        }
-
-        proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEPING, mSleeping);
-        proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SHUTTING_DOWN,
-                mShuttingDown);
-        mVrController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
-    }
-
-    int getCurrentUserId() {
-        return mAmInternal.getCurrentUserId();
-    }
-
-    private void enforceNotIsolatedCaller(String caller) {
-        if (UserHandle.isIsolated(Binder.getCallingUid())) {
-            throw new SecurityException("Isolated process not allowed to call " + caller);
-        }
-    }
-
-    public Configuration getConfiguration() {
-        Configuration ci;
-        synchronized(mGlobalLock) {
-            ci = new Configuration(getGlobalConfigurationForCallingPid());
-            ci.userSetLocale = false;
-        }
-        return ci;
-    }
-
-    /**
-     * Current global configuration information. Contains general settings for the entire system,
-     * also corresponds to the merged configuration of the default display.
-     */
-    Configuration getGlobalConfiguration() {
-        return mStackSupervisor.getConfiguration();
-    }
-
-    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
-            boolean initLocale) {
-        return updateConfigurationLocked(values, starting, initLocale, false /* deferResume */);
-    }
-
-    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
-            boolean initLocale, boolean deferResume) {
-        // pass UserHandle.USER_NULL as userId because we don't persist configuration for any user
-        return updateConfigurationLocked(values, starting, initLocale, false /* persistent */,
-                UserHandle.USER_NULL, deferResume);
-    }
-
-    void updatePersistentConfiguration(Configuration values, @UserIdInt int userId) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                updateConfigurationLocked(values, null, false, true, userId,
-                        false /* deferResume */);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    void updateUserConfiguration() {
-        synchronized (mGlobalLock) {
-            final Configuration configuration = new Configuration(getGlobalConfiguration());
-            final int currentUserId = mAmInternal.getCurrentUserId();
-            Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration,
-                    currentUserId, Settings.System.canWrite(mContext));
-            updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */,
-                    false /* persistent */, currentUserId, false /* deferResume */);
-        }
-    }
-
-    private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
-            boolean initLocale, boolean persistent, int userId, boolean deferResume) {
-        return updateConfigurationLocked(values, starting, initLocale, persistent, userId,
-                deferResume, null /* result */);
-    }
-
-    /**
-     * Do either or both things: (1) change the current configuration, and (2)
-     * make sure the given activity is running with the (now) current
-     * configuration.  Returns true if the activity has been left running, or
-     * false if <var>starting</var> is being destroyed to match the new
-     * configuration.
-     *
-     * @param userId is only used when persistent parameter is set to true to persist configuration
-     *               for that particular user
-     */
-    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
-            boolean initLocale, boolean persistent, int userId, boolean deferResume,
-            ActivityTaskManagerService.UpdateConfigurationResult result) {
-        int changes = 0;
-        boolean kept = true;
-
-        if (mWindowManager != null) {
-            mWindowManager.deferSurfaceLayout();
-        }
-        try {
-            if (values != null) {
-                changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
-                        deferResume);
-            }
-
-            kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
-        } finally {
-            if (mWindowManager != null) {
-                mWindowManager.continueSurfaceLayout();
-            }
-        }
-
-        if (result != null) {
-            result.changes = changes;
-            result.activityRelaunched = !kept;
-        }
-        return kept;
-    }
-
-    /** Update default (global) configuration and notify listeners about changes. */
-    private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
-            boolean persistent, int userId, boolean deferResume) {
-        mTempConfig.setTo(getGlobalConfiguration());
-        final int changes = mTempConfig.updateFrom(values);
-        if (changes == 0) {
-            // Since calling to Activity.setRequestedOrientation leads to freezing the window with
-            // setting WindowManagerService.mWaitingForConfig to true, it is important that we call
-            // performDisplayOverrideConfigUpdate in order to send the new display configuration
-            // (even if there are no actual changes) to unfreeze the window.
-            performDisplayOverrideConfigUpdate(values, deferResume, DEFAULT_DISPLAY);
-            return 0;
-        }
-
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
-                "Updating global configuration to: " + values);
-
-        EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
-        StatsLog.write(StatsLog.RESOURCE_CONFIGURATION_CHANGED,
-                values.colorMode,
-                values.densityDpi,
-                values.fontScale,
-                values.hardKeyboardHidden,
-                values.keyboard,
-                values.keyboardHidden,
-                values.mcc,
-                values.mnc,
-                values.navigation,
-                values.navigationHidden,
-                values.orientation,
-                values.screenHeightDp,
-                values.screenLayout,
-                values.screenWidthDp,
-                values.smallestScreenWidthDp,
-                values.touchscreen,
-                values.uiMode);
-
-
-        if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
-            final LocaleList locales = values.getLocales();
-            int bestLocaleIndex = 0;
-            if (locales.size() > 1) {
-                if (mSupportedSystemLocales == null) {
-                    mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
-                }
-                bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
-            }
-            SystemProperties.set("persist.sys.locale",
-                    locales.get(bestLocaleIndex).toLanguageTag());
-            LocaleList.setDefault(locales, bestLocaleIndex);
-
-            final Message m = PooledLambda.obtainMessage(
-                    ActivityTaskManagerService::sendLocaleToMountDaemonMsg, this,
-                    locales.get(bestLocaleIndex));
-            mH.sendMessage(m);
-        }
-
-        mTempConfig.seq = increaseConfigurationSeqLocked();
-
-        // Update stored global config and notify everyone about the change.
-        mStackSupervisor.onConfigurationChanged(mTempConfig);
-
-        Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
-        // TODO(multi-display): Update UsageEvents#Event to include displayId.
-        mUsageStatsInternal.reportConfigurationChange(mTempConfig, mAmInternal.getCurrentUserId());
-
-        // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
-        updateShouldShowDialogsLocked(mTempConfig);
-
-        AttributeCache ac = AttributeCache.instance();
-        if (ac != null) {
-            ac.updateConfiguration(mTempConfig);
-        }
-
-        // Make sure all resources in our process are updated right now, so that anyone who is going
-        // to retrieve resource values after we return will be sure to get the new ones. This is
-        // especially important during boot, where the first config change needs to guarantee all
-        // resources have that config before following boot code is executed.
-        mSystemThread.applyConfigurationToResources(mTempConfig);
-
-        // We need another copy of global config because we're scheduling some calls instead of
-        // running them in place. We need to be sure that object we send will be handled unchanged.
-        final Configuration configCopy = new Configuration(mTempConfig);
-        if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
-            final Message msg = PooledLambda.obtainMessage(
-                    ActivityTaskManagerService::sendPutConfigurationForUserMsg,
-                    this, userId, configCopy);
-            mH.sendMessage(msg);
-        }
-
-        for (int i = mPidMap.size() - 1; i >= 0; i--) {
-            final int pid = mPidMap.keyAt(i);
-            final WindowProcessController app = mPidMap.get(pid);
-            if (DEBUG_CONFIGURATION) {
-                Slog.v(TAG_CONFIGURATION, "Update process config of "
-                        + app.mName + " to new config " + configCopy);
-            }
-            app.onConfigurationChanged(configCopy);
-        }
-
-        final Message msg = PooledLambda.obtainMessage(
-                ActivityManagerInternal::broadcastGlobalConfigurationChanged,
-                mAmInternal, changes, initLocale);
-        mH.sendMessage(msg);
-
-        // Override configuration of the default display duplicates global config, so we need to
-        // update it also. This will also notify WindowManager about changes.
-        performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
-                DEFAULT_DISPLAY);
-
-        return changes;
-    }
-
-    boolean updateDisplayOverrideConfigurationLocked(Configuration values, ActivityRecord starting,
-            boolean deferResume, int displayId) {
-        return updateDisplayOverrideConfigurationLocked(values, starting, deferResume /* deferResume */,
-                displayId, null /* result */);
-    }
-
-    /**
-     * Updates override configuration specific for the selected display. If no config is provided,
-     * new one will be computed in WM based on current display info.
-     */
-    boolean updateDisplayOverrideConfigurationLocked(Configuration values,
-            ActivityRecord starting, boolean deferResume, int displayId,
-            ActivityTaskManagerService.UpdateConfigurationResult result) {
-        int changes = 0;
-        boolean kept = true;
-
-        if (mWindowManager != null) {
-            mWindowManager.deferSurfaceLayout();
-        }
-        try {
-            if (values != null) {
-                if (displayId == DEFAULT_DISPLAY) {
-                    // Override configuration of the default display duplicates global config, so
-                    // we're calling global config update instead for default display. It will also
-                    // apply the correct override config.
-                    changes = updateGlobalConfigurationLocked(values, false /* initLocale */,
-                            false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume);
-                } else {
-                    changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId);
-                }
-            }
-
-            kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
-        } finally {
-            if (mWindowManager != null) {
-                mWindowManager.continueSurfaceLayout();
-            }
-        }
-
-        if (result != null) {
-            result.changes = changes;
-            result.activityRelaunched = !kept;
-        }
-        return kept;
-    }
-
-    private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
-            int displayId) {
-        mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
-        final int changes = mTempConfig.updateFrom(values);
-        if (changes != 0) {
-            Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
-                    + mTempConfig + " for displayId=" + displayId);
-            mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId);
-
-            final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
-            if (isDensityChange && displayId == DEFAULT_DISPLAY) {
-                mAppWarnings.onDensityChanged();
-
-                // Post message to start process to avoid possible deadlock of calling into AMS with
-                // the ATMS lock held.
-                final Message msg = PooledLambda.obtainMessage(
-                        ActivityManagerInternal::killAllBackgroundProcessesExcept, mAmInternal,
-                        N, ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
-                mH.sendMessage(msg);
-            }
-        }
-
-        // Update the configuration with WM first and check if any of the stacks need to be resized
-        // due to the configuration change. If so, resize the stacks now and do any relaunches if
-        // necessary. This way we don't need to relaunch again afterwards in
-        // ensureActivityConfiguration().
-        if (mWindowManager != null) {
-            final int[] resizedStacks =
-                    mWindowManager.setNewDisplayOverrideConfiguration(mTempConfig, displayId);
-            if (resizedStacks != null) {
-                for (int stackId : resizedStacks) {
-                    resizeStackWithBoundsFromWindowManager(stackId, deferResume);
-                }
-            }
-        }
-
-        return changes;
-    }
-
-    private void updateEventDispatchingLocked(boolean booted) {
-        mWindowManager.setEventDispatching(booted && !mShuttingDown);
-    }
-
-    private void sendPutConfigurationForUserMsg(int userId, Configuration config) {
-        final ContentResolver resolver = mContext.getContentResolver();
-        Settings.System.putConfigurationForUser(resolver, config, userId);
-    }
-
-    private void sendLocaleToMountDaemonMsg(Locale l) {
-        try {
-            IBinder service = ServiceManager.getService("mount");
-            IStorageManager storageManager = IStorageManager.Stub.asInterface(service);
-            Log.d(TAG, "Storing locale " + l.toLanguageTag() + " for decryption UI");
-            storageManager.setField(StorageManager.SYSTEM_LOCALE_KEY, l.toLanguageTag());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error storing locale for decryption UI", e);
-        }
-    }
-
-    boolean isActivityStartsLoggingEnabled() {
-        return mAmInternal.isActivityStartsLoggingEnabled();
-    }
-
-    void enableScreenAfterBoot(boolean booted) {
-        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
-                SystemClock.uptimeMillis());
-        mWindowManager.enableScreenAfterBoot();
-
-        synchronized (mGlobalLock) {
-            updateEventDispatchingLocked(booted);
-        }
-    }
-
-    boolean canShowErrorDialogs() {
-        return mShowDialogs && !mSleeping && !mShuttingDown
-                && !mKeyguardController.isKeyguardOrAodShowing(DEFAULT_DISPLAY)
-                && !hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
-                mAmInternal.getCurrentUserId())
-                && !(UserManager.isDeviceInDemoMode(mContext)
-                && mAmInternal.getCurrentUser().isDemo());
-    }
-
-    static long getInputDispatchingTimeoutLocked(ActivityRecord r) {
-        if (r == null || !r.hasProcess()) {
-            return KEY_DISPATCHING_TIMEOUT_MS;
-        }
-        return getInputDispatchingTimeoutLocked(r.app);
-    }
-
-    private static long getInputDispatchingTimeoutLocked(WindowProcessController r) {
-        return r != null ? r.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
-    }
-
-    /**
-     * Decide based on the configuration whether we should show the ANR,
-     * crash, etc dialogs.  The idea is that if there is no affordance to
-     * press the on-screen buttons, or the user experience would be more
-     * greatly impacted than the crash itself, we shouldn't show the dialog.
-     *
-     * A thought: SystemUI might also want to get told about this, the Power
-     * dialog / global actions also might want different behaviors.
-     */
-    private void updateShouldShowDialogsLocked(Configuration config) {
-        final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
-                && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
-                && config.navigation == Configuration.NAVIGATION_NONAV);
-        int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
-        final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
-                && !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER)
-                && modeType != Configuration.UI_MODE_TYPE_TELEVISION
-                && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
-        final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(),
-                HIDE_ERROR_DIALOGS, 0) != 0;
-        mShowDialogs = inputMethodExists && uiModeSupportsDialogs && !hideDialogsSet;
-    }
-
-    private void updateFontScaleIfNeeded(@UserIdInt int userId) {
-        final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(),
-                FONT_SCALE, 1.0f, userId);
-
-        synchronized (this) {
-            if (getGlobalConfiguration().fontScale == scaleFactor) {
-                return;
-            }
-
-            final Configuration configuration
-                    = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
-            configuration.fontScale = scaleFactor;
-            updatePersistentConfiguration(configuration, userId);
-        }
-    }
-
-    // Actually is sleeping or shutting down or whatever else in the future
-    // is an inactive state.
-    boolean isSleepingOrShuttingDownLocked() {
-        return isSleepingLocked() || mShuttingDown;
-    }
-
-    boolean isSleepingLocked() {
-        return mSleeping;
-    }
-
-    /** Update AMS states when an activity is resumed. */
-    void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
-        final TaskRecord task = r.getTask();
-        if (task.isActivityTypeStandard()) {
-            if (mCurAppTimeTracker != r.appTimeTracker) {
-                // We are switching app tracking.  Complete the current one.
-                if (mCurAppTimeTracker != null) {
-                    mCurAppTimeTracker.stop();
-                    mH.obtainMessage(
-                            REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
-                    mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
-                    mCurAppTimeTracker = null;
-                }
-                if (r.appTimeTracker != null) {
-                    mCurAppTimeTracker = r.appTimeTracker;
-                    startTimeTrackingFocusedActivityLocked();
-                }
-            } else {
-                startTimeTrackingFocusedActivityLocked();
-            }
-        } else {
-            r.appTimeTracker = null;
-        }
-        // TODO: VI Maybe r.task.voiceInteractor || r.voiceInteractor != null
-        // TODO: Probably not, because we don't want to resume voice on switching
-        // back to this activity
-        if (task.voiceInteractor != null) {
-            startRunningVoiceLocked(task.voiceSession, r.info.applicationInfo.uid);
-        } else {
-            finishRunningVoiceLocked();
-
-            if (mLastResumedActivity != null) {
-                final IVoiceInteractionSession session;
-
-                final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask();
-                if (lastResumedActivityTask != null
-                        && lastResumedActivityTask.voiceSession != null) {
-                    session = lastResumedActivityTask.voiceSession;
-                } else {
-                    session = mLastResumedActivity.voiceSession;
-                }
-
-                if (session != null) {
-                    // We had been in a voice interaction session, but now focused has
-                    // move to something different.  Just finish the session, we can't
-                    // return to it and retain the proper state and synchronization with
-                    // the voice interaction service.
-                    finishVoiceTask(session);
-                }
-            }
-        }
-
-        if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) {
-            mAmInternal.sendForegroundProfileChanged(r.userId);
-        }
-        updateResumedAppTrace(r);
-        mLastResumedActivity = r;
-
-        r.getDisplay().setFocusedApp(r, true);
-
-        applyUpdateLockStateLocked(r);
-        applyUpdateVrModeLocked(r);
-
-        EventLogTags.writeAmSetResumedActivity(
-                r == null ? -1 : r.userId,
-                r == null ? "NULL" : r.shortComponentName,
-                reason);
-    }
-
-    ActivityTaskManagerInternal.SleepToken acquireSleepToken(String tag, int displayId) {
-        synchronized (mGlobalLock) {
-            final ActivityTaskManagerInternal.SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
-            updateSleepIfNeededLocked();
-            return token;
-        }
-    }
-
-    void updateSleepIfNeededLocked() {
-        final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
-        final boolean wasSleeping = mSleeping;
-        boolean updateOomAdj = false;
-
-        if (!shouldSleep) {
-            // If wasSleeping is true, we need to wake up activity manager state from when
-            // we started sleeping. In either case, we need to apply the sleep tokens, which
-            // will wake up stacks or put them to sleep as appropriate.
-            if (wasSleeping) {
-                mSleeping = false;
-                StatsLog.write(StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
-                        StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__AWAKE);
-                startTimeTrackingFocusedActivityLocked();
-                mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
-                mStackSupervisor.comeOutOfSleepIfNeededLocked();
-            }
-            mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
-            if (wasSleeping) {
-                updateOomAdj = true;
-            }
-        } else if (!mSleeping && shouldSleep) {
-            mSleeping = true;
-            StatsLog.write(StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
-                    StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__ASLEEP);
-            if (mCurAppTimeTracker != null) {
-                mCurAppTimeTracker.stop();
-            }
-            mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
-            mStackSupervisor.goingToSleepLocked();
-            updateResumedAppTrace(null /* resumed */);
-            updateOomAdj = true;
-        }
-        if (updateOomAdj) {
-            mH.post(mAmInternal::updateOomAdj);
-        }
-    }
-
-    void updateOomAdj() {
-        mH.post(mAmInternal::updateOomAdj);
-    }
-
-    void updateCpuStats() {
-        mH.post(mAmInternal::updateCpuStats);
-    }
-
-    void updateUsageStats(ActivityRecord component, boolean resumed) {
-        final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats,
-                mAmInternal, component.realActivity, component.app.mUid, component.userId, resumed);
-        mH.sendMessage(m);
-    }
-
-    void setBooting(boolean booting) {
-        mAmInternal.setBooting(booting);
-    }
-
-    boolean isBooting() {
-        return mAmInternal.isBooting();
-    }
-
-    void setBooted(boolean booted) {
-        mAmInternal.setBooted(booted);
-    }
-
-    boolean isBooted() {
-        return mAmInternal.isBooted();
-    }
-
-    void postFinishBooting(boolean finishBooting, boolean enableScreen) {
-        mH.post(() -> {
-            if (finishBooting) {
-                mAmInternal.finishBooting();
-            }
-            if (enableScreen) {
-                mInternal.enableScreenAfterBoot(isBooted());
-            }
-        });
-    }
-
-    void setHeavyWeightProcess(ActivityRecord root) {
-        mHeavyWeightProcess = root.app;
-        final Message m = PooledLambda.obtainMessage(
-                ActivityTaskManagerService::postHeavyWeightProcessNotification, this,
-                root.app, root.intent, root.userId);
-        mH.sendMessage(m);
-    }
-
-    void clearHeavyWeightProcessIfEquals(WindowProcessController proc) {
-        if (mHeavyWeightProcess == null || mHeavyWeightProcess != proc) {
-            return;
-        }
-
-        mHeavyWeightProcess = null;
-        final Message m = PooledLambda.obtainMessage(
-                ActivityTaskManagerService::cancelHeavyWeightProcessNotification, this,
-                proc.mUserId);
-        mH.sendMessage(m);
-    }
-
-    private void cancelHeavyWeightProcessNotification(int userId) {
-        final INotificationManager inm = NotificationManager.getService();
-        if (inm == null) {
-            return;
-        }
-        try {
-            inm.cancelNotificationWithTag("android", null,
-                    SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, userId);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Error canceling notification for service", e);
-        } catch (RemoteException e) {
-        }
-
-    }
-
-    private void postHeavyWeightProcessNotification(
-            WindowProcessController proc, Intent intent, int userId) {
-        if (proc == null) {
-            return;
-        }
-
-        final INotificationManager inm = NotificationManager.getService();
-        if (inm == null) {
-            return;
-        }
-
-        try {
-            Context context = mContext.createPackageContext(proc.mInfo.packageName, 0);
-            String text = mContext.getString(R.string.heavy_weight_notification,
-                    context.getApplicationInfo().loadLabel(context.getPackageManager()));
-            Notification notification =
-                    new Notification.Builder(context,
-                            SystemNotificationChannels.HEAVY_WEIGHT_APP)
-                            .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
-                            .setWhen(0)
-                            .setOngoing(true)
-                            .setTicker(text)
-                            .setColor(mContext.getColor(
-                                    com.android.internal.R.color.system_notification_accent_color))
-                            .setContentTitle(text)
-                            .setContentText(
-                                    mContext.getText(R.string.heavy_weight_notification_detail))
-                            .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
-                                    intent, PendingIntent.FLAG_CANCEL_CURRENT, null,
-                                    new UserHandle(userId)))
-                            .build();
-            try {
-                inm.enqueueNotificationWithTag("android", "android", null,
-                        SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, notification, userId);
-            } catch (RuntimeException e) {
-                Slog.w(TAG, "Error showing notification for heavy-weight app", e);
-            } catch (RemoteException e) {
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            Slog.w(TAG, "Unable to create context for heavy notification", e);
-        }
-
-    }
-
-    IIntentSender getIntentSenderLocked(int type, String packageName, int callingUid, int userId,
-            IBinder token, String resultWho, int requestCode, Intent[] intents,
-            String[] resolvedTypes, int flags, Bundle bOptions) {
-
-        ActivityRecord activity = null;
-        if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
-            activity = ActivityRecord.isInStackLocked(token);
-            if (activity == null) {
-                Slog.w(TAG, "Failed createPendingResult: activity " + token + " not in any stack");
-                return null;
-            }
-            if (activity.finishing) {
-                Slog.w(TAG, "Failed createPendingResult: activity " + activity + " is finishing");
-                return null;
-            }
-        }
-
-        final PendingIntentRecord rec = mPendingIntentController.getIntentSender(type, packageName,
-                callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
-                bOptions);
-        final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
-        if (noCreate) {
-            return rec;
-        }
-        if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
-            if (activity.pendingResults == null) {
-                activity.pendingResults = new HashSet<>();
-            }
-            activity.pendingResults.add(rec.ref);
-        }
-        return rec;
-    }
-
-    // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
-    private void startTimeTrackingFocusedActivityLocked() {
-        final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
-        if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
-            mCurAppTimeTracker.start(resumedActivity.packageName);
-        }
-    }
-
-    private void updateResumedAppTrace(@Nullable ActivityRecord resumed) {
-        if (mTracedResumedActivity != null) {
-            Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER,
-                    constructResumedTraceName(mTracedResumedActivity.packageName), 0);
-        }
-        if (resumed != null) {
-            Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER,
-                    constructResumedTraceName(resumed.packageName), 0);
-        }
-        mTracedResumedActivity = resumed;
-    }
-
-    private String constructResumedTraceName(String packageName) {
-        return "focused app: " + packageName;
-    }
-
-    /** Helper method that requests bounds from WM and applies them to stack. */
-    private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
-        final Rect newStackBounds = new Rect();
-        final ActivityStack stack = mStackSupervisor.getStack(stackId);
-
-        // TODO(b/71548119): Revert CL introducing below once cause of mismatch is found.
-        if (stack == null) {
-            final StringWriter writer = new StringWriter();
-            final PrintWriter printWriter = new PrintWriter(writer);
-            mStackSupervisor.dumpDisplays(printWriter);
-            printWriter.flush();
-
-            Log.wtf(TAG, "stack not found:" + stackId + " displays:" + writer);
-        }
-
-        stack.getBoundsForNewConfiguration(newStackBounds);
-        mStackSupervisor.resizeStackLocked(
-                stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
-                null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
-                false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
-    }
-
-    /** Applies latest configuration and/or visibility updates if needed. */
-    private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
-        boolean kept = true;
-        final ActivityStack mainStack = mStackSupervisor.getTopDisplayFocusedStack();
-        // mainStack is null during startup.
-        if (mainStack != null) {
-            if (changes != 0 && starting == null) {
-                // If the configuration changed, and the caller is not already
-                // in the process of starting an activity, then find the top
-                // activity to check if its configuration needs to change.
-                starting = mainStack.topRunningActivityLocked();
-            }
-
-            if (starting != null) {
-                kept = starting.ensureActivityConfiguration(changes,
-                        false /* preserveWindow */);
-                // And we need to make sure at this point that all other activities
-                // are made visible with the correct configuration.
-                mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
-                        !PRESERVE_WINDOWS);
-            }
-        }
-
-        return kept;
-    }
-
-    void scheduleAppGcsLocked() {
-        mH.post(() -> mAmInternal.scheduleAppGcs());
-    }
-
-    CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
-        return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
-    }
-
-    /**
-     * Returns the PackageManager. Used by classes hosted by {@link ActivityTaskManagerService}. The
-     * PackageManager could be unavailable at construction time and therefore needs to be accessed
-     * on demand.
-     */
-    IPackageManager getPackageManager() {
-        return AppGlobals.getPackageManager();
-    }
-
-    PackageManagerInternal getPackageManagerInternalLocked() {
-        if (mPmInternal == null) {
-            mPmInternal = LocalServices.getService(PackageManagerInternal.class);
-        }
-        return mPmInternal;
-    }
-
-    AppWarnings getAppWarningsLocked() {
-        return mAppWarnings;
-    }
-
-    Intent getHomeIntent() {
-        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
-        intent.setComponent(mTopComponent);
-        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
-        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
-            intent.addCategory(Intent.CATEGORY_HOME);
-        }
-        return intent;
-    }
-
-    /**
-     * This starts home activity on displays that can have system decorations and only if the
-     * home activity can have multiple instances.
-     */
-    boolean startHomeActivityLocked(int userId, String reason, int displayId) {
-        if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL && mTopAction == null) {
-            // We are running in factory test mode, but unable to find the factory test app, so just
-            // sit around displaying the error message and don't try to start anything.
-            return false;
-        }
-
-        final Intent intent = getHomeIntent();
-        ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
-        if (aInfo != null) {
-            intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
-            // Don't do this if the home app is currently being instrumented.
-            aInfo = new ActivityInfo(aInfo);
-            aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
-            WindowProcessController app =
-                    getProcessController(aInfo.processName, aInfo.applicationInfo.uid);
-            if (app == null || !app.isInstrumenting()) {
-                intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
-                final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
-                // For ANR debugging to verify if the user activity is the one that actually
-                // launched.
-                final String myReason = reason + ":" + userId + ":" + resolvedUserId;
-                getActivityStartController().startHomeActivity(intent, aInfo, myReason, displayId);
-            }
-        } else {
-            Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
-        }
-
-        return true;
-    }
-
-    private ActivityInfo resolveActivityInfo(Intent intent, int flags, int userId) {
-        ActivityInfo ai = null;
-        final ComponentName comp = intent.getComponent();
-        try {
-            if (comp != null) {
-                // Factory test.
-                ai = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
-            } else {
-                ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(
-                        intent,
-                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                        flags, userId);
-
-                if (info != null) {
-                    ai = info.activityInfo;
-                }
-            }
-        } catch (RemoteException e) {
-            // ignore
-        }
-
-        return ai;
-    }
-
-    ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
-        if (info == null) return null;
-        ApplicationInfo newInfo = new ApplicationInfo(info);
-        newInfo.initForUser(userId);
-        return newInfo;
-    }
-
-    WindowProcessController getProcessController(String processName, int uid) {
-        if (uid == SYSTEM_UID) {
-            // The system gets to run in any process. If there are multiple processes with the same
-            // uid, just pick the first (this should never happen).
-            final SparseArray<WindowProcessController> procs =
-                    mProcessNames.getMap().get(processName);
-            if (procs == null) return null;
-            final int procCount = procs.size();
-            for (int i = 0; i < procCount; i++) {
-                final int procUid = procs.keyAt(i);
-                if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
-                    // Don't use an app process or different user process for system component.
-                    continue;
-                }
-                return procs.valueAt(i);
-            }
-        }
-
-        return mProcessNames.get(processName, uid);
-    }
-
-    WindowProcessController getProcessController(IApplicationThread thread) {
-        if (thread == null) {
-            return null;
-        }
-
-        final IBinder threadBinder = thread.asBinder();
-        final ArrayMap<String, SparseArray<WindowProcessController>> pmap = mProcessNames.getMap();
-        for (int i = pmap.size()-1; i >= 0; i--) {
-            final SparseArray<WindowProcessController> procs = pmap.valueAt(i);
-            for (int j = procs.size() - 1; j >= 0; j--) {
-                final WindowProcessController proc = procs.valueAt(j);
-                if (proc.hasThread() && proc.getThread().asBinder() == threadBinder) {
-                    return proc;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    int getUidStateLocked(int uid) {
-        return mActiveUids.get(uid, PROCESS_STATE_NONEXISTENT);
-    }
-
-    /**
-     * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on
-     * the whitelist
-     */
-    String getPendingTempWhitelistTagForUidLocked(int uid) {
-        return mPendingTempWhitelist.get(uid);
-    }
-
-    void logAppTooSlow(WindowProcessController app, long startTime, String msg) {
-        if (true || Build.IS_USER) {
-            return;
-        }
-
-        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-        StrictMode.allowThreadDiskWrites();
-        try {
-            File tracesDir = new File("/data/anr");
-            File tracesFile = null;
-            try {
-                tracesFile = File.createTempFile("app_slow", null, tracesDir);
-
-                StringBuilder sb = new StringBuilder();
-                Time tobj = new Time();
-                tobj.set(System.currentTimeMillis());
-                sb.append(tobj.format("%Y-%m-%d %H:%M:%S"));
-                sb.append(": ");
-                TimeUtils.formatDuration(SystemClock.uptimeMillis()-startTime, sb);
-                sb.append(" since ");
-                sb.append(msg);
-                FileOutputStream fos = new FileOutputStream(tracesFile);
-                fos.write(sb.toString().getBytes());
-                if (app == null) {
-                    fos.write("\n*** No application process!".getBytes());
-                }
-                fos.close();
-                FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw-
-            } catch (IOException e) {
-                Slog.w(TAG, "Unable to prepare slow app traces file: " + tracesFile, e);
-                return;
-            }
-
-            if (app != null && app.getPid() > 0) {
-                ArrayList<Integer> firstPids = new ArrayList<Integer>();
-                firstPids.add(app.getPid());
-                dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null);
-            }
-
-            File lastTracesFile = null;
-            File curTracesFile = null;
-            for (int i=9; i>=0; i--) {
-                String name = String.format(Locale.US, "slow%02d.txt", i);
-                curTracesFile = new File(tracesDir, name);
-                if (curTracesFile.exists()) {
-                    if (lastTracesFile != null) {
-                        curTracesFile.renameTo(lastTracesFile);
-                    } else {
-                        curTracesFile.delete();
-                    }
-                }
-                lastTracesFile = curTracesFile;
-            }
-            tracesFile.renameTo(curTracesFile);
-        } finally {
-            StrictMode.setThreadPolicy(oldPolicy);
-        }
-    }
-
-    final class H extends Handler {
-        static final int REPORT_TIME_TRACKER_MSG = 1;
-        static final int FIRST_ACTIVITY_STACK_MSG = 100;
-        static final int FIRST_SUPERVISOR_STACK_MSG = 200;
-
-        public H(Looper looper) {
-            super(looper, null, true);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case REPORT_TIME_TRACKER_MSG: {
-                    AppTimeTracker tracker = (AppTimeTracker) msg.obj;
-                    tracker.deliverResult(mContext);
-                } break;
-            }
-        }
-    }
-
-    final class UiHandler extends Handler {
-        static final int DISMISS_DIALOG_UI_MSG = 1;
-
-        public UiHandler() {
-            super(com.android.server.UiThread.get().getLooper(), null, true);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case DISMISS_DIALOG_UI_MSG: {
-                    final Dialog d = (Dialog) msg.obj;
-                    d.dismiss();
-                    break;
-                }
-            }
-        }
-    }
-
-    final class LocalService extends ActivityTaskManagerInternal {
-        @Override
-        public SleepToken acquireSleepToken(String tag, int displayId) {
-            Preconditions.checkNotNull(tag);
-            return ActivityTaskManagerService.this.acquireSleepToken(tag, displayId);
-        }
-
-        @Override
-        public ComponentName getHomeActivityForUser(int userId) {
-            synchronized (mGlobalLock) {
-                ActivityRecord homeActivity =
-                        mStackSupervisor.getDefaultDisplayHomeActivityForUser(userId);
-                return homeActivity == null ? null : homeActivity.realActivity;
-            }
-        }
-
-        @Override
-        public void onLocalVoiceInteractionStarted(IBinder activity,
-                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
-            synchronized (mGlobalLock) {
-                onLocalVoiceInteractionStartedLocked(activity, voiceSession, voiceInteractor);
-            }
-        }
-
-        @Override
-        public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
-                        reasons, timestamp);
-            }
-        }
-
-        @Override
-        public void notifyAppTransitionFinished() {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.notifyAppTransitionDone();
-            }
-        }
-
-        @Override
-        public void notifyAppTransitionCancelled() {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.notifyAppTransitionDone();
-            }
-        }
-
-        @Override
-        public List<IBinder> getTopVisibleActivities() {
-            synchronized (mGlobalLock) {
-                return mStackSupervisor.getTopVisibleActivities();
-            }
-        }
-
-        @Override
-        public void notifyDockedStackMinimizedChanged(boolean minimized) {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.setDockedStackMinimized(minimized);
-            }
-        }
-
-        @Override
-        public int startActivitiesAsPackage(String packageName, int userId, Intent[] intents,
-                Bundle bOptions) {
-            Preconditions.checkNotNull(intents, "intents");
-            final String[] resolvedTypes = new String[intents.length];
-
-            // UID of the package on user userId.
-            // "= 0" is needed because otherwise catch(RemoteException) would make it look like
-            // packageUid may not be initialized.
-            int packageUid = 0;
-            final long ident = Binder.clearCallingIdentity();
-
-            try {
-                for (int i = 0; i < intents.length; i++) {
-                    resolvedTypes[i] =
-                            intents[i].resolveTypeIfNeeded(mContext.getContentResolver());
-                }
-
-                packageUid = AppGlobals.getPackageManager().getPackageUid(
-                        packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
-            } catch (RemoteException e) {
-                // Shouldn't happen.
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-
-            synchronized (mGlobalLock) {
-                return getActivityStartController().startActivitiesInPackage(
-                        packageUid, packageName,
-                        intents, resolvedTypes, null /* resultTo */,
-                        SafeActivityOptions.fromBundle(bOptions), userId,
-                        false /* validateIncomingUser */, null /* originatingPendingIntent */);
-            }
-        }
-
-        @Override
-        public int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
-                String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-                boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent) {
-            synchronized (mGlobalLock) {
-                return getActivityStartController().startActivitiesInPackage(uid, callingPackage,
-                        intents, resolvedTypes, resultTo, options, userId, validateIncomingUser,
-                        originatingPendingIntent);
-            }
-        }
-
-        @Override
-        public int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
-                String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
-                String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
-                int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
-                PendingIntentRecord originatingPendingIntent) {
-            synchronized (mGlobalLock) {
-                return getActivityStartController().startActivityInPackage(uid, realCallingPid,
-                        realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho,
-                        requestCode, startFlags, options, userId, inTask, reason,
-                        validateIncomingUser, originatingPendingIntent);
-            }
-        }
-
-        @Override
-        public int startActivityAsUser(IApplicationThread caller, String callerPacakge,
-                Intent intent, Bundle options, int userId) {
-            return ActivityTaskManagerService.this.startActivityAsUser(
-                    caller, callerPacakge, intent,
-                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, userId,
-                    false /*validateIncomingUser*/);
-        }
-
-        @Override
-        public void notifyKeyguardFlagsChanged(@Nullable Runnable callback) {
-            synchronized (mGlobalLock) {
-
-                // We might change the visibilities here, so prepare an empty app transition which
-                // might be overridden later if we actually change visibilities.
-                final boolean wasTransitionSet =
-                        mWindowManager.getPendingAppTransition() != TRANSIT_NONE;
-                if (!wasTransitionSet) {
-                    mWindowManager.prepareAppTransition(TRANSIT_NONE,
-                            false /* alwaysKeepCurrent */);
-                }
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-
-                // If there was a transition set already we don't want to interfere with it as we
-                // might be starting it too early.
-                if (!wasTransitionSet) {
-                    mWindowManager.executeAppTransition();
-                }
-            }
-            if (callback != null) {
-                callback.run();
-            }
-        }
-
-        @Override
-        public void notifyKeyguardTrustedChanged() {
-            synchronized (mGlobalLock) {
-                if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                }
-            }
-        }
-
-        /**
-         * Called after virtual display Id is updated by
-         * {@link com.android.server.vr.Vr2dDisplay} with a specific
-         * {@param vrVr2dDisplayId}.
-         */
-        @Override
-        public void setVr2dDisplayId(int vr2dDisplayId) {
-            if (DEBUG_STACK) Slog.d(TAG, "setVr2dDisplayId called for: " + vr2dDisplayId);
-            synchronized (mGlobalLock) {
-                mVr2dDisplayId = vr2dDisplayId;
-            }
-        }
-
-        @Override
-        public void setFocusedActivity(IBinder token) {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-                if (r == null) {
-                    throw new IllegalArgumentException(
-                            "setFocusedActivity: No activity record matching token=" + token);
-                }
-                if (r.moveFocusableActivityToTop("setFocusedActivity")) {
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-                }
-            }
-        }
-
-        @Override
-        public void registerScreenObserver(ScreenObserver observer) {
-            mScreenObservers.add(observer);
-        }
-
-        @Override
-        public boolean isCallerRecents(int callingUid) {
-            return getRecentTasks().isCallerRecents(callingUid);
-        }
-
-        @Override
-        public boolean isRecentsComponentHomeActivity(int userId) {
-            return getRecentTasks().isRecentsComponentHomeActivity(userId);
-        }
-
-        @Override
-        public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
-            ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeStackPosition);
-        }
-
-        @Override
-        public void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
-            ActivityTaskManagerService.this.enforceCallerIsRecentsOrHasPermission(permission, func);
-        }
-
-        @Override
-        public void notifyActiveVoiceInteractionServiceChanged(ComponentName component) {
-            synchronized (mGlobalLock) {
-                mActiveVoiceInteractionServiceComponent = component;
-            }
-        }
-
-        @Override
-        public void setAllowAppSwitches(@NonNull String type, int uid, int userId) {
-            if (!mAmInternal.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) {
-                return;
-            }
-            synchronized (mGlobalLock) {
-                ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(userId);
-                if (types == null) {
-                    if (uid < 0) {
-                        return;
-                    }
-                    types = new ArrayMap<>();
-                    mAllowAppSwitchUids.put(userId, types);
-                }
-                if (uid < 0) {
-                    types.remove(type);
-                } else {
-                    types.put(type, uid);
-                }
-            }
-        }
-
-        @Override
-        public void onUserStopped(int userId) {
-            synchronized (mGlobalLock) {
-                getRecentTasks().unloadUserDataFromMemoryLocked(userId);
-                mAllowAppSwitchUids.remove(userId);
-            }
-        }
-
-        @Override
-        public boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
-            synchronized (mGlobalLock) {
-                return ActivityTaskManagerService.this.isGetTasksAllowed(
-                        caller, callingPid, callingUid);
-            }
-        }
-
-        @Override
-        public void onProcessAdded(WindowProcessController proc) {
-            synchronized (mGlobalLock) {
-                mProcessNames.put(proc.mName, proc.mUid, proc);
-            }
-        }
-
-        @Override
-        public void onProcessRemoved(String name, int uid) {
-            synchronized (mGlobalLock) {
-                mProcessNames.remove(name, uid);
-            }
-        }
-
-        @Override
-        public void onCleanUpApplicationRecord(WindowProcessController proc) {
-            synchronized (mGlobalLock) {
-                if (proc == mHomeProcess) {
-                    mHomeProcess = null;
-                }
-                if (proc == mPreviousProcess) {
-                    mPreviousProcess = null;
-                }
-            }
-        }
-
-        @Override
-        public int getTopProcessState() {
-            synchronized (mGlobalLock) {
-                return mTopProcessState;
-            }
-        }
-
-        @Override
-        public boolean isHeavyWeightProcess(WindowProcessController proc) {
-            synchronized (mGlobalLock) {
-                return proc == mHeavyWeightProcess;
-            }
-        }
-
-        @Override
-        public void clearHeavyWeightProcessIfEquals(WindowProcessController proc) {
-            synchronized (mGlobalLock) {
-                ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(proc);
-            }
-        }
-
-        @Override
-        public void finishHeavyWeightApp() {
-            synchronized (mGlobalLock) {
-                if (mHeavyWeightProcess != null) {
-                    mHeavyWeightProcess.finishActivities();
-                }
-                ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(
-                        mHeavyWeightProcess);
-            }
-        }
-
-        @Override
-        public boolean isSleeping() {
-            synchronized (mGlobalLock) {
-                return isSleepingLocked();
-            }
-        }
-
-        @Override
-        public boolean isShuttingDown() {
-            synchronized (mGlobalLock) {
-                return mShuttingDown;
-            }
-        }
-
-        @Override
-        public boolean shuttingDown(boolean booted, int timeout) {
-            synchronized (mGlobalLock) {
-                mShuttingDown = true;
-                mStackSupervisor.prepareForShutdownLocked();
-                updateEventDispatchingLocked(booted);
-                return mStackSupervisor.shutdownLocked(timeout);
-            }
-        }
-
-        @Override
-        public void enableScreenAfterBoot(boolean booted) {
-            synchronized (mGlobalLock) {
-                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
-                        SystemClock.uptimeMillis());
-                mWindowManager.enableScreenAfterBoot();
-                updateEventDispatchingLocked(booted);
-            }
-        }
-
-        @Override
-        public boolean showStrictModeViolationDialog() {
-            synchronized (mGlobalLock) {
-                return mShowDialogs && !mSleeping && !mShuttingDown;
-            }
-        }
-
-        @Override
-        public void showSystemReadyErrorDialogsIfNeeded() {
-            synchronized (mGlobalLock) {
-                try {
-                    if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
-                        Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your"
-                                + " data partition or your device will be unstable.");
-                        mUiHandler.post(() -> {
-                            if (mShowDialogs) {
-                                AlertDialog d = new BaseErrorDialog(mUiContext);
-                                d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
-                                d.setCancelable(false);
-                                d.setTitle(mUiContext.getText(R.string.android_system_label));
-                                d.setMessage(mUiContext.getText(R.string.system_error_wipe_data));
-                                d.setButton(DialogInterface.BUTTON_POSITIVE,
-                                        mUiContext.getText(R.string.ok),
-                                        mUiHandler.obtainMessage(DISMISS_DIALOG_UI_MSG, d));
-                                d.show();
-                            }
-                        });
-                    }
-                } catch (RemoteException e) {
-                }
-
-                if (!Build.isBuildConsistent()) {
-                    Slog.e(TAG, "Build fingerprint is not consistent, warning user");
-                    mUiHandler.post(() -> {
-                        if (mShowDialogs) {
-                            AlertDialog d = new BaseErrorDialog(mUiContext);
-                            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
-                            d.setCancelable(false);
-                            d.setTitle(mUiContext.getText(R.string.android_system_label));
-                            d.setMessage(mUiContext.getText(R.string.system_error_manufacturer));
-                            d.setButton(DialogInterface.BUTTON_POSITIVE,
-                                    mUiContext.getText(R.string.ok),
-                                    mUiHandler.obtainMessage(DISMISS_DIALOG_UI_MSG, d));
-                            d.show();
-                        }
-                    });
-                }
-            }
-        }
-
-        @Override
-        public void onProcessMapped(int pid, WindowProcessController proc) {
-            synchronized (mGlobalLock) {
-                mPidMap.put(pid, proc);
-            }
-        }
-
-        @Override
-        public void onProcessUnMapped(int pid) {
-            synchronized (mGlobalLock) {
-                mPidMap.remove(pid);
-            }
-        }
-
-        @Override
-        public void onPackageDataCleared(String name) {
-            synchronized (mGlobalLock) {
-                mCompatModePackages.handlePackageDataClearedLocked(name);
-                mAppWarnings.onPackageDataCleared(name);
-            }
-        }
-
-        @Override
-        public void onPackageUninstalled(String name) {
-            synchronized (mGlobalLock) {
-                mAppWarnings.onPackageUninstalled(name);
-                mCompatModePackages.handlePackageUninstalledLocked(name);
-            }
-        }
-
-        @Override
-        public void onPackageAdded(String name, boolean replacing) {
-            synchronized (mGlobalLock) {
-                mCompatModePackages.handlePackageAddedLocked(name, replacing);
-            }
-        }
-
-        @Override
-        public void onPackageReplaced(ApplicationInfo aInfo) {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
-            }
-        }
-
-        @Override
-        public CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
-            synchronized (mGlobalLock) {
-                return compatibilityInfoForPackageLocked(ai);
-            }
-        }
-
-        /**
-         * Set the corresponding display information for the process global configuration. To be
-         * called when we need to show IME on a different display.
-         *
-         * @param pid The process id associated with the IME window.
-         * @param displayId The ID of the display showing the IME.
-         */
-        @Override
-        public void onImeWindowSetOnDisplay(final int pid, final int displayId) {
-            if (pid == MY_PID || pid < 0) {
-                if (DEBUG_CONFIGURATION) {
-                    Slog.w(TAG,
-                            "Trying to update display configuration for system/invalid process.");
-                }
-                return;
-            }
-            mH.post(() -> {
-                synchronized (mGlobalLock) {
-                    final ActivityDisplay activityDisplay =
-                            mStackSupervisor.getActivityDisplay(displayId);
-                    if (activityDisplay == null) {
-                        // Call might come when display is not yet added or has been removed.
-                        if (DEBUG_CONFIGURATION) {
-                            Slog.w(TAG, "Trying to update display configuration for non-existing "
-                                    + "displayId=" + displayId);
-                        }
-                        return;
-                    }
-                    final WindowProcessController process = mPidMap.get(pid);
-                    if (process == null) {
-                        if (DEBUG_CONFIGURATION) {
-                            Slog.w(TAG, "Trying to update display configuration for invalid "
-                                    + "process, pid=" + pid);
-                        }
-                        return;
-                    }
-                    process.registerDisplayConfigurationListenerLocked(activityDisplay);
-                }
-            });
-
-        }
-
-        @Override
-        public void sendActivityResult(int callingUid, IBinder activityToken, String resultWho,
-                int requestCode, int resultCode, Intent data) {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
-                if (r != null && r.getStack() != null) {
-                    r.getStack().sendActivityResultLocked(callingUid, r, resultWho, requestCode,
-                            resultCode, data);
-                }
-            }
-        }
-
-        @Override
-        public void clearPendingResultForActivity(IBinder activityToken,
-                WeakReference<PendingIntentRecord> pir) {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
-                if (r != null && r.pendingResults != null) {
-                    r.pendingResults.remove(pir);
-                }
-            }
-        }
-
-        @Override
-        public IIntentSender getIntentSender(int type, String packageName,
-                int callingUid, int userId, IBinder token, String resultWho,
-                int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
-                Bundle bOptions) {
-            synchronized (mGlobalLock) {
-                return getIntentSenderLocked(type, packageName, callingUid, userId, token,
-                        resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
-            }
-        }
-
-        @Override
-        public ActivityServiceConnectionsHolder getServiceConnectionsHolder(IBinder token) {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r == null) {
-                    return null;
-                }
-                if (r.mServiceConnectionsHolder == null) {
-                    r.mServiceConnectionsHolder = new ActivityServiceConnectionsHolder(
-                            ActivityTaskManagerService.this, r);
-                }
-
-                return r.mServiceConnectionsHolder;
-            }
-        }
-
-        @Override
-        public Intent getHomeIntent() {
-            synchronized (mGlobalLock) {
-                return ActivityTaskManagerService.this.getHomeIntent();
-            }
-        }
-
-        @Override
-        public boolean startHomeActivity(int userId, String reason) {
-            synchronized (mGlobalLock) {
-                return startHomeActivityLocked(userId, reason, DEFAULT_DISPLAY);
-            }
-        }
-
-        @Override
-        public boolean isFactoryTestProcess(WindowProcessController wpc) {
-            synchronized (mGlobalLock) {
-                if (mFactoryTest == FACTORY_TEST_OFF) {
-                    return false;
-                }
-                if (mFactoryTest == FACTORY_TEST_LOW_LEVEL && mTopComponent != null
-                        && wpc.mName.equals(mTopComponent.getPackageName())) {
-                    return true;
-                }
-                return mFactoryTest == FACTORY_TEST_HIGH_LEVEL
-                        && (wpc.mInfo.flags & FLAG_FACTORY_TEST) != 0;
-            }
-        }
-
-        @Override
-        public void updateTopComponentForFactoryTest() {
-            synchronized (mGlobalLock) {
-                if (mFactoryTest != FACTORY_TEST_LOW_LEVEL) {
-                    return;
-                }
-                final ResolveInfo ri = mContext.getPackageManager()
-                        .resolveActivity(new Intent(Intent.ACTION_FACTORY_TEST), STOCK_PM_FLAGS);
-                final CharSequence errorMsg;
-                if (ri != null) {
-                    final ActivityInfo ai = ri.activityInfo;
-                    final ApplicationInfo app = ai.applicationInfo;
-                    if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                        mTopAction = Intent.ACTION_FACTORY_TEST;
-                        mTopData = null;
-                        mTopComponent = new ComponentName(app.packageName, ai.name);
-                        errorMsg = null;
-                    } else {
-                        errorMsg = mContext.getResources().getText(
-                                com.android.internal.R.string.factorytest_not_system);
-                    }
-                } else {
-                    errorMsg = mContext.getResources().getText(
-                            com.android.internal.R.string.factorytest_no_action);
-                }
-                if (errorMsg == null) {
-                    return;
-                }
-
-                mTopAction = null;
-                mTopData = null;
-                mTopComponent = null;
-                mUiHandler.post(() -> {
-                    Dialog d = new FactoryErrorDialog(mUiContext, errorMsg);
-                    d.show();
-                    mAmInternal.ensureBootCompleted();
-                });
-            }
-        }
-
-        @Override
-        public void handleAppDied(WindowProcessController wpc, boolean restarting,
-                Runnable finishInstrumentationCallback) {
-            synchronized (mGlobalLock) {
-                // Remove this application's activities from active lists.
-                boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(wpc);
-
-                wpc.clearRecentTasks();
-                wpc.clearActivities();
-
-                if (wpc.isInstrumenting()) {
-                    finishInstrumentationCallback.run();
-                }
-
-                mWindowManager.deferSurfaceLayout();
-                try {
-                    if (!restarting && hasVisibleActivities
-                            && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) {
-                        // If there was nothing to resume, and we are not already restarting this
-                        // process, but there is a visible activity that is hosted by the process...
-                        // then make sure all visible activities are running, taking care of
-                        // restarting this process.
-                        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                    }
-                } finally {
-                    mWindowManager.continueSurfaceLayout();
-                }
-            }
-        }
-
-        @Override
-        public void closeSystemDialogs(String reason) {
-            enforceNotIsolatedCaller("closeSystemDialogs");
-
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                synchronized (mGlobalLock) {
-                    // Only allow this from foreground processes, so that background
-                    // applications can't abuse it to prevent system UI from being shown.
-                    if (uid >= FIRST_APPLICATION_UID) {
-                        final WindowProcessController proc = mPidMap.get(pid);
-                        if (!proc.isPerceptible()) {
-                            Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
-                                    + " from background process " + proc);
-                            return;
-                        }
-                    }
-                    mWindowManager.closeSystemDialogs(reason);
-
-                    mStackSupervisor.closeSystemDialogsLocked();
-                }
-                // Call into AM outside the synchronized block.
-                mAmInternal.broadcastCloseSystemDialogs(reason);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-
-        @Override
-        public void cleanupDisabledPackageComponents(
-                String packageName, Set<String> disabledClasses, int userId, boolean booted) {
-            synchronized (mGlobalLock) {
-                // Clean-up disabled activities.
-                if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
-                        packageName, disabledClasses, true, false, userId) && booted) {
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-                    mStackSupervisor.scheduleIdleLocked();
-                }
-
-                // Clean-up disabled tasks
-                getRecentTasks().cleanupDisabledPackageTasksLocked(
-                        packageName, disabledClasses, userId);
-            }
-        }
-
-        @Override
-        public boolean onForceStopPackage(String packageName, boolean doit, boolean evenPersistent,
-                int userId) {
-            synchronized (mGlobalLock) {
-
-                boolean didSomething =
-                        getActivityStartController().clearPendingActivityLaunches(packageName);
-                didSomething |= mStackSupervisor.finishDisabledPackageActivitiesLocked(packageName,
-                        null, doit, evenPersistent, userId);
-                return didSomething;
-            }
-        }
-
-        @Override
-        public void resumeTopActivities(boolean scheduleIdle) {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-                if (scheduleIdle) {
-                    mStackSupervisor.scheduleIdleLocked();
-                }
-            }
-        }
-
-        @Override
-        public void preBindApplication(WindowProcessController wpc) {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(wpc.mInfo);
-            }
-        }
-
-        @Override
-        public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
-            synchronized (mGlobalLock) {
-                return mStackSupervisor.attachApplicationLocked(wpc);
-            }
-        }
-
-        @Override
-        public void notifyLockedProfile(@UserIdInt int userId, int currentUserId) {
-            try {
-                if (!AppGlobals.getPackageManager().isUidPrivileged(Binder.getCallingUid())) {
-                    throw new SecurityException("Only privileged app can call notifyLockedProfile");
-                }
-            } catch (RemoteException ex) {
-                throw new SecurityException("Fail to check is caller a privileged app", ex);
-            }
-
-            synchronized (mGlobalLock) {
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    if (mAmInternal.shouldConfirmCredentials(userId)) {
-                        if (mKeyguardController.isKeyguardLocked()) {
-                            // Showing launcher to avoid user entering credential twice.
-                            startHomeActivity(currentUserId, "notifyLockedProfile");
-                        }
-                        mStackSupervisor.lockAllProfileTasks(userId);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            }
-        }
-
-        @Override
-        public void startConfirmDeviceCredentialIntent(Intent intent, Bundle options) {
-            mAmInternal.enforceCallingPermission(
-                    MANAGE_ACTIVITY_STACKS, "startConfirmDeviceCredentialIntent");
-
-            synchronized (mGlobalLock) {
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
-                            FLAG_ACTIVITY_TASK_ON_HOME);
-                    ActivityOptions activityOptions = options != null
-                            ? new ActivityOptions(options) : ActivityOptions.makeBasic();
-                    activityOptions.setLaunchTaskId(
-                            mStackSupervisor.getDefaultDisplayHomeActivity().getTask().taskId);
-                    mContext.startActivityAsUser(intent, activityOptions.toBundle(),
-                            UserHandle.CURRENT);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            }
-        }
-
-        @Override
-        public void writeActivitiesToProto(ProtoOutputStream proto) {
-            synchronized (mGlobalLock) {
-                // The output proto of "activity --proto activities"
-                // is ActivityManagerServiceDumpActivitiesProto
-                mStackSupervisor.writeToProto(proto,
-                        ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
-            }
-        }
-
-        @Override
-        public void saveANRState(String reason) {
-            synchronized (mGlobalLock) {
-                final StringWriter sw = new StringWriter();
-                final PrintWriter pw = new FastPrintWriter(sw, false, 1024);
-                pw.println("  ANR time: " + DateFormat.getDateTimeInstance().format(new Date()));
-                if (reason != null) {
-                    pw.println("  Reason: " + reason);
-                }
-                pw.println();
-                getActivityStartController().dump(pw, "  ", null);
-                pw.println();
-                pw.println("-------------------------------------------------------------------------------");
-                dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
-                        true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */,
-                        "" /* header */);
-                pw.println();
-                pw.close();
-
-                mLastANRState = sw.toString();
-            }
-        }
-
-        @Override
-        public void clearSavedANRState() {
-            synchronized (mGlobalLock) {
-                mLastANRState = null;
-            }
-        }
-
-        @Override
-        public void dump(String cmd, FileDescriptor fd, PrintWriter pw, String[] args, int opti,
-                boolean dumpAll, boolean dumpClient, String dumpPackage) {
-            synchronized (mGlobalLock) {
-                if (DUMP_ACTIVITIES_CMD.equals(cmd) || DUMP_ACTIVITIES_SHORT_CMD.equals(cmd)) {
-                    dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
-                } else if (DUMP_LASTANR_CMD.equals(cmd)) {
-                    dumpLastANRLocked(pw);
-                } else if (DUMP_LASTANR_TRACES_CMD.equals(cmd)) {
-                    dumpLastANRTracesLocked(pw);
-                } else if (DUMP_STARTER_CMD.equals(cmd)) {
-                    dumpActivityStarterLocked(pw, dumpPackage);
-                } else if (DUMP_CONTAINERS_CMD.equals(cmd)) {
-                    dumpActivityContainersLocked(pw);
-                } else if (DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd)) {
-                    if (getRecentTasks() != null) {
-                        getRecentTasks().dump(pw, dumpAll, dumpPackage);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public boolean dumpForProcesses(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
-                String dumpPackage, int dumpAppId, boolean needSep, boolean testPssMode,
-                int wakefulness) {
-            synchronized (mGlobalLock) {
-                if (mHomeProcess != null && (dumpPackage == null
-                        || mHomeProcess.mPkgList.contains(dumpPackage))) {
-                    if (needSep) {
-                        pw.println();
-                        needSep = false;
-                    }
-                    pw.println("  mHomeProcess: " + mHomeProcess);
-                }
-                if (mPreviousProcess != null && (dumpPackage == null
-                        || mPreviousProcess.mPkgList.contains(dumpPackage))) {
-                    if (needSep) {
-                        pw.println();
-                        needSep = false;
-                    }
-                    pw.println("  mPreviousProcess: " + mPreviousProcess);
-                }
-                if (dumpAll && (mPreviousProcess == null || dumpPackage == null
-                        || mPreviousProcess.mPkgList.contains(dumpPackage))) {
-                    StringBuilder sb = new StringBuilder(128);
-                    sb.append("  mPreviousProcessVisibleTime: ");
-                    TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb);
-                    pw.println(sb);
-                }
-                if (mHeavyWeightProcess != null && (dumpPackage == null
-                        || mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
-                    if (needSep) {
-                        pw.println();
-                        needSep = false;
-                    }
-                    pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
-                }
-                if (dumpPackage == null) {
-                    pw.println("  mGlobalConfiguration: " + getGlobalConfiguration());
-                    mStackSupervisor.dumpDisplayConfigs(pw, "  ");
-                }
-                if (dumpAll) {
-                    if (dumpPackage == null) {
-                        pw.println("  mConfigWillChange: "
-                                + getTopDisplayFocusedStack().mConfigWillChange);
-                    }
-                    if (mCompatModePackages.getPackages().size() > 0) {
-                        boolean printed = false;
-                        for (Map.Entry<String, Integer> entry
-                                : mCompatModePackages.getPackages().entrySet()) {
-                            String pkg = entry.getKey();
-                            int mode = entry.getValue();
-                            if (dumpPackage != null && !dumpPackage.equals(pkg)) {
-                                continue;
-                            }
-                            if (!printed) {
-                                pw.println("  mScreenCompatPackages:");
-                                printed = true;
-                            }
-                            pw.println("    " + pkg + ": " + mode);
-                        }
-                    }
-                }
-
-                if (dumpPackage == null) {
-                    pw.println("  mWakefulness="
-                            + PowerManagerInternal.wakefulnessToString(wakefulness));
-                    pw.println("  mSleepTokens=" + mStackSupervisor.mSleepTokens);
-                    if (mRunningVoice != null) {
-                        pw.println("  mRunningVoice=" + mRunningVoice);
-                        pw.println("  mVoiceWakeLock" + mVoiceWakeLock);
-                    }
-                    pw.println("  mSleeping=" + mSleeping);
-                    pw.println("  mShuttingDown=" + mShuttingDown + " mTestPssMode=" + testPssMode);
-                    pw.println("  mVrController=" + mVrController);
-                }
-                if (mCurAppTimeTracker != null) {
-                    mCurAppTimeTracker.dumpWithHeader(pw, "  ", true);
-                }
-                if (mAllowAppSwitchUids.size() > 0) {
-                    boolean printed = false;
-                    for (int i = 0; i < mAllowAppSwitchUids.size(); i++) {
-                        ArrayMap<String, Integer> types = mAllowAppSwitchUids.valueAt(i);
-                        for (int j = 0; j < types.size(); j++) {
-                            if (dumpPackage == null ||
-                                    UserHandle.getAppId(types.valueAt(j).intValue()) == dumpAppId) {
-                                if (needSep) {
-                                    pw.println();
-                                    needSep = false;
-                                }
-                                if (!printed) {
-                                    pw.println("  mAllowAppSwitchUids:");
-                                    printed = true;
-                                }
-                                pw.print("    User ");
-                                pw.print(mAllowAppSwitchUids.keyAt(i));
-                                pw.print(": Type ");
-                                pw.print(types.keyAt(j));
-                                pw.print(" = ");
-                                UserHandle.formatUid(pw, types.valueAt(j).intValue());
-                                pw.println();
-                            }
-                        }
-                    }
-                }
-                if (dumpPackage == null) {
-                    if (mController != null) {
-                        pw.println("  mController=" + mController
-                                + " mControllerIsAMonkey=" + mControllerIsAMonkey);
-                    }
-                    pw.println("  mGoingToSleep=" + mStackSupervisor.mGoingToSleep);
-                    pw.println("  mLaunchingActivity=" + mStackSupervisor.mLaunchingActivity);
-                }
-
-                return needSep;
-            }
-        }
-
-        @Override
-        public void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage) {
-            synchronized (mGlobalLock) {
-                if (dumpPackage == null) {
-                    getGlobalConfiguration().writeToProto(proto, GLOBAL_CONFIGURATION);
-                    proto.write(CONFIG_WILL_CHANGE, getTopDisplayFocusedStack().mConfigWillChange);
-                    writeSleepStateToProto(proto);
-                    if (mController != null) {
-                        final long token = proto.start(CONTROLLER);
-                        proto.write(CONTROLLER, mController.toString());
-                        proto.write(IS_A_MONKEY, mControllerIsAMonkey);
-                        proto.end(token);
-                    }
-                    mStackSupervisor.mGoingToSleep.writeToProto(proto, GOING_TO_SLEEP);
-                    mStackSupervisor.mLaunchingActivity.writeToProto(proto, LAUNCHING_ACTIVITY);
-                }
-
-                if (mHomeProcess != null && (dumpPackage == null
-                        || mHomeProcess.mPkgList.contains(dumpPackage))) {
-                    mHomeProcess.writeToProto(proto, HOME_PROC);
-                }
-
-                if (mPreviousProcess != null && (dumpPackage == null
-                        || mPreviousProcess.mPkgList.contains(dumpPackage))) {
-                    mPreviousProcess.writeToProto(proto, PREVIOUS_PROC);
-                    proto.write(PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
-                }
-
-                if (mHeavyWeightProcess != null && (dumpPackage == null
-                        || mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
-                    mHeavyWeightProcess.writeToProto(proto, HEAVY_WEIGHT_PROC);
-                }
-
-                for (Map.Entry<String, Integer> entry
-                        : mCompatModePackages.getPackages().entrySet()) {
-                    String pkg = entry.getKey();
-                    int mode = entry.getValue();
-                    if (dumpPackage == null || dumpPackage.equals(pkg)) {
-                        long compatToken = proto.start(SCREEN_COMPAT_PACKAGES);
-                        proto.write(PACKAGE, pkg);
-                        proto.write(MODE, mode);
-                        proto.end(compatToken);
-                    }
-                }
-
-                if (mCurAppTimeTracker != null) {
-                    mCurAppTimeTracker.writeToProto(proto, CURRENT_TRACKER, true);
-                }
-
-            }
-        }
-
-        @Override
-        public boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
-                String[] args, int opti, boolean dumpAll, boolean dumpVisibleStacksOnly,
-                boolean dumpFocusedStackOnly) {
-            synchronized (mGlobalLock) {
-                return ActivityTaskManagerService.this.dumpActivity(fd, pw, name, args, opti,
-                        dumpAll, dumpVisibleStacksOnly, dumpFocusedStackOnly);
-            }
-        }
-
-        @Override
-        public boolean canGcNow() {
-            synchronized (mGlobalLock) {
-                return isSleeping() || mStackSupervisor.allResumedActivitiesIdle();
-            }
-        }
-
-        @Override
-        public WindowProcessController getTopApp() {
-            synchronized (mGlobalLock) {
-                final ActivityRecord top = mStackSupervisor.getTopResumedActivity();
-                return top != null ? top.app : null;
-            }
-        }
-
-        @Override
-        public void rankTaskLayersIfNeeded() {
-            synchronized (mGlobalLock) {
-                if (mStackSupervisor != null) {
-                    mStackSupervisor.rankTaskLayersIfNeeded();
-                }
-            }
-        }
-
-        @Override
-        public void scheduleDestroyAllActivities(String reason) {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.scheduleDestroyAllActivities(null, reason);
-            }
-        }
-
-        @Override
-        public void removeUser(int userId) {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.removeUserLocked(userId);
-            }
-        }
-
-        @Override
-        public boolean switchUser(int userId, UserState userState) {
-            synchronized (mGlobalLock) {
-                return mStackSupervisor.switchUserLocked(userId, userState);
-            }
-        }
-
-        @Override
-        public void onHandleAppCrash(WindowProcessController wpc) {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.handleAppCrashLocked(wpc);
-            }
-        }
-
-        @Override
-        public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) {
-            synchronized (mGlobalLock) {
-                return mStackSupervisor.finishTopCrashedActivitiesLocked(crashedApp, reason);
-            }
-        }
-
-        @Override
-        public void onUidActive(int uid, int procState) {
-            synchronized (mGlobalLock) {
-                mActiveUids.put(uid, procState);
-            }
-        }
-
-        @Override
-        public void onUidInactive(int uid) {
-            synchronized (mGlobalLock) {
-                mActiveUids.remove(uid);
-            }
-        }
-
-        @Override
-        public void onActiveUidsCleared() {
-            synchronized (mGlobalLock) {
-                mActiveUids.clear();
-            }
-        }
-
-        @Override
-        public void onUidProcStateChanged(int uid, int procState) {
-            synchronized (mGlobalLock) {
-                if (mActiveUids.get(uid) != null) {
-                    mActiveUids.put(uid, procState);
-                }
-            }
-        }
-
-        @Override
-        public void onUidAddedToPendingTempWhitelist(int uid, String tag) {
-            synchronized (mGlobalLock) {
-                mPendingTempWhitelist.put(uid, tag);
-            }
-        }
-
-        @Override
-        public void onUidRemovedFromPendingTempWhitelist(int uid) {
-            synchronized (mGlobalLock) {
-                mPendingTempWhitelist.remove(uid);
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index e8ec057..dd3f3b5 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -17,12 +17,13 @@
 package com.android.server.am;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 import static com.android.server.am.ActivityManagerService.SYSTEM_DEBUGGABLE;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
@@ -37,7 +38,6 @@
 import android.os.Binder;
 import android.os.Message;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -54,12 +54,11 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.server.RescueParty;
-import com.android.server.Watchdog;
+import com.android.server.wm.WindowProcessController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Collections;
-import java.util.Set;
 
 /**
  * Controls error conditions in applications.
@@ -474,7 +473,7 @@
                 mService.mProcessList.removeProcessLocked(r, false, true, "crash");
                 if (taskId != INVALID_TASK_ID) {
                     try {
-                        mService.mActivityTaskManager.startActivityFromRecents(taskId,
+                        mService.startActivityFromRecents(taskId,
                                 ActivityOptions.makeBasic().toBundle());
                     } catch (IllegalArgumentException e) {
                         // Hmm...that didn't work. Task should either be in recents or associated
@@ -526,41 +525,29 @@
                                                        String shortMsg, String longMsg,
                                                        String stackTrace, long timeMillis,
                                                        int callingPid, int callingUid) {
-        if (mService.mActivityTaskManager.mController == null) {
-            return false;
-        }
+        String name = r != null ? r.processName : null;
+        int pid = r != null ? r.pid : callingPid;
+        int uid = r != null ? r.info.uid : callingUid;
 
-        try {
-            String name = r != null ? r.processName : null;
-            int pid = r != null ? r.pid : callingPid;
-            int uid = r != null ? r.info.uid : callingUid;
-            if (!mService.mActivityTaskManager.mController.appCrashed(name, pid,
-                    shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
-                if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
-                        && "Native crash".equals(crashInfo.exceptionClassName)) {
-                    Slog.w(TAG, "Skip killing native crashed app " + name
-                            + "(" + pid + ") during testing");
-                } else {
-                    Slog.w(TAG, "Force-killing crashed app " + name
-                            + " at watcher's request");
-                    if (r != null) {
-                        if (!makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, null))
-                        {
-                            r.kill("crash", true);
-                        }
-                    } else {
-                        // Huh.
-                        Process.killProcess(pid);
-                        ProcessList.killProcessGroup(uid, pid);
+        return mService.mAtmInternal.handleAppCrashInActivityController(
+                name, pid, shortMsg, longMsg, timeMillis, crashInfo.stackTrace, () -> {
+            if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
+                    && "Native crash".equals(crashInfo.exceptionClassName)) {
+                Slog.w(TAG, "Skip killing native crashed app " + name
+                        + "(" + pid + ") during testing");
+            } else {
+                Slog.w(TAG, "Force-killing crashed app " + name + " at watcher's request");
+                if (r != null) {
+                    if (!makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, null)) {
+                        r.kill("crash", true);
                     }
+                } else {
+                    // Huh.
+                    Process.killProcess(pid);
+                    ProcessList.killProcessGroup(uid, pid);
                 }
-                return true;
             }
-        } catch (RemoteException e) {
-            mService.mActivityTaskManager.mController = null;
-            Watchdog.getInstance().setActivityController(null);
-        }
-        return false;
+        });
     }
 
     private boolean makeAppCrashingLocked(ProcessRecord app,
@@ -631,7 +618,7 @@
         report.installerPackageName = r.errorReportReceiver.getPackageName();
         report.processName = r.processName;
         report.time = timeMillis;
-        report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+        report.systemApp = (r.info.flags & FLAG_SYSTEM) != 0;
 
         if (r.isCrashing() || r.forceCrashReport) {
             report.type = ApplicationErrorReport.TYPE_CRASH;
@@ -742,9 +729,9 @@
         // with a home activity running in the process to prevent a repeatedly crashing app
         // from blocking the user to manually clear the list.
         final WindowProcessController proc = app.getWindowProcessController();
-        final WindowProcessController homeProc = mService.mActivityTaskManager.mHomeProcess;
+        final WindowProcessController homeProc = mService.mAtmInternal.getHomeProcess();
         if (proc == homeProc && proc.hasActivities()
-                && (homeProc.mInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+                && (((ProcessRecord) homeProc.mOwner).info.flags & FLAG_SYSTEM) == 0) {
             proc.clearPackagePreferredForHomeActivities();
         }
 
@@ -806,7 +793,7 @@
                     mService.mUserController.getCurrentUserId()) != 0;
             final boolean crashSilenced = mAppsNotReportingCrashes != null &&
                     mAppsNotReportingCrashes.contains(proc.info.packageName);
-            if ((mService.mActivityTaskManager.canShowErrorDialogs() || showBackground)
+            if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
                     && !crashSilenced
                     && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
                 proc.crashDialog = dialogToShow = new AppErrorDialog(mContext, mService, data);
@@ -859,7 +846,7 @@
 
             boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
                     Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
-            if (mService.mActivityTaskManager.canShowErrorDialogs() || showBackground) {
+            if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) {
                 dialogToShow = new AppNotRespondingDialog(mService, mContext, data);
                 proc.anrDialog = dialogToShow;
             } else {
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
deleted file mode 100644
index a1f1ff9..0000000
--- a/services/core/java/com/android/server/am/AppTaskImpl.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
-import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-
-import android.app.ActivityManager;
-import android.app.IAppTask;
-import android.app.IApplicationThread;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.UserHandle;
-
-/**
- * An implementation of IAppTask, that allows an app to manage its own tasks via
- * {@link android.app.ActivityManager.AppTask}.  We keep track of the callingUid to ensure that
- * only the process that calls getAppTasks() can call the AppTask methods.
- */
-class AppTaskImpl extends IAppTask.Stub {
-    private ActivityTaskManagerService mService;
-
-    private int mTaskId;
-    private int mCallingUid;
-
-    public AppTaskImpl(ActivityTaskManagerService service, int taskId, int callingUid) {
-        mService = service;
-        mTaskId = taskId;
-        mCallingUid = callingUid;
-    }
-
-    private void checkCaller() {
-        if (mCallingUid != Binder.getCallingUid()) {
-            throw new SecurityException("Caller " + mCallingUid
-                    + " does not match caller of getAppTasks(): " + Binder.getCallingUid());
-        }
-    }
-
-    @Override
-    public void finishAndRemoveTask() {
-        checkCaller();
-
-        synchronized (mService.mGlobalLock) {
-            long origId = Binder.clearCallingIdentity();
-            try {
-                // We remove the task from recents to preserve backwards
-                if (!mService.mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
-                        REMOVE_FROM_RECENTS, "finish-and-remove-task")) {
-                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public ActivityManager.RecentTaskInfo getTaskInfo() {
-        checkCaller();
-
-        synchronized (mService.mGlobalLock) {
-            long origId = Binder.clearCallingIdentity();
-            try {
-                TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
-                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-                if (tr == null) {
-                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
-                }
-                return mService.getRecentTasks().createRecentTaskInfo(tr);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public void moveToFront() {
-        checkCaller();
-        // Will bring task to front if it already has a root activity.
-        final int callingPid = Binder.getCallingPid();
-        final int callingUid = Binder.getCallingUid();
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mService.mGlobalLock) {
-                mService.mStackSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId,
-                        null);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public int startActivity(IBinder whoThread, String callingPackage,
-            Intent intent, String resolvedType, Bundle bOptions) {
-        checkCaller();
-
-        int callingUser = UserHandle.getCallingUserId();
-        TaskRecord tr;
-        IApplicationThread appThread;
-        synchronized (mService.mGlobalLock) {
-            tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
-                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-            if (tr == null) {
-                throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
-            }
-            appThread = IApplicationThread.Stub.asInterface(whoThread);
-            if (appThread == null) {
-                throw new IllegalArgumentException("Bad app thread " + appThread);
-            }
-        }
-
-        return mService.getActivityStartController().obtainStarter(intent, "AppTaskImpl")
-                .setCaller(appThread)
-                .setCallingPackage(callingPackage)
-                .setResolvedType(resolvedType)
-                .setActivityOptions(bOptions)
-                .setMayWait(callingUser)
-                .setInTask(tr)
-                .execute();
-    }
-
-    @Override
-    public void setExcludeFromRecents(boolean exclude) {
-        checkCaller();
-
-        synchronized (mService.mGlobalLock) {
-            long origId = Binder.clearCallingIdentity();
-            try {
-                TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
-                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-                if (tr == null) {
-                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
-                }
-                Intent intent = tr.getBaseIntent();
-                if (exclude) {
-                    intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                } else {
-                    intent.setFlags(intent.getFlags()
-                            & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/AppTimeTracker.java b/services/core/java/com/android/server/am/AppTimeTracker.java
index 772865d..debe0a9 100644
--- a/services/core/java/com/android/server/am/AppTimeTracker.java
+++ b/services/core/java/com/android/server/am/AppTimeTracker.java
@@ -122,7 +122,7 @@
         }
     }
 
-    void writeToProto(ProtoOutputStream proto, long fieldId, boolean details) {
+    public void writeToProto(ProtoOutputStream proto, long fieldId, boolean details) {
         final long token = proto.start(fieldId);
         proto.write(AppTimeTrackerProto.RECEIVER, mReceiver.toString());
         proto.write(AppTimeTrackerProto.TOTAL_DURATION_MS, mTotalTime);
diff --git a/services/core/java/com/android/server/am/AppWarnings.java b/services/core/java/com/android/server/am/AppWarnings.java
deleted file mode 100644
index a705180..0000000
--- a/services/core/java/com/android/server/am/AppWarnings.java
+++ /dev/null
@@ -1,569 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import android.annotation.UiThread;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.AtomicFile;
-import android.util.DisplayMetrics;
-import android.util.Slog;
-import android.util.Xml;
-
-import com.android.internal.util.FastXmlSerializer;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-
-/**
- * Manages warning dialogs shown during application lifecycle.
- */
-class AppWarnings {
-    private static final String TAG = "AppWarnings";
-    private static final String CONFIG_FILE_NAME = "packages-warnings.xml";
-
-    public static final int FLAG_HIDE_DISPLAY_SIZE = 0x01;
-    public static final int FLAG_HIDE_COMPILE_SDK = 0x02;
-    public static final int FLAG_HIDE_DEPRECATED_SDK = 0x04;
-
-    private final HashMap<String, Integer> mPackageFlags = new HashMap<>();
-
-    private final ActivityTaskManagerService mAtm;
-    private final Context mUiContext;
-    private final ConfigHandler mHandler;
-    private final UiHandler mUiHandler;
-    private final AtomicFile mConfigFile;
-
-    private UnsupportedDisplaySizeDialog mUnsupportedDisplaySizeDialog;
-    private UnsupportedCompileSdkDialog mUnsupportedCompileSdkDialog;
-    private DeprecatedTargetSdkVersionDialog mDeprecatedTargetSdkVersionDialog;
-
-    /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
-    private HashSet<ComponentName> mAlwaysShowUnsupportedCompileSdkWarningActivities =
-            new HashSet<>();
-
-    /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
-    void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) {
-        mAlwaysShowUnsupportedCompileSdkWarningActivities.add(activity);
-    }
-
-    /**
-     * Creates a new warning dialog manager.
-     * <p>
-     * <strong>Note:</strong> Must be called from the ActivityManagerService thread.
-     *
-     * @param atm
-     * @param uiContext
-     * @param handler
-     * @param uiHandler
-     * @param systemDir
-     */
-    public AppWarnings(ActivityTaskManagerService atm, Context uiContext, Handler handler,
-            Handler uiHandler, File systemDir) {
-        mAtm = atm;
-        mUiContext = uiContext;
-        mHandler = new ConfigHandler(handler.getLooper());
-        mUiHandler = new UiHandler(uiHandler.getLooper());
-        mConfigFile = new AtomicFile(new File(systemDir, CONFIG_FILE_NAME), "warnings-config");
-
-        readConfigFromFileAmsThread();
-    }
-
-    /**
-     * Shows the "unsupported display size" warning, if necessary.
-     *
-     * @param r activity record for which the warning may be displayed
-     */
-    public void showUnsupportedDisplaySizeDialogIfNeeded(ActivityRecord r) {
-        final Configuration globalConfig = mAtm.getGlobalConfiguration();
-        if (globalConfig.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE
-                && r.appInfo.requiresSmallestWidthDp > globalConfig.smallestScreenWidthDp) {
-            mUiHandler.showUnsupportedDisplaySizeDialog(r);
-        }
-    }
-
-    /**
-     * Shows the "unsupported compile SDK" warning, if necessary.
-     *
-     * @param r activity record for which the warning may be displayed
-     */
-    public void showUnsupportedCompileSdkDialogIfNeeded(ActivityRecord r) {
-        if (r.appInfo.compileSdkVersion == 0 || r.appInfo.compileSdkVersionCodename == null) {
-            // We don't know enough about this package. Abort!
-            return;
-        }
-
-        // TODO(b/75318890): Need to move this to when the app actually crashes.
-        if (/*ActivityManager.isRunningInTestHarness()
-                &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(r.realActivity)) {
-            // Don't show warning if we are running in a test harness and we don't have to always
-            // show for this activity.
-            return;
-        }
-
-        // If the application was built against an pre-release SDK that's older than the current
-        // platform OR if the current platform is pre-release and older than the SDK against which
-        // the application was built OR both are pre-release with the same SDK_INT but different
-        // codenames (e.g. simultaneous pre-release development), then we're likely to run into
-        // compatibility issues. Warn the user and offer to check for an update.
-        final int compileSdk = r.appInfo.compileSdkVersion;
-        final int platformSdk = Build.VERSION.SDK_INT;
-        final boolean isCompileSdkPreview = !"REL".equals(r.appInfo.compileSdkVersionCodename);
-        final boolean isPlatformSdkPreview = !"REL".equals(Build.VERSION.CODENAME);
-        if ((isCompileSdkPreview && compileSdk < platformSdk)
-                || (isPlatformSdkPreview && platformSdk < compileSdk)
-                || (isCompileSdkPreview && isPlatformSdkPreview && platformSdk == compileSdk
-                    && !Build.VERSION.CODENAME.equals(r.appInfo.compileSdkVersionCodename))) {
-            mUiHandler.showUnsupportedCompileSdkDialog(r);
-        }
-    }
-
-    /**
-     * Shows the "deprecated target sdk" warning, if necessary.
-     *
-     * @param r activity record for which the warning may be displayed
-     */
-    public void showDeprecatedTargetDialogIfNeeded(ActivityRecord r) {
-        if (r.appInfo.targetSdkVersion < Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT) {
-            mUiHandler.showDeprecatedTargetDialog(r);
-        }
-    }
-
-    /**
-     * Called when an activity is being started.
-     *
-     * @param r record for the activity being started
-     */
-    public void onStartActivity(ActivityRecord r) {
-        showUnsupportedCompileSdkDialogIfNeeded(r);
-        showUnsupportedDisplaySizeDialogIfNeeded(r);
-        showDeprecatedTargetDialogIfNeeded(r);
-    }
-
-    /**
-     * Called when an activity was previously started and is being resumed.
-     *
-     * @param r record for the activity being resumed
-     */
-    public void onResumeActivity(ActivityRecord r) {
-        showUnsupportedDisplaySizeDialogIfNeeded(r);
-    }
-
-    /**
-     * Called by ActivityManagerService when package data has been cleared.
-     *
-     * @param name the package whose data has been cleared
-     */
-    public void onPackageDataCleared(String name) {
-        removePackageAndHideDialogs(name);
-    }
-
-    /**
-     * Called by ActivityManagerService when a package has been uninstalled.
-     *
-     * @param name the package that has been uninstalled
-     */
-    public void onPackageUninstalled(String name) {
-        removePackageAndHideDialogs(name);
-    }
-
-    /**
-     * Called by ActivityManagerService when the default display density has changed.
-     */
-    public void onDensityChanged() {
-        mUiHandler.hideUnsupportedDisplaySizeDialog();
-    }
-
-    /**
-     * Does what it says on the tin.
-     */
-    private void removePackageAndHideDialogs(String name) {
-        mUiHandler.hideDialogsForPackage(name);
-
-        synchronized (mPackageFlags) {
-            mPackageFlags.remove(name);
-            mHandler.scheduleWrite();
-        }
-    }
-
-    /**
-     * Hides the "unsupported display size" warning.
-     * <p>
-     * <strong>Note:</strong> Must be called on the UI thread.
-     */
-    @UiThread
-    private void hideUnsupportedDisplaySizeDialogUiThread() {
-        if (mUnsupportedDisplaySizeDialog != null) {
-            mUnsupportedDisplaySizeDialog.dismiss();
-            mUnsupportedDisplaySizeDialog = null;
-        }
-    }
-
-    /**
-     * Shows the "unsupported display size" warning for the given application.
-     * <p>
-     * <strong>Note:</strong> Must be called on the UI thread.
-     *
-     * @param ar record for the activity that triggered the warning
-     */
-    @UiThread
-    private void showUnsupportedDisplaySizeDialogUiThread(ActivityRecord ar) {
-        if (mUnsupportedDisplaySizeDialog != null) {
-            mUnsupportedDisplaySizeDialog.dismiss();
-            mUnsupportedDisplaySizeDialog = null;
-        }
-        if (ar != null && !hasPackageFlag(
-                ar.packageName, FLAG_HIDE_DISPLAY_SIZE)) {
-            mUnsupportedDisplaySizeDialog = new UnsupportedDisplaySizeDialog(
-                    AppWarnings.this, mUiContext, ar.info.applicationInfo);
-            mUnsupportedDisplaySizeDialog.show();
-        }
-    }
-
-    /**
-     * Shows the "unsupported compile SDK" warning for the given application.
-     * <p>
-     * <strong>Note:</strong> Must be called on the UI thread.
-     *
-     * @param ar record for the activity that triggered the warning
-     */
-    @UiThread
-    private void showUnsupportedCompileSdkDialogUiThread(ActivityRecord ar) {
-        if (mUnsupportedCompileSdkDialog != null) {
-            mUnsupportedCompileSdkDialog.dismiss();
-            mUnsupportedCompileSdkDialog = null;
-        }
-        if (ar != null && !hasPackageFlag(
-                ar.packageName, FLAG_HIDE_COMPILE_SDK)) {
-            mUnsupportedCompileSdkDialog = new UnsupportedCompileSdkDialog(
-                    AppWarnings.this, mUiContext, ar.info.applicationInfo);
-            mUnsupportedCompileSdkDialog.show();
-        }
-    }
-
-    /**
-     * Shows the "deprecated target sdk version" warning for the given application.
-     * <p>
-     * <strong>Note:</strong> Must be called on the UI thread.
-     *
-     * @param ar record for the activity that triggered the warning
-     */
-    @UiThread
-    private void showDeprecatedTargetSdkDialogUiThread(ActivityRecord ar) {
-        if (mDeprecatedTargetSdkVersionDialog != null) {
-            mDeprecatedTargetSdkVersionDialog.dismiss();
-            mDeprecatedTargetSdkVersionDialog = null;
-        }
-        if (ar != null && !hasPackageFlag(
-                ar.packageName, FLAG_HIDE_DEPRECATED_SDK)) {
-            mDeprecatedTargetSdkVersionDialog = new DeprecatedTargetSdkVersionDialog(
-                    AppWarnings.this, mUiContext, ar.info.applicationInfo);
-            mDeprecatedTargetSdkVersionDialog.show();
-        }
-    }
-
-    /**
-     * Dismisses all warnings for the given package.
-     * <p>
-     * <strong>Note:</strong> Must be called on the UI thread.
-     *
-     * @param name the package for which warnings should be dismissed, or {@code null} to dismiss
-     *             all warnings
-     */
-    @UiThread
-    private void hideDialogsForPackageUiThread(String name) {
-        // Hides the "unsupported display" dialog if necessary.
-        if (mUnsupportedDisplaySizeDialog != null && (name == null || name.equals(
-                mUnsupportedDisplaySizeDialog.getPackageName()))) {
-            mUnsupportedDisplaySizeDialog.dismiss();
-            mUnsupportedDisplaySizeDialog = null;
-        }
-
-        // Hides the "unsupported compile SDK" dialog if necessary.
-        if (mUnsupportedCompileSdkDialog != null && (name == null || name.equals(
-                mUnsupportedCompileSdkDialog.getPackageName()))) {
-            mUnsupportedCompileSdkDialog.dismiss();
-            mUnsupportedCompileSdkDialog = null;
-        }
-
-        // Hides the "deprecated target sdk version" dialog if necessary.
-        if (mDeprecatedTargetSdkVersionDialog != null && (name == null || name.equals(
-                mDeprecatedTargetSdkVersionDialog.getPackageName()))) {
-            mDeprecatedTargetSdkVersionDialog.dismiss();
-            mDeprecatedTargetSdkVersionDialog = null;
-        }
-    }
-
-    /**
-     * Returns the value of the flag for the given package.
-     *
-     * @param name the package from which to retrieve the flag
-     * @param flag the bitmask for the flag to retrieve
-     * @return {@code true} if the flag is enabled, {@code false} otherwise
-     */
-    boolean hasPackageFlag(String name, int flag) {
-        return (getPackageFlags(name) & flag) == flag;
-    }
-
-    /**
-     * Sets the flag for the given package to the specified value.
-     *
-     * @param name the package on which to set the flag
-     * @param flag the bitmask for flag to set
-     * @param enabled the value to set for the flag
-     */
-    void setPackageFlag(String name, int flag, boolean enabled) {
-        synchronized (mPackageFlags) {
-            final int curFlags = getPackageFlags(name);
-            final int newFlags = enabled ? (curFlags | flag) : (curFlags & ~flag);
-            if (curFlags != newFlags) {
-                if (newFlags != 0) {
-                    mPackageFlags.put(name, newFlags);
-                } else {
-                    mPackageFlags.remove(name);
-                }
-                mHandler.scheduleWrite();
-            }
-        }
-    }
-
-    /**
-     * Returns the bitmask of flags set for the specified package.
-     */
-    private int getPackageFlags(String name) {
-        synchronized (mPackageFlags) {
-            return mPackageFlags.getOrDefault(name, 0);
-        }
-    }
-
-    /**
-     * Handles messages on the system process UI thread.
-     */
-    private final class UiHandler extends Handler {
-        private static final int MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG = 1;
-        private static final int MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG = 2;
-        private static final int MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG = 3;
-        private static final int MSG_HIDE_DIALOGS_FOR_PACKAGE = 4;
-        private static final int MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG = 5;
-
-        public UiHandler(Looper looper) {
-            super(looper, null, true);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG: {
-                    final ActivityRecord ar = (ActivityRecord) msg.obj;
-                    showUnsupportedDisplaySizeDialogUiThread(ar);
-                } break;
-                case MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG: {
-                    hideUnsupportedDisplaySizeDialogUiThread();
-                } break;
-                case MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG: {
-                    final ActivityRecord ar = (ActivityRecord) msg.obj;
-                    showUnsupportedCompileSdkDialogUiThread(ar);
-                } break;
-                case MSG_HIDE_DIALOGS_FOR_PACKAGE: {
-                    final String name = (String) msg.obj;
-                    hideDialogsForPackageUiThread(name);
-                } break;
-                case MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG: {
-                    final ActivityRecord ar = (ActivityRecord) msg.obj;
-                    showDeprecatedTargetSdkDialogUiThread(ar);
-                } break;
-            }
-        }
-
-        public void showUnsupportedDisplaySizeDialog(ActivityRecord r) {
-            removeMessages(MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG);
-            obtainMessage(MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG, r).sendToTarget();
-        }
-
-        public void hideUnsupportedDisplaySizeDialog() {
-            removeMessages(MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG);
-            sendEmptyMessage(MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG);
-        }
-
-        public void showUnsupportedCompileSdkDialog(ActivityRecord r) {
-            removeMessages(MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG);
-            obtainMessage(MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG, r).sendToTarget();
-        }
-
-        public void showDeprecatedTargetDialog(ActivityRecord r) {
-            removeMessages(MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG);
-            obtainMessage(MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG, r).sendToTarget();
-        }
-
-        public void hideDialogsForPackage(String name) {
-            obtainMessage(MSG_HIDE_DIALOGS_FOR_PACKAGE, name).sendToTarget();
-        }
-    }
-
-    /**
-     * Handles messages on the ActivityTaskManagerService thread.
-     */
-    private final class ConfigHandler extends Handler {
-        private static final int MSG_WRITE = 1;
-
-        private static final int DELAY_MSG_WRITE = 10000;
-
-        public ConfigHandler(Looper looper) {
-            super(looper, null, true);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_WRITE:
-                    writeConfigToFileAmsThread();
-                    break;
-            }
-        }
-
-        public void scheduleWrite() {
-            removeMessages(MSG_WRITE);
-            sendEmptyMessageDelayed(MSG_WRITE, DELAY_MSG_WRITE);
-        }
-    }
-
-    /**
-     * Writes the configuration file.
-     * <p>
-     * <strong>Note:</strong> Should be called from the ActivityManagerService thread unless you
-     * don't care where you're doing I/O operations. But you <i>do</i> care, don't you?
-     */
-    private void writeConfigToFileAmsThread() {
-        // Create a shallow copy so that we don't have to synchronize on config.
-        final HashMap<String, Integer> packageFlags;
-        synchronized (mPackageFlags) {
-            packageFlags = new HashMap<>(mPackageFlags);
-        }
-
-        FileOutputStream fos = null;
-        try {
-            fos = mConfigFile.startWrite();
-
-            final XmlSerializer out = new FastXmlSerializer();
-            out.setOutput(fos, StandardCharsets.UTF_8.name());
-            out.startDocument(null, true);
-            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-            out.startTag(null, "packages");
-
-            for (Map.Entry<String, Integer> entry : packageFlags.entrySet()) {
-                String pkg = entry.getKey();
-                int mode = entry.getValue();
-                if (mode == 0) {
-                    continue;
-                }
-                out.startTag(null, "package");
-                out.attribute(null, "name", pkg);
-                out.attribute(null, "flags", Integer.toString(mode));
-                out.endTag(null, "package");
-            }
-
-            out.endTag(null, "packages");
-            out.endDocument();
-
-            mConfigFile.finishWrite(fos);
-        } catch (java.io.IOException e1) {
-            Slog.w(TAG, "Error writing package metadata", e1);
-            if (fos != null) {
-                mConfigFile.failWrite(fos);
-            }
-        }
-    }
-
-    /**
-     * Reads the configuration file and populates the package flags.
-     * <p>
-     * <strong>Note:</strong> Must be called from the constructor (and thus on the
-     * ActivityManagerService thread) since we don't synchronize on config.
-     */
-    private void readConfigFromFileAmsThread() {
-        FileInputStream fis = null;
-
-        try {
-            fis = mConfigFile.openRead();
-
-            final XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(fis, StandardCharsets.UTF_8.name());
-
-            int eventType = parser.getEventType();
-            while (eventType != XmlPullParser.START_TAG &&
-                    eventType != XmlPullParser.END_DOCUMENT) {
-                eventType = parser.next();
-            }
-            if (eventType == XmlPullParser.END_DOCUMENT) {
-                return;
-            }
-
-            String tagName = parser.getName();
-            if ("packages".equals(tagName)) {
-                eventType = parser.next();
-                do {
-                    if (eventType == XmlPullParser.START_TAG) {
-                        tagName = parser.getName();
-                        if (parser.getDepth() == 2) {
-                            if ("package".equals(tagName)) {
-                                final String name = parser.getAttributeValue(null, "name");
-                                if (name != null) {
-                                    final String flags = parser.getAttributeValue(
-                                            null, "flags");
-                                    int flagsInt = 0;
-                                    if (flags != null) {
-                                        try {
-                                            flagsInt = Integer.parseInt(flags);
-                                        } catch (NumberFormatException e) {
-                                        }
-                                    }
-                                    mPackageFlags.put(name, flagsInt);
-                                }
-                            }
-                        }
-                    }
-                    eventType = parser.next();
-                } while (eventType != XmlPullParser.END_DOCUMENT);
-            }
-        } catch (XmlPullParserException e) {
-            Slog.w(TAG, "Error reading package metadata", e);
-        } catch (java.io.IOException e) {
-            if (fis != null) Slog.w(TAG, "Error reading package metadata", e);
-        } finally {
-            if (fis != null) {
-                try {
-                    fis.close();
-                } catch (java.io.IOException e1) {
-                }
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/AssistDataReceiverProxy.java b/services/core/java/com/android/server/am/AssistDataReceiverProxy.java
deleted file mode 100644
index 9991ce1..0000000
--- a/services/core/java/com/android/server/am/AssistDataReceiverProxy.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
-import android.app.IAssistDataReceiver;
-import android.graphics.Bitmap;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
-
-/**
- * Proxies assist data to the given receiver, skipping all callbacks if the receiver dies.
- */
-class AssistDataReceiverProxy implements AssistDataRequesterCallbacks,
-        Binder.DeathRecipient {
-
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "AssistDataReceiverProxy" : TAG_AM;
-
-    private String mCallerPackage;
-    private IAssistDataReceiver mReceiver;
-
-    public AssistDataReceiverProxy(IAssistDataReceiver receiver, String callerPackage) {
-        mReceiver = receiver;
-        mCallerPackage = callerPackage;
-        linkToDeath();
-    }
-
-    @Override
-    public boolean canHandleReceivedAssistDataLocked() {
-        // We are forwarding, so we can always receive this data
-        return true;
-    }
-
-    @Override
-    public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
-        if (mReceiver != null) {
-            try {
-                mReceiver.onHandleAssistData(data);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to proxy assist data to receiver in package="
-                        + mCallerPackage, e);
-            }
-        }
-    }
-
-    @Override
-    public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
-        if (mReceiver != null) {
-            try {
-                mReceiver.onHandleAssistScreenshot(screenshot);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to proxy assist screenshot to receiver in package="
-                        + mCallerPackage, e);
-            }
-        }
-    }
-
-    @Override
-    public void onAssistRequestCompleted() {
-        unlinkToDeath();
-    }
-
-    @Override
-    public void binderDied() {
-        unlinkToDeath();
-    }
-
-    private void linkToDeath() {
-        try {
-            mReceiver.asBinder().linkToDeath(this, 0);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Could not link to client death", e);
-        }
-    }
-
-    private void unlinkToDeath() {
-        if (mReceiver != null) {
-            mReceiver.asBinder().unlinkToDeath(this, 0);
-        }
-        mReceiver = null;
-    }
-}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java
index cd4d6a3..aabb587 100644
--- a/services/core/java/com/android/server/am/BaseErrorDialog.java
+++ b/services/core/java/com/android/server/am/BaseErrorDialog.java
@@ -26,7 +26,7 @@
 import android.view.WindowManager;
 import android.widget.Button;
 
-class BaseErrorDialog extends AlertDialog {
+public class BaseErrorDialog extends AlertDialog {
     private static final int ENABLE_BUTTONS = 0;
     private static final int DISABLE_BUTTONS = 1;
 
diff --git a/services/core/java/com/android/server/am/ClientLifecycleManager.java b/services/core/java/com/android/server/am/ClientLifecycleManager.java
deleted file mode 100644
index ae8d9fc..0000000
--- a/services/core/java/com/android/server/am/ClientLifecycleManager.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import android.annotation.NonNull;
-import android.app.IApplicationThread;
-import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.ClientTransactionItem;
-import android.app.servertransaction.ActivityLifecycleItem;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-/**
- * Class that is able to combine multiple client lifecycle transition requests and/or callbacks,
- * and execute them as a single transaction.
- *
- * @see ClientTransaction
- */
-class ClientLifecycleManager {
-    // TODO(lifecycler): Implement building transactions or global transaction.
-    // TODO(lifecycler): Use object pools for transactions and transaction items.
-
-    /**
-     * Schedule a transaction, which may consist of multiple callbacks and a lifecycle request.
-     * @param transaction A sequence of client transaction items.
-     * @throws RemoteException
-     *
-     * @see ClientTransaction
-     */
-    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
-        final IApplicationThread client = transaction.getClient();
-        transaction.schedule();
-        if (!(client instanceof Binder)) {
-            // If client is not an instance of Binder - it's a remote call and at this point it is
-            // safe to recycle the object. All objects used for local calls will be recycled after
-            // the transaction is executed on client in ActivityThread.
-            transaction.recycle();
-        }
-    }
-
-    /**
-     * Schedule a single lifecycle request or callback to client activity.
-     * @param client Target client.
-     * @param activityToken Target activity token.
-     * @param stateRequest A request to move target activity to a desired lifecycle state.
-     * @throws RemoteException
-     *
-     * @see ClientTransactionItem
-     */
-    void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
-            @NonNull ActivityLifecycleItem stateRequest) throws RemoteException {
-        final ClientTransaction clientTransaction = transactionWithState(client, activityToken,
-                stateRequest);
-        scheduleTransaction(clientTransaction);
-    }
-
-    /**
-     * Schedule a single callback delivery to client activity.
-     * @param client Target client.
-     * @param activityToken Target activity token.
-     * @param callback A request to deliver a callback.
-     * @throws RemoteException
-     *
-     * @see ClientTransactionItem
-     */
-    void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
-            @NonNull ClientTransactionItem callback) throws RemoteException {
-        final ClientTransaction clientTransaction = transactionWithCallback(client, activityToken,
-                callback);
-        scheduleTransaction(clientTransaction);
-    }
-
-    /**
-     * Schedule a single callback delivery to client application.
-     * @param client Target client.
-     * @param callback A request to deliver a callback.
-     * @throws RemoteException
-     *
-     * @see ClientTransactionItem
-     */
-    void scheduleTransaction(@NonNull IApplicationThread client,
-            @NonNull ClientTransactionItem callback) throws RemoteException {
-        final ClientTransaction clientTransaction = transactionWithCallback(client,
-                null /* activityToken */, callback);
-        scheduleTransaction(clientTransaction);
-    }
-
-    /**
-     * @return A new instance of {@link ClientTransaction} with a single lifecycle state request.
-     *
-     * @see ClientTransaction
-     * @see ClientTransactionItem
-     */
-    private static ClientTransaction transactionWithState(@NonNull IApplicationThread client,
-            @NonNull IBinder activityToken, @NonNull ActivityLifecycleItem stateRequest) {
-        final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken);
-        clientTransaction.setLifecycleStateRequest(stateRequest);
-        return clientTransaction;
-    }
-
-    /**
-     * @return A new instance of {@link ClientTransaction} with a single callback invocation.
-     *
-     * @see ClientTransaction
-     * @see ClientTransactionItem
-     */
-    private static ClientTransaction transactionWithCallback(@NonNull IApplicationThread client,
-            IBinder activityToken, @NonNull ClientTransactionItem callback) {
-        final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken);
-        clientTransaction.addCallback(callback);
-        return clientTransaction;
-    }
-}
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
deleted file mode 100644
index 3c4ab00..0000000
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * Copyright (C) 2014 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.server.am;
-
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import com.android.internal.util.FastXmlSerializer;
-
-import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.AtomicFile;
-import android.util.Slog;
-import android.util.Xml;
-
-public final class CompatModePackages {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_ATM;
-    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
-
-    private final ActivityTaskManagerService mService;
-    private final AtomicFile mFile;
-
-    // Compatibility state: no longer ask user to select the mode.
-    private static final int COMPAT_FLAG_DONT_ASK = 1<<0;
-    // Compatibility state: compatibility mode is enabled.
-    private static final int COMPAT_FLAG_ENABLED = 1<<1;
-
-    private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
-
-    private static final int MSG_WRITE = 300;
-
-    private final CompatHandler mHandler;
-
-    private final class CompatHandler extends Handler {
-        public CompatHandler(Looper looper) {
-            super(looper, null, true);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_WRITE:
-                    saveCompatModes();
-                    break;
-            }
-        }
-    };
-
-    public CompatModePackages(ActivityTaskManagerService service, File systemDir, Handler handler) {
-        mService = service;
-        mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"), "compat-mode");
-        mHandler = new CompatHandler(handler.getLooper());
-
-        FileInputStream fis = null;
-        try {
-            fis = mFile.openRead();
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(fis, StandardCharsets.UTF_8.name());
-            int eventType = parser.getEventType();
-            while (eventType != XmlPullParser.START_TAG &&
-                    eventType != XmlPullParser.END_DOCUMENT) {
-                eventType = parser.next();
-            }
-            if (eventType == XmlPullParser.END_DOCUMENT) {
-                return;
-            }
-
-            String tagName = parser.getName();
-            if ("compat-packages".equals(tagName)) {
-                eventType = parser.next();
-                do {
-                    if (eventType == XmlPullParser.START_TAG) {
-                        tagName = parser.getName();
-                        if (parser.getDepth() == 2) {
-                            if ("pkg".equals(tagName)) {
-                                String pkg = parser.getAttributeValue(null, "name");
-                                if (pkg != null) {
-                                    String mode = parser.getAttributeValue(null, "mode");
-                                    int modeInt = 0;
-                                    if (mode != null) {
-                                        try {
-                                            modeInt = Integer.parseInt(mode);
-                                        } catch (NumberFormatException e) {
-                                        }
-                                    }
-                                    mPackages.put(pkg, modeInt);
-                                }
-                            }
-                        }
-                    }
-                    eventType = parser.next();
-                } while (eventType != XmlPullParser.END_DOCUMENT);
-            }
-        } catch (XmlPullParserException e) {
-            Slog.w(TAG, "Error reading compat-packages", e);
-        } catch (java.io.IOException e) {
-            if (fis != null) Slog.w(TAG, "Error reading compat-packages", e);
-        } finally {
-            if (fis != null) {
-                try {
-                    fis.close();
-                } catch (java.io.IOException e1) {
-                }
-            }
-        }
-    }
-
-    public HashMap<String, Integer> getPackages() {
-        return mPackages;
-    }
-
-    private int getPackageFlags(String packageName) {
-        Integer flags = mPackages.get(packageName);
-        return flags != null ? flags : 0;
-    }
-
-    public void handlePackageDataClearedLocked(String packageName) {
-        // User has explicitly asked to clear all associated data.
-        removePackage(packageName);
-    }
-
-    public void handlePackageUninstalledLocked(String packageName) {
-        // Clear settings when app is uninstalled since this is an explicit
-        // signal from the user to remove the app and all associated data.
-        removePackage(packageName);
-    }
-
-    private void removePackage(String packageName) {
-        if (mPackages.containsKey(packageName)) {
-            mPackages.remove(packageName);
-            scheduleWrite();
-        }
-    }
-
-    public void handlePackageAddedLocked(String packageName, boolean updated) {
-        ApplicationInfo ai = null;
-        try {
-            ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
-        } catch (RemoteException e) {
-        }
-        if (ai == null) {
-            return;
-        }
-        CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
-        final boolean mayCompat = !ci.alwaysSupportsScreen()
-                && !ci.neverSupportsScreen();
-
-        if (updated) {
-            // Update -- if the app no longer can run in compat mode, clear
-            // any current settings for it.
-            if (!mayCompat && mPackages.containsKey(packageName)) {
-                mPackages.remove(packageName);
-                scheduleWrite();
-            }
-        }
-    }
-
-    private void scheduleWrite() {
-        mHandler.removeMessages(MSG_WRITE);
-        Message msg = mHandler.obtainMessage(MSG_WRITE);
-        mHandler.sendMessageDelayed(msg, 10000);
-    }
-
-    public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
-        final Configuration globalConfig = mService.getGlobalConfiguration();
-        CompatibilityInfo ci = new CompatibilityInfo(ai, globalConfig.screenLayout,
-                globalConfig.smallestScreenWidthDp,
-                (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
-        //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
-        return ci;
-    }
-
-    public int computeCompatModeLocked(ApplicationInfo ai) {
-        final boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
-        final Configuration globalConfig = mService.getGlobalConfiguration();
-        final CompatibilityInfo info = new CompatibilityInfo(ai, globalConfig.screenLayout,
-                globalConfig.smallestScreenWidthDp, enabled);
-        if (info.alwaysSupportsScreen()) {
-            return ActivityManager.COMPAT_MODE_NEVER;
-        }
-        if (info.neverSupportsScreen()) {
-            return ActivityManager.COMPAT_MODE_ALWAYS;
-        }
-        return enabled ? ActivityManager.COMPAT_MODE_ENABLED
-                : ActivityManager.COMPAT_MODE_DISABLED;
-    }
-
-    public boolean getPackageAskCompatModeLocked(String packageName) {
-        return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
-    }
-
-    public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
-        setPackageFlagLocked(packageName, COMPAT_FLAG_DONT_ASK, ask);
-    }
-
-    private void setPackageFlagLocked(String packageName, int flag, boolean set) {
-        final int curFlags = getPackageFlags(packageName);
-        final int newFlags = set ? (curFlags & ~flag) : (curFlags | flag);
-        if (curFlags != newFlags) {
-            if (newFlags != 0) {
-                mPackages.put(packageName, newFlags);
-            } else {
-                mPackages.remove(packageName);
-            }
-            scheduleWrite();
-        }
-    }
-
-    public int getPackageScreenCompatModeLocked(String packageName) {
-        ApplicationInfo ai = null;
-        try {
-            ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
-        } catch (RemoteException e) {
-        }
-        if (ai == null) {
-            return ActivityManager.COMPAT_MODE_UNKNOWN;
-        }
-        return computeCompatModeLocked(ai);
-    }
-
-    public void setPackageScreenCompatModeLocked(String packageName, int mode) {
-        ApplicationInfo ai = null;
-        try {
-            ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
-        } catch (RemoteException e) {
-        }
-        if (ai == null) {
-            Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
-            return;
-        }
-        setPackageScreenCompatModeLocked(ai, mode);
-    }
-
-    void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
-        final String packageName = ai.packageName;
-
-        int curFlags = getPackageFlags(packageName);
-
-        boolean enable;
-        switch (mode) {
-            case ActivityManager.COMPAT_MODE_DISABLED:
-                enable = false;
-                break;
-            case ActivityManager.COMPAT_MODE_ENABLED:
-                enable = true;
-                break;
-            case ActivityManager.COMPAT_MODE_TOGGLE:
-                enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
-                break;
-            default:
-                Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
-                return;
-        }
-
-        int newFlags = curFlags;
-        if (enable) {
-            newFlags |= COMPAT_FLAG_ENABLED;
-        } else {
-            newFlags &= ~COMPAT_FLAG_ENABLED;
-        }
-
-        CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
-        if (ci.alwaysSupportsScreen()) {
-            Slog.w(TAG, "Ignoring compat mode change of " + packageName
-                    + "; compatibility never needed");
-            newFlags = 0;
-        }
-        if (ci.neverSupportsScreen()) {
-            Slog.w(TAG, "Ignoring compat mode change of " + packageName
-                    + "; compatibility always needed");
-            newFlags = 0;
-        }
-
-        if (newFlags != curFlags) {
-            if (newFlags != 0) {
-                mPackages.put(packageName, newFlags);
-            } else {
-                mPackages.remove(packageName);
-            }
-
-            // Need to get compatibility info in new state.
-            ci = compatibilityInfoForPackageLocked(ai);
-
-            scheduleWrite();
-
-            final ActivityStack stack = mService.getTopDisplayFocusedStack();
-            ActivityRecord starting = stack.restartPackage(packageName);
-
-            // Tell all processes that loaded this package about the change.
-            for (int i = mService.mPidMap.size() - 1; i >= 0; i--) {
-                final WindowProcessController app = mService.mPidMap.valueAt(i);
-                if (!app.mPkgList.contains(packageName)) {
-                    continue;
-                }
-                try {
-                    if (app.hasThread()) {
-                        if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
-                                + app.mName + " new compat " + ci);
-                        app.getThread().updatePackageCompatibilityInfo(packageName, ci);
-                    }
-                } catch (Exception e) {
-                }
-            }
-
-            if (starting != null) {
-                starting.ensureActivityConfiguration(0 /* globalChanges */,
-                        false /* preserveWindow */);
-                // And we need to make sure at this point that all other activities
-                // are made visible with the correct configuration.
-                stack.ensureActivitiesVisibleLocked(starting, 0, !PRESERVE_WINDOWS);
-            }
-        }
-    }
-
-    private void saveCompatModes() {
-        HashMap<String, Integer> pkgs;
-        synchronized (mService.mGlobalLock) {
-            pkgs = new HashMap<>(mPackages);
-        }
-
-        FileOutputStream fos = null;
-
-        try {
-            fos = mFile.startWrite();
-            XmlSerializer out = new FastXmlSerializer();
-            out.setOutput(fos, StandardCharsets.UTF_8.name());
-            out.startDocument(null, true);
-            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-            out.startTag(null, "compat-packages");
-
-            final IPackageManager pm = AppGlobals.getPackageManager();
-            final Configuration globalConfig = mService.getGlobalConfiguration();
-            final int screenLayout = globalConfig.screenLayout;
-            final int smallestScreenWidthDp = globalConfig.smallestScreenWidthDp;
-            final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
-            while (it.hasNext()) {
-                Map.Entry<String, Integer> entry = it.next();
-                String pkg = entry.getKey();
-                int mode = entry.getValue();
-                if (mode == 0) {
-                    continue;
-                }
-                ApplicationInfo ai = null;
-                try {
-                    ai = pm.getApplicationInfo(pkg, 0, 0);
-                } catch (RemoteException e) {
-                }
-                if (ai == null) {
-                    continue;
-                }
-                CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout,
-                        smallestScreenWidthDp, false);
-                if (info.alwaysSupportsScreen()) {
-                    continue;
-                }
-                if (info.neverSupportsScreen()) {
-                    continue;
-                }
-                out.startTag(null, "pkg");
-                out.attribute(null, "name", pkg);
-                out.attribute(null, "mode", Integer.toString(mode));
-                out.endTag(null, "pkg");
-            }
-
-            out.endTag(null, "compat-packages");
-            out.endDocument();
-
-            mFile.finishWrite(fos);
-        } catch (java.io.IOException e1) {
-            Slog.w(TAG, "Error writing compat packages", e1);
-            if (fos != null) {
-                mFile.failWrite(fos);
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 1242ed6..37d07bb 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -27,6 +27,7 @@
 
 import com.android.internal.app.procstats.AssociationState;
 import com.android.internal.app.procstats.ProcessStats;
+import com.android.server.wm.ActivityServiceConnectionsHolder;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 48e26ed..968c17f 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -59,6 +59,7 @@
         sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS_GLES, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class);
         // add other global settings here...
     }
diff --git a/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java b/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java
deleted file mode 100644
index b39873f..0000000
--- a/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageItemInfo;
-import android.content.pm.PackageManager;
-import android.os.SystemPropertiesProto;
-import android.util.Log;
-import android.view.Window;
-import android.view.WindowManager;
-
-import com.android.internal.R;
-import com.android.server.utils.AppInstallerUtil;
-
-public class DeprecatedTargetSdkVersionDialog {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "DeprecatedTargetSdkVersionDialog" : TAG_ATM;
-
-    private final AlertDialog mDialog;
-    private final String mPackageName;
-
-    public DeprecatedTargetSdkVersionDialog(final AppWarnings manager, Context context,
-            ApplicationInfo appInfo) {
-        mPackageName = appInfo.packageName;
-
-        final PackageManager pm = context.getPackageManager();
-        final CharSequence label = appInfo.loadSafeLabel(pm,
-                PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
-                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE
-                        | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
-        final CharSequence message = context.getString(R.string.deprecated_target_sdk_message);
-
-        final AlertDialog.Builder builder = new AlertDialog.Builder(context)
-                .setPositiveButton(R.string.ok, (dialog, which) ->
-                    manager.setPackageFlag(
-                            mPackageName, AppWarnings.FLAG_HIDE_DEPRECATED_SDK, true))
-                .setMessage(message)
-                .setTitle(label);
-
-        // If we might be able to update the app, show a button.
-        final Intent installerIntent = AppInstallerUtil.createIntent(context, appInfo.packageName);
-        if (installerIntent != null) {
-            builder.setNeutralButton(R.string.deprecated_target_sdk_app_store,
-                    (dialog, which) -> {
-                        context.startActivity(installerIntent);
-                    });
-        }
-
-        // Ensure the content view is prepared.
-        mDialog = builder.create();
-        mDialog.create();
-
-        final Window window = mDialog.getWindow();
-        window.setType(WindowManager.LayoutParams.TYPE_PHONE);
-
-        // DO NOT MODIFY. Used by CTS to verify the dialog is displayed.
-        window.getAttributes().setTitle("DeprecatedTargetSdkVersionDialog");
-    }
-
-    public String getPackageName() {
-        return mPackageName;
-    }
-
-    public void show() {
-        Log.w(TAG, "Showing SDK deprecation warning for package " + mPackageName);
-        mDialog.show();
-    }
-
-    public void dismiss() {
-        mDialog.dismiss();
-    }
-}
diff --git a/services/core/java/com/android/server/am/FactoryErrorDialog.java b/services/core/java/com/android/server/am/FactoryErrorDialog.java
deleted file mode 100644
index f4632c1..0000000
--- a/services/core/java/com/android/server/am/FactoryErrorDialog.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2006 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.server.am;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Handler;
-import android.os.Message;
-import android.view.WindowManager;
-
-final class FactoryErrorDialog extends BaseErrorDialog {
-    public FactoryErrorDialog(Context context, CharSequence msg) {
-        super(context);
-        setCancelable(false);
-        setTitle(context.getText(com.android.internal.R.string.factorytest_failed));
-        setMessage(msg);
-        setButton(DialogInterface.BUTTON_POSITIVE,
-                context.getText(com.android.internal.R.string.factorytest_reboot),
-                mHandler.obtainMessage(0));
-        WindowManager.LayoutParams attrs = getWindow().getAttributes();
-        attrs.setTitle("Factory Error");
-        getWindow().setAttributes(attrs);
-    }
-    
-    public void onStop() {
-    }
-
-    private final Handler mHandler = new Handler() {
-        public void handleMessage(Message msg) {
-            throw new RuntimeException("Rebooting from failed factory test");
-        }
-    };
-}
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
deleted file mode 100644
index 28b2a42..0000000
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ /dev/null
@@ -1,512 +0,0 @@
-/*
- * Copyright (C) 2016 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.server.am;
-
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
-import static android.view.WindowManager.TRANSIT_UNSET;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
-
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
-import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING;
-import static com.android.server.am.KeyguardOccludedProto.DISPLAY_ID;
-import static com.android.server.am.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
-
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
-import com.android.server.wm.WindowManagerService;
-
-import java.io.PrintWriter;
-
-/**
- * Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
- * currently visible.
- * <p>
- * Note that everything in this class should only be accessed with the AM lock being held.
- */
-class KeyguardController {
-
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_ATM;
-
-    private final ActivityStackSupervisor mStackSupervisor;
-    private WindowManagerService mWindowManager;
-    private boolean mKeyguardShowing;
-    private boolean mAodShowing;
-    private boolean mKeyguardGoingAway;
-    private boolean mDismissalRequested;
-    private int mBeforeUnoccludeTransit;
-    private int mVisibilityTransactionDepth;
-    // TODO(b/111955725): Support multiple external displays
-    private int mSecondaryDisplayShowing = INVALID_DISPLAY;
-    private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
-    private final ActivityTaskManagerService mService;
-
-    KeyguardController(ActivityTaskManagerService service,
-            ActivityStackSupervisor stackSupervisor) {
-        mService = service;
-        mStackSupervisor = stackSupervisor;
-    }
-
-    void setWindowManager(WindowManagerService windowManager) {
-        mWindowManager = windowManager;
-    }
-
-    /**
-     * @return true if either Keyguard or AOD are showing, not going away, and not being occluded
-     *         on the given display, false otherwise
-     */
-    boolean isKeyguardOrAodShowing(int displayId) {
-        return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway
-                && !isDisplayOccluded(displayId);
-    }
-
-    /**
-     * @return true if Keyguard is showing, not going away, and not being occluded on the given
-     *         display, false otherwise
-     */
-    boolean isKeyguardShowing(int displayId) {
-        return mKeyguardShowing && !mKeyguardGoingAway && !isDisplayOccluded(displayId);
-    }
-
-    /**
-     * @return true if Keyguard is either showing or occluded, but not going away
-     */
-    boolean isKeyguardLocked() {
-        return mKeyguardShowing && !mKeyguardGoingAway;
-    }
-
-    /**
-     * @return {@code true} if the keyguard is going away, {@code false} otherwise.
-     */
-    boolean isKeyguardGoingAway() {
-        // Also check keyguard showing in case value is stale.
-        return mKeyguardGoingAway && mKeyguardShowing;
-    }
-
-    /**
-     * Update the Keyguard showing state.
-     */
-    void setKeyguardShown(boolean keyguardShowing, boolean aodShowing,
-            int secondaryDisplayShowing) {
-        boolean showingChanged = keyguardShowing != mKeyguardShowing || aodShowing != mAodShowing;
-        // If keyguard is going away, but SystemUI aborted the transition, need to reset state.
-        showingChanged |= mKeyguardGoingAway && keyguardShowing;
-        if (!showingChanged && secondaryDisplayShowing == mSecondaryDisplayShowing) {
-            return;
-        }
-        mKeyguardShowing = keyguardShowing;
-        mAodShowing = aodShowing;
-        mSecondaryDisplayShowing = secondaryDisplayShowing;
-        mWindowManager.setAodShowing(aodShowing);
-        if (showingChanged) {
-            dismissDockedStackIfNeeded();
-            setKeyguardGoingAway(false);
-            // TODO(b/113840485): Check usage for non-default display
-            mWindowManager.setKeyguardOrAodShowingOnDefaultDisplay(
-                    isKeyguardOrAodShowing(DEFAULT_DISPLAY));
-            if (keyguardShowing) {
-                mDismissalRequested = false;
-            }
-        }
-        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        updateKeyguardSleepToken();
-    }
-
-    /**
-     * Called when Keyguard is going away.
-     *
-     * @param flags See {@link WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
-     *              etc.
-     */
-    void keyguardGoingAway(int flags) {
-        if (!mKeyguardShowing) {
-            return;
-        }
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway");
-        mWindowManager.deferSurfaceLayout();
-        try {
-            setKeyguardGoingAway(true);
-            mWindowManager.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
-                    false /* alwaysKeepCurrent */, convertTransitFlags(flags),
-                    false /* forceOverride */);
-            updateKeyguardSleepToken();
-
-            // Some stack visibility might change (e.g. docked stack)
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-            mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
-            mWindowManager.executeAppTransition();
-        } finally {
-            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
-            mWindowManager.continueSurfaceLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
-    void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence message) {
-        final ActivityRecord activityRecord = ActivityRecord.forTokenLocked(token);
-        if (activityRecord == null || !activityRecord.visibleIgnoringKeyguard) {
-            failCallback(callback);
-            return;
-        }
-        Slog.i(TAG, "Activity requesting to dismiss Keyguard: " + activityRecord);
-
-        // If the client has requested to dismiss the keyguard and the Activity has the flag to
-        // turn the screen on, wakeup the screen if it's the top Activity.
-        if (activityRecord.getTurnScreenOnFlag() && activityRecord.isTopRunningActivity()) {
-            mStackSupervisor.wakeUp("dismissKeyguard");
-        }
-
-        mWindowManager.dismissKeyguard(callback, message);
-    }
-
-    private void setKeyguardGoingAway(boolean keyguardGoingAway) {
-        mKeyguardGoingAway = keyguardGoingAway;
-        mWindowManager.setKeyguardGoingAway(keyguardGoingAway);
-    }
-
-    private void failCallback(IKeyguardDismissCallback callback) {
-        try {
-            callback.onDismissError();
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failed to call callback", e);
-        }
-    }
-
-    private int convertTransitFlags(int keyguardGoingAwayFlags) {
-        int result = 0;
-        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) {
-            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-        }
-        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0) {
-            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-        }
-        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0) {
-            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-        }
-        return result;
-    }
-
-    /**
-     * Starts a batch of visibility updates.
-     */
-    void beginActivityVisibilityUpdate() {
-        mVisibilityTransactionDepth++;
-    }
-
-    /**
-     * Ends a batch of visibility updates. After all batches are done, this method makes sure to
-     * update lockscreen occluded/dismiss state if needed.
-     */
-    void endActivityVisibilityUpdate() {
-        mVisibilityTransactionDepth--;
-        if (mVisibilityTransactionDepth == 0) {
-            visibilitiesUpdated();
-        }
-    }
-
-    /**
-     * @return True if we may show an activity while Keyguard is showing because we are in the
-     *         process of dismissing it anyways, false otherwise.
-     */
-    boolean canShowActivityWhileKeyguardShowing(ActivityRecord r, boolean dismissKeyguard) {
-
-        // Allow to show it when we are about to dismiss Keyguard. This isn't allowed if r is
-        // already the dismissing activity, in which case we don't allow it to repeatedly dismiss
-        // Keyguard.
-        return dismissKeyguard && canDismissKeyguard() && !mAodShowing
-                && (mDismissalRequested
-                || getDisplay(r.getDisplayId()).mDismissingKeyguardActivity != r);
-    }
-
-    /**
-     * @return True if we may show an activity while Keyguard is occluded, false otherwise.
-     */
-    boolean canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked) {
-        return showWhenLocked || dismissKeyguard && !mWindowManager.isKeyguardSecure();
-    }
-
-    private void visibilitiesUpdated() {
-        boolean requestDismissKeyguard = false;
-        for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
-            final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
-            final KeyguardDisplayState state = getDisplay(display.mDisplayId);
-            state.visibilitiesUpdated(this, display);
-            requestDismissKeyguard |= state.mRequestDismissKeyguard;
-        }
-
-        // Dismissing Keyguard happens globally using the information from all displays.
-        if (requestDismissKeyguard) {
-            handleDismissKeyguard();
-        }
-    }
-
-    /**
-     * Called when occluded state changed.
-     */
-    private void handleOccludedChanged() {
-        mWindowManager.onKeyguardOccludedChanged(isDisplayOccluded(DEFAULT_DISPLAY));
-        if (isKeyguardLocked()) {
-            mWindowManager.deferSurfaceLayout();
-            try {
-                mWindowManager.prepareAppTransition(resolveOccludeTransit(),
-                        false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
-                updateKeyguardSleepToken();
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                mWindowManager.executeAppTransition();
-            } finally {
-                mWindowManager.continueSurfaceLayout();
-            }
-        }
-        dismissDockedStackIfNeeded();
-    }
-
-    /**
-     * Called when somebody wants to dismiss the Keyguard via the flag.
-     */
-    private void handleDismissKeyguard() {
-        // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
-        // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
-        // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
-        if (mWindowManager.isKeyguardSecure()) {
-            mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
-            mDismissalRequested = true;
-
-            // If we are about to unocclude the Keyguard, but we can dismiss it without security,
-            // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
-            if (mKeyguardShowing && canDismissKeyguard()
-                    && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
-                mWindowManager.prepareAppTransition(mBeforeUnoccludeTransit,
-                        false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                mWindowManager.executeAppTransition();
-            }
-        }
-    }
-
-    private boolean isDisplayOccluded(int displayId) {
-        return getDisplay(displayId).mOccluded;
-    }
-
-    /**
-     * @return true if Keyguard can be currently dismissed without entering credentials.
-     */
-    boolean canDismissKeyguard() {
-        return mWindowManager.isKeyguardTrusted() || !mWindowManager.isKeyguardSecure();
-    }
-
-    private int resolveOccludeTransit() {
-        if (mBeforeUnoccludeTransit != TRANSIT_UNSET
-                && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
-                // TODO(b/113840485): Handle app transition for individual display.
-                && isDisplayOccluded(DEFAULT_DISPLAY)) {
-
-            // Reuse old transit in case we are occluding Keyguard again, meaning that we never
-            // actually occclude/unocclude Keyguard, but just run a normal transition.
-            return mBeforeUnoccludeTransit;
-            // TODO(b/113840485): Handle app transition for individual display.
-        } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) {
-
-            // Save transit in case we dismiss/occlude Keyguard shortly after.
-            mBeforeUnoccludeTransit = mWindowManager.getPendingAppTransition();
-            return TRANSIT_KEYGUARD_UNOCCLUDE;
-        } else {
-            return TRANSIT_KEYGUARD_OCCLUDE;
-        }
-    }
-
-    private void dismissDockedStackIfNeeded() {
-        // TODO(b/113840485): Handle docked stack for individual display.
-        if (mKeyguardShowing && isDisplayOccluded(DEFAULT_DISPLAY)) {
-            // The lock screen is currently showing, but is occluded by a window that can
-            // show on top of the lock screen. In this can we want to dismiss the docked
-            // stack since it will be complicated/risky to try to put the activity on top
-            // of the lock screen in the right fullscreen configuration.
-            final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
-            if (stack == null) {
-                return;
-            }
-            mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
-                    stack.isFocusedStackOnDisplay());
-        }
-    }
-
-    private void updateKeyguardSleepToken() {
-        for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
-            final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
-            final KeyguardDisplayState state = getDisplay(display.mDisplayId);
-            if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) {
-                state.acquiredSleepToken();
-            } else if (!isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken != null) {
-                state.releaseSleepToken();
-            }
-        }
-    }
-
-    private KeyguardDisplayState getDisplay(int displayId) {
-        if (mDisplayStates.get(displayId) == null) {
-            mDisplayStates.append(displayId,
-                    new KeyguardDisplayState(mService, displayId));
-        }
-        return mDisplayStates.get(displayId);
-    }
-
-    void onDisplayRemoved(int displayId) {
-        if (mDisplayStates.get(displayId) != null) {
-            mDisplayStates.get(displayId).onRemoved();
-            mDisplayStates.remove(displayId);
-        }
-    }
-
-    /** Represents Keyguard state per individual display. */
-    private static class KeyguardDisplayState {
-        private final int mDisplayId;
-        private boolean mOccluded;
-        private ActivityRecord mDismissingKeyguardActivity;
-        private boolean mRequestDismissKeyguard;
-        private final ActivityTaskManagerService mService;
-        private SleepToken mSleepToken;
-
-        KeyguardDisplayState(ActivityTaskManagerService service, int displayId) {
-            mService = service;
-            mDisplayId = displayId;
-        }
-
-        void onRemoved() {
-            mDismissingKeyguardActivity = null;
-            releaseSleepToken();
-        }
-
-        void acquiredSleepToken() {
-            if (mSleepToken == null) {
-                mSleepToken = mService.acquireSleepToken("keyguard", mDisplayId);
-            }
-        }
-
-        void releaseSleepToken() {
-            if (mSleepToken != null) {
-                mSleepToken.release();
-                mSleepToken = null;
-            }
-        }
-
-        void visibilitiesUpdated(KeyguardController controller, ActivityDisplay display) {
-            final boolean lastOccluded = mOccluded;
-            final ActivityRecord lastDismissActivity = mDismissingKeyguardActivity;
-            mRequestDismissKeyguard = false;
-            mOccluded = false;
-            mDismissingKeyguardActivity = null;
-
-            // Only the top activity of the focused stack on each display may control it's
-            // occluded state.
-            final ActivityStack focusedStack = display.getFocusedStack();
-            if (focusedStack != null) {
-                final ActivityRecord topDismissing =
-                        focusedStack.getTopDismissingKeyguardActivity();
-                mOccluded = focusedStack.topActivityOccludesKeyguard() || (topDismissing != null
-                                && focusedStack.topRunningActivityLocked() == topDismissing
-                                && controller.canShowWhileOccluded(
-                                true /* dismissKeyguard */,
-                                false /* showWhenLocked */));
-                if (focusedStack.getTopDismissingKeyguardActivity() != null) {
-                    mDismissingKeyguardActivity = focusedStack.getTopDismissingKeyguardActivity();
-                }
-                mOccluded |= controller.mWindowManager.isShowingDream();
-            }
-
-            // TODO(b/113840485): Handle app transition for individual display.
-            // For now, only default display can change occluded.
-            if (lastOccluded != mOccluded && mDisplayId == DEFAULT_DISPLAY) {
-                controller.handleOccludedChanged();
-            }
-            if (lastDismissActivity != mDismissingKeyguardActivity && !mOccluded
-                    && mDismissingKeyguardActivity != null
-                    && controller.mWindowManager.isKeyguardSecure()) {
-                mRequestDismissKeyguard = true;
-            }
-        }
-
-        void dumpStatus(PrintWriter pw, String prefix) {
-            final StringBuilder sb = new StringBuilder();
-            sb.append(prefix);
-            sb.append("  Occluded=").append(mOccluded)
-                    .append(" DismissingKeyguardActivity=")
-                    .append(mDismissingKeyguardActivity)
-                    .append(" at display=")
-                    .append(mDisplayId);
-            pw.println(sb.toString());
-        }
-
-        void writeToProto(ProtoOutputStream proto, long fieldId) {
-            final long token = proto.start(fieldId);
-            proto.write(DISPLAY_ID, mDisplayId);
-            proto.write(KEYGUARD_OCCLUDED, mOccluded);
-            proto.end(token);
-        }
-    }
-
-    void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + "KeyguardController:");
-        pw.println(prefix + "  mKeyguardShowing=" + mKeyguardShowing);
-        pw.println(prefix + "  mAodShowing=" + mAodShowing);
-        pw.println(prefix + "  mKeyguardGoingAway=" + mKeyguardGoingAway);
-        dumpDisplayStates(pw, prefix);
-        pw.println(prefix + "  mDismissalRequested=" + mDismissalRequested);
-        pw.println(prefix + "  mVisibilityTransactionDepth=" + mVisibilityTransactionDepth);
-    }
-
-    void writeToProto(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-        proto.write(KEYGUARD_SHOWING, mKeyguardShowing);
-        writeDisplayStatesToProto(proto, KEYGUARD_OCCLUDED_STATES);
-        proto.end(token);
-    }
-
-    private void dumpDisplayStates(PrintWriter pw, String prefix) {
-        for (int i = 0; i < mDisplayStates.size(); i++) {
-            mDisplayStates.valueAt(i).dumpStatus(pw, prefix);
-        }
-    }
-
-    private void writeDisplayStatesToProto(ProtoOutputStream proto, long fieldId) {
-        for (int i = 0; i < mDisplayStates.size(); i++) {
-            mDisplayStates.valueAt(i).writeToProto(proto, fieldId);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/LaunchParamsController.java b/services/core/java/com/android/server/am/LaunchParamsController.java
deleted file mode 100644
index 68e897f..0000000
--- a/services/core/java/com/android/server/am/LaunchParamsController.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
-
-import android.annotation.IntDef;
-import android.app.ActivityOptions;
-import android.content.pm.ActivityInfo.WindowLayout;
-import android.graphics.Rect;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * {@link LaunchParamsController} calculates the {@link LaunchParams} by coordinating between
- * registered {@link LaunchParamsModifier}s.
- */
-class LaunchParamsController {
-    private final ActivityTaskManagerService mService;
-    private final List<LaunchParamsModifier> mModifiers = new ArrayList<>();
-
-    // Temporary {@link LaunchParams} for internal calculations. This is kept separate from
-    // {@code mTmpCurrent} and {@code mTmpResult} to prevent clobbering values.
-    private final LaunchParams mTmpParams = new LaunchParams();
-
-    private final LaunchParams mTmpCurrent = new LaunchParams();
-    private final LaunchParams mTmpResult = new LaunchParams();
-
-    LaunchParamsController(ActivityTaskManagerService service) {
-       mService = service;
-    }
-
-    /**
-     * Creates a {@link LaunchParamsController} with default registered
-     * {@link LaunchParamsModifier}s.
-     */
-    void registerDefaultModifiers(ActivityStackSupervisor supervisor) {
-        // {@link TaskLaunchParamsModifier} handles window layout preferences.
-        registerModifier(new TaskLaunchParamsModifier(supervisor));
-    }
-
-    /**
-     * Returns the {@link LaunchParams} calculated by the registered modifiers
-     * @param task      The {@link TaskRecord} currently being positioned.
-     * @param layout    The specified {@link WindowLayout}.
-     * @param activity  The {@link ActivityRecord} currently being positioned.
-     * @param source    The {@link ActivityRecord} from which activity was started from.
-     * @param options   The {@link ActivityOptions} specified for the activity.
-     * @param result    The resulting params.
-     */
-    void calculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
-                   ActivityRecord source, ActivityOptions options, LaunchParams result) {
-        result.reset();
-
-        // We start at the last registered {@link LaunchParamsModifier} as this represents
-        // The modifier closest to the product level. Moving back through the list moves closer to
-        // the platform logic.
-        for (int i = mModifiers.size() - 1; i >= 0; --i) {
-            mTmpCurrent.set(result);
-            mTmpResult.reset();
-            final LaunchParamsModifier modifier = mModifiers.get(i);
-
-            switch(modifier.onCalculate(task, layout, activity, source, options, mTmpCurrent,
-                    mTmpResult)) {
-                case RESULT_SKIP:
-                    // Do not apply any results when we are told to skip
-                    continue;
-                case RESULT_DONE:
-                    // Set result and return immediately.
-                    result.set(mTmpResult);
-                    return;
-                case RESULT_CONTINUE:
-                    // Set result and continue
-                    result.set(mTmpResult);
-                    break;
-            }
-        }
-
-        if (activity != null && activity.requestedVrComponent != null) {
-            // Check if the Activity is a VR activity. If so, it should be launched in main display.
-            result.mPreferredDisplayId = DEFAULT_DISPLAY;
-        } else if (mService.mVr2dDisplayId != INVALID_DISPLAY) {
-            // Get the virtual display ID from ActivityTaskManagerService. If that's set we
-            // should always use that.
-            result.mPreferredDisplayId = mService.mVr2dDisplayId;
-        }
-    }
-
-    /**
-     * A convenience method for laying out a task.
-     * @return {@code true} if bounds were set on the task. {@code false} otherwise.
-     */
-    boolean layoutTask(TaskRecord task, WindowLayout layout) {
-        return layoutTask(task, layout, null /*activity*/, null /*source*/, null /*options*/);
-    }
-
-    boolean layoutTask(TaskRecord task, WindowLayout layout, ActivityRecord activity,
-            ActivityRecord source, ActivityOptions options) {
-        calculate(task, layout, activity, source, options, mTmpParams);
-
-        // No changes, return.
-        if (mTmpParams.isEmpty()) {
-            return false;
-        }
-
-        mService.mWindowManager.deferSurfaceLayout();
-
-        try {
-            if (mTmpParams.hasPreferredDisplay()
-                    && mTmpParams.mPreferredDisplayId != task.getStack().getDisplay().mDisplayId) {
-                mService.moveStackToDisplay(task.getStackId(), mTmpParams.mPreferredDisplayId);
-            }
-
-            if (mTmpParams.hasWindowingMode()
-                    && mTmpParams.mWindowingMode != task.getStack().getWindowingMode()) {
-                task.getStack().setWindowingMode(mTmpParams.mWindowingMode);
-            }
-
-            if (!mTmpParams.mBounds.isEmpty()) {
-                task.updateOverrideConfiguration(mTmpParams.mBounds);
-                return true;
-            } else {
-                return false;
-            }
-        } finally {
-            mService.mWindowManager.continueSurfaceLayout();
-        }
-    }
-
-    /**
-     * Adds a modifier to participate in future bounds calculation. Note that the last registered
-     * {@link LaunchParamsModifier} will be the first to calculate the bounds.
-     */
-    void registerModifier(LaunchParamsModifier modifier) {
-        if (mModifiers.contains(modifier)) {
-            return;
-        }
-
-        mModifiers.add(modifier);
-    }
-
-    /**
-     * A container for holding launch related fields.
-     */
-    static class LaunchParams {
-        /** The bounds within the parent container. */
-        final Rect mBounds = new Rect();
-
-        /** The id of the display the {@link TaskRecord} would prefer to be on. */
-        int mPreferredDisplayId;
-
-        /** The windowing mode to be in. */
-        int mWindowingMode;
-
-        /** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */
-        void reset() {
-            mBounds.setEmpty();
-            mPreferredDisplayId = INVALID_DISPLAY;
-            mWindowingMode = WINDOWING_MODE_UNDEFINED;
-        }
-
-        /** Copies the values set on the passed in {@link LaunchParams}. */
-        void set(LaunchParams params) {
-            mBounds.set(params.mBounds);
-            mPreferredDisplayId = params.mPreferredDisplayId;
-            mWindowingMode = params.mWindowingMode;
-        }
-
-        /** Returns {@code true} if no values have been explicitly set. */
-        boolean isEmpty() {
-            return mBounds.isEmpty() && mPreferredDisplayId == INVALID_DISPLAY
-                    && mWindowingMode == WINDOWING_MODE_UNDEFINED;
-        }
-
-        boolean hasWindowingMode() {
-            return mWindowingMode != WINDOWING_MODE_UNDEFINED;
-        }
-
-        boolean hasPreferredDisplay() {
-            return mPreferredDisplayId != INVALID_DISPLAY;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-
-            LaunchParams that = (LaunchParams) o;
-
-            if (mPreferredDisplayId != that.mPreferredDisplayId) return false;
-            if (mWindowingMode != that.mWindowingMode) return false;
-            return mBounds != null ? mBounds.equals(that.mBounds) : that.mBounds == null;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = mBounds != null ? mBounds.hashCode() : 0;
-            result = 31 * result + mPreferredDisplayId;
-            result = 31 * result + mWindowingMode;
-            return result;
-        }
-    }
-
-    /**
-     * An interface implemented by those wanting to participate in bounds calculation.
-     */
-    interface LaunchParamsModifier {
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE})
-        @interface Result {}
-
-        /** Returned when the modifier does not want to influence the bounds calculation */
-        int RESULT_SKIP = 0;
-        /**
-         * Returned when the modifier has changed the bounds and would like its results to be the
-         * final bounds applied.
-         */
-        int RESULT_DONE = 1;
-        /**
-         * Returned when the modifier has changed the bounds but is okay with other modifiers
-         * influencing the bounds.
-         */
-        int RESULT_CONTINUE = 2;
-
-        /**
-         * Returns the launch params that the provided activity launch params should be overridden
-         * to. {@link LaunchParamsModifier} can use this for various purposes, including: 1)
-         * Providing default bounds if the launch bounds have not been provided. 2) Repositioning
-         * the task so it doesn't get placed over an existing task. 3) Resizing the task so that its
-         * dimensions match the activity's requested orientation.
-         *
-         * @param task          Can be: 1) the target task in which the source activity wants to
-         *                      launch the target activity; 2) a newly created task that Android
-         *                      gives a chance to override its launching bounds; 3) {@code null} if
-         *                      this is called to override an activity's launching bounds.
-         * @param layout        Desired layout when activity is first launched.
-         * @param activity      Activity that is being started. This can be {@code null} on
-         *                      re-parenting an activity to a new task (e.g. for
-         *                      Picture-In-Picture). Tasks being created because an activity was
-         *                      launched should have this be non-null.
-         * @param source        the Activity that launched a new task. Could be {@code null}.
-         * @param options       {@link ActivityOptions} used to start the activity with.
-         * @param currentParams launching params after the process of last {@link
-         *                      LaunchParamsModifier}.
-         * @param outParams     the result params to be set.
-         * @return see {@link LaunchParamsModifier.Result}
-         */
-        @Result
-        int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
-                ActivityRecord source, ActivityOptions options, LaunchParams currentParams,
-                LaunchParams outParams);
-    }
-}
diff --git a/services/core/java/com/android/server/am/LaunchWarningWindow.java b/services/core/java/com/android/server/am/LaunchWarningWindow.java
deleted file mode 100644
index 30c3066..0000000
--- a/services/core/java/com/android/server/am/LaunchWarningWindow.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2011 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.server.am;
-
-import com.android.internal.R;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.util.TypedValue;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-public final class LaunchWarningWindow extends Dialog {
-    public LaunchWarningWindow(Context context, ActivityRecord cur, ActivityRecord next) {
-        super(context, R.style.Theme_Toast);
-
-        requestWindowFeature(Window.FEATURE_LEFT_ICON);
-        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
-        
-        setContentView(R.layout.launch_warning);
-        setTitle(context.getText(R.string.launch_warning_title));
-
-        TypedValue out = new TypedValue();
-        getContext().getTheme().resolveAttribute(android.R.attr.alertDialogIcon, out, true);
-        getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, out.resourceId);
-
-        ImageView icon = (ImageView)findViewById(R.id.replace_app_icon);
-        icon.setImageDrawable(next.info.applicationInfo.loadIcon(context.getPackageManager()));
-        TextView text = (TextView)findViewById(R.id.replace_message);
-        text.setText(context.getResources().getString(R.string.launch_warning_replace,
-                next.info.applicationInfo.loadLabel(context.getPackageManager()).toString()));
-        icon = (ImageView)findViewById(R.id.original_app_icon);
-        icon.setImageDrawable(cur.info.applicationInfo.loadIcon(context.getPackageManager()));
-        text = (TextView)findViewById(R.id.original_message);
-        text.setText(context.getResources().getString(R.string.launch_warning_original,
-                cur.info.applicationInfo.loadLabel(context.getPackageManager()).toString()));
-    }
-}
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
deleted file mode 100644
index 5b31d5f..0000000
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ /dev/null
@@ -1,896 +0,0 @@
-/*
- * Copyright 2017, 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.server.am;
-
-import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
-import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.Context.DEVICE_POLICY_SERVICE;
-import static android.content.Context.STATUS_BAR_SERVICE;
-import static android.content.Intent.ACTION_CALL_EMERGENCY;
-import static android.os.UserHandle.USER_ALL;
-import static android.os.UserHandle.USER_CURRENT;
-import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.StatusBarManager;
-import android.app.admin.DevicePolicyManager;
-import android.app.admin.IDevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.Settings;
-import android.telecom.TelecomManager;
-import android.util.Pair;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.server.LocalServices;
-import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.WindowManagerService;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * Helper class that deals with all things related to task locking. This includes the screen pinning
- * mode that can be launched via System UI as well as the fully locked mode that can be achieved
- * on fully managed devices.
- *
- * Note: All methods in this class should only be called with the ActivityManagerService lock held.
- *
- * @see Activity#startLockTask()
- * @see Activity#stopLockTask()
- */
-public class LockTaskController {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "LockTaskController" : TAG_ATM;
-    private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
-
-    @VisibleForTesting
-    static final int STATUS_BAR_MASK_LOCKED = StatusBarManager.DISABLE_MASK
-            & (~StatusBarManager.DISABLE_EXPAND)
-            & (~StatusBarManager.DISABLE_NOTIFICATION_TICKER)
-            & (~StatusBarManager.DISABLE_SYSTEM_INFO)
-            & (~StatusBarManager.DISABLE_BACK);
-    @VisibleForTesting
-    static final int STATUS_BAR_MASK_PINNED = StatusBarManager.DISABLE_MASK
-            & (~StatusBarManager.DISABLE_BACK)
-            & (~StatusBarManager.DISABLE_HOME)
-            & (~StatusBarManager.DISABLE_RECENT);
-
-    private static final SparseArray<Pair<Integer, Integer>> STATUS_BAR_FLAG_MAP_LOCKED;
-    static {
-        STATUS_BAR_FLAG_MAP_LOCKED = new SparseArray<>();
-
-        STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO,
-                new Pair<>(StatusBarManager.DISABLE_CLOCK, StatusBarManager.DISABLE2_SYSTEM_ICONS));
-
-        STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS,
-                new Pair<>(StatusBarManager.DISABLE_NOTIFICATION_ICONS
-                        | StatusBarManager.DISABLE_NOTIFICATION_ALERTS,
-                        StatusBarManager.DISABLE2_NOTIFICATION_SHADE));
-
-        STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_HOME,
-                new Pair<>(StatusBarManager.DISABLE_HOME, StatusBarManager.DISABLE2_NONE));
-
-        STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW,
-                new Pair<>(StatusBarManager.DISABLE_RECENT, StatusBarManager.DISABLE2_NONE));
-
-        STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
-                new Pair<>(StatusBarManager.DISABLE_NONE,
-                        StatusBarManager.DISABLE2_GLOBAL_ACTIONS));
-    }
-
-    /** Tag used for disabling of keyguard */
-    private static final String LOCK_TASK_TAG = "Lock-to-App";
-
-    private final IBinder mToken = new Binder();
-    private final ActivityStackSupervisor mSupervisor;
-    private final Context mContext;
-
-    // The following system services cannot be final, because they do not exist when this class
-    // is instantiated during device boot
-    @VisibleForTesting
-    IStatusBarService mStatusBarService;
-    @VisibleForTesting
-    IDevicePolicyManager mDevicePolicyManager;
-    @VisibleForTesting
-    WindowManagerService mWindowManager;
-    @VisibleForTesting
-    LockPatternUtils mLockPatternUtils;
-    @VisibleForTesting
-    TelecomManager mTelecomManager;
-
-    /**
-     * The chain of tasks in LockTask mode, in the order of when they first entered LockTask mode.
-     *
-     * The first task in the list, which started the current LockTask session, is called the root
-     * task. It coincides with the Home task in a typical multi-app kiosk deployment. When there are
-     * more than one locked tasks, the root task can't be finished. Nor can it be moved to the back
-     * of the stack by {@link ActivityStack#moveTaskToBackLocked(int)};
-     *
-     * Calling {@link Activity#stopLockTask()} on the root task will finish all tasks but itself in
-     * this list, and the device will exit LockTask mode.
-     *
-     * The list is empty if LockTask is inactive.
-     */
-    private final ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>();
-
-    /**
-     * Packages that are allowed to be launched into the lock task mode for each user.
-     */
-    private final SparseArray<String[]> mLockTaskPackages = new SparseArray<>();
-
-    /**
-     * Features that are allowed by DPC to show during LockTask mode.
-     */
-    private final SparseIntArray mLockTaskFeatures = new SparseIntArray();
-
-    /**
-     * Store the current lock task mode. Possible values:
-     * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
-     * {@link ActivityManager#LOCK_TASK_MODE_PINNED}
-     */
-    private int mLockTaskModeState = LOCK_TASK_MODE_NONE;
-
-    /**
-     * This is ActivityStackSupervisor's Handler.
-     */
-    private final Handler mHandler;
-
-    LockTaskController(Context context, ActivityStackSupervisor supervisor,
-            Handler handler) {
-        mContext = context;
-        mSupervisor = supervisor;
-        mHandler = handler;
-    }
-
-    /**
-     * Set the window manager instance used in this class. This is necessary, because the window
-     * manager does not exist during instantiation of this class.
-     */
-    void setWindowManager(WindowManagerService windowManager) {
-        mWindowManager = windowManager;
-    }
-
-    /**
-     * @return the current lock task state. This can be any of
-     * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
-     * {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
-     */
-    int getLockTaskModeState() {
-        return mLockTaskModeState;
-    }
-
-    /**
-     * @return whether the given task is locked at the moment. Locked tasks cannot be moved to the
-     * back of the stack.
-     */
-    @VisibleForTesting
-    boolean isTaskLocked(TaskRecord task) {
-        return mLockTaskModeTasks.contains(task);
-    }
-
-    /**
-     * @return {@code true} whether this task first started the current LockTask session.
-     */
-    private boolean isRootTask(TaskRecord task) {
-        return mLockTaskModeTasks.indexOf(task) == 0;
-    }
-
-    /**
-     * @return whether the given activity is blocked from finishing, because it is the only activity
-     * of the last locked task and finishing it would mean that lock task mode is ended illegally.
-     */
-    boolean activityBlockedFromFinish(ActivityRecord activity) {
-        final TaskRecord task = activity.getTask();
-        if (activity == task.getRootActivity()
-                && activity == task.getTopActivity()
-                && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
-                && isRootTask(task)) {
-            Slog.i(TAG, "Not finishing task in lock task mode");
-            showLockTaskToast();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @return whether the given task can be moved to the back of the stack with
-     * {@link ActivityStack#moveTaskToBackLocked(int)}
-     * @see #mLockTaskModeTasks
-     */
-    boolean canMoveTaskToBack(TaskRecord task) {
-        if (isRootTask(task)) {
-            showLockTaskToast();
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * @return whether the requested task is allowed to be locked (either whitelisted, or declares
-     * lockTaskMode="always" in the manifest).
-     */
-    boolean isTaskWhitelisted(TaskRecord task) {
-        switch(task.mLockTaskAuth) {
-            case LOCK_TASK_AUTH_WHITELISTED:
-            case LOCK_TASK_AUTH_LAUNCHABLE:
-            case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
-                return true;
-            case LOCK_TASK_AUTH_PINNABLE:
-            case LOCK_TASK_AUTH_DONT_LOCK:
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * @return whether the requested task is disallowed to be launched.
-     */
-    boolean isLockTaskModeViolation(TaskRecord task) {
-        return isLockTaskModeViolation(task, false);
-    }
-
-    /**
-     * @param isNewClearTask whether the task would be cleared as part of the operation.
-     * @return whether the requested task is disallowed to be launched.
-     */
-    boolean isLockTaskModeViolation(TaskRecord task, boolean isNewClearTask) {
-        if (isLockTaskModeViolationInternal(task, isNewClearTask)) {
-            showLockTaskToast();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @return the root task of the lock task.
-     */
-    TaskRecord getRootTask() {
-        if (mLockTaskModeTasks.isEmpty()) {
-            return null;
-        }
-        return mLockTaskModeTasks.get(0);
-    }
-
-    private boolean isLockTaskModeViolationInternal(TaskRecord task, boolean isNewClearTask) {
-        // TODO: Double check what's going on here. If the task is already in lock task mode, it's
-        // likely whitelisted, so will return false below.
-        if (isTaskLocked(task) && !isNewClearTask) {
-            // If the task is already at the top and won't be cleared, then allow the operation
-            return false;
-        }
-
-        // Allow recents activity if enabled by policy
-        if (task.isActivityTypeRecents() && isRecentsAllowed(task.userId)) {
-            return false;
-        }
-
-        // Allow emergency calling when the device is protected by a locked keyguard
-        if (isKeyguardAllowed(task.userId) && isEmergencyCallTask(task)) {
-            return false;
-        }
-
-        return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty());
-    }
-
-    private boolean isRecentsAllowed(int userId) {
-        return (getLockTaskFeaturesForUser(userId)
-                & DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW) != 0;
-    }
-
-    private boolean isKeyguardAllowed(int userId) {
-        return (getLockTaskFeaturesForUser(userId)
-                & DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0;
-    }
-
-    private boolean isEmergencyCallTask(TaskRecord task) {
-        final Intent intent = task.intent;
-        if (intent == null) {
-            return false;
-        }
-
-        // 1. The emergency keypad activity launched on top of the keyguard
-        if (EMERGENCY_DIALER_COMPONENT.equals(intent.getComponent())) {
-            return true;
-        }
-
-        // 2. The intent sent by the keypad, which is handled by Telephony
-        if (ACTION_CALL_EMERGENCY.equals(intent.getAction())) {
-            return true;
-        }
-
-        // 3. Telephony then starts the default package for making the call
-        final TelecomManager tm = getTelecomManager();
-        final String dialerPackage = tm != null ? tm.getSystemDialerPackage() : null;
-        if (dialerPackage != null && dialerPackage.equals(intent.getComponent().getPackageName())) {
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Stop the current lock task mode.
-     *
-     * This is called by {@link ActivityManagerService} and performs various checks before actually
-     * finishing the locked task.
-     *
-     * @param task the task that requested the end of lock task mode ({@code null} for quitting app
-     *             pinning mode)
-     * @param isSystemCaller indicates whether this request comes from the system via
-     *                       {@link ActivityTaskManagerService#stopSystemLockTaskMode()}. If
-     *                       {@code true}, it means the user intends to stop pinned mode through UI;
-     *                       otherwise, it's called by an app and we need to stop locked or pinned
-     *                       mode, subject to checks.
-     * @param callingUid the caller that requested the end of lock task mode.
-     * @throws IllegalArgumentException if the calling task is invalid (e.g., {@code null} or not in
-     *                                  foreground)
-     * @throws SecurityException if the caller is not authorized to stop the lock task mode, i.e. if
-     *                           they differ from the one that launched lock task mode.
-     */
-    void stopLockTaskMode(@Nullable TaskRecord task, boolean isSystemCaller, int callingUid) {
-        if (mLockTaskModeState == LOCK_TASK_MODE_NONE) {
-            return;
-        }
-
-        if (isSystemCaller) {
-            if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
-                clearLockedTasks("stopAppPinning");
-            } else {
-                Slog.e(TAG_LOCKTASK, "Attempted to stop LockTask with isSystemCaller=true");
-                showLockTaskToast();
-            }
-
-        } else {
-            // Ensure calling activity is not null
-            if (task == null) {
-                throw new IllegalArgumentException("can't stop LockTask for null task");
-            }
-
-            // Ensure the same caller for startLockTaskMode and stopLockTaskMode.
-            // It is possible lockTaskMode was started by the system process because
-            // android:lockTaskMode is set to a locking value in the application manifest
-            // instead of the app calling startLockTaskMode. In this case
-            // {@link TaskRecord.mLockTaskUid} will be 0, so we compare the callingUid to the
-            // {@link TaskRecord.effectiveUid} instead. Also caller with
-            // {@link MANAGE_ACTIVITY_STACKS} can stop any lock task.
-            if (callingUid != task.mLockTaskUid
-                    && (task.mLockTaskUid != 0 || callingUid != task.effectiveUid)) {
-                throw new SecurityException("Invalid uid, expected " + task.mLockTaskUid
-                        + " callingUid=" + callingUid + " effectiveUid=" + task.effectiveUid);
-            }
-
-            // We don't care if it's pinned or locked mode; this will stop it anyways.
-            clearLockedTask(task);
-        }
-    }
-
-    /**
-     * Clear all locked tasks and request the end of LockTask mode.
-     *
-     * This method is called by {@link UserController} when starting a new foreground user, and,
-     * unlike {@link #stopLockTaskMode(TaskRecord, boolean, int)}, it doesn't perform the checks.
-     */
-    void clearLockedTasks(String reason) {
-        if (DEBUG_LOCKTASK) Slog.i(TAG_LOCKTASK, "clearLockedTasks: " + reason);
-        if (!mLockTaskModeTasks.isEmpty()) {
-            clearLockedTask(mLockTaskModeTasks.get(0));
-        }
-    }
-
-    /**
-     * Clear one locked task from LockTask mode.
-     *
-     * If the requested task is the root task (see {@link #mLockTaskModeTasks}), then all locked
-     * tasks are cleared. Otherwise, only the requested task is cleared. LockTask mode is stopped
-     * when the last locked task is cleared.
-     *
-     * @param task the task to be cleared from LockTask mode.
-     */
-    void clearLockedTask(final TaskRecord task) {
-        if (task == null || mLockTaskModeTasks.isEmpty()) return;
-
-        if (task == mLockTaskModeTasks.get(0)) {
-            // We're removing the root task while there are other locked tasks. Therefore we should
-            // clear all locked tasks in reverse order.
-            for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx > 0; --taskNdx) {
-                clearLockedTask(mLockTaskModeTasks.get(taskNdx));
-            }
-        }
-
-        removeLockedTask(task);
-        if (mLockTaskModeTasks.isEmpty()) {
-            return;
-        }
-        task.performClearTaskLocked();
-        mSupervisor.resumeFocusedStacksTopActivitiesLocked();
-    }
-
-    /**
-     * Remove the given task from the locked task list. If this was the last task in the list,
-     * lock task mode is stopped.
-     */
-    private void removeLockedTask(final TaskRecord task) {
-        if (!mLockTaskModeTasks.remove(task)) {
-            return;
-        }
-        if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: removed " + task);
-        if (mLockTaskModeTasks.isEmpty()) {
-            if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task +
-                    " last task, reverting locktask mode. Callers=" + Debug.getCallers(3));
-            mHandler.post(() -> performStopLockTask(task.userId));
-        }
-    }
-
-    // This method should only be called on the handler thread
-    private void performStopLockTask(int userId) {
-        // When lock task ends, we enable the status bars.
-        try {
-            setStatusBarState(LOCK_TASK_MODE_NONE, userId);
-            setKeyguardState(LOCK_TASK_MODE_NONE, userId);
-            if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
-                lockKeyguardIfNeeded();
-            }
-            if (getDevicePolicyManager() != null) {
-                getDevicePolicyManager().notifyLockTaskModeChanged(false, null, userId);
-            }
-            if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
-                getStatusBarService().showPinningEnterExitToast(false /* entering */);
-            }
-            mWindowManager.onLockTaskStateChanged(LOCK_TASK_MODE_NONE);
-        } catch (RemoteException ex) {
-            throw new RuntimeException(ex);
-        } finally {
-            mLockTaskModeState = LOCK_TASK_MODE_NONE;
-        }
-    }
-
-    /**
-     * Show the lock task violation toast. Currently we only show toast for screen pinning mode, and
-     * no-op if the device is in locked mode.
-     */
-    void showLockTaskToast() {
-        if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
-            try {
-                getStatusBarService().showPinningEscapeToast();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to send pinning escape toast", e);
-            }
-        }
-    }
-
-    // Starting lock task
-
-    /**
-     * Method to start lock task mode on a given task.
-     *
-     * @param task the task that should be locked.
-     * @param isSystemCaller indicates whether this request was initiated by the system via
-     *                       {@link ActivityTaskManagerService#startSystemLockTaskMode(int)}. If
-     *                       {@code true}, this intends to start pinned mode; otherwise, we look
-     *                       at the calling task's mLockTaskAuth to decide which mode to start.
-     * @param callingUid the caller that requested the launch of lock task mode.
-     */
-    void startLockTaskMode(@NonNull TaskRecord task, boolean isSystemCaller, int callingUid) {
-        if (!isSystemCaller) {
-            task.mLockTaskUid = callingUid;
-            if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
-                // startLockTask() called by app, but app is not part of lock task whitelist. Show
-                // app pinning request. We will come back here with isSystemCaller true.
-                if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user");
-                StatusBarManagerInternal statusBarManager = LocalServices.getService(
-                        StatusBarManagerInternal.class);
-                if (statusBarManager != null) {
-                    statusBarManager.showScreenPinningRequest(task.taskId);
-                }
-                return;
-            }
-        }
-
-        // System can only initiate screen pinning, not full lock task mode
-        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
-                isSystemCaller ? "Locking pinned" : "Locking fully");
-        setLockTaskMode(task, isSystemCaller ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED,
-                "startLockTask", true);
-    }
-
-    /**
-     * Start lock task mode on the given task.
-     * @param lockTaskModeState whether fully locked or pinned mode.
-     * @param andResume whether the task should be brought to foreground as part of the operation.
-     */
-    private void setLockTaskMode(@NonNull TaskRecord task, int lockTaskModeState,
-                                 String reason, boolean andResume) {
-        // Should have already been checked, but do it again.
-        if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
-            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
-                    "setLockTaskMode: Can't lock due to auth");
-            return;
-        }
-        if (isLockTaskModeViolation(task)) {
-            Slog.e(TAG_LOCKTASK, "setLockTaskMode: Attempt to start an unauthorized lock task.");
-            return;
-        }
-
-        final Intent taskIntent = task.intent;
-        if (mLockTaskModeTasks.isEmpty() && taskIntent != null) {
-            mSupervisor.mRecentTasks.onLockTaskModeStateChanged(lockTaskModeState, task.userId);
-            // Start lock task on the handler thread
-            mHandler.post(() -> performStartLockTask(
-                    taskIntent.getComponent().getPackageName(),
-                    task.userId,
-                    lockTaskModeState));
-        }
-        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskMode: Locking to " + task +
-                " Callers=" + Debug.getCallers(4));
-
-        if (!mLockTaskModeTasks.contains(task)) {
-            mLockTaskModeTasks.add(task);
-        }
-
-        if (task.mLockTaskUid == -1) {
-            task.mLockTaskUid = task.effectiveUid;
-        }
-
-        if (andResume) {
-            mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
-                    lockTaskModeState != LOCK_TASK_MODE_NONE);
-            mSupervisor.resumeFocusedStacksTopActivitiesLocked();
-            mWindowManager.executeAppTransition();
-        } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
-            mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
-                    DEFAULT_DISPLAY, task.getStack(), true /* forceNonResizable */);
-        }
-    }
-
-    // This method should only be called on the handler thread
-    private void performStartLockTask(String packageName, int userId, int lockTaskModeState) {
-        // When lock task starts, we disable the status bars.
-        try {
-            if (lockTaskModeState == LOCK_TASK_MODE_PINNED) {
-                getStatusBarService().showPinningEnterExitToast(true /* entering */);
-            }
-            mWindowManager.onLockTaskStateChanged(lockTaskModeState);
-            mLockTaskModeState = lockTaskModeState;
-            setStatusBarState(lockTaskModeState, userId);
-            setKeyguardState(lockTaskModeState, userId);
-            if (getDevicePolicyManager() != null) {
-                getDevicePolicyManager().notifyLockTaskModeChanged(true, packageName, userId);
-            }
-        } catch (RemoteException ex) {
-            throw new RuntimeException(ex);
-        }
-    }
-
-    /**
-     * Update packages that are allowed to be launched in lock task mode.
-     * @param userId Which user this whitelist is associated with
-     * @param packages The whitelist of packages allowed in lock task mode
-     * @see #mLockTaskPackages
-     */
-    void updateLockTaskPackages(int userId, String[] packages) {
-        mLockTaskPackages.put(userId, packages);
-
-        boolean taskChanged = false;
-        for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord lockedTask = mLockTaskModeTasks.get(taskNdx);
-            final boolean wasWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
-                    || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
-            lockedTask.setLockTaskAuth();
-            final boolean isWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
-                    || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
-
-            if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED
-                    || lockedTask.userId != userId
-                    || !wasWhitelisted || isWhitelisted) {
-                continue;
-            }
-
-            // Terminate locked tasks that have recently lost whitelist authorization.
-            if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
-                    lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString());
-            removeLockedTask(lockedTask);
-            lockedTask.performClearTaskLocked();
-            taskChanged = true;
-        }
-
-        for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) {
-            mSupervisor.getChildAt(displayNdx).onLockTaskPackagesUpdated();
-        }
-
-        final ActivityRecord r = mSupervisor.topRunningActivityLocked();
-        final TaskRecord task = (r != null) ? r.getTask() : null;
-        if (mLockTaskModeTasks.isEmpty() && task!= null
-                && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
-            // This task must have just been authorized.
-            if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK,
-                    "onLockTaskPackagesUpdated: starting new locktask task=" + task);
-            setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated", false);
-            taskChanged = true;
-        }
-
-        if (taskChanged) {
-            mSupervisor.resumeFocusedStacksTopActivitiesLocked();
-        }
-    }
-
-    boolean isPackageWhitelisted(int userId, String pkg) {
-        if (pkg == null) {
-            return false;
-        }
-        String[] whitelist;
-        whitelist = mLockTaskPackages.get(userId);
-        if (whitelist == null) {
-            return false;
-        }
-        for (String whitelistedPkg : whitelist) {
-            if (pkg.equals(whitelistedPkg)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Update the UI features that are enabled for LockTask mode.
-     * @param userId Which user these feature flags are associated with
-     * @param flags Bitfield of feature flags
-     * @see DevicePolicyManager#setLockTaskFeatures(ComponentName, int)
-     */
-    void updateLockTaskFeatures(int userId, int flags) {
-        int oldFlags = getLockTaskFeaturesForUser(userId);
-        if (flags == oldFlags) {
-            return;
-        }
-
-        mLockTaskFeatures.put(userId, flags);
-        if (!mLockTaskModeTasks.isEmpty() && userId == mLockTaskModeTasks.get(0).userId) {
-            mHandler.post(() -> {
-                if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
-                    setStatusBarState(mLockTaskModeState, userId);
-                    setKeyguardState(mLockTaskModeState, userId);
-                }
-            });
-        }
-    }
-
-    /**
-     * Helper method for configuring the status bar disabled state.
-     * Should only be called on the handler thread to avoid race.
-     */
-    private void setStatusBarState(int lockTaskModeState, int userId) {
-        IStatusBarService statusBar = getStatusBarService();
-        if (statusBar == null) {
-            Slog.e(TAG, "Can't find StatusBarService");
-            return;
-        }
-
-        // Default state, when lockTaskModeState == LOCK_TASK_MODE_NONE
-        int flags1 = StatusBarManager.DISABLE_NONE;
-        int flags2 = StatusBarManager.DISABLE2_NONE;
-
-        if (lockTaskModeState == LOCK_TASK_MODE_PINNED) {
-            flags1 = STATUS_BAR_MASK_PINNED;
-
-        } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) {
-            int lockTaskFeatures = getLockTaskFeaturesForUser(userId);
-            Pair<Integer, Integer> statusBarFlags = getStatusBarDisableFlags(lockTaskFeatures);
-            flags1 = statusBarFlags.first;
-            flags2 = statusBarFlags.second;
-        }
-
-        try {
-            statusBar.disable(flags1, mToken, mContext.getPackageName());
-            statusBar.disable2(flags2, mToken, mContext.getPackageName());
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to set status bar flags", e);
-        }
-    }
-
-    /**
-     * Helper method for configuring the keyguard disabled state.
-     * Should only be called on the handler thread to avoid race.
-     */
-    private void setKeyguardState(int lockTaskModeState, int userId) {
-        if (lockTaskModeState == LOCK_TASK_MODE_NONE) {
-            mWindowManager.reenableKeyguard(mToken);
-
-        } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) {
-            if (isKeyguardAllowed(userId)) {
-                mWindowManager.reenableKeyguard(mToken);
-            } else {
-                // If keyguard is not secure and it is locked, dismiss the keyguard before
-                // disabling it, which avoids the platform to think the keyguard is still on.
-                if (mWindowManager.isKeyguardLocked() && !mWindowManager.isKeyguardSecure()) {
-                    mWindowManager.dismissKeyguard(new IKeyguardDismissCallback.Stub() {
-                        @Override
-                        public void onDismissError() throws RemoteException {
-                            Slog.i(TAG, "setKeyguardState: failed to dismiss keyguard");
-                        }
-
-                        @Override
-                        public void onDismissSucceeded() throws RemoteException {
-                            mHandler.post(
-                                    () -> mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG));
-                        }
-
-                        @Override
-                        public void onDismissCancelled() throws RemoteException {
-                            Slog.i(TAG, "setKeyguardState: dismiss cancelled");
-                        }
-                    }, null);
-                } else {
-                    mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
-                }
-            }
-
-        } else { // lockTaskModeState == LOCK_TASK_MODE_PINNED
-            mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
-        }
-    }
-
-    /**
-     * Helper method for locking the device immediately. This may be necessary when the device
-     * leaves the pinned mode.
-     */
-    private void lockKeyguardIfNeeded() {
-        try {
-            boolean shouldLockKeyguard = Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(),
-                    Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
-                    USER_CURRENT) != 0;
-            if (shouldLockKeyguard) {
-                mWindowManager.lockNow(null);
-                mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
-                getLockPatternUtils().requireCredentialEntry(USER_ALL);
-            }
-        } catch (Settings.SettingNotFoundException e) {
-            // No setting, don't lock.
-        }
-    }
-
-    /**
-     * Translates from LockTask feature flags to StatusBarManager disable and disable2 flags.
-     * @param lockTaskFlags Bitfield of flags as per
-     *                      {@link DevicePolicyManager#setLockTaskFeatures(ComponentName, int)}
-     * @return A {@link Pair} of {@link StatusBarManager#disable(int)} and
-     *         {@link StatusBarManager#disable2(int)} flags
-     */
-    @VisibleForTesting
-    Pair<Integer, Integer> getStatusBarDisableFlags(int lockTaskFlags) {
-        // Everything is disabled by default
-        int flags1 = StatusBarManager.DISABLE_MASK;
-        int flags2 = StatusBarManager.DISABLE2_MASK;
-        for (int i = STATUS_BAR_FLAG_MAP_LOCKED.size() - 1; i >= 0; i--) {
-            Pair<Integer, Integer> statusBarFlags = STATUS_BAR_FLAG_MAP_LOCKED.valueAt(i);
-            if ((STATUS_BAR_FLAG_MAP_LOCKED.keyAt(i) & lockTaskFlags) != 0) {
-                flags1 &= ~statusBarFlags.first;
-                flags2 &= ~statusBarFlags.second;
-            }
-        }
-        // Some flags are not used for LockTask purposes, so we mask them
-        flags1 &= STATUS_BAR_MASK_LOCKED;
-        return new Pair<>(flags1, flags2);
-    }
-
-    /**
-     * Gets the cached value of LockTask feature flags for a specific user.
-     */
-    private int getLockTaskFeaturesForUser(int userId) {
-        return mLockTaskFeatures.get(userId, DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
-    }
-
-    // Should only be called on the handler thread
-    @Nullable
-    private IStatusBarService getStatusBarService() {
-        if (mStatusBarService == null) {
-            mStatusBarService = IStatusBarService.Stub.asInterface(
-                    ServiceManager.checkService(STATUS_BAR_SERVICE));
-            if (mStatusBarService == null) {
-                Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
-            }
-        }
-        return mStatusBarService;
-    }
-
-    // Should only be called on the handler thread
-    @Nullable
-    private IDevicePolicyManager getDevicePolicyManager() {
-        if (mDevicePolicyManager == null) {
-            mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
-                    ServiceManager.checkService(DEVICE_POLICY_SERVICE));
-            if (mDevicePolicyManager == null) {
-                Slog.w(TAG, "warning: no DEVICE_POLICY_SERVICE");
-            }
-        }
-        return mDevicePolicyManager;
-    }
-
-    @NonNull
-    private LockPatternUtils getLockPatternUtils() {
-        if (mLockPatternUtils == null) {
-            // We don't preserve the LPU object to save memory
-            return new LockPatternUtils(mContext);
-        }
-        return mLockPatternUtils;
-    }
-
-    @Nullable
-    private TelecomManager getTelecomManager() {
-        if (mTelecomManager == null) {
-            // We don't preserve the TelecomManager object to save memory
-            return mContext.getSystemService(TelecomManager.class);
-        }
-        return mTelecomManager;
-    }
-
-    public void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + "LockTaskController");
-        prefix = prefix + "  ";
-        pw.println(prefix + "mLockTaskModeState=" + lockTaskModeToString());
-        pw.println(prefix + "mLockTaskModeTasks=");
-        for (int i = 0; i < mLockTaskModeTasks.size(); ++i) {
-            pw.println(prefix + "  #" + i + " " + mLockTaskModeTasks.get(i));
-        }
-        pw.println(prefix + "mLockTaskPackages (userId:packages)=");
-        for (int i = 0; i < mLockTaskPackages.size(); ++i) {
-            pw.println(prefix + "  u" + mLockTaskPackages.keyAt(i)
-                    + ":" + Arrays.toString(mLockTaskPackages.valueAt(i)));
-        }
-    }
-
-    private String lockTaskModeToString() {
-        switch (mLockTaskModeState) {
-            case LOCK_TASK_MODE_LOCKED:
-                return "LOCKED";
-            case LOCK_TASK_MODE_PINNED:
-                return "PINNED";
-            case LOCK_TASK_MODE_NONE:
-                return "NONE";
-            default: return "unknown=" + mLockTaskModeState;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index 85ee7e6..c978c13 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -16,13 +16,15 @@
 
 package com.android.server.am;
 
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.annotation.Nullable;
 import android.os.FileUtils;
 import android.os.SystemProperties;
+import android.system.Os;
+import android.system.OsConstants;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -36,7 +38,7 @@
 /**
  * Static utility methods related to {@link MemoryStat}.
  */
-final class MemoryStatUtil {
+public final class MemoryStatUtil {
     /**
      * Which native processes to create {@link MemoryStat} for.
      *
@@ -71,6 +73,7 @@
 
     static final int BYTES_IN_KILOBYTE = 1024;
     static final int PAGE_SIZE = 4096;
+    static final long JIFFY_NANOS = 1_000_000_000 / Os.sysconf(OsConstants._SC_CLK_TCK);
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
 
@@ -103,6 +106,7 @@
 
     private static final int PGFAULT_INDEX = 9;
     private static final int PGMAJFAULT_INDEX = 11;
+    private static final int START_TIME_INDEX = 21;
     private static final int RSS_IN_PAGES_INDEX = 23;
 
     private MemoryStatUtil() {}
@@ -114,7 +118,7 @@
      * Returns null if no stats can be read.
      */
     @Nullable
-    static MemoryStat readMemoryStatFromFilesystem(int uid, int pid) {
+    public static MemoryStat readMemoryStatFromFilesystem(int uid, int pid) {
         return hasMemcg() ? readMemoryStatFromMemcg(uid, pid) : readMemoryStatFromProcfs(pid);
     }
 
@@ -238,6 +242,7 @@
             memoryStat.pgfault = Long.parseLong(splits[PGFAULT_INDEX]);
             memoryStat.pgmajfault = Long.parseLong(splits[PGMAJFAULT_INDEX]);
             memoryStat.rssInBytes = Long.parseLong(splits[RSS_IN_PAGES_INDEX]) * PAGE_SIZE;
+            memoryStat.startTimeNanos = Long.parseLong(splits[START_TIME_INDEX]) * JIFFY_NANOS;
             return memoryStat;
         } catch (NumberFormatException e) {
             Slog.e(TAG, "Failed to parse value", e);
@@ -266,18 +271,20 @@
         return DEVICE_HAS_PER_APP_MEMCG;
     }
 
-    static final class MemoryStat {
+    public static final class MemoryStat {
         /** Number of page faults */
-        long pgfault;
+        public long pgfault;
         /** Number of major page faults */
-        long pgmajfault;
+        public long pgmajfault;
         /** Number of bytes of anonymous and swap cache memory */
-        long rssInBytes;
+        public long rssInBytes;
         /** Number of bytes of page cache memory */
-        long cacheInBytes;
+        public long cacheInBytes;
         /** Number of bytes of swap usage */
-        long swapInBytes;
+        public long swapInBytes;
         /** Number of bytes of peak anonymous and swap cache memory */
-        long rssHighWatermarkInBytes;
+        public long rssHighWatermarkInBytes;
+        /** Device time when the processes started. */
+        public long startTimeNanos;
     }
 }
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index a9c00a7..a5d4738 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -43,6 +43,7 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.SafeActivityOptions;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -84,8 +85,8 @@
         }
     }
 
-    PendingIntentRecord getIntentSender(int type, String packageName, int callingUid, int userId,
-            IBinder token, String resultWho, int requestCode, Intent[] intents,
+    public PendingIntentRecord getIntentSender(int type, String packageName, int callingUid,
+            int userId, IBinder token, String resultWho, int requestCode, Intent[] intents,
             String[] resolvedTypes, int flags, Bundle bOptions) {
         synchronized (mLock) {
             if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSender(): uid=" + callingUid);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 2dcddff..447243b 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -39,6 +39,7 @@
 
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.wm.SafeActivityOptions;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -50,7 +51,7 @@
     final PendingIntentController controller;
     final Key key;
     final int uid;
-    final WeakReference<PendingIntentRecord> ref;
+    public final WeakReference<PendingIntentRecord> ref;
     boolean sent = false;
     boolean canceled = false;
     private ArrayMap<IBinder, Long> whitelistDuration;
@@ -248,7 +249,7 @@
                 requiredPermission, null, null, 0, 0, 0, options);
     }
 
-    int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+    public int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken,
             IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
             String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) {
         if (intent != null) intent.setDefusable(true);
@@ -450,7 +451,7 @@
         }
     }
 
-    void dump(PrintWriter pw, String prefix) {
+    public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("uid="); pw.print(uid);
                 pw.print(" packageName="); pw.print(key.packageName);
                 pw.print(" type="); pw.print(key.typeName());
diff --git a/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java b/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java
deleted file mode 100644
index 877d896..0000000
--- a/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.os.Handler;
-import android.util.ArrayMap;
-import android.view.RemoteAnimationAdapter;
-
-/**
- * Registry to keep track of remote animations to be run for activity starts from a certain package.
- *
- * @see ActivityManagerService#registerRemoteAnimationForNextActivityStart
- */
-class PendingRemoteAnimationRegistry {
-
-    private static final long TIMEOUT_MS = 3000;
-
-    private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
-    private final Handler mHandler;
-    private final ActivityTaskManagerService mService;
-
-    PendingRemoteAnimationRegistry(ActivityTaskManagerService service, Handler handler) {
-        mService = service;
-        mHandler = handler;
-    }
-
-    /**
-     * Adds a remote animation to be run for all activity starts originating from a certain package.
-     */
-    void addPendingAnimation(String packageName, RemoteAnimationAdapter adapter) {
-        mEntries.put(packageName, new Entry(packageName, adapter));
-    }
-
-    /**
-     * Overrides the activity options with a registered remote animation for a certain calling
-     * package if such a remote animation is registered.
-     */
-    ActivityOptions overrideOptionsIfNeeded(String callingPackage,
-            @Nullable ActivityOptions options) {
-        final Entry entry = mEntries.get(callingPackage);
-        if (entry == null) {
-            return options;
-        }
-        if (options == null) {
-            options = ActivityOptions.makeRemoteAnimation(entry.adapter);
-        } else {
-            options.setRemoteAnimationAdapter(entry.adapter);
-        }
-        mEntries.remove(callingPackage);
-        return options;
-    }
-
-    private class Entry {
-        final String packageName;
-        final RemoteAnimationAdapter adapter;
-
-        Entry(String packageName, RemoteAnimationAdapter adapter) {
-            this.packageName = packageName;
-            this.adapter = adapter;
-            mHandler.postDelayed(() -> {
-                synchronized (mService.mGlobalLock) {
-                    final Entry entry = mEntries.get(packageName);
-                    if (entry == this) {
-                        mEntries.remove(packageName);
-                    }
-                }
-            }, TIMEOUT_MS);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/PersisterQueue.java b/services/core/java/com/android/server/am/PersisterQueue.java
deleted file mode 100644
index 60ea0fa..0000000
--- a/services/core/java/com/android/server/am/PersisterQueue.java
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import android.os.Process;
-import android.os.SystemClock;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.function.Predicate;
-
-/**
- * The common threading logic for persisters to use so that they can run in the same threads.
- * Methods in this class are synchronized on its instance, so caller could also synchronize on
- * its instance to perform modifications in items.
- */
-class PersisterQueue {
-    private static final String TAG = "PersisterQueue";
-    private static final boolean DEBUG = false;
-
-    /** When not flushing don't write out files faster than this */
-    private static final long INTER_WRITE_DELAY_MS = 500;
-
-    /**
-     * When not flushing delay this long before writing the first file out. This gives the next task
-     * being launched a chance to load its resources without this occupying IO bandwidth.
-     */
-    private static final long PRE_TASK_DELAY_MS = 3000;
-
-    /** The maximum number of entries to keep in the queue before draining it automatically. */
-    private static final int MAX_WRITE_QUEUE_LENGTH = 6;
-
-    /** Special value for mWriteTime to mean don't wait, just write */
-    private static final long FLUSH_QUEUE = -1;
-
-    /** An {@link WriteQueueItem} that doesn't do anything. Used to trigger {@link
-     * Listener#onPreProcessItem}. */
-    static final WriteQueueItem EMPTY_ITEM = () -> { };
-
-    private final long mInterWriteDelayMs;
-    private final long mPreTaskDelayMs;
-    private final LazyTaskWriterThread mLazyTaskWriterThread;
-    private final ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<>();
-
-    private final ArrayList<Listener> mListeners = new ArrayList<>();
-
-    /**
-     * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
-     * until the image queue is drained and all tasks needing persisting are written to disk. There
-     * is no delay between writes. == 0 We are Idle. Next writes will be delayed by
-     * #PRE_TASK_DELAY_MS. > 0 We are Actively writing. Next write will be at this time. Subsequent
-     * writes will be delayed by #INTER_WRITE_DELAY_MS.
-     */
-    private long mNextWriteTime = 0;
-
-    PersisterQueue() {
-        this(INTER_WRITE_DELAY_MS, PRE_TASK_DELAY_MS);
-    }
-
-    /** Used for tests to reduce waiting time. */
-    @VisibleForTesting
-    PersisterQueue(long interWriteDelayMs, long preTaskDelayMs) {
-        if (interWriteDelayMs < 0 || preTaskDelayMs < 0) {
-            throw new IllegalArgumentException("Both inter-write delay and pre-task delay need to"
-                    + "be non-negative. inter-write delay: " + interWriteDelayMs
-                    + "ms pre-task delay: " + preTaskDelayMs);
-        }
-        mInterWriteDelayMs = interWriteDelayMs;
-        mPreTaskDelayMs = preTaskDelayMs;
-        mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
-    }
-
-    synchronized void startPersisting() {
-        if (!mLazyTaskWriterThread.isAlive()) {
-            mLazyTaskWriterThread.start();
-        }
-    }
-
-    /** Stops persisting thread. Should only be used in tests. */
-    @VisibleForTesting
-    void stopPersisting() throws InterruptedException {
-        if (!mLazyTaskWriterThread.isAlive()) {
-            return;
-        }
-
-        synchronized (this) {
-            mLazyTaskWriterThread.interrupt();
-        }
-        mLazyTaskWriterThread.join();
-    }
-
-    synchronized void addItem(WriteQueueItem item, boolean flush) {
-        mWriteQueue.add(item);
-
-        if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
-            mNextWriteTime = FLUSH_QUEUE;
-        } else if (mNextWriteTime == 0) {
-            mNextWriteTime = SystemClock.uptimeMillis() + mPreTaskDelayMs;
-        }
-        notify();
-    }
-
-    synchronized <T extends WriteQueueItem> T findLastItem(Predicate<T> predicate, Class<T> clazz) {
-        for (int i = mWriteQueue.size() - 1; i >= 0; --i) {
-            WriteQueueItem writeQueueItem = mWriteQueue.get(i);
-            if (clazz.isInstance(writeQueueItem)) {
-                T item = clazz.cast(writeQueueItem);
-                if (predicate.test(item)) {
-                    return item;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    synchronized <T extends WriteQueueItem> void removeItems(Predicate<T> predicate,
-            Class<T> clazz) {
-        for (int i = mWriteQueue.size() - 1; i >= 0; --i) {
-            WriteQueueItem writeQueueItem = mWriteQueue.get(i);
-            if (clazz.isInstance(writeQueueItem)) {
-                T item = clazz.cast(writeQueueItem);
-                if (predicate.test(item)) {
-                    if (DEBUG) Slog.d(TAG, "Removing " + item + " from write queue.");
-                    mWriteQueue.remove(i);
-                }
-            }
-        }
-    }
-
-    synchronized void flush() {
-        mNextWriteTime = FLUSH_QUEUE;
-        notifyAll();
-        do {
-            try {
-                wait();
-            } catch (InterruptedException e) {
-            }
-        } while (mNextWriteTime == FLUSH_QUEUE);
-    }
-
-    void yieldIfQueueTooDeep() {
-        boolean stall = false;
-        synchronized (this) {
-            if (mNextWriteTime == FLUSH_QUEUE) {
-                stall = true;
-            }
-        }
-        if (stall) {
-            Thread.yield();
-        }
-    }
-
-    void addListener(Listener listener) {
-        mListeners.add(listener);
-    }
-
-    private void processNextItem() throws InterruptedException {
-        // This part is extracted into a method so that the GC can clearly see the end of the
-        // scope of the variable 'item'.  If this part was in the loop in LazyTaskWriterThread, the
-        // last item it processed would always "leak".
-        // See https://b.corp.google.com/issues/64438652#comment7
-
-        // If mNextWriteTime, then don't delay between each call to saveToXml().
-        final WriteQueueItem item;
-        synchronized (this) {
-            if (mNextWriteTime != FLUSH_QUEUE) {
-                // The next write we don't have to wait so long.
-                mNextWriteTime = SystemClock.uptimeMillis() + mInterWriteDelayMs;
-                if (DEBUG) {
-                    Slog.d(TAG, "Next write time may be in " + mInterWriteDelayMs
-                            + " msec. (" + mNextWriteTime + ")");
-                }
-            }
-
-            while (mWriteQueue.isEmpty()) {
-                if (mNextWriteTime != 0) {
-                    mNextWriteTime = 0; // idle.
-                    notify(); // May need to wake up flush().
-                }
-                // Make sure we exit this thread correctly when interrupted before going to
-                // indefinite wait.
-                if (Thread.currentThread().isInterrupted()) {
-                    throw new InterruptedException();
-                }
-                if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
-                wait();
-                // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
-                // from now.
-            }
-            item = mWriteQueue.remove(0);
-
-            long now = SystemClock.uptimeMillis();
-            if (DEBUG) {
-                Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" + mNextWriteTime
-                        + " mWriteQueue.size=" + mWriteQueue.size());
-            }
-            while (now < mNextWriteTime) {
-                if (DEBUG) {
-                    Slog.d(TAG, "LazyTaskWriter: waiting " + (mNextWriteTime - now));
-                }
-                wait(mNextWriteTime - now);
-                now = SystemClock.uptimeMillis();
-            }
-
-            // Got something to do.
-        }
-
-        item.process();
-    }
-
-    interface WriteQueueItem {
-        void process();
-    }
-
-    interface Listener {
-        /**
-         * Called before {@link PersisterQueue} tries to process next item.
-         *
-         * Note if the queue is empty, this callback will be called before the indefinite wait. This
-         * will be called once when {@link PersisterQueue} starts the internal thread before the
-         * indefinite wait.
-         *
-         * This callback is called w/o locking the instance of {@link PersisterQueue}.
-         *
-         * @param queueEmpty {@code true} if the queue is empty, which indicates {@link
-         * PersisterQueue} is likely to enter indefinite wait; or {@code false} if there is still
-         * item to process.
-         */
-        void onPreProcessItem(boolean queueEmpty);
-    }
-
-    private class LazyTaskWriterThread extends Thread {
-
-        private LazyTaskWriterThread(String name) {
-            super(name);
-        }
-
-        @Override
-        public void run() {
-            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-            try {
-                while (true) {
-                    final boolean probablyDone;
-                    synchronized (PersisterQueue.this) {
-                        probablyDone = mWriteQueue.isEmpty();
-                    }
-
-                    for (int i = mListeners.size() - 1; i >= 0; --i) {
-                        mListeners.get(i).onPreProcessItem(probablyDone);
-                    }
-
-                    processNextItem();
-                }
-            } catch (InterruptedException e) {
-                Slog.e(TAG, "Persister thread is exiting. Should never happen in prod, but"
-                        + "it's OK in tests.");
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
deleted file mode 100644
index edc6e53..0000000
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-
-import android.app.RemoteAction;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-
-import com.android.server.wm.PinnedStackWindowController;
-import com.android.server.wm.PinnedStackWindowListener;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * State and management of the pinned stack of activities.
- */
-class PinnedActivityStack extends ActivityStack<PinnedStackWindowController>
-        implements PinnedStackWindowListener {
-
-    PinnedActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
-            boolean onTop) {
-        super(display, stackId, supervisor, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, onTop);
-    }
-
-    @Override
-    PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
-            Rect outBounds) {
-        return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
-                mStackSupervisor.mWindowManager);
-    }
-
-    Rect getDefaultPictureInPictureBounds(float aspectRatio) {
-        return getWindowContainerController().getPictureInPictureBounds(aspectRatio,
-                null /* currentStackBounds */);
-    }
-
-    void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration,
-            boolean fromFullscreen) {
-        if (skipResizeAnimation(toBounds == null /* toFullscreen */)) {
-            mService.moveTasksToFullscreenStack(mStackId, true /* onTop */);
-        } else {
-            getWindowContainerController().animateResizePinnedStack(toBounds, sourceHintBounds,
-                    animationDuration, fromFullscreen);
-        }
-    }
-
-    private boolean skipResizeAnimation(boolean toFullscreen) {
-        if (!toFullscreen) {
-            return false;
-        }
-        final Configuration parentConfig = getParent().getConfiguration();
-        final ActivityRecord top = topRunningNonOverlayTaskActivity();
-        return top != null && !top.isConfigurationCompatible(parentConfig);
-    }
-
-    void setPictureInPictureAspectRatio(float aspectRatio) {
-        getWindowContainerController().setPictureInPictureAspectRatio(aspectRatio);
-    }
-
-    void setPictureInPictureActions(List<RemoteAction> actions) {
-        getWindowContainerController().setPictureInPictureActions(actions);
-    }
-
-    boolean isAnimatingBoundsToFullscreen() {
-        return getWindowContainerController().isAnimatingBoundsToFullscreen();
-    }
-
-    /**
-     * Returns whether to defer the scheduling of the multi-window mode.
-     */
-    boolean deferScheduleMultiWindowModeChanged() {
-        // For the pinned stack, the deferring of the multi-window mode changed is tied to the
-        // transition animation into picture-in-picture, and is called once the animation completes,
-        // or is interrupted in a way that would leave the stack in a non-fullscreen state.
-        // @see BoundsAnimationController
-        // @see BoundsAnimationControllerTests
-        return mWindowContainerController.deferScheduleMultiWindowModeChanged();
-    }
-
-    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
-            boolean forceUpdate) {
-        // It is guaranteed that the activities requiring the update will be in the pinned stack at
-        // this point (either reparented before the animation into PiP, or before reparenting after
-        // the animation out of PiP)
-        synchronized (mService) {
-            ArrayList<TaskRecord> tasks = getAllTasks();
-            for (int i = 0; i < tasks.size(); i++ ) {
-                mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
-                        forceUpdate);
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 805b979b..93c8391 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -92,6 +92,7 @@
 import com.android.server.ServiceThread;
 import com.android.server.Watchdog;
 import com.android.server.pm.dex.DexManager;
+import com.android.server.wm.ActivityServiceConnectionsHolder;
 import com.android.server.wm.WindowManagerService;
 
 import dalvik.system.VMRuntime;
@@ -99,14 +100,22 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
+import libcore.io.IoUtils;
+
 /**
  * Activity manager code dealing with processes.
+ *
+ * Method naming convention:
+ * <ul>
+ * <li> Methods suffixed with "LS" should be called within the {@link #sLmkdSocketLock} lock.
+ * </ul>
  */
 public final class ProcessList {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
@@ -204,7 +213,7 @@
     // Activity manager's version of Process.THREAD_GROUP_DEFAULT
     static final int SCHED_GROUP_DEFAULT = 2;
     // Activity manager's version of Process.THREAD_GROUP_TOP_APP
-    static final int SCHED_GROUP_TOP_APP = 3;
+    public static final int SCHED_GROUP_TOP_APP = 3;
     // Activity manager's version of Process.THREAD_GROUP_TOP_APP
     // Disambiguate between actual top app and processes bound to the top app
     static final int SCHED_GROUP_TOP_APP_BOUND = 4;
@@ -229,10 +238,12 @@
     // LMK_PROCPRIO <pid> <uid> <prio>
     // LMK_PROCREMOVE <pid>
     // LMK_PROCPURGE
+    // LMK_GETKILLCNT
     static final byte LMK_TARGET = 0;
     static final byte LMK_PROCPRIO = 1;
     static final byte LMK_PROCREMOVE = 2;
     static final byte LMK_PROCPURGE = 3;
+    static final byte LMK_GETKILLCNT = 4;
 
     ActivityManagerService mService = null;
 
@@ -268,9 +279,17 @@
 
     private boolean mHaveDisplaySize;
 
+    private static Object sLmkdSocketLock = new Object();
+
+    @GuardedBy("sLmkdSocketLock")
     private static LocalSocket sLmkdSocket;
+
+    @GuardedBy("sLmkdSocketLock")
     private static OutputStream sLmkdOutputStream;
 
+    @GuardedBy("sLmkdSocketLock")
+    private static InputStream sLmkdInputStream;
+
     /**
      * Temporary to avoid allocations.  Protected by main lock.
      */
@@ -505,7 +524,7 @@
                 buf.putInt(mOomAdj[i]);
             }
 
-            writeLmkd(buf);
+            writeLmkd(buf, null);
             SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
         }
         // GB: 2048,3072,4096,6144,7168,8192
@@ -978,7 +997,7 @@
         buf.putInt(pid);
         buf.putInt(uid);
         buf.putInt(amt);
-        writeLmkd(buf);
+        writeLmkd(buf, null);
         long now = SystemClock.elapsedRealtime();
         if ((now-start) > 250) {
             Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid
@@ -997,16 +1016,38 @@
         ByteBuffer buf = ByteBuffer.allocate(4 * 2);
         buf.putInt(LMK_PROCREMOVE);
         buf.putInt(pid);
-        writeLmkd(buf);
+        writeLmkd(buf, null);
     }
 
-    private static boolean openLmkdSocket() {
+    /*
+     * {@hide}
+     */
+    public static final Integer getLmkdKillCount(int min_oom_adj, int max_oom_adj) {
+        ByteBuffer buf = ByteBuffer.allocate(4 * 3);
+        ByteBuffer repl = ByteBuffer.allocate(4 * 2);
+        buf.putInt(LMK_GETKILLCNT);
+        buf.putInt(min_oom_adj);
+        buf.putInt(max_oom_adj);
+        if (writeLmkd(buf, repl)) {
+            int i = repl.getInt();
+            if (i != LMK_GETKILLCNT) {
+                Slog.e("ActivityManager", "Failed to get kill count, code mismatch");
+                return null;
+            }
+            return new Integer(repl.getInt());
+        }
+        return null;
+    }
+
+    @GuardedBy("sLmkdSocketLock")
+    private static boolean openLmkdSocketLS() {
         try {
             sLmkdSocket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET);
             sLmkdSocket.connect(
                 new LocalSocketAddress("lmkd",
                         LocalSocketAddress.Namespace.RESERVED));
             sLmkdOutputStream = sLmkdSocket.getOutputStream();
+            sLmkdInputStream = sLmkdSocket.getInputStream();
         } catch (IOException ex) {
             Slog.w(TAG, "lowmemorykiller daemon socket open failed");
             sLmkdSocket = null;
@@ -1017,47 +1058,63 @@
     }
 
     // Never call directly, use writeLmkd() instead
-    private static boolean writeLmkdCommand(ByteBuffer buf) {
+    @GuardedBy("sLmkdSocketLock")
+    private static boolean writeLmkdCommandLS(ByteBuffer buf) {
         try {
             sLmkdOutputStream.write(buf.array(), 0, buf.position());
         } catch (IOException ex) {
             Slog.w(TAG, "Error writing to lowmemorykiller socket");
-
-            try {
-                sLmkdSocket.close();
-            } catch (IOException ex2) {
-            }
-
+            IoUtils.closeQuietly(sLmkdSocket);
             sLmkdSocket = null;
             return false;
         }
         return true;
     }
 
-    private static void writeLmkd(ByteBuffer buf) {
-
-        for (int i = 0; i < 3; i++) {
-            if (sLmkdSocket == null) {
-                if (openLmkdSocket() == false) {
-                    try {
-                        Thread.sleep(1000);
-                    } catch (InterruptedException ie) {
-                    }
-                    continue;
-                }
-
-                // Purge any previously registered pids
-                ByteBuffer purge_buf = ByteBuffer.allocate(4);
-                purge_buf.putInt(LMK_PROCPURGE);
-                if (writeLmkdCommand(purge_buf) == false) {
-                    // Write failed, skip the rest and retry
-                    continue;
-                }
+    // Never call directly, use writeLmkd() instead
+    @GuardedBy("sLmkdSocketLock")
+    private static boolean readLmkdReplyLS(ByteBuffer buf) {
+        int len;
+        try {
+            len = sLmkdInputStream.read(buf.array(), 0, buf.array().length);
+            if (len == buf.array().length) {
+                return true;
             }
-            if (writeLmkdCommand(buf)) {
-                return;
+        } catch (IOException ex) {
+            Slog.w(TAG, "Error reading from lowmemorykiller socket");
+        }
+
+        IoUtils.closeQuietly(sLmkdSocket);
+        sLmkdSocket = null;
+        return false;
+    }
+
+    private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
+        synchronized (sLmkdSocketLock) {
+            for (int i = 0; i < 3; i++) {
+                if (sLmkdSocket == null) {
+                    if (openLmkdSocketLS() == false) {
+                        try {
+                            Thread.sleep(1000);
+                        } catch (InterruptedException ie) {
+                        }
+                        continue;
+                    }
+
+                    // Purge any previously registered pids
+                    ByteBuffer purge_buf = ByteBuffer.allocate(4);
+                    purge_buf.putInt(LMK_PROCPURGE);
+                    if (writeLmkdCommandLS(purge_buf) == false) {
+                        // Write failed, skip the rest and retry
+                        continue;
+                    }
+                }
+                if (writeLmkdCommandLS(buf) && (repl == null || readLmkdReplyLS(repl))) {
+                    return true;
+                }
             }
         }
+        return false;
     }
 
     static void killProcessGroup(int uid, int pid) {
@@ -1982,8 +2039,7 @@
             StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
                     StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
         }
-        final ProcessRecord r = new ProcessRecord(mService, info, proc, uid,
-                mService.getGlobalConfiguration());
+        final ProcessRecord r = new ProcessRecord(mService, info, proc, uid);
 
         if (!mService.mBooted && !mService.mBooting
                 && userId == UserHandle.USER_SYSTEM
@@ -2468,9 +2524,13 @@
                     currApp.importanceReasonImportance =
                             ActivityManager.RunningAppProcessInfo.procStateToImportance(
                                     app.adjSourceProcState);
-                } else if (app.adjSource instanceof ActivityRecord) {
-                    ActivityRecord r = (ActivityRecord)app.adjSource;
-                    if (r.app != null) currApp.importanceReasonPid = r.app.getPid();
+                } else if (app.adjSource instanceof ActivityServiceConnectionsHolder) {
+                    final ActivityServiceConnectionsHolder r =
+                            (ActivityServiceConnectionsHolder) app.adjSource;
+                    final int pid = r.getActivityPid();
+                    if (pid != -1) {
+                        currApp.importanceReasonPid = pid;
+                    }
                 }
                 if (app.adjTarget instanceof ComponentName) {
                     currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index fa7a08b..8a3f139 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -33,7 +33,6 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.Debug;
 import android.os.IBinder;
@@ -58,7 +57,8 @@
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.ProcessCpuTracker;
-import com.android.server.Watchdog;
+import com.android.server.wm.WindowProcessController;
+import com.android.server.wm.WindowProcessListener;
 
 import java.io.File;
 import java.io.IOException;
@@ -535,7 +535,7 @@
     }
 
     ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName,
-            int _uid, Configuration config) {
+            int _uid) {
         mService = _service;
         info = _info;
         isolated = _info.uid != _uid;
@@ -549,7 +549,7 @@
         removed = false;
         lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis();
         mWindowProcessController = new WindowProcessController(
-                mService.mActivityTaskManager, info, processName, uid, userId, this, this, config);
+                mService.mActivityTaskManager, info, processName, uid, userId, this, this);
         pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
     }
 
@@ -1123,8 +1123,9 @@
     @Override
     public void clearProfilerIfNeeded() {
         synchronized (mService) {
-            if (mService.mProfileProc == null || mService.mProfilerInfo == null
-                    || mService.mProfileProc != this) {
+            if (mService.mProfileData.getProfileProc() == null
+                    || mService.mProfileData.getProfilerInfo() == null
+                    || mService.mProfileData.getProfileProc() != this) {
                 return;
             }
             mService.clearProfilerLocked();
@@ -1198,32 +1199,15 @@
     }
 
     @Override
-    public ProfilerInfo onStartActivity(int topProcessState) {
+    public void onStartActivity(int topProcessState, boolean setProfileProc) {
         synchronized (mService) {
-            ProfilerInfo profilerInfo = null;
-            if (mService.mProfileApp != null && mService.mProfileApp.equals(processName)) {
-                if (mService.mProfileProc == null || mService.mProfileProc == this) {
-                    mService.mProfileProc = this;
-                    final ProfilerInfo profilerInfoSvc = mService.mProfilerInfo;
-                    if (profilerInfoSvc != null && profilerInfoSvc.profileFile != null) {
-                        if (profilerInfoSvc.profileFd != null) {
-                            try {
-                                profilerInfoSvc.profileFd = profilerInfoSvc.profileFd.dup();
-                            } catch (IOException e) {
-                                profilerInfoSvc.closeFd();
-                            }
-                        }
-
-                        profilerInfo = new ProfilerInfo(profilerInfoSvc);
-                    }
-                }
+            if (setProfileProc) {
+                mService.mProfileData.setProfileProc(this);
             }
 
             hasShownUi = true;
             setPendingUiClean(true);
             forceProcessStateUpTo(topProcessState);
-
-            return profilerInfo;
         }
     }
 
@@ -1244,19 +1228,7 @@
         ArrayList<Integer> firstPids = new ArrayList<>(5);
         SparseArray<Boolean> lastPids = new SparseArray<>(20);
 
-        if (mService.mActivityTaskManager.mController != null) {
-            try {
-                // 0 == continue, -1 = kill process immediately
-                int res = mService.mActivityTaskManager.mController.appEarlyNotResponding(
-                        processName, pid, annotation);
-                if (res < 0 && pid != MY_PID) {
-                    kill("anr", true);
-                }
-            } catch (RemoteException e) {
-                mService.mActivityTaskManager.mController = null;
-                Watchdog.getInstance().setActivityController(null);
-            }
-        }
+        mWindowProcessController.appEarlyNotResponding(annotation, () -> kill("anr", true));
 
         long anrTime = SystemClock.uptimeMillis();
         if (ActivityManagerService.MONITOR_CPU_USAGE) {
@@ -1271,7 +1243,7 @@
 
         synchronized (mService) {
             // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
-            if (mService.mActivityTaskManager.mShuttingDown) {
+            if (mService.mAtmInternal.isShuttingDown()) {
                 Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
                 return;
             } else if (isNotResponding()) {
@@ -1412,25 +1384,13 @@
         mService.addErrorToDropBox("anr", this, processName, activityShortComponentName,
                 parentShortComponentName, parentPr, annotation, cpuInfo, tracesFile, null);
 
-        if (mService.mActivityTaskManager.mController != null) {
-            try {
-                // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
-                int res = mService.mActivityTaskManager.mController.appNotResponding(
-                        processName, pid, info.toString());
-                if (res != 0) {
-                    if (res < 0 && pid != MY_PID) {
-                        kill("anr", true);
-                    } else {
-                        synchronized (mService) {
-                            mService.mServices.scheduleServiceTimeoutLocked(this);
-                        }
+        if (mWindowProcessController.appNotResponding(info.toString(), () -> kill("anr", true),
+                () -> {
+                    synchronized (mService) {
+                        mService.mServices.scheduleServiceTimeoutLocked(this);
                     }
-                    return;
-                }
-            } catch (RemoteException e) {
-                mService.mActivityTaskManager.mController = null;
-                Watchdog.getInstance().setActivityController(null);
-            }
+                })) {
+            return;
         }
 
         synchronized (mService) {
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
deleted file mode 100644
index 57f939f..0000000
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ /dev/null
@@ -1,1620 +0,0 @@
-/*
- * Copyright (C) 2014 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.server.am;
-
-import static android.app.ActivityManager.FLAG_AND_UNLOCKED;
-import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.os.Process.SYSTEM_UID;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.AppGlobals;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.am.TaskRecord.TaskActivitiesReport;
-
-import com.google.android.collect.Sets;
-
-import java.io.File;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Class for managing the recent tasks list. The list is ordered by most recent (index 0) to the
- * least recent.
- *
- * The trimming logic can be boiled down to the following.  For recent task list with a number of
- * tasks, the visible tasks are an interleaving subset of tasks that would normally be presented to
- * the user. Non-visible tasks are not considered for trimming. Of the visible tasks, only a
- * sub-range are presented to the user, based on the device type, last task active time, or other
- * task state. Tasks that are not in the visible range and are not returnable from the SystemUI
- * (considering the back stack) are considered trimmable. If the device does not support recent
- * tasks, then trimming is completely disabled.
- *
- * eg.
- * L = [TTTTTTTTTTTTTTTTTTTTTTTTTT] // list of tasks
- *     [VVV  VV   VVVV  V V V     ] // Visible tasks
- *     [RRR  RR   XXXX  X X X     ] // Visible range tasks, eg. if the device only shows 5 tasks,
- *                                  // 'X' tasks are trimmed.
- */
-class RecentTasks {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_ATM;
-    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
-    private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
-
-    private static final int DEFAULT_INITIAL_CAPACITY = 5;
-
-    // Whether or not to move all affiliated tasks to the front when one of the tasks is launched
-    private static final boolean MOVE_AFFILIATED_TASKS_TO_FRONT = false;
-
-    // Comparator to sort by taskId
-    private static final Comparator<TaskRecord> TASK_ID_COMPARATOR =
-            (lhs, rhs) -> rhs.taskId - lhs.taskId;
-
-    // Placeholder variables to keep track of activities/apps that are no longer avialble while
-    // iterating through the recents list
-    private static final ActivityInfo NO_ACTIVITY_INFO_TOKEN = new ActivityInfo();
-    private static final ApplicationInfo NO_APPLICATION_INFO_TOKEN = new ApplicationInfo();
-
-    /**
-     * Callbacks made when manipulating the list.
-     */
-    interface Callbacks {
-        /**
-         * Called when a task is added to the recent tasks list.
-         */
-        void onRecentTaskAdded(TaskRecord task);
-
-        /**
-         * Called when a task is removed from the recent tasks list.
-         */
-        void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess);
-    }
-
-    /**
-     * Save recent tasks information across reboots.
-     */
-    private final TaskPersister mTaskPersister;
-    private final ActivityTaskManagerService mService;
-    private final ActivityStackSupervisor mSupervisor;
-
-    /**
-     * Keeps track of the static recents package/component which is granted additional permissions
-     * to call recents-related APIs.
-     */
-    private int mRecentsUid = -1;
-    private ComponentName mRecentsComponent = null;
-
-    /**
-     * Mapping of user id -> whether recent tasks have been loaded for that user.
-     */
-    private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(
-            DEFAULT_INITIAL_CAPACITY);
-
-    /**
-     * Stores for each user task ids that are taken by tasks residing in persistent storage. These
-     * tasks may or may not currently be in memory.
-     */
-    private final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>(
-            DEFAULT_INITIAL_CAPACITY);
-
-    // List of all active recent tasks
-    private final ArrayList<TaskRecord> mTasks = new ArrayList<>();
-    private final ArrayList<Callbacks> mCallbacks = new ArrayList<>();
-
-    // These values are generally loaded from resources, but can be set dynamically in the tests
-    private boolean mHasVisibleRecentTasks;
-    private int mGlobalMaxNumTasks;
-    private int mMinNumVisibleTasks;
-    private int mMaxNumVisibleTasks;
-    private long mActiveTasksSessionDurationMs;
-
-    // Mainly to avoid object recreation on multiple calls.
-    private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<>();
-    private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>();
-    private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>();
-    private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray();
-    private final TaskActivitiesReport mTmpReport = new TaskActivitiesReport();
-
-    @VisibleForTesting
-    RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) {
-        mService = service;
-        mSupervisor = mService.mStackSupervisor;
-        mTaskPersister = taskPersister;
-        mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
-        mHasVisibleRecentTasks = true;
-    }
-
-    RecentTasks(ActivityTaskManagerService service, ActivityStackSupervisor stackSupervisor) {
-        final File systemDir = Environment.getDataSystemDirectory();
-        final Resources res = service.mContext.getResources();
-        mService = service;
-        mSupervisor = mService.mStackSupervisor;
-        mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this);
-        mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
-        mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
-        loadParametersFromResources(res);
-    }
-
-    @VisibleForTesting
-    void setParameters(int minNumVisibleTasks, int maxNumVisibleTasks,
-            long activeSessionDurationMs) {
-        mMinNumVisibleTasks = minNumVisibleTasks;
-        mMaxNumVisibleTasks = maxNumVisibleTasks;
-        mActiveTasksSessionDurationMs = activeSessionDurationMs;
-    }
-
-    @VisibleForTesting
-    void setGlobalMaxNumTasks(int globalMaxNumTasks) {
-        mGlobalMaxNumTasks = globalMaxNumTasks;
-    }
-
-    /**
-     * Loads the parameters from the system resources.
-     */
-    @VisibleForTesting
-    void loadParametersFromResources(Resources res) {
-        if (ActivityManager.isLowRamDeviceStatic()) {
-            mMinNumVisibleTasks = res.getInteger(
-                    com.android.internal.R.integer.config_minNumVisibleRecentTasks_lowRam);
-            mMaxNumVisibleTasks = res.getInteger(
-                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks_lowRam);
-        } else if (SystemProperties.getBoolean("ro.recents.grid", false)) {
-            mMinNumVisibleTasks = res.getInteger(
-                    com.android.internal.R.integer.config_minNumVisibleRecentTasks_grid);
-            mMaxNumVisibleTasks = res.getInteger(
-                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks_grid);
-        } else {
-            mMinNumVisibleTasks = res.getInteger(
-                    com.android.internal.R.integer.config_minNumVisibleRecentTasks);
-            mMaxNumVisibleTasks = res.getInteger(
-                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks);
-        }
-        final int sessionDurationHrs = res.getInteger(
-                com.android.internal.R.integer.config_activeTaskDurationHours);
-        mActiveTasksSessionDurationMs = (sessionDurationHrs > 0)
-                ? TimeUnit.HOURS.toMillis(sessionDurationHrs)
-                : -1;
-    }
-
-    /**
-     * Loads the static recents component.  This is called after the system is ready, but before
-     * any dependent services (like SystemUI) is started.
-     */
-    void loadRecentsComponent(Resources res) {
-        final String rawRecentsComponent = res.getString(
-                com.android.internal.R.string.config_recentsComponentName);
-        if (TextUtils.isEmpty(rawRecentsComponent)) {
-            return;
-        }
-
-        final ComponentName cn = ComponentName.unflattenFromString(rawRecentsComponent);
-        if (cn != null) {
-            try {
-                final ApplicationInfo appInfo = AppGlobals.getPackageManager()
-                        .getApplicationInfo(cn.getPackageName(), 0, mService.mContext.getUserId());
-                if (appInfo != null) {
-                    mRecentsUid = appInfo.uid;
-                    mRecentsComponent = cn;
-                }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Could not load application info for recents component: " + cn);
-            }
-        }
-    }
-
-    /**
-     * @return whether the current caller has the same uid as the recents component.
-     */
-    boolean isCallerRecents(int callingUid) {
-        return UserHandle.isSameApp(callingUid, mRecentsUid);
-    }
-
-    /**
-     * @return whether the given component is the recents component and shares the same uid as the
-     *         recents component.
-     */
-    boolean isRecentsComponent(ComponentName cn, int uid) {
-        return cn.equals(mRecentsComponent) && UserHandle.isSameApp(uid, mRecentsUid);
-    }
-
-    /**
-     * @return whether the home app is also the active handler of recent tasks.
-     */
-    boolean isRecentsComponentHomeActivity(int userId) {
-        final ComponentName defaultHomeActivity = mService.getPackageManagerInternalLocked()
-                .getDefaultHomeActivity(userId);
-        return defaultHomeActivity != null && mRecentsComponent != null &&
-                defaultHomeActivity.getPackageName().equals(mRecentsComponent.getPackageName());
-    }
-
-    /**
-     * @return the recents component.
-     */
-    ComponentName getRecentsComponent() {
-        return mRecentsComponent;
-    }
-
-    /**
-     * @return the uid for the recents component.
-     */
-    int getRecentsComponentUid() {
-        return mRecentsUid;
-    }
-
-    void registerCallback(Callbacks callback) {
-        mCallbacks.add(callback);
-    }
-
-    void unregisterCallback(Callbacks callback) {
-        mCallbacks.remove(callback);
-    }
-
-    private void notifyTaskAdded(TaskRecord task) {
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            mCallbacks.get(i).onRecentTaskAdded(task);
-        }
-    }
-
-    private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed, killProcess);
-        }
-    }
-
-    /**
-     * Loads the persistent recentTasks for {@code userId} into this list from persistent storage.
-     * Does nothing if they are already loaded.
-     *
-     * @param userId the user Id
-     */
-    void loadUserRecentsLocked(int userId) {
-        if (mUsersWithRecentsLoaded.get(userId)) {
-            // User already loaded, return early
-            return;
-        }
-
-        // Load the task ids if not loaded.
-        loadPersistedTaskIdsForUserLocked(userId);
-
-        // Check if any tasks are added before recents is loaded
-        final SparseBooleanArray preaddedTasks = new SparseBooleanArray();
-        for (final TaskRecord task : mTasks) {
-            if (task.userId == userId && shouldPersistTaskLocked(task)) {
-                preaddedTasks.put(task.taskId, true);
-            }
-        }
-
-        Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
-        mTasks.addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks));
-        cleanupLocked(userId);
-        mUsersWithRecentsLoaded.put(userId, true);
-
-        // If we have tasks added before loading recents, we need to update persistent task IDs.
-        if (preaddedTasks.size() > 0) {
-            syncPersistentTaskIdsLocked();
-        }
-    }
-
-    private void loadPersistedTaskIdsForUserLocked(int userId) {
-        // An empty instead of a null set here means that no persistent taskIds were present
-        // on file when we loaded them.
-        if (mPersistedTaskIds.get(userId) == null) {
-            mPersistedTaskIds.put(userId, mTaskPersister.loadPersistedTaskIdsForUser(userId));
-            Slog.i(TAG, "Loaded persisted task ids for user " + userId);
-        }
-    }
-
-    /**
-     * @return whether the {@param taskId} is currently in use for the given user.
-     */
-    boolean containsTaskId(int taskId, int userId) {
-        loadPersistedTaskIdsForUserLocked(userId);
-        return mPersistedTaskIds.get(userId).get(taskId);
-    }
-
-    /**
-     * @return all the task ids for the user with the given {@param userId}.
-     */
-    SparseBooleanArray getTaskIdsForUser(int userId) {
-        loadPersistedTaskIdsForUserLocked(userId);
-        return mPersistedTaskIds.get(userId);
-    }
-
-    /**
-     * Kicks off the task persister to write any pending tasks to disk.
-     */
-    void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
-        final ActivityStack stack = task != null ? task.getStack() : null;
-        if (stack != null && stack.isHomeOrRecentsStack()) {
-            // Never persist the home or recents stack.
-            return;
-        }
-        syncPersistentTaskIdsLocked();
-        mTaskPersister.wakeup(task, flush);
-    }
-
-    private void syncPersistentTaskIdsLocked() {
-        for (int i = mPersistedTaskIds.size() - 1; i >= 0; i--) {
-            int userId = mPersistedTaskIds.keyAt(i);
-            if (mUsersWithRecentsLoaded.get(userId)) {
-                // Recents are loaded only after task ids are loaded. Therefore, the set of taskids
-                // referenced here should not be null.
-                mPersistedTaskIds.valueAt(i).clear();
-            }
-        }
-        for (int i = mTasks.size() - 1; i >= 0; i--) {
-            final TaskRecord task = mTasks.get(i);
-            if (shouldPersistTaskLocked(task)) {
-                // Set of persisted taskIds for task.userId should not be null here
-                // TODO Investigate why it can happen. For now initialize with an empty set
-                if (mPersistedTaskIds.get(task.userId) == null) {
-                    Slog.wtf(TAG, "No task ids found for userId " + task.userId + ". task=" + task
-                            + " mPersistedTaskIds=" + mPersistedTaskIds);
-                    mPersistedTaskIds.put(task.userId, new SparseBooleanArray());
-                }
-                mPersistedTaskIds.get(task.userId).put(task.taskId, true);
-            }
-        }
-    }
-
-    private static boolean shouldPersistTaskLocked(TaskRecord task) {
-        final ActivityStack stack = task.getStack();
-        return task.isPersistable && (stack == null || !stack.isHomeOrRecentsStack());
-    }
-
-    void onSystemReadyLocked() {
-        loadRecentsComponent(mService.mContext.getResources());
-        mTasks.clear();
-        mTaskPersister.onSystemReady();
-    }
-
-    Bitmap getTaskDescriptionIcon(String path) {
-        return mTaskPersister.getTaskDescriptionIcon(path);
-    }
-
-    void saveImage(Bitmap image, String path) {
-        mTaskPersister.saveImage(image, path);
-    }
-
-    void flush() {
-        synchronized (mService.mGlobalLock) {
-            syncPersistentTaskIdsLocked();
-        }
-        mTaskPersister.flush();
-    }
-
-    /**
-     * Returns all userIds for which recents from persistent storage are loaded into this list.
-     *
-     * @return an array of userIds.
-     */
-    int[] usersWithRecentsLoadedLocked() {
-        int[] usersWithRecentsLoaded = new int[mUsersWithRecentsLoaded.size()];
-        int len = 0;
-        for (int i = 0; i < usersWithRecentsLoaded.length; i++) {
-            int userId = mUsersWithRecentsLoaded.keyAt(i);
-            if (mUsersWithRecentsLoaded.valueAt(i)) {
-                usersWithRecentsLoaded[len++] = userId;
-            }
-        }
-        if (len < usersWithRecentsLoaded.length) {
-            // should never happen.
-            return Arrays.copyOf(usersWithRecentsLoaded, len);
-        }
-        return usersWithRecentsLoaded;
-    }
-
-    /**
-     * Removes recent tasks and any other state kept in memory for the passed in user. Does not
-     * touch the information present on persistent storage.
-     *
-     * @param userId the id of the user
-     */
-    void unloadUserDataFromMemoryLocked(int userId) {
-        if (mUsersWithRecentsLoaded.get(userId)) {
-            Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
-            mUsersWithRecentsLoaded.delete(userId);
-            removeTasksForUserLocked(userId);
-        }
-        mPersistedTaskIds.delete(userId);
-        mTaskPersister.unloadUserDataFromMemory(userId);
-    }
-
-    /** Remove recent tasks for a user. */
-    private void removeTasksForUserLocked(int userId) {
-        if(userId <= 0) {
-            Slog.i(TAG, "Can't remove recent task on user " + userId);
-            return;
-        }
-
-        for (int i = mTasks.size() - 1; i >= 0; --i) {
-            TaskRecord tr = mTasks.get(i);
-            if (tr.userId == userId) {
-                if(DEBUG_TASKS) Slog.i(TAG_TASKS,
-                        "remove RecentTask " + tr + " when finishing user" + userId);
-                remove(tr);
-            }
-        }
-    }
-
-    void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) {
-        final Set<String> packageNames = Sets.newHashSet(packages);
-        for (int i = mTasks.size() - 1; i >= 0; --i) {
-            final TaskRecord tr = mTasks.get(i);
-            if (tr.realActivity != null
-                    && packageNames.contains(tr.realActivity.getPackageName())
-                    && tr.userId == userId
-                    && tr.realActivitySuspended != suspended) {
-               tr.realActivitySuspended = suspended;
-               if (suspended) {
-                   mSupervisor.removeTaskByIdLocked(tr.taskId, false,
-                           REMOVE_FROM_RECENTS, "suspended-package");
-               }
-               notifyTaskPersisterLocked(tr, false);
-            }
-        }
-    }
-
-    void onLockTaskModeStateChanged(int lockTaskModeState, int userId) {
-        if (lockTaskModeState != ActivityManager.LOCK_TASK_MODE_LOCKED) {
-            return;
-        }
-        for (int i = mTasks.size() - 1; i >= 0; --i) {
-            final TaskRecord tr = mTasks.get(i);
-            if (tr.userId == userId && !mService.getLockTaskController().isTaskWhitelisted(tr)) {
-                remove(tr);
-            }
-        }
-    }
-
-    void removeTasksByPackageName(String packageName, int userId) {
-        for (int i = mTasks.size() - 1; i >= 0; --i) {
-            final TaskRecord tr = mTasks.get(i);
-            final String taskPackageName =
-                    tr.getBaseIntent().getComponent().getPackageName();
-            if (tr.userId != userId) continue;
-            if (!taskPackageName.equals(packageName)) continue;
-
-            mSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS,
-                    "remove-package-task");
-        }
-    }
-
-    void removeAllVisibleTasks() {
-        for (int i = mTasks.size() - 1; i >= 0; --i) {
-            final TaskRecord tr = mTasks.get(i);
-            if (isVisibleRecentTask(tr)) {
-                mTasks.remove(i);
-                notifyTaskRemoved(tr, true /* wasTrimmed */, true /* killProcess */);
-            }
-        }
-    }
-
-    void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses,
-            int userId) {
-        for (int i = mTasks.size() - 1; i >= 0; --i) {
-            final TaskRecord tr = mTasks.get(i);
-            if (userId != UserHandle.USER_ALL && tr.userId != userId) {
-                continue;
-            }
-
-            ComponentName cn = tr.intent != null ? tr.intent.getComponent() : null;
-            final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
-                    && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
-            if (sameComponent) {
-                mSupervisor.removeTaskByIdLocked(tr.taskId, false,
-                        REMOVE_FROM_RECENTS, "disabled-package");
-            }
-        }
-    }
-
-    /**
-     * Update the recent tasks lists: make sure tasks should still be here (their
-     * applications / activities still exist), update their availability, fix-up ordering
-     * of affiliations.
-     */
-    void cleanupLocked(int userId) {
-        int recentsCount = mTasks.size();
-        if (recentsCount == 0) {
-            // Happens when called from the packagemanager broadcast before boot,
-            // or just any empty list.
-            return;
-        }
-
-        // Clear the temp lists
-        mTmpAvailActCache.clear();
-        mTmpAvailAppCache.clear();
-
-        final IPackageManager pm = AppGlobals.getPackageManager();
-        for (int i = recentsCount - 1; i >= 0; i--) {
-            final TaskRecord task = mTasks.get(i);
-            if (userId != UserHandle.USER_ALL && task.userId != userId) {
-                // Only look at tasks for the user ID of interest.
-                continue;
-            }
-            if (task.autoRemoveRecents && task.getTopActivity() == null) {
-                // This situation is broken, and we should just get rid of it now.
-                remove(task);
-                Slog.w(TAG, "Removing auto-remove without activity: " + task);
-                continue;
-            }
-            // Check whether this activity is currently available.
-            if (task.realActivity != null) {
-                ActivityInfo ai = mTmpAvailActCache.get(task.realActivity);
-                if (ai == null) {
-                    try {
-                        // At this first cut, we're only interested in
-                        // activities that are fully runnable based on
-                        // current system state.
-                        ai = pm.getActivityInfo(task.realActivity,
-                                PackageManager.MATCH_DEBUG_TRIAGED_MISSING
-                                        | ActivityManagerService.STOCK_PM_FLAGS, userId);
-                    } catch (RemoteException e) {
-                        // Will never happen.
-                        continue;
-                    }
-                    if (ai == null) {
-                        ai = NO_ACTIVITY_INFO_TOKEN;
-                    }
-                    mTmpAvailActCache.put(task.realActivity, ai);
-                }
-                if (ai == NO_ACTIVITY_INFO_TOKEN) {
-                    // This could be either because the activity no longer exists, or the
-                    // app is temporarily gone. For the former we want to remove the recents
-                    // entry; for the latter we want to mark it as unavailable.
-                    ApplicationInfo app = mTmpAvailAppCache
-                            .get(task.realActivity.getPackageName());
-                    if (app == null) {
-                        try {
-                            app = pm.getApplicationInfo(task.realActivity.getPackageName(),
-                                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
-                        } catch (RemoteException e) {
-                            // Will never happen.
-                            continue;
-                        }
-                        if (app == null) {
-                            app = NO_APPLICATION_INFO_TOKEN;
-                        }
-                        mTmpAvailAppCache.put(task.realActivity.getPackageName(), app);
-                    }
-                    if (app == NO_APPLICATION_INFO_TOKEN
-                            || (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
-                        // Doesn't exist any more! Good-bye.
-                        remove(task);
-                        Slog.w(TAG, "Removing no longer valid recent: " + task);
-                        continue;
-                    } else {
-                        // Otherwise just not available for now.
-                        if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
-                                "Making recent unavailable: " + task);
-                        task.isAvailable = false;
-                    }
-                } else {
-                    if (!ai.enabled || !ai.applicationInfo.enabled
-                            || (ai.applicationInfo.flags
-                                    & ApplicationInfo.FLAG_INSTALLED) == 0) {
-                        if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
-                                "Making recent unavailable: " + task
-                                        + " (enabled=" + ai.enabled + "/"
-                                        + ai.applicationInfo.enabled
-                                        + " flags="
-                                        + Integer.toHexString(ai.applicationInfo.flags)
-                                        + ")");
-                        task.isAvailable = false;
-                    } else {
-                        if (DEBUG_RECENTS && !task.isAvailable) Slog.d(TAG_RECENTS,
-                                "Making recent available: " + task);
-                        task.isAvailable = true;
-                    }
-                }
-            }
-        }
-
-        // Verify the affiliate chain for each task.
-        int i = 0;
-        recentsCount = mTasks.size();
-        while (i < recentsCount) {
-            i = processNextAffiliateChainLocked(i);
-        }
-        // recent tasks are now in sorted, affiliated order.
-    }
-
-    /**
-     * @return whether the given {@param task} can be added to the list without causing another
-     * task to be trimmed as a result of that add.
-     */
-    private boolean canAddTaskWithoutTrim(TaskRecord task) {
-        return findRemoveIndexForAddTask(task) == -1;
-    }
-
-    /**
-     * Returns the list of {@link ActivityManager.AppTask}s.
-     */
-    ArrayList<IBinder> getAppTasksList(int callingUid, String callingPackage) {
-        final ArrayList<IBinder> list = new ArrayList<>();
-        final int size = mTasks.size();
-        for (int i = 0; i < size; i++) {
-            final TaskRecord tr = mTasks.get(i);
-            // Skip tasks that do not match the caller.  We don't need to verify
-            // callingPackage, because we are also limiting to callingUid and know
-            // that will limit to the correct security sandbox.
-            if (tr.effectiveUid != callingUid) {
-                continue;
-            }
-            Intent intent = tr.getBaseIntent();
-            if (intent == null || !callingPackage.equals(intent.getComponent().getPackageName())) {
-                continue;
-            }
-            AppTaskImpl taskImpl = new AppTaskImpl(mService, tr.taskId, callingUid);
-            list.add(taskImpl.asBinder());
-        }
-        return list;
-    }
-
-    @VisibleForTesting
-    Set<Integer> getProfileIds(int userId) {
-        Set<Integer> userIds = new ArraySet<>();
-        final List<UserInfo> profiles = mService.getUserManager().getProfiles(userId,
-                false /* enabledOnly */);
-        for (int i = profiles.size() - 1; i >= 0; --i) {
-            userIds.add(profiles.get(i).id);
-        }
-        return userIds;
-    }
-
-    @VisibleForTesting
-    UserInfo getUserInfo(int userId) {
-        return mService.getUserManager().getUserInfo(userId);
-    }
-
-    @VisibleForTesting
-    int[] getCurrentProfileIds() {
-        return mService.mAmInternal.getCurrentProfileIds();
-    }
-
-    /**
-     * @return the list of recent tasks for presentation.
-     */
-    ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
-            boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
-        return new ParceledListSlice<>(getRecentTasksImpl(maxNum, flags, getTasksAllowed,
-                getDetailedTasks, userId, callingUid));
-    }
-
-
-    /**
-     * @return the list of recent tasks for presentation.
-     */
-    ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags,
-            boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
-        final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
-
-        if (!mService.mAmInternal.isUserRunning(userId, FLAG_AND_UNLOCKED)) {
-            Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
-            return new ArrayList<>();
-        }
-        loadUserRecentsLocked(userId);
-
-        final Set<Integer> includedUsers = getProfileIds(userId);
-        includedUsers.add(Integer.valueOf(userId));
-
-        final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>();
-        final int size = mTasks.size();
-        int numVisibleTasks = 0;
-        for (int i = 0; i < size; i++) {
-            final TaskRecord tr = mTasks.get(i);
-
-            if (isVisibleRecentTask(tr)) {
-                numVisibleTasks++;
-                if (isInVisibleRange(tr, numVisibleTasks)) {
-                    // Fall through
-                } else {
-                    // Not in visible range
-                    continue;
-                }
-            } else {
-                // Not visible
-                continue;
-            }
-
-            // Skip remaining tasks once we reach the requested size
-            if (res.size() >= maxNum) {
-                continue;
-            }
-
-            // Only add calling user or related users recent tasks
-            if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
-                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
-                continue;
-            }
-
-            if (tr.realActivitySuspended) {
-                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
-                continue;
-            }
-
-            // Return the entry if desired by the caller.  We always return
-            // the first entry, because callers always expect this to be the
-            // foreground app.  We may filter others if the caller has
-            // not supplied RECENT_WITH_EXCLUDED and there is some reason
-            // we should exclude the entry.
-
-            if (i == 0
-                    || withExcluded
-                    || (tr.intent == null)
-                    || ((tr.intent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
-                    == 0)) {
-                if (!getTasksAllowed) {
-                    // If the caller doesn't have the GET_TASKS permission, then only
-                    // allow them to see a small subset of tasks -- their own and home.
-                    if (!tr.isActivityTypeHome() && tr.effectiveUid != callingUid) {
-                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
-                        continue;
-                    }
-                }
-                if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
-                    // Don't include auto remove tasks that are finished or finishing.
-                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                            "Skipping, auto-remove without activity: " + tr);
-                    continue;
-                }
-                if ((flags & RECENT_IGNORE_UNAVAILABLE) != 0 && !tr.isAvailable) {
-                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                            "Skipping, unavail real act: " + tr);
-                    continue;
-                }
-
-                if (!tr.mUserSetupComplete) {
-                    // Don't include task launched while user is not done setting-up.
-                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                            "Skipping, user setup not complete: " + tr);
-                    continue;
-                }
-
-                final ActivityManager.RecentTaskInfo rti = createRecentTaskInfo(tr);
-                if (!getDetailedTasks) {
-                    rti.baseIntent.replaceExtras((Bundle)null);
-                }
-
-                res.add(rti);
-            }
-        }
-        return res;
-    }
-
-    /**
-     * @return the list of persistable task ids.
-     */
-    void getPersistableTaskIds(ArraySet<Integer> persistentTaskIds) {
-        final int size = mTasks.size();
-        for (int i = 0; i < size; i++) {
-            final TaskRecord task = mTasks.get(i);
-            if (TaskPersister.DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task
-                    + " persistable=" + task.isPersistable);
-            final ActivityStack stack = task.getStack();
-            if ((task.isPersistable || task.inRecents)
-                    && (stack == null || !stack.isHomeOrRecentsStack())) {
-                if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
-                persistentTaskIds.add(task.taskId);
-            } else {
-                if (TaskPersister.DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task="
-                        + task);
-            }
-        }
-    }
-
-    @VisibleForTesting
-    ArrayList<TaskRecord> getRawTasks() {
-        return mTasks;
-    }
-
-    /**
-     * @return ids of tasks that are presented in Recents UI.
-     */
-    SparseBooleanArray getRecentTaskIds() {
-        final SparseBooleanArray res = new SparseBooleanArray();
-        final int size = mTasks.size();
-        int numVisibleTasks = 0;
-        for (int i = 0; i < size; i++) {
-            final TaskRecord tr = mTasks.get(i);
-            if (isVisibleRecentTask(tr)) {
-                numVisibleTasks++;
-                if (isInVisibleRange(tr, numVisibleTasks)) {
-                    res.put(tr.taskId, true);
-                }
-            }
-        }
-        return res;
-    }
-
-    /**
-     * @return the task in the task list with the given {@param id} if one exists.
-     */
-    TaskRecord getTask(int id) {
-        final int recentsCount = mTasks.size();
-        for (int i = 0; i < recentsCount; i++) {
-            TaskRecord tr = mTasks.get(i);
-            if (tr.taskId == id) {
-                return tr;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Add a new task to the recent tasks list.
-     */
-    void add(TaskRecord task) {
-        if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task);
-
-        final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId
-                || task.mNextAffiliateTaskId != INVALID_TASK_ID
-                || task.mPrevAffiliateTaskId != INVALID_TASK_ID;
-
-        int recentsCount = mTasks.size();
-        // Quick case: never add voice sessions.
-        // TODO: VI what about if it's just an activity?
-        // Probably nothing to do here
-        if (task.voiceSession != null) {
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                    "addRecent: not adding voice interaction " + task);
-            return;
-        }
-        // Another quick case: check if the top-most recent task is the same.
-        if (!isAffiliated && recentsCount > 0 && mTasks.get(0) == task) {
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: already at top: " + task);
-            return;
-        }
-        // Another quick case: check if this is part of a set of affiliated
-        // tasks that are at the top.
-        if (isAffiliated && recentsCount > 0 && task.inRecents
-                && task.mAffiliatedTaskId == mTasks.get(0).mAffiliatedTaskId) {
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + mTasks.get(0)
-                    + " at top when adding " + task);
-            return;
-        }
-
-        boolean needAffiliationFix = false;
-
-        // Slightly less quick case: the task is already in recents, so all we need
-        // to do is move it.
-        if (task.inRecents) {
-            int taskIndex = mTasks.indexOf(task);
-            if (taskIndex >= 0) {
-                if (!isAffiliated || !MOVE_AFFILIATED_TASKS_TO_FRONT) {
-                    // Simple case: this is not an affiliated task, so we just move it to the front.
-                    mTasks.remove(taskIndex);
-                    mTasks.add(0, task);
-                    notifyTaskPersisterLocked(task, false);
-                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
-                            + " from " + taskIndex);
-                    return;
-                } else {
-                    // More complicated: need to keep all affiliated tasks together.
-                    if (moveAffiliatedTasksToFront(task, taskIndex)) {
-                        // All went well.
-                        return;
-                    }
-
-                    // Uh oh...  something bad in the affiliation chain, try to rebuild
-                    // everything and then go through our general path of adding a new task.
-                    needAffiliationFix = true;
-                }
-            } else {
-                Slog.wtf(TAG, "Task with inRecent not in recents: " + task);
-                needAffiliationFix = true;
-            }
-        }
-
-        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task);
-        removeForAddTask(task);
-
-        task.inRecents = true;
-        if (!isAffiliated || needAffiliationFix) {
-            // If this is a simple non-affiliated task, or we had some failure trying to
-            // handle it as part of an affilated task, then just place it at the top.
-            mTasks.add(0, task);
-            notifyTaskAdded(task);
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task);
-        } else if (isAffiliated) {
-            // If this is a new affiliated task, then move all of the affiliated tasks
-            // to the front and insert this new one.
-            TaskRecord other = task.mNextAffiliate;
-            if (other == null) {
-                other = task.mPrevAffiliate;
-            }
-            if (other != null) {
-                int otherIndex = mTasks.indexOf(other);
-                if (otherIndex >= 0) {
-                    // Insert new task at appropriate location.
-                    int taskIndex;
-                    if (other == task.mNextAffiliate) {
-                        // We found the index of our next affiliation, which is who is
-                        // before us in the list, so add after that point.
-                        taskIndex = otherIndex+1;
-                    } else {
-                        // We found the index of our previous affiliation, which is who is
-                        // after us in the list, so add at their position.
-                        taskIndex = otherIndex;
-                    }
-                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                            "addRecent: new affiliated task added at " + taskIndex + ": " + task);
-                    mTasks.add(taskIndex, task);
-                    notifyTaskAdded(task);
-
-                    // Now move everything to the front.
-                    if (moveAffiliatedTasksToFront(task, taskIndex)) {
-                        // All went well.
-                        return;
-                    }
-
-                    // Uh oh...  something bad in the affiliation chain, try to rebuild
-                    // everything and then go through our general path of adding a new task.
-                    needAffiliationFix = true;
-                } else {
-                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                            "addRecent: couldn't find other affiliation " + other);
-                    needAffiliationFix = true;
-                }
-            } else {
-                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-                        "addRecent: adding affiliated task without next/prev:" + task);
-                needAffiliationFix = true;
-            }
-        }
-
-        if (needAffiliationFix) {
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations");
-            cleanupLocked(task.userId);
-        }
-
-        // Trim the set of tasks to the active set
-        trimInactiveRecentTasks();
-    }
-
-    /**
-     * Add the task to the bottom if possible.
-     */
-    boolean addToBottom(TaskRecord task) {
-        if (!canAddTaskWithoutTrim(task)) {
-            // Adding this task would cause the task to be removed (since it's appended at
-            // the bottom and would be trimmed) so just return now
-            return false;
-        }
-
-        add(task);
-        return true;
-    }
-
-    /**
-     * Remove a task from the recent tasks list.
-     */
-    void remove(TaskRecord task) {
-        mTasks.remove(task);
-        notifyTaskRemoved(task, false /* wasTrimmed */, false /* killProcess */);
-    }
-
-    /**
-     * Trims the recents task list to the global max number of recents.
-     */
-    private void trimInactiveRecentTasks() {
-        int recentsCount = mTasks.size();
-
-        // Remove from the end of the list until we reach the max number of recents
-        while (recentsCount > mGlobalMaxNumTasks) {
-            final TaskRecord tr = mTasks.remove(recentsCount - 1);
-            notifyTaskRemoved(tr, true /* wasTrimmed */, false /* killProcess */);
-            recentsCount--;
-            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming over max-recents task=" + tr
-                    + " max=" + mGlobalMaxNumTasks);
-        }
-
-        // Remove any tasks that belong to currently quiet profiles
-        final int[] profileUserIds = getCurrentProfileIds();
-        mTmpQuietProfileUserIds.clear();
-        for (int userId : profileUserIds) {
-            final UserInfo userInfo = getUserInfo(userId);
-            if (userInfo != null && userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) {
-                mTmpQuietProfileUserIds.put(userId, true);
-            }
-            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "User: " + userInfo
-                    + " quiet=" + mTmpQuietProfileUserIds.get(userId));
-        }
-
-        // Remove any inactive tasks, calculate the latest set of visible tasks
-        int numVisibleTasks = 0;
-        for (int i = 0; i < mTasks.size();) {
-            final TaskRecord task = mTasks.get(i);
-
-            if (isActiveRecentTask(task, mTmpQuietProfileUserIds)) {
-                if (!mHasVisibleRecentTasks) {
-                    // Keep all active tasks if visible recent tasks is not supported
-                    i++;
-                    continue;
-                }
-
-                if (!isVisibleRecentTask(task)) {
-                    // Keep all active-but-invisible tasks
-                    i++;
-                    continue;
-                } else {
-                    numVisibleTasks++;
-                    if (isInVisibleRange(task, numVisibleTasks) || !isTrimmable(task)) {
-                        // Keep visible tasks in range
-                        i++;
-                        continue;
-                    } else {
-                        // Fall through to trim visible tasks that are no longer in range and
-                        // trimmable
-                        if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
-                                "Trimming out-of-range visible task=" + task);
-                    }
-                }
-            } else {
-                // Fall through to trim inactive tasks
-                if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming inactive task=" + task);
-            }
-
-            // Task is no longer active, trim it from the list
-            mTasks.remove(task);
-            notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */);
-            notifyTaskPersisterLocked(task, false /* flush */);
-        }
-    }
-
-    /**
-     * @return whether the given task should be considered active.
-     */
-    private boolean isActiveRecentTask(TaskRecord task, SparseBooleanArray quietProfileUserIds) {
-        if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isActiveRecentTask: task=" + task
-                + " globalMax=" + mGlobalMaxNumTasks);
-
-        if (quietProfileUserIds.get(task.userId)) {
-            // Quiet profile user's tasks are never active
-            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\tisQuietProfileTask=true");
-            return false;
-        }
-
-        if (task.mAffiliatedTaskId != INVALID_TASK_ID && task.mAffiliatedTaskId != task.taskId) {
-            // Keep the task active if its affiliated task is also active
-            final TaskRecord affiliatedTask = getTask(task.mAffiliatedTaskId);
-            if (affiliatedTask != null) {
-                if (!isActiveRecentTask(affiliatedTask, quietProfileUserIds)) {
-                    if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
-                            "\taffiliatedWithTask=" + affiliatedTask + " is not active");
-                    return false;
-                }
-            }
-        }
-
-        // All other tasks are considered active
-        return true;
-    }
-
-    /**
-     * @return whether the given active task should be presented to the user through SystemUI.
-     */
-    private boolean isVisibleRecentTask(TaskRecord task) {
-        if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isVisibleRecentTask: task=" + task
-                + " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks
-                + " sessionDuration=" + mActiveTasksSessionDurationMs
-                + " inactiveDuration=" + task.getInactiveDuration()
-                + " activityType=" + task.getActivityType()
-                + " windowingMode=" + task.getWindowingMode()
-                + " intentFlags=" + task.getBaseIntent().getFlags());
-
-        switch (task.getActivityType()) {
-            case ACTIVITY_TYPE_HOME:
-            case ACTIVITY_TYPE_RECENTS:
-                // Ignore certain activity types completely
-                return false;
-            case ACTIVITY_TYPE_ASSISTANT:
-                // Ignore assistant that chose to be excluded from Recents, even if it's a top
-                // task.
-                if ((task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
-                        == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) {
-                    return false;
-                }
-        }
-
-        // Ignore certain windowing modes
-        switch (task.getWindowingMode()) {
-            case WINDOWING_MODE_PINNED:
-                return false;
-            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-                if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().topTask());
-                final ActivityStack stack = task.getStack();
-                if (stack != null && stack.topTask() == task) {
-                    // Only the non-top task of the primary split screen mode is visible
-                    return false;
-                }
-        }
-
-        // If we're in lock task mode, ignore the root task
-        if (task == mService.getLockTaskController().getRootTask()) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * @return whether the given visible task is within the policy range.
-     */
-    private boolean isInVisibleRange(TaskRecord task, int numVisibleTasks) {
-        // Keep the last most task even if it is excluded from recents
-        final boolean isExcludeFromRecents =
-                (task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
-                        == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
-        if (isExcludeFromRecents) {
-            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true");
-            return numVisibleTasks == 1;
-        }
-
-        if (mMinNumVisibleTasks >= 0 && numVisibleTasks <= mMinNumVisibleTasks) {
-            // Always keep up to the min number of recent tasks, after that fall through to the
-            // checks below
-            return true;
-        }
-
-        if (mMaxNumVisibleTasks >= 0) {
-            // Always keep up to the max number of recent tasks, but return false afterwards
-            return numVisibleTasks <= mMaxNumVisibleTasks;
-        }
-
-        if (mActiveTasksSessionDurationMs > 0) {
-            // Keep the task if the inactive time is within the session window, this check must come
-            // after the checks for the min/max visible task range
-            if (task.getInactiveDuration() <= mActiveTasksSessionDurationMs) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * @return whether the given task can be trimmed even if it is outside the visible range.
-     */
-    protected boolean isTrimmable(TaskRecord task) {
-        final ActivityStack stack = task.getStack();
-
-        // No stack for task, just trim it
-        if (stack == null) {
-            return true;
-        }
-
-        // Ignore tasks from different displays
-        // TODO (b/115289124): No Recents on non-default displays.
-        if (stack.mDisplayId != DEFAULT_DISPLAY) {
-            return false;
-        }
-
-        // Trim tasks that are in stacks that are behind the home stack
-        final ActivityDisplay display = stack.getDisplay();
-        return display.getIndexOf(stack) < display.getIndexOf(display.getHomeStack());
-    }
-
-    /**
-     * If needed, remove oldest existing entries in recents that are for the same kind
-     * of task as the given one.
-     */
-    private void removeForAddTask(TaskRecord task) {
-        final int removeIndex = findRemoveIndexForAddTask(task);
-        if (removeIndex == -1) {
-            // Nothing to trim
-            return;
-        }
-
-        // There is a similar task that will be removed for the addition of {@param task}, but it
-        // can be the same task, and if so, the task will be re-added in add(), so skip the
-        // callbacks here.
-        final TaskRecord removedTask = mTasks.remove(removeIndex);
-        if (removedTask != task) {
-            notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */);
-            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask
-                    + " for addition of task=" + task);
-        }
-        notifyTaskPersisterLocked(removedTask, false /* flush */);
-    }
-
-    /**
-     * Find the task that would be removed if the given {@param task} is added to the recent tasks
-     * list (if any).
-     */
-    private int findRemoveIndexForAddTask(TaskRecord task) {
-        final int recentsCount = mTasks.size();
-        final Intent intent = task.intent;
-        final boolean document = intent != null && intent.isDocument();
-        int maxRecents = task.maxRecents - 1;
-        for (int i = 0; i < recentsCount; i++) {
-            final TaskRecord tr = mTasks.get(i);
-            if (task != tr) {
-                if (!hasCompatibleActivityTypeAndWindowingMode(task, tr)
-                        || task.userId != tr.userId) {
-                    continue;
-                }
-                final Intent trIntent = tr.intent;
-                final boolean sameAffinity =
-                        task.affinity != null && task.affinity.equals(tr.affinity);
-                final boolean sameIntent = intent != null && intent.filterEquals(trIntent);
-                boolean multiTasksAllowed = false;
-                final int flags = intent.getFlags();
-                if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0
-                        && (flags & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
-                    multiTasksAllowed = true;
-                }
-                final boolean trIsDocument = trIntent != null && trIntent.isDocument();
-                final boolean bothDocuments = document && trIsDocument;
-                if (!sameAffinity && !sameIntent && !bothDocuments) {
-                    continue;
-                }
-
-                if (bothDocuments) {
-                    // Do these documents belong to the same activity?
-                    final boolean sameActivity = task.realActivity != null
-                            && tr.realActivity != null
-                            && task.realActivity.equals(tr.realActivity);
-                    if (!sameActivity) {
-                        // If the document is open in another app or is not the same document, we
-                        // don't need to trim it.
-                        continue;
-                    } else if (maxRecents > 0) {
-                        --maxRecents;
-                        if (!sameIntent || multiTasksAllowed) {
-                            // We don't want to trim if we are not over the max allowed entries and
-                            // the tasks are not of the same intent filter, or multiple entries for
-                            // the task is allowed.
-                            continue;
-                        }
-                    }
-                    // Hit the maximum number of documents for this task. Fall through
-                    // and remove this document from recents.
-                } else if (document || trIsDocument) {
-                    // Only one of these is a document. Not the droid we're looking for.
-                    continue;
-                }
-            }
-            return i;
-        }
-        return -1;
-    }
-
-    // Extract the affiliates of the chain containing recent at index start.
-    private int processNextAffiliateChainLocked(int start) {
-        final TaskRecord startTask = mTasks.get(start);
-        final int affiliateId = startTask.mAffiliatedTaskId;
-
-        // Quick identification of isolated tasks. I.e. those not launched behind.
-        if (startTask.taskId == affiliateId && startTask.mPrevAffiliate == null &&
-                startTask.mNextAffiliate == null) {
-            // There is still a slim chance that there are other tasks that point to this task
-            // and that the chain is so messed up that this task no longer points to them but
-            // the gain of this optimization outweighs the risk.
-            startTask.inRecents = true;
-            return start + 1;
-        }
-
-        // Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents.
-        mTmpRecents.clear();
-        for (int i = mTasks.size() - 1; i >= start; --i) {
-            final TaskRecord task = mTasks.get(i);
-            if (task.mAffiliatedTaskId == affiliateId) {
-                mTasks.remove(i);
-                mTmpRecents.add(task);
-            }
-        }
-
-        // Sort them all by taskId. That is the order they were create in and that order will
-        // always be correct.
-        Collections.sort(mTmpRecents, TASK_ID_COMPARATOR);
-
-        // Go through and fix up the linked list.
-        // The first one is the end of the chain and has no next.
-        final TaskRecord first = mTmpRecents.get(0);
-        first.inRecents = true;
-        if (first.mNextAffiliate != null) {
-            Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate);
-            first.setNextAffiliate(null);
-            notifyTaskPersisterLocked(first, false);
-        }
-        // Everything in the middle is doubly linked from next to prev.
-        final int tmpSize = mTmpRecents.size();
-        for (int i = 0; i < tmpSize - 1; ++i) {
-            final TaskRecord next = mTmpRecents.get(i);
-            final TaskRecord prev = mTmpRecents.get(i + 1);
-            if (next.mPrevAffiliate != prev) {
-                Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate +
-                        " setting prev=" + prev);
-                next.setPrevAffiliate(prev);
-                notifyTaskPersisterLocked(next, false);
-            }
-            if (prev.mNextAffiliate != next) {
-                Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate +
-                        " setting next=" + next);
-                prev.setNextAffiliate(next);
-                notifyTaskPersisterLocked(prev, false);
-            }
-            prev.inRecents = true;
-        }
-        // The last one is the beginning of the list and has no prev.
-        final TaskRecord last = mTmpRecents.get(tmpSize - 1);
-        if (last.mPrevAffiliate != null) {
-            Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate);
-            last.setPrevAffiliate(null);
-            notifyTaskPersisterLocked(last, false);
-        }
-
-        // Insert the group back into mTmpTasks at start.
-        mTasks.addAll(start, mTmpRecents);
-        mTmpRecents.clear();
-
-        // Let the caller know where we left off.
-        return start + tmpSize;
-    }
-
-    private boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) {
-        int recentsCount = mTasks.size();
-        TaskRecord top = task;
-        int topIndex = taskIndex;
-        while (top.mNextAffiliate != null && topIndex > 0) {
-            top = top.mNextAffiliate;
-            topIndex--;
-        }
-        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at "
-                + topIndex + " from intial " + taskIndex);
-        // Find the end of the chain, doing a sanity check along the way.
-        boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId;
-        int endIndex = topIndex;
-        TaskRecord prev = top;
-        while (endIndex < recentsCount) {
-            TaskRecord cur = mTasks.get(endIndex);
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @"
-                    + endIndex + " " + cur);
-            if (cur == top) {
-                // Verify start of the chain.
-                if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) {
-                    Slog.wtf(TAG, "Bad chain @" + endIndex
-                            + ": first task has next affiliate: " + prev);
-                    sane = false;
-                    break;
-                }
-            } else {
-                // Verify middle of the chain's next points back to the one before.
-                if (cur.mNextAffiliate != prev
-                        || cur.mNextAffiliateTaskId != prev.taskId) {
-                    Slog.wtf(TAG, "Bad chain @" + endIndex
-                            + ": middle task " + cur + " @" + endIndex
-                            + " has bad next affiliate "
-                            + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId
-                            + ", expected " + prev);
-                    sane = false;
-                    break;
-                }
-            }
-            if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) {
-                // Chain ends here.
-                if (cur.mPrevAffiliate != null) {
-                    Slog.wtf(TAG, "Bad chain @" + endIndex
-                            + ": last task " + cur + " has previous affiliate "
-                            + cur.mPrevAffiliate);
-                    sane = false;
-                }
-                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex);
-                break;
-            } else {
-                // Verify middle of the chain's prev points to a valid item.
-                if (cur.mPrevAffiliate == null) {
-                    Slog.wtf(TAG, "Bad chain @" + endIndex
-                            + ": task " + cur + " has previous affiliate "
-                            + cur.mPrevAffiliate + " but should be id "
-                            + cur.mPrevAffiliate);
-                    sane = false;
-                    break;
-                }
-            }
-            if (cur.mAffiliatedTaskId != task.mAffiliatedTaskId) {
-                Slog.wtf(TAG, "Bad chain @" + endIndex
-                        + ": task " + cur + " has affiliated id "
-                        + cur.mAffiliatedTaskId + " but should be "
-                        + task.mAffiliatedTaskId);
-                sane = false;
-                break;
-            }
-            prev = cur;
-            endIndex++;
-            if (endIndex >= recentsCount) {
-                Slog.wtf(TAG, "Bad chain ran off index " + endIndex
-                        + ": last task " + prev);
-                sane = false;
-                break;
-            }
-        }
-        if (sane) {
-            if (endIndex < taskIndex) {
-                Slog.wtf(TAG, "Bad chain @" + endIndex
-                        + ": did not extend to task " + task + " @" + taskIndex);
-                sane = false;
-            }
-        }
-        if (sane) {
-            // All looks good, we can just move all of the affiliated tasks
-            // to the top.
-            for (int i=topIndex; i<=endIndex; i++) {
-                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task
-                        + " from " + i + " to " + (i-topIndex));
-                TaskRecord cur = mTasks.remove(i);
-                mTasks.add(i - topIndex, cur);
-            }
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks  " +  topIndex
-                    + " to " + endIndex);
-            return true;
-        }
-
-        // Whoops, couldn't do it.
-        return false;
-    }
-
-    void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) {
-        pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)");
-        pw.println("mRecentsUid=" + mRecentsUid);
-        pw.println("mRecentsComponent=" + mRecentsComponent);
-        if (mTasks.isEmpty()) {
-            return;
-        }
-
-        // Dump raw recent task list
-        boolean printedAnything = false;
-        boolean printedHeader = false;
-        final int size = mTasks.size();
-        for (int i = 0; i < size; i++) {
-            final TaskRecord tr = mTasks.get(i);
-            if (dumpPackage != null && (tr.realActivity == null ||
-                    !dumpPackage.equals(tr.realActivity.getPackageName()))) {
-                continue;
-            }
-
-            if (!printedHeader) {
-                pw.println("  Recent tasks:");
-                printedHeader = true;
-                printedAnything = true;
-            }
-            pw.print("  * Recent #"); pw.print(i); pw.print(": ");
-            pw.println(tr);
-            if (dumpAll) {
-                tr.dump(pw, "    ");
-            }
-        }
-
-        // Dump visible recent task list
-        if (mHasVisibleRecentTasks) {
-            // Reset the header flag for the next block
-            printedHeader = false;
-            ArrayList<ActivityManager.RecentTaskInfo> tasks = getRecentTasksImpl(Integer.MAX_VALUE,
-                    0, true /* getTasksAllowed */, false /* getDetailedTasks */,
-                    mService.getCurrentUserId(), SYSTEM_UID);
-            for (int i = 0; i < tasks.size(); i++) {
-                final ActivityManager.RecentTaskInfo taskInfo = tasks.get(i);
-                if (!printedHeader) {
-                    if (printedAnything) {
-                        // Separate from the last block if it printed
-                        pw.println();
-                    }
-                    pw.println("  Visible recent tasks (most recent first):");
-                    printedHeader = true;
-                    printedAnything = true;
-                }
-
-                pw.print("  * RecentTaskInfo #"); pw.print(i); pw.print(": ");
-                taskInfo.dump(pw, "    ");
-            }
-        }
-
-        if (!printedAnything) {
-            pw.println("  (nothing)");
-        }
-    }
-
-    /**
-     * Creates a new RecentTaskInfo from a TaskRecord.
-     */
-    ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) {
-        ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
-        tr.fillTaskInfo(rti, mTmpReport);
-        // Fill in some deprecated values
-        rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
-        rti.persistentId = rti.taskId;
-        return rti;
-    }
-
-    /**
-     * @return Whether the activity types and windowing modes of the two tasks are considered
-     *         compatible. This is necessary because we currently don't persist the activity type
-     *         or the windowing mode with the task, so they can be undefined when restored.
-     */
-    private boolean hasCompatibleActivityTypeAndWindowingMode(TaskRecord t1, TaskRecord t2) {
-        final int activityType = t1.getActivityType();
-        final int windowingMode = t1.getWindowingMode();
-        final boolean isUndefinedType = activityType == ACTIVITY_TYPE_UNDEFINED;
-        final boolean isUndefinedMode = windowingMode == WINDOWING_MODE_UNDEFINED;
-        final int otherActivityType = t2.getActivityType();
-        final int otherWindowingMode = t2.getWindowingMode();
-        final boolean isOtherUndefinedType = otherActivityType == ACTIVITY_TYPE_UNDEFINED;
-        final boolean isOtherUndefinedMode = otherWindowingMode == WINDOWING_MODE_UNDEFINED;
-
-        // An activity type and windowing mode is compatible if they are the exact same type/mode,
-        // or if one of the type/modes is undefined
-        final boolean isCompatibleType = activityType == otherActivityType
-                || isUndefinedType || isOtherUndefinedType;
-        final boolean isCompatibleMode = windowingMode == otherWindowingMode
-                || isUndefinedMode || isOtherUndefinedMode;
-
-        return isCompatibleType && isCompatibleMode;
-    }
-}
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
deleted file mode 100644
index c5586bb..0000000
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
-import static android.app.AppOpsManager.OP_NONE;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
-import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
-import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
-
-import android.app.ActivityOptions;
-import android.app.AppOpsManager;
-import android.app.IAssistDataReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.util.Slog;
-import android.view.IRecentsAnimationRunner;
-import com.android.server.wm.RecentsAnimationController;
-import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
-import com.android.server.wm.WindowManagerService;
-
-/**
- * Manages the recents animation, including the reordering of the stacks for the transition and
- * cleanup. See {@link com.android.server.wm.RecentsAnimationController}.
- */
-class RecentsAnimation implements RecentsAnimationCallbacks,
-        ActivityDisplay.OnStackOrderChangedListener {
-    private static final String TAG = RecentsAnimation.class.getSimpleName();
-    private static final boolean DEBUG = false;
-
-    private final ActivityTaskManagerService mService;
-    private final ActivityStackSupervisor mStackSupervisor;
-    private final ActivityStartController mActivityStartController;
-    private final WindowManagerService mWindowManager;
-    private final ActivityDisplay mDefaultDisplay;
-    private final int mCallingPid;
-
-    private int mTargetActivityType;
-    private AssistDataRequester mAssistDataRequester;
-
-    // The stack to restore the target stack behind when the animation is finished
-    private ActivityStack mRestoreTargetBehindStack;
-
-    RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor,
-            ActivityStartController activityStartController, WindowManagerService wm,
-            int callingPid) {
-        mService = atm;
-        mStackSupervisor = stackSupervisor;
-        mDefaultDisplay = stackSupervisor.getDefaultDisplay();
-        mActivityStartController = activityStartController;
-        mWindowManager = wm;
-        mCallingPid = callingPid;
-    }
-
-    void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
-            ComponentName recentsComponent, int recentsUid,
-            IAssistDataReceiver assistDataReceiver) {
-        if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + intent
-                + " assistDataReceiver=" + assistDataReceiver);
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
-
-        if (!mWindowManager.canStartRecentsAnimation()) {
-            notifyAnimationCancelBeforeStart(recentsAnimationRunner);
-            if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition="
-                        + mWindowManager.getPendingAppTransition());
-            return;
-        }
-
-        // If the activity is associated with the recents stack, then try and get that first
-        mTargetActivityType = intent.getComponent() != null
-                && recentsComponent.equals(intent.getComponent())
-                        ? ACTIVITY_TYPE_RECENTS
-                        : ACTIVITY_TYPE_HOME;
-        final ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
-                mTargetActivityType);
-        ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent());
-        final boolean hasExistingActivity = targetActivity != null;
-        if (hasExistingActivity) {
-            final ActivityDisplay display = targetActivity.getDisplay();
-            mRestoreTargetBehindStack = display.getStackAbove(targetStack);
-            if (mRestoreTargetBehindStack == null) {
-                notifyAnimationCancelBeforeStart(recentsAnimationRunner);
-                if (DEBUG) Slog.d(TAG, "No stack above target stack=" + targetStack);
-                return;
-            }
-        }
-
-        // Send launch hint if we are actually launching the target. If it's already visible
-        // (shouldn't happen in general) we don't need to send it.
-        if (targetActivity == null || !targetActivity.visible) {
-            mStackSupervisor.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */,
-                    targetActivity);
-        }
-
-        mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
-
-        mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, true);
-
-        mWindowManager.deferSurfaceLayout();
-        try {
-            // Kick off the assist data request in the background before showing the target activity
-            if (assistDataReceiver != null) {
-                final AppOpsManager appOpsManager = (AppOpsManager)
-                        mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
-                final AssistDataReceiverProxy proxy = new AssistDataReceiverProxy(
-                        assistDataReceiver, recentsComponent.getPackageName());
-                mAssistDataRequester = new AssistDataRequester(mService.mContext,
-                        mWindowManager, appOpsManager, proxy, this, OP_ASSIST_STRUCTURE, OP_NONE);
-                mAssistDataRequester.requestAssistData(mStackSupervisor.getTopVisibleActivities(),
-                        true /* fetchData */, false /* fetchScreenshots */,
-                        true /* allowFetchData */, false /* allowFetchScreenshots */,
-                        recentsUid, recentsComponent.getPackageName());
-            }
-
-            if (hasExistingActivity) {
-                // Move the recents activity into place for the animation if it is not top most
-                mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
-                if (DEBUG) Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
-                            + mDefaultDisplay.getStackAbove(targetStack));
-
-                // If there are multiple tasks in the target stack (ie. the home stack, with 3p
-                // and default launchers coexisting), then move the task to the top as a part of
-                // moving the stack to the front
-                if (targetStack.topTask() != targetActivity.getTask()) {
-                    targetStack.addTask(targetActivity.getTask(), true /* toTop */,
-                            "startRecentsActivity");
-                }
-            } else {
-                // No recents activity
-                ActivityOptions options = ActivityOptions.makeBasic();
-                options.setLaunchActivityType(mTargetActivityType);
-                options.setAvoidMoveToFront();
-                intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION);
-
-                mActivityStartController
-                        .obtainStarter(intent, "startRecentsActivity_noTargetActivity")
-                        .setCallingUid(recentsUid)
-                        .setCallingPackage(recentsComponent.getPackageName())
-                        .setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
-                        .setMayWait(mService.getCurrentUserId())
-                        .execute();
-                mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
-                mWindowManager.executeAppTransition();
-
-                targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
-                        mTargetActivityType).getTopActivity();
-
-                // TODO: Maybe wait for app to draw in this particular case?
-
-                if (DEBUG) Slog.d(TAG, "Started intent=" + intent);
-            }
-
-            // Mark the target activity as launch-behind to bump its visibility for the
-            // duration of the gesture that is driven by the recents component
-            targetActivity.mLaunchTaskBehind = true;
-
-            // Fetch all the surface controls and pass them to the client to get the animation
-            // started. Cancel any existing recents animation running synchronously (do not hold the
-            // WM lock)
-            mWindowManager.cancelRecentsAnimationSynchronously(REORDER_MOVE_TO_ORIGINAL_POSITION,
-                    "startRecentsActivity");
-            mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
-                    this, mDefaultDisplay.mDisplayId,
-                    mStackSupervisor.mRecentTasks.getRecentTaskIds());
-
-            // If we updated the launch-behind state, update the visibility of the activities after
-            // we fetch the visible tasks to be controlled by the animation
-            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-
-            mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
-                    targetActivity);
-
-            // Register for stack order changes
-            mDefaultDisplay.registerStackOrderChangedListener(this);
-        } catch (Exception e) {
-            Slog.e(TAG, "Failed to start recents activity", e);
-            throw e;
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
-    private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
-        synchronized (mService.mGlobalLock) {
-            if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller="
-                    + mWindowManager.getRecentsAnimationController()
-                    + " reorderMode=" + reorderMode);
-
-            // Cancel the associated assistant data request
-            if (mAssistDataRequester != null) {
-                mAssistDataRequester.cancel();
-                mAssistDataRequester = null;
-            }
-
-            // Unregister for stack order changes
-            mDefaultDisplay.unregisterStackOrderChangedListener(this);
-
-            if (mWindowManager.getRecentsAnimationController() == null) return;
-
-            // Just to be sure end the launch hint in case the target activity was never launched.
-            // However, if we're keeping the activity and making it visible, we can leave it on.
-            if (reorderMode != REORDER_KEEP_IN_PLACE) {
-                mStackSupervisor.sendPowerHintForLaunchEndIfNeeded();
-            }
-
-            mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, false);
-
-            mWindowManager.inSurfaceTransaction(() -> {
-                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
-                        "RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
-                mWindowManager.deferSurfaceLayout();
-                try {
-                    mWindowManager.cleanupRecentsAnimation(reorderMode);
-
-                    final ActivityStack targetStack = mDefaultDisplay.getStack(
-                            WINDOWING_MODE_UNDEFINED, mTargetActivityType);
-                    final ActivityRecord targetActivity = targetStack != null
-                            ? targetStack.getTopActivity()
-                            : null;
-                    if (DEBUG) Slog.d(TAG, "onAnimationFinished(): targetStack=" + targetStack
-                            + " targetActivity=" + targetActivity
-                            + " mRestoreTargetBehindStack=" + mRestoreTargetBehindStack);
-                    if (targetActivity == null) {
-                        return;
-                    }
-
-                    // Restore the launched-behind state
-                    targetActivity.mLaunchTaskBehind = false;
-
-                    if (reorderMode == REORDER_MOVE_TO_TOP) {
-                        // Bring the target stack to the front
-                        mStackSupervisor.mNoAnimActivities.add(targetActivity);
-                        targetStack.moveToFront("RecentsAnimation.onAnimationFinished()");
-                        if (DEBUG) {
-                            final ActivityStack topStack = getTopNonAlwaysOnTopStack();
-                            if (topStack != targetStack) {
-                                Slog.w(TAG, "Expected target stack=" + targetStack
-                                        + " to be top most but found stack=" + topStack);
-                            }
-                        }
-                    } else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){
-                        // Restore the target stack to its previous position
-                        final ActivityDisplay display = targetActivity.getDisplay();
-                        display.moveStackBehindStack(targetStack, mRestoreTargetBehindStack);
-                        if (DEBUG) {
-                            final ActivityStack aboveTargetStack =
-                                    mDefaultDisplay.getStackAbove(targetStack);
-                            if (mRestoreTargetBehindStack != null
-                                    && aboveTargetStack != mRestoreTargetBehindStack) {
-                                Slog.w(TAG, "Expected target stack=" + targetStack
-                                        + " to restored behind stack=" + mRestoreTargetBehindStack
-                                        + " but it is behind stack=" + aboveTargetStack);
-                            }
-                        }
-                    } else {
-                        // Keep target stack in place, nothing changes, so ignore the transition
-                        // logic below
-                        return;
-                    }
-
-                    mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-
-                    // No reason to wait for the pausing activity in this case, as the hiding of
-                    // surfaces needs to be done immediately.
-                    mWindowManager.executeAppTransition();
-
-                    // After reordering the stacks, reset the minimized state. At this point, either
-                    // the target activity is now top-most and we will stay minimized (if in
-                    // split-screen), or we will have returned to the app, and the minimized state
-                    // should be reset
-                    mWindowManager.checkSplitScreenMinimizedChanged(true /* animate */);
-                } catch (Exception e) {
-                    Slog.e(TAG, "Failed to clean up recents activity", e);
-                    throw e;
-                } finally {
-                    mWindowManager.continueSurfaceLayout();
-                    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-                }
-            });
-        }
-    }
-
-    @Override
-    public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode,
-            boolean runSychronously) {
-        if (runSychronously) {
-            finishAnimation(reorderMode);
-        } else {
-            mService.mH.post(() -> finishAnimation(reorderMode));
-        }
-    }
-
-    @Override
-    public void onStackOrderChanged() {
-        // If the activity display stack order changes, cancel any running recents animation in
-        // place
-        mWindowManager.cancelRecentsAnimationSynchronously(REORDER_KEEP_IN_PLACE,
-                "stackOrderChanged");
-    }
-
-    /**
-     * Called only when the animation should be canceled prior to starting.
-     */
-    private void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
-        try {
-            recentsAnimationRunner.onAnimationCanceled();
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to cancel recents animation before start", e);
-        }
-    }
-
-    /**
-     * @return The top stack that is not always-on-top.
-     */
-    private ActivityStack getTopNonAlwaysOnTopStack() {
-        for (int i = mDefaultDisplay.getChildCount() - 1; i >= 0; i--) {
-            final ActivityStack s = mDefaultDisplay.getChildAt(i);
-            if (s.getWindowConfiguration().isAlwaysOnTop()) {
-                continue;
-            }
-            return s;
-        }
-        return null;
-    }
-
-    /**
-     * @return the top activity in the {@param targetStack} matching the {@param component}, or just
-     * the top activity of the top task if no task matches the component.
-     */
-    private ActivityRecord getTargetActivity(ActivityStack targetStack, ComponentName component) {
-        if (targetStack == null) {
-            return null;
-        }
-
-        for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
-            final TaskRecord task = (TaskRecord) targetStack.getChildAt(i);
-            if (task.getBaseIntent().getComponent().equals(component)) {
-                return task.getTopActivity();
-            }
-        }
-        return targetStack.getTopActivity();
-    }
-}
diff --git a/services/core/java/com/android/server/am/RunningTasks.java b/services/core/java/com/android/server/am/RunningTasks.java
deleted file mode 100644
index d878f51..0000000
--- a/services/core/java/com/android/server/am/RunningTasks.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.WindowConfiguration.ActivityType;
-import android.app.WindowConfiguration.WindowingMode;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.TreeSet;
-
-/**
- * Class for resolving the set of running tasks in the system.
- */
-class RunningTasks {
-
-    // Comparator to sort by last active time (descending)
-    private static final Comparator<TaskRecord> LAST_ACTIVE_TIME_COMPARATOR =
-            (o1, o2) -> Long.signum(o2.lastActiveTime - o1.lastActiveTime);
-
-    private final TaskRecord.TaskActivitiesReport mTmpReport =
-            new TaskRecord.TaskActivitiesReport();
-    private final TreeSet<TaskRecord> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR);
-    private final ArrayList<TaskRecord> mTmpStackTasks = new ArrayList<>();
-
-    void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType,
-            @WindowingMode int ignoreWindowingMode, ArrayList<ActivityDisplay> activityDisplays,
-            int callingUid, boolean allowed) {
-        // Return early if there are no tasks to fetch
-        if (maxNum <= 0) {
-            return;
-        }
-
-        // Gather all of the tasks across all of the tasks, and add them to the sorted set
-        mTmpSortedSet.clear();
-        final int numDisplays = activityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = activityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                mTmpStackTasks.clear();
-                stack.getRunningTasks(mTmpStackTasks, ignoreActivityType, ignoreWindowingMode,
-                        callingUid, allowed);
-                mTmpSortedSet.addAll(mTmpStackTasks);
-            }
-        }
-
-        // Take the first {@param maxNum} tasks and create running task infos for them
-        final Iterator<TaskRecord> iter = mTmpSortedSet.iterator();
-        while (iter.hasNext()) {
-            if (maxNum == 0) {
-                break;
-            }
-
-            final TaskRecord task = iter.next();
-            list.add(createRunningTaskInfo(task));
-            maxNum--;
-        }
-    }
-
-    /**
-     * Constructs a {@link RunningTaskInfo} from a given {@param task}.
-     */
-    private RunningTaskInfo createRunningTaskInfo(TaskRecord task) {
-        final RunningTaskInfo rti = new RunningTaskInfo();
-        task.fillTaskInfo(rti, mTmpReport);
-        // Fill in some deprecated values
-        rti.id = rti.taskId;
-        return rti;
-    }
-}
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
deleted file mode 100644
index 1152165..0000000
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
-import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.content.pm.PackageManager.PERMISSION_DENIED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.view.Display.INVALID_DISPLAY;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.UserHandle;
-import android.util.Slog;
-import android.view.RemoteAnimationAdapter;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Wraps {@link ActivityOptions}, records binder identity, and checks permission when retrieving
- * the inner options. Also supports having two set of options: Once from the original caller, and
- * once from the caller that is overriding it, which happens when sending a {@link PendingIntent}.
- */
-public class SafeActivityOptions {
-
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_ATM;
-
-    private final int mOriginalCallingPid;
-    private final int mOriginalCallingUid;
-    private int mRealCallingPid;
-    private int mRealCallingUid;
-    private final @Nullable ActivityOptions mOriginalOptions;
-    private @Nullable ActivityOptions mCallerOptions;
-
-    /**
-     * Constructs a new instance from a bundle and records {@link Binder#getCallingPid}/
-     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
-     * this object.
-     *
-     * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
-     */
-    static SafeActivityOptions fromBundle(Bundle bOptions) {
-        return bOptions != null
-                ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions))
-                : null;
-    }
-
-    /**
-     * Constructs a new instance and records {@link Binder#getCallingPid}/
-     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
-     * this object.
-     *
-     * @param options The options to wrap.
-     */
-    SafeActivityOptions(@Nullable ActivityOptions options) {
-        mOriginalCallingPid = Binder.getCallingPid();
-        mOriginalCallingUid = Binder.getCallingUid();
-        mOriginalOptions = options;
-    }
-
-    /**
-     * Overrides options with options from a caller and records {@link Binder#getCallingPid}/
-     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
-     * method.
-     */
-    void setCallerOptions(@Nullable ActivityOptions options) {
-        mRealCallingPid = Binder.getCallingPid();
-        mRealCallingUid = Binder.getCallingUid();
-        mCallerOptions = options;
-    }
-
-    /**
-     * Performs permission check and retrieves the options.
-     *
-     * @param r The record of the being started activity.
-     */
-    ActivityOptions getOptions(ActivityRecord r) throws SecurityException {
-        return getOptions(r.intent, r.info, r.app, r.mStackSupervisor);
-    }
-
-    /**
-     * Performs permission check and retrieves the options when options are not being used to launch
-     * a specific activity (i.e. a task is moved to front).
-     */
-    ActivityOptions getOptions(ActivityStackSupervisor supervisor) throws SecurityException {
-        return getOptions(null, null, null, supervisor);
-    }
-
-    /**
-     * Performs permission check and retrieves the options.
-     *
-     * @param intent The intent that is being launched.
-     * @param aInfo The info of the activity being launched.
-     * @param callerApp The record of the caller.
-     */
-    ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
-            @Nullable WindowProcessController callerApp,
-            ActivityStackSupervisor supervisor) throws SecurityException {
-        if (mOriginalOptions != null) {
-            checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions,
-                    mOriginalCallingPid, mOriginalCallingUid);
-            setCallingPidForRemoteAnimationAdapter(mOriginalOptions, mOriginalCallingPid);
-        }
-        if (mCallerOptions != null) {
-            checkPermissions(intent, aInfo, callerApp, supervisor, mCallerOptions,
-                    mRealCallingPid, mRealCallingUid);
-            setCallingPidForRemoteAnimationAdapter(mCallerOptions, mRealCallingPid);
-        }
-        return mergeActivityOptions(mOriginalOptions, mCallerOptions);
-    }
-
-    private void setCallingPidForRemoteAnimationAdapter(ActivityOptions options, int callingPid) {
-        final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
-        if (adapter == null) {
-            return;
-        }
-        if (callingPid == Process.myPid()) {
-            Slog.wtf(TAG, "Safe activity options constructed after clearing calling id");
-            return;
-        }
-        adapter.setCallingPid(callingPid);
-    }
-
-    /**
-     * @see ActivityOptions#popAppVerificationBundle
-     */
-    Bundle popAppVerificationBundle() {
-        return mOriginalOptions != null ? mOriginalOptions.popAppVerificationBundle() : null;
-    }
-
-    private void abort() {
-        if (mOriginalOptions != null) {
-            ActivityOptions.abort(mOriginalOptions);
-        }
-        if (mCallerOptions != null) {
-            ActivityOptions.abort(mCallerOptions);
-        }
-    }
-
-    static void abort(@Nullable SafeActivityOptions options) {
-        if (options != null) {
-            options.abort();
-        }
-    }
-
-    /**
-     * Merges two activity options into one, with {@code options2} taking precedence in case of a
-     * conflict.
-     */
-    @VisibleForTesting
-    @Nullable ActivityOptions mergeActivityOptions(@Nullable ActivityOptions options1,
-            @Nullable ActivityOptions options2) {
-        if (options1 == null) {
-            return options2;
-        }
-        if (options2 == null) {
-            return options1;
-        }
-        final Bundle b1 = options1.toBundle();
-        final Bundle b2 = options2.toBundle();
-        b1.putAll(b2);
-        return ActivityOptions.fromBundle(b1);
-    }
-
-    private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
-            @Nullable WindowProcessController callerApp, ActivityStackSupervisor supervisor,
-            ActivityOptions options, int callingPid, int callingUid) {
-        // If a launch task id is specified, then ensure that the caller is the recents
-        // component or has the START_TASKS_FROM_RECENTS permission
-        if (options.getLaunchTaskId() != INVALID_TASK_ID
-                && !supervisor.mRecentTasks.isCallerRecents(callingUid)) {
-            final int startInTaskPerm = ActivityTaskManagerService.checkPermission(
-                    START_TASKS_FROM_RECENTS, callingPid, callingUid);
-            if (startInTaskPerm == PERMISSION_DENIED) {
-                final String msg = "Permission Denial: starting " + getIntentString(intent)
-                        + " from " + callerApp + " (pid=" + callingPid
-                        + ", uid=" + callingUid + ") with launchTaskId="
-                        + options.getLaunchTaskId();
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
-            }
-        }
-        // Check if someone tries to launch an activity on a private display with a different
-        // owner.
-        final int launchDisplayId = options.getLaunchDisplayId();
-        if (aInfo != null && launchDisplayId != INVALID_DISPLAY
-                && !supervisor.isCallerAllowedToLaunchOnDisplay(callingPid, callingUid,
-                        launchDisplayId, aInfo)) {
-            final String msg = "Permission Denial: starting " + getIntentString(intent)
-                    + " from " + callerApp + " (pid=" + callingPid
-                    + ", uid=" + callingUid + ") with launchDisplayId="
-                    + launchDisplayId;
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-        // Check if someone tries to launch an unwhitelisted activity into LockTask mode.
-        final boolean lockTaskMode = options.getLockTaskMode();
-        if (aInfo != null && lockTaskMode
-                && !supervisor.mService.getLockTaskController().isPackageWhitelisted(
-                        UserHandle.getUserId(callingUid), aInfo.packageName)) {
-            final String msg = "Permission Denial: starting " + getIntentString(intent)
-                    + " from " + callerApp + " (pid=" + callingPid
-                    + ", uid=" + callingUid + ") with lockTaskMode=true";
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-
-        // Check permission for remote animations
-        final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
-        if (adapter != null && supervisor.mService.checkPermission(
-                CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
-                        != PERMISSION_GRANTED) {
-            final String msg = "Permission Denial: starting " + getIntentString(intent)
-                    + " from " + callerApp + " (pid=" + callingPid
-                    + ", uid=" + callingUid + ") with remoteAnimationAdapter";
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-    }
-
-    private String getIntentString(Intent intent) {
-        return intent != null ? intent.toString() : "(no intent)";
-    }
-}
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
deleted file mode 100644
index efb80be..0000000
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright (C) 2016 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.server.am;
-
-import android.app.ActivityManager.TaskSnapshot;
-import android.app.ITaskStackListener;
-import android.app.ActivityManager.TaskDescription;
-import android.content.ComponentName;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-
-import java.util.ArrayList;
-
-class TaskChangeNotificationController {
-    private static final int LOG_STACK_STATE_MSG = 1;
-    private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2;
-    private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3;
-    private static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4;
-    private static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5;
-    private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6;
-    private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7;
-    private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8;
-    private static final int NOTIFY_TASK_REMOVED_LISTENERS_MSG = 9;
-    private static final int NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG = 10;
-    private static final int NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG = 11;
-    private static final int NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS = 12;
-    private static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
-    private static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14;
-    private static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15;
-    private static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16;
-    private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17;
-    private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18;
-
-    // Delay in notifying task stack change listeners (in millis)
-    private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
-
-    // Global lock used by the service the instantiate objects of this class.
-    private final Object mServiceLock;
-    private final ActivityStackSupervisor mStackSupervisor;
-    private final Handler mHandler;
-
-    // Task stack change listeners in a remote process.
-    private final RemoteCallbackList<ITaskStackListener> mRemoteTaskStackListeners =
-            new RemoteCallbackList<>();
-
-    /*
-     * Task stack change listeners in a local process. Tracked separately so that they can be
-     * called on the same thread.
-     */
-    private final ArrayList<ITaskStackListener> mLocalTaskStackListeners = new ArrayList<>();
-
-    private final TaskStackConsumer mNotifyTaskStackChanged = (l, m) -> {
-        l.onTaskStackChanged();
-    };
-
-    private final TaskStackConsumer mNotifyTaskCreated = (l, m) -> {
-        l.onTaskCreated(m.arg1, (ComponentName) m.obj);
-    };
-
-    private final TaskStackConsumer mNotifyTaskRemoved = (l, m) -> {
-        l.onTaskRemoved(m.arg1);
-    };
-
-    private final TaskStackConsumer mNotifyTaskMovedToFront = (l, m) -> {
-        l.onTaskMovedToFront(m.arg1);
-    };
-
-    private final TaskStackConsumer mNotifyTaskDescriptionChanged = (l, m) -> {
-        l.onTaskDescriptionChanged(m.arg1, (TaskDescription) m.obj);
-    };
-
-    private final TaskStackConsumer mNotifyActivityRequestedOrientationChanged = (l, m) -> {
-        l.onActivityRequestedOrientationChanged(m.arg1, m.arg2);
-    };
-
-    private final TaskStackConsumer mNotifyTaskRemovalStarted = (l, m) -> {
-        l.onTaskRemovalStarted(m.arg1);
-    };
-
-    private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> {
-        l.onActivityPinned((String) m.obj /* packageName */, m.sendingUid /* userId */,
-                m.arg1 /* taskId */, m.arg2 /* stackId */);
-    };
-
-    private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> {
-        l.onActivityUnpinned();
-    };
-
-    private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> {
-        l.onPinnedActivityRestartAttempt(m.arg1 != 0);
-    };
-
-    private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> {
-        l.onPinnedStackAnimationStarted();
-    };
-
-    private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> {
-        l.onPinnedStackAnimationEnded();
-    };
-
-    private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> {
-        l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2);
-    };
-
-    private final TaskStackConsumer mNotifyActivityDismissingDockedStack = (l, m) -> {
-        l.onActivityDismissingDockedStack();
-    };
-
-    private final TaskStackConsumer mNotifyActivityLaunchOnSecondaryDisplayFailed = (l, m) -> {
-        l.onActivityLaunchOnSecondaryDisplayFailed();
-    };
-
-    private final TaskStackConsumer mNotifyTaskProfileLocked = (l, m) -> {
-        l.onTaskProfileLocked(m.arg1, m.arg2);
-    };
-
-    private final TaskStackConsumer mNotifyTaskSnapshotChanged = (l, m) -> {
-        l.onTaskSnapshotChanged(m.arg1, (TaskSnapshot) m.obj);
-    };
-
-    @FunctionalInterface
-    public interface TaskStackConsumer {
-        void accept(ITaskStackListener t, Message m) throws RemoteException;
-    }
-
-    private class MainHandler extends Handler {
-        public MainHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case LOG_STACK_STATE_MSG: {
-                    synchronized (mServiceLock) {
-                        mStackSupervisor.logStackState();
-                    }
-                    break;
-                }
-                case NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyTaskStackChanged, msg);
-                    break;
-                case NOTIFY_TASK_ADDED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyTaskCreated, msg);
-                    break;
-                case NOTIFY_TASK_REMOVED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyTaskRemoved, msg);
-                    break;
-                case NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyTaskMovedToFront, msg);
-                    break;
-                case NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyTaskDescriptionChanged, msg);
-                    break;
-                case NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS:
-                    forAllRemoteListeners(mNotifyActivityRequestedOrientationChanged, msg);
-                    break;
-                case NOTIFY_TASK_REMOVAL_STARTED_LISTENERS:
-                    forAllRemoteListeners(mNotifyTaskRemovalStarted, msg);
-                    break;
-                case NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyActivityPinned, msg);
-                    break;
-                case NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyActivityUnpinned, msg);
-                    break;
-                case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg);
-                    break;
-                case NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyPinnedStackAnimationStarted, msg);
-                    break;
-                case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyPinnedStackAnimationEnded, msg);
-                    break;
-                case NOTIFY_FORCED_RESIZABLE_MSG:
-                    forAllRemoteListeners(mNotifyActivityForcedResizable, msg);
-                    break;
-                case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG:
-                    forAllRemoteListeners(mNotifyActivityDismissingDockedStack, msg);
-                    break;
-                case NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG:
-                    forAllRemoteListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg);
-                    break;
-                case NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyTaskProfileLocked, msg);
-                    break;
-                case NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyTaskSnapshotChanged, msg);
-                    break;
-            }
-        }
-    }
-
-    public TaskChangeNotificationController(Object serviceLock,
-            ActivityStackSupervisor stackSupervisor, Handler handler) {
-        mServiceLock = serviceLock;
-        mStackSupervisor = stackSupervisor;
-        mHandler = new MainHandler(handler.getLooper());
-    }
-
-    public void registerTaskStackListener(ITaskStackListener listener) {
-        synchronized (mServiceLock) {
-            if (listener != null) {
-                if (Binder.getCallingPid() == android.os.Process.myPid()) {
-                    if (!mLocalTaskStackListeners.contains(listener)) {
-                        mLocalTaskStackListeners.add(listener);
-                    }
-                } else {
-                    mRemoteTaskStackListeners.register(listener);
-                }
-            }
-        }
-    }
-
-    public void unregisterTaskStackListener(ITaskStackListener listener) {
-        synchronized (mServiceLock) {
-            if (listener != null) {
-                if (Binder.getCallingPid() == android.os.Process.myPid()) {
-                    mLocalTaskStackListeners.remove(listener);
-                } else {
-                    mRemoteTaskStackListeners.unregister(listener);
-                }
-            }
-        }
-    }
-
-    private void forAllRemoteListeners(TaskStackConsumer callback, Message message) {
-        synchronized (mServiceLock) {
-            for (int i = mRemoteTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
-                try {
-                    // Make a one-way callback to the listener
-                    callback.accept(mRemoteTaskStackListeners.getBroadcastItem(i), message);
-                } catch (RemoteException e) {
-                    // Handled by the RemoteCallbackList.
-                }
-            }
-            mRemoteTaskStackListeners.finishBroadcast();
-        }
-    }
-
-    private void forAllLocalListeners(TaskStackConsumer callback, Message message) {
-        synchronized (mServiceLock) {
-            for (int i = mLocalTaskStackListeners.size() - 1; i >= 0; i--) {
-                try {
-                    callback.accept(mLocalTaskStackListeners.get(i), message);
-                } catch (RemoteException e) {
-                    // Never thrown since this is called locally.
-                }
-            }
-        }
-    }
-
-    /** Notifies all listeners when the task stack has changed. */
-    void notifyTaskStackChanged() {
-        mHandler.sendEmptyMessage(LOG_STACK_STATE_MSG);
-        mHandler.removeMessages(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
-        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
-        forAllLocalListeners(mNotifyTaskStackChanged, msg);
-        // Only the main task stack change notification requires a delay.
-        mHandler.sendMessageDelayed(msg, NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY);
-    }
-
-    /** Notifies all listeners when an Activity is pinned. */
-    void notifyActivityPinned(ActivityRecord r) {
-        mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
-        final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
-                r.getTask().taskId, r.getStackId(), r.packageName);
-        msg.sendingUid = r.userId;
-        forAllLocalListeners(mNotifyActivityPinned, msg);
-        msg.sendToTarget();
-    }
-
-    /** Notifies all listeners when an Activity is unpinned. */
-    void notifyActivityUnpinned() {
-        mHandler.removeMessages(NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG);
-        final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG);
-        forAllLocalListeners(mNotifyActivityUnpinned, msg);
-        msg.sendToTarget();
-    }
-
-    /**
-     * Notifies all listeners when an attempt was made to start an an activity that is already
-     * running in the pinned stack and the activity was not actually started, but the task is
-     * either brought to the front or a new Intent is delivered to it.
-     */
-    void notifyPinnedActivityRestartAttempt(boolean clearedTask) {
-        mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
-        final Message msg =
-                mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG,
-                        clearedTask ? 1 : 0, 0);
-        forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg);
-        msg.sendToTarget();
-    }
-
-    /** Notifies all listeners when the pinned stack animation starts. */
-    void notifyPinnedStackAnimationStarted() {
-        mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
-        final Message msg =
-                mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
-        forAllLocalListeners(mNotifyPinnedStackAnimationStarted, msg);
-        msg.sendToTarget();
-    }
-
-    /** Notifies all listeners when the pinned stack animation ends. */
-    void notifyPinnedStackAnimationEnded() {
-        mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
-        final Message msg =
-                mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
-        forAllLocalListeners(mNotifyPinnedStackAnimationEnded, msg);
-        msg.sendToTarget();
-    }
-
-    void notifyActivityDismissingDockedStack() {
-        mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
-        final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
-        forAllLocalListeners(mNotifyActivityDismissingDockedStack, msg);
-        msg.sendToTarget();
-    }
-
-    void notifyActivityForcedResizable(int taskId, int reason, String packageName) {
-        mHandler.removeMessages(NOTIFY_FORCED_RESIZABLE_MSG);
-        final Message msg = mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, taskId, reason,
-                packageName);
-        forAllLocalListeners(mNotifyActivityForcedResizable, msg);
-        msg.sendToTarget();
-    }
-
-    void notifyActivityLaunchOnSecondaryDisplayFailed() {
-        mHandler.removeMessages(NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG);
-        final Message msg = mHandler.obtainMessage(
-                NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG);
-        forAllLocalListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg);
-        msg.sendToTarget();
-    }
-
-    void notifyTaskCreated(int taskId, ComponentName componentName) {
-        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_ADDED_LISTENERS_MSG,
-                taskId, 0 /* unused */, componentName);
-        forAllLocalListeners(mNotifyTaskCreated, msg);
-        msg.sendToTarget();
-    }
-
-    void notifyTaskRemoved(int taskId) {
-        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_REMOVED_LISTENERS_MSG,
-                taskId, 0 /* unused */);
-        forAllLocalListeners(mNotifyTaskRemoved, msg);
-        msg.sendToTarget();
-    }
-
-    void notifyTaskMovedToFront(int taskId) {
-        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG,
-                taskId, 0 /* unused */);
-        forAllLocalListeners(mNotifyTaskMovedToFront, msg);
-        msg.sendToTarget();
-    }
-
-    void notifyTaskDescriptionChanged(int taskId, TaskDescription taskDescription) {
-        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG,
-                taskId, 0 /* unused */, taskDescription);
-        forAllLocalListeners(mNotifyTaskDescriptionChanged, msg);
-        msg.sendToTarget();
-
-    }
-
-    void notifyActivityRequestedOrientationChanged(int taskId, int orientation) {
-        final Message msg = mHandler.obtainMessage(
-                NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS, taskId, orientation);
-        forAllLocalListeners(mNotifyActivityRequestedOrientationChanged, msg);
-        msg.sendToTarget();
-    }
-
-    /**
-     * Notify listeners that the task is about to be finished before its surfaces are removed from
-     * the window manager. This allows interested parties to perform relevant animations before
-     * the window disappears.
-     */
-    void notifyTaskRemovalStarted(int taskId) {
-        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_REMOVAL_STARTED_LISTENERS, taskId,
-                0 /* unused */);
-        forAllLocalListeners(mNotifyTaskRemovalStarted, msg);
-        msg.sendToTarget();
-
-    }
-
-    /**
-     * Notify listeners that the task has been put in a locked state because one or more of the
-     * activities inside it belong to a managed profile user that has been locked.
-     */
-    void notifyTaskProfileLocked(int taskId, int userId) {
-        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG, taskId,
-                userId);
-        forAllLocalListeners(mNotifyTaskProfileLocked, msg);
-        msg.sendToTarget();
-    }
-
-    /**
-     * Notify listeners that the snapshot of a task has changed.
-     */
-    void notifyTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
-        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG,
-                taskId, 0, snapshot);
-        forAllLocalListeners(mNotifyTaskSnapshotChanged, msg);
-        msg.sendToTarget();
-    }
-}
diff --git a/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java
deleted file mode 100644
index 111adec..0000000
--- a/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java
+++ /dev/null
@@ -1,803 +0,0 @@
-/*
- * Copyright (C) 2015 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.server.am;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT;
-import static android.util.DisplayMetrics.DENSITY_DEFAULT;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.app.WindowConfiguration;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.graphics.Rect;
-import android.os.Build;
-import android.util.Slog;
-import android.view.Gravity;
-
-import com.android.server.am.LaunchParamsController.LaunchParams;
-import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The class that defines the default launch params for tasks.
- */
-class TaskLaunchParamsModifier implements LaunchParamsModifier {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskLaunchParamsModifier" : TAG_ATM;
-    private static final boolean DEBUG = false;
-
-    // A mask for SUPPORTS_SCREEN that indicates the activity supports resize.
-    private static final int SUPPORTS_SCREEN_RESIZEABLE_MASK =
-            ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES
-                    | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
-                    | ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
-                    | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS
-                    | ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES
-                    | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
-
-    // Screen size of Nexus 5x
-    private static final int DEFAULT_PORTRAIT_PHONE_WIDTH_DP = 412;
-    private static final int DEFAULT_PORTRAIT_PHONE_HEIGHT_DP = 732;
-
-    // Allowance of size matching.
-    private static final int EPSILON = 2;
-
-    // Cascade window offset.
-    private static final int CASCADING_OFFSET_DP = 75;
-
-    // Threshold how close window corners have to be to call them colliding.
-    private static final int BOUNDS_CONFLICT_THRESHOLD = 4;
-
-    // Divide display size by this number to get each step to adjust bounds to avoid conflict.
-    private static final int STEP_DENOMINATOR = 16;
-
-    // We always want to step by at least this.
-    private static final int MINIMAL_STEP = 1;
-
-    private final ActivityStackSupervisor mSupervisor;
-    private final Rect mTmpBounds = new Rect();
-    private final int[] mTmpDirections = new int[2];
-
-    private StringBuilder mLogBuilder;
-
-    TaskLaunchParamsModifier(ActivityStackSupervisor supervisor) {
-        mSupervisor = supervisor;
-    }
-
-    @Override
-    public int onCalculate(TaskRecord task, ActivityInfo.WindowLayout layout,
-                           ActivityRecord activity, ActivityRecord source, ActivityOptions options,
-                           LaunchParams currentParams, LaunchParams outParams) {
-        initLogBuilder(task, activity);
-        final int result = calculate(task, layout, activity, source, options, currentParams,
-                outParams);
-        outputLog();
-        return result;
-    }
-
-    private int calculate(TaskRecord task, ActivityInfo.WindowLayout layout,
-            ActivityRecord activity, ActivityRecord source, ActivityOptions options,
-            LaunchParams currentParams, LaunchParams outParams) {
-        final ActivityRecord root;
-        if (task != null) {
-            root = task.getRootActivity() == null ? activity : task.getRootActivity();
-        } else {
-            root = activity;
-        }
-
-        // TODO: Investigate whether we can safely ignore all cases where we don't have root
-        // activity available. Note we can't know if the bounds are valid if we're not sure of the
-        // requested orientation of the root activity. Therefore if we found such a case we may need
-        // to pass the activity into this modifier in that case.
-        if (root == null) {
-            // There is a case that can lead us here. The caller is moving the top activity that is
-            // in a task that has multiple activities to PIP mode. For that the caller is creating a
-            // new task to host the activity so that we only move the top activity to PIP mode and
-            // keep other activities in the previous task. There is no point to apply the launch
-            // logic in this case.
-            return RESULT_SKIP;
-        }
-
-        // STEP 1: Determine the display to launch the activity/task.
-        final int displayId = getPreferredLaunchDisplay(options, source, currentParams);
-        outParams.mPreferredDisplayId = displayId;
-        ActivityDisplay display = mSupervisor.getActivityDisplay(displayId);
-        if (DEBUG) {
-            appendLog("display-id=" + outParams.mPreferredDisplayId + " display-windowing-mode="
-                    + display.getWindowingMode());
-        }
-
-        // STEP 2: Resolve launch windowing mode.
-        // STEP 2.1: Determine if any parameter has specified initial bounds. That might be the
-        // launch bounds from activity options, or size/gravity passed in layout. It also treats the
-        // launch windowing mode in options as a suggestion for future resolution.
-        int launchMode = options != null ? options.getLaunchWindowingMode()
-                : WINDOWING_MODE_UNDEFINED;
-        // hasInitialBounds is set if either activity options or layout has specified bounds. If
-        // that's set we'll skip some adjustments later to avoid overriding the initial bounds.
-        boolean hasInitialBounds = false;
-        final boolean canApplyFreeformPolicy = canApplyFreeformWindowPolicy(display, launchMode);
-        if (mSupervisor.canUseActivityOptionsLaunchBounds(options)
-                && (canApplyFreeformPolicy || canApplyPipWindowPolicy(launchMode))) {
-            hasInitialBounds = true;
-            launchMode = launchMode == WINDOWING_MODE_UNDEFINED
-                    ? WINDOWING_MODE_FREEFORM
-                    : launchMode;
-            outParams.mBounds.set(options.getLaunchBounds());
-            if (DEBUG) appendLog("activity-options-bounds=" + outParams.mBounds);
-        } else if (launchMode == WINDOWING_MODE_PINNED) {
-            // System controls PIP window's bounds, so don't apply launch bounds.
-            if (DEBUG) appendLog("empty-window-layout-for-pip");
-        } else if (launchMode == WINDOWING_MODE_FULLSCREEN) {
-            if (DEBUG) appendLog("activity-options-fullscreen=" + outParams.mBounds);
-        } else if (layout != null && canApplyFreeformPolicy) {
-            getLayoutBounds(display, root, layout, mTmpBounds);
-            if (!mTmpBounds.isEmpty()) {
-                launchMode = WINDOWING_MODE_FREEFORM;
-                outParams.mBounds.set(mTmpBounds);
-                hasInitialBounds = true;
-                if (DEBUG) appendLog("bounds-from-layout=" + outParams.mBounds);
-            } else {
-                if (DEBUG) appendLog("empty-window-layout");
-            }
-        }
-
-        // STEP 2.2: Check if previous modifier or the controller (referred as "callers" below) has
-        // some opinions on launch mode and launch bounds. If they have opinions and there is no
-        // initial bounds set in parameters. Note the check on display ID is also input param
-        // related because we always defer to callers' suggestion if there is no specific display ID
-        // in options or from source activity.
-        //
-        // If opinions from callers don't need any further resolution, we try to honor that as is as
-        // much as possible later.
-
-        // Flag to indicate if current param needs no further resolution. It's true it current
-        // param isn't freeform mode, or it already has launch bounds.
-        boolean fullyResolvedCurrentParam = false;
-        // We inherit launch params from previous modifiers or LaunchParamsController if options,
-        // layout and display conditions are not contradictory to their suggestions. It's important
-        // to carry over their values because LaunchParamsController doesn't automatically do that.
-        if (!currentParams.isEmpty() && !hasInitialBounds
-                && (!currentParams.hasPreferredDisplay()
-                    || displayId == currentParams.mPreferredDisplayId)) {
-            if (currentParams.hasWindowingMode()) {
-                launchMode = currentParams.mWindowingMode;
-                fullyResolvedCurrentParam = (launchMode != WINDOWING_MODE_FREEFORM);
-                if (DEBUG) {
-                    appendLog("inherit-" + WindowConfiguration.windowingModeToString(launchMode));
-                }
-            }
-
-            if (!currentParams.mBounds.isEmpty()) {
-                outParams.mBounds.set(currentParams.mBounds);
-                fullyResolvedCurrentParam = true;
-                if (DEBUG) appendLog("inherit-bounds=" + outParams.mBounds);
-            }
-        }
-
-        // STEP 2.3: Adjust launch parameters as needed for freeform display. We enforce the policy
-        // that legacy (pre-D) apps and those apps that can't handle multiple screen density well
-        // are forced to be maximized. The rest of this step is to define the default policy when
-        // there is no initial bounds or a fully resolved current params from callers. Right now we
-        // launch all possible tasks/activities that can handle freeform into freeform mode.
-        if (display.inFreeformWindowingMode()) {
-            if (launchMode == WINDOWING_MODE_PINNED) {
-                if (DEBUG) appendLog("picture-in-picture");
-            } else if (isTaskForcedMaximized(root)) {
-                // We're launching an activity that probably can't handle resizing nicely, so force
-                // it to be maximized even someone suggests launching it in freeform using launch
-                // options.
-                launchMode = WINDOWING_MODE_FULLSCREEN;
-                outParams.mBounds.setEmpty();
-                if (DEBUG) appendLog("forced-maximize");
-            } else if (fullyResolvedCurrentParam) {
-                // Don't adjust launch mode if that's inherited, except when we're launching an
-                // activity that should be forced to maximize.
-                if (DEBUG) appendLog("skip-adjustment-fully-resolved-params");
-            } else if (launchMode != WINDOWING_MODE_FREEFORM
-                    && (isNOrGreater(root) || isPreNResizeable(root))) {
-                // We're launching a pre-N and post-D activity that supports resizing, or a post-N
-                // activity. They can handle freeform nicely so launch them in freeform.
-                // Use undefined because we know we're in a freeform display.
-                launchMode = WINDOWING_MODE_UNDEFINED;
-                if (DEBUG) appendLog("should-be-freeform");
-            }
-        } else {
-            if (DEBUG) appendLog("non-freeform-display");
-        }
-        // If launch mode matches display windowing mode, let it inherit from display.
-        outParams.mWindowingMode = launchMode == display.getWindowingMode()
-                ? WINDOWING_MODE_UNDEFINED : launchMode;
-
-        // STEP 3: Determine final launch bounds based on resolved windowing mode and activity
-        // requested orientation. We set bounds to empty for fullscreen mode and keep bounds as is
-        // for all other windowing modes that's not freeform mode. One can read comments in
-        // relevant methods to further understand this step.
-        //
-        // We skip making adjustments if the params are fully resolved from previous results and
-        // trust that they are valid.
-        if (!fullyResolvedCurrentParam) {
-            final int resolvedMode = (launchMode != WINDOWING_MODE_UNDEFINED) ? launchMode
-                    : display.getWindowingMode();
-            if (source != null && source.inFreeformWindowingMode()
-                    && resolvedMode == WINDOWING_MODE_FREEFORM
-                    && outParams.mBounds.isEmpty()
-                    && source.getDisplayId() == display.mDisplayId) {
-                // Set bounds to be not very far from source activity.
-                cascadeBounds(source.getBounds(), display, outParams.mBounds);
-            }
-            getTaskBounds(root, display, layout, resolvedMode, hasInitialBounds, outParams.mBounds);
-        }
-
-        return RESULT_CONTINUE;
-    }
-
-    private int getPreferredLaunchDisplay(@Nullable ActivityOptions options,
-            ActivityRecord source, LaunchParams currentParams) {
-        int displayId = INVALID_DISPLAY;
-        final int optionLaunchId = options != null ? options.getLaunchDisplayId() : INVALID_DISPLAY;
-        if (optionLaunchId != INVALID_DISPLAY) {
-            if (DEBUG) appendLog("display-from-option=" + optionLaunchId);
-            displayId = optionLaunchId;
-        }
-
-        if (displayId == INVALID_DISPLAY && source != null) {
-            final int sourceDisplayId = source.getDisplayId();
-            if (DEBUG) appendLog("display-from-source=" + sourceDisplayId);
-            displayId = sourceDisplayId;
-        }
-
-        if (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) == null) {
-            displayId = INVALID_DISPLAY;
-        }
-        displayId = (displayId == INVALID_DISPLAY) ? currentParams.mPreferredDisplayId : displayId;
-
-        displayId = (displayId == INVALID_DISPLAY) ? DEFAULT_DISPLAY : displayId;
-
-        return displayId;
-    }
-
-    private boolean canApplyFreeformWindowPolicy(@NonNull ActivityDisplay display, int launchMode) {
-        return mSupervisor.mService.mSupportsFreeformWindowManagement
-                && (display.inFreeformWindowingMode() || launchMode == WINDOWING_MODE_FREEFORM);
-    }
-
-    private boolean canApplyPipWindowPolicy(int launchMode) {
-        return mSupervisor.mService.mSupportsPictureInPicture
-                && launchMode == WINDOWING_MODE_PINNED;
-    }
-
-    private void getLayoutBounds(@NonNull ActivityDisplay display, @NonNull ActivityRecord root,
-            @NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect outBounds) {
-        final int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
-        final int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
-        if (!windowLayout.hasSpecifiedSize() && verticalGravity == 0 && horizontalGravity == 0) {
-            outBounds.setEmpty();
-            return;
-        }
-
-        final Rect bounds = display.getBounds();
-        final int defaultWidth = bounds.width();
-        final int defaultHeight = bounds.height();
-
-        int width;
-        int height;
-        if (!windowLayout.hasSpecifiedSize()) {
-            outBounds.setEmpty();
-            getTaskBounds(root, display, windowLayout, WINDOWING_MODE_FREEFORM,
-                    /* hasInitialBounds */ false, outBounds);
-            width = outBounds.width();
-            height = outBounds.height();
-        } else {
-            width = defaultWidth;
-            if (windowLayout.width > 0 && windowLayout.width < defaultWidth) {
-                width = windowLayout.width;
-            } else if (windowLayout.widthFraction > 0 && windowLayout.widthFraction < 1.0f) {
-                width = (int) (width * windowLayout.widthFraction);
-            }
-
-            height = defaultHeight;
-            if (windowLayout.height > 0 && windowLayout.height < defaultHeight) {
-                height = windowLayout.height;
-            } else if (windowLayout.heightFraction > 0 && windowLayout.heightFraction < 1.0f) {
-                height = (int) (height * windowLayout.heightFraction);
-            }
-        }
-
-        final float fractionOfHorizontalOffset;
-        switch (horizontalGravity) {
-            case Gravity.LEFT:
-                fractionOfHorizontalOffset = 0f;
-                break;
-            case Gravity.RIGHT:
-                fractionOfHorizontalOffset = 1f;
-                break;
-            default:
-                fractionOfHorizontalOffset = 0.5f;
-        }
-
-        final float fractionOfVerticalOffset;
-        switch (verticalGravity) {
-            case Gravity.TOP:
-                fractionOfVerticalOffset = 0f;
-                break;
-            case Gravity.BOTTOM:
-                fractionOfVerticalOffset = 1f;
-                break;
-            default:
-                fractionOfVerticalOffset = 0.5f;
-        }
-
-        outBounds.set(0, 0, width, height);
-        final int xOffset = (int) (fractionOfHorizontalOffset * (defaultWidth - width));
-        final int yOffset = (int) (fractionOfVerticalOffset * (defaultHeight - height));
-        outBounds.offset(xOffset, yOffset);
-    }
-
-    /**
-     * Returns if task is forced to maximize.
-     *
-     * There are several cases where we force a task to maximize:
-     * 1) Root activity is targeting pre-Donut, which by default can't handle multiple screen
-     *    densities, so resizing will likely cause issues;
-     * 2) Root activity doesn't declare any flag that it supports any screen density, so resizing
-     *    may also cause issues;
-     * 3) Root activity is not resizeable, for which we shouldn't allow user resize it.
-     *
-     * @param root the root activity to check against.
-     * @return {@code true} if it should be forced to maximize; {@code false} otherwise.
-     */
-    private boolean isTaskForcedMaximized(@NonNull ActivityRecord root) {
-        if (root.appInfo.targetSdkVersion < Build.VERSION_CODES.DONUT
-                || (root.appInfo.flags & SUPPORTS_SCREEN_RESIZEABLE_MASK) == 0) {
-            return true;
-        }
-
-        return !root.isResizeable();
-    }
-
-    private boolean isNOrGreater(@NonNull ActivityRecord root) {
-        return root.appInfo.targetSdkVersion >= Build.VERSION_CODES.N;
-    }
-
-    /**
-     * Resolves activity requested orientation to 4 categories:
-     * 1) {@link ActivityInfo#SCREEN_ORIENTATION_LOCKED} indicating app wants to lock down
-     *    orientation;
-     * 2) {@link ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE} indicating app wants to be in landscape;
-     * 3) {@link ActivityInfo#SCREEN_ORIENTATION_PORTRAIT} indicating app wants to be in portrait;
-     * 4) {@link ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} indicating app can handle any
-     *    orientation.
-     *
-     * @param activity the activity to check
-     * @return corresponding resolved orientation value.
-     */
-    private int resolveOrientation(@NonNull ActivityRecord activity) {
-        int orientation = activity.info.screenOrientation;
-        switch (orientation) {
-            case SCREEN_ORIENTATION_NOSENSOR:
-            case SCREEN_ORIENTATION_LOCKED:
-                orientation = SCREEN_ORIENTATION_LOCKED;
-                break;
-            case SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
-            case SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
-            case SCREEN_ORIENTATION_USER_LANDSCAPE:
-            case SCREEN_ORIENTATION_LANDSCAPE:
-                if (DEBUG) appendLog("activity-requested-landscape");
-                orientation = SCREEN_ORIENTATION_LANDSCAPE;
-                break;
-            case SCREEN_ORIENTATION_SENSOR_PORTRAIT:
-            case SCREEN_ORIENTATION_REVERSE_PORTRAIT:
-            case SCREEN_ORIENTATION_USER_PORTRAIT:
-            case SCREEN_ORIENTATION_PORTRAIT:
-                if (DEBUG) appendLog("activity-requested-portrait");
-                orientation = SCREEN_ORIENTATION_PORTRAIT;
-                break;
-            default:
-                orientation = SCREEN_ORIENTATION_UNSPECIFIED;
-        }
-
-        return orientation;
-    }
-
-    private boolean isPreNResizeable(ActivityRecord root) {
-        return root.appInfo.targetSdkVersion < Build.VERSION_CODES.N && root.isResizeable();
-    }
-
-    private void cascadeBounds(@NonNull Rect srcBounds, @NonNull ActivityDisplay display,
-            @NonNull Rect outBounds) {
-        outBounds.set(srcBounds);
-        float density = (float) display.getConfiguration().densityDpi / DENSITY_DEFAULT;
-        final int defaultOffset = (int) (CASCADING_OFFSET_DP * density + 0.5f);
-
-        display.getBounds(mTmpBounds);
-        final int dx = Math.min(defaultOffset, Math.max(0, mTmpBounds.right - srcBounds.right));
-        final int dy = Math.min(defaultOffset, Math.max(0, mTmpBounds.bottom - srcBounds.bottom));
-        outBounds.offset(dx, dy);
-    }
-
-    private void getTaskBounds(@NonNull ActivityRecord root, @NonNull ActivityDisplay display,
-            @NonNull ActivityInfo.WindowLayout layout, int resolvedMode, boolean hasInitialBounds,
-            @NonNull Rect inOutBounds) {
-        if (resolvedMode == WINDOWING_MODE_FULLSCREEN) {
-            // We don't handle letterboxing here. Letterboxing will be handled by valid checks
-            // later.
-            inOutBounds.setEmpty();
-            if (DEBUG) appendLog("maximized-bounds");
-            return;
-        }
-
-        if (resolvedMode != WINDOWING_MODE_FREEFORM) {
-            // We don't apply freeform bounds adjustment to other windowing modes.
-            if (DEBUG) {
-                appendLog("skip-bounds-" + WindowConfiguration.windowingModeToString(resolvedMode));
-            }
-            return;
-        }
-
-        final int orientation = resolveOrientation(root, display, inOutBounds);
-        if (orientation != SCREEN_ORIENTATION_PORTRAIT
-                && orientation != SCREEN_ORIENTATION_LANDSCAPE) {
-            throw new IllegalStateException(
-                    "Orientation must be one of portrait or landscape, but it's "
-                    + ActivityInfo.screenOrientationToString(orientation));
-        }
-
-        // First we get the default size we want.
-        getDefaultFreeformSize(display, layout, orientation, mTmpBounds);
-        if (hasInitialBounds || sizeMatches(inOutBounds, mTmpBounds)) {
-            // We're here because either input parameters specified initial bounds, or the suggested
-            // bounds have the same size of the default freeform size. We should use the suggested
-            // bounds if possible -- so if app can handle the orientation we just use it, and if not
-            // we transpose the suggested bounds in-place.
-            if (orientation == orientationFromBounds(inOutBounds)) {
-                if (DEBUG) appendLog("freeform-size-orientation-match=" + inOutBounds);
-            } else {
-                // Meh, orientation doesn't match. Let's rotate inOutBounds in-place.
-                centerBounds(display, inOutBounds.height(), inOutBounds.width(), inOutBounds);
-                if (DEBUG) appendLog("freeform-orientation-mismatch=" + inOutBounds);
-            }
-        } else {
-            // We are here either because there is no suggested bounds, or the suggested bounds is
-            // a cascade from source activity. We should use the default freeform size and center it
-            // to the center of suggested bounds (or the display if no suggested bounds). The
-            // default size might be too big to center to source activity bounds in display, so we
-            // may need to move it back to the display.
-            centerBounds(display, mTmpBounds.width(), mTmpBounds.height(), inOutBounds);
-            adjustBoundsToFitInDisplay(display, inOutBounds);
-            if (DEBUG) appendLog("freeform-size-mismatch=" + inOutBounds);
-        }
-
-        // Lastly we adjust bounds to avoid conflicts with other tasks as much as possible.
-        adjustBoundsToAvoidConflict(display, inOutBounds);
-    }
-
-    private int resolveOrientation(@NonNull ActivityRecord root, @NonNull ActivityDisplay display,
-            @NonNull Rect bounds) {
-        int orientation = resolveOrientation(root);
-
-        if (orientation == SCREEN_ORIENTATION_LOCKED) {
-            orientation = bounds.isEmpty() ? display.getConfiguration().orientation
-                    : orientationFromBounds(bounds);
-            if (DEBUG) {
-                appendLog(bounds.isEmpty() ? "locked-orientation-from-display=" + orientation
-                        : "locked-orientation-from-bounds=" + bounds);
-            }
-        }
-
-        if (orientation == SCREEN_ORIENTATION_UNSPECIFIED) {
-            orientation = bounds.isEmpty() ? SCREEN_ORIENTATION_PORTRAIT
-                    : orientationFromBounds(bounds);
-            if (DEBUG) {
-                appendLog(bounds.isEmpty() ? "default-portrait"
-                        : "orientation-from-bounds=" + bounds);
-            }
-        }
-
-        return orientation;
-    }
-
-    private void getDefaultFreeformSize(@NonNull ActivityDisplay display,
-            @NonNull ActivityInfo.WindowLayout layout, int orientation, @NonNull Rect bounds) {
-        // Default size, which is letterboxing/pillarboxing in display. That's to say the large
-        // dimension of default size is the small dimension of display size, and the small dimension
-        // of default size is calculated to keep the same aspect ratio as the display's.
-        Rect displayBounds = display.getBounds();
-        final int portraitHeight = Math.min(displayBounds.width(), displayBounds.height());
-        final int otherDimension = Math.max(displayBounds.width(), displayBounds.height());
-        final int portraitWidth = (portraitHeight * portraitHeight) / otherDimension;
-        final int defaultWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitHeight
-                : portraitWidth;
-        final int defaultHeight = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitWidth
-                : portraitHeight;
-
-        // Get window size based on Nexus 5x screen, we assume that this is enough to show content
-        // of activities.
-        final float density = (float) display.getConfiguration().densityDpi / DENSITY_DEFAULT;
-        final int phonePortraitWidth = (int) (DEFAULT_PORTRAIT_PHONE_WIDTH_DP * density + 0.5f);
-        final int phonePortraitHeight = (int) (DEFAULT_PORTRAIT_PHONE_HEIGHT_DP * density + 0.5f);
-        final int phoneWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? phonePortraitHeight
-                : phonePortraitWidth;
-        final int phoneHeight = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? phonePortraitWidth
-                : phonePortraitHeight;
-
-        // Minimum layout requirements.
-        final int layoutMinWidth = (layout == null) ? -1 : layout.minWidth;
-        final int layoutMinHeight = (layout == null) ? -1 : layout.minHeight;
-
-        // Final result.
-        final int width = Math.min(defaultWidth, Math.max(phoneWidth, layoutMinWidth));
-        final int height = Math.min(defaultHeight, Math.max(phoneHeight, layoutMinHeight));
-
-        bounds.set(0, 0, width, height);
-    }
-
-    /**
-     * Gets centered bounds of width x height. If inOutBounds is not empty, the result bounds
-     * centers at its center or display's center if inOutBounds is empty.
-     */
-    private void centerBounds(@NonNull ActivityDisplay display, int width, int height,
-            @NonNull Rect inOutBounds) {
-        if (inOutBounds.isEmpty()) {
-            display.getBounds(inOutBounds);
-        }
-        final int left = inOutBounds.centerX() - width / 2;
-        final int top = inOutBounds.centerY() - height / 2;
-        inOutBounds.set(left, top, left + width, top + height);
-    }
-
-    private void adjustBoundsToFitInDisplay(@NonNull ActivityDisplay display,
-            @NonNull Rect inOutBounds) {
-        final Rect displayBounds = display.getBounds();
-
-        if (displayBounds.width() < inOutBounds.width()
-                || displayBounds.height() < inOutBounds.height()) {
-            // There is no way for us to fit the bounds in the display without changing width
-            // or height. Don't even try it.
-            return;
-        }
-
-        final int dx;
-        if (inOutBounds.right > displayBounds.right) {
-            // Right edge is out of display.
-            dx = displayBounds.right - inOutBounds.right;
-        } else if (inOutBounds.left < displayBounds.left) {
-            // Left edge is out of display.
-            dx = displayBounds.left - inOutBounds.left;
-        } else {
-            // Vertical edges are all in display.
-            dx = 0;
-        }
-
-        final int dy;
-        if (inOutBounds.top < displayBounds.top) {
-            // Top edge is out of display.
-            dy = displayBounds.top - inOutBounds.top;
-        } else if (inOutBounds.bottom > displayBounds.bottom) {
-            // Bottom edge is out of display.
-            dy = displayBounds.bottom - inOutBounds.bottom;
-        } else {
-            // Horizontal edges are all in display.
-            dy = 0;
-        }
-        inOutBounds.offset(dx, dy);
-    }
-
-    /**
-     * Adjusts input bounds to avoid conflict with existing tasks in the display.
-     *
-     * If the input bounds conflict with existing tasks, this method scans the bounds in a series of
-     * directions to find a location where the we can put the bounds in display without conflict
-     * with any other tasks.
-     *
-     * It doesn't try to adjust bounds that's not fully in the given display.
-     *
-     * @param display the display which tasks are to check
-     * @param inOutBounds the bounds used to input initial bounds and output result bounds
-     */
-    private void adjustBoundsToAvoidConflict(@NonNull ActivityDisplay display,
-            @NonNull Rect inOutBounds) {
-        final Rect displayBounds = display.getBounds();
-        if (!displayBounds.contains(inOutBounds)) {
-            // The initial bounds are already out of display. The scanning algorithm below doesn't
-            // work so well with them.
-            return;
-        }
-
-        final List<TaskRecord> tasksToCheck = new ArrayList<>();
-        for (int i = 0; i < display.getChildCount(); ++i) {
-            ActivityStack<?> stack = display.getChildAt(i);
-            if (!stack.inFreeformWindowingMode()) {
-                continue;
-            }
-
-            for (int j = 0; j < stack.getChildCount(); ++j) {
-                tasksToCheck.add(stack.getChildAt(j));
-            }
-        }
-
-        if (!boundsConflict(tasksToCheck, inOutBounds)) {
-            // Current proposal doesn't conflict with any task. Early return to avoid unnecessary
-            // calculation.
-            return;
-        }
-
-        calculateCandidateShiftDirections(displayBounds, inOutBounds);
-        for (int direction : mTmpDirections) {
-            if (direction == Gravity.NO_GRAVITY) {
-                // We exhausted candidate directions, give up.
-                break;
-            }
-
-            mTmpBounds.set(inOutBounds);
-            while (boundsConflict(tasksToCheck, mTmpBounds) && displayBounds.contains(mTmpBounds)) {
-                shiftBounds(direction, displayBounds, mTmpBounds);
-            }
-
-            if (!boundsConflict(tasksToCheck, mTmpBounds) && displayBounds.contains(mTmpBounds)) {
-                // Found a candidate. Just use this.
-                inOutBounds.set(mTmpBounds);
-                if (DEBUG) appendLog("avoid-bounds-conflict=" + inOutBounds);
-                return;
-            }
-
-            // Didn't find a conflict free bounds here. Try the next candidate direction.
-        }
-
-        // We failed to find a conflict free location. Just keep the original result.
-    }
-
-    /**
-     * Determines scanning directions and their priorities to avoid bounds conflict.
-     *
-     * @param availableBounds bounds that the result must be in
-     * @param initialBounds initial bounds when start scanning
-     */
-    private void calculateCandidateShiftDirections(@NonNull Rect availableBounds,
-            @NonNull Rect initialBounds) {
-        for (int i = 0; i < mTmpDirections.length; ++i) {
-            mTmpDirections[i] = Gravity.NO_GRAVITY;
-        }
-
-        final int oneThirdWidth = (2 * availableBounds.left + availableBounds.right) / 3;
-        final int twoThirdWidth = (availableBounds.left + 2 * availableBounds.right) / 3;
-        final int centerX = initialBounds.centerX();
-        if (centerX < oneThirdWidth) {
-            // Too close to left, just scan to the right.
-            mTmpDirections[0] = Gravity.RIGHT;
-            return;
-        } else if (centerX > twoThirdWidth) {
-            // Too close to right, just scan to the left.
-            mTmpDirections[0] = Gravity.LEFT;
-            return;
-        }
-
-        final int oneThirdHeight = (2 * availableBounds.top + availableBounds.bottom) / 3;
-        final int twoThirdHeight = (availableBounds.top + 2 * availableBounds.bottom) / 3;
-        final int centerY = initialBounds.centerY();
-        if (centerY < oneThirdHeight || centerY > twoThirdHeight) {
-            // Too close to top or bottom boundary and we're in the middle horizontally, scan
-            // horizontally in both directions.
-            mTmpDirections[0] = Gravity.RIGHT;
-            mTmpDirections[1] = Gravity.LEFT;
-            return;
-        }
-
-        // We're in the center region both horizontally and vertically. Scan in both directions of
-        // primary diagonal.
-        mTmpDirections[0] = Gravity.BOTTOM | Gravity.RIGHT;
-        mTmpDirections[1] = Gravity.TOP | Gravity.LEFT;
-    }
-
-    private boolean boundsConflict(@NonNull List<TaskRecord> tasks, @NonNull Rect bounds) {
-        for (TaskRecord task : tasks) {
-            final Rect taskBounds = task.getBounds();
-            final boolean leftClose = Math.abs(taskBounds.left - bounds.left)
-                    < BOUNDS_CONFLICT_THRESHOLD;
-            final boolean topClose = Math.abs(taskBounds.top - bounds.top)
-                    < BOUNDS_CONFLICT_THRESHOLD;
-            final boolean rightClose = Math.abs(taskBounds.right - bounds.right)
-                    < BOUNDS_CONFLICT_THRESHOLD;
-            final boolean bottomClose = Math.abs(taskBounds.bottom - bounds.bottom)
-                    < BOUNDS_CONFLICT_THRESHOLD;
-
-            if ((leftClose && topClose) || (leftClose && bottomClose) || (rightClose && topClose)
-                    || (rightClose && bottomClose)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    private void shiftBounds(int direction, @NonNull Rect availableRect,
-            @NonNull Rect inOutBounds) {
-        final int horizontalOffset;
-        switch (direction & Gravity.HORIZONTAL_GRAVITY_MASK) {
-            case Gravity.LEFT:
-                horizontalOffset = -Math.max(MINIMAL_STEP,
-                        availableRect.width() / STEP_DENOMINATOR);
-                break;
-            case Gravity.RIGHT:
-                horizontalOffset = Math.max(MINIMAL_STEP, availableRect.width() / STEP_DENOMINATOR);
-                break;
-            default:
-                horizontalOffset = 0;
-        }
-
-        final int verticalOffset;
-        switch (direction & Gravity.VERTICAL_GRAVITY_MASK) {
-            case Gravity.TOP:
-                verticalOffset = -Math.max(MINIMAL_STEP, availableRect.height() / STEP_DENOMINATOR);
-                break;
-            case Gravity.BOTTOM:
-                verticalOffset = Math.max(MINIMAL_STEP, availableRect.height() / STEP_DENOMINATOR);
-                break;
-            default:
-                verticalOffset = 0;
-        }
-
-        inOutBounds.offset(horizontalOffset, verticalOffset);
-    }
-
-    private void initLogBuilder(TaskRecord task, ActivityRecord activity) {
-        if (DEBUG) {
-            mLogBuilder = new StringBuilder("TaskLaunchParamsModifier:task=" + task
-                    + " activity=" + activity);
-        }
-    }
-
-    private void appendLog(String log) {
-        if (DEBUG) mLogBuilder.append(" ").append(log);
-    }
-
-    private void outputLog() {
-        if (DEBUG) Slog.d(TAG, mLogBuilder.toString());
-    }
-
-    private static int orientationFromBounds(Rect bounds) {
-        return bounds.width() > bounds.height() ? SCREEN_ORIENTATION_LANDSCAPE
-                : SCREEN_ORIENTATION_PORTRAIT;
-    }
-
-    private static boolean sizeMatches(Rect left, Rect right) {
-        return (Math.abs(right.width() - left.width()) < EPSILON)
-                && (Math.abs(right.height() - left.height()) < EPSILON);
-    }
-}
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
deleted file mode 100644
index fc5dfb4..0000000
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ /dev/null
@@ -1,641 +0,0 @@
-/*
- * Copyright (C) 2014 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.server.am;
-
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
-
-import android.annotation.NonNull;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Debug;
-import android.os.Environment;
-import android.os.FileUtils;
-import android.os.SystemClock;
-import android.util.ArraySet;
-import android.util.AtomicFile;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.Xml;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
-
-import libcore.io.IoUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * Persister that saves recent tasks into disk.
- */
-public class TaskPersister implements PersisterQueue.Listener {
-    static final String TAG = "TaskPersister";
-    static final boolean DEBUG = false;
-    static final String IMAGE_EXTENSION = ".png";
-
-    private static final String TASKS_DIRNAME = "recent_tasks";
-    private static final String TASK_FILENAME_SUFFIX = "_task.xml";
-    private static final String IMAGES_DIRNAME = "recent_images";
-    private static final String PERSISTED_TASK_IDS_FILENAME = "persisted_taskIds.txt";
-
-    private static final String TAG_TASK = "task";
-
-    private final ActivityTaskManagerService mService;
-    private final ActivityStackSupervisor mStackSupervisor;
-    private final RecentTasks mRecentTasks;
-    private final SparseArray<SparseBooleanArray> mTaskIdsInFile = new SparseArray<>();
-    private final File mTaskIdsDir;
-    // To lock file operations in TaskPersister
-    private final Object mIoLock = new Object();
-    private final PersisterQueue mPersisterQueue;
-
-    private final ArraySet<Integer> mTmpTaskIds = new ArraySet<>();
-
-    TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
-            ActivityTaskManagerService service, RecentTasks recentTasks) {
-
-        final File legacyImagesDir = new File(systemDir, IMAGES_DIRNAME);
-        if (legacyImagesDir.exists()) {
-            if (!FileUtils.deleteContents(legacyImagesDir) || !legacyImagesDir.delete()) {
-                Slog.i(TAG, "Failure deleting legacy images directory: " + legacyImagesDir);
-            }
-        }
-
-        final File legacyTasksDir = new File(systemDir, TASKS_DIRNAME);
-        if (legacyTasksDir.exists()) {
-            if (!FileUtils.deleteContents(legacyTasksDir) || !legacyTasksDir.delete()) {
-                Slog.i(TAG, "Failure deleting legacy tasks directory: " + legacyTasksDir);
-            }
-        }
-
-        mTaskIdsDir = new File(Environment.getDataDirectory(), "system_de");
-        mStackSupervisor = stackSupervisor;
-        mService = service;
-        mRecentTasks = recentTasks;
-        mPersisterQueue = new PersisterQueue();
-        mPersisterQueue.addListener(this);
-    }
-
-    @VisibleForTesting
-    TaskPersister(File workingDir) {
-        mTaskIdsDir = workingDir;
-        mStackSupervisor = null;
-        mService = null;
-        mRecentTasks = null;
-        mPersisterQueue = new PersisterQueue();
-        mPersisterQueue.addListener(this);
-    }
-
-    void onSystemReady() {
-        mPersisterQueue.startPersisting();
-    }
-
-    private void removeThumbnails(TaskRecord task) {
-        mPersisterQueue.removeItems(
-                item -> {
-                    File file = new File(item.mFilePath);
-                    return file.getName().startsWith(Integer.toString(task.taskId));
-                },
-                ImageWriteQueueItem.class);
-    }
-
-    @NonNull
-    SparseBooleanArray loadPersistedTaskIdsForUser(int userId) {
-        if (mTaskIdsInFile.get(userId) != null) {
-            return mTaskIdsInFile.get(userId).clone();
-        }
-        final SparseBooleanArray persistedTaskIds = new SparseBooleanArray();
-        synchronized (mIoLock) {
-            BufferedReader reader = null;
-            String line;
-            try {
-                reader = new BufferedReader(new FileReader(getUserPersistedTaskIdsFile(userId)));
-                while ((line = reader.readLine()) != null) {
-                    for (String taskIdString : line.split("\\s+")) {
-                        int id = Integer.parseInt(taskIdString);
-                        persistedTaskIds.put(id, true);
-                    }
-                }
-            } catch (FileNotFoundException e) {
-                // File doesn't exist. Ignore.
-            } catch (Exception e) {
-                Slog.e(TAG, "Error while reading taskIds file for user " + userId, e);
-            } finally {
-                IoUtils.closeQuietly(reader);
-            }
-        }
-        mTaskIdsInFile.put(userId, persistedTaskIds);
-        return persistedTaskIds.clone();
-    }
-
-
-    @VisibleForTesting
-    void writePersistedTaskIdsForUser(@NonNull SparseBooleanArray taskIds, int userId) {
-        if (userId < 0) {
-            return;
-        }
-        final File persistedTaskIdsFile = getUserPersistedTaskIdsFile(userId);
-        synchronized (mIoLock) {
-            BufferedWriter writer = null;
-            try {
-                writer = new BufferedWriter(new FileWriter(persistedTaskIdsFile));
-                for (int i = 0; i < taskIds.size(); i++) {
-                    if (taskIds.valueAt(i)) {
-                        writer.write(String.valueOf(taskIds.keyAt(i)));
-                        writer.newLine();
-                    }
-                }
-            } catch (Exception e) {
-                Slog.e(TAG, "Error while writing taskIds file for user " + userId, e);
-            } finally {
-                IoUtils.closeQuietly(writer);
-            }
-        }
-    }
-
-    void unloadUserDataFromMemory(int userId) {
-        mTaskIdsInFile.delete(userId);
-    }
-
-    void wakeup(TaskRecord task, boolean flush) {
-        synchronized (mPersisterQueue) {
-            if (task != null) {
-                final TaskWriteQueueItem item = mPersisterQueue.findLastItem(
-                        queueItem -> task == queueItem.mTask, TaskWriteQueueItem.class);
-                if (item != null && !task.inRecents) {
-                    removeThumbnails(task);
-                }
-
-                if (item == null && task.isPersistable) {
-                    mPersisterQueue.addItem(new TaskWriteQueueItem(task, mService), flush);
-                }
-            } else {
-                // Dummy. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is
-                // notified.
-                mPersisterQueue.addItem(PersisterQueue.EMPTY_ITEM, flush);
-            }
-            if (DEBUG) {
-                Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " Callers="
-                        + Debug.getCallers(4));
-            }
-        }
-
-        mPersisterQueue.yieldIfQueueTooDeep();
-    }
-
-    void flush() {
-        mPersisterQueue.flush();
-    }
-
-    void saveImage(Bitmap image, String filePath) {
-        synchronized (mPersisterQueue) {
-            final ImageWriteQueueItem item = mPersisterQueue.findLastItem(
-                    queueItem -> queueItem.mFilePath.equals(filePath), ImageWriteQueueItem.class);
-            if (item != null) {
-                // replace the Bitmap with the new one.
-                item.mImage = image;
-            } else {
-                mPersisterQueue.addItem(new ImageWriteQueueItem(filePath, image),
-                        /* flush */ false);
-            }
-            if (DEBUG) Slog.d(TAG, "saveImage: filePath=" + filePath + " now=" +
-                    SystemClock.uptimeMillis() + " Callers=" + Debug.getCallers(4));
-        }
-
-        mPersisterQueue.yieldIfQueueTooDeep();
-    }
-
-    Bitmap getTaskDescriptionIcon(String filePath) {
-        // See if it is in the write queue
-        final Bitmap icon = getImageFromWriteQueue(filePath);
-        if (icon != null) {
-            return icon;
-        }
-        return restoreImage(filePath);
-    }
-
-    private Bitmap getImageFromWriteQueue(String filePath) {
-        final ImageWriteQueueItem item = mPersisterQueue.findLastItem(
-                queueItem -> queueItem.mFilePath.equals(filePath), ImageWriteQueueItem.class);
-        return item != null ? item.mImage : null;
-    }
-
-    private String fileToString(File file) {
-        final String newline = System.lineSeparator();
-        try {
-            BufferedReader reader = new BufferedReader(new FileReader(file));
-            StringBuffer sb = new StringBuffer((int) file.length() * 2);
-            String line;
-            while ((line = reader.readLine()) != null) {
-                sb.append(line + newline);
-            }
-            reader.close();
-            return sb.toString();
-        } catch (IOException ioe) {
-            Slog.e(TAG, "Couldn't read file " + file.getName());
-            return null;
-        }
-    }
-
-    private TaskRecord taskIdToTask(int taskId, ArrayList<TaskRecord> tasks) {
-        if (taskId < 0) {
-            return null;
-        }
-        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = tasks.get(taskNdx);
-            if (task.taskId == taskId) {
-                return task;
-            }
-        }
-        Slog.e(TAG, "Restore affiliation error looking for taskId=" + taskId);
-        return null;
-    }
-
-    List<TaskRecord> restoreTasksForUserLocked(final int userId, SparseBooleanArray preaddedTasks) {
-        final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
-        ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
-
-        File userTasksDir = getUserTasksDir(userId);
-
-        File[] recentFiles = userTasksDir.listFiles();
-        if (recentFiles == null) {
-            Slog.e(TAG, "restoreTasksForUserLocked: Unable to list files from " + userTasksDir);
-            return tasks;
-        }
-
-        for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
-            File taskFile = recentFiles[taskNdx];
-            if (DEBUG) {
-                Slog.d(TAG, "restoreTasksForUserLocked: userId=" + userId
-                        + ", taskFile=" + taskFile.getName());
-            }
-
-            if (!taskFile.getName().endsWith(TASK_FILENAME_SUFFIX)) {
-                continue;
-            }
-            try {
-                final int taskId = Integer.parseInt(taskFile.getName().substring(
-                        0 /* beginIndex */,
-                        taskFile.getName().length() - TASK_FILENAME_SUFFIX.length()));
-                if (preaddedTasks.get(taskId, false)) {
-                    Slog.w(TAG, "Task #" + taskId +
-                            " has already been created so we don't restore again");
-                    continue;
-                }
-            } catch (NumberFormatException e) {
-                Slog.w(TAG, "Unexpected task file name", e);
-                continue;
-            }
-
-            BufferedReader reader = null;
-            boolean deleteFile = false;
-            try {
-                reader = new BufferedReader(new FileReader(taskFile));
-                final XmlPullParser in = Xml.newPullParser();
-                in.setInput(reader);
-
-                int event;
-                while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
-                        event != XmlPullParser.END_TAG) {
-                    final String name = in.getName();
-                    if (event == XmlPullParser.START_TAG) {
-                        if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: START_TAG name=" + name);
-                        if (TAG_TASK.equals(name)) {
-                            final TaskRecord task = TaskRecord.restoreFromXml(in, mStackSupervisor);
-                            if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: restored task="
-                                    + task);
-                            if (task != null) {
-                                // XXX Don't add to write queue... there is no reason to write
-                                // out the stuff we just read, if we don't write it we will
-                                // read the same thing again.
-                                // mWriteQueue.add(new TaskWriteQueueItem(task));
-
-                                final int taskId = task.taskId;
-                                if (mStackSupervisor.anyTaskForIdLocked(taskId,
-                                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
-                                    // Should not happen.
-                                    Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
-                                } else if (userId != task.userId) {
-                                    // Should not happen.
-                                    Slog.wtf(TAG, "Task with userId " + task.userId + " found in "
-                                            + userTasksDir.getAbsolutePath());
-                                } else {
-                                    // Looks fine.
-                                    mStackSupervisor.setNextTaskIdForUserLocked(taskId, userId);
-                                    task.isPersistable = true;
-                                    tasks.add(task);
-                                    recoveredTaskIds.add(taskId);
-                                }
-                            } else {
-                                Slog.e(TAG, "restoreTasksForUserLocked: Unable to restore taskFile="
-                                        + taskFile + ": " + fileToString(taskFile));
-                            }
-                        } else {
-                            Slog.wtf(TAG, "restoreTasksForUserLocked: Unknown xml event=" + event
-                                    + " name=" + name);
-                        }
-                    }
-                    XmlUtils.skipCurrentTag(in);
-                }
-            } catch (Exception e) {
-                Slog.wtf(TAG, "Unable to parse " + taskFile + ". Error ", e);
-                Slog.e(TAG, "Failing file: " + fileToString(taskFile));
-                deleteFile = true;
-            } finally {
-                IoUtils.closeQuietly(reader);
-                if (deleteFile) {
-                    if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
-                    taskFile.delete();
-                }
-            }
-        }
-
-        if (!DEBUG) {
-            removeObsoleteFiles(recoveredTaskIds, userTasksDir.listFiles());
-        }
-
-        // Fix up task affiliation from taskIds
-        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = tasks.get(taskNdx);
-            task.setPrevAffiliate(taskIdToTask(task.mPrevAffiliateTaskId, tasks));
-            task.setNextAffiliate(taskIdToTask(task.mNextAffiliateTaskId, tasks));
-        }
-
-        Collections.sort(tasks, new Comparator<TaskRecord>() {
-            @Override
-            public int compare(TaskRecord lhs, TaskRecord rhs) {
-                final long diff = rhs.mLastTimeMoved - lhs.mLastTimeMoved;
-                if (diff < 0) {
-                    return -1;
-                } else if (diff > 0) {
-                    return +1;
-                } else {
-                    return 0;
-                }
-            }
-        });
-        return tasks;
-    }
-
-    @Override
-    public void onPreProcessItem(boolean queueEmpty) {
-        // We can't lock mService while locking the queue, but we don't want to
-        // call removeObsoleteFiles before every item, only the last time
-        // before going to sleep. The risk is that we call removeObsoleteFiles()
-        // successively.
-        if (queueEmpty) {
-            if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
-            mTmpTaskIds.clear();
-            synchronized (mService.mGlobalLock) {
-                if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
-                mRecentTasks.getPersistableTaskIds(mTmpTaskIds);
-                mService.mWindowManager.removeObsoleteTaskFiles(mTmpTaskIds,
-                        mRecentTasks.usersWithRecentsLoadedLocked());
-            }
-            removeObsoleteFiles(mTmpTaskIds);
-        }
-        writeTaskIdsFiles();
-    }
-
-    private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
-        if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: persistentTaskIds=" + persistentTaskIds +
-                " files=" + files);
-        if (files == null) {
-            Slog.e(TAG, "File error accessing recents directory (directory doesn't exist?).");
-            return;
-        }
-        for (int fileNdx = 0; fileNdx < files.length; ++fileNdx) {
-            File file = files[fileNdx];
-            String filename = file.getName();
-            final int taskIdEnd = filename.indexOf('_');
-            if (taskIdEnd > 0) {
-                final int taskId;
-                try {
-                    taskId = Integer.parseInt(filename.substring(0, taskIdEnd));
-                    if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: Found taskId=" + taskId);
-                } catch (Exception e) {
-                    Slog.wtf(TAG, "removeObsoleteFiles: Can't parse file=" + file.getName());
-                    file.delete();
-                    continue;
-                }
-                if (!persistentTaskIds.contains(taskId)) {
-                    if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: deleting file=" + file.getName());
-                    file.delete();
-                }
-            }
-        }
-    }
-
-    private void writeTaskIdsFiles() {
-        SparseArray<SparseBooleanArray> changedTaskIdsPerUser = new SparseArray<>();
-        synchronized (mService.mGlobalLock) {
-            for (int userId : mRecentTasks.usersWithRecentsLoadedLocked()) {
-                SparseBooleanArray taskIdsToSave = mRecentTasks.getTaskIdsForUser(userId);
-                SparseBooleanArray persistedIdsInFile = mTaskIdsInFile.get(userId);
-                if (persistedIdsInFile != null && persistedIdsInFile.equals(taskIdsToSave)) {
-                    continue;
-                } else {
-                    SparseBooleanArray taskIdsToSaveCopy = taskIdsToSave.clone();
-                    mTaskIdsInFile.put(userId, taskIdsToSaveCopy);
-                    changedTaskIdsPerUser.put(userId, taskIdsToSaveCopy);
-                }
-            }
-        }
-        for (int i = 0; i < changedTaskIdsPerUser.size(); i++) {
-            writePersistedTaskIdsForUser(changedTaskIdsPerUser.valueAt(i),
-                    changedTaskIdsPerUser.keyAt(i));
-        }
-    }
-
-    private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) {
-        int[] candidateUserIds;
-        synchronized (mService.mGlobalLock) {
-            // Remove only from directories of the users who have recents in memory synchronized
-            // with persistent storage.
-            candidateUserIds = mRecentTasks.usersWithRecentsLoadedLocked();
-        }
-        for (int userId : candidateUserIds) {
-            removeObsoleteFiles(persistentTaskIds, getUserImagesDir(userId).listFiles());
-            removeObsoleteFiles(persistentTaskIds, getUserTasksDir(userId).listFiles());
-        }
-    }
-
-    static Bitmap restoreImage(String filename) {
-        if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename);
-        return BitmapFactory.decodeFile(filename);
-    }
-
-    private File getUserPersistedTaskIdsFile(int userId) {
-        File userTaskIdsDir = new File(mTaskIdsDir, String.valueOf(userId));
-        if (!userTaskIdsDir.exists() && !userTaskIdsDir.mkdirs()) {
-            Slog.e(TAG, "Error while creating user directory: " + userTaskIdsDir);
-        }
-        return new File(userTaskIdsDir, PERSISTED_TASK_IDS_FILENAME);
-    }
-
-    static File getUserTasksDir(int userId) {
-        File userTasksDir = new File(Environment.getDataSystemCeDirectory(userId), TASKS_DIRNAME);
-
-        if (!userTasksDir.exists()) {
-            if (!userTasksDir.mkdir()) {
-                Slog.e(TAG, "Failure creating tasks directory for user " + userId + ": "
-                        + userTasksDir);
-            }
-        }
-        return userTasksDir;
-    }
-
-    static File getUserImagesDir(int userId) {
-        return new File(Environment.getDataSystemCeDirectory(userId), IMAGES_DIRNAME);
-    }
-
-    private static boolean createParentDirectory(String filePath) {
-        File parentDir = new File(filePath).getParentFile();
-        return parentDir.exists() || parentDir.mkdirs();
-    }
-
-    private static class TaskWriteQueueItem implements PersisterQueue.WriteQueueItem {
-        private final ActivityTaskManagerService mService;
-        private final TaskRecord mTask;
-
-        TaskWriteQueueItem(TaskRecord task, ActivityTaskManagerService service) {
-            mTask = task;
-            mService = service;
-        }
-
-        private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
-            if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
-            final XmlSerializer xmlSerializer = new FastXmlSerializer();
-            StringWriter stringWriter = new StringWriter();
-            xmlSerializer.setOutput(stringWriter);
-
-            if (DEBUG) {
-                xmlSerializer.setFeature(
-                        "http://xmlpull.org/v1/doc/features.html#indent-output", true);
-            }
-
-            // save task
-            xmlSerializer.startDocument(null, true);
-
-            xmlSerializer.startTag(null, TAG_TASK);
-            task.saveToXml(xmlSerializer);
-            xmlSerializer.endTag(null, TAG_TASK);
-
-            xmlSerializer.endDocument();
-            xmlSerializer.flush();
-
-            return stringWriter;
-        }
-
-        @Override
-        public void process() {
-            // Write out one task.
-            StringWriter stringWriter = null;
-            TaskRecord task = mTask;
-            if (DEBUG) Slog.d(TAG, "Writing task=" + task);
-            synchronized (mService.mGlobalLock) {
-                if (task.inRecents) {
-                    // Still there.
-                    try {
-                        if (DEBUG) Slog.d(TAG, "Saving task=" + task);
-                        stringWriter = saveToXml(task);
-                    } catch (IOException e) {
-                    } catch (XmlPullParserException e) {
-                    }
-                }
-            }
-            if (stringWriter != null) {
-                // Write out xml file while not holding mService lock.
-                FileOutputStream file = null;
-                AtomicFile atomicFile = null;
-                try {
-                    atomicFile = new AtomicFile(new File(
-                            getUserTasksDir(task.userId),
-                            String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
-                    file = atomicFile.startWrite();
-                    file.write(stringWriter.toString().getBytes());
-                    file.write('\n');
-                    atomicFile.finishWrite(file);
-                } catch (IOException e) {
-                    if (file != null) {
-                        atomicFile.failWrite(file);
-                    }
-                    Slog.e(TAG,
-                            "Unable to open " + atomicFile + " for persisting. " + e);
-                }
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "TaskWriteQueueItem{task=" + mTask + "}";
-        }
-    }
-
-    private static class ImageWriteQueueItem implements PersisterQueue.WriteQueueItem {
-        final String mFilePath;
-        Bitmap mImage;
-
-        ImageWriteQueueItem(String filePath, Bitmap image) {
-            mFilePath = filePath;
-            mImage = image;
-        }
-
-        @Override
-        public void process() {
-            final String filePath = mFilePath;
-            if (!createParentDirectory(filePath)) {
-                Slog.e(TAG, "Error while creating images directory for file: " + filePath);
-                return;
-            }
-            final Bitmap bitmap = mImage;
-            if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
-            FileOutputStream imageFile = null;
-            try {
-                imageFile = new FileOutputStream(new File(filePath));
-                bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
-            } catch (Exception e) {
-                Slog.e(TAG, "saveImage: unable to save " + filePath, e);
-            } finally {
-                IoUtils.closeQuietly(imageFile);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "ImageWriteQueueItem{path=" + mFilePath
-                    + ", image=(" + mImage.getWidth() + "x" + mImage.getHeight() + ")}";
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
deleted file mode 100644
index 5f59163..0000000
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ /dev/null
@@ -1,2549 +0,0 @@
-/*
- * Copyright (C) 2006 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.server.am;
-
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
-import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
-import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
-import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityRecord.STARTING_WINDOW_SHOWN;
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING_TO_TOP;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
-import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.TaskRecordProto.ACTIVITIES;
-import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
-import static com.android.server.am.TaskRecordProto.BOUNDS;
-import static com.android.server.am.TaskRecordProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.TaskRecordProto.FULLSCREEN;
-import static com.android.server.am.TaskRecordProto.ID;
-import static com.android.server.am.TaskRecordProto.LAST_NON_FULLSCREEN_BOUNDS;
-import static com.android.server.am.TaskRecordProto.MIN_HEIGHT;
-import static com.android.server.am.TaskRecordProto.MIN_WIDTH;
-import static com.android.server.am.TaskRecordProto.ORIG_ACTIVITY;
-import static com.android.server.am.TaskRecordProto.REAL_ACTIVITY;
-import static com.android.server.am.TaskRecordProto.RESIZE_MODE;
-import static com.android.server.am.TaskRecordProto.STACK_ID;
-import static java.lang.Integer.MAX_VALUE;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityManager.TaskDescription;
-import android.app.ActivityManager.TaskSnapshot;
-import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.app.AppGlobals;
-import android.app.TaskInfo;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Debug;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.voice.IVoiceInteractionSession;
-import android.util.DisplayMetrics;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.util.XmlUtils;
-import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.wm.AppWindowContainerController;
-import com.android.server.wm.ConfigurationContainer;
-import com.android.server.wm.StackWindowController;
-import com.android.server.wm.TaskWindowContainerController;
-import com.android.server.wm.TaskWindowContainerListener;
-import com.android.server.wm.WindowManagerService;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Objects;
-
-// TODO: Make package private again once move to WM package is complete.
-public class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_ATM;
-    private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
-    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
-    private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
-    private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
-
-    private static final String ATTR_TASKID = "task_id";
-    private static final String TAG_INTENT = "intent";
-    private static final String TAG_AFFINITYINTENT = "affinity_intent";
-    private static final String ATTR_REALACTIVITY = "real_activity";
-    private static final String ATTR_REALACTIVITY_SUSPENDED = "real_activity_suspended";
-    private static final String ATTR_ORIGACTIVITY = "orig_activity";
-    private static final String TAG_ACTIVITY = "activity";
-    private static final String ATTR_AFFINITY = "affinity";
-    private static final String ATTR_ROOT_AFFINITY = "root_affinity";
-    private static final String ATTR_ROOTHASRESET = "root_has_reset";
-    private static final String ATTR_AUTOREMOVERECENTS = "auto_remove_recents";
-    private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
-    private static final String ATTR_USERID = "user_id";
-    private static final String ATTR_USER_SETUP_COMPLETE = "user_setup_complete";
-    private static final String ATTR_EFFECTIVE_UID = "effective_uid";
-    @Deprecated
-    private static final String ATTR_TASKTYPE = "task_type";
-    private static final String ATTR_LASTDESCRIPTION = "last_description";
-    private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
-    private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
-    private static final String ATTR_TASK_AFFILIATION = "task_affiliation";
-    private static final String ATTR_PREV_AFFILIATION = "prev_affiliation";
-    private static final String ATTR_NEXT_AFFILIATION = "next_affiliation";
-    private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
-    private static final String ATTR_CALLING_UID = "calling_uid";
-    private static final String ATTR_CALLING_PACKAGE = "calling_package";
-    private static final String ATTR_SUPPORTS_PICTURE_IN_PICTURE = "supports_picture_in_picture";
-    private static final String ATTR_RESIZE_MODE = "resize_mode";
-    private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
-    private static final String ATTR_MIN_WIDTH = "min_width";
-    private static final String ATTR_MIN_HEIGHT = "min_height";
-    private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version";
-
-    // Current version of the task record we persist. Used to check if we need to run any upgrade
-    // code.
-    private static final int PERSIST_TASK_VERSION = 1;
-
-    private static final int INVALID_MIN_SIZE = -1;
-
-    /**
-     * The modes to control how the stack is moved to the front when calling
-     * {@link TaskRecord#reparent}.
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            REPARENT_MOVE_STACK_TO_FRONT,
-            REPARENT_KEEP_STACK_AT_FRONT,
-            REPARENT_LEAVE_STACK_IN_PLACE
-    })
-    @interface ReparentMoveStackMode {}
-    // Moves the stack to the front if it was not at the front
-    static final int REPARENT_MOVE_STACK_TO_FRONT = 0;
-    // Only moves the stack to the front if it was focused or front most already
-    static final int REPARENT_KEEP_STACK_AT_FRONT = 1;
-    // Do not move the stack as a part of reparenting
-    static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
-
-    /**
-     * The factory used to create {@link TaskRecord}. This allows OEM subclass {@link TaskRecord}.
-     */
-    private static TaskRecordFactory sTaskRecordFactory;
-
-    final int taskId;       // Unique identifier for this task.
-    String affinity;        // The affinity name for this task, or null; may change identity.
-    String rootAffinity;    // Initial base affinity, or null; does not change from initial root.
-    final IVoiceInteractionSession voiceSession;    // Voice interaction session driving task
-    final IVoiceInteractor voiceInteractor;         // Associated interactor to provide to app
-    Intent intent;          // The original intent that started the task. Note that this value can
-                            // be null.
-    Intent affinityIntent;  // Intent of affinity-moved activity that started this task.
-    int effectiveUid;       // The current effective uid of the identity of this task.
-    ComponentName origActivity; // The non-alias activity component of the intent.
-    ComponentName realActivity; // The actual activity component that started the task.
-    boolean realActivitySuspended; // True if the actual activity component that started the
-                                   // task is suspended.
-    boolean inRecents;      // Actually in the recents list?
-    long lastActiveTime;    // Last time this task was active in the current device session,
-                            // including sleep. This time is initialized to the elapsed time when
-                            // restored from disk.
-    boolean isAvailable;    // Is the activity available to be launched?
-    boolean rootWasReset;   // True if the intent at the root of the task had
-                            // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
-    boolean autoRemoveRecents;  // If true, we should automatically remove the task from
-                                // recents when activity finishes
-    boolean askedCompatMode;// Have asked the user about compat mode for this task.
-    boolean hasBeenVisible; // Set if any activities in the task have been visible to the user.
-
-    String stringName;      // caching of toString() result.
-    int userId;             // user for which this task was created
-    boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity
-                                // was changed.
-
-    int numFullscreen;      // Number of fullscreen activities.
-
-    int mResizeMode;        // The resize mode of this task and its activities.
-                            // Based on the {@link ActivityInfo#resizeMode} of the root activity.
-    private boolean mSupportsPictureInPicture;  // Whether or not this task and its activities
-            // support PiP. Based on the {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE} flag
-            // of the root activity.
-    /** Can't be put in lockTask mode. */
-    final static int LOCK_TASK_AUTH_DONT_LOCK = 0;
-    /** Can enter app pinning with user approval. Can never start over existing lockTask task. */
-    final static int LOCK_TASK_AUTH_PINNABLE = 1;
-    /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */
-    final static int LOCK_TASK_AUTH_LAUNCHABLE = 2;
-    /** Can enter lockTask without user approval. Can start over existing lockTask task. */
-    final static int LOCK_TASK_AUTH_WHITELISTED = 3;
-    /** Priv-app that starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing
-     * lockTask task. */
-    final static int LOCK_TASK_AUTH_LAUNCHABLE_PRIV = 4;
-    int mLockTaskAuth = LOCK_TASK_AUTH_PINNABLE;
-
-    int mLockTaskUid = -1;  // The uid of the application that called startLockTask().
-
-    // This represents the last resolved activity values for this task
-    // NOTE: This value needs to be persisted with each task
-    TaskDescription lastTaskDescription = new TaskDescription();
-
-    /** List of all activities in the task arranged in history order */
-    final ArrayList<ActivityRecord> mActivities;
-
-    /** Current stack. Setter must always be used to update the value. */
-    private ActivityStack mStack;
-
-    /** The process that had previously hosted the root activity of this task.
-     * Used to know that we should try harder to keep this process around, in case the
-     * user wants to return to it. */
-    private WindowProcessController mRootProcess;
-
-    /** Takes on same value as first root activity */
-    boolean isPersistable = false;
-    int maxRecents;
-
-    /** Only used for persistable tasks, otherwise 0. The last time this task was moved. Used for
-     * determining the order when restoring. Sign indicates whether last task movement was to front
-     * (positive) or back (negative). Absolute value indicates time. */
-    long mLastTimeMoved = System.currentTimeMillis();
-
-    /** If original intent did not allow relinquishing task identity, save that information */
-    private boolean mNeverRelinquishIdentity = true;
-
-    // Used in the unique case where we are clearing the task in order to reuse it. In that case we
-    // do not want to delete the stack when the task goes empty.
-    private boolean mReuseTask = false;
-
-    CharSequence lastDescription; // Last description captured for this item.
-
-    int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
-    int mAffiliatedTaskColor; // color of the parent task affiliation.
-    TaskRecord mPrevAffiliate; // previous task in affiliated chain.
-    int mPrevAffiliateTaskId = INVALID_TASK_ID; // previous id for persistence.
-    TaskRecord mNextAffiliate; // next task in affiliated chain.
-    int mNextAffiliateTaskId = INVALID_TASK_ID; // next id for persistence.
-
-    // For relaunching the task from recents as though it was launched by the original launcher.
-    int mCallingUid;
-    String mCallingPackage;
-
-    final ActivityTaskManagerService mService;
-
-    private final Rect mTmpStableBounds = new Rect();
-    private final Rect mTmpNonDecorBounds = new Rect();
-    private final Rect mTmpRect = new Rect();
-
-    // Last non-fullscreen bounds the task was launched in or resized to.
-    // The information is persisted and used to determine the appropriate stack to launch the
-    // task into on restore.
-    Rect mLastNonFullscreenBounds = null;
-    // Minimal width and height of this task when it's resizeable. -1 means it should use the
-    // default minimal width/height.
-    int mMinWidth;
-    int mMinHeight;
-
-    // Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
-    // This number will be assigned when we evaluate OOM scores for all visible tasks.
-    int mLayerRank = -1;
-
-    /** Helper object used for updating override configuration. */
-    private Configuration mTmpConfig = new Configuration();
-
-    private TaskWindowContainerController mWindowContainerController;
-
-    /**
-     * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
-     * ActivityInfo, Intent, TaskDescription)} instead.
-     */
-    TaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info, Intent _intent,
-            IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
-        mService = service;
-        userId = UserHandle.getUserId(info.applicationInfo.uid);
-        taskId = _taskId;
-        lastActiveTime = SystemClock.elapsedRealtime();
-        mAffiliatedTaskId = _taskId;
-        voiceSession = _voiceSession;
-        voiceInteractor = _voiceInteractor;
-        isAvailable = true;
-        mActivities = new ArrayList<>();
-        mCallingUid = info.applicationInfo.uid;
-        mCallingPackage = info.packageName;
-        setIntent(_intent, info);
-        setMinDimensions(info);
-        touchActiveTime();
-        mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
-    }
-
-    /**
-     * Don't use constructor directly.
-     * Use {@link #create(ActivityTaskManagerService, int, ActivityInfo,
-     * Intent, IVoiceInteractionSession, IVoiceInteractor)} instead.
-     */
-    TaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info, Intent _intent,
-            TaskDescription _taskDescription) {
-        mService = service;
-        userId = UserHandle.getUserId(info.applicationInfo.uid);
-        taskId = _taskId;
-        lastActiveTime = SystemClock.elapsedRealtime();
-        mAffiliatedTaskId = _taskId;
-        voiceSession = null;
-        voiceInteractor = null;
-        isAvailable = true;
-        mActivities = new ArrayList<>();
-        mCallingUid = info.applicationInfo.uid;
-        mCallingPackage = info.packageName;
-        setIntent(_intent, info);
-        setMinDimensions(info);
-
-        isPersistable = true;
-        // Clamp to [1, max].
-        maxRecents = Math.min(Math.max(info.maxRecents, 1),
-                ActivityTaskManager.getMaxAppRecentsLimitStatic());
-
-        lastTaskDescription = _taskDescription;
-        touchActiveTime();
-        mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
-    }
-
-    /**
-     * Don't use constructor directly. This is only used by XML parser.
-     */
-    TaskRecord(ActivityTaskManagerService service, int _taskId, Intent _intent,
-            Intent _affinityIntent, String _affinity, String _rootAffinity,
-            ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
-            boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId,
-            int _effectiveUid, String _lastDescription, ArrayList<ActivityRecord> activities,
-            long lastTimeMoved, boolean neverRelinquishIdentity,
-            TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId,
-            int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-            int resizeMode, boolean supportsPictureInPicture, boolean _realActivitySuspended,
-            boolean userSetupComplete, int minWidth, int minHeight) {
-        mService = service;
-        taskId = _taskId;
-        intent = _intent;
-        affinityIntent = _affinityIntent;
-        affinity = _affinity;
-        rootAffinity = _rootAffinity;
-        voiceSession = null;
-        voiceInteractor = null;
-        realActivity = _realActivity;
-        realActivitySuspended = _realActivitySuspended;
-        origActivity = _origActivity;
-        rootWasReset = _rootWasReset;
-        isAvailable = true;
-        autoRemoveRecents = _autoRemoveRecents;
-        askedCompatMode = _askedCompatMode;
-        userId = _userId;
-        mUserSetupComplete = userSetupComplete;
-        effectiveUid = _effectiveUid;
-        lastActiveTime = SystemClock.elapsedRealtime();
-        lastDescription = _lastDescription;
-        mActivities = activities;
-        mLastTimeMoved = lastTimeMoved;
-        mNeverRelinquishIdentity = neverRelinquishIdentity;
-        lastTaskDescription = _lastTaskDescription;
-        mAffiliatedTaskId = taskAffiliation;
-        mAffiliatedTaskColor = taskAffiliationColor;
-        mPrevAffiliateTaskId = prevTaskId;
-        mNextAffiliateTaskId = nextTaskId;
-        mCallingUid = callingUid;
-        mCallingPackage = callingPackage;
-        mResizeMode = resizeMode;
-        mSupportsPictureInPicture = supportsPictureInPicture;
-        mMinWidth = minWidth;
-        mMinHeight = minHeight;
-        mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
-    }
-
-    TaskWindowContainerController getWindowContainerController() {
-        return mWindowContainerController;
-    }
-
-    void createWindowContainer(boolean onTop, boolean showForAllUsers) {
-        if (mWindowContainerController != null) {
-            throw new IllegalArgumentException("Window container=" + mWindowContainerController
-                    + " already created for task=" + this);
-        }
-
-        final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
-        setWindowContainerController(new TaskWindowContainerController(taskId, this,
-                getStack().getWindowContainerController(), userId, bounds,
-                mResizeMode, mSupportsPictureInPicture, onTop,
-                showForAllUsers, lastTaskDescription));
-    }
-
-    /**
-     * Should only be invoked from {@link #createWindowContainer(boolean, boolean)}.
-     */
-    @VisibleForTesting
-    protected void setWindowContainerController(TaskWindowContainerController controller) {
-        if (mWindowContainerController != null) {
-            throw new IllegalArgumentException("Window container=" + mWindowContainerController
-                    + " already created for task=" + this);
-        }
-
-        mWindowContainerController = controller;
-    }
-
-    void removeWindowContainer() {
-        mService.getLockTaskController().clearLockedTask(this);
-        mWindowContainerController.removeContainer();
-        if (!getWindowConfiguration().persistTaskBounds()) {
-            // Reset current bounds for task whose bounds shouldn't be persisted so it uses
-            // default configuration the next time it launches.
-            updateOverrideConfiguration(null);
-        }
-        mService.getTaskChangeNotificationController().notifyTaskRemoved(taskId);
-        mWindowContainerController = null;
-    }
-
-    @Override
-    public void onSnapshotChanged(TaskSnapshot snapshot) {
-        mService.getTaskChangeNotificationController().notifyTaskSnapshotChanged(taskId, snapshot);
-    }
-
-    void setResizeMode(int resizeMode) {
-        if (mResizeMode == resizeMode) {
-            return;
-        }
-        mResizeMode = resizeMode;
-        mWindowContainerController.setResizeable(resizeMode);
-        mService.mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-    }
-
-    void setTaskDockedResizing(boolean resizing) {
-        mWindowContainerController.setTaskDockedResizing(resizing);
-    }
-
-    // TODO: Consolidate this with the resize() method below.
-    @Override
-    public void requestResize(Rect bounds, int resizeMode) {
-        mService.resizeTask(taskId, bounds, resizeMode);
-    }
-
-    boolean resize(Rect bounds, int resizeMode, boolean preserveWindow, boolean deferResume) {
-        mService.mWindowManager.deferSurfaceLayout();
-
-        try {
-            if (!isResizeable()) {
-                Slog.w(TAG, "resizeTask: task " + this + " not resizeable.");
-                return true;
-            }
-
-            // If this is a forced resize, let it go through even if the bounds is not changing,
-            // as we might need a relayout due to surface size change (to/from fullscreen).
-            final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
-            if (equivalentOverrideBounds(bounds) && !forced) {
-                // Nothing to do here...
-                return true;
-            }
-
-            if (mWindowContainerController == null) {
-                // Task doesn't exist in window manager yet (e.g. was restored from recents).
-                // All we can do for now is update the bounds so it can be used when the task is
-                // added to window manager.
-                updateOverrideConfiguration(bounds);
-                if (!inFreeformWindowingMode()) {
-                    // re-restore the task so it can have the proper stack association.
-                    mService.mStackSupervisor.restoreRecentTaskLocked(this, null, !ON_TOP);
-                }
-                return true;
-            }
-
-            if (!canResizeToBounds(bounds)) {
-                throw new IllegalArgumentException("resizeTask: Can not resize task=" + this
-                        + " to bounds=" + bounds + " resizeMode=" + mResizeMode);
-            }
-
-            // Do not move the task to another stack here.
-            // This method assumes that the task is already placed in the right stack.
-            // we do not mess with that decision and we only do the resize!
-
-            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + taskId);
-
-            final boolean updatedConfig = updateOverrideConfiguration(bounds);
-            // This variable holds information whether the configuration didn't change in a significant
-
-            // way and the activity was kept the way it was. If it's false, it means the activity
-            // had
-            // to be relaunched due to configuration change.
-            boolean kept = true;
-            if (updatedConfig) {
-                final ActivityRecord r = topRunningActivityLocked();
-                if (r != null && !deferResume) {
-                    kept = r.ensureActivityConfiguration(0 /* globalChanges */,
-                            preserveWindow);
-                    // Preserve other windows for resizing because if resizing happens when there
-                    // is a dialog activity in the front, the activity that still shows some
-                    // content to the user will become black and cause flickers. Note in most cases
-                    // this won't cause tons of irrelevant windows being preserved because only
-                    // activities in this task may experience a bounds change. Configs for other
-                    // activities stay the same.
-                    mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0,
-                            preserveWindow);
-                    if (!kept) {
-                        mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-                    }
-                }
-            }
-            mWindowContainerController.resize(kept, forced);
-
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-            return kept;
-        } finally {
-            mService.mWindowManager.continueSurfaceLayout();
-        }
-    }
-
-    // TODO: Investigate combining with the resize() method above.
-    void resizeWindowContainer() {
-        mWindowContainerController.resize(false /* relayout */, false /* forced */);
-    }
-
-    void getWindowContainerBounds(Rect bounds) {
-        mWindowContainerController.getBounds(bounds);
-    }
-
-    /**
-     * Convenience method to reparent a task to the top or bottom position of the stack.
-     */
-    boolean reparent(ActivityStack preferredStack, boolean toTop,
-            @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
-            String reason) {
-        return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate, deferResume,
-                true /* schedulePictureInPictureModeChange */, reason);
-    }
-
-    /**
-     * Convenience method to reparent a task to the top or bottom position of the stack, with
-     * an option to skip scheduling the picture-in-picture mode change.
-     */
-    boolean reparent(ActivityStack preferredStack, boolean toTop,
-            @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
-            boolean schedulePictureInPictureModeChange, String reason) {
-        return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate,
-                deferResume, schedulePictureInPictureModeChange, reason);
-    }
-
-    /** Convenience method to reparent a task to a specific position of the stack. */
-    boolean reparent(ActivityStack preferredStack, int position,
-            @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
-            String reason) {
-        return reparent(preferredStack, position, moveStackMode, animate, deferResume,
-                true /* schedulePictureInPictureModeChange */, reason);
-    }
-
-    /**
-     * Reparents the task into a preferred stack, creating it if necessary.
-     *
-     * @param preferredStack the target stack to move this task
-     * @param position the position to place this task in the new stack
-     * @param animate whether or not we should wait for the new window created as a part of the
-     *            reparenting to be drawn and animated in
-     * @param moveStackMode whether or not to move the stack to the front always, only if it was
-     *            previously focused & in front, or never
-     * @param deferResume whether or not to update the visibility of other tasks and stacks that may
-     *            have changed as a result of this reparenting
-     * @param schedulePictureInPictureModeChange specifies whether or not to schedule the PiP mode
-     *            change. Callers may set this to false if they are explicitly scheduling PiP mode
-     *            changes themselves, like during the PiP animation
-     * @param reason the caller of this reparenting
-     * @return whether the task was reparented
-     */
-    // TODO: Inspect all call sites and change to just changing windowing mode of the stack vs.
-    // re-parenting the task. Can only be done when we are no longer using static stack Ids.
-    boolean reparent(ActivityStack preferredStack, int position,
-            @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
-            boolean schedulePictureInPictureModeChange, String reason) {
-        final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
-        final WindowManagerService windowManager = mService.mWindowManager;
-        final ActivityStack sourceStack = getStack();
-        final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack,
-                position == MAX_VALUE);
-        if (toStack == sourceStack) {
-            return false;
-        }
-        if (!canBeLaunchedOnDisplay(toStack.mDisplayId)) {
-            return false;
-        }
-
-        final int toStackWindowingMode = toStack.getWindowingMode();
-        final ActivityRecord topActivity = getTopActivity();
-
-        final boolean mightReplaceWindow = topActivity != null
-                && replaceWindowsOnTaskMove(getWindowingMode(), toStackWindowingMode);
-        if (mightReplaceWindow) {
-            // We are about to relaunch the activity because its configuration changed due to
-            // being maximized, i.e. size change. The activity will first remove the old window
-            // and then add a new one. This call will tell window manager about this, so it can
-            // preserve the old window until the new one is drawn. This prevents having a gap
-            // between the removal and addition, in which no window is visible. We also want the
-            // entrance of the new window to be properly animated.
-            // Note here we always set the replacing window first, as the flags might be needed
-            // during the relaunch. If we end up not doing any relaunch, we clear the flags later.
-            windowManager.setWillReplaceWindow(topActivity.appToken, animate);
-        }
-
-        windowManager.deferSurfaceLayout();
-        boolean kept = true;
-        try {
-            final ActivityRecord r = topRunningActivityLocked();
-            final boolean wasFocused = r != null && supervisor.isTopDisplayFocusedStack(sourceStack)
-                    && (topRunningActivityLocked() == r);
-            final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r;
-            final boolean wasPaused = r != null && sourceStack.mPausingActivity == r;
-
-            // In some cases the focused stack isn't the front stack. E.g. pinned stack.
-            // Whenever we are moving the top activity from the front stack we want to make sure to
-            // move the stack to the front.
-            final boolean wasFront = r != null && sourceStack.isTopStackOnDisplay()
-                    && (sourceStack.topRunningActivityLocked() == r);
-
-            // Adjust the position for the new parent stack as needed.
-            position = toStack.getAdjustedPositionForTask(this, position, null /* starting */);
-
-            // Must reparent first in window manager to avoid a situation where AM can delete the
-            // we are coming from in WM before we reparent because it became empty.
-            mWindowContainerController.reparent(toStack.getWindowContainerController(), position,
-                    moveStackMode == REPARENT_MOVE_STACK_TO_FRONT);
-
-            final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
-                    || (moveStackMode == REPARENT_KEEP_STACK_AT_FRONT && (wasFocused || wasFront));
-            // Move the task
-            sourceStack.removeTask(this, reason, moveStackToFront
-                    ? REMOVE_TASK_MODE_MOVING_TO_TOP : REMOVE_TASK_MODE_MOVING);
-            toStack.addTask(this, position, false /* schedulePictureInPictureModeChange */, reason);
-
-            if (schedulePictureInPictureModeChange) {
-                // Notify of picture-in-picture mode changes
-                supervisor.scheduleUpdatePictureInPictureModeIfNeeded(this, sourceStack);
-            }
-
-            // TODO: Ensure that this is actually necessary here
-            // Notify the voice session if required
-            if (voiceSession != null) {
-                try {
-                    voiceSession.taskStarted(intent, taskId);
-                } catch (RemoteException e) {
-                }
-            }
-
-            // If the task had focus before (or we're requested to move focus), move focus to the
-            // new stack by moving the stack to the front.
-            if (r != null) {
-                toStack.moveToFrontAndResumeStateIfNeeded(r, moveStackToFront, wasResumed,
-                        wasPaused, reason);
-            }
-            if (!animate) {
-                mService.mStackSupervisor.mNoAnimActivities.add(topActivity);
-            }
-
-            // We might trigger a configuration change. Save the current task bounds for freezing.
-            // TODO: Should this call be moved inside the resize method in WM?
-            toStack.prepareFreezingTaskBounds();
-
-            // Make sure the task has the appropriate bounds/size for the stack it is in.
-            final boolean toStackSplitScreenPrimary =
-                    toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-            final Rect configBounds = getOverrideBounds();
-            if ((toStackWindowingMode == WINDOWING_MODE_FULLSCREEN
-                    || toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
-                    && !Objects.equals(configBounds, toStack.getOverrideBounds())) {
-                kept = resize(toStack.getOverrideBounds(), RESIZE_MODE_SYSTEM, !mightReplaceWindow,
-                        deferResume);
-            } else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) {
-                Rect bounds = getLaunchBounds();
-                if (bounds == null) {
-                    mService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
-                    bounds = configBounds;
-                }
-                kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
-            } else if (toStackSplitScreenPrimary || toStackWindowingMode == WINDOWING_MODE_PINNED) {
-                if (toStackSplitScreenPrimary && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) {
-                    // Move recents to front so it is not behind home stack when going into docked
-                    // mode
-                    mService.mStackSupervisor.moveRecentsStackToFront(reason);
-                }
-                kept = resize(toStack.getOverrideBounds(), RESIZE_MODE_SYSTEM, !mightReplaceWindow,
-                        deferResume);
-            }
-        } finally {
-            windowManager.continueSurfaceLayout();
-        }
-
-        if (mightReplaceWindow) {
-            // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
-            // window), we need to clear the replace window settings. Otherwise, we schedule a
-            // timeout to remove the old window if the replacing window is not coming in time.
-            windowManager.scheduleClearWillReplaceWindows(topActivity.appToken, !kept);
-        }
-
-        if (!deferResume) {
-            // The task might have already been running and its visibility needs to be synchronized
-            // with the visibility of the stack / windows.
-            supervisor.ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
-            supervisor.resumeFocusedStacksTopActivitiesLocked();
-        }
-
-        // TODO: Handle incorrect request to move before the actual move, not after.
-        supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(),
-                DEFAULT_DISPLAY, toStack);
-
-        return (preferredStack == toStack);
-    }
-
-    /**
-     * @return True if the windows of tasks being moved to the target stack from the source stack
-     * should be replaced, meaning that window manager will keep the old window around until the new
-     * is ready.
-     */
-    private static boolean replaceWindowsOnTaskMove(
-            int sourceWindowingMode, int targetWindowingMode) {
-        return sourceWindowingMode == WINDOWING_MODE_FREEFORM
-                || targetWindowingMode == WINDOWING_MODE_FREEFORM;
-    }
-
-    void cancelWindowTransition() {
-        mWindowContainerController.cancelWindowTransition();
-    }
-
-    /**
-     * DO NOT HOLD THE ACTIVITY MANAGER LOCK WHEN CALLING THIS METHOD!
-     */
-    TaskSnapshot getSnapshot(boolean reducedResolution) {
-
-        // TODO: Move this to {@link TaskWindowContainerController} once recent tasks are more
-        // synchronized between AM and WM.
-        return mService.mWindowManager.getTaskSnapshot(taskId, userId, reducedResolution);
-    }
-
-    void touchActiveTime() {
-        lastActiveTime = SystemClock.elapsedRealtime();
-    }
-
-    long getInactiveDuration() {
-        return SystemClock.elapsedRealtime() - lastActiveTime;
-    }
-
-    /** Sets the original intent, and the calling uid and package. */
-    void setIntent(ActivityRecord r) {
-        mCallingUid = r.launchedFromUid;
-        mCallingPackage = r.launchedFromPackage;
-        setIntent(r.intent, r.info);
-        setLockTaskAuth(r);
-    }
-
-    /** Sets the original intent, _without_ updating the calling uid or package. */
-    private void setIntent(Intent _intent, ActivityInfo info) {
-        if (intent == null) {
-            mNeverRelinquishIdentity =
-                    (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
-        } else if (mNeverRelinquishIdentity) {
-            return;
-        }
-
-        affinity = info.taskAffinity;
-        if (intent == null) {
-            // If this task already has an intent associated with it, don't set the root
-            // affinity -- we don't want it changing after initially set, but the initially
-            // set value may be null.
-            rootAffinity = affinity;
-        }
-        effectiveUid = info.applicationInfo.uid;
-        stringName = null;
-
-        if (info.targetActivity == null) {
-            if (_intent != null) {
-                // If this Intent has a selector, we want to clear it for the
-                // recent task since it is not relevant if the user later wants
-                // to re-launch the app.
-                if (_intent.getSelector() != null || _intent.getSourceBounds() != null) {
-                    _intent = new Intent(_intent);
-                    _intent.setSelector(null);
-                    _intent.setSourceBounds(null);
-                }
-            }
-            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Setting Intent of " + this + " to " + _intent);
-            intent = _intent;
-            realActivity = _intent != null ? _intent.getComponent() : null;
-            origActivity = null;
-        } else {
-            ComponentName targetComponent = new ComponentName(
-                    info.packageName, info.targetActivity);
-            if (_intent != null) {
-                Intent targetIntent = new Intent(_intent);
-                targetIntent.setComponent(targetComponent);
-                targetIntent.setSelector(null);
-                targetIntent.setSourceBounds(null);
-                if (DEBUG_TASKS) Slog.v(TAG_TASKS,
-                        "Setting Intent of " + this + " to target " + targetIntent);
-                intent = targetIntent;
-                realActivity = targetComponent;
-                origActivity = _intent.getComponent();
-            } else {
-                intent = null;
-                realActivity = targetComponent;
-                origActivity = new ComponentName(info.packageName, info.name);
-            }
-        }
-
-        final int intentFlags = intent == null ? 0 : intent.getFlags();
-        if ((intentFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
-            // Once we are set to an Intent with this flag, we count this
-            // task as having a true root activity.
-            rootWasReset = true;
-        }
-        userId = UserHandle.getUserId(info.applicationInfo.uid);
-        mUserSetupComplete = Settings.Secure.getIntForUser(mService.mContext.getContentResolver(),
-                USER_SETUP_COMPLETE, 0, userId) != 0;
-        if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
-            // If the activity itself has requested auto-remove, then just always do it.
-            autoRemoveRecents = true;
-        } else if ((intentFlags & (FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS))
-                == FLAG_ACTIVITY_NEW_DOCUMENT) {
-            // If the caller has not asked for the document to be retained, then we may
-            // want to turn on auto-remove, depending on whether the target has set its
-            // own document launch mode.
-            if (info.documentLaunchMode != ActivityInfo.DOCUMENT_LAUNCH_NONE) {
-                autoRemoveRecents = false;
-            } else {
-                autoRemoveRecents = true;
-            }
-        } else {
-            autoRemoveRecents = false;
-        }
-        mResizeMode = info.resizeMode;
-        mSupportsPictureInPicture = info.supportsPictureInPicture();
-    }
-
-    /** Sets the original minimal width and height. */
-    private void setMinDimensions(ActivityInfo info) {
-        if (info != null && info.windowLayout != null) {
-            mMinWidth = info.windowLayout.minWidth;
-            mMinHeight = info.windowLayout.minHeight;
-        } else {
-            mMinWidth = INVALID_MIN_SIZE;
-            mMinHeight = INVALID_MIN_SIZE;
-        }
-    }
-
-    /**
-     * Return true if the input activity has the same intent filter as the intent this task
-     * record is based on (normally the root activity intent).
-     */
-    boolean isSameIntentFilter(ActivityRecord r) {
-        final Intent intent = new Intent(r.intent);
-        // Correct the activity intent for aliasing. The task record intent will always be based on
-        // the real activity that will be launched not the alias, so we need to use an intent with
-        // the component name pointing to the real activity not the alias in the activity record.
-        intent.setComponent(r.realActivity);
-        return intent.filterEquals(this.intent);
-    }
-
-    boolean returnsToHomeStack() {
-        final int returnHomeFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME;
-        return intent != null && (intent.getFlags() & returnHomeFlags) == returnHomeFlags;
-    }
-
-    void setPrevAffiliate(TaskRecord prevAffiliate) {
-        mPrevAffiliate = prevAffiliate;
-        mPrevAffiliateTaskId = prevAffiliate == null ? INVALID_TASK_ID : prevAffiliate.taskId;
-    }
-
-    void setNextAffiliate(TaskRecord nextAffiliate) {
-        mNextAffiliate = nextAffiliate;
-        mNextAffiliateTaskId = nextAffiliate == null ? INVALID_TASK_ID : nextAffiliate.taskId;
-    }
-
-    <T extends ActivityStack> T getStack() {
-        return (T) mStack;
-    }
-
-    /**
-     * Must be used for setting parent stack because it performs configuration updates.
-     * Must be called after adding task as a child to the stack.
-     */
-    void setStack(ActivityStack stack) {
-        if (stack != null && !stack.isInStackLocked(this)) {
-            throw new IllegalStateException("Task must be added as a Stack child first.");
-        }
-        final ActivityStack oldStack = mStack;
-        mStack = stack;
-
-        // If the new {@link TaskRecord} is from a different {@link ActivityStack}, remove this
-        // {@link ActivityRecord} from its current {@link ActivityStack}.
-
-        if (oldStack != mStack) {
-            for (int i = getChildCount() - 1; i >= 0; --i) {
-                final ActivityRecord activity = getChildAt(i);
-
-                if (oldStack != null) {
-                    oldStack.onActivityRemovedFromStack(activity);
-                }
-
-                if (mStack != null) {
-                    stack.onActivityAddedToStack(activity);
-                }
-            }
-        }
-
-        onParentChanged();
-    }
-
-    /**
-     * @return Id of current stack, {@link INVALID_STACK_ID} if no stack is set.
-     */
-    int getStackId() {
-        return mStack != null ? mStack.mStackId : INVALID_STACK_ID;
-    }
-
-    @Override
-    protected int getChildCount() {
-        return mActivities.size();
-    }
-
-    @Override
-    protected ActivityRecord getChildAt(int index) {
-        return mActivities.get(index);
-    }
-
-    @Override
-    protected ConfigurationContainer getParent() {
-        return mStack;
-    }
-
-    @Override
-    protected void onParentChanged() {
-        super.onParentChanged();
-        mService.mStackSupervisor.updateUIDsPresentOnDisplay();
-    }
-
-    // Close up recents linked list.
-    private void closeRecentsChain() {
-        if (mPrevAffiliate != null) {
-            mPrevAffiliate.setNextAffiliate(mNextAffiliate);
-        }
-        if (mNextAffiliate != null) {
-            mNextAffiliate.setPrevAffiliate(mPrevAffiliate);
-        }
-        setPrevAffiliate(null);
-        setNextAffiliate(null);
-    }
-
-    void removedFromRecents() {
-        closeRecentsChain();
-        if (inRecents) {
-            inRecents = false;
-            mService.notifyTaskPersisterLocked(this, false);
-        }
-
-        clearRootProcess();
-
-        // TODO: Use window container controller once tasks are better synced between AM and WM
-        mService.mWindowManager.notifyTaskRemovedFromRecents(taskId, userId);
-    }
-
-    void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) {
-        closeRecentsChain();
-        mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId;
-        mAffiliatedTaskColor = taskToAffiliateWith.mAffiliatedTaskColor;
-        // Find the end
-        while (taskToAffiliateWith.mNextAffiliate != null) {
-            final TaskRecord nextRecents = taskToAffiliateWith.mNextAffiliate;
-            if (nextRecents.mAffiliatedTaskId != mAffiliatedTaskId) {
-                Slog.e(TAG, "setTaskToAffiliateWith: nextRecents=" + nextRecents + " affilTaskId="
-                        + nextRecents.mAffiliatedTaskId + " should be " + mAffiliatedTaskId);
-                if (nextRecents.mPrevAffiliate == taskToAffiliateWith) {
-                    nextRecents.setPrevAffiliate(null);
-                }
-                taskToAffiliateWith.setNextAffiliate(null);
-                break;
-            }
-            taskToAffiliateWith = nextRecents;
-        }
-        taskToAffiliateWith.setNextAffiliate(this);
-        setPrevAffiliate(taskToAffiliateWith);
-        setNextAffiliate(null);
-    }
-
-    /** Returns the intent for the root activity for this task */
-    Intent getBaseIntent() {
-        return intent != null ? intent : affinityIntent;
-    }
-
-    /** Returns the first non-finishing activity from the root. */
-    ActivityRecord getRootActivity() {
-        for (int i = 0; i < mActivities.size(); i++) {
-            final ActivityRecord r = mActivities.get(i);
-            if (r.finishing) {
-                continue;
-            }
-            return r;
-        }
-        return null;
-    }
-
-    ActivityRecord getTopActivity() {
-        return getTopActivity(true /* includeOverlays */);
-    }
-
-    ActivityRecord getTopActivity(boolean includeOverlays) {
-        for (int i = mActivities.size() - 1; i >= 0; --i) {
-            final ActivityRecord r = mActivities.get(i);
-            if (r.finishing || (!includeOverlays && r.mTaskOverlay)) {
-                continue;
-            }
-            return r;
-        }
-        return null;
-    }
-
-    ActivityRecord topRunningActivityLocked() {
-        if (mStack != null) {
-            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = mActivities.get(activityNdx);
-                if (!r.finishing && r.okToShowLocked()) {
-                    return r;
-                }
-            }
-        }
-        return null;
-    }
-
-    boolean isVisible() {
-        for (int i = mActivities.size() - 1; i >= 0; --i) {
-            final ActivityRecord r = mActivities.get(i);
-            if (r.visible) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
-        if (mStack != null) {
-            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = mActivities.get(activityNdx);
-                if (!r.finishing && r.okToShowLocked() && r.visibleIgnoringKeyguard) {
-                    outActivities.add(r);
-                }
-            }
-        }
-    }
-
-    ActivityRecord topRunningActivityWithStartingWindowLocked() {
-        if (mStack != null) {
-            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = mActivities.get(activityNdx);
-                if (r.mStartingWindowState != STARTING_WINDOW_SHOWN
-                        || r.finishing || !r.okToShowLocked()) {
-                    continue;
-                }
-                return r;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Return the number of running activities, and the number of non-finishing/initializing
-     * activities in the provided {@param reportOut} respectively.
-     */
-    void getNumRunningActivities(TaskActivitiesReport reportOut) {
-        reportOut.reset();
-        for (int i = mActivities.size() - 1; i >= 0; --i) {
-            final ActivityRecord r = mActivities.get(i);
-            if (r.finishing) {
-                continue;
-            }
-
-            reportOut.base = r;
-
-            // Increment the total number of non-finishing activities
-            reportOut.numActivities++;
-
-            if (reportOut.top == null || (reportOut.top.isState(ActivityState.INITIALIZING))) {
-                reportOut.top = r;
-                // Reset the number of running activities until we hit the first non-initializing
-                // activity
-                reportOut.numRunning = 0;
-            }
-            if (r.attachedToProcess()) {
-                // Increment the number of actually running activities
-                reportOut.numRunning++;
-            }
-        }
-    }
-
-    boolean okToShowLocked() {
-        // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
-        // okay to show the activity when locked.
-        return mService.mStackSupervisor.isCurrentProfileLocked(userId)
-                || topRunningActivityLocked() != null;
-    }
-
-    /** Call after activity movement or finish to make sure that frontOfTask is set correctly */
-    final void setFrontOfTask() {
-        boolean foundFront = false;
-        final int numActivities = mActivities.size();
-        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
-            final ActivityRecord r = mActivities.get(activityNdx);
-            if (foundFront || r.finishing) {
-                r.frontOfTask = false;
-            } else {
-                r.frontOfTask = true;
-                // Set frontOfTask false for every following activity.
-                foundFront = true;
-            }
-        }
-        if (!foundFront && numActivities > 0) {
-            // All activities of this task are finishing. As we ought to have a frontOfTask
-            // activity, make the bottom activity front.
-            mActivities.get(0).frontOfTask = true;
-        }
-    }
-
-    /**
-     * Reorder the history stack so that the passed activity is brought to the front.
-     */
-    final void moveActivityToFrontLocked(ActivityRecord newTop) {
-        if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
-                "Removing and adding activity " + newTop
-                + " to stack at top callers=" + Debug.getCallers(4));
-
-        mActivities.remove(newTop);
-        mActivities.add(newTop);
-
-        // Make sure window manager is aware of the position change.
-        mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController);
-        updateEffectiveIntent();
-
-        setFrontOfTask();
-    }
-
-    void addActivityAtBottom(ActivityRecord r) {
-        addActivityAtIndex(0, r);
-    }
-
-    void addActivityToTop(ActivityRecord r) {
-        addActivityAtIndex(mActivities.size(), r);
-    }
-
-    @Override
-    /*@WindowConfiguration.ActivityType*/
-    public int getActivityType() {
-        final int applicationType = super.getActivityType();
-        if (applicationType != ACTIVITY_TYPE_UNDEFINED || mActivities.isEmpty()) {
-            return applicationType;
-        }
-        return mActivities.get(0).getActivityType();
-    }
-
-    /**
-     * Adds an activity {@param r} at the given {@param index}. The activity {@param r} must either
-     * be in the current task or unparented to any task.
-     */
-    void addActivityAtIndex(int index, ActivityRecord r) {
-        TaskRecord task = r.getTask();
-        if (task != null && task != this) {
-            throw new IllegalArgumentException("Can not add r=" + " to task=" + this
-                    + " current parent=" + task);
-        }
-
-        r.setTask(this);
-
-        // Remove r first, and if it wasn't already in the list and it's fullscreen, count it.
-        if (!mActivities.remove(r) && r.fullscreen) {
-            // Was not previously in list.
-            numFullscreen++;
-        }
-        // Only set this based on the first activity
-        if (mActivities.isEmpty()) {
-            if (r.getActivityType() == ACTIVITY_TYPE_UNDEFINED) {
-                // Normally non-standard activity type for the activity record will be set when the
-                // object is created, however we delay setting the standard application type until
-                // this point so that the task can set the type for additional activities added in
-                // the else condition below.
-                r.setActivityType(ACTIVITY_TYPE_STANDARD);
-            }
-            setActivityType(r.getActivityType());
-            isPersistable = r.isPersistable();
-            mCallingUid = r.launchedFromUid;
-            mCallingPackage = r.launchedFromPackage;
-            // Clamp to [1, max].
-            maxRecents = Math.min(Math.max(r.info.maxRecents, 1),
-                    ActivityTaskManager.getMaxAppRecentsLimitStatic());
-        } else {
-            // Otherwise make all added activities match this one.
-            r.setActivityType(getActivityType());
-        }
-
-        final int size = mActivities.size();
-
-        if (index == size && size > 0) {
-            final ActivityRecord top = mActivities.get(size - 1);
-            if (top.mTaskOverlay) {
-                // Place below the task overlay activity since the overlay activity should always
-                // be on top.
-                index--;
-            }
-        }
-
-        index = Math.min(size, index);
-        mActivities.add(index, r);
-
-        updateEffectiveIntent();
-        if (r.isPersistable()) {
-            mService.notifyTaskPersisterLocked(this, false);
-        }
-
-        // Sync. with window manager
-        updateOverrideConfigurationFromLaunchBounds();
-        final AppWindowContainerController appController = r.getWindowContainerController();
-        if (appController != null) {
-            // Only attempt to move in WM if the child has a controller. It is possible we haven't
-            // created controller for the activity we are starting yet.
-            mWindowContainerController.positionChildAt(appController, index);
-        }
-
-        // Make sure the list of display UID whitelists is updated
-        // now that this record is in a new task.
-        mService.mStackSupervisor.updateUIDsPresentOnDisplay();
-    }
-
-    /**
-     * Removes the specified activity from this task.
-     * @param r The {@link ActivityRecord} to remove.
-     * @return true if this was the last activity in the task.
-     */
-    boolean removeActivity(ActivityRecord r) {
-        return removeActivity(r, false /* reparenting */);
-    }
-
-    boolean removeActivity(ActivityRecord r, boolean reparenting) {
-        if (r.getTask() != this) {
-            throw new IllegalArgumentException(
-                    "Activity=" + r + " does not belong to task=" + this);
-        }
-
-        r.setTask(null /* task */, reparenting /* reparenting */);
-
-        if (mActivities.remove(r) && r.fullscreen) {
-            // Was previously in list.
-            numFullscreen--;
-        }
-        if (r.isPersistable()) {
-            mService.notifyTaskPersisterLocked(this, false);
-        }
-
-        if (inPinnedWindowingMode()) {
-            // We normally notify listeners of task stack changes on pause, however pinned stack
-            // activities are normally in the paused state so no notification will be sent there
-            // before the activity is removed. We send it here so instead.
-            mService.getTaskChangeNotificationController().notifyTaskStackChanged();
-        }
-
-        if (mActivities.isEmpty()) {
-            return !mReuseTask;
-        }
-        updateEffectiveIntent();
-        return false;
-    }
-
-    /**
-     * @return whether or not there are ONLY task overlay activities in the stack.
-     *         If {@param excludeFinishing} is set, then ignore finishing activities in the check.
-     *         If there are no task overlay activities, this call returns false.
-     */
-    boolean onlyHasTaskOverlayActivities(boolean excludeFinishing) {
-        int count = 0;
-        for (int i = mActivities.size() - 1; i >= 0; i--) {
-            final ActivityRecord r = mActivities.get(i);
-            if (excludeFinishing && r.finishing) {
-                continue;
-            }
-            if (!r.mTaskOverlay) {
-                return false;
-            }
-            count++;
-        }
-        return count > 0;
-    }
-
-    boolean autoRemoveFromRecents() {
-        // We will automatically remove the task either if it has explicitly asked for
-        // this, or it is empty and has never contained an activity that got shown to
-        // the user.
-        return autoRemoveRecents || (mActivities.isEmpty() && !hasBeenVisible);
-    }
-
-    /**
-     * Completely remove all activities associated with an existing
-     * task starting at a specified index.
-     */
-    final void performClearTaskAtIndexLocked(int activityNdx, boolean pauseImmediately,
-            String reason) {
-        int numActivities = mActivities.size();
-        for ( ; activityNdx < numActivities; ++activityNdx) {
-            final ActivityRecord r = mActivities.get(activityNdx);
-            if (r.finishing) {
-                continue;
-            }
-            if (mStack == null) {
-                // Task was restored from persistent storage.
-                r.takeFromHistory();
-                mActivities.remove(activityNdx);
-                --activityNdx;
-                --numActivities;
-            } else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED, null,
-                    reason, false, pauseImmediately)) {
-                --activityNdx;
-                --numActivities;
-            }
-        }
-    }
-
-    /**
-     * Completely remove all activities associated with an existing task.
-     */
-    void performClearTaskLocked() {
-        mReuseTask = true;
-        performClearTaskAtIndexLocked(0, !PAUSE_IMMEDIATELY, "clear-task-all");
-        mReuseTask = false;
-    }
-
-    ActivityRecord performClearTaskForReuseLocked(ActivityRecord newR, int launchFlags) {
-        mReuseTask = true;
-        final ActivityRecord result = performClearTaskLocked(newR, launchFlags);
-        mReuseTask = false;
-        return result;
-    }
-
-    /**
-     * Perform clear operation as requested by
-     * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
-     * stack to the given task, then look for
-     * an instance of that activity in the stack and, if found, finish all
-     * activities on top of it and return the instance.
-     *
-     * @param newR Description of the new activity being started.
-     * @return Returns the old activity that should be continued to be used,
-     * or null if none was found.
-     */
-    final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
-        int numActivities = mActivities.size();
-        for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
-            ActivityRecord r = mActivities.get(activityNdx);
-            if (r.finishing) {
-                continue;
-            }
-            if (r.realActivity.equals(newR.realActivity)) {
-                // Here it is!  Now finish everything in front...
-                final ActivityRecord ret = r;
-
-                for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
-                    r = mActivities.get(activityNdx);
-                    if (r.finishing) {
-                        continue;
-                    }
-                    ActivityOptions opts = r.takeOptionsLocked();
-                    if (opts != null) {
-                        ret.updateOptionsLocked(opts);
-                    }
-                    if (mStack != null && mStack.finishActivityLocked(
-                            r, Activity.RESULT_CANCELED, null, "clear-task-stack", false)) {
-                        --activityNdx;
-                        --numActivities;
-                    }
-                }
-
-                // Finally, if this is a normal launch mode (that is, not
-                // expecting onNewIntent()), then we will finish the current
-                // instance of the activity so a new fresh one can be started.
-                if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
-                        && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0
-                        && !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
-                    if (!ret.finishing) {
-                        if (mStack != null) {
-                            mStack.finishActivityLocked(
-                                    ret, Activity.RESULT_CANCELED, null, "clear-task-top", false);
-                        }
-                        return null;
-                    }
-                }
-
-                return ret;
-            }
-        }
-
-        return null;
-    }
-
-    void removeTaskActivitiesLocked(boolean pauseImmediately, String reason) {
-        // Just remove the entire task.
-        performClearTaskAtIndexLocked(0, pauseImmediately, reason);
-    }
-
-    String lockTaskAuthToString() {
-        switch (mLockTaskAuth) {
-            case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK";
-            case LOCK_TASK_AUTH_PINNABLE: return "LOCK_TASK_AUTH_PINNABLE";
-            case LOCK_TASK_AUTH_LAUNCHABLE: return "LOCK_TASK_AUTH_LAUNCHABLE";
-            case LOCK_TASK_AUTH_WHITELISTED: return "LOCK_TASK_AUTH_WHITELISTED";
-            case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return "LOCK_TASK_AUTH_LAUNCHABLE_PRIV";
-            default: return "unknown=" + mLockTaskAuth;
-        }
-    }
-
-    void setLockTaskAuth() {
-        setLockTaskAuth(getRootActivity());
-    }
-
-    private void setLockTaskAuth(@Nullable ActivityRecord r) {
-        if (r == null) {
-            mLockTaskAuth = LOCK_TASK_AUTH_PINNABLE;
-            return;
-        }
-
-        final String pkg = (realActivity != null) ? realActivity.getPackageName() : null;
-        final LockTaskController lockTaskController = mService.getLockTaskController();
-        switch (r.lockTaskLaunchMode) {
-            case LOCK_TASK_LAUNCH_MODE_DEFAULT:
-                mLockTaskAuth = lockTaskController.isPackageWhitelisted(userId, pkg)
-                        ? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
-                break;
-
-            case LOCK_TASK_LAUNCH_MODE_NEVER:
-                mLockTaskAuth = LOCK_TASK_AUTH_DONT_LOCK;
-                break;
-
-            case LOCK_TASK_LAUNCH_MODE_ALWAYS:
-                mLockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-                break;
-
-            case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
-                mLockTaskAuth = lockTaskController.isPackageWhitelisted(userId, pkg)
-                        ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
-                break;
-        }
-        if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this +
-                " mLockTaskAuth=" + lockTaskAuthToString());
-    }
-
-    private boolean isResizeable(boolean checkSupportsPip) {
-        return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
-                || (checkSupportsPip && mSupportsPictureInPicture));
-    }
-
-    boolean isResizeable() {
-        return isResizeable(true /* checkSupportsPip */);
-    }
-
-    @Override
-    public boolean supportsSplitScreenWindowingMode() {
-        // A task can not be docked even if it is considered resizeable because it only supports
-        // picture-in-picture mode but has a non-resizeable resizeMode
-        return super.supportsSplitScreenWindowingMode()
-                && mService.mSupportsSplitScreenMultiWindow
-                && (mService.mForceResizableActivities
-                        || (isResizeable(false /* checkSupportsPip */)
-                                && !ActivityInfo.isPreserveOrientationMode(mResizeMode)));
-    }
-
-    /**
-     * Check whether this task can be launched on the specified display.
-     *
-     * @param displayId Target display id.
-     * @return {@code true} if either it is the default display or this activity can be put on a
-     *         secondary display.
-     */
-    boolean canBeLaunchedOnDisplay(int displayId) {
-        return mService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
-                -1 /* don't check PID */, -1 /* don't check UID */, null /* activityInfo */);
-    }
-
-    /**
-     * Check that a given bounds matches the application requested orientation.
-     *
-     * @param bounds The bounds to be tested.
-     * @return True if the requested bounds are okay for a resizing request.
-     */
-    private boolean canResizeToBounds(Rect bounds) {
-        if (bounds == null || !inFreeformWindowingMode()) {
-            // Note: If not on the freeform workspace, we ignore the bounds.
-            return true;
-        }
-        final boolean landscape = bounds.width() > bounds.height();
-        final Rect configBounds = getOverrideBounds();
-        if (mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION) {
-            return configBounds.isEmpty()
-                    || landscape == (configBounds.width() > configBounds.height());
-        }
-        return (mResizeMode != RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY || !landscape)
-                && (mResizeMode != RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY || landscape);
-    }
-
-    /**
-     * @return {@code true} if the task is being cleared for the purposes of being reused.
-     */
-    boolean isClearingToReuseTask() {
-        return mReuseTask;
-    }
-
-    /**
-     * Find the activity in the history stack within the given task.  Returns
-     * the index within the history at which it's found, or < 0 if not found.
-     */
-    final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
-        final ComponentName realActivity = r.realActivity;
-        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-            ActivityRecord candidate = mActivities.get(activityNdx);
-            if (candidate.finishing) {
-                continue;
-            }
-            if (candidate.realActivity.equals(realActivity)) {
-                return candidate;
-            }
-        }
-        return null;
-    }
-
-    /** Updates the last task description values. */
-    void updateTaskDescription() {
-        // Traverse upwards looking for any break between main task activities and
-        // utility activities.
-        int activityNdx;
-        final int numActivities = mActivities.size();
-        final boolean relinquish = numActivities != 0 &&
-                (mActivities.get(0).info.flags & FLAG_RELINQUISH_TASK_IDENTITY) != 0;
-        for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
-                ++activityNdx) {
-            final ActivityRecord r = mActivities.get(activityNdx);
-            if (relinquish && (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
-                // This will be the top activity for determining taskDescription. Pre-inc to
-                // overcome initial decrement below.
-                ++activityNdx;
-                break;
-            }
-            if (r.intent != null &&
-                    (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
-                break;
-            }
-        }
-        if (activityNdx > 0) {
-            // Traverse downwards starting below break looking for set label, icon.
-            // Note that if there are activities in the task but none of them set the
-            // recent activity values, then we do not fall back to the last set
-            // values in the TaskRecord.
-            String label = null;
-            String iconFilename = null;
-            int iconResource = -1;
-            int colorPrimary = 0;
-            int colorBackground = 0;
-            int statusBarColor = 0;
-            int navigationBarColor = 0;
-            boolean topActivity = true;
-            for (--activityNdx; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = mActivities.get(activityNdx);
-                if (r.mTaskOverlay) {
-                    continue;
-                }
-                if (r.taskDescription != null) {
-                    if (label == null) {
-                        label = r.taskDescription.getLabel();
-                    }
-                    if (iconResource == -1) {
-                        iconResource = r.taskDescription.getIconResource();
-                    }
-                    if (iconFilename == null) {
-                        iconFilename = r.taskDescription.getIconFilename();
-                    }
-                    if (colorPrimary == 0) {
-                        colorPrimary = r.taskDescription.getPrimaryColor();
-                    }
-                    if (topActivity) {
-                        colorBackground = r.taskDescription.getBackgroundColor();
-                        statusBarColor = r.taskDescription.getStatusBarColor();
-                        navigationBarColor = r.taskDescription.getNavigationBarColor();
-                    }
-                }
-                topActivity = false;
-            }
-            lastTaskDescription = new TaskDescription(label, null, iconResource, iconFilename,
-                    colorPrimary, colorBackground, statusBarColor, navigationBarColor);
-            if (mWindowContainerController != null) {
-                mWindowContainerController.setTaskDescription(lastTaskDescription);
-            }
-            // Update the task affiliation color if we are the parent of the group
-            if (taskId == mAffiliatedTaskId) {
-                mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor();
-            }
-        }
-    }
-
-    int findEffectiveRootIndex() {
-        int effectiveNdx = 0;
-        final int topActivityNdx = mActivities.size() - 1;
-        for (int activityNdx = 0; activityNdx <= topActivityNdx; ++activityNdx) {
-            final ActivityRecord r = mActivities.get(activityNdx);
-            if (r.finishing) {
-                continue;
-            }
-            effectiveNdx = activityNdx;
-            if ((r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
-                break;
-            }
-        }
-        return effectiveNdx;
-    }
-
-    void updateEffectiveIntent() {
-        final int effectiveRootIndex = findEffectiveRootIndex();
-        final ActivityRecord r = mActivities.get(effectiveRootIndex);
-        setIntent(r);
-
-        // Update the task description when the activities change
-        updateTaskDescription();
-    }
-
-    private void adjustForMinimalTaskDimensions(Rect bounds) {
-        if (bounds == null) {
-            return;
-        }
-        int minWidth = mMinWidth;
-        int minHeight = mMinHeight;
-        // If the task has no requested minimal size, we'd like to enforce a minimal size
-        // so that the user can not render the task too small to manipulate. We don't need
-        // to do this for the pinned stack as the bounds are controlled by the system.
-        if (!inPinnedWindowingMode()) {
-            final int defaultMinSizeDp =
-                    mService.mStackSupervisor.mDefaultMinSizeOfResizeableTaskDp;
-            final ActivityDisplay display =
-                    mService.mStackSupervisor.getActivityDisplay(mStack.mDisplayId);
-            final float density =
-                    (float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT;
-            final int defaultMinSize = (int) (defaultMinSizeDp * density);
-
-            if (minWidth == INVALID_MIN_SIZE) {
-                minWidth = defaultMinSize;
-            }
-            if (minHeight == INVALID_MIN_SIZE) {
-                minHeight = defaultMinSize;
-            }
-        }
-        final boolean adjustWidth = minWidth > bounds.width();
-        final boolean adjustHeight = minHeight > bounds.height();
-        if (!(adjustWidth || adjustHeight)) {
-            return;
-        }
-
-        final Rect configBounds = getOverrideBounds();
-        if (adjustWidth) {
-            if (!configBounds.isEmpty() && bounds.right == configBounds.right) {
-                bounds.left = bounds.right - minWidth;
-            } else {
-                // Either left bounds match, or neither match, or the previous bounds were
-                // fullscreen and we default to keeping left.
-                bounds.right = bounds.left + minWidth;
-            }
-        }
-        if (adjustHeight) {
-            if (!configBounds.isEmpty() && bounds.bottom == configBounds.bottom) {
-                bounds.top = bounds.bottom - minHeight;
-            } else {
-                // Either top bounds match, or neither match, or the previous bounds were
-                // fullscreen and we default to keeping top.
-                bounds.bottom = bounds.top + minHeight;
-            }
-        }
-    }
-
-    /**
-     * @return a new Configuration for this Task, given the provided {@param bounds} and
-     *         {@param insetBounds}.
-     */
-    Configuration computeNewOverrideConfigurationForBounds(Rect bounds, Rect insetBounds) {
-        // Compute a new override configuration for the given bounds, if fullscreen bounds
-        // (bounds == null), then leave the override config unset
-        final Configuration newOverrideConfig = new Configuration();
-        if (bounds != null) {
-            newOverrideConfig.setTo(getOverrideConfiguration());
-            mTmpRect.set(bounds);
-            adjustForMinimalTaskDimensions(mTmpRect);
-            computeOverrideConfiguration(newOverrideConfig, mTmpRect, insetBounds,
-                    mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
-        }
-
-        return newOverrideConfig;
-    }
-
-    /**
-     * Update task's override configuration based on the bounds.
-     * @param bounds The bounds of the task.
-     * @return True if the override configuration was updated.
-     */
-    boolean updateOverrideConfiguration(Rect bounds) {
-        return updateOverrideConfiguration(bounds, null /* insetBounds */);
-    }
-
-    void setLastNonFullscreenBounds(Rect bounds) {
-        if (mLastNonFullscreenBounds == null) {
-            mLastNonFullscreenBounds = new Rect(bounds);
-        } else {
-            mLastNonFullscreenBounds.set(bounds);
-        }
-    }
-
-    /**
-     * Update task's override configuration based on the bounds.
-     * @param bounds The bounds of the task.
-     * @param insetBounds The bounds used to calculate the system insets, which is used here to
-     *                    subtract the navigation bar/status bar size from the screen size reported
-     *                    to the application. See {@link IActivityTaskManager#resizeDockedStack}.
-     * @return True if the override configuration was updated.
-     */
-    boolean updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
-        if (equivalentOverrideBounds(bounds)) {
-            return false;
-        }
-        final Rect currentBounds = getOverrideBounds();
-
-        mTmpConfig.setTo(getOverrideConfiguration());
-        final Configuration newConfig = getOverrideConfiguration();
-
-        final boolean matchParentBounds = bounds == null || bounds.isEmpty();
-        final boolean persistBounds = getWindowConfiguration().persistTaskBounds();
-        if (matchParentBounds) {
-            if (!currentBounds.isEmpty() && persistBounds) {
-                setLastNonFullscreenBounds(currentBounds);
-            }
-            setBounds(null);
-            newConfig.unset();
-        } else {
-            mTmpRect.set(bounds);
-            adjustForMinimalTaskDimensions(mTmpRect);
-            setBounds(mTmpRect);
-
-            if (mStack == null || persistBounds) {
-                setLastNonFullscreenBounds(getOverrideBounds());
-            }
-            computeOverrideConfiguration(newConfig, mTmpRect, insetBounds,
-                    mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
-        }
-        onOverrideConfigurationChanged(newConfig);
-        return !mTmpConfig.equals(newConfig);
-    }
-
-    /**
-     * This should be called when an child activity changes state. This should only
-     * be called from
-     * {@link ActivityRecord#setState(ActivityState, String)} .
-     * @param record The {@link ActivityRecord} whose state has changed.
-     * @param state The new state.
-     * @param reason The reason for the change.
-     */
-    void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) {
-        final ActivityStack parent = getStack();
-
-        if (parent != null) {
-            parent.onActivityStateChanged(record, state, reason);
-        }
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newParentConfig) {
-        final boolean wasInMultiWindowMode = inMultiWindowMode();
-        super.onConfigurationChanged(newParentConfig);
-        if (wasInMultiWindowMode != inMultiWindowMode()) {
-            mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this);
-        }
-        // TODO: Should also take care of Pip mode changes here.
-    }
-
-    /** Clears passed config and fills it with new override values. */
-    // TODO(b/36505427): TaskRecord.computeOverrideConfiguration() is a utility method that doesn't
-    // depend on task or stacks, but uses those object to get the display to base the calculation
-    // on. Probably best to centralize calculations like this in ConfigurationContainer.
-    void computeOverrideConfiguration(Configuration config, Rect bounds, Rect insetBounds,
-            boolean overrideWidth, boolean overrideHeight) {
-        mTmpNonDecorBounds.set(bounds);
-        mTmpStableBounds.set(bounds);
-
-        config.unset();
-        final Configuration parentConfig = getParent().getConfiguration();
-
-        final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
-
-        if (mStack != null) {
-            final StackWindowController stackController = mStack.getWindowContainerController();
-            stackController.adjustConfigurationForBounds(bounds, insetBounds,
-                    mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density,
-                    config, parentConfig, getWindowingMode());
-        } else {
-            throw new IllegalArgumentException("Expected stack when calculating override config");
-        }
-
-        config.orientation = (config.screenWidthDp <= config.screenHeightDp)
-                ? Configuration.ORIENTATION_PORTRAIT
-                : Configuration.ORIENTATION_LANDSCAPE;
-
-        // For calculating screen layout, we need to use the non-decor inset screen area for the
-        // calculation for compatibility reasons, i.e. screen area without system bars that could
-        // never go away in Honeycomb.
-        final int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
-        final int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
-        // We're only overriding LONG, SIZE and COMPAT parts of screenLayout, so we start override
-        // calculation with partial default.
-        // Reducing the screen layout starting from its parent config.
-        final int sl = parentConfig.screenLayout &
-                (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
-        final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
-        final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
-        config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
-    }
-
-    Rect updateOverrideConfigurationFromLaunchBounds() {
-        final Rect bounds = getLaunchBounds();
-        updateOverrideConfiguration(bounds);
-        if (bounds != null && !bounds.isEmpty()) {
-            // TODO: Review if we actually want to do this - we are setting the launch bounds
-            // directly here.
-            bounds.set(getOverrideBounds());
-        }
-        return bounds;
-    }
-
-    /** Updates the task's bounds and override configuration to match what is expected for the
-     * input stack. */
-    void updateOverrideConfigurationForStack(ActivityStack inStack) {
-        if (mStack != null && mStack == inStack) {
-            return;
-        }
-
-        if (inStack.inFreeformWindowingMode()) {
-            if (!isResizeable()) {
-                throw new IllegalArgumentException("Can not position non-resizeable task="
-                        + this + " in stack=" + inStack);
-            }
-            if (!matchParentBounds()) {
-                return;
-            }
-            if (mLastNonFullscreenBounds != null) {
-                updateOverrideConfiguration(mLastNonFullscreenBounds);
-            } else {
-                mService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
-            }
-        } else {
-            updateOverrideConfiguration(inStack.getOverrideBounds());
-        }
-    }
-
-    /** Returns the bounds that should be used to launch this task. */
-    Rect getLaunchBounds() {
-        if (mStack == null) {
-            return null;
-        }
-
-        final int windowingMode = getWindowingMode();
-        if (!isActivityTypeStandardOrUndefined()
-                || windowingMode == WINDOWING_MODE_FULLSCREEN
-                || (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) {
-            return isResizeable() ? mStack.getOverrideBounds() : null;
-        } else if (!getWindowConfiguration().persistTaskBounds()) {
-            return mStack.getOverrideBounds();
-        }
-        return mLastNonFullscreenBounds;
-    }
-
-    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
-        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-            final ActivityRecord r = mActivities.get(activityNdx);
-            if (r.visible) {
-                r.showStartingWindow(null /* prev */, false /* newTask */, taskSwitch);
-            }
-        }
-    }
-
-    void setRootProcess(WindowProcessController proc) {
-        clearRootProcess();
-        if (intent != null &&
-                (intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0) {
-            mRootProcess = proc;
-            mRootProcess.addRecentTask(this);
-        }
-    }
-
-    void clearRootProcess() {
-        if (mRootProcess != null) {
-            mRootProcess.removeRecentTask(this);
-            mRootProcess = null;
-        }
-    }
-
-    void clearAllPendingOptions() {
-        for (int i = getChildCount() - 1; i >= 0; i--) {
-            getChildAt(i).clearOptionsLocked(false /* withAbort */);
-        }
-    }
-
-    /**
-     * Fills in a {@link TaskInfo} with information from this task.
-     * @param info the {@link TaskInfo} to fill in
-     * @param reuseActivitiesReport a temporary activities report that we can reuse to fetch the
-     *                              running activities
-     */
-    void fillTaskInfo(TaskInfo info, TaskActivitiesReport reuseActivitiesReport) {
-        getNumRunningActivities(reuseActivitiesReport);
-        info.userId = userId;
-        info.stackId = getStackId();
-        info.taskId = taskId;
-        info.isRunning = getTopActivity() != null;
-        info.baseIntent = getBaseIntent();
-        info.baseActivity = reuseActivitiesReport.base != null
-                ? reuseActivitiesReport.base.intent.getComponent()
-                : null;
-        info.topActivity = reuseActivitiesReport.top != null
-                ? reuseActivitiesReport.top.intent.getComponent()
-                : null;
-        info.origActivity = origActivity;
-        info.realActivity = realActivity;
-        info.numActivities = reuseActivitiesReport.numActivities;
-        info.lastActiveTime = lastActiveTime;
-        info.taskDescription = new ActivityManager.TaskDescription(lastTaskDescription);
-        info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
-        info.resizeMode = mResizeMode;
-        info.configuration.setTo(getConfiguration());
-    }
-
-    void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.print("userId="); pw.print(userId);
-                pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
-                pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
-                pw.print(" mUserSetupComplete="); pw.print(mUserSetupComplete);
-                pw.print(" mCallingPackage="); pw.println(mCallingPackage);
-        if (affinity != null || rootAffinity != null) {
-            pw.print(prefix); pw.print("affinity="); pw.print(affinity);
-            if (affinity == null || !affinity.equals(rootAffinity)) {
-                pw.print(" root="); pw.println(rootAffinity);
-            } else {
-                pw.println();
-            }
-        }
-        if (voiceSession != null || voiceInteractor != null) {
-            pw.print(prefix); pw.print("VOICE: session=0x");
-            pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
-            pw.print(" interactor=0x");
-            pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor)));
-        }
-        if (intent != null) {
-            StringBuilder sb = new StringBuilder(128);
-            sb.append(prefix); sb.append("intent={");
-            intent.toShortString(sb, false, true, false, true);
-            sb.append('}');
-            pw.println(sb.toString());
-        }
-        if (affinityIntent != null) {
-            StringBuilder sb = new StringBuilder(128);
-            sb.append(prefix); sb.append("affinityIntent={");
-            affinityIntent.toShortString(sb, false, true, false, true);
-            sb.append('}');
-            pw.println(sb.toString());
-        }
-        if (origActivity != null) {
-            pw.print(prefix); pw.print("origActivity=");
-            pw.println(origActivity.flattenToShortString());
-        }
-        if (realActivity != null) {
-            pw.print(prefix); pw.print("realActivity=");
-            pw.println(realActivity.flattenToShortString());
-        }
-        if (autoRemoveRecents || isPersistable || !isActivityTypeStandard() || numFullscreen != 0) {
-            pw.print(prefix); pw.print("autoRemoveRecents="); pw.print(autoRemoveRecents);
-                    pw.print(" isPersistable="); pw.print(isPersistable);
-                    pw.print(" numFullscreen="); pw.print(numFullscreen);
-                    pw.print(" activityType="); pw.println(getActivityType());
-        }
-        if (rootWasReset || mNeverRelinquishIdentity || mReuseTask
-                || mLockTaskAuth != LOCK_TASK_AUTH_PINNABLE) {
-            pw.print(prefix); pw.print("rootWasReset="); pw.print(rootWasReset);
-                    pw.print(" mNeverRelinquishIdentity="); pw.print(mNeverRelinquishIdentity);
-                    pw.print(" mReuseTask="); pw.print(mReuseTask);
-                    pw.print(" mLockTaskAuth="); pw.println(lockTaskAuthToString());
-        }
-        if (mAffiliatedTaskId != taskId || mPrevAffiliateTaskId != INVALID_TASK_ID
-                || mPrevAffiliate != null || mNextAffiliateTaskId != INVALID_TASK_ID
-                || mNextAffiliate != null) {
-            pw.print(prefix); pw.print("affiliation="); pw.print(mAffiliatedTaskId);
-                    pw.print(" prevAffiliation="); pw.print(mPrevAffiliateTaskId);
-                    pw.print(" (");
-                    if (mPrevAffiliate == null) {
-                        pw.print("null");
-                    } else {
-                        pw.print(Integer.toHexString(System.identityHashCode(mPrevAffiliate)));
-                    }
-                    pw.print(") nextAffiliation="); pw.print(mNextAffiliateTaskId);
-                    pw.print(" (");
-                    if (mNextAffiliate == null) {
-                        pw.print("null");
-                    } else {
-                        pw.print(Integer.toHexString(System.identityHashCode(mNextAffiliate)));
-                    }
-                    pw.println(")");
-        }
-        pw.print(prefix); pw.print("Activities="); pw.println(mActivities);
-        if (!askedCompatMode || !inRecents || !isAvailable) {
-            pw.print(prefix); pw.print("askedCompatMode="); pw.print(askedCompatMode);
-                    pw.print(" inRecents="); pw.print(inRecents);
-                    pw.print(" isAvailable="); pw.println(isAvailable);
-        }
-        if (lastDescription != null) {
-            pw.print(prefix); pw.print("lastDescription="); pw.println(lastDescription);
-        }
-        if (mRootProcess != null) {
-            pw.print(prefix); pw.print("mRootProcess="); pw.println(mRootProcess);
-        }
-        pw.print(prefix); pw.print("stackId="); pw.println(getStackId());
-        pw.print(prefix + "hasBeenVisible=" + hasBeenVisible);
-                pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode));
-                pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture);
-                pw.print(" isResizeable=" + isResizeable());
-                pw.print(" lastActiveTime=" + lastActiveTime);
-                pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        if (stringName != null) {
-            sb.append(stringName);
-            sb.append(" U=");
-            sb.append(userId);
-            sb.append(" StackId=");
-            sb.append(getStackId());
-            sb.append(" sz=");
-            sb.append(mActivities.size());
-            sb.append('}');
-            return sb.toString();
-        }
-        sb.append("TaskRecord{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(" #");
-        sb.append(taskId);
-        if (affinity != null) {
-            sb.append(" A=");
-            sb.append(affinity);
-        } else if (intent != null) {
-            sb.append(" I=");
-            sb.append(intent.getComponent().flattenToShortString());
-        } else if (affinityIntent != null && affinityIntent.getComponent() != null) {
-            sb.append(" aI=");
-            sb.append(affinityIntent.getComponent().flattenToShortString());
-        } else {
-            sb.append(" ??");
-        }
-        stringName = sb.toString();
-        return toString();
-    }
-
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
-        proto.write(ID, taskId);
-        for (int i = mActivities.size() - 1; i >= 0; i--) {
-            ActivityRecord activity = mActivities.get(i);
-            activity.writeToProto(proto, ACTIVITIES);
-        }
-        proto.write(STACK_ID, mStack.mStackId);
-        if (mLastNonFullscreenBounds != null) {
-            mLastNonFullscreenBounds.writeToProto(proto, LAST_NON_FULLSCREEN_BOUNDS);
-        }
-        if (realActivity != null) {
-            proto.write(REAL_ACTIVITY, realActivity.flattenToShortString());
-        }
-        if (origActivity != null) {
-            proto.write(ORIG_ACTIVITY, origActivity.flattenToShortString());
-        }
-        proto.write(ACTIVITY_TYPE, getActivityType());
-        proto.write(RESIZE_MODE, mResizeMode);
-        // TODO: Remove, no longer needed with windowingMode.
-        proto.write(FULLSCREEN, matchParentBounds());
-
-        if (!matchParentBounds()) {
-            final Rect bounds = getOverrideBounds();
-            bounds.writeToProto(proto, BOUNDS);
-        }
-        proto.write(MIN_WIDTH, mMinWidth);
-        proto.write(MIN_HEIGHT, mMinHeight);
-        proto.end(token);
-    }
-
-    /**
-     * See {@link #getNumRunningActivities(TaskActivitiesReport)}.
-     */
-    static class TaskActivitiesReport {
-        int numRunning;
-        int numActivities;
-        ActivityRecord top;
-        ActivityRecord base;
-
-        void reset() {
-            numRunning = numActivities = 0;
-            top = base = null;
-        }
-    }
-
-    /**
-     * Saves this {@link TaskRecord} to XML using given serializer.
-     */
-    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
-        if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
-
-        out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
-        if (realActivity != null) {
-            out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
-        }
-        out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended));
-        if (origActivity != null) {
-            out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
-        }
-        // Write affinity, and root affinity if it is different from affinity.
-        // We use the special string "@" for a null root affinity, so we can identify
-        // later whether we were given a root affinity or should just make it the
-        // same as the affinity.
-        if (affinity != null) {
-            out.attribute(null, ATTR_AFFINITY, affinity);
-            if (!affinity.equals(rootAffinity)) {
-                out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
-            }
-        } else if (rootAffinity != null) {
-            out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
-        }
-        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
-        out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
-        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
-        out.attribute(null, ATTR_USERID, String.valueOf(userId));
-        out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
-        out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
-        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
-        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
-        if (lastDescription != null) {
-            out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
-        }
-        if (lastTaskDescription != null) {
-            lastTaskDescription.saveToXml(out);
-        }
-        out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
-        out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
-        out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
-        out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
-        out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
-        out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
-        out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
-        out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
-                String.valueOf(mSupportsPictureInPicture));
-        if (mLastNonFullscreenBounds != null) {
-            out.attribute(
-                    null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString());
-        }
-        out.attribute(null, ATTR_MIN_WIDTH, String.valueOf(mMinWidth));
-        out.attribute(null, ATTR_MIN_HEIGHT, String.valueOf(mMinHeight));
-        out.attribute(null, ATTR_PERSIST_TASK_VERSION, String.valueOf(PERSIST_TASK_VERSION));
-
-        if (affinityIntent != null) {
-            out.startTag(null, TAG_AFFINITYINTENT);
-            affinityIntent.saveToXml(out);
-            out.endTag(null, TAG_AFFINITYINTENT);
-        }
-
-        if (intent != null) {
-            out.startTag(null, TAG_INTENT);
-            intent.saveToXml(out);
-            out.endTag(null, TAG_INTENT);
-        }
-
-        final ArrayList<ActivityRecord> activities = mActivities;
-        final int numActivities = activities.size();
-        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
-            final ActivityRecord r = activities.get(activityNdx);
-            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
-                    ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
-                            | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) &&
-                            activityNdx > 0) {
-                // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
-                break;
-            }
-            out.startTag(null, TAG_ACTIVITY);
-            r.saveToXml(out);
-            out.endTag(null, TAG_ACTIVITY);
-        }
-    }
-
-    @VisibleForTesting
-    static TaskRecordFactory getTaskRecordFactory() {
-        if (sTaskRecordFactory == null) {
-            setTaskRecordFactory(new TaskRecordFactory());
-        }
-        return sTaskRecordFactory;
-    }
-
-    static void setTaskRecordFactory(TaskRecordFactory factory) {
-        sTaskRecordFactory = factory;
-    }
-
-    static TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
-            Intent intent, IVoiceInteractionSession voiceSession,
-            IVoiceInteractor voiceInteractor) {
-        return getTaskRecordFactory().create(
-                service, taskId, info, intent, voiceSession, voiceInteractor);
-    }
-
-    static TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
-            Intent intent, TaskDescription taskDescription) {
-        return getTaskRecordFactory().create(service, taskId, info, intent, taskDescription);
-    }
-
-    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
-            throws IOException, XmlPullParserException {
-        return getTaskRecordFactory().restoreFromXml(in, stackSupervisor);
-    }
-
-    /**
-     * A factory class used to create {@link TaskRecord} or its subclass if any. This can be
-     * specified when system boots by setting it with
-     * {@link #setTaskRecordFactory(TaskRecordFactory)}.
-     */
-    static class TaskRecordFactory {
-
-        TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
-                Intent intent, IVoiceInteractionSession voiceSession,
-                IVoiceInteractor voiceInteractor) {
-            return new TaskRecord(
-                    service, taskId, info, intent, voiceSession, voiceInteractor);
-        }
-
-        TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
-                Intent intent, TaskDescription taskDescription) {
-            return new TaskRecord(service, taskId, info, intent, taskDescription);
-        }
-
-        /**
-         * Should only be used when we're restoring {@link TaskRecord} from storage.
-         */
-        TaskRecord create(ActivityTaskManagerService service, int taskId, Intent intent,
-                Intent affinityIntent, String affinity, String rootAffinity,
-                ComponentName realActivity, ComponentName origActivity, boolean rootWasReset,
-                boolean autoRemoveRecents, boolean askedCompatMode, int userId,
-                int effectiveUid, String lastDescription, ArrayList<ActivityRecord> activities,
-                long lastTimeMoved, boolean neverRelinquishIdentity,
-                TaskDescription lastTaskDescription, int taskAffiliation, int prevTaskId,
-                int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-                int resizeMode, boolean supportsPictureInPicture, boolean realActivitySuspended,
-                boolean userSetupComplete, int minWidth, int minHeight) {
-            return new TaskRecord(service, taskId, intent, affinityIntent, affinity,
-                    rootAffinity, realActivity, origActivity, rootWasReset, autoRemoveRecents,
-                    askedCompatMode, userId, effectiveUid, lastDescription, activities,
-                    lastTimeMoved, neverRelinquishIdentity, lastTaskDescription, taskAffiliation,
-                    prevTaskId, nextTaskId, taskAffiliationColor, callingUid, callingPackage,
-                    resizeMode, supportsPictureInPicture, realActivitySuspended, userSetupComplete,
-                    minWidth, minHeight);
-        }
-
-        TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
-                throws IOException, XmlPullParserException {
-            Intent intent = null;
-            Intent affinityIntent = null;
-            ArrayList<ActivityRecord> activities = new ArrayList<>();
-            ComponentName realActivity = null;
-            boolean realActivitySuspended = false;
-            ComponentName origActivity = null;
-            String affinity = null;
-            String rootAffinity = null;
-            boolean hasRootAffinity = false;
-            boolean rootHasReset = false;
-            boolean autoRemoveRecents = false;
-            boolean askedCompatMode = false;
-            int taskType = 0;
-            int userId = 0;
-            boolean userSetupComplete = true;
-            int effectiveUid = -1;
-            String lastDescription = null;
-            long lastTimeOnTop = 0;
-            boolean neverRelinquishIdentity = true;
-            int taskId = INVALID_TASK_ID;
-            final int outerDepth = in.getDepth();
-            TaskDescription taskDescription = new TaskDescription();
-            int taskAffiliation = INVALID_TASK_ID;
-            int taskAffiliationColor = 0;
-            int prevTaskId = INVALID_TASK_ID;
-            int nextTaskId = INVALID_TASK_ID;
-            int callingUid = -1;
-            String callingPackage = "";
-            int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
-            boolean supportsPictureInPicture = false;
-            Rect lastNonFullscreenBounds = null;
-            int minWidth = INVALID_MIN_SIZE;
-            int minHeight = INVALID_MIN_SIZE;
-            int persistTaskVersion = 0;
-
-            for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
-                final String attrName = in.getAttributeName(attrNdx);
-                final String attrValue = in.getAttributeValue(attrNdx);
-                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
-                        attrName + " value=" + attrValue);
-                switch (attrName) {
-                    case ATTR_TASKID:
-                        if (taskId == INVALID_TASK_ID) taskId = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_REALACTIVITY:
-                        realActivity = ComponentName.unflattenFromString(attrValue);
-                        break;
-                    case ATTR_REALACTIVITY_SUSPENDED:
-                        realActivitySuspended = Boolean.valueOf(attrValue);
-                        break;
-                    case ATTR_ORIGACTIVITY:
-                        origActivity = ComponentName.unflattenFromString(attrValue);
-                        break;
-                    case ATTR_AFFINITY:
-                        affinity = attrValue;
-                        break;
-                    case ATTR_ROOT_AFFINITY:
-                        rootAffinity = attrValue;
-                        hasRootAffinity = true;
-                        break;
-                    case ATTR_ROOTHASRESET:
-                        rootHasReset = Boolean.parseBoolean(attrValue);
-                        break;
-                    case ATTR_AUTOREMOVERECENTS:
-                        autoRemoveRecents = Boolean.parseBoolean(attrValue);
-                        break;
-                    case ATTR_ASKEDCOMPATMODE:
-                        askedCompatMode = Boolean.parseBoolean(attrValue);
-                        break;
-                    case ATTR_USERID:
-                        userId = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_USER_SETUP_COMPLETE:
-                        userSetupComplete = Boolean.parseBoolean(attrValue);
-                        break;
-                    case ATTR_EFFECTIVE_UID:
-                        effectiveUid = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_TASKTYPE:
-                        taskType = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_LASTDESCRIPTION:
-                        lastDescription = attrValue;
-                        break;
-                    case ATTR_LASTTIMEMOVED:
-                        lastTimeOnTop = Long.parseLong(attrValue);
-                        break;
-                    case ATTR_NEVERRELINQUISH:
-                        neverRelinquishIdentity = Boolean.parseBoolean(attrValue);
-                        break;
-                    case ATTR_TASK_AFFILIATION:
-                        taskAffiliation = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_PREV_AFFILIATION:
-                        prevTaskId = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_NEXT_AFFILIATION:
-                        nextTaskId = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_TASK_AFFILIATION_COLOR:
-                        taskAffiliationColor = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_CALLING_UID:
-                        callingUid = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_CALLING_PACKAGE:
-                        callingPackage = attrValue;
-                        break;
-                    case ATTR_RESIZE_MODE:
-                        resizeMode = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_SUPPORTS_PICTURE_IN_PICTURE:
-                        supportsPictureInPicture = Boolean.parseBoolean(attrValue);
-                        break;
-                    case ATTR_NON_FULLSCREEN_BOUNDS:
-                        lastNonFullscreenBounds = Rect.unflattenFromString(attrValue);
-                        break;
-                    case ATTR_MIN_WIDTH:
-                        minWidth = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_MIN_HEIGHT:
-                        minHeight = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_PERSIST_TASK_VERSION:
-                        persistTaskVersion = Integer.parseInt(attrValue);
-                        break;
-                    default:
-                        if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
-                            taskDescription.restoreFromXml(attrName, attrValue);
-                        } else {
-                            Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
-                        }
-                }
-            }
-
-            int event;
-            while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
-                    (event != XmlPullParser.END_TAG || in.getDepth() >= outerDepth)) {
-                if (event == XmlPullParser.START_TAG) {
-                    final String name = in.getName();
-                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
-                            "TaskRecord: START_TAG name=" + name);
-                    if (TAG_AFFINITYINTENT.equals(name)) {
-                        affinityIntent = Intent.restoreFromXml(in);
-                    } else if (TAG_INTENT.equals(name)) {
-                        intent = Intent.restoreFromXml(in);
-                    } else if (TAG_ACTIVITY.equals(name)) {
-                        ActivityRecord activity =
-                                ActivityRecord.restoreFromXml(in, stackSupervisor);
-                        if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
-                                activity);
-                        if (activity != null) {
-                            activities.add(activity);
-                        }
-                    } else {
-                        handleUnknownTag(name, in);
-                    }
-                }
-            }
-            if (!hasRootAffinity) {
-                rootAffinity = affinity;
-            } else if ("@".equals(rootAffinity)) {
-                rootAffinity = null;
-            }
-            if (effectiveUid <= 0) {
-                Intent checkIntent = intent != null ? intent : affinityIntent;
-                effectiveUid = 0;
-                if (checkIntent != null) {
-                    IPackageManager pm = AppGlobals.getPackageManager();
-                    try {
-                        ApplicationInfo ai = pm.getApplicationInfo(
-                                checkIntent.getComponent().getPackageName(),
-                                PackageManager.MATCH_UNINSTALLED_PACKAGES
-                                        | PackageManager.MATCH_DISABLED_COMPONENTS, userId);
-                        if (ai != null) {
-                            effectiveUid = ai.uid;
-                        }
-                    } catch (RemoteException e) {
-                    }
-                }
-                Slog.w(TAG, "Updating task #" + taskId + " for " + checkIntent
-                        + ": effectiveUid=" + effectiveUid);
-            }
-
-            if (persistTaskVersion < 1) {
-                // We need to convert the resize mode of home activities saved before version one if
-                // they are marked as RESIZE_MODE_RESIZEABLE to
-                // RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION since we didn't have that differentiation
-                // before version 1 and the system didn't resize home activities before then.
-                if (taskType == 1 /* old home type */ && resizeMode == RESIZE_MODE_RESIZEABLE) {
-                    resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-                }
-            } else {
-                // This activity has previously marked itself explicitly as both resizeable and
-                // supporting picture-in-picture.  Since there is no longer a requirement for
-                // picture-in-picture activities to be resizeable, we can mark this simply as
-                // resizeable and supporting picture-in-picture separately.
-                if (resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED) {
-                    resizeMode = RESIZE_MODE_RESIZEABLE;
-                    supportsPictureInPicture = true;
-                }
-            }
-
-            final TaskRecord task = create(stackSupervisor.mService,
-                    taskId, intent, affinityIntent,
-                    affinity, rootAffinity, realActivity, origActivity, rootHasReset,
-                    autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
-                    activities, lastTimeOnTop, neverRelinquishIdentity, taskDescription,
-                    taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
-                    callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
-                    userSetupComplete, minWidth, minHeight);
-            task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
-            task.setBounds(lastNonFullscreenBounds);
-
-            for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
-                activities.get(activityNdx).setTask(task);
-            }
-
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
-            return task;
-        }
-
-        void handleUnknownTag(String name, XmlPullParser in)
-                throws IOException, XmlPullParserException {
-            Slog.e(TAG, "restoreTask: Unexpected name=" + name);
-            XmlUtils.skipCurrentTag(in);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/UnsupportedCompileSdkDialog.java b/services/core/java/com/android/server/am/UnsupportedCompileSdkDialog.java
deleted file mode 100644
index 7348a0d..0000000
--- a/services/core/java/com/android/server/am/UnsupportedCompileSdkDialog.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageItemInfo;
-import android.content.pm.PackageManager;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.CheckBox;
-
-import com.android.internal.R;
-import com.android.server.utils.AppInstallerUtil;
-
-public class UnsupportedCompileSdkDialog {
-    private final AlertDialog mDialog;
-    private final String mPackageName;
-
-    public UnsupportedCompileSdkDialog(final AppWarnings manager, Context context,
-            ApplicationInfo appInfo) {
-        mPackageName = appInfo.packageName;
-
-        final PackageManager pm = context.getPackageManager();
-        final CharSequence label = appInfo.loadSafeLabel(pm,
-                PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
-                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE
-                        | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
-        final CharSequence message = context.getString(R.string.unsupported_compile_sdk_message,
-                label);
-
-        final AlertDialog.Builder builder = new AlertDialog.Builder(context)
-                .setPositiveButton(R.string.ok, null)
-                .setMessage(message)
-                .setView(R.layout.unsupported_compile_sdk_dialog_content);
-
-        // If we might be able to update the app, show a button.
-        final Intent installerIntent = AppInstallerUtil.createIntent(context, appInfo.packageName);
-        if (installerIntent != null) {
-                builder.setNeutralButton(R.string.unsupported_compile_sdk_check_update,
-                        (dialog, which) -> context.startActivity(installerIntent));
-        }
-
-        // Ensure the content view is prepared.
-        mDialog = builder.create();
-        mDialog.create();
-
-        final Window window = mDialog.getWindow();
-        window.setType(WindowManager.LayoutParams.TYPE_PHONE);
-
-        // DO NOT MODIFY. Used by CTS to verify the dialog is displayed.
-        window.getAttributes().setTitle("UnsupportedCompileSdkDialog");
-
-        final CheckBox alwaysShow = mDialog.findViewById(R.id.ask_checkbox);
-        alwaysShow.setChecked(true);
-        alwaysShow.setOnCheckedChangeListener((buttonView, isChecked) -> manager.setPackageFlag(
-                mPackageName, AppWarnings.FLAG_HIDE_COMPILE_SDK, !isChecked));
-    }
-
-    public String getPackageName() {
-        return mPackageName;
-    }
-
-    public void show() {
-        mDialog.show();
-    }
-
-    public void dismiss() {
-        mDialog.dismiss();
-    }
-}
diff --git a/services/core/java/com/android/server/am/UnsupportedDisplaySizeDialog.java b/services/core/java/com/android/server/am/UnsupportedDisplaySizeDialog.java
deleted file mode 100644
index 1d6438c..0000000
--- a/services/core/java/com/android/server/am/UnsupportedDisplaySizeDialog.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2011 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.server.am;
-
-import com.android.internal.R;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageItemInfo;
-import android.content.pm.PackageManager;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.CheckBox;
-
-public class UnsupportedDisplaySizeDialog {
-    private final AlertDialog mDialog;
-    private final String mPackageName;
-
-    public UnsupportedDisplaySizeDialog(final AppWarnings manager, Context context,
-            ApplicationInfo appInfo) {
-        mPackageName = appInfo.packageName;
-
-        final PackageManager pm = context.getPackageManager();
-        final CharSequence label = appInfo.loadSafeLabel(pm,
-                PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
-                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE
-                        | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
-        final CharSequence message = context.getString(
-                R.string.unsupported_display_size_message, label);
-
-        mDialog = new AlertDialog.Builder(context)
-                .setPositiveButton(R.string.ok, null)
-                .setMessage(message)
-                .setView(R.layout.unsupported_display_size_dialog_content)
-                .create();
-
-        // Ensure the content view is prepared.
-        mDialog.create();
-
-        final Window window = mDialog.getWindow();
-        window.setType(WindowManager.LayoutParams.TYPE_PHONE);
-
-        // DO NOT MODIFY. Used by CTS to verify the dialog is displayed.
-        window.getAttributes().setTitle("UnsupportedDisplaySizeDialog");
-
-        final CheckBox alwaysShow = mDialog.findViewById(R.id.ask_checkbox);
-        alwaysShow.setChecked(true);
-        alwaysShow.setOnCheckedChangeListener((buttonView, isChecked) -> manager.setPackageFlag(
-                mPackageName, AppWarnings.FLAG_HIDE_DISPLAY_SIZE, !isChecked));
-    }
-
-    public String getPackageName() {
-        return mPackageName;
-    }
-
-    public void show() {
-        mDialog.show();
-    }
-
-    public void dismiss() {
-        mDialog.dismiss();
-    }
-}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index d2dd3db..353f787 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -22,14 +22,15 @@
 import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
 import static android.app.ActivityManager.USER_OP_IS_CURRENT;
 import static android.app.ActivityManager.USER_OP_SUCCESS;
-import static android.os.Process.SHELL_UID;
-import static android.os.Process.SYSTEM_UID;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
+import static android.os.Process.SHELL_UID;
+import static android.os.Process.SYSTEM_UID;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 import static com.android.server.am.UserState.STATE_BOOTING;
 import static com.android.server.am.UserState.STATE_RUNNING_LOCKED;
@@ -40,7 +41,6 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
-import com.android.server.wm.ActivityTaskManagerInternal;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.Dialog;
@@ -99,6 +99,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemServiceManager;
 import com.android.server.pm.UserManagerService;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerService;
 
 import java.io.PrintWriter;
@@ -549,7 +550,8 @@
         final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
         bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
-                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+                | Intent.FLAG_RECEIVER_OFFLOAD);
         mInjector.broadcastIntent(bootIntent, null, new IIntentReceiver.Stub() {
                     @Override
                     public void performReceive(Intent intent, int resultCode, String data,
@@ -2189,7 +2191,7 @@
         }
 
         void updateUserConfiguration() {
-            mService.mActivityTaskManager.updateUserConfiguration();
+            mService.mAtmInternal.updateUserConfiguration();
         }
 
         void clearBroadcastQueueForUser(int userId) {
@@ -2199,9 +2201,7 @@
         }
 
         void loadUserRecents(int userId) {
-            synchronized (mService) {
-                mService.mActivityTaskManager.getRecentTasks().loadUserRecentsLocked(userId);
-            }
+            mService.mAtmInternal.loadRecentTasksForUser(userId);
         }
 
         void startPersistentApps(int matchFlags) {
@@ -2254,13 +2254,11 @@
         }
 
         protected void clearAllLockedTasks(String reason) {
-            synchronized (mService) {
-                mService.mActivityTaskManager.getLockTaskController().clearLockedTasks(reason);
-            }
+            mService.mAtmInternal.clearLockedTasks(reason);
         }
 
         protected boolean isCallerRecents(int callingUid) {
-            return mService.mActivityTaskManager.getRecentTasks().isCallerRecents(callingUid);
+            return mService.mAtmInternal.isCallerRecents(callingUid);
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
deleted file mode 100644
index 51d86d6..0000000
--- a/services/core/java/com/android/server/am/VrController.java
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import android.content.ComponentName;
-import android.os.Process;
-import android.service.vr.IPersistentVrStateCallbacks;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-import android.util.proto.ProtoUtils;
-
-import com.android.server.LocalServices;
-import com.android.server.vr.VrManagerInternal;
-
-/**
- * Helper class for {@link ActivityManagerService} responsible for VrMode-related ActivityManager
- * functionality.
- *
- * <p>Specifically, this class is responsible for:
- * <ul>
- * <li>Adjusting the scheduling of VR render threads while in VR mode.
- * <li>Handling ActivityManager calls to set a VR or a 'persistent' VR thread.
- * <li>Tracking the state of ActivityManagerService's view of VR-related behavior flags.
- * </ul>
- *
- * <p>This is NOT the class that manages the system VR mode lifecycle. The class responsible for
- * handling everything related to VR mode state changes (e.g. the lifecycles of the associated
- * VrListenerService, VrStateCallbacks, VR HAL etc.) is VrManagerService.
- *
- * <p>This class is exclusively for use by ActivityManagerService. Do not add callbacks or other
- * functionality to this for things that belong in VrManagerService.
- */
-final class VrController {
-    private static final String TAG = "VrController";
-
-    // VR state flags.
-    private static final int FLAG_NON_VR_MODE = 0;
-    private static final int FLAG_VR_MODE = 1;
-    private static final int FLAG_PERSISTENT_VR_MODE = 2;
-
-    // Keep the enum lists in sync
-    private static int[] ORIG_ENUMS = new int[] {
-            FLAG_NON_VR_MODE,
-            FLAG_VR_MODE,
-            FLAG_PERSISTENT_VR_MODE,
-    };
-    private static int[] PROTO_ENUMS = new int[] {
-            VrControllerProto.FLAG_NON_VR_MODE,
-            VrControllerProto.FLAG_VR_MODE,
-            VrControllerProto.FLAG_PERSISTENT_VR_MODE,
-    };
-
-    // Invariants maintained for mVrState
-    //
-    //   Always true:
-    //      - Only a single VR-related thread will have elevated scheduling priorities at a time
-    //        across all threads in all processes (and for all possible running modes).
-    //
-    //   Always true while FLAG_PERSISTENT_VR_MODE is set:
-    //      - An application has set a flag to run in persistent VR mode the next time VR mode is
-    //        entered. The device may or may not be in VR mode.
-    //      - mVrState will contain FLAG_PERSISTENT_VR_MODE
-    //      - An application may set a persistent VR thread that gains elevated scheduling
-    //        priorities via a call to setPersistentVrThread.
-    //      - Calls to set a regular (non-persistent) VR thread via setVrThread will fail, and
-    //        thread that had previously elevated its scheduling priority in this way is returned
-    //        to its normal scheduling priority.
-    //
-    //   Always true while FLAG_VR_MODE is set:
-    //      - The current top application is running in VR mode.
-    //      - mVrState will contain FLAG_VR_MODE
-    //
-    //   While FLAG_VR_MODE is set without FLAG_PERSISTENT_VR_MODE:
-    //      - The current top application may set one of its threads to run at an elevated
-    //        scheduling priority via a call to setVrThread.
-    //
-    //   While FLAG_VR_MODE is set with FLAG_PERSISTENT_VR_MODE:
-    //      - The current top application may NOT set one of its threads to run at an elevated
-    //        scheduling priority via a call to setVrThread (instead, the persistent VR thread will
-    //        be kept if an application has set one).
-    //
-    //   While mVrState == FLAG_NON_VR_MODE:
-    //      - Calls to setVrThread will fail.
-    //      - Calls to setPersistentVrThread will fail.
-    //      - No threads will have elevated scheduling priority for VR.
-    //
-    private int mVrState = FLAG_NON_VR_MODE;
-
-    // The single VR render thread on the device that is given elevated scheduling priority.
-    private int mVrRenderThreadTid = 0;
-
-    private final Object mGlobalAmLock;
-
-    private final IPersistentVrStateCallbacks mPersistentVrModeListener =
-            new IPersistentVrStateCallbacks.Stub() {
-        @Override
-        public void onPersistentVrStateChanged(boolean enabled) {
-            synchronized(mGlobalAmLock) {
-                // Note: This is the only place where mVrState should have its
-                // FLAG_PERSISTENT_VR_MODE setting changed.
-                if (enabled) {
-                    setVrRenderThreadLocked(0, ProcessList.SCHED_GROUP_TOP_APP, true);
-                    mVrState |= FLAG_PERSISTENT_VR_MODE;
-                } else {
-                    setPersistentVrRenderThreadLocked(0, true);
-                    mVrState &= ~FLAG_PERSISTENT_VR_MODE;
-                }
-            }
-        }
-    };
-
-    /**
-     * Create new VrController instance.
-     *
-     * @param globalAmLock the global ActivityManagerService lock.
-     */
-    public VrController(final Object globalAmLock) {
-        mGlobalAmLock = globalAmLock;
-    }
-
-    /**
-     * Called when ActivityManagerService receives its systemReady call during boot.
-     */
-    public void onSystemReady() {
-        VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class);
-        if (vrManagerInternal != null) {
-            vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
-        }
-    }
-
-    /**
-     * Called when ActivityManagerService's TOP_APP process has changed.
-     *
-     * <p>Note: This must be called with the global ActivityManagerService lock held.
-     *
-     * @param proc is the WindowProcessController of the process that entered or left the TOP_APP
-     *            scheduling group.
-     */
-    public void onTopProcChangedLocked(WindowProcessController proc) {
-        final int curSchedGroup = proc.getCurrentSchedulingGroup();
-        if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
-            setVrRenderThreadLocked(proc.mVrThreadTid, curSchedGroup, true);
-        } else {
-            if (proc.mVrThreadTid == mVrRenderThreadTid) {
-                clearVrRenderThreadLocked(true);
-            }
-        }
-    }
-
-    /**
-     * Called when ActivityManagerService is switching VR mode for the TOP_APP process.
-     *
-     * @param record the ActivityRecord of the activity changing the system VR mode.
-     * @return {@code true} if the VR state changed.
-     */
-    public boolean onVrModeChanged(ActivityRecord record) {
-        // This message means that the top focused activity enabled VR mode (or an activity
-        // that previously set this has become focused).
-        VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
-        if (vrService == null) {
-            // VR mode isn't supported on this device.
-            return false;
-        }
-        boolean vrMode;
-        ComponentName requestedPackage;
-        ComponentName callingPackage;
-        int userId;
-        int processId = -1;
-        boolean changed = false;
-        synchronized (mGlobalAmLock) {
-            vrMode = record.requestedVrComponent != null;
-            requestedPackage = record.requestedVrComponent;
-            userId = record.userId;
-            callingPackage = record.info.getComponentName();
-
-            // Tell the VrController that a VR mode change is requested.
-            changed = changeVrModeLocked(vrMode, record.app);
-
-            if (record.app != null) {
-                processId = record.app.getPid();
-            }
-        }
-
-        // Tell VrManager that a VR mode changed is requested, VrManager will handle
-        // notifying all non-AM dependencies if needed.
-        vrService.setVrMode(vrMode, requestedPackage, userId, processId, callingPackage);
-        return changed;
-    }
-
-    /**
-     * Called to set an application's VR thread.
-     *
-     * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set,
-     * or the scheduling group of the thread is not for the current top app.  If this succeeds, any
-     * previous VR thread will be returned to a normal sheduling priority; if this fails, the
-     * scheduling for the previous thread will be unaffected.
-     *
-     * <p>Note: This must be called with the global ActivityManagerService lock and the
-     *     mPidsSelfLocked object locks held.
-     *
-     * @param tid the tid of the thread to set, or 0 to unset the current thread.
-     * @param pid the pid of the process owning the thread to set.
-     * @param proc the WindowProcessController of the process owning the thread to set.
-     */
-    public void setVrThreadLocked(int tid, int pid, WindowProcessController proc) {
-        if (hasPersistentVrFlagSet()) {
-            Slog.w(TAG, "VR thread cannot be set in persistent VR mode!");
-            return;
-        }
-        if (proc == null) {
-           Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!");
-           return;
-        }
-        if (tid != 0) {
-            enforceThreadInProcess(tid, pid);
-        }
-        if (!inVrMode()) {
-            Slog.w(TAG, "VR thread cannot be set when not in VR mode!");
-        } else {
-            setVrRenderThreadLocked(tid, proc.getCurrentSchedulingGroup(), false);
-        }
-        proc.mVrThreadTid = (tid > 0) ? tid : 0;
-    }
-
-    /**
-     * Called to set an application's persistent VR thread.
-     *
-     * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds,
-     * any previous VR thread will be returned to a normal sheduling priority; if this fails,
-     * the scheduling for the previous thread will be unaffected.
-     *
-     * <p>Note: This must be called with the global ActivityManagerService lock and the
-     *     mPidsSelfLocked object locks held.
-     *
-     * @param tid the tid of the thread to set, or 0 to unset the current thread.
-     * @param pid the pid of the process owning the thread to set.
-     * @param proc the process owning the thread to set.
-     */
-    public void setPersistentVrThreadLocked(int tid, int pid, WindowProcessController proc) {
-        if (!hasPersistentVrFlagSet()) {
-            Slog.w(TAG, "Persistent VR thread may only be set in persistent VR mode!");
-            return;
-        }
-        if (proc == null) {
-           Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!");
-           return;
-        }
-        if (tid != 0) {
-            enforceThreadInProcess(tid, pid);
-        }
-        setPersistentVrRenderThreadLocked(tid, false);
-    }
-
-    /**
-     * Return {@code true} when UI features incompatible with VR mode should be disabled.
-     *
-     * <p>Note: This must be called with the global ActivityManagerService lock held.
-     */
-    public boolean shouldDisableNonVrUiLocked() {
-        return mVrState != FLAG_NON_VR_MODE;
-    }
-
-    /**
-     * Called when to update this VrController instance's state when the system VR mode is being
-     * changed.
-     *
-     * <p>Note: This must be called with the global ActivityManagerService lock held.
-     *
-     * @param vrMode {@code true} if the system VR mode is being enabled.
-     * @param proc the WindowProcessController of the process enabling the system VR mode.
-     *
-     * @return {@code true} if our state changed.
-     */
-    private boolean changeVrModeLocked(boolean vrMode, WindowProcessController proc) {
-        final int oldVrState = mVrState;
-
-        // This is the only place where mVrState should have its FLAG_VR_MODE setting
-        // changed.
-        if (vrMode) {
-            mVrState |= FLAG_VR_MODE;
-        } else {
-            mVrState &= ~FLAG_VR_MODE;
-        }
-
-        boolean changed = (oldVrState != mVrState);
-
-        if (changed) {
-            if (proc != null) {
-                if (proc.mVrThreadTid > 0) {
-                    setVrRenderThreadLocked(
-                            proc.mVrThreadTid, proc.getCurrentSchedulingGroup(), false);
-                }
-            } else {
-              clearVrRenderThreadLocked(false);
-            }
-        }
-        return changed;
-    }
-
-    /**
-     * Set the given thread as the new VR thread, and give it special scheduling priority.
-     *
-     * <p>If the current thread is this thread, do nothing. If the current thread is different from
-     * the given thread, the current thread will be returned to a normal scheduling priority.
-     *
-     * @param newTid the tid of the thread to set, or 0 to unset the current thread.
-     * @param suppressLogs {@code true} if any error logging should be disabled.
-     *
-     * @return the tid of the thread configured to run at the scheduling priority for VR
-     *          mode after this call completes (this may be the previous thread).
-     */
-    private int updateVrRenderThreadLocked(int newTid, boolean suppressLogs) {
-        if (mVrRenderThreadTid == newTid) {
-            return mVrRenderThreadTid;
-        }
-
-        if (mVrRenderThreadTid > 0) {
-            ActivityManagerService.scheduleAsRegularPriority(mVrRenderThreadTid, suppressLogs);
-            mVrRenderThreadTid = 0;
-        }
-
-        if (newTid > 0) {
-            mVrRenderThreadTid = newTid;
-            ActivityManagerService.scheduleAsFifoPriority(mVrRenderThreadTid, suppressLogs);
-        }
-        return mVrRenderThreadTid;
-    }
-
-    /**
-     * Set special scheduling for the given application persistent VR thread, if allowed.
-     *
-     * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds,
-     * any previous VR thread will be returned to a normal sheduling priority; if this fails,
-     * the scheduling for the previous thread will be unaffected.
-     *
-     * @param newTid the tid of the thread to set, or 0 to unset the current thread.
-     * @param suppressLogs {@code true} if any error logging should be disabled.
-     *
-     * @return the tid of the thread configured to run at the scheduling priority for VR
-     *          mode after this call completes (this may be the previous thread).
-     */
-    private int setPersistentVrRenderThreadLocked(int newTid, boolean suppressLogs) {
-       if (!hasPersistentVrFlagSet()) {
-            if (!suppressLogs) {
-                Slog.w(TAG, "Failed to set persistent VR thread, "
-                        + "system not in persistent VR mode.");
-            }
-            return mVrRenderThreadTid;
-        }
-        return updateVrRenderThreadLocked(newTid, suppressLogs);
-    }
-
-    /**
-     * Set special scheduling for the given application VR thread, if allowed.
-     *
-     * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set,
-     * or the scheduling group of the thread is not for the current top app.  If this succeeds, any
-     * previous VR thread will be returned to a normal sheduling priority; if this fails, the
-     * scheduling for the previous thread will be unaffected.
-     *
-     * @param newTid the tid of the thread to set, or 0 to unset the current thread.
-     * @param schedGroup the current scheduling group of the thread to set.
-     * @param suppressLogs {@code true} if any error logging should be disabled.
-     *
-     * @return the tid of the thread configured to run at the scheduling priority for VR
-     *          mode after this call completes (this may be the previous thread).
-     */
-    private int setVrRenderThreadLocked(int newTid, int schedGroup, boolean suppressLogs) {
-        boolean inVr = inVrMode();
-        boolean inPersistentVr = hasPersistentVrFlagSet();
-        if (!inVr || inPersistentVr || schedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-            if (!suppressLogs) {
-               String reason = "caller is not the current top application.";
-               if (!inVr) {
-                   reason = "system not in VR mode.";
-               } else if (inPersistentVr) {
-                   reason = "system in persistent VR mode.";
-               }
-               Slog.w(TAG, "Failed to set VR thread, " + reason);
-            }
-            return mVrRenderThreadTid;
-        }
-        return updateVrRenderThreadLocked(newTid, suppressLogs);
-    }
-
-    /**
-     * Unset any special scheduling used for the current VR render thread, and return it to normal
-     * scheduling priority.
-     *
-     * @param suppressLogs {@code true} if any error logging should be disabled.
-     */
-    private void clearVrRenderThreadLocked(boolean suppressLogs) {
-        updateVrRenderThreadLocked(0, suppressLogs);
-    }
-
-    /**
-     * Check that the given tid is running in the process for the given pid, and throw an exception
-     * if not.
-     */
-    private void enforceThreadInProcess(int tid, int pid) {
-        if (!Process.isThreadInProcess(pid, tid)) {
-            throw new IllegalArgumentException("VR thread does not belong to process");
-        }
-    }
-
-    /**
-     * True when the system is in VR mode.
-     */
-    private boolean inVrMode() {
-        return (mVrState & FLAG_VR_MODE) != 0;
-    }
-
-    /**
-     * True when the persistent VR mode flag has been set.
-     *
-     * Note: Currently this does not necessarily mean that the system is in VR mode.
-     */
-    private boolean hasPersistentVrFlagSet() {
-        return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0;
-    }
-
-    @Override
-    public String toString() {
-      return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid);
-    }
-
-    void writeToProto(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-        ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, VrControllerProto.VR_MODE,
-                mVrState, ORIG_ENUMS, PROTO_ENUMS);
-        proto.write(VrControllerProto.RENDER_THREAD_ID, mVrRenderThreadTid);
-        proto.end(token);
-    }
-}
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
deleted file mode 100644
index 94f1002..0000000
--- a/services/core/java/com/android/server/am/WindowProcessController.java
+++ /dev/null
@@ -1,840 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
-import static android.view.Display.INVALID_DISPLAY;
-
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityTaskManagerService
-        .INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
-import static com.android.server.am.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-
-import android.app.Activity;
-import android.app.ActivityThread;
-import android.app.IApplicationThread;
-import android.app.ProfilerInfo;
-import android.app.servertransaction.ConfigurationChangeItem;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.app.HeavyWeightSwitcherActivity;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.wm.ConfigurationContainer;
-import com.android.server.wm.ConfigurationContainerListener;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-/**
- * The Activity Manager (AM) package manages the lifecycle of processes in the system through
- * {@link ProcessRecord}. However, it is important for the Window Manager (WM) package to be aware
- * of the processes and their state since it affects how WM manages windows and activities. This
- * class that allows the {@link ProcessRecord} object in the AM package to communicate important
- * changes to its state to the WM package in a structured way. WM package also uses
- * {@link WindowProcessListener} to request changes to the process state on the AM side.
- * Note that public calls into this class are assumed to be originating from outside the
- * window manager so the window manager lock is held and appropriate permissions are checked before
- * calls are allowed to proceed.
- */
-public class WindowProcessController extends ConfigurationContainer<ConfigurationContainer>
-        implements ConfigurationContainerListener {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowProcessController" : TAG_ATM;
-    private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
-    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
-
-    // all about the first app in the process
-    final ApplicationInfo mInfo;
-    final String mName;
-    final int mUid;
-    // The process of this application; 0 if none
-    private volatile int mPid;
-    // user of process.
-    final int mUserId;
-    // The owner of this window process controller object. Mainly for identification when we
-    // communicate back to the activity manager side.
-    public final Object mOwner;
-    // List of packages running in the process
-    final ArraySet<String> mPkgList = new ArraySet<>();
-    private final WindowProcessListener mListener;
-    private final ActivityTaskManagerService mAtm;
-    // The actual proc...  may be null only if 'persistent' is true (in which case we are in the
-    // process of launching the app)
-    private volatile IApplicationThread mThread;
-    // Currently desired scheduling class
-    private volatile int mCurSchedGroup;
-    // Currently computed process state
-    private volatile int mCurProcState = PROCESS_STATE_NONEXISTENT;
-    // Last reported process state;
-    private volatile int mRepProcState = PROCESS_STATE_NONEXISTENT;
-    // are we in the process of crashing?
-    private volatile boolean mCrashing;
-    // does the app have a not responding dialog?
-    private volatile boolean mNotResponding;
-    // always keep this application running?
-    private volatile boolean mPersistent;
-    // The ABI this process was launched with
-    private volatile String mRequiredAbi;
-    // Running any services that are foreground?
-    private volatile boolean mHasForegroundServices;
-    // Running any activities that are foreground?
-    private volatile boolean mHasForegroundActivities;
-    // Are there any client services with activities?
-    private volatile boolean mHasClientActivities;
-    // Is this process currently showing a non-activity UI that the user is interacting with?
-    // E.g. The status bar when it is expanded, but not when it is minimized. When true the process
-    // will be set to use the ProcessList#SCHED_GROUP_TOP_APP scheduling group to boost performance.
-    private volatile boolean mHasTopUi;
-    // Is the process currently showing a non-activity UI that overlays on-top of activity UIs on
-    // screen. E.g. display a window of type
-    // android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY When true the process will
-    // oom adj score will be set to ProcessList#PERCEPTIBLE_APP_ADJ at minimum to reduce the chance
-    // of the process getting killed.
-    private volatile boolean mHasOverlayUi;
-    // Want to clean up resources from showing UI?
-    private volatile boolean mPendingUiClean;
-    // The time we sent the last interaction event
-    private volatile long mInteractionEventTime;
-    // When we became foreground for interaction purposes
-    private volatile long mFgInteractionTime;
-    // When (uptime) the process last became unimportant
-    private volatile long mWhenUnimportant;
-    // was app launched for debugging?
-    private volatile boolean mDebugging;
-    // Active instrumentation running in process?
-    private volatile boolean mInstrumenting;
-    // This process it perceptible by the user.
-    private volatile boolean mPerceptible;
-    // Set to true when process was launched with a wrapper attached
-    private volatile boolean mUsingWrapper;
-
-    // Thread currently set for VR scheduling
-    int mVrThreadTid;
-
-    // all activities running in the process
-    private final ArrayList<ActivityRecord> mActivities = new ArrayList<>();
-    // any tasks this process had run root activities in
-    private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<>();
-
-    // Last configuration that was reported to the process.
-    private final Configuration mLastReportedConfiguration;
-    // Registered display id as a listener to override config change
-    private int mDisplayId;
-
-    WindowProcessController(ActivityTaskManagerService atm, ApplicationInfo info, String name,
-            int uid, int userId, Object owner, WindowProcessListener listener,
-            Configuration config) {
-        mInfo = info;
-        mName = name;
-        mUid = uid;
-        mUserId = userId;
-        mOwner = owner;
-        mListener = listener;
-        mAtm = atm;
-        mLastReportedConfiguration = new Configuration();
-        mDisplayId = INVALID_DISPLAY;
-        if (config != null) {
-            onConfigurationChanged(config);
-        }
-    }
-
-    public void setPid(int pid) {
-        mPid = pid;
-    }
-
-    int getPid() {
-        return mPid;
-    }
-
-    public void setThread(IApplicationThread thread) {
-        mThread = thread;
-    }
-
-    IApplicationThread getThread() {
-        return mThread;
-    }
-
-    boolean hasThread() {
-        return mThread != null;
-    }
-
-    public void setCurrentSchedulingGroup(int curSchedGroup) {
-        mCurSchedGroup = curSchedGroup;
-    }
-
-    int getCurrentSchedulingGroup() {
-        return mCurSchedGroup;
-    }
-
-    public void setCurrentProcState(int curProcState) {
-        mCurProcState = curProcState;
-    }
-
-    int getCurrentProcState() {
-        return mCurProcState;
-    }
-
-    public void setReportedProcState(int repProcState) {
-        mRepProcState = repProcState;
-    }
-
-    int getReportedProcState() {
-        return mRepProcState;
-    }
-
-    public void setCrashing(boolean crashing) {
-        mCrashing = crashing;
-    }
-
-    boolean isCrashing() {
-        return mCrashing;
-    }
-
-    public void setNotResponding(boolean notResponding) {
-        mNotResponding = notResponding;
-    }
-
-    boolean isNotResponding() {
-        return mNotResponding;
-    }
-
-    public void setPersistent(boolean persistent) {
-        mPersistent = persistent;
-    }
-
-    boolean isPersistent() {
-        return mPersistent;
-    }
-
-    public void setHasForegroundServices(boolean hasForegroundServices) {
-        mHasForegroundServices = hasForegroundServices;
-    }
-
-    boolean hasForegroundServices() {
-        return mHasForegroundServices;
-    }
-
-    public void setHasForegroundActivities(boolean hasForegroundActivities) {
-        mHasForegroundActivities = hasForegroundActivities;
-    }
-
-    boolean hasForegroundActivities() {
-        return mHasForegroundActivities;
-    }
-
-    public void setHasClientActivities(boolean hasClientActivities) {
-        mHasClientActivities = hasClientActivities;
-    }
-
-    boolean hasClientActivities() {
-        return mHasClientActivities;
-    }
-
-    public void setHasTopUi(boolean hasTopUi) {
-        mHasTopUi = hasTopUi;
-    }
-
-    boolean hasTopUi() {
-        return mHasTopUi;
-    }
-
-    public void setHasOverlayUi(boolean hasOverlayUi) {
-        mHasOverlayUi = hasOverlayUi;
-    }
-
-    boolean hasOverlayUi() {
-        return mHasOverlayUi;
-    }
-
-    public void setPendingUiClean(boolean hasPendingUiClean) {
-        mPendingUiClean = hasPendingUiClean;
-    }
-
-    boolean hasPendingUiClean() {
-        return mPendingUiClean;
-    }
-
-    void postPendingUiCleanMsg(boolean pendingUiClean) {
-        if (mListener == null) return;
-        // Posting on handler so WM lock isn't held when we call into AM.
-        final Message m = PooledLambda.obtainMessage(
-                WindowProcessListener::setPendingUiClean, mListener, pendingUiClean);
-        mAtm.mH.sendMessage(m);
-    }
-
-    public void setInteractionEventTime(long interactionEventTime) {
-        mInteractionEventTime = interactionEventTime;
-    }
-
-    long getInteractionEventTime() {
-        return mInteractionEventTime;
-    }
-
-    public void setFgInteractionTime(long fgInteractionTime) {
-        mFgInteractionTime = fgInteractionTime;
-    }
-
-    long getFgInteractionTime() {
-        return mFgInteractionTime;
-    }
-
-    public void setWhenUnimportant(long whenUnimportant) {
-        mWhenUnimportant = whenUnimportant;
-    }
-
-    long getWhenUnimportant() {
-        return mWhenUnimportant;
-    }
-
-    public void setRequiredAbi(String requiredAbi) {
-        mRequiredAbi = requiredAbi;
-    }
-
-    String getRequiredAbi() {
-        return mRequiredAbi;
-    }
-
-    public void setDebugging(boolean debugging) {
-        mDebugging = debugging;
-    }
-
-    boolean isDebugging() {
-        return mDebugging;
-    }
-
-    public void setUsingWrapper(boolean usingWrapper) {
-        mUsingWrapper = usingWrapper;
-    }
-
-    boolean isUsingWrapper() {
-        return mUsingWrapper;
-    }
-
-    public void setInstrumenting(boolean instrumenting) {
-        mInstrumenting = instrumenting;
-    }
-
-    boolean isInstrumenting() {
-        return mInstrumenting;
-    }
-
-    public void setPerceptible(boolean perceptible) {
-        mPerceptible = perceptible;
-    }
-
-    boolean isPerceptible() {
-        return mPerceptible;
-    }
-
-    @Override
-    protected int getChildCount() {
-        return 0;
-    }
-
-    @Override
-    protected ConfigurationContainer getChildAt(int index) {
-        return null;
-    }
-
-    @Override
-    protected ConfigurationContainer getParent() {
-        return null;
-    }
-
-    public void addPackage(String packageName) {
-        synchronized (mAtm.mGlobalLock) {
-            mPkgList.add(packageName);
-        }
-    }
-
-    public void clearPackageList() {
-        synchronized (mAtm.mGlobalLock) {
-            mPkgList.clear();
-        }
-    }
-
-    void addActivityIfNeeded(ActivityRecord r) {
-        if (mActivities.contains(r)) {
-            return;
-        }
-        mActivities.add(r);
-    }
-
-    void removeActivity(ActivityRecord r) {
-        mActivities.remove(r);
-    }
-
-    public void clearActivities() {
-        synchronized (mAtm.mGlobalLock) {
-            mActivities.clear();
-        }
-    }
-
-    public boolean hasActivities() {
-        synchronized (mAtm.mGlobalLock) {
-            return !mActivities.isEmpty();
-        }
-    }
-
-    public boolean hasVisibleActivities() {
-        synchronized (mAtm.mGlobalLock) {
-            for (int i = mActivities.size() - 1; i >= 0; --i) {
-                final ActivityRecord r = mActivities.get(i);
-                if (r.visible) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    public boolean hasActivitiesOrRecentTasks() {
-        synchronized (mAtm.mGlobalLock) {
-            return !mActivities.isEmpty() || !mRecentTasks.isEmpty();
-        }
-    }
-
-    public void stopFreezingActivities() {
-        synchronized (mAtm.mGlobalLock) {
-            int i = mActivities.size();
-            while (i > 0) {
-                i--;
-                mActivities.get(i).stopFreezingScreenLocked(true);
-            }
-        }
-    }
-
-    public void finishActivities() {
-        synchronized (mAtm.mGlobalLock) {
-            ArrayList<ActivityRecord> activities = new ArrayList<>(mActivities);
-            for (int i = 0; i < activities.size(); i++) {
-                final ActivityRecord r = activities.get(i);
-                if (!r.finishing && r.isInStackLocked()) {
-                    r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
-                            null, "finish-heavy", true);
-                }
-            }
-        }
-    }
-
-    public boolean isInterestingToUser() {
-        synchronized (mAtm.mGlobalLock) {
-            final int size = mActivities.size();
-            for (int i = 0; i < size; i++) {
-                ActivityRecord r = mActivities.get(i);
-                if (r.isInterestingToUserLocked()) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    public boolean hasRunningActivity(String packageName) {
-        synchronized (mAtm.mGlobalLock) {
-            for (int i = mActivities.size() - 1; i >= 0; --i) {
-                final ActivityRecord r = mActivities.get(i);
-                if (packageName.equals(r.packageName)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    public void clearPackagePreferredForHomeActivities() {
-        synchronized (mAtm.mGlobalLock) {
-            for (int i = mActivities.size() - 1; i >= 0; --i) {
-                final ActivityRecord r = mActivities.get(i);
-                if (r.isActivityTypeHome()) {
-                    Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
-                    try {
-                        ActivityThread.getPackageManager()
-                                .clearPackagePreferredActivities(r.packageName);
-                    } catch (RemoteException c) {
-                        // pm is in same process, this will never happen.
-                    }
-                }
-            }
-        }
-    }
-
-    boolean hasStartedActivity(ActivityRecord launchedActivity) {
-        for (int i = mActivities.size() - 1; i >= 0; i--) {
-            final ActivityRecord activity = mActivities.get(i);
-            if (launchedActivity == activity) {
-                continue;
-            }
-            if (!activity.stopped) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-
-    public void updateIntentForHeavyWeightActivity(Intent intent) {
-        synchronized (mAtm.mGlobalLock) {
-            if (mActivities.isEmpty()) {
-                return;
-            }
-            ActivityRecord hist = mActivities.get(0);
-            intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName);
-            intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTask().taskId);
-        }
-    }
-
-    boolean shouldKillProcessForRemovedTask(TaskRecord tr) {
-        for (int k = 0; k < mActivities.size(); k++) {
-            final ActivityRecord activity = mActivities.get(k);
-            if (!activity.stopped) {
-                // Don't kill process(es) that has an activity not stopped.
-                return false;
-            }
-            final TaskRecord otherTask = activity.getTask();
-            if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
-                // Don't kill process(es) that has an activity in a different task that is
-                // also in recents.
-                return false;
-            }
-        }
-        return true;
-    }
-
-    ArraySet<TaskRecord> getReleaseSomeActivitiesTasks() {
-        // Examine all activities currently running in the process.
-        TaskRecord firstTask = null;
-        // Tasks is non-null only if two or more tasks are found.
-        ArraySet<TaskRecord> tasks = null;
-        if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + this);
-        for (int i = 0; i < mActivities.size(); i++) {
-            final ActivityRecord r = mActivities.get(i);
-            // First, if we find an activity that is in the process of being destroyed,
-            // then we just aren't going to do anything for now; we want things to settle
-            // down before we try to prune more activities.
-            if (r.finishing || r.isState(DESTROYING, DESTROYED)) {
-                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
-                return null;
-            }
-            // Don't consider any activies that are currently not in a state where they
-            // can be destroyed.
-            if (r.visible || !r.stopped || !r.haveState
-                    || r.isState(RESUMED, PAUSING, PAUSED, STOPPING)) {
-                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
-                continue;
-            }
-
-            final TaskRecord task = r.getTask();
-            if (task != null) {
-                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
-                        + " from " + r);
-                if (firstTask == null) {
-                    firstTask = task;
-                } else if (firstTask != task) {
-                    if (tasks == null) {
-                        tasks = new ArraySet<>();
-                        tasks.add(firstTask);
-                    }
-                    tasks.add(task);
-                }
-            }
-        }
-
-        return tasks;
-    }
-
-    public interface ComputeOomAdjCallback {
-        void onVisibleActivity();
-        void onPausedActivity();
-        void onStoppingActivity(boolean finishing);
-        void onOtherActivity();
-    }
-
-    public int computeOomAdjFromActivities(int minTaskLayer, ComputeOomAdjCallback callback) {
-        synchronized (mAtm.mGlobalLock) {
-            final int activitiesSize = mActivities.size();
-            for (int j = 0; j < activitiesSize; j++) {
-                final ActivityRecord r = mActivities.get(j);
-                if (r.app != this) {
-                    Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
-                            + " instead of expected " + this);
-                    if (r.app == null || (r.app.mUid == mUid)) {
-                        // Only fix things up when they look sane
-                        r.setProcess(this);
-                    } else {
-                        continue;
-                    }
-                }
-                if (r.visible) {
-                    callback.onVisibleActivity();
-                    final TaskRecord task = r.getTask();
-                    if (task != null && minTaskLayer > 0) {
-                        final int layer = task.mLayerRank;
-                        if (layer >= 0 && minTaskLayer > layer) {
-                            minTaskLayer = layer;
-                        }
-                    }
-                    break;
-                } else if (r.isState(PAUSING, PAUSED)) {
-                    callback.onPausedActivity();
-                } else if (r.isState(STOPPING)) {
-                    callback.onStoppingActivity(r.finishing);
-                } else {
-                    callback.onOtherActivity();
-                }
-            }
-        }
-
-        return minTaskLayer;
-    }
-
-    int computeRelaunchReason() {
-        synchronized (mAtm.mGlobalLock) {
-            final int activitiesSize = mActivities.size();
-            for (int i = activitiesSize - 1; i >= 0; i--) {
-                final ActivityRecord r = mActivities.get(i);
-                if (r.mRelaunchReason != RELAUNCH_REASON_NONE) {
-                    return r.mRelaunchReason;
-                }
-            }
-        }
-        return RELAUNCH_REASON_NONE;
-    }
-
-    public long getInputDispatchingTimeout() {
-        synchronized (mAtm.mGlobalLock) {
-            return isInstrumenting() || isUsingWrapper()
-                    ? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS : KEY_DISPATCHING_TIMEOUT_MS;
-        }
-    }
-
-    void clearProfilerIfNeeded() {
-        if (mListener == null) return;
-        // Posting on handler so WM lock isn't held when we call into AM.
-        mAtm.mH.sendMessage(PooledLambda.obtainMessage(
-                WindowProcessListener::clearProfilerIfNeeded, mListener));
-    }
-
-    void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
-            boolean activityChange, boolean updateOomAdj) {
-        if (mListener == null) return;
-        // Posting on handler so WM lock isn't held when we call into AM.
-        final Message m = PooledLambda.obtainMessage(WindowProcessListener::updateProcessInfo,
-                mListener, updateServiceConnectionActivities, updateLru, activityChange,
-                updateOomAdj);
-        mAtm.mH.sendMessage(m);
-    }
-
-    void updateServiceConnectionActivities() {
-        if (mListener == null) return;
-        // Posting on handler so WM lock isn't held when we call into AM.
-        mAtm.mH.sendMessage(PooledLambda.obtainMessage(
-                WindowProcessListener::updateServiceConnectionActivities, mListener));
-    }
-
-    void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
-        if (mListener == null) return;
-        // Posting on handler so WM lock isn't held when we call into AM.
-        final Message m = PooledLambda.obtainMessage(
-                WindowProcessListener::setPendingUiCleanAndForceProcessStateUpTo,
-                mListener, newState);
-        mAtm.mH.sendMessage(m);
-    }
-
-    void setRemoved(boolean removed) {
-        if (mListener == null) return;
-        // Posting on handler so WM lock isn't held when we call into AM.
-        final Message m = PooledLambda.obtainMessage(
-                WindowProcessListener::setRemoved, mListener, removed);
-        mAtm.mH.sendMessage(m);
-    }
-
-    void clearWaitingToKill() {
-        if (mListener == null) return;
-        // Posting on handler so WM lock isn't held when we call into AM.
-        final Message m = PooledLambda.obtainMessage(
-                WindowProcessListener::clearWaitingToKill, mListener);
-        mAtm.mH.sendMessage(m);
-    }
-
-    void addPackage(String pkg, long versionCode) {
-        // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are
-        // using WM lock. Need to figure-out if it is okay to do this asynchronously.
-        if (mListener == null) return;
-        mListener.addPackage(pkg, versionCode);
-    }
-
-    ProfilerInfo onStartActivity(int topProcessState) {
-        // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are
-        // using WM lock. Need to figure-out if it is okay to do this asynchronously.
-        if (mListener == null) return null;
-        return mListener.onStartActivity(topProcessState);
-    }
-
-    public void appDied() {
-        // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are
-        // using WM lock. Need to figure-out if it is okay to do this asynchronously.
-        if (mListener == null) return;
-        mListener.appDied();
-    }
-
-    void registerDisplayConfigurationListenerLocked(ActivityDisplay activityDisplay) {
-        if (activityDisplay == null) {
-            return;
-        }
-        // A process can only register to one display to listener to the override configuration
-        // change. Unregister existing listener if it has one before register the new one.
-        unregisterDisplayConfigurationListenerLocked();
-        mDisplayId = activityDisplay.mDisplayId;
-        activityDisplay.registerConfigurationChangeListener(this);
-    }
-
-    private void unregisterDisplayConfigurationListenerLocked() {
-        if (mDisplayId == INVALID_DISPLAY) {
-            return;
-        }
-        final ActivityDisplay activityDisplay =
-                mAtm.mStackSupervisor.getActivityDisplay(mDisplayId);
-        if (activityDisplay != null) {
-            mAtm.mStackSupervisor.getActivityDisplay(
-                    mDisplayId).unregisterConfigurationChangeListener(this);
-        }
-        mDisplayId = INVALID_DISPLAY;
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newGlobalConfig) {
-        super.onConfigurationChanged(newGlobalConfig);
-        updateConfiguration();
-    }
-
-    @Override
-    public void onOverrideConfigurationChanged(Configuration newOverrideConfig) {
-        super.onOverrideConfigurationChanged(newOverrideConfig);
-        updateConfiguration();
-    }
-
-    private void updateConfiguration() {
-        final Configuration config = getConfiguration();
-        if (mLastReportedConfiguration.diff(config) == 0) {
-            // Nothing changed.
-            return;
-        }
-
-        try {
-            if (mThread == null) {
-                return;
-            }
-            if (DEBUG_CONFIGURATION) {
-                Slog.v(TAG_CONFIGURATION, "Sending to proc " + mName
-                        + " new config " + config);
-            }
-            config.seq = mAtm.increaseConfigurationSeqLocked();
-            mAtm.getLifecycleManager().scheduleTransaction(mThread,
-                    ConfigurationChangeItem.obtain(config));
-            setLastReportedConfiguration(config);
-        } catch (Exception e) {
-            Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
-        }
-    }
-
-    private void setLastReportedConfiguration(Configuration config) {
-        mLastReportedConfiguration.setTo(config);
-    }
-
-    Configuration getLastReportedConfiguration() {
-        return mLastReportedConfiguration;
-    }
-
-    /** Returns the total time (in milliseconds) spent executing in both user and system code. */
-    public long getCpuTime() {
-        return (mListener != null) ? mListener.getCpuTime() : 0;
-    }
-
-    void addRecentTask(TaskRecord task) {
-        mRecentTasks.add(task);
-    }
-
-    void removeRecentTask(TaskRecord task) {
-        mRecentTasks.remove(task);
-    }
-
-    public boolean hasRecentTasks() {
-        synchronized (mAtm.mGlobalLock) {
-            return !mRecentTasks.isEmpty();
-        }
-    }
-
-    public void clearRecentTasks() {
-        synchronized (mAtm.mGlobalLock) {
-            for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
-                mRecentTasks.get(i).clearRootProcess();
-            }
-            mRecentTasks.clear();
-        }
-    }
-
-    public void dump(PrintWriter pw, String prefix) {
-        synchronized (mAtm.mGlobalLock) {
-            if (mActivities.size() > 0) {
-                pw.print(prefix); pw.println("Activities:");
-                for (int i = 0; i < mActivities.size(); i++) {
-                    pw.print(prefix); pw.print("  - "); pw.println(mActivities.get(i));
-                }
-            }
-
-            if (mRecentTasks.size() > 0) {
-                pw.println(prefix + "Recent Tasks:");
-                for (int i = 0; i < mRecentTasks.size(); i++) {
-                    pw.println(prefix + "  - " + mRecentTasks.get(i));
-                }
-            }
-
-            if (mVrThreadTid != 0) {
-                pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
-            }
-        }
-        pw.println(prefix + " Configuration=" + getConfiguration());
-        pw.println(prefix + " OverrideConfiguration=" + getOverrideConfiguration());
-        pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration);
-    }
-
-    void writeToProto(ProtoOutputStream proto, long fieldId) {
-        if (mListener != null) {
-            mListener.writeToProto(proto, fieldId);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/WindowProcessListener.java b/services/core/java/com/android/server/am/WindowProcessListener.java
deleted file mode 100644
index 4a7e6e8..0000000
--- a/services/core/java/com/android/server/am/WindowProcessListener.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import android.app.ProfilerInfo;
-import android.content.pm.ApplicationInfo;
-import android.util.proto.ProtoOutputStream;
-
-/**
- * Interface used by the owner/creator of a process that owns windows to listen to changes from the
- * WM side.
- * @see WindowProcessController
- */
-public interface WindowProcessListener {
-
-    /** Clear the profiler record if we are currently profiling this process. */
-    void clearProfilerIfNeeded();
-
-    /** Update the service connection for this process based on activities it might have. */
-    void updateServiceConnectionActivities();
-
-    /** Set or clear flag that we would like to clean-up UI resources for this process. */
-    void setPendingUiClean(boolean pendingUiClean);
-
-    /**
-     * Set flag that we would like to clean-up UI resources for this process and force new process
-     * state.
-     */
-    void setPendingUiCleanAndForceProcessStateUpTo(int newState);
-
-    /** Update the process information. */
-    void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
-            boolean activityChange, boolean updateOomAdj);
-
-    /** Set process package been removed from device. */
-    void setRemoved(boolean removed);
-
-    /** Returns the total time (in milliseconds) spent executing in both user and system code. */
-    long getCpuTime();
-
-    /** Clears the waiting to kill reason for this process. */
-    void clearWaitingToKill();
-
-    /** Adds the package to the process. */
-    void addPackage(String pkg, long versionCode);
-
-    /** Called when we are in the process on starting an activity. */
-    ProfilerInfo onStartActivity(int topProcessState);
-
-    /** App died :(...oh well */
-    void appDied();
-    void writeToProto(ProtoOutputStream proto, long fieldId);
-}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5103974..67d27c9 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4293,7 +4293,7 @@
             return false;
         }
 
-        NotificationManager.Policy zenPolicy = mNm.getNotificationPolicy();
+        NotificationManager.Policy zenPolicy = mNm.getConsolidatedNotificationPolicy();
         final boolean muteAlarms = (zenPolicy.priorityCategories
                 & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) == 0;
         final boolean muteMedia = (zenPolicy.priorityCategories
@@ -4301,7 +4301,8 @@
         final boolean muteSystem = (zenPolicy.priorityCategories
                 & NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) == 0;
         final boolean muteNotificationAndRing = ZenModeConfig
-                .areAllPriorityOnlyNotificationZenSoundsMuted(mNm.getNotificationPolicy());
+                .areAllPriorityOnlyNotificationZenSoundsMuted(
+                        mNm.getConsolidatedNotificationPolicy());
         return muteAlarms && isAlarm(streamType)
                 || muteMedia && isMedia(streamType)
                 || muteSystem && isSystem(streamType)
@@ -4323,7 +4324,7 @@
     private boolean updateZenModeAffectedStreams() {
         int zenModeAffectedStreams = 0;
         if (mSystemReady && mNm.getZenMode() == Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
-            NotificationManager.Policy zenPolicy = mNm.getNotificationPolicy();
+            NotificationManager.Policy zenPolicy = mNm.getConsolidatedNotificationPolicy();
             if ((zenPolicy.priorityCategories
                     & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) == 0) {
                 zenModeAffectedStreams |= 1 << AudioManager.STREAM_ALARM;
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index b80dca6..278c55f 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -272,7 +272,7 @@
                 final int error = result.second;
 
                 // Check for errors, notify callback, and return
-                if (error != BiometricConstants.BIOMETRIC_ERROR_NONE) {
+                if (error != BiometricConstants.BIOMETRIC_SUCCESS) {
                     try {
                         final String hardwareUnavailable =
                                 getContext().getString(R.string.biometric_error_hw_unavailable);
@@ -540,7 +540,7 @@
             return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
         }
 
-        return new Pair<>(modality, BiometricConstants.BIOMETRIC_ERROR_NONE);
+        return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
     }
 
     private boolean isEnabledForApp(int modality) {
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index f6af52a..bc3cc3b 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -70,7 +70,7 @@
 /**
  * A service to manage multiple clients that want to access the face HAL API.
  * The service is responsible for maintaining a list of clients and dispatching all
- * face -related events.
+ * face-related events.
  *
  * @hide
  */
@@ -362,7 +362,7 @@
                 result = mDaemon != null ? mDaemon.setRequireAttention(requireAttention, byteToken)
                         : Status.INTERNAL_ERROR;
             } catch (RemoteException e) {
-                Slog.e(getTag(), "Unable to setRequireAttention to " + requireAttention);
+                Slog.e(getTag(), "Unable to setRequireAttention to " + requireAttention, e);
                 result = Status.INTERNAL_ERROR;
             }
 
@@ -382,10 +382,23 @@
             try {
                 result = mDaemon != null ? mDaemon.getRequireAttention(byteToken).value : true;
             } catch (RemoteException e) {
-                Slog.e(getTag(), "Unable to getRequireAttention");
+                Slog.e(getTag(), "Unable to getRequireAttention", e);
             }
             return result;
         }
+
+        @Override
+        public void userActivity() {
+            checkPermission(MANAGE_BIOMETRIC);
+
+            if (mDaemon != null) {
+                try {
+                    mDaemon.userActivity();
+                } catch (RemoteException e) {
+                    Slog.e(getTag(), "Unable to send userActivity", e);
+                }
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java
new file mode 100644
index 0000000..37cdc2a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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.server.biometrics.iris;
+
+import android.content.Context;
+
+import com.android.server.biometrics.BiometricServiceBase;
+import com.android.server.biometrics.BiometricUtils;
+import com.android.server.biometrics.Metrics;
+
+/**
+ * A service to manage multiple clients that want to access the Iris HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * iris-related events.
+ *
+ * TODO: The vendor is expected to fill in the service. See
+ * {@link com.android.server.biometrics.fingerprint.FingerprintService}
+ *
+ * @hide
+ */
+public class IrisService extends BiometricServiceBase {
+
+    private static final String TAG = "IrisService";
+
+    /**
+     * Initializes the system service.
+     * <p>
+     * Subclasses must define a single argument constructor that accepts the context
+     * and passes it to super.
+     * </p>
+     *
+     * @param context The system server context.
+     */
+    public IrisService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+    @Override
+    protected BiometricUtils getBiometricUtils() {
+        return null;
+    }
+
+    @Override
+    protected int getFailedAttemptsLockoutTimed() {
+        return 0;
+    }
+
+    @Override
+    protected int getFailedAttemptsLockoutPermanent() {
+        return 0;
+    }
+
+    @Override
+    protected Metrics getMetrics() {
+        return null;
+    }
+
+    @Override
+    protected boolean hasReachedEnrollmentLimit(int userId) {
+        return false;
+    }
+
+    @Override
+    protected void updateActiveGroup(int userId, String clientPackage) {
+
+    }
+
+    @Override
+    protected String getLockoutResetIntent() {
+        return null;
+    }
+
+    @Override
+    protected String getLockoutBroadcastPermission() {
+        return null;
+    }
+
+    @Override
+    protected long getHalDeviceId() {
+        return 0;
+    }
+
+    @Override
+    protected void handleUserSwitching(int userId) {
+
+    }
+
+    @Override
+    protected boolean hasEnrolledBiometrics(int userId) {
+        return false;
+    }
+
+    @Override
+    protected String getManageBiometricPermission() {
+        return null;
+    }
+
+    @Override
+    protected void checkUseBiometricPermission() {
+
+    }
+
+    @Override
+    protected boolean checkAppOps(int uid, String opPackageName) {
+        return false;
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index f523d59..6596d27 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -93,7 +93,9 @@
         // We only run clat on networks that don't have a native IPv4 address.
         final boolean hasIPv4Address =
                 (nai.linkProperties != null) && nai.linkProperties.hasIPv4Address();
-        return supported && connected && !hasIPv4Address;
+        final boolean skip464xlat =
+                (nai.netMisc() != null) && nai.netMisc().skip464xlat;
+        return supported && connected && !hasIPv4Address && !skip464xlat;
     }
 
     /**
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 4f31e53..422f556 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -208,7 +208,8 @@
 
         for (INetdEventCallback callback : mNetdEventCallbackList) {
             if (callback != null) {
-                callback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid);
+                callback.onDnsEvent(netId, eventType, returnCode, hostname, ipAddresses,
+                        ipAddressesCount, timestamp, uid);
             }
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 505480e..262184b 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -266,6 +266,10 @@
         return mConnService;
     }
 
+    public NetworkMisc netMisc() {
+        return networkMisc;
+    }
+
     public Handler handler() {
         return mHandler;
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 30659c1..de4f2d8 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -227,6 +227,12 @@
     public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = BASE + 14;
     private static final int CMD_EVALUATE_PRIVATE_DNS = BASE + 15;
 
+    /**
+     * Message to self indicating captive portal detection is completed.
+     * obj = CaptivePortalProbeResult for detection result;
+     */
+    public static final int CMD_PROBE_COMPLETE = BASE + 16;
+
     // Start mReevaluateDelayMs at this value and double.
     private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
     private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
@@ -290,6 +296,7 @@
     private final State mEvaluatingState = new EvaluatingState();
     private final State mCaptivePortalState = new CaptivePortalState();
     private final State mEvaluatingPrivateDnsState = new EvaluatingPrivateDnsState();
+    private final State mProbingState = new ProbingState();
 
     private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
 
@@ -304,6 +311,9 @@
     private final Random mRandom;
     private int mNextFallbackUrlIndex = 0;
 
+    private int mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
+    private int mEvaluateAttempts = 0;
+
     public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
             NetworkRequest defaultRequest) {
         this(context, handler, networkAgentInfo, defaultRequest, new IpConnectivityLog(),
@@ -334,6 +344,7 @@
         addState(mDefaultState);
         addState(mMaybeNotifyState, mDefaultState);
             addState(mEvaluatingState, mMaybeNotifyState);
+                addState(mProbingState, mEvaluatingState);
             addState(mCaptivePortalState, mMaybeNotifyState);
         addState(mEvaluatingPrivateDnsState, mDefaultState);
         addState(mValidatedState, mDefaultState);
@@ -582,9 +593,6 @@
     // Being in the EvaluatingState State indicates the Network is being evaluated for internet
     // connectivity, or that the user has indicated that this network is unwanted.
     private class EvaluatingState extends State {
-        private int mReevaluateDelayMs;
-        private int mAttempts;
-
         @Override
         public void enter() {
             // If we have already started to track time spent in EvaluatingState
@@ -599,7 +607,7 @@
                 mUidResponsibleForReeval = INVALID_UID;
             }
             mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
-            mAttempts = 0;
+            mEvaluateAttempts = 0;
         }
 
         @Override
@@ -630,42 +638,15 @@
                         transitionTo(mValidatedState);
                         return HANDLED;
                     }
-                    mAttempts++;
-                    // Note: This call to isCaptivePortal() could take up to a minute. Resolving the
-                    // server's IP addresses could hit the DNS timeout, and attempting connections
-                    // to each of the server's several IP addresses (currently one IPv4 and one
-                    // IPv6) could each take SOCKET_TIMEOUT_MS.  During this time this StateMachine
-                    // will be unresponsive. isCaptivePortal() could be executed on another Thread
-                    // if this is found to cause problems.
-                    CaptivePortalProbeResult probeResult = isCaptivePortal();
-                    if (probeResult.isSuccessful()) {
-                        // Transit EvaluatingPrivateDnsState to get to Validated
-                        // state (even if no Private DNS validation required).
-                        transitionTo(mEvaluatingPrivateDnsState);
-                    } else if (probeResult.isPortal()) {
-                        notifyNetworkTestResultInvalid(probeResult.redirectUrl);
-                        mLastPortalProbeResult = probeResult;
-                        transitionTo(mCaptivePortalState);
-                    } else {
-                        final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
-                        sendMessageDelayed(msg, mReevaluateDelayMs);
-                        logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
-                        notifyNetworkTestResultInvalid(probeResult.redirectUrl);
-                        if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
-                            // Don't continue to blame UID forever.
-                            TrafficStats.clearThreadStatsUid();
-                        }
-                        mReevaluateDelayMs *= 2;
-                        if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
-                            mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
-                        }
-                    }
+                    mEvaluateAttempts++;
+
+                    transitionTo(mProbingState);
                     return HANDLED;
                 case CMD_FORCE_REEVALUATION:
                     // Before IGNORE_REEVALUATE_ATTEMPTS attempts are made,
                     // ignore any re-evaluation requests. After, restart the
                     // evaluation process via EvaluatingState#enter.
-                    return (mAttempts < IGNORE_REEVALUATE_ATTEMPTS) ? HANDLED : NOT_HANDLED;
+                    return (mEvaluateAttempts < IGNORE_REEVALUATE_ATTEMPTS) ? HANDLED : NOT_HANDLED;
                 default:
                     return NOT_HANDLED;
             }
@@ -852,6 +833,76 @@
         }
     }
 
+    private class ProbingState extends State {
+        private Thread mThread;
+
+        @Override
+        public void enter() {
+            mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE,
+                    isCaptivePortal())));
+            mThread.start();
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            switch (message.what) {
+                case CMD_PROBE_COMPLETE:
+                    // Currently, it's not possible to exit this state without mThread having
+                    // terminated. Therefore, this state can never get CMD_PROBE_COMPLETE from a
+                    // stale thread that is not mThread.
+                    // TODO: As soon as it's possible to exit this state without mThread having
+                    // terminated, ensure that CMD_PROBE_COMPLETE from stale threads are ignored.
+                    // This could be done via a sequence number, or by changing mThread to a class
+                    // that has a stopped volatile boolean or AtomicBoolean.
+                    final CaptivePortalProbeResult probeResult =
+                            (CaptivePortalProbeResult) message.obj;
+
+                    if (probeResult.isSuccessful()) {
+                        // Transit EvaluatingPrivateDnsState to get to Validated
+                        // state (even if no Private DNS validation required).
+                        transitionTo(mEvaluatingPrivateDnsState);
+                    } else if (probeResult.isPortal()) {
+                        notifyNetworkTestResultInvalid(probeResult.redirectUrl);
+                        mLastPortalProbeResult = probeResult;
+                        transitionTo(mCaptivePortalState);
+                    } else {
+                        final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
+                        sendMessageDelayed(msg, mReevaluateDelayMs);
+                        logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
+                        notifyNetworkTestResultInvalid(probeResult.redirectUrl);
+                        if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
+                            // Don't continue to blame UID forever.
+                            TrafficStats.clearThreadStatsUid();
+                        }
+                        mReevaluateDelayMs *= 2;
+                        if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
+                            mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
+                        }
+                    }
+                    return HANDLED;
+                case CMD_REEVALUATE:
+                    // Leave the event to EvaluatingState. Defer this message will result in reset
+                    // of mReevaluateDelayMs and mEvaluateAttempts.
+                    return NOT_HANDLED;
+                default:
+                    // TODO: Some events may able to handle in this state, instead of deferring to
+                    // next state.
+                    deferMessage(message);
+                    return HANDLED;
+            }
+        }
+
+        @Override
+        public void exit() {
+            // If StateMachine get here, the probe started in enter() is guaranteed to have
+            // completed, because in this state, all messages except CMD_PROBE_COMPLETE and
+            // CMD_REEVALUATE are deferred. CMD_REEVALUATE cannot be in the queue, because it is
+            // only ever sent in EvaluatingState#enter, and the StateMachine reach this state by
+            // processing it. Therefore, there is no need to stop the thread.
+            mThread = null;
+        }
+    }
+
     // Limits the list of IP addresses returned by getAllByName or tried by openConnection to at
     // most one per address family. This ensures we only wait up to 20 seconds for TCP connections
     // to complete, regardless of how many IP addresses a host has.
@@ -1031,10 +1082,10 @@
                 result.isPortal() /* isCaptivePortal */,
                 startTime, endTime);
 
-        log("isCaptivePortal: isSuccessful()=" + result.isSuccessful() +
-                " isPortal()=" + result.isPortal() +
-                " RedirectUrl=" + result.redirectUrl +
-                " StartTime=" + startTime + " EndTime=" + endTime);
+        log("isCaptivePortal: isSuccessful()=" + result.isSuccessful()
+                + " isPortal()=" + result.isPortal()
+                + " RedirectUrl=" + result.redirectUrl
+                + " Time=" + (endTime - startTime) + "ms");
 
         return result;
     }
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 7b8571c..deaa334 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -23,6 +23,8 @@
 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
 import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
 
 import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
@@ -64,6 +66,7 @@
     private static final boolean DBG = true;
     private static final Boolean SYSTEM = Boolean.TRUE;
     private static final Boolean NETWORK = Boolean.FALSE;
+    private static final int VERSION_Q = Build.VERSION_CODES.Q;
 
     private final Context mContext;
     private final PackageManager mPackageManager;
@@ -87,7 +90,7 @@
             public void onReceive(Context context, Intent intent) {
                 String action = intent.getAction();
                 int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                int appUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                int appUid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
                 Uri appData = intent.getData();
                 String appName = appData != null ? appData.getSchemeSpecificPart() : null;
 
@@ -127,7 +130,7 @@
         }
 
         for (PackageInfo app : apps) {
-            int uid = app.applicationInfo != null ? app.applicationInfo.uid : -1;
+            int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
             if (uid < 0) {
                 continue;
             }
@@ -162,6 +165,11 @@
     }
 
     @VisibleForTesting
+    int getDeviceFirstSdkInt() {
+        return Build.VERSION.FIRST_SDK_INT;
+    }
+
+    @VisibleForTesting
     boolean hasPermission(PackageInfo app, String permission) {
         if (app.requestedPermissions != null) {
             for (String p : app.requestedPermissions) {
@@ -180,10 +188,17 @@
     private boolean hasRestrictedNetworkPermission(PackageInfo app) {
         // TODO : remove this check in the future(b/31479477). All apps should just
         // request the appropriate permission for their use case since android Q.
-        if (app.applicationInfo != null
-                && app.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q
-                && isVendorApp(app.applicationInfo)) {
-            return true;
+        if (app.applicationInfo != null) {
+            // Backward compatibility for b/114245686, on devices that launched before Q daemons
+            // and apps running as the system UID are exempted from this check.
+            if (app.applicationInfo.uid == SYSTEM_UID && getDeviceFirstSdkInt() < VERSION_Q) {
+                return true;
+            }
+
+            if (app.applicationInfo.targetSdkVersion < VERSION_Q
+                    && isVendorApp(app.applicationInfo)) {
+                return true;
+            }
         }
         return hasPermission(app, CONNECTIVITY_INTERNAL)
                 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 48082b6..b7ed2f9 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -381,6 +381,15 @@
     }
 
     /**
+     * Check whether to prevent all traffic outside of a VPN even when the VPN is not connected.
+     *
+     * @return {@code true} if VPN lockdown is enabled.
+     */
+    public boolean getLockdown() {
+        return mLockdown;
+    }
+
+    /**
      * Checks if a VPN app supports always-on mode.
      *
      * In order to support the always-on feature, an app has to
@@ -1533,17 +1542,15 @@
     }
 
     /**
-     * @return {@code true} if {@param uid} is blocked by an always-on VPN.
-     *         A UID is blocked if it's included in one of the mBlockedUsers ranges and the VPN is
-     *         not connected, or if the VPN is connected but does not apply to the UID.
+     * @param uid The target uid.
      *
+     * @return {@code true} if {@code uid} is included in one of the mBlockedUsers ranges and the
+     * VPN is not connected, or if the VPN is connected but does not apply to the {@code uid}.
+     *
+     * @apiNote This method don't check VPN lockdown status.
      * @see #mBlockedUsers
      */
     public synchronized boolean isBlockingUid(int uid) {
-        if (!mLockdown) {
-            return false;
-        }
-
         if (mNetworkInfo.isConnected()) {
             return !appliesToUid(uid);
         } else {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e3a1a91..9d5d65d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -136,6 +136,7 @@
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.internal.inputmethod.StartInputFlags;
 import com.android.internal.inputmethod.StartInputReason;
 import com.android.internal.inputmethod.UnbindReason;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -1916,8 +1917,8 @@
     @GuardedBy("mMethodMap")
     @NonNull
     InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
-            @MissingMethodFlags int missingMethods, @NonNull EditorInfo attribute, int controlFlags,
-            @StartInputReason int startInputReason) {
+            @MissingMethodFlags int missingMethods, @NonNull EditorInfo attribute,
+            @StartInputFlags int startInputFlags, @StartInputReason int startInputReason) {
         // If no method is currently selected, do nothing.
         if (mCurMethodId == null) {
             return InputBindResult.NO_IME;
@@ -1979,7 +1980,7 @@
                 // Fast case: if we are already connected to the input method,
                 // then just return it.
                 return attachNewInputLocked(startInputReason,
-                        (controlFlags&InputMethodManager.CONTROL_START_INITIAL) != 0);
+                        (startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0);
             }
             if (mHaveConnection) {
                 if (mCurMethod != null) {
@@ -2794,15 +2795,15 @@
     @Override
     public InputBindResult startInputOrWindowGainedFocus(
             @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
-            int controlFlags, @SoftInputModeFlags int softInputMode, int windowFlags,
-            @Nullable EditorInfo attribute, IInputContext inputContext,
+            @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
+            int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
             @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
         if (windowToken == null) {
             Slog.e(TAG, "windowToken cannot be null.");
             return InputBindResult.NULL;
         }
         final InputBindResult result = startInputOrWindowGainedFocusInternal(startInputReason,
-                client, windowToken, controlFlags, softInputMode, windowFlags, attribute,
+                client, windowToken, startInputFlags, softInputMode, windowFlags, attribute,
                 inputContext, missingMethods, unverifiedTargetSdkVersion);
         if (result == null) {
             // This must never happen, but just in case.
@@ -2818,9 +2819,10 @@
     @NonNull
     private InputBindResult startInputOrWindowGainedFocusInternal(
             @StartInputReason int startInputReason, IInputMethodClient client,
-            @NonNull IBinder windowToken, int controlFlags, @SoftInputModeFlags int softInputMode,
-            int windowFlags, EditorInfo attribute, IInputContext inputContext,
-            @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
+            @NonNull IBinder windowToken, @StartInputFlags int startInputFlags,
+            @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
+            IInputContext inputContext, @MissingMethodFlags int missingMethods,
+            int unverifiedTargetSdkVersion) {
         // Needs to check the validity before clearing calling identity
         final boolean calledFromValidUser = calledFromValidUser();
         InputBindResult res = null;
@@ -2836,7 +2838,8 @@
                         + " missingMethods="
                         + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
                         + " attribute=" + attribute
-                        + " controlFlags=#" + Integer.toHexString(controlFlags)
+                        + " startInputFlags="
+                        + InputMethodDebug.startInputFlagsToString(startInputFlags)
                         + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
                         + " windowFlags=#" + Integer.toHexString(windowFlags)
                         + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
@@ -2882,7 +2885,7 @@
                     }
                     if (attribute != null) {
                         return startInputUncheckedLocked(cs, inputContext, missingMethods,
-                                attribute, controlFlags, startInputReason);
+                                attribute, startInputFlags, startInputReason);
                     }
                     return new InputBindResult(
                             InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
@@ -2905,7 +2908,7 @@
                         || mRes.getConfiguration().isLayoutSizeAtLeast(
                                 Configuration.SCREENLAYOUT_SIZE_LARGE);
                 final boolean isTextEditor =
-                        (controlFlags&InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR) != 0;
+                        (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0;
 
                 // We want to start input before showing the IME, but after closing
                 // it.  We want to do this after closing it to help the IME disappear
@@ -2943,8 +2946,8 @@
                             // is more room for the target window + IME.
                             if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
                             if (attribute != null) {
-                                res = startInputUncheckedLocked(cs, inputContext,
-                                        missingMethods, attribute, controlFlags, startInputReason);
+                                res = startInputUncheckedLocked(cs, inputContext, missingMethods,
+                                        attribute, startInputFlags, startInputReason);
                                 didStart = true;
                             }
                             showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
@@ -2969,10 +2972,10 @@
                                 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
                             if (DEBUG) Slog.v(TAG, "Window asks to show input going forward");
                             if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
-                                    unverifiedTargetSdkVersion, controlFlags)) {
+                                    unverifiedTargetSdkVersion, startInputFlags)) {
                                 if (attribute != null) {
                                     res = startInputUncheckedLocked(cs, inputContext,
-                                            missingMethods, attribute, controlFlags,
+                                            missingMethods, attribute, startInputFlags,
                                             startInputReason);
                                     didStart = true;
                                 }
@@ -2987,10 +2990,10 @@
                     case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
                         if (DEBUG) Slog.v(TAG, "Window asks to always show input");
                         if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
-                                unverifiedTargetSdkVersion, controlFlags)) {
+                                unverifiedTargetSdkVersion, startInputFlags)) {
                             if (attribute != null) {
                                 res = startInputUncheckedLocked(cs, inputContext, missingMethods,
-                                        attribute, controlFlags, startInputReason);
+                                        attribute, startInputFlags, startInputReason);
                                 didStart = true;
                             }
                             showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
@@ -3005,11 +3008,10 @@
                 if (!didStart) {
                     if (attribute != null) {
                         if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
-                                || (controlFlags
-                                & InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR) != 0) {
+                                || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
                             res = startInputUncheckedLocked(cs, inputContext, missingMethods,
                                     attribute,
-                                    controlFlags, startInputReason);
+                                    startInputFlags, startInputReason);
                         } else {
                             res = InputBindResult.NO_EDITOR;
                         }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index e951b27..154e8b3 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -16,9 +16,6 @@
 
 package com.android.server.inputmethod;
 
-import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR;
-import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_VIEW_HAS_FOCUS;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -44,6 +41,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.StartInputFlags;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -1299,15 +1297,15 @@
     }
 
     public static boolean isSoftInputModeStateVisibleAllowed(
-            int targetSdkVersion, int controlFlags) {
+            int targetSdkVersion, @StartInputFlags int startInputFlags) {
         if (targetSdkVersion < Build.VERSION_CODES.P) {
             // for compatibility.
             return true;
         }
-        if ((controlFlags & CONTROL_WINDOW_VIEW_HAS_FOCUS) == 0) {
+        if ((startInputFlags & StartInputFlags.VIEW_HAS_FOCUS) == 0) {
             return false;
         }
-        if ((controlFlags & CONTROL_WINDOW_IS_TEXT_EDITOR) == 0) {
+        if ((startInputFlags & StartInputFlags.IS_TEXT_EDITOR) == 0) {
             return false;
         }
         return true;
diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
index f82b976..22fabb2 100644
--- a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
@@ -16,59 +16,26 @@
 
 package com.android.server.location;
 
-import com.android.server.ServiceWatcher;
-
 import android.content.Context;
 import android.hardware.location.ActivityRecognitionHardware;
 import android.hardware.location.IActivityRecognitionHardwareClient;
 import android.hardware.location.IActivityRecognitionHardwareWatcher;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.os.BackgroundThread;
+import com.android.server.ServiceWatcher;
+
 /**
  * Proxy class to bind GmsCore to the ActivityRecognitionHardware.
  *
  * @hide
  */
 public class ActivityRecognitionProxy {
+
     private static final String TAG = "ActivityRecognitionProxy";
 
-    private final ServiceWatcher mServiceWatcher;
-    private final boolean mIsSupported;
-    private final ActivityRecognitionHardware mInstance;
-
-    private ActivityRecognitionProxy(
-            Context context,
-            Handler handler,
-            boolean activityRecognitionHardwareIsSupported,
-            ActivityRecognitionHardware activityRecognitionHardware,
-            int overlaySwitchResId,
-            int defaultServicePackageNameResId,
-            int initialPackageNameResId) {
-        mIsSupported = activityRecognitionHardwareIsSupported;
-        mInstance = activityRecognitionHardware;
-
-        Runnable newServiceWork = new Runnable() {
-            @Override
-            public void run() {
-                bindProvider();
-            }
-        };
-
-        // prepare the connection to the provider
-        mServiceWatcher = new ServiceWatcher(
-                context,
-                TAG,
-                "com.android.location.service.ActivityRecognitionProvider",
-                overlaySwitchResId,
-                defaultServicePackageNameResId,
-                initialPackageNameResId,
-                newServiceWork,
-                handler);
-    }
-
     /**
      * Creates an instance of the proxy and binds it to the appropriate FusedProvider.
      *
@@ -76,7 +43,6 @@
      */
     public static ActivityRecognitionProxy createAndBind(
             Context context,
-            Handler handler,
             boolean activityRecognitionHardwareIsSupported,
             ActivityRecognitionHardware activityRecognitionHardware,
             int overlaySwitchResId,
@@ -84,74 +50,69 @@
             int initialPackageNameResId) {
         ActivityRecognitionProxy activityRecognitionProxy = new ActivityRecognitionProxy(
                 context,
-                handler,
                 activityRecognitionHardwareIsSupported,
                 activityRecognitionHardware,
                 overlaySwitchResId,
                 defaultServicePackageNameResId,
                 initialPackageNameResId);
 
-        // try to bind the provider
-        if (!activityRecognitionProxy.mServiceWatcher.start()) {
-            Log.e(TAG, "ServiceWatcher could not start.");
+        if (activityRecognitionProxy.mServiceWatcher.start()) {
+            return activityRecognitionProxy;
+        } else {
             return null;
         }
-        return activityRecognitionProxy;
     }
 
-    /**
-     * Helper function to bind the FusedLocationHardware to the appropriate FusedProvider instance.
-     */
-    private void bindProvider() {
-        if (!mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-            @Override
-            public void run(IBinder binder) {
-                String descriptor;
-                try {
-                    descriptor = binder.getInterfaceDescriptor();
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Unable to get interface descriptor.", e);
-                    return;
-                }
+    private final ServiceWatcher mServiceWatcher;
+    private final boolean mIsSupported;
+    private final ActivityRecognitionHardware mInstance;
 
-                if (IActivityRecognitionHardwareWatcher.class.getCanonicalName()
-                        .equals(descriptor)) {
-                    IActivityRecognitionHardwareWatcher watcher =
-                            IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
-                    if (watcher == null) {
-                        Log.e(TAG, "No watcher found on connection.");
-                        return;
-                    }
-                    if (mInstance == null) {
-                        // to keep backwards compatibility do not update the watcher when there is
-                        // no instance available, or it will cause an NPE
-                        Log.d(TAG, "AR HW instance not available, binding will be a no-op.");
-                        return;
-                    }
-                    try {
-                        watcher.onInstanceChanged(mInstance);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error delivering hardware interface to watcher.", e);
-                    }
-                } else if (IActivityRecognitionHardwareClient.class.getCanonicalName()
-                            .equals(descriptor)) {
-                    IActivityRecognitionHardwareClient client =
-                            IActivityRecognitionHardwareClient.Stub.asInterface(binder);
-                    if (client == null) {
-                        Log.e(TAG, "No client found on connection.");
-                        return;
-                    }
-                    try {
-                        client.onAvailabilityChanged(mIsSupported, mInstance);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error delivering hardware interface to client.", e);
-                    }
-                } else {
-                    Log.e(TAG, "Invalid descriptor found on connection: " + descriptor);
-                }
+    private ActivityRecognitionProxy(
+            Context context,
+            boolean activityRecognitionHardwareIsSupported,
+            ActivityRecognitionHardware activityRecognitionHardware,
+            int overlaySwitchResId,
+            int defaultServicePackageNameResId,
+            int initialPackageNameResId) {
+        mIsSupported = activityRecognitionHardwareIsSupported;
+        mInstance = activityRecognitionHardware;
+
+        mServiceWatcher = new ServiceWatcher(
+                context,
+                TAG,
+                "com.android.location.service.ActivityRecognitionProvider",
+                overlaySwitchResId,
+                defaultServicePackageNameResId,
+                initialPackageNameResId,
+                BackgroundThread.getHandler()) {
+            @Override
+            protected void onBind() {
+                runOnBinder(ActivityRecognitionProxy.this::initializeService);
             }
-        })) {
-            Log.e(TAG, "Null binder found on connection.");
+        };
+    }
+
+    private void initializeService(IBinder binder) {
+        try {
+            String descriptor = binder.getInterfaceDescriptor();
+
+            if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(
+                    descriptor)) {
+                IActivityRecognitionHardwareWatcher watcher =
+                        IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
+                if (mInstance != null) {
+                    watcher.onInstanceChanged(mInstance);
+                }
+            } else if (IActivityRecognitionHardwareClient.class.getCanonicalName()
+                    .equals(descriptor)) {
+                IActivityRecognitionHardwareClient client =
+                        IActivityRecognitionHardwareClient.Stub.asInterface(binder);
+                client.onAvailabilityChanged(mIsSupported, mInstance);
+            } else {
+                Log.e(TAG, "Invalid descriptor found on connection: " + descriptor);
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
         }
     }
 }
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index 1d0ab8f..002d4e1 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -16,12 +16,15 @@
 
 package com.android.server.location;
 
+import android.Manifest;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
 import android.hardware.contexthub.V1_0.IContexthub;
 import android.hardware.contexthub.V1_0.Result;
 import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubManager;
 import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.IContextHubClient;
 import android.hardware.location.IContextHubClientCallback;
@@ -30,7 +33,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
 
 /**
  * A class that acts as a broker for the ContextHubClient, which handles messaging and life-cycle
@@ -69,14 +72,15 @@
     private final short mHostEndPointId;
 
     /*
-     * The remote callback interface for this client.
+     * The remote callback interface for this client. This will be set to null whenever the
+     * client connection is closed (either explicitly or via binder death).
      */
-    private final IContextHubClientCallback mCallbackInterface;
+    private IContextHubClientCallback mCallbackInterface = null;
 
     /*
-     * false if the connection has been closed by the client, true otherwise.
+     * True if the client is still registered with the Context Hub Service, false otherwise.
      */
-    private final AtomicBoolean mConnectionOpen = new AtomicBoolean(true);
+    private boolean mRegistered = true;
 
     /*
      * Internal interface used to invoke client callbacks.
@@ -85,25 +89,82 @@
         void accept(IContextHubClientCallback callback) throws RemoteException;
     }
 
+    /*
+     * The PendingIntent registered with this client.
+     */
+    private final PendingIntentRequest mPendingIntentRequest = new PendingIntentRequest();
+
+    /*
+     * Helper class to manage registered PendingIntent requests from the client.
+     */
+    private class PendingIntentRequest {
+        /*
+         * The PendingIntent object to request, null if there is no open request.
+         */
+        private PendingIntent mPendingIntent;
+
+        /*
+         * The ID of the nanoapp the request is for, invalid if there is no open request.
+         */
+        private long mNanoAppId;
+
+        PendingIntentRequest() {}
+
+        PendingIntentRequest(PendingIntent pendingIntent, long nanoAppId) {
+            mPendingIntent = pendingIntent;
+            mNanoAppId = nanoAppId;
+        }
+
+        public long getNanoAppId() {
+            return mNanoAppId;
+        }
+
+        public PendingIntent getPendingIntent() {
+            return mPendingIntent;
+        }
+
+        public boolean hasPendingIntent() {
+            return mPendingIntent != null;
+        }
+
+        public void clear() {
+            mPendingIntent = null;
+        }
+
+        public boolean register(PendingIntent pendingIntent, long nanoAppId) {
+            boolean success = false;
+            if (hasPendingIntent()) {
+                Log.e(TAG, "Failed to register PendingIntent: registered PendingIntent exists");
+            } else {
+                mNanoAppId = nanoAppId;
+                mPendingIntent = pendingIntent;
+                success = true;
+            }
+
+            return success;
+        }
+
+        public boolean unregister(PendingIntent pendingIntent) {
+            boolean success = false;
+            if (!hasPendingIntent() || !mPendingIntent.equals(pendingIntent)) {
+                Log.e(TAG, "Failed to unregister PendingIntent: PendingIntent is not registered");
+            } else {
+                mPendingIntent = null;
+                success = true;
+            }
+
+            return success;
+        }
+    }
+
     /* package */ ContextHubClientBroker(
             Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
-            ContextHubInfo contextHubInfo, short hostEndPointId,
-            IContextHubClientCallback callback) {
+            ContextHubInfo contextHubInfo, short hostEndPointId) {
         mContext = context;
         mContextHubProxy = contextHubProxy;
         mClientManager = clientManager;
         mAttachedContextHubInfo = contextHubInfo;
         mHostEndPointId = hostEndPointId;
-        mCallbackInterface = callback;
-    }
-
-    /**
-     * Attaches a death recipient for this client
-     *
-     * @throws RemoteException if the client has already died
-     */
-    /* package */ void attachDeathRecipient() throws RemoteException {
-        mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
     }
 
     /**
@@ -118,9 +179,13 @@
         ContextHubServiceUtil.checkPermissions(mContext);
 
         int result;
-        if (mConnectionOpen.get()) {
-            ContextHubMsg messageToNanoApp = ContextHubServiceUtil.createHidlContextHubMessage(
-                    mHostEndPointId, message);
+        IContextHubClientCallback callback = null;
+        synchronized (this) {
+            callback = mCallbackInterface;
+        }
+        if (callback != null) {
+            ContextHubMsg messageToNanoApp =
+                    ContextHubServiceUtil.createHidlContextHubMessage(mHostEndPointId, message);
 
             int contextHubId = mAttachedContextHubInfo.getId();
             try {
@@ -139,24 +204,47 @@
     }
 
     /**
-     * @param intent the intent to register
-     * @param nanoAppId the ID of the nanoapp to send events for
+     * @param pendingIntent the intent to register
+     * @param nanoAppId     the ID of the nanoapp to send events for
      * @return true on success, false otherwise
      */
     @Override
-    public boolean registerIntent(PendingIntent intent, long nanoAppId) {
-        // TODO: Implement this
-        return false;
+    public boolean registerIntent(PendingIntent pendingIntent, long nanoAppId) {
+        ContextHubServiceUtil.checkPermissions(mContext);
+        if (mClientManager.isPendingIntentRegistered(pendingIntent)) {
+            Log.e(TAG, "Failed to register PendingIntent: already registered");
+            return false;
+        }
+
+        boolean success = false;
+        synchronized (this) {
+            if (mCallbackInterface == null) {
+                Log.e(TAG, "Failed to register PendingIntent: client connection is closed");
+            } else {
+                success = mPendingIntentRequest.register(pendingIntent, nanoAppId);
+            }
+        }
+
+        return success;
     }
 
     /**
-     * @param intent the intent to unregister
+     * @param pendingIntent the intent to unregister
      * @return true on success, false otherwise
      */
     @Override
-    public boolean unregisterIntent(PendingIntent intent) {
-        // TODO: Implement this
-        return false;
+    public boolean unregisterIntent(PendingIntent pendingIntent) {
+        ContextHubServiceUtil.checkPermissions(mContext);
+
+        boolean success = false;
+        synchronized (this) {
+            success = mPendingIntentRequest.unregister(pendingIntent);
+            if (mCallbackInterface == null) {
+                close();
+            }
+        }
+
+        return success;
     }
 
     /**
@@ -164,8 +252,15 @@
      */
     @Override
     public void close() {
-        if (mConnectionOpen.getAndSet(false)) {
-            mClientManager.unregisterClient(mHostEndPointId);
+        synchronized (this) {
+            if (mCallbackInterface != null) {
+                mCallbackInterface.asBinder().unlinkToDeath(this, 0 /* flags */);
+                mCallbackInterface = null;
+            }
+            if (!mPendingIntentRequest.hasPendingIntent() && mRegistered) {
+                mClientManager.unregisterClient(mHostEndPointId);
+                mRegistered = false;
+            }
         }
     }
 
@@ -178,6 +273,37 @@
     }
 
     /**
+     * Sets the callback interface for this client, only if the callback is currently unregistered.
+     *
+     * Also attaches a death recipient to a ContextHubClientBroker object. If unsuccessful, the
+     * connection is closed.
+     *
+     * @param callback the callback interface
+     * @return true if the callback was successfully set, false otherwise
+     *
+     * @throws IllegalStateException if the client has already been registered to a callback
+     */
+    /* package */
+    synchronized boolean setCallback(IContextHubClientCallback callback) {
+        boolean success = false;
+        if (mCallbackInterface != null) {
+            throw new IllegalStateException("Client is already registered with a callback");
+        } else {
+            mCallbackInterface = callback;
+            try {
+                mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
+                success = true;
+            } catch (RemoteException e) {
+                // The client process has died, so we close the connection.
+                Log.e(TAG, "Failed to attach death recipient to client");
+                close();
+            }
+        }
+
+        return success;
+    }
+
+    /**
      * @return the ID of the context hub this client is attached to
      */
     /* package */ int getAttachedContextHubId() {
@@ -197,7 +323,12 @@
      * @param message the message that came from a nanoapp
      */
     /* package */ void sendMessageToClient(NanoAppMessage message) {
-        invokeCallbackConcurrent(callback -> callback.onMessageFromNanoApp(message));
+        invokeCallback(callback -> callback.onMessageFromNanoApp(message));
+
+        Supplier<Intent> supplier =
+                () -> createIntent(ContextHubManager.EVENT_NANOAPP_MESSAGE, message.getNanoAppId())
+                        .putExtra(ContextHubManager.EXTRA_MESSAGE, message);
+        sendPendingIntent(supplier);
     }
 
     /**
@@ -206,7 +337,8 @@
      * @param nanoAppId the ID of the nanoapp that was loaded.
      */
     /* package */ void onNanoAppLoaded(long nanoAppId) {
-        invokeCallbackConcurrent(callback -> callback.onNanoAppLoaded(nanoAppId));
+        invokeCallback(callback -> callback.onNanoAppLoaded(nanoAppId));
+        sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_NANOAPP_LOADED, nanoAppId));
     }
 
     /**
@@ -215,14 +347,16 @@
      * @param nanoAppId the ID of the nanoapp that was unloaded.
      */
     /* package */ void onNanoAppUnloaded(long nanoAppId) {
-        invokeCallbackConcurrent(callback -> callback.onNanoAppUnloaded(nanoAppId));
+        invokeCallback(callback -> callback.onNanoAppUnloaded(nanoAppId));
+        sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_NANOAPP_UNLOADED, nanoAppId));
     }
 
     /**
      * Notifies the client of a hub reset event if the connection is open.
      */
     /* package */ void onHubReset() {
-        invokeCallbackConcurrent(callback -> callback.onHubReset());
+        invokeCallback(callback -> callback.onHubReset());
+        sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_HUB_RESET));
     }
 
     /**
@@ -232,7 +366,24 @@
      * @param abortCode the nanoapp specific abort code
      */
     /* package */ void onNanoAppAborted(long nanoAppId, int abortCode) {
-        invokeCallbackConcurrent(callback -> callback.onNanoAppAborted(nanoAppId, abortCode));
+        invokeCallback(callback -> callback.onNanoAppAborted(nanoAppId, abortCode));
+
+        Supplier<Intent> supplier =
+                () -> createIntent(ContextHubManager.EVENT_NANOAPP_ABORTED, nanoAppId)
+                        .putExtra(ContextHubManager.EXTRA_NANOAPP_ABORT_CODE, abortCode);
+        sendPendingIntent(supplier);
+    }
+
+    /**
+     * @param intent the PendingIntent to compare to
+     * @return true if the given PendingIntent is currently registered, false otherwise
+     */
+    /* package */ boolean hasPendingIntent(PendingIntent intent) {
+        PendingIntent pendingIntent = null;
+        synchronized (this) {
+            pendingIntent = mPendingIntentRequest.getPendingIntent();
+        }
+        return (pendingIntent != null) && pendingIntent.equals(intent);
     }
 
     /**
@@ -240,8 +391,8 @@
      *
      * @param consumer the consumer specifying the callback to invoke
      */
-    private void invokeCallbackConcurrent(CallbackConsumer consumer) {
-        if (mConnectionOpen.get()) {
+    private synchronized void invokeCallback(CallbackConsumer consumer) {
+        if (mCallbackInterface != null) {
             try {
                 consumer.accept(mCallbackInterface);
             } catch (RemoteException e) {
@@ -250,4 +401,56 @@
             }
         }
     }
+
+    /**
+     * Creates an Intent object containing the ContextHubManager.EXTRA_EVENT_TYPE extra field
+     *
+     * @param eventType the ContextHubManager.Event type describing the event
+     * @return the Intent object
+     */
+    private Intent createIntent(int eventType) {
+        Intent intent = new Intent();
+        intent.putExtra(ContextHubManager.EXTRA_EVENT_TYPE, eventType);
+        intent.putExtra(ContextHubManager.EXTRA_CONTEXT_HUB_INFO, mAttachedContextHubInfo);
+        return intent;
+    }
+
+    /**
+     * Creates an Intent object containing the ContextHubManager.EXTRA_EVENT_TYPE and the
+     * ContextHubManager.EXTRA_NANOAPP_ID extra fields
+     *
+     * @param eventType the ContextHubManager.Event type describing the event
+     * @param nanoAppId the ID of the nanoapp this event is for
+     * @return the Intent object
+     */
+    private Intent createIntent(int eventType, long nanoAppId) {
+        Intent intent = createIntent(eventType);
+        intent.putExtra(ContextHubManager.EXTRA_NANOAPP_ID, nanoAppId);
+        return intent;
+    }
+
+    /**
+     * Sends an intent to any existing PendingIntent
+     *
+     * @param supplier method to create the extra Intent
+     */
+    private synchronized void sendPendingIntent(Supplier<Intent> supplier) {
+        if (mPendingIntentRequest.hasPendingIntent()) {
+            Intent intent = supplier.get();
+            try {
+                mPendingIntentRequest.getPendingIntent().send(
+                        mContext, 0 /* code */, intent, null /* onFinished */, null /* Handler */,
+                        Manifest.permission.LOCATION_HARDWARE /* requiredPermission */,
+                        null /* options */);
+            } catch (PendingIntent.CanceledException e) {
+                // The PendingIntent is no longer valid
+                Log.w(TAG, "PendingIntent has been canceled, unregistering from client"
+                        + " (host endpoint ID " + mHostEndPointId + ")");
+                mPendingIntentRequest.clear();
+                if (mCallbackInterface == null) {
+                    close();
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index eda8c6f..fe93a1a 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.location;
 
+import android.app.PendingIntent;
 import android.content.Context;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
 import android.hardware.contexthub.V1_0.IContexthub;
@@ -23,7 +24,6 @@
 import android.hardware.location.IContextHubClient;
 import android.hardware.location.IContextHubClientCallback;
 import android.hardware.location.NanoAppMessage;
-import android.os.RemoteException;
 import android.util.Log;
 
 import java.util.concurrent.ConcurrentHashMap;
@@ -88,15 +88,9 @@
      */
     /* package */ IContextHubClient registerClient(
             IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
-        ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubInfo);
-
-        try {
-            broker.attachDeathRecipient();
-        } catch (RemoteException e) {
-            // The client process has died, so we close the connection and return null.
-            Log.e(TAG, "Failed to attach death recipient to client");
-            broker.close();
-            return null;
+        ContextHubClientBroker broker = createNewClientBroker(contextHubInfo);
+        if (!broker.setCallback(clientCallback)) {
+            return null; // Client process has died, so we return null
         }
 
         Log.d(TAG, "Registered client with host endpoint ID " + broker.getHostEndPointId());
@@ -104,6 +98,36 @@
     }
 
     /**
+     * Binds a existing and registered client with a new callback interface, provided a previously
+     * registered PendingIntent.
+     *
+     * @param pendingIntent  a previously registered PendingIntent for a registered client
+     * @param clientCallback the callback interface of the client to bind to
+     * @param contextHubId   the ID of the hub this client is attached to
+     *
+     * @return the client interface
+     *
+     * @throws IllegalArgumentException if no matching client is found
+     * @throws IllegalStateException    if the client has already been registered to a callback
+     */
+    /* package */ IContextHubClient bindClient(
+            PendingIntent pendingIntent, IContextHubClientCallback clientCallback,
+            int contextHubId) {
+        ContextHubClientBroker broker = getClientBroker(pendingIntent, contextHubId);
+        if (broker == null) {
+            throw new IllegalArgumentException("Could not find client of Context Hub (ID = "
+                    + contextHubId + ") with PendingIntent");
+        }
+
+        if (!broker.setCallback(clientCallback)) {
+            return null; // Client process has died, so we return null
+        }
+
+        Log.d(TAG, "Re-registered client with host endpoint ID " + broker.getHostEndPointId());
+        return IContextHubClient.Stub.asInterface(broker);
+    }
+
+    /**
      * Handles a message sent from a nanoapp.
      *
      * @param contextHubId the ID of the hub where the nanoapp sent the message from
@@ -179,10 +203,23 @@
     }
 
     /**
+     * @param pendingIntent the PendingIntent to check
+     * @return true if the given PendingIntent is registered by a client, false otherwise
+     */
+    /* package */ boolean isPendingIntentRegistered(PendingIntent pendingIntent) {
+        for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
+            if (broker.hasPendingIntent(pendingIntent)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
      * Creates a new ContextHubClientBroker object for a client and registers it with the client
      * manager.
      *
-     * @param clientCallback the callback interface of the client to register
      * @param contextHubInfo the object describing the hub this client is attached to
      *
      * @return the ContextHubClientBroker object
@@ -190,7 +227,7 @@
      * @throws IllegalStateException if max number of clients have already registered
      */
     private synchronized ContextHubClientBroker createNewClientBroker(
-            IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
+            ContextHubInfo contextHubInfo) {
         if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) {
             throw new IllegalStateException("Could not register client - max limit exceeded");
         }
@@ -200,8 +237,7 @@
         for (int i = 0; i <= MAX_CLIENT_ID; i++) {
             if (!mHostEndPointIdToClientMap.containsKey((short) id)) {
                 broker = new ContextHubClientBroker(
-                        mContext, mContextHubProxy, this, contextHubInfo, (short) id,
-                        clientCallback);
+                        mContext, mContextHubProxy, this, contextHubInfo, (short) id);
                 mHostEndPointIdToClientMap.put((short) id, broker);
                 mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
                 break;
@@ -236,4 +272,22 @@
             }
         }
     }
+
+    /**
+     * Retrieves a ContextHubClientBroker object with a matching PendingIntent and Context Hub ID.
+     *
+     * @param pendingIntent the PendingIntent to match
+     * @param contextHubId  the ID of the Context Hub the client is attached to
+     * @return the matching ContextHubClientBroker, null if not found
+     */
+    private ContextHubClientBroker getClientBroker(PendingIntent pendingIntent, int contextHubId) {
+        for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
+            if (broker.hasPendingIntent(pendingIntent)
+                    && broker.getAttachedContextHubId() == contextHubId) {
+                return broker;
+            }
+        }
+
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 96e9337..215e67c 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.location;
 
+import android.app.PendingIntent;
 import android.content.Context;
 import android.hardware.contexthub.V1_0.AsyncEventType;
 import android.hardware.contexthub.V1_0.ContextHub;
@@ -415,10 +416,12 @@
         checkPermissions();
 
         ArrayList<Integer> foundInstances = new ArrayList<>();
-        for (NanoAppInstanceInfo info : mNanoAppStateManager.getNanoAppInstanceInfoCollection()) {
-            if (filter.testMatch(info)) {
-                foundInstances.add(info.getHandle());
-            }
+        if (filter != null) {
+            mNanoAppStateManager.foreachNanoAppInstanceInfo((info) -> {
+                if (filter.testMatch(info)) {
+                    foundInstances.add(info.getHandle());
+                }
+            });
         }
 
         int[] retArray = new int[foundInstances.size()];
@@ -629,6 +632,37 @@
     }
 
     /**
+     * Recreates and binds a IContextHubClientCallback interface to an existing and registered
+     * client at the service for the specified Context Hub, provided a previously registered
+     * PendingIntent.
+     *
+     * @param pendingIntent  the PendingIntent previously registered for the client
+     * @param clientCallback the client interface to register with the service
+     * @param contextHubId   the ID of the hub this client is attached to
+     * @return the generated client interface, null if registration was unsuccessful
+     *
+     * @throws IllegalArgumentException if contextHubId is not a valid ID
+     * @throws NullPointerException if clientCallback or pendingIntent is null
+     */
+    @Override
+    public IContextHubClient bindClient(
+            PendingIntent pendingIntent, IContextHubClientCallback clientCallback,
+            int contextHubId) throws RemoteException {
+        checkPermissions();
+        if (!isValidContextHubId(contextHubId)) {
+            throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
+        }
+        if (pendingIntent == null) {
+            throw new NullPointerException("Cannot create client with null pending intent");
+        }
+        if (clientCallback == null) {
+            throw new NullPointerException("Cannot create client with null callback");
+        }
+
+        return mClientManager.bindClient(pendingIntent, clientCallback, contextHubId);
+    }
+
+    /**
      * Loads a nanoapp binary at the specified Context hub.
      *
      * @param contextHubId        the ID of the hub to load the binary
@@ -767,9 +801,7 @@
         pw.println("");
         pw.println("=================== NANOAPPS ====================");
         // Dump nanoAppHash
-        for (NanoAppInstanceInfo info : mNanoAppStateManager.getNanoAppInstanceInfoCollection()) {
-            pw.println(info);
-        }
+        mNanoAppStateManager.foreachNanoAppInstanceInfo((info) -> pw.println(info));
 
         // dump eventLog
     }
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
index 9ad4aa1..f1de371 100644
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ b/services/core/java/com/android/server/location/GeocoderProxy.java
@@ -20,12 +20,12 @@
 import android.location.Address;
 import android.location.GeocoderParams;
 import android.location.IGeocodeProvider;
-import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.os.BackgroundThread;
 import com.android.server.ServiceWatcher;
+
 import java.util.List;
 
 /**
@@ -36,14 +36,13 @@
 
     private static final String SERVICE_ACTION = "com.android.location.service.GeocodeProvider";
 
-    private final Context mContext;
     private final ServiceWatcher mServiceWatcher;
 
     public static GeocoderProxy createAndBind(Context context,
             int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId, Handler handler) {
+            int initialPackageNamesResId) {
         GeocoderProxy proxy = new GeocoderProxy(context, overlaySwitchResId,
-            defaultServicePackageNameResId, initialPackageNamesResId, handler);
+                defaultServicePackageNameResId, initialPackageNamesResId);
         if (proxy.bind()) {
             return proxy;
         } else {
@@ -53,34 +52,30 @@
 
     private GeocoderProxy(Context context,
             int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId, Handler handler) {
-        mContext = context;
-
-        mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, overlaySwitchResId,
-            defaultServicePackageNameResId, initialPackageNamesResId, null, handler);
+            int initialPackageNamesResId) {
+        mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
+                defaultServicePackageNameResId, initialPackageNamesResId,
+                BackgroundThread.getHandler());
     }
 
-    private boolean bind () {
+    private boolean bind() {
         return mServiceWatcher.start();
     }
 
     public String getConnectedPackageName() {
-        return mServiceWatcher.getBestPackageName();
+        return mServiceWatcher.getCurrentPackageName();
     }
 
     public String getFromLocation(double latitude, double longitude, int maxResults,
             GeocoderParams params, List<Address> addrs) {
-        final String[] result = new String[] {"Service not Available"};
-        mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-            @Override
-            public void run(IBinder binder) {
-                IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder);
-                try {
-                    result[0] = provider.getFromLocation(
-                            latitude, longitude, maxResults, params, addrs);
-                } catch (RemoteException e) {
-                    Log.w(TAG, e);
-                }
+        final String[] result = new String[]{"Service not Available"};
+        mServiceWatcher.runOnBinder(binder -> {
+            IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder);
+            try {
+                result[0] = provider.getFromLocation(
+                        latitude, longitude, maxResults, params, addrs);
+            } catch (RemoteException e) {
+                Log.w(TAG, e);
             }
         });
         return result[0];
@@ -90,18 +85,15 @@
             double lowerLeftLatitude, double lowerLeftLongitude,
             double upperRightLatitude, double upperRightLongitude, int maxResults,
             GeocoderParams params, List<Address> addrs) {
-        final String[] result = new String[] {"Service not Available"};
-        mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-            @Override
-            public void run(IBinder binder) {
-                IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder);
-                try {
-                    result[0] = provider.getFromLocationName(locationName, lowerLeftLatitude,
-                            lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
-                            maxResults, params, addrs);
-                } catch (RemoteException e) {
-                    Log.w(TAG, e);
-                }
+        final String[] result = new String[]{"Service not Available"};
+        mServiceWatcher.runOnBinder(binder -> {
+            IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder);
+            try {
+                result[0] = provider.getFromLocationName(locationName, lowerLeftLatitude,
+                        lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
+                        maxResults, params, addrs);
+            } catch (RemoteException e) {
+                Log.w(TAG, e);
             }
         });
         return result[0];
diff --git a/services/core/java/com/android/server/location/GeofenceProxy.java b/services/core/java/com/android/server/location/GeofenceProxy.java
index eb47b2f..ca4f7fe 100644
--- a/services/core/java/com/android/server/location/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/GeofenceProxy.java
@@ -15,61 +15,60 @@
  */
 package com.android.server.location;
 
+import android.annotation.Nullable;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.hardware.location.GeofenceHardwareService;
 import android.hardware.location.IGeofenceHardware;
+import android.location.IFusedGeofenceHardware;
 import android.location.IGeofenceProvider;
 import android.location.IGpsGeofenceHardware;
-import android.location.IFusedGeofenceHardware;
-import android.content.Context;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
+
+import com.android.internal.os.BackgroundThread;
 import com.android.server.ServiceWatcher;
 
 /**
  * @hide
  */
 public final class GeofenceProxy {
+
     private static final String TAG = "GeofenceProxy";
-    private static final String SERVICE_ACTION =
-            "com.android.location.service.GeofenceProvider";
-    private final ServiceWatcher mServiceWatcher;
+    private static final String SERVICE_ACTION = "com.android.location.service.GeofenceProvider";
+
     private final Context mContext;
+    private final ServiceWatcher mServiceWatcher;
+
+    @Nullable
     private final IGpsGeofenceHardware mGpsGeofenceHardware;
+    @Nullable
     private final IFusedGeofenceHardware mFusedGeofenceHardware;
 
-    private final Object mLock = new Object();
+    private volatile IGeofenceHardware mGeofenceHardware;
 
-    // Access to mGeofenceHardware needs to be synchronized by mLock.
-    private IGeofenceHardware mGeofenceHardware;
-
-    private static final int GEOFENCE_PROVIDER_CONNECTED = 1;
-    private static final int GEOFENCE_HARDWARE_CONNECTED = 2;
-    private static final int GEOFENCE_HARDWARE_DISCONNECTED = 3;
-    private static final int GEOFENCE_GPS_HARDWARE_CONNECTED = 4;
-    private static final int GEOFENCE_GPS_HARDWARE_DISCONNECTED = 5;
-
-    private Runnable mRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mHandler.sendEmptyMessage(GEOFENCE_PROVIDER_CONNECTED);
+    private final ServiceWatcher.BinderRunner mUpdateGeofenceHardware = (binder) -> {
+        IGeofenceProvider provider = IGeofenceProvider.Stub.asInterface(binder);
+        try {
+            provider.setGeofenceHardware(mGeofenceHardware);
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
         }
     };
 
     public static GeofenceProxy createAndBind(Context context,
             int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence,
-            IFusedGeofenceHardware fusedGeofenceHardware) {
+            int initialPackageNamesResId, @Nullable IGpsGeofenceHardware gpsGeofence,
+            @Nullable IFusedGeofenceHardware fusedGeofenceHardware) {
         GeofenceProxy proxy = new GeofenceProxy(context, overlaySwitchResId,
-            defaultServicePackageNameResId, initialPackageNamesResId, handler, gpsGeofence,
-            fusedGeofenceHardware);
-        if (proxy.bindGeofenceProvider()) {
+                defaultServicePackageNameResId, initialPackageNamesResId, gpsGeofence,
+                fusedGeofenceHardware);
+
+        if (proxy.bind()) {
             return proxy;
         } else {
             return null;
@@ -78,111 +77,56 @@
 
     private GeofenceProxy(Context context,
             int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence,
-            IFusedGeofenceHardware fusedGeofenceHardware) {
+            int initialPackageNamesResId, @Nullable IGpsGeofenceHardware gpsGeofence,
+            @Nullable IFusedGeofenceHardware fusedGeofenceHardware) {
         mContext = context;
         mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
-            defaultServicePackageNameResId, initialPackageNamesResId, mRunnable, handler);
+                defaultServicePackageNameResId, initialPackageNamesResId,
+                BackgroundThread.getHandler()) {
+            @Override
+            protected void onBind() {
+                runOnBinder(mUpdateGeofenceHardware);
+            }
+        };
+
         mGpsGeofenceHardware = gpsGeofence;
         mFusedGeofenceHardware = fusedGeofenceHardware;
-        bindHardwareGeofence();
+
+        mGeofenceHardware = null;
     }
 
-    private boolean bindGeofenceProvider() {
-        return mServiceWatcher.start();
+    private boolean bind() {
+        if (mServiceWatcher.start()) {
+            mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.class),
+                    new GeofenceProxyServiceConnection(), Context.BIND_AUTO_CREATE,
+                    UserHandle.SYSTEM);
+            return true;
+        }
+
+        return false;
     }
 
-    private void bindHardwareGeofence() {
-        mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.class),
-                mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
-    }
+    private class GeofenceProxyServiceConnection implements ServiceConnection {
 
-    private ServiceConnection mServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            synchronized (mLock) {
-                mGeofenceHardware = IGeofenceHardware.Stub.asInterface(service);
-                mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_CONNECTED);
+            IGeofenceHardware geofenceHardware = IGeofenceHardware.Stub.asInterface(service);
+
+            try {
+                geofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
+                geofenceHardware.setFusedGeofenceHardware(mFusedGeofenceHardware);
+
+                mGeofenceHardware = geofenceHardware;
+                mServiceWatcher.runOnBinder(mUpdateGeofenceHardware);
+            } catch (Exception e) {
+                Log.w(TAG, e);
             }
         }
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
-            synchronized (mLock) {
-                mGeofenceHardware = null;
-                mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_DISCONNECTED);
-            }
-        }
-    };
-
-    private void setGeofenceHardwareInProviderLocked() {
-        mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-            @Override
-            public void run(IBinder binder) {
-                final IGeofenceProvider provider = IGeofenceProvider.Stub.asInterface(binder);
-                try {
-                    provider.setGeofenceHardware(mGeofenceHardware);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Remote Exception: setGeofenceHardwareInProviderLocked: " + e);
-                }
-            }
-        });
-    }
-
-    private void setGpsGeofenceLocked() {
-        try {
-            if (mGpsGeofenceHardware != null) {
-                mGeofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error while connecting to GeofenceHardwareService");
+            mGeofenceHardware = null;
+            mServiceWatcher.runOnBinder(mUpdateGeofenceHardware);
         }
     }
-
-    private void setFusedGeofenceLocked() {
-        try {
-            mGeofenceHardware.setFusedGeofenceHardware(mFusedGeofenceHardware);
-        } catch(RemoteException e) {
-            Log.e(TAG, "Error while connecting to GeofenceHardwareService");
-        }
-    }
-
-    // This needs to be reworked, when more services get added,
-    // Might need a state machine or add a framework utility class,
-    private Handler mHandler = new Handler() {
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case GEOFENCE_PROVIDER_CONNECTED:
-                    synchronized (mLock) {
-                        if (mGeofenceHardware != null) {
-                            setGeofenceHardwareInProviderLocked();
-                        }
-                        // else: the geofence provider will be notified when the connection to
-                        // GeofenceHardwareService is established.
-                    }
-                    break;
-                case GEOFENCE_HARDWARE_CONNECTED:
-                    synchronized (mLock) {
-                        // Theoretically this won't happen because once the GeofenceHardwareService
-                        // is connected to, we won't lose connection to it because it's a system
-                        // service. But this check does make the code more robust.
-                        if (mGeofenceHardware != null) {
-                            setGpsGeofenceLocked();
-                            setFusedGeofenceLocked();
-                            setGeofenceHardwareInProviderLocked();
-                        }
-                    }
-                    break;
-                case GEOFENCE_HARDWARE_DISCONNECTED:
-                    synchronized (mLock) {
-                        if (mGeofenceHardware == null) {
-                            setGeofenceHardwareInProviderLocked();
-                        }
-                    }
-                    break;
-            }
-        }
-    };
 }
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 16eae62..bb86b48 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -16,26 +16,28 @@
 
 package com.android.server.location;
 
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-
+import android.annotation.Nullable;
 import android.content.Context;
 import android.location.LocationProvider;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.WorkSource;
 import android.util.Log;
 
-import com.android.internal.location.ProviderProperties;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.TransferPipe;
 import com.android.server.LocationManagerService;
 import com.android.server.ServiceWatcher;
 
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+
 /**
  * Proxy for ILocationProvider implementations.
  */
@@ -43,25 +45,36 @@
     private static final String TAG = "LocationProviderProxy";
     private static final boolean D = LocationManagerService.D;
 
-    private final Context mContext;
-    private final String mName;
     private final ServiceWatcher mServiceWatcher;
 
-    private Object mLock = new Object();
+    private final String mName;
 
-    // cached values set by the location manager, synchronized on mLock
-    private ProviderProperties mProperties;
-    private boolean mEnabled = false;
-    private ProviderRequest mRequest = null;
-    private WorkSource mWorksource = new WorkSource();
+    // used to ensure that updates to mRequest and mWorkSource are atomic
+    private final Object mRequestLock = new Object();
 
+
+    private volatile boolean mEnabled = false;
+    @Nullable
+    private volatile ProviderProperties mProperties;
+
+    @GuardedBy("mRequestLock")
+    @Nullable
+    private ProviderRequest mRequest;
+    @GuardedBy("mRequestLock")
+    private WorkSource mWorkSource;
+
+    /**
+     * Creates a new LocationProviderProxy and immediately begins binding to the best applicable
+     * service.
+     */
+    @Nullable
     public static LocationProviderProxy createAndBind(
             Context context, String name, String action,
             int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId, Handler handler) {
-        LocationProviderProxy proxy = new LocationProviderProxy(context, name, action,
-                overlaySwitchResId, defaultServicePackageNameResId, initialPackageNamesResId,
-                handler);
+            int initialPackageNamesResId) {
+        LocationProviderProxy proxy = new LocationProviderProxy(context, name,
+                action, overlaySwitchResId, defaultServicePackageNameResId,
+                initialPackageNamesResId);
         if (proxy.bind()) {
             return proxy;
         } else {
@@ -69,78 +82,66 @@
         }
     }
 
-    private LocationProviderProxy(Context context, String name, String action,
-            int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId, Handler handler) {
-        mContext = context;
-        mName = name;
-        mServiceWatcher = new ServiceWatcher(mContext, TAG + "-" + name, action, overlaySwitchResId,
+    private LocationProviderProxy(Context context, String name,
+            String action, int overlaySwitchResId, int defaultServicePackageNameResId,
+            int initialPackageNamesResId) {
+
+        mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
                 defaultServicePackageNameResId, initialPackageNamesResId,
-                mNewServiceWork, handler);
+                BackgroundThread.getHandler()) {
+            @Override
+            protected void onBind() {
+                runOnBinder(LocationProviderProxy.this::initializeService);
+            }
+        };
+        mName = name;
+
+        mProperties = null;
+        mRequest = null;
+        mWorkSource = new WorkSource();
     }
 
-    private boolean bind () {
+    private boolean bind() {
         return mServiceWatcher.start();
     }
 
-    public String getConnectedPackageName() {
-        return mServiceWatcher.getBestPackageName();
+    private void initializeService(IBinder binder) {
+        ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+        if (D) Log.d(TAG, "applying state to connected service");
+
+        ProviderProperties[] properties = new ProviderProperties[1];
+        ProviderRequest request;
+        WorkSource source;
+        synchronized (mRequestLock) {
+            request = mRequest;
+            source = mWorkSource;
+        }
+
+        try {
+            // load properties from provider
+            properties[0] = service.getProperties();
+            if (properties[0] == null) {
+                Log.e(TAG, mServiceWatcher.getCurrentPackageName()
+                        + " has invalid location provider properties");
+            }
+
+            // apply current state to new service
+            if (mEnabled) {
+                service.enable();
+                if (request != null) {
+                    service.setRequest(request, source);
+                }
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
+        }
+
+        mProperties = properties[0];
     }
 
-    /**
-     * Work to apply current state to a newly connected provider.
-     * Remember we can switch the service that implements a providers
-     * at run-time, so need to apply current state.
-     */
-    private Runnable mNewServiceWork = new Runnable() {
-        @Override
-        public void run() {
-            if (D) Log.d(TAG, "applying state to connected service");
-
-            boolean enabled;
-            final ProviderProperties[] properties = new ProviderProperties[1];
-            ProviderRequest request;
-            WorkSource source;
-            synchronized (mLock) {
-                enabled = mEnabled;
-                request = mRequest;
-                source = mWorksource;
-            }
-
-
-            mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-                @Override
-                public void run(IBinder binder) {
-                    ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-                    try {
-                        // load properties from provider
-                        properties[0] = service.getProperties();
-                        if (properties[0] == null) {
-                            Log.e(TAG, mServiceWatcher.getBestPackageName() +
-                                    " has invalid location provider properties");
-                        }
-
-                        // apply current state to new service
-                        if (enabled) {
-                            service.enable();
-                            if (request != null) {
-                                service.setRequest(request, source);
-                            }
-                        }
-                    } catch (RemoteException e) {
-                        Log.w(TAG, e);
-                    } catch (Exception e) {
-                        // never let remote service crash system server
-                        Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
-                    }
-                }
-            });
-
-            synchronized (mLock) {
-                mProperties = properties[0];
-            }
-        }
-    };
+    public String getConnectedPackageName() {
+        return mServiceWatcher.getCurrentPackageName();
+    }
 
     @Override
     public String getName() {
@@ -149,78 +150,52 @@
 
     @Override
     public ProviderProperties getProperties() {
-        synchronized (mLock) {
-            return mProperties;
-        }
+        return mProperties;
     }
 
     @Override
     public void enable() {
-        synchronized (mLock) {
-            mEnabled = true;
-        }
-        mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-            @Override
-            public void run(IBinder binder) {
-                ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-                try {
-                    service.enable();
-                } catch (RemoteException e) {
-                    Log.w(TAG, e);
-                } catch (Exception e) {
-                    // never let remote service crash system server
-                    Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
-                }
+        mEnabled = true;
+        mServiceWatcher.runOnBinder(binder -> {
+            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+            try {
+                service.enable();
+            } catch (RemoteException e) {
+                Log.w(TAG, e);
             }
         });
     }
 
     @Override
     public void disable() {
-        synchronized (mLock) {
-            mEnabled = false;
-        }
-        mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-            @Override
-            public void run(IBinder binder) {
-                ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-                try {
-                    service.disable();
-                } catch (RemoteException e) {
-                    Log.w(TAG, e);
-                } catch (Exception e) {
-                    // never let remote service crash system server
-                    Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
-                }
+        mEnabled = false;
+        mServiceWatcher.runOnBinder(binder -> {
+            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+            try {
+                service.disable();
+            } catch (RemoteException e) {
+                Log.w(TAG, e);
             }
         });
     }
 
     @Override
     public boolean isEnabled() {
-        synchronized (mLock) {
-            return mEnabled;
-        }
+        return mEnabled;
     }
 
     @Override
     public void setRequest(ProviderRequest request, WorkSource source) {
-        synchronized (mLock) {
+        synchronized (mRequestLock) {
             mRequest = request;
-            mWorksource = source;
+            mWorkSource = source;
         }
-        mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-            @Override
-            public void run(IBinder binder) {
-                ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-                try {
-                    service.setRequest(request, source);
-                } catch (RemoteException e) {
-                    Log.w(TAG, e);
-                } catch (Exception e) {
-                    // never let remote service crash system server
-                    Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
-                }
+        mServiceWatcher.runOnBinder(binder -> {
+            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+            try {
+                service.setRequest(request, source);
+            } catch (RemoteException e) {
+                Log.w(TAG, e);
             }
         });
     }
@@ -229,39 +204,28 @@
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.append("REMOTE SERVICE");
         pw.append(" name=").append(mName);
-        pw.append(" pkg=").append(mServiceWatcher.getBestPackageName());
-        pw.append(" version=").append("" + mServiceWatcher.getBestVersion());
+        pw.append(" pkg=").append(mServiceWatcher.getCurrentPackageName());
+        pw.append(" version=").append(Integer.toString(mServiceWatcher.getCurrentPackageVersion()));
         pw.append('\n');
-        if (!mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-            @Override
-            public void run(IBinder binder) {
-                ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-                try {
-                    TransferPipe.dumpAsync(service.asBinder(), fd, args);
-                } catch (IOException | RemoteException e) {
-                    pw.println("Failed to dump location provider: " + e);
-                }
+        mServiceWatcher.runOnBinder(binder -> {
+            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+            try {
+                TransferPipe.dumpAsync(service.asBinder(), fd, args);
+            } catch (IOException | RemoteException e) {
+                pw.println("Failed to dump location provider: " + e);
             }
-        })) {
-            pw.println("service down (null)");
-        }
+        });
     }
 
     @Override
     public int getStatus(Bundle extras) {
-        final int[] result = new int[] {LocationProvider.TEMPORARILY_UNAVAILABLE};
-        mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-            @Override
-            public void run(IBinder binder) {
-                ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-                try {
-                    result[0] = service.getStatus(extras);
-                } catch (RemoteException e) {
-                    Log.w(TAG, e);
-                } catch (Exception e) {
-                    // never let remote service crash system server
-                    Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
-                }
+        int[] result = new int[]{LocationProvider.TEMPORARILY_UNAVAILABLE};
+        mServiceWatcher.runOnBinder(binder -> {
+            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+            try {
+                result[0] = service.getStatus(extras);
+            } catch (RemoteException e) {
+                Log.w(TAG, e);
             }
         });
         return result[0];
@@ -269,19 +233,13 @@
 
     @Override
     public long getStatusUpdateTime() {
-        final long[] result = new long[] {0L};
-        mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-            @Override
-            public void run(IBinder binder) {
-                ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-                try {
-                    result[0] = service.getStatusUpdateTime();
-                } catch (RemoteException e) {
-                    Log.w(TAG, e);
-                } catch (Exception e) {
-                    // never let remote service crash system server
-                    Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
-                }
+        long[] result = new long[]{0L};
+        mServiceWatcher.runOnBinder(binder -> {
+            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+            try {
+                result[0] = service.getStatusUpdateTime();
+            } catch (RemoteException e) {
+                Log.w(TAG, e);
             }
         });
         return result[0];
@@ -289,21 +247,15 @@
 
     @Override
     public boolean sendExtraCommand(String command, Bundle extras) {
-        final boolean[] result = new boolean[] {false};
-        mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
-            @Override
-            public void run(IBinder binder) {
-                ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-                try {
-                    result[0] = service.sendExtraCommand(command, extras);
-                } catch (RemoteException e) {
-                    Log.w(TAG, e);
-                } catch (Exception e) {
-                    // never let remote service crash system server
-                    Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
-                }
+        boolean[] result = new boolean[]{false};
+        mServiceWatcher.runOnBinder(binder -> {
+            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+            try {
+                result[0] = service.sendExtraCommand(command, extras);
+            } catch (RemoteException e) {
+                Log.w(TAG, e);
             }
         });
         return result[0];
     }
- }
+}
diff --git a/services/core/java/com/android/server/location/NanoAppStateManager.java b/services/core/java/com/android/server/location/NanoAppStateManager.java
index 9869626..e26ccc3 100644
--- a/services/core/java/com/android/server/location/NanoAppStateManager.java
+++ b/services/core/java/com/android/server/location/NanoAppStateManager.java
@@ -21,11 +21,11 @@
 import android.hardware.location.NanoAppInstanceInfo;
 import android.util.Log;
 
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.function.Consumer;
 
 /**
  * Manages the state of loaded nanoapps at the Context Hubs.
@@ -70,11 +70,15 @@
     }
 
     /**
-     * @return a collection of NanoAppInstanceInfo objects in the cache
+     * Invokes a Consumer operation for each NanoAppInstanceInfo entry in the cache
+     *
+     * @param consumer the Consumer operation to perform
      */
     /* package */
-    synchronized Collection<NanoAppInstanceInfo> getNanoAppInstanceInfoCollection() {
-        return mNanoAppHash.values();
+    synchronized void foreachNanoAppInstanceInfo(Consumer<NanoAppInstanceInfo> consumer) {
+        for (NanoAppInstanceInfo info : mNanoAppHash.values()) {
+            consumer.accept(info);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 3f03169..dc3bfbc 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -105,6 +105,7 @@
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
+import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.PowerManager;
@@ -323,6 +324,12 @@
                 Clock.systemUTC());
     }
 
+    private static final class NetworkStatsHandler extends Handler {
+        NetworkStatsHandler(Looper looper, Handler.Callback callback) {
+            super(looper, callback);
+        }
+    }
+
     public static NetworkStatsService create(Context context,
                 INetworkManagementService networkManager) {
         AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
@@ -339,7 +346,7 @@
         HandlerThread handlerThread = new HandlerThread(TAG);
         Handler.Callback callback = new HandlerCallback(service);
         handlerThread.start();
-        Handler handler = new Handler(handlerThread.getLooper(), callback);
+        Handler handler = new NetworkStatsHandler(handlerThread.getLooper(), callback);
         service.setHandler(handler, callback);
         return service;
     }
@@ -1620,7 +1627,8 @@
         // fold tethering stats and operations into uid snapshot
         final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
         tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
-        NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot);
+        NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot,
+                mUseBpfTrafficStats);
         uidSnapshot.combineAllValues(tetherSnapshot);
 
         final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
@@ -1630,7 +1638,8 @@
         final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
         if (vtStats != null) {
             vtStats.filter(UID_ALL, ifaces, TAG_ALL);
-            NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats);
+            NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats,
+                    mUseBpfTrafficStats);
             uidSnapshot.combineAllValues(vtStats);
         }
 
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
index 29b1339..fa90e90 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -142,8 +142,8 @@
 
     private final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() {
         @Override
-        public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
-                long timestamp, int uid) {
+        public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+                String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
             if (!mIsLoggingEnabled) {
                 return;
             }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 93b83ae..924b075 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3399,6 +3399,16 @@
             }
         }
 
+        @Override
+        public Policy getConsolidatedNotificationPolicy() {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return mZenModeHelper.getConsolidatedNotificationPolicy();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
         /**
          * Sets the notification policy.  Apps that target API levels below
          * {@link android.os.Build.VERSION_CODES#P} cannot change user-designated values to
@@ -4660,7 +4670,6 @@
                 }
 
                 mRankingHelper.extractSignals(r);
-
                 // tell the assistant service about the notification
                 if (mAssistants.isEnabled()) {
                     mAssistants.onNotificationEnqueued(r);
@@ -5570,7 +5579,7 @@
         record.setIntercepted(mZenModeHelper.shouldIntercept(record));
         if (record.isIntercepted()) {
             record.setSuppressedVisualEffects(
-                    mZenModeHelper.getNotificationPolicy().suppressedVisualEffects);
+                    mZenModeHelper.getConsolidatedNotificationPolicy().suppressedVisualEffects);
         } else {
             record.setSuppressedVisualEffects(0);
         }
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 3a0ab77..8fce5e3 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -515,8 +515,20 @@
         if (oldGroup != null) {
             group.setChannels(oldGroup.getChannels());
 
+            // apps can't update the blocked status or app overlay permission
             if (fromTargetApp) {
                 group.setBlocked(oldGroup.isBlocked());
+                group.setAllowAppOverlay(oldGroup.canOverlayApps());
+                group.unlockFields(group.getUserLockedFields());
+                group.lockFields(oldGroup.getUserLockedFields());
+            } else {
+                // but the system can
+                if (group.isBlocked() != oldGroup.isBlocked()) {
+                    group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
+                }
+                if (group.canOverlayApps() != oldGroup.canOverlayApps()) {
+                    group.lockFields(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY);
+                }
             }
         }
         r.groups.put(group.getId(), group);
@@ -1071,6 +1083,9 @@
         if (original.canShowBadge() != update.canShowBadge()) {
             update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
         }
+        if (original.isAppOverlayAllowed() != update.isAppOverlayAllowed()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY);
+        }
     }
 
     public void dump(PrintWriter pw, String prefix,
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index b016faf..c6af756 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -63,6 +63,7 @@
     private static final int TYPE_SUPPRESSOR_CHANGED = 14;
     private static final int TYPE_LISTENER_HINTS_CHANGED = 15;
     private static final int TYPE_SET_NOTIFICATION_POLICY = 16;
+    private static final int TYPE_SET_CONSOLIDATED_ZEN_POLICY = 17;
 
     private static int sNext;
     private static int sSize;
@@ -103,6 +104,14 @@
         append(TYPE_SET_ZEN_MODE, zenModeToString(zenMode) + "," + reason);
     }
 
+    /**
+     * trace setting the consolidated zen policy
+     */
+    public static void traceSetConsolidatedZenPolicy(NotificationManager.Policy policy,
+            String reason) {
+        append(TYPE_SET_CONSOLIDATED_ZEN_POLICY, policy.toString() + "," + reason);
+    }
+
     public static void traceUpdateZenMode(int fromMode, int toMode) {
         append(TYPE_UPDATE_ZEN_MODE, zenModeToString(fromMode) + " -> " + zenModeToString(toMode));
     }
diff --git a/services/core/java/com/android/server/notification/ZenModeExtractor.java b/services/core/java/com/android/server/notification/ZenModeExtractor.java
index a0aa1c3..f3da079 100644
--- a/services/core/java/com/android/server/notification/ZenModeExtractor.java
+++ b/services/core/java/com/android/server/notification/ZenModeExtractor.java
@@ -16,9 +16,6 @@
 
 package com.android.server.notification;
 
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
-
 import android.content.Context;
 import android.util.Log;
 import android.util.Slog;
@@ -50,7 +47,7 @@
         record.setIntercepted(mZenModeHelper.shouldIntercept(record));
         if (record.isIntercepted()) {
             record.setSuppressedVisualEffects(
-                    mZenModeHelper.getNotificationPolicy().suppressedVisualEffects);
+                    mZenModeHelper.getConsolidatedNotificationPolicy().suppressedVisualEffects);
         } else {
             record.setSuppressedVisualEffects(0);
         }
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index 28cee7a..6045f6c 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -23,11 +23,9 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.media.AudioAttributes;
-import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings.Global;
-import android.provider.Settings.Secure;
 import android.service.notification.ZenModeConfig;
 import android.telecom.TelecomManager;
 import android.util.ArrayMap;
@@ -38,7 +36,6 @@
 
 import java.io.PrintWriter;
 import java.util.Date;
-import java.util.Objects;
 
 public class ZenModeFiltering {
     private static final String TAG = ZenModeHelper.TAG;
@@ -88,20 +85,21 @@
      * @param timeoutAffinity affinity to return when the timeout specified via
      *                        <code>contactsTimeoutMs</code> is hit
      */
-    public static boolean matchesCallFilter(Context context, int zen, ZenModeConfig config,
-            UserHandle userHandle, Bundle extras, ValidateNotificationPeople validator,
-            int contactsTimeoutMs, float timeoutAffinity) {
+    public static boolean matchesCallFilter(Context context, int zen, NotificationManager.Policy
+            consolidatedPolicy, UserHandle userHandle, Bundle extras,
+            ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) {
         if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
         if (zen == Global.ZEN_MODE_ALARMS) return false; // not an alarm
         if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
-            if (config.allowRepeatCallers && REPEAT_CALLERS.isRepeat(context, extras)) {
+            if (consolidatedPolicy.allowRepeatCallers()
+                    && REPEAT_CALLERS.isRepeat(context, extras)) {
                 return true;
             }
-            if (!config.allowCalls) return false; // no other calls get through
+            if (!consolidatedPolicy.allowCalls()) return false; // no other calls get through
             if (validator != null) {
                 final float contactAffinity = validator.getContactAffinity(userHandle, extras,
                         contactsTimeoutMs, timeoutAffinity);
-                return audienceMatches(config.allowCallsFrom, contactAffinity);
+                return audienceMatches(consolidatedPolicy.allowCallsFrom(), contactAffinity);
             }
         }
         return true;
@@ -116,13 +114,17 @@
         REPEAT_CALLERS.recordCall(mContext, extras(record));
     }
 
-    public boolean shouldIntercept(int zen, ZenModeConfig config, NotificationRecord record) {
+    /**
+     * Whether to intercept the notification based on the policy
+     */
+    public boolean shouldIntercept(int zen, NotificationManager.Policy policy,
+            NotificationRecord record) {
         // Zen mode is ignored for critical notifications.
         if (zen == ZEN_MODE_OFF || isCritical(record)) {
             return false;
         }
         // Make an exception to policy for the notification saying that policy has changed
-        if (NotificationManager.Policy.areAllVisualEffectsSuppressed(config.suppressedVisualEffects)
+        if (NotificationManager.Policy.areAllVisualEffectsSuppressed(policy.suppressedVisualEffects)
                 && "android".equals(record.sbn.getPackageName())
                 && SystemMessageProto.SystemMessage.NOTE_ZEN_UPGRADE == record.sbn.getId()) {
             ZenLog.traceNotIntercepted(record, "systemDndChangedNotification");
@@ -148,54 +150,54 @@
                 }
 
                 if (isAlarm(record)) {
-                    if (!config.allowAlarms) {
+                    if (!policy.allowAlarms()) {
                         ZenLog.traceIntercepted(record, "!allowAlarms");
                         return true;
                     }
                     return false;
                 }
                 if (isCall(record)) {
-                    if (config.allowRepeatCallers
+                    if (policy.allowRepeatCallers()
                             && REPEAT_CALLERS.isRepeat(mContext, extras(record))) {
                         ZenLog.traceNotIntercepted(record, "repeatCaller");
                         return false;
                     }
-                    if (!config.allowCalls) {
+                    if (!policy.allowCalls()) {
                         ZenLog.traceIntercepted(record, "!allowCalls");
                         return true;
                     }
-                    return shouldInterceptAudience(config.allowCallsFrom, record);
+                    return shouldInterceptAudience(policy.allowCallsFrom(), record);
                 }
                 if (isMessage(record)) {
-                    if (!config.allowMessages) {
+                    if (!policy.allowMessages()) {
                         ZenLog.traceIntercepted(record, "!allowMessages");
                         return true;
                     }
-                    return shouldInterceptAudience(config.allowMessagesFrom, record);
+                    return shouldInterceptAudience(policy.allowMessagesFrom(), record);
                 }
                 if (isEvent(record)) {
-                    if (!config.allowEvents) {
+                    if (!policy.allowEvents()) {
                         ZenLog.traceIntercepted(record, "!allowEvents");
                         return true;
                     }
                     return false;
                 }
                 if (isReminder(record)) {
-                    if (!config.allowReminders) {
+                    if (!policy.allowReminders()) {
                         ZenLog.traceIntercepted(record, "!allowReminders");
                         return true;
                     }
                     return false;
                 }
                 if (isMedia(record)) {
-                    if (!config.allowMedia) {
+                    if (!policy.allowMedia()) {
                         ZenLog.traceIntercepted(record, "!allowMedia");
                         return true;
                     }
                     return false;
                 }
                 if (isSystem(record)) {
-                    if (!config.allowSystem) {
+                    if (!policy.allowSystem()) {
                         ZenLog.traceIntercepted(record, "!allowSystem");
                         return true;
                     }
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 44b80c1..f279af0 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -54,6 +54,7 @@
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ZenRule;
 import android.service.notification.ZenModeProto;
+import android.service.notification.ZenPolicy;
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -95,7 +96,7 @@
     private final SettingsObserver mSettingsObserver;
     @VisibleForTesting protected final AppOpsManager mAppOps;
     @VisibleForTesting protected final NotificationManager mNotificationManager;
-    protected ZenModeConfig mDefaultConfig;
+    @VisibleForTesting protected ZenModeConfig mDefaultConfig;
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final ZenModeFiltering mFiltering;
     protected final RingerModeDelegate mRingerModeDelegate = new
@@ -106,6 +107,7 @@
     private final ConditionProviders.Config mServiceConfig;
 
     @VisibleForTesting protected int mZenMode;
+    @VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
     private int mUser = UserHandle.USER_SYSTEM;
     @VisibleForTesting protected ZenModeConfig mConfig;
     @VisibleForTesting protected AudioManagerInternal mAudioManager;
@@ -150,8 +152,8 @@
     public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
             ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) {
         synchronized (mConfig) {
-            return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConfig, userHandle,
-                    extras, validator, contactsTimeoutMs, timeoutAffinity);
+            return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConsolidatedPolicy,
+                    userHandle, extras, validator, contactsTimeoutMs, timeoutAffinity);
         }
     }
 
@@ -165,7 +167,7 @@
 
     public boolean shouldIntercept(NotificationRecord record) {
         synchronized (mConfig) {
-            return mFiltering.shouldIntercept(mZenMode, mConfig, record);
+            return mFiltering.shouldIntercept(mZenMode, mConsolidatedPolicy, record);
         }
     }
 
@@ -307,9 +309,6 @@
             newConfig = mConfig.copy();
             ZenRule rule = new ZenRule();
             populateZenRule(automaticZenRule, rule, true);
-            if (newConfig.automaticRules.put(rule.id, rule) != null) {
-                rule.modified = true;
-            }
             if (setConfigLocked(newConfig, reason, rule.component, true)) {
                 return rule.id;
             } else {
@@ -339,9 +338,6 @@
                 }
             }
             populateZenRule(automaticZenRule, rule, false);
-            if (newConfig.automaticRules.put(ruleId, rule) != null) {
-                rule.modified = true;
-            }
             return setConfigLocked(newConfig, reason, rule.component, true);
         }
     }
@@ -429,13 +425,16 @@
         updateDefaultAutomaticRuleNames();
         for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
             ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
-            // if default rule wasn't modified, use localized name instead of previous
-            if (currRule != null && !currRule.modified && !defaultRule.name.equals(currRule.name)) {
-                if (canManageAutomaticZenRule(defaultRule)) {
+            // if default rule wasn't user-modified nor enabled, use localized name
+            // instead of previous system name
+            if (currRule != null && !currRule.modified && !currRule.enabled
+                    && !defaultRule.name.equals(currRule.name)) {
+                if (canManageAutomaticZenRule(currRule)) {
                     if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name "
                             + "from " + currRule.name + " to " + defaultRule.name);
                     // update default rule (if locale changed, name of rule will change)
-                    updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(defaultRule),
+                    currRule.name = defaultRule.name;
+                    updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
                             "locale changed");
                 }
             }
@@ -479,6 +478,10 @@
         rule.condition = null;
         rule.conditionId = automaticZenRule.getConditionId();
         rule.enabled = automaticZenRule.isEnabled();
+        rule.modified = automaticZenRule.isModified();
+        if (automaticZenRule.getZenPolicy() != null) {
+            rule.zenPolicy = automaticZenRule.getZenPolicy();
+        }
         rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
                 automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
     }
@@ -549,6 +552,7 @@
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mZenMode=");
         pw.println(Global.zenModeToString(mZenMode));
+        pw.print("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
         final int N = mConfigs.size();
         for (int i = 0; i < N; i++) {
             dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
@@ -713,6 +717,16 @@
         }
     }
 
+    /**
+     * @return a copy of the zen mode consolidated policy
+     */
+    public Policy getConsolidatedNotificationPolicy() {
+        if (mConsolidatedPolicy == null) {
+            return null;
+        }
+        return mConsolidatedPolicy.copy();
+    }
+
     public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
             String reason) {
         return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/);
@@ -747,6 +761,7 @@
                     getNotificationPolicy(config));
             if (!config.equals(mConfig)) {
                 dispatchOnConfigChanged();
+                updateConsolidatedPolicy(reason);
             }
             if (policyChanged) {
                 dispatchOnPolicyChanged();
@@ -794,13 +809,18 @@
     @VisibleForTesting
     protected void evaluateZenMode(String reason, boolean setRingerMode) {
         if (DEBUG) Log.d(TAG, "evaluateZenMode");
+        if (mConfig == null) return;
+        final int policyHashBefore = mConsolidatedPolicy == null ? 0
+                : mConsolidatedPolicy.hashCode();
         final int zenBefore = mZenMode;
         final int zen = computeZenMode();
         ZenLog.traceSetZenMode(zen, reason);
         mZenMode = zen;
         setZenModeSetting(mZenMode);
+        updateConsolidatedPolicy(reason);
         updateRingerModeAffectedStreams();
-        if (setRingerMode && zen != zenBefore) {
+        if (setRingerMode && (zen != zenBefore || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                && policyHashBefore != mConsolidatedPolicy.hashCode()))) {
             applyZenToRingerMode();
         }
         applyRestrictions();
@@ -815,9 +835,7 @@
         }
     }
 
-
     private int computeZenMode() {
-        // TODO: use mConfig.zenPolicy
         if (mConfig == null) return Global.ZEN_MODE_OFF;
         synchronized (mConfig) {
             if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
@@ -839,6 +857,24 @@
         }
     }
 
+    private void updateConsolidatedPolicy(String reason) {
+        if (mConfig == null) return;
+        synchronized (mConfig) {
+            ZenPolicy policy = new ZenPolicy();
+            for (ZenRule automaticRule : mConfig.automaticRules.values()) {
+                if (automaticRule.isAutomaticActive()) {
+                    policy.apply(automaticRule.zenPolicy);
+                }
+            }
+            Policy newPolicy = mConfig.toNotificationPolicy(policy);
+            if (!Objects.equals(mConsolidatedPolicy, newPolicy)) {
+                mConsolidatedPolicy = newPolicy;
+                dispatchOnConsolidatedPolicyChanged();
+                ZenLog.traceSetConsolidatedZenPolicy(mConsolidatedPolicy, reason);
+            }
+        }
+    }
+
     private void updateDefaultAutomaticRuleNames() {
         for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
             if (ZenModeConfig.EVENTS_DEFAULT_RULE_ID.equals(rule.id)) {
@@ -856,23 +892,28 @@
         final boolean zenPriorityOnly = mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         final boolean zenSilence = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
         final boolean zenAlarmsOnly = mZenMode == Global.ZEN_MODE_ALARMS;
+        final boolean allowCalls = mConsolidatedPolicy.allowCalls();
+        final boolean allowRepeatCallers = mConsolidatedPolicy.allowRepeatCallers();
+        final boolean allowSystem = mConsolidatedPolicy.allowSystem();
+        final boolean allowMedia = mConsolidatedPolicy.allowMedia();
+        final boolean allowAlarms = mConsolidatedPolicy.allowAlarms();
 
         // notification restrictions
         final boolean muteNotifications =
                 (mSuppressedEffects & SUPPRESSED_EFFECT_NOTIFICATIONS) != 0;
         // call restrictions
         final boolean muteCalls = zenAlarmsOnly
-                || (zenPriorityOnly && !mConfig.allowCalls && !mConfig.allowRepeatCallers)
+                || (zenPriorityOnly && !allowCalls && !allowRepeatCallers)
                 || (mSuppressedEffects & SUPPRESSED_EFFECT_CALLS) != 0;
         // alarm restrictions
-        final boolean muteAlarms = zenPriorityOnly && !mConfig.allowAlarms;
+        final boolean muteAlarms = zenPriorityOnly && !allowAlarms;
         // media restrictions
-        final boolean muteMedia = zenPriorityOnly && !mConfig.allowMedia;
+        final boolean muteMedia = zenPriorityOnly && !allowMedia;
         // system restrictions
-        final boolean muteSystem = zenAlarmsOnly || (zenPriorityOnly && !mConfig.allowSystem);
+        final boolean muteSystem = zenAlarmsOnly || (zenPriorityOnly && !allowSystem);
         // total silence restrictions
-        final boolean muteEverything = zenSilence
-                || (zenPriorityOnly && ZenModeConfig.areAllZenBehaviorSoundsMuted(mConfig));
+        final boolean muteEverything = zenSilence || (zenPriorityOnly
+                && ZenModeConfig.areAllZenBehaviorSoundsMuted(mConsolidatedPolicy));
 
         for (int usage : AudioAttributes.SDK_USAGES) {
             final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage);
@@ -968,6 +1009,12 @@
         }
     }
 
+    private void dispatchOnConsolidatedPolicyChanged() {
+        for (Callback callback : mCallbacks) {
+            callback.onConsolidatedPolicyChanged();
+        }
+    }
+
     private void dispatchOnZenModeChanged() {
         for (Callback callback : mCallbacks) {
             callback.onZenModeChanged();
@@ -1188,7 +1235,7 @@
         int content = R.string.zen_upgrade_notification_content;
         int drawable = R.drawable.ic_zen_24dp;
         if (NotificationManager.Policy.areAllVisualEffectsSuppressed(
-                getNotificationPolicy().suppressedVisualEffects)) {
+                getConsolidatedNotificationPolicy().suppressedVisualEffects)) {
             title = R.string.zen_upgrade_notification_visd_title;
             content = R.string.zen_upgrade_notification_visd_content;
             drawable = R.drawable.ic_dnd_block_notifications;
@@ -1362,6 +1409,6 @@
         void onConfigChanged() {}
         void onZenModeChanged() {}
         void onPolicyChanged() {}
+        void onConsolidatedPolicyChanged() {}
     }
-
 }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 404f152..275f3dc 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -77,7 +77,6 @@
  */
 public class LauncherAppsService extends SystemService {
 
-    private static final boolean SHOW_HIDDEN_APP_ENABLED = false;
     private final LauncherAppsImpl mLauncherAppsImpl;
 
     public LauncherAppsService(Context context) {
@@ -310,22 +309,28 @@
                             .addCategory(Intent.CATEGORY_LAUNCHER)
                             .setPackage(packageName),
                     user);
-            if (!SHOW_HIDDEN_APP_ENABLED) {
+            if (Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 0) == 0) {
                 return launcherActivities;
             }
 
             final int callingUid = injectBinderCallingUid();
             final ArrayList<ResolveInfo> result = new ArrayList<>(launcherActivities.getList());
+            final PackageManagerInternal pmInt =
+                    LocalServices.getService(PackageManagerInternal.class);
             if (packageName != null) {
-                // If target package has launcher activities, then return those launcher
-                // activities. Otherwise, return hidden activity that forwards user to app
-                // details page.
+                // If this hidden app should not be shown, return the original list.
+                // Otherwise, inject hidden activity that forwards user to app details page.
                 if (result.size() > 0) {
                     return launcherActivities;
                 }
-                ResolveInfo info = getHiddenAppActivityInfo(packageName, callingUid, user);
-                if (info != null) {
-                    result.add(info);
+                ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, /*flags*/ 0,
+                        callingUid, user.getIdentifier());
+                if (shouldShowHiddenApp(appInfo)) {
+                    ResolveInfo info = getHiddenAppActivityInfo(packageName, callingUid, user);
+                    if (info != null) {
+                        result.add(info);
+                    }
                 }
                 return new ParceledListSlice<>(result);
             }
@@ -336,8 +341,6 @@
                 for (ResolveInfo info : result) {
                     visiblePackages.add(info.activityInfo.packageName);
                 }
-                final PackageManagerInternal pmInt =
-                        LocalServices.getService(PackageManagerInternal.class);
                 List<ApplicationInfo> installedPackages = pmInt.getInstalledApplications(0,
                         user.getIdentifier(), callingUid);
                 for (ApplicationInfo applicationInfo : installedPackages) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0b32d1a..1a5b86c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -58,7 +58,6 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SELinux;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
@@ -108,7 +107,9 @@
 import java.util.Objects;
 import java.util.Random;
 
-public class PackageInstallerService extends IPackageInstaller.Stub {
+/** The service responsible for installing packages. */
+public class PackageInstallerService extends IPackageInstaller.Stub implements
+        PackageSessionProvider {
     private static final String TAG = "PackageInstaller";
     private static final boolean LOGD = false;
 
@@ -297,6 +298,7 @@
             in.setInput(fis, StandardCharsets.UTF_8.name());
 
             int type;
+            PackageInstallerSession currentSession = null;
             while ((type = in.next()) != END_DOCUMENT) {
                 if (type == START_TAG) {
                     final String tag = in.getName();
@@ -304,8 +306,10 @@
                         final PackageInstallerSession session;
                         try {
                             session = PackageInstallerSession.readFromXml(in, mInternalCallback,
-                                    mContext, mPm, mInstallThread.getLooper(), mSessionsDir);
+                                    mContext, mPm, mInstallThread.getLooper(), mSessionsDir, this);
+                            currentSession = session;
                         } catch (Exception e) {
+                            currentSession = null;
                             Slog.e(TAG, "Could not read session", e);
                             continue;
                         }
@@ -330,6 +334,10 @@
                             addHistoricalSessionLocked(session);
                         }
                         mAllocatedSessions.put(session.sessionId, true);
+                    } else if (currentSession != null
+                            && PackageInstallerSession.TAG_CHILD_SESSION.equals(tag)) {
+                        currentSession.addChildSessionIdInternal(
+                                PackageInstallerSession.readChildSessionIdFromXml(in));
                     }
                 }
             }
@@ -437,70 +445,72 @@
             }
         }
 
-        // Only system components can circumvent runtime permissions when installing.
-        if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
-                && mContext.checkCallingOrSelfPermission(Manifest.permission
-                .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
-            throw new SecurityException("You need the "
-                    + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
-                    + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
-        }
-
-        if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
-                || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-            throw new IllegalArgumentException(
-                    "New installs into ASEC containers no longer supported");
-        }
-
-        // Defensively resize giant app icons
-        if (params.appIcon != null) {
-            final ActivityManager am = (ActivityManager) mContext.getSystemService(
-                    Context.ACTIVITY_SERVICE);
-            final int iconSize = am.getLauncherLargeIconSize();
-            if ((params.appIcon.getWidth() > iconSize * 2)
-                    || (params.appIcon.getHeight() > iconSize * 2)) {
-                params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
-                        true);
-            }
-        }
-
-        switch (params.mode) {
-            case SessionParams.MODE_FULL_INSTALL:
-            case SessionParams.MODE_INHERIT_EXISTING:
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid install mode: " + params.mode);
-        }
-
-        // If caller requested explicit location, sanity check it, otherwise
-        // resolve the best internal or adopted location.
-        if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
-            if (!PackageHelper.fitsOnInternal(mContext, params)) {
-                throw new IOException("No suitable internal storage available");
+        if (!params.isMultiPackage) {
+            // Only system components can circumvent runtime permissions when installing.
+            if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
+                    && mContext.checkCallingOrSelfPermission(Manifest.permission
+                    .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
+                throw new SecurityException("You need the "
+                        + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
+                        + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
             }
 
-        } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-            if (!PackageHelper.fitsOnExternal(mContext, params)) {
-                throw new IOException("No suitable external storage available");
+            if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
+                    || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
+                throw new IllegalArgumentException(
+                        "New installs into ASEC containers no longer supported");
             }
 
-        } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
-            // For now, installs to adopted media are treated as internal from
-            // an install flag point-of-view.
-            params.setInstallFlagsInternal();
+            // Defensively resize giant app icons
+            if (params.appIcon != null) {
+                final ActivityManager am = (ActivityManager) mContext.getSystemService(
+                        Context.ACTIVITY_SERVICE);
+                final int iconSize = am.getLauncherLargeIconSize();
+                if ((params.appIcon.getWidth() > iconSize * 2)
+                        || (params.appIcon.getHeight() > iconSize * 2)) {
+                    params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
+                            true);
+                }
+            }
 
-        } else {
-            // For now, installs to adopted media are treated as internal from
-            // an install flag point-of-view.
-            params.setInstallFlagsInternal();
+            switch (params.mode) {
+                case SessionParams.MODE_FULL_INSTALL:
+                case SessionParams.MODE_INHERIT_EXISTING:
+                    break;
+                default:
+                    throw new IllegalArgumentException("Invalid install mode: " + params.mode);
+            }
 
-            // Resolve best location for install, based on combination of
-            // requested install flags, delta size, and manifest settings.
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
+            // If caller requested explicit location, sanity check it, otherwise
+            // resolve the best internal or adopted location.
+            if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
+                if (!PackageHelper.fitsOnInternal(mContext, params)) {
+                    throw new IOException("No suitable internal storage available");
+                }
+
+            } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
+                if (!PackageHelper.fitsOnExternal(mContext, params)) {
+                    throw new IOException("No suitable external storage available");
+                }
+
+            } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
+                // For now, installs to adopted media are treated as internal from
+                // an install flag point-of-view.
+                params.setInstallFlagsInternal();
+
+            } else {
+                // For now, installs to adopted media are treated as internal from
+                // an install flag point-of-view.
+                params.setInstallFlagsInternal();
+
+                // Resolve best location for install, based on combination of
+                // requested install flags, delta size, and manifest settings.
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             }
         }
 
@@ -526,17 +536,19 @@
         // We're staging to exactly one location
         File stageDir = null;
         String stageCid = null;
-        if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
-            final boolean isInstant =
-                    (params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
-            stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);
-        } else {
-            stageCid = buildExternalStageCid(sessionId);
+        if (!params.isMultiPackage) {
+            if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
+                final boolean isInstant =
+                        (params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
+                stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);
+            } else {
+                stageCid = buildExternalStageCid(sessionId);
+            }
         }
-
-        session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
-                mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
-                params, createdMillis, stageDir, stageCid, false, false);
+        session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
+                mInstallThread.getLooper(), sessionId, userId, installerPackageName,
+                callingUid, params, createdMillis, stageDir, stageCid, false, false, null,
+                SessionInfo.INVALID_ID);
 
         synchronized (mSessions) {
             mSessions.put(sessionId, session);
@@ -646,8 +658,8 @@
         }
 
         try {
-            Os.mkdir(stageDir.getAbsolutePath(), 0755);
-            Os.chmod(stageDir.getAbsolutePath(), 0755);
+            Os.mkdir(stageDir.getAbsolutePath(), 0775);
+            Os.chmod(stageDir.getAbsolutePath(), 0775);
         } catch (ErrnoException e) {
             // This purposefully throws if directory already exists
             throw new IOException("Failed to prepare session dir: " + stageDir, e);
@@ -679,7 +691,7 @@
         synchronized (mSessions) {
             for (int i = 0; i < mSessions.size(); i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
-                if (session.userId == userId) {
+                if (session.userId == userId && !session.hasParentSessionId()) {
                     result.add(session.generateInfo(false));
                 }
             }
@@ -700,7 +712,7 @@
 
                 SessionInfo info = session.generateInfo(false);
                 if (Objects.equals(info.getInstallerPackageName(), installerPackageName)
-                        && session.userId == userId) {
+                        && session.userId == userId && !session.hasParentSessionId()) {
                     result.add(info);
                 }
             }
@@ -782,6 +794,13 @@
         mCallbacks.unregister(callback);
     }
 
+    @Override
+    public PackageInstallerSession getSession(int sessionId) {
+        synchronized (mSessions) {
+            return mSessions.get(sessionId);
+        }
+    }
+
     private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
             int installerUid) {
         int count = 0;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 51225a7..26f6e96 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -43,9 +43,12 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.apex.IApexService;
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
@@ -68,6 +71,7 @@
 import android.os.FileBridge;
 import android.os.FileUtils;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -75,6 +79,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.RevocableFileDescriptor;
+import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
@@ -88,6 +93,7 @@
 import android.util.ExceptionUtils;
 import android.util.MathUtils;
 import android.util.Slog;
+import android.util.SparseIntArray;
 import android.util.apk.ApkSignatureVerifier;
 
 import com.android.internal.annotations.GuardedBy;
@@ -128,6 +134,7 @@
 
     /** XML constants used for persisting a session */
     static final String TAG_SESSION = "session";
+    static final String TAG_CHILD_SESSION = "childSession";
     private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
     private static final String ATTR_SESSION_ID = "sessionId";
     private static final String ATTR_USER_ID = "userId";
@@ -138,6 +145,8 @@
     private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
     private static final String ATTR_PREPARED = "prepared";
     private static final String ATTR_SEALED = "sealed";
+    private static final String ATTR_MULTI_PACKAGE = "multiPackage";
+    private static final String ATTR_PARENT_SESSION_ID = "parentSessionId";
     private static final String ATTR_MODE = "mode";
     private static final String ATTR_INSTALL_FLAGS = "installFlags";
     private static final String ATTR_INSTALL_LOCATION = "installLocation";
@@ -155,6 +164,7 @@
     private static final String ATTR_INSTALL_REASON = "installRason";
 
     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
+    private static final int[] EMPTY_CHILD_SESSION_ARRAY = {};
 
     // TODO: enforce INSTALL_ALLOW_TEST
     // TODO: enforce INSTALL_ALLOW_DOWNGRADE
@@ -163,6 +173,7 @@
     private final Context mContext;
     private final PackageManagerService mPm;
     private final Handler mHandler;
+    private final PackageSessionProvider mSessionProvider;
 
     final int sessionId;
     final int userId;
@@ -234,6 +245,10 @@
     private long mVersionCode;
     @GuardedBy("mLock")
     private PackageParser.SigningDetails mSigningDetails;
+    @GuardedBy("mLock")
+    private SparseIntArray mChildSessionIds = new SparseIntArray();
+    @GuardedBy("mLock")
+    private int mParentSessionId;
 
     /**
      * Path to the validated base APK for this session, which may point at an
@@ -370,12 +385,16 @@
     }
 
     public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
-            Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
+            Context context, PackageManagerService pm,
+            PackageSessionProvider sessionProvider, Looper looper,
+            int sessionId, int userId,
             String installerPackageName, int installerUid, SessionParams params, long createdMillis,
-            File stageDir, String stageCid, boolean prepared, boolean sealed) {
+            File stageDir, String stageCid, boolean prepared, boolean sealed,
+            @Nullable int[] childSessionIds, int parentSessionId) {
         mCallback = callback;
         mContext = context;
         mPm = pm;
+        mSessionProvider = sessionProvider;
         mHandler = new Handler(looper, mHandlerCallback);
 
         this.sessionId = sessionId;
@@ -387,8 +406,14 @@
         this.createdMillis = createdMillis;
         this.stageDir = stageDir;
         this.stageCid = stageCid;
+        if (childSessionIds != null) {
+            for (int childSessionId : childSessionIds) {
+                mChildSessionIds.put(childSessionId, 0);
+            }
+        }
+        this.mParentSessionId = parentSessionId;
 
-        if ((stageDir == null) == (stageCid == null)) {
+        if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) {
             throw new IllegalArgumentException(
                     "Exactly one of stageDir or stageCid stage must be set");
         }
@@ -437,6 +462,12 @@
             info.referrerUri = params.referrerUri;
             info.grantedRuntimePermissions = params.grantedRuntimePermissions;
             info.installFlags = params.installFlags;
+            info.isMultiPackage = params.isMultiPackage;
+            info.parentSessionId = mParentSessionId;
+            info.childSessionIds = mChildSessionIds.copyKeys();
+            if (info.childSessionIds == null) {
+                info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY;
+            }
         }
         return info;
     }
@@ -760,6 +791,92 @@
 
     @Override
     public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
+        if (!markAsCommitted(statusReceiver, forTransfer  /* enforce */)) {
+            return;
+        }
+        if (isMultiPackage()) {
+
+            final SparseIntArray remainingSessions = mChildSessionIds.clone();
+            final ChildStatusIntentReceiver localIntentReceiver =
+                    new ChildStatusIntentReceiver(remainingSessions, statusReceiver);
+            for (int childSessionId : getChildSessionIds()) {
+                mSessionProvider.getSession(childSessionId)
+                        .markAsCommitted(localIntentReceiver.getIntentSender(), forTransfer);
+            }
+        }
+        mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
+    }
+
+    private class ChildStatusIntentReceiver {
+        private final SparseIntArray mChildSessionsRemaining;
+        private final IntentSender mStatusReceiver;
+        private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+            @Override
+            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+                statusUpdate(intent);
+            }
+        };
+
+        private ChildStatusIntentReceiver(SparseIntArray remainingSessions,
+                IntentSender statusReceiver) {
+            this.mChildSessionsRemaining = remainingSessions;
+            this.mStatusReceiver = statusReceiver;
+        }
+
+        public IntentSender getIntentSender() {
+            return new IntentSender((IIntentSender) mLocalSender);
+        }
+
+        public void statusUpdate(Intent intent) {
+            mHandler.post(() -> {
+                if (mChildSessionsRemaining.size() == 0) {
+                    return;
+                }
+                final int sessionId = intent.getIntExtra(
+                        PackageInstaller.EXTRA_SESSION_ID, 0);
+                final int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                        PackageInstaller.STATUS_FAILURE);
+                final int sessionIndex = mChildSessionsRemaining.indexOfKey(sessionId);
+                if (PackageInstaller.STATUS_SUCCESS == status) {
+                    mChildSessionsRemaining.removeAt(sessionIndex);
+                    if (mChildSessionsRemaining.size() == 0) {
+                        try {
+                            intent.putExtra(PackageInstaller.EXTRA_SESSION_ID,
+                                    PackageInstallerSession.this.sessionId);
+                            mStatusReceiver.sendIntent(mContext, 0, intent, null, null);
+                        } catch (IntentSender.SendIntentException ignore) {
+                        }
+                    }
+                } else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) {
+                    try {
+                        mStatusReceiver.sendIntent(mContext, 0, intent, null, null);
+                    } catch (IntentSender.SendIntentException ignore) {
+                    }
+                } else {
+                    intent.putExtra(PackageInstaller.EXTRA_SESSION_ID,
+                            PackageInstallerSession.this.sessionId);
+                    mChildSessionsRemaining.clear(); // we're done. Don't send any more.
+                    try {
+                        mStatusReceiver.sendIntent(mContext, 0, intent, null, null);
+                    } catch (IntentSender.SendIntentException ignore) {
+                    }
+                }
+            });
+        }
+    }
+
+
+    /**
+     * Do everything but actually commit the session. If this was not already called, the session
+     * will be sealed and marked as committed. The caller of this method is responsible for
+     * subsequently submitting this session for processing.
+     *
+     * This method may be called multiple times to update the status receiver validate caller
+     * permissions.
+     */
+    public boolean markAsCommitted(
+            @NonNull IntentSender statusReceiver, boolean forTransfer) {
         Preconditions.checkNotNull(statusReceiver);
 
         final boolean wasSealed;
@@ -784,6 +901,12 @@
                 }
             }
 
+            // After validations and updating the observer, we can skip re-sealing, etc. because we
+            // have already marked ourselves as committed.
+            if (mCommitted) {
+                return true;
+            }
+
             wasSealed = mSealed;
             if (!mSealed) {
                 try {
@@ -794,7 +917,7 @@
                     // Do now throw an exception here to stay compatible with O and older
                     destroyInternal();
                     dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
-                    return;
+                    return false;
                 }
             }
 
@@ -807,7 +930,6 @@
             mActiveCount.incrementAndGet();
 
             mCommitted = true;
-            mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
         }
 
         if (!wasSealed) {
@@ -816,6 +938,7 @@
             // the session lock, since otherwise it's a lock inversion.
             mCallback.onSessionSealedBlocking(this);
         }
+        return true;
     }
 
     /**
@@ -831,29 +954,37 @@
         assertNoWriteFileTransfersOpenLocked();
         assertPreparedAndNotDestroyedLocked("sealing of session");
 
-        final PackageInfo pkgInfo = mPm.getPackageInfo(
-                params.appPackageName, PackageManager.GET_SIGNATURES
-                        | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
-
-        resolveStageDirLocked();
-
         mSealed = true;
 
-        // Verify that stage looks sane with respect to existing application.
-        // This currently only ensures packageName, versionCode, and certificate
-        // consistency.
-        try {
-            validateInstallLocked(pkgInfo);
-        } catch (PackageManagerException e) {
-            throw e;
-        } catch (Throwable e) {
-            // Convert all exceptions into package manager exceptions as only those are handled
-            // in the code above
-            throw new PackageManagerException(e);
-        }
-
         // Read transfers from the original owner stay open, but as the session's data
         // cannot be modified anymore, there is no leak of information.
+        if (!params.isMultiPackage) {
+            final PackageInfo pkgInfo = mPm.getPackageInfo(
+                    params.appPackageName, PackageManager.GET_SIGNATURES
+                            | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
+
+            resolveStageDirLocked();
+
+            // Verify that stage looks sane with respect to existing application.
+            // This currently only ensures packageName, versionCode, and certificate
+            // consistency.
+            try {
+                if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+                    validateApexInstallLocked(pkgInfo);
+                } else {
+                    // Verify that stage looks sane with respect to existing application.
+                    // This currently only ensures packageName, versionCode, and certificate
+                    // consistency.
+                    validateApkInstallLocked(pkgInfo);
+                }
+            } catch (PackageManagerException e) {
+                throw e;
+            } catch (Throwable e) {
+                // Convert all exceptions into package manager exceptions as only those are handled
+                // in the code above
+                throw new PackageManagerException(e);
+            }
+        }
     }
 
     @Override
@@ -911,10 +1042,83 @@
     @GuardedBy("mLock")
     private void commitLocked()
             throws PackageManagerException {
-        if (mRelinquished) {
-            Slog.d(TAG, "Ignoring commit after previous commit relinquished control");
+        final PackageManagerService.ActiveInstallSession committingSession =
+                makeSessionActiveLocked();
+        if (committingSession == null) {
             return;
         }
+        if (isMultiPackage()) {
+            final int[] childSessionIds = getChildSessionIds();
+            List<PackageManagerService.ActiveInstallSession> childSessions =
+                    new ArrayList<>(childSessionIds.length);
+            boolean success = true;
+            PackageManagerException failure = null;
+            for (int childSessionId : getChildSessionIds()) {
+                final PackageInstallerSession session = mSessionProvider.getSession(childSessionId);
+                try {
+                    final PackageManagerService.ActiveInstallSession activeSession =
+                            session.makeSessionActiveLocked();
+                    if (activeSession != null) {
+                        if ((activeSession.getSessionParams().installFlags
+                                & PackageManager.INSTALL_APEX) != 0) {
+                            // TODO(b/118865310): Add exception to this case for staged installs
+                            throw new PackageManagerException(
+                                    PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
+                                    "Atomic install is not supported for APEX packages.");
+                        }
+                        childSessions.add(activeSession);
+                    }
+                } catch (PackageManagerException e) {
+                    failure = e;
+                    success = false;
+                }
+            }
+            if (!success) {
+                try {
+                    mRemoteObserver.onPackageInstalled(
+                            null, failure.error, failure.getLocalizedMessage(), null);
+                } catch (RemoteException ignored) {
+                }
+                return;
+            }
+            mPm.installStage(childSessions);
+        } else {
+            if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+                commitApexLocked();
+            } else {
+                mPm.installStage(committingSession);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void commitApexLocked() throws PackageManagerException {
+        try {
+            IApexService apex = IApexService.Stub.asInterface(
+                    ServiceManager.getService("apexservice"));
+            apex.stagePackage(mResolvedBaseFile.toString());
+        } catch (Throwable e) {
+            // Convert all exceptions into package manager exceptions as only those are handled
+            // in the code above
+            throw new PackageManagerException(e);
+        } finally {
+            destroyInternal();
+            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "APEX installed", null);
+        }
+    }
+
+    /**
+     * Stages this session for install and returns a
+     * {@link PackageManagerService.ActiveInstallSession} representing this new staged state or null
+     * in case permissions need to be requested before install can proceed.
+     */
+    @GuardedBy("mLock")
+    private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
+            throws PackageManagerException {
+        if (mRelinquished) {
+            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                    "Session relinquished");
+        }
         if (mDestroyed) {
             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
         }
@@ -922,83 +1126,86 @@
             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
         }
 
-        Preconditions.checkNotNull(mPackageName);
-        Preconditions.checkNotNull(mSigningDetails);
-        Preconditions.checkNotNull(mResolvedBaseFile);
+        if (!params.isMultiPackage) {
+            Preconditions.checkNotNull(mPackageName);
+            Preconditions.checkNotNull(mSigningDetails);
+            Preconditions.checkNotNull(mResolvedBaseFile);
 
-        if (needToAskForPermissionsLocked()) {
-            // User needs to confirm installation; give installer an intent they can use to involve
-            // user.
-            final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
-            intent.setPackage(mPm.getPackageInstallerPackageName());
-            intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
-            try {
-                mRemoteObserver.onUserActionRequired(intent);
-            } catch (RemoteException ignored) {
-            }
-
-            // Commit was keeping session marked as active until now; release
-            // that extra refcount so session appears idle.
-            closeInternal(false);
-            return;
-        }
-
-        // Inherit any packages and native libraries from existing install that
-        // haven't been overridden.
-        if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
-            try {
-                final List<File> fromFiles = mResolvedInheritedFiles;
-                final File toDir = resolveStageDirLocked();
-
-                if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
-                if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
-                    throw new IllegalStateException("mInheritedFilesBase == null");
+            if (needToAskForPermissionsLocked()) {
+                // User needs to confirm installation;
+                // give installer an intent they can use to involve
+                // user.
+                final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
+                intent.setPackage(mPm.getPackageInstallerPackageName());
+                intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+                try {
+                    mRemoteObserver.onUserActionRequired(intent);
+                } catch (RemoteException ignored) {
                 }
 
-                if (isLinkPossible(fromFiles, toDir)) {
-                    if (!mResolvedInstructionSets.isEmpty()) {
-                        final File oatDir = new File(toDir, "oat");
-                        createOatDirs(mResolvedInstructionSets, oatDir);
+                // Commit was keeping session marked as active until now; release
+                // that extra refcount so session appears idle.
+                closeInternal(false);
+                return null;
+            }
+
+            // Inherit any packages and native libraries from existing install that
+            // haven't been overridden.
+            if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+                try {
+                    final List<File> fromFiles = mResolvedInheritedFiles;
+                    final File toDir = resolveStageDirLocked();
+
+                    if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
+                    if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
+                        throw new IllegalStateException("mInheritedFilesBase == null");
                     }
-                    // pre-create lib dirs for linking if necessary
-                    if (!mResolvedNativeLibPaths.isEmpty()) {
-                        for (String libPath : mResolvedNativeLibPaths) {
-                            // "/lib/arm64" -> ["lib", "arm64"]
-                            final int splitIndex = libPath.lastIndexOf('/');
-                            if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
-                                Slog.e(TAG, "Skipping native library creation for linking due to "
-                                        + "invalid path: " + libPath);
-                                continue;
-                            }
-                            final String libDirPath = libPath.substring(1, splitIndex);
-                            final File libDir = new File(toDir, libDirPath);
-                            if (!libDir.exists()) {
-                                NativeLibraryHelper.createNativeLibrarySubdir(libDir);
-                            }
-                            final String archDirPath = libPath.substring(splitIndex + 1);
-                            NativeLibraryHelper.createNativeLibrarySubdir(
-                                    new File(libDir, archDirPath));
+
+                    if (isLinkPossible(fromFiles, toDir)) {
+                        if (!mResolvedInstructionSets.isEmpty()) {
+                            final File oatDir = new File(toDir, "oat");
+                            createOatDirs(mResolvedInstructionSets, oatDir);
                         }
+                        // pre-create lib dirs for linking if necessary
+                        if (!mResolvedNativeLibPaths.isEmpty()) {
+                            for (String libPath : mResolvedNativeLibPaths) {
+                                // "/lib/arm64" -> ["lib", "arm64"]
+                                final int splitIndex = libPath.lastIndexOf('/');
+                                if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
+                                    Slog.e(TAG,
+                                            "Skipping native library creation for linking due to "
+                                                    + "invalid path: " + libPath);
+                                    continue;
+                                }
+                                final String libDirPath = libPath.substring(1, splitIndex);
+                                final File libDir = new File(toDir, libDirPath);
+                                if (!libDir.exists()) {
+                                    NativeLibraryHelper.createNativeLibrarySubdir(libDir);
+                                }
+                                final String archDirPath = libPath.substring(splitIndex + 1);
+                                NativeLibraryHelper.createNativeLibrarySubdir(
+                                        new File(libDir, archDirPath));
+                            }
+                        }
+                        linkFiles(fromFiles, toDir, mInheritedFilesBase);
+                    } else {
+                        // TODO: this should delegate to DCS so the system process
+                        // avoids holding open FDs into containers.
+                        copyFiles(fromFiles, toDir);
                     }
-                    linkFiles(fromFiles, toDir, mInheritedFilesBase);
-                } else {
-                    // TODO: this should delegate to DCS so the system process
-                    // avoids holding open FDs into containers.
-                    copyFiles(fromFiles, toDir);
+                } catch (IOException e) {
+                    throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
+                            "Failed to inherit existing install", e);
                 }
-            } catch (IOException e) {
-                throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
-                        "Failed to inherit existing install", e);
             }
+
+            // TODO: surface more granular state from dexopt
+            mInternalProgress = 0.5f;
+            computeProgressLocked(true);
+
+            // Unpack native libraries
+            extractNativeLibraries(mResolvedStageDir, params.abiOverride, mayInheritNativeLibs());
         }
-
-        // TODO: surface more granular state from dexopt
-        mInternalProgress = 0.5f;
-        computeProgressLocked(true);
-
-        // Unpack native libraries
-        extractNativeLibraries(mResolvedStageDir, params.abiOverride, mayInheritNativeLibs());
-
         // We've reached point of no return; call into PMS to install the stage.
         // Regardless of success or failure we always destroy session.
         final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
@@ -1023,8 +1230,11 @@
         }
 
         mRelinquished = true;
-        mPm.installStage(mPackageName, stageDir, localObserver, params,
-                mInstallerPackageName, mInstallerUid, user, mSigningDetails);
+        final PackageManagerService.ActiveInstallSession activeInstallSession =
+                new PackageManagerService.ActiveInstallSession(mPackageName, stageDir,
+                        localObserver, params, mInstallerPackageName, mInstallerUid, user,
+                        mSigningDetails);
+        return activeInstallSession;
     }
 
     private static void maybeRenameFile(File from, File to) throws PackageManagerException {
@@ -1047,6 +1257,57 @@
                 (params.installFlags & PackageManager.DONT_KILL_APP) != 0;
     }
 
+    @GuardedBy("mLock")
+    private void validateApexInstallLocked(@Nullable PackageInfo pkgInfo)
+            throws PackageManagerException {
+        mResolvedStagedFiles.clear();
+        mResolvedInheritedFiles.clear();
+
+        try {
+            resolveStageDirLocked();
+        } catch (IOException e) {
+            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
+                "Failed to resolve stage location", e);
+        }
+
+        final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
+        if (ArrayUtils.isEmpty(addedFiles)) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
+        }
+
+        if (addedFiles.length > 1) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                "Only one APEX file at a time might be installed");
+        }
+        File addedFile = addedFiles[0];
+        final ApkLite apk;
+        try {
+            apk = PackageParser.parseApkLite(
+                addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
+        } catch (PackageParserException e) {
+            throw PackageManagerException.from(e);
+        }
+
+        mPackageName = apk.packageName;
+        mVersionCode = apk.getLongVersionCode();
+        mSigningDetails = apk.signingDetails;
+        mResolvedBaseFile = addedFile;
+
+        assertApkConsistentLocked(String.valueOf(addedFile), apk);
+
+        if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
+            try {
+                // STOPSHIP: For APEX we should also implement proper APK Signature verification.
+                mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
+                    pkgInfo.applicationInfo.sourceDir,
+                    PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
+            } catch (PackageParserException e) {
+                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    "Couldn't obtain signatures from base APK");
+            }
+        }
+    }
+
     /**
      * Validate install by confirming that all application packages are have
      * consistent package name, version code, and signing certificates.
@@ -1060,7 +1321,7 @@
      * {@link PackageManagerService}.
      */
     @GuardedBy("mLock")
-    private void validateInstallLocked(@Nullable PackageInfo pkgInfo)
+    private void validateApkInstallLocked(@Nullable PackageInfo pkgInfo)
             throws PackageManagerException {
         ApkLite baseApk = null;
         mPackageName = null;
@@ -1475,6 +1736,14 @@
         }
     }
 
+    /**
+     * Adds a child session ID without any safety / sanity checks. This should only be used to
+     * build a session from XML or similar.
+     */
+    void addChildSessionIdInternal(int sessionId) {
+        mChildSessionIds.put(sessionId, 0);
+    }
+
     public void open() throws IOException {
         if (mActiveCount.getAndIncrement() == 0) {
             mCallback.onSessionActiveChanged(this, true);
@@ -1486,6 +1755,8 @@
             if (!mPrepared) {
                 if (stageDir != null) {
                     prepareStageDir(stageDir);
+                } else if (params.isMultiPackage) {
+                    // it's all ok
                 } else {
                     throw new IllegalArgumentException("stageDir must be set");
                 }
@@ -1534,6 +1805,81 @@
         dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
     }
 
+    @Override
+    public boolean isMultiPackage() {
+        return params.isMultiPackage;
+    }
+
+    @Override
+    public int[] getChildSessionIds() {
+        final int[] childSessionIds = mChildSessionIds.copyKeys();
+        if (childSessionIds != null) {
+            return childSessionIds;
+        }
+        return EMPTY_CHILD_SESSION_ARRAY;
+    }
+
+    @Override
+    public void addChildSessionId(int sessionId) {
+        final PackageInstallerSession session = mSessionProvider.getSession(sessionId);
+        if (session == null) {
+            throw new RemoteException("Unable to add child.",
+                    new PackageManagerException("Child session " + sessionId + " does not exist"),
+                    false, true).rethrowAsRuntimeException();
+        }
+        synchronized (mLock) {
+            final int indexOfSession = mChildSessionIds.indexOfKey(sessionId);
+            if (indexOfSession >= 0) {
+                return;
+            }
+            session.setParentSessionId(this.sessionId);
+            addChildSessionIdInternal(sessionId);
+        }
+    }
+
+    @Override
+    public void removeChildSessionId(int sessionId) {
+        final PackageInstallerSession session = mSessionProvider.getSession(sessionId);
+        synchronized (mLock) {
+            final int indexOfSession = mChildSessionIds.indexOfKey(sessionId);
+            if (session != null) {
+                session.setParentSessionId(SessionInfo.INVALID_ID);
+            }
+            if (indexOfSession < 0) {
+                // not added in the first place; no-op
+                return;
+            }
+            mChildSessionIds.removeAt(indexOfSession);
+        }
+    }
+
+    /**
+     * Sets the parent session ID if not already set.
+     * If {@link SessionInfo#INVALID_ID} is passed, it will be unset.
+     */
+    void setParentSessionId(int parentSessionId) {
+        synchronized (mLock) {
+            if (parentSessionId != SessionInfo.INVALID_ID
+                    && mParentSessionId != SessionInfo.INVALID_ID) {
+                throw new RemoteException("Unable to set parent session.",
+                        new PackageManagerException(
+                                "The parent of " + sessionId + " is" + " already set to "
+                                        + mParentSessionId), false,
+                        true).rethrowAsRuntimeException();
+            }
+            this.mParentSessionId = parentSessionId;
+        }
+    }
+
+    boolean hasParentSessionId() {
+        return mParentSessionId != SessionInfo.INVALID_ID;
+    }
+
+    @Override
+    public int getParentSessionId() {
+        return mParentSessionId;
+    }
+
     private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
         final IPackageInstallObserver2 observer;
         final String packageName;
@@ -1623,6 +1969,7 @@
         pw.printPair("mBridges", mBridges.size());
         pw.printPair("mFinalStatus", mFinalStatus);
         pw.printPair("mFinalMessage", mFinalMessage);
+        pw.printPair("params.isMultiPackage", params.isMultiPackage);
         pw.println();
 
         pw.decreaseIndent();
@@ -1673,6 +2020,10 @@
             writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
             writeBooleanAttribute(out, ATTR_SEALED, isSealed());
 
+            writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
+            // TODO(patb,109941548): avoid writing to xml and instead infer / validate this after
+            //                       we've read all sessions.
+            writeIntAttribute(out, ATTR_PARENT_SESSION_ID, mParentSessionId);
             writeIntAttribute(out, ATTR_MODE, params.mode);
             writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
             writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
@@ -1707,6 +2058,12 @@
 
                 params.appIconLastModified = appIconFile.lastModified();
             }
+            final int[] childSessionIds = getChildSessionIds();
+            for (int childSessionId : childSessionIds) {
+                out.startTag(null, TAG_CHILD_SESSION);
+                writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
+                out.endTag(null, TAG_CHILD_SESSION);
+            }
         }
 
         out.endTag(null, TAG_SESSION);
@@ -1751,11 +2108,15 @@
      * @param installerThread Thread to be used for callbacks of this session
      * @param sessionsDir The directory the sessions are stored in
      *
+     * @param sessionProvider
      * @return The newly created session
      */
+    // TODO(patb,109941548): modify readFromXml to consume to the next tag session tag so we
+    //                       can have a complete session for the constructor
     public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
             @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
-            @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir)
+            @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir,
+            @NonNull PackageSessionProvider sessionProvider)
             throws IOException, XmlPullParserException {
         final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
         final int userId = readIntAttribute(in, ATTR_USER_ID);
@@ -1768,9 +2129,12 @@
         final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
         final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
         final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
+        final int parentSessionId = readIntAttribute(in, ATTR_PARENT_SESSION_ID,
+                SessionInfo.INVALID_ID);
 
         final SessionParams params = new SessionParams(
                 SessionParams.MODE_INVALID);
+        params.isMultiPackage = readBooleanAttribute(in, ATTR_MULTI_PACKAGE, false);
         params.mode = readIntAttribute(in, ATTR_MODE);
         params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
         params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
@@ -1793,9 +2157,16 @@
             params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
             params.appIconLastModified = appIconFile.lastModified();
         }
-
-        return new PackageInstallerSession(callback, context, pm,
+        return new PackageInstallerSession(callback, context, pm, sessionProvider,
                 installerThread, sessionId, userId, installerPackageName, installerUid,
-                params, createdMillis, stageDir, stageCid, prepared, sealed);
+                params, createdMillis, stageDir, stageCid, prepared, sealed,
+                EMPTY_CHILD_SESSION_ARRAY, parentSessionId);
+    }
+
+    /**
+     * Reads the session ID from a child session tag stored in the provided {@link XmlPullParser}
+     */
+    static int readChildSessionIdFromXml(@NonNull XmlPullParser in) {
+        return readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID);
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9399ebf..ed5b33b6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -60,6 +60,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
 import static android.content.pm.PackageManager.INSTALL_INTERNAL;
+import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
@@ -85,6 +86,11 @@
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.PackageParser.isApkFile;
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
 import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
@@ -234,7 +240,6 @@
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
-import android.permission.PermissionManager;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
 import android.security.KeyStore;
@@ -322,6 +327,7 @@
 import dalvik.system.VMRuntime;
 
 import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -365,6 +371,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
 import java.util.function.Predicate;
 
 /**
@@ -846,23 +853,9 @@
     final ParallelPackageParserCallback mParallelPackageParserCallback =
             new ParallelPackageParserCallback();
 
-    public static final class SharedLibraryEntry {
-        public final @Nullable String path;
-        public final @Nullable String apk;
-        public final @NonNull SharedLibraryInfo info;
-
-        SharedLibraryEntry(String _path, String _apk, String name, long version, int type,
-                String declaringPackageName, long declaringPackageVersionCode) {
-            path = _path;
-            apk = _apk;
-            info = new SharedLibraryInfo(name, version, type, new VersionedPackage(
-                    declaringPackageName, declaringPackageVersionCode), null);
-        }
-    }
-
     // Currently known shared libraries.
-    final ArrayMap<String, LongSparseArray<SharedLibraryEntry>> mSharedLibraries = new ArrayMap<>();
-    final ArrayMap<String, LongSparseArray<SharedLibraryEntry>> mStaticLibsByDeclaringPackage =
+    final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries = new ArrayMap<>();
+    final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mStaticLibsByDeclaringPackage =
             new ArrayMap<>();
 
     // Mapping from instrumentation class names to info about them.
@@ -2103,6 +2096,28 @@
         }
     }
 
+    @GuardedBy("mPackages")
+    private void setupBuiltinSharedLibraryDependenciesLocked() {
+        // Builtin libraries don't have versions.
+        long version = SharedLibraryInfo.VERSION_UNDEFINED;
+
+        SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(ANDROID_HIDL_MANAGER, version);
+        if (libraryInfo != null) {
+            libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_HIDL_BASE, version));
+        }
+
+        libraryInfo = getSharedLibraryInfoLPr(ANDROID_TEST_RUNNER, version);
+        if (libraryInfo != null) {
+            libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_MOCK, version));
+            libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_BASE, version));
+        }
+
+        libraryInfo = getSharedLibraryInfoLPr(ANDROID_TEST_MOCK, version);
+        if (libraryInfo != null) {
+            libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_BASE, version));
+        }
+    }
+
     public PackageManagerService(Context context, Installer installer,
             boolean factoryTest, boolean onlyCore) {
         LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
@@ -2218,6 +2233,9 @@
                 addSharedLibraryLPw(path, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
                         SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
             }
+            // Builtin libraries cannot encode their dependency where they are
+            // defined, so fix that now.
+            setupBuiltinSharedLibraryDependenciesLocked();
 
             SELinuxMMAC.readInstallPolicy();
 
@@ -2912,60 +2930,6 @@
 
             checkDefaultBrowser();
 
-            // If a granted permission is split, all new permissions should be granted too
-            if (mIsUpgrade) {
-                final int callingUid = getCallingUid();
-
-                final List<PermissionManager.SplitPermissionInfo> splitPermissions =
-                        mContext.getSystemService(PermissionManager.class).getSplitPermissions();
-                final int numSplitPerms = splitPermissions.size();
-                for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
-                    final PermissionManager.SplitPermissionInfo splitPerm =
-                            splitPermissions.get(splitPermNum);
-                    final String rootPerm = splitPerm.getSplitPermission();
-
-                    if (preUpgradeSdkVersion >= splitPerm.getTargetSdk()) {
-                        continue;
-                    }
-
-                    final int numPackages = mPackages.size();
-                    for (int packageNum = 0; packageNum < numPackages; packageNum++) {
-                        final PackageParser.Package pkg = mPackages.valueAt(packageNum);
-
-                        if (pkg.applicationInfo.targetSdkVersion >= splitPerm.getTargetSdk()
-                                || !pkg.requestedPermissions.contains(rootPerm)) {
-                            continue;
-                        }
-
-                        final int userId = UserHandle.getUserId(pkg.applicationInfo.uid);
-                        final String pkgName = pkg.packageName;
-
-                        if (checkPermission(rootPerm, pkgName, userId) == PERMISSION_DENIED) {
-                            continue;
-                        }
-
-                        final List<String> newPerms = splitPerm.getNewPermissions();
-
-                        final int numNewPerms = newPerms.size();
-                        for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) {
-                            final String newPerm = newPerms.get(newPermNum);
-                            if (checkPermission(newPerm, pkgName, userId) == PERMISSION_GRANTED) {
-                                continue;
-                            }
-
-                            if (DEBUG_PERMISSIONS) {
-                                Slog.v(TAG, "Granting " + newPerm + " to " + pkgName
-                                        + " as the root permission " + rootPerm
-                                        + " is already granted");
-                            }
-
-                            mPermissionManager.grantRuntimePermission(newPerm, pkgName, true,
-                                    callingUid, userId, null);
-                        }
-                    }
-                }
-            }
-
             // clear only after permissions and other defaults have been updated
             mExistingSystemPackages.clear();
             mPromoteSystemApps = false;
@@ -3325,11 +3289,15 @@
 
     private @NonNull String getRequiredSharedLibraryLPr(String name, int version) {
         synchronized (mPackages) {
-            SharedLibraryEntry libraryEntry = getSharedLibraryEntryLPr(name, version);
-            if (libraryEntry == null) {
+            SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(name, version);
+            if (libraryInfo == null) {
                 throw new IllegalStateException("Missing required shared library:" + name);
             }
-            return libraryEntry.apk;
+            String packageName = libraryInfo.getPackageName();
+            if (packageName == null) {
+                throw new IllegalStateException("Expected a package for shared library " + name);
+            }
+            return packageName;
         }
     }
 
@@ -4078,9 +4046,9 @@
             return false;
         }
 
-        final SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(ps.pkg.staticSharedLibName,
+        final SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(ps.pkg.staticSharedLibName,
                 ps.pkg.staticSharedLibVersion);
-        if (libEntry == null) {
+        if (libraryInfo == null) {
             return false;
         }
 
@@ -4097,11 +4065,11 @@
             PackageSetting uidPs = mSettings.getPackageLPr(uidPackageName);
             if (uidPs != null) {
                 final int index = ArrayUtils.indexOf(uidPs.usesStaticLibraries,
-                        libEntry.info.getName());
+                        libraryInfo.getName());
                 if (index < 0) {
                     continue;
                 }
-                if (uidPs.pkg.usesStaticLibrariesVersions[index] == libEntry.info.getLongVersion()) {
+                if (uidPs.pkg.usesStaticLibrariesVersions[index] == libraryInfo.getLongVersion()) {
                     return false;
                 }
             }
@@ -4297,8 +4265,12 @@
             int filterCallingUid, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForApplication(flags, userId, packageName);
-        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
-                false /* requireFullPermission */, false /* checkShell */, "get application info");
+
+        if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
+            mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+                    false /* requireFullPermission */, false /* checkShell */,
+                    "get application info");
+        }
 
         // writer
         synchronized (mPackages) {
@@ -4498,14 +4470,14 @@
             final int[] allUsers = sUserManager.getUserIds();
             final int libCount = mSharedLibraries.size();
             for (int i = 0; i < libCount; i++) {
-                final LongSparseArray<SharedLibraryEntry> versionedLib
+                final LongSparseArray<SharedLibraryInfo> versionedLib
                         = mSharedLibraries.valueAt(i);
                 if (versionedLib == null) {
                     continue;
                 }
                 final int versionCount = versionedLib.size();
                 for (int j = 0; j < versionCount; j++) {
-                    SharedLibraryInfo libInfo = versionedLib.valueAt(j).info;
+                    SharedLibraryInfo libInfo = versionedLib.valueAt(j);
                     // Skip packages that are not static shared libs.
                     if (!libInfo.isStatic()) {
                         break;
@@ -4846,14 +4818,14 @@
 
             final int libCount = mSharedLibraries.size();
             for (int i = 0; i < libCount; i++) {
-                LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+                LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.valueAt(i);
                 if (versionedLib == null) {
                     continue;
                 }
 
                 final int versionCount = versionedLib.size();
                 for (int j = 0; j < versionCount; j++) {
-                    SharedLibraryInfo libInfo = versionedLib.valueAt(j).info;
+                    SharedLibraryInfo libInfo = versionedLib.valueAt(j);
                     if (!canSeeStaticLibraries && libInfo.isStatic()) {
                         break;
                     }
@@ -4869,10 +4841,13 @@
                         Binder.restoreCallingIdentity(identity);
                     }
 
-                    SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getName(),
-                            libInfo.getLongVersion(), libInfo.getType(),
-                            libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo,
-                            flags, userId));
+                    SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getPath(),
+                            libInfo.getPackageName(), libInfo.getName(), libInfo.getLongVersion(),
+                            libInfo.getType(), libInfo.getDeclaringPackage(),
+                            getPackagesUsingSharedLibraryLPr(libInfo, flags, userId),
+                            (libInfo.getDependencies() == null
+                                    ? null
+                                    : new ArrayList(libInfo.getDependencies())));
 
                     if (result == null) {
                         result = new ArrayList<>();
@@ -4988,28 +4963,28 @@
             Set<String> libs = null;
             final int libCount = mSharedLibraries.size();
             for (int i = 0; i < libCount; i++) {
-                LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+                LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.valueAt(i);
                 if (versionedLib == null) {
                     continue;
                 }
                 final int versionCount = versionedLib.size();
                 for (int j = 0; j < versionCount; j++) {
-                    SharedLibraryEntry libEntry = versionedLib.valueAt(j);
-                    if (!libEntry.info.isStatic()) {
+                    SharedLibraryInfo libraryInfo = versionedLib.valueAt(j);
+                    if (!libraryInfo.isStatic()) {
                         if (libs == null) {
                             libs = new ArraySet<>();
                         }
-                        libs.add(libEntry.info.getName());
+                        libs.add(libraryInfo.getName());
                         break;
                     }
-                    PackageSetting ps = mSettings.getPackageLPr(libEntry.apk);
+                    PackageSetting ps = mSettings.getPackageLPr(libraryInfo.getPackageName());
                     if (ps != null && !filterSharedLibPackageLPr(ps, Binder.getCallingUid(),
                             UserHandle.getUserId(Binder.getCallingUid()),
                             PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) {
                         if (libs == null) {
                             libs = new ArraySet<>();
                         }
-                        libs.add(libEntry.info.getName());
+                        libs.add(libraryInfo.getName());
                         break;
                     }
                 }
@@ -9326,24 +9301,24 @@
 
     private PackageParser.Package findSharedNonSystemLibrary(String name, long version) {
         synchronized (mPackages) {
-            SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(name, version);
-            if (libEntry != null) {
-                return mPackages.get(libEntry.apk);
+            SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(name, version);
+            if (libraryInfo != null) {
+                return mPackages.get(libraryInfo.getPackageName());
             }
             return null;
         }
     }
 
-    private SharedLibraryEntry getSharedLibraryEntryLPr(String name, long version) {
-        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+    private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
+        LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
             return null;
         }
         return versionedLib.get(version);
     }
 
-    private SharedLibraryEntry getLatestSharedLibraVersionLPr(PackageParser.Package pkg) {
-        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
+    private SharedLibraryInfo getLatestSharedLibraVersionLPr(PackageParser.Package pkg) {
+        LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
                 pkg.staticSharedLibName);
         if (versionedLib == null) {
             return null;
@@ -9540,7 +9515,8 @@
                     }
                 }
                 if (deleteSandboxData && getStorageManagerInternal() != null) {
-                    getStorageManagerInternal().destroySandboxForApp(pkg.packageName, realUserId);
+                    getStorageManagerInternal().destroySandboxForApp(pkg.packageName,
+                            pkg.mSharedUserId, realUserId);
                 }
             } catch (PackageManagerException e) {
                 // Should not happen
@@ -9602,15 +9578,34 @@
     }
 
     @GuardedBy("mPackages")
-    private void addSharedLibraryLPr(Set<String> usesLibraryFiles,
-            SharedLibraryEntry file,
-            PackageParser.Package changingLib) {
-        if (file.path != null) {
-            usesLibraryFiles.add(file.path);
+    private void applyDefiningSharedLibraryUpdateLocked(
+            PackageParser.Package pkg, SharedLibraryInfo libInfo,
+            BiConsumer<SharedLibraryInfo, SharedLibraryInfo> action) {
+        if (pkg.isLibrary()) {
+            if (pkg.staticSharedLibName != null) {
+                SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
+                        pkg.staticSharedLibName, pkg.staticSharedLibVersion);
+                action.accept(definedLibrary, libInfo);
+            } else {
+                for (String libraryName : pkg.libraryNames) {
+                    SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
+                            libraryName, SharedLibraryInfo.VERSION_UNDEFINED);
+                    action.accept(definedLibrary, libInfo);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mPackages")
+    private void addSharedLibraryLPr(PackageParser.Package pkg, Set<String> usesLibraryFiles,
+            SharedLibraryInfo libInfo, PackageParser.Package changingLib) {
+
+        if (libInfo.getPath() != null) {
+            usesLibraryFiles.add(libInfo.getPath());
             return;
         }
-        PackageParser.Package p = mPackages.get(file.apk);
-        if (changingLib != null && changingLib.packageName.equals(file.apk)) {
+        PackageParser.Package p = mPackages.get(libInfo.getPackageName());
+        if (changingLib != null && changingLib.packageName.equals(libInfo.getPackageName())) {
             // If we are doing this while in the middle of updating a library apk,
             // then we need to make sure to use that new apk for determining the
             // dependencies here.  (We haven't yet finished committing the new apk
@@ -9621,6 +9616,10 @@
         }
         if (p != null) {
             usesLibraryFiles.addAll(p.getAllCodePaths());
+            // If the package provides libraries, add the dependency to them.
+            applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> {
+                definingLibrary.addDependency(dependency);
+            });
             if (p.usesLibraryFiles != null) {
                 Collections.addAll(usesLibraryFiles, p.usesLibraryFiles);
             }
@@ -9633,46 +9632,61 @@
         if (pkg == null) {
             return;
         }
+
+        // If the package provides libraries, clear their old dependencies.
+        // This method will set them up again.
+        applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> {
+            definingLibrary.clearDependencies();
+        });
         // The collection used here must maintain the order of addition (so
         // that libraries are searched in the correct order) and must have no
         // duplicates.
-        Set<String> usesLibraryFiles = null;
+        ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
         if (pkg.usesLibraries != null) {
-            usesLibraryFiles = addSharedLibrariesLPw(pkg.usesLibraries,
-                    null, null, pkg.packageName, changingLib, true,
+            usesLibraryInfos = addSharedLibrariesLPw(pkg.usesLibraries,
+                    null, null, pkg.packageName, true,
                     pkg.applicationInfo.targetSdkVersion, null);
         }
         if (pkg.usesStaticLibraries != null) {
-            usesLibraryFiles = addSharedLibrariesLPw(pkg.usesStaticLibraries,
+            usesLibraryInfos = addSharedLibrariesLPw(pkg.usesStaticLibraries,
                     pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests,
-                    pkg.packageName, changingLib, true,
-                    pkg.applicationInfo.targetSdkVersion, usesLibraryFiles);
+                    pkg.packageName, true,
+                    pkg.applicationInfo.targetSdkVersion, usesLibraryInfos);
         }
         if (pkg.usesOptionalLibraries != null) {
-            usesLibraryFiles = addSharedLibrariesLPw(pkg.usesOptionalLibraries,
-                    null, null, pkg.packageName, changingLib, false,
-                    pkg.applicationInfo.targetSdkVersion, usesLibraryFiles);
+            usesLibraryInfos = addSharedLibrariesLPw(pkg.usesOptionalLibraries,
+                    null, null, pkg.packageName, false,
+                    pkg.applicationInfo.targetSdkVersion, usesLibraryInfos);
         }
-        if (!ArrayUtils.isEmpty(usesLibraryFiles)) {
+        if (usesLibraryInfos != null) {
+            pkg.usesLibraryInfos = usesLibraryInfos;
+            // Use LinkedHashSet to preserve the order of files added to
+            // usesLibraryFiles while eliminating duplicates.
+            Set<String> usesLibraryFiles = new LinkedHashSet<>();
+            for (SharedLibraryInfo libInfo : usesLibraryInfos) {
+                addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib);
+            }
             pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]);
         } else {
+            pkg.usesLibraryInfos = null;
             pkg.usesLibraryFiles = null;
         }
     }
 
     @GuardedBy("mPackages")
-    private Set<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
+    private ArrayList<SharedLibraryInfo> addSharedLibrariesLPw(
+            @NonNull List<String> requestedLibraries,
             @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
-            @NonNull String packageName, @Nullable PackageParser.Package changingLib,
-            boolean required, int targetSdk, @Nullable Set<String> outUsedLibraries)
+            @NonNull String packageName, boolean required, int targetSdk,
+            @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries)
             throws PackageManagerException {
         final int libCount = requestedLibraries.size();
         for (int i = 0; i < libCount; i++) {
             final String libName = requestedLibraries.get(i);
             final long libVersion = requiredVersions != null ? requiredVersions[i]
                     : SharedLibraryInfo.VERSION_UNDEFINED;
-            final SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(libName, libVersion);
-            if (libEntry == null) {
+            final SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(libName, libVersion);
+            if (libraryInfo == null) {
                 if (required) {
                     throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
                             "Package " + packageName + " requires unavailable shared library "
@@ -9684,14 +9698,14 @@
                 }
             } else {
                 if (requiredVersions != null && requiredCertDigests != null) {
-                    if (libEntry.info.getLongVersion() != requiredVersions[i]) {
+                    if (libraryInfo.getLongVersion() != requiredVersions[i]) {
                         throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
                             "Package " + packageName + " requires unavailable static shared"
                                     + " library " + libName + " version "
-                                    + libEntry.info.getLongVersion() + "; failing!");
+                                    + libraryInfo.getLongVersion() + "; failing!");
                     }
 
-                    PackageParser.Package libPkg = mPackages.get(libEntry.apk);
+                    PackageParser.Package libPkg = mPackages.get(libraryInfo.getPackageName());
                     if (libPkg == null) {
                         throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
                                 "Package " + packageName + " requires unavailable static shared"
@@ -9748,11 +9762,9 @@
                 }
 
                 if (outUsedLibraries == null) {
-                    // Use LinkedHashSet to preserve the order of files added to
-                    // usesLibraryFiles while eliminating duplicates.
-                    outUsedLibraries = new LinkedHashSet<>();
+                    outUsedLibraries = new ArrayList<>();
                 }
-                addSharedLibraryLPr(outUsedLibraries, libEntry, changingLib);
+                outUsedLibraries.add(libraryInfo);
             }
         }
         return outUsedLibraries;
@@ -10167,9 +10179,9 @@
         // library in order to compare signatures.
         PackageSetting signatureCheckPs = pkgSetting;
         if (pkg.applicationInfo.isStaticSharedLibrary()) {
-            SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
-            if (libraryEntry != null) {
-                signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
+            SharedLibraryInfo libraryInfo = getLatestSharedLibraVersionLPr(pkg);
+            if (libraryInfo != null) {
+                signatureCheckPs = mSettings.getPackageLPr(libraryInfo.getPackageName());
             }
         }
 
@@ -11012,12 +11024,12 @@
                 long minVersionCode = Long.MIN_VALUE;
                 long maxVersionCode = Long.MAX_VALUE;
 
-                LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
+                LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
                         pkg.staticSharedLibName);
                 if (versionedLib != null) {
                     final int versionCount = versionedLib.size();
                     for (int i = 0; i < versionCount; i++) {
-                        SharedLibraryInfo libInfo = versionedLib.valueAt(i).info;
+                        SharedLibraryInfo libInfo = versionedLib.valueAt(i);
                         final long libVersionCode = libInfo.getDeclaringPackage()
                                 .getLongVersionCode();
                         if (libInfo.getLongVersion() <  pkg.staticSharedLibVersion) {
@@ -11185,7 +11197,7 @@
 
     private boolean addSharedLibraryLPw(String path, String apk, String name, long version,
             int type, String declaringPackageName, long declaringVersionCode) {
-        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+        LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
             versionedLib = new LongSparseArray<>();
             mSharedLibraries.put(name, versionedLib);
@@ -11195,14 +11207,15 @@
         } else if (versionedLib.indexOfKey(version) >= 0) {
             return false;
         }
-        SharedLibraryEntry libEntry = new SharedLibraryEntry(path, apk, name,
-                version, type, declaringPackageName, declaringVersionCode);
-        versionedLib.put(version, libEntry);
+        SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, apk, name,
+                version, type, new VersionedPackage(declaringPackageName, declaringVersionCode),
+                null, null);
+        versionedLib.put(version, libraryInfo);
         return true;
     }
 
     private boolean removeSharedLibraryLPw(String name, long version) {
-        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+        LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
             return false;
         }
@@ -11210,12 +11223,12 @@
         if (libIdx < 0) {
             return false;
         }
-        SharedLibraryEntry libEntry = versionedLib.valueAt(libIdx);
+        SharedLibraryInfo libraryInfo = versionedLib.valueAt(libIdx);
         versionedLib.remove(version);
         if (versionedLib.size() <= 0) {
             mSharedLibraries.remove(name);
-            if (libEntry.info.getType() == SharedLibraryInfo.TYPE_STATIC) {
-                mStaticLibsByDeclaringPackage.remove(libEntry.info.getDeclaringPackage()
+            if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) {
+                mStaticLibsByDeclaringPackage.remove(libraryInfo.getDeclaringPackage()
                         .getPackageName());
             }
         }
@@ -12062,8 +12075,7 @@
     void cleanPackageDataStructuresLILPw(PackageParser.Package pkg, boolean chatty) {
         mComponentResolver.removeAllComponents(pkg, chatty);
 
-        final ArrayList<String> allPackageNames = new ArrayList<>(mPackages.keySet());
-        mPermissionManager.removeAllPermissions(pkg, allPackageNames, mPermissionCallback, chatty);
+        mPermissionManager.removeAllPermissions(pkg, chatty);
 
         final int instrumentationSize = pkg.instrumentation.size();
         StringBuilder r = null;
@@ -12291,28 +12303,15 @@
         return installReason;
     }
 
-    void installStage(String packageName, File stagedDir,
-            IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
-            String installerPackageName, int installerUid, UserHandle user,
-            PackageParser.SigningDetails signingDetails) {
+    void installStage(ActiveInstallSession activeInstallSession) {
         if (DEBUG_INSTANT) {
-            if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
-                Slog.d(TAG, "Ephemeral install of " + packageName);
+            if ((activeInstallSession.getSessionParams().installFlags
+                    & PackageManager.INSTALL_INSTANT_APP) != 0) {
+                Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
             }
         }
-        final VerificationInfo verificationInfo = new VerificationInfo(
-                sessionParams.originatingUri, sessionParams.referrerUri,
-                sessionParams.originatingUid, installerUid);
-
-        final OriginInfo origin = OriginInfo.fromStagedFile(stagedDir);
-
         final Message msg = mHandler.obtainMessage(INIT_COPY);
-        final int installReason = fixUpInstallReason(installerPackageName, installerUid,
-                sessionParams.installReason);
-        final InstallParams params = new InstallParams(origin, null, observer,
-                sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
-                verificationInfo, user, sessionParams.abiOverride,
-                sessionParams.grantedRuntimePermissions, signingDetails, installReason);
+        final InstallParams params = new InstallParams(activeInstallSession);
         params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
         msg.obj = params;
 
@@ -12324,6 +12323,22 @@
         mHandler.sendMessage(msg);
     }
 
+    void installStage(List<ActiveInstallSession> children)
+            throws PackageManagerException {
+        final Message msg = mHandler.obtainMessage(INIT_COPY);
+        final MultiPackageInstallParams params =
+                new MultiPackageInstallParams(UserHandle.ALL, children);
+        params.setTraceMethod("installStageMultiPackage")
+                .setTraceCookie(System.identityHashCode(params));
+        msg.obj = params;
+
+        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStageMultiPackage",
+                System.identityHashCode(msg.obj));
+        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+                System.identityHashCode(msg.obj));
+        mHandler.sendMessage(msg);
+    }
+
     private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting,
             int userId) {
         final boolean isSystem = isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
@@ -13501,88 +13516,113 @@
     }
 
     private void processPendingInstall(final InstallArgs args, final int currentStatus) {
-        // Queue up an async operation since the package installation may take a little while.
-        mHandler.post(new Runnable() {
-            public void run() {
-                mHandler.removeCallbacks(this);
-                 // Result object to be returned
-                PackageInstalledInfo res = new PackageInstalledInfo();
-                res.setReturnCode(currentStatus);
-                res.uid = -1;
-                res.pkg = null;
-                res.removedInfo = null;
-                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
-                    args.doPreInstall(res.returnCode);
-                    synchronized (mInstallLock) {
-                        installPackageTracedLI(args, res);
-                    }
-                    args.doPostInstall(res.returnCode, res.uid);
+        if (args.mMultiPackageInstallParams != null) {
+            args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);
+        } else {
+            PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
+            processInstallRequestsAsync(
+                    res.returnCode == PackageManager.INSTALL_SUCCEEDED,
+                    Collections.singletonList(new InstallRequest(args, res)));
+        }
+    }
+
+    // Queue up an async operation since the package installation may take a little while.
+    private void processInstallRequestsAsync(boolean success,
+            List<InstallRequest> installRequests) {
+        mHandler.post(() -> {
+            if (success) {
+                for (InstallRequest request : installRequests) {
+                    request.args.doPreInstall(request.installResult.returnCode);
                 }
-
-                // A restore should be performed at this point if (a) the install
-                // succeeded, (b) the operation is not an update, and (c) the new
-                // package has not opted out of backup participation.
-                final boolean update = res.removedInfo != null
-                        && res.removedInfo.removedPackage != null;
-                final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
-                boolean doRestore = !update
-                        && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
-
-                // Set up the post-install work request bookkeeping.  This will be used
-                // and cleaned up by the post-install event handling regardless of whether
-                // there's a restore pass performed.  Token values are >= 1.
-                int token;
-                if (mNextInstallToken < 0) mNextInstallToken = 1;
-                token = mNextInstallToken++;
-
-                PostInstallData data = new PostInstallData(args, res);
-                mRunningInstalls.put(token, data);
-                if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
-
-                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
-                    // Pass responsibility to the Backup Manager.  It will perform a
-                    // restore if appropriate, then pass responsibility back to the
-                    // Package Manager to run the post-install observer callbacks
-                    // and broadcasts.
-                    IBackupManager bm = IBackupManager.Stub.asInterface(
-                            ServiceManager.getService(Context.BACKUP_SERVICE));
-                    if (bm != null) {
-                        if (DEBUG_INSTALL) Log.v(TAG, "token " + token
-                                + " to BM for possible restore");
-                        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
-                        try {
-                            // TODO: http://b/22388012
-                            if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
-                                bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
-                            } else {
-                                doRestore = false;
-                            }
-                        } catch (RemoteException e) {
-                            // can't happen; the backup manager is local
-                        } catch (Exception e) {
-                            Slog.e(TAG, "Exception trying to enqueue restore", e);
-                            doRestore = false;
-                        }
-                    } else {
-                        Slog.e(TAG, "Backup Manager not found!");
-                        doRestore = false;
-                    }
+                synchronized (mInstallLock) {
+                    installPackagesTracedLI(installRequests);
                 }
-
-                if (!doRestore) {
-                    // No restore possible, or the Backup Manager was mysteriously not
-                    // available -- just fire the post-install work request directly.
-                    if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
-
-                    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);
-
-                    Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
-                    mHandler.sendMessage(msg);
+                for (InstallRequest request : installRequests) {
+                    request.args.doPostInstall(
+                            request.installResult.returnCode, request.installResult.uid);
                 }
             }
+            for (InstallRequest request : installRequests) {
+                resolvePackageInstalledInfo(request.args,
+                        request.installResult);
+            }
         });
     }
 
+    private PackageInstalledInfo createPackageInstalledInfo(
+            int currentStatus) {
+        PackageInstalledInfo res = new PackageInstalledInfo();
+        res.setReturnCode(currentStatus);
+        res.uid = -1;
+        res.pkg = null;
+        res.removedInfo = null;
+        return res;
+    }
+
+    private void resolvePackageInstalledInfo(InstallArgs args, PackageInstalledInfo res) {
+        // A restore should be performed at this point if (a) the install
+        // succeeded, (b) the operation is not an update, and (c) the new
+        // package has not opted out of backup participation.
+        final boolean update = res.removedInfo != null
+                && res.removedInfo.removedPackage != null;
+        final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
+        boolean doRestore = !update
+                && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
+
+        // Set up the post-install work request bookkeeping.  This will be used
+        // and cleaned up by the post-install event handling regardless of whether
+        // there's a restore pass performed.  Token values are >= 1.
+        int token;
+        if (mNextInstallToken < 0) mNextInstallToken = 1;
+        token = mNextInstallToken++;
+
+        PostInstallData data = new PostInstallData(args, res);
+        mRunningInstalls.put(token, data);
+        if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
+
+        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
+            // Pass responsibility to the Backup Manager.  It will perform a
+            // restore if appropriate, then pass responsibility back to the
+            // Package Manager to run the post-install observer callbacks
+            // and broadcasts.
+            IBackupManager bm = IBackupManager.Stub.asInterface(
+                    ServiceManager.getService(Context.BACKUP_SERVICE));
+            if (bm != null) {
+                if (DEBUG_INSTALL) {
+                    Log.v(TAG, "token " + token + " to BM for possible restore");
+                }
+                Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
+                try {
+                    // TODO: http://b/22388012
+                    if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
+                        bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
+                    } else {
+                        doRestore = false;
+                    }
+                } catch (RemoteException e) {
+                    // can't happen; the backup manager is local
+                } catch (Exception e) {
+                    Slog.e(TAG, "Exception trying to enqueue restore", e);
+                    doRestore = false;
+                }
+            } else {
+                Slog.e(TAG, "Backup Manager not found!");
+                doRestore = false;
+            }
+        }
+
+        if (!doRestore) {
+            // No restore possible, or the Backup Manager was mysteriously not
+            // available -- just fire the post-install work request directly.
+            if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
+
+            Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);
+
+            Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
+            mHandler.sendMessage(msg);
+        }
+    }
+
     /**
      * Callback from PackageSettings whenever an app is first transitioned out of the
      * 'stopped' state.  Normally we just issue the broadcast, but we can't do that if
@@ -13770,7 +13810,83 @@
         }
     }
 
+    /**
+     * Container for a multi-package install which refers to all install sessions and args being
+     * committed together.
+     */
+    class MultiPackageInstallParams extends HandlerParams {
+
+        private int mRet = INSTALL_SUCCEEDED;
+        @NonNull
+        private final ArrayList<InstallParams> mChildParams;
+        @NonNull
+        private final Map<InstallArgs, Integer> mVerifiedState;
+
+        MultiPackageInstallParams(
+                @NonNull UserHandle user,
+                @NonNull List<ActiveInstallSession> activeInstallSessions)
+                throws PackageManagerException {
+            super(user);
+            if (activeInstallSessions.size() == 0) {
+                throw new PackageManagerException("No child sessions found!");
+            }
+            mChildParams = new ArrayList<>(activeInstallSessions.size());
+            for (int i = 0; i < activeInstallSessions.size(); i++) {
+                final InstallParams childParams = new InstallParams(activeInstallSessions.get(i));
+                childParams.mParentInstallParams = this;
+                this.mChildParams.add(childParams);
+            }
+            this.mVerifiedState = new ArrayMap<>(mChildParams.size());
+        }
+
+        @Override
+        void handleStartCopy() {
+            for (InstallParams params : mChildParams) {
+                params.handleStartCopy();
+                if (params.mRet != INSTALL_SUCCEEDED) {
+                    mRet = params.mRet;
+                    break;
+                }
+            }
+        }
+
+        @Override
+        void handleReturnCode() {
+            for (InstallParams params : mChildParams) {
+                params.handleReturnCode();
+                if (params.mRet != INSTALL_SUCCEEDED) {
+                    mRet = params.mRet;
+                    break;
+                }
+            }
+        }
+
+        void tryProcessInstallRequest(InstallArgs args, int currentStatus) {
+            mVerifiedState.put(args, currentStatus);
+            boolean success = true;
+            if (mVerifiedState.size() != mChildParams.size()) {
+                return;
+            }
+            for (Integer status : mVerifiedState.values()) {
+                if (status == PackageManager.INSTALL_UNKNOWN) {
+                    return;
+                } else if (status != PackageManager.INSTALL_SUCCEEDED) {
+                    success = false;
+                    break;
+                }
+            }
+            final List<InstallRequest> installRequests = new ArrayList<>(mVerifiedState.size());
+            for (Map.Entry<InstallArgs, Integer> entry : mVerifiedState.entrySet()) {
+                installRequests.add(new InstallRequest(entry.getKey(),
+                        createPackageInstalledInfo(entry.getValue())));
+            }
+            processInstallRequestsAsync(success, installRequests);
+        }
+    }
+
     class InstallParams extends HandlerParams {
+        // TODO: see if we can collapse this into ActiveInstallSession
+
         final OriginInfo origin;
         final MoveInfo move;
         final IPackageInstallObserver2 observer;
@@ -13778,17 +13894,20 @@
         final String installerPackageName;
         final String volumeUuid;
         private InstallArgs mArgs;
-        private int mRet;
+        int mRet;
         final String packageAbiOverride;
         final String[] grantedRuntimePermissions;
         final VerificationInfo verificationInfo;
         final PackageParser.SigningDetails signingDetails;
         final int installReason;
+        @Nullable
+        MultiPackageInstallParams mParentInstallParams;
 
         InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
                 int installFlags, String installerPackageName, String volumeUuid,
                 VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
-                String[] grantedPermissions, PackageParser.SigningDetails signingDetails, int installReason) {
+                String[] grantedPermissions, PackageParser.SigningDetails signingDetails,
+                int installReason) {
             super(user);
             this.origin = origin;
             this.move = move;
@@ -13803,6 +13922,34 @@
             this.installReason = installReason;
         }
 
+        InstallParams(ActiveInstallSession activeInstallSession) {
+            super(activeInstallSession.getUser());
+            if (DEBUG_INSTANT) {
+                if ((activeInstallSession.getSessionParams().installFlags
+                        & PackageManager.INSTALL_INSTANT_APP) != 0) {
+                    Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
+                }
+            }
+            verificationInfo = new VerificationInfo(
+                    activeInstallSession.getSessionParams().originatingUri,
+                    activeInstallSession.getSessionParams().referrerUri,
+                    activeInstallSession.getSessionParams().originatingUid,
+                    activeInstallSession.getInstallerUid());
+            origin = OriginInfo.fromStagedFile(activeInstallSession.getStagedDir());
+            move = null;
+            installReason = fixUpInstallReason(activeInstallSession.getInstallerPackageName(),
+                    activeInstallSession.getInstallerUid(),
+                    activeInstallSession.getSessionParams().installReason);
+            observer = activeInstallSession.getObserver();
+            installFlags = activeInstallSession.getSessionParams().installFlags;
+            installerPackageName = activeInstallSession.getInstallerPackageName();
+            volumeUuid = activeInstallSession.getSessionParams().volumeUuid;
+            packageAbiOverride = activeInstallSession.getSessionParams().abiOverride;
+            grantedRuntimePermissions =
+                    activeInstallSession.getSessionParams().grantedRuntimePermissions;
+            signingDetails = activeInstallSession.getSigningDetails();
+        }
+
         @Override
         public String toString() {
             return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
@@ -14224,6 +14371,7 @@
         final int traceCookie;
         final PackageParser.SigningDetails signingDetails;
         final int installReason;
+        @Nullable final MultiPackageInstallParams mMultiPackageInstallParams;
 
         // The list of instruction sets supported by this app. This is currently
         // only used during the rmdex() phase to clean up resources. We can get rid of this
@@ -14234,8 +14382,9 @@
                 int installFlags, String installerPackageName, String volumeUuid,
                 UserHandle user, String[] instructionSets,
                 String abiOverride, String[] installGrantPermissions,
-                String traceMethod, int traceCookie, PackageParser.SigningDetails signingDetails,
-                int installReason) {
+                String traceMethod, int traceCookie, SigningDetails signingDetails,
+                int installReason,
+                MultiPackageInstallParams multiPackageInstallParams) {
             this.origin = origin;
             this.move = move;
             this.installFlags = installFlags;
@@ -14250,6 +14399,7 @@
             this.traceCookie = traceCookie;
             this.signingDetails = signingDetails;
             this.installReason = installReason;
+            this.mMultiPackageInstallParams = multiPackageInstallParams;
         }
 
         abstract int copyApk();
@@ -14345,7 +14495,7 @@
                     params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
                     params.grantedRuntimePermissions,
                     params.traceMethod, params.traceCookie, params.signingDetails,
-                    params.installReason);
+                    params.installReason, params.mParentInstallParams);
             if (isFwdLocked()) {
                 throw new IllegalArgumentException("Forward locking only supported in ASEC");
             }
@@ -14355,7 +14505,7 @@
         FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
             super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets,
                     null, null, null, 0, PackageParser.SigningDetails.UNKNOWN,
-                    PackageManager.INSTALL_REASON_UNKNOWN);
+                    PackageManager.INSTALL_REASON_UNKNOWN, null /* parent */);
             this.codeFile = (codePath != null) ? new File(codePath) : null;
             this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
         }
@@ -14547,7 +14697,7 @@
                     params.getUser(), null /* instruction sets */, params.packageAbiOverride,
                     params.grantedRuntimePermissions,
                     params.traceMethod, params.traceCookie, params.signingDetails,
-                    params.installReason);
+                    params.installReason, params.mParentInstallParams);
         }
 
         int copyApk() {
@@ -14952,10 +15102,10 @@
     }
 
     @GuardedBy({"mInstallLock", "mPackages"})
-    private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo installResult) {
+    private void installPackagesTracedLI(List<InstallRequest> requests) {
         try {
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");
-            installPackagesLI(Collections.singletonList(new InstallRequest(args, installResult)));
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
+            installPackagesLI(requests);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -15148,14 +15298,8 @@
                             pkgList.add(oldPackage.applicationInfo.packageName);
                             sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
                         }
-
-                        clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
-                                | StorageManager.FLAG_STORAGE_CE
-                                | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
                     }
 
-
-
                     // Update the in-memory copy of the previous code paths.
                     PackageSetting ps1 = mSettings.mPackages.get(
                             reconciledPkg.prepareResult.existingPackage.packageName);
@@ -15301,7 +15445,7 @@
                             request.installResult.setError(
                                     PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
                                     "Duplicate package " + result.pkgSetting.pkg.packageName
-                                            + " in atomic install request.");
+                                            + " in multi-package install request.");
                             return;
                         }
                     }
@@ -15359,7 +15503,8 @@
 
     /**
      * On successful install, executes remaining steps after commit completes and the package lock
-     * is released.
+     * is released. These are typically more expensive or require calls to installd, which often
+     * locks on {@link #mPackages}.
      */
     private void executePostCommitSteps(CommitRequest commitRequest) {
         for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
@@ -15847,9 +15992,9 @@
                 // the package setting for the latest library version.
                 PackageSetting signatureCheckPs = ps;
                 if (pkg.applicationInfo.isStaticSharedLibrary()) {
-                    SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
-                    if (libraryEntry != null) {
-                        signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
+                    SharedLibraryInfo libraryInfo = getLatestSharedLibraVersionLPr(pkg);
+                    if (libraryInfo != null) {
+                        signatureCheckPs = mSettings.getPackageLPr(libraryInfo.getPackageName());
                     }
                 }
 
@@ -16088,7 +16233,6 @@
         try {
             final PackageParser.Package existingPackage;
             String renamedPackage = null;
-            boolean clearCodeCache = false;
             boolean sysPkg = false;
             String targetVolumeUuid = volumeUuid;
             int targetScanFlags = scanFlags;
@@ -16309,7 +16453,6 @@
                         Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
                                 + ", old=" + oldPackage);
                     }
-                    clearCodeCache = true;
                     res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
                     pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
                             ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
@@ -16365,7 +16508,7 @@
             shouldCloseFreezerBeforeReturn = false;
             return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
                     args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
-                    clearCodeCache, sysPkg, renamedPackage, freezer);
+                    replace /* clearCodeCache */, sysPkg, renamedPackage, freezer);
         } finally {
             if (shouldCloseFreezerBeforeReturn) {
                 freezer.close();
@@ -16727,7 +16870,7 @@
         packageName = normalizedPackageName != null ? normalizedPackageName : packageName;
 
         // Is this a static library?
-        LongSparseArray<SharedLibraryEntry> versionedLib =
+        LongSparseArray<SharedLibraryInfo> versionedLib =
                 mStaticLibsByDeclaringPackage.get(packageName);
         if (versionedLib == null || versionedLib.size() <= 0) {
             return packageName;
@@ -16739,7 +16882,7 @@
         if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID
                 && callingAppId != Process.ROOT_UID) {
             versionsCallerCanSee = new LongSparseLongArray();
-            String libName = versionedLib.valueAt(0).info.getName();
+            String libName = versionedLib.valueAt(0).getName();
             String[] uidPackages = getPackagesForUid(Binder.getCallingUid());
             if (uidPackages != null) {
                 for (String uidPackage : uidPackages) {
@@ -16759,29 +16902,29 @@
         }
 
         // Find the version the caller can see and the app version code
-        SharedLibraryEntry highestVersion = null;
+        SharedLibraryInfo highestVersion = null;
         final int versionCount = versionedLib.size();
         for (int i = 0; i < versionCount; i++) {
-            SharedLibraryEntry libEntry = versionedLib.valueAt(i);
+            SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
             if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey(
-                    libEntry.info.getLongVersion()) < 0) {
+                    libraryInfo.getLongVersion()) < 0) {
                 continue;
             }
-            final long libVersionCode = libEntry.info.getDeclaringPackage().getLongVersionCode();
+            final long libVersionCode = libraryInfo.getDeclaringPackage().getLongVersionCode();
             if (versionCode != PackageManager.VERSION_CODE_HIGHEST) {
                 if (libVersionCode == versionCode) {
-                    return libEntry.apk;
+                    return libraryInfo.getPackageName();
                 }
             } else if (highestVersion == null) {
-                highestVersion = libEntry;
-            } else if (libVersionCode  > highestVersion.info
+                highestVersion = libraryInfo;
+            } else if (libVersionCode  > highestVersion
                     .getDeclaringPackage().getLongVersionCode()) {
-                highestVersion = libEntry;
+                highestVersion = libraryInfo;
             }
         }
 
         if (highestVersion != null) {
-            return highestVersion.apk;
+            return highestVersion.getPackageName();
         }
 
         return packageName;
@@ -16943,19 +17086,19 @@
             allUsers = sUserManager.getUserIds();
 
             if (pkg != null && pkg.staticSharedLibName != null) {
-                SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(pkg.staticSharedLibName,
+                SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(pkg.staticSharedLibName,
                         pkg.staticSharedLibVersion);
-                if (libEntry != null) {
+                if (libraryInfo != null) {
                     for (int currUserId : allUsers) {
                         if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) {
                             continue;
                         }
                         List<VersionedPackage> libClientPackages = getPackagesUsingSharedLibraryLPr(
-                                libEntry.info, 0, currUserId);
+                                libraryInfo, 0, currUserId);
                         if (!ArrayUtils.isEmpty(libClientPackages)) {
                             Slog.w(TAG, "Not removing package " + pkg.manifestPackageName
-                                    + " hosting lib " + libEntry.info.getName() + " version "
-                                    + libEntry.info.getLongVersion() + " used by " + libClientPackages
+                                    + " hosting lib " + libraryInfo.getName() + " version "
+                                    + libraryInfo.getLongVersion() + " used by " + libClientPackages
                                     + " for user " + currUserId);
                             return PackageManager.DELETE_FAILED_USED_SHARED_LIBRARY;
                         }
@@ -20224,14 +20367,14 @@
                 final Iterator<String> it = mSharedLibraries.keySet().iterator();
                 while (it.hasNext()) {
                     String libName = it.next();
-                    LongSparseArray<SharedLibraryEntry> versionedLib
+                    LongSparseArray<SharedLibraryInfo> versionedLib
                             = mSharedLibraries.get(libName);
                     if (versionedLib == null) {
                         continue;
                     }
                     final int versionCount = versionedLib.size();
                     for (int i = 0; i < versionCount; i++) {
-                        SharedLibraryEntry libEntry = versionedLib.valueAt(i);
+                        SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
                         if (!checkin) {
                             if (!printedHeader) {
                                 if (dumpState.onTitlePrinted())
@@ -20243,19 +20386,19 @@
                         } else {
                             pw.print("lib,");
                         }
-                        pw.print(libEntry.info.getName());
-                        if (libEntry.info.isStatic()) {
-                            pw.print(" version=" + libEntry.info.getLongVersion());
+                        pw.print(libraryInfo.getName());
+                        if (libraryInfo.isStatic()) {
+                            pw.print(" version=" + libraryInfo.getLongVersion());
                         }
                         if (!checkin) {
                             pw.print(" -> ");
                         }
-                        if (libEntry.path != null) {
+                        if (libraryInfo.getPath() != null) {
                             pw.print(" (jar) ");
-                            pw.print(libEntry.path);
+                            pw.print(libraryInfo.getPath());
                         } else {
                             pw.print(" (apk) ");
-                            pw.print(libEntry.apk);
+                            pw.print(libraryInfo.getPackageName());
                         }
                         pw.println();
                     }
@@ -20591,22 +20734,24 @@
         final int count = mSharedLibraries.size();
         for (int i = 0; i < count; i++) {
             final String libName = mSharedLibraries.keyAt(i);
-            LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName);
+            LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName);
             if (versionedLib == null) {
                 continue;
             }
             final int versionCount = versionedLib.size();
             for (int j = 0; j < versionCount; j++) {
-                final SharedLibraryEntry libEntry = versionedLib.valueAt(j);
+                final SharedLibraryInfo libraryInfo = versionedLib.valueAt(j);
                 final long sharedLibraryToken =
                         proto.start(PackageServiceDumpProto.SHARED_LIBRARIES);
-                proto.write(PackageServiceDumpProto.SharedLibraryProto.NAME, libEntry.info.getName());
-                final boolean isJar = (libEntry.path != null);
+                proto.write(PackageServiceDumpProto.SharedLibraryProto.NAME, libraryInfo.getName());
+                final boolean isJar = (libraryInfo.getPath() != null);
                 proto.write(PackageServiceDumpProto.SharedLibraryProto.IS_JAR, isJar);
                 if (isJar) {
-                    proto.write(PackageServiceDumpProto.SharedLibraryProto.PATH, libEntry.path);
+                    proto.write(PackageServiceDumpProto.SharedLibraryProto.PATH,
+                            libraryInfo.getPath());
                 } else {
-                    proto.write(PackageServiceDumpProto.SharedLibraryProto.APK, libEntry.apk);
+                    proto.write(PackageServiceDumpProto.SharedLibraryProto.APK,
+                            libraryInfo.getPackageName());
                 }
                 proto.end(sharedLibraryToken);
             }
@@ -22941,6 +23086,20 @@
         }
 
         @Override
+        public String getSharedUserIdForPackage(String packageName) {
+            synchronized (mPackages) {
+                return getSharedUserIdForPackageLocked(packageName);
+            }
+        }
+
+        @Override
+        public String[] getPackagesForSharedUserId(String sharedUserId, int userId) {
+            synchronized (mPackages) {
+                return getPackagesForSharedUserIdLocked(sharedUserId, userId);
+            }
+        }
+
+        @Override
         public boolean isOnlyCoreApps() {
             return PackageManagerService.this.isOnlyCoreApps();
         }
@@ -22952,6 +23111,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private SparseArray<String> getAppsWithSharedUserIdsLocked() {
         final SparseArray<String> sharedUserIds = new SparseArray<>();
         synchronized (mPackages) {
@@ -22962,6 +23122,38 @@
         return sharedUserIds;
     }
 
+    @GuardedBy("mPackages")
+    private String getSharedUserIdForPackageLocked(String packageName) {
+        final PackageSetting ps = mSettings.mPackages.get(packageName);
+        return (ps != null && ps.isSharedUser()) ? ps.sharedUser.name : null;
+    }
+
+    @GuardedBy("mPackages")
+    private String[] getPackagesForSharedUserIdLocked(String sharedUserId, int userId) {
+        try {
+            final SharedUserSetting sus = mSettings.getSharedUserLPw(
+                    sharedUserId, 0, 0, false);
+            if (sus == null) {
+                return EmptyArray.STRING;
+            }
+            String[] res = new String[sus.packages.size()];
+            final Iterator<PackageSetting> it = sus.packages.iterator();
+            int i = 0;
+            while (it.hasNext()) {
+                PackageSetting ps = it.next();
+                if (ps.getInstalled(userId)) {
+                    res[i++] = ps.name;
+                } else {
+                    res = ArrayUtils.removeElement(String.class, res, res[i]);
+                }
+            }
+            return res;
+        } catch (PackageManagerException e) {
+            // Should not happen
+        }
+        return EmptyArray.STRING;
+    }
+
     @Override
     public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) {
         enforceSystemOrPhoneCaller("grantPermissionsToEnabledCarrierApps");
@@ -23325,6 +23517,62 @@
 
         return mProtectedPackages.isPackageStateProtected(userId, packageName);
     }
+
+    static class ActiveInstallSession {
+        private final String mPackageName;
+        private final File mStagedDir;
+        private final IPackageInstallObserver2 mObserver;
+        private final PackageInstaller.SessionParams mSessionParams;
+        private final String mInstallerPackageName;
+        private final int mInstallerUid;
+        private final UserHandle mUser;
+        private final SigningDetails mSigningDetails;
+
+        ActiveInstallSession(String packageName, File stagedDir, IPackageInstallObserver2 observer,
+                PackageInstaller.SessionParams sessionParams, String installerPackageName,
+                int installerUid, UserHandle user, SigningDetails signingDetails) {
+            mPackageName = packageName;
+            mStagedDir = stagedDir;
+            mObserver = observer;
+            mSessionParams = sessionParams;
+            mInstallerPackageName = installerPackageName;
+            mInstallerUid = installerUid;
+            mUser = user;
+            mSigningDetails = signingDetails;
+        }
+
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        public File getStagedDir() {
+            return mStagedDir;
+        }
+
+        public IPackageInstallObserver2 getObserver() {
+            return mObserver;
+        }
+
+        public PackageInstaller.SessionParams getSessionParams() {
+            return mSessionParams;
+        }
+
+        public String getInstallerPackageName() {
+            return mInstallerPackageName;
+        }
+
+        public int getInstallerUid() {
+            return mInstallerUid;
+        }
+
+        public UserHandle getUser() {
+            return mUser;
+        }
+
+        public SigningDetails getSigningDetails() {
+            return mSigningDetails;
+        }
+    }
 }
 
 interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index e25cca4..31711e5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -173,6 +173,8 @@
                     return runSetInstallLocation();
                 case "get-install-location":
                     return runGetInstallLocation();
+                case "install-add-session":
+                    return runInstallAddSession();
                 case "move-package":
                     return runMovePackage();
                 case "move-primary-storage":
@@ -920,7 +922,10 @@
                 pw.println("Error: must either specify a package size or an APK file");
                 return 1;
             }
-            if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
+            final boolean isApex =
+                    (params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
+            String splitName = "base." + (isApex ? "apex" : "apk");
+            if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName,
                     false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                 return 1;
             }
@@ -980,6 +985,23 @@
         return doWriteSplit(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
     }
 
+    private int runInstallAddSession() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final int parentSessionId = Integer.parseInt(getNextArg());
+
+        List<Integer> otherSessionIds = new ArrayList<>();
+        String opt;
+        while ((opt = getNextArg()) != null) {
+            otherSessionIds.add(Integer.parseInt(opt));
+        }
+        if (otherSessionIds.size() == 0) {
+            pw.println("Error: At least two sessions are required.");
+            return 1;
+        }
+        return doInstallAddSession(parentSessionId, ArrayUtils.convertToIntArray(otherSessionIds),
+                true /*logSuccess*/);
+    }
+
     private int runInstallRemove() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
 
@@ -2262,6 +2284,12 @@
                 case "--force-sdk":
                     sessionParams.installFlags |= PackageManager.INSTALL_FORCE_SDK;
                     break;
+                case "--apex":
+                    sessionParams.installFlags |= PackageManager.INSTALL_APEX;
+                    break;
+                case "--multi-package":
+                    sessionParams.setMultiPackage();
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown option " + opt);
             }
@@ -2494,6 +2522,30 @@
         }
     }
 
+    private int doInstallAddSession(int parentId, int[] sessionIds, boolean logSuccess)
+            throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        PackageInstaller.Session session = null;
+        try {
+            session = new PackageInstaller.Session(
+                    mInterface.getPackageInstaller().openSession(parentId));
+            if (!session.isMultiPackage()) {
+                getErrPrintWriter().println(
+                        "Error: parent session ID is not a multi-package session");
+                return 1;
+            }
+            for (int i = 0; i < sessionIds.length; i++) {
+                session.addChildSessionId(sessionIds[i]);
+            }
+            if (logSuccess) {
+                pw.println("Success");
+            }
+            return 0;
+        } finally {
+            IoUtils.closeQuietly(session);
+        }
+    }
+
     private int doRemoveSplit(int sessionId, String splitName, boolean logSuccess)
             throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
@@ -2515,24 +2567,26 @@
         }
     }
 
-    private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
+    private int doCommitSession(int sessionId, boolean logSuccess)
+            throws RemoteException {
+
         final PrintWriter pw = getOutPrintWriter();
         PackageInstaller.Session session = null;
         try {
             session = new PackageInstaller.Session(
                     mInterface.getPackageInstaller().openSession(sessionId));
-
-            // Sanity check that all .dm files match an apk.
-            // (The installer does not support standalone .dm files and will not process them.)
-            try {
-                DexMetadataHelper.validateDexPaths(session.getNames());
-            } catch (IllegalStateException | IOException e) {
-                pw.println("Warning [Could not validate the dex paths: " + e.getMessage() + "]");
+            if (!session.isMultiPackage()) {
+                // Sanity check that all .dm files match an apk.
+                // (The installer does not support standalone .dm files and will not process them.)
+                try {
+                    DexMetadataHelper.validateDexPaths(session.getNames());
+                } catch (IllegalStateException | IOException e) {
+                    pw.println(
+                            "Warning [Could not validate the dex paths: " + e.getMessage() + "]");
+                }
             }
-
             final LocalIntentReceiver receiver = new LocalIntentReceiver();
             session.commit(receiver.getIntentSender());
-
             final Intent result = receiver.getResult();
             final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                     PackageInstaller.STATUS_FAILURE);
@@ -2803,6 +2857,7 @@
         pw.println("       [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
         pw.println("       [--preload] [--instantapp] [--full] [--dont-kill]");
         pw.println("       [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
+        pw.println("       [--multi-package]");
         pw.println("    Like \"install\", but starts an install session.  Use \"install-write\"");
         pw.println("    to push data into the session, and \"install-commit\" to finish.");
         pw.println("");
@@ -2811,6 +2866,9 @@
         pw.println("    will be read from stdin.  Options are:");
         pw.println("      -S: size in bytes of package, required for stdin");
         pw.println("");
+        pw.println("  install-add-session MULTI_PACKAGE_SESSION_ID CHILD_SESSION_IDs");
+        pw.println("    Add one or more session IDs to a multi-package session.");
+        pw.println("");
         pw.println("  install-commit SESSION_ID");
         pw.println("    Commit the given active install session, installing the app.");
         pw.println("");
diff --git a/services/core/java/com/android/server/pm/PackageSessionProvider.java b/services/core/java/com/android/server/pm/PackageSessionProvider.java
new file mode 100644
index 0000000..af11e77
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageSessionProvider.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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.server.pm;
+
+/** Provides access to individual sessions managed by the install service */
+public interface PackageSessionProvider {
+
+    /**
+     * Get the sessions for the provided session IDs. Null will be returned for session IDs that
+     * do not exist.
+     */
+    PackageInstallerSession getSession(int sessionId);
+
+}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index e2818b7..4c93441 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -20,6 +20,7 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -256,6 +257,7 @@
     private static final String ATTR_USER_SET = "set";
     private static final String ATTR_USER_FIXED = "fixed";
     private static final String ATTR_REVOKE_ON_UPGRADE = "rou";
+    private static final String ATTR_REVOKE_WHEN_REQUESTED = "rwr";
 
     // Flag mask of restored permission grants that are applied at install time
     private static final int USER_RUNTIME_GRANT_MASK =
@@ -5011,6 +5013,9 @@
                                 if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
                                     pw.print(" revoke_on_upgrade");
                                 }
+                                if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
+                                    pw.print(" revoke_when_requested");
+                                }
                                 pw.println();
                             }
                         }
@@ -5326,6 +5331,11 @@
                                     if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
                                         serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
                                     }
+                                    if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)
+                                            != 0) {
+                                        serializer.attribute(null, ATTR_REVOKE_WHEN_REQUESTED,
+                                                "true");
+                                    }
                                     serializer.endTag(null, TAG_PERMISSION_ENTRY);
                                 }
                                 serializer.endTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
@@ -5512,6 +5522,10 @@
                         if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) {
                             permBits |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
                         }
+                        if ("true".equals(parser.getAttributeValue(null,
+                                ATTR_REVOKE_WHEN_REQUESTED))) {
+                            permBits |= FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+                        }
 
                         if (isGranted || permBits != 0) {
                             rememberRestoredUserGrantLPr(pkgName, permName, isGranted, permBits, userId);
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 392d4d8..753c283 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -30,6 +30,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings.Global;
+import android.util.Log;
 import android.util.Slog;
 import android.util.jar.StrictJarFile;
 
@@ -74,7 +75,7 @@
     private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST =
             "pm.dexopt.priv-apps-oob-list";
 
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final Context mContext;
 
@@ -192,6 +193,16 @@
         String[] classLoaderContexts = DexoptUtils.processContextForDexLoad(
                 classLoaderNames, classPaths);
 
+        // A null classLoaderContexts means that there are unsupported class loaders in the
+        // chain.
+        if (classLoaderContexts == null) {
+            if (DEBUG) {
+                Slog.i(TAG, loadingAppInfo.packageName +
+                        " uses unsupported class loader in " + classLoaderNames);
+            }
+            return;
+        }
+
         int dexPathIndex = 0;
         for (String dexPath : dexPathsToRegister) {
             // Find the owning package name.
@@ -219,14 +230,10 @@
                 }
 
                 // Record dex file usage. If the current usage is a new pattern (e.g. new secondary,
-                // or UsedBytOtherApps), record will return true and we trigger an async write
+                // or UsedByOtherApps), record will return true and we trigger an async write
                 // to disk to make sure we don't loose the data in case of a reboot.
 
-                // A null classLoaderContexts means that there are unsupported class loaders in the
-                // chain.
-                String classLoaderContext = classLoaderContexts == null
-                        ? PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT
-                        : classLoaderContexts[dexPathIndex];
+                String classLoaderContext = classLoaderContexts[dexPathIndex];
                 if (mPackageDexUsage.record(searchResult.mOwningPackageName,
                         dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
                         loadingAppInfo.packageName, classLoaderContext)) {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 602ce3b..86f7380 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -21,6 +21,7 @@
 import android.os.Build;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastPrintWriter;
 import com.android.server.pm.AbstractStatsBase;
 import com.android.server.pm.PackageManagerServiceUtils;
@@ -78,14 +79,16 @@
     // skip optimizations on that dex files.
     /*package*/ static final String VARIABLE_CLASS_LOADER_CONTEXT =
             "=VariableClassLoaderContext=";
-    // The marker used for unsupported class loader contexts.
-    /*package*/ static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
-            "=UnsupportedClassLoaderContext=";
     // The markers used for unknown class loader contexts. This can happen if the dex file was
     // recorded in a previous version and we didn't have a chance to update its usage.
     /*package*/ static final String UNKNOWN_CLASS_LOADER_CONTEXT =
             "=UnknownClassLoaderContext=";
 
+    // The marker used for unsupported class loader contexts (no longer written, may occur in old
+    // files so discarded on read).
+    private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
+            "=UnsupportedClassLoaderContext=";
+
     // Map which structures the information we have on a package.
     // Maps package name to package data (which stores info about UsedByOtherApps and
     // secondary dex files.).
@@ -365,6 +368,12 @@
                 Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
                 String classLoaderContext = maybeReadClassLoaderContext(in, version);
 
+                if (UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(classLoaderContext)) {
+                    // We used to record use of unsupported class loaders, but we no longer do.
+                    // Discard such entries; they will be deleted when we next write the file.
+                    continue;
+                }
+
                 int ownerUserId = Integer.parseInt(elems[0]);
                 boolean isUsedByOtherApps = readBoolean(elems[1]);
                 DexUseInfo dexUseInfo = new DexUseInfo(isUsedByOtherApps, ownerUserId,
@@ -709,13 +718,13 @@
         //      the compiled code will be private.
         private boolean mUsedByOtherAppsBeforeUpgrade;
 
-        public PackageUseInfo() {
+        /*package*/ PackageUseInfo() {
             mCodePathsUsedByOtherApps = new HashMap<>();
             mDexUseInfoMap = new HashMap<>();
         }
 
         // Creates a deep copy of the `other`.
-        public PackageUseInfo(PackageUseInfo other) {
+        private PackageUseInfo(PackageUseInfo other) {
             mCodePathsUsedByOtherApps = new HashMap<>();
             for (Map.Entry<String, Set<String>> e : other.mCodePathsUsedByOtherApps.entrySet()) {
                 mCodePathsUsedByOtherApps.put(e.getKey(), new HashSet<>(e.getValue()));
@@ -796,8 +805,9 @@
         // Packages who load this dex file.
         private final Set<String> mLoadingPackages;
 
-        public DexUseInfo(boolean isUsedByOtherApps, int ownerUserId, String classLoaderContext,
-                String loaderIsa) {
+        @VisibleForTesting
+        /* package */ DexUseInfo(boolean isUsedByOtherApps, int ownerUserId,
+                String classLoaderContext, String loaderIsa) {
             mIsUsedByOtherApps = isUsedByOtherApps;
             mOwnerUserId = ownerUserId;
             mClassLoaderContext = classLoaderContext;
@@ -809,7 +819,7 @@
         }
 
         // Creates a deep copy of the `other`.
-        public DexUseInfo(DexUseInfo other) {
+        private DexUseInfo(DexUseInfo other) {
             mIsUsedByOtherApps = other.mIsUsedByOtherApps;
             mOwnerUserId = other.mOwnerUserId;
             mClassLoaderContext = other.mClassLoaderContext;
@@ -827,11 +837,7 @@
             if (UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext)) {
                 // Can happen if we read a previous version.
                 mClassLoaderContext = dexUseInfo.mClassLoaderContext;
-            } else if (UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(dexUseInfo.mClassLoaderContext)) {
-                // We detected an unsupported context.
-                mClassLoaderContext = UNSUPPORTED_CLASS_LOADER_CONTEXT;
-            } else if (!UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext) &&
-                    !Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
+            } else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
                 // We detected a context change.
                 mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT;
             }
@@ -846,7 +852,7 @@
             return mIsUsedByOtherApps;
         }
 
-        public int getOwnerUserId() {
+        /* package */ int getOwnerUserId() {
             return mOwnerUserId;
         }
 
@@ -860,17 +866,15 @@
 
         public String getClassLoaderContext() { return mClassLoaderContext; }
 
-        public boolean isUnsupportedClassLoaderContext() {
-            return UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
-        }
-
-        public boolean isUnknownClassLoaderContext() {
+        @VisibleForTesting
+        /* package */ boolean isUnknownClassLoaderContext() {
             // The class loader context may be unknown if we loaded the data from a previous version
             // which didn't save the context.
             return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
         }
 
-        public boolean isVariableClassLoaderContext() {
+        @VisibleForTesting
+        /* package */ boolean isVariableClassLoaderContext() {
             return VARIABLE_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
         }
     }
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 45cb477..32b2bf0 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.AppOpsManager;
 import android.app.DownloadManager;
 import android.app.SearchManager;
 import android.app.admin.DevicePolicyManager;
@@ -40,7 +41,6 @@
 import android.content.pm.ResolveInfo;
 import android.media.RingtoneManager;
 import android.net.Uri;
-import android.os.Binder;
 import android.os.Build;
 import android.os.Environment;
 import android.os.Handler;
@@ -344,40 +344,43 @@
     @SafeVarargs
     private final void grantIgnoringSystemPackage(String packageName, int userId,
             Set<String>... permissionGroups) {
-        grantPermissionsToSystemPackage(packageName, userId, false, true, permissionGroups);
+        grantPermissionsToPackage(
+                packageName, userId, true /* ignoreSystemPackage */, permissionGroups);
     }
 
     @SafeVarargs
     private final void grantSystemFixedPermissionsToSystemPackage(String packageName, int userId,
             Set<String>... permissionGroups) {
-        grantPermissionsToSystemPackage(packageName, userId, true, false, permissionGroups);
+        grantPermissionsToSystemPackage(
+                packageName, userId, true /* systemFixed */, permissionGroups);
     }
 
     @SafeVarargs
     private final void grantPermissionsToSystemPackage(
             String packageName, int userId, Set<String>... permissionGroups) {
-        grantPermissionsToSystemPackage(packageName, userId, false, false, permissionGroups);
+        grantPermissionsToSystemPackage(
+                packageName, userId, false /* systemFixed */, permissionGroups);
     }
 
     @SafeVarargs
     private final void grantPermissionsToSystemPackage(String packageName, int userId,
-            boolean systemFixed, boolean ignoreSystemPackage, Set<String>... permissionGroups) {
-        if (!ignoreSystemPackage && !isSystemPackage(packageName)) {
+            boolean systemFixed, Set<String>... permissionGroups) {
+        if (!isSystemPackage(packageName)) {
             return;
         }
-        grantRuntimePermissionsToPackage(getSystemPackageInfo(packageName),
-                userId, systemFixed, ignoreSystemPackage, permissionGroups);
+        grantPermissionsToPackage(getSystemPackageInfo(packageName),
+                userId, systemFixed, false /* ignoreSystemPackage */, permissionGroups);
     }
 
     @SafeVarargs
-    private final void grantRuntimePermissionsToPackage(String packageName, int userId,
-            boolean systemFixed, boolean ignoreSystemPackage, Set<String>... permissionGroups) {
-        grantRuntimePermissionsToPackage(getPackageInfo(packageName),
-                userId, systemFixed, ignoreSystemPackage, permissionGroups);
+    private final void grantPermissionsToPackage(String packageName, int userId,
+            boolean ignoreSystemPackage, Set<String>... permissionGroups) {
+        grantPermissionsToPackage(getPackageInfo(packageName),
+                userId, false /* systemFixed */, ignoreSystemPackage, permissionGroups);
     }
 
     @SafeVarargs
-    private final void grantRuntimePermissionsToPackage(PackageInfo packageName, int userId,
+    private final void grantPermissionsToPackage(PackageInfo packageName, int userId,
             boolean systemFixed, boolean ignoreSystemPackage, Set<String>... permissionGroups) {
         if (packageName == null) return;
         if (doesPackageSupportRuntimePermissions(packageName)) {
@@ -589,9 +592,8 @@
                 browserPackage = null;
             }
         }
-        grantRuntimePermissionsToPackage(browserPackage, userId,
-                false /* systemFixed */, false /* ignoreSystemPackage */,
-                LOCATION_PERMISSIONS);
+        grantPermissionsToPackage(browserPackage, userId,
+                false /* ignoreSystemPackage */, LOCATION_PERMISSIONS);
 
         // Voice interaction
         if (voiceInteractPackageNames != null) {
@@ -786,7 +788,7 @@
             return;
         }
         Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
-        grantRuntimePermissionsToPackage(packageName, userId, false, false,
+        grantPermissionsToPackage(packageName, userId, false /* ignoreSystemPackage */,
                 PHONE_PERMISSIONS, MICROPHONE_PERMISSIONS);
     }
 
@@ -878,9 +880,8 @@
     }
 
     private String getDefaultSystemHandlerActivityPackage(Intent intent, int userId) {
-        ResolveInfo handler = mServiceInternal.resolveIntent(intent,
-                intent.resolveType(mContext.getContentResolver()), DEFAULT_INTENT_QUERY_FLAGS,
-                userId, false, Binder.getCallingUid());
+        ResolveInfo handler = mContext.getPackageManager().resolveActivityAsUser(
+                intent, DEFAULT_INTENT_QUERY_FLAGS, userId);
         if (handler == null || handler.activityInfo == null) {
             return null;
         }
@@ -897,8 +898,8 @@
 
     private String getDefaultSystemHandlerServicePackage(
             Intent intent, int userId) {
-        List<ResolveInfo> handlers = mServiceInternal.queryIntentServices(
-                intent, DEFAULT_INTENT_QUERY_FLAGS, Binder.getCallingUid(), userId);
+        List<ResolveInfo> handlers = mContext.getPackageManager().queryIntentServicesAsUser(
+                intent, DEFAULT_INTENT_QUERY_FLAGS, userId);
         if (handlers == null) {
             return null;
         }
@@ -922,10 +923,8 @@
         for (String syncAdapterPackageName : syncAdapterPackageNames) {
             homeIntent.setPackage(syncAdapterPackageName);
 
-            ResolveInfo homeActivity = mServiceInternal.resolveIntent(homeIntent,
-                    homeIntent.resolveType(mContext.getContentResolver()),
-                    DEFAULT_INTENT_QUERY_FLAGS,
-                    userId, false, Binder.getCallingUid());
+            ResolveInfo homeActivity = mContext.getPackageManager().resolveActivityAsUser(
+                    homeIntent, DEFAULT_INTENT_QUERY_FLAGS, userId);
             if (homeActivity != null) {
                 continue;
             }
@@ -939,7 +938,7 @@
     }
 
     private String getDefaultProviderAuthorityPackage(String authority, int userId) {
-        ProviderInfo provider = mServiceInternal.resolveContentProvider(
+        ProviderInfo provider = mContext.getPackageManager().resolveContentProviderAsUser(
                 authority, DEFAULT_INTENT_QUERY_FLAGS, userId);
         if (provider != null) {
             return provider.packageName;
@@ -978,8 +977,9 @@
                 continue;
             }
 
-            final int flags = mServiceInternal.getPermissionFlagsTEMP(
-                    permission, packageName, userId);
+            UserHandle user = UserHandle.of(userId);
+            final int flags = mContext.getPackageManager()
+                    .getPermissionFlags(permission, packageName, user);
 
             // We didn't get this through the default grant policy. Move along.
             if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) == 0) {
@@ -995,7 +995,7 @@
             if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 && !systemFixed) {
                 continue;
             }
-            mServiceInternal.revokeRuntimePermission(packageName, permission, userId, false);
+            mContext.getPackageManager().revokeRuntimePermission(packageName, permission, user);
 
             if (DEBUG) {
                 Log.i(TAG, "revoked " + (systemFixed ? "fixed " : "not fixed ")
@@ -1005,8 +1005,43 @@
             // Remove the GRANTED_BY_DEFAULT flag without touching the others.
             // Note that we do not revoke FLAG_PERMISSION_SYSTEM_FIXED. That bit remains
             // sticky once set.
-            mServiceInternal.updatePermissionFlagsTEMP(permission, packageName,
-                    PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0, userId);
+            mContext.getPackageManager().updatePermissionFlags(permission, packageName,
+                    PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0, user);
+        }
+    }
+
+    /**
+     * Check if a permission is already fixed or is set by the user.
+     *
+     * <p>A permission should not be set by the default policy if the user or other policies already
+     * set the permission.
+     *
+     * @param flags The flags of the permission
+     *
+     * @return {@code true} iff the permission can be set without violating a policy of the users
+     *         intention
+     */
+    private boolean isFixedOrUserSet(int flags) {
+        return (flags & (PackageManager.FLAG_PERMISSION_USER_SET
+                | PackageManager.FLAG_PERMISSION_USER_FIXED
+                | PackageManager.FLAG_PERMISSION_POLICY_FIXED
+                | PackageManager.FLAG_PERMISSION_SYSTEM_FIXED)) != 0;
+    }
+
+    /**
+     * Return the background permission for a permission.
+     *
+     * @param permission The name of the foreground permission
+     *
+     * @return The name of the background permission or {@code null} if the permission has no
+     *         background permission
+     */
+    private @Nullable String getBackgroundPermission(@NonNull String permission) {
+        try {
+            return mContext.getPackageManager().getPermissionInfo(permission,
+                    0).backgroundPermission;
+        } catch (NameNotFoundException e) {
+            return null;
         }
     }
 
@@ -1022,9 +1057,15 @@
             return;
         }
 
+        PackageManager pm = mContext.getPackageManager();
         final ArraySet<String> permissions = new ArraySet<>(permissionsWithoutSplits);
         ApplicationInfo applicationInfo = pkg.applicationInfo;
 
+        int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+        if (systemFixed) {
+            newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+        }
+
         // Automatically attempt to grant split permissions to older APKs
         final List<PermissionManager.SplitPermissionInfo> splitPermissions =
                 mContext.getSystemService(PermissionManager.class).getSplitPermissions();
@@ -1064,9 +1105,28 @@
             }
         }
 
-        final int grantablePermissionCount = requestedPermissions.length;
-        for (int i = 0; i < grantablePermissionCount; i++) {
+        final int numRequestedPermissions = requestedPermissions.length;
+
+        // Sort requested permissions so that all permissions that are a foreground permission (i.e.
+        // permisions that have background permission) are before their background permissions.
+        final String[] sortedRequestedPermissions = new String[numRequestedPermissions];
+        int numForeground = 0;
+        int numOther = 0;
+        for (int i = 0; i < numRequestedPermissions; i++) {
             String permission = requestedPermissions[i];
+            if (getBackgroundPermission(permission) != null) {
+                sortedRequestedPermissions[numForeground] = permission;
+                numForeground++;
+            } else {
+                sortedRequestedPermissions[numRequestedPermissions - 1 - numOther] =
+                        permission;
+                numOther++;
+            }
+        }
+
+        for (int requestedPermissionNum = 0; requestedPermissionNum < numRequestedPermissions;
+                requestedPermissionNum++) {
+            String permission = requestedPermissions[requestedPermissionNum];
 
             // If there is a disabled system app it may request a permission the updated
             // version ot the data partition doesn't, In this case skip the permission.
@@ -1075,16 +1135,18 @@
             }
 
             if (permissions.contains(permission)) {
-                final int flags = mServiceInternal.getPermissionFlagsTEMP(
-                        permission, pkg.packageName, userId);
+                UserHandle user = UserHandle.of(userId);
+                final int flags = mContext.getPackageManager().getPermissionFlags(
+                        permission, pkg.packageName, user);
 
-                // If any flags are set to the permission, then it is either set in
-                // its current state by the system or device/profile owner or the user.
-                // In all these cases we do not want to clobber the current state.
+                // Certain flags imply that the permission's current state by the system or
+                // device/profile owner or the user. In these cases we do not want to clobber the
+                // current state.
+                //
                 // Unless the caller wants to override user choices. The override is
                 // to make sure we can grant the needed permission to the default
                 // sms and phone apps after the user chooses this in the UI.
-                if (flags == 0 || ignoreSystemPackage) {
+                if (!isFixedOrUserSet(flags) || ignoreSystemPackage) {
                     // Never clobber policy fixed permissions.
                     // We must allow the grant of a system-fixed permission because
                     // system-fixed is sticky, but the permission itself may be revoked.
@@ -1092,20 +1154,58 @@
                         continue;
                     }
 
-                    mServiceInternal.grantRuntimePermission(
-                            pkg.packageName, permission, userId, false);
+                    int uid = UserHandle.getUid(userId,
+                            UserHandle.getAppId(pkg.applicationInfo.uid));
+                    String op = AppOpsManager.permissionToOp(permission);
+
+                    mContext.getPackageManager()
+                            .grantRuntimePermission(pkg.packageName, permission, user);
+
+                    mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName,
+                            newFlags, newFlags, user);
+
+                    List<String> fgPerms = mPermissionManager.getBackgroundPermissions()
+                            .get(permission);
+                    if (fgPerms != null) {
+                        int numFgPerms = fgPerms.size();
+                        for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) {
+                            String fgPerm = fgPerms.get(fgPermNum);
+
+                            if (pm.checkPermission(fgPerm, pkg.packageName)
+                                    == PackageManager.PERMISSION_GRANTED) {
+                                // Upgrade the app-op state of the fg permission to allow bg access
+                                mContext.getSystemService(AppOpsManager.class).setMode(
+                                        AppOpsManager.permissionToOp(fgPerm), uid,
+                                        pkg.packageName, AppOpsManager.MODE_ALLOWED);
+
+                                break;
+                            }
+                        }
+                    }
+
+                    String bgPerm = getBackgroundPermission(permission);
+                    if (bgPerm == null) {
+                        if (op != null) {
+                            mContext.getSystemService(AppOpsManager.class).setMode(op, uid,
+                                    pkg.packageName, AppOpsManager.MODE_ALLOWED);
+                        }
+                    } else {
+                        int mode;
+                        if (pm.checkPermission(bgPerm, pkg.packageName)
+                                == PackageManager.PERMISSION_GRANTED) {
+                            mode = AppOpsManager.MODE_ALLOWED;
+                        } else {
+                            mode = AppOpsManager.MODE_FOREGROUND;
+                        }
+
+                        mContext.getSystemService(AppOpsManager.class).setMode(op, uid,
+                                pkg.packageName, mode);
+                    }
+
                     if (DEBUG) {
                         Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")
                                 + permission + " to default handler " + pkg);
                     }
-
-                    int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
-                    if (systemFixed) {
-                        newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
-                    }
-
-                    mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
-                            newFlags, newFlags, userId);
                 }
 
                 // If a component gets a permission for being the default handler A
@@ -1117,8 +1217,8 @@
                         Log.i(TAG, "Granted not fixed " + permission + " to default handler "
                                 + pkg);
                     }
-                    mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
-                            PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId);
+                    mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName,
+                            PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, user);
                 }
             }
         }
@@ -1135,10 +1235,12 @@
 
     private PackageInfo getPackageInfo(String pkg,
             @PackageManager.PackageInfoFlags int extraFlags) {
-        return mServiceInternal.getPackageInfo(pkg,
-                DEFAULT_PACKAGE_INFO_QUERY_FLAGS | extraFlags,
-                //TODO is this the right filterCallingUid?
-                UserHandle.USER_SYSTEM, UserHandle.USER_SYSTEM);
+        try {
+            return mContext.getPackageManager().getPackageInfo(pkg,
+                    DEFAULT_PACKAGE_INFO_QUERY_FLAGS | extraFlags);
+        } catch (NameNotFoundException e) {
+            return null;
+        }
     }
 
     private boolean isSysComponentOrPersistentPlatformSignedPrivApp(PackageInfo pkg) {
diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS
index ffc4731..88b97ea 100644
--- a/services/core/java/com/android/server/pm/permission/OWNERS
+++ b/services/core/java/com/android/server/pm/permission/OWNERS
@@ -1,8 +1,9 @@
 per-file DefaultPermissionGrantPolicy.java = bpoiesz@google.com
-per-file DefaultPermissionGrantPolicy.java = fkupolov@google.com
 per-file DefaultPermissionGrantPolicy.java = hackbod@android.com
 per-file DefaultPermissionGrantPolicy.java = jsharkey@android.com
 per-file DefaultPermissionGrantPolicy.java = svetoslavganov@google.com
 per-file DefaultPermissionGrantPolicy.java = toddke@google.com
 per-file DefaultPermissionGrantPolicy.java = yamasani@google.com
 per-file DefaultPermissionGrantPolicy.java = patb@google.com
+per-file DefaultPermissionGrantPolicy.java = eugenesusla@google.com
+per-file DefaultPermissionGrantPolicy.java = moltmann@google.com
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
index 80a5fbb6..ec15c16 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -107,11 +107,7 @@
      */
     public abstract void addAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
     public abstract void addAllPermissionGroups(@NonNull PackageParser.Package pkg, boolean chatty);
-    public abstract void removeAllPermissions(
-            @NonNull PackageParser.Package pkg,
-            @NonNull List<String> allPackageNames,
-            @Nullable PermissionCallback permissionCallback,
-            boolean chatty);
+    public abstract void removeAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
     public abstract boolean addDynamicPermission(@NonNull PermissionInfo info, boolean async,
             int callingUid, @Nullable PermissionCallback callback);
     public abstract void removeDynamicPermission(@NonNull String permName, int callingUid,
@@ -185,4 +181,4 @@
 
     /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
     public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 4b6760c..b788935 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -16,10 +16,28 @@
 
 package com.android.server.pm.permission;
 
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.MODE_FOREGROUND;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.app.AppOpsManager.permissionToOp;
+import static android.app.AppOpsManager.permissionToOpCode;
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.UserHandle.getAppId;
+import static android.os.UserHandle.getUid;
 
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
 import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
@@ -30,8 +48,10 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
+import android.app.AppOpsManagerInternal;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageParser;
@@ -39,7 +59,6 @@
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.metrics.LogMaker;
-import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -51,6 +70,7 @@
 import android.os.UserManagerInternal;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
+import android.permission.PermissionManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -154,6 +174,13 @@
     @GuardedBy("mLock")
     private boolean mSystemReady;
 
+    /**
+     * For each foreground/background permission the mapping:
+     * Background permission -> foreground permissions
+     */
+    @GuardedBy("mLock")
+    private ArrayMap<String, List<String>> mBackgroundPermissions;
+
     PermissionManagerService(Context context,
             @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
             @NonNull Object externalLock) {
@@ -478,9 +505,8 @@
                                         " to " + newPermissionGroupName);
 
                                 try {
-                                    revokeRuntimePermission(permissionName, packageName,
-                                            mSettings.getPermission(permissionName), false,
-                                            Process.SYSTEM_UID, userId, permissionCallback, false);
+                                    revokeRuntimePermission(permissionName, packageName, false,
+                                            Process.SYSTEM_UID, userId, permissionCallback);
                                 } catch (IllegalArgumentException e) {
                                     Slog.e(TAG, "Could not revoke " + permissionName + " from "
                                             + packageName, e);
@@ -573,59 +599,9 @@
 
     }
 
-    private void revokeAllPermissions(
-            @NonNull List<BasePermission> bps,
-            @NonNull List<String> allPackageNames,
-            @Nullable PermissionCallback permissionCallback) {
-        AsyncTask.execute(() -> {
-            final int numRemovedPermissions = bps.size();
-            for (int permissionNum = 0; permissionNum < numRemovedPermissions; permissionNum++) {
-                final int[] userIds = mUserManagerInt.getUserIds();
-                final int numUserIds = userIds.length;
-
-                final int numPackages = allPackageNames.size();
-                for (int packageNum = 0; packageNum < numPackages; packageNum++) {
-                    final String packageName = allPackageNames.get(packageNum);
-                    final ApplicationInfo applicationInfo = mPackageManagerInt.getApplicationInfo(
-                            packageName, 0, Process.SYSTEM_UID, UserHandle.USER_SYSTEM);
-                    if (applicationInfo != null
-                            && applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
-                        continue;
-                    }
-                    for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
-                        final int userId = userIds[userIdNum];
-                        final String permissionName = bps.get(permissionNum).getName();
-                        if (checkPermission(permissionName, packageName, UserHandle.USER_SYSTEM,
-                                userId) == PackageManager.PERMISSION_GRANTED) {
-                            try {
-                                revokeRuntimePermission(
-                                        permissionName,
-                                        packageName,
-                                        bps.get(permissionNum),
-                                        false,
-                                        Process.SYSTEM_UID,
-                                        userId,
-                                        permissionCallback,
-                                        true);
-                            } catch (IllegalArgumentException e) {
-                                Slog.e(TAG, "Could not revoke " + permissionName + " from "
-                                        + packageName, e);
-                            }
-                        }
-                    }
-                }
-            }
-        });
-    }
-
-    private void removeAllPermissions(
-            @NonNull PackageParser.Package pkg,
-            @NonNull List<String> allPackageNames,
-            @Nullable PermissionCallback permissionCallback,
-            boolean chatty) {
+    private void removeAllPermissions(PackageParser.Package pkg, boolean chatty) {
         synchronized (mLock) {
             int N = pkg.permissions.size();
-            List<BasePermission> bps = new ArrayList<BasePermission>(N);
             StringBuilder r = null;
             for (int i=0; i<N; i++) {
                 PackageParser.Permission p = pkg.permissions.get(i);
@@ -634,9 +610,6 @@
                     bp = mSettings.mPermissionTrees.get(p.info.name);
                 }
                 if (bp != null && bp.isPermission(p)) {
-                    if ((p.info.getProtection() & PermissionInfo.PROTECTION_DANGEROUS) != 0) {
-                        bps.add(bp);
-                    }
                     bp.setPermission(null);
                     if (DEBUG_REMOVE && chatty) {
                         if (r == null) {
@@ -655,7 +628,6 @@
                     }
                 }
             }
-            revokeAllPermissions(bps, allPackageNames, permissionCallback);
             if (r != null) {
                 if (DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
             }
@@ -737,8 +709,24 @@
         }
     }
 
-    private void grantPermissions(PackageParser.Package pkg, boolean replace,
-            String packageOfInterest, PermissionCallback callback) {
+    /**
+     * Restore the permission state for a package.
+     *
+     * <ul>
+     *     <li>During boot the state gets restored from the disk</li>
+     *     <li>During app update the state gets restored from the last version of the app</li>
+     * </ul>
+     *
+     * <p>This restores the permission state for all users.
+     *
+     * @param pkg the package the permissions belong to
+     * @param replace if the package is getting replaced (this might change the requested
+     *                permissions of this package)
+     * @param packageOfInterest If this is the name of {@code pkg} add extra logging
+     * @param callback Result call back
+     */
+    private void restorePermissionState(@NonNull PackageParser.Package pkg, boolean replace,
+            @Nullable String packageOfInterest, @Nullable PermissionCallback callback) {
         // IMPORTANT: There are two types of permissions: install and runtime.
         // Install time permissions are granted when the app is installed to
         // all device users and users added in the future. Runtime permissions
@@ -866,7 +854,8 @@
                 }
 
                 if (DEBUG_PERMISSIONS) {
-                    Slog.i(TAG, "Granting permission " + perm + " to package " + pkg.packageName);
+                    Slog.i(TAG, "Considering granting permission " + perm + " to package "
+                            + pkg.packageName);
                 }
 
                 if (grant != GRANT_DENIED) {
@@ -1055,6 +1044,11 @@
                 // changed.
                 ps.setInstallPermissionsFixed(true);
             }
+
+            updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg,
+                    updatedUserIds);
+            updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions,
+                    permissionsState, pkg, updatedUserIds);
         }
 
         // Persist the runtime permissions state for users with changes. If permissions
@@ -1065,6 +1059,317 @@
         }
     }
 
+    /**
+     * Set app op for a app-op related to a permission.
+     *
+     * @param permission The permission the app-op belongs to
+     * @param pkg The package the permission belongs to
+     * @param userId The user to be changed
+     * @param mode The new mode to set
+     */
+    private void setAppOpMode(@NonNull String permission, @NonNull PackageParser.Package pkg,
+            @UserIdInt int userId, int mode) {
+        AppOpsManagerInternal appOpsInternal = LocalServices.getService(
+                AppOpsManagerInternal.class);
+
+        appOpsInternal.setMode(permissionToOpCode(permission),
+                getUid(userId, getAppId(pkg.applicationInfo.uid)), pkg.packageName, mode,
+                (pkg.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0);
+    }
+
+    /**
+     * Revoke permissions that are not implicit anymore and that have
+     * {@link PackageManager#FLAG_PERMISSION_REVOKE_WHEN_REQUESTED} set.
+     *
+     * @param ps The state of the permissions of the package
+     * @param pkg The package that is currently looked at
+     * @param updatedUserIds a list of user ids that needs to be amended if the permission state
+     *                       for a user is changed.
+     *
+     * @return The updated value of the {@code updatedUserIds} parameter
+     */
+    private @NonNull int[] revokePermissionsNoLongerImplicitLocked(
+            @NonNull PermissionsState ps, @NonNull PackageParser.Package pkg,
+            @NonNull int[] updatedUserIds) {
+        AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
+
+        String pkgName = pkg.packageName;
+
+        int[] users = UserManagerService.getInstance().getUserIds();
+        int numUsers = users.length;
+        for (int i = 0; i < numUsers; i++) {
+            int userId = users[i];
+
+            for (String permission : ps.getPermissions(userId)) {
+                if (!pkg.implicitPermissions.contains(permission)) {
+                    if (!ps.hasInstallPermission(permission)) {
+                        int flags = ps.getRuntimePermissionState(permission, userId).getFlags();
+
+                        if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
+                            BasePermission bp = mSettings.getPermissionLocked(permission);
+
+                            ps.updatePermissionFlags(bp, userId,
+                                    FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
+                                            | FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_USER_SET,
+                                    0);
+                            updatedUserIds = ArrayUtils.appendInt(updatedUserIds,
+                                    userId);
+
+                            if ((flags & (FLAG_PERMISSION_GRANTED_BY_DEFAULT
+                                    | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED))
+                                    == 0) {
+                                if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+                                    if (permissionToOpCode(permission) != OP_NONE) {
+                                        setAppOpMode(permission, pkg, userId, MODE_IGNORED);
+
+                                        if (DEBUG_PERMISSIONS) {
+                                            Slog.i(TAG, "Revoking app-op "
+                                                    + permissionToOp(permission) + " for " + pkgName
+                                                    + " as it is now requested");
+                                        }
+                                    }
+                                } else {
+                                    int revokeResult = ps.revokeRuntimePermission(bp, userId);
+                                    if (revokeResult
+                                            != PermissionsState.PERMISSION_OPERATION_FAILURE) {
+
+                                        if (DEBUG_PERMISSIONS) {
+                                            Slog.i(TAG, "Revoking runtime permission " + permission
+                                                    + " for " + pkgName
+                                                    + " as it is now requested");
+                                        }
+                                    }
+                                }
+
+                                List<String> fgPerms = mBackgroundPermissions.get(permission);
+                                if (fgPerms != null) {
+                                    int numFgPerms = fgPerms.size();
+                                    for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) {
+                                        String fgPerm = fgPerms.get(fgPermNum);
+
+                                        int mode = appOpsManager.unsafeCheckOpRaw(
+                                                permissionToOp(fgPerm),
+                                                getUid(userId, getAppId(pkg.applicationInfo.uid)),
+                                                pkgName);
+
+                                        if (mode == MODE_ALLOWED) {
+                                            setAppOpMode(fgPerm, pkg, userId, MODE_FOREGROUND);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return updatedUserIds;
+    }
+
+    /**
+     * {@code newPerm} is newly added; Inherit the state from {@code sourcePerms}.
+     *
+     * <p>A single new permission can be split off from several source permissions. In this case
+     * the most leniant state is inherited.
+     *
+     * <p>Warning: This does not handle foreground / background permissions
+     *
+     * @param sourcePerms The permissions to inherit from
+     * @param newPerm The permission to inherit to
+     * @param ps The permission state of the package
+     * @param pkg The package requesting the permissions
+     * @param userId The user the permission belongs to
+     */
+    private void inheritPermissionStateToNewImplicitPermissionLocked(
+            @NonNull ArraySet<String> sourcePerms, @NonNull String newPerm,
+            @NonNull PermissionsState ps, @NonNull PackageParser.Package pkg,
+            @UserIdInt int userId) {
+        AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
+        String pkgName = pkg.packageName;
+
+        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+            if (permissionToOp(newPerm) != null) {
+                int mostLenientSourceMode = MODE_ERRORED;
+
+                // Find most lenient source permission state.
+                int numSourcePerms = sourcePerms.size();
+                for (int i = 0; i < numSourcePerms; i++) {
+                    String sourcePerm = sourcePerms.valueAt(i);
+
+                    if (ps.hasRuntimePermission(sourcePerm, userId)) {
+                        String sourceOp = permissionToOp(sourcePerm);
+
+                        if (sourceOp != null) {
+                            int mode = appOpsManager.unsafeCheckOpRaw(sourceOp,
+                                    getUid(userId, getAppId(pkg.applicationInfo.uid)), pkgName);
+
+                            if (mode == MODE_FOREGROUND) {
+                                throw new IllegalArgumentException("split permission" + sourcePerm
+                                        + " has app-op state " + AppOpsManager.MODE_NAMES[mode]);
+                            }
+
+                            // Leniency order: allowed > ignored > default
+                            if (mode == MODE_ALLOWED) {
+                                mostLenientSourceMode = MODE_ALLOWED;
+                                break;
+                            } else if (mode == MODE_IGNORED) {
+                                mostLenientSourceMode = MODE_IGNORED;
+                            } else if (mode == MODE_DEFAULT
+                                    && mostLenientSourceMode != MODE_IGNORED) {
+                                mostLenientSourceMode = MODE_DEFAULT;
+                            }
+                        }
+                    }
+                }
+
+                if (mostLenientSourceMode != MODE_ERRORED) {
+                    if (DEBUG_PERMISSIONS) {
+                        Slog.i(TAG, newPerm + " inherits app-ops state " + mostLenientSourceMode
+                                + " from " + sourcePerms + " for " + pkgName);
+                    }
+
+                    setAppOpMode(newPerm, pkg, userId, mostLenientSourceMode);
+                }
+            }
+        } else {
+            boolean isGranted = false;
+
+            int numSourcePerm = sourcePerms.size();
+            for (int i = 0; i < numSourcePerm; i++) {
+                String sourcePerm = sourcePerms.valueAt(i);
+                if (ps.hasRuntimePermission(sourcePerm, userId)
+                        && ps.getRuntimePermissionState(sourcePerm, userId).isGranted()) {
+                    isGranted = true;
+                    break;
+                }
+            }
+
+            if (isGranted) {
+                if (DEBUG_PERMISSIONS) {
+                    Slog.i(TAG, newPerm + " inherits runtime perm grant from " + sourcePerms
+                            + " for " + pkgName);
+                }
+
+                ps.grantRuntimePermission(mSettings.getPermissionLocked(newPerm), userId);
+            }
+        }
+    }
+
+    /**
+     * Set the state of a implicit permission that is seen for the first time.
+     *
+     * @param origPs The permission state of the package before the split
+     * @param ps The new permission state
+     * @param pkg The package the permission belongs to
+     * @param updatedUserIds List of users for which the permission state has already been changed
+     *
+     * @return  List of users for which the permission state has been changed
+     */
+    private @NonNull int[] setInitialGrantForNewImplicitPermissionsLocked(
+            @NonNull PermissionsState origPs,
+            @NonNull PermissionsState ps, @NonNull PackageParser.Package pkg,
+            @NonNull int[] updatedUserIds) {
+        AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
+
+        String pkgName = pkg.packageName;
+        ArraySet<String> newImplicitPermissions = new ArraySet<>();
+
+        int numRequestedPerms = pkg.requestedPermissions.size();
+        for (int i = 0; i < numRequestedPerms; i++) {
+            BasePermission bp = mSettings.getPermissionLocked(pkg.requestedPermissions.get(i));
+            if (bp != null) {
+                String perm = bp.getName();
+
+                if (!origPs.hasRequestedPermission(perm) && pkg.implicitPermissions.contains(
+                        perm)) {
+                    newImplicitPermissions.add(perm);
+
+                    if (DEBUG_PERMISSIONS) {
+                        Slog.i(TAG, perm + " is newly added for " + pkgName);
+                    }
+                }
+            }
+        }
+
+        ArrayMap<String, ArraySet<String>> newToSplitPerms = new ArrayMap<>();
+
+        int numSplitPerms = PermissionManager.SPLIT_PERMISSIONS.size();
+        for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
+            PermissionManager.SplitPermissionInfo spi =
+                    PermissionManager.SPLIT_PERMISSIONS.get(splitPermNum);
+
+            List<String> newPerms = spi.getNewPermissions();
+            int numNewPerms = newPerms.size();
+            for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) {
+                String newPerm = newPerms.get(newPermNum);
+
+                ArraySet<String> splitPerms = newToSplitPerms.get(newPerm);
+                if (splitPerms == null) {
+                    splitPerms = new ArraySet<>();
+                    newToSplitPerms.put(newPerm, splitPerms);
+                }
+
+                splitPerms.add(spi.getSplitPermission());
+            }
+        }
+
+        int numNewImplicitPerms = newImplicitPermissions.size();
+        for (int newImplicitPermNum = 0; newImplicitPermNum < numNewImplicitPerms;
+                newImplicitPermNum++) {
+            String newPerm = newImplicitPermissions.valueAt(newImplicitPermNum);
+            ArraySet<String> sourcePerms = newToSplitPerms.get(newPerm);
+
+            if (sourcePerms != null) {
+                if (!ps.hasInstallPermission(newPerm)) {
+                    BasePermission bp = mSettings.getPermissionLocked(newPerm);
+
+                    int[] users = UserManagerService.getInstance().getUserIds();
+                    int numUsers = users.length;
+                    for (int userNum = 0; userNum < numUsers; userNum++) {
+                        int userId = users[userNum];
+
+                        ps.updatePermissionFlags(bp, userId,
+                                FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
+                                FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
+                        updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+
+                        // SPECIAL BEHAVIOR for background location. Foreground only by default.
+                        if (newPerm.equals(ACCESS_BACKGROUND_LOCATION)) {
+                            int numSourcePerms = sourcePerms.size();
+                            for (int sourcePermNum = 0; sourcePermNum < numSourcePerms;
+                                    sourcePermNum++) {
+                                String sourcePerm = sourcePerms.valueAt(sourcePermNum);
+
+                                if (appOpsManager.unsafeCheckOpNoThrow(permissionToOp(sourcePerm),
+                                        getUid(userId, getAppId(pkg.applicationInfo.uid)), pkgName)
+                                        == MODE_ALLOWED) {
+                                    setAppOpMode(sourcePerm, pkg, userId, MODE_FOREGROUND);
+                                }
+                            }
+                        } else {
+                            if (!origPs.hasRequestedPermission(sourcePerms)) {
+                                // Both permissions are new, do nothing
+                                if (DEBUG_PERMISSIONS) {
+                                    Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms
+                                            + " for " + pkgName
+                                            + " as split permission is also new");
+                                }
+
+                                break;
+                            } else {
+                                inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms,
+                                        newPerm, ps, pkg, userId);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return updatedUserIds;
+    }
+
     private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
         boolean allowed = false;
         final int NP = PackageParser.NEW_PERMISSIONS.length;
@@ -1571,10 +1876,9 @@
         }
 
     }
-    
-    private void revokeRuntimePermission(String permName, String packageName, BasePermission bp,
-            boolean overridePolicy, int callingUid, int userId, PermissionCallback callback,
-            boolean permissionRemoved) {
+
+    private void revokeRuntimePermission(String permName, String packageName,
+            boolean overridePolicy, int callingUid, int userId, PermissionCallback callback) {
         if (!mUserManagerInt.exists(userId)) {
             Log.e(TAG, "No such user:" + userId);
             return;
@@ -1599,7 +1903,7 @@
         if (mPackageManagerInt.filterAppAccess(pkg, Binder.getCallingUid(), userId)) {
             throw new IllegalArgumentException("Unknown package: " + packageName);
         }
-
+        final BasePermission bp = mSettings.getPermissionLocked(permName);
         if (bp == null) {
             throw new IllegalArgumentException("Unknown permission: " + permName);
         }
@@ -1812,7 +2116,30 @@
         // and make sure there are no dangling permissions.
         flags = updatePermissions(changingPkgName, changingPkg, flags);
 
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "grantPermissions");
+        synchronized (mLock) {
+            if (mBackgroundPermissions == null) {
+                // Cache background -> foreground permission mapping.
+                // Only system declares background permissions, hence mapping does never change.
+                mBackgroundPermissions = new ArrayMap<>();
+                for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
+                    if (bp.perm != null && bp.perm.info != null
+                            && bp.perm.info.backgroundPermission != null) {
+                        String fgPerm = bp.name;
+                        String bgPerm = bp.perm.info.backgroundPermission;
+
+                        List<String> fgPerms = mBackgroundPermissions.get(bgPerm);
+                        if (fgPerms == null) {
+                            fgPerms = new ArrayList<>();
+                            mBackgroundPermissions.put(bgPerm, fgPerms);
+                        }
+
+                        fgPerms.add(fgPerm);
+                    }
+                }
+            }
+        }
+
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "restorePermissionState");
         // Now update the permissions for all packages, in particular
         // replace the granted permissions of the system packages.
         if ((flags & UPDATE_PERMISSIONS_ALL) != 0) {
@@ -1822,7 +2149,7 @@
                     final String volumeUuid = getVolumeUuidForPackage(pkg);
                     final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0)
                             && Objects.equals(replaceVolumeUuid, volumeUuid);
-                    grantPermissions(pkg, replace, changingPkgName, callback);
+                    restorePermissionState(pkg, replace, changingPkgName, callback);
                 }
             }
         }
@@ -1832,7 +2159,7 @@
             final String volumeUuid = getVolumeUuidForPackage(changingPkg);
             final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)
                     && Objects.equals(replaceVolumeUuid, volumeUuid);
-            grantPermissions(changingPkg, replace, changingPkgName, callback);
+            restorePermissionState(changingPkg, replace, changingPkgName, callback);
         }
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
     }
@@ -2129,6 +2456,17 @@
         mMetricsLogger.write(log);
     }
 
+    /**
+     * Get the mapping of background permissions to their foreground permissions.
+     *
+     * <p>Only initialized in the system server.
+     *
+     * @return the map &lt;bg permission -> list&lt;fg perm&gt;&gt;
+     */
+    public @Nullable ArrayMap<String, List<String>> getBackgroundPermissions() {
+        return mBackgroundPermissions;
+    }
+
     private class PermissionManagerInternalImpl extends PermissionManagerInternal {
         @Override
         public void systemReady() {
@@ -2156,10 +2494,8 @@
             PermissionManagerService.this.addAllPermissionGroups(pkg, chatty);
         }
         @Override
-        public void removeAllPermissions(Package pkg, List<String> allPackageNames,
-                PermissionCallback permissionCallback, boolean chatty) {
-            PermissionManagerService.this.removeAllPermissions(
-                    pkg, allPackageNames, permissionCallback, chatty);
+        public void removeAllPermissions(Package pkg, boolean chatty) {
+            PermissionManagerService.this.removeAllPermissions(pkg, chatty);
         }
         @Override
         public boolean addDynamicPermission(PermissionInfo info, boolean async, int callingUid,
@@ -2195,8 +2531,7 @@
                 boolean overridePolicy, int callingUid, int userId,
                 PermissionCallback callback) {
             PermissionManagerService.this.revokeRuntimePermission(permName, packageName,
-                    mSettings.getPermission(permName), overridePolicy, callingUid, userId,
-                    callback, false);
+                    overridePolicy, callingUid, userId, callback);
         }
         @Override
         public void updatePermissions(String packageName, Package pkg, boolean replaceGrant,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java
index 82d6b22..c615ee5 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java
@@ -30,6 +30,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import com.android.internal.annotations.GuardedBy;
 
 /**
  * This class encapsulates the permissions for a package or a shared user.
@@ -62,6 +63,9 @@
 
     private static final int[] NO_GIDS = {};
 
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
     private ArrayMap<String, PermissionData> mPermissions;
 
     private int[] mGlobalGids = NO_GIDS;
@@ -96,22 +100,25 @@
         if (other == this) {
             return;
         }
-        if (mPermissions != null) {
-            if (other.mPermissions == null) {
-                mPermissions = null;
-            } else {
-                mPermissions.clear();
+
+        synchronized (mLock) {
+            if (mPermissions != null) {
+                if (other.mPermissions == null) {
+                    mPermissions = null;
+                } else {
+                    mPermissions.clear();
+                }
             }
-        }
-        if (other.mPermissions != null) {
-            if (mPermissions == null) {
-                mPermissions = new ArrayMap<>();
-            }
-            final int permissionCount = other.mPermissions.size();
-            for (int i = 0; i < permissionCount; i++) {
-                String name = other.mPermissions.keyAt(i);
-                PermissionData permissionData = other.mPermissions.valueAt(i);
-                mPermissions.put(name, new PermissionData(permissionData));
+            if (other.mPermissions != null) {
+                if (mPermissions == null) {
+                    mPermissions = new ArrayMap<>();
+                }
+                final int permissionCount = other.mPermissions.size();
+                for (int i = 0; i < permissionCount; i++) {
+                    String name = other.mPermissions.keyAt(i);
+                    PermissionData permissionData = other.mPermissions.valueAt(i);
+                    mPermissions.put(name, new PermissionData(permissionData));
+                }
             }
         }
 
@@ -154,13 +161,16 @@
         }
         final PermissionsState other = (PermissionsState) obj;
 
-        if (mPermissions == null) {
-            if (other.mPermissions != null) {
+        synchronized (mLock) {
+            if (mPermissions == null) {
+                if (other.mPermissions != null) {
+                    return false;
+                }
+            } else if (!mPermissions.equals(other.mPermissions)) {
                 return false;
             }
-        } else if (!mPermissions.equals(other.mPermissions)) {
-            return false;
         }
+
         if (mPermissionReviewRequired == null) {
             if (other.mPermissionReviewRequired != null) {
                 return false;
@@ -267,12 +277,15 @@
     public boolean hasPermission(String name, int userId) {
         enforceValidUserId(userId);
 
-        if (mPermissions == null) {
-            return false;
+        synchronized (mLock) {
+            if (mPermissions == null) {
+                return false;
+            }
+            PermissionData permissionData = mPermissions.get(name);
+
+            return permissionData != null && permissionData.isGranted(userId);
         }
 
-        PermissionData permissionData = mPermissions.get(name);
-        return permissionData != null && permissionData.isGranted(userId);
     }
 
     /**
@@ -280,14 +293,17 @@
      * whether or not it has been granted.
      */
     public boolean hasRequestedPermission(ArraySet<String> names) {
-        if (mPermissions == null) {
-            return false;
-        }
-        for (int i=names.size()-1; i>=0; i--) {
-            if (mPermissions.get(names.valueAt(i)) != null) {
-                return true;
+        synchronized (mLock) {
+            if (mPermissions == null) {
+                return false;
+            }
+            for (int i=names.size()-1; i>=0; i--) {
+                if (mPermissions.get(names.valueAt(i)) != null) {
+                    return true;
+                }
             }
         }
+
         return false;
     }
 
@@ -308,29 +324,31 @@
     public Set<String> getPermissions(int userId) {
         enforceValidUserId(userId);
 
-        if (mPermissions == null) {
-            return Collections.emptySet();
-        }
-
-        Set<String> permissions = new ArraySet<>(mPermissions.size());
-
-        final int permissionCount = mPermissions.size();
-        for (int i = 0; i < permissionCount; i++) {
-            String permission = mPermissions.keyAt(i);
-
-            if (hasInstallPermission(permission)) {
-                permissions.add(permission);
-                continue;
+        synchronized (mLock) {
+            if (mPermissions == null) {
+                return Collections.emptySet();
             }
 
-            if (userId != UserHandle.USER_ALL) {
-                if (hasRuntimePermission(permission, userId)) {
+            Set<String> permissions = new ArraySet<>(mPermissions.size());
+
+            final int permissionCount = mPermissions.size();
+            for (int i = 0; i < permissionCount; i++) {
+                String permission = mPermissions.keyAt(i);
+
+                if (hasInstallPermission(permission)) {
                     permissions.add(permission);
+                    continue;
+                }
+
+                if (userId != UserHandle.USER_ALL) {
+                    if (hasRuntimePermission(permission, userId)) {
+                        permissions.add(permission);
+                    }
                 }
             }
-        }
 
-        return permissions;
+            return permissions;
+        }
     }
 
     /**
@@ -407,14 +425,20 @@
 
         final boolean mayChangeFlags = flagValues != 0 || flagMask != 0;
 
-        if (mPermissions == null) {
-            if (!mayChangeFlags) {
-                return false;
+        synchronized (mLock) {
+            if (mPermissions == null) {
+                if (!mayChangeFlags) {
+                    return false;
+                }
+                ensurePermissionData(permission);
             }
-            ensurePermissionData(permission);
         }
 
-        PermissionData permissionData = mPermissions.get(permission.getName());
+        PermissionData permissionData = null;
+        synchronized (mLock) {
+            permissionData = mPermissions.get(permission.getName());
+        }
+
         if (permissionData == null) {
             if (!mayChangeFlags) {
                 return false;
@@ -447,14 +471,17 @@
     }
 
     private boolean hasPermissionRequiringReview(int userId) {
-        final int permissionCount = mPermissions.size();
-        for (int i = 0; i < permissionCount; i++) {
-            final PermissionData permission = mPermissions.valueAt(i);
-            if ((permission.getFlags(userId)
-                    & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                return true;
+        synchronized (mLock) {
+            final int permissionCount = mPermissions.size();
+            for (int i = 0; i < permissionCount; i++) {
+                final PermissionData permission = mPermissions.valueAt(i);
+                if ((permission.getFlags(userId)
+                        & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                    return true;
+                }
             }
         }
+
         return false;
     }
 
@@ -462,16 +489,19 @@
             int userId, int flagMask, int flagValues) {
         enforceValidUserId(userId);
 
-        if (mPermissions == null) {
-            return false;
+        synchronized (mLock) {
+            if (mPermissions == null) {
+                return false;
+            }
+            boolean changed = false;
+            final int permissionCount = mPermissions.size();
+            for (int i = 0; i < permissionCount; i++) {
+                PermissionData permissionData = mPermissions.valueAt(i);
+                changed |= permissionData.updateFlags(userId, flagMask, flagValues);
+            }
+
+            return changed;
         }
-        boolean changed = false;
-        final int permissionCount = mPermissions.size();
-        for (int i = 0; i < permissionCount; i++) {
-            PermissionData permissionData = mPermissions.valueAt(i);
-            changed |= permissionData.updateFlags(userId, flagMask, flagValues);
-        }
-        return changed;
     }
 
     /**
@@ -487,17 +517,19 @@
 
         int[] gids = mGlobalGids;
 
-        if (mPermissions != null) {
-            final int permissionCount = mPermissions.size();
-            for (int i = 0; i < permissionCount; i++) {
-                String permission = mPermissions.keyAt(i);
-                if (!hasPermission(permission, userId)) {
-                    continue;
-                }
-                PermissionData permissionData = mPermissions.valueAt(i);
-                final int[] permGids = permissionData.computeGids(userId);
-                if (permGids != NO_GIDS) {
-                    gids = appendInts(gids, permGids);
+        synchronized (mLock) {
+            if (mPermissions != null) {
+                final int permissionCount = mPermissions.size();
+                for (int i = 0; i < permissionCount; i++) {
+                    String permission = mPermissions.keyAt(i);
+                    if (!hasPermission(permission, userId)) {
+                        continue;
+                    }
+                    PermissionData permissionData = mPermissions.valueAt(i);
+                    final int[] permGids = permissionData.computeGids(userId);
+                    if (permGids != NO_GIDS) {
+                        gids = appendInts(gids, permGids);
+                    }
                 }
             }
         }
@@ -527,41 +559,50 @@
      */
     public void reset() {
         mGlobalGids = NO_GIDS;
-        mPermissions = null;
+
+        synchronized (mLock) {
+            mPermissions = null;
+        }
+
         mPermissionReviewRequired = null;
     }
 
     private PermissionState getPermissionState(String name, int userId) {
-        if (mPermissions == null) {
-            return null;
+        synchronized (mLock) {
+            if (mPermissions == null) {
+                return null;
+            }
+            PermissionData permissionData = mPermissions.get(name);
+            if (permissionData == null) {
+                return null;
+            }
+
+            return permissionData.getPermissionState(userId);
         }
-        PermissionData permissionData = mPermissions.get(name);
-        if (permissionData == null) {
-            return null;
-        }
-        return permissionData.getPermissionState(userId);
     }
 
     private List<PermissionState> getPermissionStatesInternal(int userId) {
         enforceValidUserId(userId);
 
-        if (mPermissions == null) {
-            return Collections.emptyList();
-        }
-
-        List<PermissionState> permissionStates = new ArrayList<>();
-
-        final int permissionCount = mPermissions.size();
-        for (int i = 0; i < permissionCount; i++) {
-            PermissionData permissionData = mPermissions.valueAt(i);
-
-            PermissionState permissionState = permissionData.getPermissionState(userId);
-            if (permissionState != null) {
-                permissionStates.add(permissionState);
+        synchronized (mLock) {
+            if (mPermissions == null) {
+                return Collections.emptyList();
             }
-        }
 
-        return permissionStates;
+            List<PermissionState> permissionStates = new ArrayList<>();
+
+            final int permissionCount = mPermissions.size();
+            for (int i = 0; i < permissionCount; i++) {
+                PermissionData permissionData = mPermissions.valueAt(i);
+
+                PermissionState permissionState = permissionData.getPermissionState(userId);
+                if (permissionState != null) {
+                    permissionStates.add(permissionState);
+                }
+            }
+
+            return permissionStates;
+        }
     }
 
     private int grantPermission(BasePermission permission, int userId) {
@@ -597,7 +638,10 @@
         final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
         final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
 
-        PermissionData permissionData = mPermissions.get(permName);
+        PermissionData permissionData = null;
+        synchronized (mLock) {
+            permissionData = mPermissions.get(permName);
+        }
 
         if (!permissionData.revoke(userId)) {
             return PERMISSION_OPERATION_FAILURE;
@@ -635,25 +679,32 @@
 
     private PermissionData ensurePermissionData(BasePermission permission) {
         final String permName = permission.getName();
-        if (mPermissions == null) {
-            mPermissions = new ArrayMap<>();
+
+        synchronized (mLock) {
+            if (mPermissions == null) {
+                mPermissions = new ArrayMap<>();
+            }
+            PermissionData permissionData = mPermissions.get(permName);
+            if (permissionData == null) {
+                permissionData = new PermissionData(permission);
+                mPermissions.put(permName, permissionData);
+            }
+            return permissionData;
         }
-        PermissionData permissionData = mPermissions.get(permName);
-        if (permissionData == null) {
-            permissionData = new PermissionData(permission);
-            mPermissions.put(permName, permissionData);
-        }
-        return permissionData;
+
     }
 
     private void ensureNoPermissionData(String name) {
-        if (mPermissions == null) {
-            return;
+        synchronized (mLock) {
+            if (mPermissions == null) {
+                return;
+            }
+            mPermissions.remove(name);
+            if (mPermissions.isEmpty()) {
+                mPermissions = null;
+            }
         }
-        mPermissions.remove(name);
-        if (mPermissions.isEmpty()) {
-            mPermissions = null;
-        }
+
     }
 
     private static final class PermissionData {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 371ac4f..97af045 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -41,6 +41,7 @@
 import static android.os.Build.VERSION_CODES.O;
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Display.STATE_OFF;
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
@@ -155,6 +156,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
@@ -665,9 +667,6 @@
     SleepToken mDreamingSleepToken;
     SleepToken mScreenOffSleepToken;
     volatile boolean mKeyguardOccluded;
-    boolean mHomePressed;
-    boolean mHomeConsumed;
-    boolean mHomeDoubleTapPending;
     Intent mHomeIntent;
     Intent mCarDockIntent;
     Intent mDeskDockIntent;
@@ -865,7 +864,7 @@
                     launchVoiceAssistWithWakeLock();
                     break;
                 case MSG_POWER_DELAYED_PRESS:
-                    powerPress((Long)msg.obj, msg.arg1 != 0, msg.arg2);
+                    powerPress((Long) msg.obj, msg.arg1 != 0, msg.arg2);
                     finishPowerKeyPress();
                     break;
                 case MSG_POWER_LONG_PRESS:
@@ -1340,7 +1339,7 @@
                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
                     goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
-                    launchHomeFromHotKey();
+                    launchHomeFromHotKey(DEFAULT_DISPLAY);
                     break;
                 case SHORT_PRESS_POWER_GO_HOME:
                     shortPressPowerGoHome();
@@ -1369,7 +1368,8 @@
     }
 
     private void shortPressPowerGoHome() {
-        launchHomeFromHotKey(true /* awakenFromDreams */, false /*respectKeyguard*/);
+        launchHomeFromHotKey(DEFAULT_DISPLAY, true /* awakenFromDreams */,
+                false /*respectKeyguard*/);
         if (isKeyguardShowingAndNotOccluded()) {
             // Notify keyguard so it can do any special handling for the power button since the
             // device will not power off and only launch home.
@@ -1505,7 +1505,8 @@
 
     private void sleepPress() {
         if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
-            launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/);
+            launchHomeFromHotKey(DEFAULT_DISPLAY, false /* awakenDreams */,
+                    true /*respectKeyguard*/);
         }
     }
 
@@ -1683,7 +1684,7 @@
                 Settings.Secure.TV_USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
     }
 
-    private void handleShortPressOnHome() {
+    private void handleShortPressOnHome(int displayId) {
         // Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
         final HdmiControl hdmiControl = getHdmiControl();
         if (hdmiControl != null) {
@@ -1698,7 +1699,7 @@
         }
 
         // Go home!
-        launchHomeFromHotKey();
+        launchHomeFromHotKey(displayId);
     }
 
     /**
@@ -1745,26 +1746,6 @@
         }
     }
 
-    private void handleLongPressOnHome(int deviceId) {
-        if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_NOTHING) {
-            return;
-        }
-        mHomeConsumed = true;
-        performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
-                "Home - Long Press");
-        switch (mLongPressOnHomeBehavior) {
-            case LONG_PRESS_HOME_ALL_APPS:
-                launchAllAppsAction();
-                break;
-            case LONG_PRESS_HOME_ASSIST:
-                launchAssistAction(null, deviceId);
-                break;
-            default:
-                Log.w(TAG, "Undefined home long press behavior: " + mLongPressOnHomeBehavior);
-                break;
-        }
-    }
-
     private void launchAllAppsAction() {
         Intent intent = new Intent(Intent.ACTION_ALL_APPS);
         if (mHasFeatureLeanback) {
@@ -1781,13 +1762,6 @@
         startActivityAsUser(intent, UserHandle.CURRENT);
     }
 
-    private void handleDoubleTapOnHome() {
-        if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
-            mHomeConsumed = true;
-            toggleRecentApps();
-        }
-    }
-
     private void showPictureInPictureMenu(KeyEvent event) {
         if (DEBUG_INPUT) Log.d(TAG, "showPictureInPictureMenu event=" + event);
         mHandler.removeMessages(MSG_SHOW_PICTURE_IN_PICTURE_MENU);
@@ -1803,15 +1777,147 @@
         }
     }
 
-    private final Runnable mHomeDoubleTapTimeoutRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (mHomeDoubleTapPending) {
-                mHomeDoubleTapPending = false;
-                handleShortPressOnHome();
+    /** A handler to handle home keys per display */
+    private class DisplayHomeButtonHandler {
+
+        private final int mDisplayId;
+
+        private boolean mHomeDoubleTapPending;
+        private boolean mHomePressed;
+        private boolean mHomeConsumed;
+
+        private final Runnable mHomeDoubleTapTimeoutRunnable = new Runnable() {
+            @Override
+            public void run() {
+                if (mHomeDoubleTapPending) {
+                    mHomeDoubleTapPending = false;
+                    handleShortPressOnHome(mDisplayId);
+                }
+            }
+        };
+
+        DisplayHomeButtonHandler(int displayId) {
+            mDisplayId = displayId;
+        }
+
+        int handleHomeButton(WindowState win, KeyEvent event) {
+            final boolean keyguardOn = keyguardOn();
+            final int repeatCount = event.getRepeatCount();
+            final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+            final boolean canceled = event.isCanceled();
+
+            if (DEBUG_INPUT) {
+                Log.d(TAG, String.format("handleHomeButton in display#%d mHomePressed = %b",
+                        mDisplayId, mHomePressed));
+            }
+
+            // If we have released the home key, and didn't do anything else
+            // while it was pressed, then it is time to go home!
+            if (!down) {
+                if (mDisplayId == DEFAULT_DISPLAY) {
+                    cancelPreloadRecentApps();
+                }
+
+                mHomePressed = false;
+                if (mHomeConsumed) {
+                    mHomeConsumed = false;
+                    return -1;
+                }
+
+                if (canceled) {
+                    Log.i(TAG, "Ignoring HOME; event canceled.");
+                    return -1;
+                }
+
+                // Delay handling home if a double-tap is possible.
+                if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
+                    mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
+                    mHomeDoubleTapPending = true;
+                    mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
+                            ViewConfiguration.getDoubleTapTimeout());
+                    return -1;
+                }
+
+                handleShortPressOnHome(mDisplayId);
+                return -1;
+            }
+
+            // If a system window has focus, then it doesn't make sense
+            // right now to interact with applications.
+            WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
+            if (attrs != null) {
+                final int type = attrs.type;
+                if (type == TYPE_KEYGUARD_DIALOG
+                        || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+                    // the "app" is keyguard, so give it the key
+                    return 0;
+                }
+                for (int t : WINDOW_TYPES_WHERE_HOME_DOESNT_WORK) {
+                    if (type == t) {
+                        // don't do anything, but also don't pass it to the app
+                        return -1;
+                    }
+                }
+            }
+
+            // Remember that home is pressed and handle special actions.
+            if (repeatCount == 0) {
+                mHomePressed = true;
+                if (mHomeDoubleTapPending) {
+                    mHomeDoubleTapPending = false;
+                    mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
+                    handleDoubleTapOnHome();
+                // TODO(multi-display): Remove display id check once we support recents on
+                // multi-display
+                } else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI
+                        && mDisplayId == DEFAULT_DISPLAY) {
+                    preloadRecentApps();
+                }
+            } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
+                if (!keyguardOn) {
+                    handleLongPressOnHome(event.getDeviceId());
+                }
+            }
+            return -1;
+        }
+
+        private void handleDoubleTapOnHome() {
+            if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
+                mHomeConsumed = true;
+                toggleRecentApps();
             }
         }
-    };
+
+        private void handleLongPressOnHome(int deviceId) {
+            if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_NOTHING) {
+                return;
+            }
+            mHomeConsumed = true;
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+                    "Home - Long Press");
+            switch (mLongPressOnHomeBehavior) {
+                case LONG_PRESS_HOME_ALL_APPS:
+                    launchAllAppsAction();
+                    break;
+                case LONG_PRESS_HOME_ASSIST:
+                    launchAssistAction(null, deviceId);
+                    break;
+                default:
+                    Log.w(TAG, "Undefined home long press behavior: "
+                            + mLongPressOnHomeBehavior);
+                    break;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return String.format("mDisplayId = %d, mHomePressed = %b", mDisplayId, mHomePressed);
+        }
+    }
+
+    /** A DisplayHomeButtonHandler map indexed by display id */
+    private final SparseArray<DisplayHomeButtonHandler> mDisplayHomeButtonHandlers =
+            new SparseArray<>();
 
     private boolean isRoundWindow() {
         return mContext.getResources().getConfiguration().isScreenRound();
@@ -3259,6 +3365,7 @@
             WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
         };
 
+    // TODO(b/117479243): handle it in InputPolicy
     /** {@inheritDoc} */
     @Override
     public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
@@ -3269,11 +3376,11 @@
         final int flags = event.getFlags();
         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         final boolean canceled = event.isCanceled();
+        final int displayId = event.getDisplayId();
 
         if (DEBUG_INPUT) {
             Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
-                    + repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed
-                    + " canceled=" + canceled);
+                    + repeatCount + " keyguardOn=" + keyguardOn + " canceled=" + canceled);
         }
 
         // If we think we might have a volume down & power key chord on the way
@@ -3358,71 +3465,12 @@
         // it handle it, because that gives us the correct 5 second
         // timeout.
         if (keyCode == KeyEvent.KEYCODE_HOME) {
-
-            // If we have released the home key, and didn't do anything else
-            // while it was pressed, then it is time to go home!
-            if (!down) {
-                cancelPreloadRecentApps();
-
-                mHomePressed = false;
-                if (mHomeConsumed) {
-                    mHomeConsumed = false;
-                    return -1;
-                }
-
-                if (canceled) {
-                    Log.i(TAG, "Ignoring HOME; event canceled.");
-                    return -1;
-                }
-
-                // Delay handling home if a double-tap is possible.
-                if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
-                    mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
-                    mHomeDoubleTapPending = true;
-                    mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
-                            ViewConfiguration.getDoubleTapTimeout());
-                    return -1;
-                }
-
-                handleShortPressOnHome();
-                return -1;
+            DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId);
+            if (handler == null) {
+                handler = new DisplayHomeButtonHandler(displayId);
+                mDisplayHomeButtonHandlers.put(displayId, handler);
             }
-
-            // If a system window has focus, then it doesn't make sense
-            // right now to interact with applications.
-            WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
-            if (attrs != null) {
-                final int type = attrs.type;
-                if (type == TYPE_KEYGUARD_DIALOG
-                        || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
-                    // the "app" is keyguard, so give it the key
-                    return 0;
-                }
-                final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
-                for (int i=0; i<typeCount; i++) {
-                    if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
-                        // don't do anything, but also don't pass it to the app
-                        return -1;
-                    }
-                }
-            }
-
-            // Remember that home is pressed and handle special actions.
-            if (repeatCount == 0) {
-                mHomePressed = true;
-                if (mHomeDoubleTapPending) {
-                    mHomeDoubleTapPending = false;
-                    mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
-                    handleDoubleTapOnHome();
-                } else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
-                    preloadRecentApps();
-                }
-            } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
-                if (!keyguardOn) {
-                    handleLongPressOnHome(event.getDeviceId());
-                }
-            }
-            return -1;
+            return handler.handleHomeButton(win, event);
         } else if (keyCode == KeyEvent.KEYCODE_MENU) {
             // Hijack modified menu keys for debugging features
             final int chordBug = KeyEvent.META_SHIFT_ON;
@@ -3820,6 +3868,7 @@
         }
     }
 
+    // TODO(b/117479243): handle it in InputPolicy
     /** {@inheritDoc} */
     @Override
     public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
@@ -3862,7 +3911,7 @@
                         event.getAction(), fallbackAction.keyCode,
                         event.getRepeatCount(), fallbackAction.metaState,
                         event.getDeviceId(), event.getScanCode(),
-                        flags, event.getSource(), null);
+                        flags, event.getSource(), event.getDisplayId(), null);
 
                 if (!interceptFallback(win, fallbackEvent, policyFlags)) {
                     fallbackEvent.recycle();
@@ -3991,8 +4040,12 @@
     }
 
     private void startActivityAsUser(Intent intent, UserHandle handle) {
+        startActivityAsUser(intent, null, handle);
+    }
+
+    private void startActivityAsUser(Intent intent, Bundle bundle, UserHandle handle) {
         if (isUserSetupComplete()) {
-            mContext.startActivityAsUser(intent, handle);
+            mContext.startActivityAsUser(intent, bundle, handle);
         } else {
             Slog.i(TAG, "Not starting activity because user setup is in progress: " + intent);
         }
@@ -4067,15 +4120,16 @@
         }
     }
 
-    void launchHomeFromHotKey() {
-        launchHomeFromHotKey(true /* awakenFromDreams */, true /*respectKeyguard*/);
+    void launchHomeFromHotKey(int displayId) {
+        launchHomeFromHotKey(displayId, true /* awakenFromDreams */, true /*respectKeyguard*/);
     }
 
     /**
      * A home key -> launch home action was detected.  Take the appropriate action
      * given the situation with the keyguard.
      */
-    void launchHomeFromHotKey(final boolean awakenFromDreams, final boolean respectKeyguard) {
+    void launchHomeFromHotKey(int displayId, final boolean awakenFromDreams,
+            final boolean respectKeyguard) {
         // Abort possibly stuck animations.
         mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
 
@@ -4092,7 +4146,7 @@
                     @Override
                     public void onKeyguardExitResult(boolean success) {
                         if (success) {
-                            startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
+                            startDockOrHome(displayId, true /*fromHomeKey*/, awakenFromDreams);
                         }
                     }
                 });
@@ -4113,7 +4167,7 @@
             hideRecentApps(false, true);
         } else {
             // Otherwise, just launch Home
-            startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
+            startDockOrHome(displayId, true /*fromHomeKey*/, awakenFromDreams);
         }
     }
 
@@ -5677,7 +5731,7 @@
         mDefaultDisplayPolicy.setHdmiPlugged(plugged, true /* force */);
     }
 
-
+    // TODO(b/117479243): handle it in InputPolicy
     /** {@inheritDoc} */
     @Override
     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
@@ -5690,6 +5744,7 @@
         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         final boolean canceled = event.isCanceled();
         final int keyCode = event.getKeyCode();
+        final int displayId = event.getDisplayId();
 
         final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
 
@@ -6193,7 +6248,7 @@
         return true;
     }
 
-
+    // TODO(b/117479243): handle it in InputPolicy
     /** {@inheritDoc} */
     @Override
     public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
@@ -7269,7 +7324,7 @@
         return null;
     }
 
-    void startDockOrHome(boolean fromHomeKey, boolean awakenFromDreams) {
+    void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams) {
         try {
             ActivityManager.getService().stopAppSwitches();
         } catch (RemoteException e) {}
@@ -7299,8 +7354,13 @@
         } else {
             intent = mHomeIntent;
         }
+        final Bundle bundle = getLaunchDisplayIdBundle(displayId);
+        startActivityAsUser(intent, bundle, UserHandle.CURRENT);
+    }
 
-        startActivityAsUser(intent, UserHandle.CURRENT);
+    private @Nullable Bundle getLaunchDisplayIdBundle(int displayId) {
+        return (displayId == INVALID_DISPLAY) ? null
+                : ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
     }
 
     /**
@@ -7314,7 +7374,7 @@
         }
         if (false) {
             // This code always brings home to the front.
-            startDockOrHome(false /*fromHomeKey*/, true /* awakenFromDreams */);
+            startDockOrHome(DEFAULT_DISPLAY, false /*fromHomeKey*/, true /* awakenFromDreams */);
         } else {
             // This code brings home to the front or, if it is already
             // at the front, puts the device to sleep.
@@ -7325,7 +7385,7 @@
                 } else {
                     ActivityManager.getService().stopAppSwitches();
                     sendCloseSystemWindows();
-                    Intent dock = createHomeDockIntent();
+                    final Intent dock = createHomeDockIntent();
                     if (dock != null) {
                         int result = ActivityTaskManager.getService()
                                 .startActivityAsUser(null, null, dock,
@@ -7891,8 +7951,7 @@
         // requires freezing various Surface states and won't work well
         // with animations, so we disable it in the animation case for now.
         if (w != null && !w.isAnimatingLw() &&
-                ((w.getAttrs().rotationAnimation == ROTATION_ANIMATION_JUMPCUT) ||
-                        (w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS))) {
+                w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) {
             return true;
         }
         return false;
@@ -8014,7 +8073,13 @@
                 pw.print(incallBackBehaviorToString(mIncallBackBehavior));
                 pw.print(" mEndcallBehavior=");
                 pw.println(endcallBehaviorToString(mEndcallBehavior));
-        pw.print(prefix); pw.print("mHomePressed="); pw.println(mHomePressed);
+        pw.print(prefix);
+        // TODO(b/117479243): handle it in InputPolicy
+        pw.print("mDisplayHomeButtonHandlers=");
+        for (int i = 0; i < mDisplayHomeButtonHandlers.size(); i++) {
+            final int key = mDisplayHomeButtonHandlers.keyAt(i);
+            pw.println(mDisplayHomeButtonHandlers.get(key));
+        }
         pw.print(prefix); pw.print("mDockLayer="); pw.print(mDockLayer);
                 pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer);
         pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 1e0b52a..ae1090c 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -1,13 +1,11 @@
 package com.android.server.policy.keyguard;
 
-import static android.view.Display.INVALID_DISPLAY;
 import static com.android.server.wm.KeyguardServiceDelegateProto.INTERACTIVE_STATE;
 import static com.android.server.wm.KeyguardServiceDelegateProto.OCCLUDED;
 import static com.android.server.wm.KeyguardServiceDelegateProto.SCREEN_STATE;
 import static com.android.server.wm.KeyguardServiceDelegateProto.SECURE;
 import static com.android.server.wm.KeyguardServiceDelegateProto.SHOWING;
 
-import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -212,10 +210,10 @@
             mHandler.post(() -> {
                 try {
                     // There are no longer any keyguard windows on secondary displays, so pass
-                    // INVALID_DISPLAY. All that means is that showWhenLocked activities on
-                    // secondary displays now get to show.
+                    // {@code null}. All that means is that showWhenLocked activities on
+                    // external displays now get to show.
                     ActivityTaskManager.getService().setLockScreenShown(true /* keyguardShowing */,
-                            false /* aodShowing */, INVALID_DISPLAY);
+                            false /* aodShowing */, null /* secondaryDisplaysShowing */);
                 } catch (RemoteException e) {
                     // Local call.
                 }
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 4f8e6b6..6d7b04c 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -57,16 +57,32 @@
 
     public static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE.
 
-    // Secure setting for GPS behavior when battery saver mode is on.
-    public static final String SECURE_KEY_GPS_MODE = "batterySaverGpsMode";
-
     private static final String KEY_GPS_MODE = "gps_mode";
     private static final String KEY_VIBRATION_DISABLED = "vibration_disabled";
     private static final String KEY_ANIMATION_DISABLED = "animation_disabled";
     private static final String KEY_SOUNDTRIGGER_DISABLED = "soundtrigger_disabled";
-    private static final String KEY_FIREWALL_DISABLED = "firewall_disabled";
+
+    /**
+     * Disable turning on the network firewall when Battery Saver is turned on.
+     * If set to false, the firewall WILL be turned on when Battery Saver is turned on.
+     * If set to true, the firewall WILL NOT be turned on when Battery Saver is turned on.
+     */
+    private static final String KEY_ACTIVATE_FIREWALL_DISABLED = "firewall_disabled";
+
+    /**
+     * Disable turning on the special low power screen brightness dimming when Battery Saver is
+     * turned on.
+     * If set to false, the screen brightness dimming WILL be turned on by Battery Saver.
+     * If set to true, the screen brightness WILL NOT be turned on by Battery Saver.
+     */
     private static final String KEY_ADJUST_BRIGHTNESS_DISABLED = "adjust_brightness_disabled";
-    private static final String KEY_DATASAVER_DISABLED = "datasaver_disabled";
+
+    /**
+     * Disable turning on Data Saver when Battery Saver is turned on.
+     * If set to false, Data Saver WILL be turned on when Battery Saver is turned on.
+     * If set to true, Data Saver WILL NOT be turned on when Battery Saver is turned on.
+     */
+    private static final String KEY_ACTIVATE_DATASAVER_DISABLED = "datasaver_disabled";
     private static final String KEY_LAUNCH_BOOST_DISABLED = "launch_boost_disabled";
     private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
     private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
@@ -82,6 +98,28 @@
     private static final String KEY_CPU_FREQ_INTERACTIVE = "cpufreq-i";
     private static final String KEY_CPU_FREQ_NONINTERACTIVE = "cpufreq-n";
 
+    private static final Policy sDefaultPolicy = new Policy(
+            0.5f,  /* adjustBrightnessFactor */
+            true,  /* deferFullBackup */
+            true,  /* deferKeyValueBackup */
+            false, /* disableAnimation */
+            true,  /* disableAod */
+            true,  /* disableLaunchBoost */
+            true,  /* disableOptionalSensors */
+            true,  /* disableSoundTrigger */
+            true,  /* disableVibration */
+            false, /* enableAdjustBrightness */
+            false, /* enableDataSaver */
+            true,  /* enableFirewall */
+            false, /* enableQuickDoze */
+            new ArrayMap<>(), /* filesForInteractive */
+            new ArrayMap<>(), /* filesForNoninteractive */
+            true, /* forceAllAppsStandby */
+            true, /* forceBackgroundCheck */
+            PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF, /* gpsMode */
+            false /* sendTronLog */
+    );
+
     private final Object mLock;
     private final Handler mHandler;
 
@@ -101,145 +139,20 @@
     private String mEventLogKeys;
 
     /**
-     * {@code true} if vibration is disabled in battery saver mode.
-     *
-     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
-     * @see #KEY_VIBRATION_DISABLED
-     */
-    @GuardedBy("mLock")
-    private boolean mVibrationDisabledConfig;
-
-    /**
-     * Whether vibration should *really* be disabled -- i.e. {@link #mVibrationDisabledConfig}
+     * Whether vibration should *really* be disabled -- i.e. {@link Policy#disableVibration}
      * is true *and* {@link #mAccessibilityEnabled} is false.
      */
     @GuardedBy("mLock")
-    private boolean mVibrationDisabledEffective;
+    private boolean mDisableVibrationEffective;
 
     /**
-     * {@code true} if animation is disabled in battery saver mode.
-     *
-     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
-     * @see #KEY_ANIMATION_DISABLED
+     * Whether accessibility is currently enabled or not.
      */
     @GuardedBy("mLock")
-    private boolean mAnimationDisabled;
+    private boolean mAccessibilityEnabled;
 
-    /**
-     * {@code true} if sound trigger is disabled in battery saver mode
-     * in battery saver mode.
-     *
-     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
-     * @see #KEY_SOUNDTRIGGER_DISABLED
-     */
     @GuardedBy("mLock")
-    private boolean mSoundTriggerDisabled;
-
-    /**
-     * {@code true} if full backup is deferred in battery saver mode.
-     *
-     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
-     * @see #KEY_FULLBACKUP_DEFERRED
-     */
-    @GuardedBy("mLock")
-    private boolean mFullBackupDeferred;
-
-    /**
-     * {@code true} if key value backup is deferred in battery saver mode.
-     *
-     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
-     * @see #KEY_KEYVALUE_DEFERRED
-     */
-    @GuardedBy("mLock")
-    private boolean mKeyValueBackupDeferred;
-
-    /**
-     * {@code true} if network policy firewall is disabled in battery saver mode.
-     *
-     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
-     * @see #KEY_FIREWALL_DISABLED
-     */
-    @GuardedBy("mLock")
-    private boolean mFireWallDisabled;
-
-    /**
-     * {@code true} if adjust brightness is disabled in battery saver mode.
-     *
-     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
-     * @see #KEY_ADJUST_BRIGHTNESS_DISABLED
-     */
-    @GuardedBy("mLock")
-    private boolean mAdjustBrightnessDisabled;
-
-    /**
-     * {@code true} if data saver is disabled in battery saver mode.
-     *
-     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
-     * @see #KEY_DATASAVER_DISABLED
-     */
-    @GuardedBy("mLock")
-    private boolean mDataSaverDisabled;
-
-    /**
-     * {@code true} if launch boost should be disabled on battery saver.
-     */
-    @GuardedBy("mLock")
-    private boolean mLaunchBoostDisabled;
-
-    /**
-     * This is the flag to decide the gps mode in battery saver mode.
-     *
-     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
-     * @see #KEY_GPS_MODE
-     */
-    @GuardedBy("mLock")
-    private int mGpsMode;
-
-    /**
-     * This is the flag to decide the how much to adjust the screen brightness. This is
-     * the float value from 0 to 1 where 1 means don't change brightness.
-     *
-     * @see Settings.Global#BATTERY_SAVER_CONSTANTS
-     * @see #KEY_ADJUST_BRIGHTNESS_FACTOR
-     */
-    @GuardedBy("mLock")
-    private float mAdjustBrightnessFactor;
-
-    /**
-     * Whether to put all apps in the stand-by mode.
-     */
-    @GuardedBy("mLock")
-    private boolean mForceAllAppsStandby;
-
-    /**
-     * Whether to put all apps in the stand-by mode.
-     */
-    @GuardedBy("mLock")
-    private boolean mForceBackgroundCheck;
-
-    /**
-     * Whether to show non-essential sensors (e.g. edge sensors) or not.
-     */
-    @GuardedBy("mLock")
-    private boolean mOptionalSensorsDisabled;
-
-    /**
-     * Whether AOD is enabled or not.
-     */
-    @GuardedBy("mLock")
-    private boolean mAodDisabled;
-
-    /**
-     * Whether Quick Doze is enabled or not.
-     */
-    @GuardedBy("mLock")
-    private boolean mQuickDozeEnabled;
-
-    /**
-     * Whether BatterySavingStats should send tron events.
-     */
-    @GuardedBy("mLock")
-    private boolean mSendTronLog;
+    private Policy mCurrPolicy = sDefaultPolicy;
 
     private final Context mContext;
     private final ContentResolver mContentResolver;
@@ -248,30 +161,6 @@
     @GuardedBy("mLock")
     private final List<BatterySaverPolicyListener> mListeners = new ArrayList<>();
 
-    /**
-     * List of [Filename -> content] that should be written when battery saver is activated
-     * and the device is interactive.
-     *
-     * We use this to change the max CPU frequencies.
-     */
-    @GuardedBy("mLock")
-    private ArrayMap<String, String> mFilesForInteractive;
-
-    /**
-     * List of [Filename -> content] that should be written when battery saver is activated
-     * and the device is non-interactive.
-     *
-     * We use this to change the max CPU frequencies.
-     */
-    @GuardedBy("mLock")
-    private ArrayMap<String, String> mFilesForNoninteractive;
-
-    /**
-     * Whether accessibility is enabled or not.
-     */
-    @GuardedBy("mLock")
-    private boolean mAccessibilityEnabled;
-
     public interface BatterySaverPolicyListener {
         void onBatterySaverPolicyChanged(BatterySaverPolicy policy);
     }
@@ -379,36 +268,7 @@
 
         final KeyValueListParser parser = new KeyValueListParser(',');
 
-        // Non-device-specific parameters.
-        try {
-            parser.setString(setting);
-        } catch (IllegalArgumentException e) {
-            Slog.wtf(TAG, "Bad battery saver constants: " + setting);
-        }
-
-        mVibrationDisabledConfig = parser.getBoolean(KEY_VIBRATION_DISABLED, true);
-        mAnimationDisabled = parser.getBoolean(KEY_ANIMATION_DISABLED, false);
-        mSoundTriggerDisabled = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED, true);
-        mFullBackupDeferred = parser.getBoolean(KEY_FULLBACKUP_DEFERRED, true);
-        mKeyValueBackupDeferred = parser.getBoolean(KEY_KEYVALUE_DEFERRED, true);
-        mFireWallDisabled = parser.getBoolean(KEY_FIREWALL_DISABLED, false);
-        mAdjustBrightnessDisabled = parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, true);
-        mAdjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
-        mDataSaverDisabled = parser.getBoolean(KEY_DATASAVER_DISABLED, true);
-        mLaunchBoostDisabled = parser.getBoolean(KEY_LAUNCH_BOOST_DISABLED, true);
-        mForceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY, true);
-        mForceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK, true);
-        mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
-        mAodDisabled = parser.getBoolean(KEY_AOD_DISABLED, true);
-        mQuickDozeEnabled = parser.getBoolean(KEY_QUICK_DOZE_ENABLED, false);
-        mSendTronLog = parser.getBoolean(KEY_SEND_TRON_LOG, false);
-
-        // Get default value from Settings.Secure
-        final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
-                PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
-        mGpsMode = parser.getInt(KEY_GPS_MODE, defaultGpsMode);
-
-        // Non-device-specific parameters.
+        // Device-specific parameters.
         try {
             parser.setString(deviceSpecificSetting);
         } catch (IllegalArgumentException e) {
@@ -416,41 +276,280 @@
                     + deviceSpecificSetting);
         }
 
-        mFilesForInteractive = (new CpuFrequencies()).parseString(
-                parser.getString(KEY_CPU_FREQ_INTERACTIVE, "")).toSysFileMap();
+        final String cpuFreqInteractive = parser.getString(KEY_CPU_FREQ_INTERACTIVE, "");
+        final String cpuFreqNoninteractive = parser.getString(KEY_CPU_FREQ_NONINTERACTIVE, "");
 
-        mFilesForNoninteractive = (new CpuFrequencies()).parseString(
-                parser.getString(KEY_CPU_FREQ_NONINTERACTIVE, "")).toSysFileMap();
+        // Non-device-specific parameters.
+        try {
+            parser.setString(setting);
+        } catch (IllegalArgumentException e) {
+            Slog.wtf(TAG, "Bad battery saver constants: " + setting);
+        }
+
+        float adjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR,
+                sDefaultPolicy.adjustBrightnessFactor);
+        boolean deferFullBackup = parser.getBoolean(KEY_FULLBACKUP_DEFERRED,
+                sDefaultPolicy.deferFullBackup);
+        boolean deferKeyValueBackup = parser.getBoolean(KEY_KEYVALUE_DEFERRED,
+                sDefaultPolicy.deferKeyValueBackup);
+        boolean disableAnimation = parser.getBoolean(KEY_ANIMATION_DISABLED,
+                sDefaultPolicy.disableAnimation);
+        boolean disableAod = parser.getBoolean(KEY_AOD_DISABLED, sDefaultPolicy.disableAod);
+        boolean disableLaunchBoost = parser.getBoolean(KEY_LAUNCH_BOOST_DISABLED,
+                sDefaultPolicy.disableLaunchBoost);
+        boolean disableOptionalSensors = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED,
+                sDefaultPolicy.disableOptionalSensors);
+        boolean disableSoundTrigger = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED,
+                sDefaultPolicy.disableSoundTrigger);
+        boolean disableVibrationConfig = parser.getBoolean(KEY_VIBRATION_DISABLED,
+                sDefaultPolicy.disableVibration);
+        boolean enableAdjustBrightness = !parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED,
+                !sDefaultPolicy.enableAdjustBrightness);
+        boolean enableDataSaver = !parser.getBoolean(KEY_ACTIVATE_DATASAVER_DISABLED,
+                !sDefaultPolicy.enableDataSaver);
+        boolean enableFirewall = !parser.getBoolean(KEY_ACTIVATE_FIREWALL_DISABLED,
+                !sDefaultPolicy.enableFirewall);
+        boolean enableQuickDoze = parser.getBoolean(KEY_QUICK_DOZE_ENABLED,
+                sDefaultPolicy.enableQuickDoze);
+        boolean forceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY,
+                sDefaultPolicy.forceAllAppsStandby);
+        boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK,
+                sDefaultPolicy.forceBackgroundCheck);
+        int gpsMode = parser.getInt(KEY_GPS_MODE, sDefaultPolicy.gpsMode);
+        boolean sendTronLog = parser.getBoolean(KEY_SEND_TRON_LOG, sDefaultPolicy.sendTronLog);
+
+        mCurrPolicy = new Policy(
+                adjustBrightnessFactor,
+                deferFullBackup,
+                deferKeyValueBackup,
+                disableAnimation,
+                disableAod,
+                disableLaunchBoost,
+                disableOptionalSensors,
+                disableSoundTrigger,
+                /* disableVibration */
+                disableVibrationConfig,
+                enableAdjustBrightness,
+                enableDataSaver,
+                enableFirewall,
+                enableQuickDoze,
+                /* filesForInteractive */
+                (new CpuFrequencies()).parseString(cpuFreqInteractive).toSysFileMap(),
+                /* filesForNoninteractive */
+                (new CpuFrequencies()).parseString(cpuFreqNoninteractive).toSysFileMap(),
+                forceAllAppsStandby,
+                forceBackgroundCheck,
+                gpsMode,
+                sendTronLog
+        );
 
         // Update the effective policy.
-        mVibrationDisabledEffective = mVibrationDisabledConfig
+        mDisableVibrationEffective = mCurrPolicy.disableVibration
                 && !mAccessibilityEnabled; // Don't disable vibration when accessibility is on.
 
         final StringBuilder sb = new StringBuilder();
 
-        if (mForceAllAppsStandby) sb.append("A");
-        if (mForceBackgroundCheck) sb.append("B");
+        if (mCurrPolicy.forceAllAppsStandby) sb.append("A");
+        if (mCurrPolicy.forceBackgroundCheck) sb.append("B");
 
-        if (mVibrationDisabledEffective) sb.append("v");
-        if (mAnimationDisabled) sb.append("a");
-        if (mSoundTriggerDisabled) sb.append("s");
-        if (mFullBackupDeferred) sb.append("F");
-        if (mKeyValueBackupDeferred) sb.append("K");
-        if (!mFireWallDisabled) sb.append("f");
-        if (!mDataSaverDisabled) sb.append("d");
-        if (!mAdjustBrightnessDisabled) sb.append("b");
+        if (mDisableVibrationEffective) sb.append("v");
+        if (mCurrPolicy.disableAnimation) sb.append("a");
+        if (mCurrPolicy.disableSoundTrigger) sb.append("s");
+        if (mCurrPolicy.deferFullBackup) sb.append("F");
+        if (mCurrPolicy.deferKeyValueBackup) sb.append("K");
+        if (mCurrPolicy.enableFirewall) sb.append("f");
+        if (mCurrPolicy.enableDataSaver) sb.append("d");
+        if (mCurrPolicy.enableAdjustBrightness) sb.append("b");
 
-        if (mLaunchBoostDisabled) sb.append("l");
-        if (mOptionalSensorsDisabled) sb.append("S");
-        if (mAodDisabled) sb.append("o");
-        if (mQuickDozeEnabled) sb.append("q");
-        if (mSendTronLog) sb.append("t");
+        if (mCurrPolicy.disableLaunchBoost) sb.append("l");
+        if (mCurrPolicy.disableOptionalSensors) sb.append("S");
+        if (mCurrPolicy.disableAod) sb.append("o");
+        if (mCurrPolicy.enableQuickDoze) sb.append("q");
+        if (mCurrPolicy.sendTronLog) sb.append("t");
 
-        sb.append(mGpsMode);
+        sb.append(mCurrPolicy.gpsMode);
 
         mEventLogKeys = sb.toString();
 
-        mBatterySavingStats.setSendTronLog(mSendTronLog);
+        mBatterySavingStats.setSendTronLog(mCurrPolicy.sendTronLog);
+    }
+
+    private static class Policy {
+        /**
+         * This is the flag to decide the how much to adjust the screen brightness. This is
+         * the float value from 0 to 1 where 1 means don't change brightness.
+         *
+         * @see Settings.Global#BATTERY_SAVER_CONSTANTS
+         * @see #KEY_ADJUST_BRIGHTNESS_FACTOR
+         */
+        public final float adjustBrightnessFactor;
+
+        /**
+         * {@code true} if full backup is deferred in battery saver mode.
+         *
+         * @see Settings.Global#BATTERY_SAVER_CONSTANTS
+         * @see #KEY_FULLBACKUP_DEFERRED
+         */
+        public final boolean deferFullBackup;
+
+        /**
+         * {@code true} if key value backup is deferred in battery saver mode.
+         *
+         * @see Settings.Global#BATTERY_SAVER_CONSTANTS
+         * @see #KEY_KEYVALUE_DEFERRED
+         */
+        public final boolean deferKeyValueBackup;
+
+        /**
+         * {@code true} if animation is disabled in battery saver mode.
+         *
+         * @see Settings.Global#BATTERY_SAVER_CONSTANTS
+         * @see #KEY_ANIMATION_DISABLED
+         */
+        public final boolean disableAnimation;
+
+        /**
+         * {@code true} if AOD is disabled in battery saver mode.
+         */
+        public final boolean disableAod;
+
+        /**
+         * {@code true} if launch boost should be disabled on battery saver.
+         */
+        public final boolean disableLaunchBoost;
+
+        /**
+         * Whether to show non-essential sensors (e.g. edge sensors) or not.
+         */
+        public final boolean disableOptionalSensors;
+
+        /**
+         * {@code true} if sound trigger is disabled in battery saver mode
+         * in battery saver mode.
+         *
+         * @see Settings.Global#BATTERY_SAVER_CONSTANTS
+         * @see #KEY_SOUNDTRIGGER_DISABLED
+         */
+        public final boolean disableSoundTrigger;
+
+        /**
+         * {@code true} if vibration is disabled in battery saver mode.
+         *
+         * @see Settings.Global#BATTERY_SAVER_CONSTANTS
+         * @see #KEY_VIBRATION_DISABLED
+         */
+        public final boolean disableVibration;
+
+        /**
+         * {@code true} if low power mode brightness adjustment should be turned on in battery saver
+         * mode.
+         *
+         * @see Settings.Global#BATTERY_SAVER_CONSTANTS
+         * @see #KEY_ADJUST_BRIGHTNESS_DISABLED
+         */
+        public final boolean enableAdjustBrightness;
+
+        /**
+         * {@code true} if data saver should be turned on in battery saver mode.
+         *
+         * @see Settings.Global#BATTERY_SAVER_CONSTANTS
+         * @see #KEY_ACTIVATE_DATASAVER_DISABLED
+         */
+        public final boolean enableDataSaver;
+
+        /**
+         * {@code true} if network policy firewall should be turned on in battery saver mode.
+         *
+         * @see Settings.Global#BATTERY_SAVER_CONSTANTS
+         * @see #KEY_ACTIVATE_FIREWALL_DISABLED
+         */
+        public final boolean enableFirewall;
+
+        /**
+         * Whether Quick Doze is enabled or not.
+         */
+        public final boolean enableQuickDoze;
+
+        /**
+         * List of [Filename -> content] that should be written when battery saver is activated
+         * and the device is interactive.
+         *
+         * We use this to change the max CPU frequencies.
+         */
+        public final ArrayMap<String, String> filesForInteractive;
+
+        /**
+         * List of [Filename -> content] that should be written when battery saver is activated
+         * and the device is non-interactive.
+         *
+         * We use this to change the max CPU frequencies.
+         */
+        public final ArrayMap<String, String> filesForNoninteractive;
+
+        /**
+         * Whether to put all apps in the stand-by mode.
+         */
+        public final boolean forceAllAppsStandby;
+
+        /**
+         * Whether to put all apps in the stand-by mode.
+         */
+        public final boolean forceBackgroundCheck;
+
+        /**
+         * This is the flag to decide the gps mode in battery saver mode.
+         *
+         * @see Settings.Global#BATTERY_SAVER_CONSTANTS
+         * @see #KEY_GPS_MODE
+         */
+        public final int gpsMode;
+
+        /**
+         * Whether BatterySavingStats should send tron events.
+         */
+        public final boolean sendTronLog;
+
+        Policy(
+                float adjustBrightnessFactor,
+                boolean deferFullBackup,
+                boolean deferKeyValueBackup,
+                boolean disableAnimation,
+                boolean disableAod,
+                boolean disableLaunchBoost,
+                boolean disableOptionalSensors,
+                boolean disableSoundTrigger,
+                boolean disableVibration,
+                boolean enableAdjustBrightness,
+                boolean enableDataSaver,
+                boolean enableFirewall,
+                boolean enableQuickDoze,
+                ArrayMap<String, String> filesForInteractive,
+                ArrayMap<String, String> filesForNoninteractive,
+                boolean forceAllAppsStandby,
+                boolean forceBackgroundCheck,
+                int gpsMode,
+                boolean sendTronLog) {
+
+            this.adjustBrightnessFactor = adjustBrightnessFactor;
+            this.deferFullBackup = deferFullBackup;
+            this.deferKeyValueBackup = deferKeyValueBackup;
+            this.disableAnimation = disableAnimation;
+            this.disableAod = disableAod;
+            this.disableLaunchBoost = disableLaunchBoost;
+            this.disableOptionalSensors = disableOptionalSensors;
+            this.disableSoundTrigger = disableSoundTrigger;
+            this.disableVibration = disableVibration;
+            this.enableAdjustBrightness = enableAdjustBrightness;
+            this.enableDataSaver = enableDataSaver;
+            this.enableFirewall = enableFirewall;
+            this.enableQuickDoze = enableQuickDoze;
+            this.filesForInteractive = filesForInteractive;
+            this.filesForNoninteractive = filesForNoninteractive;
+            this.forceAllAppsStandby = forceAllAppsStandby;
+            this.forceBackgroundCheck = forceBackgroundCheck;
+            this.gpsMode = gpsMode;
+            this.sendTronLog = sendTronLog;
+        }
     }
 
     /**
@@ -473,47 +572,47 @@
             switch (type) {
                 case ServiceType.GPS:
                     return builder.setBatterySaverEnabled(realMode)
-                            .setGpsMode(mGpsMode)
+                            .setGpsMode(mCurrPolicy.gpsMode)
                             .build();
                 case ServiceType.ANIMATION:
-                    return builder.setBatterySaverEnabled(mAnimationDisabled)
+                    return builder.setBatterySaverEnabled(mCurrPolicy.disableAnimation)
                             .build();
                 case ServiceType.FULL_BACKUP:
-                    return builder.setBatterySaverEnabled(mFullBackupDeferred)
+                    return builder.setBatterySaverEnabled(mCurrPolicy.deferFullBackup)
                             .build();
                 case ServiceType.KEYVALUE_BACKUP:
-                    return builder.setBatterySaverEnabled(mKeyValueBackupDeferred)
+                    return builder.setBatterySaverEnabled(mCurrPolicy.deferKeyValueBackup)
                             .build();
                 case ServiceType.NETWORK_FIREWALL:
-                    return builder.setBatterySaverEnabled(!mFireWallDisabled)
+                    return builder.setBatterySaverEnabled(mCurrPolicy.enableFirewall)
                             .build();
                 case ServiceType.SCREEN_BRIGHTNESS:
-                    return builder.setBatterySaverEnabled(!mAdjustBrightnessDisabled)
-                            .setBrightnessFactor(mAdjustBrightnessFactor)
+                    return builder.setBatterySaverEnabled(mCurrPolicy.enableAdjustBrightness)
+                            .setBrightnessFactor(mCurrPolicy.adjustBrightnessFactor)
                             .build();
                 case ServiceType.DATA_SAVER:
-                    return builder.setBatterySaverEnabled(!mDataSaverDisabled)
+                    return builder.setBatterySaverEnabled(mCurrPolicy.enableDataSaver)
                             .build();
                 case ServiceType.SOUND:
-                    return builder.setBatterySaverEnabled(mSoundTriggerDisabled)
+                    return builder.setBatterySaverEnabled(mCurrPolicy.disableSoundTrigger)
                             .build();
                 case ServiceType.VIBRATION:
-                    return builder.setBatterySaverEnabled(mVibrationDisabledEffective)
+                    return builder.setBatterySaverEnabled(mDisableVibrationEffective)
                             .build();
                 case ServiceType.FORCE_ALL_APPS_STANDBY:
-                    return builder.setBatterySaverEnabled(mForceAllAppsStandby)
+                    return builder.setBatterySaverEnabled(mCurrPolicy.forceAllAppsStandby)
                             .build();
                 case ServiceType.FORCE_BACKGROUND_CHECK:
-                    return builder.setBatterySaverEnabled(mForceBackgroundCheck)
+                    return builder.setBatterySaverEnabled(mCurrPolicy.forceBackgroundCheck)
                             .build();
                 case ServiceType.OPTIONAL_SENSORS:
-                    return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
+                    return builder.setBatterySaverEnabled(mCurrPolicy.disableOptionalSensors)
                             .build();
                 case ServiceType.AOD:
-                    return builder.setBatterySaverEnabled(mAodDisabled)
+                    return builder.setBatterySaverEnabled(mCurrPolicy.disableAod)
                             .build();
                 case ServiceType.QUICK_DOZE:
-                    return builder.setBatterySaverEnabled(mQuickDozeEnabled)
+                    return builder.setBatterySaverEnabled(mCurrPolicy.enableQuickDoze)
                             .build();
                 default:
                     return builder.setBatterySaverEnabled(realMode)
@@ -524,19 +623,20 @@
 
     public int getGpsMode() {
         synchronized (mLock) {
-            return mGpsMode;
+            return mCurrPolicy.gpsMode;
         }
     }
 
     public ArrayMap<String, String> getFileValues(boolean interactive) {
         synchronized (mLock) {
-            return interactive ? mFilesForInteractive : mFilesForNoninteractive;
+            return interactive ? mCurrPolicy.filesForInteractive
+                    : mCurrPolicy.filesForNoninteractive;
         }
     }
 
     public boolean isLaunchBoostDisabled() {
         synchronized (mLock) {
-            return mLaunchBoostDisabled;
+            return mCurrPolicy.disableLaunchBoost;
         }
     }
 
@@ -560,31 +660,35 @@
 
             pw.println();
             pw.println("  mAccessibilityEnabled=" + mAccessibilityEnabled);
-            pw.println("  " + KEY_VIBRATION_DISABLED + ":config=" + mVibrationDisabledConfig);
-            pw.println("  " + KEY_VIBRATION_DISABLED + ":effective=" + mVibrationDisabledEffective);
-            pw.println("  " + KEY_ANIMATION_DISABLED + "=" + mAnimationDisabled);
-            pw.println("  " + KEY_FULLBACKUP_DEFERRED + "=" + mFullBackupDeferred);
-            pw.println("  " + KEY_KEYVALUE_DEFERRED + "=" + mKeyValueBackupDeferred);
-            pw.println("  " + KEY_FIREWALL_DISABLED + "=" + mFireWallDisabled);
-            pw.println("  " + KEY_DATASAVER_DISABLED + "=" + mDataSaverDisabled);
-            pw.println("  " + KEY_LAUNCH_BOOST_DISABLED + "=" + mLaunchBoostDisabled);
-            pw.println("  " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled);
-            pw.println("  " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor);
-            pw.println("  " + KEY_GPS_MODE + "=" + mGpsMode);
-            pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY + "=" + mForceAllAppsStandby);
-            pw.println("  " + KEY_FORCE_BACKGROUND_CHECK + "=" + mForceBackgroundCheck);
-            pw.println("  " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
-            pw.println("  " + KEY_AOD_DISABLED + "=" + mAodDisabled);
-            pw.println("  " + KEY_QUICK_DOZE_ENABLED + "=" + mQuickDozeEnabled);
-            pw.println("  " + KEY_SEND_TRON_LOG + "=" + mSendTronLog);
+            pw.println("  " + KEY_VIBRATION_DISABLED + ":config=" + mCurrPolicy.disableVibration);
+            pw.println("  " + KEY_VIBRATION_DISABLED + ":effective=" + mDisableVibrationEffective);
+            pw.println("  " + KEY_ANIMATION_DISABLED + "=" + mCurrPolicy.disableAnimation);
+            pw.println("  " + KEY_FULLBACKUP_DEFERRED + "=" + mCurrPolicy.deferFullBackup);
+            pw.println("  " + KEY_KEYVALUE_DEFERRED + "=" + mCurrPolicy.deferKeyValueBackup);
+            pw.println("  " + KEY_ACTIVATE_FIREWALL_DISABLED + "=" + !mCurrPolicy.enableFirewall);
+            pw.println("  " + KEY_ACTIVATE_DATASAVER_DISABLED + "=" + !mCurrPolicy.enableDataSaver);
+            pw.println("  " + KEY_LAUNCH_BOOST_DISABLED + "=" + mCurrPolicy.disableLaunchBoost);
+            pw.println("  " + KEY_ADJUST_BRIGHTNESS_DISABLED + "="
+                    + !mCurrPolicy.enableAdjustBrightness);
+            pw.println(
+                    "  " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mCurrPolicy.adjustBrightnessFactor);
+            pw.println("  " + KEY_GPS_MODE + "=" + mCurrPolicy.gpsMode);
+            pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY + "=" + mCurrPolicy.forceAllAppsStandby);
+            pw.println("  " + KEY_FORCE_BACKGROUND_CHECK + "=" + mCurrPolicy.forceBackgroundCheck);
+            pw.println("  " + KEY_OPTIONAL_SENSORS_DISABLED + "="
+                    + mCurrPolicy.disableOptionalSensors);
+            pw.println("  " + KEY_AOD_DISABLED + "=" + mCurrPolicy.disableAod);
+            pw.println("  " + KEY_SOUNDTRIGGER_DISABLED + "=" + mCurrPolicy.disableSoundTrigger);
+            pw.println("  " + KEY_QUICK_DOZE_ENABLED + "=" + mCurrPolicy.enableQuickDoze);
+            pw.println("  " + KEY_SEND_TRON_LOG + "=" + mCurrPolicy.sendTronLog);
             pw.println();
 
             pw.print("  Interactive File values:\n");
-            dumpMap(pw, "    ", mFilesForInteractive);
+            dumpMap(pw, "    ", mCurrPolicy.filesForInteractive);
             pw.println();
 
             pw.print("  Noninteractive File values:\n");
-            dumpMap(pw, "    ", mFilesForNoninteractive);
+            dumpMap(pw, "    ", mCurrPolicy.filesForNoninteractive);
         }
     }
 
diff --git a/services/core/java/com/android/server/power/OWNERS b/services/core/java/com/android/server/power/OWNERS
index d118c4e..20e4985 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -2,3 +2,4 @@
 
 per-file BatterySaverPolicy.java=omakoto@google.com
 per-file ShutdownThread.java=fkupolov@google.com
+per-file ThermalManagerService.java=wvw@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
new file mode 100644
index 0000000..812fd82
--- /dev/null
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2018 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.server.power;
+
+import android.content.Context;
+import android.hardware.thermal.V1_0.ThermalStatus;
+import android.hardware.thermal.V1_0.ThermalStatusCode;
+import android.hardware.thermal.V1_1.IThermalCallback;
+import android.hardware.thermal.V2_0.IThermalChangedCallback;
+import android.hardware.thermal.V2_0.ThrottlingSeverity;
+import android.os.Binder;
+import android.os.HwBinder;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
+import android.os.PowerManager;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
+import com.android.server.FgThread;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * This is a system service that listens to HAL thermal events and dispatch those to listeners.
+ * <p>The service will also trigger actions based on severity of the throttling status.</p>
+ *
+ * @hide
+ */
+public class ThermalManagerService extends SystemService {
+    private static final String TAG = ThermalManagerService.class.getSimpleName();
+
+    /** Registered observers of the thermal changed events. Cookie is used to store type */
+    @GuardedBy("mLock")
+    private final RemoteCallbackList<IThermalEventListener> mThermalEventListeners =
+            new RemoteCallbackList<>();
+
+    /** Lock to protect HAL handles and listen list. */
+    private final Object mLock = new Object();
+
+    /** Newly registered callback. */
+    @GuardedBy("mLock")
+    private IThermalEventListener mNewListenerCallback = null;
+
+    /** Newly registered callback type, null means not filter type. */
+    @GuardedBy("mLock")
+    private Integer mNewListenerType = null;
+
+    /** Local PMS handle. */
+    private final PowerManager mPowerManager;
+
+    /** Proxy object for the Thermal HAL 2.0 service. */
+    @GuardedBy("mLock")
+    private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
+
+    /** Proxy object for the Thermal HAL 1.1 service. */
+    @GuardedBy("mLock")
+    private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null;
+
+    /** Cookie for matching the right end point. */
+    private static final int THERMAL_HAL_DEATH_COOKIE = 5612;
+
+    /** HWbinder callback for Thermal HAL 2.0. */
+    private final IThermalChangedCallback.Stub mThermalCallback20 =
+            new IThermalChangedCallback.Stub() {
+                @Override
+                public void notifyThrottling(
+                        android.hardware.thermal.V2_0.Temperature temperature) {
+                    android.os.Temperature thermalSvcTemp = new android.os.Temperature(
+                            temperature.value, temperature.type, temperature.name,
+                            temperature.throttlingStatus);
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        notifyThrottlingImpl(thermalSvcTemp);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            };
+
+    /** HWbinder callback for Thermal HAL 1.1. */
+    private final IThermalCallback.Stub mThermalCallback11 =
+            new IThermalCallback.Stub() {
+                @Override
+                public void notifyThrottling(boolean isThrottling,
+                        android.hardware.thermal.V1_0.Temperature temperature) {
+                    android.os.Temperature thermalSvcTemp = new android.os.Temperature(
+                            temperature.currentValue, temperature.type, temperature.name,
+                            isThrottling ? ThrottlingSeverity.SEVERE : ThrottlingSeverity.NONE);
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        notifyThrottlingImpl(thermalSvcTemp);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            };
+
+    public ThermalManagerService(Context context) {
+        super(context);
+        mPowerManager = context.getSystemService(PowerManager.class);
+    }
+
+    private void setNewListener(IThermalEventListener listener, Integer type) {
+        synchronized (mLock) {
+            mNewListenerCallback = listener;
+            mNewListenerType = type;
+        }
+    }
+
+    private void clearNewListener() {
+        synchronized (mLock) {
+            mNewListenerCallback = null;
+            mNewListenerType = null;
+        }
+    }
+
+    private final IThermalService.Stub mService = new IThermalService.Stub() {
+        @Override
+        public void registerThermalEventListener(IThermalEventListener listener) {
+            synchronized (mLock) {
+                mThermalEventListeners.register(listener, null);
+                // Notify its callback after new client registered.
+                setNewListener(listener, null);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    notifyCurrentTemperaturesLocked();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                    clearNewListener();
+                }
+            }
+        }
+
+        @Override
+        public void registerThermalEventListenerWithType(IThermalEventListener listener, int type) {
+            synchronized (mLock) {
+                mThermalEventListeners.register(listener, new Integer(type));
+                setNewListener(listener, new Integer(type));
+                // Notify its callback after new client registered.
+                long token = Binder.clearCallingIdentity();
+                try {
+                    notifyCurrentTemperaturesLocked();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                    clearNewListener();
+                }
+            }
+        }
+
+        @Override
+        public void unregisterThermalEventListener(IThermalEventListener listener) {
+            synchronized (mLock) {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mThermalEventListeners.unregister(listener);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public List<android.os.Temperature> getCurrentTemperatures() {
+            List<android.os.Temperature> ret;
+            long token = Binder.clearCallingIdentity();
+            try {
+                ret = getCurrentTemperaturesInternal(false, 0 /* not used */);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            return ret;
+        }
+
+        @Override
+        public List<android.os.Temperature> getCurrentTemperaturesWithType(int type) {
+            List<android.os.Temperature> ret;
+            long token = Binder.clearCallingIdentity();
+            try {
+                ret = getCurrentTemperaturesInternal(true, type);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            return ret;
+        }
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
+            pw.println("ThermalEventListeners dump:");
+            synchronized (mLock) {
+                mThermalEventListeners.dump(pw, "\t");
+                pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes" : "no"));
+                pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes" : "no"));
+            }
+        }
+    };
+
+    private List<android.os.Temperature> getCurrentTemperaturesInternal(boolean shouldFilter,
+            int type) {
+        List<android.os.Temperature> ret = new ArrayList<>();
+        synchronized (mLock) {
+            if (mThermalHal20 == null) {
+                return ret;
+            }
+            try {
+                mThermalHal20.getCurrentTemperatures(shouldFilter, type,
+                        (ThermalStatus status,
+                                ArrayList<android.hardware.thermal.V2_0.Temperature>
+                                        temperatures) -> {
+                            if (ThermalStatusCode.SUCCESS == status.code) {
+                                for (android.hardware.thermal.V2_0.Temperature
+                                        temperature : temperatures) {
+                                    ret.add(new android.os.Temperature(
+                                            temperature.value, temperature.type, temperature.name,
+                                            temperature.throttlingStatus));
+                                }
+                            } else {
+                                Slog.e(TAG,
+                                        "Couldn't get temperatures because of HAL error: "
+                                                + status.debugMessage);
+                            }
+
+                        });
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
+                connectToHalLocked();
+                // Post to listeners after reconnect to HAL.
+                notifyCurrentTemperaturesLocked();
+            }
+        }
+        return ret;
+    }
+
+    private void notifyListener(android.os.Temperature temperature, IThermalEventListener listener,
+            Integer type) {
+        // Skip if listener registered with a different type
+        if (type != null && type != temperature.getType()) {
+            return;
+        }
+        final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> {
+            try {
+                listener.notifyThrottling(temperature);
+            } catch (RemoteException | RuntimeException e) {
+                Slog.e(TAG, "Thermal callback failed to call", e);
+            }
+        });
+        if (!thermalCallbackQueued) {
+            Slog.e(TAG, "Thermal callback failed to queue");
+        }
+    }
+
+    private void notifyThrottlingImpl(android.os.Temperature temperature) {
+        synchronized (mLock) {
+            // Thermal Shutdown for Skin temperature
+            if (temperature.getStatus() == android.os.Temperature.THROTTLING_SHUTDOWN
+                    && temperature.getType() == android.os.Temperature.TYPE_SKIN) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+
+            if (mNewListenerCallback != null) {
+                // Only notify current newly added callback.
+                notifyListener(temperature, mNewListenerCallback, mNewListenerType);
+            } else {
+                final int length = mThermalEventListeners.beginBroadcast();
+                try {
+                    for (int i = 0; i < length; i++) {
+                        final IThermalEventListener listener =
+                                mThermalEventListeners.getBroadcastItem(i);
+                        final Integer type = (Integer) mThermalEventListeners.getBroadcastCookie(i);
+                        notifyListener(temperature, listener, type);
+                    }
+                } finally {
+                    mThermalEventListeners.finishBroadcast();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.THERMAL_SERVICE, mService);
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+            onActivityManagerReady();
+        }
+    }
+
+    private void notifyCurrentTemperaturesCallbackLocked(ThermalStatus status,
+            ArrayList<android.hardware.thermal.V2_0.Temperature> temperatures) {
+        if (ThermalStatusCode.SUCCESS != status.code) {
+            Slog.e(TAG, "Couldn't get temperatures because of HAL error: "
+                    + status.debugMessage);
+            return;
+        }
+        for (android.hardware.thermal.V2_0.Temperature temperature : temperatures) {
+            android.os.Temperature thermal_svc_temp =
+                    new android.os.Temperature(
+                            temperature.value, temperature.type,
+                            temperature.name,
+                            temperature.throttlingStatus);
+            notifyThrottlingImpl(thermal_svc_temp);
+        }
+    }
+
+    private void notifyCurrentTemperaturesLocked() {
+        if (mThermalHal20 == null) {
+            return;
+        }
+        try {
+            mThermalHal20.getCurrentTemperatures(false, 0,
+                    this::notifyCurrentTemperaturesCallbackLocked);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Couldn't get temperatures, reconnecting...", e);
+            connectToHalLocked();
+        }
+    }
+
+    private void onActivityManagerReady() {
+        synchronized (mLock) {
+            connectToHalLocked();
+            // Post to listeners after connect to HAL.
+            notifyCurrentTemperaturesLocked();
+        }
+    }
+
+    final class DeathRecipient implements HwBinder.DeathRecipient {
+        @Override
+        public void serviceDied(long cookie) {
+            if (cookie == THERMAL_HAL_DEATH_COOKIE) {
+                Slog.e(TAG, "Thermal HAL service died cookie: " + cookie);
+                synchronized (mLock) {
+                    connectToHalLocked();
+                    // Post to listeners after reconnect to HAL.
+                    notifyCurrentTemperaturesLocked();
+                }
+            }
+        }
+    }
+
+    private void connectToHalLocked() {
+        try {
+            mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService();
+            mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
+            mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false,
+                    0 /* not used */);
+        } catch (NoSuchElementException | RemoteException e) {
+            Slog.e(TAG, "Thermal HAL 2.0 service not connected, trying 1.1.");
+            mThermalHal20 = null;
+            try {
+                mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService();
+                mThermalHal11.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
+                mThermalHal11.registerThermalCallback(mThermalCallback11);
+            } catch (NoSuchElementException | RemoteException e2) {
+                Slog.e(TAG,
+                        "Thermal HAL 1.1 service not connected, no thermal call back "
+                                + "will be called.");
+                mThermalHal11 = null;
+            }
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
new file mode 100644
index 0000000..b670291
--- /dev/null
+++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2018 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.server.role;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.role.IRoleManagerCallback;
+import android.app.role.RoleManagerCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.rolecontrollerservice.IRoleControllerService;
+import android.rolecontrollerservice.RoleControllerService;
+import android.util.Slog;
+
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ * Handles connection with {@link RoleControllerService}.
+ */
+public class RemoteRoleControllerService {
+
+    private static final String LOG_TAG = RemoteRoleControllerService.class.getSimpleName();
+
+    @NonNull
+    private final Connection mConnection;
+
+    public RemoteRoleControllerService(@UserIdInt int userId, @NonNull Context context) {
+        mConnection = new Connection(userId, context);
+    }
+
+    /**
+     * Add a specific application to the holders of a role. If the role is exclusive, the previous
+     * holder will be replaced.
+     *
+     * @see RoleControllerService#onAddRoleHolder(String, String, RoleManagerCallback)
+     */
+    public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
+            @NonNull IRoleManagerCallback callback) {
+        mConnection.enqueueCall(new Connection.Call((service, callbackDelegate) ->
+                service.onAddRoleHolder(roleName, packageName, callbackDelegate), callback));
+    }
+
+    /**
+     * Remove a specific application from the holders of a role.
+     *
+     * @see RoleControllerService#onRemoveRoleHolder(String, String, RoleManagerCallback)
+     */
+    public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
+            @NonNull IRoleManagerCallback callback) {
+        mConnection.enqueueCall(new Connection.Call((service, callbackDelegate) ->
+                service.onRemoveRoleHolder(roleName, packageName, callbackDelegate), callback));
+    }
+
+    /**
+     * Remove all holders of a role.
+     *
+     * @see RoleControllerService#onClearRoleHolders(String, RoleManagerCallback)
+     */
+    public void onClearRoleHolders(@NonNull String roleName,
+            @NonNull IRoleManagerCallback callback) {
+        mConnection.enqueueCall(new Connection.Call((service, callbackDelegate) ->
+                service.onClearRoleHolders(roleName, callbackDelegate), callback));
+    }
+
+    private static final class Connection implements ServiceConnection {
+
+        private static final long UNBIND_DELAY_MILLIS = 15 * 1000;
+
+        @UserIdInt
+        private final int mUserId;
+
+        @NonNull
+        private final Context mContext;
+
+        private boolean mBound;
+
+        @Nullable
+        private IRoleControllerService mService;
+
+        @NonNull
+        private final Queue<Call> mPendingCalls = new ArrayDeque<>();
+
+        @NonNull
+        private final Handler mMainHandler = Handler.getMain();
+
+        @NonNull
+        private final Runnable mUnbindRunnable = this::unbind;
+
+        Connection(@UserIdInt int userId, @NonNull Context context) {
+            mUserId = userId;
+            mContext = context;
+        }
+
+        @MainThread
+        @Override
+        public void onServiceConnected(@NonNull ComponentName name, @NonNull IBinder service) {
+            mService = IRoleControllerService.Stub.asInterface(service);
+            executePendingCalls();
+        }
+
+        @MainThread
+        private void executePendingCalls() {
+            while (!mPendingCalls.isEmpty()) {
+                Call call = mPendingCalls.poll();
+                call.execute(mService);
+            }
+            scheduleUnbind();
+        }
+
+        @MainThread
+        @Override
+        public void onServiceDisconnected(@NonNull ComponentName name) {
+            mService = null;
+        }
+
+        @MainThread
+        @Override
+        public void onBindingDied(@NonNull ComponentName name) {
+            unbind();
+        }
+
+        public void enqueueCall(@NonNull Call call) {
+            mMainHandler.post(PooledLambda.obtainRunnable(this::executeCall, call));
+        }
+
+        @MainThread
+        private void executeCall(@NonNull Call call) {
+            ensureBound();
+            if (mService == null) {
+                mPendingCalls.offer(call);
+                return;
+            }
+            call.execute(mService);
+            scheduleUnbind();
+        }
+
+        @MainThread
+        private void ensureBound() {
+            mMainHandler.removeCallbacks(mUnbindRunnable);
+            if (!mBound) {
+                Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE);
+                intent.setPackage(mContext.getPackageManager()
+                        .getPermissionControllerPackageName());
+                mBound = mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
+                        UserHandle.of(mUserId));
+            }
+        }
+
+        private void scheduleUnbind() {
+            mMainHandler.removeCallbacks(mUnbindRunnable);
+            mMainHandler.postDelayed(mUnbindRunnable, UNBIND_DELAY_MILLIS);
+        }
+
+        @MainThread
+        private void unbind() {
+            if (mBound) {
+                mService = null;
+                mContext.unbindService(this);
+                mBound = false;
+            }
+        }
+
+        public static class Call {
+
+            private static final int TIMEOUT_MILLIS = 15 * 1000;
+
+            @NonNull
+            private final CallExecutor mCallExecutor;
+
+            @NonNull
+            private final IRoleManagerCallback mCallback;
+
+            @NonNull
+            private final Handler mMainHandler = Handler.getMain();
+
+            @NonNull
+            private final Runnable mTimeoutRunnable = () -> notifyCallback(false);
+
+            private boolean mCallbackNotified;
+
+            private Call(@NonNull CallExecutor callExecutor,
+                    @NonNull IRoleManagerCallback callback) {
+                mCallExecutor = callExecutor;
+                mCallback = callback;
+            }
+
+            @MainThread
+            public void execute(IRoleControllerService service) {
+                try {
+                    mMainHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MILLIS);
+                    mCallExecutor.execute(service, new CallbackDelegate());
+                } catch (RemoteException e) {
+                    Slog.e(LOG_TAG, "Error calling RoleControllerService", e);
+                    notifyCallback(false);
+                }
+            }
+
+            @MainThread
+            private void notifyCallback(boolean success) {
+                if (mCallbackNotified) {
+                    return;
+                }
+                mCallbackNotified = true;
+                mMainHandler.removeCallbacks(mTimeoutRunnable);
+                try {
+                    if (success) {
+                        mCallback.onSuccess();
+                    } else {
+                        mCallback.onFailure();
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(LOG_TAG, "Error calling " + (success ? "onSuccess()" : "onFailure()")
+                            + " callback", e);
+                }
+            }
+
+            @FunctionalInterface
+            public interface CallExecutor {
+
+                @MainThread
+                void execute(IRoleControllerService service, IRoleManagerCallback callbackDelegate)
+                        throws RemoteException;
+            }
+
+            private class CallbackDelegate extends IRoleManagerCallback.Stub {
+
+                @Override
+                public void onSuccess() throws RemoteException {
+                    mMainHandler.post(PooledLambda.obtainRunnable(Call.this::notifyCallback, true));
+                }
+
+                @Override
+                public void onFailure() throws RemoteException {
+                    mMainHandler.post(PooledLambda.obtainRunnable(Call.this::notifyCallback,
+                            false));
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
new file mode 100644
index 0000000..b7d2ce2
--- /dev/null
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2018 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.server.role;
+
+import android.Manifest;
+import android.annotation.CheckResult;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.role.IRoleManager;
+import android.app.role.IRoleManagerCallback;
+import android.app.role.RoleManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+import android.os.UserManagerInternal;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Service for role management.
+ *
+ * @see RoleManager
+ */
+public class RoleManagerService extends SystemService {
+
+    private static final String LOG_TAG = RoleManagerService.class.getSimpleName();
+
+    @NonNull
+    private final UserManagerInternal mUserManagerInternal;
+    @NonNull
+    private final AppOpsManager mAppOpsManager;
+
+    @NonNull
+    private final Object mLock = new Object();
+
+    /**
+     * Maps user id to its state.
+     */
+    @GuardedBy("mLock")
+    @NonNull
+    private final SparseArray<RoleUserState> mUserStates = new SparseArray<>();
+
+    /**
+     * Maps user id to its controller service.
+     */
+    @GuardedBy("mLock")
+    @NonNull
+    private final SparseArray<RemoteRoleControllerService> mControllerServices =
+            new SparseArray<>();
+
+    public RoleManagerService(@NonNull Context context) {
+        super(context);
+
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+        mAppOpsManager = context.getSystemService(AppOpsManager.class);
+
+        registerUserRemovedReceiver();
+    }
+
+    private void registerUserRemovedReceiver() {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+        getContext().registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (TextUtils.equals(intent.getAction(), Intent.ACTION_USER_REMOVED)) {
+                    int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                    onRemoveUser(userId);
+                }
+            }
+        }, UserHandle.ALL, intentFilter, null, null);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.ROLE_SERVICE, new Stub());
+    }
+
+    @Override
+    public void onStartUser(@UserIdInt int userId) {
+        synchronized (mLock) {
+            getUserStateLocked(userId);
+        }
+    }
+
+    @GuardedBy("mLock")
+    @NonNull
+    private RoleUserState getUserStateLocked(@UserIdInt int userId) {
+        RoleUserState userState = mUserStates.get(userId);
+        if (userState == null) {
+            userState = new RoleUserState(userId);
+            userState.readSyncLocked();
+            mUserStates.put(userId, userState);
+        }
+        return userState;
+    }
+
+    @GuardedBy("mLock")
+    @NonNull
+    private RemoteRoleControllerService getControllerService(@UserIdInt int userId) {
+        RemoteRoleControllerService controllerService = mControllerServices.get(userId);
+        if (controllerService == null) {
+            controllerService = new RemoteRoleControllerService(userId, getContext());
+            mControllerServices.put(userId, controllerService);
+        }
+        return controllerService;
+    }
+
+    private void onRemoveUser(@UserIdInt int userId) {
+        synchronized (mLock) {
+            mControllerServices.remove(userId);
+            RoleUserState userState = mUserStates.removeReturnOld(userId);
+            if (userState != null) {
+                userState.destroySyncLocked();
+            }
+        }
+    }
+
+    private class Stub extends IRoleManager.Stub {
+
+        @Override
+        public boolean isRoleAvailable(@NonNull String roleName) {
+            Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+
+            int userId = UserHandle.getUserId(getCallingUid());
+            synchronized (mLock) {
+                RoleUserState userState = getUserStateLocked(userId);
+                return userState.isRoleAvailableLocked(roleName);
+            }
+        }
+
+        @Override
+        public boolean isRoleHeld(@NonNull String roleName, @NonNull String packageName) {
+            Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+            Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+            int callingUid = getCallingUid();
+            mAppOpsManager.checkPackage(callingUid, packageName);
+
+            int userId = UserHandle.getUserId(callingUid);
+            ArraySet<String> roleHolders = getRoleHoldersInternal(roleName, userId);
+            if (roleHolders == null) {
+                return false;
+            }
+            return roleHolders.contains(packageName);
+        }
+
+        @NonNull
+        @Override
+        public List<String> getRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId) {
+            Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+            if (!mUserManagerInternal.exists(userId)) {
+                Slog.e(LOG_TAG, "user " + userId + " does not exist");
+                return Collections.emptyList();
+            }
+            userId = handleIncomingUser(userId, "getRoleHoldersAsUser");
+            getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+                    "getRoleHoldersAsUser");
+
+            ArraySet<String> roleHolders = getRoleHoldersInternal(roleName, userId);
+            if (roleHolders == null) {
+                return Collections.emptyList();
+            }
+            return new ArrayList<>(roleHolders);
+        }
+
+        @Nullable
+        private ArraySet<String> getRoleHoldersInternal(@NonNull String roleName,
+                @UserIdInt int userId) {
+            synchronized (mLock) {
+                RoleUserState userState = getUserStateLocked(userId);
+                return userState.getRoleHoldersLocked(roleName);
+            }
+        }
+
+        @Override
+        public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
+                @UserIdInt int userId, @NonNull IRoleManagerCallback callback) {
+            Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+            Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+            Preconditions.checkNotNull(callback, "callback cannot be null");
+            if (!mUserManagerInternal.exists(userId)) {
+                Slog.e(LOG_TAG, "user " + userId + " does not exist");
+                return;
+            }
+            userId = handleIncomingUser(userId, "addRoleHolderAsUser");
+            getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+                    "addRoleHolderAsUser");
+
+            getControllerService(userId).onAddRoleHolder(roleName, packageName, callback);
+        }
+
+        @Override
+        public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
+                @UserIdInt int userId, @NonNull IRoleManagerCallback callback) {
+            Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+            Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+            Preconditions.checkNotNull(callback, "callback cannot be null");
+            if (!mUserManagerInternal.exists(userId)) {
+                Slog.e(LOG_TAG, "user " + userId + " does not exist");
+                return;
+            }
+            userId = handleIncomingUser(userId, "removeRoleHolderAsUser");
+            getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+                    "removeRoleHolderAsUser");
+
+            getControllerService(userId).onRemoveRoleHolder(roleName, packageName,
+                    callback);
+        }
+
+        @Override
+        public void clearRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId,
+                @NonNull IRoleManagerCallback callback) {
+            Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+            Preconditions.checkNotNull(callback, "callback cannot be null");
+            if (!mUserManagerInternal.exists(userId)) {
+                Slog.e(LOG_TAG, "user " + userId + " does not exist");
+                return;
+            }
+            userId = handleIncomingUser(userId, "clearRoleHoldersAsUser");
+            getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+                    "clearRoleHoldersAsUser");
+
+            getControllerService(userId).onClearRoleHolders(roleName, callback);
+        }
+
+        @Override
+        public boolean addRoleHolderFromController(@NonNull String roleName,
+                @NonNull String packageName) {
+            Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+            Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+            getContext().enforceCallingOrSelfPermission(
+                    RoleManager.PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER,
+                    "addRoleHolderFromController");
+
+            int userId = UserHandle.getCallingUserId();
+            synchronized (mLock) {
+                RoleUserState userState = getUserStateLocked(userId);
+                return userState.addRoleHolderLocked(roleName, packageName);
+            }
+        }
+
+        @Override
+        public boolean removeRoleHolderFromController(@NonNull String roleName,
+                @NonNull String packageName) {
+            Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+            Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+            getContext().enforceCallingOrSelfPermission(
+                    RoleManager.PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER,
+                    "removeRoleHolderFromController");
+
+            int userId = UserHandle.getCallingUserId();
+            synchronized (mLock) {
+                RoleUserState userState = getUserStateLocked(userId);
+                return userState.removeRoleHolderLocked(roleName, packageName);
+            }
+        }
+
+        @CheckResult
+        private int handleIncomingUser(@UserIdInt int userId, @NonNull String name) {
+            return ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
+                    false, true, name, null);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
new file mode 100644
index 0000000..caa7c28
--- /dev/null
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2018 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.server.role;
+
+import android.annotation.CheckResult;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
+import android.os.Environment;
+import android.os.Handler;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Stores the state of roles for a user.
+ */
+public class RoleUserState {
+
+    private static final String LOG_TAG = RoleUserState.class.getSimpleName();
+
+    public static final int VERSION_UNDEFINED = -1;
+
+    private static final String ROLES_FILE_NAME = "roles.xml";
+
+    private static final String TAG_ROLES = "roles";
+    private static final String TAG_ROLE = "role";
+    private static final String TAG_HOLDER = "holder";
+    private static final String ATTRIBUTE_VERSION = "version";
+    private static final String ATTRIBUTE_NAME = "name";
+
+    @UserIdInt
+    private final int mUserId;
+
+    @GuardedBy("RoleManagerService.mLock")
+    private int mVersion = VERSION_UNDEFINED;
+
+    /**
+     * Maps role names to its holders' package names. The values should never be null.
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
+
+    @GuardedBy("RoleManagerService.mLock")
+    private boolean mDestroyed;
+
+    private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());
+
+    public RoleUserState(@UserIdInt int userId) {
+        mUserId = userId;
+    }
+
+    /**
+     * Get the version of this user state.
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    public int getVersionLocked() {
+        throwIfDestroyedLocked();
+        return mVersion;
+    }
+
+    /**
+     * Set the version of this user state.
+     *
+     * @param version the version to set
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    public void setVersionLocked(int version) {
+        throwIfDestroyedLocked();
+        mVersion = version;
+    }
+
+    /**
+     * Get whether the role is available.
+     *
+     * @param roleName the name of the role to get the holders for
+     *
+     * @return whether the role is available
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    public boolean isRoleAvailableLocked(@NonNull String roleName) {
+        throwIfDestroyedLocked();
+        return mRoles.containsKey(roleName);
+    }
+
+    /**
+     * Get the holders of a role.
+     *
+     * @param roleName the name of the role to query for
+     *
+     * @return the set of role holders. {@code null} should not be returned and indicates an issue.
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    @Nullable
+    public ArraySet<String> getRoleHoldersLocked(@NonNull String roleName) {
+        throwIfDestroyedLocked();
+        return mRoles.get(roleName);
+    }
+
+    /**
+     * Add a holder to a role.
+     *
+     * @param roleName the name of the role to add the holder to
+     * @param packageName the package name of the new holder
+     *
+     * @return {@code false} only if the set of role holders is null, which should not happen and
+     *         indicates an issue.
+     */
+    @CheckResult
+    @GuardedBy("RoleManagerService.mLock")
+    public boolean addRoleHolderLocked(@NonNull String roleName, @NonNull String packageName) {
+        throwIfDestroyedLocked();
+        ArraySet<String> roleHolders = mRoles.get(roleName);
+        if (roleHolders == null) {
+            return false;
+        }
+        roleHolders.add(packageName);
+        return true;
+    }
+
+    /**
+     * Remove a holder from a role.
+     *
+     * @param roleName the name of the role to remove the holder from
+     * @param packageName the package name of the holder to remove
+     *
+     * @return {@code false} only if the set of role holders is null, which should not happen and
+     *         indicates an issue.
+     */
+    @CheckResult
+    @GuardedBy("RoleManagerService.mLock")
+    public boolean removeRoleHolderLocked(@NonNull String roleName, @NonNull String packageName) {
+        throwIfDestroyedLocked();
+        ArraySet<String> roleHolders = mRoles.get(roleName);
+        if (roleHolders == null) {
+            return false;
+        }
+        roleHolders.remove(packageName);
+        return true;
+    }
+
+    /**
+     * Schedule writing the state to file.
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    public void writeAsyncLocked() {
+        throwIfDestroyedLocked();
+        int version = mVersion;
+        ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
+        for (int i = 0, size = mRoles.size(); i < size; ++i) {
+            String roleName = mRoles.keyAt(i);
+            ArraySet<String> roleHolders = mRoles.valueAt(i);
+            roleHolders = new ArraySet<>(roleHolders);
+            roles.put(roleName, roleHolders);
+        }
+        mWriteHandler.removeCallbacksAndMessages(null);
+        mWriteHandler.sendMessage(PooledLambda.obtainMessage(this::writeSync, version, roles));
+    }
+
+    @WorkerThread
+    private void writeSync(int version, @NonNull ArrayMap<String, ArraySet<String>> roles) {
+        AtomicFile destination = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
+        FileOutputStream out = null;
+        try {
+            out = destination.startWrite();
+
+            XmlSerializer serializer = Xml.newSerializer();
+            serializer.setOutput(out, StandardCharsets.UTF_8.name());
+            serializer.setFeature(
+                    "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            serializer.startDocument(null, true);
+
+            serializeRoles(serializer, version, roles);
+
+            serializer.endDocument();
+            destination.finishWrite(out);
+        } catch (Throwable t) {
+            // Any error while writing is fatal.
+            Slog.wtf(LOG_TAG, "Failed to write roles file, restoring backup", t);
+            destination.failWrite(out);
+        } finally {
+            IoUtils.closeQuietly(out);
+        }
+    }
+
+    @WorkerThread
+    private void serializeRoles(@NonNull XmlSerializer serializer, int version,
+            @NonNull ArrayMap<String, ArraySet<String>> roles) throws IOException {
+        serializer.startTag(null, TAG_ROLES);
+        serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
+        for (int i = 0, size = roles.size(); i < size; ++i) {
+            String roleName = roles.keyAt(i);
+            ArraySet<String> roleHolders = roles.valueAt(i);
+            serializer.startTag(null, TAG_ROLE);
+            serializer.attribute(null, ATTRIBUTE_NAME, roleName);
+            serializeRoleHolders(serializer, roleHolders);
+            serializer.endTag(null, TAG_ROLE);
+        }
+        serializer.endTag(null, TAG_ROLES);
+    }
+
+    @WorkerThread
+    private void serializeRoleHolders(@NonNull XmlSerializer serializer,
+            @NonNull ArraySet<String> roleHolders) throws IOException {
+        for (int i = 0, size = roleHolders.size(); i < size; ++i) {
+            String roleHolder = roleHolders.valueAt(i);
+            serializer.startTag(null, TAG_HOLDER);
+            serializer.attribute(null, ATTRIBUTE_NAME, roleHolder);
+            serializer.endTag(null, TAG_HOLDER);
+        }
+    }
+
+    /**
+     * Read the state from file.
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    public void readSyncLocked() {
+        if (mRoles != null) {
+            throw new IllegalStateException("This RoleUserState has already read the XML file");
+        }
+        File file = getFile(mUserId);
+        FileInputStream in;
+        try {
+            in = new AtomicFile(file).openRead();
+        } catch (FileNotFoundException e) {
+            Slog.i(LOG_TAG, "No roles file found");
+            return;
+        }
+
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(in, null);
+            parseXmlLocked(parser);
+        } catch (XmlPullParserException | IOException e) {
+            throw new IllegalStateException("Failed to parse roles file: " + file , e);
+        } finally {
+            IoUtils.closeQuietly(in);
+        }
+    }
+
+    private void parseXmlLocked(@NonNull XmlPullParser parser) throws IOException,
+            XmlPullParserException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            if (parser.getName().equals(TAG_ROLES)) {
+                parseRolesLocked(parser);
+                return;
+            }
+        }
+    }
+
+    private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException,
+            XmlPullParserException {
+        mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION));
+        mRoles = new ArrayMap<>();
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            if (parser.getName().equals(TAG_ROLE)) {
+                String roleName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
+                ArraySet<String> roleHolders = parseRoleHoldersLocked(parser);
+                mRoles.put(roleName, roleHolders);
+            }
+        }
+    }
+
+    @NonNull
+    private ArraySet<String> parseRoleHoldersLocked(@NonNull XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        ArraySet<String> roleHolders = new ArraySet<>();
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            if (parser.getName().equals(TAG_HOLDER)) {
+                String roleHolder = parser.getAttributeValue(null, ATTRIBUTE_NAME);
+                roleHolders.add(roleHolder);
+            }
+        }
+        return roleHolders;
+    }
+
+    /**
+     * Destroy this state and delete the corresponding file. Any pending writes to the file will be
+     * cancelled and any future interaction with this state will throw an exception.
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    public void destroySyncLocked() {
+        throwIfDestroyedLocked();
+        mWriteHandler.removeCallbacksAndMessages(null);
+        getFile(mUserId).delete();
+        mDestroyed = true;
+    }
+
+    @GuardedBy("RoleManagerService.mLock")
+    private void throwIfDestroyedLocked() {
+        if (mDestroyed) {
+            throw new IllegalStateException("This RoleUserState has already been destroyed");
+        }
+    }
+
+    private static @NonNull File getFile(@UserIdInt int userId) {
+        return new File(Environment.getUserSystemDirectory(userId), ROLES_FILE_NAME);
+    }
+}
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 8070f3a..514dfed 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -138,8 +138,8 @@
      * <p>It is worthy to note that {@code trackedBufferFactory} generates a "tracked" {@code
      * ByteBuffer}. The data will be used outside this method via the factory itself.
      *
-     * @return fs-verity measurement of {@code filePath}, which is a SHA-256 of fs-verity descriptor
-     *         and authenticated extensions.
+     * @return fs-verity signed data (struct fsverity_digest_disk) of {@code filePath}, which
+     *         includes SHA-256 of fs-verity descriptor and authenticated extensions.
      */
     private static byte[] generateFsverityMetadata(String filePath, String signaturePath,
             @NonNull TrackedShmBufferFactory trackedBufferFactory)
@@ -151,8 +151,10 @@
 
             ByteBuffer buffer = result.verityData;
             buffer.position(result.merkleTreeSize);
-            return generateFsverityDescriptorAndMeasurement(file, result.rootHash, signaturePath,
-                    buffer);
+
+            final byte[] measurement = generateFsverityDescriptorAndMeasurement(file,
+                    result.rootHash, signaturePath, buffer);
+            return constructFsveritySignedDataNative(measurement);
         }
     }
 
@@ -211,6 +213,7 @@
         return md.digest();
     }
 
+    private static native byte[] constructFsveritySignedDataNative(@NonNull byte[] measurement);
     private static native byte[] constructFsverityDescriptorNative(long fileSize);
     private static native byte[] constructFsverityExtensionNative(short extensionId,
             int extensionDataSize);
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index f10fe58..2be55c0 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -80,6 +80,7 @@
 import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.os.BinderCallsStats.ExportedCallStat;
 import com.android.internal.os.KernelCpuSpeedReader;
+import com.android.internal.os.KernelCpuThreadReader;
 import com.android.internal.os.KernelUidCpuActiveTimeReader;
 import com.android.internal.os.KernelUidCpuClusterTimeReader;
 import com.android.internal.os.KernelUidCpuFreqTimeReader;
@@ -191,6 +192,8 @@
             new KernelUidCpuClusterTimeReader();
     private StoragedUidIoStatsReader mStoragedUidIoStatsReader =
             new StoragedUidIoStatsReader();
+    @Nullable
+    private final KernelCpuThreadReader mKernelCpuThreadReader;
 
     private static IThermalService sThermalService;
     private File mBaseDir =
@@ -250,8 +253,8 @@
         if (b != null) {
             sThermalService = IThermalService.Stub.asInterface(b);
             try {
-                sThermalService.registerThermalEventListener(
-                        new ThermalEventListener());
+                sThermalService.registerThermalEventListenerWithType(
+                        new ThermalEventListener(), Temperature.TYPE_SKIN);
                 Slog.i(TAG, "register thermal listener successfully");
             } catch (RemoteException e) {
                 // Should never happen.
@@ -265,6 +268,7 @@
         handlerThread.start();
         mHandler = new CompanionHandler(handlerThread.getLooper());
 
+        mKernelCpuThreadReader = KernelCpuThreadReader.create();
     }
 
     @Override
@@ -1000,6 +1004,7 @@
             e.writeLong(processMemoryState.cacheInBytes);
             e.writeLong(processMemoryState.swapInBytes);
             e.writeLong(processMemoryState.rssHighWatermarkInBytes);
+            e.writeLong(processMemoryState.startTimeNanos);
             pulledData.add(e);
         }
     }
@@ -1017,6 +1022,7 @@
             e.writeLong(processMemoryState.pgmajfault);
             e.writeLong(processMemoryState.rssInBytes);
             e.writeLong(processMemoryState.rssHighWatermarkInBytes);
+            e.writeLong(processMemoryState.startTimeNanos);
             pulledData.add(e);
         }
     }
@@ -1034,7 +1040,7 @@
         binderStats.reset();
         for (ExportedCallStat callStat : callStats) {
             StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(callStat.uid);
+            e.writeInt(callStat.workSourceUid);
             e.writeString(callStat.className);
             e.writeString(callStat.methodName);
             e.writeLong(callStat.callCount);
@@ -1047,6 +1053,7 @@
             e.writeLong(callStat.maxRequestSizeBytes);
             e.writeLong(callStat.recordedCallCount);
             e.writeInt(callStat.screenInteractive ? 1 : 0);
+            e.writeInt(callStat.callingUid);
             pulledData.add(e);
         }
     }
@@ -1444,6 +1451,46 @@
         }
     }
 
+    private void pullCpuTimePerThreadFreq(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        if (this.mKernelCpuThreadReader == null) {
+            return;
+        }
+        KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = this.mKernelCpuThreadReader
+                .getCurrentProcessCpuUsage();
+        if (processCpuUsage == null) {
+            return;
+        }
+        int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
+        for (KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage
+                : processCpuUsage.threadCpuUsages) {
+            if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
+                Slog.w(TAG, "Unexpected number of usage times,"
+                        + " expected " + cpuFrequencies.length
+                        + " but got " + threadCpuUsage.usageTimesMillis.length);
+                continue;
+            }
+
+            for (int i = 0; i < threadCpuUsage.usageTimesMillis.length; i++) {
+                // Do not report CPU usage at a frequency when it's zero
+                if (threadCpuUsage.usageTimesMillis[i] == 0) {
+                    continue;
+                }
+
+                StatsLogEventWrapper e =
+                        new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+                e.writeInt(processCpuUsage.uid);
+                e.writeInt(processCpuUsage.processId);
+                e.writeInt(threadCpuUsage.threadId);
+                e.writeString(processCpuUsage.processName);
+                e.writeString(threadCpuUsage.threadName);
+                e.writeInt(cpuFrequencies[i]);
+                e.writeInt(threadCpuUsage.usageTimesMillis[i]);
+                pulledData.add(e);
+            }
+        }
+    }
+
     /**
      * Pulls various data.
      */
@@ -1582,6 +1629,10 @@
                 pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.CPU_TIME_PER_THREAD_FREQ: {
+                pullCpuTimePerThreadFreq(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
@@ -1802,7 +1853,8 @@
     // Thermal event received from vendor thermal management subsystem
     private static final class ThermalEventListener extends IThermalEventListener.Stub {
         @Override
-        public void notifyThrottling(boolean isThrottling, Temperature temp) {
+        public void notifyThrottling(Temperature temp) {
+            boolean isThrottling = temp.getStatus() >= Temperature.THROTTLING_SEVERE;
             StatsLog.write(StatsLog.THERMAL_THROTTLING, temp.getType(),
                     isThrottling ? 1 : 0, temp.getValue());
         }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 9edb3d0..e4d1cfe 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -735,7 +735,7 @@
                 }
 
                 public void setShown(boolean shown, boolean animate) {
-                    synchronized (mService.mWindowMap) {
+                    synchronized (mService.mGlobalLock) {
                         if (mShown == shown) {
                             return;
                         }
@@ -750,13 +750,13 @@
                 @SuppressWarnings("unused")
                 // Called reflectively from an animator.
                 public int getAlpha() {
-                    synchronized (mService.mWindowMap) {
+                    synchronized (mService.mGlobalLock) {
                         return mAlpha;
                     }
                 }
 
                 public void setAlpha(int alpha) {
-                    synchronized (mService.mWindowMap) {
+                    synchronized (mService.mGlobalLock) {
                         if (mAlpha == alpha) {
                             return;
                         }
@@ -769,7 +769,7 @@
                 }
 
                 public void setBounds(Region bounds) {
-                    synchronized (mService.mWindowMap) {
+                    synchronized (mService.mGlobalLock) {
                         if (mBounds.equals(bounds)) {
                             return;
                         }
@@ -782,7 +782,7 @@
                 }
 
                 public void updateSize() {
-                    synchronized (mService.mWindowMap) {
+                    synchronized (mService.mGlobalLock) {
                         mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
                         mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
                         invalidate(mDirtyRect);
@@ -801,7 +801,7 @@
 
                 /** NOTE: This has to be called within a surface transaction. */
                 public void drawIfNeeded() {
-                    synchronized (mService.mWindowMap) {
+                    synchronized (mService.mGlobalLock) {
                         if (!mInvalidated) {
                             return;
                         }
@@ -948,7 +948,7 @@
                     } break;
 
                     case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
-                        synchronized (mService.mWindowMap) {
+                        synchronized (mService.mGlobalLock) {
                             if (mMagnifedViewport.isMagnifyingLocked()
                                     || isForceShowingMagnifiableBoundsLocked()) {
                                 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
@@ -1039,7 +1039,7 @@
             boolean windowsChanged = false;
             List<WindowInfo> windows = new ArrayList<WindowInfo>();
 
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 // Do not send the windows if there is no current focus as
                 // the window manager is still looking for where to put it.
                 // We will do the work when we get a focus change callback.
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
new file mode 100644
index 0000000..478a11a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -0,0 +1,1346 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_PRIVATE;
+import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
+
+import static com.android.server.am.ActivityDisplayProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityDisplayProto.FOCUSED_STACK_ID;
+import static com.android.server.am.ActivityDisplayProto.ID;
+import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
+import static com.android.server.am.ActivityDisplayProto.STACKS;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult;
+import static com.android.server.wm.ActivityStackSupervisor.TAG_STATES;
+import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.app.WindowConfiguration;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.os.UserHandle;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.am.EventLogTags;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Exactly one of these classes per Display in the system. Capable of holding zero or more
+ * attached {@link ActivityStack}s.
+ */
+class ActivityDisplay extends ConfigurationContainer<ActivityStack>
+        implements WindowContainerListener {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_ATM;
+    private static final String TAG_STACK = TAG + POSTFIX_STACK;
+
+    static final int POSITION_TOP = Integer.MAX_VALUE;
+    static final int POSITION_BOTTOM = Integer.MIN_VALUE;
+
+
+    /**
+     * Counter for next free stack ID to use for dynamic activity stacks. Unique across displays.
+     */
+    private static int sNextFreeStackId = 0;
+
+    private ActivityStackSupervisor mSupervisor;
+    /** Actual Display this object tracks. */
+    int mDisplayId;
+    Display mDisplay;
+
+    /**
+     * All of the stacks on this display. Order matters, topmost stack is in front of all other
+     * stacks, bottommost behind. Accessed directly by ActivityManager package classes. Any calls
+     * changing the list should also call {@link #onStackOrderChanged()}.
+     */
+    private final ArrayList<ActivityStack> mStacks = new ArrayList<>();
+    private ArrayList<OnStackOrderChangedListener> mStackOrderChangedCallbacks = new ArrayList<>();
+
+    /** Array of all UIDs that are present on the display. */
+    private IntArray mDisplayAccessUIDs = new IntArray();
+
+    /** All tokens used to put activities on this stack to sleep (including mOffToken) */
+    final ArrayList<ActivityTaskManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>();
+    /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
+    ActivityTaskManagerInternal.SleepToken mOffToken;
+
+    private boolean mSleeping;
+
+    /**
+     * The display is removed from the system and we are just waiting for all activities on it to be
+     * finished before removing this object.
+     */
+    private boolean mRemoved;
+
+    /**
+     * A focusable stack that is purposely to be positioned at the top. Although the stack may not
+     * have the topmost index, it is used as a preferred candidate to prevent being unable to resume
+     * target stack properly when there are other focusable always-on-top stacks.
+     */
+    private ActivityStack mPreferredTopFocusableStack;
+
+    /**
+     * If this is the same as {@link #getFocusedStack} then the activity on the top of the focused
+     * stack has been resumed. If stacks are changing position this will hold the old stack until
+     * the new stack becomes resumed after which it will be set to current focused stack.
+     */
+    private ActivityStack mLastFocusedStack;
+
+    // Cached reference to some special stacks we tend to get a lot so we don't need to loop
+    // through the list to find them.
+    private ActivityStack mHomeStack = null;
+    private ActivityStack mRecentsStack = null;
+    private ActivityStack mPinnedStack = null;
+    private ActivityStack mSplitScreenPrimaryStack = null;
+
+    // Used in updating the display size
+    private Point mTmpDisplaySize = new Point();
+
+    private DisplayWindowController mWindowContainerController;
+
+    private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
+
+    ActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
+        mSupervisor = supervisor;
+        mDisplayId = display.getDisplayId();
+        mDisplay = display;
+        mWindowContainerController = createWindowContainerController();
+        updateBounds();
+    }
+
+    protected DisplayWindowController createWindowContainerController() {
+        return new DisplayWindowController(mDisplay, this);
+    }
+
+    DisplayWindowController getWindowContainerController() {
+        return mWindowContainerController;
+    }
+
+    void updateBounds() {
+        mDisplay.getRealSize(mTmpDisplaySize);
+        setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
+    }
+
+    void onDisplayChanged() {
+        // The window policy is responsible for stopping activities on the default display.
+        final int displayId = mDisplay.getDisplayId();
+        if (displayId != DEFAULT_DISPLAY) {
+            final int displayState = mDisplay.getState();
+            if (displayState == Display.STATE_OFF && mOffToken == null) {
+                mOffToken = mSupervisor.mService.acquireSleepToken("Display-off", displayId);
+            } else if (displayState == Display.STATE_ON && mOffToken != null) {
+                mOffToken.release();
+                mOffToken = null;
+            }
+        }
+
+        updateBounds();
+        mWindowContainerController.onDisplayChanged();
+    }
+
+    void addChild(ActivityStack stack, int position) {
+        if (position == POSITION_BOTTOM) {
+            position = 0;
+        } else if (position == POSITION_TOP) {
+            position = mStacks.size();
+        }
+        if (DEBUG_STACK) Slog.v(TAG_STACK, "addChild: attaching " + stack
+                + " to displayId=" + mDisplayId + " position=" + position);
+        addStackReferenceIfNeeded(stack);
+        positionChildAt(stack, position);
+        mSupervisor.mService.updateSleepIfNeededLocked();
+    }
+
+    void removeChild(ActivityStack stack) {
+        if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack
+                + " from displayId=" + mDisplayId);
+        mStacks.remove(stack);
+        if (mPreferredTopFocusableStack == stack) {
+            mPreferredTopFocusableStack = null;
+        }
+        removeStackReferenceIfNeeded(stack);
+        releaseSelfIfNeeded();
+        mSupervisor.mService.updateSleepIfNeededLocked();
+        onStackOrderChanged();
+    }
+
+    void positionChildAtTop(ActivityStack stack, boolean includingParents) {
+        positionChildAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
+    }
+
+    void positionChildAtTop(ActivityStack stack, boolean includingParents,
+            String updateLastFocusedStackReason) {
+        positionChildAt(stack, mStacks.size(), includingParents, updateLastFocusedStackReason);
+    }
+
+    void positionChildAtBottom(ActivityStack stack) {
+        positionChildAtBottom(stack, null /* updateLastFocusedStackReason */);
+    }
+
+    void positionChildAtBottom(ActivityStack stack, String updateLastFocusedStackReason) {
+        positionChildAt(stack, 0, false /* includingParents */, updateLastFocusedStackReason);
+    }
+
+    private void positionChildAt(ActivityStack stack, int position) {
+        positionChildAt(stack, position, false /* includingParents */,
+                null /* updateLastFocusedStackReason */);
+    }
+
+    private void positionChildAt(ActivityStack stack, int position, boolean includingParents,
+            String updateLastFocusedStackReason) {
+        // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
+        //       the position internally, also update the logic here
+        final ActivityStack prevFocusedStack = updateLastFocusedStackReason != null
+                ? getFocusedStack() : null;
+        final boolean wasContained = mStacks.remove(stack);
+        final int insertPosition = getTopInsertPosition(stack, position);
+        mStacks.add(insertPosition, stack);
+
+        // The insert position may be adjusted to non-top when there is always-on-top stack. Since
+        // the original position is preferred to be top, the stack should have higher priority when
+        // we are looking for top focusable stack. The condition {@code wasContained} restricts the
+        // preferred stack is set only when moving an existing stack to top instead of adding a new
+        // stack that may be too early (e.g. in the middle of launching or reparenting).
+        if (wasContained && position >= mStacks.size() - 1 && stack.isFocusableAndVisible()) {
+            mPreferredTopFocusableStack = stack;
+        } else if (mPreferredTopFocusableStack == stack) {
+            mPreferredTopFocusableStack = null;
+        }
+
+        if (updateLastFocusedStackReason != null) {
+            final ActivityStack currentFocusedStack = getFocusedStack();
+            if (currentFocusedStack != prevFocusedStack) {
+                mLastFocusedStack = prevFocusedStack;
+                EventLogTags.writeAmFocusedStack(mSupervisor.mCurrentUser, mDisplayId,
+                        currentFocusedStack == null ? -1 : currentFocusedStack.getStackId(),
+                        mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(),
+                        updateLastFocusedStackReason);
+            }
+        }
+
+        // Since positionChildAt() is called during the creation process of pinned stacks,
+        // ActivityStack#getWindowContainerController() can be null. In this special case,
+        // since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
+        // we don't have to call WindowContainerController#positionChildAt() here.
+        if (stack.getWindowContainerController() != null) {
+            mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
+                    insertPosition, includingParents);
+        }
+        onStackOrderChanged();
+    }
+
+    private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
+        int position = mStacks.size();
+        if (stack.inPinnedWindowingMode()) {
+            // Stack in pinned windowing mode is z-ordered on-top of all other stacks so okay to
+            // just return the candidate position.
+            return Math.min(position, candidatePosition);
+        }
+        while (position > 0) {
+            final ActivityStack targetStack = mStacks.get(position - 1);
+            if (!targetStack.isAlwaysOnTop()) {
+                // We reached a stack that isn't always-on-top.
+                break;
+            }
+            if (stack.isAlwaysOnTop() && !targetStack.inPinnedWindowingMode()) {
+                // Always on-top non-pinned windowing mode stacks can go anywhere below pinned stack.
+                break;
+            }
+            position--;
+        }
+        return Math.min(position, candidatePosition);
+    }
+
+    <T extends ActivityStack> T getStack(int stackId) {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack stack = mStacks.get(i);
+            if (stack.mStackId == stackId) {
+                return (T) stack;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return the topmost stack on the display that is compatible with the input windowing mode and
+     * activity type. {@code null} means no compatible stack on the display.
+     * @see ConfigurationContainer#isCompatible(int, int)
+     */
+    <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+        if (activityType == ACTIVITY_TYPE_HOME) {
+            return (T) mHomeStack;
+        } else if (activityType == ACTIVITY_TYPE_RECENTS) {
+            return (T) mRecentsStack;
+        }
+        if (windowingMode == WINDOWING_MODE_PINNED) {
+            return (T) mPinnedStack;
+        } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            return (T) mSplitScreenPrimaryStack;
+        }
+
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack stack = mStacks.get(i);
+            if (stack.isCompatible(windowingMode, activityType)) {
+                return (T) stack;
+            }
+        }
+        return null;
+    }
+
+    private boolean alwaysCreateStack(int windowingMode, int activityType) {
+        // Always create a stack for fullscreen, freeform, and split-screen-secondary windowing
+        // modes so that we can manage visual ordering and return types correctly.
+        return activityType == ACTIVITY_TYPE_STANDARD
+                && (windowingMode == WINDOWING_MODE_FULLSCREEN
+                || windowingMode == WINDOWING_MODE_FREEFORM
+                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+    }
+
+    /**
+     * Returns an existing stack compatible with the windowing mode and activity type or creates one
+     * if a compatible stack doesn't exist.
+     * @see #getStack(int, int)
+     * @see #createStack(int, int, boolean)
+     */
+    <T extends ActivityStack> T getOrCreateStack(int windowingMode, int activityType,
+            boolean onTop) {
+        if (!alwaysCreateStack(windowingMode, activityType)) {
+            T stack = getStack(windowingMode, activityType);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return createStack(windowingMode, activityType, onTop);
+    }
+
+    /**
+     * Returns an existing stack compatible with the input params or creates one
+     * if a compatible stack doesn't exist.
+     * @see #getOrCreateStack(int, int, boolean)
+     */
+    <T extends ActivityStack> T getOrCreateStack(@Nullable ActivityRecord r,
+            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, int activityType,
+            boolean onTop) {
+        // First preference is the windowing mode in the activity options if set.
+        int windowingMode = (options != null)
+                ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
+        // Validate that our desired windowingMode will work under the current conditions.
+        // UNDEFINED windowing mode is a valid result and means that the new stack will inherit
+        // it's display's windowing mode.
+        windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
+        return getOrCreateStack(windowingMode, activityType, onTop);
+    }
+
+    @VisibleForTesting
+    int getNextStackId() {
+        return sNextFreeStackId++;
+    }
+
+    /**
+     * Creates a stack matching the input windowing mode and activity type on this display.
+     * @param windowingMode The windowing mode the stack should be created in. If
+     *                      {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will
+     *                      inherit it's parent's windowing mode.
+     * @param activityType The activityType the stack should be created in. If
+     *                     {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
+     *                     be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
+     * @param onTop If true the stack will be created at the top of the display, else at the bottom.
+     * @return The newly created stack.
+     */
+    <T extends ActivityStack> T createStack(int windowingMode, int activityType, boolean onTop) {
+
+        if (activityType == ACTIVITY_TYPE_UNDEFINED) {
+            // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
+            // anything else should be passing it in anyways...
+            activityType = ACTIVITY_TYPE_STANDARD;
+        }
+
+        if (activityType != ACTIVITY_TYPE_STANDARD) {
+            // For now there can be only one stack of a particular non-standard activity type on a
+            // display. So, get that ignoring whatever windowing mode it is currently in.
+            T stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+            if (stack != null) {
+                throw new IllegalArgumentException("Stack=" + stack + " of activityType="
+                        + activityType + " already on display=" + this + ". Can't have multiple.");
+            }
+        }
+
+        final ActivityTaskManagerService service = mSupervisor.mService;
+        if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
+                service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
+                service.mSupportsPictureInPicture, activityType)) {
+            throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
+                    + windowingMode);
+        }
+
+        final int stackId = getNextStackId();
+        return createStackUnchecked(windowingMode, activityType, stackId, onTop);
+    }
+
+    @VisibleForTesting
+    <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+            int stackId, boolean onTop) {
+        if (windowingMode == WINDOWING_MODE_PINNED) {
+            return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
+        }
+        return (T) new ActivityStack(
+                        this, stackId, mSupervisor, windowingMode, activityType, onTop);
+    }
+
+    /**
+     * Get the preferred focusable stack in priority. If the preferred stack does not exist, find a
+     * focusable and visible stack from the top of stacks in this display.
+     */
+    ActivityStack getFocusedStack() {
+        if (mPreferredTopFocusableStack != null) {
+            return mPreferredTopFocusableStack;
+        }
+
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack stack = mStacks.get(i);
+            if (stack.isFocusableAndVisible()) {
+                return stack;
+            }
+        }
+
+        return null;
+    }
+
+    ActivityStack getNextFocusableStack() {
+        return getNextFocusableStack(null /* currentFocus */, false /* ignoreCurrent */);
+    }
+
+    ActivityStack getNextFocusableStack(ActivityStack currentFocus, boolean ignoreCurrent) {
+        final int currentWindowingMode = currentFocus != null
+                ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
+
+        ActivityStack candidate = null;
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack stack = mStacks.get(i);
+            if (ignoreCurrent && stack == currentFocus) {
+                continue;
+            }
+            if (!stack.isFocusableAndVisible()) {
+                continue;
+            }
+
+            if (currentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                    && candidate == null && stack.inSplitScreenPrimaryWindowingMode()) {
+                // If the currently focused stack is in split-screen secondary we save off the
+                // top primary split-screen stack as a candidate for focus because we might
+                // prefer focus to move to an other stack to avoid primary split-screen stack
+                // overlapping with a fullscreen stack when a fullscreen stack is higher in z
+                // than the next split-screen stack. Assistant stack, I am looking at you...
+                // We only move the focus to the primary-split screen stack if there isn't a
+                // better alternative.
+                candidate = stack;
+                continue;
+            }
+            if (candidate != null && stack.inSplitScreenSecondaryWindowingMode()) {
+                // Use the candidate stack since we are now at the secondary split-screen.
+                return candidate;
+            }
+            return stack;
+        }
+        return candidate;
+    }
+
+    ActivityRecord getResumedActivity() {
+        final ActivityStack focusedStack = getFocusedStack();
+        if (focusedStack == null) {
+            return null;
+        }
+        // TODO(b/111541062): Move this into ActivityStack#getResumedActivity()
+        // Check if the focused stack has the resumed activity
+        ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+        if (resumedActivity == null || resumedActivity.app == null) {
+            // If there is no registered resumed activity in the stack or it is not running -
+            // try to use previously resumed one.
+            resumedActivity = focusedStack.mPausingActivity;
+            if (resumedActivity == null || resumedActivity.app == null) {
+                // If previously resumed activity doesn't work either - find the topmost running
+                // activity that can be focused.
+                resumedActivity = focusedStack.topRunningActivityLocked(true /* focusableOnly */);
+            }
+        }
+        return resumedActivity;
+    }
+
+    ActivityStack getLastFocusedStack() {
+        return mLastFocusedStack;
+    }
+
+    boolean allResumedActivitiesComplete() {
+        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityRecord r = mStacks.get(stackNdx).getResumedActivity();
+            if (r != null && !r.isState(RESUMED)) {
+                return false;
+            }
+        }
+        final ActivityStack currentFocusedStack = getFocusedStack();
+        if (DEBUG_STACK) {
+            Slog.d(TAG_STACK, "allResumedActivitiesComplete: mLastFocusedStack changing from="
+                    + mLastFocusedStack + " to=" + currentFocusedStack);
+        }
+        mLastFocusedStack = currentFocusedStack;
+        return true;
+    }
+
+    /**
+     * Pause all activities in either all of the stacks or just the back stacks.
+     * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
+     * @param resuming The resuming activity.
+     * @param dontWait The resuming activity isn't going to wait for all activities to be paused
+     *                 before resuming.
+     * @return true if any activity was paused as a result of this call.
+     */
+    boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
+        boolean someActivityPaused = false;
+        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = mStacks.get(stackNdx);
+            // TODO(b/111541062): Check if resumed activity on this display instead
+            if (!mSupervisor.isTopDisplayFocusedStack(stack)
+                    && stack.getResumedActivity() != null) {
+                if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
+                        " mResumedActivity=" + stack.getResumedActivity());
+                someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
+                        dontWait);
+            }
+        }
+        return someActivityPaused;
+    }
+
+    /**
+     * Find task for putting the Activity in.
+     */
+    void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplay,
+            FindTaskResult result) {
+        mTmpFindTaskResult.clear();
+        for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = getChildAt(stackNdx);
+            if (!r.hasCompatibleActivityType(stack)) {
+                if (DEBUG_TASKS) {
+                    Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
+                }
+                continue;
+            }
+
+            stack.findTaskLocked(r, mTmpFindTaskResult);
+            // It is possible to have tasks in multiple stacks with the same root affinity, so
+            // we should keep looking after finding an affinity match to see if there is a
+            // better match in another stack. Also, task affinity isn't a good enough reason
+            // to target a display which isn't the source of the intent, so skip any affinity
+            // matches not on the specified display.
+            if (mTmpFindTaskResult.mRecord != null) {
+                if (mTmpFindTaskResult.mIdealMatch) {
+                    result.setTo(mTmpFindTaskResult);
+                    return;
+                } else if (isPreferredDisplay) {
+                    // Note: since the traversing through the stacks is top down, the floating
+                    // tasks should always have lower priority than any affinity-matching tasks
+                    // in the fullscreen stacks
+                    result.setTo(mTmpFindTaskResult);
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+     */
+    void removeStacksInWindowingModes(int... windowingModes) {
+        if (windowingModes == null || windowingModes.length == 0) {
+            return;
+        }
+
+        for (int j = windowingModes.length - 1 ; j >= 0; --j) {
+            final int windowingMode = windowingModes[j];
+            for (int i = mStacks.size() - 1; i >= 0; --i) {
+                final ActivityStack stack = mStacks.get(i);
+                if (!stack.isActivityTypeStandardOrUndefined()) {
+                    continue;
+                }
+                if (stack.getWindowingMode() != windowingMode) {
+                    continue;
+                }
+                mSupervisor.removeStack(stack);
+            }
+        }
+    }
+
+    void removeStacksWithActivityTypes(int... activityTypes) {
+        if (activityTypes == null || activityTypes.length == 0) {
+            return;
+        }
+
+        for (int j = activityTypes.length - 1 ; j >= 0; --j) {
+            final int activityType = activityTypes[j];
+            for (int i = mStacks.size() - 1; i >= 0; --i) {
+                final ActivityStack stack = mStacks.get(i);
+                if (stack.getActivityType() == activityType) {
+                    mSupervisor.removeStack(stack);
+                }
+            }
+        }
+    }
+
+    void onStackWindowingModeChanged(ActivityStack stack) {
+        removeStackReferenceIfNeeded(stack);
+        addStackReferenceIfNeeded(stack);
+    }
+
+    private void addStackReferenceIfNeeded(ActivityStack stack) {
+        final int activityType = stack.getActivityType();
+        final int windowingMode = stack.getWindowingMode();
+
+        if (activityType == ACTIVITY_TYPE_HOME) {
+            if (mHomeStack != null && mHomeStack != stack) {
+                throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
+                        + mHomeStack + " already exist on display=" + this + " stack=" + stack);
+            }
+            mHomeStack = stack;
+        } else if (activityType == ACTIVITY_TYPE_RECENTS) {
+            if (mRecentsStack != null && mRecentsStack != stack) {
+                throw new IllegalArgumentException("addStackReferenceIfNeeded: recents stack="
+                        + mRecentsStack + " already exist on display=" + this + " stack=" + stack);
+            }
+            mRecentsStack = stack;
+        }
+        if (windowingMode == WINDOWING_MODE_PINNED) {
+            if (mPinnedStack != null && mPinnedStack != stack) {
+                throw new IllegalArgumentException("addStackReferenceIfNeeded: pinned stack="
+                        + mPinnedStack + " already exist on display=" + this
+                        + " stack=" + stack);
+            }
+            mPinnedStack = stack;
+        } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            if (mSplitScreenPrimaryStack != null && mSplitScreenPrimaryStack != stack) {
+                throw new IllegalArgumentException("addStackReferenceIfNeeded:"
+                        + " split-screen-primary" + " stack=" + mSplitScreenPrimaryStack
+                        + " already exist on display=" + this + " stack=" + stack);
+            }
+            mSplitScreenPrimaryStack = stack;
+            onSplitScreenModeActivated();
+        }
+    }
+
+    private void removeStackReferenceIfNeeded(ActivityStack stack) {
+        if (stack == mHomeStack) {
+            mHomeStack = null;
+        } else if (stack == mRecentsStack) {
+            mRecentsStack = null;
+        } else if (stack == mPinnedStack) {
+            mPinnedStack = null;
+        } else if (stack == mSplitScreenPrimaryStack) {
+            mSplitScreenPrimaryStack = null;
+            // Inform the reset of the system that split-screen mode was dismissed so things like
+            // resizing all the other stacks can take place.
+            onSplitScreenModeDismissed();
+        }
+    }
+
+    private void onSplitScreenModeDismissed() {
+        mSupervisor.mWindowManager.deferSurfaceLayout();
+        try {
+            // Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
+            for (int i = mStacks.size() - 1; i >= 0; --i) {
+                final ActivityStack otherStack = mStacks.get(i);
+                if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
+                    continue;
+                }
+                otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED, false /* animate */,
+                        false /* showRecents */, false /* enteringSplitScreenMode */,
+                        true /* deferEnsuringVisibility */);
+            }
+        } finally {
+            final ActivityStack topFullscreenStack =
+                    getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
+            if (topFullscreenStack != null && mHomeStack != null && !isTopStack(mHomeStack)) {
+                // Whenever split-screen is dismissed we want the home stack directly behind the
+                // current top fullscreen stack so it shows up when the top stack is finished.
+                // TODO: Would be better to use ActivityDisplay.positionChildAt() for this, however
+                // ActivityDisplay doesn't have a direct controller to WM side yet. We can switch
+                // once we have that.
+                mHomeStack.moveToFront("onSplitScreenModeDismissed");
+                topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
+            }
+            mSupervisor.mWindowManager.continueSurfaceLayout();
+        }
+    }
+
+    private void onSplitScreenModeActivated() {
+        mSupervisor.mWindowManager.deferSurfaceLayout();
+        try {
+            // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
+            for (int i = mStacks.size() - 1; i >= 0; --i) {
+                final ActivityStack otherStack = mStacks.get(i);
+                if (otherStack == mSplitScreenPrimaryStack
+                        || !otherStack.affectedBySplitScreenResize()) {
+                    continue;
+                }
+                otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+                        false /* animate */, false /* showRecents */,
+                        true /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */);
+            }
+        } finally {
+            mSupervisor.mWindowManager.continueSurfaceLayout();
+        }
+    }
+
+    /**
+     * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
+     * @param windowingMode The windowing mode we are checking support for.
+     * @param supportsMultiWindow If we should consider support for multi-window mode in general.
+     * @param supportsSplitScreen If we should consider support for split-screen multi-window.
+     * @param supportsFreeform If we should consider support for freeform multi-window.
+     * @param supportsPip If we should consider support for picture-in-picture mutli-window.
+     * @param activityType The activity type under consideration.
+     * @return true if the windowing mode is supported.
+     */
+    private boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
+            boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
+            int activityType) {
+
+        if (windowingMode == WINDOWING_MODE_UNDEFINED
+                || windowingMode == WINDOWING_MODE_FULLSCREEN) {
+            return true;
+        }
+        if (!supportsMultiWindow) {
+            return false;
+        }
+
+        final int displayWindowingMode = getWindowingMode();
+        if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+            return supportsSplitScreen
+                    && WindowConfiguration.supportSplitScreenWindowingMode(activityType)
+                    // Freeform windows and split-screen windows don't mix well, so prevent
+                    // split windowing modes on freeform displays.
+                    && displayWindowingMode != WINDOWING_MODE_FREEFORM;
+        }
+
+        if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
+            return false;
+        }
+
+        if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Resolves the windowing mode that an {@link ActivityRecord} would be in if started on this
+     * display with the provided parameters.
+     *
+     * @param r The ActivityRecord in question.
+     * @param options Options to start with.
+     * @param task The task within-which the activity would start.
+     * @param activityType The type of activity to start.
+     * @return The resolved (not UNDEFINED) windowing-mode that the activity would be in.
+     */
+    int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+            @Nullable TaskRecord task, int activityType) {
+
+        // First preference if the windowing mode in the activity options if set.
+        int windowingMode = (options != null)
+                ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
+
+        // If windowing mode is unset, then next preference is the candidate task, then the
+        // activity record.
+        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+            if (task != null) {
+                windowingMode = task.getWindowingMode();
+            }
+            if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) {
+                windowingMode = r.getWindowingMode();
+            }
+            if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+                // Use the display's windowing mode.
+                windowingMode = getWindowingMode();
+            }
+        }
+        windowingMode = validateWindowingMode(windowingMode, r, task, activityType);
+        return windowingMode != WINDOWING_MODE_UNDEFINED
+                ? windowingMode : WINDOWING_MODE_FULLSCREEN;
+    }
+
+    /**
+     * Check that the requested windowing-mode is appropriate for the specified task and/or activity
+     * on this display.
+     *
+     * @param windowingMode The windowing-mode to validate.
+     * @param r The {@link ActivityRecord} to check against.
+     * @param task The {@link TaskRecord} to check against.
+     * @param activityType An activity type.
+     * @return The provided windowingMode or the closest valid mode which is appropriate.
+     */
+    int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r,
+        @Nullable TaskRecord task, int activityType) {
+        // Make sure the windowing mode we are trying to use makes sense for what is supported.
+        final ActivityTaskManagerService service = mSupervisor.mService;
+        boolean supportsMultiWindow = service.mSupportsMultiWindow;
+        boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow;
+        boolean supportsFreeform = service.mSupportsFreeformWindowManagement;
+        boolean supportsPip = service.mSupportsPictureInPicture;
+        if (supportsMultiWindow) {
+            if (task != null) {
+                supportsMultiWindow = task.isResizeable();
+                supportsSplitScreen = task.supportsSplitScreenWindowingMode();
+                // TODO: Do we need to check for freeform and Pip support here?
+            } else if (r != null) {
+                supportsMultiWindow = r.isResizeable();
+                supportsSplitScreen = r.supportsSplitScreenWindowingMode();
+                supportsFreeform = r.supportsFreeform();
+                supportsPip = r.supportsPictureInPicture();
+            }
+        }
+
+        final boolean inSplitScreenMode = hasSplitScreenPrimaryStack();
+        if (!inSplitScreenMode
+                && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
+            // Switch to the display's windowing mode if we are not in split-screen mode and we are
+            // trying to launch in split-screen secondary.
+            windowingMode = WINDOWING_MODE_UNDEFINED;
+        } else if (inSplitScreenMode && (windowingMode == WINDOWING_MODE_FULLSCREEN
+                        || windowingMode == WINDOWING_MODE_UNDEFINED)
+                && supportsSplitScreen) {
+            windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+        }
+
+        if (windowingMode != WINDOWING_MODE_UNDEFINED
+                && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
+                        supportsFreeform, supportsPip, activityType)) {
+            return windowingMode;
+        }
+        return WINDOWING_MODE_UNDEFINED;
+    }
+
+    /**
+     * Get the topmost stack on the display. It may be different from focused stack, because
+     * some stacks are not focusable (e.g. PiP).
+     */
+    ActivityStack getTopStack() {
+        return mStacks.isEmpty() ? null : mStacks.get(mStacks.size() - 1);
+    }
+
+    boolean isTopStack(ActivityStack stack) {
+        return stack == getTopStack();
+    }
+
+    boolean isTopNotPinnedStack(ActivityStack stack) {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack current = mStacks.get(i);
+            if (!current.inPinnedWindowingMode()) {
+                return current == stack;
+            }
+        }
+        return false;
+    }
+
+    ActivityStack getTopStackInWindowingMode(int windowingMode) {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack current = mStacks.get(i);
+            if (windowingMode == current.getWindowingMode()) {
+                return current;
+            }
+        }
+        return null;
+    }
+
+    ActivityRecord topRunningActivity() {
+        return topRunningActivity(false /* considerKeyguardState */);
+    }
+
+    /**
+     * Returns the top running activity in the focused stack. In the case the focused stack has no
+     * such activity, the next focusable stack on this display is returned.
+     *
+     * @param considerKeyguardState Indicates whether the locked state should be considered. if
+     *                              {@code true} and the keyguard is locked, only activities that
+     *                              can be shown on top of the keyguard will be considered.
+     * @return The top running activity. {@code null} if none is available.
+     */
+    ActivityRecord topRunningActivity(boolean considerKeyguardState) {
+        ActivityRecord topRunning = null;
+        final ActivityStack focusedStack = getFocusedStack();
+        if (focusedStack != null) {
+            topRunning = focusedStack.topRunningActivityLocked();
+        }
+
+        // Look in other focusable stacks.
+        if (topRunning == null) {
+            for (int i = mStacks.size() - 1; i >= 0; --i) {
+                final ActivityStack stack = mStacks.get(i);
+                // Only consider focusable stacks other than the current focused one.
+                if (stack == focusedStack || !stack.isFocusable()) {
+                    continue;
+                }
+                topRunning = stack.topRunningActivityLocked();
+                if (topRunning != null) {
+                    break;
+                }
+            }
+        }
+
+        // This activity can be considered the top running activity if we are not considering
+        // the locked state, the keyguard isn't locked, or we can show when locked.
+        if (topRunning != null && considerKeyguardState
+                && mSupervisor.getKeyguardController().isKeyguardLocked()
+                && !topRunning.canShowWhenLocked()) {
+            return null;
+        }
+
+        return topRunning;
+    }
+
+    int getIndexOf(ActivityStack stack) {
+        return mStacks.indexOf(stack);
+    }
+
+    @Override
+    public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+        final int currRotation = getOverrideConfiguration().windowConfiguration.getRotation();
+        if (currRotation != ROTATION_UNDEFINED
+                && currRotation != overrideConfiguration.windowConfiguration.getRotation()
+                && getWindowContainerController() != null) {
+            getWindowContainerController().applyRotation(currRotation,
+                    overrideConfiguration.windowConfiguration.getRotation());
+        }
+        super.onOverrideConfigurationChanged(overrideConfiguration);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newParentConfig) {
+        // update resources before cascade so that docked/pinned stacks use the correct info
+        getWindowContainerController().preOnConfigurationChanged();
+        super.onConfigurationChanged(newParentConfig);
+    }
+
+    void onLockTaskPackagesUpdated() {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            mStacks.get(i).onLockTaskPackagesUpdated();
+        }
+    }
+
+    /** We are in the process of exiting split-screen mode. */
+    void onExitingSplitScreenMode() {
+        // Remove reference to the primary-split-screen stack so it no longer has any effect on the
+        // display. For example, we want to be able to create fullscreen stack for standard activity
+        // types when exiting split-screen mode.
+        mSplitScreenPrimaryStack = null;
+    }
+
+    ActivityStack getSplitScreenPrimaryStack() {
+        return mSplitScreenPrimaryStack;
+    }
+
+    boolean hasSplitScreenPrimaryStack() {
+        return mSplitScreenPrimaryStack != null;
+    }
+
+    PinnedActivityStack getPinnedStack() {
+        return (PinnedActivityStack) mPinnedStack;
+    }
+
+    boolean hasPinnedStack() {
+        return mPinnedStack != null;
+    }
+
+    @Override
+    public String toString() {
+        return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
+    }
+
+    @Override
+    protected int getChildCount() {
+        return mStacks.size();
+    }
+
+    @Override
+    protected ActivityStack getChildAt(int index) {
+        return mStacks.get(index);
+    }
+
+    @Override
+    protected ConfigurationContainer getParent() {
+        return mSupervisor;
+    }
+
+    boolean isPrivate() {
+        return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
+    }
+
+    boolean isUidPresent(int uid) {
+        for (ActivityStack stack : mStacks) {
+            if (stack.isUidPresent(uid)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @see #mRemoved
+     */
+    boolean isRemoved() {
+        return mRemoved;
+    }
+
+    void remove() {
+        final boolean destroyContentOnRemoval = shouldDestroyContentOnRemove();
+        ActivityStack lastReparentedStack = null;
+        mPreferredTopFocusableStack = null;
+
+        // Stacks could be reparented from the removed display to other display. While
+        // reparenting the last stack of the removed display, the remove display is ready to be
+        // released (no more ActivityStack). But, we cannot release it at that moment or the
+        // related WindowContainer and WindowContainerController will also be removed. So, we
+        // set display as removed after reparenting stack finished.
+        final ActivityDisplay toDisplay = mSupervisor.getDefaultDisplay();
+        mSupervisor.beginDeferResume();
+        try {
+            int numStacks = mStacks.size();
+            // Keep the order from bottom to top.
+            for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
+                final ActivityStack stack = mStacks.get(stackNdx);
+                // Always finish non-standard type stacks.
+                if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) {
+                    stack.finishAllActivitiesLocked(true /* immediately */);
+                } else {
+                    // If default display is in split-window mode, set windowing mode of the stack
+                    // to split-screen secondary. Otherwise, set the windowing mode to undefined by
+                    // default to let stack inherited the windowing mode from the new display.
+                    final int windowingMode = toDisplay.hasSplitScreenPrimaryStack()
+                            ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                            : WINDOWING_MODE_UNDEFINED;
+                    stack.reparent(toDisplay, true /* onTop */, true /* displayRemoved */);
+                    stack.setWindowingMode(windowingMode);
+                    lastReparentedStack = stack;
+                }
+                // Stacks may be removed from this display. Ensure each stack will be processed and
+                // the loop will end.
+                stackNdx -= numStacks - mStacks.size();
+                numStacks = mStacks.size();
+            }
+        } finally {
+            mSupervisor.endDeferResume();
+        }
+        mRemoved = true;
+
+        // Only update focus/visibility for the last one because there may be many stacks are
+        // reparented and the intermediate states are unnecessary.
+        if (lastReparentedStack != null) {
+            lastReparentedStack.postReparent();
+        }
+        releaseSelfIfNeeded();
+
+        if (!mAllSleepTokens.isEmpty()) {
+            mSupervisor.mSleepTokens.removeAll(mAllSleepTokens);
+            mAllSleepTokens.clear();
+            mSupervisor.mService.updateSleepIfNeededLocked();
+        }
+    }
+
+    private void releaseSelfIfNeeded() {
+        if (mStacks.isEmpty() && mRemoved) {
+            mWindowContainerController.removeContainer();
+            mWindowContainerController = null;
+            mSupervisor.removeChild(this);
+            mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId);
+        }
+    }
+
+    /** Update and get all UIDs that are present on the display and have access to it. */
+    IntArray getPresentUIDs() {
+        mDisplayAccessUIDs.clear();
+        for (ActivityStack stack : mStacks) {
+            stack.getPresentUIDs(mDisplayAccessUIDs);
+        }
+        return mDisplayAccessUIDs;
+    }
+
+    /**
+     * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+     */
+    boolean supportsSystemDecorations() {
+        return mDisplay.supportsSystemDecorations()
+                // TODO (b/111363427): Remove this and set the new FLAG_SHOULD_SHOW_LAUNCHER flag
+                // (b/114338689) whenever vr 2d display id is set.
+                || mDisplayId == mSupervisor.mService.mVr2dDisplayId;
+    }
+
+    @VisibleForTesting
+    boolean shouldDestroyContentOnRemove() {
+        return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
+    }
+
+    boolean shouldSleep() {
+        return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
+                && (mSupervisor.mService.mRunningVoice == null);
+    }
+
+    void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
+        mWindowContainerController.setFocusedApp(r.appToken, moveFocusNow);
+    }
+
+    /**
+     * @return the stack currently above the {@param stack}.  Can be null if the {@param stack} is
+     *         already top-most.
+     */
+    ActivityStack getStackAbove(ActivityStack stack) {
+        final int stackIndex = mStacks.indexOf(stack) + 1;
+        return (stackIndex < mStacks.size()) ? mStacks.get(stackIndex) : null;
+    }
+
+    /**
+     * Adjusts the {@param stack} behind the last visible stack in the display if necessary.
+     * Generally used in conjunction with {@link #moveStackBehindStack}.
+     */
+    void moveStackBehindBottomMostVisibleStack(ActivityStack stack) {
+        if (stack.shouldBeVisible(null)) {
+            // Skip if the stack is already visible
+            return;
+        }
+
+        // Move the stack to the bottom to not affect the following visibility checks
+        positionChildAtBottom(stack);
+
+        // Find the next position where the stack should be placed
+        final int numStacks = mStacks.size();
+        for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
+            final ActivityStack s = mStacks.get(stackNdx);
+            if (s == stack) {
+                continue;
+            }
+            final int winMode = s.getWindowingMode();
+            final boolean isValidWindowingMode = winMode == WINDOWING_MODE_FULLSCREEN ||
+                    winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+            if (s.shouldBeVisible(null) && isValidWindowingMode) {
+                // Move the provided stack to behind this stack
+                positionChildAt(stack, Math.max(0, stackNdx - 1));
+                break;
+            }
+        }
+    }
+
+    /**
+     * Moves the {@param stack} behind the given {@param behindStack} if possible. If
+     * {@param behindStack} is not currently in the display, then then the stack is moved to the
+     * back. Generally used in conjunction with {@link #moveStackBehindBottomMostVisibleStack}.
+     */
+    void moveStackBehindStack(ActivityStack stack, ActivityStack behindStack) {
+        if (behindStack == null || behindStack == stack) {
+            return;
+        }
+
+        // Note that positionChildAt will first remove the given stack before inserting into the
+        // list, so we need to adjust the insertion index to account for the removed index
+        // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
+        //       position internally
+        final int stackIndex = mStacks.indexOf(stack);
+        final int behindStackIndex = mStacks.indexOf(behindStack);
+        final int insertIndex = stackIndex <= behindStackIndex
+                ? behindStackIndex - 1 : behindStackIndex;
+        positionChildAt(stack, Math.max(0, insertIndex));
+    }
+
+    void moveHomeStackToFront(String reason) {
+        if (mHomeStack != null) {
+            mHomeStack.moveToFront(reason);
+        }
+    }
+
+    /** Returns true if the focus activity was adjusted to the home stack top activity. */
+    boolean moveHomeActivityToTop(String reason) {
+        final ActivityRecord top = getHomeActivity();
+        if (top == null) {
+            return false;
+        }
+        top.moveFocusableActivityToTop(reason);
+        return true;
+    }
+
+    @Nullable
+    ActivityStack getHomeStack() {
+        return mHomeStack;
+    }
+
+    @Nullable
+    ActivityRecord getHomeActivity() {
+        return getHomeActivityForUser(mSupervisor.mCurrentUser);
+    }
+
+    @Nullable
+    ActivityRecord getHomeActivityForUser(int userId) {
+        if (mHomeStack == null) {
+            return null;
+        }
+
+        final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
+        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = tasks.get(taskNdx);
+            if (!task.isActivityTypeHome()) {
+                continue;
+            }
+
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (r.isActivityTypeHome()
+                        && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
+                    return r;
+                }
+            }
+        }
+        return null;
+    }
+
+    boolean isSleeping() {
+        return mSleeping;
+    }
+
+    void setIsSleeping(boolean asleep) {
+        mSleeping = asleep;
+    }
+
+    /**
+     * Adds a listener to be notified whenever the stack order in the display changes. Currently
+     * only used by the {@link RecentsAnimation} to determine whether to interrupt and cancel the
+     * current animation when the system state changes.
+     */
+    void registerStackOrderChangedListener(OnStackOrderChangedListener listener) {
+        if (!mStackOrderChangedCallbacks.contains(listener)) {
+            mStackOrderChangedCallbacks.add(listener);
+        }
+    }
+
+    /**
+     * Removes a previously registered stack order change listener.
+     */
+    void unregisterStackOrderChangedListener(OnStackOrderChangedListener listener) {
+        mStackOrderChangedCallbacks.remove(listener);
+    }
+
+    private void onStackOrderChanged() {
+        for (int i = mStackOrderChangedCallbacks.size() - 1; i >= 0; i--) {
+            mStackOrderChangedCallbacks.get(i).onStackOrderChanged();
+        }
+    }
+
+    /**
+     * See {@link DisplayWindowController#deferUpdateImeTarget()}
+     */
+    public void deferUpdateImeTarget() {
+        mWindowContainerController.deferUpdateImeTarget();
+    }
+
+    /**
+     * See {@link DisplayWindowController#deferUpdateImeTarget()}
+     */
+    public void continueUpdateImeTarget() {
+        mWindowContainerController.continueUpdateImeTarget();
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size());
+        final String myPrefix = prefix + " ";
+        if (mHomeStack != null) {
+            pw.println(myPrefix + "mHomeStack=" + mHomeStack);
+        }
+        if (mRecentsStack != null) {
+            pw.println(myPrefix + "mRecentsStack=" + mRecentsStack);
+        }
+        if (mPinnedStack != null) {
+            pw.println(myPrefix + "mPinnedStack=" + mPinnedStack);
+        }
+        if (mSplitScreenPrimaryStack != null) {
+            pw.println(myPrefix + "mSplitScreenPrimaryStack=" + mSplitScreenPrimaryStack);
+        }
+        if (mPreferredTopFocusableStack != null) {
+            pw.println(myPrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
+        }
+        if (mLastFocusedStack != null) {
+            pw.println(myPrefix + "mLastFocusedStack=" + mLastFocusedStack);
+        }
+    }
+
+    public void dumpStacks(PrintWriter pw) {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            pw.print(mStacks.get(i).mStackId);
+            if (i > 0) {
+                pw.print(",");
+            }
+        }
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
+        proto.write(ID, mDisplayId);
+        final ActivityStack focusedStack = getFocusedStack();
+        if (focusedStack != null) {
+            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+            final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
+            if (focusedActivity != null) {
+                focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+            }
+        } else {
+            proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
+        }
+        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = mStacks.get(stackNdx);
+            stack.writeToProto(proto, STACKS);
+        }
+        proto.end(token);
+    }
+
+    /**
+     * Callback for when the order of the stacks in the display changes.
+     */
+    interface OnStackOrderChangedListener {
+        void onStackOrderChanged();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
new file mode 100644
index 0000000..e3133ef
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Observe activity manager launch sequences.
+ *
+ * The activity manager can have at most 1 concurrent launch sequences. Calls to this interface
+ * are ordered by a happens-before relation for each defined state transition (see below).
+ *
+ * When a new launch sequence is made, that sequence is in the {@code INTENT_STARTED} state which
+ * is communicated by the {@link #onIntentStarted} callback. This is a transient state.
+ *
+ * The intent can fail to launch the activity, in which case the sequence's state transitions to
+ * {@code INTENT_FAILED} via {@link #onIntentFailed}. This is a terminal state.
+ *
+ * If an activity is successfully started, the launch sequence's state will transition into
+ * {@code STARTED} via {@link #onActivityLaunched}. This is a transient state.
+ *
+ * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled}
+ * or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states.
+ *
+ * Note that the {@link ActivityRecord} provided as a parameter to some state transitions isn't
+ * necessarily the same within a single launch sequence: it is only the top-most activity at the
+ * time (if any). Trampoline activities coalesce several activity starts into a single launch
+ * sequence.
+ *
+ * Upon reaching a terminal state, it is considered that there are no active launch sequences
+ * until a subsequent transition into {@code INTENT_STARTED} initiates a new launch sequence.
+ *
+ * <pre>
+ *        ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ╔══════════════════════════╗
+ *    ╴╴▶ ⋮ INTENT_STARTED ⋮ ──▶ ⋮     ACTIVITY_LAUNCHED     ⋮ ──▶ ║ ACTIVITY_LAUNCH_FINISHED ║
+ *        └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     ╚══════════════════════════╝
+ *          :                      :
+ *          :                      :
+ *          ▼                      ▼
+ *        ╔════════════════╗     ╔═══════════════════════════╗
+ *        ║ INTENT_FAILED  ║     ║ ACTIVITY_LAUNCH_CANCELLED ║
+ *        ╚════════════════╝     ╚═══════════════════════════╝
+ * </pre>
+ */
+public interface ActivityMetricsLaunchObserver {
+    /**
+     * The 'temperature' at which a launch sequence had started.
+     *
+     * The lower the temperature the more work has to be done during start-up.
+     * A 'cold' temperature means that a new process has been started and likely
+     * nothing is cached.
+     *
+     * A hot temperature means the existing activity is brought to the foreground.
+     * It may need to regenerate some objects as a result of {@code onTrimMemory}.
+     *
+     * A warm temperature is in the middle; an existing process is used, but the activity
+     * has to be created from scratch with {@code #onCreate}.
+     *
+     * @see https://developer.android.com/topic/performance/vitals/launch-time
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            TEMPERATURE_COLD,
+            TEMPERATURE_WARM,
+            TEMPERATURE_HOT
+    })
+    @interface Temperature {}
+
+    /** Cold launch sequence: a new process has started. */
+    public static final int TEMPERATURE_COLD = 1;
+    /** Warm launch sequence: process reused, but activity has to be created. */
+    public static final int TEMPERATURE_WARM = 2;
+    /** Hot launch sequence: process reused, activity brought-to-top. */
+    public static final int TEMPERATURE_HOT = 3;
+
+    /**
+     * Notifies the observer that a new launch sequence has begun as a result of a new intent.
+     *
+     * Once a launch sequence begins, the resolved activity will either subsequently start with
+     * {@link #onActivityLaunched} or abort early (for example due to a resolution error or due to
+     * a security error) with {@link #onIntentFailed}.
+     *
+     * Multiple calls to this method cannot occur without first terminating the current
+     * launch sequence.
+     */
+    public void onIntentStarted(@NonNull Intent intent);
+
+    /**
+     * Notifies the observer that the current launch sequence has failed to launch an activity.
+     *
+     * This function call terminates the current launch sequence. The next method call, if any,
+     * must be {@link #onIntentStarted}.
+     *
+     * Examples of this happening:
+     *  - Failure to resolve to an activity
+     *  - Calling package did not have the security permissions to call the requested activity
+     *  - Resolved activity was already running and only needed to be brought to the top
+     *
+     * Multiple calls to this method cannot occur without first terminating the current
+     * launch sequence.
+     */
+    public void onIntentFailed();
+
+    /**
+     * Notifies the observer that the current launch sequence had begun starting an activity.
+     *
+     * This is an intermediate state: once an activity begins starting, the entire launch sequence
+     * will later terminate by either finishing or cancelling.
+     *
+     * The initial activity is the first activity to be started as part of a launch sequence:
+     * it is represented by {@param activity} However, it isn't
+     * necessarily the activity which will be considered as displayed when the activity
+     * finishes launching (e.g. {@code activity} in {@link #onActivityLaunchFinished}).
+     *
+     * Multiple calls to this method cannot occur without first terminating the current
+     * launch sequence.
+     */
+    public void onActivityLaunched(@NonNull ActivityRecord activity,
+                                   @Temperature int temperature);
+
+    /**
+     * Notifies the observer that the current launch sequence has been aborted.
+     *
+     * This function call terminates the current launch sequence. The next method call, if any,
+     * must be {@link #onIntentStarted}.
+     *
+     * This can happen for many reasons, for example the user switches away to another app
+     * prior to the launch sequence completing, or the application being killed.
+     *
+     * Multiple calls to this method cannot occur without first terminating the current
+     * launch sequence.
+     *
+     * @param abortingActivity the last activity that had the top-most window during abort
+     *                         (this can be {@code null} in rare situations its unknown).
+     *
+     * @apiNote The aborting activity isn't necessarily the same as the starting activity;
+     *          in the case of a trampoline, multiple activities could've been started
+     *          and only the latest activity is reported here.
+     */
+    public void onActivityLaunchCancelled(@Nullable ActivityRecord abortingActivity);
+
+    /**
+     * Notifies the observer that the current launch sequence has been successfully finished.
+     *
+     * This function call terminates the current launch sequence. The next method call, if any,
+     * must be {@link #onIntentStarted}.
+     *
+     * A launch sequence is considered to be successfully finished when a frame is fully
+     * drawn for the first time: the top-most activity at the time is what's reported here.
+     *
+     * @param finalActivity the top-most activity whose windows were first to fully draw
+     *
+     * Multiple calls to this method cannot occur without first terminating the current
+     * launch sequence.
+     *
+     * @apiNote The finishing activity isn't necessarily the same as the starting activity;
+     *          in the case of a trampoline, multiple activities could've been started
+     *          and only the latest activity that was top-most during first-frame drawn
+     *          is reported here.
+     */
+    public void onActivityLaunchFinished(@NonNull ActivityRecord finalActivity);
+}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
new file mode 100644
index 0000000..9b01dfd
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -0,0 +1,1065 @@
+package com.android.server.wm;
+
+import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityManager.processStateAmToProto;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_ACTIVITY_START;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_BIND_APPLICATION_DELAY_MS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_CALLING_PACKAGE_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_CANCELLED;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DELAY_MS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_IS_EPHEMERAL;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_PROCESS_RUNNING;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_REPORTED_DRAWN;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_REPORTED_DRAWN_MS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_FLAGS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_FULLSCREEN;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_VISIBLE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_LAUNCH_MODE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_PROCESS_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_REAL_ACTIVITY;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_PACKAGE_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_UID;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_UID_HAS_ANY_VISIBLE_WINDOW;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_UID_PROC_STATE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CLASS_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_COMING_FROM_PENDING_INTENT;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_LAUNCH_TOKEN;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INTENT_ACTION;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_CUR_PROC_STATE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_CLIENT_ACTIVITIES;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_FOREGROUND_ACTIVITIES;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_FOREGROUND_SERVICES;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_OVERLAY_UI;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_TOP_UI;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_MILLIS_SINCE_FG_INTERACTION;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_MILLIS_SINCE_LAST_INTERACTION_EVENT;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_MILLIS_SINCE_UNIMPORTANT;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_PENDING_UI_CLEAN;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_PROCESS_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_PROC_STATE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_PACKAGE_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_SHORT_COMPONENT_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID_PROC_STATE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_WHITELIST_TAG;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_FILTER;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_REASON;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_HOT_LAUNCH;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.EventLogTags.AM_ACTIVITY_LAUNCH_TIME;
+import static com.android.server.am.MemoryStatUtil.MemoryStat;
+import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
+
+import android.app.WindowConfiguration.WindowingMode;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.dex.ArtManagerInternal;
+import android.content.pm.dex.PackageOptimizationInfo;
+import android.metrics.LogMaker;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.StatsLog;
+import android.util.TimeUtils;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.SomeArgs;
+import com.android.server.LocalServices;
+
+/**
+ * Listens to activity launches, transitions, visibility changes and window drawn callbacks to
+ * determine app launch times and draw delays. Source of truth for activity metrics and provides
+ * data for Tron, logcat, event logs and {@link android.app.WaitResult}.
+ *
+ * Tests:
+ * atest CtsActivityManagerDeviceTestCases:ActivityMetricsLoggerTests
+ */
+class ActivityMetricsLogger {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityMetricsLogger" : TAG_ATM;
+
+    // Window modes we are interested in logging. If we ever introduce a new type, we need to add
+    // a value here and increase the {@link #TRON_WINDOW_STATE_VARZ_STRINGS} array.
+    private static final int WINDOW_STATE_STANDARD = 0;
+    private static final int WINDOW_STATE_SIDE_BY_SIDE = 1;
+    private static final int WINDOW_STATE_FREEFORM = 2;
+    private static final int WINDOW_STATE_ASSISTANT = 3;
+    private static final int WINDOW_STATE_INVALID = -1;
+
+    private static final long INVALID_START_TIME = -1;
+    private static final int INVALID_DELAY = -1;
+    private static final int INVALID_TRANSITION_TYPE = -1;
+
+    private static final int MSG_CHECK_VISIBILITY = 0;
+
+    // Preallocated strings we are sending to tron, so we don't have to allocate a new one every
+    // time we log.
+    private static final String[] TRON_WINDOW_STATE_VARZ_STRINGS = {
+            "window_time_0", "window_time_1", "window_time_2", "window_time_3"};
+
+    private int mWindowState = WINDOW_STATE_STANDARD;
+    private long mLastLogTimeSecs;
+    private final ActivityStackSupervisor mSupervisor;
+    private final Context mContext;
+    private final MetricsLogger mMetricsLogger = new MetricsLogger();
+
+    // set to INVALID_START_TIME in reset.
+    // set to valid value in notifyActivityLaunching
+    private long mCurrentTransitionStartTime = INVALID_START_TIME;
+    private long mLastTransitionStartTime = INVALID_START_TIME;
+
+    private int mCurrentTransitionDeviceUptime;
+    private int mCurrentTransitionDelayMs;
+
+    /** If the any app transitions have been logged as starting, after the latest reset. */
+    private boolean mLoggedTransitionStarting;
+
+    /** Map : @WindowingMode int => WindowingModeTransitionInfo */
+    private final SparseArray<WindowingModeTransitionInfo> mWindowingModeTransitionInfo =
+            new SparseArray<>();
+    /** Map : @WindowingMode int => WindowingModeTransitionInfo */
+    private final SparseArray<WindowingModeTransitionInfo> mLastWindowingModeTransitionInfo =
+            new SparseArray<>();
+    private final H mHandler;
+
+    private ArtManagerInternal mArtManagerInternal;
+    private final StringBuilder mStringBuilder = new StringBuilder();
+
+    /**
+     * Due to the global single concurrent launch sequence, all calls to this observer must be made
+     * in-order on the same thread to fulfill the "happens-before" guarantee in LaunchObserver.
+     */
+    private final ActivityMetricsLaunchObserver mLaunchObserver = null;
+
+    private final class H extends Handler {
+
+        public H(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_CHECK_VISIBILITY:
+                    final SomeArgs args = (SomeArgs) msg.obj;
+                    checkVisibility((TaskRecord) args.arg1, (ActivityRecord) args.arg2);
+                    break;
+            }
+        }
+    }
+
+    private final class WindowingModeTransitionInfo {
+        /** The latest activity to have been launched. */
+        private ActivityRecord launchedActivity;
+        private int startResult;
+        private boolean currentTransitionProcessRunning;
+        /** Elapsed time from when we launch an activity to when its windows are drawn. */
+        private int windowsDrawnDelayMs;
+        private int startingWindowDelayMs = INVALID_DELAY;
+        private int bindApplicationDelayMs = INVALID_DELAY;
+        private int reason = APP_TRANSITION_TIMEOUT;
+        private boolean loggedWindowsDrawn;
+        private boolean loggedStartingWindowDrawn;
+        private boolean launchTraceActive;
+    }
+
+    final class WindowingModeTransitionInfoSnapshot {
+        final private ApplicationInfo applicationInfo;
+        final private WindowProcessController processRecord;
+        final String packageName;
+        final String launchedActivityName;
+        final private String launchedActivityLaunchedFromPackage;
+        final private String launchedActivityLaunchToken;
+        final private String launchedActivityAppRecordRequiredAbi;
+        final String launchedActivityShortComponentName;
+        final private String processName;
+        final private int reason;
+        final private int startingWindowDelayMs;
+        final private int bindApplicationDelayMs;
+        final int windowsDrawnDelayMs;
+        final int type;
+        final int userId;
+        /**
+         * Elapsed time from when we launch an activity to when the app reported it was
+         * fully drawn. If this is not reported then the value is set to INVALID_DELAY.
+         */
+        final int windowsFullyDrawnDelayMs;
+        final int activityRecordIdHashCode;
+
+        private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info) {
+            this(info, info.launchedActivity);
+        }
+
+        private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info,
+                ActivityRecord launchedActivity) {
+            this(info, launchedActivity, INVALID_DELAY);
+        }
+
+        private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info,
+                ActivityRecord launchedActivity, int windowsFullyDrawnDelayMs) {
+            applicationInfo = launchedActivity.appInfo;
+            packageName = launchedActivity.packageName;
+            launchedActivityName = launchedActivity.info.name;
+            launchedActivityLaunchedFromPackage = launchedActivity.launchedFromPackage;
+            launchedActivityLaunchToken = launchedActivity.info.launchToken;
+            launchedActivityAppRecordRequiredAbi = launchedActivity.app == null
+                    ? null
+                    : launchedActivity.app.getRequiredAbi();
+            reason = info.reason;
+            startingWindowDelayMs = info.startingWindowDelayMs;
+            bindApplicationDelayMs = info.bindApplicationDelayMs;
+            windowsDrawnDelayMs = info.windowsDrawnDelayMs;
+            type = getTransitionType(info);
+            processRecord = findProcessForActivity(launchedActivity);
+            processName = launchedActivity.processName;
+            userId = launchedActivity.userId;
+            launchedActivityShortComponentName = launchedActivity.shortComponentName;
+            activityRecordIdHashCode = System.identityHashCode(launchedActivity);
+            this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
+        }
+    }
+
+    ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context, Looper looper) {
+        mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
+        mSupervisor = supervisor;
+        mContext = context;
+        mHandler = new H(looper);
+    }
+
+    void logWindowState() {
+        final long now = SystemClock.elapsedRealtime() / 1000;
+        if (mWindowState != WINDOW_STATE_INVALID) {
+            // We log even if the window state hasn't changed, because the user might remain in
+            // home/fullscreen move forever and we would like to track this kind of behavior
+            // too.
+            MetricsLogger.count(mContext, TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState],
+                    (int) (now - mLastLogTimeSecs));
+        }
+        mLastLogTimeSecs = now;
+
+        mWindowState = WINDOW_STATE_INVALID;
+        ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+        if (stack == null) {
+            return;
+        }
+
+        if (stack.isActivityTypeAssistant()) {
+            mWindowState = WINDOW_STATE_ASSISTANT;
+            return;
+        }
+
+        @WindowingMode int windowingMode = stack.getWindowingMode();
+        if (windowingMode == WINDOWING_MODE_PINNED) {
+            stack = mSupervisor.findStackBehind(stack);
+            windowingMode = stack.getWindowingMode();
+        }
+        switch (windowingMode) {
+            case WINDOWING_MODE_FULLSCREEN:
+                mWindowState = WINDOW_STATE_STANDARD;
+                break;
+            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+            case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
+                mWindowState = WINDOW_STATE_SIDE_BY_SIDE;
+                break;
+            case WINDOWING_MODE_FREEFORM:
+                mWindowState = WINDOW_STATE_FREEFORM;
+                break;
+            default:
+                if (windowingMode != WINDOWING_MODE_UNDEFINED) {
+                    throw new IllegalStateException("Unknown windowing mode for stack=" + stack
+                            + " windowingMode=" + windowingMode);
+                }
+        }
+    }
+
+    /**
+     * Notifies the tracker at the earliest possible point when we are starting to launch an
+     * activity.
+     */
+    void notifyActivityLaunching(Intent intent) {
+        if (DEBUG_METRICS) {
+            Slog.i(TAG, String.format("notifyActivityLaunching: active:%b, intent:%s",
+                                      isAnyTransitionActive(),
+                                      intent));
+        }
+
+        if (!isAnyTransitionActive()) {
+
+            mCurrentTransitionStartTime = SystemClock.uptimeMillis();
+            mLastTransitionStartTime = mCurrentTransitionStartTime;
+
+            launchObserverNotifyIntentStarted(intent);
+        }
+    }
+
+    /**
+     * Notifies the tracker that the activity is actually launching.
+     *
+     * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the
+     *                   launch
+     * @param launchedActivity the activity that is being launched
+     */
+    void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity) {
+        final WindowProcessController processRecord = findProcessForActivity(launchedActivity);
+        final boolean processRunning = processRecord != null;
+
+        // We consider this a "process switch" if the process of the activity that gets launched
+        // didn't have an activity that was in started state. In this case, we assume that lot
+        // of caches might be purged so the time until it produces the first frame is very
+        // interesting.
+        final boolean processSwitch = processRecord == null
+                || !processRecord.hasStartedActivity(launchedActivity);
+
+        notifyActivityLaunched(resultCode, launchedActivity, processRunning, processSwitch);
+    }
+
+    /**
+     * Notifies the tracker the the activity is actually launching.
+     *
+     * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the
+     *                   launch
+     * @param launchedActivity the activity being launched
+     * @param processRunning whether the process that will contains the activity is already running
+     * @param processSwitch whether the process that will contain the activity didn't have any
+     *                      activity that was stopped, i.e. the started activity is "switching"
+     *                      processes
+     */
+    private void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity,
+            boolean processRunning, boolean processSwitch) {
+
+        if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched"
+                + " resultCode=" + resultCode
+                + " launchedActivity=" + launchedActivity
+                + " processRunning=" + processRunning
+                + " processSwitch=" + processSwitch);
+
+        // If we are already in an existing transition, only update the activity name, but not the
+        // other attributes.
+        final @WindowingMode int windowingMode = launchedActivity != null
+                ? launchedActivity.getWindowingMode()
+                : WINDOWING_MODE_UNDEFINED;
+        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
+        if (mCurrentTransitionStartTime == INVALID_START_TIME) {
+            // No transition is active ignore this launch.
+            return;
+        }
+
+        if (launchedActivity != null && launchedActivity.nowVisible) {
+            // Launched activity is already visible. We cannot measure windows drawn delay.
+            reset(true /* abort */, info, "launched activity already visible");
+            return;
+        }
+
+        if (launchedActivity != null && info != null) {
+            // If we are already in an existing transition, only update the activity name, but not
+            // the other attributes.
+
+            // Coalesce multiple (trampoline) activities from a single sequence together.
+            info.launchedActivity = launchedActivity;
+            return;
+        }
+
+        final boolean otherWindowModesLaunching =
+                mWindowingModeTransitionInfo.size() > 0 && info == null;
+        if ((!isLoggableResultCode(resultCode) || launchedActivity == null || !processSwitch
+                || windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) {
+            // Failed to launch or it was not a process switch, so we don't care about the timing.
+            reset(true /* abort */, info, "failed to launch or not a process switch");
+            return;
+        } else if (otherWindowModesLaunching) {
+            // Don't log this windowing mode but continue with the other windowing modes.
+            return;
+        }
+
+        if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched successful");
+
+        // A new launch sequence [with the windowingMode] has begun.
+        // Start tracking it.
+        final WindowingModeTransitionInfo newInfo = new WindowingModeTransitionInfo();
+        newInfo.launchedActivity = launchedActivity;
+        newInfo.currentTransitionProcessRunning = processRunning;
+        newInfo.startResult = resultCode;
+        mWindowingModeTransitionInfo.put(windowingMode, newInfo);
+        mLastWindowingModeTransitionInfo.put(windowingMode, newInfo);
+        mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000);
+        startTraces(newInfo);
+        launchObserverNotifyActivityLaunched(newInfo);
+    }
+
+    /**
+     * @return True if we should start logging an event for an activity start that returned
+     *         {@code resultCode} and that we'll indeed get a windows drawn event.
+     */
+    private boolean isLoggableResultCode(int resultCode) {
+        return resultCode == START_SUCCESS || resultCode == START_TASK_TO_FRONT;
+    }
+
+    /**
+     * Notifies the tracker that all windows of the app have been drawn.
+     */
+    WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(@WindowingMode int windowingMode,
+                                                           long timestamp) {
+        if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode);
+
+        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
+        if (info == null || info.loggedWindowsDrawn) {
+            return null;
+        }
+        info.windowsDrawnDelayMs = calculateDelay(timestamp);
+        info.loggedWindowsDrawn = true;
+        final WindowingModeTransitionInfoSnapshot infoSnapshot =
+                new WindowingModeTransitionInfoSnapshot(info);
+        if (allWindowsDrawn() && mLoggedTransitionStarting) {
+            reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn");
+        }
+        return infoSnapshot;
+    }
+
+    /**
+     * Notifies the tracker that the starting window was drawn.
+     */
+    void notifyStartingWindowDrawn(@WindowingMode int windowingMode, long timestamp) {
+        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
+        if (info == null || info.loggedStartingWindowDrawn) {
+            return;
+        }
+        info.loggedStartingWindowDrawn = true;
+        info.startingWindowDelayMs = calculateDelay(timestamp);
+    }
+
+    /**
+     * Notifies the tracker that the app transition is starting.
+     *
+     * @param windowingModeToReason A map from windowing mode to a reason integer, which must be on
+     *                              of ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
+     */
+    void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestamp) {
+        if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
+            // Ignore calls to this made after a reset and prior to notifyActivityLaunching.
+
+            // Ignore any subsequent notifyTransitionStarting until the next reset.
+            return;
+        }
+        if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
+        mCurrentTransitionDelayMs = calculateDelay(timestamp);
+        mLoggedTransitionStarting = true;
+
+        WindowingModeTransitionInfo foundInfo = null;
+        for (int index = windowingModeToReason.size() - 1; index >= 0; index--) {
+            final @WindowingMode int windowingMode = windowingModeToReason.keyAt(index);
+            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
+                    windowingMode);
+            if (info == null) {
+                continue;
+            }
+            info.reason = windowingModeToReason.valueAt(index);
+            foundInfo = info;
+        }
+        if (allWindowsDrawn()) {
+            reset(false /* abort */, foundInfo, "notifyTransitionStarting - all windows drawn");
+        }
+    }
+
+    /**
+     * Notifies the tracker that the visibility of an app is changing.
+     *
+     * @param activityRecord the app that is changing its visibility
+     */
+    void notifyVisibilityChanged(ActivityRecord activityRecord) {
+        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
+                activityRecord.getWindowingMode());
+        if (info == null) {
+            return;
+        }
+        if (info.launchedActivity != activityRecord) {
+            return;
+        }
+        final TaskRecord t = activityRecord.getTask();
+        final SomeArgs args = SomeArgs.obtain();
+        args.arg1 = t;
+        args.arg2 = activityRecord;
+        mHandler.obtainMessage(MSG_CHECK_VISIBILITY, args).sendToTarget();
+    }
+
+    private void checkVisibility(TaskRecord t, ActivityRecord r) {
+        synchronized (mSupervisor.mService.mGlobalLock) {
+
+            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
+                    r.getWindowingMode());
+
+            // If we have an active transition that's waiting on a certain activity that will be
+            // invisible now, we'll never get onWindowsDrawn, so abort the transition if necessary.
+            if (info != null && !t.isVisible()) {
+                if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible"
+                        + " activity=" + r);
+                logAppTransitionCancel(info);
+                mWindowingModeTransitionInfo.remove(r.getWindowingMode());
+                if (mWindowingModeTransitionInfo.size() == 0) {
+                    reset(true /* abort */, info, "notifyVisibilityChanged to invisible");
+                }
+            }
+        }
+    }
+
+    /**
+     * Notifies the tracker that we called immediately before we call bindApplication on the client.
+     *
+     * @param appInfo The client into which we'll call bindApplication.
+     */
+    void notifyBindApplication(ApplicationInfo appInfo) {
+        for (int i = mWindowingModeTransitionInfo.size() - 1; i >= 0; i--) {
+            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(i);
+
+            // App isn't attached to record yet, so match with info.
+            if (info.launchedActivity.appInfo == appInfo) {
+                info.bindApplicationDelayMs = calculateCurrentDelay();
+            }
+        }
+    }
+
+    private boolean allWindowsDrawn() {
+        for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) {
+            if (!mWindowingModeTransitionInfo.valueAt(index).loggedWindowsDrawn) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean isAnyTransitionActive() {
+        return mCurrentTransitionStartTime != INVALID_START_TIME
+                && mWindowingModeTransitionInfo.size() > 0;
+    }
+
+    private void reset(boolean abort, WindowingModeTransitionInfo info, String cause) {
+        if (DEBUG_METRICS) Slog.i(TAG, "reset abort=" + abort + ",cause=" + cause);
+        if (!abort && isAnyTransitionActive()) {
+            logAppTransitionMultiEvents();
+        }
+        stopLaunchTrace(info);
+
+        // Ignore reset-after reset.
+        if (isAnyTransitionActive()) {
+            // LaunchObserver callbacks.
+            if (abort) {
+                launchObserverNotifyActivityLaunchCancelled(info);
+            } else {
+                launchObserverNotifyActivityLaunchFinished(info);
+            }
+        } else {
+            launchObserverNotifyIntentFailed();
+        }
+
+        mCurrentTransitionStartTime = INVALID_START_TIME;
+        mCurrentTransitionDelayMs = INVALID_DELAY;
+        mLoggedTransitionStarting = false;
+        mWindowingModeTransitionInfo.clear();
+    }
+
+    private int calculateCurrentDelay() {
+        // Shouldn't take more than 25 days to launch an app, so int is fine here.
+        return (int) (SystemClock.uptimeMillis() - mCurrentTransitionStartTime);
+    }
+
+    private int calculateDelay(long timestamp) {
+        // Shouldn't take more than 25 days to launch an app, so int is fine here.
+        return (int) (timestamp - mCurrentTransitionStartTime);
+    }
+
+    private void logAppTransitionCancel(WindowingModeTransitionInfo info) {
+        final int type = getTransitionType(info);
+        if (type == INVALID_TRANSITION_TYPE) {
+            return;
+        }
+        final LogMaker builder = new LogMaker(APP_TRANSITION_CANCELLED);
+        builder.setPackageName(info.launchedActivity.packageName);
+        builder.setType(type);
+        builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name);
+        mMetricsLogger.write(builder);
+        StatsLog.write(
+                StatsLog.APP_START_CANCELED,
+                info.launchedActivity.appInfo.uid,
+                info.launchedActivity.packageName,
+                convertAppStartTransitionType(type),
+                info.launchedActivity.info.name);
+        if (DEBUG_METRICS) {
+            Slog.i(TAG, String.format("APP_START_CANCELED(%s, %s, %s, %s)",
+                    info.launchedActivity.appInfo.uid,
+                    info.launchedActivity.packageName,
+                    convertAppStartTransitionType(type),
+                    info.launchedActivity.info.name));
+        }
+    }
+
+    private void logAppTransitionMultiEvents() {
+        if (DEBUG_METRICS) Slog.i(TAG, "logging transition events");
+        for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) {
+            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(index);
+            final int type = getTransitionType(info);
+            if (type == INVALID_TRANSITION_TYPE) {
+                return;
+            }
+
+            // Take a snapshot of the transition info before sending it to the handler for logging.
+            // This will avoid any races with other operations that modify the ActivityRecord.
+            final WindowingModeTransitionInfoSnapshot infoSnapshot =
+                    new WindowingModeTransitionInfoSnapshot(info);
+            final int currentTransitionDeviceUptime = mCurrentTransitionDeviceUptime;
+            final int currentTransitionDelayMs = mCurrentTransitionDelayMs;
+            BackgroundThread.getHandler().post(() -> logAppTransition(
+                    currentTransitionDeviceUptime, currentTransitionDelayMs, infoSnapshot));
+            BackgroundThread.getHandler().post(() -> logAppDisplayed(infoSnapshot));
+
+            info.launchedActivity.info.launchToken = null;
+        }
+    }
+
+    // This gets called on a background thread without holding the activity manager lock.
+    private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs,
+            WindowingModeTransitionInfoSnapshot info) {
+        final LogMaker builder = new LogMaker(APP_TRANSITION);
+        builder.setPackageName(info.packageName);
+        builder.setType(info.type);
+        builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivityName);
+        final boolean isInstantApp = info.applicationInfo.isInstantApp();
+        if (info.launchedActivityLaunchedFromPackage != null) {
+            builder.addTaggedData(APP_TRANSITION_CALLING_PACKAGE_NAME,
+                    info.launchedActivityLaunchedFromPackage);
+        }
+        String launchToken = info.launchedActivityLaunchToken;
+        if (launchToken != null) {
+            builder.addTaggedData(FIELD_INSTANT_APP_LAUNCH_TOKEN, launchToken);
+        }
+        builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
+        builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
+                currentTransitionDeviceUptime);
+        builder.addTaggedData(APP_TRANSITION_DELAY_MS, currentTransitionDelayMs);
+        builder.setSubtype(info.reason);
+        if (info.startingWindowDelayMs != INVALID_DELAY) {
+            builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
+                    info.startingWindowDelayMs);
+        }
+        if (info.bindApplicationDelayMs != INVALID_DELAY) {
+            builder.addTaggedData(APP_TRANSITION_BIND_APPLICATION_DELAY_MS,
+                    info.bindApplicationDelayMs);
+        }
+        builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs);
+        final ArtManagerInternal artManagerInternal = getArtManagerInternal();
+        final PackageOptimizationInfo packageOptimizationInfo =
+                (artManagerInternal == null) || (info.launchedActivityAppRecordRequiredAbi == null)
+                ? PackageOptimizationInfo.createWithNoInfo()
+                : artManagerInternal.getPackageOptimizationInfo(
+                        info.applicationInfo,
+                        info.launchedActivityAppRecordRequiredAbi);
+        builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_REASON,
+                packageOptimizationInfo.getCompilationReason());
+        builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_FILTER,
+                packageOptimizationInfo.getCompilationFilter());
+        mMetricsLogger.write(builder);
+        StatsLog.write(
+                StatsLog.APP_START_OCCURRED,
+                info.applicationInfo.uid,
+                info.packageName,
+                convertAppStartTransitionType(info.type),
+                info.launchedActivityName,
+                info.launchedActivityLaunchedFromPackage,
+                isInstantApp,
+                currentTransitionDeviceUptime * 1000,
+                info.reason,
+                currentTransitionDelayMs,
+                info.startingWindowDelayMs,
+                info.bindApplicationDelayMs,
+                info.windowsDrawnDelayMs,
+                launchToken,
+                packageOptimizationInfo.getCompilationReason(),
+                packageOptimizationInfo.getCompilationFilter());
+
+        if (DEBUG_METRICS) {
+            Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)",
+                    info.applicationInfo.uid,
+                    info.packageName,
+                    convertAppStartTransitionType(info.type),
+                    info.launchedActivityName,
+                    info.launchedActivityLaunchedFromPackage));
+        }
+
+
+        logAppStartMemoryStateCapture(info);
+    }
+
+    private void logAppDisplayed(WindowingModeTransitionInfoSnapshot info) {
+        if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) {
+            return;
+        }
+
+        EventLog.writeEvent(AM_ACTIVITY_LAUNCH_TIME,
+                info.userId, info.activityRecordIdHashCode, info.launchedActivityShortComponentName,
+                info.windowsDrawnDelayMs);
+
+        StringBuilder sb = mStringBuilder;
+        sb.setLength(0);
+        sb.append("Displayed ");
+        sb.append(info.launchedActivityShortComponentName);
+        sb.append(": ");
+        TimeUtils.formatDuration(info.windowsDrawnDelayMs, sb);
+        Log.i(TAG, sb.toString());
+    }
+
+    private int convertAppStartTransitionType(int tronType) {
+        if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
+            return StatsLog.APP_START_OCCURRED__TYPE__COLD;
+        }
+        if (tronType == TYPE_TRANSITION_WARM_LAUNCH) {
+            return StatsLog.APP_START_OCCURRED__TYPE__WARM;
+        }
+        if (tronType == TYPE_TRANSITION_HOT_LAUNCH) {
+            return StatsLog.APP_START_OCCURRED__TYPE__HOT;
+        }
+        return StatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
+     }
+
+    WindowingModeTransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r,
+            boolean restoredFromBundle) {
+        final WindowingModeTransitionInfo info = mLastWindowingModeTransitionInfo.get(
+                r.getWindowingMode());
+        if (info == null) {
+            return null;
+        }
+
+        // Record the handling of the reportFullyDrawn callback in the trace system. This is not
+        // actually used to trace this function, but instead the logical task that this function
+        // fullfils (handling reportFullyDrawn() callbacks).
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                "ActivityManager:ReportingFullyDrawn " + info.launchedActivity.packageName);
+
+        final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
+        builder.setPackageName(r.packageName);
+        builder.addTaggedData(FIELD_CLASS_NAME, r.info.name);
+        long startupTimeMs = SystemClock.uptimeMillis() - mLastTransitionStartTime;
+        builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS, startupTimeMs);
+        builder.setType(restoredFromBundle
+                ? TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE
+                : TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE);
+        builder.addTaggedData(APP_TRANSITION_PROCESS_RUNNING,
+                info.currentTransitionProcessRunning ? 1 : 0);
+        mMetricsLogger.write(builder);
+        StatsLog.write(
+                StatsLog.APP_START_FULLY_DRAWN,
+                info.launchedActivity.appInfo.uid,
+                info.launchedActivity.packageName,
+                restoredFromBundle
+                        ? StatsLog.APP_START_FULLY_DRAWN__TYPE__WITH_BUNDLE
+                        : StatsLog.APP_START_FULLY_DRAWN__TYPE__WITHOUT_BUNDLE,
+                info.launchedActivity.info.name,
+                info.currentTransitionProcessRunning,
+                startupTimeMs);
+
+        // Ends the trace started at the beginning of this function. This is located here to allow
+        // the trace slice to have a noticable duration.
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+        final WindowingModeTransitionInfoSnapshot infoSnapshot =
+                new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
+        BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
+        return infoSnapshot;
+    }
+
+    private void logAppFullyDrawn(WindowingModeTransitionInfoSnapshot info) {
+        if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) {
+            return;
+        }
+
+        StringBuilder sb = mStringBuilder;
+        sb.setLength(0);
+        sb.append("Fully drawn ");
+        sb.append(info.launchedActivityShortComponentName);
+        sb.append(": ");
+        TimeUtils.formatDuration(info.windowsFullyDrawnDelayMs, sb);
+        Log.i(TAG, sb.toString());
+    }
+
+    void logActivityStart(Intent intent, WindowProcessController callerApp, ActivityRecord r,
+            int callingUid, String callingPackage, int callingUidProcState,
+            boolean callingUidHasAnyVisibleWindow,
+            int realCallingUid, int realCallingUidProcState,
+            boolean realCallingUidHasAnyVisibleWindow,
+            int targetUid, String targetPackage, int targetUidProcState,
+            boolean targetUidHasAnyVisibleWindow, String targetWhitelistTag,
+            boolean comingFromPendingIntent) {
+
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long nowUptime = SystemClock.uptimeMillis();
+        final LogMaker builder = new LogMaker(ACTION_ACTIVITY_START);
+        builder.setTimestamp(System.currentTimeMillis());
+        builder.addTaggedData(FIELD_CALLING_UID, callingUid);
+        builder.addTaggedData(FIELD_CALLING_PACKAGE_NAME, callingPackage);
+        builder.addTaggedData(FIELD_CALLING_UID_PROC_STATE,
+                processStateAmToProto(callingUidProcState));
+        builder.addTaggedData(FIELD_CALLING_UID_HAS_ANY_VISIBLE_WINDOW,
+                callingUidHasAnyVisibleWindow ? 1 : 0);
+        builder.addTaggedData(FIELD_REAL_CALLING_UID, realCallingUid);
+        builder.addTaggedData(FIELD_REAL_CALLING_UID_PROC_STATE,
+                processStateAmToProto(realCallingUidProcState));
+        builder.addTaggedData(FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW,
+                realCallingUidHasAnyVisibleWindow ? 1 : 0);
+        builder.addTaggedData(FIELD_TARGET_UID, targetUid);
+        builder.addTaggedData(FIELD_TARGET_PACKAGE_NAME, targetPackage);
+        builder.addTaggedData(FIELD_TARGET_UID_PROC_STATE,
+                processStateAmToProto(targetUidProcState));
+        builder.addTaggedData(FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW,
+                targetUidHasAnyVisibleWindow ? 1 : 0);
+        builder.addTaggedData(FIELD_TARGET_WHITELIST_TAG, targetWhitelistTag);
+        builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);
+        builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0);
+        builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
+        if (callerApp != null) {
+            builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.mName);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_CUR_PROC_STATE,
+                    processStateAmToProto(callerApp.getCurrentProcState()));
+            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_CLIENT_ACTIVITIES,
+                    callerApp.hasClientActivities() ? 1 : 0);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_FOREGROUND_SERVICES,
+                    callerApp.hasForegroundServices() ? 1 : 0);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_FOREGROUND_ACTIVITIES,
+                    callerApp.hasForegroundActivities() ? 1 : 0);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_TOP_UI, callerApp.hasTopUi() ? 1 : 0);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_OVERLAY_UI,
+                    callerApp.hasOverlayUi() ? 1 : 0);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_PENDING_UI_CLEAN,
+                    callerApp.hasPendingUiClean() ? 1 : 0);
+            if (callerApp.getInteractionEventTime() != 0) {
+                builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_LAST_INTERACTION_EVENT,
+                        (nowElapsed - callerApp.getInteractionEventTime()));
+            }
+            if (callerApp.getFgInteractionTime() != 0) {
+                builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_FG_INTERACTION,
+                        (nowElapsed - callerApp.getFgInteractionTime()));
+            }
+            if (callerApp.getWhenUnimportant() != 0) {
+                builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_UNIMPORTANT,
+                        (nowUptime - callerApp.getWhenUnimportant()));
+            }
+        }
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY, r.realActivity.toShortString());
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0);
+        if (r.lastVisibleTime != 0) {
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE,
+                    (nowUptime - r.lastVisibleTime));
+        }
+        if (r.resultTo != null) {
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME, r.resultTo.packageName);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME,
+                    r.resultTo.shortComponentName);
+        }
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD,
+                r.visibleIgnoringKeyguard ? 1 : 0);
+        if (r.lastLaunchTime != 0) {
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH,
+                    (nowUptime - r.lastLaunchTime));
+        }
+        mMetricsLogger.write(builder);
+    }
+
+    private int getTransitionType(WindowingModeTransitionInfo info) {
+        if (info.currentTransitionProcessRunning) {
+            if (info.startResult == START_SUCCESS) {
+                return TYPE_TRANSITION_WARM_LAUNCH;
+            } else if (info.startResult == START_TASK_TO_FRONT) {
+                return TYPE_TRANSITION_HOT_LAUNCH;
+            }
+        } else if (info.startResult == START_SUCCESS) {
+            return TYPE_TRANSITION_COLD_LAUNCH;
+        }
+        return INVALID_TRANSITION_TYPE;
+    }
+
+    private void logAppStartMemoryStateCapture(WindowingModeTransitionInfoSnapshot info) {
+        if (info.processRecord == null) {
+            if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture processRecord null");
+            return;
+        }
+
+        final int pid = info.processRecord.getPid();
+        final int uid = info.applicationInfo.uid;
+        final MemoryStat memoryStat = readMemoryStatFromFilesystem(uid, pid);
+        if (memoryStat == null) {
+            if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture memoryStat null");
+            return;
+        }
+
+        StatsLog.write(
+                StatsLog.APP_START_MEMORY_STATE_CAPTURED,
+                uid,
+                info.processName,
+                info.launchedActivityName,
+                memoryStat.pgfault,
+                memoryStat.pgmajfault,
+                memoryStat.rssInBytes,
+                memoryStat.cacheInBytes,
+                memoryStat.swapInBytes);
+    }
+
+    private WindowProcessController findProcessForActivity(ActivityRecord launchedActivity) {
+        return launchedActivity != null
+                ? mSupervisor.mService.mProcessNames.get(
+                        launchedActivity.processName, launchedActivity.appInfo.uid)
+                : null;
+    }
+
+    private ArtManagerInternal getArtManagerInternal() {
+        if (mArtManagerInternal == null) {
+            // Note that this may be null.
+            // ArtManagerInternal is registered during PackageManagerService
+            // initialization which happens after ActivityManagerService.
+            mArtManagerInternal = LocalServices.getService(ArtManagerInternal.class);
+        }
+        return mArtManagerInternal;
+    }
+
+    /**
+     * Starts traces for app launch.
+     *
+     * @param info
+     * */
+    private void startTraces(WindowingModeTransitionInfo info) {
+        if (info == null) {
+            return;
+        }
+        int transitionType = getTransitionType(info);
+        if (!info.launchTraceActive && transitionType == TYPE_TRANSITION_WARM_LAUNCH
+                || transitionType == TYPE_TRANSITION_COLD_LAUNCH) {
+            Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
+                    + info.launchedActivity.packageName, 0);
+            info.launchTraceActive = true;
+        }
+    }
+
+    private void stopLaunchTrace(WindowingModeTransitionInfo info) {
+        if (info == null) {
+            return;
+        }
+        if (info.launchTraceActive) {
+            Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
+                    + info.launchedActivity.packageName, 0);
+            info.launchTraceActive = false;
+        }
+    }
+
+    /** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */
+    private void launchObserverNotifyIntentStarted(Intent intent) {
+        if (mLaunchObserver != null) {
+            // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+            mLaunchObserver.onIntentStarted(intent);
+        }
+    }
+
+    /**
+     * Notify the {@link ActivityMetricsLaunchObserver} that the previous launch sequence has
+     * aborted due to intent failure (e.g. intent resolve failed or security error, etc) or
+     * intent being delivered to the top running activity.
+     */
+    private void launchObserverNotifyIntentFailed() {
+        if (mLaunchObserver != null) {
+            mLaunchObserver.onIntentFailed();
+        }
+    }
+
+    /**
+     * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
+     * has started.
+     */
+    private void launchObserverNotifyActivityLaunched(WindowingModeTransitionInfo info) {
+        @ActivityMetricsLaunchObserver.Temperature int temperature =
+                convertTransitionTypeToLaunchObserverTemperature(getTransitionType(info));
+
+        if (mLaunchObserver != null) {
+            // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+            mLaunchObserver.onActivityLaunched(info.launchedActivity,
+                                               temperature);
+        }
+    }
+
+    /**
+     * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence is
+     * cancelled.
+     */
+    private void launchObserverNotifyActivityLaunchCancelled(WindowingModeTransitionInfo info) {
+        final ActivityRecord launchedActivity = info != null ? info.launchedActivity : null;
+
+        if (mLaunchObserver != null) {
+            mLaunchObserver.onActivityLaunchCancelled(launchedActivity);
+        }
+    }
+
+    /**
+     * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
+     * has fully finished (successfully).
+     */
+    private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) {
+        final ActivityRecord launchedActivity = info.launchedActivity;
+
+        if (mLaunchObserver != null) {
+            mLaunchObserver.onActivityLaunchFinished(launchedActivity);
+        }
+    }
+
+    private static @ActivityMetricsLaunchObserver.Temperature int
+            convertTransitionTypeToLaunchObserverTemperature(int transitionType) {
+        switch (transitionType) {
+            case TYPE_TRANSITION_WARM_LAUNCH:
+                return ActivityMetricsLaunchObserver.TEMPERATURE_WARM;
+            case TYPE_TRANSITION_HOT_LAUNCH:
+                return ActivityMetricsLaunchObserver.TEMPERATURE_HOT;
+            case TYPE_TRANSITION_COLD_LAUNCH:
+                return ActivityMetricsLaunchObserver.TEMPERATURE_COLD;
+            default:
+                return -1;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
new file mode 100644
index 0000000..8223693
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -0,0 +1,2956 @@
+/*
+ * Copyright (C) 2006 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.server.wm;
+
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
+import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
+import static android.app.WaitResult.INVALID_DELAY;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.activityTypeToString;
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.CATEGORY_HOME;
+import static android.content.Intent.CATEGORY_LAUNCHER;
+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_SCREEN_LAYOUT;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
+import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE;
+import static android.content.pm.ActivityInfo.FLAG_MULTIPROCESS;
+import static android.content.pm.ActivityInfo.FLAG_NO_HISTORY;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
+import static android.content.pm.ActivityInfo.FLAG_STATE_NOT_NEEDED;
+import static android.content.pm.ActivityInfo.FLAG_TURN_SCREEN_ON;
+import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
+import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS;
+import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
+import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
+import static android.content.res.Configuration.EMPTY;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
+import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET;
+import static android.os.Build.VERSION_CODES.HONEYCOMB;
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Process.SYSTEM_UID;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
+import static com.android.server.am.ActivityRecordProto.PROC_ID;
+import static com.android.server.am.ActivityRecordProto.STATE;
+import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
+import static com.android.server.am.ActivityRecordProto.VISIBLE;
+import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStack.LAUNCH_TICK;
+import static com.android.server.wm.ActivityStack.LAUNCH_TICK_MSG;
+import static com.android.server.wm.ActivityStack.PAUSE_TIMEOUT_MSG;
+import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
+import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
+import static com.android.server.wm.TaskPersister.DEBUG;
+import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.IdentifierProto.USER_ID;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager.TaskDescription;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.app.PictureInPictureParams;
+import android.app.ResultInfo;
+import android.app.servertransaction.ActivityConfigurationChangeItem;
+import android.app.servertransaction.ActivityLifecycleItem;
+import android.app.servertransaction.ActivityRelaunchItem;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.MoveToDisplayItem;
+import android.app.servertransaction.MultiWindowModeChangeItem;
+import android.app.servertransaction.NewIntentItem;
+import android.app.servertransaction.PauseActivityItem;
+import android.app.servertransaction.PipModeChangeItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.app.servertransaction.WindowVisibilityItem;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.MergedConfiguration;
+import android.util.Slog;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.view.IApplicationToken;
+import android.view.RemoteAnimationDefinition;
+import android.view.WindowManager.LayoutParams;
+
+import com.android.internal.R;
+import com.android.internal.app.ResolverActivity;
+import com.android.internal.content.ReferrerIntent;
+import com.android.internal.util.XmlUtils;
+import com.android.server.AttributeCache;
+import com.android.server.AttributeCache.Entry;
+import com.android.server.am.AppTimeTracker;
+import com.android.server.am.PendingIntentRecord;
+import com.android.server.wm.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot;
+import com.android.server.wm.ActivityStack.ActivityState;
+import com.android.server.uri.UriPermissionOwner;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * An entry in the history stack, representing an activity.
+ */
+final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
+    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
+    private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
+    private static final String TAG_STATES = TAG + POSTFIX_STATES;
+    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
+    private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
+    // TODO(b/67864419): Remove once recents component is overridden
+    private static final String LEGACY_RECENTS_PACKAGE_NAME = "com.android.systemui.recents";
+
+    private static final boolean SHOW_ACTIVITY_START_TIME = true;
+
+    private static final String ATTR_ID = "id";
+    private static final String TAG_INTENT = "intent";
+    private static final String ATTR_USERID = "user_id";
+    private static final String TAG_PERSISTABLEBUNDLE = "persistable_bundle";
+    private static final String ATTR_LAUNCHEDFROMUID = "launched_from_uid";
+    private static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package";
+    private static final String ATTR_RESOLVEDTYPE = "resolved_type";
+    private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
+    static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
+
+    final ActivityTaskManagerService service; // owner
+    final IApplicationToken.Stub appToken; // window manager token
+    AppWindowContainerController mWindowContainerController;
+    final ActivityInfo info; // all about me
+    // TODO: This is duplicated state already contained in info.applicationInfo - remove
+    ApplicationInfo appInfo; // information about activity's app
+    final int launchedFromPid; // always the pid who started the activity.
+    final int launchedFromUid; // always the uid who started the activity.
+    final String launchedFromPackage; // always the package who started the activity.
+    final int userId;          // Which user is this running for?
+    final Intent intent;    // the original intent that generated us
+    final ComponentName realActivity;  // the intent component, or target of an alias.
+    final String shortComponentName; // the short component name of the intent
+    final String resolvedType; // as per original caller;
+    final String packageName; // the package implementing intent's component
+    final String processName; // process where this component wants to run
+    final String taskAffinity; // as per ActivityInfo.taskAffinity
+    final boolean stateNotNeeded; // As per ActivityInfo.flags
+    boolean fullscreen; // The activity is opaque and fills the entire space of this task.
+    // TODO: See if it possible to combine this with the fullscreen field.
+    final boolean hasWallpaper; // Has a wallpaper window as a background.
+    final boolean noDisplay;  // activity is not displayed?
+    private final boolean componentSpecified;  // did caller specify an explicit component?
+    final boolean rootVoiceInteraction;  // was this the root activity of a voice interaction?
+
+    private CharSequence nonLocalizedLabel;  // the label information from the package mgr.
+    private int labelRes;           // the label information from the package mgr.
+    private int icon;               // resource identifier of activity's icon.
+    private int logo;               // resource identifier of activity's logo.
+    private int theme;              // resource identifier of activity's theme.
+    private int realTheme;          // actual theme resource we will use, never 0.
+    private int windowFlags;        // custom window flags for preview window.
+    private TaskRecord task;        // the task this is in.
+    private long createTime = System.currentTimeMillis();
+    long lastVisibleTime;   // last time this activity became visible
+    long cpuTimeAtResume;   // the cpu time of host process at the time of resuming activity
+    long pauseTime;         // last time we started pausing the activity
+    long launchTickTime;    // base time for launch tick messages
+    // Last configuration reported to the activity in the client process.
+    private MergedConfiguration mLastReportedConfiguration;
+    private int mLastReportedDisplayId;
+    private boolean mLastReportedMultiWindowMode;
+    private boolean mLastReportedPictureInPictureMode;
+    CompatibilityInfo compat;// last used compatibility mode
+    ActivityRecord resultTo; // who started this entry, so will get our reply
+    final String resultWho; // additional identifier for use by resultTo.
+    final int requestCode;  // code given by requester (resultTo)
+    ArrayList<ResultInfo> results; // pending ActivityResult objs we have received
+    HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
+    ArrayList<ReferrerIntent> newIntents; // any pending new intents for single-top mode
+    ActivityOptions pendingOptions; // most recently given options
+    ActivityOptions returningOptions; // options that are coming back via convertToTranslucent
+    AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
+    ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections.
+    UriPermissionOwner uriPermissions; // current special URI access perms.
+    WindowProcessController app;      // if non-null, hosting application
+    private ActivityState mState;    // current state we are in
+    Bundle  icicle;         // last saved activity state
+    PersistableBundle persistentState; // last persistently saved activity state
+    // TODO: See if this is still needed.
+    boolean frontOfTask;    // is this the root activity of its task?
+    boolean launchFailed;   // set if a launched failed, to abort on 2nd try
+    boolean haveState;      // have we gotten the last activity state?
+    boolean stopped;        // is activity pause finished?
+    boolean delayedResume;  // not yet resumed because of stopped app switches?
+    boolean finishing;      // activity in pending finish list?
+    boolean deferRelaunchUntilPaused;   // relaunch of activity is being deferred until pause is
+                                        // completed
+    boolean preserveWindowOnDeferredRelaunch; // activity windows are preserved on deferred relaunch
+    int configChangeFlags;  // which config values have changed
+    private boolean keysPaused;     // has key dispatching been paused for it?
+    int launchMode;         // the launch mode activity attribute.
+    int lockTaskLaunchMode; // the lockTaskMode manifest attribute, subject to override
+    boolean visible;        // does this activity's window need to be shown?
+    boolean visibleIgnoringKeyguard; // is this activity visible, ignoring the fact that Keyguard
+                                     // might hide this activity?
+    private boolean mDeferHidingClient; // If true we told WM to defer reporting to the client
+                                        // process that it is hidden.
+    boolean sleeping;       // have we told the activity to sleep?
+    boolean nowVisible;     // is this activity's window visible?
+    boolean mClientVisibilityDeferred;// was the visibility change message to client deferred?
+    boolean idle;           // has the activity gone idle?
+    boolean hasBeenLaunched;// has this activity ever been launched?
+    boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
+    boolean immersive;      // immersive mode (don't interrupt if possible)
+    boolean forceNewConfig; // force re-create with new config next time
+    boolean supportsEnterPipOnTaskSwitch;  // This flag is set by the system to indicate that the
+        // activity can enter picture in picture while pausing (only when switching to another task)
+    PictureInPictureParams pictureInPictureArgs = new PictureInPictureParams.Builder().build();
+        // The PiP params used when deferring the entering of picture-in-picture.
+    int launchCount;        // count of launches since last state
+    long lastLaunchTime;    // time of last launch of this activity
+    ComponentName requestedVrComponent; // the requested component for handling VR mode.
+
+    String stringName;      // for caching of toString().
+
+    private boolean inHistory;  // are we in the history stack?
+    final ActivityStackSupervisor mStackSupervisor;
+
+    static final int STARTING_WINDOW_NOT_SHOWN = 0;
+    static final int STARTING_WINDOW_SHOWN = 1;
+    static final int STARTING_WINDOW_REMOVED = 2;
+    int mStartingWindowState = STARTING_WINDOW_NOT_SHOWN;
+    boolean mTaskOverlay = false; // Task is always on-top of other activities in the task.
+
+    // Marking the reason why this activity is being relaunched. Mainly used to track that this
+    // activity is being relaunched to fulfill a resize request due to compatibility issues, e.g. in
+    // pre-NYC apps that don't have a sense of being resized.
+    int mRelaunchReason = RELAUNCH_REASON_NONE;
+
+    TaskDescription taskDescription; // the recents information for this activity
+    boolean mLaunchTaskBehind; // this activity is actively being launched with
+        // ActivityOptions.setLaunchTaskBehind, will be cleared once launch is completed.
+
+    // These configurations are collected from application's resources based on size-sensitive
+    // qualifiers. For example, layout-w800dp will be added to mHorizontalSizeConfigurations as 800
+    // and drawable-sw400dp will be added to both as 400.
+    private int[] mVerticalSizeConfigurations;
+    private int[] mHorizontalSizeConfigurations;
+    private int[] mSmallestSizeConfigurations;
+
+    boolean pendingVoiceInteractionStart;   // Waiting for activity-invoked voice session
+    IVoiceInteractionSession voiceSession;  // Voice interaction session for this activity
+
+    // A hint to override the window specified rotation animation, or -1
+    // to use the window specified value. We use this so that
+    // we can select the right animation in the cases of starting
+    // windows, where the app hasn't had time to set a value
+    // on the window.
+    int mRotationAnimationHint = -1;
+
+    private boolean mShowWhenLocked;
+    private boolean mTurnScreenOn;
+
+    /**
+     * Temp configs used in {@link #ensureActivityConfiguration(int, boolean)}
+     */
+    private final Configuration mTmpConfig = new Configuration();
+    private final Rect mTmpBounds = new Rect();
+
+    private static String startingWindowStateToString(int state) {
+        switch (state) {
+            case STARTING_WINDOW_NOT_SHOWN:
+                return "STARTING_WINDOW_NOT_SHOWN";
+            case STARTING_WINDOW_SHOWN:
+                return "STARTING_WINDOW_SHOWN";
+            case STARTING_WINDOW_REMOVED:
+                return "STARTING_WINDOW_REMOVED";
+            default:
+                return "unknown state=" + state;
+        }
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        final long now = SystemClock.uptimeMillis();
+        pw.print(prefix); pw.print("packageName="); pw.print(packageName);
+                pw.print(" processName="); pw.println(processName);
+        pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
+                pw.print(" launchedFromPackage="); pw.print(launchedFromPackage);
+                pw.print(" userId="); pw.println(userId);
+        pw.print(prefix); pw.print("app="); pw.println(app);
+        pw.print(prefix); pw.println(intent.toInsecureStringWithClip());
+        pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask);
+                pw.print(" task="); pw.println(task);
+        pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity);
+        pw.print(prefix); pw.print("realActivity=");
+                pw.println(realActivity.flattenToShortString());
+        if (appInfo != null) {
+            pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
+            if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+                pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
+            }
+            pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
+            if (appInfo.splitSourceDirs != null) {
+                pw.print(prefix); pw.print("splitDir=");
+                        pw.println(Arrays.toString(appInfo.splitSourceDirs));
+            }
+        }
+        pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
+                pw.print(" componentSpecified="); pw.print(componentSpecified);
+                pw.print(" mActivityType="); pw.println(
+                        activityTypeToString(getActivityType()));
+        if (rootVoiceInteraction) {
+            pw.print(prefix); pw.print("rootVoiceInteraction="); pw.println(rootVoiceInteraction);
+        }
+        pw.print(prefix); pw.print("compat="); pw.print(compat);
+                pw.print(" labelRes=0x"); pw.print(Integer.toHexString(labelRes));
+                pw.print(" icon=0x"); pw.print(Integer.toHexString(icon));
+                pw.print(" theme=0x"); pw.println(Integer.toHexString(theme));
+        pw.println(prefix + "mLastReportedConfigurations:");
+        mLastReportedConfiguration.dump(pw, prefix + " ");
+
+        pw.print(prefix); pw.print("CurrentConfiguration="); pw.println(getConfiguration());
+        if (!getOverrideConfiguration().equals(EMPTY)) {
+            pw.println(prefix + "OverrideConfiguration=" + getOverrideConfiguration());
+        }
+        if (!matchParentBounds()) {
+            pw.println(prefix + "bounds=" + getBounds());
+        }
+        if (resultTo != null || resultWho != null) {
+            pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
+                    pw.print(" resultWho="); pw.print(resultWho);
+                    pw.print(" resultCode="); pw.println(requestCode);
+        }
+        if (taskDescription != null) {
+            final String iconFilename = taskDescription.getIconFilename();
+            if (iconFilename != null || taskDescription.getLabel() != null ||
+                    taskDescription.getPrimaryColor() != 0) {
+                pw.print(prefix); pw.print("taskDescription:");
+                        pw.print(" label=\""); pw.print(taskDescription.getLabel());
+                                pw.print("\"");
+                        pw.print(" icon="); pw.print(taskDescription.getInMemoryIcon() != null
+                                ? taskDescription.getInMemoryIcon().getByteCount() + " bytes"
+                                : "null");
+                        pw.print(" iconResource="); pw.print(taskDescription.getIconResource());
+                        pw.print(" iconFilename="); pw.print(taskDescription.getIconFilename());
+                        pw.print(" primaryColor=");
+                        pw.println(Integer.toHexString(taskDescription.getPrimaryColor()));
+                        pw.print(prefix + " backgroundColor=");
+                        pw.println(Integer.toHexString(taskDescription.getBackgroundColor()));
+                        pw.print(prefix + " statusBarColor=");
+                        pw.println(Integer.toHexString(taskDescription.getStatusBarColor()));
+                        pw.print(prefix + " navigationBarColor=");
+                        pw.println(Integer.toHexString(taskDescription.getNavigationBarColor()));
+            }
+        }
+        if (results != null) {
+            pw.print(prefix); pw.print("results="); pw.println(results);
+        }
+        if (pendingResults != null && pendingResults.size() > 0) {
+            pw.print(prefix); pw.println("Pending Results:");
+            for (WeakReference<PendingIntentRecord> wpir : pendingResults) {
+                PendingIntentRecord pir = wpir != null ? wpir.get() : null;
+                pw.print(prefix); pw.print("  - ");
+                if (pir == null) {
+                    pw.println("null");
+                } else {
+                    pw.println(pir);
+                    pir.dump(pw, prefix + "    ");
+                }
+            }
+        }
+        if (newIntents != null && newIntents.size() > 0) {
+            pw.print(prefix); pw.println("Pending New Intents:");
+            for (int i=0; i<newIntents.size(); i++) {
+                Intent intent = newIntents.get(i);
+                pw.print(prefix); pw.print("  - ");
+                if (intent == null) {
+                    pw.println("null");
+                } else {
+                    pw.println(intent.toShortString(false, true, false, true));
+                }
+            }
+        }
+        if (pendingOptions != null) {
+            pw.print(prefix); pw.print("pendingOptions="); pw.println(pendingOptions);
+        }
+        if (appTimeTracker != null) {
+            appTimeTracker.dumpWithHeader(pw, prefix, false);
+        }
+        if (uriPermissions != null) {
+            uriPermissions.dump(pw, prefix);
+        }
+        pw.print(prefix); pw.print("launchFailed="); pw.print(launchFailed);
+                pw.print(" launchCount="); pw.print(launchCount);
+                pw.print(" lastLaunchTime=");
+                if (lastLaunchTime == 0) pw.print("0");
+                else TimeUtils.formatDuration(lastLaunchTime, now, pw);
+                pw.println();
+        pw.print(prefix); pw.print("haveState="); pw.print(haveState);
+                pw.print(" icicle="); pw.println(icicle);
+        pw.print(prefix); pw.print("state="); pw.print(mState);
+                pw.print(" stopped="); pw.print(stopped);
+                pw.print(" delayedResume="); pw.print(delayedResume);
+                pw.print(" finishing="); pw.println(finishing);
+        pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
+                pw.print(" inHistory="); pw.print(inHistory);
+                pw.print(" visible="); pw.print(visible);
+                pw.print(" sleeping="); pw.print(sleeping);
+                pw.print(" idle="); pw.print(idle);
+                pw.print(" mStartingWindowState=");
+                pw.println(startingWindowStateToString(mStartingWindowState));
+        pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen);
+                pw.print(" noDisplay="); pw.print(noDisplay);
+                pw.print(" immersive="); pw.print(immersive);
+                pw.print(" launchMode="); pw.println(launchMode);
+        pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
+                pw.print(" forceNewConfig="); pw.println(forceNewConfig);
+        pw.print(prefix); pw.print("mActivityType=");
+                pw.println(activityTypeToString(getActivityType()));
+        if (requestedVrComponent != null) {
+            pw.print(prefix);
+            pw.print("requestedVrComponent=");
+            pw.println(requestedVrComponent);
+        }
+        final boolean waitingVisible =
+                mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this);
+        if (lastVisibleTime != 0 || waitingVisible || nowVisible) {
+            pw.print(prefix); pw.print("waitingVisible="); pw.print(waitingVisible);
+                    pw.print(" nowVisible="); pw.print(nowVisible);
+                    pw.print(" lastVisibleTime=");
+                    if (lastVisibleTime == 0) pw.print("0");
+                    else TimeUtils.formatDuration(lastVisibleTime, now, pw);
+                    pw.println();
+        }
+        if (mDeferHidingClient) {
+            pw.println(prefix + "mDeferHidingClient=" + mDeferHidingClient);
+        }
+        if (deferRelaunchUntilPaused || configChangeFlags != 0) {
+            pw.print(prefix); pw.print("deferRelaunchUntilPaused="); pw.print(deferRelaunchUntilPaused);
+                    pw.print(" configChangeFlags=");
+                    pw.println(Integer.toHexString(configChangeFlags));
+        }
+        if (mServiceConnectionsHolder != null) {
+            pw.print(prefix); pw.print("connections="); pw.println(mServiceConnectionsHolder);
+        }
+        if (info != null) {
+            pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
+            pw.println(prefix + "mLastReportedMultiWindowMode=" + mLastReportedMultiWindowMode
+                    + " mLastReportedPictureInPictureMode=" + mLastReportedPictureInPictureMode);
+            if (info.supportsPictureInPicture()) {
+                pw.println(prefix + "supportsPictureInPicture=" + info.supportsPictureInPicture());
+                pw.println(prefix + "supportsEnterPipOnTaskSwitch: "
+                        + supportsEnterPipOnTaskSwitch);
+            }
+            if (info.maxAspectRatio != 0) {
+                pw.println(prefix + "maxAspectRatio=" + info.maxAspectRatio);
+            }
+        }
+    }
+
+    void updateApplicationInfo(ApplicationInfo aInfo) {
+        appInfo = aInfo;
+        info.applicationInfo = aInfo;
+    }
+
+    private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) {
+        return crossesSizeThreshold(mHorizontalSizeConfigurations, firstDp, secondDp);
+    }
+
+    private boolean crossesVerticalSizeThreshold(int firstDp, int secondDp) {
+        return crossesSizeThreshold(mVerticalSizeConfigurations, firstDp, secondDp);
+    }
+
+    private boolean crossesSmallestSizeThreshold(int firstDp, int secondDp) {
+        return crossesSizeThreshold(mSmallestSizeConfigurations, firstDp, secondDp);
+    }
+
+    /**
+     * The purpose of this method is to decide whether the activity needs to be relaunched upon
+     * changing its size. In most cases the activities don't need to be relaunched, if the resize
+     * is small, all the activity content has to do is relayout itself within new bounds. There are
+     * cases however, where the activity's content would be completely changed in the new size and
+     * the full relaunch is required.
+     *
+     * The activity will report to us vertical and horizontal thresholds after which a relaunch is
+     * required. These thresholds are collected from the application resource qualifiers. For
+     * example, if application has layout-w600dp resource directory, then it needs a relaunch when
+     * we resize from width of 650dp to 550dp, as it crosses the 600dp threshold. However, if
+     * it resizes width from 620dp to 700dp, it won't be relaunched as it stays on the same side
+     * of the threshold.
+     */
+    private static boolean crossesSizeThreshold(int[] thresholds, int firstDp,
+            int secondDp) {
+        if (thresholds == null) {
+            return false;
+        }
+        for (int i = thresholds.length - 1; i >= 0; i--) {
+            final int threshold = thresholds[i];
+            if ((firstDp < threshold && secondDp >= threshold)
+                    || (firstDp >= threshold && secondDp < threshold)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void setSizeConfigurations(int[] horizontalSizeConfiguration,
+            int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
+        mHorizontalSizeConfigurations = horizontalSizeConfiguration;
+        mVerticalSizeConfigurations = verticalSizeConfigurations;
+        mSmallestSizeConfigurations = smallestSizeConfigurations;
+    }
+
+    private void scheduleActivityMovedToDisplay(int displayId, Configuration config) {
+        if (!attachedToProcess()) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.w(TAG,
+                    "Can't report activity moved to display - client not running, activityRecord="
+                            + this + ", displayId=" + displayId);
+            return;
+        }
+        try {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+                    "Reporting activity moved to display" + ", activityRecord=" + this
+                            + ", displayId=" + displayId + ", config=" + config);
+
+            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                    MoveToDisplayItem.obtain(displayId, config));
+        } catch (RemoteException e) {
+            // If process died, whatever.
+        }
+    }
+
+    private void scheduleConfigurationChanged(Configuration config) {
+        if (!attachedToProcess()) {
+            if (DEBUG_CONFIGURATION) Slog.w(TAG,
+                    "Can't report activity configuration update - client not running"
+                            + ", activityRecord=" + this);
+            return;
+        }
+        try {
+            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
+                    + config);
+
+            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                    ActivityConfigurationChangeItem.obtain(config));
+        } catch (RemoteException e) {
+            // If process died, whatever.
+        }
+    }
+
+    void updateMultiWindowMode() {
+        if (task == null || task.getStack() == null || !attachedToProcess()) {
+            return;
+        }
+
+        if (task.getStack().deferScheduleMultiWindowModeChanged()) {
+            // Don't do anything if we are currently deferring multi-window mode change.
+            return;
+        }
+
+        // An activity is considered to be in multi-window mode if its task isn't fullscreen.
+        final boolean inMultiWindowMode = inMultiWindowMode();
+        if (inMultiWindowMode != mLastReportedMultiWindowMode) {
+            mLastReportedMultiWindowMode = inMultiWindowMode;
+            scheduleMultiWindowModeChanged(getConfiguration());
+        }
+    }
+
+    private void scheduleMultiWindowModeChanged(Configuration overrideConfig) {
+        try {
+            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                    MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode, overrideConfig));
+        } catch (Exception e) {
+            // If process died, I don't care.
+        }
+    }
+
+    void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) {
+        if (task == null || task.getStack() == null || !attachedToProcess()) {
+            return;
+        }
+
+        final boolean inPictureInPictureMode = inPinnedWindowingMode() && targetStackBounds != null;
+        if (inPictureInPictureMode != mLastReportedPictureInPictureMode || forceUpdate) {
+            // Picture-in-picture mode changes also trigger a multi-window mode change as well, so
+            // update that here in order. Set the last reported MW state to the same as the PiP
+            // state since we haven't yet actually resized the task (these callbacks need to
+            // preceed the configuration change from the resiez.
+            // TODO(110009072): Once we move these callbacks to the client, remove all logic related
+            // to forcing the update of the picture-in-picture mode as a part of the PiP animation.
+            mLastReportedPictureInPictureMode = inPictureInPictureMode;
+            mLastReportedMultiWindowMode = inPictureInPictureMode;
+            final Configuration newConfig = task.computeNewOverrideConfigurationForBounds(
+                    targetStackBounds, null);
+            schedulePictureInPictureModeChanged(newConfig);
+            scheduleMultiWindowModeChanged(newConfig);
+        }
+    }
+
+    private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
+        try {
+            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                    PipModeChangeItem.obtain(mLastReportedPictureInPictureMode,
+                            overrideConfig));
+        } catch (Exception e) {
+            // If process died, no one cares.
+        }
+    }
+
+    @Override
+    protected int getChildCount() {
+        // {@link ActivityRecord} is a leaf node and has no children.
+        return 0;
+    }
+
+    @Override
+    protected ConfigurationContainer getChildAt(int index) {
+        return null;
+    }
+
+    @Override
+    protected ConfigurationContainer getParent() {
+        return getTask();
+    }
+
+    TaskRecord getTask() {
+        return task;
+    }
+
+    /**
+     * Sets reference to the {@link TaskRecord} the {@link ActivityRecord} will treat as its parent.
+     * Note that this does not actually add the {@link ActivityRecord} as a {@link TaskRecord}
+     * children. However, this method will clean up references to this {@link ActivityRecord} in
+     * {@link ActivityStack}.
+     * @param task The new parent {@link TaskRecord}.
+     */
+    void setTask(TaskRecord task) {
+        setTask(task /* task */, false /* reparenting */);
+    }
+
+    /**
+     * This method should only be called by {@link TaskRecord#removeActivity(ActivityRecord)}.
+     * @param task          The new parent task.
+     * @param reparenting   Whether we're in the middle of reparenting.
+     */
+    void setTask(TaskRecord task, boolean reparenting) {
+        // Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}.
+        if (task != null && task == getTask()) {
+            return;
+        }
+
+        final ActivityStack oldStack = getStack();
+        final ActivityStack newStack = task != null ? task.getStack() : null;
+
+        // Inform old stack (if present) of activity removal and new stack (if set) of activity
+        // addition.
+        if (oldStack != newStack) {
+            if (!reparenting && oldStack != null) {
+                oldStack.onActivityRemovedFromStack(this);
+            }
+
+            if (newStack != null) {
+                newStack.onActivityAddedToStack(this);
+            }
+        }
+
+        this.task = task;
+
+        if (!reparenting) {
+            onParentChanged();
+        }
+    }
+
+    /**
+     * See {@link AppWindowContainerController#setWillCloseOrEnterPip(boolean)}
+     */
+    void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
+        getWindowContainerController().setWillCloseOrEnterPip(willCloseOrEnterPip);
+    }
+
+    static class Token extends IApplicationToken.Stub {
+        private final WeakReference<ActivityRecord> weakActivity;
+        private final String name;
+
+        Token(ActivityRecord activity, Intent intent) {
+            weakActivity = new WeakReference<>(activity);
+            name = intent.getComponent().flattenToShortString();
+        }
+
+        private static ActivityRecord tokenToActivityRecordLocked(Token token) {
+            if (token == null) {
+                return null;
+            }
+            ActivityRecord r = token.weakActivity.get();
+            if (r == null || r.getStack() == null) {
+                return null;
+            }
+            return r;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("Token{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(' ');
+            sb.append(weakActivity.get());
+            sb.append('}');
+            return sb.toString();
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+    }
+
+    static ActivityRecord forTokenLocked(IBinder token) {
+        try {
+            return Token.tokenToActivityRecordLocked((Token)token);
+        } catch (ClassCastException e) {
+            Slog.w(TAG, "Bad activity token: " + token, e);
+            return null;
+        }
+    }
+
+    boolean isResolverActivity() {
+        return ResolverActivity.class.getName().equals(realActivity.getClassName());
+    }
+
+    boolean isResolverOrChildActivity() {
+        if (!"android".equals(packageName)) {
+            return false;
+        }
+        try {
+            return ResolverActivity.class.isAssignableFrom(
+                    Object.class.getClassLoader().loadClass(realActivity.getClassName()));
+        } catch (ClassNotFoundException e) {
+            return false;
+        }
+    }
+
+    ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
+            int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage, Intent _intent,
+            String _resolvedType, ActivityInfo aInfo, Configuration _configuration,
+            ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified,
+            boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
+            ActivityOptions options, ActivityRecord sourceRecord) {
+        service = _service;
+        appToken = new Token(this, _intent);
+        info = aInfo;
+        launchedFromPid = _launchedFromPid;
+        launchedFromUid = _launchedFromUid;
+        launchedFromPackage = _launchedFromPackage;
+        userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
+        intent = _intent;
+        shortComponentName = _intent.getComponent().flattenToShortString();
+        resolvedType = _resolvedType;
+        componentSpecified = _componentSpecified;
+        rootVoiceInteraction = _rootVoiceInteraction;
+        mLastReportedConfiguration = new MergedConfiguration(_configuration);
+        resultTo = _resultTo;
+        resultWho = _resultWho;
+        requestCode = _reqCode;
+        setState(INITIALIZING, "ActivityRecord ctor");
+        frontOfTask = false;
+        launchFailed = false;
+        stopped = false;
+        delayedResume = false;
+        finishing = false;
+        deferRelaunchUntilPaused = false;
+        keysPaused = false;
+        inHistory = false;
+        visible = false;
+        nowVisible = false;
+        idle = false;
+        hasBeenLaunched = false;
+        mStackSupervisor = supervisor;
+
+        // This starts out true, since the initial state of an activity is that we have everything,
+        // and we shouldn't never consider it lacking in state to be removed if it dies.
+        haveState = true;
+
+        // If the class name in the intent doesn't match that of the target, this is
+        // probably an alias. We have to create a new ComponentName object to keep track
+        // of the real activity name, so that FLAG_ACTIVITY_CLEAR_TOP is handled properly.
+        if (aInfo.targetActivity == null
+                || (aInfo.targetActivity.equals(_intent.getComponent().getClassName())
+                && (aInfo.launchMode == LAUNCH_MULTIPLE
+                || aInfo.launchMode == LAUNCH_SINGLE_TOP))) {
+            realActivity = _intent.getComponent();
+        } else {
+            realActivity = new ComponentName(aInfo.packageName, aInfo.targetActivity);
+        }
+        taskAffinity = aInfo.taskAffinity;
+        stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
+        appInfo = aInfo.applicationInfo;
+        nonLocalizedLabel = aInfo.nonLocalizedLabel;
+        labelRes = aInfo.labelRes;
+        if (nonLocalizedLabel == null && labelRes == 0) {
+            ApplicationInfo app = aInfo.applicationInfo;
+            nonLocalizedLabel = app.nonLocalizedLabel;
+            labelRes = app.labelRes;
+        }
+        icon = aInfo.getIconResource();
+        logo = aInfo.getLogoResource();
+        theme = aInfo.getThemeResource();
+        realTheme = theme;
+        if (realTheme == 0) {
+            realTheme = aInfo.applicationInfo.targetSdkVersion < HONEYCOMB
+                    ? android.R.style.Theme : android.R.style.Theme_Holo;
+        }
+        if ((aInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
+            windowFlags |= LayoutParams.FLAG_HARDWARE_ACCELERATED;
+        }
+        if ((aInfo.flags & FLAG_MULTIPROCESS) != 0 && _caller != null
+                && (aInfo.applicationInfo.uid == SYSTEM_UID
+                    || aInfo.applicationInfo.uid == _caller.mInfo.uid)) {
+            processName = _caller.mName;
+        } else {
+            processName = aInfo.processName;
+        }
+
+        if ((aInfo.flags & FLAG_EXCLUDE_FROM_RECENTS) != 0) {
+            intent.addFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        }
+
+        packageName = aInfo.applicationInfo.packageName;
+        launchMode = aInfo.launchMode;
+
+        Entry ent = AttributeCache.instance().get(packageName,
+                realTheme, com.android.internal.R.styleable.Window, userId);
+
+        if (ent != null) {
+            fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array);
+            hasWallpaper = ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
+            noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
+        } else {
+            hasWallpaper = false;
+            noDisplay = false;
+        }
+
+        setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);
+
+        immersive = (aInfo.flags & FLAG_IMMERSIVE) != 0;
+
+        requestedVrComponent = (aInfo.requestedVrComponent == null) ?
+                null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
+
+        mShowWhenLocked = (aInfo.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
+        mTurnScreenOn = (aInfo.flags & FLAG_TURN_SCREEN_ON) != 0;
+
+        mRotationAnimationHint = aInfo.rotationAnimation;
+        lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
+        if (appInfo.isPrivilegedApp() && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
+                || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
+            lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+        }
+
+        if (options != null) {
+            pendingOptions = options;
+            mLaunchTaskBehind = options.getLaunchTaskBehind();
+
+            final int rotationAnimation = pendingOptions.getRotationAnimationHint();
+            // Only override manifest supplied option if set.
+            if (rotationAnimation >= 0) {
+                mRotationAnimationHint = rotationAnimation;
+            }
+            final PendingIntent usageReport = pendingOptions.getUsageTimeReport();
+            if (usageReport != null) {
+                appTimeTracker = new AppTimeTracker(usageReport);
+            }
+            final boolean useLockTask = pendingOptions.getLockTaskMode();
+            if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
+                lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+            }
+        }
+    }
+
+    void setProcess(WindowProcessController proc) {
+        app = proc;
+        final ActivityRecord root = task != null ? task.getRootActivity() : null;
+        if (root == this) {
+            task.setRootProcess(proc);
+        }
+    }
+
+    boolean hasProcess() {
+        return app != null;
+    }
+
+    boolean attachedToProcess() {
+        return hasProcess() && app.hasThread();
+    }
+
+    AppWindowContainerController getWindowContainerController() {
+        return mWindowContainerController;
+    }
+
+    void createWindowContainer() {
+        if (mWindowContainerController != null) {
+            throw new IllegalArgumentException("Window container=" + mWindowContainerController
+                    + " already created for r=" + this);
+        }
+
+        inHistory = true;
+
+        final TaskWindowContainerController taskController = task.getWindowContainerController();
+
+        // TODO(b/36505427): Maybe this call should be moved inside updateOverrideConfiguration()
+        task.updateOverrideConfigurationFromLaunchBounds();
+        // Make sure override configuration is up-to-date before using to create window controller.
+        updateOverrideConfiguration();
+
+        mWindowContainerController = new AppWindowContainerController(taskController, appToken,
+                this, Integer.MAX_VALUE /* add on top */, info.screenOrientation, fullscreen,
+                (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
+                task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
+                appInfo.targetSdkVersion, mRotationAnimationHint,
+                ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
+
+        task.addActivityToTop(this);
+
+        // When an activity is started directly into a split-screen fullscreen stack, we need to
+        // update the initial multi-window modes so that the callbacks are scheduled correctly when
+        // the user leaves that mode.
+        mLastReportedMultiWindowMode = inMultiWindowMode();
+        mLastReportedPictureInPictureMode = inPinnedWindowingMode();
+    }
+
+    void removeWindowContainer() {
+        // Do not try to remove a window container if we have already removed it.
+        if (mWindowContainerController == null) {
+            return;
+        }
+
+        // Resume key dispatching if it is currently paused before we remove the container.
+        resumeKeyDispatchingLocked();
+
+        mWindowContainerController.removeContainer(getDisplayId());
+        mWindowContainerController = null;
+    }
+
+    /**
+     * Reparents this activity into {@param newTask} at the provided {@param position}.  The caller
+     * should ensure that the {@param newTask} is not already the parent of this activity.
+     */
+    void reparent(TaskRecord newTask, int position, String reason) {
+        final TaskRecord prevTask = task;
+        if (prevTask == newTask) {
+            throw new IllegalArgumentException(reason + ": task=" + newTask
+                    + " is already the parent of r=" + this);
+        }
+
+        // TODO: Ensure that we do not directly reparent activities across stacks, as that may leave
+        //       the stacks in strange states. For now, we should use Task.reparent() to ensure that
+        //       the stack is left in an OK state.
+        if (prevTask != null && newTask != null && prevTask.getStack() != newTask.getStack()) {
+            throw new IllegalArgumentException(reason + ": task=" + newTask
+                    + " is in a different stack (" + newTask.getStackId() + ") than the parent of"
+                    + " r=" + this + " (" + prevTask.getStackId() + ")");
+        }
+
+        // Must reparent first in window manager
+        mWindowContainerController.reparent(newTask.getWindowContainerController(), position);
+
+        // Reparenting prevents informing the parent stack of activity removal in the case that
+        // the new stack has the same parent. we must manually signal here if this is not the case.
+        final ActivityStack prevStack = prevTask.getStack();
+
+        if (prevStack != newTask.getStack()) {
+            prevStack.onActivityRemovedFromStack(this);
+        }
+        // Remove the activity from the old task and add it to the new task.
+        prevTask.removeActivity(this, true /* reparenting */);
+
+        newTask.addActivityAtIndex(position, this);
+    }
+
+    private boolean isHomeIntent(Intent intent) {
+        return ACTION_MAIN.equals(intent.getAction())
+                && intent.hasCategory(CATEGORY_HOME)
+                && intent.getCategories().size() == 1
+                && intent.getData() == null
+                && intent.getType() == null;
+    }
+
+    static boolean isMainIntent(Intent intent) {
+        return ACTION_MAIN.equals(intent.getAction())
+                && intent.hasCategory(CATEGORY_LAUNCHER)
+                && intent.getCategories().size() == 1
+                && intent.getData() == null
+                && intent.getType() == null;
+    }
+
+    private boolean canLaunchHomeActivity(int uid, ActivityRecord sourceRecord) {
+        if (uid == Process.myUid() || uid == 0) {
+            // System process can launch home activity.
+            return true;
+        }
+        // Allow the recents component to launch the home activity.
+        final RecentTasks recentTasks = mStackSupervisor.mService.getRecentTasks();
+        if (recentTasks != null && recentTasks.isCallerRecents(uid)) {
+            return true;
+        }
+        // Resolver activity can launch home activity.
+        return sourceRecord != null && sourceRecord.isResolverActivity();
+    }
+
+    /**
+     * @return whether the given package name can launch an assist activity.
+     */
+    private boolean canLaunchAssistActivity(String packageName) {
+        final ComponentName assistComponent =
+                service.mActiveVoiceInteractionServiceComponent;
+        if (assistComponent != null) {
+            return assistComponent.getPackageName().equals(packageName);
+        }
+        return false;
+    }
+
+    private void setActivityType(boolean componentSpecified, int launchedFromUid, Intent intent,
+            ActivityOptions options, ActivityRecord sourceRecord) {
+        int activityType = ACTIVITY_TYPE_UNDEFINED;
+        if ((!componentSpecified || canLaunchHomeActivity(launchedFromUid, sourceRecord))
+                && isHomeIntent(intent) && !isResolverActivity()) {
+            // This sure looks like a home activity!
+            activityType = ACTIVITY_TYPE_HOME;
+
+            if (info.resizeMode == RESIZE_MODE_FORCE_RESIZEABLE
+                    || info.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+                // We only allow home activities to be resizeable if they explicitly requested it.
+                info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+            }
+        } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME)
+                || service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
+            activityType = ACTIVITY_TYPE_RECENTS;
+        } else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
+                && canLaunchAssistActivity(launchedFromPackage)) {
+            activityType = ACTIVITY_TYPE_ASSISTANT;
+        }
+        setActivityType(activityType);
+    }
+
+    void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) {
+        if (launchMode != LAUNCH_SINGLE_INSTANCE && launchMode != LAUNCH_SINGLE_TASK) {
+            task.setTaskToAffiliateWith(taskToAffiliateWith);
+        }
+    }
+
+    /**
+     * @return Stack value from current task, null if there is no task.
+     */
+    <T extends ActivityStack> T getStack() {
+        return task != null ? (T) task.getStack() : null;
+    }
+
+    int getStackId() {
+        return getStack() != null ? getStack().mStackId : INVALID_STACK_ID;
+    }
+
+    ActivityDisplay getDisplay() {
+        final ActivityStack stack = getStack();
+        return stack != null ? stack.getDisplay() : null;
+    }
+
+    boolean changeWindowTranslucency(boolean toOpaque) {
+        if (fullscreen == toOpaque) {
+            return false;
+        }
+
+        // Keep track of the number of fullscreen activities in this task.
+        task.numFullscreen += toOpaque ? +1 : -1;
+
+        fullscreen = toOpaque;
+        return true;
+    }
+
+    void takeFromHistory() {
+        if (inHistory) {
+            inHistory = false;
+            if (task != null && !finishing) {
+                task = null;
+            }
+            clearOptionsLocked();
+        }
+    }
+
+    boolean isInHistory() {
+        return inHistory;
+    }
+
+    boolean isInStackLocked() {
+        final ActivityStack stack = getStack();
+        return stack != null && stack.isInStackLocked(this) != null;
+    }
+
+    boolean isPersistable() {
+        return (info.persistableMode == PERSIST_ROOT_ONLY ||
+                info.persistableMode == PERSIST_ACROSS_REBOOTS) &&
+                (intent == null || (intent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0);
+    }
+
+    boolean isFocusable() {
+        return mStackSupervisor.isFocusable(this, isAlwaysFocusable());
+    }
+
+    boolean isResizeable() {
+        return ActivityInfo.isResizeableMode(info.resizeMode) || info.supportsPictureInPicture();
+    }
+
+    /**
+     * @return whether this activity is non-resizeable or forced to be resizeable
+     */
+    boolean isNonResizableOrForcedResizable() {
+        return info.resizeMode != RESIZE_MODE_RESIZEABLE
+                && info.resizeMode != RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+    }
+
+    /**
+     * @return whether this activity supports PiP multi-window and can be put in the pinned stack.
+     */
+    boolean supportsPictureInPicture() {
+        return service.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined()
+                && info.supportsPictureInPicture();
+    }
+
+    /**
+     * @return whether this activity supports split-screen multi-window and can be put in the docked
+     *         stack.
+     */
+    @Override
+    public boolean supportsSplitScreenWindowingMode() {
+        // An activity can not be docked even if it is considered resizeable because it only
+        // supports picture-in-picture mode but has a non-resizeable resizeMode
+        return super.supportsSplitScreenWindowingMode()
+                && service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
+    }
+
+    /**
+     * @return whether this activity supports freeform multi-window and can be put in the freeform
+     *         stack.
+     */
+    boolean supportsFreeform() {
+        return service.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
+    }
+
+    /**
+     * @return whether this activity supports non-PiP multi-window.
+     */
+    private boolean supportsResizeableMultiWindow() {
+        return service.mSupportsMultiWindow && !isActivityTypeHome()
+                && (ActivityInfo.isResizeableMode(info.resizeMode)
+                        || service.mForceResizableActivities);
+    }
+
+    /**
+     * Check whether this activity can be launched on the specified display.
+     *
+     * @param displayId Target display id.
+     * @return {@code true} if either it is the default display or this activity can be put on a
+     *         secondary screen.
+     */
+    boolean canBeLaunchedOnDisplay(int displayId) {
+        return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
+                launchedFromUid, info);
+    }
+
+    /**
+     * @param beforeStopping Whether this check is for an auto-enter-pip operation, that is to say
+     *         the activity has requested to enter PiP when it would otherwise be stopped.
+     *
+     * @return whether this activity is currently allowed to enter PIP.
+     */
+    boolean checkEnterPictureInPictureState(String caller, boolean beforeStopping) {
+        if (!supportsPictureInPicture()) {
+            return false;
+        }
+
+        // Check app-ops and see if PiP is supported for this package
+        if (!checkEnterPictureInPictureAppOpsState()) {
+            return false;
+        }
+
+        // Check to see if we are in VR mode, and disallow PiP if so
+        if (service.shouldDisableNonVrUiLocked()) {
+            return false;
+        }
+
+        boolean isKeyguardLocked = service.isKeyguardLocked();
+        boolean isCurrentAppLocked =
+                service.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+        final ActivityDisplay display = getDisplay();
+        boolean hasPinnedStack = display != null && display.hasPinnedStack();
+        // Don't return early if !isNotLocked, since we want to throw an exception if the activity
+        // is in an incorrect state
+        boolean isNotLockedOrOnKeyguard = !isKeyguardLocked && !isCurrentAppLocked;
+
+        // We don't allow auto-PiP when something else is already pipped.
+        if (beforeStopping && hasPinnedStack) {
+            return false;
+        }
+
+        switch (mState) {
+            case RESUMED:
+                // When visible, allow entering PiP if the app is not locked.  If it is over the
+                // keyguard, then we will prompt to unlock in the caller before entering PiP.
+                return !isCurrentAppLocked &&
+                        (supportsEnterPipOnTaskSwitch || !beforeStopping);
+            case PAUSING:
+            case PAUSED:
+                // When pausing, then only allow enter PiP as in the resume state, and in addition,
+                // require that there is not an existing PiP activity and that the current system
+                // state supports entering PiP
+                return isNotLockedOrOnKeyguard && !hasPinnedStack
+                        && supportsEnterPipOnTaskSwitch;
+            case STOPPING:
+                // When stopping in a valid state, then only allow enter PiP as in the pause state.
+                // Otherwise, fall through to throw an exception if the caller is trying to enter
+                // PiP in an invalid stopping state.
+                if (supportsEnterPipOnTaskSwitch) {
+                    return isNotLockedOrOnKeyguard && !hasPinnedStack;
+                }
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * @return Whether AppOps allows this package to enter picture-in-picture.
+     */
+    private boolean checkEnterPictureInPictureAppOpsState() {
+        return service.getAppOpsService().checkOperation(
+                OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) == MODE_ALLOWED;
+    }
+
+    boolean isAlwaysFocusable() {
+        return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0;
+    }
+
+    /** Move activity with its stack to front and make the stack focused. */
+    boolean moveFocusableActivityToTop(String reason) {
+        if (!isFocusable()) {
+            if (DEBUG_FOCUS) {
+                Slog.d(TAG_FOCUS, "moveActivityStackToFront: unfocusable activity=" + this);
+            }
+            return false;
+        }
+
+        final TaskRecord task = getTask();
+        final ActivityStack stack = getStack();
+        if (stack == null) {
+            Slog.w(TAG, "moveActivityStackToFront: invalid task or stack: activity="
+                    + this + " task=" + task);
+            return false;
+        }
+
+        if (mStackSupervisor.getTopResumedActivity() == this) {
+            if (DEBUG_FOCUS) {
+                Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this);
+            }
+            return false;
+        }
+
+        if (DEBUG_FOCUS) {
+            Slog.d(TAG_FOCUS, "moveActivityStackToFront: activity=" + this);
+        }
+
+        stack.moveToFront(reason, task);
+        // Report top activity change to tracking services and WM
+        if (mStackSupervisor.getTopResumedActivity() == this) {
+            // TODO(b/111361570): Support multiple focused apps in WM
+            service.setResumedActivityUncheckLocked(this, reason);
+        }
+        return true;
+    }
+
+    /**
+     * @return true if the activity contains windows that have
+     *         {@link LayoutParams#FLAG_DISMISS_KEYGUARD} set
+     */
+    boolean hasDismissKeyguardWindows() {
+        return service.mWindowManager.containsDismissKeyguardWindow(appToken);
+    }
+
+    void makeFinishingLocked() {
+        if (finishing) {
+            return;
+        }
+        finishing = true;
+        if (stopped) {
+            clearOptionsLocked();
+        }
+
+        if (service != null) {
+            service.getTaskChangeNotificationController().notifyTaskStackChanged();
+        }
+    }
+
+    UriPermissionOwner getUriPermissionsLocked() {
+        if (uriPermissions == null) {
+            uriPermissions = new UriPermissionOwner(service.mUgmInternal, this);
+        }
+        return uriPermissions;
+    }
+
+    void addResultLocked(ActivityRecord from, String resultWho,
+            int requestCode, int resultCode,
+            Intent resultData) {
+        ActivityResult r = new ActivityResult(from, resultWho,
+                requestCode, resultCode, resultData);
+        if (results == null) {
+            results = new ArrayList<ResultInfo>();
+        }
+        results.add(r);
+    }
+
+    void removeResultsLocked(ActivityRecord from, String resultWho,
+            int requestCode) {
+        if (results != null) {
+            for (int i=results.size()-1; i>=0; i--) {
+                ActivityResult r = (ActivityResult)results.get(i);
+                if (r.mFrom != from) continue;
+                if (r.mResultWho == null) {
+                    if (resultWho != null) continue;
+                } else {
+                    if (!r.mResultWho.equals(resultWho)) continue;
+                }
+                if (r.mRequestCode != requestCode) continue;
+
+                results.remove(i);
+            }
+        }
+    }
+
+    private void addNewIntentLocked(ReferrerIntent intent) {
+        if (newIntents == null) {
+            newIntents = new ArrayList<>();
+        }
+        newIntents.add(intent);
+    }
+
+    final boolean isSleeping() {
+        final ActivityStack stack = getStack();
+        return stack != null ? stack.shouldSleepActivities() : service.isSleepingLocked();
+    }
+
+    /**
+     * Deliver a new Intent to an existing activity, so that its onNewIntent()
+     * method will be called at the proper time.
+     */
+    final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
+        // The activity now gets access to the data associated with this Intent.
+        service.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
+                intent, getUriPermissionsLocked(), userId);
+        final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
+        boolean unsent = true;
+        final boolean isTopActivityWhileSleeping = isTopRunningActivity() && isSleeping();
+
+        // We want to immediately deliver the intent to the activity if:
+        // - It is currently resumed or paused. i.e. it is currently visible to the user and we want
+        //   the user to see the visual effects caused by the intent delivery now.
+        // - The device is sleeping and it is the top activity behind the lock screen (b/6700897).
+        if ((mState == RESUMED || mState == PAUSED || isTopActivityWhileSleeping)
+                && attachedToProcess()) {
+            try {
+                ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
+                ar.add(rintent);
+                service.getLifecycleManager().scheduleTransaction(
+                        app.getThread(), appToken, NewIntentItem.obtain(ar, mState == PAUSED));
+                unsent = false;
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
+            } catch (NullPointerException e) {
+                Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
+            }
+        }
+        if (unsent) {
+            addNewIntentLocked(rintent);
+        }
+    }
+
+    void updateOptionsLocked(ActivityOptions options) {
+        if (options != null) {
+            if (pendingOptions != null) {
+                pendingOptions.abort();
+            }
+            pendingOptions = options;
+        }
+    }
+
+    void applyOptionsLocked() {
+        if (pendingOptions != null
+                && pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) {
+            mWindowContainerController.applyOptionsLocked(pendingOptions, intent);
+            if (task == null) {
+                clearOptionsLocked(false /* withAbort */);
+            } else {
+                // This will clear the options for all the ActivityRecords for this Task.
+                task.clearAllPendingOptions();
+            }
+        }
+    }
+
+    ActivityOptions getOptionsForTargetActivityLocked() {
+        return pendingOptions != null ? pendingOptions.forTargetActivity() : null;
+    }
+
+    void clearOptionsLocked() {
+        clearOptionsLocked(true /* withAbort */);
+    }
+
+    void clearOptionsLocked(boolean withAbort) {
+        if (withAbort && pendingOptions != null) {
+            pendingOptions.abort();
+        }
+        pendingOptions = null;
+    }
+
+    ActivityOptions takeOptionsLocked() {
+        ActivityOptions opts = pendingOptions;
+        pendingOptions = null;
+        return opts;
+    }
+
+    void removeUriPermissionsLocked() {
+        if (uriPermissions != null) {
+            uriPermissions.removeUriPermissions();
+            uriPermissions = null;
+        }
+    }
+
+    void pauseKeyDispatchingLocked() {
+        if (!keysPaused) {
+            keysPaused = true;
+
+            if (mWindowContainerController != null) {
+                mWindowContainerController.pauseKeyDispatching();
+            }
+        }
+    }
+
+    void resumeKeyDispatchingLocked() {
+        if (keysPaused) {
+            keysPaused = false;
+
+            if (mWindowContainerController != null) {
+                mWindowContainerController.resumeKeyDispatching();
+            }
+        }
+    }
+
+    private void updateTaskDescription(CharSequence description) {
+        task.lastDescription = description;
+    }
+
+    void setDeferHidingClient(boolean deferHidingClient) {
+        if (mDeferHidingClient == deferHidingClient) {
+            return;
+        }
+        mDeferHidingClient = deferHidingClient;
+        if (!mDeferHidingClient && !visible) {
+            // Hiding the client is no longer deferred and the app isn't visible still, go ahead and
+            // update the visibility.
+            setVisibility(false);
+        }
+    }
+
+    void setVisibility(boolean visible) {
+        mWindowContainerController.setVisibility(visible, mDeferHidingClient);
+        mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
+    }
+
+    // TODO: Look into merging with #setVisibility()
+    void setVisible(boolean newVisible) {
+        visible = newVisible;
+        mDeferHidingClient = !visible && mDeferHidingClient;
+        setVisibility(visible);
+        mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
+    }
+
+    void setState(ActivityState state, String reason) {
+        if (DEBUG_STATES) Slog.v(TAG_STATES, "State movement: " + this + " from:" + getState()
+                        + " to:" + state + " reason:" + reason);
+
+        if (state == mState) {
+            // No need to do anything if state doesn't change.
+            if (DEBUG_STATES) Slog.v(TAG_STATES, "State unchanged from:" + state);
+            return;
+        }
+
+        mState = state;
+
+        final TaskRecord parent = getTask();
+
+        if (parent != null) {
+            parent.onActivityStateChanged(this, state, reason);
+        }
+
+        // The WindowManager interprets the app stopping signal as
+        // an indication that the Surface will eventually be destroyed.
+        // This however isn't necessarily true if we are going to sleep.
+        if (state == STOPPING && !isSleeping()) {
+            mWindowContainerController.notifyAppStopping();
+        }
+    }
+
+    ActivityState getState() {
+        return mState;
+    }
+
+    /**
+     * Returns {@code true} if the Activity is in the specified state.
+     */
+    boolean isState(ActivityState state) {
+        return state == mState;
+    }
+
+    /**
+     * Returns {@code true} if the Activity is in one of the specified states.
+     */
+    boolean isState(ActivityState state1, ActivityState state2) {
+        return state1 == mState || state2 == mState;
+    }
+
+    /**
+     * Returns {@code true} if the Activity is in one of the specified states.
+     */
+    boolean isState(ActivityState state1, ActivityState state2, ActivityState state3) {
+        return state1 == mState || state2 == mState || state3 == mState;
+    }
+
+    /**
+     * Returns {@code true} if the Activity is in one of the specified states.
+     */
+    boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
+            ActivityState state4) {
+        return state1 == mState || state2 == mState || state3 == mState || state4 == mState;
+    }
+
+    void notifyAppResumed(boolean wasStopped) {
+        mWindowContainerController.notifyAppResumed(wasStopped);
+    }
+
+    void notifyUnknownVisibilityLaunched() {
+
+        // No display activities never add a window, so there is no point in waiting them for
+        // relayout.
+        if (!noDisplay) {
+            mWindowContainerController.notifyUnknownVisibilityLaunched();
+        }
+    }
+
+    /**
+     * @return true if the input activity should be made visible, ignoring any effect Keyguard
+     * might have on the visibility
+     *
+     * @see {@link ActivityStack#checkKeyguardVisibility}
+     */
+    boolean shouldBeVisibleIgnoringKeyguard(boolean behindFullscreenActivity) {
+        if (!okToShowLocked()) {
+            return false;
+        }
+
+        return !behindFullscreenActivity || mLaunchTaskBehind;
+    }
+
+    void makeVisibleIfNeeded(ActivityRecord starting, boolean reportToClient) {
+        // This activity is not currently visible, but is running. Tell it to become visible.
+        if (mState == RESUMED || this == starting) {
+            if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY,
+                    "Not making visible, r=" + this + " state=" + mState + " starting=" + starting);
+            return;
+        }
+
+        // If this activity is paused, tell it to now show its window.
+        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                "Making visible and scheduling visibility: " + this);
+        final ActivityStack stack = getStack();
+        try {
+            if (stack.mTranslucentActivityWaiting != null) {
+                updateOptionsLocked(returningOptions);
+                stack.mUndrawnActivitiesBelowTopTranslucent.add(this);
+            }
+            setVisible(true);
+            sleeping = false;
+            app.postPendingUiCleanMsg(true);
+            if (reportToClient) {
+                makeClientVisible();
+            } else {
+                mClientVisibilityDeferred = true;
+            }
+            // The activity may be waiting for stop, but that is no longer appropriate for it.
+            mStackSupervisor.mStoppingActivities.remove(this);
+            mStackSupervisor.mGoingToSleepActivities.remove(this);
+        } catch (Exception e) {
+            // Just skip on any failure; we'll make it visible when it next restarts.
+            Slog.w(TAG, "Exception thrown making visible: " + intent.getComponent(), e);
+        }
+        handleAlreadyVisible();
+    }
+
+    /** Send visibility change message to the client and pause if needed. */
+    void makeClientVisible() {
+        mClientVisibilityDeferred = false;
+        try {
+            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                    WindowVisibilityItem.obtain(true /* showWindow */));
+            if (shouldPauseWhenBecomingVisible()) {
+                // An activity must be in the {@link PAUSING} state for the system to validate
+                // the move to {@link PAUSED}.
+                setState(PAUSING, "makeVisibleIfNeeded");
+                service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                        PauseActivityItem.obtain(finishing, false /* userLeaving */,
+                                configChangeFlags, false /* dontReport */));
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e);
+        }
+    }
+
+    /** Check if activity should be moved to PAUSED state when it becomes visible. */
+    private boolean shouldPauseWhenBecomingVisible() {
+        // If the activity is stopped or stopping, cycle to the paused state. We avoid doing
+        // this when there is an activity waiting to become translucent as the extra binder
+        // calls will lead to noticeable jank. A later call to
+        // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to the proper
+        // paused state. We also avoid doing this for the activity the stack supervisor
+        // considers the resumed activity, as normal means will bring the activity from STOPPED
+        // to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
+        if (!isState(STOPPED, STOPPING) || getStack().mTranslucentActivityWaiting != null
+                || isResumedActivityOnDisplay()) {
+            return false;
+        }
+
+        // Check if position in task allows to become paused
+        final int positionInTask = task.mActivities.indexOf(this);
+        if (positionInTask == -1) {
+            throw new IllegalStateException("Activity not found in its task");
+        }
+        if (positionInTask == task.mActivities.size() - 1) {
+            // It's the topmost activity in the task - should become paused now
+            return true;
+        }
+        // Check if activity above is finishing now and this one becomes the topmost in task.
+        final ActivityRecord activityAbove = task.mActivities.get(positionInTask + 1);
+        if (activityAbove.finishing && results == null) {
+            // We will only allow pausing if activity above wasn't launched for result. Otherwise it
+            // will cause this activity to resume before getting result.
+            return true;
+        }
+        return false;
+    }
+
+    boolean handleAlreadyVisible() {
+        stopFreezingScreenLocked(false);
+        try {
+            if (returningOptions != null) {
+                app.getThread().scheduleOnNewActivityOptions(appToken, returningOptions.toBundle());
+            }
+        } catch(RemoteException e) {
+        }
+        return mState == RESUMED;
+    }
+
+    static void activityResumedLocked(IBinder token) {
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        if (DEBUG_SAVED_STATE) Slog.i(TAG_STATES, "Resumed activity; dropping state of: " + r);
+        if (r != null) {
+            r.icicle = null;
+            r.haveState = false;
+        }
+    }
+
+    /**
+     * Once we know that we have asked an application to put an activity in the resumed state
+     * (either by launching it or explicitly telling it), this function updates the rest of our
+     * state to match that fact.
+     */
+    void completeResumeLocked() {
+        final boolean wasVisible = visible;
+        setVisible(true);
+        if (!wasVisible) {
+            // Visibility has changed, so take a note of it so we call the TaskStackChangedListener
+            mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
+        }
+        idle = false;
+        results = null;
+        newIntents = null;
+        stopped = false;
+
+        if (isActivityTypeHome()) {
+            WindowProcessController app = task.mActivities.get(0).app;
+            if (hasProcess() && app != service.mHomeProcess) {
+                service.mHomeProcess = app;
+            }
+        }
+
+        if (nowVisible) {
+            // We won't get a call to reportActivityVisibleLocked() so dismiss lockscreen now.
+            mStackSupervisor.reportActivityVisibleLocked(this);
+        }
+
+        // Schedule an idle timeout in case the app doesn't do it for us.
+        mStackSupervisor.scheduleIdleTimeoutLocked(this);
+
+        mStackSupervisor.reportResumedActivityLocked(this);
+
+        resumeKeyDispatchingLocked();
+        final ActivityStack stack = getStack();
+        mStackSupervisor.mNoAnimActivities.clear();
+
+        // Mark the point when the activity is resuming
+        // TODO: To be more accurate, the mark should be before the onCreate,
+        //       not after the onResume. But for subsequent starts, onResume is fine.
+        if (hasProcess()) {
+            cpuTimeAtResume = app.getCpuTime();
+        } else {
+            cpuTimeAtResume = 0; // Couldn't get the cpu time of process
+        }
+
+        returningOptions = null;
+
+        if (canTurnScreenOn()) {
+            mStackSupervisor.wakeUp("turnScreenOnFlag");
+        } else {
+            // If the screen is going to turn on because the caller explicitly requested it and
+            // the keyguard is not showing don't attempt to sleep. Otherwise the Activity will
+            // pause and then resume again later, which will result in a double life-cycle event.
+            stack.checkReadyForSleep();
+        }
+    }
+
+    final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
+            CharSequence description) {
+        final ActivityStack stack = getStack();
+        if (mState != STOPPING) {
+            Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this);
+            stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
+            return;
+        }
+        if (newPersistentState != null) {
+            persistentState = newPersistentState;
+            service.notifyTaskPersisterLocked(task, false);
+        }
+        if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + icicle);
+
+        if (newIcicle != null) {
+            // If icicle is null, this is happening due to a timeout, so we haven't really saved
+            // the state.
+            icicle = newIcicle;
+            haveState = true;
+            launchCount = 0;
+            updateTaskDescription(description);
+        }
+        if (!stopped) {
+            if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPED: " + this + " (stop complete)");
+            stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
+            stopped = true;
+            setState(STOPPED, "activityStoppedLocked");
+
+            mWindowContainerController.notifyAppStopped();
+
+            if (finishing) {
+                clearOptionsLocked();
+            } else {
+                if (deferRelaunchUntilPaused) {
+                    stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config");
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                } else {
+                    mStackSupervisor.updatePreviousProcessLocked(this);
+                }
+            }
+        }
+    }
+
+    void startLaunchTickingLocked() {
+        if (Build.IS_USER) {
+            return;
+        }
+        if (launchTickTime == 0) {
+            launchTickTime = SystemClock.uptimeMillis();
+            continueLaunchTickingLocked();
+        }
+    }
+
+    boolean continueLaunchTickingLocked() {
+        if (launchTickTime == 0) {
+            return false;
+        }
+
+        final ActivityStack stack = getStack();
+        if (stack == null) {
+            return false;
+        }
+
+        Message msg = stack.mHandler.obtainMessage(LAUNCH_TICK_MSG, this);
+        stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
+        stack.mHandler.sendMessageDelayed(msg, LAUNCH_TICK);
+        return true;
+    }
+
+    void finishLaunchTickingLocked() {
+        launchTickTime = 0;
+        final ActivityStack stack = getStack();
+        if (stack != null) {
+            stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
+        }
+    }
+
+    // IApplicationToken
+
+    public boolean mayFreezeScreenLocked(WindowProcessController app) {
+        // Only freeze the screen if this activity is currently attached to
+        // an application, and that application is not blocked or unresponding.
+        // In any other case, we can't count on getting the screen unfrozen,
+        // so it is best to leave as-is.
+        return hasProcess() && !app.isCrashing() && !app.isNotResponding();
+    }
+
+    public void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
+        if (mayFreezeScreenLocked(app)) {
+            mWindowContainerController.startFreezingScreen(configChanges);
+        }
+    }
+
+    public void stopFreezingScreenLocked(boolean force) {
+        if (force || frozenBeforeDestroy) {
+            frozenBeforeDestroy = false;
+            mWindowContainerController.stopFreezingScreen(force);
+        }
+    }
+
+    public void reportFullyDrawnLocked(boolean restoredFromBundle) {
+        final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
+                .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
+        if (info != null) {
+            mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
+                    info.windowsFullyDrawnDelayMs);
+        }
+    }
+    @Override
+    public void onStartingWindowDrawn(long timestamp) {
+        synchronized (service.mGlobalLock) {
+            mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
+                    getWindowingMode(), timestamp);
+        }
+    }
+
+    @Override
+    public void onWindowsDrawn(long timestamp) {
+        synchronized (service.mGlobalLock) {
+            final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
+                    .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp);
+            final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
+            mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
+                    windowsDrawnDelayMs);
+            mStackSupervisor.sendWaitingVisibleReportLocked(this);
+            finishLaunchTickingLocked();
+            if (task != null) {
+                task.hasBeenVisible = true;
+            }
+        }
+    }
+
+    @Override
+    public void onWindowsVisible() {
+        synchronized (service.mGlobalLock) {
+            mStackSupervisor.reportActivityVisibleLocked(this);
+            if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this);
+            if (!nowVisible) {
+                nowVisible = true;
+                lastVisibleTime = SystemClock.uptimeMillis();
+                if (idle || mStackSupervisor.isStoppingNoHistoryActivity()) {
+                    // If this activity was already idle or there is an activity that must be
+                    // stopped immediately after visible, then we now need to make sure we perform
+                    // the full stop of any activities that are waiting to do so. This is because
+                    // we won't do that while they are still waiting for this one to become visible.
+                    final int size = mStackSupervisor.mActivitiesWaitingForVisibleActivity.size();
+                    if (size > 0) {
+                        for (int i = 0; i < size; i++) {
+                            final ActivityRecord r =
+                                    mStackSupervisor.mActivitiesWaitingForVisibleActivity.get(i);
+                            if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "Was waiting for visible: " + r);
+                        }
+                        mStackSupervisor.mActivitiesWaitingForVisibleActivity.clear();
+                        mStackSupervisor.scheduleIdleLocked();
+                    }
+                } else {
+                    // Instead of doing the full stop routine here, let's just hide any activities
+                    // we now can, and let them stop when the normal idle happens.
+                    mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */,
+                            false /* remove */, true /* processPausingActivities */);
+                }
+                service.scheduleAppGcsLocked();
+            }
+        }
+    }
+
+    @Override
+    public void onWindowsGone() {
+        synchronized (service.mGlobalLock) {
+            if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
+            nowVisible = false;
+        }
+    }
+
+    @Override
+    public boolean keyDispatchingTimedOut(String reason, int windowPid) {
+        ActivityRecord anrActivity;
+        WindowProcessController anrApp;
+        boolean windowFromSameProcessAsActivity;
+        synchronized (service.mGlobalLock) {
+            anrActivity = getWaitingHistoryRecordLocked();
+            anrApp = app;
+            windowFromSameProcessAsActivity =
+                    !hasProcess() || app.getPid() == windowPid || windowPid == -1;
+        }
+
+        if (windowFromSameProcessAsActivity) {
+            return service.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
+                    anrActivity.shortComponentName, anrActivity.appInfo, shortComponentName,
+                    app, false, reason);
+        } else {
+            // In this case another process added windows using this activity token. So, we call the
+            // generic service input dispatch timed out method so that the right process is blamed.
+            return service.mAmInternal.inputDispatchingTimedOut(
+                    windowPid, false /* aboveSystem */, reason) < 0;
+        }
+    }
+
+    private ActivityRecord getWaitingHistoryRecordLocked() {
+        // First find the real culprit...  if this activity is waiting for
+        // another activity to start or has stopped, then the key dispatching
+        // timeout should not be caused by this.
+        if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this) || stopped) {
+            final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
+            // Try to use the one which is closest to top.
+            ActivityRecord r = stack.getResumedActivity();
+            if (r == null) {
+                r = stack.mPausingActivity;
+            }
+            if (r != null) {
+                return r;
+            }
+        }
+        return this;
+    }
+
+    /** Checks whether the activity should be shown for current user. */
+    public boolean okToShowLocked() {
+        // We cannot show activities when the device is locked and the application is not
+        // encryption aware.
+        if (!StorageManager.isUserKeyUnlocked(userId)
+                && !info.applicationInfo.isEncryptionAware()) {
+            return false;
+        }
+
+        return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0
+                || (mStackSupervisor.isCurrentProfileLocked(userId)
+                && service.mAmInternal.isUserRunning(userId, 0 /* flags */));
+    }
+
+    /**
+     * This method will return true if the activity is either visible, is becoming visible, is
+     * currently pausing, or is resumed.
+     */
+    public boolean isInterestingToUserLocked() {
+        return visible || nowVisible || mState == PAUSING || mState == RESUMED;
+    }
+
+    void setSleeping(boolean _sleeping) {
+        setSleeping(_sleeping, false);
+    }
+
+    void setSleeping(boolean _sleeping, boolean force) {
+        if (!force && sleeping == _sleeping) {
+            return;
+        }
+        if (attachedToProcess()) {
+            try {
+                app.getThread().scheduleSleeping(appToken, _sleeping);
+                if (_sleeping && !mStackSupervisor.mGoingToSleepActivities.contains(this)) {
+                    mStackSupervisor.mGoingToSleepActivities.add(this);
+                }
+                sleeping = _sleeping;
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Exception thrown when sleeping: " + intent.getComponent(), e);
+            }
+        }
+    }
+
+    static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        if (r == null) {
+            return INVALID_TASK_ID;
+        }
+        final TaskRecord task = r.task;
+        final int activityNdx = task.mActivities.indexOf(r);
+        if (activityNdx < 0 || (onlyRoot && activityNdx > task.findEffectiveRootIndex())) {
+            return INVALID_TASK_ID;
+        }
+        return task.taskId;
+    }
+
+    static ActivityRecord isInStackLocked(IBinder token) {
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        return (r != null) ? r.getStack().isInStackLocked(r) : null;
+    }
+
+    static ActivityStack getStackLocked(IBinder token) {
+        final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+        if (r != null) {
+            return r.getStack();
+        }
+        return null;
+    }
+
+    /**
+     * @return display id to which this record is attached,
+     *         {@link android.view.Display#INVALID_DISPLAY} if not attached.
+     */
+    int getDisplayId() {
+        final ActivityStack stack = getStack();
+        if (stack == null) {
+            return INVALID_DISPLAY;
+        }
+        return stack.mDisplayId;
+    }
+
+    final boolean isDestroyable() {
+        if (finishing || !hasProcess()) {
+            // This would be redundant.
+            return false;
+        }
+        final ActivityStack stack = getStack();
+        if (stack == null || this == stack.getResumedActivity() || this == stack.mPausingActivity
+                || !haveState || !stopped) {
+            // We're not ready for this kind of thing.
+            return false;
+        }
+        if (visible) {
+            // The user would notice this!
+            return false;
+        }
+        return true;
+    }
+
+    private static String createImageFilename(long createTime, int taskId) {
+        return String.valueOf(taskId) + ACTIVITY_ICON_SUFFIX + createTime +
+                IMAGE_EXTENSION;
+    }
+
+    void setTaskDescription(TaskDescription _taskDescription) {
+        Bitmap icon;
+        if (_taskDescription.getIconFilename() == null &&
+                (icon = _taskDescription.getIcon()) != null) {
+            final String iconFilename = createImageFilename(createTime, task.taskId);
+            final File iconFile = new File(TaskPersister.getUserImagesDir(task.userId),
+                    iconFilename);
+            final String iconFilePath = iconFile.getAbsolutePath();
+            service.getRecentTasks().saveImage(icon, iconFilePath);
+            _taskDescription.setIconFilename(iconFilePath);
+        }
+        taskDescription = _taskDescription;
+    }
+
+    void setVoiceSessionLocked(IVoiceInteractionSession session) {
+        voiceSession = session;
+        pendingVoiceInteractionStart = false;
+    }
+
+    void clearVoiceSessionLocked() {
+        voiceSession = null;
+        pendingVoiceInteractionStart = false;
+    }
+
+    void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
+        showStartingWindow(prev, newTask, taskSwitch, false /* fromRecents */);
+    }
+
+    void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
+            boolean fromRecents) {
+        if (mWindowContainerController == null) {
+            return;
+        }
+        if (mTaskOverlay) {
+            // We don't show starting window for overlay activities.
+            return;
+        }
+        if (pendingOptions != null
+                && pendingOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
+            // Don't show starting window when using shared element transition.
+            return;
+        }
+
+        final CompatibilityInfo compatInfo =
+                service.compatibilityInfoForPackageLocked(info.applicationInfo);
+        final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
+                compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+                prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
+                allowTaskSnapshot(),
+                mState.ordinal() >= RESUMED.ordinal() && mState.ordinal() <= STOPPED.ordinal(),
+                fromRecents);
+        if (shown) {
+            mStartingWindowState = STARTING_WINDOW_SHOWN;
+        }
+    }
+
+    void removeOrphanedStartingWindow(boolean behindFullscreenActivity) {
+        if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) {
+            if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
+            mStartingWindowState = STARTING_WINDOW_REMOVED;
+            mWindowContainerController.removeStartingWindow();
+        }
+    }
+
+    int getRequestedOrientation() {
+        return mWindowContainerController.getOrientation();
+    }
+
+    void setRequestedOrientation(int requestedOrientation) {
+        final int displayId = getDisplayId();
+        final Configuration displayConfig =
+                mStackSupervisor.getDisplayOverrideConfiguration(displayId);
+
+        final Configuration config = mWindowContainerController.setOrientation(requestedOrientation,
+                displayId, displayConfig, mayFreezeScreenLocked(app));
+        if (config != null) {
+            frozenBeforeDestroy = true;
+            if (!service.updateDisplayOverrideConfigurationLocked(config, this,
+                    false /* deferResume */, displayId)) {
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            }
+        }
+        service.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
+                task.taskId, requestedOrientation);
+    }
+
+    void setDisablePreviewScreenshots(boolean disable) {
+        mWindowContainerController.setDisablePreviewScreenshots(disable);
+    }
+
+    /**
+     * Set the last reported global configuration to the client. Should be called whenever a new
+     * global configuration is sent to the client for this activity.
+     */
+    void setLastReportedGlobalConfiguration(@NonNull Configuration config) {
+        mLastReportedConfiguration.setGlobalConfiguration(config);
+    }
+
+    /**
+     * Set the last reported configuration to the client. Should be called whenever
+     * a new merged configuration is sent to the client for this activity.
+     */
+    void setLastReportedConfiguration(@NonNull MergedConfiguration config) {
+        setLastReportedConfiguration(config.getGlobalConfiguration(),
+            config.getOverrideConfiguration());
+    }
+
+    private void setLastReportedConfiguration(Configuration global, Configuration override) {
+        mLastReportedConfiguration.setConfiguration(global, override);
+    }
+
+    // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
+    private void updateOverrideConfiguration() {
+        mTmpConfig.unset();
+        computeBounds(mTmpBounds);
+
+        if (mTmpBounds.equals(getOverrideBounds())) {
+            return;
+        }
+
+        setBounds(mTmpBounds);
+
+        final Rect updatedBounds = getOverrideBounds();
+
+        // Bounds changed...update configuration to match.
+        if (!matchParentBounds()) {
+            task.computeOverrideConfiguration(mTmpConfig, updatedBounds, null /* insetBounds */,
+                    false /* overrideWidth */, false /* overrideHeight */);
+        }
+
+        onOverrideConfigurationChanged(mTmpConfig);
+    }
+
+    /** Returns true if the configuration is compatible with this activity. */
+    boolean isConfigurationCompatible(Configuration config) {
+        final int orientation = mWindowContainerController != null
+                ? mWindowContainerController.getOrientation() : info.screenOrientation;
+        if (isFixedOrientationPortrait(orientation)
+                && config.orientation != ORIENTATION_PORTRAIT) {
+            return false;
+        }
+        if (isFixedOrientationLandscape(orientation)
+                && config.orientation != ORIENTATION_LANDSCAPE) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Computes the bounds to fit the Activity within the bounds of the {@link Configuration}.
+     */
+    // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
+    private void computeBounds(Rect outBounds) {
+        outBounds.setEmpty();
+        final float maxAspectRatio = info.maxAspectRatio;
+        final ActivityStack stack = getStack();
+        if (task == null || stack == null || task.inMultiWindowMode() || maxAspectRatio == 0
+                || isInVrUiMode(getConfiguration())) {
+            // We don't set override configuration if that activity task isn't fullscreen. I.e. the
+            // activity is in multi-window mode. Or, there isn't a max aspect ratio specified for
+            // the activity. This is indicated by an empty {@link outBounds}. We also don't set it
+            // if we are in VR mode.
+            return;
+        }
+
+        // We must base this on the parent configuration, because we set our override
+        // configuration's appBounds based on the result of this method. If we used our own
+        // configuration, it would be influenced by past invocations.
+        final Rect appBounds = getParent().getWindowConfiguration().getAppBounds();
+        final int containingAppWidth = appBounds.width();
+        final int containingAppHeight = appBounds.height();
+        int maxActivityWidth = containingAppWidth;
+        int maxActivityHeight = containingAppHeight;
+
+        if (containingAppWidth < containingAppHeight) {
+            // Width is the shorter side, so we use that to figure-out what the max. height
+            // should be given the aspect ratio.
+            maxActivityHeight = (int) ((maxActivityWidth * maxAspectRatio) + 0.5f);
+        } else {
+            // Height is the shorter side, so we use that to figure-out what the max. width
+            // should be given the aspect ratio.
+            maxActivityWidth = (int) ((maxActivityHeight * maxAspectRatio) + 0.5f);
+        }
+
+        if (containingAppWidth <= maxActivityWidth && containingAppHeight <= maxActivityHeight) {
+            // The display matches or is less than the activity aspect ratio, so nothing else to do.
+            // Return the existing bounds. If this method is running for the first time,
+            // {@link #getOverrideBounds()} will be empty (representing no override). If the method has run
+            // before, then effect of {@link #getOverrideBounds()} will already have been applied to the
+            // value returned from {@link getConfiguration}. Refer to
+            // {@link TaskRecord#computeOverrideConfiguration}.
+            outBounds.set(getOverrideBounds());
+            return;
+        }
+
+        // Compute configuration based on max supported width and height.
+        // Also account for the left / top insets (e.g. from display cutouts), which will be clipped
+        // away later in StackWindowController.adjustConfigurationForBounds(). Otherwise, the app
+        // bounds would end up too small.
+        outBounds.set(0, 0, maxActivityWidth + appBounds.left, maxActivityHeight + appBounds.top);
+
+        if (service.mWindowManager.getNavBarPosition() == NAV_BAR_LEFT) {
+            // Position the activity frame on the opposite side of the nav bar.
+            outBounds.left = appBounds.right - maxActivityWidth;
+            outBounds.right = appBounds.right;
+        }
+    }
+
+    /**
+     * @return {@code true} if this activity was reparented to another display but
+     *         {@link #ensureActivityConfiguration} is not called.
+     */
+    boolean shouldUpdateConfigForDisplayChanged() {
+        return mLastReportedDisplayId != getDisplayId();
+    }
+
+    boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
+        return ensureActivityConfiguration(globalChanges, preserveWindow,
+                false /* ignoreStopState */);
+    }
+
+    /**
+     * Make sure the given activity matches the current configuration. Ensures the HistoryRecord
+     * is updated with the correct configuration and all other bookkeeping is handled.
+     *
+     * @param globalChanges The changes to the global configuration.
+     * @param preserveWindow If the activity window should be preserved on screen if the activity
+     *                       is relaunched.
+     * @param ignoreStopState If we should try to relaunch the activity even if it is in the stopped
+     *                        state. This is useful for the case where we know the activity will be
+     *                        visible soon and we want to ensure its configuration before we make it
+     *                        visible.
+     * @return True if the activity was relaunched and false if it wasn't relaunched because we
+     *         can't or the app handles the specific configuration that is changing.
+     */
+    boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
+            boolean ignoreStopState) {
+        final ActivityStack stack = getStack();
+        if (stack.mConfigWillChange) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                    "Skipping config check (will change): " + this);
+            return true;
+        }
+
+        // We don't worry about activities that are finishing.
+        if (finishing) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                    "Configuration doesn't matter in finishing " + this);
+            stopFreezingScreenLocked(false);
+            return true;
+        }
+
+        if (!ignoreStopState && (mState == STOPPING || mState == STOPPED)) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                    "Skipping config check stopped or stopping: " + this);
+            return true;
+        }
+
+        // TODO: We should add ActivityRecord.shouldBeVisible() that checks if the activity should
+        // be visible based on the stack, task, and lockscreen state and use that here instead. The
+        // method should be based on the logic in ActivityStack.ensureActivitiesVisibleLocked().
+        // Skip updating configuration for activity is a stack that shouldn't be visible.
+        if (!stack.shouldBeVisible(null /* starting */)) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                    "Skipping config check invisible stack: " + this);
+            return true;
+        }
+
+        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                "Ensuring correct configuration: " + this);
+
+        final int newDisplayId = getDisplayId();
+        final boolean displayChanged = mLastReportedDisplayId != newDisplayId;
+        if (displayChanged) {
+            mLastReportedDisplayId = newDisplayId;
+        }
+        // TODO(b/36505427): Is there a better place to do this?
+        updateOverrideConfiguration();
+
+        // Short circuit: if the two full configurations are equal (the common case), then there is
+        // nothing to do.  We test the full configuration instead of the global and merged override
+        // configurations because there are cases (like moving a task to the pinned stack) where
+        // the combine configurations are equal, but would otherwise differ in the override config
+        mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
+        if (getConfiguration().equals(mTmpConfig) && !forceNewConfig && !displayChanged) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                    "Configuration & display unchanged in " + this);
+            return true;
+        }
+
+        // Okay we now are going to make this activity have the new config.
+        // But then we need to figure out how it needs to deal with that.
+
+        // Find changes between last reported merged configuration and the current one. This is used
+        // to decide whether to relaunch an activity or just report a configuration change.
+        final int changes = getConfigurationChanges(mTmpConfig);
+
+        // Update last reported values.
+        final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
+
+        setLastReportedConfiguration(service.getGlobalConfiguration(), newMergedOverrideConfig);
+
+        if (mState == INITIALIZING) {
+            // No need to relaunch or schedule new config for activity that hasn't been launched
+            // yet. We do, however, return after applying the config to activity record, so that
+            // it will use it for launch transaction.
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                    "Skipping config check for initializing activity: " + this);
+            return true;
+        }
+
+        if (changes == 0 && !forceNewConfig) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                    "Configuration no differences in " + this);
+            // There are no significant differences, so we won't relaunch but should still deliver
+            // the new configuration to the client process.
+            if (displayChanged) {
+                scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
+            } else {
+                scheduleConfigurationChanged(newMergedOverrideConfig);
+            }
+            return true;
+        }
+
+        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                "Configuration changes for " + this + ", allChanges="
+                        + Configuration.configurationDiffToString(changes));
+
+        // If the activity isn't currently running, just leave the new configuration and it will
+        // pick that up next time it starts.
+        if (!attachedToProcess()) {
+            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                    "Configuration doesn't matter not running " + this);
+            stopFreezingScreenLocked(false);
+            forceNewConfig = false;
+            return true;
+        }
+
+        // Figure out how to handle the changes between the configurations.
+        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                "Checking to restart " + info.name + ": changed=0x"
+                        + Integer.toHexString(changes) + ", handles=0x"
+                        + Integer.toHexString(info.getRealConfigChanged())
+                        + ", mLastReportedConfiguration=" + mLastReportedConfiguration);
+
+        if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
+            // Aha, the activity isn't handling the change, so DIE DIE DIE.
+            configChangeFlags |= changes;
+            startFreezingScreenLocked(app, globalChanges);
+            forceNewConfig = false;
+            preserveWindow &= isResizeOnlyChange(changes);
+            final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
+            if (hasResizeChange) {
+                final boolean isDragResizing =
+                        getTask().getWindowContainerController().isDragResizing();
+                mRelaunchReason = isDragResizing ? RELAUNCH_REASON_FREE_RESIZE
+                        : RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+            } else {
+                mRelaunchReason = RELAUNCH_REASON_NONE;
+            }
+            if (!attachedToProcess()) {
+                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                        "Config is destroying non-running " + this);
+                stack.destroyActivityLocked(this, true, "config");
+            } else if (mState == PAUSING) {
+                // A little annoying: we are waiting for this activity to finish pausing. Let's not
+                // do anything now, but just flag that it needs to be restarted when done pausing.
+                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                        "Config is skipping already pausing " + this);
+                deferRelaunchUntilPaused = true;
+                preserveWindowOnDeferredRelaunch = preserveWindow;
+                return true;
+            } else if (mState == RESUMED) {
+                // Try to optimize this case: the configuration is changing and we need to restart
+                // the top, resumed activity. Instead of doing the normal handshaking, just say
+                // "restart!".
+                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                        "Config is relaunching resumed " + this);
+
+                if (DEBUG_STATES && !visible) {
+                    Slog.v(TAG_STATES, "Config is relaunching resumed invisible activity " + this
+                            + " called by " + Debug.getCallers(4));
+                }
+
+                relaunchActivityLocked(true /* andResume */, preserveWindow);
+            } else {
+                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                        "Config is relaunching non-resumed " + this);
+                relaunchActivityLocked(false /* andResume */, preserveWindow);
+            }
+
+            // All done...  tell the caller we weren't able to keep this activity around.
+            return false;
+        }
+
+        // Default case: the activity can handle this new configuration, so hand it over.
+        // NOTE: We only forward the override configuration as the system level configuration
+        // changes is always sent to all processes when they happen so it can just use whatever
+        // system level configuration it last got.
+        if (displayChanged) {
+            scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
+        } else {
+            scheduleConfigurationChanged(newMergedOverrideConfig);
+        }
+        stopFreezingScreenLocked(false);
+
+        return true;
+    }
+
+    /**
+     * When assessing a configuration change, decide if the changes flags and the new configurations
+     * should cause the Activity to relaunch.
+     *
+     * @param changes the changes due to the given configuration.
+     * @param changesConfig the configuration that was used to calculate the given changes via a
+     *        call to getConfigurationChanges.
+     */
+    private boolean shouldRelaunchLocked(int changes, Configuration changesConfig) {
+        int configChanged = info.getRealConfigChanged();
+        boolean onlyVrUiModeChanged = onlyVrUiModeChanged(changes, changesConfig);
+
+        // Override for apps targeting pre-O sdks
+        // If a device is in VR mode, and we're transitioning into VR ui mode, add ignore ui mode
+        // to the config change.
+        // For O and later, apps will be required to add configChanges="uimode" to their manifest.
+        if (appInfo.targetSdkVersion < O
+                && requestedVrComponent != null
+                && onlyVrUiModeChanged) {
+            configChanged |= CONFIG_UI_MODE;
+        }
+
+        return (changes&(~configChanged)) != 0;
+    }
+
+    /**
+     * Returns true if the configuration change is solely due to the UI mode switching into or out
+     * of UI_MODE_TYPE_VR_HEADSET.
+     */
+    private boolean onlyVrUiModeChanged(int changes, Configuration lastReportedConfig) {
+        final Configuration currentConfig = getConfiguration();
+        return changes == CONFIG_UI_MODE && (isInVrUiMode(currentConfig)
+            != isInVrUiMode(lastReportedConfig));
+    }
+
+    private int getConfigurationChanges(Configuration lastReportedConfig) {
+        // Determine what has changed.  May be nothing, if this is a config that has come back from
+        // the app after going idle.  In that case we just want to leave the official config object
+        // now in the activity and do nothing else.
+        final Configuration currentConfig = getConfiguration();
+        int changes = lastReportedConfig.diff(currentConfig);
+        // We don't want to use size changes if they don't cross boundaries that are important to
+        // the app.
+        if ((changes & CONFIG_SCREEN_SIZE) != 0) {
+            final boolean crosses = crossesHorizontalSizeThreshold(lastReportedConfig.screenWidthDp,
+                    currentConfig.screenWidthDp)
+                    || crossesVerticalSizeThreshold(lastReportedConfig.screenHeightDp,
+                    currentConfig.screenHeightDp);
+            if (!crosses) {
+                changes &= ~CONFIG_SCREEN_SIZE;
+            }
+        }
+        if ((changes & CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
+            final int oldSmallest = lastReportedConfig.smallestScreenWidthDp;
+            final int newSmallest = currentConfig.smallestScreenWidthDp;
+            if (!crossesSmallestSizeThreshold(oldSmallest, newSmallest)) {
+                changes &= ~CONFIG_SMALLEST_SCREEN_SIZE;
+            }
+        }
+        // We don't want window configuration to cause relaunches.
+        if ((changes & CONFIG_WINDOW_CONFIGURATION) != 0) {
+            changes &= ~CONFIG_WINDOW_CONFIGURATION;
+        }
+
+        return changes;
+    }
+
+    private static boolean isResizeOnlyChange(int change) {
+        return (change & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_ORIENTATION
+                | CONFIG_SCREEN_LAYOUT)) == 0;
+    }
+
+    private static boolean hasResizeChange(int change) {
+        return (change & (CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_ORIENTATION
+                | CONFIG_SCREEN_LAYOUT)) != 0;
+    }
+
+    void relaunchActivityLocked(boolean andResume, boolean preserveWindow) {
+        if (service.mSuppressResizeConfigChanges && preserveWindow) {
+            configChangeFlags = 0;
+            return;
+        }
+
+        List<ResultInfo> pendingResults = null;
+        List<ReferrerIntent> pendingNewIntents = null;
+        if (andResume) {
+            pendingResults = results;
+            pendingNewIntents = newIntents;
+        }
+        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+                "Relaunching: " + this + " with results=" + pendingResults
+                        + " newIntents=" + pendingNewIntents + " andResume=" + andResume
+                        + " preserveWindow=" + preserveWindow);
+        EventLog.writeEvent(andResume ? AM_RELAUNCH_RESUME_ACTIVITY
+                        : AM_RELAUNCH_ACTIVITY, userId, System.identityHashCode(this),
+                task.taskId, shortComponentName);
+
+        startFreezingScreenLocked(app, 0);
+
+        try {
+            if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH,
+                    "Moving to " + (andResume ? "RESUMED" : "PAUSED") + " Relaunching " + this
+                            + " callers=" + Debug.getCallers(6));
+            forceNewConfig = false;
+            mStackSupervisor.activityRelaunchingLocked(this);
+            final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
+                    pendingNewIntents, configChangeFlags,
+                    new MergedConfiguration(service.getGlobalConfiguration(),
+                            getMergedOverrideConfiguration()),
+                    preserveWindow);
+            final ActivityLifecycleItem lifecycleItem;
+            if (andResume) {
+                lifecycleItem = ResumeActivityItem.obtain(
+                        getDisplay().getWindowContainerController().isNextTransitionForward());
+            } else {
+                lifecycleItem = PauseActivityItem.obtain();
+            }
+            final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), appToken);
+            transaction.addCallback(callbackItem);
+            transaction.setLifecycleStateRequest(lifecycleItem);
+            service.getLifecycleManager().scheduleTransaction(transaction);
+            // Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
+            // request resume if this activity is currently resumed, which implies we aren't
+            // sleeping.
+        } catch (RemoteException e) {
+            if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, "Relaunch failed", e);
+        }
+
+        if (andResume) {
+            if (DEBUG_STATES) {
+                Slog.d(TAG_STATES, "Resumed after relaunch " + this);
+            }
+            results = null;
+            newIntents = null;
+            service.getAppWarningsLocked().onResumeActivity(this);
+        } else {
+            final ActivityStack stack = getStack();
+            if (stack != null) {
+                stack.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
+            }
+            setState(PAUSED, "relaunchActivityLocked");
+        }
+
+        configChangeFlags = 0;
+        deferRelaunchUntilPaused = false;
+        preserveWindowOnDeferredRelaunch = false;
+    }
+
+    private boolean isProcessRunning() {
+        WindowProcessController proc = app;
+        if (proc == null) {
+            proc = service.mProcessNames.get(processName, info.applicationInfo.uid);
+        }
+        return proc != null && proc.hasThread();
+    }
+
+    /**
+     * @return Whether a task snapshot starting window may be shown.
+     */
+    private boolean allowTaskSnapshot() {
+        if (newIntents == null) {
+            return true;
+        }
+
+        // Restrict task snapshot starting window to launcher start, or there is no intent at all
+        // (eg. task being brought to front). If the intent is something else, likely the app is
+        // going to show some specific page or view, instead of what's left last time.
+        for (int i = newIntents.size() - 1; i >= 0; i--) {
+            final Intent intent = newIntents.get(i);
+            if (intent != null && !ActivityRecord.isMainIntent(intent)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns {@code true} if the associated activity has the no history flag set on it.
+     * {@code false} otherwise.
+     */
+    boolean isNoHistory() {
+        return (intent.getFlags() & FLAG_ACTIVITY_NO_HISTORY) != 0
+                || (info.flags & FLAG_NO_HISTORY) != 0;
+    }
+
+    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+        out.attribute(null, ATTR_ID, String.valueOf(createTime));
+        out.attribute(null, ATTR_LAUNCHEDFROMUID, String.valueOf(launchedFromUid));
+        if (launchedFromPackage != null) {
+            out.attribute(null, ATTR_LAUNCHEDFROMPACKAGE, launchedFromPackage);
+        }
+        if (resolvedType != null) {
+            out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType);
+        }
+        out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified));
+        out.attribute(null, ATTR_USERID, String.valueOf(userId));
+
+        if (taskDescription != null) {
+            taskDescription.saveToXml(out);
+        }
+
+        out.startTag(null, TAG_INTENT);
+        intent.saveToXml(out);
+        out.endTag(null, TAG_INTENT);
+
+        if (isPersistable() && persistentState != null) {
+            out.startTag(null, TAG_PERSISTABLEBUNDLE);
+            persistentState.saveToXml(out);
+            out.endTag(null, TAG_PERSISTABLEBUNDLE);
+        }
+    }
+
+    static ActivityRecord restoreFromXml(XmlPullParser in,
+            ActivityStackSupervisor stackSupervisor) throws IOException, XmlPullParserException {
+        Intent intent = null;
+        PersistableBundle persistentState = null;
+        int launchedFromUid = 0;
+        String launchedFromPackage = null;
+        String resolvedType = null;
+        boolean componentSpecified = false;
+        int userId = 0;
+        long createTime = -1;
+        final int outerDepth = in.getDepth();
+        TaskDescription taskDescription = new TaskDescription();
+
+        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
+            final String attrName = in.getAttributeName(attrNdx);
+            final String attrValue = in.getAttributeValue(attrNdx);
+            if (DEBUG) Slog.d(TaskPersister.TAG,
+                        "ActivityRecord: attribute name=" + attrName + " value=" + attrValue);
+            if (ATTR_ID.equals(attrName)) {
+                createTime = Long.parseLong(attrValue);
+            } else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) {
+                launchedFromUid = Integer.parseInt(attrValue);
+            } else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) {
+                launchedFromPackage = attrValue;
+            } else if (ATTR_RESOLVEDTYPE.equals(attrName)) {
+                resolvedType = attrValue;
+            } else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) {
+                componentSpecified = Boolean.parseBoolean(attrValue);
+            } else if (ATTR_USERID.equals(attrName)) {
+                userId = Integer.parseInt(attrValue);
+            } else if (attrName.startsWith(ATTR_TASKDESCRIPTION_PREFIX)) {
+                taskDescription.restoreFromXml(attrName, attrValue);
+            } else {
+                Log.d(TAG, "Unknown ActivityRecord attribute=" + attrName);
+            }
+        }
+
+        int event;
+        while (((event = in.next()) != END_DOCUMENT) &&
+                (event != END_TAG || in.getDepth() >= outerDepth)) {
+            if (event == START_TAG) {
+                final String name = in.getName();
+                if (DEBUG)
+                        Slog.d(TaskPersister.TAG, "ActivityRecord: START_TAG name=" + name);
+                if (TAG_INTENT.equals(name)) {
+                    intent = Intent.restoreFromXml(in);
+                    if (DEBUG)
+                            Slog.d(TaskPersister.TAG, "ActivityRecord: intent=" + intent);
+                } else if (TAG_PERSISTABLEBUNDLE.equals(name)) {
+                    persistentState = PersistableBundle.restoreFromXml(in);
+                    if (DEBUG) Slog.d(TaskPersister.TAG,
+                            "ActivityRecord: persistentState=" + persistentState);
+                } else {
+                    Slog.w(TAG, "restoreActivity: unexpected name=" + name);
+                    XmlUtils.skipCurrentTag(in);
+                }
+            }
+        }
+
+        if (intent == null) {
+            throw new XmlPullParserException("restoreActivity error intent=" + intent);
+        }
+
+        final ActivityTaskManagerService service = stackSupervisor.mService;
+        final ActivityInfo aInfo = stackSupervisor.resolveActivity(intent, resolvedType, 0, null,
+                userId, Binder.getCallingUid());
+        if (aInfo == null) {
+            throw new XmlPullParserException("restoreActivity resolver error. Intent=" + intent +
+                    " resolvedType=" + resolvedType);
+        }
+        final ActivityRecord r = new ActivityRecord(service, null /* caller */,
+                0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, intent, resolvedType,
+                aInfo, service.getConfiguration(), null /* resultTo */, null /* resultWho */,
+                0 /* reqCode */, componentSpecified, false /* rootVoiceInteraction */,
+                stackSupervisor, null /* options */, null /* sourceRecord */);
+
+        r.persistentState = persistentState;
+        r.taskDescription = taskDescription;
+        r.createTime = createTime;
+
+        return r;
+    }
+
+    private static boolean isInVrUiMode(Configuration config) {
+        return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET;
+    }
+
+    int getUid() {
+        return info.applicationInfo.uid;
+    }
+
+    void setShowWhenLocked(boolean showWhenLocked) {
+        mShowWhenLocked = showWhenLocked;
+        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0 /* configChanges */,
+                false /* preserveWindows */);
+    }
+
+    /**
+     * @return true if the activity windowing mode is not
+     *         {@link android.app.WindowConfiguration#WINDOWING_MODE_PINNED} and activity contains
+     *         windows that have {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the activity
+     *         has set {@link #mShowWhenLocked}.
+     *         Multi-windowing mode will be exited if true is returned.
+     */
+    boolean canShowWhenLocked() {
+        return !inPinnedWindowingMode() && (mShowWhenLocked
+                || service.mWindowManager.containsShowWhenLockedWindow(appToken));
+    }
+
+    void setTurnScreenOn(boolean turnScreenOn) {
+        mTurnScreenOn = turnScreenOn;
+    }
+
+    /**
+     * Determines whether this ActivityRecord can turn the screen on. It checks whether the flag
+     * {@link #mTurnScreenOn} is set and checks whether the ActivityRecord should be visible
+     * depending on Keyguard state
+     *
+     * @return true if the screen can be turned on, false otherwise.
+     */
+    boolean canTurnScreenOn() {
+        final ActivityStack stack = getStack();
+        return mTurnScreenOn && stack != null &&
+                stack.checkKeyguardVisibility(this, true /* shouldBeVisible */, true /* isTop */);
+    }
+
+    boolean getTurnScreenOnFlag() {
+        return mTurnScreenOn;
+    }
+
+    boolean isTopRunningActivity() {
+        return mStackSupervisor.topRunningActivityLocked() == this;
+    }
+
+    /**
+     * @return {@code true} if this is the resumed activity on its current display, {@code false}
+     * otherwise.
+     */
+    boolean isResumedActivityOnDisplay() {
+        final ActivityDisplay display = getDisplay();
+        return display != null && this == display.getResumedActivity();
+    }
+
+    void registerRemoteAnimations(RemoteAnimationDefinition definition) {
+        mWindowContainerController.registerRemoteAnimations(definition);
+    }
+
+    @Override
+    public String toString() {
+        if (stringName != null) {
+            return stringName + " t" + (task == null ? INVALID_TASK_ID : task.taskId) +
+                    (finishing ? " f}" : "}");
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("ActivityRecord{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(" u");
+        sb.append(userId);
+        sb.append(' ');
+        sb.append(intent.getComponent().flattenToShortString());
+        stringName = sb.toString();
+        return toString();
+    }
+
+    void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(HASH_CODE, System.identityHashCode(this));
+        proto.write(USER_ID, userId);
+        proto.write(TITLE, intent.getComponent().flattenToShortString());
+        proto.end(token);
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
+        writeIdentifierToProto(proto, IDENTIFIER);
+        proto.write(STATE, mState.toString());
+        proto.write(VISIBLE, visible);
+        proto.write(FRONT_OF_TASK, frontOfTask);
+        if (hasProcess()) {
+            proto.write(PROC_ID, app.getPid());
+        }
+        proto.write(TRANSLUCENT, !fullscreen);
+        proto.end(token);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityResult.java b/services/core/java/com/android/server/wm/ActivityResult.java
new file mode 100644
index 0000000..f2510de
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityResult.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 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.server.wm;
+
+import android.app.ResultInfo;
+import android.content.Intent;
+
+/**
+ * Pending result information to send back to an activity.
+ */
+final class ActivityResult extends ResultInfo {
+    final ActivityRecord mFrom;
+    
+    public ActivityResult(ActivityRecord from, String resultWho,
+            int requestCode, int resultCode, Intent data) {
+        super(resultWho, requestCode, resultCode, data);
+        mFrom = from;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
new file mode 100644
index 0000000..ad46248
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.function.Consumer;
+
+/**
+ * Class for tracking the connections to services on the AM side that activities on the
+ * WM side (in the future) bind with for things like oom score adjustment. Would normally be one
+ * instance of this per activity for tracking all services connected to that activity. AM will
+ * sometimes query this to bump the OOM score for the processes with services connected to visible
+ * activities.
+ */
+public class ActivityServiceConnectionsHolder<T> {
+
+    private final ActivityTaskManagerService mService;
+
+    /** The activity the owns this service connection object. */
+    private final ActivityRecord mActivity;
+
+    /**
+     * The service connection object bounded with the owning activity. They represent
+     * ConnectionRecord on the AM side, however we don't need to know their object representation
+     * on the WM side since we don't perform operations on the object. Mainly here for communication
+     * and booking with the AM side.
+     */
+    private HashSet<T> mConnections;
+
+    ActivityServiceConnectionsHolder(ActivityTaskManagerService service, ActivityRecord activity) {
+        mService = service;
+        mActivity = activity;
+    }
+
+    /** Adds a connection record that the activity has bound to a specific service. */
+    public void addConnection(T c) {
+        synchronized (mService.mGlobalLock) {
+            if (mConnections == null) {
+                mConnections = new HashSet<>();
+            }
+            mConnections.add(c);
+        }
+    }
+
+    /** Removed a connection record between the activity and a specific service. */
+    public void removeConnection(T c) {
+        synchronized (mService.mGlobalLock) {
+            if (mConnections == null) {
+                return;
+            }
+            mConnections.remove(c);
+        }
+    }
+
+    public boolean isActivityVisible() {
+        synchronized (mService.mGlobalLock) {
+            return mActivity.visible || mActivity.isState(RESUMED, PAUSING);
+        }
+    }
+
+    public int getActivityPid() {
+        synchronized (mService.mGlobalLock) {
+            return mActivity.hasProcess() ? mActivity.app.getPid() : -1;
+        }
+    }
+
+    public void forEachConnection(Consumer<T> consumer) {
+        synchronized (mService.mGlobalLock) {
+            if (mConnections == null || mConnections.isEmpty()) {
+                return;
+            }
+            final Iterator<T> it = mConnections.iterator();
+            while (it.hasNext()) {
+                T c = it.next();
+                consumer.accept(c);
+            }
+        }
+    }
+
+    /** Removes the connection between the activity and all services that were connected to it. */
+    void disconnectActivityFromServices() {
+        if (mConnections == null || mConnections.isEmpty()) {
+            return;
+        }
+        mService.mH.post(() -> mService.mAmInternal.disconnectActivityFromServices(this));
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        synchronized (mService.mGlobalLock) {
+            pw.println(prefix + "activity=" + mActivity);
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
new file mode 100644
index 0000000..1944184
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -0,0 +1,5472 @@
+/*
+ * Copyright (C) 2010 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.server.wm;
+
+import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.windowingModeToString;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
+import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+
+import static com.android.server.am.ActivityStackProto.BOUNDS;
+import static com.android.server.am.ActivityStackProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityStackProto.DISPLAY_ID;
+import static com.android.server.am.ActivityStackProto.FULLSCREEN;
+import static com.android.server.am.ActivityStackProto.ID;
+import static com.android.server.am.ActivityStackProto.RESUMED_ACTIVITY;
+import static com.android.server.am.ActivityStackProto.TASKS;
+import static com.android.server.wm.ActivityDisplay.POSITION_BOTTOM;
+import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult;
+import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CLEANUP;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+
+import static java.lang.Integer.MAX_VALUE;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
+import android.app.AppGlobals;
+import android.app.IActivityController;
+import android.app.ResultInfo;
+import android.app.WindowConfiguration.ActivityType;
+import android.app.WindowConfiguration.WindowingMode;
+import android.app.servertransaction.ActivityResultItem;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.DestroyActivityItem;
+import android.app.servertransaction.NewIntentItem;
+import android.app.servertransaction.PauseActivityItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.app.servertransaction.StopActivityItem;
+import android.app.servertransaction.WindowVisibilityItem;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.ArraySet;
+import android.util.EventLog;
+import android.util.IntArray;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.am.ActivityManagerService.ItemMatcher;
+import com.android.server.am.AppTimeTracker;
+import com.android.server.am.EventLogTags;
+import com.android.server.am.PendingIntentRecord;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * State and management of a single stack of activities.
+ */
+class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
+        implements StackWindowListener {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
+    private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
+    private static final String TAG_APP = TAG + POSTFIX_APP;
+    private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
+    private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS;
+    private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
+    private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
+    private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
+    private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
+    private static final String TAG_STACK = TAG + POSTFIX_STACK;
+    private static final String TAG_STATES = TAG + POSTFIX_STATES;
+    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+    private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+    private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
+    private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
+    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
+
+    // Ticks during which we check progress while waiting for an app to launch.
+    static final int LAUNCH_TICK = 500;
+
+    // How long we wait until giving up on the last activity to pause.  This
+    // is short because it directly impacts the responsiveness of starting the
+    // next activity.
+    private static final int PAUSE_TIMEOUT = 500;
+
+    // How long we wait for the activity to tell us it has stopped before
+    // giving up.  This is a good amount of time because we really need this
+    // from the application in order to get its saved state. Once the stop
+    // is complete we may start destroying client resources triggering
+    // crashes if the UI thread was hung. We put this timeout one second behind
+    // the ANR timeout so these situations will generate ANR instead of
+    // Surface lost or other errors.
+    private static final int STOP_TIMEOUT = 11 * 1000;
+
+    // How long we wait until giving up on an activity telling us it has
+    // finished destroying itself.
+    private static final int DESTROY_TIMEOUT = 10 * 1000;
+
+    // Set to false to disable the preview that is shown while a new activity
+    // is being started.
+    private static final boolean SHOW_APP_STARTING_PREVIEW = true;
+
+    // How long to wait for all background Activities to redraw following a call to
+    // convertToTranslucent().
+    private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
+
+    // How many activities have to be scheduled to stop to force a stop pass.
+    private static final int MAX_STOPPING_TO_FORCE = 3;
+
+    @Override
+    protected int getChildCount() {
+        return mTaskHistory.size();
+    }
+
+    @Override
+    protected TaskRecord getChildAt(int index) {
+        return mTaskHistory.get(index);
+    }
+
+    @Override
+    protected ConfigurationContainer getParent() {
+        return getDisplay();
+    }
+
+    @Override
+    protected void onParentChanged() {
+        super.onParentChanged();
+        mStackSupervisor.updateUIDsPresentOnDisplay();
+    }
+
+    enum ActivityState {
+        INITIALIZING,
+        RESUMED,
+        PAUSING,
+        PAUSED,
+        STOPPING,
+        STOPPED,
+        FINISHING,
+        DESTROYING,
+        DESTROYED
+    }
+
+    @VisibleForTesting
+    /* The various modes for the method {@link #removeTask}. */
+    // Task is being completely removed from all stacks in the system.
+    protected static final int REMOVE_TASK_MODE_DESTROYING = 0;
+    // Task is being removed from this stack so we can add it to another stack. In the case we are
+    // moving we don't want to perform some operations on the task like removing it from window
+    // manager or recents.
+    static final int REMOVE_TASK_MODE_MOVING = 1;
+    // Similar to {@link #REMOVE_TASK_MODE_MOVING} and the task will be added to the top of its new
+    // stack and the new stack will be on top of all stacks.
+    static final int REMOVE_TASK_MODE_MOVING_TO_TOP = 2;
+
+    // The height/width divide used when fitting a task within a bounds with method
+    // {@link #fitWithinBounds}.
+    // We always want the task to to be visible in the bounds without affecting its size when
+    // fitting. To make sure this is the case, we don't adjust the task left or top side pass
+    // the input bounds right or bottom side minus the width or height divided by this value.
+    private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
+
+    final ActivityTaskManagerService mService;
+    private final WindowManagerService mWindowManager;
+    T mWindowContainerController;
+
+    /**
+     * The back history of all previous (and possibly still
+     * running) activities.  It contains #TaskRecord objects.
+     */
+    private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>();
+
+    /**
+     * List of running activities, sorted by recent usage.
+     * The first entry in the list is the least recently used.
+     * It contains HistoryRecord objects.
+     */
+    final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<>();
+
+    /**
+     * When we are in the process of pausing an activity, before starting the
+     * next one, this variable holds the activity that is currently being paused.
+     */
+    ActivityRecord mPausingActivity = null;
+
+    /**
+     * This is the last activity that we put into the paused state.  This is
+     * used to determine if we need to do an activity transition while sleeping,
+     * when we normally hold the top activity paused.
+     */
+    ActivityRecord mLastPausedActivity = null;
+
+    /**
+     * Activities that specify No History must be removed once the user navigates away from them.
+     * If the device goes to sleep with such an activity in the paused state then we save it here
+     * and finish it later if another activity replaces it on wakeup.
+     */
+    ActivityRecord mLastNoHistoryActivity = null;
+
+    /**
+     * Current activity that is resumed, or null if there is none.
+     */
+    ActivityRecord mResumedActivity = null;
+
+    // The topmost Activity passed to convertToTranslucent(). When non-null it means we are
+    // waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they
+    // are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the
+    // Activity in mTranslucentActivityWaiting is notified via
+    // Activity.onTranslucentConversionComplete(false). If a timeout occurs prior to the last
+    // background activity being drawn then the same call will be made with a true value.
+    ActivityRecord mTranslucentActivityWaiting = null;
+    ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent = new ArrayList<>();
+
+    /**
+     * Set when we know we are going to be calling updateConfiguration()
+     * soon, so want to skip intermediate config checks.
+     */
+    boolean mConfigWillChange;
+
+    /**
+     * When set, will force the stack to report as invisible.
+     */
+    boolean mForceHidden = false;
+
+    private boolean mUpdateBoundsDeferred;
+    private boolean mUpdateBoundsDeferredCalled;
+    private final Rect mDeferredBounds = new Rect();
+    private final Rect mDeferredTaskBounds = new Rect();
+    private final Rect mDeferredTaskInsetBounds = new Rect();
+
+    int mCurrentUser;
+
+    final int mStackId;
+    /** The attached Display's unique identifier, or -1 if detached */
+    int mDisplayId;
+
+    /** Stores the override windowing-mode from before a transient mode change (eg. split) */
+    private int mRestoreOverrideWindowingMode = WINDOWING_MODE_UNDEFINED;
+
+    private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
+    private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
+    private final Rect mTmpRect2 = new Rect();
+    private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
+
+    /** List for processing through a set of activities */
+    private final ArrayList<ActivityRecord> mTmpActivities = new ArrayList<>();
+
+    /** Run all ActivityStacks through this */
+    protected final ActivityStackSupervisor mStackSupervisor;
+
+    private boolean mTopActivityOccludesKeyguard;
+    private ActivityRecord mTopDismissingKeyguardActivity;
+
+    static final int PAUSE_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
+    static final int DESTROY_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 2;
+    static final int LAUNCH_TICK_MSG = FIRST_ACTIVITY_STACK_MSG + 3;
+    static final int STOP_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 4;
+    static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5;
+    static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6;
+
+    private static class ScheduleDestroyArgs {
+        final WindowProcessController mOwner;
+        final String mReason;
+        ScheduleDestroyArgs(WindowProcessController owner, String reason) {
+            mOwner = owner;
+            mReason = reason;
+        }
+    }
+
+    final Handler mHandler;
+
+    private class ActivityStackHandler extends Handler {
+
+        ActivityStackHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case PAUSE_TIMEOUT_MSG: {
+                    ActivityRecord r = (ActivityRecord)msg.obj;
+                    // We don't at this point know if the activity is fullscreen,
+                    // so we need to be conservative and assume it isn't.
+                    Slog.w(TAG, "Activity pause timeout for " + r);
+                    synchronized (mService.mGlobalLock) {
+                        if (r.hasProcess()) {
+                            mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
+                        }
+                        activityPausedLocked(r.appToken, true);
+                    }
+                } break;
+                case LAUNCH_TICK_MSG: {
+                    ActivityRecord r = (ActivityRecord)msg.obj;
+                    synchronized (mService.mGlobalLock) {
+                        if (r.continueLaunchTickingLocked()) {
+                            mService.logAppTooSlow(r.app, r.launchTickTime, "launching " + r);
+                        }
+                    }
+                } break;
+                case DESTROY_TIMEOUT_MSG: {
+                    ActivityRecord r = (ActivityRecord)msg.obj;
+                    // We don't at this point know if the activity is fullscreen,
+                    // so we need to be conservative and assume it isn't.
+                    Slog.w(TAG, "Activity destroy timeout for " + r);
+                    synchronized (mService.mGlobalLock) {
+                        activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout");
+                    }
+                } break;
+                case STOP_TIMEOUT_MSG: {
+                    ActivityRecord r = (ActivityRecord)msg.obj;
+                    // We don't at this point know if the activity is fullscreen,
+                    // so we need to be conservative and assume it isn't.
+                    Slog.w(TAG, "Activity stop timeout for " + r);
+                    synchronized (mService.mGlobalLock) {
+                        if (r.isInHistory()) {
+                            r.activityStoppedLocked(null /* icicle */,
+                                    null /* persistentState */, null /* description */);
+                        }
+                    }
+                } break;
+                case DESTROY_ACTIVITIES_MSG: {
+                    ScheduleDestroyArgs args = (ScheduleDestroyArgs)msg.obj;
+                    synchronized (mService.mGlobalLock) {
+                        destroyActivitiesLocked(args.mOwner, args.mReason);
+                    }
+                } break;
+                case TRANSLUCENT_TIMEOUT_MSG: {
+                    synchronized (mService.mGlobalLock) {
+                        notifyActivityDrawnLocked(null);
+                    }
+                } break;
+            }
+        }
+    }
+
+    int numActivities() {
+        int count = 0;
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            count += mTaskHistory.get(taskNdx).mActivities.size();
+        }
+        return count;
+    }
+
+    ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
+            int windowingMode, int activityType, boolean onTop) {
+        mStackSupervisor = supervisor;
+        mService = supervisor.mService;
+        mHandler = new ActivityStackHandler(supervisor.mLooper);
+        mWindowManager = mService.mWindowManager;
+        mStackId = stackId;
+        mCurrentUser = mService.mAmInternal.getCurrentUserId();
+        mTmpRect2.setEmpty();
+        // Set display id before setting activity and window type to make sure it won't affect
+        // stacks on a wrong display.
+        mDisplayId = display.mDisplayId;
+        setActivityType(activityType);
+        setWindowingMode(windowingMode);
+        mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
+                mTmpRect2);
+        postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
+    }
+
+    T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
+        return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
+                mStackSupervisor.mWindowManager);
+    }
+
+    T getWindowContainerController() {
+        return mWindowContainerController;
+    }
+
+    /**
+     * This should be called when an activity in a child task changes state. This should only
+     * be called from
+     * {@link TaskRecord#onActivityStateChanged(ActivityRecord, ActivityState, String)}.
+     * @param record The {@link ActivityRecord} whose state has changed.
+     * @param state The new state.
+     * @param reason The reason for the change.
+     */
+    void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) {
+        if (record == mResumedActivity && state != RESUMED) {
+            setResumedActivity(null, reason + " - onActivityStateChanged");
+        }
+
+        if (state == RESUMED) {
+            if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
+                    + reason);
+            setResumedActivity(record, reason + " - onActivityStateChanged");
+            if (record == mStackSupervisor.getTopResumedActivity()) {
+                // TODO(b/111361570): Support multiple focused apps in WM
+                mService.setResumedActivityUncheckLocked(record, reason);
+            }
+            mStackSupervisor.mRecentTasks.add(record.getTask());
+        }
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newParentConfig) {
+        final int prevWindowingMode = getWindowingMode();
+        final boolean prevIsAlwaysOnTop = isAlwaysOnTop();
+        final ActivityDisplay display = getDisplay();
+
+        getBounds(mTmpRect2);
+        final boolean hasNewBounds = display != null && getWindowContainerController() != null
+                && getWindowContainerController().updateBoundsForConfigChange(
+                        newParentConfig, getConfiguration(), mTmpRect2);
+
+        super.onConfigurationChanged(newParentConfig);
+        if (display == null) {
+          return;
+        }
+        if (prevWindowingMode != getWindowingMode()) {
+            display.onStackWindowingModeChanged(this);
+        }
+        if (hasNewBounds) {
+            resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
+        }
+        if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
+            // Since always on top is only on when the stack is freeform or pinned, the state
+            // can be toggled when the windowing mode changes. We must make sure the stack is
+            // placed properly when always on top state changes.
+            display.positionChildAtTop(this, false /* includingParents */);
+        }
+    }
+
+    @Override
+    public void setWindowingMode(int windowingMode) {
+        setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
+                false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */);
+    }
+
+    /**
+     * A transient windowing mode is one which activities enter into temporarily. Examples of this
+     * are Split window modes and pip. Non-transient modes are modes that displays can adopt.
+     *
+     * @param windowingMode the windowingMode to test for transient-ness.
+     * @return {@code true} if the windowing mode is transient, {@code false} otherwise.
+     */
+    private static boolean isTransientWindowingMode(int windowingMode) {
+        // TODO(b/114842032): add PIP if/when it uses mode transitions instead of task reparenting
+        return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+    }
+
+    /**
+     * Specialization of {@link #setWindowingMode(int)} for this subclass.
+     *
+     * @param preferredWindowingMode the preferred windowing mode. This may not be honored depending
+     *         on the state of things. For example, WINDOWING_MODE_UNDEFINED will resolve to the
+     *         previous non-transient mode if this stack is currently in a transient mode.
+     * @param animate Can be used to prevent animation.
+     * @param showRecents Controls whether recents is shown on the other side of a split while
+     *         entering split mode.
+     * @param enteringSplitScreenMode {@code true} if entering split mode.
+     * @param deferEnsuringVisibility Whether visibility updates are deferred. This is set when
+     *         many operations (which can effect visibility) are being performed in bulk.
+     */
+    void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents,
+            boolean enteringSplitScreenMode, boolean deferEnsuringVisibility) {
+        final boolean creating = mWindowContainerController == null;
+        final int currentMode = getWindowingMode();
+        final int currentOverrideMode = getOverrideWindowingMode();
+        final ActivityDisplay display = getDisplay();
+        final TaskRecord topTask = topTask();
+        final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
+        int windowingMode = preferredWindowingMode;
+        if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED
+                && isTransientWindowingMode(currentMode)) {
+            // Leaving a transient mode. Interpret UNDEFINED as "restore"
+            windowingMode = mRestoreOverrideWindowingMode;
+        }
+        mTmpOptions.setLaunchWindowingMode(windowingMode);
+
+        // Need to make sure windowing mode is supported. If we in the process of creating the stack
+        // no need to resolve the windowing mode again as it is already resolved to the right mode.
+        if (!creating) {
+            windowingMode = display.validateWindowingMode(windowingMode,
+                    null /* ActivityRecord */, topTask, getActivityType());
+        }
+        if (splitScreenStack == this
+                && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+            // Resolution to split-screen secondary for the primary split-screen stack means
+            // we want to leave split-screen mode.
+            windowingMode = mRestoreOverrideWindowingMode;
+        }
+
+        final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryStack();
+
+        // Don't send non-resizeable notifications if the windowing mode changed was a side effect
+        // of us entering split-screen mode.
+        final boolean sendNonResizeableNotification = !enteringSplitScreenMode;
+        // Take any required action due to us not supporting the preferred windowing mode.
+        if (alreadyInSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
+                && sendNonResizeableNotification && isActivityTypeStandardOrUndefined()) {
+            final boolean preferredSplitScreen =
+                    preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                    || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+            if (preferredSplitScreen || creating) {
+                // Looks like we can't launch in split screen mode or the stack we are launching
+                // doesn't support split-screen mode, go ahead an dismiss split-screen and display a
+                // warning toast about it.
+                mService.getTaskChangeNotificationController().notifyActivityDismissingDockedStack();
+                display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_UNDEFINED,
+                        false /* animate */, false /* showRecents */,
+                        false /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */);
+            }
+        }
+
+        if (currentMode == windowingMode) {
+            // You are already in the window mode, so we can skip most of the work below. However,
+            // it's possible that we have inherited the current windowing mode from a parent. So,
+            // fulfill this method's contract by setting the override mode directly.
+            getOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+            return;
+        }
+
+        final WindowManagerService wm = mService.mWindowManager;
+        final ActivityRecord topActivity = getTopActivity();
+
+        // For now, assume that the Stack's windowing mode is what will actually be used
+        // by it's activities. In the future, there may be situations where this doesn't
+        // happen; so at that point, this message will need to handle that.
+        int likelyResolvedMode = windowingMode;
+        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+            final ConfigurationContainer parent = getParent();
+            likelyResolvedMode = parent != null ? parent.getWindowingMode()
+                    : WINDOWING_MODE_FULLSCREEN;
+        }
+        if (sendNonResizeableNotification && likelyResolvedMode != WINDOWING_MODE_FULLSCREEN
+                && topActivity != null && topActivity.isNonResizableOrForcedResizable()
+                && !topActivity.noDisplay) {
+            // Inform the user that they are starting an app that may not work correctly in
+            // multi-window mode.
+            final String packageName = topActivity.appInfo.packageName;
+            mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
+                    topTask.taskId, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN, packageName);
+        }
+
+        wm.deferSurfaceLayout();
+        try {
+            if (!animate && topActivity != null) {
+                mStackSupervisor.mNoAnimActivities.add(topActivity);
+            }
+            super.setWindowingMode(windowingMode);
+            // setWindowingMode triggers an onConfigurationChanged cascade which can result in a
+            // different resolved windowing mode (usually when preferredWindowingMode is UNDEFINED).
+            windowingMode = getWindowingMode();
+
+            if (creating) {
+                // Nothing else to do if we don't have a window container yet. E.g. call from ctor.
+                return;
+            }
+
+            if (windowingMode == WINDOWING_MODE_PINNED || currentMode == WINDOWING_MODE_PINNED) {
+                // TODO: Need to remove use of PinnedActivityStack for this to be supported.
+                // NOTE: Need to ASS.scheduleUpdatePictureInPictureModeIfNeeded() in
+                // setWindowModeUnchecked() when this support is added. See TaskRecord.reparent()
+                throw new IllegalArgumentException(
+                        "Changing pinned windowing mode not currently supported");
+            }
+
+            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && splitScreenStack != null) {
+                // We already have a split-screen stack in this display, so just move the tasks over.
+                // TODO: Figure-out how to do all the stuff in
+                // AMS.setTaskWindowingModeSplitScreenPrimary
+                throw new IllegalArgumentException("Setting primary split-screen windowing mode"
+                        + " while there is already one isn't currently supported");
+                //return;
+            }
+            if (isTransientWindowingMode(windowingMode) && !isTransientWindowingMode(currentMode)) {
+                mRestoreOverrideWindowingMode = currentOverrideMode;
+            }
+
+            mTmpRect2.setEmpty();
+            if (windowingMode != WINDOWING_MODE_FULLSCREEN) {
+                mWindowContainerController.getRawBounds(mTmpRect2);
+            }
+
+            if (!Objects.equals(getOverrideBounds(), mTmpRect2)) {
+                resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
+            }
+        } finally {
+            if (showRecents && !alreadyInSplitScreenMode && mDisplayId == DEFAULT_DISPLAY
+                    && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                // Make sure recents stack exist when creating a dock stack as it normally needs to
+                // be on the other side of the docked stack and we make visibility decisions based
+                // on that.
+                // TODO: This is only here to help out with the case where recents stack doesn't
+                // exist yet. For that case the initial size of the split-screen stack will be the
+                // the one where the home stack is visible since recents isn't visible yet, but the
+                // divider will be off. I think we should just make the initial bounds that of home
+                // so that the divider matches and remove this logic.
+                // TODO: This is currently only called when entering split-screen while in another
+                // task, and from the tests
+                // TODO (b/78247419): Check if launcher and overview are same then move home stack
+                // instead of recents stack. Then fix the rotation animation from fullscreen to
+                // minimized mode
+                final ActivityStack recentStack = display.getOrCreateStack(
+                        WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS,
+                        true /* onTop */);
+                recentStack.moveToFront("setWindowingMode");
+                // If task moved to docked stack - show recents if needed.
+                mService.mWindowManager.showRecentApps();
+            }
+            wm.continueSurfaceLayout();
+        }
+
+        if (!deferEnsuringVisibility) {
+            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        }
+    }
+
+    @Override
+    public boolean isCompatible(int windowingMode, int activityType) {
+        // TODO: Should we just move this to ConfigurationContainer?
+        if (activityType == ACTIVITY_TYPE_UNDEFINED) {
+            // Undefined activity types end up in a standard stack once the stack is created on a
+            // display, so they should be considered compatible.
+            activityType = ACTIVITY_TYPE_STANDARD;
+        }
+        return super.isCompatible(windowingMode, activityType);
+    }
+
+    /** Adds the stack to specified display and calls WindowManager to do the same. */
+    void reparent(ActivityDisplay activityDisplay, boolean onTop, boolean displayRemoved) {
+        // TODO: We should probably resolve the windowing mode for the stack on the new display here
+        // so that it end up in a compatible mode in the new display. e.g. split-screen secondary.
+        removeFromDisplay();
+        // Reparent the window container before we try to update the position when adding it to
+        // the new display below
+        mTmpRect2.setEmpty();
+        mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop);
+        postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
+        if (!displayRemoved) {
+            postReparent();
+        }
+    }
+
+    /** Resume next focusable stack after reparenting to another display. */
+    void postReparent() {
+        adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */);
+        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        // Update visibility of activities before notifying WM. This way it won't try to resize
+        // windows that are no longer visible.
+        mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+                !PRESERVE_WINDOWS);
+    }
+
+    /**
+     * Updates internal state after adding to new display.
+     * @param activityDisplay New display to which this stack was attached.
+     * @param bounds Updated bounds.
+     */
+    private void postAddToDisplay(ActivityDisplay activityDisplay, Rect bounds, boolean onTop) {
+        if (mDisplayId != activityDisplay.mDisplayId) {
+            // rotations are relative to the display, so pretend like our current rotation is
+            // the same as the new display so we don't try to rotate bounds.
+            getConfiguration().windowConfiguration.setRotation(
+                    activityDisplay.getWindowConfiguration().getRotation());
+        }
+        mDisplayId = activityDisplay.mDisplayId;
+        setBounds(bounds);
+        onParentChanged();
+
+        activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
+        if (inSplitScreenPrimaryWindowingMode()) {
+            // If we created a docked stack we want to resize it so it resizes all other stacks
+            // in the system.
+            mStackSupervisor.resizeDockedStackLocked(
+                    getOverrideBounds(), null, null, null, null, PRESERVE_WINDOWS);
+        }
+    }
+
+    /**
+     * Updates the inner state of the stack to remove it from its current parent, so it can be
+     * either destroyed completely or re-parented.
+     */
+    private void removeFromDisplay() {
+        final ActivityDisplay display = getDisplay();
+        if (display != null) {
+            display.removeChild(this);
+        }
+        mDisplayId = INVALID_DISPLAY;
+    }
+
+    /** Removes the stack completely. Also calls WindowManager to do the same on its side. */
+    void remove() {
+        removeFromDisplay();
+        mWindowContainerController.removeContainer();
+        mWindowContainerController = null;
+        onParentChanged();
+    }
+
+    ActivityDisplay getDisplay() {
+        return mStackSupervisor.getActivityDisplay(mDisplayId);
+    }
+
+    /**
+     * @see #getStackDockedModeBounds(Rect, Rect, Rect, boolean)
+     */
+    void getStackDockedModeBounds(Rect currentTempTaskBounds, Rect outStackBounds,
+            Rect outTempTaskBounds, boolean ignoreVisibility) {
+        mWindowContainerController.getStackDockedModeBounds(currentTempTaskBounds,
+                outStackBounds, outTempTaskBounds, ignoreVisibility);
+    }
+
+    void prepareFreezingTaskBounds() {
+        mWindowContainerController.prepareFreezingTaskBounds();
+    }
+
+    void getWindowContainerBounds(Rect outBounds) {
+        if (mWindowContainerController != null) {
+            mWindowContainerController.getBounds(outBounds);
+            return;
+        }
+        outBounds.setEmpty();
+    }
+
+    void positionChildWindowContainerAtTop(TaskRecord child) {
+        mWindowContainerController.positionChildAtTop(child.getWindowContainerController(),
+                true /* includingParents */);
+    }
+
+    void positionChildWindowContainerAtBottom(TaskRecord child) {
+        // If there are other focusable stacks on the display, the z-order of the display should not
+        // be changed just because a task was placed at the bottom. E.g. if it is moving the topmost
+        // task to bottom, the next focusable stack on the same display should be focused.
+        final ActivityStack nextFocusableStack = getDisplay().getNextFocusableStack(
+                child.getStack(), true /* ignoreCurrent */);
+        mWindowContainerController.positionChildAtBottom(child.getWindowContainerController(),
+                nextFocusableStack == null /* includingParents */);
+    }
+
+    /**
+     * Returns whether to defer the scheduling of the multi-window mode.
+     */
+    boolean deferScheduleMultiWindowModeChanged() {
+        return false;
+    }
+
+    /**
+     * Defers updating the bounds of the stack. If the stack was resized/repositioned while
+     * deferring, the bounds will update in {@link #continueUpdateBounds()}.
+     */
+    void deferUpdateBounds() {
+        if (!mUpdateBoundsDeferred) {
+            mUpdateBoundsDeferred = true;
+            mUpdateBoundsDeferredCalled = false;
+        }
+    }
+
+    /**
+     * Continues updating bounds after updates have been deferred. If there was a resize attempt
+     * between {@link #deferUpdateBounds()} and {@link #continueUpdateBounds()}, the stack will
+     * be resized to that bounds.
+     */
+    void continueUpdateBounds() {
+        final boolean wasDeferred = mUpdateBoundsDeferred;
+        mUpdateBoundsDeferred = false;
+        if (wasDeferred && mUpdateBoundsDeferredCalled) {
+            resize(mDeferredBounds.isEmpty() ? null : mDeferredBounds,
+                    mDeferredTaskBounds.isEmpty() ? null : mDeferredTaskBounds,
+                    mDeferredTaskInsetBounds.isEmpty() ? null : mDeferredTaskInsetBounds);
+        }
+    }
+
+    boolean updateBoundsAllowed(Rect bounds, Rect tempTaskBounds,
+            Rect tempTaskInsetBounds) {
+        if (!mUpdateBoundsDeferred) {
+            return true;
+        }
+        if (bounds != null) {
+            mDeferredBounds.set(bounds);
+        } else {
+            mDeferredBounds.setEmpty();
+        }
+        if (tempTaskBounds != null) {
+            mDeferredTaskBounds.set(tempTaskBounds);
+        } else {
+            mDeferredTaskBounds.setEmpty();
+        }
+        if (tempTaskInsetBounds != null) {
+            mDeferredTaskInsetBounds.set(tempTaskInsetBounds);
+        } else {
+            mDeferredTaskInsetBounds.setEmpty();
+        }
+        mUpdateBoundsDeferredCalled = true;
+        return false;
+    }
+
+    @Override
+    public int setBounds(Rect bounds) {
+        return super.setBounds(!inMultiWindowMode() ? null : bounds);
+    }
+
+    ActivityRecord topRunningActivityLocked() {
+        return topRunningActivityLocked(false /* focusableOnly */);
+    }
+
+    void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
+        outActivities.clear();
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            mTaskHistory.get(taskNdx).getAllRunningVisibleActivitiesLocked(outActivities);
+        }
+    }
+
+    ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked();
+            if (r != null && (!focusableOnly || r.isFocusable())) {
+                return r;
+            }
+        }
+        return null;
+    }
+
+    ActivityRecord topRunningNonOverlayTaskActivity() {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (!r.finishing && !r.mTaskOverlay) {
+                    return r;
+                }
+            }
+        }
+        return null;
+    }
+
+    ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                ActivityRecord r = activities.get(activityNdx);
+                if (!r.finishing && !r.delayedResume && r != notTop && r.okToShowLocked()) {
+                    return r;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * This is a simplified version of topRunningActivityLocked that provides a number of
+     * optional skip-over modes.  It is intended for use with the ActivityController hook only.
+     *
+     * @param token If non-null, any history records matching this token will be skipped.
+     * @param taskId If non-zero, we'll attempt to skip over records with the same task ID.
+     *
+     * @return Returns the HistoryRecord of the next activity on the stack.
+     */
+    final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            TaskRecord task = mTaskHistory.get(taskNdx);
+            if (task.taskId == taskId) {
+                continue;
+            }
+            ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int i = activities.size() - 1; i >= 0; --i) {
+                final ActivityRecord r = activities.get(i);
+                // Note: the taskId check depends on real taskId fields being non-zero
+                if (!r.finishing && (token != r.appToken) && r.okToShowLocked()) {
+                    return r;
+                }
+            }
+        }
+        return null;
+    }
+
+    ActivityRecord getTopActivity() {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ActivityRecord r = mTaskHistory.get(taskNdx).getTopActivity();
+            if (r != null) {
+                return r;
+            }
+        }
+        return null;
+    }
+
+    final TaskRecord topTask() {
+        final int size = mTaskHistory.size();
+        if (size > 0) {
+            return mTaskHistory.get(size - 1);
+        }
+        return null;
+    }
+
+    private TaskRecord bottomTask() {
+        if (mTaskHistory.isEmpty()) {
+            return null;
+        }
+        return mTaskHistory.get(0);
+    }
+
+    TaskRecord taskForIdLocked(int id) {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            if (task.taskId == id) {
+                return task;
+            }
+        }
+        return null;
+    }
+
+    ActivityRecord isInStackLocked(IBinder token) {
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        return isInStackLocked(r);
+    }
+
+    ActivityRecord isInStackLocked(ActivityRecord r) {
+        if (r == null) {
+            return null;
+        }
+        final TaskRecord task = r.getTask();
+        final ActivityStack stack = r.getStack();
+        if (stack != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) {
+            if (stack != this) Slog.w(TAG,
+                    "Illegal state! task does not point to stack it is in.");
+            return r;
+        }
+        return null;
+    }
+
+    boolean isInStackLocked(TaskRecord task) {
+        return mTaskHistory.contains(task);
+    }
+
+    /** Checks if there are tasks with specific UID in the stack. */
+    boolean isUidPresent(int uid) {
+        for (TaskRecord task : mTaskHistory) {
+            for (ActivityRecord r : task.mActivities) {
+                if (r.getUid() == uid) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /** Get all UIDs that are present in the stack. */
+    void getPresentUIDs(IntArray presentUIDs) {
+        for (TaskRecord task : mTaskHistory) {
+            for (ActivityRecord r : task.mActivities) {
+                presentUIDs.add(r.getUid());
+            }
+        }
+    }
+
+    final void removeActivitiesFromLRUListLocked(TaskRecord task) {
+        for (ActivityRecord r : task.mActivities) {
+            mLRUActivities.remove(r);
+        }
+    }
+
+    final boolean updateLRUListLocked(ActivityRecord r) {
+        final boolean hadit = mLRUActivities.remove(r);
+        mLRUActivities.add(r);
+        return hadit;
+    }
+
+    final boolean isHomeOrRecentsStack() {
+        return isActivityTypeHome() || isActivityTypeRecents();
+    }
+
+    final boolean isOnHomeDisplay() {
+        return mDisplayId == DEFAULT_DISPLAY;
+    }
+
+    private boolean returnsToHomeStack() {
+        return !inMultiWindowMode()
+                && !mTaskHistory.isEmpty()
+                && mTaskHistory.get(0).returnsToHomeStack();
+    }
+
+    void moveToFront(String reason) {
+        moveToFront(reason, null);
+    }
+
+    /**
+     * @param reason The reason for moving the stack to the front.
+     * @param task If non-null, the task will be moved to the top of the stack.
+     * */
+    void moveToFront(String reason, TaskRecord task) {
+        if (!isAttached()) {
+            return;
+        }
+
+        final ActivityDisplay display = getDisplay();
+
+        if (inSplitScreenSecondaryWindowingMode()) {
+            // If the stack is in split-screen seconardy mode, we need to make sure we move the
+            // primary split-screen stack forward in the case it is currently behind a fullscreen
+            // stack so both halves of the split-screen appear on-top and the fullscreen stack isn't
+            // cutting between them.
+            // TODO(b/70677280): This is a workaround until we can fix as part of b/70677280.
+            final ActivityStack topFullScreenStack =
+                    display.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
+            if (topFullScreenStack != null) {
+                final ActivityStack primarySplitScreenStack = display.getSplitScreenPrimaryStack();
+                if (display.getIndexOf(topFullScreenStack)
+                        > display.getIndexOf(primarySplitScreenStack)) {
+                    primarySplitScreenStack.moveToFront(reason + " splitScreenToTop");
+                }
+            }
+        }
+
+        if (!isActivityTypeHome() && returnsToHomeStack()) {
+            // Make sure the home stack is behind this stack since that is where we should return to
+            // when this stack is no longer visible.
+            display.moveHomeStackToFront(reason + " returnToHome");
+        }
+
+        final boolean movingTask = task != null;
+        display.positionChildAtTop(this, !movingTask /* includingParents */, reason);
+        if (movingTask) {
+            // This also moves the entire hierarchy branch to top, including parents
+            insertTaskAtTop(task, null /* starting */);
+        }
+    }
+
+    /**
+     * @param reason The reason for moving the stack to the back.
+     * @param task If non-null, the task will be moved to the bottom of the stack.
+     **/
+    void moveToBack(String reason, TaskRecord task) {
+        if (!isAttached()) {
+            return;
+        }
+
+        /**
+         * The intent behind moving a primary split screen stack to the back is usually to hide
+         * behind the home stack. Exit split screen in this case.
+         */
+        if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            setWindowingMode(WINDOWING_MODE_UNDEFINED);
+        }
+
+        getDisplay().positionChildAtBottom(this, reason);
+        if (task != null) {
+            insertTaskAtBottom(task);
+        }
+    }
+
+    boolean isFocusable() {
+        final ActivityRecord r = topRunningActivityLocked();
+        return mStackSupervisor.isFocusable(this, r != null && r.isFocusable());
+    }
+
+    boolean isFocusableAndVisible() {
+        return isFocusable() && shouldBeVisible(null /* starting */);
+    }
+
+    final boolean isAttached() {
+        final ActivityDisplay display = getDisplay();
+        return display != null && !display.isRemoved();
+    }
+
+    /**
+     * Returns the top activity in any existing task matching the given Intent in the input result.
+     * Returns null if no such task is found.
+     */
+    void findTaskLocked(ActivityRecord target, FindTaskResult result) {
+        Intent intent = target.intent;
+        ActivityInfo info = target.info;
+        ComponentName cls = intent.getComponent();
+        if (info.targetActivity != null) {
+            cls = new ComponentName(info.packageName, info.targetActivity);
+        }
+        final int userId = UserHandle.getUserId(info.applicationInfo.uid);
+        boolean isDocument = intent != null & intent.isDocument();
+        // If documentData is non-null then it must match the existing task data.
+        Uri documentData = isDocument ? intent.getData() : null;
+
+        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + target + " in " + this);
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            if (task.voiceSession != null) {
+                // We never match voice sessions; those always run independently.
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": voice session");
+                continue;
+            }
+            if (task.userId != userId) {
+                // Looking for a different task.
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": different user");
+                continue;
+            }
+
+            // Overlays should not be considered as the task's logical top activity.
+            final ActivityRecord r = task.getTopActivity(false /* includeOverlays */);
+            if (r == null || r.finishing || r.userId != userId ||
+                    r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
+                continue;
+            }
+            if (!r.hasCompatibleActivityType(target)) {
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch activity type");
+                continue;
+            }
+
+            final Intent taskIntent = task.intent;
+            final Intent affinityIntent = task.affinityIntent;
+            final boolean taskIsDocument;
+            final Uri taskDocumentData;
+            if (taskIntent != null && taskIntent.isDocument()) {
+                taskIsDocument = true;
+                taskDocumentData = taskIntent.getData();
+            } else if (affinityIntent != null && affinityIntent.isDocument()) {
+                taskIsDocument = true;
+                taskDocumentData = affinityIntent.getData();
+            } else {
+                taskIsDocument = false;
+                taskDocumentData = null;
+            }
+
+            if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls="
+                    + taskIntent.getComponent().flattenToShortString()
+                    + "/aff=" + r.getTask().rootAffinity + " to new cls="
+                    + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
+            // TODO Refactor to remove duplications. Check if logic can be simplified.
+            if (taskIntent != null && taskIntent.getComponent() != null &&
+                    taskIntent.getComponent().compareTo(cls) == 0 &&
+                    Objects.equals(documentData, taskDocumentData)) {
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
+                //dump();
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS,
+                        "For Intent " + intent + " bringing to top: " + r.intent);
+                result.mRecord = r;
+                result.mIdealMatch = true;
+                break;
+            } else if (affinityIntent != null && affinityIntent.getComponent() != null &&
+                    affinityIntent.getComponent().compareTo(cls) == 0 &&
+                    Objects.equals(documentData, taskDocumentData)) {
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
+                //dump();
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS,
+                        "For Intent " + intent + " bringing to top: " + r.intent);
+                result.mRecord = r;
+                result.mIdealMatch = true;
+                break;
+            } else if (!isDocument && !taskIsDocument
+                    && result.mRecord == null && task.rootAffinity != null) {
+                if (task.rootAffinity.equals(target.taskAffinity)) {
+                    if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!");
+                    // It is possible for multiple tasks to have the same root affinity especially
+                    // if they are in separate stacks. We save off this candidate, but keep looking
+                    // to see if there is a better candidate.
+                    result.mRecord = r;
+                    result.mIdealMatch = false;
+                }
+            } else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
+        }
+    }
+
+    /**
+     * Returns the first activity (starting from the top of the stack) that
+     * is the same as the given activity.  Returns null if no such activity
+     * is found.
+     */
+    ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
+                                      boolean compareIntentFilters) {
+        ComponentName cls = intent.getComponent();
+        if (info.targetActivity != null) {
+            cls = new ComponentName(info.packageName, info.targetActivity);
+        }
+        final int userId = UserHandle.getUserId(info.applicationInfo.uid);
+
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                ActivityRecord r = activities.get(activityNdx);
+                if (!r.okToShowLocked()) {
+                    continue;
+                }
+                if (!r.finishing && r.userId == userId) {
+                    if (compareIntentFilters) {
+                        if (r.intent.filterEquals(intent)) {
+                            return r;
+                        }
+                    } else {
+                        if (r.intent.getComponent().equals(cls)) {
+                            return r;
+                        }
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /*
+     * Move the activities around in the stack to bring a user to the foreground.
+     */
+    final void switchUserLocked(int userId) {
+        if (mCurrentUser == userId) {
+            return;
+        }
+        mCurrentUser = userId;
+
+        // Move userId's tasks to the top.
+        int index = mTaskHistory.size();
+        for (int i = 0; i < index; ) {
+            final TaskRecord task = mTaskHistory.get(i);
+
+            if (task.okToShowLocked()) {
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() +
+                        " moving " + task + " to top");
+                mTaskHistory.remove(i);
+                mTaskHistory.add(task);
+                --index;
+                // Use same value for i.
+            } else {
+                ++i;
+            }
+        }
+    }
+
+    void minimalResumeActivityLocked(ActivityRecord r) {
+        if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + r + " (starting new instance)"
+                + " callers=" + Debug.getCallers(5));
+        r.setState(RESUMED, "minimalResumeActivityLocked");
+        r.completeResumeLocked();
+        if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE,
+                "Launch completed; removing icicle of " + r.icicle);
+    }
+
+    private void clearLaunchTime(ActivityRecord r) {
+        // Make sure that there is no activity waiting for this to launch.
+        if (!mStackSupervisor.mWaitingActivityLaunched.isEmpty()) {
+            mStackSupervisor.removeTimeoutsForActivityLocked(r);
+            mStackSupervisor.scheduleIdleTimeoutLocked(r);
+        }
+    }
+
+    void awakeFromSleepingLocked() {
+        // Ensure activities are no longer sleeping.
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                activities.get(activityNdx).setSleeping(false);
+            }
+        }
+        if (mPausingActivity != null) {
+            Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
+            activityPausedLocked(mPausingActivity.appToken, true);
+        }
+    }
+
+    void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
+        final String packageName = aInfo.packageName;
+        final int userId = UserHandle.getUserId(aInfo.uid);
+
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final List<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord ar = activities.get(activityNdx);
+
+                if ((userId == ar.userId) && packageName.equals(ar.packageName)) {
+                    ar.updateApplicationInfo(aInfo);
+                }
+            }
+        }
+    }
+
+    void checkReadyForSleep() {
+        if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
+            mStackSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
+        }
+    }
+
+    /**
+     * Tries to put the activities in the stack to sleep.
+     *
+     * If the stack is not in a state where its activities can be put to sleep, this function will
+     * start any necessary actions to move the stack into such a state. It is expected that this
+     * function get called again when those actions complete.
+     *
+     * @param shuttingDown true when the called because the device is shutting down.
+     * @return true if the stack finished going to sleep, false if the stack only started the
+     * process of going to sleep (checkReadyForSleep will be called when that process finishes).
+     */
+    boolean goToSleepIfPossible(boolean shuttingDown) {
+        boolean shouldSleep = true;
+
+        if (mResumedActivity != null) {
+            // Still have something resumed; can't sleep until it is paused.
+            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep needs to pause " + mResumedActivity);
+            if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
+                    "Sleep => pause with userLeaving=false");
+
+            startPausingLocked(false, true, null, false);
+            shouldSleep = false ;
+        } else if (mPausingActivity != null) {
+            // Still waiting for something to pause; can't sleep yet.
+            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still waiting to pause " + mPausingActivity);
+            shouldSleep = false;
+        }
+
+        if (!shuttingDown) {
+            if (containsActivityFromStack(mStackSupervisor.mStoppingActivities)) {
+                // Still need to tell some activities to stop; can't sleep yet.
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
+                        + mStackSupervisor.mStoppingActivities.size() + " activities");
+
+                mStackSupervisor.scheduleIdleLocked();
+                shouldSleep = false;
+            }
+
+            if (containsActivityFromStack(mStackSupervisor.mGoingToSleepActivities)) {
+                // Still need to tell some activities to sleep; can't sleep yet.
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to sleep "
+                        + mStackSupervisor.mGoingToSleepActivities.size() + " activities");
+                shouldSleep = false;
+            }
+        }
+
+        if (shouldSleep) {
+            goToSleep();
+        }
+
+        return shouldSleep;
+    }
+
+    void goToSleep() {
+        // Ensure visibility without updating configuration, as activities are about to sleep.
+        ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+                !PRESERVE_WINDOWS);
+
+        // Make sure any paused or stopped but visible activities are now sleeping.
+        // This ensures that the activity's onStop() is called.
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (r.isState(STOPPING, STOPPED, PAUSED, PAUSING)) {
+                    r.setSleeping(true);
+                }
+            }
+        }
+    }
+
+    private boolean containsActivityFromStack(List<ActivityRecord> rs) {
+        for (ActivityRecord r : rs) {
+            if (r.getStack() == this) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Schedule a pause timeout in case the app doesn't respond. We don't give it much time because
+     * this directly impacts the responsiveness seen by the user.
+     */
+    private void schedulePauseTimeout(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
+        msg.obj = r;
+        r.pauseTime = SystemClock.uptimeMillis();
+        mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
+        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
+    }
+
+    /**
+     * Start pausing the currently resumed activity.  It is an error to call this if there
+     * is already an activity being paused or there is no resumed activity.
+     *
+     * @param userLeaving True if this should result in an onUserLeaving to the current activity.
+     * @param uiSleeping True if this is happening with the user interface going to sleep (the
+     * screen turning off).
+     * @param resuming The activity we are currently trying to resume or null if this is not being
+     *                 called as part of resuming the top activity, so we shouldn't try to instigate
+     *                 a resume here if not null.
+     * @param pauseImmediately True if the caller does not want to wait for the activity callback to
+     *                         complete pausing.
+     * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
+     * it to tell us when it is done.
+     */
+    final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
+            ActivityRecord resuming, boolean pauseImmediately) {
+        if (mPausingActivity != null) {
+            Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+                    + " state=" + mPausingActivity.getState());
+            if (!shouldSleepActivities()) {
+                // Avoid recursion among check for sleep and complete pause during sleeping.
+                // Because activity will be paused immediately after resume, just let pause
+                // be completed by the order of activity paused from clients.
+                completePauseLocked(false, resuming);
+            }
+        }
+        ActivityRecord prev = mResumedActivity;
+
+        if (prev == null) {
+            if (resuming == null) {
+                Slog.wtf(TAG, "Trying to pause when nothing is resumed");
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            }
+            return false;
+        }
+
+        if (prev == resuming) {
+            Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
+            return false;
+        }
+
+        if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSING: " + prev);
+        else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev);
+        mPausingActivity = prev;
+        mLastPausedActivity = prev;
+        mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
+                || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
+        prev.setState(PAUSING, "startPausingLocked");
+        prev.getTask().touchActiveTime();
+        clearLaunchTime(prev);
+
+        mService.updateCpuStats();
+
+        if (prev.attachedToProcess()) {
+            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
+            try {
+                EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
+                        prev.shortComponentName, "userLeaving=" + userLeaving);
+                mService.updateUsageStats(prev, false);
+
+                mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+                        prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
+                                prev.configChangeFlags, pauseImmediately));
+            } catch (Exception e) {
+                // Ignore exception, if process died other code will cleanup.
+                Slog.w(TAG, "Exception thrown during pause", e);
+                mPausingActivity = null;
+                mLastPausedActivity = null;
+                mLastNoHistoryActivity = null;
+            }
+        } else {
+            mPausingActivity = null;
+            mLastPausedActivity = null;
+            mLastNoHistoryActivity = null;
+        }
+
+        // If we are not going to sleep, we want to ensure the device is
+        // awake until the next activity is started.
+        if (!uiSleeping && !mService.isSleepingOrShuttingDownLocked()) {
+            mStackSupervisor.acquireLaunchWakelock();
+        }
+
+        if (mPausingActivity != null) {
+            // Have the window manager pause its key dispatching until the new
+            // activity has started.  If we're pausing the activity just because
+            // the screen is being turned off and the UI is sleeping, don't interrupt
+            // key dispatch; the same activity will pick it up again on wakeup.
+            if (!uiSleeping) {
+                prev.pauseKeyDispatchingLocked();
+            } else if (DEBUG_PAUSE) {
+                 Slog.v(TAG_PAUSE, "Key dispatch not paused for screen off");
+            }
+
+            if (pauseImmediately) {
+                // If the caller said they don't want to wait for the pause, then complete
+                // the pause now.
+                completePauseLocked(false, resuming);
+                return false;
+
+            } else {
+                schedulePauseTimeout(prev);
+                return true;
+            }
+
+        } else {
+            // This activity failed to schedule the
+            // pause, so just treat it as being paused now.
+            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
+            if (resuming == null) {
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            }
+            return false;
+        }
+    }
+
+    final void activityPausedLocked(IBinder token, boolean timeout) {
+        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,
+            "Activity paused: token=" + token + ", timeout=" + timeout);
+
+        final ActivityRecord r = isInStackLocked(token);
+        if (r != null) {
+            mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+            if (mPausingActivity == r) {
+                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r
+                        + (timeout ? " (due to timeout)" : " (pause complete)"));
+                mService.mWindowManager.deferSurfaceLayout();
+                try {
+                    completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
+                } finally {
+                    mService.mWindowManager.continueSurfaceLayout();
+                }
+                return;
+            } else {
+                EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
+                        r.userId, System.identityHashCode(r), r.shortComponentName,
+                        mPausingActivity != null
+                            ? mPausingActivity.shortComponentName : "(none)");
+                if (r.isState(PAUSING)) {
+                    r.setState(PAUSED, "activityPausedLocked");
+                    if (r.finishing) {
+                        if (DEBUG_PAUSE) Slog.v(TAG,
+                                "Executing finish of failed to pause activity: " + r);
+                        finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false,
+                                "activityPausedLocked");
+                    }
+                }
+            }
+        }
+        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+    }
+
+    private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
+        ActivityRecord prev = mPausingActivity;
+        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev);
+
+        if (prev != null) {
+            prev.setWillCloseOrEnterPip(false);
+            final boolean wasStopping = prev.isState(STOPPING);
+            prev.setState(PAUSED, "completePausedLocked");
+            if (prev.finishing) {
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
+                prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
+                        "completedPausedLocked");
+            } else if (prev.hasProcess()) {
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
+                        + " wasStopping=" + wasStopping + " visible=" + prev.visible);
+                if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(prev)) {
+                    if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG_PAUSE,
+                            "Complete pause, no longer waiting: " + prev);
+                }
+                if (prev.deferRelaunchUntilPaused) {
+                    // Complete the deferred relaunch that was waiting for pause to complete.
+                    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev);
+                    prev.relaunchActivityLocked(false /* andResume */,
+                            prev.preserveWindowOnDeferredRelaunch);
+                } else if (wasStopping) {
+                    // We are also stopping, the stop request must have gone soon after the pause.
+                    // We can't clobber it, because the stop confirmation will not be handled.
+                    // We don't need to schedule another stop, we only need to let it happen.
+                    prev.setState(STOPPING, "completePausedLocked");
+                } else if (!prev.visible || shouldSleepOrShutDownActivities()) {
+                    // Clear out any deferred client hide we might currently have.
+                    prev.setDeferHidingClient(false);
+                    // If we were visible then resumeTopActivities will release resources before
+                    // stopping.
+                    addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */);
+                }
+            } else {
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev);
+                prev = null;
+            }
+            // It is possible the activity was freezing the screen before it was paused.
+            // In that case go ahead and remove the freeze this activity has on the screen
+            // since it is no longer visible.
+            if (prev != null) {
+                prev.stopFreezingScreenLocked(true /*force*/);
+            }
+            mPausingActivity = null;
+        }
+
+        if (resumeNext) {
+            final ActivityStack topStack = mStackSupervisor.getTopDisplayFocusedStack();
+            if (!topStack.shouldSleepOrShutDownActivities()) {
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(topStack, prev, null);
+            } else {
+                checkReadyForSleep();
+                ActivityRecord top = topStack.topRunningActivityLocked();
+                if (top == null || (prev != null && top != prev)) {
+                    // If there are no more activities available to run, do resume anyway to start
+                    // something. Also if the top activity on the stack is not the just paused
+                    // activity, we need to go ahead and resume it to ensure we complete an
+                    // in-flight app switch.
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                }
+            }
+        }
+
+        if (prev != null) {
+            prev.resumeKeyDispatchingLocked();
+
+            if (prev.hasProcess() && prev.cpuTimeAtResume > 0) {
+                final long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume;
+                if (diff > 0) {
+                    final Runnable r = PooledLambda.obtainRunnable(
+                            ActivityManagerInternal::updateForegroundTimeIfOnBattery,
+                            mService.mAmInternal, prev.info.packageName,
+                            prev.info.applicationInfo.uid,
+                            diff);
+                    mService.mH.post(r);
+                }
+            }
+            prev.cpuTimeAtResume = 0; // reset it
+        }
+
+        // Notify when the task stack has changed, but only if visibilities changed (not just
+        // focus). Also if there is an active pinned stack - we always want to notify it about
+        // task stack changes, because its positioning may depend on it.
+        if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
+                || getDisplay().hasPinnedStack()) {
+            mService.getTaskChangeNotificationController().notifyTaskStackChanged();
+            mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
+        }
+
+        mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);
+    }
+
+    private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
+        if (!mStackSupervisor.mStoppingActivities.contains(r)) {
+            mStackSupervisor.mStoppingActivities.add(r);
+
+            // Some activity is waiting for another activity to become visible before it's being
+            // stopped, which means that we also want to wait with stopping this one to avoid
+            // flickers.
+            if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.isEmpty()
+                    && !mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) {
+                if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "adding to waiting visible activity=" + r
+                        + " existing=" + mStackSupervisor.mActivitiesWaitingForVisibleActivity);
+                mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(r);
+            }
+        }
+
+        // If we already have a few activities waiting to stop, then give up
+        // on things going idle and start clearing them out. Or if r is the
+        // last of activity of the last task the stack will be empty and must
+        // be cleared immediately.
+        boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE
+                || (r.frontOfTask && mTaskHistory.size() <= 1);
+        if (scheduleIdle || forceIdle) {
+            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle="
+                    + forceIdle + "immediate=" + !idleDelayed);
+            if (!idleDelayed) {
+                mStackSupervisor.scheduleIdleLocked();
+            } else {
+                mStackSupervisor.scheduleIdleTimeoutLocked(r);
+            }
+        } else {
+            checkReadyForSleep();
+        }
+    }
+
+    /**
+     * Returns true if the stack is translucent and can have other contents visible behind it if
+     * needed. A stack is considered translucent if it don't contain a visible or
+     * starting (about to be visible) activity that is fullscreen (opaque).
+     * @param starting The currently starting activity or null if there is none.
+     */
+    @VisibleForTesting
+    boolean isStackTranslucent(ActivityRecord starting) {
+        if (!isAttached() || mForceHidden) {
+            return true;
+        }
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+
+                if (r.finishing) {
+                    // We don't factor in finishing activities when determining translucency since
+                    // they will be gone soon.
+                    continue;
+                }
+
+                if (!r.visibleIgnoringKeyguard && r != starting) {
+                    // Also ignore invisible activities that are not the currently starting
+                    // activity (about to be visible).
+                    continue;
+                }
+
+                if (r.fullscreen || r.hasWallpaper) {
+                    // Stack isn't translucent if it has at least one fullscreen activity
+                    // that is visible.
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    boolean isTopStackOnDisplay() {
+        final ActivityDisplay display = getDisplay();
+        return display != null && display.isTopStack(this);
+    }
+
+    /**
+     * @return {@code true} if this is the focused stack on its current display, {@code false}
+     * otherwise.
+     */
+    boolean isFocusedStackOnDisplay() {
+        final ActivityDisplay display = getDisplay();
+        return display != null && this == display.getFocusedStack();
+    }
+
+    boolean isTopActivityVisible() {
+        final ActivityRecord topActivity = getTopActivity();
+        return topActivity != null && topActivity.visible;
+    }
+
+    /**
+     * Returns true if the stack should be visible.
+     *
+     * @param starting The currently starting activity or null if there is none.
+     */
+    boolean shouldBeVisible(ActivityRecord starting) {
+        if (!isAttached() || mForceHidden) {
+            return false;
+        }
+
+        final ActivityDisplay display = getDisplay();
+        boolean gotSplitScreenStack = false;
+        boolean gotOpaqueSplitScreenPrimary = false;
+        boolean gotOpaqueSplitScreenSecondary = false;
+        final int windowingMode = getWindowingMode();
+        final boolean isAssistantType = isActivityTypeAssistant();
+        for (int i = display.getChildCount() - 1; i >= 0; --i) {
+            final ActivityStack other = display.getChildAt(i);
+            final boolean hasRunningActivities = other.topRunningActivityLocked() != null;
+            if (other == this) {
+                // Should be visible if there is no other stack occluding it, unless it doesn't
+                // have any running activities, not starting one and not home stack.
+                return hasRunningActivities || isInStackLocked(starting) != null
+                        || isActivityTypeHome();
+            }
+
+            if (!hasRunningActivities) {
+                continue;
+            }
+
+            final int otherWindowingMode = other.getWindowingMode();
+
+            if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+                // In this case the home stack isn't resizeable even though we are in split-screen
+                // mode. We still want the primary splitscreen stack to be visible as there will be
+                // a slight hint of it in the status bar area above the non-resizeable home
+                // activity. In addition, if the fullscreen assistant is over primary splitscreen
+                // stack, the stack should still be visible in the background as long as the recents
+                // animation is running.
+                final int activityType = other.getActivityType();
+                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                    if (activityType == ACTIVITY_TYPE_HOME
+                            || (activityType == ACTIVITY_TYPE_ASSISTANT
+                                    && mWindowManager.getRecentsAnimationController() != null)) {
+                       return true;
+                    }
+                }
+                if (other.isStackTranslucent(starting)) {
+                    // Can be visible behind a translucent fullscreen stack.
+                    continue;
+                }
+                return false;
+            } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                    && !gotOpaqueSplitScreenPrimary) {
+                gotSplitScreenStack = true;
+                gotOpaqueSplitScreenPrimary =
+                        !other.isStackTranslucent(starting);
+                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                        && gotOpaqueSplitScreenPrimary) {
+                    // Can not be visible behind another opaque stack in split-screen-primary mode.
+                    return false;
+                }
+            } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                    && !gotOpaqueSplitScreenSecondary) {
+                gotSplitScreenStack = true;
+                gotOpaqueSplitScreenSecondary =
+                        !other.isStackTranslucent(starting);
+                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                        && gotOpaqueSplitScreenSecondary) {
+                    // Can not be visible behind another opaque stack in split-screen-secondary mode.
+                    return false;
+                }
+            }
+            if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
+                // Can not be visible if we are in split-screen windowing mode and both halves of
+                // the screen are opaque.
+                return false;
+            }
+            if (isAssistantType && gotSplitScreenStack) {
+                // Assistant stack can't be visible behind split-screen. In addition to this not
+                // making sense, it also works around an issue here we boost the z-order of the
+                // assistant window surfaces in window manager whenever it is visible.
+                return false;
+            }
+        }
+
+        // Well, nothing is stopping you from being visible...
+        return true;
+    }
+
+    final int rankTaskLayers(int baseLayer) {
+        int layer = 0;
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            ActivityRecord r = task.topRunningActivityLocked();
+            if (r == null || r.finishing || !r.visible) {
+                task.mLayerRank = -1;
+            } else {
+                task.mLayerRank = baseLayer + layer++;
+            }
+        }
+        return layer;
+    }
+
+    /**
+     * Make sure that all activities that need to be visible in the stack (that is, they
+     * currently can be seen by the user) actually are and update their configuration.
+     */
+    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+            boolean preserveWindows) {
+        ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
+                true /* notifyClients */);
+    }
+
+    /**
+     * Ensure visibility with an option to also update the configuration of visible activities.
+     * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
+     * @see ActivityStackSupervisor#ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
+     */
+    // TODO: Should be re-worked based on the fact that each task as a stack in most cases.
+    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+            boolean preserveWindows, boolean notifyClients) {
+        mTopActivityOccludesKeyguard = false;
+        mTopDismissingKeyguardActivity = null;
+        mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
+        try {
+            ActivityRecord top = topRunningActivityLocked();
+            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + top
+                    + " configChanges=0x" + Integer.toHexString(configChanges));
+            if (top != null) {
+                checkTranslucentActivityWaiting(top);
+            }
+
+            // If the top activity is not fullscreen, then we need to
+            // make sure any activities under it are now visible.
+            boolean aboveTop = top != null;
+            final boolean stackShouldBeVisible = shouldBeVisible(starting);
+            boolean behindFullscreenActivity = !stackShouldBeVisible;
+            boolean resumeNextActivity = mStackSupervisor.isTopDisplayFocusedStack(this)
+                    && (isInStackLocked(starting) == null);
+            final boolean isTopNotPinnedStack =
+                    isAttached() && getDisplay().isTopNotPinnedStack(this);
+            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+                final TaskRecord task = mTaskHistory.get(taskNdx);
+                final ArrayList<ActivityRecord> activities = task.mActivities;
+                for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                    final ActivityRecord r = activities.get(activityNdx);
+                    if (r.finishing) {
+                        continue;
+                    }
+                    final boolean isTop = r == top;
+                    if (aboveTop && !isTop) {
+                        continue;
+                    }
+                    aboveTop = false;
+
+                    // Check whether activity should be visible without Keyguard influence
+                    final boolean visibleIgnoringKeyguard = r.shouldBeVisibleIgnoringKeyguard(
+                            behindFullscreenActivity);
+                    r.visibleIgnoringKeyguard = visibleIgnoringKeyguard;
+
+                    // Now check whether it's really visible depending on Keyguard state.
+                    final boolean reallyVisible = checkKeyguardVisibility(r,
+                            visibleIgnoringKeyguard, isTop && isTopNotPinnedStack);
+                    if (visibleIgnoringKeyguard) {
+                        behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible,
+                                behindFullscreenActivity, r);
+                    }
+                    if (reallyVisible) {
+                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r
+                                + " finishing=" + r.finishing + " state=" + r.getState());
+                        // First: if this is not the current activity being started, make
+                        // sure it matches the current configuration.
+                        if (r != starting && notifyClients) {
+                            r.ensureActivityConfiguration(0 /* globalChanges */, preserveWindows,
+                                    true /* ignoreStopState */);
+                        }
+
+                        if (!r.attachedToProcess()) {
+                            if (makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
+                                    resumeNextActivity, r)) {
+                                if (activityNdx >= activities.size()) {
+                                    // Record may be removed if its process needs to restart.
+                                    activityNdx = activities.size() - 1;
+                                } else {
+                                    resumeNextActivity = false;
+                                }
+                            }
+                        } else if (r.visible) {
+                            // If this activity is already visible, then there is nothing to do here.
+                            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                                    "Skipping: already visible at " + r);
+
+                            if (r.mClientVisibilityDeferred && notifyClients) {
+                                r.makeClientVisible();
+                            }
+
+                            if (r.handleAlreadyVisible()) {
+                                resumeNextActivity = false;
+                            }
+                        } else {
+                            r.makeVisibleIfNeeded(starting, notifyClients);
+                        }
+                        // Aggregate current change flags.
+                        configChanges |= r.configChangeFlags;
+                    } else {
+                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
+                                + " finishing=" + r.finishing + " state=" + r.getState()
+                                + " stackShouldBeVisible=" + stackShouldBeVisible
+                                + " behindFullscreenActivity=" + behindFullscreenActivity
+                                + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
+                        makeInvisible(r);
+                    }
+                }
+                final int windowingMode = getWindowingMode();
+                if (windowingMode == WINDOWING_MODE_FREEFORM) {
+                    // The visibility of tasks and the activities they contain in freeform stack are
+                    // determined individually unlike other stacks where the visibility or fullscreen
+                    // status of an activity in a previous task affects other.
+                    behindFullscreenActivity = !stackShouldBeVisible;
+                } else if (isActivityTypeHome()) {
+                    if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + task
+                            + " stackShouldBeVisible=" + stackShouldBeVisible
+                            + " behindFullscreenActivity=" + behindFullscreenActivity);
+                    // No other task in the home stack should be visible behind the home activity.
+                    // Home activities is usually a translucent activity with the wallpaper behind
+                    // them. However, when they don't have the wallpaper behind them, we want to
+                    // show activities in the next application stack behind them vs. another
+                    // task in the home stack like recents.
+                    behindFullscreenActivity = true;
+                }
+            }
+
+            if (mTranslucentActivityWaiting != null &&
+                    mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
+                // Nothing is getting drawn or everything was already visible, don't wait for timeout.
+                notifyActivityDrawnLocked(null);
+            }
+        } finally {
+            mStackSupervisor.getKeyguardController().endActivityVisibilityUpdate();
+        }
+    }
+
+    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            mTaskHistory.get(taskNdx).addStartingWindowsForVisibleActivities(taskSwitch);
+        }
+    }
+
+    /**
+     * @return true if the top visible activity wants to occlude the Keyguard, false otherwise
+     */
+    boolean topActivityOccludesKeyguard() {
+        return mTopActivityOccludesKeyguard;
+    }
+
+    /**
+     * Returns true if this stack should be resized to match the bounds specified by
+     * {@link ActivityOptions#setLaunchBounds} when launching an activity into the stack.
+     */
+    boolean resizeStackWithLaunchBounds() {
+        return inPinnedWindowingMode();
+    }
+
+    @Override
+    public boolean supportsSplitScreenWindowingMode() {
+        final TaskRecord topTask = topTask();
+        return super.supportsSplitScreenWindowingMode()
+                && (topTask == null || topTask.supportsSplitScreenWindowingMode());
+    }
+
+    /** @return True if the resizing of the primary-split-screen stack affects this stack size. */
+    boolean affectedBySplitScreenResize() {
+        if (!supportsSplitScreenWindowingMode()) {
+            return false;
+        }
+        final int windowingMode = getWindowingMode();
+        return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
+    }
+
+    /**
+     * @return the top most visible activity that wants to dismiss Keyguard
+     */
+    ActivityRecord getTopDismissingKeyguardActivity() {
+        return mTopDismissingKeyguardActivity;
+    }
+
+    /**
+     * Checks whether {@param r} should be visible depending on Keyguard state and updates
+     * {@link #mTopActivityOccludesKeyguard} and {@link #mTopDismissingKeyguardActivity} if
+     * necessary.
+     *
+     * @return true if {@param r} is visible taken Keyguard state into account, false otherwise
+     */
+    boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible, boolean isTop) {
+        final int displayId = mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY;
+        final boolean keyguardOrAodShowing = mStackSupervisor.getKeyguardController()
+                .isKeyguardOrAodShowing(displayId);
+        final boolean keyguardLocked = mStackSupervisor.getKeyguardController().isKeyguardLocked();
+        final boolean showWhenLocked = r.canShowWhenLocked();
+        final boolean dismissKeyguard = r.hasDismissKeyguardWindows();
+        if (shouldBeVisible) {
+            if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
+                mTopDismissingKeyguardActivity = r;
+            }
+
+            // Only the top activity may control occluded, as we can't occlude the Keyguard if the
+            // top app doesn't want to occlude it.
+            if (isTop) {
+                mTopActivityOccludesKeyguard |= showWhenLocked;
+            }
+
+            final boolean canShowWithKeyguard = canShowWithInsecureKeyguard()
+                    && mStackSupervisor.getKeyguardController().canDismissKeyguard();
+            if (canShowWithKeyguard) {
+                return true;
+            }
+        }
+        if (keyguardOrAodShowing) {
+            // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
+            // right away and AOD isn't visible.
+            return shouldBeVisible && mStackSupervisor.getKeyguardController()
+                    .canShowActivityWhileKeyguardShowing(r, dismissKeyguard);
+        } else if (keyguardLocked) {
+            return shouldBeVisible && mStackSupervisor.getKeyguardController().canShowWhileOccluded(
+                    dismissKeyguard, showWhenLocked);
+        } else {
+            return shouldBeVisible;
+        }
+    }
+
+    /**
+     * Check if the display to which this stack is attached has
+     * {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
+     */
+    private boolean canShowWithInsecureKeyguard() {
+        final ActivityDisplay activityDisplay = getDisplay();
+        if (activityDisplay == null) {
+            throw new IllegalStateException("Stack is not attached to any display, stackId="
+                    + mStackId);
+        }
+
+        final int flags = activityDisplay.mDisplay.getFlags();
+        return (flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0;
+    }
+
+    private void checkTranslucentActivityWaiting(ActivityRecord top) {
+        if (mTranslucentActivityWaiting != top) {
+            mUndrawnActivitiesBelowTopTranslucent.clear();
+            if (mTranslucentActivityWaiting != null) {
+                // Call the callback with a timeout indication.
+                notifyActivityDrawnLocked(null);
+                mTranslucentActivityWaiting = null;
+            }
+            mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+        }
+    }
+
+    private boolean makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges,
+            boolean isTop, boolean andResume, ActivityRecord r) {
+        // We need to make sure the app is running if it's the top, or it is just made visible from
+        // invisible. If the app is already visible, it must have died while it was visible. In this
+        // case, we'll show the dead window but will not restart the app. Otherwise we could end up
+        // thrashing.
+        if (isTop || !r.visible) {
+            // This activity needs to be visible, but isn't even running...
+            // get it started and resume if no other stack in this stack is resumed.
+            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Start and freeze screen for " + r);
+            if (r != starting) {
+                r.startFreezingScreenLocked(r.app, configChanges);
+            }
+            if (!r.visible || r.mLaunchTaskBehind) {
+                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r);
+                r.setVisible(true);
+            }
+            if (r != starting) {
+                mStackSupervisor.startSpecificActivityLocked(r, andResume, false);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // TODO: Should probably be moved into ActivityRecord.
+    private void makeInvisible(ActivityRecord r) {
+        if (!r.visible) {
+            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
+            return;
+        }
+        // Now for any activities that aren't visible to the user, make sure they no longer are
+        // keeping the screen frozen.
+        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.getState());
+        try {
+            final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState(
+                    "makeInvisible", true /* beforeStopping */);
+            // Defer telling the client it is hidden if it can enter Pip and isn't current paused,
+            // stopped or stopping. This gives it a chance to enter Pip in onPause().
+            // TODO: There is still a question surrounding activities in multi-window mode that want
+            // to enter Pip after they are paused, but are still visible. I they should be okay to
+            // enter Pip in those cases, but not "auto-Pip" which is what this condition covers and
+            // the current contract for "auto-Pip" is that the app should enter it before onPause
+            // returns. Just need to confirm this reasoning makes sense.
+            final boolean deferHidingClient = canEnterPictureInPicture
+                    && !r.isState(STOPPING, STOPPED, PAUSED);
+            r.setDeferHidingClient(deferHidingClient);
+            r.setVisible(false);
+
+            switch (r.getState()) {
+                case STOPPING:
+                case STOPPED:
+                    if (r.attachedToProcess()) {
+                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                                "Scheduling invisibility: " + r);
+                        mService.getLifecycleManager().scheduleTransaction(r.app.getThread(),
+                                r.appToken, WindowVisibilityItem.obtain(false /* showWindow */));
+                    }
+
+                    // Reset the flag indicating that an app can enter picture-in-picture once the
+                    // activity is hidden
+                    r.supportsEnterPipOnTaskSwitch = false;
+                    break;
+
+                case INITIALIZING:
+                case RESUMED:
+                case PAUSING:
+                case PAUSED:
+                    addToStopping(r, true /* scheduleIdle */,
+                            canEnterPictureInPicture /* idleDelayed */);
+                    break;
+
+                default:
+                    break;
+            }
+        } catch (Exception e) {
+            // Just skip on any failure; we'll make it visible when it next restarts.
+            Slog.w(TAG, "Exception thrown making hidden: " + r.intent.getComponent(), e);
+        }
+    }
+
+    private boolean updateBehindFullscreen(boolean stackInvisible, boolean behindFullscreenActivity,
+            ActivityRecord r) {
+        if (r.fullscreen) {
+            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
+                        + " stackInvisible=" + stackInvisible
+                        + " behindFullscreenActivity=" + behindFullscreenActivity);
+            // At this point, nothing else needs to be shown in this task.
+            behindFullscreenActivity = true;
+        }
+        return behindFullscreenActivity;
+    }
+
+    void convertActivityToTranslucent(ActivityRecord r) {
+        mTranslucentActivityWaiting = r;
+        mUndrawnActivitiesBelowTopTranslucent.clear();
+        mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
+    }
+
+    void clearOtherAppTimeTrackers(AppTimeTracker except) {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if ( r.appTimeTracker != except) {
+                    r.appTimeTracker = null;
+                }
+            }
+        }
+    }
+
+    /**
+     * Called as activities below the top translucent activity are redrawn. When the last one is
+     * redrawn notify the top activity by calling
+     * {@link Activity#onTranslucentConversionComplete}.
+     *
+     * @param r The most recent background activity to be drawn. Or, if r is null then a timeout
+     * occurred and the activity will be notified immediately.
+     */
+    void notifyActivityDrawnLocked(ActivityRecord r) {
+        if ((r == null)
+                || (mUndrawnActivitiesBelowTopTranslucent.remove(r) &&
+                        mUndrawnActivitiesBelowTopTranslucent.isEmpty())) {
+            // The last undrawn activity below the top has just been drawn. If there is an
+            // opaque activity at the top, notify it that it can become translucent safely now.
+            final ActivityRecord waitingActivity = mTranslucentActivityWaiting;
+            mTranslucentActivityWaiting = null;
+            mUndrawnActivitiesBelowTopTranslucent.clear();
+            mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+
+            if (waitingActivity != null) {
+                mWindowManager.setWindowOpaque(waitingActivity.appToken, false);
+                if (waitingActivity.attachedToProcess()) {
+                    try {
+                        waitingActivity.app.getThread().scheduleTranslucentConversionComplete(
+                                waitingActivity.appToken, r != null);
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+        }
+    }
+
+    /** If any activities below the top running one are in the INITIALIZING state and they have a
+     * starting window displayed then remove that starting window. It is possible that the activity
+     * in this state will never resumed in which case that starting window will be orphaned. */
+    void cancelInitializingActivities() {
+        final ActivityRecord topActivity = topRunningActivityLocked();
+        boolean aboveTop = true;
+        // We don't want to clear starting window for activities that aren't behind fullscreen
+        // activities as we need to display their starting window until they are done initializing.
+        boolean behindFullscreenActivity = false;
+
+        if (!shouldBeVisible(null)) {
+            // The stack is not visible, so no activity in it should be displaying a starting
+            // window. Mark all activities below top and behind fullscreen.
+            aboveTop = false;
+            behindFullscreenActivity = true;
+        }
+
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (aboveTop) {
+                    if (r == topActivity) {
+                        aboveTop = false;
+                    }
+                    behindFullscreenActivity |= r.fullscreen;
+                    continue;
+                }
+
+                r.removeOrphanedStartingWindow(behindFullscreenActivity);
+                behindFullscreenActivity |= r.fullscreen;
+            }
+        }
+    }
+
+    /**
+     * Ensure that the top activity in the stack is resumed.
+     *
+     * @param prev The previously resumed activity, for when in the process
+     * of pausing; can be null to call from elsewhere.
+     * @param options Activity options.
+     *
+     * @return Returns true if something is being resumed, or false if
+     * nothing happened.
+     *
+     * NOTE: It is not safe to call this method directly as it can cause an activity in a
+     *       non-focused stack to be resumed.
+     *       Use {@link ActivityStackSupervisor#resumeFocusedStacksTopActivitiesLocked} to resume the
+     *       right activity for the current system state.
+     */
+    @GuardedBy("mService")
+    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
+        if (mStackSupervisor.inResumeTopActivity) {
+            // Don't even start recursing.
+            return false;
+        }
+
+        boolean result = false;
+        try {
+            // Protect against recursion.
+            mStackSupervisor.inResumeTopActivity = true;
+            result = resumeTopActivityInnerLocked(prev, options);
+
+            // When resuming the top activity, it may be necessary to pause the top activity (for
+            // example, returning to the lock screen. We suppress the normal pause logic in
+            // {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the
+            // end. We call the {@link ActivityStackSupervisor#checkReadyForSleepLocked} again here
+            // to ensure any necessary pause logic occurs. In the case where the Activity will be
+            // shown regardless of the lock screen, the call to
+            // {@link ActivityStackSupervisor#checkReadyForSleepLocked} is skipped.
+            final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
+            if (next == null || !next.canTurnScreenOn()) {
+                checkReadyForSleep();
+            }
+        } finally {
+            mStackSupervisor.inResumeTopActivity = false;
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns the currently resumed activity.
+     */
+    protected ActivityRecord getResumedActivity() {
+        return mResumedActivity;
+    }
+
+    private void setResumedActivity(ActivityRecord r, String reason) {
+        if (mResumedActivity == r) {
+            return;
+        }
+
+        if (DEBUG_STACK) Slog.d(TAG_STACK, "setResumedActivity stack:" + this + " + from: "
+                + mResumedActivity + " to:" + r + " reason:" + reason);
+        mResumedActivity = r;
+    }
+
+    @GuardedBy("mService")
+    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
+        if (!mService.isBooting() && !mService.isBooted()) {
+            // Not ready yet!
+            return false;
+        }
+
+        // Find the next top-most activity to resume in this stack that is not finishing and is
+        // focusable. If it is not focusable, we will fall into the case below to resume the
+        // top activity in the next focusable task.
+        final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
+
+        final boolean hasRunningActivity = next != null;
+
+        // TODO: Maybe this entire condition can get removed?
+        if (hasRunningActivity && !isAttached()) {
+            return false;
+        }
+
+        mStackSupervisor.cancelInitializingActivities();
+
+        // Remember how we'll process this pause/resume situation, and ensure
+        // that the state is reset however we wind up proceeding.
+        boolean userLeaving = mStackSupervisor.mUserLeaving;
+        mStackSupervisor.mUserLeaving = false;
+
+        if (!hasRunningActivity) {
+            // There are no activities left in the stack, let's look somewhere else.
+            return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities");
+        }
+
+        next.delayedResume = false;
+        final ActivityDisplay display = getDisplay();
+
+        // If the top activity is the resumed one, nothing to do.
+        if (mResumedActivity == next && next.isState(RESUMED)
+                && display.allResumedActivitiesComplete()) {
+            // Make sure we have executed any pending transitions, since there
+            // should be nothing left to do at this point.
+            executeAppTransition(options);
+            if (DEBUG_STATES) Slog.d(TAG_STATES,
+                    "resumeTopActivityLocked: Top activity resumed " + next);
+            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+            return false;
+        }
+
+        // If we are sleeping, and there is no resumed activity, and the top
+        // activity is paused, well that is the state we want.
+        if (shouldSleepOrShutDownActivities()
+                && mLastPausedActivity == next
+                && mStackSupervisor.allPausedActivitiesComplete()) {
+            // If the current top activity may be able to occlude keyguard but the occluded state
+            // has not been set, update visibility and check again if we should continue to resume.
+            boolean nothingToResume = true;
+            if (!mService.mShuttingDown && !mTopActivityOccludesKeyguard
+                    && next.canShowWhenLocked()) {
+                ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+                        !PRESERVE_WINDOWS);
+                nothingToResume = shouldSleepActivities();
+            }
+            if (nothingToResume) {
+                // Make sure we have executed any pending transitions, since there
+                // should be nothing left to do at this point.
+                executeAppTransition(options);
+                if (DEBUG_STATES) Slog.d(TAG_STATES,
+                        "resumeTopActivityLocked: Going to sleep and all paused");
+                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+                return false;
+            }
+        }
+
+        // Make sure that the user who owns this activity is started.  If not,
+        // we will just leave it as is because someone should be bringing
+        // another user's activities to the top of the stack.
+        if (!mService.mAmInternal.hasStartedUserState(next.userId)) {
+            Slog.w(TAG, "Skipping resume of top activity " + next
+                    + ": user " + next.userId + " is stopped");
+            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+            return false;
+        }
+
+        // The activity may be waiting for stop, but that is no longer
+        // appropriate for it.
+        mStackSupervisor.mStoppingActivities.remove(next);
+        mStackSupervisor.mGoingToSleepActivities.remove(next);
+        next.sleeping = false;
+        mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(next);
+
+        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
+
+        // If we are currently pausing an activity, then don't do anything until that is done.
+        if (!mStackSupervisor.allPausedActivitiesComplete()) {
+            if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
+                    "resumeTopActivityLocked: Skip resume: some activity pausing.");
+            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+            return false;
+        }
+
+        mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);
+
+        boolean lastResumedCanPip = false;
+        ActivityRecord lastResumed = null;
+        final ActivityStack lastFocusedStack = display.getLastFocusedStack();
+        if (lastFocusedStack != null && lastFocusedStack != this) {
+            // So, why aren't we using prev here??? See the param comment on the method. prev doesn't
+            // represent the last resumed activity. However, the last focus stack does if it isn't null.
+            lastResumed = lastFocusedStack.mResumedActivity;
+            if (userLeaving && inMultiWindowMode() && lastFocusedStack.shouldBeVisible(next)) {
+                // The user isn't leaving if this stack is the multi-window mode and the last
+                // focused stack should still be visible.
+                if(DEBUG_USER_LEAVING) Slog.i(TAG_USER_LEAVING, "Overriding userLeaving to false"
+                        + " next=" + next + " lastResumed=" + lastResumed);
+                userLeaving = false;
+            }
+            lastResumedCanPip = lastResumed != null && lastResumed.checkEnterPictureInPictureState(
+                    "resumeTopActivity", userLeaving /* beforeStopping */);
+        }
+        // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous activity
+        // to be paused, while at the same time resuming the new resume activity only if the
+        // previous activity can't go into Pip since we want to give Pip activities a chance to
+        // enter Pip before resuming the next activity.
+        final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0
+                && !lastResumedCanPip;
+
+        boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
+        if (mResumedActivity != null) {
+            if (DEBUG_STATES) Slog.d(TAG_STATES,
+                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
+            pausing |= startPausingLocked(userLeaving, false, next, false);
+        }
+        if (pausing && !resumeWhilePausing) {
+            if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG_STATES,
+                    "resumeTopActivityLocked: Skip resume: need to start pausing");
+            // At this point we want to put the upcoming activity's process
+            // at the top of the LRU list, since we know we will be needing it
+            // very soon and it would be a waste to let it get killed if it
+            // happens to be sitting towards the end.
+            if (next.attachedToProcess()) {
+                next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+                        true /* updateLru */, true /* activityChange */, false /* updateOomAdj */);
+            }
+            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+            if (lastResumed != null) {
+                lastResumed.setWillCloseOrEnterPip(true);
+            }
+            return true;
+        } else if (mResumedActivity == next && next.isState(RESUMED)
+                && display.allResumedActivitiesComplete()) {
+            // It is possible for the activity to be resumed when we paused back stacks above if the
+            // next activity doesn't have to wait for pause to complete.
+            // So, nothing else to-do except:
+            // Make sure we have executed any pending transitions, since there
+            // should be nothing left to do at this point.
+            executeAppTransition(options);
+            if (DEBUG_STATES) Slog.d(TAG_STATES,
+                    "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);
+            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+            return true;
+        }
+
+        // If the most recent activity was noHistory but was only stopped rather
+        // than stopped+finished because the device went to sleep, we need to make
+        // sure to finish it as we're making a new activity topmost.
+        if (shouldSleepActivities() && mLastNoHistoryActivity != null &&
+                !mLastNoHistoryActivity.finishing) {
+            if (DEBUG_STATES) Slog.d(TAG_STATES,
+                    "no-history finish of " + mLastNoHistoryActivity + " on new resume");
+            requestFinishActivityLocked(mLastNoHistoryActivity.appToken, Activity.RESULT_CANCELED,
+                    null, "resume-no-history", false);
+            mLastNoHistoryActivity = null;
+        }
+
+        if (prev != null && prev != next) {
+            if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(prev)
+                    && next != null && !next.nowVisible) {
+                mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev);
+                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+                        "Resuming top, waiting visible to hide: " + prev);
+            } else {
+                // The next activity is already visible, so hide the previous
+                // activity's windows right now so we can show the new one ASAP.
+                // We only do this if the previous is finishing, which should mean
+                // it is on top of the one being resumed so hiding it quickly
+                // is good.  Otherwise, we want to do the normal route of allowing
+                // the resumed activity to be shown so we can decide if the
+                // previous should actually be hidden depending on whether the
+                // new one is found to be full-screen or not.
+                if (prev.finishing) {
+                    prev.setVisibility(false);
+                    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+                            "Not waiting for visible to hide: " + prev + ", waitingVisible="
+                            + mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(prev)
+                            + ", nowVisible=" + next.nowVisible);
+                } else {
+                    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+                            "Previous already visible but still waiting to hide: " + prev
+                            + ", waitingVisible="
+                            + mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(prev)
+                            + ", nowVisible=" + next.nowVisible);
+                }
+            }
+        }
+
+        // Launching this app's activity, make sure the app is no longer
+        // considered stopped.
+        try {
+            AppGlobals.getPackageManager().setPackageStoppedState(
+                    next.packageName, false, next.userId); /* TODO: Verify if correct userid */
+        } catch (RemoteException e1) {
+        } catch (IllegalArgumentException e) {
+            Slog.w(TAG, "Failed trying to unstop package "
+                    + next.packageName + ": " + e);
+        }
+
+        // We are starting up the next activity, so tell the window manager
+        // that the previous one will be hidden soon.  This way it can know
+        // to ignore it when computing the desired screen orientation.
+        boolean anim = true;
+        final DisplayWindowController dwc = getDisplay().getWindowContainerController();
+        if (prev != null) {
+            if (prev.finishing) {
+                if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
+                        "Prepare close transition: prev=" + prev);
+                if (mStackSupervisor.mNoAnimActivities.contains(prev)) {
+                    anim = false;
+                    dwc.prepareAppTransition(TRANSIT_NONE, false);
+                } else {
+                    dwc.prepareAppTransition(
+                            prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE
+                                    : TRANSIT_TASK_CLOSE, false);
+                }
+                prev.setVisibility(false);
+            } else {
+                if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
+                        "Prepare open transition: prev=" + prev);
+                if (mStackSupervisor.mNoAnimActivities.contains(next)) {
+                    anim = false;
+                    dwc.prepareAppTransition(TRANSIT_NONE, false);
+                } else {
+                    dwc.prepareAppTransition(
+                            prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN
+                                    : next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND
+                                            : TRANSIT_TASK_OPEN, false);
+                }
+            }
+        } else {
+            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
+            if (mStackSupervisor.mNoAnimActivities.contains(next)) {
+                anim = false;
+                dwc.prepareAppTransition(TRANSIT_NONE, false);
+            } else {
+                dwc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false);
+            }
+        }
+
+        if (anim) {
+            next.applyOptionsLocked();
+        } else {
+            next.clearOptionsLocked();
+        }
+
+        mStackSupervisor.mNoAnimActivities.clear();
+
+        if (next.attachedToProcess()) {
+            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
+                    + " stopped=" + next.stopped + " visible=" + next.visible);
+
+            // If the previous activity is translucent, force a visibility update of
+            // the next activity, so that it's added to WM's opening app list, and
+            // transition animation can be set up properly.
+            // For example, pressing Home button with a translucent activity in focus.
+            // Launcher is already visible in this case. If we don't add it to opening
+            // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
+            // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
+            final boolean lastActivityTranslucent = lastFocusedStack != null
+                    && (lastFocusedStack.inMultiWindowMode()
+                    || (lastFocusedStack.mLastPausedActivity != null
+                    && !lastFocusedStack.mLastPausedActivity.fullscreen));
+
+            // This activity is now becoming visible.
+            if (!next.visible || next.stopped || lastActivityTranslucent) {
+                next.setVisibility(true);
+            }
+
+            // schedule launch ticks to collect information about slow apps.
+            next.startLaunchTickingLocked();
+
+            ActivityRecord lastResumedActivity =
+                    lastFocusedStack == null ? null : lastFocusedStack.mResumedActivity;
+            final ActivityState lastState = next.getState();
+
+            mService.updateCpuStats();
+
+            if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next
+                    + " (in existing)");
+
+            next.setState(RESUMED, "resumeTopActivityInnerLocked");
+
+            next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+                    true /* updateLru */, true /* activityChange */, true /* updateOomAdj */);
+            updateLRUListLocked(next);
+
+            // Have the window manager re-evaluate the orientation of
+            // the screen based on the new activity order.
+            boolean notUpdated = true;
+
+            if (isFocusedStackOnDisplay()) {
+                // We have special rotation behavior when here is some active activity that
+                // requests specific orientation or Keyguard is locked. Make sure all activity
+                // visibilities are set correctly as well as the transition is updated if needed
+                // to get the correct rotation behavior. Otherwise the following call to update
+                // the orientation may cause incorrect configurations delivered to client as a
+                // result of invisible window resize.
+                // TODO: Remove this once visibilities are set correctly immediately when
+                // starting an activity.
+                notUpdated = !mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
+                        true /* markFrozenIfConfigChanged */, false /* deferResume */);
+            }
+
+            if (notUpdated) {
+                // The configuration update wasn't able to keep the existing
+                // instance of the activity, and instead started a new one.
+                // We should be all done, but let's just make sure our activity
+                // is still at the top and schedule another run if something
+                // weird happened.
+                ActivityRecord nextNext = topRunningActivityLocked();
+                if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
+                        "Activity config changed during resume: " + next
+                                + ", new next: " + nextNext);
+                if (nextNext != next) {
+                    // Do over!
+                    mStackSupervisor.scheduleResumeTopActivities();
+                }
+                if (!next.visible || next.stopped) {
+                    next.setVisibility(true);
+                }
+                next.completeResumeLocked();
+                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+                return true;
+            }
+
+            try {
+                final ClientTransaction transaction =
+                        ClientTransaction.obtain(next.app.getThread(), next.appToken);
+                // Deliver all pending results.
+                ArrayList<ResultInfo> a = next.results;
+                if (a != null) {
+                    final int N = a.size();
+                    if (!next.finishing && N > 0) {
+                        if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
+                                "Delivering results to " + next + ": " + a);
+                        transaction.addCallback(ActivityResultItem.obtain(a));
+                    }
+                }
+
+                if (next.newIntents != null) {
+                    transaction.addCallback(NewIntentItem.obtain(next.newIntents,
+                            false /* andPause */));
+                }
+
+                // Well the app will no longer be stopped.
+                // Clear app token stopped state in window manager if needed.
+                next.notifyAppResumed(next.stopped);
+
+                EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
+                        System.identityHashCode(next), next.getTask().taskId,
+                        next.shortComponentName);
+
+                next.sleeping = false;
+                mService.getAppWarningsLocked().onResumeActivity(next);
+                next.app.setPendingUiCleanAndForceProcessStateUpTo(mService.mTopProcessState);
+                next.clearOptionsLocked();
+                transaction.setLifecycleStateRequest(
+                        ResumeActivityItem.obtain(next.app.getReportedProcState(),
+                                getDisplay().getWindowContainerController()
+                                        .isNextTransitionForward()));
+                mService.getLifecycleManager().scheduleTransaction(transaction);
+
+                if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
+                        + next);
+            } catch (Exception e) {
+                // Whoops, need to restart this activity!
+                if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to "
+                        + lastState + ": " + next);
+                next.setState(lastState, "resumeTopActivityInnerLocked");
+
+                // lastResumedActivity being non-null implies there is a lastStack present.
+                if (lastResumedActivity != null) {
+                    lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
+                }
+
+                Slog.i(TAG, "Restarting because process died: " + next);
+                if (!next.hasBeenLaunched) {
+                    next.hasBeenLaunched = true;
+                } else  if (SHOW_APP_STARTING_PREVIEW && lastFocusedStack != null
+                        && lastFocusedStack.isTopStackOnDisplay()) {
+                    next.showStartingWindow(null /* prev */, false /* newTask */,
+                            false /* taskSwitch */);
+                }
+                mStackSupervisor.startSpecificActivityLocked(next, true, false);
+                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+                return true;
+            }
+
+            // From this point on, if something goes wrong there is no way
+            // to recover the activity.
+            try {
+                next.completeResumeLocked();
+            } catch (Exception e) {
+                // If any exception gets thrown, toss away this
+                // activity and try the next one.
+                Slog.w(TAG, "Exception thrown during resume of " + next, e);
+                requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
+                        "resume-exception", true);
+                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+                return true;
+            }
+        } else {
+            // Whoops, need to restart this activity!
+            if (!next.hasBeenLaunched) {
+                next.hasBeenLaunched = true;
+            } else {
+                if (SHOW_APP_STARTING_PREVIEW) {
+                    next.showStartingWindow(null /* prev */, false /* newTask */,
+                            false /* taskSwich */);
+                }
+                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
+            }
+            if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Restarting " + next);
+            mStackSupervisor.startSpecificActivityLocked(next, true, true);
+        }
+
+        if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+        return true;
+    }
+
+    private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev,
+            ActivityOptions options, String reason) {
+        final ActivityStack nextFocusedStack = adjustFocusToNextFocusableStack(reason);
+        if (nextFocusedStack != null) {
+            // Try to move focus to the next visible stack with a running activity if this
+            // stack is not covering the entire screen or is on a secondary display (with no home
+            // stack).
+            return mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(nextFocusedStack, prev,
+                    null /* targetOptions */);
+        }
+
+        // Let's just start up the Launcher...
+        ActivityOptions.abort(options);
+        if (DEBUG_STATES) Slog.d(TAG_STATES,
+                "resumeTopActivityInNextFocusableStack: " + reason + ", go home");
+        if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+        return mStackSupervisor.resumeHomeActivity(prev, reason, mDisplayId);
+    }
+
+    /** Returns the position the input task should be placed in this stack. */
+    int getAdjustedPositionForTask(TaskRecord task, int suggestedPosition,
+            ActivityRecord starting) {
+
+        int maxPosition = mTaskHistory.size();
+        if ((starting != null && starting.okToShowLocked())
+                || (starting == null && task.okToShowLocked())) {
+            // If the task or starting activity can be shown, then whatever position is okay.
+            return Math.min(suggestedPosition, maxPosition);
+        }
+
+        // The task can't be shown, put non-current user tasks below current user tasks.
+        while (maxPosition > 0) {
+            final TaskRecord tmpTask = mTaskHistory.get(maxPosition - 1);
+            if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId)
+                    || tmpTask.topRunningActivityLocked() == null) {
+                break;
+            }
+            maxPosition--;
+        }
+
+        return  Math.min(suggestedPosition, maxPosition);
+    }
+
+    /**
+     * Used from {@link ActivityStack#positionTask(TaskRecord, int)}.
+     * @see ActivityTaskManagerService#positionTaskInStack(int, int, int).
+     */
+    private void insertTaskAtPosition(TaskRecord task, int position) {
+        if (position >= mTaskHistory.size()) {
+            insertTaskAtTop(task, null);
+            return;
+        } else if (position <= 0) {
+            insertTaskAtBottom(task);
+            return;
+        }
+        position = getAdjustedPositionForTask(task, position, null /* starting */);
+        mTaskHistory.remove(task);
+        mTaskHistory.add(position, task);
+        mWindowContainerController.positionChildAt(task.getWindowContainerController(), position);
+        updateTaskMovement(task, true);
+    }
+
+    private void insertTaskAtTop(TaskRecord task, ActivityRecord starting) {
+        // TODO: Better place to put all the code below...may be addTask...
+        mTaskHistory.remove(task);
+        // Now put task at top.
+        final int position = getAdjustedPositionForTask(task, mTaskHistory.size(), starting);
+        mTaskHistory.add(position, task);
+        updateTaskMovement(task, true);
+        positionChildWindowContainerAtTop(task);
+    }
+
+    private void insertTaskAtBottom(TaskRecord task) {
+        mTaskHistory.remove(task);
+        final int position = getAdjustedPositionForTask(task, 0, null);
+        mTaskHistory.add(position, task);
+        updateTaskMovement(task, true);
+        positionChildWindowContainerAtBottom(task);
+    }
+
+    void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
+            boolean newTask, boolean keepCurTransition, ActivityOptions options) {
+        TaskRecord rTask = r.getTask();
+        final int taskId = rTask.taskId;
+        // mLaunchTaskBehind tasks get placed at the back of the task stack.
+        if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
+            // Last activity in task had been removed or ActivityManagerService is reusing task.
+            // Insert or replace.
+            // Might not even be in.
+            insertTaskAtTop(rTask, r);
+        }
+        TaskRecord task = null;
+        if (!newTask) {
+            // If starting in an existing task, find where that is...
+            boolean startIt = true;
+            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+                task = mTaskHistory.get(taskNdx);
+                if (task.getTopActivity() == null) {
+                    // All activities in task are finishing.
+                    continue;
+                }
+                if (task == rTask) {
+                    // Here it is!  Now, if this is not yet visible to the
+                    // user, then just add it without starting; it will
+                    // get started when the user navigates back to it.
+                    if (!startIt) {
+                        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
+                                + task, new RuntimeException("here").fillInStackTrace());
+                        r.createWindowContainer();
+                        ActivityOptions.abort(options);
+                        return;
+                    }
+                    break;
+                } else if (task.numFullscreen > 0) {
+                    startIt = false;
+                }
+            }
+        }
+
+        // Place a new activity at top of stack, so it is next to interact with the user.
+
+        // If we are not placing the new activity frontmost, we do not want to deliver the
+        // onUserLeaving callback to the actual frontmost activity
+        final TaskRecord activityTask = r.getTask();
+        if (task == activityTask && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
+            mStackSupervisor.mUserLeaving = false;
+            if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
+                    "startActivity() behind front, mUserLeaving=false");
+        }
+
+        task = activityTask;
+
+        // Slot the activity into the history stack and proceed
+        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
+                new RuntimeException("here").fillInStackTrace());
+        // TODO: Need to investigate if it is okay for the controller to already be created by the
+        // time we get to this point. I think it is, but need to double check.
+        // Use test in b/34179495 to trace the call path.
+        if (r.getWindowContainerController() == null) {
+            r.createWindowContainer();
+        }
+        task.setFrontOfTask();
+
+        if (!isHomeOrRecentsStack() || numActivities() > 0) {
+            final DisplayWindowController dwc = getDisplay().getWindowContainerController();
+            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
+                    "Prepare open transition: starting " + r);
+            if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+                dwc.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
+                mStackSupervisor.mNoAnimActivities.add(r);
+            } else {
+                int transit = TRANSIT_ACTIVITY_OPEN;
+                if (newTask) {
+                    if (r.mLaunchTaskBehind) {
+                        transit = TRANSIT_TASK_OPEN_BEHIND;
+                    } else {
+                        // If a new task is being launched, then mark the existing top activity as
+                        // supporting picture-in-picture while pausing only if the starting activity
+                        // would not be considered an overlay on top of the current activity
+                        // (eg. not fullscreen, or the assistant)
+                        if (canEnterPipOnTaskSwitch(focusedTopActivity,
+                                null /* toFrontTask */, r, options)) {
+                            focusedTopActivity.supportsEnterPipOnTaskSwitch = true;
+                        }
+                        transit = TRANSIT_TASK_OPEN;
+                    }
+                }
+                dwc.prepareAppTransition(transit, keepCurTransition);
+                mStackSupervisor.mNoAnimActivities.remove(r);
+            }
+            boolean doShow = true;
+            if (newTask) {
+                // Even though this activity is starting fresh, we still need
+                // to reset it to make sure we apply affinities to move any
+                // existing activities from other tasks in to it.
+                // If the caller has requested that the target task be
+                // reset, then do so.
+                if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+                    resetTaskIfNeededLocked(r, r);
+                    doShow = topRunningNonDelayedActivityLocked(null) == r;
+                }
+            } else if (options != null && options.getAnimationType()
+                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
+                doShow = false;
+            }
+            if (r.mLaunchTaskBehind) {
+                // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
+                // tell WindowManager that r is visible even though it is at the back of the stack.
+                r.setVisibility(true);
+                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
+                // Figure out if we are transitioning from another activity that is
+                // "has the same starting icon" as the next one.  This allows the
+                // window manager to keep the previous window it had previously
+                // created, if it still had one.
+                TaskRecord prevTask = r.getTask();
+                ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked();
+                if (prev != null) {
+                    // We don't want to reuse the previous starting preview if:
+                    // (1) The current activity is in a different task.
+                    if (prev.getTask() != prevTask) {
+                        prev = null;
+                    }
+                    // (2) The current activity is already displayed.
+                    else if (prev.nowVisible) {
+                        prev = null;
+                    }
+                }
+                r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));
+            }
+        } else {
+            // If this is the first activity, don't do any fancy animations,
+            // because there is nothing for it to animate on top of.
+            ActivityOptions.abort(options);
+        }
+    }
+
+    /**
+     * @return Whether the switch to another task can trigger the currently running activity to
+     * enter PiP while it is pausing (if supported). Only one of {@param toFrontTask} or
+     * {@param toFrontActivity} should be set.
+     */
+    private boolean canEnterPipOnTaskSwitch(ActivityRecord pipCandidate,
+            TaskRecord toFrontTask, ActivityRecord toFrontActivity, ActivityOptions opts) {
+        if (opts != null && opts.disallowEnterPictureInPictureWhileLaunching()) {
+            // Ensure the caller has requested not to trigger auto-enter PiP
+            return false;
+        }
+        if (pipCandidate == null || pipCandidate.inPinnedWindowingMode()) {
+            // Ensure that we do not trigger entering PiP an activity on the pinned stack
+            return false;
+        }
+        final ActivityStack targetStack = toFrontTask != null
+                ? toFrontTask.getStack() : toFrontActivity.getStack();
+        if (targetStack != null && targetStack.isActivityTypeAssistant()) {
+            // Ensure the task/activity being brought forward is not the assistant
+            return false;
+        }
+        return true;
+    }
+
+    private boolean isTaskSwitch(ActivityRecord r,
+            ActivityRecord topFocusedActivity) {
+        return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask();
+    }
+
+    /**
+     * Perform a reset of the given task, if needed as part of launching it.
+     * Returns the new HistoryRecord at the top of the task.
+     */
+    /**
+     * Helper method for #resetTaskIfNeededLocked.
+     * We are inside of the task being reset...  we'll either finish this activity, push it out
+     * for another task, or leave it as-is.
+     * @param task The task containing the Activity (taskTop) that might be reset.
+     * @param forceReset
+     * @return An ActivityOptions that needs to be processed.
+     */
+    private ActivityOptions resetTargetTaskIfNeededLocked(TaskRecord task, boolean forceReset) {
+        ActivityOptions topOptions = null;
+
+        int replyChainEnd = -1;
+        boolean canMoveOptions = true;
+
+        // We only do this for activities that are not the root of the task (since if we finish
+        // the root, we may no longer have the task!).
+        final ArrayList<ActivityRecord> activities = task.mActivities;
+        final int numActivities = activities.size();
+        final int rootActivityNdx = task.findEffectiveRootIndex();
+        for (int i = numActivities - 1; i > rootActivityNdx; --i ) {
+            ActivityRecord target = activities.get(i);
+            if (target.frontOfTask)
+                break;
+
+            final int flags = target.info.flags;
+            final boolean finishOnTaskLaunch =
+                    (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
+            final boolean allowTaskReparenting =
+                    (flags & ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
+            final boolean clearWhenTaskReset =
+                    (target.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0;
+
+            if (!finishOnTaskLaunch
+                    && !clearWhenTaskReset
+                    && target.resultTo != null) {
+                // If this activity is sending a reply to a previous
+                // activity, we can't do anything with it now until
+                // we reach the start of the reply chain.
+                // XXX note that we are assuming the result is always
+                // to the previous activity, which is almost always
+                // the case but we really shouldn't count on.
+                if (replyChainEnd < 0) {
+                    replyChainEnd = i;
+                }
+            } else if (!finishOnTaskLaunch
+                    && !clearWhenTaskReset
+                    && allowTaskReparenting
+                    && target.taskAffinity != null
+                    && !target.taskAffinity.equals(task.affinity)) {
+                // If this activity has an affinity for another
+                // task, then we need to move it out of here.  We will
+                // move it as far out of the way as possible, to the
+                // bottom of the activity stack.  This also keeps it
+                // correctly ordered with any activities we previously
+                // moved.
+                final TaskRecord targetTask;
+                final ActivityRecord bottom =
+                        !mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ?
+                                mTaskHistory.get(0).mActivities.get(0) : null;
+                if (bottom != null && target.taskAffinity != null
+                        && target.taskAffinity.equals(bottom.getTask().affinity)) {
+                    // If the activity currently at the bottom has the
+                    // same task affinity as the one we are moving,
+                    // then merge it into the same task.
+                    targetTask = bottom.getTask();
+                    if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
+                            + " out to bottom task " + targetTask);
+                } else {
+                    targetTask = createTaskRecord(
+                            mStackSupervisor.getNextTaskIdForUserLocked(target.userId),
+                            target.info, null, null, null, false);
+                    targetTask.affinityIntent = target.intent;
+                    if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
+                            + " out to new task " + targetTask);
+                }
+
+                boolean noOptions = canMoveOptions;
+                final int start = replyChainEnd < 0 ? i : replyChainEnd;
+                for (int srcPos = start; srcPos >= i; --srcPos) {
+                    final ActivityRecord p = activities.get(srcPos);
+                    if (p.finishing) {
+                        continue;
+                    }
+
+                    canMoveOptions = false;
+                    if (noOptions && topOptions == null) {
+                        topOptions = p.takeOptionsLocked();
+                        if (topOptions != null) {
+                            noOptions = false;
+                        }
+                    }
+                    if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
+                            "Removing activity " + p + " from task=" + task + " adding to task="
+                            + targetTask + " Callers=" + Debug.getCallers(4));
+                    if (DEBUG_TASKS) Slog.v(TAG_TASKS,
+                            "Pushing next activity " + p + " out to target's task " + target);
+                    p.reparent(targetTask, 0 /* position - bottom */, "resetTargetTaskIfNeeded");
+                }
+
+                positionChildWindowContainerAtBottom(targetTask);
+                replyChainEnd = -1;
+            } else if (forceReset || finishOnTaskLaunch || clearWhenTaskReset) {
+                // If the activity should just be removed -- either
+                // because it asks for it, or the task should be
+                // cleared -- then finish it and anything that is
+                // part of its reply chain.
+                int end;
+                if (clearWhenTaskReset) {
+                    // In this case, we want to finish this activity
+                    // and everything above it, so be sneaky and pretend
+                    // like these are all in the reply chain.
+                    end = activities.size() - 1;
+                } else if (replyChainEnd < 0) {
+                    end = i;
+                } else {
+                    end = replyChainEnd;
+                }
+                boolean noOptions = canMoveOptions;
+                for (int srcPos = i; srcPos <= end; srcPos++) {
+                    ActivityRecord p = activities.get(srcPos);
+                    if (p.finishing) {
+                        continue;
+                    }
+                    canMoveOptions = false;
+                    if (noOptions && topOptions == null) {
+                        topOptions = p.takeOptionsLocked();
+                        if (topOptions != null) {
+                            noOptions = false;
+                        }
+                    }
+                    if (DEBUG_TASKS) Slog.w(TAG_TASKS,
+                            "resetTaskIntendedTask: calling finishActivity on " + p);
+                    if (finishActivityLocked(
+                            p, Activity.RESULT_CANCELED, null, "reset-task", false)) {
+                        end--;
+                        srcPos--;
+                    }
+                }
+                replyChainEnd = -1;
+            } else {
+                // If we were in the middle of a chain, well the
+                // activity that started it all doesn't want anything
+                // special, so leave it all as-is.
+                replyChainEnd = -1;
+            }
+        }
+
+        return topOptions;
+    }
+
+    /**
+     * Helper method for #resetTaskIfNeededLocked. Processes all of the activities in a given
+     * TaskRecord looking for an affinity with the task of resetTaskIfNeededLocked.taskTop.
+     * @param affinityTask The task we are looking for an affinity to.
+     * @param task Task that resetTaskIfNeededLocked.taskTop belongs to.
+     * @param topTaskIsHigher True if #task has already been processed by resetTaskIfNeededLocked.
+     * @param forceReset Flag passed in to resetTaskIfNeededLocked.
+     */
+    private int resetAffinityTaskIfNeededLocked(TaskRecord affinityTask, TaskRecord task,
+            boolean topTaskIsHigher, boolean forceReset, int taskInsertionPoint) {
+        int replyChainEnd = -1;
+        final int taskId = task.taskId;
+        final String taskAffinity = task.affinity;
+
+        final ArrayList<ActivityRecord> activities = affinityTask.mActivities;
+        final int numActivities = activities.size();
+        final int rootActivityNdx = affinityTask.findEffectiveRootIndex();
+
+        // Do not operate on or below the effective root Activity.
+        for (int i = numActivities - 1; i > rootActivityNdx; --i) {
+            ActivityRecord target = activities.get(i);
+            if (target.frontOfTask)
+                break;
+
+            final int flags = target.info.flags;
+            boolean finishOnTaskLaunch = (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
+            boolean allowTaskReparenting = (flags & ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
+
+            if (target.resultTo != null) {
+                // If this activity is sending a reply to a previous
+                // activity, we can't do anything with it now until
+                // we reach the start of the reply chain.
+                // XXX note that we are assuming the result is always
+                // to the previous activity, which is almost always
+                // the case but we really shouldn't count on.
+                if (replyChainEnd < 0) {
+                    replyChainEnd = i;
+                }
+            } else if (topTaskIsHigher
+                    && allowTaskReparenting
+                    && taskAffinity != null
+                    && taskAffinity.equals(target.taskAffinity)) {
+                // This activity has an affinity for our task. Either remove it if we are
+                // clearing or move it over to our task.  Note that
+                // we currently punt on the case where we are resetting a
+                // task that is not at the top but who has activities above
+                // with an affinity to it...  this is really not a normal
+                // case, and we will need to later pull that task to the front
+                // and usually at that point we will do the reset and pick
+                // up those remaining activities.  (This only happens if
+                // someone starts an activity in a new task from an activity
+                // in a task that is not currently on top.)
+                if (forceReset || finishOnTaskLaunch) {
+                    final int start = replyChainEnd >= 0 ? replyChainEnd : i;
+                    if (DEBUG_TASKS) Slog.v(TAG_TASKS,
+                            "Finishing task at index " + start + " to " + i);
+                    for (int srcPos = start; srcPos >= i; --srcPos) {
+                        final ActivityRecord p = activities.get(srcPos);
+                        if (p.finishing) {
+                            continue;
+                        }
+                        finishActivityLocked(
+                                p, Activity.RESULT_CANCELED, null, "move-affinity", false);
+                    }
+                } else {
+                    if (taskInsertionPoint < 0) {
+                        taskInsertionPoint = task.mActivities.size();
+
+                    }
+
+                    final int start = replyChainEnd >= 0 ? replyChainEnd : i;
+                    if (DEBUG_TASKS) Slog.v(TAG_TASKS,
+                            "Reparenting from task=" + affinityTask + ":" + start + "-" + i
+                            + " to task=" + task + ":" + taskInsertionPoint);
+                    for (int srcPos = start; srcPos >= i; --srcPos) {
+                        final ActivityRecord p = activities.get(srcPos);
+                        p.reparent(task, taskInsertionPoint, "resetAffinityTaskIfNeededLocked");
+
+                        if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
+                                "Removing and adding activity " + p + " to stack at " + task
+                                + " callers=" + Debug.getCallers(3));
+                        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Pulling activity " + p
+                                + " from " + srcPos + " in to resetting task " + task);
+                    }
+                    positionChildWindowContainerAtTop(task);
+
+                    // Now we've moved it in to place...  but what if this is
+                    // a singleTop activity and we have put it on top of another
+                    // instance of the same activity?  Then we drop the instance
+                    // below so it remains singleTop.
+                    if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
+                        ArrayList<ActivityRecord> taskActivities = task.mActivities;
+                        int targetNdx = taskActivities.indexOf(target);
+                        if (targetNdx > 0) {
+                            ActivityRecord p = taskActivities.get(targetNdx - 1);
+                            if (p.intent.getComponent().equals(target.intent.getComponent())) {
+                                finishActivityLocked(p, Activity.RESULT_CANCELED, null, "replace",
+                                        false);
+                            }
+                        }
+                    }
+                }
+
+                replyChainEnd = -1;
+            }
+        }
+        return taskInsertionPoint;
+    }
+
+    final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
+            ActivityRecord newActivity) {
+        final boolean forceReset =
+                (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
+        final TaskRecord task = taskTop.getTask();
+
+        /** False until we evaluate the TaskRecord associated with taskTop. Switches to true
+         * for remaining tasks. Used for later tasks to reparent to task. */
+        boolean taskFound = false;
+
+        /** If ActivityOptions are moved out and need to be aborted or moved to taskTop. */
+        ActivityOptions topOptions = null;
+
+        // Preserve the location for reparenting in the new task.
+        int reparentInsertionPoint = -1;
+
+        for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
+            final TaskRecord targetTask = mTaskHistory.get(i);
+
+            if (targetTask == task) {
+                topOptions = resetTargetTaskIfNeededLocked(task, forceReset);
+                taskFound = true;
+            } else {
+                reparentInsertionPoint = resetAffinityTaskIfNeededLocked(targetTask, task,
+                        taskFound, forceReset, reparentInsertionPoint);
+            }
+        }
+
+        int taskNdx = mTaskHistory.indexOf(task);
+        if (taskNdx >= 0) {
+            do {
+                taskTop = mTaskHistory.get(taskNdx--).getTopActivity();
+            } while (taskTop == null && taskNdx >= 0);
+        }
+
+        if (topOptions != null) {
+            // If we got some ActivityOptions from an activity on top that
+            // was removed from the task, propagate them to the new real top.
+            if (taskTop != null) {
+                taskTop.updateOptionsLocked(topOptions);
+            } else {
+                topOptions.abort();
+            }
+        }
+
+        return taskTop;
+    }
+
+    void sendActivityResultLocked(int callingUid, ActivityRecord r,
+            String resultWho, int requestCode, int resultCode, Intent data) {
+
+        if (callingUid > 0) {
+            mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
+                    data, r.getUriPermissionsLocked(), r.userId);
+        }
+
+        if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
+                + " : who=" + resultWho + " req=" + requestCode
+                + " res=" + resultCode + " data=" + data);
+        if (mResumedActivity == r && r.attachedToProcess()) {
+            try {
+                ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+                list.add(new ResultInfo(resultWho, requestCode,
+                        resultCode, data));
+                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
+                        ActivityResultItem.obtain(list));
+                return;
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception thrown sending result to " + r, e);
+            }
+        }
+
+        r.addResultLocked(null, resultWho, requestCode, resultCode, data);
+    }
+
+    /** Returns true if the task is one of the task finishing on-top of the top running task. */
+    private boolean isATopFinishingTask(TaskRecord task) {
+        for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
+            final TaskRecord current = mTaskHistory.get(i);
+            final ActivityRecord r = current.topRunningActivityLocked();
+            if (r != null) {
+                // We got a top running activity, so there isn't a top finishing task...
+                return false;
+            }
+            if (current == task) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void adjustFocusedActivityStack(ActivityRecord r, String reason) {
+        if (!mStackSupervisor.isTopDisplayFocusedStack(this) ||
+                ((mResumedActivity != r) && (mResumedActivity != null))) {
+            return;
+        }
+
+        final ActivityRecord next = topRunningActivityLocked();
+        final String myReason = reason + " adjustFocus";
+
+        if (next == r) {
+            final ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
+            if (top != null) {
+                top.moveFocusableActivityToTop(myReason);
+            }
+            return;
+        }
+
+        if (next != null && isFocusable()) {
+            // Keep focus in stack if we have a top running activity and are focusable.
+            return;
+        }
+
+        // Task is not guaranteed to be non-null. For example, destroying the
+        // {@link ActivityRecord} will disassociate the task from the activity.
+        final TaskRecord task = r.getTask();
+
+        if (task == null) {
+            throw new IllegalStateException("activity no longer associated with task:" + r);
+        }
+
+        // Move focus to next focusable stack if possible.
+        final ActivityStack nextFocusableStack = adjustFocusToNextFocusableStack(myReason);
+        if (nextFocusableStack != null) {
+            final ActivityRecord top = nextFocusableStack.topRunningActivityLocked();
+            if (top != null && top == mStackSupervisor.getTopResumedActivity()) {
+                // TODO(b/111361570): Remove this and update focused app per-display in
+                // WindowManager every time an activity becomes resumed in
+                // ActivityTaskManagerService#setResumedActivityUncheckLocked().
+                mService.setResumedActivityUncheckLocked(top, reason);
+            }
+            return;
+        }
+
+        // Whatever...go home.
+        getDisplay().moveHomeActivityToTop(myReason);
+    }
+
+    /**
+     * Find next proper focusable stack and make it focused.
+     * @return The stack that now got the focus, {@code null} if none found.
+     */
+    ActivityStack adjustFocusToNextFocusableStack(String reason) {
+        return adjustFocusToNextFocusableStack(reason, false /* allowFocusSelf */);
+    }
+
+    /**
+     * Find next proper focusable stack and make it focused.
+     * @param allowFocusSelf Is the focus allowed to remain on the same stack.
+     * @return The stack that now got the focus, {@code null} if none found.
+     */
+    private ActivityStack adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) {
+        final ActivityStack stack =
+                mStackSupervisor.getNextFocusableStackLocked(this, !allowFocusSelf);
+        final String myReason = reason + " adjustFocusToNextFocusableStack";
+        if (stack == null) {
+            return null;
+        }
+
+        final ActivityRecord top = stack.topRunningActivityLocked();
+
+        if (stack.isActivityTypeHome() && (top == null || !top.visible)) {
+            // If we will be focusing on the home stack next and its current top activity isn't
+            // visible, then use the move the home stack task to top to make the activity visible.
+            stack.getDisplay().moveHomeActivityToTop(reason);
+            return stack;
+        }
+
+        stack.moveToFront(myReason);
+        return stack;
+    }
+
+    final void stopActivityLocked(ActivityRecord r) {
+        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + r);
+        if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
+                || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
+            if (!r.finishing) {
+                if (!shouldSleepActivities()) {
+                    if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r);
+                    if (requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
+                            "stop-no-history", false)) {
+                        // If {@link requestFinishActivityLocked} returns {@code true},
+                        // {@link adjustFocusedActivityStack} would have been already called.
+                        r.resumeKeyDispatchingLocked();
+                        return;
+                    }
+                } else {
+                    if (DEBUG_STATES) Slog.d(TAG_STATES, "Not finishing noHistory " + r
+                            + " on stop because we're just sleeping");
+                }
+            }
+        }
+
+        if (r.attachedToProcess()) {
+            adjustFocusedActivityStack(r, "stopActivity");
+            r.resumeKeyDispatchingLocked();
+            try {
+                r.stopped = false;
+                if (DEBUG_STATES) Slog.v(TAG_STATES,
+                        "Moving to STOPPING: " + r + " (stop requested)");
+                r.setState(STOPPING, "stopActivityLocked");
+                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                        "Stopping visible=" + r.visible + " for " + r);
+                if (!r.visible) {
+                    r.setVisible(false);
+                }
+                EventLogTags.writeAmStopActivity(
+                        r.userId, System.identityHashCode(r), r.shortComponentName);
+                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
+                        StopActivityItem.obtain(r.visible, r.configChangeFlags));
+                if (shouldSleepOrShutDownActivities()) {
+                    r.setSleeping(true);
+                }
+                Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
+                mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
+            } catch (Exception e) {
+                // Maybe just ignore exceptions here...  if the process
+                // has crashed, our death notification will clean things
+                // up.
+                Slog.w(TAG, "Exception thrown during pause", e);
+                // Just in case, assume it to be stopped.
+                r.stopped = true;
+                if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r);
+                r.setState(STOPPED, "stopActivityLocked");
+                if (r.deferRelaunchUntilPaused) {
+                    destroyActivityLocked(r, true, "stop-except");
+                }
+            }
+        }
+    }
+
+    /**
+     * @return Returns true if the activity is being finished, false if for
+     * some reason it is being left as-is.
+     */
+    final boolean requestFinishActivityLocked(IBinder token, int resultCode,
+            Intent resultData, String reason, boolean oomAdj) {
+        ActivityRecord r = isInStackLocked(token);
+        if (DEBUG_RESULTS || DEBUG_STATES) Slog.v(TAG_STATES,
+                "Finishing activity token=" + token + " r="
+                + ", result=" + resultCode + ", data=" + resultData
+                + ", reason=" + reason);
+        if (r == null) {
+            return false;
+        }
+
+        finishActivityLocked(r, resultCode, resultData, reason, oomAdj);
+        return true;
+    }
+
+    final void finishSubActivityLocked(ActivityRecord self, String resultWho, int requestCode) {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                ActivityRecord r = activities.get(activityNdx);
+                if (r.resultTo == self && r.requestCode == requestCode) {
+                    if ((r.resultWho == null && resultWho == null) ||
+                        (r.resultWho != null && r.resultWho.equals(resultWho))) {
+                        finishActivityLocked(r, Activity.RESULT_CANCELED, null, "request-sub",
+                                false);
+                    }
+                }
+            }
+        }
+        mService.updateOomAdj();
+    }
+
+    /**
+     * Finish the topmost activity that belongs to the crashed app. We may also finish the activity
+     * that requested launch of the crashed one to prevent launch-crash loop.
+     * @param app The app that crashed.
+     * @param reason Reason to perform this action.
+     * @return The task that was finished in this stack, {@code null} if top running activity does
+     *         not belong to the crashed app.
+     */
+    final TaskRecord finishTopCrashedActivityLocked(WindowProcessController app, String reason) {
+        ActivityRecord r = topRunningActivityLocked();
+        TaskRecord finishedTask = null;
+        if (r == null || r.app != app) {
+            return null;
+        }
+        Slog.w(TAG, "  Force finishing activity "
+                + r.intent.getComponent().flattenToShortString());
+        finishedTask = r.getTask();
+        int taskNdx = mTaskHistory.indexOf(finishedTask);
+        final TaskRecord task = finishedTask;
+        int activityNdx = task.mActivities.indexOf(r);
+        getDisplay().getWindowContainerController().prepareAppTransition(
+                TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
+        finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
+        finishedTask = task;
+        // Also terminate any activities below it that aren't yet
+        // stopped, to avoid a situation where one will get
+        // re-start our crashing activity once it gets resumed again.
+        --activityNdx;
+        if (activityNdx < 0) {
+            do {
+                --taskNdx;
+                if (taskNdx < 0) {
+                    break;
+                }
+                activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
+            } while (activityNdx < 0);
+        }
+        if (activityNdx >= 0) {
+            r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
+            if (r.isState(RESUMED, PAUSING, PAUSED)) {
+                if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
+                    Slog.w(TAG, "  Force finishing activity "
+                            + r.intent.getComponent().flattenToShortString());
+                    finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
+                }
+            }
+        }
+        return finishedTask;
+    }
+
+    final void finishVoiceTask(IVoiceInteractionSession session) {
+        IBinder sessionBinder = session.asBinder();
+        boolean didOne = false;
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            TaskRecord tr = mTaskHistory.get(taskNdx);
+            if (tr.voiceSession != null && tr.voiceSession.asBinder() == sessionBinder) {
+                for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+                    ActivityRecord r = tr.mActivities.get(activityNdx);
+                    if (!r.finishing) {
+                        finishActivityLocked(r, Activity.RESULT_CANCELED, null, "finish-voice",
+                                false);
+                        didOne = true;
+                    }
+                }
+            } else {
+                // Check if any of the activities are using voice
+                for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+                    ActivityRecord r = tr.mActivities.get(activityNdx);
+                    if (r.voiceSession != null && r.voiceSession.asBinder() == sessionBinder) {
+                        // Inform of cancellation
+                        r.clearVoiceSessionLocked();
+                        try {
+                            r.app.getThread().scheduleLocalVoiceInteractionStarted(
+                                    r.appToken, null);
+                        } catch (RemoteException re) {
+                            // Ok
+                        }
+                        mService.finishRunningVoiceLocked();
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (didOne) {
+            mService.updateOomAdj();
+        }
+    }
+
+    final boolean finishActivityAffinityLocked(ActivityRecord r) {
+        ArrayList<ActivityRecord> activities = r.getTask().mActivities;
+        for (int index = activities.indexOf(r); index >= 0; --index) {
+            ActivityRecord cur = activities.get(index);
+            if (!Objects.equals(cur.taskAffinity, r.taskAffinity)) {
+                break;
+            }
+            finishActivityLocked(cur, Activity.RESULT_CANCELED, null, "request-affinity", true);
+        }
+        return true;
+    }
+
+    private void finishActivityResultsLocked(ActivityRecord r, int resultCode, Intent resultData) {
+        // send the result
+        ActivityRecord resultTo = r.resultTo;
+        if (resultTo != null) {
+            if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Adding result to " + resultTo
+                    + " who=" + r.resultWho + " req=" + r.requestCode
+                    + " res=" + resultCode + " data=" + resultData);
+            if (resultTo.userId != r.userId) {
+                if (resultData != null) {
+                    resultData.prepareToLeaveUser(r.userId);
+                }
+            }
+            if (r.info.applicationInfo.uid > 0) {
+                mService.mUgmInternal.grantUriPermissionFromIntent(r.info.applicationInfo.uid,
+                        resultTo.packageName, resultData,
+                        resultTo.getUriPermissionsLocked(), resultTo.userId);
+            }
+            resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData);
+            r.resultTo = null;
+        }
+        else if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "No result destination from " + r);
+
+        // Make sure this HistoryRecord is not holding on to other resources,
+        // because clients have remote IPC references to this object so we
+        // can't assume that will go away and want to avoid circular IPC refs.
+        r.results = null;
+        r.pendingResults = null;
+        r.newIntents = null;
+        r.icicle = null;
+    }
+
+    /**
+     * See {@link #finishActivityLocked(ActivityRecord, int, Intent, String, boolean, boolean)}
+     */
+    final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
+            String reason, boolean oomAdj) {
+        return finishActivityLocked(r, resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY);
+    }
+
+    /**
+     * @return Returns true if this activity has been removed from the history
+     * list, or false if it is still in the list and will be removed later.
+     */
+    final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
+            String reason, boolean oomAdj, boolean pauseImmediately) {
+        if (r.finishing) {
+            Slog.w(TAG, "Duplicate finish request for " + r);
+            return false;
+        }
+
+        mWindowManager.deferSurfaceLayout();
+        try {
+            r.makeFinishingLocked();
+            final TaskRecord task = r.getTask();
+            EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
+                    r.userId, System.identityHashCode(r),
+                    task.taskId, r.shortComponentName, reason);
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            final int index = activities.indexOf(r);
+            if (index < (activities.size() - 1)) {
+                task.setFrontOfTask();
+                if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+                    // If the caller asked that this activity (and all above it)
+                    // be cleared when the task is reset, don't lose that information,
+                    // but propagate it up to the next activity.
+                    ActivityRecord next = activities.get(index+1);
+                    next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+                }
+            }
+
+            r.pauseKeyDispatchingLocked();
+
+            adjustFocusedActivityStack(r, "finishActivity");
+
+            finishActivityResultsLocked(r, resultCode, resultData);
+
+            final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
+            final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
+            if (mResumedActivity == r) {
+                if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
+                        "Prepare close transition: finishing " + r);
+                if (endTask) {
+                    mService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
+                            task.taskId);
+                }
+                getDisplay().getWindowContainerController().prepareAppTransition(transit, false);
+
+                // Tell window manager to prepare for this one to be removed.
+                r.setVisibility(false);
+
+                if (mPausingActivity == null) {
+                    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish needs to pause: " + r);
+                    if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
+                            "finish() => pause with userLeaving=false");
+                    startPausingLocked(false, false, null, pauseImmediately);
+                }
+
+                if (endTask) {
+                    mService.getLockTaskController().clearLockedTask(task);
+                }
+            } else if (!r.isState(PAUSING)) {
+                // If the activity is PAUSING, we will complete the finish once
+                // it is done pausing; else we can just directly finish it here.
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + r);
+                if (r.visible) {
+                    prepareActivityHideTransitionAnimation(r, transit);
+                }
+
+                final int finishMode = (r.visible || r.nowVisible) ? FINISH_AFTER_VISIBLE
+                        : FINISH_AFTER_PAUSE;
+                final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj,
+                        "finishActivityLocked") == null;
+
+                // The following code is an optimization. When the last non-task overlay activity
+                // is removed from the task, we remove the entire task from the stack. However,
+                // since that is done after the scheduled destroy callback from the activity, that
+                // call to change the visibility of the task overlay activities would be out of
+                // sync with the activitiy visibility being set for this finishing activity above.
+                // In this case, we can set the visibility of all the task overlay activities when
+                // we detect the last one is finishing to keep them in sync.
+                if (task.onlyHasTaskOverlayActivities(true /* excludeFinishing */)) {
+                    for (ActivityRecord taskOverlay : task.mActivities) {
+                        if (!taskOverlay.mTaskOverlay) {
+                            continue;
+                        }
+                        prepareActivityHideTransitionAnimation(taskOverlay, transit);
+                    }
+                }
+                return removedActivity;
+            } else {
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + r);
+            }
+
+            return false;
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+        }
+    }
+
+    private void prepareActivityHideTransitionAnimation(ActivityRecord r, int transit) {
+        final DisplayWindowController dwc = getDisplay().getWindowContainerController();
+        dwc.prepareAppTransition(transit, false);
+        r.setVisibility(false);
+        dwc.executeAppTransition();
+        if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) {
+            mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(r);
+        }
+    }
+
+    static final int FINISH_IMMEDIATELY = 0;
+    static final int FINISH_AFTER_PAUSE = 1;
+    static final int FINISH_AFTER_VISIBLE = 2;
+
+    final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj,
+            String reason) {
+        // First things first: if this activity is currently visible,
+        // and the resumed activity is not yet visible, then hold off on
+        // finishing until the resumed one becomes visible.
+
+        // The activity that we are finishing may be over the lock screen. In this case, we do not
+        // want to consider activities that cannot be shown on the lock screen as running and should
+        // proceed with finishing the activity if there is no valid next top running activity.
+        final ActivityDisplay display = getDisplay();
+        final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */);
+
+        if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
+                && next != null && !next.nowVisible) {
+            if (!mStackSupervisor.mStoppingActivities.contains(r)) {
+                addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */);
+            }
+            if (DEBUG_STATES) Slog.v(TAG_STATES,
+                    "Moving to STOPPING: "+ r + " (finish requested)");
+            r.setState(STOPPING, "finishCurrentActivityLocked");
+            if (oomAdj) {
+                mService.updateOomAdj();
+            }
+            return r;
+        }
+
+        // make sure the record is cleaned out of other places.
+        mStackSupervisor.mStoppingActivities.remove(r);
+        mStackSupervisor.mGoingToSleepActivities.remove(r);
+        mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(r);
+        final ActivityState prevState = r.getState();
+        if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + r);
+
+        r.setState(FINISHING, "finishCurrentActivityLocked");
+        final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE
+                && prevState == PAUSED && (r.getStack() != display.getFocusedStack()
+                        || (next == null && display.topRunningActivity() == null));
+
+        if (mode == FINISH_IMMEDIATELY
+                || (prevState == PAUSED
+                    && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
+                || finishingInNonFocusedStackOrNoRunning
+                || prevState == STOPPING
+                || prevState == STOPPED
+                || prevState == ActivityState.INITIALIZING) {
+            r.makeFinishingLocked();
+            boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
+
+            if (finishingInNonFocusedStackOrNoRunning) {
+                // Finishing activity that was in paused state and it was in not currently focused
+                // stack, need to make something visible in its place. Also if the display does not
+                // have running activity, the configuration may need to be updated for restoring
+                // original orientation of the display.
+                mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
+                        false /* markFrozenIfConfigChanged */, true /* deferResume */);
+            }
+            if (activityRemoved) {
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            }
+            if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
+                    "destroyActivityLocked: finishCurrentActivityLocked r=" + r +
+                    " destroy returned removed=" + activityRemoved);
+            return activityRemoved ? null : r;
+        }
+
+        // Need to go through the full pause cycle to get this
+        // activity into the stopped state and then finish it.
+        if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r);
+        mStackSupervisor.mFinishingActivities.add(r);
+        r.resumeKeyDispatchingLocked();
+        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        return r;
+    }
+
+    void finishAllActivitiesLocked(boolean immediately) {
+        boolean noActivitiesInStack = true;
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                noActivitiesInStack = false;
+                if (r.finishing && !immediately) {
+                    continue;
+                }
+                Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r + " immediately");
+                finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false,
+                        "finishAllActivitiesLocked");
+            }
+        }
+        if (noActivitiesInStack) {
+            remove();
+        }
+    }
+
+    /** @return true if the stack behind this one is a standard activity type. */
+    boolean inFrontOfStandardStack() {
+        final ActivityDisplay display = getDisplay();
+        if (display == null) {
+            return false;
+        }
+        final int index = display.getIndexOf(this);
+        if (index == 0) {
+            return false;
+        }
+        final ActivityStack stackBehind = display.getChildAt(index - 1);
+        return stackBehind.isActivityTypeStandard();
+    }
+
+    boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) {
+        // Basic case: for simple app-centric recents, we need to recreate
+        // the task if the affinity has changed.
+        if (srec == null || srec.getTask().affinity == null ||
+                !srec.getTask().affinity.equals(destAffinity)) {
+            return true;
+        }
+        // Document-centric case: an app may be split in to multiple documents;
+        // they need to re-create their task if this current activity is the root
+        // of a document, unless simply finishing it will return them to the the
+        // correct app behind.
+        final TaskRecord task = srec.getTask();
+        if (srec.frontOfTask && task.getBaseIntent() != null && task.getBaseIntent().isDocument()) {
+            // Okay, this activity is at the root of its task.  What to do, what to do...
+            if (!inFrontOfStandardStack()) {
+                // Finishing won't return to an application, so we need to recreate.
+                return true;
+            }
+            // We now need to get the task below it to determine what to do.
+            int taskIdx = mTaskHistory.indexOf(task);
+            if (taskIdx <= 0) {
+                Slog.w(TAG, "shouldUpRecreateTask: task not in history for " + srec);
+                return false;
+            }
+            final TaskRecord prevTask = mTaskHistory.get(taskIdx);
+            if (!task.affinity.equals(prevTask.affinity)) {
+                // These are different apps, so need to recreate.
+                return true;
+            }
+        }
+        return false;
+    }
+
+    final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode,
+            Intent resultData) {
+        final TaskRecord task = srec.getTask();
+        final ArrayList<ActivityRecord> activities = task.mActivities;
+        final int start = activities.indexOf(srec);
+        if (!mTaskHistory.contains(task) || (start < 0)) {
+            return false;
+        }
+        int finishTo = start - 1;
+        ActivityRecord parent = finishTo < 0 ? null : activities.get(finishTo);
+        boolean foundParentInTask = false;
+        final ComponentName dest = destIntent.getComponent();
+        if (start > 0 && dest != null) {
+            for (int i = finishTo; i >= 0; i--) {
+                ActivityRecord r = activities.get(i);
+                if (r.info.packageName.equals(dest.getPackageName()) &&
+                        r.info.name.equals(dest.getClassName())) {
+                    finishTo = i;
+                    parent = r;
+                    foundParentInTask = true;
+                    break;
+                }
+            }
+        }
+
+        // TODO: There is a dup. of this block of code in ActivityTaskManagerService.finishActivity
+        // We should consolidate.
+        IActivityController controller = mService.mController;
+        if (controller != null) {
+            ActivityRecord next = topRunningActivityLocked(srec.appToken, 0);
+            if (next != null) {
+                // ask watcher if this is allowed
+                boolean resumeOK = true;
+                try {
+                    resumeOK = controller.activityResuming(next.packageName);
+                } catch (RemoteException e) {
+                    mService.mController = null;
+                    Watchdog.getInstance().setActivityController(null);
+                }
+
+                if (!resumeOK) {
+                    return false;
+                }
+            }
+        }
+        final long origId = Binder.clearCallingIdentity();
+        for (int i = start; i > finishTo; i--) {
+            ActivityRecord r = activities.get(i);
+            requestFinishActivityLocked(r.appToken, resultCode, resultData, "navigate-up", true);
+            // Only return the supplied result for the first activity finished
+            resultCode = Activity.RESULT_CANCELED;
+            resultData = null;
+        }
+
+        if (parent != null && foundParentInTask) {
+            final int parentLaunchMode = parent.info.launchMode;
+            final int destIntentFlags = destIntent.getFlags();
+            if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
+                    parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
+                    parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
+                    (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+                parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent,
+                        srec.packageName);
+            } else {
+                try {
+                    ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
+                            destIntent.getComponent(), ActivityManagerService.STOCK_PM_FLAGS,
+                            srec.userId);
+                    // TODO(b/64750076): Check if calling pid should really be -1.
+                    final int res = mService.getActivityStartController()
+                            .obtainStarter(destIntent, "navigateUpTo")
+                            .setCaller(srec.app.getThread())
+                            .setActivityInfo(aInfo)
+                            .setResultTo(parent.appToken)
+                            .setCallingPid(-1)
+                            .setCallingUid(parent.launchedFromUid)
+                            .setCallingPackage(parent.launchedFromPackage)
+                            .setRealCallingPid(-1)
+                            .setRealCallingUid(parent.launchedFromUid)
+                            .setComponentSpecified(true)
+                            .execute();
+                    foundParentInTask = res == ActivityManager.START_SUCCESS;
+                } catch (RemoteException e) {
+                    foundParentInTask = false;
+                }
+                requestFinishActivityLocked(parent.appToken, resultCode,
+                        resultData, "navigate-top", true);
+            }
+        }
+        Binder.restoreCallingIdentity(origId);
+        return foundParentInTask;
+    }
+
+    /**
+     * Remove any state associated with the {@link ActivityRecord}. This should be called whenever
+     * an activity moves away from the stack.
+     */
+    void onActivityRemovedFromStack(ActivityRecord r) {
+        removeTimeoutsForActivityLocked(r);
+
+        if (mResumedActivity != null && mResumedActivity == r) {
+            setResumedActivity(null, "onActivityRemovedFromStack");
+        }
+        if (mPausingActivity != null && mPausingActivity == r) {
+            mPausingActivity = null;
+        }
+    }
+
+    void onActivityAddedToStack(ActivityRecord r) {
+        if(r.getState() == RESUMED) {
+            setResumedActivity(r, "onActivityAddedToStack");
+        }
+    }
+
+    /**
+     * Perform the common clean-up of an activity record.  This is called both
+     * as part of destroyActivityLocked() (when destroying the client-side
+     * representation) and cleaning things up as a result of its hosting
+     * processing going away, in which case there is no remaining client-side
+     * state to destroy so only the cleanup here is needed.
+     *
+     * Note: Call before #removeActivityFromHistoryLocked.
+     */
+    private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean setState) {
+        onActivityRemovedFromStack(r);
+
+        r.deferRelaunchUntilPaused = false;
+        r.frozenBeforeDestroy = false;
+
+        if (setState) {
+            if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (cleaning up)");
+            r.setState(DESTROYED, "cleanupActivityLocked");
+            if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + r);
+            r.app = null;
+        }
+
+        // Inform supervisor the activity has been removed.
+        mStackSupervisor.cleanupActivity(r);
+
+
+        // Remove any pending results.
+        if (r.finishing && r.pendingResults != null) {
+            for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
+                PendingIntentRecord rec = apr.get();
+                if (rec != null) {
+                    mService.mPendingIntentController.cancelIntentSender(rec, false);
+                }
+            }
+            r.pendingResults = null;
+        }
+
+        if (cleanServices) {
+            cleanUpActivityServicesLocked(r);
+        }
+
+        // Get rid of any pending idle timeouts.
+        removeTimeoutsForActivityLocked(r);
+        // Clean-up activities are no longer relaunching (e.g. app process died). Notify window
+        // manager so it can update its bookkeeping.
+        mWindowManager.notifyAppRelaunchesCleared(r.appToken);
+    }
+
+    private void removeTimeoutsForActivityLocked(ActivityRecord r) {
+        mStackSupervisor.removeTimeoutsForActivityLocked(r);
+        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+        mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
+        mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
+        r.finishLaunchTickingLocked();
+    }
+
+    private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) {
+        finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null);
+        r.makeFinishingLocked();
+        if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
+                "Removing activity " + r + " from stack callers=" + Debug.getCallers(5));
+
+        r.takeFromHistory();
+        removeTimeoutsForActivityLocked(r);
+        if (DEBUG_STATES) Slog.v(TAG_STATES,
+                "Moving to DESTROYED: " + r + " (removed from history)");
+        r.setState(DESTROYED, "removeActivityFromHistoryLocked");
+        if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r);
+        r.app = null;
+        r.removeWindowContainer();
+        final TaskRecord task = r.getTask();
+        final boolean lastActivity = task != null ? task.removeActivity(r) : false;
+        // If we are removing the last activity in the task, not including task overlay activities,
+        // then fall through into the block below to remove the entire task itself
+        final boolean onlyHasTaskOverlays = task != null
+                ? task.onlyHasTaskOverlayActivities(false /* excludingFinishing */) : false;
+
+        if (lastActivity || onlyHasTaskOverlays) {
+            if (DEBUG_STACK) {
+                Slog.i(TAG_STACK,
+                        "removeActivityFromHistoryLocked: last activity removed from " + this
+                                + " onlyHasTaskOverlays=" + onlyHasTaskOverlays);
+            }
+
+            // The following block can be executed multiple times if there is more than one overlay.
+            // {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup
+            // of the task by id and exiting early if not found.
+            if (onlyHasTaskOverlays) {
+                // When destroying a task, tell the supervisor to remove it so that any activity it
+                // has can be cleaned up correctly. This is currently the only place where we remove
+                // a task with the DESTROYING mode, so instead of passing the onlyHasTaskOverlays
+                // state into removeTask(), we just clear the task here before the other residual
+                // work.
+                // TODO: If the callers to removeTask() changes such that we have multiple places
+                //       where we are destroying the task, move this back into removeTask()
+                mStackSupervisor.removeTaskByIdLocked(task.taskId, false /* killProcess */,
+                        !REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY, reason);
+            }
+
+            // We must keep the task around until all activities are destroyed. The following
+            // statement will only execute once since overlays are also considered activities.
+            if (lastActivity) {
+                removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING);
+            }
+        }
+        cleanUpActivityServicesLocked(r);
+        r.removeUriPermissionsLocked();
+    }
+
+    /**
+     * Perform clean-up of service connections in an activity record.
+     */
+    private void cleanUpActivityServicesLocked(ActivityRecord r) {
+        if (r.mServiceConnectionsHolder == null) {
+            return;
+        }
+        // Throw away any services that have been bound by this activity.
+        r.mServiceConnectionsHolder.disconnectActivityFromServices();
+    }
+
+    final void scheduleDestroyActivities(WindowProcessController owner, String reason) {
+        Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
+        msg.obj = new ScheduleDestroyArgs(owner, reason);
+        mHandler.sendMessage(msg);
+    }
+
+    private void destroyActivitiesLocked(WindowProcessController owner, String reason) {
+        boolean lastIsOpaque = false;
+        boolean activityRemoved = false;
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (r.finishing) {
+                    continue;
+                }
+                if (r.fullscreen) {
+                    lastIsOpaque = true;
+                }
+                if (owner != null && r.app != owner) {
+                    continue;
+                }
+                if (!lastIsOpaque) {
+                    continue;
+                }
+                if (r.isDestroyable()) {
+                    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Destroying " + r
+                            + " in state " + r.getState()
+                            + " resumed=" + mResumedActivity
+                            + " pausing=" + mPausingActivity + " for reason " + reason);
+                    if (destroyActivityLocked(r, true, reason)) {
+                        activityRemoved = true;
+                    }
+                }
+            }
+        }
+        if (activityRemoved) {
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        }
+    }
+
+    final boolean safelyDestroyActivityLocked(ActivityRecord r, String reason) {
+        if (r.isDestroyable()) {
+            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+                    "Destroying " + r + " in state " + r.getState() + " resumed=" + mResumedActivity
+                    + " pausing=" + mPausingActivity + " for reason " + reason);
+            return destroyActivityLocked(r, true, reason);
+        }
+        return false;
+    }
+
+    final int releaseSomeActivitiesLocked(WindowProcessController app, ArraySet<TaskRecord> tasks,
+            String reason) {
+        // Iterate over tasks starting at the back (oldest) first.
+        if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + app);
+        int maxTasks = tasks.size() / 4;
+        if (maxTasks < 1) {
+            maxTasks = 1;
+        }
+        int numReleased = 0;
+        for (int taskNdx = 0; taskNdx < mTaskHistory.size() && maxTasks > 0; taskNdx++) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            if (!tasks.contains(task)) {
+                continue;
+            }
+            if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Looking for activities to release in " + task);
+            int curNum = 0;
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int actNdx = 0; actNdx < activities.size(); actNdx++) {
+                final ActivityRecord activity = activities.get(actNdx);
+                if (activity.app == app && activity.isDestroyable()) {
+                    if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + activity
+                            + " in state " + activity.getState() + " resumed=" + mResumedActivity
+                            + " pausing=" + mPausingActivity + " for reason " + reason);
+                    destroyActivityLocked(activity, true, reason);
+                    if (activities.get(actNdx) != activity) {
+                        // Was removed from list, back up so we don't miss the next one.
+                        actNdx--;
+                    }
+                    curNum++;
+                }
+            }
+            if (curNum > 0) {
+                numReleased += curNum;
+                maxTasks--;
+                if (mTaskHistory.get(taskNdx) != task) {
+                    // The entire task got removed, back up so we don't miss the next one.
+                    taskNdx--;
+                }
+            }
+        }
+        if (DEBUG_RELEASE) Slog.d(TAG_RELEASE,
+                "Done releasing: did " + numReleased + " activities");
+        return numReleased;
+    }
+
+    /**
+     * Destroy the current CLIENT SIDE instance of an activity.  This may be
+     * called both when actually finishing an activity, or when performing
+     * a configuration switch where we destroy the current client-side object
+     * but then create a new client-side object for this same HistoryRecord.
+     */
+    final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
+        if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
+                "Removing activity from " + reason + ": token=" + r
+                        + ", app=" + (r.hasProcess() ? r.app.mName : "(null)"));
+
+        if (r.isState(DESTROYING, DESTROYED)) {
+            if (DEBUG_STATES) Slog.v(TAG_STATES, "activity " + r + " already destroying."
+                    + "skipping request with reason:" + reason);
+            return false;
+        }
+
+        EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
+                r.userId, System.identityHashCode(r),
+                r.getTask().taskId, r.shortComponentName, reason);
+
+        boolean removedFromHistory = false;
+
+        cleanUpActivityLocked(r, false, false);
+
+        final boolean hadApp = r.hasProcess();
+
+        if (hadApp) {
+            if (removeFromApp) {
+                r.app.removeActivity(r);
+                if (!r.app.hasActivities()) {
+                    mService.clearHeavyWeightProcessIfEquals(r.app);
+                }
+                if (!r.app.hasActivities()) {
+                    // Update any services we are bound to that might care about whether
+                    // their client may have activities.
+                    // No longer have activities, so update LRU list and oom adj.
+                    r.app.updateProcessInfo(true, true, false, true);
+                }
+            }
+
+            boolean skipDestroy = false;
+
+            try {
+                if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
+                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
+                        DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
+            } catch (Exception e) {
+                // We can just ignore exceptions here...  if the process
+                // has crashed, our death notification will clean things
+                // up.
+                //Slog.w(TAG, "Exception thrown during finish", e);
+                if (r.finishing) {
+                    removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy");
+                    removedFromHistory = true;
+                    skipDestroy = true;
+                }
+            }
+
+            r.nowVisible = false;
+
+            // If the activity is finishing, we need to wait on removing it
+            // from the list to give it a chance to do its cleanup.  During
+            // that time it may make calls back with its token so we need to
+            // be able to find it on the list and so we don't want to remove
+            // it from the list yet.  Otherwise, we can just immediately put
+            // it in the destroyed state since we are not removing it from the
+            // list.
+            if (r.finishing && !skipDestroy) {
+                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYING: " + r
+                        + " (destroy requested)");
+                r.setState(DESTROYING,
+                        "destroyActivityLocked. finishing and not skipping destroy");
+                Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
+                mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
+            } else {
+                if (DEBUG_STATES) Slog.v(TAG_STATES,
+                        "Moving to DESTROYED: " + r + " (destroy skipped)");
+                r.setState(DESTROYED,
+                        "destroyActivityLocked. not finishing or skipping destroy");
+                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + r);
+                r.app = null;
+            }
+        } else {
+            // remove this record from the history.
+            if (r.finishing) {
+                removeActivityFromHistoryLocked(r, reason + " hadNoApp");
+                removedFromHistory = true;
+            } else {
+                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (no app)");
+                r.setState(DESTROYED, "destroyActivityLocked. not finishing and had no app");
+                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + r);
+                r.app = null;
+            }
+        }
+
+        r.configChangeFlags = 0;
+
+        if (!mLRUActivities.remove(r) && hadApp) {
+            Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
+        }
+
+        return removedFromHistory;
+    }
+
+    final void activityDestroyedLocked(IBinder token, String reason) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            activityDestroyedLocked(ActivityRecord.forTokenLocked(token), reason);
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    /**
+     * This method is to only be called from the client via binder when the activity is destroyed
+     * AND finished.
+     */
+    final void activityDestroyedLocked(ActivityRecord record, String reason) {
+        if (record != null) {
+            mHandler.removeMessages(DESTROY_TIMEOUT_MSG, record);
+        }
+
+        if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + record);
+
+        if (isInStackLocked(record) != null) {
+            if (record.isState(DESTROYING, DESTROYED)) {
+                cleanUpActivityLocked(record, true, false);
+                removeActivityFromHistoryLocked(record, reason);
+            }
+        }
+
+        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+    }
+
+    private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
+            WindowProcessController app, String listName) {
+        int i = list.size();
+        if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
+            "Removing app " + app + " from list " + listName + " with " + i + " entries");
+        while (i > 0) {
+            i--;
+            ActivityRecord r = list.get(i);
+            if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Record #" + i + " " + r);
+            if (r.app == app) {
+                if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "---> REMOVING this entry!");
+                list.remove(i);
+                removeTimeoutsForActivityLocked(r);
+            }
+        }
+    }
+
+    private boolean removeHistoryRecordsForAppLocked(WindowProcessController app) {
+        removeHistoryRecordsForAppLocked(mLRUActivities, app, "mLRUActivities");
+        removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
+                "mStoppingActivities");
+        removeHistoryRecordsForAppLocked(mStackSupervisor.mGoingToSleepActivities, app,
+                "mGoingToSleepActivities");
+        removeHistoryRecordsForAppLocked(mStackSupervisor.mActivitiesWaitingForVisibleActivity, app,
+                "mActivitiesWaitingForVisibleActivity");
+        removeHistoryRecordsForAppLocked(mStackSupervisor.mFinishingActivities, app,
+                "mFinishingActivities");
+
+        boolean hasVisibleActivities = false;
+
+        // Clean out the history list.
+        int i = numActivities();
+        if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
+                "Removing app " + app + " from history with " + i + " entries");
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            mTmpActivities.clear();
+            mTmpActivities.addAll(activities);
+
+            while (!mTmpActivities.isEmpty()) {
+                final int targetIndex = mTmpActivities.size() - 1;
+                final ActivityRecord r = mTmpActivities.remove(targetIndex);
+                if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
+                        "Record #" + targetIndex + " " + r + ": app=" + r.app);
+
+                if (r.app == app) {
+                    if (r.visible) {
+                        hasVisibleActivities = true;
+                    }
+                    final boolean remove;
+                    if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
+                            || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
+                            && r.launchCount < 3 && !r.finishing) {
+                        // If the process crashed during a resize, always try to relaunch it, unless
+                        // it has failed more than twice. Skip activities that's already finishing
+                        // cleanly by itself.
+                        remove = false;
+                    } else if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
+                        // Don't currently have state for the activity, or
+                        // it is finishing -- always remove it.
+                        remove = true;
+                    } else if (!r.visible && r.launchCount > 2 &&
+                            r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
+                        // We have launched this activity too many times since it was
+                        // able to run, so give up and remove it.
+                        // (Note if the activity is visible, we don't remove the record.
+                        // We leave the dead window on the screen but the process will
+                        // not be restarted unless user explicitly tap on it.)
+                        remove = true;
+                    } else {
+                        // The process may be gone, but the activity lives on!
+                        remove = false;
+                    }
+                    if (remove) {
+                        if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE,
+                                "Removing activity " + r + " from stack at " + i
+                                + ": haveState=" + r.haveState
+                                + " stateNotNeeded=" + r.stateNotNeeded
+                                + " finishing=" + r.finishing
+                                + " state=" + r.getState() + " callers=" + Debug.getCallers(5));
+                        if (!r.finishing) {
+                            Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
+                            EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
+                                    r.userId, System.identityHashCode(r),
+                                    r.getTask().taskId, r.shortComponentName,
+                                    "proc died without state saved");
+                            if (r.getState() == RESUMED) {
+                                mService.updateUsageStats(r, false);
+                            }
+                        }
+                    } else {
+                        // We have the current state for this activity, so
+                        // it can be restarted later when needed.
+                        if (DEBUG_ALL) Slog.v(TAG, "Keeping entry, setting app to null");
+                        if (DEBUG_APP) Slog.v(TAG_APP,
+                                "Clearing app during removeHistory for activity " + r);
+                        r.app = null;
+                        // Set nowVisible to previous visible state. If the app was visible while
+                        // it died, we leave the dead window on screen so it's basically visible.
+                        // This is needed when user later tap on the dead window, we need to stop
+                        // other apps when user transfers focus to the restarted activity.
+                        r.nowVisible = r.visible;
+                        if (!r.haveState) {
+                            if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE,
+                                    "App died, clearing saved state of " + r);
+                            r.icicle = null;
+                        }
+                    }
+                    cleanUpActivityLocked(r, true, true);
+                    if (remove) {
+                        removeActivityFromHistoryLocked(r, "appDied");
+                    }
+                }
+            }
+        }
+
+        return hasVisibleActivities;
+    }
+
+    private void updateTransitLocked(int transit, ActivityOptions options) {
+        if (options != null) {
+            ActivityRecord r = topRunningActivityLocked();
+            if (r != null && !r.isState(RESUMED)) {
+                r.updateOptionsLocked(options);
+            } else {
+                ActivityOptions.abort(options);
+            }
+        }
+        getDisplay().getWindowContainerController().prepareAppTransition(transit, false);
+    }
+
+    private void updateTaskMovement(TaskRecord task, boolean toFront) {
+        if (task.isPersistable) {
+            task.mLastTimeMoved = System.currentTimeMillis();
+            // Sign is used to keep tasks sorted when persisted. Tasks sent to the bottom most
+            // recently will be most negative, tasks sent to the bottom before that will be less
+            // negative. Similarly for recent tasks moved to the top which will be most positive.
+            if (!toFront) {
+                task.mLastTimeMoved *= -1;
+            }
+        }
+        mStackSupervisor.invalidateTaskLayers();
+    }
+
+    final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
+            AppTimeTracker timeTracker, String reason) {
+        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
+
+        final ActivityStack topStack = getDisplay().getTopStack();
+        final ActivityRecord topActivity = topStack != null ? topStack.getTopActivity() : null;
+        final int numTasks = mTaskHistory.size();
+        final int index = mTaskHistory.indexOf(tr);
+        if (numTasks == 0 || index < 0)  {
+            // nothing to do!
+            if (noAnimation) {
+                ActivityOptions.abort(options);
+            } else {
+                updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
+            }
+            return;
+        }
+
+        if (timeTracker != null) {
+            // The caller wants a time tracker associated with this task.
+            for (int i = tr.mActivities.size() - 1; i >= 0; i--) {
+                tr.mActivities.get(i).appTimeTracker = timeTracker;
+            }
+        }
+
+        try {
+            // Defer updating the IME target since the new IME target will try to get computed
+            // before updating all closing and opening apps, which can cause the ime target to
+            // get calculated incorrectly.
+            getDisplay().deferUpdateImeTarget();
+
+            // Shift all activities with this task up to the top
+            // of the stack, keeping them in the same internal order.
+            insertTaskAtTop(tr, null);
+
+            // Don't refocus if invisible to current user
+            final ActivityRecord top = tr.getTopActivity();
+            if (top == null || !top.okToShowLocked()) {
+                if (top != null) {
+                    mStackSupervisor.mRecentTasks.add(top.getTask());
+                }
+                ActivityOptions.abort(options);
+                return;
+            }
+
+            // Set focus to the top running activity of this stack.
+            final ActivityRecord r = topRunningActivityLocked();
+            if (r != null) {
+                r.moveFocusableActivityToTop(reason);
+            }
+
+            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
+            if (noAnimation) {
+                getDisplay().getWindowContainerController().prepareAppTransition(
+                        TRANSIT_NONE, false);
+                if (r != null) {
+                    mStackSupervisor.mNoAnimActivities.add(r);
+                }
+                ActivityOptions.abort(options);
+            } else {
+                updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
+            }
+            // If a new task is moved to the front, then mark the existing top activity as
+            // supporting
+
+            // picture-in-picture while paused only if the task would not be considered an oerlay
+            // on top
+            // of the current activity (eg. not fullscreen, or the assistant)
+            if (canEnterPipOnTaskSwitch(topActivity, tr, null /* toFrontActivity */,
+                    options)) {
+                topActivity.supportsEnterPipOnTaskSwitch = true;
+            }
+
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
+
+            mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.taskId);
+        } finally {
+            getDisplay().continueUpdateImeTarget();
+        }
+    }
+
+    /**
+     * Worker method for rearranging history stack. Implements the function of moving all
+     * activities for a specific task (gathering them if disjoint) into a single group at the
+     * bottom of the stack.
+     *
+     * If a watcher is installed, the action is preflighted and the watcher has an opportunity
+     * to premeptively cancel the move.
+     *
+     * @param taskId The taskId to collect and move to the bottom.
+     * @return Returns true if the move completed, false if not.
+     */
+    final boolean moveTaskToBackLocked(int taskId) {
+        final TaskRecord tr = taskForIdLocked(taskId);
+        if (tr == null) {
+            Slog.i(TAG, "moveTaskToBack: bad taskId=" + taskId);
+            return false;
+        }
+        Slog.i(TAG, "moveTaskToBack: " + tr);
+
+        // In LockTask mode, moving a locked task to the back of the stack may expose unlocked
+        // ones. Therefore we need to check if this operation is allowed.
+        if (!mService.getLockTaskController().canMoveTaskToBack(tr)) {
+            return false;
+        }
+
+        // If we have a watcher, preflight the move before committing to it.  First check
+        // for *other* available tasks, but if none are available, then try again allowing the
+        // current task to be selected.
+        if (isTopStackOnDisplay() && mService.mController != null) {
+            ActivityRecord next = topRunningActivityLocked(null, taskId);
+            if (next == null) {
+                next = topRunningActivityLocked(null, 0);
+            }
+            if (next != null) {
+                // ask watcher if this is allowed
+                boolean moveOK = true;
+                try {
+                    moveOK = mService.mController.activityResuming(next.packageName);
+                } catch (RemoteException e) {
+                    mService.mController = null;
+                    Watchdog.getInstance().setActivityController(null);
+                }
+                if (!moveOK) {
+                    return false;
+                }
+            }
+        }
+
+        if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task=" + taskId);
+
+        mTaskHistory.remove(tr);
+        mTaskHistory.add(0, tr);
+        updateTaskMovement(tr, false);
+
+        getDisplay().getWindowContainerController().prepareAppTransition(
+                TRANSIT_TASK_TO_BACK, false);
+        moveToBack("moveTaskToBackLocked", tr);
+
+        if (inPinnedWindowingMode()) {
+            mStackSupervisor.removeStack(this);
+            return true;
+        }
+
+        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        return true;
+    }
+
+    static void logStartActivity(int tag, ActivityRecord r, TaskRecord task) {
+        final Uri data = r.intent.getData();
+        final String strData = data != null ? data.toSafeString() : null;
+
+        EventLog.writeEvent(tag,
+                r.userId, System.identityHashCode(r), task.taskId,
+                r.shortComponentName, r.intent.getAction(),
+                r.intent.getType(), strData, r.intent.getFlags());
+    }
+
+    /**
+     * Ensures all visible activities at or below the input activity have the right configuration.
+     */
+    void ensureVisibleActivitiesConfigurationLocked(ActivityRecord start, boolean preserveWindow) {
+        if (start == null || !start.visible) {
+            return;
+        }
+
+        final TaskRecord startTask = start.getTask();
+        boolean behindFullscreen = false;
+        boolean updatedConfig = false;
+
+        for (int taskIndex = mTaskHistory.indexOf(startTask); taskIndex >= 0; --taskIndex) {
+            final TaskRecord task = mTaskHistory.get(taskIndex);
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            int activityIndex =
+                    (start.getTask() == task) ? activities.indexOf(start) : activities.size() - 1;
+            for (; activityIndex >= 0; --activityIndex) {
+                final ActivityRecord r = activities.get(activityIndex);
+                updatedConfig |= r.ensureActivityConfiguration(0 /* globalChanges */,
+                        preserveWindow);
+                if (r.fullscreen) {
+                    behindFullscreen = true;
+                    break;
+                }
+            }
+            if (behindFullscreen) {
+                break;
+            }
+        }
+        if (updatedConfig) {
+            // Ensure the resumed state of the focus activity if we updated the configuration of
+            // any activity.
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        }
+    }
+
+    // TODO: Figure-out a way to consolidate with resize() method below.
+    @Override
+    public void requestResize(Rect bounds) {
+        mService.resizeStack(mStackId, bounds,
+                true /* allowResizeInDockedMode */, false /* preserveWindows */,
+                false /* animate */, -1 /* animationDuration */);
+    }
+
+    // TODO: Can only be called from special methods in ActivityStackSupervisor.
+    // Need to consolidate those calls points into this resize method so anyone can call directly.
+    void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds) {
+        if (!updateBoundsAllowed(bounds, tempTaskBounds, tempTaskInsetBounds)) {
+            return;
+        }
+
+        // Update override configurations of all tasks in the stack.
+        final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
+        final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds;
+
+        mTmpBounds.clear();
+        mTmpInsetBounds.clear();
+
+        for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
+            final TaskRecord task = mTaskHistory.get(i);
+            if (task.isResizeable()) {
+                if (inFreeformWindowingMode()) {
+                    // TODO(b/71028874): Can be removed since each freeform task is its own
+                    //                   stack.
+                    // For freeform stack we don't adjust the size of the tasks to match that
+                    // of the stack, but we do try to make sure the tasks are still contained
+                    // with the bounds of the stack.
+                    if (task.getOverrideBounds() != null) {
+                        mTmpRect2.set(task.getOverrideBounds());
+                        fitWithinBounds(mTmpRect2, bounds);
+                        task.updateOverrideConfiguration(mTmpRect2);
+                    }
+                } else {
+                    task.updateOverrideConfiguration(taskBounds, insetBounds);
+                }
+            }
+
+            mTmpBounds.put(task.taskId, task.getOverrideBounds());
+            if (tempTaskInsetBounds != null) {
+                mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
+            }
+        }
+
+        mWindowContainerController.resize(bounds, mTmpBounds, mTmpInsetBounds);
+        setBounds(bounds);
+    }
+
+    void onPipAnimationEndResize() {
+        mWindowContainerController.onPipAnimationEndResize();
+    }
+
+
+    /**
+     * Adjust bounds to stay within stack bounds.
+     *
+     * Since bounds might be outside of stack bounds, this method tries to move the bounds in a way
+     * that keep them unchanged, but be contained within the stack bounds.
+     *
+     * @param bounds Bounds to be adjusted.
+     * @param stackBounds Bounds within which the other bounds should remain.
+     */
+    private static void fitWithinBounds(Rect bounds, Rect stackBounds) {
+        if (stackBounds == null || stackBounds.isEmpty() || stackBounds.contains(bounds)) {
+            return;
+        }
+
+        if (bounds.left < stackBounds.left || bounds.right > stackBounds.right) {
+            final int maxRight = stackBounds.right
+                    - (stackBounds.width() / FIT_WITHIN_BOUNDS_DIVIDER);
+            int horizontalDiff = stackBounds.left - bounds.left;
+            if ((horizontalDiff < 0 && bounds.left >= maxRight)
+                    || (bounds.left + horizontalDiff >= maxRight)) {
+                horizontalDiff = maxRight - bounds.left;
+            }
+            bounds.left += horizontalDiff;
+            bounds.right += horizontalDiff;
+        }
+
+        if (bounds.top < stackBounds.top || bounds.bottom > stackBounds.bottom) {
+            final int maxBottom = stackBounds.bottom
+                    - (stackBounds.height() / FIT_WITHIN_BOUNDS_DIVIDER);
+            int verticalDiff = stackBounds.top - bounds.top;
+            if ((verticalDiff < 0 && bounds.top >= maxBottom)
+                    || (bounds.top + verticalDiff >= maxBottom)) {
+                verticalDiff = maxBottom - bounds.top;
+            }
+            bounds.top += verticalDiff;
+            bounds.bottom += verticalDiff;
+        }
+    }
+
+    boolean willActivityBeVisibleLocked(IBinder token) {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (r.appToken == token) {
+                    return true;
+                }
+                if (r.fullscreen && !r.finishing) {
+                    return false;
+                }
+            }
+        }
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        if (r == null) {
+            return false;
+        }
+        if (r.finishing) Slog.e(TAG, "willActivityBeVisibleLocked: Returning false,"
+                + " would have returned true for r=" + r);
+        return !r.finishing;
+    }
+
+    void closeSystemDialogsLocked() {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
+                    finishActivityLocked(r, Activity.RESULT_CANCELED, null, "close-sys", true);
+                }
+            }
+        }
+    }
+
+    boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses,
+            boolean doit, boolean evenPersistent, int userId) {
+        boolean didSomething = false;
+        TaskRecord lastTask = null;
+        ComponentName homeActivity = null;
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            mTmpActivities.clear();
+            mTmpActivities.addAll(activities);
+
+            while (!mTmpActivities.isEmpty()) {
+                ActivityRecord r = mTmpActivities.remove(0);
+                final boolean sameComponent =
+                        (r.packageName.equals(packageName) && (filterByClasses == null
+                                || filterByClasses.contains(r.realActivity.getClassName())))
+                        || (packageName == null && r.userId == userId);
+                if ((userId == UserHandle.USER_ALL || r.userId == userId)
+                        && (sameComponent || r.getTask() == lastTask)
+                        && (r.app == null || evenPersistent || !r.app.isPersistent())) {
+                    if (!doit) {
+                        if (r.finishing) {
+                            // If this activity is just finishing, then it is not
+                            // interesting as far as something to stop.
+                            continue;
+                        }
+                        return true;
+                    }
+                    if (r.isActivityTypeHome()) {
+                        if (homeActivity != null && homeActivity.equals(r.realActivity)) {
+                            Slog.i(TAG, "Skip force-stop again " + r);
+                            continue;
+                        } else {
+                            homeActivity = r.realActivity;
+                        }
+                    }
+                    didSomething = true;
+                    Slog.i(TAG, "  Force finishing activity " + r);
+                    if (sameComponent) {
+                        if (r.hasProcess()) {
+                            r.app.setRemoved(true);
+                        }
+                        r.app = null;
+                    }
+                    lastTask = r.getTask();
+                    finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop",
+                            true);
+                }
+            }
+        }
+        return didSomething;
+    }
+
+    /**
+     * @return The set of running tasks through {@param tasksOut} that are available to the caller.
+     *         If {@param ignoreActivityType} or {@param ignoreWindowingMode} are not undefined,
+     *         then skip running tasks that match those types.
+     */
+    void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType,
+            @WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) {
+        boolean focusedStack = mStackSupervisor.getTopDisplayFocusedStack() == this;
+        boolean topTask = true;
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            if (task.getTopActivity() == null) {
+                // Skip if there are no activities in the task
+                continue;
+            }
+            if (!allowed && !task.isActivityTypeHome() && task.effectiveUid != callingUid) {
+                // Skip if the caller can't fetch this task
+                continue;
+            }
+            if (ignoreActivityType != ACTIVITY_TYPE_UNDEFINED
+                    && task.getActivityType() == ignoreActivityType) {
+                // Skip ignored activity type
+                continue;
+            }
+            if (ignoreWindowingMode != WINDOWING_MODE_UNDEFINED
+                    && task.getWindowingMode() == ignoreWindowingMode) {
+                // Skip ignored windowing mode
+                continue;
+            }
+            if (focusedStack && topTask) {
+                // For the focused stack top task, update the last stack active time so that it can
+                // be used to determine the order of the tasks (it may not be set for newly created
+                // tasks)
+                task.lastActiveTime = SystemClock.elapsedRealtime();
+                topTask = false;
+            }
+            tasksOut.add(task);
+        }
+    }
+
+    void unhandledBackLocked() {
+        final int top = mTaskHistory.size() - 1;
+        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Performing unhandledBack(): top activity at " + top);
+        if (top >= 0) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
+            int activityTop = activities.size() - 1;
+            if (activityTop >= 0) {
+                finishActivityLocked(activities.get(activityTop), Activity.RESULT_CANCELED, null,
+                        "unhandled-back", true);
+            }
+        }
+    }
+
+    /**
+     * Reset local parameters because an app's activity died.
+     * @param app The app of the activity that died.
+     * @return result from removeHistoryRecordsForAppLocked.
+     */
+    boolean handleAppDiedLocked(WindowProcessController app) {
+        if (mPausingActivity != null && mPausingActivity.app == app) {
+            if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG_PAUSE,
+                    "App died while pausing: " + mPausingActivity);
+            mPausingActivity = null;
+        }
+        if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
+            mLastPausedActivity = null;
+            mLastNoHistoryActivity = null;
+        }
+
+        return removeHistoryRecordsForAppLocked(app);
+    }
+
+    void handleAppCrashLocked(WindowProcessController app) {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (r.app == app) {
+                    Slog.w(TAG, "  Force finishing activity "
+                            + r.intent.getComponent().flattenToShortString());
+                    // Force the destroy to skip right to removal.
+                    r.app = null;
+                    getDisplay().getWindowContainerController().prepareAppTransition(
+                            TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
+                    finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false,
+                            "handleAppCrashedLocked");
+                }
+            }
+        }
+    }
+
+    boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+            boolean dumpClient, String dumpPackage, boolean needSep) {
+
+        if (mTaskHistory.isEmpty()) {
+            return false;
+        }
+        final String prefix = "    ";
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            if (needSep) {
+                pw.println("");
+            }
+            pw.println(prefix + "Task id #" + task.taskId);
+            pw.println(prefix + "mBounds=" + task.getOverrideBounds());
+            pw.println(prefix + "mMinWidth=" + task.mMinWidth);
+            pw.println(prefix + "mMinHeight=" + task.mMinHeight);
+            pw.println(prefix + "mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
+            pw.println(prefix + "* " + task);
+            task.dump(pw, prefix + "  ");
+            ActivityStackSupervisor.dumpHistoryList(fd, pw, mTaskHistory.get(taskNdx).mActivities,
+                    prefix, "Hist", true, !dumpAll, dumpClient, dumpPackage, false, null, task);
+        }
+        return true;
+    }
+
+    ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
+        ArrayList<ActivityRecord> activities = new ArrayList<>();
+
+        if ("all".equals(name)) {
+            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+                activities.addAll(mTaskHistory.get(taskNdx).mActivities);
+            }
+        } else if ("top".equals(name)) {
+            final int top = mTaskHistory.size() - 1;
+            if (top >= 0) {
+                final ArrayList<ActivityRecord> list = mTaskHistory.get(top).mActivities;
+                int listTop = list.size() - 1;
+                if (listTop >= 0) {
+                    activities.add(list.get(listTop));
+                }
+            }
+        } else {
+            ItemMatcher matcher = new ItemMatcher();
+            matcher.build(name);
+
+            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+                for (ActivityRecord r1 : mTaskHistory.get(taskNdx).mActivities) {
+                    if (matcher.match(r1, r1.intent.getComponent())) {
+                        activities.add(r1);
+                    }
+                }
+            }
+        }
+
+        return activities;
+    }
+
+    ActivityRecord restartPackage(String packageName) {
+        ActivityRecord starting = topRunningActivityLocked();
+
+        // All activities that came from the package must be
+        // restarted as if there was a config change.
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord a = activities.get(activityNdx);
+                if (a.info.packageName.equals(packageName)) {
+                    a.forceNewConfig = true;
+                    if (starting != null && a == starting && a.visible) {
+                        a.startFreezingScreenLocked(starting.app,
+                                CONFIG_SCREEN_LAYOUT);
+                    }
+                }
+            }
+        }
+
+        return starting;
+    }
+
+    /**
+     * Removes the input task from this stack.
+     * @param task to remove.
+     * @param reason for removal.
+     * @param mode task removal mode. Either {@link #REMOVE_TASK_MODE_DESTROYING},
+     *             {@link #REMOVE_TASK_MODE_MOVING}, {@link #REMOVE_TASK_MODE_MOVING_TO_TOP}.
+     */
+    void removeTask(TaskRecord task, String reason, int mode) {
+        for (ActivityRecord record : task.mActivities) {
+            onActivityRemovedFromStack(record);
+        }
+
+        final boolean removed = mTaskHistory.remove(task);
+
+        if (removed) {
+            EventLog.writeEvent(EventLogTags.AM_REMOVE_TASK, task.taskId, getStackId());
+        }
+
+        removeActivitiesFromLRUListLocked(task);
+        updateTaskMovement(task, true);
+
+        if (mode == REMOVE_TASK_MODE_DESTROYING && task.mActivities.isEmpty()) {
+            // TODO: VI what about activity?
+            final boolean isVoiceSession = task.voiceSession != null;
+            if (isVoiceSession) {
+                try {
+                    task.voiceSession.taskFinished(task.intent, task.taskId);
+                } catch (RemoteException e) {
+                }
+            }
+            if (task.autoRemoveFromRecents() || isVoiceSession) {
+                // Task creator asked to remove this when done, or this task was a voice
+                // interaction, so it should not remain on the recent tasks list.
+                mStackSupervisor.mRecentTasks.remove(task);
+            }
+
+            task.removeWindowContainer();
+        }
+
+        if (mTaskHistory.isEmpty()) {
+            if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this);
+            // We only need to adjust focused stack if this stack is in focus and we are not in the
+            // process of moving the task to the top of the stack that will be focused.
+            if (mode != REMOVE_TASK_MODE_MOVING_TO_TOP
+                    && mStackSupervisor.isTopDisplayFocusedStack(this)) {
+                String myReason = reason + " leftTaskHistoryEmpty";
+                if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) {
+                    getDisplay().moveHomeStackToFront(myReason);
+                }
+            }
+            if (isAttached()) {
+                getDisplay().positionChildAtBottom(this);
+            }
+            if (!isActivityTypeHome() || getDisplay().isRemoved()) {
+                remove();
+            }
+        }
+
+        task.setStack(null);
+
+        // Notify if a task from the pinned stack is being removed (or moved depending on the mode)
+        if (inPinnedWindowingMode()) {
+            mService.getTaskChangeNotificationController().notifyActivityUnpinned();
+        }
+    }
+
+    TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            boolean toTop) {
+        return createTaskRecord(taskId, info, intent, voiceSession, voiceInteractor, toTop,
+                null /*activity*/, null /*source*/, null /*options*/);
+    }
+
+    TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            boolean toTop, ActivityRecord activity, ActivityRecord source,
+            ActivityOptions options) {
+        final TaskRecord task = TaskRecord.create(
+                mService, taskId, info, intent, voiceSession, voiceInteractor);
+        // add the task to stack first, mTaskPositioner might need the stack association
+        addTask(task, toTop, "createTaskRecord");
+        final int displayId = mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY;
+        final boolean isLockscreenShown = mService.mStackSupervisor.getKeyguardController()
+                .isKeyguardOrAodShowing(displayId);
+        if (!mStackSupervisor.getLaunchParamsController()
+                .layoutTask(task, info.windowLayout, activity, source, options)
+                && !matchParentBounds() && task.isResizeable() && !isLockscreenShown) {
+            task.updateOverrideConfiguration(getOverrideBounds());
+        }
+        task.createWindowContainer(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
+        return task;
+    }
+
+    ArrayList<TaskRecord> getAllTasks() {
+        return new ArrayList<>(mTaskHistory);
+    }
+
+    void addTask(final TaskRecord task, final boolean toTop, String reason) {
+        addTask(task, toTop ? MAX_VALUE : 0, true /* schedulePictureInPictureModeChange */, reason);
+        if (toTop) {
+            // TODO: figure-out a way to remove this call.
+            positionChildWindowContainerAtTop(task);
+        }
+    }
+
+    // TODO: This shouldn't allow automatic reparenting. Remove the call to preAddTask and deal
+    // with the fall-out...
+    void addTask(final TaskRecord task, int position, boolean schedulePictureInPictureModeChange,
+            String reason) {
+        // TODO: Is this remove really needed? Need to look into the call path for the other addTask
+        mTaskHistory.remove(task);
+        position = getAdjustedPositionForTask(task, position, null /* starting */);
+        final boolean toTop = position >= mTaskHistory.size();
+        final ActivityStack prevStack = preAddTask(task, reason, toTop);
+
+        mTaskHistory.add(position, task);
+        task.setStack(this);
+
+        updateTaskMovement(task, toTop);
+
+        postAddTask(task, prevStack, schedulePictureInPictureModeChange);
+    }
+
+    void positionChildAt(TaskRecord task, int index) {
+
+        if (task.getStack() != this) {
+            throw new IllegalArgumentException("AS.positionChildAt: task=" + task
+                    + " is not a child of stack=" + this + " current parent=" + task.getStack());
+        }
+
+        task.updateOverrideConfigurationForStack(this);
+
+        final ActivityRecord topRunningActivity = task.topRunningActivityLocked();
+        final boolean wasResumed = topRunningActivity == task.getStack().mResumedActivity;
+        insertTaskAtPosition(task, index);
+        task.setStack(this);
+        postAddTask(task, null /* prevStack */, true /* schedulePictureInPictureModeChange */);
+
+        if (wasResumed) {
+            if (mResumedActivity != null) {
+                Log.wtf(TAG, "mResumedActivity was already set when moving mResumedActivity from"
+                        + " other stack to this stack mResumedActivity=" + mResumedActivity
+                        + " other mResumedActivity=" + topRunningActivity);
+            }
+            topRunningActivity.setState(RESUMED, "positionChildAt");
+        }
+
+        // The task might have already been running and its visibility needs to be synchronized with
+        // the visibility of the stack / windows.
+        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+    }
+
+    private ActivityStack preAddTask(TaskRecord task, String reason, boolean toTop) {
+        final ActivityStack prevStack = task.getStack();
+        if (prevStack != null && prevStack != this) {
+            prevStack.removeTask(task, reason,
+                    toTop ? REMOVE_TASK_MODE_MOVING_TO_TOP : REMOVE_TASK_MODE_MOVING);
+        }
+        return prevStack;
+    }
+
+    /**
+     * @param schedulePictureInPictureModeChange specifies whether or not to schedule the PiP mode
+     *            change. Callers may set this to false if they are explicitly scheduling PiP mode
+     *            changes themselves, like during the PiP animation
+     */
+    private void postAddTask(TaskRecord task, ActivityStack prevStack,
+            boolean schedulePictureInPictureModeChange) {
+        if (schedulePictureInPictureModeChange && prevStack != null) {
+            mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, prevStack);
+        } else if (task.voiceSession != null) {
+            try {
+                task.voiceSession.taskStarted(task.intent, task.taskId);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    public void setAlwaysOnTop(boolean alwaysOnTop) {
+        if (isAlwaysOnTop() == alwaysOnTop) {
+            return;
+        }
+        super.setAlwaysOnTop(alwaysOnTop);
+        final ActivityDisplay display = getDisplay();
+        // positionChildAtTop() must be called even when always on top gets turned off because we
+        // need to make sure that the stack is moved from among always on top windows to below other
+        // always on top windows. Since the position the stack should be inserted into is calculated
+        // properly in {@link ActivityDisplay#getTopInsertPosition()} in both cases, we can just
+        // request that the stack is put at top here.
+        display.positionChildAtTop(this, false /* includingParents */);
+    }
+
+    /** NOTE: Should only be called from {@link TaskRecord#reparent}. */
+    void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
+            boolean setPause, String reason) {
+        if (!moveToFront) {
+            return;
+        }
+
+        final ActivityState origState = r.getState();
+        // If the activity owns the last resumed activity, transfer that together,
+        // so that we don't resume the same activity again in the new stack.
+        // Apps may depend on onResume()/onPause() being called in pairs.
+        if (setResume) {
+            r.setState(RESUMED, "moveToFrontAndResumeStateIfNeeded");
+            updateLRUListLocked(r);
+        }
+        // If the activity was previously pausing, then ensure we transfer that as well
+        if (setPause) {
+            mPausingActivity = r;
+            schedulePauseTimeout(r);
+        }
+        // Move the stack in which we are placing the activity to the front.
+        moveToFront(reason);
+        // If the original state is resumed, there is no state change to update focused app.
+        // So here makes sure the activity focus is set if it is the top.
+        if (origState == RESUMED && r == mStackSupervisor.getTopResumedActivity()) {
+            // TODO(b/111361570): Support multiple focused apps in WM
+            mService.setResumedActivityUncheckLocked(r, reason);
+        }
+    }
+
+    public int getStackId() {
+        return mStackId;
+    }
+
+    @Override
+    public String toString() {
+        return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
+                + " stackId=" + mStackId + " type=" + activityTypeToString(getActivityType())
+                + " mode=" + windowingModeToString(getWindowingMode())
+                + " visible=" + shouldBeVisible(null /* starting */)
+                + " translucent=" + isStackTranslucent(null /* starting */)
+                + ", "
+                + mTaskHistory.size() + " tasks}";
+    }
+
+    void onLockTaskPackagesUpdated() {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            mTaskHistory.get(taskNdx).setLockTaskAuth();
+        }
+    }
+
+    void executeAppTransition(ActivityOptions options) {
+        getDisplay().getWindowContainerController().executeAppTransition();
+        ActivityOptions.abort(options);
+    }
+
+    boolean shouldSleepActivities() {
+        final ActivityDisplay display = getDisplay();
+
+        // Do not sleep activities in this stack if we're marked as focused and the keyguard
+        // is in the process of going away.
+        if (isFocusedStackOnDisplay()
+                && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
+            return false;
+        }
+
+        return display != null ? display.isSleeping() : mService.isSleepingLocked();
+    }
+
+    boolean shouldSleepOrShutDownActivities() {
+        return shouldSleepActivities() || mService.mShuttingDown;
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
+        proto.write(ID, mStackId);
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            task.writeToProto(proto, TASKS);
+        }
+        if (mResumedActivity != null) {
+            mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+        }
+        proto.write(DISPLAY_ID, mDisplayId);
+        if (!matchParentBounds()) {
+            final Rect bounds = getOverrideBounds();
+            bounds.writeToProto(proto, BOUNDS);
+        }
+
+        // TODO: Remove, no longer needed with windowingMode.
+        proto.write(FULLSCREEN, matchParentBounds());
+        proto.end(token);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
new file mode 100644
index 0000000..6034f81
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -0,0 +1,4911 @@
+/*
+ * Copyright (C) 2013 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.server.wm;
+
+import static android.Manifest.permission.ACTIVITY_EMBEDDING;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.Manifest.permission.START_ANY_ACTIVITY;
+import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.ActivityManager.START_FLAG_DEBUG;
+import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
+import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
+import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
+import static android.app.WaitResult.INVALID_DELAY;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.windowingModeToString;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.graphics.Rect.copyOrNull;
+import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Display.TYPE_VIRTUAL;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
+
+import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
+import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
+import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
+import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
+import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
+import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IDLE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
+import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
+import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
+
+import static java.lang.Integer.MAX_VALUE;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.ProfilerInfo;
+import android.app.ResultInfo;
+import android.app.WaitResult;
+import android.app.WindowConfiguration.ActivityType;
+import android.app.WindowConfiguration.WindowingMode;
+import android.app.servertransaction.ActivityLifecycleItem;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.LaunchActivityItem;
+import android.app.servertransaction.PauseActivityItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.FactoryTest;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.WorkSource;
+import android.provider.MediaStore;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.DisplayMetrics;
+import android.util.EventLog;
+import android.util.IntArray;
+import android.util.MergedConfiguration;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.ReferrerIntent;
+import com.android.internal.os.TransferPipe;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.LocalServices;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.am.AppTimeTracker;
+import com.android.server.am.EventLogTags;
+import com.android.server.am.UserState;
+import com.android.server.wm.ActivityStack.ActivityState;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
+        RecentTasks.Callbacks, RootWindowContainerListener {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_ATM;
+    private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
+    private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
+    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
+    private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
+    private static final String TAG_STACK = TAG + POSTFIX_STACK;
+    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+    static final String TAG_STATES = TAG + POSTFIX_STATES;
+    static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+
+    /** How long we wait until giving up on the last activity telling us it is idle. */
+    static final int IDLE_TIMEOUT = 10 * 1000;
+
+    /** How long we can hold the sleep wake lock before giving up. */
+    static final int SLEEP_TIMEOUT = 5 * 1000;
+
+    // How long we can hold the launch wake lock before giving up.
+    static final int LAUNCH_TIMEOUT = 10 * 1000;
+
+    static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG;
+    static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_STACK_MSG + 1;
+    static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
+    static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
+    static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
+    static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
+    static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
+    static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
+
+    private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
+
+    // Used to indicate if an object (e.g. stack) that we are trying to get
+    // should be created if it doesn't exist already.
+    static final boolean CREATE_IF_NEEDED = true;
+
+    // Used to indicate that windows of activities should be preserved during the resize.
+    static final boolean PRESERVE_WINDOWS = true;
+
+    // Used to indicate if an object (e.g. task) should be moved/created
+    // at the top of its container (e.g. stack).
+    static final boolean ON_TOP = true;
+
+    // Don't execute any calls to resume.
+    static final boolean DEFER_RESUME = true;
+
+    // Used to indicate that a task is removed it should also be removed from recents.
+    static final boolean REMOVE_FROM_RECENTS = true;
+
+    // Used to indicate that pausing an activity should occur immediately without waiting for
+    // the activity callback indicating that it has completed pausing
+    static final boolean PAUSE_IMMEDIATELY = true;
+
+    /** True if the docked stack is currently being resized. */
+    private boolean mDockedStackResizing;
+
+    /**
+     * True if there are pending docked bounds that need to be applied after
+     * {@link #mDockedStackResizing} is reset to false.
+     */
+    private boolean mHasPendingDockedBounds;
+    private Rect mPendingDockedBounds;
+    private Rect mPendingTempDockedTaskBounds;
+    private Rect mPendingTempDockedTaskInsetBounds;
+    private Rect mPendingTempOtherTaskBounds;
+    private Rect mPendingTempOtherTaskInsetBounds;
+
+    /**
+     * The modes which affect which tasks are returned when calling
+     * {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            MATCH_TASK_IN_STACKS_ONLY,
+            MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
+            MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+    })
+    public @interface AnyTaskForIdMatchTaskMode {}
+    // Match only tasks in the current stacks
+    static final int MATCH_TASK_IN_STACKS_ONLY = 0;
+    // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks
+    static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1;
+    // Match either tasks in the current stacks, or in the recent tasks, restoring it to the
+    // provided stack id
+    static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2;
+
+    // Activity actions an app cannot start if it uses a permission which is not granted.
+    private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
+            new ArrayMap<>();
+
+    static {
+        ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE,
+                Manifest.permission.CAMERA);
+        ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_VIDEO_CAPTURE,
+                Manifest.permission.CAMERA);
+        ACTION_TO_RUNTIME_PERMISSION.put(Intent.ACTION_CALL,
+                Manifest.permission.CALL_PHONE);
+    }
+
+    /** Action restriction: launching the activity is not restricted. */
+    private static final int ACTIVITY_RESTRICTION_NONE = 0;
+    /** Action restriction: launching the activity is restricted by a permission. */
+    private static final int ACTIVITY_RESTRICTION_PERMISSION = 1;
+    /** Action restriction: launching the activity is restricted by an app op. */
+    private static final int ACTIVITY_RESTRICTION_APPOP = 2;
+
+    // For debugging to make sure the caller when acquiring/releasing our
+    // wake lock is the system process.
+    static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
+    /** The number of distinct task ids that can be assigned to the tasks of a single user */
+    private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
+
+    ActivityTaskManagerService mService;
+
+    /** The historial list of recent tasks including inactive tasks */
+    RecentTasks mRecentTasks;
+
+    /** Helper class to abstract out logic for fetching the set of currently running tasks */
+    private RunningTasks mRunningTasks;
+
+    final ActivityStackSupervisorHandler mHandler;
+    final Looper mLooper;
+
+    /** Short cut */
+    WindowManagerService mWindowManager;
+    DisplayManager mDisplayManager;
+
+    private LaunchParamsController mLaunchParamsController;
+
+    /**
+     * Maps the task identifier that activities are currently being started in to the userId of the
+     * task. Each time a new task is created, the entry for the userId of the task is incremented
+     */
+    private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20);
+
+    /** The current user */
+    int mCurrentUser;
+
+    /** List of activities that are waiting for a new activity to become visible before completing
+     * whatever operation they are supposed to do. */
+    // TODO: Remove mActivitiesWaitingForVisibleActivity list and just remove activity from
+    // mStoppingActivities when something else comes up.
+    final ArrayList<ActivityRecord> mActivitiesWaitingForVisibleActivity = new ArrayList<>();
+
+    /** List of processes waiting to find out when a specific activity becomes visible. */
+    private final ArrayList<WaitInfo> mWaitingForActivityVisible = new ArrayList<>();
+
+    /** List of processes waiting to find out about the next launched activity. */
+    final ArrayList<WaitResult> mWaitingActivityLaunched = new ArrayList<>();
+
+    /** List of activities that are ready to be stopped, but waiting for the next activity to
+     * settle down before doing so. */
+    final ArrayList<ActivityRecord> mStoppingActivities = new ArrayList<>();
+
+    /** List of activities that are ready to be finished, but waiting for the previous activity to
+     * settle down before doing so.  It contains ActivityRecord objects. */
+    final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<>();
+
+    /** List of activities that are in the process of going to sleep. */
+    final ArrayList<ActivityRecord> mGoingToSleepActivities = new ArrayList<>();
+
+    /** List of activities whose multi-window mode changed that we need to report to the
+     * application */
+    final ArrayList<ActivityRecord> mMultiWindowModeChangedActivities = new ArrayList<>();
+
+    /** List of activities whose picture-in-picture mode changed that we need to report to the
+     * application */
+    final ArrayList<ActivityRecord> mPipModeChangedActivities = new ArrayList<>();
+
+    /**
+     * Animations that for the current transition have requested not to
+     * be considered for the transition animation.
+     */
+    final ArrayList<ActivityRecord> mNoAnimActivities = new ArrayList<>();
+
+    /** The target stack bounds for the picture-in-picture mode changed that we need to report to
+     * the application */
+    Rect mPipModeChangedTargetStackBounds;
+
+    /** Used on user changes */
+    final ArrayList<UserState> mStartingUsers = new ArrayList<>();
+
+    /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
+     * is being brought in front of us. */
+    boolean mUserLeaving = false;
+
+    /** Set when a power hint has started, but not ended. */
+    private boolean mPowerHintSent;
+
+    /**
+     * We don't want to allow the device to go to sleep while in the process
+     * of launching an activity.  This is primarily to allow alarm intent
+     * receivers to launch an activity and get that to run before the device
+     * goes back to sleep.
+     */
+    PowerManager.WakeLock mLaunchingActivity;
+
+    /**
+     * Set when the system is going to sleep, until we have
+     * successfully paused the current activity and released our wake lock.
+     * At that point the system is allowed to actually sleep.
+     */
+    PowerManager.WakeLock mGoingToSleep;
+
+    /**
+     * A list of tokens that cause the top activity to be put to sleep.
+     * They are used by components that may hide and block interaction with underlying
+     * activities.
+     */
+    final ArrayList<SleepToken> mSleepTokens = new ArrayList<>();
+
+    /** Stack id of the front stack when user switched, indexed by userId. */
+    SparseIntArray mUserStackInFront = new SparseIntArray(2);
+
+    /** Reference to default display so we can quickly look it up. */
+    private ActivityDisplay mDefaultDisplay;
+
+    /**
+     * List of displays which contain activities, sorted by z-order.
+     * The last entry in the list is the topmost.
+     */
+    private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>();
+
+    private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
+
+    private DisplayManagerInternal mDisplayManagerInternal;
+
+    /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
+    boolean inResumeTopActivity;
+
+    /**
+     * Temporary rect used during docked stack resize calculation so we don't need to create a new
+     * object each time.
+     */
+    private final Rect tempRect = new Rect();
+    private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
+
+    // The default minimal size that will be used if the activity doesn't specify its minimal size.
+    // It will be calculated when the default display gets added.
+    int mDefaultMinSizeOfResizeableTaskDp = -1;
+
+    // Whether tasks have moved and we need to rank the tasks before next OOM scoring
+    private boolean mTaskLayersChanged = true;
+
+    private ActivityMetricsLogger mActivityMetricsLogger;
+
+    private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
+
+    @Override
+    protected int getChildCount() {
+        return mActivityDisplays.size();
+    }
+
+    @Override
+    protected ActivityDisplay getChildAt(int index) {
+        return mActivityDisplays.get(index);
+    }
+
+    @Override
+    protected ConfigurationContainer getParent() {
+        return null;
+    }
+
+    Configuration getDisplayOverrideConfiguration(int displayId) {
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException("No display found with id: " + displayId);
+        }
+
+        return activityDisplay.getOverrideConfiguration();
+    }
+
+    void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException("No display found with id: " + displayId);
+        }
+
+        activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
+    }
+
+    /** Check if placing task or activity on specified display is allowed. */
+    boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
+            ActivityInfo activityInfo) {
+        if (displayId == DEFAULT_DISPLAY) {
+            // No restrictions for the default display.
+            return true;
+        }
+        if (!mService.mSupportsMultiDisplay) {
+            // Can't launch on secondary displays if feature is not supported.
+            return false;
+        }
+        if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
+            // Can't place activities to a display that has restricted launch rules.
+            // In this case the request should be made by explicitly adding target display id and
+            // by caller with corresponding permissions. See #isCallerAllowedToLaunchOnDisplay().
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Check if configuration of specified display matches current global config.
+     * Used to check if we can put a non-resizeable activity on a secondary display and it will get
+     * the same config as on the default display.
+     * @param displayId Id of the display to check.
+     * @return {@code true} if configuration matches.
+     */
+    private boolean displayConfigMatchesGlobal(int displayId) {
+        if (displayId == DEFAULT_DISPLAY) {
+            return true;
+        }
+        if (displayId == INVALID_DISPLAY) {
+            return false;
+        }
+        final ActivityDisplay targetDisplay = getActivityDisplayOrCreateLocked(displayId);
+        if (targetDisplay == null) {
+            throw new IllegalArgumentException("No display found with id: " + displayId);
+        }
+        return getConfiguration().equals(targetDisplay.getConfiguration());
+    }
+
+    static class FindTaskResult {
+        ActivityRecord mRecord;
+        boolean mIdealMatch;
+
+        void clear() {
+            mRecord = null;
+            mIdealMatch = false;
+        }
+
+        void setTo(FindTaskResult result) {
+            mRecord = result.mRecord;
+            mIdealMatch = result.mIdealMatch;
+        }
+    }
+
+    private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
+
+    /**
+     * Used to keep track whether app visibilities got changed since the last pause. Useful to
+     * determine whether to invoke the task stack change listener after pausing.
+     */
+    boolean mAppVisibilitiesChangedSinceLastPause;
+
+    /**
+     * Set of tasks that are in resizing mode during an app transition to fill the "void".
+     */
+    private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
+
+
+    /**
+     * If set to {@code false} all calls to resize the docked stack {@link #resizeDockedStackLocked}
+     * will be ignored. Useful for the case where the caller is handling resizing of other stack and
+     * moving tasks around and doesn't want dock stack to be resized due to an automatic trigger
+     * like the docked stack going empty.
+     */
+    private boolean mAllowDockedStackResize = true;
+
+    /**
+     * Is dock currently minimized.
+     */
+    boolean mIsDockMinimized;
+
+    private KeyguardController mKeyguardController;
+
+    private PowerManager mPowerManager;
+    private int mDeferResumeCount;
+
+    private boolean mInitialized;
+
+    private RootWindowContainerController mWindowContainerController;
+
+    /**
+     * Description of a request to start a new activity, which has been held
+     * due to app switches being disabled.
+     */
+    static class PendingActivityLaunch {
+        final ActivityRecord r;
+        final ActivityRecord sourceRecord;
+        final int startFlags;
+        final ActivityStack stack;
+        final WindowProcessController callerApp;
+
+        PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
+                int _startFlags, ActivityStack _stack, WindowProcessController app) {
+            r = _r;
+            sourceRecord = _sourceRecord;
+            startFlags = _startFlags;
+            stack = _stack;
+            callerApp = app;
+        }
+
+        void sendErrorResult(String message) {
+            try {
+                if (callerApp.hasThread()) {
+                    callerApp.getThread().scheduleCrash(message);
+                }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Exception scheduling crash of failed "
+                        + "activity launcher sourceRecord=" + sourceRecord, e);
+            }
+        }
+    }
+
+    public ActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+        mService = service;
+        mLooper = looper;
+        mHandler = new ActivityStackSupervisorHandler(looper);
+    }
+
+    @VisibleForTesting
+    void setService(ActivityTaskManagerService service) {
+        mService = service;
+    }
+
+    @VisibleForTesting
+    void setWindowContainerController(RootWindowContainerController controller) {
+        mWindowContainerController = controller;
+    }
+
+    public void initialize() {
+        if (mInitialized) {
+            return;
+        }
+
+        mInitialized = true;
+        mRunningTasks = createRunningTasks();
+        mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, mHandler.getLooper());
+        mKeyguardController = new KeyguardController(mService, this);
+
+        mLaunchParamsController = new LaunchParamsController(mService);
+        mLaunchParamsController.registerDefaultModifiers(this);
+    }
+
+
+    public ActivityMetricsLogger getActivityMetricsLogger() {
+        return mActivityMetricsLogger;
+    }
+
+    public KeyguardController getKeyguardController() {
+        return mKeyguardController;
+    }
+
+    void setRecentTasks(RecentTasks recentTasks) {
+        mRecentTasks = recentTasks;
+        mRecentTasks.registerCallback(this);
+    }
+
+    @VisibleForTesting
+    RunningTasks createRunningTasks() {
+        return new RunningTasks();
+    }
+
+    /**
+     * At the time when the constructor runs, the power manager has not yet been
+     * initialized.  So we initialize our wakelocks afterwards.
+     */
+    void initPowerManagement() {
+        mPowerManager = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
+        mGoingToSleep = mPowerManager
+                .newWakeLock(PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
+        mLaunchingActivity = mPowerManager.newWakeLock(PARTIAL_WAKE_LOCK, "*launch*");
+        mLaunchingActivity.setReferenceCounted(false);
+    }
+
+    void setWindowManager(WindowManagerService wm) {
+        mWindowManager = wm;
+        getKeyguardController().setWindowManager(wm);
+        setWindowContainerController(new RootWindowContainerController(this));
+
+        mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
+        mDisplayManager.registerDisplayListener(this, mHandler);
+        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+
+        final Display[] displays = mDisplayManager.getDisplays();
+        for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
+            final Display display = displays[displayNdx];
+            final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
+            if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
+                mDefaultDisplay = activityDisplay;
+            }
+            addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
+        }
+        calculateDefaultMinimalSizeOfResizeableTasks();
+
+        final ActivityDisplay defaultDisplay = getDefaultDisplay();
+
+        defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+        positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
+    }
+
+    /** Change the z-order of the given display. */
+    private void positionChildAt(ActivityDisplay display, int position) {
+        if (position >= mActivityDisplays.size()) {
+            position = mActivityDisplays.size() - 1;
+        } else if (position < 0) {
+            position = 0;
+        }
+
+        if (mActivityDisplays.isEmpty()) {
+            mActivityDisplays.add(display);
+        } else if (mActivityDisplays.get(position) != display) {
+            mActivityDisplays.remove(display);
+            mActivityDisplays.add(position, display);
+        }
+    }
+
+    @Override
+    public void onChildPositionChanged(DisplayWindowController childController, int position) {
+        // Assume AM lock is held from positionChildAt of controller in each hierarchy.
+        final ActivityDisplay display = getActivityDisplay(childController.getDisplayId());
+        if (display != null) {
+            positionChildAt(display, position);
+        }
+    }
+
+    ActivityStack getTopDisplayFocusedStack() {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack();
+            if (focusedStack != null) {
+                return focusedStack;
+            }
+        }
+        return null;
+    }
+
+    ActivityRecord getTopResumedActivity() {
+        final ActivityStack focusedStack = getTopDisplayFocusedStack();
+        if (focusedStack == null) {
+            return null;
+        }
+        final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+        if (resumedActivity != null && resumedActivity.app != null) {
+            return resumedActivity;
+        }
+        // The top focused stack might not have a resumed activity yet - look on all displays in
+        // focus order.
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
+            if (resumedActivityOnDisplay != null) {
+                return resumedActivityOnDisplay;
+            }
+        }
+        return null;
+    }
+
+    boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
+        if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) {
+            return false;
+        }
+
+        return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
+    }
+
+    boolean isTopDisplayFocusedStack(ActivityStack stack) {
+        return stack != null && stack == getTopDisplayFocusedStack();
+    }
+
+    void moveRecentsStackToFront(String reason) {
+        final ActivityStack recentsStack = getDefaultDisplay().getStack(
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
+        if (recentsStack != null) {
+            recentsStack.moveToFront(reason);
+        }
+    }
+
+    boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
+        if (!mService.isBooting() && !mService.isBooted()) {
+            // Not ready yet!
+            return false;
+        }
+
+        if (displayId == INVALID_DISPLAY) {
+            displayId = DEFAULT_DISPLAY;
+        }
+
+        final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
+        final String myReason = reason + " resumeHomeActivity";
+
+        // Only resume home activity if isn't finishing.
+        if (r != null && !r.finishing) {
+            r.moveFocusableActivityToTop(myReason);
+            return resumeFocusedStacksTopActivitiesLocked(r.getStack(), prev, null);
+        }
+        return startHomeOnDisplay(mCurrentUser, myReason, displayId);
+    }
+
+    /**
+     * Check if home activity start should be allowed on a display.
+     * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched.
+     * @param displayId The id of the target display.
+     * @param allowInstrumenting Whether launching home should be allowed if being instrumented.
+     * @return {@code true} if allow to launch, {@code false} otherwise.
+     */
+    boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId,
+            boolean allowInstrumenting) {
+        if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
+                && mService.mTopAction == null) {
+            // We are running in factory test mode, but unable to find the factory test app, so
+            // just sit around displaying the error message and don't try to start anything.
+            return false;
+        }
+
+        final WindowProcessController app =
+                mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid);
+        if (!allowInstrumenting && app != null && app.isInstrumenting()) {
+            // Don't do this if the home app is currently being instrumented.
+            return false;
+        }
+
+        if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
+                && displayId == mService.mVr2dDisplayId)) {
+            // No restrictions to default display or vr 2d display.
+            return true;
+        }
+
+        final ActivityDisplay display = getActivityDisplay(displayId);
+        if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
+            // Can't launch home on display that doesn't support system decorations.
+            return false;
+        }
+
+        final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
+                && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE;
+        if (!supportMultipleInstance) {
+            // Can't launch home on other displays if it requested to be single instance.
+            return false;
+        }
+
+        return true;
+    }
+
+    TaskRecord anyTaskForIdLocked(int id) {
+        return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE);
+    }
+
+    TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode) {
+        return anyTaskForIdLocked(id, matchMode, null, !ON_TOP);
+    }
+
+    /**
+     * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
+     * @param id Id of the task we would like returned.
+     * @param matchMode The mode to match the given task id in.
+     * @param aOptions The activity options to use for restoration. Can be null.
+     * @param onTop If the stack for the task should be the topmost on the display.
+     */
+    TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode,
+            @Nullable ActivityOptions aOptions, boolean onTop) {
+        // If options are set, ensure that we are attempting to actually restore a task
+        if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
+            throw new IllegalArgumentException("Should not specify activity options for non-restore"
+                    + " lookup");
+        }
+
+        int numDisplays = mActivityDisplays.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final TaskRecord task = stack.taskForIdLocked(id);
+                if (task == null) {
+                    continue;
+                }
+                if (aOptions != null) {
+                    // Resolve the stack the task should be placed in now based on options
+                    // and reparent if needed.
+                    final ActivityStack launchStack = getLaunchStack(null, aOptions, task, onTop);
+                    if (launchStack != null && stack != launchStack) {
+                        final int reparentMode = onTop
+                                ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
+                        task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
+                                "anyTaskForIdLocked");
+                    }
+                }
+                return task;
+            }
+        }
+
+        // If we are matching stack tasks only, return now
+        if (matchMode == MATCH_TASK_IN_STACKS_ONLY) {
+            return null;
+        }
+
+        // Otherwise, check the recent tasks and return if we find it there and we are not restoring
+        // the task from recents
+        if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
+        final TaskRecord task = mRecentTasks.getTask(id);
+
+        if (task == null) {
+            if (DEBUG_RECENTS) {
+                Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
+            }
+
+            return null;
+        }
+
+        if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
+            return task;
+        }
+
+        // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+        if (!restoreRecentTaskLocked(task, aOptions, onTop)) {
+            if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
+                    "Couldn't restore task id=" + id + " found in recents");
+            return null;
+        }
+        if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
+        return task;
+    }
+
+    ActivityRecord isInAnyStackLocked(IBinder token) {
+        int numDisplays = mActivityDisplays.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord r = stack.isInStackLocked(token);
+                if (r != null) {
+                    return r;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Detects whether we should show a lock screen in front of this task for a locked user.
+     * <p>
+     * We'll do this if either of the following holds:
+     * <ul>
+     *   <li>The top activity explicitly belongs to {@param userId}.</li>
+     *   <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
+     * </ul>
+     *
+     * @return {@code true} if the top activity looks like it belongs to {@param userId}.
+     */
+    private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) {
+        // To handle the case that work app is in the task but just is not the top one.
+        final ActivityRecord activityRecord = task.getTopActivity();
+        final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
+
+        return (activityRecord != null && activityRecord.userId == userId)
+                || (resultTo != null && resultTo.userId == userId);
+    }
+
+    /**
+     * Find all visible task stacks containing {@param userId} and intercept them with an activity
+     * to block out the contents and possibly start a credential-confirming intent.
+     *
+     * @param userId user handle for the locked managed profile.
+     */
+    void lockAllProfileTasks(@UserIdInt int userId) {
+        mWindowManager.deferSurfaceLayout();
+        try {
+            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                    final ActivityStack stack = display.getChildAt(stackNdx);
+                    final List<TaskRecord> tasks = stack.getAllTasks();
+                    for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
+                        final TaskRecord task = tasks.get(taskNdx);
+
+                        // Check the task for a top activity belonging to userId, or returning a
+                        // result to an activity belonging to userId. Example case: a document
+                        // picker for personal files, opened by a work app, should still get locked.
+                        if (taskTopActivityIsUser(task, userId)) {
+                            mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
+                                    task.taskId, userId);
+                        }
+                    }
+                }
+            }
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+        }
+    }
+
+    void setNextTaskIdForUserLocked(int taskId, int userId) {
+        final int currentTaskId = mCurTaskIdForUser.get(userId, -1);
+        if (taskId > currentTaskId) {
+            mCurTaskIdForUser.put(userId, taskId);
+        }
+    }
+
+    static int nextTaskIdForUser(int taskId, int userId) {
+        int nextTaskId = taskId + 1;
+        if (nextTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) {
+            // Wrap around as there will be smaller task ids that are available now.
+            nextTaskId -= MAX_TASK_IDS_PER_USER;
+        }
+        return nextTaskId;
+    }
+
+    int getNextTaskIdForUserLocked(int userId) {
+        final int currentTaskId = mCurTaskIdForUser.get(userId, userId * MAX_TASK_IDS_PER_USER);
+        // for a userId u, a taskId can only be in the range
+        // [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER
+        // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
+        int candidateTaskId = nextTaskIdForUser(currentTaskId, userId);
+        while (mRecentTasks.containsTaskId(candidateTaskId, userId)
+                || anyTaskForIdLocked(
+                        candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
+            candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
+            if (candidateTaskId == currentTaskId) {
+                // Something wrong!
+                // All MAX_TASK_IDS_PER_USER task ids are taken up by running tasks for this user
+                throw new IllegalStateException("Cannot get an available task id."
+                        + " Reached limit of " + MAX_TASK_IDS_PER_USER
+                        + " running tasks per user.");
+            }
+        }
+        mCurTaskIdForUser.put(userId, candidateTaskId);
+        return candidateTaskId;
+    }
+
+    boolean attachApplicationLocked(WindowProcessController app) throws RemoteException {
+        final String processName = app.mName;
+        boolean didSomething = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (!isTopDisplayFocusedStack(stack)) {
+                    continue;
+                }
+                stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
+                final ActivityRecord top = stack.topRunningActivityLocked();
+                final int size = mTmpActivityList.size();
+                for (int i = 0; i < size; i++) {
+                    final ActivityRecord activity = mTmpActivityList.get(i);
+                    if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
+                            && processName.equals(activity.processName)) {
+                        try {
+                            if (realStartActivityLocked(activity, app,
+                                    top == activity /* andResume */, true /* checkConfig */)) {
+                                didSomething = true;
+                            }
+                        } catch (RemoteException e) {
+                            Slog.w(TAG, "Exception in new application when starting activity "
+                                    + top.intent.getComponent().flattenToShortString(), e);
+                            throw e;
+                        }
+                    }
+                }
+            }
+        }
+        if (!didSomething) {
+            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        }
+        return didSomething;
+    }
+
+    boolean allResumedActivitiesIdle() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            // TODO(b/117135575): Check resumed activities on all visible stacks.
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            if (display.isSleeping()) {
+                // No resumed activities while display is sleeping.
+                continue;
+            }
+
+            // If the focused stack is not null or not empty, there should have some activities
+            // resuming or resumed. Make sure these activities are idle.
+            final ActivityStack stack = display.getFocusedStack();
+            if (stack == null || stack.numActivities() == 0) {
+                continue;
+            }
+            final ActivityRecord resumedActivity = stack.getResumedActivity();
+            if (resumedActivity == null || !resumedActivity.idle) {
+                if (DEBUG_STATES) {
+                    Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
+                            + stack.mStackId + " " + resumedActivity + " not idle");
+                }
+                return false;
+            }
+        }
+        // Send launch end powerhint when idle
+        sendPowerHintForLaunchEndIfNeeded();
+        return true;
+    }
+
+    private boolean allResumedActivitiesVisible() {
+        boolean foundResumed = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord r = stack.getResumedActivity();
+                if (r != null) {
+                    if (!r.nowVisible || mActivitiesWaitingForVisibleActivity.contains(r)) {
+                        return false;
+                    }
+                    foundResumed = true;
+                }
+            }
+        }
+        return foundResumed;
+    }
+
+    private void executeAppTransitionForAllDisplay() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            display.getWindowContainerController().executeAppTransition();
+        }
+    }
+
+    /**
+     * Pause all activities in either all of the stacks or just the back stacks.
+     * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
+     * @param resuming The resuming activity.
+     * @param dontWait The resuming activity isn't going to wait for all activities to be paused
+     *                 before resuming.
+     * @return true if any activity was paused as a result of this call.
+     */
+    boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
+        boolean someActivityPaused = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            someActivityPaused |= mActivityDisplays.get(displayNdx)
+                    .pauseBackStacks(userLeaving, resuming, dontWait);
+        }
+        return someActivityPaused;
+    }
+
+    boolean allPausedActivitiesComplete() {
+        boolean pausing = true;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord r = stack.mPausingActivity;
+                if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
+                    if (DEBUG_STATES) {
+                        Slog.d(TAG_STATES,
+                                "allPausedActivitiesComplete: r=" + r + " state=" + r.getState());
+                        pausing = false;
+                    } else {
+                        return false;
+                    }
+                }
+            }
+        }
+        return pausing;
+    }
+
+    void cancelInitializingActivities() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.cancelInitializingActivities();
+            }
+        }
+    }
+
+    void waitActivityVisible(ComponentName name, WaitResult result, long startTimeMs) {
+        final WaitInfo waitInfo = new WaitInfo(name, result, startTimeMs);
+        mWaitingForActivityVisible.add(waitInfo);
+    }
+
+    void cleanupActivity(ActivityRecord r) {
+        // Make sure this record is no longer in the pending finishes list.
+        // This could happen, for example, if we are trimming activities
+        // down to the max limit while they are still waiting to finish.
+        mFinishingActivities.remove(r);
+        mActivitiesWaitingForVisibleActivity.remove(r);
+
+        for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
+            if (mWaitingForActivityVisible.get(i).matches(r.realActivity)) {
+                mWaitingForActivityVisible.remove(i);
+            }
+        }
+    }
+
+    void reportActivityVisibleLocked(ActivityRecord r) {
+        sendWaitingVisibleReportLocked(r);
+    }
+
+    void sendWaitingVisibleReportLocked(ActivityRecord r) {
+        boolean changed = false;
+        for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
+            final WaitInfo w = mWaitingForActivityVisible.get(i);
+            if (w.matches(r.realActivity)) {
+                final WaitResult result = w.getResult();
+                changed = true;
+                result.timeout = false;
+                result.who = w.getComponent();
+                result.totalTime = SystemClock.uptimeMillis() - w.getStartTime();
+                mWaitingForActivityVisible.remove(w);
+            }
+        }
+        if (changed) {
+            mService.mGlobalLock.notifyAll();
+        }
+    }
+
+    void reportWaitingActivityLaunchedIfNeeded(ActivityRecord r, int result) {
+        if (mWaitingActivityLaunched.isEmpty()) {
+            return;
+        }
+
+        if (result != START_DELIVERED_TO_TOP && result != START_TASK_TO_FRONT) {
+            return;
+        }
+
+        boolean changed = false;
+
+        for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
+            WaitResult w = mWaitingActivityLaunched.remove(i);
+            if (w.who == null) {
+                changed = true;
+                w.result = result;
+
+                // Unlike START_TASK_TO_FRONT, When an intent is delivered to top, there
+                // will be no followup launch signals. Assign the result and launched component.
+                if (result == START_DELIVERED_TO_TOP) {
+                    w.who = r.realActivity;
+                }
+            }
+        }
+
+        if (changed) {
+            mService.mGlobalLock.notifyAll();
+        }
+    }
+
+    void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime) {
+        boolean changed = false;
+        for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
+            WaitResult w = mWaitingActivityLaunched.remove(i);
+            if (w.who == null) {
+                changed = true;
+                w.timeout = timeout;
+                if (r != null) {
+                    w.who = new ComponentName(r.info.packageName, r.info.name);
+                }
+                w.totalTime = totalTime;
+                // Do not modify w.result.
+            }
+        }
+        if (changed) {
+            mService.mGlobalLock.notifyAll();
+        }
+    }
+
+    ActivityRecord topRunningActivityLocked() {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
+            if (topActivity != null) {
+                return topActivity;
+            }
+        }
+        return null;
+    }
+
+    @VisibleForTesting
+    void getRunningTasks(int maxNum, List<RunningTaskInfo> list,
+            @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode,
+            int callingUid, boolean allowed) {
+        mRunningTasks.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode,
+                mActivityDisplays, callingUid, allowed);
+    }
+
+    ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
+            ProfilerInfo profilerInfo) {
+        final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
+        if (aInfo != null) {
+            // Store the found target back into the intent, because now that
+            // we have it we never want to do this again.  For example, if the
+            // user navigates back to this point in the history, we should
+            // always restart the exact same activity.
+            intent.setComponent(new ComponentName(
+                    aInfo.applicationInfo.packageName, aInfo.name));
+
+            // Don't debug things in the system process
+            if (!aInfo.processName.equals("system")) {
+                if ((startFlags & (START_FLAG_DEBUG | START_FLAG_NATIVE_DEBUGGING
+                        | START_FLAG_TRACK_ALLOCATION)) != 0 || profilerInfo != null) {
+                    /**
+                     * Assume safe to call into AMS synchronously because the call that set these
+                     * flags should have originated from AMS which will already have its lock held.
+                     * @see ActivityManagerService#startActivityAndWait(IApplicationThread, String,
+                     * Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle, int)
+                     * TODO(b/80414790): Investigate a better way of untangling this.
+                     */
+                    mService.mAmInternal.setDebugFlagsForStartingActivity(
+                            aInfo, startFlags, profilerInfo);
+                }
+            }
+            final String intentLaunchToken = intent.getLaunchToken();
+            if (aInfo.launchToken == null && intentLaunchToken != null) {
+                aInfo.launchToken = intentLaunchToken;
+            }
+        }
+        return aInfo;
+    }
+
+    ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
+            int filterCallingUid) {
+        synchronized (mService.mGlobalLock) {
+            try {
+                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
+                int modifiedFlags = flags
+                        | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
+                if (intent.isWebIntent()
+                            || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
+                    modifiedFlags |= PackageManager.MATCH_INSTANT;
+                }
+
+                // In order to allow cross-profile lookup, we clear the calling identity here.
+                // Note the binder identity won't affect the result, but filterCallingUid will.
+
+                // Cross-user/profile call check are done at the entry points
+                // (e.g. AMS.startActivityAsUser).
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    return mService.getPackageManagerInternalLocked().resolveIntent(
+                            intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            } finally {
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            }
+        }
+    }
+
+    ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
+            ProfilerInfo profilerInfo, int userId, int filterCallingUid) {
+        final ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId, 0, filterCallingUid);
+        return resolveActivity(intent, rInfo, startFlags, profilerInfo);
+    }
+
+    private boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
+            boolean andResume, boolean checkConfig) throws RemoteException {
+
+        if (!allPausedActivitiesComplete()) {
+            // While there are activities pausing we skipping starting any new activities until
+            // pauses are complete. NOTE: that we also do this for activities that are starting in
+            // the paused state because they will first be resumed then paused on the client side.
+            if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
+                    "realStartActivityLocked: Skipping start of r=" + r
+                    + " some activities pausing...");
+            return false;
+        }
+
+        final TaskRecord task = r.getTask();
+        final ActivityStack stack = task.getStack();
+
+        beginDeferResume();
+
+        try {
+            r.startFreezingScreenLocked(proc, 0);
+
+            // schedule launch ticks to collect information about slow apps.
+            r.startLaunchTickingLocked();
+
+            r.setProcess(proc);
+
+            if (getKeyguardController().isKeyguardLocked()) {
+                r.notifyUnknownVisibilityLaunched();
+            }
+
+            // Have the window manager re-evaluate the orientation of the screen based on the new
+            // activity order.  Note that as a result of this, it can call back into the activity
+            // manager with a new orientation.  We don't care about that, because the activity is
+            // not currently running so we are just restarting it anyway.
+            if (checkConfig) {
+                // Deferring resume here because we're going to launch new activity shortly.
+                // We don't want to perform a redundant launch of the same record while ensuring
+                // configurations and trying to resume top activity of focused stack.
+                ensureVisibilityAndConfig(r, r.getDisplayId(),
+                        false /* markFrozenIfConfigChanged */, true /* deferResume */);
+            }
+
+            if (r.getStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
+                    true /* isTop */)) {
+                // We only set the visibility to true if the activity is allowed to be visible
+                // based on
+                // keyguard state. This avoids setting this into motion in window manager that is
+                // later cancelled due to later calls to ensure visible activities that set
+                // visibility back to false.
+                r.setVisibility(true);
+            }
+
+            final int applicationInfoUid =
+                    (r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
+            if ((r.userId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
+                Slog.wtf(TAG,
+                        "User ID for activity changing for " + r
+                                + " appInfo.uid=" + r.appInfo.uid
+                                + " info.ai.uid=" + applicationInfoUid
+                                + " old=" + r.app + " new=" + proc);
+            }
+
+            proc.clearWaitingToKill();
+            r.launchCount++;
+            r.lastLaunchTime = SystemClock.uptimeMillis();
+
+            if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
+
+            proc.addActivityIfNeeded(r);
+            proc.updateProcessInfo(false, true, true, true);
+
+            final LockTaskController lockTaskController = mService.getLockTaskController();
+            if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
+                    || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV
+                    || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED
+                            && lockTaskController.getLockTaskModeState()
+                                    == LOCK_TASK_MODE_LOCKED)) {
+                lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
+            }
+
+            try {
+                if (!proc.hasThread()) {
+                    throw new RemoteException();
+                }
+                List<ResultInfo> results = null;
+                List<ReferrerIntent> newIntents = null;
+                if (andResume) {
+                    // We don't need to deliver new intents and/or set results if activity is going
+                    // to pause immediately after launch.
+                    results = r.results;
+                    newIntents = r.newIntents;
+                }
+                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+                        "Launching: " + r + " icicle=" + r.icicle + " with results=" + results
+                                + " newIntents=" + newIntents + " andResume=" + andResume);
+                EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.userId,
+                        System.identityHashCode(r), task.taskId, r.shortComponentName);
+                if (r.isActivityTypeHome()) {
+                    // Home process is the root process of the task.
+                    mService.mHomeProcess = task.mActivities.get(0).app;
+                }
+                mService.getPackageManagerInternalLocked().notifyPackageUse(
+                        r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
+                r.sleeping = false;
+                r.forceNewConfig = false;
+                mService.getAppWarningsLocked().onStartActivity(r);
+                r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
+                ProfilerInfo profilerInfo = proc.onStartActivity(mService.mTopProcessState);
+
+                // Because we could be starting an Activity in the system process this may not go
+                // across a Binder interface which would create a new Configuration. Consequently
+                // we have to always create a new Configuration here.
+
+                final MergedConfiguration mergedConfiguration = new MergedConfiguration(
+                        proc.getConfiguration(), r.getMergedOverrideConfiguration());
+                r.setLastReportedConfiguration(mergedConfiguration);
+
+                logIfTransactionTooLarge(r.intent, r.icicle);
+
+
+                // Create activity launch transaction.
+                final ClientTransaction clientTransaction = ClientTransaction.obtain(
+                        proc.getThread(), r.appToken);
+
+                final DisplayWindowController dwc = r.getDisplay().getWindowContainerController();
+                clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
+                        System.identityHashCode(r), r.info,
+                        // TODO: Have this take the merged configuration instead of separate global
+                        // and override configs.
+                        mergedConfiguration.getGlobalConfiguration(),
+                        mergedConfiguration.getOverrideConfiguration(), r.compat,
+                        r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
+                        r.icicle, r.persistentState, results, newIntents,
+                        dwc.isNextTransitionForward(), profilerInfo));
+
+                // Set desired final state.
+                final ActivityLifecycleItem lifecycleItem;
+                if (andResume) {
+                    lifecycleItem = ResumeActivityItem.obtain(dwc.isNextTransitionForward());
+                } else {
+                    lifecycleItem = PauseActivityItem.obtain();
+                }
+                clientTransaction.setLifecycleStateRequest(lifecycleItem);
+
+                // Schedule transaction.
+                mService.getLifecycleManager().scheduleTransaction(clientTransaction);
+
+                if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
+                        && mService.mHasHeavyWeightFeature) {
+                    // This may be a heavy-weight process! Note that the package manager will ensure
+                    // that only activity can run in the main process of the .apk, which is the only
+                    // thing that will be considered heavy-weight.
+                    if (proc.mName.equals(proc.mInfo.packageName)) {
+                        if (mService.mHeavyWeightProcess != null
+                                && mService.mHeavyWeightProcess != proc) {
+                            Slog.w(TAG, "Starting new heavy weight process " + proc
+                                    + " when already running "
+                                    + mService.mHeavyWeightProcess);
+                        }
+                        mService.setHeavyWeightProcess(r);
+                    }
+                }
+
+            } catch (RemoteException e) {
+                if (r.launchFailed) {
+                    // This is the second time we failed -- finish activity and give up.
+                    Slog.e(TAG, "Second failure launching "
+                            + r.intent.getComponent().flattenToShortString() + ", giving up", e);
+                    proc.appDied();
+                    stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
+                            "2nd-crash", false);
+                    return false;
+                }
+
+                // This is the first time we failed -- restart process and
+                // retry.
+                r.launchFailed = true;
+                proc.removeActivity(r);
+                throw e;
+            }
+        } finally {
+            endDeferResume();
+        }
+
+        r.launchFailed = false;
+        if (stack.updateLRUListLocked(r)) {
+            Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list");
+        }
+
+        // TODO(lifecycler): Resume or pause requests are done as part of launch transaction,
+        // so updating the state should be done accordingly.
+        if (andResume && readyToResume()) {
+            // As part of the process of launching, ActivityThread also performs
+            // a resume.
+            stack.minimalResumeActivityLocked(r);
+        } else {
+            // This activity is not starting in the resumed state... which should look like we asked
+            // it to pause+stop (but remain visible), and it has done so and reported back the
+            // current icicle and other state.
+            if (DEBUG_STATES) Slog.v(TAG_STATES,
+                    "Moving to PAUSED: " + r + " (starting in paused state)");
+            r.setState(PAUSED, "realStartActivityLocked");
+        }
+
+        // Launch the new version setup screen if needed.  We do this -after-
+        // launching the initial activity (that is, home), so that it can have
+        // a chance to initialize itself while in the background, making the
+        // switch back to it faster and look better.
+        if (isTopDisplayFocusedStack(stack)) {
+            mService.getActivityStartController().startSetupActivity();
+        }
+
+        // Update any services we are bound to that might care about whether
+        // their client may have activities.
+        if (r.app != null) {
+            r.app.updateServiceConnectionActivities();
+        }
+
+        return true;
+    }
+
+    /**
+     * Ensure all activities visibility, update orientation and configuration.
+     *
+     * @param starting The currently starting activity or {@code null} if there is none.
+     * @param displayId The id of the display where operation is executed.
+     * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
+     *                                  {@code true} if config changed.
+     * @param deferResume Whether to defer resume while updating config.
+     * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
+     *         because of configuration update.
+     */
+    boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
+            boolean markFrozenIfConfigChanged, boolean deferResume) {
+        // First ensure visibility without updating the config just yet. We need this to know what
+        // activities are affecting configuration now.
+        // Passing null here for 'starting' param value, so that visibility of actual starting
+        // activity will be properly updated.
+        ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+                false /* preserveWindows */, false /* notifyClients */);
+
+        if (displayId == INVALID_DISPLAY) {
+            // The caller didn't provide a valid display id, skip updating config.
+            return true;
+        }
+
+        // Force-update the orientation from the WindowManager, since we need the true configuration
+        // to send to the client now.
+        final Configuration config = mWindowManager.updateOrientationFromAppTokens(
+                getDisplayOverrideConfiguration(displayId),
+                starting != null && starting.mayFreezeScreenLocked(starting.app)
+                        ? starting.appToken : null,
+                displayId, true /* forceUpdate */);
+        if (starting != null && markFrozenIfConfigChanged && config != null) {
+            starting.frozenBeforeDestroy = true;
+        }
+
+        // Update the configuration of the activities on the display.
+        return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
+                displayId);
+    }
+
+    private void logIfTransactionTooLarge(Intent intent, Bundle icicle) {
+        int extrasSize = 0;
+        if (intent != null) {
+            final Bundle extras = intent.getExtras();
+            if (extras != null) {
+                extrasSize = extras.getSize();
+            }
+        }
+        int icicleSize = (icicle == null ? 0 : icicle.getSize());
+        if (extrasSize + icicleSize > 200000) {
+            Slog.e(TAG, "Transaction too large, intent: " + intent + ", extras size: " + extrasSize
+                    + ", icicle size: " + icicleSize);
+        }
+    }
+
+    void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
+        // Is this activity's application already running?
+        final WindowProcessController wpc =
+                mService.getProcessController(r.processName, r.info.applicationInfo.uid);
+
+        if (wpc != null && wpc.hasThread()) {
+            try {
+                if ((r.info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
+                        || !"android".equals(r.info.packageName)) {
+                    // Don't add this if it is a platform component that is marked to run in
+                    // multiple processes, because this is actually part of the framework so doesn't
+                    // make sense to track as a separate apk in the process.
+                    wpc.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode);
+                }
+                realStartActivityLocked(r, wpc, andResume, checkConfig);
+                return;
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Exception when starting activity "
+                        + r.intent.getComponent().flattenToShortString(), e);
+            }
+
+            // If a dead object exception was thrown -- fall through to
+            // restart the application.
+        }
+
+        // Post message to start process to avoid possible deadlock of calling into AMS with the
+        // ATMS lock held.
+        final Message msg = PooledLambda.obtainMessage(
+                ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
+                r.info.applicationInfo, true, "activity", r.intent.getComponent());
+        mService.mH.sendMessage(msg);
+    }
+
+    void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
+        boolean sendHint = forceSend;
+
+        if (!sendHint) {
+            // Send power hint if we don't know what we're launching yet
+            sendHint = targetActivity == null || targetActivity.app == null;
+        }
+
+        if (!sendHint) { // targetActivity != null
+            // Send power hint when the activity's process is different than the current resumed
+            // activity on all displays, or if there are no resumed activities in the system.
+            boolean noResumedActivities = true;
+            boolean allFocusedProcessesDiffer = true;
+            for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+                final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+                final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
+                final WindowProcessController resumedActivityProcess =
+                    resumedActivity == null ? null : resumedActivity.app;
+
+                noResumedActivities &= resumedActivityProcess == null;
+                if (resumedActivityProcess != null) {
+                    allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
+                }
+            }
+            sendHint = noResumedActivities || allFocusedProcessesDiffer;
+        }
+
+        if (sendHint && mService.mPowerManagerInternal != null) {
+            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
+            mPowerHintSent = true;
+        }
+    }
+
+    void sendPowerHintForLaunchEndIfNeeded() {
+        // Trigger launch power hint if activity is launched
+        if (mPowerHintSent && mService.mPowerManagerInternal != null) {
+            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
+            mPowerHintSent = false;
+        }
+    }
+
+    boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
+            int requestCode, int callingPid, int callingUid, String callingPackage,
+            boolean ignoreTargetSecurity, boolean launchingInTask,
+            WindowProcessController callerApp, ActivityRecord resultRecord,
+            ActivityStack resultStack) {
+        final boolean isCallerRecents = mService.getRecentTasks() != null
+                && mService.getRecentTasks().isCallerRecents(callingUid);
+        final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
+                callingUid);
+        if (startAnyPerm == PERMISSION_GRANTED || (isCallerRecents && launchingInTask)) {
+            // If the caller has START_ANY_ACTIVITY, ignore all checks below. In addition, if the
+            // caller is the recents component and we are specifically starting an activity in an
+            // existing task, then also allow the activity to be fully relaunched.
+            return true;
+        }
+        final int componentRestriction = getComponentRestrictionForCallingPackage(
+                aInfo, callingPackage, callingPid, callingUid, ignoreTargetSecurity);
+        final int actionRestriction = getActionRestrictionForCallingPackage(
+                intent.getAction(), callingPackage, callingPid, callingUid);
+        if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
+                || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
+            if (resultRecord != null) {
+                resultStack.sendActivityResultLocked(-1,
+                        resultRecord, resultWho, requestCode,
+                        Activity.RESULT_CANCELED, null);
+            }
+            final String msg;
+            if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
+                msg = "Permission Denial: starting " + intent.toString()
+                        + " from " + callerApp + " (pid=" + callingPid
+                        + ", uid=" + callingUid + ")" + " with revoked permission "
+                        + ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction());
+            } else if (!aInfo.exported) {
+                msg = "Permission Denial: starting " + intent.toString()
+                        + " from " + callerApp + " (pid=" + callingPid
+                        + ", uid=" + callingUid + ")"
+                        + " not exported from uid " + aInfo.applicationInfo.uid;
+            } else {
+                msg = "Permission Denial: starting " + intent.toString()
+                        + " from " + callerApp + " (pid=" + callingPid
+                        + ", uid=" + callingUid + ")"
+                        + " requires " + aInfo.permission;
+            }
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+
+        if (actionRestriction == ACTIVITY_RESTRICTION_APPOP) {
+            final String message = "Appop Denial: starting " + intent.toString()
+                    + " from " + callerApp + " (pid=" + callingPid
+                    + ", uid=" + callingUid + ")"
+                    + " requires " + AppOpsManager.permissionToOp(
+                            ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction()));
+            Slog.w(TAG, message);
+            return false;
+        } else if (componentRestriction == ACTIVITY_RESTRICTION_APPOP) {
+            final String message = "Appop Denial: starting " + intent.toString()
+                    + " from " + callerApp + " (pid=" + callingPid
+                    + ", uid=" + callingUid + ")"
+                    + " requires appop " + AppOpsManager.permissionToOp(aInfo.permission);
+            Slog.w(TAG, message);
+            return false;
+        }
+
+        return true;
+    }
+
+    /** Check if caller is allowed to launch activities on specified display. */
+    boolean isCallerAllowedToLaunchOnDisplay(int callingPid, int callingUid, int launchDisplayId,
+            ActivityInfo aInfo) {
+        if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check: displayId=" + launchDisplayId
+                + " callingPid=" + callingPid + " callingUid=" + callingUid);
+
+        if (callingPid == -1 && callingUid == -1) {
+            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check: no caller info, skip check");
+            return true;
+        }
+
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(launchDisplayId);
+        if (activityDisplay == null || activityDisplay.isRemoved()) {
+            Slog.w(TAG, "Launch on display check: display not found");
+            return false;
+        }
+
+        // Check if the caller has enough privileges to embed activities and launch to private
+        // displays.
+        final int startAnyPerm = mService.checkPermission(INTERNAL_SYSTEM_WINDOW, callingPid,
+                callingUid);
+        if (startAnyPerm == PERMISSION_GRANTED) {
+            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+                    + " allow launch any on display");
+            return true;
+        }
+
+        // Check if caller is already present on display
+        final boolean uidPresentOnDisplay = activityDisplay.isUidPresent(callingUid);
+
+        final int displayOwnerUid = activityDisplay.mDisplay.getOwnerUid();
+        if (activityDisplay.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID
+                && displayOwnerUid != aInfo.applicationInfo.uid) {
+            // Limit launching on virtual displays, because their contents can be read from Surface
+            // by apps that created them.
+            if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
+                if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+                        + " disallow launch on virtual display for not-embedded activity.");
+                return false;
+            }
+            // Check if the caller is allowed to embed activities from other apps.
+            if (mService.checkPermission(ACTIVITY_EMBEDDING, callingPid, callingUid)
+                    == PERMISSION_DENIED && !uidPresentOnDisplay) {
+                if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+                        + " disallow activity embedding without permission.");
+                return false;
+            }
+        }
+
+        if (!activityDisplay.isPrivate()) {
+            // Anyone can launch on a public display.
+            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+                    + " allow launch on public display");
+            return true;
+        }
+
+        // Check if the caller is the owner of the display.
+        if (displayOwnerUid == callingUid) {
+            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+                    + " allow launch for owner of the display");
+            return true;
+        }
+
+        if (uidPresentOnDisplay) {
+            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+                    + " allow launch for caller present on the display");
+            return true;
+        }
+
+        Slog.w(TAG, "Launch on display check: denied");
+        return false;
+    }
+
+    /** Update lists of UIDs that are present on displays and have access to them. */
+    void updateUIDsPresentOnDisplay() {
+        mDisplayAccessUIDs.clear();
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+            // Only bother calculating the whitelist for private displays
+            if (activityDisplay.isPrivate()) {
+                mDisplayAccessUIDs.append(
+                        activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
+            }
+        }
+        // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
+        mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
+    }
+
+    UserInfo getUserInfo(int userId) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return UserManager.get(mService.mContext).getUserInfo(userId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private int getComponentRestrictionForCallingPackage(ActivityInfo activityInfo,
+            String callingPackage, int callingPid, int callingUid, boolean ignoreTargetSecurity) {
+        if (!ignoreTargetSecurity && mService.checkComponentPermission(activityInfo.permission,
+                callingPid, callingUid, activityInfo.applicationInfo.uid, activityInfo.exported)
+                == PERMISSION_DENIED) {
+            return ACTIVITY_RESTRICTION_PERMISSION;
+        }
+
+        if (activityInfo.permission == null) {
+            return ACTIVITY_RESTRICTION_NONE;
+        }
+
+        final int opCode = AppOpsManager.permissionToOpCode(activityInfo.permission);
+        if (opCode == AppOpsManager.OP_NONE) {
+            return ACTIVITY_RESTRICTION_NONE;
+        }
+
+        if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage)
+                != AppOpsManager.MODE_ALLOWED) {
+            if (!ignoreTargetSecurity) {
+                return ACTIVITY_RESTRICTION_APPOP;
+            }
+        }
+
+        return ACTIVITY_RESTRICTION_NONE;
+    }
+
+    private int getActionRestrictionForCallingPackage(String action,
+            String callingPackage, int callingPid, int callingUid) {
+        if (action == null) {
+            return ACTIVITY_RESTRICTION_NONE;
+        }
+
+        String permission = ACTION_TO_RUNTIME_PERMISSION.get(action);
+        if (permission == null) {
+            return ACTIVITY_RESTRICTION_NONE;
+        }
+
+        final PackageInfo packageInfo;
+        try {
+            packageInfo = mService.mContext.getPackageManager()
+                    .getPackageInfo(callingPackage, PackageManager.GET_PERMISSIONS);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.i(TAG, "Cannot find package info for " + callingPackage);
+            return ACTIVITY_RESTRICTION_NONE;
+        }
+
+        if (!ArrayUtils.contains(packageInfo.requestedPermissions, permission)) {
+            return ACTIVITY_RESTRICTION_NONE;
+        }
+
+        if (mService.checkPermission(permission, callingPid, callingUid) == PERMISSION_DENIED) {
+            return ACTIVITY_RESTRICTION_PERMISSION;
+        }
+
+        final int opCode = AppOpsManager.permissionToOpCode(permission);
+        if (opCode == AppOpsManager.OP_NONE) {
+            return ACTIVITY_RESTRICTION_NONE;
+        }
+
+        if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage)
+                != AppOpsManager.MODE_ALLOWED) {
+            return ACTIVITY_RESTRICTION_APPOP;
+        }
+
+        return ACTIVITY_RESTRICTION_NONE;
+    }
+
+    void setLaunchSource(int uid) {
+        mLaunchingActivity.setWorkSource(new WorkSource(uid));
+    }
+
+    void acquireLaunchWakelock() {
+        if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
+            throw new IllegalStateException("Calling must be system uid");
+        }
+        mLaunchingActivity.acquire();
+        if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
+            // To be safe, don't allow the wake lock to be held for too long.
+            mHandler.sendEmptyMessageDelayed(LAUNCH_TIMEOUT_MSG, LAUNCH_TIMEOUT);
+        }
+    }
+
+    /**
+     * Called when all resumed tasks/stacks are idle.
+     * @return the state of mService.mAm.mBooting before this was called.
+     */
+    @GuardedBy("mService")
+    private boolean checkFinishBootingLocked() {
+        final boolean booting = mService.isBooting();
+        boolean enableScreen = false;
+        mService.setBooting(false);
+        if (!mService.isBooted()) {
+            mService.setBooted(true);
+            enableScreen = true;
+        }
+        if (booting || enableScreen) {
+            mService.postFinishBooting(booting, enableScreen);
+        }
+        return booting;
+    }
+
+    // Checked.
+    @GuardedBy("mService")
+    final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
+            boolean processPausingActivities, Configuration config) {
+        if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);
+
+        ArrayList<ActivityRecord> finishes = null;
+        ArrayList<UserState> startingUsers = null;
+        int NS = 0;
+        int NF = 0;
+        boolean booting = false;
+        boolean activityRemoved = false;
+
+        ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        if (r != null) {
+            if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternalLocked: Callers="
+                    + Debug.getCallers(4));
+            mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+            r.finishLaunchTickingLocked();
+            if (fromTimeout) {
+                reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY);
+            }
+
+            // This is a hack to semi-deal with a race condition
+            // in the client where it can be constructed with a
+            // newer configuration from when we asked it to launch.
+            // We'll update with whatever configuration it now says
+            // it used to launch.
+            if (config != null) {
+                r.setLastReportedGlobalConfiguration(config);
+            }
+
+            // We are now idle.  If someone is waiting for a thumbnail from
+            // us, we can now deliver.
+            r.idle = true;
+
+            //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
+
+            // Check if able to finish booting when device is booting and all resumed activities
+            // are idle.
+            if ((mService.isBooting() && allResumedActivitiesIdle()) || fromTimeout) {
+                booting = checkFinishBootingLocked();
+            }
+
+            // When activity is idle, we consider the relaunch must be successful, so let's clear
+            // the flag.
+            r.mRelaunchReason = RELAUNCH_REASON_NONE;
+        }
+
+        if (allResumedActivitiesIdle()) {
+            if (r != null) {
+                mService.scheduleAppGcsLocked();
+            }
+
+            if (mLaunchingActivity.isHeld()) {
+                mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+                if (VALIDATE_WAKE_LOCK_CALLER &&
+                        Binder.getCallingUid() != Process.myUid()) {
+                    throw new IllegalStateException("Calling must be system uid");
+                }
+                mLaunchingActivity.release();
+            }
+            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        }
+
+        // Atomically retrieve all of the other things to do.
+        final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
+                true /* remove */, processPausingActivities);
+        NS = stops != null ? stops.size() : 0;
+        if ((NF = mFinishingActivities.size()) > 0) {
+            finishes = new ArrayList<>(mFinishingActivities);
+            mFinishingActivities.clear();
+        }
+
+        if (mStartingUsers.size() > 0) {
+            startingUsers = new ArrayList<>(mStartingUsers);
+            mStartingUsers.clear();
+        }
+
+        // Stop any activities that are scheduled to do so but have been
+        // waiting for the next one to start.
+        for (int i = 0; i < NS; i++) {
+            r = stops.get(i);
+            final ActivityStack stack = r.getStack();
+            if (stack != null) {
+                if (r.finishing) {
+                    stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
+                            "activityIdleInternalLocked");
+                } else {
+                    stack.stopActivityLocked(r);
+                }
+            }
+        }
+
+        // Finish any activities that are scheduled to do so but have been
+        // waiting for the next one to start.
+        for (int i = 0; i < NF; i++) {
+            r = finishes.get(i);
+            final ActivityStack stack = r.getStack();
+            if (stack != null) {
+                activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
+            }
+        }
+
+        if (!booting) {
+            // Complete user switch
+            if (startingUsers != null) {
+                for (int i = 0; i < startingUsers.size(); i++) {
+                    mService.mAmInternal.finishUserSwitch(startingUsers.get(i));
+                }
+            }
+        }
+
+        mService.mH.post(() -> mService.mAmInternal.trimApplications());
+        //dump();
+        //mWindowManager.dump();
+
+        if (activityRemoved) {
+            resumeFocusedStacksTopActivitiesLocked();
+        }
+
+        return r;
+    }
+
+    boolean handleAppDiedLocked(WindowProcessController app) {
+        boolean hasVisibleActivities = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                hasVisibleActivities |= stack.handleAppDiedLocked(app);
+            }
+        }
+        return hasVisibleActivities;
+    }
+
+    void closeSystemDialogsLocked() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.closeSystemDialogsLocked();
+            }
+        }
+    }
+
+    void removeUserLocked(int userId) {
+        mUserStackInFront.delete(userId);
+    }
+
+    /**
+     * Update the last used stack id for non-current user (current user's last
+     * used stack is the focused stack)
+     */
+    void updateUserStackLocked(int userId, ActivityStack stack) {
+        if (userId != mCurrentUser) {
+            mUserStackInFront.put(userId, stack != null ? stack.getStackId()
+                    : getDefaultDisplay().getHomeStack().mStackId);
+        }
+    }
+
+    /**
+     * @return true if some activity was finished (or would have finished if doit were true).
+     */
+    boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses,
+            boolean doit, boolean evenPersistent, int userId) {
+        boolean didSomething = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (stack.finishDisabledPackageActivitiesLocked(
+                        packageName, filterByClasses, doit, evenPersistent, userId)) {
+                    didSomething = true;
+                }
+            }
+        }
+        return didSomething;
+    }
+
+    void updatePreviousProcessLocked(ActivityRecord r) {
+        // Now that this process has stopped, we may want to consider
+        // it to be the previous app to try to keep around in case
+        // the user wants to return to it.
+
+        // First, found out what is currently the foreground app, so that
+        // we don't blow away the previous app if this activity is being
+        // hosted by the process that is actually still the foreground.
+        WindowProcessController fgApp = null;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (isTopDisplayFocusedStack(stack)) {
+                    final ActivityRecord resumedActivity = stack.getResumedActivity();
+                    if (resumedActivity != null) {
+                        fgApp = resumedActivity.app;
+                    } else if (stack.mPausingActivity != null) {
+                        fgApp = stack.mPausingActivity.app;
+                    }
+                    break;
+                }
+            }
+        }
+
+        // Now set this one as the previous process, only if that really
+        // makes sense to.
+        if (r.hasProcess() && fgApp != null && r.app != fgApp
+                && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
+                && r.app != mService.mHomeProcess) {
+            mService.mPreviousProcess = r.app;
+            mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
+        }
+    }
+
+    boolean resumeFocusedStacksTopActivitiesLocked() {
+        return resumeFocusedStacksTopActivitiesLocked(null, null, null);
+    }
+
+    boolean resumeFocusedStacksTopActivitiesLocked(
+            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
+
+        if (!readyToResume()) {
+            return false;
+        }
+
+        if (targetStack != null && (targetStack.isTopStackOnDisplay()
+                || getTopDisplayFocusedStack() == targetStack)) {
+            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
+        }
+
+        // Resume all top activities in focused stacks on all displays.
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final ActivityStack focusedStack = display.getFocusedStack();
+            if (focusedStack == null) {
+                continue;
+            }
+            final ActivityRecord r = focusedStack.topRunningActivityLocked();
+            if (r == null || !r.isState(RESUMED)) {
+                focusedStack.resumeTopActivityUncheckedLocked(null, null);
+            } else if (r.isState(RESUMED)) {
+                // Kick off any lingering app transitions form the MoveTaskToFront operation.
+                focusedStack.executeAppTransition(targetOptions);
+            }
+        }
+
+        return false;
+    }
+
+    void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.updateActivityApplicationInfoLocked(aInfo);
+            }
+        }
+    }
+
+    /**
+     * Finish the topmost activities in all stacks that belong to the crashed app.
+     * @param app The app that crashed.
+     * @param reason Reason to perform this action.
+     * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
+     */
+    int finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
+        TaskRecord finishedTask = null;
+        ActivityStack focusedStack = getTopDisplayFocusedStack();
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            // It is possible that request to finish activity might also remove its task and stack,
+            // so we need to be careful with indexes in the loop and check child count every time.
+            for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason);
+                if (stack == focusedStack || finishedTask == null) {
+                    finishedTask = t;
+                }
+            }
+        }
+        return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
+    }
+
+    void finishVoiceTask(IVoiceInteractionSession session) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final int numStacks = display.getChildCount();
+            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.finishVoiceTask(session);
+            }
+        }
+    }
+
+    /**
+     * This doesn't just find a task, it also moves the task to front.
+     */
+    void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
+            boolean forceNonResizeable) {
+        ActivityStack currentStack = task.getStack();
+        if (currentStack == null) {
+            Slog.e(TAG, "findTaskToMoveToFront: can't move task="
+                    + task + " to front. Stack is null");
+            return;
+        }
+
+        if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
+            mUserLeaving = true;
+        }
+
+        reason = reason + " findTaskToMoveToFront";
+        boolean reparented = false;
+        if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
+            final Rect bounds = options.getLaunchBounds();
+            task.updateOverrideConfiguration(bounds);
+
+            ActivityStack stack = getLaunchStack(null, options, task, ON_TOP);
+
+            if (stack != currentStack) {
+                moveHomeStackToFrontIfNeeded(flags, stack.getDisplay(), reason);
+                task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
+                        reason);
+                currentStack = stack;
+                reparented = true;
+                // task.reparent() should already placed the task on top,
+                // still need moveTaskToFrontLocked() below for any transition settings.
+            }
+            if (stack.resizeStackWithLaunchBounds()) {
+                resizeStackLocked(stack, bounds, null /* tempTaskBounds */,
+                        null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
+                        true /* allowResizeInDockedMode */, !DEFER_RESUME);
+            } else {
+                // WM resizeTask must be done after the task is moved to the correct stack,
+                // because Task's setBounds() also updates dim layer's bounds, but that has
+                // dependency on the stack.
+                task.resizeWindowContainer();
+            }
+        }
+
+        if (!reparented) {
+            moveHomeStackToFrontIfNeeded(flags, currentStack.getDisplay(), reason);
+        }
+
+        final ActivityRecord r = task.getTopActivity();
+        currentStack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
+                r == null ? null : r.appTimeTracker, reason);
+
+        if (DEBUG_STACK) Slog.d(TAG_STACK,
+                "findTaskToMoveToFront: moved to front of stack=" + currentStack);
+
+        handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY,
+                currentStack, forceNonResizeable);
+    }
+
+    private void moveHomeStackToFrontIfNeeded(int flags, ActivityDisplay display, String reason) {
+        final ActivityStack focusedStack = display.getFocusedStack();
+
+        if ((display.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+                && (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0)
+                || (focusedStack != null && focusedStack.isActivityTypeRecents())) {
+            // We move home stack to front when we are on a fullscreen display and caller has
+            // requested the home activity to move with it. Or the previous stack is recents.
+            display.moveHomeStackToFront(reason);
+        }
+    }
+
+    boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) {
+        // We use the launch bounds in the activity options is the device supports freeform
+        // window management or is launching into the pinned stack.
+        if (options == null || options.getLaunchBounds() == null) {
+            return false;
+        }
+        return (mService.mSupportsPictureInPicture
+                && options.getLaunchWindowingMode() == WINDOWING_MODE_PINNED)
+                || mService.mSupportsFreeformWindowManagement;
+    }
+
+    LaunchParamsController getLaunchParamsController() {
+        return mLaunchParamsController;
+    }
+
+    protected <T extends ActivityStack> T getStack(int stackId) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final T stack = mActivityDisplays.get(i).getStack(stackId);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    /** @see ActivityDisplay#getStack(int, int) */
+    private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+            @Nullable TaskRecord task) {
+        // Preference is given to the activity type for the activity then the task since the type
+        // once set shouldn't change.
+        int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+        if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
+            activityType = task.getActivityType();
+        }
+        if (activityType != ACTIVITY_TYPE_UNDEFINED) {
+            return activityType;
+        }
+        if (options != null) {
+            activityType = options.getLaunchActivityType();
+        }
+        return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD;
+    }
+
+    <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
+        return getLaunchStack(r, options, candidateTask, onTop, INVALID_DISPLAY);
+    }
+
+    /**
+     * Returns the right stack to use for launching factoring in all the input parameters.
+     *
+     * @param r The activity we are trying to launch. Can be null.
+     * @param options The activity options used to the launch. Can be null.
+     * @param candidateTask The possible task the activity might be launched in. Can be null.
+     *
+     * @return The stack to use for the launch or INVALID_STACK_ID.
+     */
+    <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
+            int candidateDisplayId) {
+        int taskId = INVALID_TASK_ID;
+        int displayId = INVALID_DISPLAY;
+        //Rect bounds = null;
+
+        // We give preference to the launch preference in activity options.
+        if (options != null) {
+            taskId = options.getLaunchTaskId();
+            displayId = options.getLaunchDisplayId();
+            // TODO: Need to work this into the equation...
+            //bounds = options.getLaunchBounds();
+        }
+
+        // First preference for stack goes to the task Id set in the activity options. Use the stack
+        // associated with that if possible.
+        if (taskId != INVALID_TASK_ID) {
+            // Temporarily set the task id to invalid in case in re-entry.
+            options.setLaunchTaskId(INVALID_TASK_ID);
+            final TaskRecord task = anyTaskForIdLocked(taskId,
+                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
+            options.setLaunchTaskId(taskId);
+            if (task != null) {
+                return task.getStack();
+            }
+        }
+
+        final int activityType = resolveActivityType(r, options, candidateTask);
+        T stack = null;
+
+        // Next preference for stack goes to the display Id set in the activity options or the
+        // candidate display.
+        if (displayId == INVALID_DISPLAY) {
+            displayId = candidateDisplayId;
+        }
+        if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
+            if (r != null) {
+                stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options);
+                if (stack != null) {
+                    return stack;
+                }
+            }
+            final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
+            if (display != null) {
+                stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+                if (stack != null) {
+                    return stack;
+                }
+            }
+        }
+
+        // Give preference to the stack and display of the input task and activity if they match the
+        // mode we want to launch into.
+        stack = null;
+        ActivityDisplay display = null;
+        if (candidateTask != null) {
+            stack = candidateTask.getStack();
+        }
+        if (stack == null && r != null) {
+            stack = r.getStack();
+        }
+        if (stack != null) {
+            display = stack.getDisplay();
+            if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
+                final int windowingMode =
+                        display.resolveWindowingMode(r, options, candidateTask, activityType);
+                if (stack.isCompatible(windowingMode, activityType)) {
+                    return stack;
+                }
+                if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
+                        && display.getSplitScreenPrimaryStack() == stack
+                        && candidateTask == stack.topTask()) {
+                    // This is a special case when we try to launch an activity that is currently on
+                    // top of split-screen primary stack, but is targeting split-screen secondary.
+                    // In this case we don't want to move it to another stack.
+                    // TODO(b/78788972): Remove after differentiating between preferred and required
+                    // launch options.
+                    return stack;
+                }
+            }
+        }
+
+        if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
+            display = getDefaultDisplay();
+        }
+
+        return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+    }
+
+    /** @return true if activity record is null or can be launched on provided display. */
+    private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) {
+        if (r == null) {
+            return true;
+        }
+        return r.canBeLaunchedOnDisplay(displayId);
+    }
+
+    /**
+     * Get a topmost stack on the display, that is a valid launch stack for specified activity.
+     * If there is no such stack, new dynamic stack can be created.
+     * @param displayId Target display.
+     * @param r Activity that should be launched there.
+     * @param candidateTask The possible task the activity might be put in.
+     * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
+     */
+    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+            @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options) {
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException(
+                    "Display with displayId=" + displayId + " not found.");
+        }
+
+        if (!r.canBeLaunchedOnDisplay(displayId)) {
+            return null;
+        }
+
+        // If {@code r} is already in target display and its task is the same as the candidate task,
+        // the intention should be getting a launch stack for the reusable activity, so we can use
+        // the existing stack.
+        if (r.getDisplayId() == displayId && r.getTask() == candidateTask) {
+            return candidateTask.getStack();
+        }
+
+        // Return the topmost valid stack on the display.
+        for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = activityDisplay.getChildAt(i);
+            if (isValidLaunchStack(stack, displayId, r)) {
+                return stack;
+            }
+        }
+
+        // If there is no valid stack on the external display - check if new dynamic stack will do.
+        if (displayId != DEFAULT_DISPLAY) {
+            return activityDisplay.createStack(
+                    options != null ? options.getLaunchWindowingMode() : r.getWindowingMode(),
+                    options != null ? options.getLaunchActivityType() : r.getActivityType(),
+                    true /*onTop*/);
+        }
+
+        Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
+        return null;
+    }
+
+    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+            @Nullable ActivityOptions options) {
+        return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options);
+    }
+
+    // TODO: Can probably be consolidated into getLaunchStack()...
+    private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) {
+        switch (stack.getActivityType()) {
+            case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
+            case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
+            case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
+        }
+        // There is a 1-to-1 relationship between stack and task when not in
+        // primary split-windowing mode.
+        if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            return false;
+        } else {
+            return r.supportsSplitScreenWindowingMode();
+        }
+    }
+
+    /**
+     * Get next focusable stack in the system. This will search through the stack on the same
+     * display as the current focused stack, looking for a focusable and visible stack, different
+     * from the target stack. If no valid candidates will be found, it will then go through all
+     * displays and stacks in last-focused order.
+     *
+     * @param currentFocus The stack that previously had focus.
+     * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
+     *                     candidate.
+     * @return Next focusable {@link ActivityStack}, {@code null} if not found.
+     */
+    ActivityStack getNextFocusableStackLocked(@NonNull ActivityStack currentFocus,
+            boolean ignoreCurrent) {
+        // First look for next focusable stack on the same display
+        final ActivityDisplay preferredDisplay = currentFocus.getDisplay();
+        final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
+                currentFocus, ignoreCurrent);
+        if (preferredFocusableStack != null) {
+            return preferredFocusableStack;
+        }
+        if (preferredDisplay.supportsSystemDecorations()) {
+            // Stop looking for focusable stack on other displays because the preferred display
+            // supports system decorations. Home activity would be launched on the same display if
+            // no focusable stack found.
+            return null;
+        }
+
+        // Now look through all displays
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            if (display == preferredDisplay) {
+                // We've already checked this one
+                continue;
+            }
+            final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
+                    ignoreCurrent);
+            if (nextFocusableStack != null) {
+                return nextFocusableStack;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Get next valid stack for launching provided activity in the system. This will search across
+     * displays and stacks in last-focused order for a focusable and visible stack, except those
+     * that are on a currently focused display.
+     *
+     * @param r The activity that is being launched.
+     * @param currentFocus The display that previously had focus and thus needs to be ignored when
+     *                     searching for the next candidate.
+     * @return Next valid {@link ActivityStack}, null if not found.
+     */
+    ActivityStack getNextValidLaunchStackLocked(@NonNull ActivityRecord r, int currentFocus) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            if (display.mDisplayId == currentFocus) {
+                continue;
+            }
+            final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r,
+                    null /* options */);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    ActivityRecord getDefaultDisplayHomeActivity() {
+        return getDefaultDisplayHomeActivityForUser(mCurrentUser);
+    }
+
+    ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
+        return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
+    }
+
+    void resizeStackLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
+            Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
+            boolean deferResume) {
+
+        if (stack.inSplitScreenPrimaryWindowingMode()) {
+            resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
+                    preserveWindows, deferResume);
+            return;
+        }
+
+        final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack();
+        if (!allowResizeInDockedMode
+                && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
+            // If the docked stack exists, don't resize non-floating stacks independently of the
+            // size computed from the docked stack size (otherwise they will be out of sync)
+            return;
+        }
+
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
+        mWindowManager.deferSurfaceLayout();
+        try {
+            if (stack.affectedBySplitScreenResize()) {
+                if (bounds == null && stack.inSplitScreenWindowingMode()) {
+                    // null bounds = fullscreen windowing mode...at least for now.
+                    stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                } else if (splitScreenActive) {
+                    // If we are in split-screen mode and this stack support split-screen, then
+                    // it should be split-screen secondary mode. i.e. adjacent to the docked stack.
+                    stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                }
+            }
+            stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
+            if (!deferResume) {
+                stack.ensureVisibleActivitiesConfigurationLocked(
+                        stack.topRunningActivityLocked(), preserveWindows);
+            }
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    void deferUpdateRecentsHomeStackBounds() {
+        deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
+        deferUpdateBounds(ACTIVITY_TYPE_HOME);
+    }
+
+    void deferUpdateBounds(int activityType) {
+        final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+        if (stack != null) {
+            stack.deferUpdateBounds();
+        }
+    }
+
+    void continueUpdateRecentsHomeStackBounds() {
+        continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
+        continueUpdateBounds(ACTIVITY_TYPE_HOME);
+    }
+
+    void continueUpdateBounds(int activityType) {
+        final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+        if (stack != null) {
+            stack.continueUpdateBounds();
+        }
+    }
+
+    void notifyAppTransitionDone() {
+        continueUpdateRecentsHomeStackBounds();
+        for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
+            final int taskId = mResizingTasksDuringAnimation.valueAt(i);
+            final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY);
+            if (task != null) {
+                task.setTaskDockedResizing(false);
+            }
+        }
+        mResizingTasksDuringAnimation.clear();
+    }
+
+    /**
+     * TODO: This should just change the windowing mode and resize vs. actually moving task around.
+     * Can do that once we are no longer using static stack ids.
+     */
+    private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
+            int toDisplayId, boolean onTop) {
+
+        mWindowManager.deferSurfaceLayout();
+        try {
+            final int windowingMode = fromStack.getWindowingMode();
+            final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED;
+            final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId);
+
+            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                // Tell the display we are exiting split-screen mode.
+                toDisplay.onExitingSplitScreenMode();
+                // We are moving all tasks from the docked stack to the fullscreen stack,
+                // which is dismissing the docked stack, so resize all other stacks to
+                // fullscreen here already so we don't end up with resize trashing.
+                for (int i = toDisplay.getChildCount() - 1; i >= 0; --i) {
+                    final ActivityStack otherStack = toDisplay.getChildAt(i);
+                    if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
+                        continue;
+                    }
+                    otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+                }
+
+                // Also disable docked stack resizing since we have manually adjusted the
+                // size of other stacks above and we don't want to trigger a docked stack
+                // resize when we remove task from it below and it is detached from the
+                // display because it no longer contains any tasks.
+                mAllowDockedStackResize = false;
+            }
+
+            // If we are moving from the pinned stack, then the animation takes care of updating
+            // the picture-in-picture mode.
+            final boolean schedulePictureInPictureModeChange = inPinnedWindowingMode;
+            final ArrayList<TaskRecord> tasks = fromStack.getAllTasks();
+
+            if (!tasks.isEmpty()) {
+                mTmpOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                final int size = tasks.size();
+                for (int i = 0; i < size; ++i) {
+                    final TaskRecord task = tasks.get(i);
+                    final ActivityStack toStack = toDisplay.getOrCreateStack(
+                                null, mTmpOptions, task, task.getActivityType(), onTop);
+
+                    if (onTop) {
+                        final boolean isTopTask = i == (size - 1);
+                        // Defer resume until all the tasks have been moved to the fullscreen stack
+                        task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
+                                isTopTask /* animate */, DEFER_RESUME,
+                                schedulePictureInPictureModeChange,
+                                "moveTasksToFullscreenStack - onTop");
+                        MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext,
+                                task.effectiveUid, task.realActivity.flattenToString());
+                    } else {
+                        // Position the tasks in the fullscreen stack in order at the bottom of the
+                        // stack. Also defer resume until all the tasks have been moved to the
+                        // fullscreen stack.
+                        task.reparent(toStack, ON_TOP,
+                                REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
+                                schedulePictureInPictureModeChange,
+                                "moveTasksToFullscreenStack - NOT_onTop");
+                    }
+                }
+            }
+
+            ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+            resumeFocusedStacksTopActivitiesLocked();
+        } finally {
+            mAllowDockedStackResize = true;
+            mWindowManager.continueSurfaceLayout();
+        }
+    }
+
+    void moveTasksToFullscreenStackLocked(ActivityStack fromStack, boolean onTop) {
+        moveTasksToFullscreenStackLocked(fromStack, DEFAULT_DISPLAY, onTop);
+    }
+
+    void moveTasksToFullscreenStackLocked(ActivityStack fromStack, int toDisplayId, boolean onTop) {
+        mWindowManager.inSurfaceTransaction(() ->
+                moveTasksToFullscreenStackInSurfaceTransaction(fromStack, toDisplayId, onTop));
+    }
+
+    void setSplitScreenResizing(boolean resizing) {
+        if (resizing == mDockedStackResizing) {
+            return;
+        }
+
+        mDockedStackResizing = resizing;
+        mWindowManager.setDockedStackResizing(resizing);
+
+        if (!resizing && mHasPendingDockedBounds) {
+            resizeDockedStackLocked(mPendingDockedBounds, mPendingTempDockedTaskBounds,
+                    mPendingTempDockedTaskInsetBounds, mPendingTempOtherTaskBounds,
+                    mPendingTempOtherTaskInsetBounds, PRESERVE_WINDOWS);
+
+            mHasPendingDockedBounds = false;
+            mPendingDockedBounds = null;
+            mPendingTempDockedTaskBounds = null;
+            mPendingTempDockedTaskInsetBounds = null;
+            mPendingTempOtherTaskBounds = null;
+            mPendingTempOtherTaskInsetBounds = null;
+        }
+    }
+
+    void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+            Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
+            boolean preserveWindows) {
+        resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
+                tempOtherTaskBounds, tempOtherTaskInsetBounds, preserveWindows,
+                false /* deferResume */);
+    }
+
+    private void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+            Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
+            boolean preserveWindows, boolean deferResume) {
+
+        if (!mAllowDockedStackResize) {
+            // Docked stack resize currently disabled.
+            return;
+        }
+
+        final ActivityStack stack = getDefaultDisplay().getSplitScreenPrimaryStack();
+        if (stack == null) {
+            Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
+            return;
+        }
+
+        if (mDockedStackResizing) {
+            mHasPendingDockedBounds = true;
+            mPendingDockedBounds = copyOrNull(dockedBounds);
+            mPendingTempDockedTaskBounds = copyOrNull(tempDockedTaskBounds);
+            mPendingTempDockedTaskInsetBounds = copyOrNull(tempDockedTaskInsetBounds);
+            mPendingTempOtherTaskBounds = copyOrNull(tempOtherTaskBounds);
+            mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds);
+        }
+
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
+        mWindowManager.deferSurfaceLayout();
+        try {
+            // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
+            mAllowDockedStackResize = false;
+            ActivityRecord r = stack.topRunningActivityLocked();
+            stack.resize(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds);
+
+            // TODO: Checking for isAttached might not be needed as if the user passes in null
+            // dockedBounds then they want the docked stack to be dismissed.
+            if (stack.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+                    || (dockedBounds == null && !stack.isAttached())) {
+                // The dock stack either was dismissed or went fullscreen, which is kinda the same.
+                // In this case we make all other static stacks fullscreen and move all
+                // docked stack tasks to the fullscreen stack.
+                moveTasksToFullscreenStackLocked(stack, ON_TOP);
+
+                // stack shouldn't contain anymore activities, so nothing to resume.
+                r = null;
+            } else {
+                // Docked stacks occupy a dedicated region on screen so the size of all other
+                // static stacks need to be adjusted so they don't overlap with the docked stack.
+                // We get the bounds to use from window manager which has been adjusted for any
+                // screen controls and is also the same for all stacks.
+                final ActivityDisplay display = getDefaultDisplay();
+                final Rect otherTaskRect = new Rect();
+                for (int i = display.getChildCount() - 1; i >= 0; --i) {
+                    final ActivityStack current = display.getChildAt(i);
+                    if (current.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                        continue;
+                    }
+                    if (!current.affectedBySplitScreenResize()) {
+                        continue;
+                    }
+                    if (mDockedStackResizing && !current.isTopActivityVisible()) {
+                        // Non-visible stacks get resized once we're done with the resize
+                        // interaction.
+                        continue;
+                    }
+                    // Need to set windowing mode here before we try to get the dock bounds.
+                    current.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                    current.getStackDockedModeBounds(
+                            tempOtherTaskBounds /* currentTempTaskBounds */,
+                            tempRect /* outStackBounds */,
+                            otherTaskRect /* outTempTaskBounds */, true /* ignoreVisibility */);
+
+                    resizeStackLocked(current, !tempRect.isEmpty() ? tempRect : null,
+                            !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
+                            tempOtherTaskInsetBounds, preserveWindows,
+                            true /* allowResizeInDockedMode */, deferResume);
+                }
+            }
+            if (!deferResume) {
+                stack.ensureVisibleActivitiesConfigurationLocked(r, preserveWindows);
+            }
+        } finally {
+            mAllowDockedStackResize = true;
+            mWindowManager.continueSurfaceLayout();
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
+        // TODO(multi-display): Pinned stack display should be passed in.
+        final PinnedActivityStack stack = getDefaultDisplay().getPinnedStack();
+        if (stack == null) {
+            Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
+            return;
+        }
+
+        // It is possible for the bounds animation from the WM to call this but be delayed by
+        // another AM call that is holding the AMS lock. In such a case, the pinnedBounds may be
+        // incorrect if AMS.resizeStackWithBoundsFromWindowManager() is already called while waiting
+        // for the AMS lock to be freed. So check and make sure these bounds are still good.
+        final PinnedStackWindowController stackController = stack.getWindowContainerController();
+        if (stackController.pinnedStackResizeDisallowed()) {
+            return;
+        }
+
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack");
+        mWindowManager.deferSurfaceLayout();
+        try {
+            ActivityRecord r = stack.topRunningActivityLocked();
+            Rect insetBounds = null;
+            if (tempPinnedTaskBounds != null && stack.isAnimatingBoundsToFullscreen()) {
+                // Use 0,0 as the position for the inset rect because we are headed for fullscreen.
+                insetBounds = tempRect;
+                insetBounds.top = 0;
+                insetBounds.left = 0;
+                insetBounds.right = tempPinnedTaskBounds.width();
+                insetBounds.bottom = tempPinnedTaskBounds.height();
+            }
+            if (pinnedBounds != null && tempPinnedTaskBounds == null) {
+                // We have finished the animation into PiP, and are resizing the tasks to match the
+                // stack bounds, while layouts are deferred, update any task state as a part of
+                // transitioning it from fullscreen into a floating state.
+                stack.onPipAnimationEndResize();
+            }
+            stack.resize(pinnedBounds, tempPinnedTaskBounds, insetBounds);
+            stack.ensureVisibleActivitiesConfigurationLocked(r, false);
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    private void removeStackInSurfaceTransaction(ActivityStack stack) {
+        final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+        if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
+            /**
+             * Workaround: Force-stop all the activities in the pinned stack before we reparent them
+             * to the fullscreen stack.  This is to guarantee that when we are removing a stack,
+             * that the client receives onStop() before it is reparented.  We do this by detaching
+             * the stack from the display so that it will be considered invisible when
+             * ensureActivitiesVisibleLocked() is called, and all of its activitys will be marked
+             * invisible as well and added to the stopping list.  After which we process the
+             * stopping list by handling the idle.
+             */
+            final PinnedActivityStack pinnedStack = (PinnedActivityStack) stack;
+            pinnedStack.mForceHidden = true;
+            pinnedStack.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+            pinnedStack.mForceHidden = false;
+            activityIdleInternalLocked(null, false /* fromTimeout */,
+                    true /* processPausingActivites */, null /* configuration */);
+
+            // Move all the tasks to the bottom of the fullscreen stack
+            moveTasksToFullscreenStackLocked(pinnedStack, !ON_TOP);
+        } else {
+            for (int i = tasks.size() - 1; i >= 0; i--) {
+                removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
+                        REMOVE_FROM_RECENTS, "remove-stack");
+            }
+        }
+    }
+
+    /**
+     * Removes the stack associated with the given {@param stack}. If the {@param stack} is the
+     * pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
+     * instead moved back onto the fullscreen stack.
+     */
+    void removeStack(ActivityStack stack) {
+        mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stack));
+    }
+
+    /**
+     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+     */
+    void removeStacksInWindowingModes(int... windowingModes) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes);
+        }
+    }
+
+    void removeStacksWithActivityTypes(int... activityTypes) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes);
+        }
+    }
+
+    /**
+     * See {@link #removeTaskByIdLocked(int, boolean, boolean, boolean)}
+     */
+    boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
+            String reason) {
+        return removeTaskByIdLocked(taskId, killProcess, removeFromRecents, !PAUSE_IMMEDIATELY,
+                reason);
+    }
+
+    /**
+     * Removes the task with the specified task id.
+     *
+     * @param taskId Identifier of the task to be removed.
+     * @param killProcess Kill any process associated with the task if possible.
+     * @param removeFromRecents Whether to also remove the task from recents.
+     * @param pauseImmediately Pauses all task activities immediately without waiting for the
+     *                         pause-complete callback from the activity.
+     * @return Returns true if the given task was found and removed.
+     */
+    boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
+            boolean pauseImmediately, String reason) {
+        final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+        if (tr != null) {
+            tr.removeTaskActivitiesLocked(pauseImmediately, reason);
+            cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
+            mService.getLockTaskController().clearLockedTask(tr);
+            if (tr.isPersistable) {
+                mService.notifyTaskPersisterLocked(null, true);
+            }
+            return true;
+        }
+        Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
+        return false;
+    }
+
+    void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
+        if (removeFromRecents) {
+            mRecentTasks.remove(tr);
+        }
+        ComponentName component = tr.getBaseIntent().getComponent();
+        if (component == null) {
+            Slog.w(TAG, "No component for base intent of task: " + tr);
+            return;
+        }
+
+        // Find any running services associated with this app and stop if needed.
+        final Message msg = PooledLambda.obtainMessage(ActivityManagerInternal::cleanUpServices,
+                mService.mAmInternal, tr.userId, component, new Intent(tr.getBaseIntent()));
+        mService.mH.sendMessage(msg);
+
+        if (!killProcess) {
+            return;
+        }
+
+        // Determine if the process(es) for this task should be killed.
+        final String pkg = component.getPackageName();
+        ArrayList<Object> procsToKill = new ArrayList<>();
+        ArrayMap<String, SparseArray<WindowProcessController>> pmap =
+                mService.mProcessNames.getMap();
+        for (int i = 0; i < pmap.size(); i++) {
+
+            SparseArray<WindowProcessController> uids = pmap.valueAt(i);
+            for (int j = 0; j < uids.size(); j++) {
+                WindowProcessController proc = uids.valueAt(j);
+                if (proc.mUserId != tr.userId) {
+                    // Don't kill process for a different user.
+                    continue;
+                }
+                if (proc == mService.mHomeProcess) {
+                    // Don't kill the home process along with tasks from the same package.
+                    continue;
+                }
+                if (!proc.mPkgList.contains(pkg)) {
+                    // Don't kill process that is not associated with this task.
+                    continue;
+                }
+
+                if (!proc.shouldKillProcessForRemovedTask(tr)) {
+                    // Don't kill process(es) that has an activity in a different task that is also
+                    // in recents, or has an activity not stopped.
+                    return;
+                }
+
+                if (proc.hasForegroundServices()) {
+                    // Don't kill process(es) with foreground service.
+                    return;
+                }
+
+                // Add process to kill list.
+                procsToKill.add(proc);
+            }
+        }
+
+        // Kill the running processes. Post on handle since we don't want to hold the service lock
+        // while calling into AM.
+        final Message m = PooledLambda.obtainMessage(
+                ActivityManagerInternal::killProcessesForRemovedTask, mService.mAmInternal,
+                procsToKill);
+        mService.mH.sendMessage(m);
+    }
+
+    /**
+     * Called to restore the state of the task into the stack that it's supposed to go into.
+     *
+     * @param task The recent task to be restored.
+     * @param aOptions The activity options to use for restoration.
+     * @param onTop If the stack for the task should be the topmost on the display.
+     * @return true if the task has been restored successfully.
+     */
+    boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions, boolean onTop) {
+        final ActivityStack stack = getLaunchStack(null, aOptions, task, onTop);
+        final ActivityStack currentStack = task.getStack();
+        if (currentStack != null) {
+            // Task has already been restored once. See if we need to do anything more
+            if (currentStack == stack) {
+                // Nothing else to do since it is already restored in the right stack.
+                return true;
+            }
+            // Remove current stack association, so we can re-associate the task with the
+            // right stack below.
+            currentStack.removeTask(task, "restoreRecentTaskLocked", REMOVE_TASK_MODE_MOVING);
+        }
+
+        stack.addTask(task, onTop, "restoreRecentTask");
+        // TODO: move call for creation here and other place into Stack.addTask()
+        task.createWindowContainer(onTop, true /* showForAllUsers */);
+        if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
+                "Added restored task=" + task + " to stack=" + stack);
+        final ArrayList<ActivityRecord> activities = task.mActivities;
+        for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+            activities.get(activityNdx).createWindowContainer();
+        }
+        return true;
+    }
+
+    @Override
+    public void onRecentTaskAdded(TaskRecord task) {
+        task.touchActiveTime();
+    }
+
+    @Override
+    public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
+        if (wasTrimmed) {
+            // Task was trimmed from the recent tasks list -- remove the active task record as well
+            // since the user won't really be able to go back to it
+            removeTaskByIdLocked(task.taskId, killProcess, false /* removeFromRecents */,
+                    !PAUSE_IMMEDIATELY, "recent-task-trimmed");
+        }
+        task.removedFromRecents();
+    }
+
+    /**
+     * Move stack with all its existing content to specified display.
+     * @param stackId Id of stack to move.
+     * @param displayId Id of display to move stack to.
+     * @param onTop Indicates whether container should be place on top or on bottom.
+     */
+    void moveStackToDisplayLocked(int stackId, int displayId, boolean onTop) {
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown displayId="
+                    + displayId);
+        }
+        final ActivityStack stack = getStack(stackId);
+        if (stack == null) {
+            throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown stackId="
+                    + stackId);
+        }
+
+        final ActivityDisplay currentDisplay = stack.getDisplay();
+        if (currentDisplay == null) {
+            throw new IllegalStateException("moveStackToDisplayLocked: Stack with stack=" + stack
+                    + " is not attached to any display.");
+        }
+
+        if (currentDisplay.mDisplayId == displayId) {
+            throw new IllegalArgumentException("Trying to move stack=" + stack
+                    + " to its current displayId=" + displayId);
+        }
+
+        stack.reparent(activityDisplay, onTop, false /* displayRemoved */);
+        // TODO(multi-display): resize stacks properly if moved from split-screen.
+    }
+
+    /**
+     * Returns the reparent target stack, creating the stack if necessary.  This call also enforces
+     * the various checks on tasks that are going to be reparented from one stack to another.
+     */
+    // TODO: Look into changing users to this method to ActivityDisplay.resolveWindowingMode()
+    ActivityStack getReparentTargetStack(TaskRecord task, ActivityStack stack, boolean toTop) {
+        final ActivityStack prevStack = task.getStack();
+        final int stackId = stack.mStackId;
+        final boolean inMultiWindowMode = stack.inMultiWindowMode();
+
+        // Check that we aren't reparenting to the same stack that the task is already in
+        if (prevStack != null && prevStack.mStackId == stackId) {
+            Slog.w(TAG, "Can not reparent to same stack, task=" + task
+                    + " already in stackId=" + stackId);
+            return prevStack;
+        }
+
+        // Ensure that we aren't trying to move into a multi-window stack without multi-window
+        // support
+        if (inMultiWindowMode && !mService.mSupportsMultiWindow) {
+            throw new IllegalArgumentException("Device doesn't support multi-window, can not"
+                    + " reparent task=" + task + " to stack=" + stack);
+        }
+
+        // Ensure that we're not moving a task to a dynamic stack if device doesn't support
+        // multi-display.
+        if (stack.mDisplayId != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
+            throw new IllegalArgumentException("Device doesn't support multi-display, can not"
+                    + " reparent task=" + task + " to stackId=" + stackId);
+        }
+
+        // Ensure that we aren't trying to move into a freeform stack without freeform support
+        if (stack.getWindowingMode() == WINDOWING_MODE_FREEFORM
+                && !mService.mSupportsFreeformWindowManagement) {
+            throw new IllegalArgumentException("Device doesn't support freeform, can not reparent"
+                    + " task=" + task);
+        }
+
+        // Leave the task in its current stack or a fullscreen stack if it isn't resizeable and the
+        // preferred stack is in multi-window mode.
+        if (inMultiWindowMode && !task.isResizeable()) {
+            Slog.w(TAG, "Can not move unresizeable task=" + task + " to multi-window stack=" + stack
+                    + " Moving to a fullscreen stack instead.");
+            if (prevStack != null) {
+                return prevStack;
+            }
+            stack = stack.getDisplay().createStack(
+                    WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop);
+        }
+        return stack;
+    }
+
+    boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) {
+        final ActivityStack stack = getStack(stackId);
+        if (stack == null) {
+            throw new IllegalArgumentException(
+                    "moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
+        }
+
+        final ActivityRecord r = stack.topRunningActivityLocked();
+        if (r == null) {
+            Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: No top running activity"
+                    + " in stack=" + stack);
+            return false;
+        }
+
+        if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
+            Slog.w(TAG,
+                    "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
+                            + " r=" + r);
+            return false;
+        }
+
+        moveActivityToPinnedStackLocked(r, null /* sourceBounds */, 0f /* aspectRatio */,
+                "moveTopActivityToPinnedStack");
+        return true;
+    }
+
+    void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
+            String reason) {
+
+        mWindowManager.deferSurfaceLayout();
+
+        final ActivityDisplay display = r.getStack().getDisplay();
+        PinnedActivityStack stack = display.getPinnedStack();
+
+        // This will clear the pinned stack by moving an existing task to the full screen stack,
+        // ensuring only one task is present.
+        if (stack != null) {
+            moveTasksToFullscreenStackLocked(stack, !ON_TOP);
+        }
+
+        // Need to make sure the pinned stack exist so we can resize it below...
+        stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
+
+        // Calculate the target bounds here before the task is reparented back into pinned windowing
+        // mode (which will reset the saved bounds)
+        final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
+
+        try {
+            final TaskRecord task = r.getTask();
+            // Resize the pinned stack to match the current size of the task the activity we are
+            // going to be moving is currently contained in. We do this to have the right starting
+            // animation bounds for the pinned stack to the desired bounds the caller wants.
+            resizeStackLocked(stack, task.getOverrideBounds(), null /* tempTaskBounds */,
+                    null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
+                    true /* allowResizeInDockedMode */, !DEFER_RESUME);
+
+            if (task.mActivities.size() == 1) {
+                // Defer resume until below, and do not schedule PiP changes until we animate below
+                task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
+                        false /* schedulePictureInPictureModeChange */, reason);
+            } else {
+                // There are multiple activities in the task and moving the top activity should
+                // reveal/leave the other activities in their original task.
+
+                // Currently, we don't support reparenting activities across tasks in two different
+                // stacks, so instead, just create a new task in the same stack, reparent the
+                // activity into that task, and then reparent the whole task to the new stack. This
+                // ensures that all the necessary work to migrate states in the old and new stacks
+                // is also done.
+                final TaskRecord newTask = task.getStack().createTaskRecord(
+                        getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true);
+                r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
+
+                // Defer resume until below, and do not schedule PiP changes until we animate below
+                newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+                        DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
+            }
+
+            // Reset the state that indicates it can enter PiP while pausing after we've moved it
+            // to the pinned stack
+            r.supportsEnterPipOnTaskSwitch = false;
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+        }
+
+        stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
+                true /* fromFullscreen */);
+
+        // Update the visibility of all activities after the they have been reparented to the new
+        // stack.  This MUST run after the animation above is scheduled to ensure that the windows
+        // drawn signal is scheduled after the bounds animation start call on the bounds animator
+        // thread.
+        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        resumeFocusedStacksTopActivitiesLocked();
+
+        mService.getTaskChangeNotificationController().notifyActivityPinned(r);
+    }
+
+    ActivityRecord findTaskLocked(ActivityRecord r, int preferredDisplayId) {
+        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
+        mTmpFindTaskResult.clear();
+
+        // Looking up task on preferred display first
+        final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId);
+        if (preferredDisplay != null) {
+            preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult);
+            if (mTmpFindTaskResult.mIdealMatch) {
+                return mTmpFindTaskResult.mRecord;
+            }
+        }
+
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            if (display.mDisplayId == preferredDisplayId) {
+                continue;
+            }
+
+            display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult);
+            if (mTmpFindTaskResult.mIdealMatch) {
+                return mTmpFindTaskResult.mRecord;
+            }
+        }
+
+        if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
+        return mTmpFindTaskResult.mRecord;
+    }
+
+    ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
+            boolean compareIntentFilters) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord ar = stack.findActivityLocked(
+                        intent, info, compareIntentFilters);
+                if (ar != null) {
+                    return ar;
+                }
+            }
+        }
+        return null;
+    }
+
+    boolean hasAwakeDisplay() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            if (!display.shouldSleep()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void goingToSleepLocked() {
+        scheduleSleepTimeout();
+        if (!mGoingToSleep.isHeld()) {
+            mGoingToSleep.acquire();
+            if (mLaunchingActivity.isHeld()) {
+                if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
+                    throw new IllegalStateException("Calling must be system uid");
+                }
+                mLaunchingActivity.release();
+                mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+            }
+        }
+
+        applySleepTokensLocked(false /* applyToStacks */);
+
+        checkReadyForSleepLocked(true /* allowDelay */);
+    }
+
+    void prepareForShutdownLocked() {
+        for (int i = 0; i < mActivityDisplays.size(); i++) {
+            createSleepTokenLocked("shutdown", mActivityDisplays.get(i).mDisplayId);
+        }
+    }
+
+    boolean shutdownLocked(int timeout) {
+        goingToSleepLocked();
+
+        boolean timedout = false;
+        final long endTime = System.currentTimeMillis() + timeout;
+        while (true) {
+            if (!putStacksToSleepLocked(true /* allowDelay */, true /* shuttingDown */)) {
+                long timeRemaining = endTime - System.currentTimeMillis();
+                if (timeRemaining > 0) {
+                    try {
+                        mService.mGlobalLock.wait(timeRemaining);
+                    } catch (InterruptedException e) {
+                    }
+                } else {
+                    Slog.w(TAG, "Activity manager shutdown timed out");
+                    timedout = true;
+                    break;
+                }
+            } else {
+                break;
+            }
+        }
+
+        // Force checkReadyForSleep to complete.
+        checkReadyForSleepLocked(false /* allowDelay */);
+
+        return timedout;
+    }
+
+    void comeOutOfSleepIfNeededLocked() {
+        removeSleepTimeouts();
+        if (mGoingToSleep.isHeld()) {
+            mGoingToSleep.release();
+        }
+    }
+
+    void applySleepTokensLocked(boolean applyToStacks) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            // Set the sleeping state of the display.
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final boolean displayShouldSleep = display.shouldSleep();
+            if (displayShouldSleep == display.isSleeping()) {
+                continue;
+            }
+            display.setIsSleeping(displayShouldSleep);
+
+            if (!applyToStacks) {
+                continue;
+            }
+
+            // Set the sleeping state of the stacks on the display.
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (displayShouldSleep) {
+                    stack.goToSleepIfPossible(false /* shuttingDown */);
+                } else {
+                    stack.awakeFromSleepingLocked();
+                    if (stack.isFocusedStackOnDisplay() && !getKeyguardController()
+                            .isKeyguardOrAodShowing(display.mDisplayId)) {
+                        // If the keyguard is unlocked - resume immediately.
+                        // It is possible that the display will not be awake at the time we
+                        // process the keyguard going away, which can happen before the sleep token
+                        // is released. As a result, it is important we resume the activity here.
+                        resumeFocusedStacksTopActivitiesLocked();
+                    }
+                }
+            }
+
+            if (displayShouldSleep || mGoingToSleepActivities.isEmpty()) {
+                continue;
+            }
+            // The display is awake now, so clean up the going to sleep list.
+            for (Iterator<ActivityRecord> it = mGoingToSleepActivities.iterator(); it.hasNext(); ) {
+                final ActivityRecord r = it.next();
+                if (r.getDisplayId() == display.mDisplayId) {
+                    it.remove();
+                }
+            }
+        }
+    }
+
+    void activitySleptLocked(ActivityRecord r) {
+        mGoingToSleepActivities.remove(r);
+        final ActivityStack s = r.getStack();
+        if (s != null) {
+            s.checkReadyForSleep();
+        } else {
+            checkReadyForSleepLocked(true);
+        }
+    }
+
+    void checkReadyForSleepLocked(boolean allowDelay) {
+        if (!mService.isSleepingOrShuttingDownLocked()) {
+            // Do not care.
+            return;
+        }
+
+        if (!putStacksToSleepLocked(allowDelay, false /* shuttingDown */)) {
+            return;
+        }
+
+        // Send launch end powerhint before going sleep
+        sendPowerHintForLaunchEndIfNeeded();
+
+        removeSleepTimeouts();
+
+        if (mGoingToSleep.isHeld()) {
+            mGoingToSleep.release();
+        }
+        if (mService.mShuttingDown) {
+            mService.mGlobalLock.notifyAll();
+        }
+    }
+
+    // Tries to put all activity stacks to sleep. Returns true if all stacks were
+    // successfully put to sleep.
+    private boolean putStacksToSleepLocked(boolean allowDelay, boolean shuttingDown) {
+        boolean allSleep = true;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (allowDelay) {
+                    allSleep &= stack.goToSleepIfPossible(shuttingDown);
+                } else {
+                    stack.goToSleep();
+                }
+            }
+        }
+        return allSleep;
+    }
+
+    boolean reportResumedActivityLocked(ActivityRecord r) {
+        // A resumed activity cannot be stopping. remove from list
+        mStoppingActivities.remove(r);
+
+        final ActivityStack stack = r.getStack();
+        if (isTopDisplayFocusedStack(stack)) {
+            mService.updateUsageStats(r, true);
+        }
+        if (stack.getDisplay().allResumedActivitiesComplete()) {
+            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+            // Make sure activity & window visibility should be identical
+            // for all displays in this stage.
+            executeAppTransitionForAllDisplay();
+            return true;
+        }
+        return false;
+    }
+
+    void handleAppCrashLocked(WindowProcessController app) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.handleAppCrashLocked(app);
+            }
+        }
+    }
+
+    // Called when WindowManager has finished animating the launchingBehind activity to the back.
+    private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
+        final TaskRecord task = r.getTask();
+        final ActivityStack stack = task.getStack();
+
+        r.mLaunchTaskBehind = false;
+        mRecentTasks.add(task);
+        mService.getTaskChangeNotificationController().notifyTaskStackChanged();
+        r.setVisibility(false);
+
+        // When launching tasks behind, update the last active time of the top task after the new
+        // task has been shown briefly
+        final ActivityRecord top = stack.getTopActivity();
+        if (top != null) {
+            top.getTask().touchActiveTime();
+        }
+    }
+
+    void scheduleLaunchTaskBehindComplete(IBinder token) {
+        mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
+    }
+
+    /**
+     * Make sure that all activities that need to be visible in the system actually are and update
+     * their configuration.
+     */
+    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+            boolean preserveWindows) {
+        ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
+                true /* notifyClients */);
+    }
+
+    /**
+     * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
+     */
+    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+            boolean preserveWindows, boolean notifyClients) {
+        getKeyguardController().beginActivityVisibilityUpdate();
+        try {
+            // First the front stacks. In case any are not fullscreen and are in front of home.
+            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                    final ActivityStack stack = display.getChildAt(stackNdx);
+                    stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
+                            notifyClients);
+                }
+            }
+        } finally {
+            getKeyguardController().endActivityVisibilityUpdate();
+        }
+    }
+
+    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.addStartingWindowsForVisibleActivities(taskSwitch);
+            }
+        }
+    }
+
+    void invalidateTaskLayers() {
+        mTaskLayersChanged = true;
+    }
+
+    void rankTaskLayersIfNeeded() {
+        if (!mTaskLayersChanged) {
+            return;
+        }
+        mTaskLayersChanged = false;
+        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            int baseLayer = 0;
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                baseLayer += stack.rankTaskLayers(baseLayer);
+            }
+        }
+    }
+
+    void clearOtherAppTimeTrackers(AppTimeTracker except) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.clearOtherAppTimeTrackers(except);
+            }
+        }
+    }
+
+    void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.scheduleDestroyActivities(app, reason);
+            }
+        }
+    }
+
+    void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
+        // Tasks is non-null only if two or more tasks are found.
+        ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
+        if (tasks == null) {
+            if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
+            return;
+        }
+        // If we have activities in multiple tasks that are in a position to be destroyed,
+        // let's iterate through the tasks and release the oldest one.
+        final int numDisplays = mActivityDisplays.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final int stackCount = display.getChildCount();
+            // Step through all stacks starting from behind, to hit the oldest things first.
+            for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                // Try to release activities in this stack; if we manage to, we are done.
+                if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
+                    return;
+                }
+            }
+        }
+    }
+
+    boolean switchUserLocked(int userId, UserState uss) {
+        final int focusStackId = getTopDisplayFocusedStack().getStackId();
+        // We dismiss the docked stack whenever we switch users.
+        final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
+        if (dockedStack != null) {
+            moveTasksToFullscreenStackLocked(dockedStack, dockedStack.isFocusedStackOnDisplay());
+        }
+        // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
+        // also cause all tasks to be moved to the fullscreen stack at a position that is
+        // appropriate.
+        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+
+        mUserStackInFront.put(mCurrentUser, focusStackId);
+        final int restoreStackId =
+                mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
+        mCurrentUser = userId;
+
+        mStartingUsers.add(uss);
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.switchUserLocked(userId);
+                TaskRecord task = stack.topTask();
+                if (task != null) {
+                    stack.positionChildWindowContainerAtTop(task);
+                }
+            }
+        }
+
+        ActivityStack stack = getStack(restoreStackId);
+        if (stack == null) {
+            stack = getDefaultDisplay().getHomeStack();
+        }
+        final boolean homeInFront = stack.isActivityTypeHome();
+        if (stack.isOnHomeDisplay()) {
+            stack.moveToFront("switchUserOnHomeDisplay");
+        } else {
+            // Stack was moved to another display while user was swapped out.
+            resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
+        }
+        return homeInFront;
+    }
+
+    /** Checks whether the userid is a profile of the current user. */
+    boolean isCurrentProfileLocked(int userId) {
+        if (userId == mCurrentUser) return true;
+        return mService.mAmInternal.isCurrentProfile(userId);
+    }
+
+    /**
+     * Returns whether a stopping activity is present that should be stopped after visible, rather
+     * than idle.
+     * @return {@code true} if such activity is present. {@code false} otherwise.
+     */
+    boolean isStoppingNoHistoryActivity() {
+        // Activities that are marked as nohistory should be stopped immediately after the resumed
+        // activity has become visible.
+        for (ActivityRecord record : mStoppingActivities) {
+            if (record.isNoHistory()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    final ArrayList<ActivityRecord> processStoppingActivitiesLocked(ActivityRecord idleActivity,
+            boolean remove, boolean processPausingActivities) {
+        ArrayList<ActivityRecord> stops = null;
+
+        final boolean nowVisible = allResumedActivitiesVisible();
+        for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+            ActivityRecord s = mStoppingActivities.get(activityNdx);
+            boolean waitingVisible = mActivitiesWaitingForVisibleActivity.contains(s);
+            if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
+                    + " waitingVisible=" + waitingVisible + " finishing=" + s.finishing);
+            if (waitingVisible && nowVisible) {
+                mActivitiesWaitingForVisibleActivity.remove(s);
+                waitingVisible = false;
+                if (s.finishing) {
+                    // If this activity is finishing, it is sitting on top of
+                    // everyone else but we now know it is no longer needed...
+                    // so get rid of it.  Otherwise, we need to go through the
+                    // normal flow and hide it once we determine that it is
+                    // hidden by the activities in front of it.
+                    if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s);
+                    s.setVisibility(false);
+                }
+            }
+            if (remove) {
+                final ActivityStack stack = s.getStack();
+                final boolean shouldSleepOrShutDown = stack != null
+                        ? stack.shouldSleepOrShutDownActivities()
+                        : mService.isSleepingOrShuttingDownLocked();
+                if (!waitingVisible || shouldSleepOrShutDown) {
+                    if (!processPausingActivities && s.isState(PAUSING)) {
+                        // Defer processing pausing activities in this iteration and reschedule
+                        // a delayed idle to reprocess it again
+                        removeTimeoutsForActivityLocked(idleActivity);
+                        scheduleIdleTimeoutLocked(idleActivity);
+                        continue;
+                    }
+
+                    if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
+                    if (stops == null) {
+                        stops = new ArrayList<>();
+                    }
+                    stops.add(s);
+
+                    // Make sure to remove it in all cases in case we entered this block with
+                    // shouldSleepOrShutDown
+                    mActivitiesWaitingForVisibleActivity.remove(s);
+                    mStoppingActivities.remove(activityNdx);
+                }
+            }
+        }
+
+        return stops;
+    }
+
+    void validateTopActivitiesLocked() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord r = stack.topRunningActivityLocked();
+                final ActivityState state = r == null ? DESTROYED : r.getState();
+                if (isTopDisplayFocusedStack(stack)) {
+                    if (r == null) Slog.e(TAG,
+                            "validateTop...: null top activity, stack=" + stack);
+                    else {
+                        final ActivityRecord pausing = stack.mPausingActivity;
+                        if (pausing != null && pausing == r) Slog.e(TAG,
+                                "validateTop...: top stack has pausing activity r=" + r
+                                + " state=" + state);
+                        if (state != INITIALIZING && state != RESUMED) Slog.e(TAG,
+                                "validateTop...: activity in front not resumed r=" + r
+                                + " state=" + state);
+                    }
+                } else {
+                    final ActivityRecord resumed = stack.getResumedActivity();
+                    if (resumed != null && resumed == r) Slog.e(TAG,
+                            "validateTop...: back stack has resumed activity r=" + r
+                            + " state=" + state);
+                    if (r != null && (state == INITIALIZING || state == RESUMED)) Slog.e(TAG,
+                            "validateTop...: activity in back resumed r=" + r + " state=" + state);
+                }
+            }
+        }
+    }
+
+    public void dumpDisplays(PrintWriter pw) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            pw.print("[id:" + display.mDisplayId + " stacks:");
+            display.dumpStacks(pw);
+            pw.print("]");
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        pw.println();
+        pw.println("ActivityStackSupervisor state:");
+        pw.print(prefix);
+        pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+        pw.print(prefix);
+        pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
+        pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            display.dump(pw, prefix);
+        }
+        if (!mWaitingForActivityVisible.isEmpty()) {
+            pw.print(prefix); pw.println("mWaitingForActivityVisible=");
+            for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) {
+                pw.print(prefix); pw.print(prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
+            }
+        }
+        pw.print(prefix); pw.print("isHomeRecentsComponent=");
+        pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+
+        getKeyguardController().dump(pw, prefix);
+        mService.getLockTaskController().dump(pw, prefix);
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
+        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+            activityDisplay.writeToProto(proto, DISPLAYS);
+        }
+        getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
+        // TODO(b/111541062): Update tests to look for resumed activities on all displays
+        final ActivityStack focusedStack = getTopDisplayFocusedStack();
+        if (focusedStack != null) {
+            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+            final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
+            if (focusedActivity != null) {
+                focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+            }
+        } else {
+            proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
+        }
+        proto.write(IS_HOME_RECENTS_COMPONENT,
+                mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+        mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES);
+        proto.end(token);
+    }
+
+    /**
+     * Dump all connected displays' configurations.
+     * @param prefix Prefix to apply to each line of the dump.
+     */
+    void dumpDisplayConfigs(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.println("Display override configurations:");
+        final int displayCount = mActivityDisplays.size();
+        for (int i = 0; i < displayCount; i++) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
+            pw.print(prefix); pw.print("  "); pw.print(activityDisplay.mDisplayId); pw.print(": ");
+                    pw.println(activityDisplay.getOverrideConfiguration());
+        }
+    }
+
+    /**
+     * Dumps the activities matching the given {@param name} in the either the focused stack
+     * or all visible stacks if {@param dumpVisibleStacks} is true.
+     */
+    ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacksOnly,
+            boolean dumpFocusedStackOnly) {
+        if (dumpFocusedStackOnly) {
+            return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
+        } else {
+            ArrayList<ActivityRecord> activities = new ArrayList<>();
+            int numDisplays = mActivityDisplays.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                    final ActivityStack stack = display.getChildAt(stackNdx);
+                    if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
+                        activities.addAll(stack.getDumpActivitiesLocked(name));
+                    }
+                }
+            }
+            return activities;
+        }
+    }
+
+    static boolean printThisActivity(PrintWriter pw, ActivityRecord activity, String dumpPackage,
+            boolean needSep, String prefix) {
+        if (activity != null) {
+            if (dumpPackage == null || dumpPackage.equals(activity.packageName)) {
+                if (needSep) {
+                    pw.println();
+                }
+                pw.print(prefix);
+                pw.println(activity);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+            boolean dumpClient, String dumpPackage) {
+        boolean printed = false;
+        boolean needSep = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+            pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
+                    pw.println(" (activities from top to bottom):");
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                pw.println();
+                pw.println("  Stack #" + stack.mStackId
+                        + ": type=" + activityTypeToString(stack.getActivityType())
+                        + " mode=" + windowingModeToString(stack.getWindowingMode()));
+                pw.println("  isSleeping=" + stack.shouldSleepActivities());
+                pw.println("  mBounds=" + stack.getOverrideBounds());
+
+                printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
+                        needSep);
+
+                printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, "    ", "Run", false,
+                        !dumpAll, false, dumpPackage, true,
+                        "    Running activities (most recent first):", null);
+
+                needSep = printed;
+                boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
+                        "    mPausingActivity: ");
+                if (pr) {
+                    printed = true;
+                    needSep = false;
+                }
+                pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep,
+                        "    mResumedActivity: ");
+                if (pr) {
+                    printed = true;
+                    needSep = false;
+                }
+                if (dumpAll) {
+                    pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
+                            "    mLastPausedActivity: ");
+                    if (pr) {
+                        printed = true;
+                        needSep = true;
+                    }
+                    printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
+                            needSep, "    mLastNoHistoryActivity: ");
+                }
+                needSep = printed;
+            }
+            printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
+                    " ResumedActivity:");
+        }
+
+        printed |= dumpHistoryList(fd, pw, mFinishingActivities, "  ", "Fin", false, !dumpAll,
+                false, dumpPackage, true, "  Activities waiting to finish:", null);
+        printed |= dumpHistoryList(fd, pw, mStoppingActivities, "  ", "Stop", false, !dumpAll,
+                false, dumpPackage, true, "  Activities waiting to stop:", null);
+        printed |= dumpHistoryList(fd, pw, mActivitiesWaitingForVisibleActivity, "  ", "Wait",
+                false, !dumpAll, false, dumpPackage, true,
+                "  Activities waiting for another to become visible:", null);
+        printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, "  ", "Sleep", false, !dumpAll,
+                false, dumpPackage, true, "  Activities waiting to sleep:", null);
+
+        return printed;
+    }
+
+    static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
+            String prefix, String label, boolean complete, boolean brief, boolean client,
+            String dumpPackage, boolean needNL, String header, TaskRecord lastTask) {
+        String innerPrefix = null;
+        String[] args = null;
+        boolean printed = false;
+        for (int i=list.size()-1; i>=0; i--) {
+            final ActivityRecord r = list.get(i);
+            if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
+                continue;
+            }
+            if (innerPrefix == null) {
+                innerPrefix = prefix + "      ";
+                args = new String[0];
+            }
+            printed = true;
+            final boolean full = !brief && (complete || !r.isInHistory());
+            if (needNL) {
+                pw.println("");
+                needNL = false;
+            }
+            if (header != null) {
+                pw.println(header);
+                header = null;
+            }
+            if (lastTask != r.getTask()) {
+                lastTask = r.getTask();
+                pw.print(prefix);
+                pw.print(full ? "* " : "  ");
+                pw.println(lastTask);
+                if (full) {
+                    lastTask.dump(pw, prefix + "  ");
+                } else if (complete) {
+                    // Complete + brief == give a summary.  Isn't that obvious?!?
+                    if (lastTask.intent != null) {
+                        pw.print(prefix); pw.print("  ");
+                                pw.println(lastTask.intent.toInsecureStringWithClip());
+                    }
+                }
+            }
+            pw.print(prefix); pw.print(full ? "  * " : "    "); pw.print(label);
+            pw.print(" #"); pw.print(i); pw.print(": ");
+            pw.println(r);
+            if (full) {
+                r.dump(pw, innerPrefix);
+            } else if (complete) {
+                // Complete + brief == give a summary.  Isn't that obvious?!?
+                pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
+                if (r.app != null) {
+                    pw.print(innerPrefix); pw.println(r.app);
+                }
+            }
+            if (client && r.attachedToProcess()) {
+                // flush anything that is already in the PrintWriter since the thread is going
+                // to write to the file descriptor directly
+                pw.flush();
+                try {
+                    TransferPipe tp = new TransferPipe();
+                    try {
+                        r.app.getThread().dumpActivity(
+                                tp.getWriteFd(), r.appToken, innerPrefix, args);
+                        // Short timeout, since blocking here can deadlock with the application.
+                        tp.go(fd, 2000);
+                    } finally {
+                        tp.kill();
+                    }
+                } catch (IOException e) {
+                    pw.println(innerPrefix + "Failure while dumping the activity: " + e);
+                } catch (RemoteException e) {
+                    pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
+                }
+                needNL = true;
+            }
+        }
+        return printed;
+    }
+
+    void scheduleIdleTimeoutLocked(ActivityRecord next) {
+        if (DEBUG_IDLE) Slog.d(TAG_IDLE,
+                "scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
+        Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
+        mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
+    }
+
+    final void scheduleIdleLocked() {
+        mHandler.sendEmptyMessage(IDLE_NOW_MSG);
+    }
+
+    void removeTimeoutsForActivityLocked(ActivityRecord r) {
+        if (DEBUG_IDLE) Slog.d(TAG_IDLE, "removeTimeoutsForActivity: Callers="
+                + Debug.getCallers(4));
+        mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+    }
+
+    final void scheduleResumeTopActivities() {
+        if (!mHandler.hasMessages(RESUME_TOP_ACTIVITY_MSG)) {
+            mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
+        }
+    }
+
+    void removeSleepTimeouts() {
+        mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
+    }
+
+    final void scheduleSleepTimeout() {
+        removeSleepTimeouts();
+        mHandler.sendEmptyMessageDelayed(SLEEP_TIMEOUT_MSG, SLEEP_TIMEOUT);
+    }
+
+    @Override
+    public void onDisplayAdded(int displayId) {
+        if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
+        synchronized (mService.mGlobalLock) {
+            getActivityDisplayOrCreateLocked(displayId);
+            // Do not start home before booting, or it may accidentally finish booting before it
+            // starts. Instead, we expect home activities to be launched when the system is ready
+            // (ActivityManagerService#systemReady).
+            if (mService.isBooted() || mService.isBooting()) {
+                startHomeOnDisplay(mCurrentUser, "displayAdded", displayId);
+            }
+        }
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
+        if (displayId == DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("Can't remove the primary display.");
+        }
+
+        synchronized (mService.mGlobalLock) {
+            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+            if (activityDisplay == null) {
+                return;
+            }
+
+            activityDisplay.remove();
+        }
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {
+        if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
+        synchronized (mService.mGlobalLock) {
+            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+            if (activityDisplay != null) {
+                activityDisplay.onDisplayChanged();
+            }
+        }
+    }
+
+    /** Check if display with specified id is added to the list. */
+    boolean isDisplayAdded(int displayId) {
+        return getActivityDisplayOrCreateLocked(displayId) != null;
+    }
+
+    // TODO: Look into consolidating with getActivityDisplayOrCreateLocked()
+    ActivityDisplay getActivityDisplay(int displayId) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
+            if (activityDisplay.mDisplayId == displayId) {
+                return activityDisplay;
+            }
+        }
+        return null;
+    }
+
+    // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
+    ActivityDisplay getDefaultDisplay() {
+        return mDefaultDisplay;
+    }
+
+    /**
+     * Get an existing instance of {@link ActivityDisplay} or create new if there is a
+     * corresponding record in display manager.
+     */
+    // TODO: Look into consolidating with getActivityDisplay()
+    ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
+        ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+        if (activityDisplay != null) {
+            return activityDisplay;
+        }
+        if (mDisplayManager == null) {
+            // The system isn't fully initialized yet.
+            return null;
+        }
+        final Display display = mDisplayManager.getDisplay(displayId);
+        if (display == null) {
+            // The display is not registered in DisplayManager.
+            return null;
+        }
+        // The display hasn't been added to ActivityManager yet, create a new record now.
+        activityDisplay = new ActivityDisplay(this, display);
+        addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
+        return activityDisplay;
+    }
+
+    boolean startHomeOnAllDisplays(int userId, String reason) {
+        boolean homeStarted = false;
+        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
+            final int displayId = mActivityDisplays.get(i).mDisplayId;
+            homeStarted |= startHomeOnDisplay(userId, reason, displayId);
+        }
+        return homeStarted;
+    }
+
+    /**
+     * This starts home activity on displays that can have system decorations and only if the
+     * home activity can have multiple instances.
+     */
+    boolean startHomeOnDisplay(int userId, String reason, int displayId) {
+        final Intent homeIntent = mService.getHomeIntent();
+        final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+        if (aInfo == null) {
+            return false;
+        }
+
+        if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
+            return false;
+        }
+
+        // Update the reason for ANR debugging to verify if the user activity is the one that
+        // actually launched.
+        final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
+                aInfo.applicationInfo.uid);
+        mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
+                displayId);
+        return true;
+    }
+
+    /**
+     * This resolves the home activity info and updates the home component of the given intent.
+     * @return the home activity info if any.
+     */
+    private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
+        final int flags = ActivityManagerService.STOCK_PM_FLAGS;
+        final ComponentName comp = homeIntent.getComponent();
+        ActivityInfo aInfo = null;
+        try {
+            if (comp != null) {
+                // Factory test.
+                aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
+            } else {
+                final String resolvedType =
+                        homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
+                final ResolveInfo info = AppGlobals.getPackageManager()
+                        .resolveIntent(homeIntent, resolvedType, flags, userId);
+                if (info != null) {
+                    aInfo = info.activityInfo;
+                }
+            }
+        } catch (RemoteException e) {
+            // ignore
+        }
+
+        if (aInfo == null) {
+            Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable());
+            return null;
+        }
+
+        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
+        aInfo = new ActivityInfo(aInfo);
+        aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
+        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
+        return aInfo;
+    }
+
+    @VisibleForTesting
+    void addChild(ActivityDisplay activityDisplay, int position) {
+        positionChildAt(activityDisplay, position);
+        mWindowContainerController.positionChildAt(
+                activityDisplay.getWindowContainerController(), position);
+    }
+
+    void removeChild(ActivityDisplay activityDisplay) {
+        // The caller must tell the controller of {@link ActivityDisplay} to release its container
+        // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}).
+        mActivityDisplays.remove(activityDisplay);
+    }
+
+    private void calculateDefaultMinimalSizeOfResizeableTasks() {
+        final Resources res = mService.mContext.getResources();
+        final float minimalSize = res.getDimension(
+                com.android.internal.R.dimen.default_minimal_size_resizable_task);
+        final DisplayMetrics dm = res.getDisplayMetrics();
+
+        mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
+    }
+
+    SleepToken createSleepTokenLocked(String tag, int displayId) {
+        final ActivityDisplay display = getActivityDisplay(displayId);
+        if (display == null) {
+            throw new IllegalArgumentException("Invalid display: " + displayId);
+        }
+
+        final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
+        mSleepTokens.add(token);
+        display.mAllSleepTokens.add(token);
+        return token;
+    }
+
+    private void removeSleepTokenLocked(SleepTokenImpl token) {
+        mSleepTokens.remove(token);
+
+        final ActivityDisplay display = getActivityDisplay(token.mDisplayId);
+        if (display != null) {
+            display.mAllSleepTokens.remove(token);
+            if (display.mAllSleepTokens.isEmpty()) {
+                mService.updateSleepIfNeededLocked();
+            }
+        }
+    }
+
+    private StackInfo getStackInfo(ActivityStack stack) {
+        final int displayId = stack.mDisplayId;
+        final ActivityDisplay display = getActivityDisplay(displayId);
+        StackInfo info = new StackInfo();
+        stack.getWindowContainerBounds(info.bounds);
+        info.displayId = displayId;
+        info.stackId = stack.mStackId;
+        info.userId = stack.mCurrentUser;
+        info.visible = stack.shouldBeVisible(null);
+        // A stack might be not attached to a display.
+        info.position = display != null ? display.getIndexOf(stack) : 0;
+        info.configuration.setTo(stack.getConfiguration());
+
+        ArrayList<TaskRecord> tasks = stack.getAllTasks();
+        final int numTasks = tasks.size();
+        int[] taskIds = new int[numTasks];
+        String[] taskNames = new String[numTasks];
+        Rect[] taskBounds = new Rect[numTasks];
+        int[] taskUserIds = new int[numTasks];
+        for (int i = 0; i < numTasks; ++i) {
+            final TaskRecord task = tasks.get(i);
+            taskIds[i] = task.taskId;
+            taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
+                    : task.realActivity != null ? task.realActivity.flattenToString()
+                    : task.getTopActivity() != null ? task.getTopActivity().packageName
+                    : "unknown";
+            taskBounds[i] = new Rect();
+            task.getWindowContainerBounds(taskBounds[i]);
+            taskUserIds[i] = task.userId;
+        }
+        info.taskIds = taskIds;
+        info.taskNames = taskNames;
+        info.taskBounds = taskBounds;
+        info.taskUserIds = taskUserIds;
+
+        final ActivityRecord top = stack.topRunningActivityLocked();
+        info.topActivity = top != null ? top.intent.getComponent() : null;
+        return info;
+    }
+
+    StackInfo getStackInfo(int stackId) {
+        ActivityStack stack = getStack(stackId);
+        if (stack != null) {
+            return getStackInfo(stack);
+        }
+        return null;
+    }
+
+    StackInfo getStackInfo(int windowingMode, int activityType) {
+        final ActivityStack stack = getStack(windowingMode, activityType);
+        return (stack != null) ? getStackInfo(stack) : null;
+    }
+
+    ArrayList<StackInfo> getAllStackInfosLocked() {
+        ArrayList<StackInfo> list = new ArrayList<>();
+        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                list.add(getStackInfo(stack));
+            }
+        }
+        return list;
+    }
+
+    void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
+            int preferredDisplayId, ActivityStack actualStack) {
+        handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayId,
+                actualStack, false /* forceNonResizable */);
+    }
+
+    void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
+            int preferredDisplayId, ActivityStack actualStack, boolean forceNonResizable) {
+        final boolean isSecondaryDisplayPreferred =
+                (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
+        final boolean inSplitScreenMode = actualStack != null
+                && actualStack.getDisplay().hasSplitScreenPrimaryStack();
+        if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+                && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
+            return;
+        }
+
+        // Handle incorrect launch/move to secondary display if needed.
+        if (isSecondaryDisplayPreferred) {
+            final int actualDisplayId = task.getStack().mDisplayId;
+            if (!task.canBeLaunchedOnDisplay(actualDisplayId)) {
+                throw new IllegalStateException("Task resolved to incompatible display");
+            }
+            if (preferredDisplayId != actualDisplayId) {
+                Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplayId);
+                // Display a warning toast that we failed to put a task on a secondary display.
+                mService.getTaskChangeNotificationController()
+                        .notifyActivityLaunchOnSecondaryDisplayFailed();
+                return;
+            } else if (!forceNonResizable && handleForcedResizableTask(task,
+                    FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY)) {
+                return;
+            }
+        }
+
+        if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
+            // Display a warning toast that we tried to put an app that doesn't support split-screen
+            // in split-screen.
+            mService.getTaskChangeNotificationController().notifyActivityDismissingDockedStack();
+
+            // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
+            // we need to move it to top of fullscreen stack, otherwise it will be covered.
+
+            final ActivityStack dockedStack =
+                    task.getStack().getDisplay().getSplitScreenPrimaryStack();
+            if (dockedStack != null) {
+                moveTasksToFullscreenStackLocked(dockedStack, actualStack == dockedStack);
+            }
+            return;
+        }
+
+        handleForcedResizableTask(task, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN);
+    }
+
+    /**
+     * @return {@code true} if the top activity of the task is forced to be resizable and the user
+     *         was notified about activity being forced resized.
+     */
+    private boolean handleForcedResizableTask(TaskRecord task, int reason) {
+        final ActivityRecord topActivity = task.getTopActivity();
+        if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
+                && !topActivity.noDisplay) {
+            final String packageName = topActivity.appInfo.packageName;
+            mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
+                    task.taskId, reason, packageName);
+            return true;
+        }
+        return false;
+    }
+
+    void activityRelaunchedLocked(IBinder token) {
+        mWindowManager.notifyAppRelaunchingFinished(token);
+        final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+        if (r != null) {
+            if (r.getStack().shouldSleepOrShutDownActivities()) {
+                r.setSleeping(true, true);
+            }
+        }
+    }
+
+    void activityRelaunchingLocked(ActivityRecord r) {
+        mWindowManager.notifyAppRelaunching(r.appToken);
+    }
+
+    void logStackState() {
+        mActivityMetricsLogger.logWindowState();
+    }
+
+    void scheduleUpdateMultiWindowMode(TaskRecord task) {
+        // If the stack is animating in a way where we will be forcing a multi-mode change at the
+        // end, then ensure that we defer all in between multi-window mode changes
+        if (task.getStack().deferScheduleMultiWindowModeChanged()) {
+            return;
+        }
+
+        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = task.mActivities.get(i);
+            if (r.attachedToProcess()) {
+                mMultiWindowModeChangedActivities.add(r);
+            }
+        }
+
+        if (!mHandler.hasMessages(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG)) {
+            mHandler.sendEmptyMessage(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG);
+        }
+    }
+
+    void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, ActivityStack prevStack) {
+        final ActivityStack stack = task.getStack();
+        if (prevStack == null || prevStack == stack
+                || (!prevStack.inPinnedWindowingMode() && !stack.inPinnedWindowingMode())) {
+            return;
+        }
+
+        scheduleUpdatePictureInPictureModeIfNeeded(task, stack.getOverrideBounds());
+    }
+
+    void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
+        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = task.mActivities.get(i);
+            if (r.attachedToProcess()) {
+                mPipModeChangedActivities.add(r);
+                // If we are scheduling pip change, then remove this activity from multi-window
+                // change list as the processing of pip change will make sure multi-window changed
+                // message is processed in the right order relative to pip changed.
+                mMultiWindowModeChangedActivities.remove(r);
+            }
+        }
+        mPipModeChangedTargetStackBounds = targetStackBounds;
+
+        if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
+            mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
+        }
+    }
+
+    void updatePictureInPictureMode(TaskRecord task, Rect targetStackBounds, boolean forceUpdate) {
+        mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
+        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = task.mActivities.get(i);
+            if (r.attachedToProcess()) {
+                r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
+            }
+        }
+    }
+
+    void setDockedStackMinimized(boolean minimized) {
+        // Get currently focused stack before setting mIsDockMinimized. We do this because if
+        // split-screen is active, primary stack will not be focusable (see #isFocusable) while
+        // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
+        final ActivityStack current = getTopDisplayFocusedStack();
+        mIsDockMinimized = minimized;
+        if (mIsDockMinimized) {
+            if (current.inSplitScreenPrimaryWindowingMode()) {
+                // The primary split-screen stack can't be focused while it is minimize, so move
+                // focus to something else.
+                current.adjustFocusToNextFocusableStack("setDockedStackMinimized");
+            }
+        }
+    }
+
+    void wakeUp(String reason) {
+        mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.am:TURN_ON:" + reason);
+    }
+
+    /**
+     * Begin deferring resume to avoid duplicate resumes in one pass.
+     */
+    void beginDeferResume() {
+        mDeferResumeCount++;
+    }
+
+    /**
+     * End deferring resume and determine if resume can be called.
+     */
+    void endDeferResume() {
+        mDeferResumeCount--;
+    }
+
+    /**
+     * @return True if resume can be called.
+     */
+    private boolean readyToResume() {
+        return mDeferResumeCount == 0;
+    }
+
+    private final class ActivityStackSupervisorHandler extends Handler {
+
+        public ActivityStackSupervisorHandler(Looper looper) {
+            super(looper);
+        }
+
+        void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) {
+            synchronized (mService.mGlobalLock) {
+                activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */,
+                        processPausingActivities, null);
+            }
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case REPORT_MULTI_WINDOW_MODE_CHANGED_MSG: {
+                    synchronized (mService.mGlobalLock) {
+                        for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
+                            final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
+                            r.updateMultiWindowMode();
+                        }
+                    }
+                } break;
+                case REPORT_PIP_MODE_CHANGED_MSG: {
+                    synchronized (mService.mGlobalLock) {
+                        for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
+                            final ActivityRecord r = mPipModeChangedActivities.remove(i);
+                            r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
+                                    false /* forceUpdate */);
+                        }
+                    }
+                } break;
+                case IDLE_TIMEOUT_MSG: {
+                    if (DEBUG_IDLE) Slog.d(TAG_IDLE,
+                            "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
+                    // We don't at this point know if the activity is fullscreen,
+                    // so we need to be conservative and assume it isn't.
+                    activityIdleInternal((ActivityRecord) msg.obj,
+                            true /* processPausingActivities */);
+                } break;
+                case IDLE_NOW_MSG: {
+                    if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
+                    activityIdleInternal((ActivityRecord) msg.obj,
+                            false /* processPausingActivities */);
+                } break;
+                case RESUME_TOP_ACTIVITY_MSG: {
+                    synchronized (mService.mGlobalLock) {
+                        resumeFocusedStacksTopActivitiesLocked();
+                    }
+                } break;
+                case SLEEP_TIMEOUT_MSG: {
+                    synchronized (mService.mGlobalLock) {
+                        if (mService.isSleepingOrShuttingDownLocked()) {
+                            Slog.w(TAG, "Sleep timeout!  Sleeping now.");
+                            checkReadyForSleepLocked(false /* allowDelay */);
+                        }
+                    }
+                } break;
+                case LAUNCH_TIMEOUT_MSG: {
+                    synchronized (mService.mGlobalLock) {
+                        if (mLaunchingActivity.isHeld()) {
+                            Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
+                            if (VALIDATE_WAKE_LOCK_CALLER
+                                    && Binder.getCallingUid() != Process.myUid()) {
+                                throw new IllegalStateException("Calling must be system uid");
+                            }
+                            mLaunchingActivity.release();
+                        }
+                    }
+                } break;
+                case LAUNCH_TASK_BEHIND_COMPLETE: {
+                    synchronized (mService.mGlobalLock) {
+                        ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
+                        if (r != null) {
+                            handleLaunchTaskBehindCompleteLocked(r);
+                        }
+                    }
+                } break;
+
+            }
+        }
+    }
+
+    ActivityStack findStackBehind(ActivityStack stack) {
+        final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
+        if (display != null) {
+            for (int i = display.getChildCount() - 1; i >= 0; i--) {
+                if (display.getChildAt(i) == stack && i > 0) {
+                    return display.getChildAt(i - 1);
+                }
+            }
+        }
+        throw new IllegalStateException("Failed to find a stack behind stack=" + stack
+                + " in=" + display);
+    }
+
+    /**
+     * Puts a task into resizing mode during the next app transition.
+     *
+     * @param task The task to put into resizing mode
+     */
+    void setResizingDuringAnimation(TaskRecord task) {
+        mResizingTasksDuringAnimation.add(task.taskId);
+        task.setTaskDockedResizing(true);
+    }
+
+    int startActivityFromRecents(int callingPid, int callingUid, int taskId,
+            SafeActivityOptions options) {
+        TaskRecord task = null;
+        final String callingPackage;
+        final Intent intent;
+        final int userId;
+        int activityType = ACTIVITY_TYPE_UNDEFINED;
+        int windowingMode = WINDOWING_MODE_UNDEFINED;
+        final ActivityOptions activityOptions = options != null
+                ? options.getOptions(this)
+                : null;
+        if (activityOptions != null) {
+            activityType = activityOptions.getLaunchActivityType();
+            windowingMode = activityOptions.getLaunchWindowingMode();
+        }
+        if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
+            throw new IllegalArgumentException("startActivityFromRecents: Task "
+                    + taskId + " can't be launch in the home/recents stack.");
+        }
+
+        mWindowManager.deferSurfaceLayout();
+        try {
+            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                mWindowManager.setDockedStackCreateState(
+                        activityOptions.getSplitScreenCreateMode(), null /* initialBounds */);
+
+                // Defer updating the stack in which recents is until the app transition is done, to
+                // not run into issues where we still need to draw the task in recents but the
+                // docked stack is already created.
+                deferUpdateRecentsHomeStackBounds();
+                // TODO(multi-display): currently recents animation only support default display.
+                mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
+            }
+
+            task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
+                    activityOptions, ON_TOP);
+            if (task == null) {
+                continueUpdateRecentsHomeStackBounds();
+                mWindowManager.executeAppTransition();
+                throw new IllegalArgumentException(
+                        "startActivityFromRecents: Task " + taskId + " not found.");
+            }
+
+            if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                // We always want to return to the home activity instead of the recents activity
+                // from whatever is started from the recents activity, so move the home stack
+                // forward.
+                // TODO (b/115289124): Multi-display supports for recents.
+                getDefaultDisplay().moveHomeStackToFront("startActivityFromRecents");
+            }
+
+            // If the user must confirm credentials (e.g. when first launching a work app and the
+            // Work Challenge is present) let startActivityInPackage handle the intercepting.
+            if (!mService.mAmInternal.shouldConfirmCredentials(task.userId)
+                    && task.getRootActivity() != null) {
+                final ActivityRecord targetActivity = task.getTopActivity();
+
+                sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity);
+                mActivityMetricsLogger.notifyActivityLaunching(task.intent);
+                try {
+                    mService.moveTaskToFrontLocked(task.taskId, 0, options,
+                            true /* fromRecents */);
+                } finally {
+                    mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
+                            targetActivity);
+                }
+
+                mService.getActivityStartController().postStartActivityProcessingForLastStarter(
+                        task.getTopActivity(), ActivityManager.START_TASK_TO_FRONT,
+                        task.getStack());
+                return ActivityManager.START_TASK_TO_FRONT;
+            }
+            callingPackage = task.mCallingPackage;
+            intent = task.intent;
+            intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+            userId = task.userId;
+            return mService.getActivityStartController().startActivityInPackage(
+                    task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null,
+                    null, 0, 0, options, userId, task, "startActivityFromRecents",
+                    false /* validateIncomingUser */, null /* originatingPendingIntent */);
+        } finally {
+            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && task != null) {
+                // If we are launching the task in the docked stack, put it into resizing mode so
+                // the window renders full-screen with the background filling the void. Also only
+                // call this at the end to make sure that tasks exists on the window manager side.
+                setResizingDuringAnimation(task);
+
+                final ActivityDisplay display = task.getStack().getDisplay();
+                final ActivityStack topSecondaryStack =
+                        display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                if (topSecondaryStack.isActivityTypeHome()) {
+                    // If the home activity is the top split-screen secondary stack, then the
+                    // primary split-screen stack is in the minimized mode which means it can't
+                    // receive input keys, so we should move the focused app to the home app so that
+                    // window manager can correctly calculate the focus window that can receive
+                    // input keys.
+                    display.moveHomeStackToFront(
+                            "startActivityFromRecents: homeVisibleInSplitScreen");
+
+                    // Immediately update the minimized docked stack mode, the upcoming animation
+                    // for the docked activity (WMS.overridePendingAppTransitionMultiThumbFuture)
+                    // will do the animation to the target bounds
+                    mWindowManager.checkSplitScreenMinimizedChanged(false /* animate */);
+                }
+            }
+            mWindowManager.continueSurfaceLayout();
+        }
+    }
+
+    /**
+     * @return a list of activities which are the top ones in each visible stack. The first
+     * entry will be the focused activity.
+     */
+    List<IBinder> getTopVisibleActivities() {
+        final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
+        final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+        // Traverse all displays.
+        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            // Traverse all stacks on a display.
+            for (int j = display.getChildCount() - 1; j >= 0; --j) {
+                final ActivityStack stack = display.getChildAt(j);
+                // Get top activity from a visible stack and add it to the list.
+                if (stack.shouldBeVisible(null /* starting */)) {
+                    final ActivityRecord top = stack.getTopActivity();
+                    if (top != null) {
+                        if (stack == topFocusedStack) {
+                            topActivityTokens.add(0, top.appToken);
+                        } else {
+                            topActivityTokens.add(top.appToken);
+                        }
+                    }
+                }
+            }
+        }
+        return topActivityTokens;
+    }
+
+    /**
+     * Internal container to store a match qualifier alongside a WaitResult.
+     */
+    static class WaitInfo {
+        private final ComponentName mTargetComponent;
+        private final WaitResult mResult;
+        /** Time stamp when we started to wait for {@link WaitResult}. */
+        private final long mStartTimeMs;
+
+        WaitInfo(ComponentName targetComponent, WaitResult result, long startTimeMs) {
+            this.mTargetComponent = targetComponent;
+            this.mResult = result;
+            this.mStartTimeMs = startTimeMs;
+        }
+
+        public boolean matches(ComponentName targetComponent) {
+            return mTargetComponent == null || mTargetComponent.equals(targetComponent);
+        }
+
+        public WaitResult getResult() {
+            return mResult;
+        }
+
+        public long getStartTime() {
+            return mStartTimeMs;
+        }
+
+        public ComponentName getComponent() {
+            return mTargetComponent;
+        }
+
+        public void dump(PrintWriter pw, String prefix) {
+            pw.println(prefix + "WaitInfo:");
+            pw.println(prefix + "  mTargetComponent=" + mTargetComponent);
+            pw.println(prefix + "  mResult=");
+            mResult.dump(pw, prefix);
+        }
+    }
+
+    private final class SleepTokenImpl extends SleepToken {
+        private final String mTag;
+        private final long mAcquireTime;
+        private final int mDisplayId;
+
+        public SleepTokenImpl(String tag, int displayId) {
+            mTag = tag;
+            mDisplayId = displayId;
+            mAcquireTime = SystemClock.uptimeMillis();
+        }
+
+        @Override
+        public void release() {
+            synchronized (mService.mGlobalLock) {
+                removeSleepTokenLocked(this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "{\"" + mTag + "\", display " + mDisplayId
+                    + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
new file mode 100644
index 0000000..904d9dd
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.app.ActivityOptions;
+import android.app.IApplicationThread;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.view.RemoteAnimationAdapter;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.am.PendingIntentRecord;
+import com.android.server.wm.ActivityStackSupervisor.PendingActivityLaunch;
+import com.android.server.wm.ActivityStarter.DefaultFactory;
+import com.android.server.wm.ActivityStarter.Factory;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Controller for delegating activity launches.
+ *
+ * This class' main objective is to take external activity start requests and prepare them into
+ * a series of discrete activity launches that can be handled by an {@link ActivityStarter}. It is
+ * also responsible for handling logic that happens around an activity launch, but doesn't
+ * necessarily influence the activity start. Examples include power hint management, processing
+ * through the pending activity list, and recording home activity launches.
+ */
+public class ActivityStartController {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStartController" : TAG_ATM;
+
+    private static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 1;
+
+    private final ActivityTaskManagerService mService;
+    private final ActivityStackSupervisor mSupervisor;
+
+    /** Last home activity record we attempted to start. */
+    private ActivityRecord mLastHomeActivityStartRecord;
+
+    /** Temporary array to capture start activity results */
+    private ActivityRecord[] tmpOutRecord = new ActivityRecord[1];
+
+    /** The result of the last home activity we attempted to start. */
+    private int mLastHomeActivityStartResult;
+
+    /** A list of activities that are waiting to launch. */
+    private final ArrayList<ActivityStackSupervisor.PendingActivityLaunch>
+            mPendingActivityLaunches = new ArrayList<>();
+
+    private final Factory mFactory;
+
+    private final Handler mHandler;
+
+    private final PendingRemoteAnimationRegistry mPendingRemoteAnimationRegistry;
+
+    boolean mCheckedForSetup = false;
+
+    private final class StartHandler extends Handler {
+        public StartHandler(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case DO_PENDING_ACTIVITY_LAUNCHES_MSG:
+                    synchronized (mService.mGlobalLock) {
+                        doPendingActivityLaunches(true);
+                    }
+                    break;
+            }
+        }
+    }
+
+    /**
+     * TODO(b/64750076): Capture information necessary for dump and
+     * {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object
+     * around
+     */
+    private ActivityStarter mLastStarter;
+
+    ActivityStartController(ActivityTaskManagerService service) {
+        this(service, service.mStackSupervisor,
+                new DefaultFactory(service, service.mStackSupervisor,
+                    new ActivityStartInterceptor(service, service.mStackSupervisor)));
+    }
+
+    @VisibleForTesting
+    ActivityStartController(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
+            Factory factory) {
+        mService = service;
+        mSupervisor = supervisor;
+        mHandler = new StartHandler(mService.mH.getLooper());
+        mFactory = factory;
+        mFactory.setController(this);
+        mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service,
+                service.mH);
+    }
+
+    /**
+     * @return A starter to configure and execute starting an activity. It is valid until after
+     *         {@link ActivityStarter#execute} is invoked. At that point, the starter should be
+     *         considered invalid and no longer modified or used.
+     */
+    ActivityStarter obtainStarter(Intent intent, String reason) {
+        return mFactory.obtain().setIntent(intent).setReason(reason);
+    }
+
+    void onExecutionComplete(ActivityStarter starter) {
+        if (mLastStarter == null) {
+            mLastStarter = mFactory.obtain();
+        }
+
+        mLastStarter.set(starter);
+        mFactory.recycle(starter);
+    }
+
+    /**
+     * TODO(b/64750076): usage of this doesn't seem right. We're making decisions based off the
+     * last starter for an arbitrary task record. Re-evaluate whether we can remove.
+     */
+    void postStartActivityProcessingForLastStarter(ActivityRecord r, int result,
+            ActivityStack targetStack) {
+        if (mLastStarter == null) {
+            return;
+        }
+
+        mLastStarter.postStartActivityProcessing(r, result, targetStack);
+    }
+
+    void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+        options.setLaunchDisplayId(displayId);
+        mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
+                .setOutActivity(tmpOutRecord)
+                .setCallingUid(0)
+                .setActivityInfo(aInfo)
+                .setActivityOptions(options.toBundle())
+                .execute();
+        mLastHomeActivityStartRecord = tmpOutRecord[0];
+        if (mSupervisor.inResumeTopActivity) {
+            // If we are in resume section already, home activity will be initialized, but not
+            // resumed (to avoid recursive resume) and will stay that way until something pokes it
+            // again. We need to schedule another resume.
+            mSupervisor.scheduleResumeTopActivities();
+        }
+    }
+
+    /**
+     * Starts the "new version setup screen" if appropriate.
+     */
+    void startSetupActivity() {
+        // Only do this once per boot.
+        if (mCheckedForSetup) {
+            return;
+        }
+
+        // We will show this screen if the current one is a different
+        // version than the last one shown, and we are not running in
+        // low-level factory test mode.
+        final ContentResolver resolver = mService.mContext.getContentResolver();
+        if (mService.mFactoryTest != FACTORY_TEST_LOW_LEVEL
+                && Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
+            mCheckedForSetup = true;
+
+            // See if we should be showing the platform update setup UI.
+            final Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
+            final List<ResolveInfo> ris =
+                    mService.mContext.getPackageManager().queryIntentActivities(intent,
+                            PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA
+                                    | ActivityManagerService.STOCK_PM_FLAGS);
+            if (!ris.isEmpty()) {
+                final ResolveInfo ri = ris.get(0);
+                String vers = ri.activityInfo.metaData != null
+                        ? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION)
+                        : null;
+                if (vers == null && ri.activityInfo.applicationInfo.metaData != null) {
+                    vers = ri.activityInfo.applicationInfo.metaData.getString(
+                            Intent.METADATA_SETUP_VERSION);
+                }
+                String lastVers = Settings.Secure.getString(
+                        resolver, Settings.Secure.LAST_SETUP_SHOWN);
+                if (vers != null && !vers.equals(lastVers)) {
+                    intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+                    intent.setComponent(new ComponentName(
+                            ri.activityInfo.packageName, ri.activityInfo.name));
+                    obtainStarter(intent, "startSetupActivity")
+                            .setCallingUid(0)
+                            .setActivityInfo(ri.activityInfo)
+                            .execute();
+                }
+            }
+        }
+    }
+
+    /**
+     * If {@code validateIncomingUser} is true, check {@code targetUserId} against the real calling
+     * user ID inferred from {@code realCallingUid}, then return the resolved user-id, taking into
+     * account "current user", etc.
+     *
+     * If {@code validateIncomingUser} is false, it skips the above check, but instead
+     * ensures {@code targetUserId} is a real user ID and not a special user ID such as
+     * {@link android.os.UserHandle#USER_ALL}, etc.
+     */
+    int checkTargetUser(int targetUserId, boolean validateIncomingUser,
+            int realCallingPid, int realCallingUid, String reason) {
+        if (validateIncomingUser) {
+            return mService.handleIncomingUser(
+                    realCallingPid, realCallingUid, targetUserId, reason);
+        } else {
+            mService.mAmInternal.ensureNotSpecialUser(targetUserId);
+            return targetUserId;
+        }
+    }
+
+    final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
+            String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
+            int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
+            PendingIntentRecord originatingPendingIntent) {
+
+        userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
+                reason);
+
+        // TODO: Switch to user app stacks here.
+        return obtainStarter(intent, reason)
+                .setCallingUid(uid)
+                .setRealCallingPid(realCallingPid)
+                .setRealCallingUid(realCallingUid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setActivityOptions(options)
+                .setMayWait(userId)
+                .setInTask(inTask)
+                .setOriginatingPendingIntent(originatingPendingIntent)
+                .execute();
+    }
+
+    /**
+     * Start intents as a package.
+     *
+     * @param uid Make a call as if this UID did.
+     * @param callingPackage Make a call as if this package did.
+     * @param intents Intents to start.
+     * @param userId Start the intents on this user.
+     * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
+     * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
+     *        null if not originated by PendingIntent
+     */
+    final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
+            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent) {
+
+        final String reason = "startActivityInPackage";
+
+        userId = checkTargetUser(userId, validateIncomingUser, Binder.getCallingPid(),
+                Binder.getCallingUid(), reason);
+
+        // TODO: Switch to user app stacks here.
+        return startActivities(null, uid, callingPackage, intents, resolvedTypes, resultTo, options,
+                userId, reason, originatingPendingIntent);
+    }
+
+    int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
+            int userId, String reason, PendingIntentRecord originatingPendingIntent) {
+        if (intents == null) {
+            throw new NullPointerException("intents is null");
+        }
+        if (resolvedTypes == null) {
+            throw new NullPointerException("resolvedTypes is null");
+        }
+        if (intents.length != resolvedTypes.length) {
+            throw new IllegalArgumentException("intents are length different than resolvedTypes");
+        }
+
+        final int realCallingPid = Binder.getCallingPid();
+        final int realCallingUid = Binder.getCallingUid();
+
+        int callingPid;
+        if (callingUid >= 0) {
+            callingPid = -1;
+        } else if (caller == null) {
+            callingPid = realCallingPid;
+            callingUid = realCallingUid;
+        } else {
+            callingPid = callingUid = -1;
+        }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mService.mGlobalLock) {
+                ActivityRecord[] outActivity = new ActivityRecord[1];
+                for (int i=0; i < intents.length; i++) {
+                    Intent intent = intents[i];
+                    if (intent == null) {
+                        continue;
+                    }
+
+                    // Refuse possible leaked file descriptors
+                    if (intent != null && intent.hasFileDescriptors()) {
+                        throw new IllegalArgumentException("File descriptors passed in Intent");
+                    }
+
+                    boolean componentSpecified = intent.getComponent() != null;
+
+                    // Don't modify the client's object!
+                    intent = new Intent(intent);
+
+                    // Collect information about the target of the Intent.
+                    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0,
+                            null, userId, ActivityStarter.computeResolveFilterUid(
+                                    callingUid, realCallingUid, UserHandle.USER_NULL));
+                    // TODO: New, check if this is correct
+                    aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
+
+                    if (aInfo != null &&
+                            (aInfo.applicationInfo.privateFlags
+                                    & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)  != 0) {
+                        throw new IllegalArgumentException(
+                                "FLAG_CANT_SAVE_STATE not supported here");
+                    }
+
+                    final boolean top = i == intents.length - 1;
+                    final SafeActivityOptions checkedOptions = top
+                            ? options
+                            : null;
+                    final int res = obtainStarter(intent, reason)
+                            .setCaller(caller)
+                            .setResolvedType(resolvedTypes[i])
+                            .setActivityInfo(aInfo)
+                            .setResultTo(resultTo)
+                            .setRequestCode(-1)
+                            .setCallingPid(callingPid)
+                            .setCallingUid(callingUid)
+                            .setCallingPackage(callingPackage)
+                            .setRealCallingPid(realCallingPid)
+                            .setRealCallingUid(realCallingUid)
+                            .setActivityOptions(checkedOptions)
+                            .setComponentSpecified(componentSpecified)
+                            .setOutActivity(outActivity)
+
+                            // Top activity decides on animation being run, so we allow only for the
+                            // top one as otherwise an activity below might consume it.
+                            .setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
+                            .setOriginatingPendingIntent(originatingPendingIntent)
+                            .execute();
+
+                    if (res < 0) {
+                        return res;
+                    }
+
+                    resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+
+        return START_SUCCESS;
+    }
+
+    void schedulePendingActivityLaunches(long delayMs) {
+        mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
+        Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
+        mHandler.sendMessageDelayed(msg, delayMs);
+    }
+
+    void doPendingActivityLaunches(boolean doResume) {
+        while (!mPendingActivityLaunches.isEmpty()) {
+            final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
+            final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
+            final ActivityStarter starter = obtainStarter(null /* intent */,
+                    "pendingActivityLaunch");
+            try {
+                starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
+                        resume, pal.r.pendingOptions, null, null /* outRecords */);
+            } catch (Exception e) {
+                Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
+                pal.sendErrorResult(e.getMessage());
+            }
+        }
+    }
+
+    void addPendingActivityLaunch(PendingActivityLaunch launch) {
+        mPendingActivityLaunches.add(launch);
+    }
+
+    boolean clearPendingActivityLaunches(String packageName) {
+        final int pendingLaunches = mPendingActivityLaunches.size();
+
+        for (int palNdx = pendingLaunches - 1; palNdx >= 0; --palNdx) {
+            final PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
+            final ActivityRecord r = pal.r;
+            if (r != null && r.packageName.equals(packageName)) {
+                mPendingActivityLaunches.remove(palNdx);
+            }
+        }
+        return mPendingActivityLaunches.size() < pendingLaunches;
+    }
+
+    void registerRemoteAnimationForNextActivityStart(String packageName,
+            RemoteAnimationAdapter adapter) {
+        mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);
+    }
+
+    PendingRemoteAnimationRegistry getPendingRemoteAnimationRegistry() {
+        return mPendingRemoteAnimationRegistry;
+    }
+
+    void dump(PrintWriter pw, String prefix, String dumpPackage) {
+        pw.print(prefix);
+        pw.print("mLastHomeActivityStartResult=");
+        pw.println(mLastHomeActivityStartResult);
+
+        if (mLastHomeActivityStartRecord != null) {
+            pw.print(prefix);
+            pw.println("mLastHomeActivityStartRecord:");
+            mLastHomeActivityStartRecord.dump(pw, prefix + "  ");
+        }
+
+        final boolean dumpPackagePresent = dumpPackage != null;
+
+        if (mLastStarter != null) {
+            final boolean dump = !dumpPackagePresent
+                    || mLastStarter.relatedToPackage(dumpPackage)
+                    || (mLastHomeActivityStartRecord != null
+                            && dumpPackage.equals(mLastHomeActivityStartRecord.packageName));
+
+            if (dump) {
+                pw.print(prefix);
+                mLastStarter.dump(pw, prefix + "  ");
+
+                if (dumpPackagePresent) {
+                    return;
+                }
+            }
+        }
+
+        if (dumpPackagePresent) {
+            pw.print(prefix);
+            pw.println("(nothing)");
+        }
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        for (PendingActivityLaunch activity: mPendingActivityLaunches) {
+            activity.r.writeIdentifierToProto(proto, fieldId);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
new file mode 100644
index 0000000..ee5a43c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2016 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.server.wm;
+
+import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
+import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION;
+import static android.app.admin.DevicePolicyManager.POLICY_SUSPEND_PACKAGES;
+import static android.content.Context.KEYGUARD_SERVICE;
+import static android.content.Intent.EXTRA_INTENT;
+import static android.content.Intent.EXTRA_PACKAGE_NAME;
+import static android.content.Intent.EXTRA_TASK_ID;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
+
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+
+import android.app.ActivityOptions;
+import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.Context;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.content.pm.SuspendDialogInfo;
+import android.content.pm.UserInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.HarmfulAppWarningActivity;
+import com.android.internal.app.SuspendedAppActivity;
+import com.android.internal.app.UnlaunchableAppActivity;
+import com.android.server.LocalServices;
+import com.android.server.am.ActivityManagerService;
+
+/**
+ * A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked}
+ * It's initialized via setStates and interception occurs via the intercept method.
+ *
+ * Note that this class is instantiated when {@link ActivityManagerService} gets created so there
+ * is no guarantee that other system services are already present.
+ */
+class ActivityStartInterceptor {
+
+    private final ActivityTaskManagerService mService;
+    private final ActivityStackSupervisor mSupervisor;
+    private final Context mServiceContext;
+
+    // UserManager cannot be final as it's not ready when this class is instantiated during boot
+    private UserManager mUserManager;
+
+    /*
+     * Per-intent states loaded from ActivityStarter than shouldn't be changed by any
+     * interception routines.
+     */
+    private int mRealCallingPid;
+    private int mRealCallingUid;
+    private int mUserId;
+    private int mStartFlags;
+    private String mCallingPackage;
+
+    /*
+     * Per-intent states that were load from ActivityStarter and are subject to modifications
+     * by the interception routines. After calling {@link #intercept} the caller should assign
+     * these values back to {@link ActivityStarter#startActivityLocked}'s local variables if
+     * {@link #intercept} returns true.
+     */
+    Intent mIntent;
+    int mCallingPid;
+    int mCallingUid;
+    ResolveInfo mRInfo;
+    ActivityInfo mAInfo;
+    String mResolvedType;
+    TaskRecord mInTask;
+    ActivityOptions mActivityOptions;
+
+    ActivityStartInterceptor(
+            ActivityTaskManagerService service, ActivityStackSupervisor supervisor) {
+        this(service, supervisor, service.mContext);
+    }
+
+    @VisibleForTesting
+    ActivityStartInterceptor(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
+            Context context) {
+        mService = service;
+        mSupervisor = supervisor;
+        mServiceContext = context;
+    }
+
+    /**
+     * Effectively initialize the class before intercepting the start intent. The values set in this
+     * method should not be changed during intercept.
+     */
+    void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags,
+            String callingPackage) {
+        mRealCallingPid = realCallingPid;
+        mRealCallingUid = realCallingUid;
+        mUserId = userId;
+        mStartFlags = startFlags;
+        mCallingPackage = callingPackage;
+    }
+
+    private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
+        Bundle activityOptions = deferCrossProfileAppsAnimationIfNecessary();
+        final IIntentSender target = mService.getIntentSenderLocked(
+                INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/,
+                null /*resultCode*/, 0 /*requestCode*/,
+                new Intent[] { mIntent }, new String[] { mResolvedType },
+                flags, activityOptions);
+        return new IntentSender(target);
+    }
+
+    /**
+     * Intercept the launch intent based on various signals. If an interception happened the
+     * internal variables get assigned and need to be read explicitly by the caller.
+     *
+     * @return true if an interception occurred
+     */
+    boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
+            TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions) {
+        mUserManager = UserManager.get(mServiceContext);
+
+        mIntent = intent;
+        mCallingPid = callingPid;
+        mCallingUid = callingUid;
+        mRInfo = rInfo;
+        mAInfo = aInfo;
+        mResolvedType = resolvedType;
+        mInTask = inTask;
+        mActivityOptions = activityOptions;
+
+        if (interceptSuspendedPackageIfNeeded()) {
+            // Skip the rest of interceptions as the package is suspended by device admin so
+            // no user action can undo this.
+            return true;
+        }
+        if (interceptQuietProfileIfNeeded()) {
+            // If work profile is turned off, skip the work challenge since the profile can only
+            // be unlocked when profile's user is running.
+            return true;
+        }
+        if (interceptHarmfulAppIfNeeded()) {
+            // If the app has a "harmful app" warning associated with it, we should ask to uninstall
+            // before issuing the work challenge.
+            return true;
+        }
+        return interceptWorkProfileChallengeIfNeeded();
+    }
+
+    /**
+     * If the activity option is the {@link ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} one,
+     * defer the animation until the original intent is started.
+     *
+     * @return the activity option used to start the original intent.
+     */
+    private Bundle deferCrossProfileAppsAnimationIfNecessary() {
+        if (mActivityOptions != null
+                && mActivityOptions.getAnimationType() == ANIM_OPEN_CROSS_PROFILE_APPS) {
+            mActivityOptions = null;
+            return ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle();
+        }
+        return null;
+    }
+
+    private boolean interceptQuietProfileIfNeeded() {
+        // Do not intercept if the user has not turned off the profile
+        if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) {
+            return false;
+        }
+
+        IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
+                FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT);
+
+        mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId, target);
+        mCallingPid = mRealCallingPid;
+        mCallingUid = mRealCallingUid;
+        mResolvedType = null;
+
+        final UserInfo parent = mUserManager.getProfileParent(mUserId);
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid);
+        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+        return true;
+    }
+
+    private boolean interceptSuspendedByAdminPackage() {
+        DevicePolicyManagerInternal devicePolicyManager = LocalServices
+                .getService(DevicePolicyManagerInternal.class);
+        if (devicePolicyManager == null) {
+            return false;
+        }
+        mIntent = devicePolicyManager.createShowAdminSupportIntent(mUserId, true);
+        mIntent.putExtra(EXTRA_RESTRICTION, POLICY_SUSPEND_PACKAGES);
+
+        mCallingPid = mRealCallingPid;
+        mCallingUid = mRealCallingUid;
+        mResolvedType = null;
+
+        final UserInfo parent = mUserManager.getProfileParent(mUserId);
+        if (parent != null) {
+            mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0,
+                    mRealCallingUid);
+        } else {
+            mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0,
+                    mRealCallingUid);
+        }
+        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+        return true;
+    }
+
+    private boolean interceptSuspendedPackageIfNeeded() {
+        // Do not intercept if the package is not suspended
+        if (mAInfo == null || mAInfo.applicationInfo == null ||
+                (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
+            return false;
+        }
+        final PackageManagerInternal pmi = mService.getPackageManagerInternalLocked();
+        if (pmi == null) {
+            return false;
+        }
+        final String suspendedPackage = mAInfo.applicationInfo.packageName;
+        final String suspendingPackage = pmi.getSuspendingPackage(suspendedPackage, mUserId);
+        if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
+            return interceptSuspendedByAdminPackage();
+        }
+        final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage, mUserId);
+        mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage,
+                suspendingPackage, dialogInfo, mUserId);
+        mCallingPid = mRealCallingPid;
+        mCallingUid = mRealCallingUid;
+        mResolvedType = null;
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
+        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+        return true;
+    }
+
+    private boolean interceptWorkProfileChallengeIfNeeded() {
+        final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
+        if (interceptingIntent == null) {
+            return false;
+        }
+        mIntent = interceptingIntent;
+        mCallingPid = mRealCallingPid;
+        mCallingUid = mRealCallingUid;
+        mResolvedType = null;
+        // If we are intercepting and there was a task, convert it into an extra for the
+        // ConfirmCredentials intent and unassign it, as otherwise the task will move to
+        // front even if ConfirmCredentials is cancelled.
+        if (mInTask != null) {
+            mIntent.putExtra(EXTRA_TASK_ID, mInTask.taskId);
+            mInTask = null;
+        }
+        if (mActivityOptions == null) {
+            mActivityOptions = ActivityOptions.makeBasic();
+        }
+
+        ActivityRecord homeActivityRecord = mSupervisor.getDefaultDisplayHomeActivity();
+        if (homeActivityRecord != null && homeActivityRecord.getTask() != null) {
+            // Showing credential confirmation activity in home task to avoid stopping multi-windowed
+            // mode after showing the full-screen credential confirmation activity.
+            mActivityOptions.setLaunchTaskId(homeActivityRecord.getTask().taskId);
+        }
+
+        final UserInfo parent = mUserManager.getProfileParent(mUserId);
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid);
+        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+        return true;
+    }
+
+    /**
+     * Creates an intent to intercept the current activity start with Confirm Credentials if needed.
+     *
+     * @return The intercepting intent if needed.
+     */
+    private Intent interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId) {
+        if (!mService.mAmInternal.shouldConfirmCredentials(userId)) {
+            return null;
+        }
+        // TODO(b/28935539): should allow certain activities to bypass work challenge
+        final IntentSender target = createIntentSenderForOriginalIntent(Binder.getCallingUid(),
+                FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
+        final KeyguardManager km = (KeyguardManager) mServiceContext
+                .getSystemService(KEYGUARD_SERVICE);
+        final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
+        if (newIntent == null) {
+            return null;
+        }
+        newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
+                FLAG_ACTIVITY_TASK_ON_HOME);
+        newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
+        newIntent.putExtra(EXTRA_INTENT, target);
+        return newIntent;
+    }
+
+    private boolean interceptHarmfulAppIfNeeded() {
+        CharSequence harmfulAppWarning;
+        try {
+            harmfulAppWarning = mService.getPackageManager()
+                    .getHarmfulAppWarning(mAInfo.packageName, mUserId);
+        } catch (RemoteException ex) {
+            return false;
+        }
+
+        if (harmfulAppWarning == null) {
+            return false;
+        }
+
+        final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
+                FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
+
+        mIntent = HarmfulAppWarningActivity.createHarmfulAppWarningIntent(mServiceContext,
+                mAInfo.packageName, target, harmfulAppWarning);
+
+        mCallingPid = mRealCallingPid;
+        mCallingUid = mRealCallingUid;
+        mResolvedType = null;
+
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
+        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+        return true;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
new file mode 100644
index 0000000..83db8de
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -0,0 +1,2704 @@
+/*
+ * Copyright (C) 2016 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.server.wm;
+
+import static android.app.Activity.RESULT_CANCELED;
+import static android.app.ActivityManager.START_ABORTED;
+import static android.app.ActivityManager.START_CANCELED;
+import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
+import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.ActivityManager.START_FLAG_ONLY_IF_NEEDED;
+import static android.app.ActivityManager.START_RETURN_INTENT_TO_CALLER;
+import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
+import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
+import static android.content.Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP;
+import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
+import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
+import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
+import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
+import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.am.EventLogTags.AM_NEW_INTENT;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
+import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.IApplicationThread;
+import android.app.PendingIntent;
+import android.app.ProfilerInfo;
+import android.app.WaitResult;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.AuxiliaryResolveInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.voice.IVoiceInteractionSession;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Pools.SynchronizedPool;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.EventLogTags;
+import com.android.server.am.PendingIntentRecord;
+import com.android.server.pm.InstantAppResolver;
+import com.android.server.wm.ActivityStackSupervisor.PendingActivityLaunch;
+import com.android.server.wm.LaunchParamsController.LaunchParams;
+
+import java.io.PrintWriter;
+import java.text.DateFormat;
+import java.util.Date;
+
+/**
+ * Controller for interpreting how and then launching an activity.
+ *
+ * This class collects all the logic for determining how an intent and flags should be turned into
+ * an activity and associated task and stack.
+ */
+class ActivityStarter {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStarter" : TAG_ATM;
+    private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
+    private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
+    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
+    private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
+    private static final int INVALID_LAUNCH_MODE = -1;
+
+    private final ActivityTaskManagerService mService;
+    private final ActivityStackSupervisor mSupervisor;
+    private final ActivityStartInterceptor mInterceptor;
+    private final ActivityStartController mController;
+
+    // Share state variable among methods when starting an activity.
+    private ActivityRecord mStartActivity;
+    private Intent mIntent;
+    private int mCallingUid;
+    private ActivityOptions mOptions;
+
+    private int mLaunchMode;
+    private boolean mLaunchTaskBehind;
+    private int mLaunchFlags;
+
+    private LaunchParams mLaunchParams = new LaunchParams();
+
+    private ActivityRecord mNotTop;
+    private boolean mDoResume;
+    private int mStartFlags;
+    private ActivityRecord mSourceRecord;
+
+    // The display to launch the activity onto, barring any strong reason to do otherwise.
+    private int mPreferredDisplayId;
+
+    private TaskRecord mInTask;
+    private boolean mAddingToTask;
+    private TaskRecord mReuseTask;
+
+    private ActivityInfo mNewTaskInfo;
+    private Intent mNewTaskIntent;
+    private ActivityStack mSourceStack;
+    private ActivityStack mTargetStack;
+    private boolean mMovedToFront;
+    private boolean mNoAnimation;
+    private boolean mKeepCurTransition;
+    private boolean mAvoidMoveToFront;
+
+    // We must track when we deliver the new intent since multiple code paths invoke
+    // {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used
+    // inside {@link #deliverNewIntent} to suppress duplicate requests and ensure the intent is
+    // delivered at most once.
+    private boolean mIntentDelivered;
+
+    private IVoiceInteractionSession mVoiceSession;
+    private IVoiceInteractor mVoiceInteractor;
+
+    // Last activity record we attempted to start
+    private final ActivityRecord[] mLastStartActivityRecord = new ActivityRecord[1];
+    // The result of the last activity we attempted to start.
+    private int mLastStartActivityResult;
+    // Time in milli seconds we attempted to start the last activity.
+    private long mLastStartActivityTimeMs;
+    // The reason we were trying to start the last activity
+    private String mLastStartReason;
+
+    /*
+     * Request details provided through setter methods. Should be reset after {@link #execute()}
+     * to avoid unnecessarily retaining parameters. Note that the request is ignored when
+     * {@link #startResolvedActivity} is invoked directly.
+     */
+    private Request mRequest = new Request();
+
+    /**
+     * An interface that to provide {@link ActivityStarter} instances to the controller. This is
+     * used by tests to inject their own starter implementations for verification purposes.
+     */
+    @VisibleForTesting
+    interface Factory {
+        /**
+         * Sets the {@link ActivityStartController} to be passed to {@link ActivityStarter}.
+         */
+        void setController(ActivityStartController controller);
+
+        /**
+         * Generates an {@link ActivityStarter} that is ready to handle a new start request.
+         * @param controller The {@link ActivityStartController} which the starter who will own
+         *                   this instance.
+         * @return an {@link ActivityStarter}
+         */
+        ActivityStarter obtain();
+
+        /**
+         * Recycles a starter for reuse.
+         */
+        void recycle(ActivityStarter starter);
+    }
+
+    /**
+     * Default implementation of {@link StarterFactory}.
+     */
+    static class DefaultFactory implements Factory {
+        /**
+         * The maximum count of starters that should be active at one time:
+         * 1. last ran starter (for logging and post activity processing)
+         * 2. current running starter
+         * 3. starter from re-entry in (2)
+         */
+        private final int MAX_STARTER_COUNT = 3;
+
+        private ActivityStartController mController;
+        private ActivityTaskManagerService mService;
+        private ActivityStackSupervisor mSupervisor;
+        private ActivityStartInterceptor mInterceptor;
+
+        private SynchronizedPool<ActivityStarter> mStarterPool =
+                new SynchronizedPool<>(MAX_STARTER_COUNT);
+
+        DefaultFactory(ActivityTaskManagerService service,
+                ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
+            mService = service;
+            mSupervisor = supervisor;
+            mInterceptor = interceptor;
+        }
+
+        @Override
+        public void setController(ActivityStartController controller) {
+            mController = controller;
+        }
+
+        @Override
+        public ActivityStarter obtain() {
+            ActivityStarter starter = mStarterPool.acquire();
+
+            if (starter == null) {
+                starter = new ActivityStarter(mController, mService, mSupervisor, mInterceptor);
+            }
+
+            return starter;
+        }
+
+        @Override
+        public void recycle(ActivityStarter starter) {
+            starter.reset(true /* clearRequest*/);
+            mStarterPool.release(starter);
+        }
+    }
+
+    /**
+     * Container for capturing initial start request details. This information is NOT reset until
+     * the {@link ActivityStarter} is recycled, allowing for multiple invocations with the same
+     * parameters.
+     *
+     * TODO(b/64750076): Investigate consolidating member variables of {@link ActivityStarter} with
+     * the request object. Note that some member variables are referenced in
+     * {@link #dump(PrintWriter, String)} and therefore cannot be cleared immediately after
+     * execution.
+     */
+    private static class Request {
+        private static final int DEFAULT_CALLING_UID = -1;
+        private static final int DEFAULT_CALLING_PID = 0;
+
+        IApplicationThread caller;
+        Intent intent;
+        Intent ephemeralIntent;
+        String resolvedType;
+        ActivityInfo activityInfo;
+        ResolveInfo resolveInfo;
+        IVoiceInteractionSession voiceSession;
+        IVoiceInteractor voiceInteractor;
+        IBinder resultTo;
+        String resultWho;
+        int requestCode;
+        int callingPid = DEFAULT_CALLING_UID;
+        int callingUid = DEFAULT_CALLING_PID;
+        String callingPackage;
+        int realCallingPid;
+        int realCallingUid;
+        int startFlags;
+        SafeActivityOptions activityOptions;
+        boolean ignoreTargetSecurity;
+        boolean componentSpecified;
+        boolean avoidMoveToFront;
+        ActivityRecord[] outActivity;
+        TaskRecord inTask;
+        String reason;
+        ProfilerInfo profilerInfo;
+        Configuration globalConfig;
+        int userId;
+        WaitResult waitResult;
+        int filterCallingUid;
+        PendingIntentRecord originatingPendingIntent;
+
+        /**
+         * If set to {@code true}, allows this activity start to look into
+         * {@link PendingRemoteAnimationRegistry}
+         */
+        boolean allowPendingRemoteAnimationRegistryLookup;
+
+        /**
+         * Indicates that we should wait for the result of the start request. This flag is set when
+         * {@link ActivityStarter#setMayWait(int)} is called.
+         * {@see ActivityStarter#startActivityMayWait}.
+         */
+        boolean mayWait;
+
+        /**
+         * Ensure constructed request matches reset instance.
+         */
+        Request() {
+            reset();
+        }
+
+        /**
+         * Sets values back to the initial state, clearing any held references.
+         */
+        void reset() {
+            caller = null;
+            intent = null;
+            ephemeralIntent = null;
+            resolvedType = null;
+            activityInfo = null;
+            resolveInfo = null;
+            voiceSession = null;
+            voiceInteractor = null;
+            resultTo = null;
+            resultWho = null;
+            requestCode = 0;
+            callingPid = DEFAULT_CALLING_PID;
+            callingUid = DEFAULT_CALLING_UID;
+            callingPackage = null;
+            realCallingPid = 0;
+            realCallingUid = 0;
+            startFlags = 0;
+            activityOptions = null;
+            ignoreTargetSecurity = false;
+            componentSpecified = false;
+            outActivity = null;
+            inTask = null;
+            reason = null;
+            profilerInfo = null;
+            globalConfig = null;
+            userId = 0;
+            waitResult = null;
+            mayWait = false;
+            avoidMoveToFront = false;
+            allowPendingRemoteAnimationRegistryLookup = true;
+            filterCallingUid = UserHandle.USER_NULL;
+            originatingPendingIntent = null;
+        }
+
+        /**
+         * Adopts all values from passed in request.
+         */
+        void set(Request request) {
+            caller = request.caller;
+            intent = request.intent;
+            ephemeralIntent = request.ephemeralIntent;
+            resolvedType = request.resolvedType;
+            activityInfo = request.activityInfo;
+            resolveInfo = request.resolveInfo;
+            voiceSession = request.voiceSession;
+            voiceInteractor = request.voiceInteractor;
+            resultTo = request.resultTo;
+            resultWho = request.resultWho;
+            requestCode = request.requestCode;
+            callingPid = request.callingPid;
+            callingUid = request.callingUid;
+            callingPackage = request.callingPackage;
+            realCallingPid = request.realCallingPid;
+            realCallingUid = request.realCallingUid;
+            startFlags = request.startFlags;
+            activityOptions = request.activityOptions;
+            ignoreTargetSecurity = request.ignoreTargetSecurity;
+            componentSpecified = request.componentSpecified;
+            outActivity = request.outActivity;
+            inTask = request.inTask;
+            reason = request.reason;
+            profilerInfo = request.profilerInfo;
+            globalConfig = request.globalConfig;
+            userId = request.userId;
+            waitResult = request.waitResult;
+            mayWait = request.mayWait;
+            avoidMoveToFront = request.avoidMoveToFront;
+            allowPendingRemoteAnimationRegistryLookup
+                    = request.allowPendingRemoteAnimationRegistryLookup;
+            filterCallingUid = request.filterCallingUid;
+            originatingPendingIntent = request.originatingPendingIntent;
+        }
+    }
+
+    ActivityStarter(ActivityStartController controller, ActivityTaskManagerService service,
+            ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
+        mController = controller;
+        mService = service;
+        mSupervisor = supervisor;
+        mInterceptor = interceptor;
+        reset(true);
+    }
+
+    /**
+     * Effectively duplicates the starter passed in. All state and request values will be
+     * mirrored.
+     * @param starter
+     */
+    void set(ActivityStarter starter) {
+        mStartActivity = starter.mStartActivity;
+        mIntent = starter.mIntent;
+        mCallingUid = starter.mCallingUid;
+        mOptions = starter.mOptions;
+
+        mLaunchTaskBehind = starter.mLaunchTaskBehind;
+        mLaunchFlags = starter.mLaunchFlags;
+        mLaunchMode = starter.mLaunchMode;
+
+        mLaunchParams.set(starter.mLaunchParams);
+
+        mNotTop = starter.mNotTop;
+        mDoResume = starter.mDoResume;
+        mStartFlags = starter.mStartFlags;
+        mSourceRecord = starter.mSourceRecord;
+        mPreferredDisplayId = starter.mPreferredDisplayId;
+
+        mInTask = starter.mInTask;
+        mAddingToTask = starter.mAddingToTask;
+        mReuseTask = starter.mReuseTask;
+
+        mNewTaskInfo = starter.mNewTaskInfo;
+        mNewTaskIntent = starter.mNewTaskIntent;
+        mSourceStack = starter.mSourceStack;
+
+        mTargetStack = starter.mTargetStack;
+        mMovedToFront = starter.mMovedToFront;
+        mNoAnimation = starter.mNoAnimation;
+        mKeepCurTransition = starter.mKeepCurTransition;
+        mAvoidMoveToFront = starter.mAvoidMoveToFront;
+
+        mVoiceSession = starter.mVoiceSession;
+        mVoiceInteractor = starter.mVoiceInteractor;
+
+        mIntentDelivered = starter.mIntentDelivered;
+
+        mRequest.set(starter.mRequest);
+    }
+
+    ActivityRecord getStartActivity() {
+        return mStartActivity;
+    }
+
+    boolean relatedToPackage(String packageName) {
+        return (mLastStartActivityRecord[0] != null
+                && packageName.equals(mLastStartActivityRecord[0].packageName))
+                || (mStartActivity != null && packageName.equals(mStartActivity.packageName));
+    }
+
+    /**
+     * Starts an activity based on the request parameters provided earlier.
+     * @return The starter result.
+     */
+    int execute() {
+        try {
+            // TODO(b/64750076): Look into passing request directly to these methods to allow
+            // for transactional diffs and preprocessing.
+            if (mRequest.mayWait) {
+                return startActivityMayWait(mRequest.caller, mRequest.callingUid,
+                        mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,
+                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
+                        mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
+                        mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
+                        mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
+                        mRequest.inTask, mRequest.reason,
+                        mRequest.allowPendingRemoteAnimationRegistryLookup,
+                        mRequest.originatingPendingIntent);
+            } else {
+                return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
+                        mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
+                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
+                        mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,
+                        mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,
+                        mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,
+                        mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
+                        mRequest.outActivity, mRequest.inTask, mRequest.reason,
+                        mRequest.allowPendingRemoteAnimationRegistryLookup,
+                        mRequest.originatingPendingIntent);
+            }
+        } finally {
+            onExecutionComplete();
+        }
+    }
+
+    /**
+     * Starts an activity based on the provided {@link ActivityRecord} and environment parameters.
+     * Note that this method is called internally as well as part of {@link #startActivity}.
+     *
+     * @return The start result.
+     */
+    int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
+            ActivityRecord[] outActivity) {
+        try {
+            return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
+                    doResume, options, inTask, outActivity);
+        } finally {
+            onExecutionComplete();
+        }
+    }
+
+    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
+            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
+            SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
+            ActivityRecord[] outActivity, TaskRecord inTask, String reason,
+            boolean allowPendingRemoteAnimationRegistryLookup,
+            PendingIntentRecord originatingPendingIntent) {
+
+        if (TextUtils.isEmpty(reason)) {
+            throw new IllegalArgumentException("Need to specify a reason.");
+        }
+        mLastStartReason = reason;
+        mLastStartActivityTimeMs = System.currentTimeMillis();
+        mLastStartActivityRecord[0] = null;
+
+        mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType,
+                aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
+                callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
+                options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
+                inTask, allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent);
+
+        if (outActivity != null) {
+            // mLastStartActivityRecord[0] is set in the call to startActivity above.
+            outActivity[0] = mLastStartActivityRecord[0];
+        }
+
+        return getExternalResult(mLastStartActivityResult);
+    }
+
+    static int getExternalResult(int result) {
+        // Aborted results are treated as successes externally, but we must track them internally.
+        return result != START_ABORTED ? result : START_SUCCESS;
+    }
+
+    /**
+     * Called when execution is complete. Sets state indicating completion and proceeds with
+     * recycling if appropriate.
+     */
+    private void onExecutionComplete() {
+        mController.onExecutionComplete(this);
+    }
+
+    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
+            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
+            SafeActivityOptions options,
+            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
+            TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
+            PendingIntentRecord originatingPendingIntent) {
+        int err = ActivityManager.START_SUCCESS;
+        // Pull the optional Ephemeral Installer-only bundle out of the options early.
+        final Bundle verificationBundle
+                = options != null ? options.popAppVerificationBundle() : null;
+
+        WindowProcessController callerApp = null;
+        if (caller != null) {
+            callerApp = mService.getProcessController(caller);
+            if (callerApp != null) {
+                callingPid = callerApp.getPid();
+                callingUid = callerApp.mInfo.uid;
+            } else {
+                Slog.w(TAG, "Unable to find app for caller " + caller
+                        + " (pid=" + callingPid + ") when starting: "
+                        + intent.toString());
+                err = ActivityManager.START_PERMISSION_DENIED;
+            }
+        }
+
+        final int userId = aInfo != null && aInfo.applicationInfo != null
+                ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
+
+        if (err == ActivityManager.START_SUCCESS) {
+            Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
+                    + "} from uid " + callingUid);
+        }
+
+        ActivityRecord sourceRecord = null;
+        ActivityRecord resultRecord = null;
+        if (resultTo != null) {
+            sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
+            if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
+                    "Will send result to " + resultTo + " " + sourceRecord);
+            if (sourceRecord != null) {
+                if (requestCode >= 0 && !sourceRecord.finishing) {
+                    resultRecord = sourceRecord;
+                }
+            }
+        }
+
+        final int launchFlags = intent.getFlags();
+
+        if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
+            // Transfer the result target from the source activity to the new
+            // one being started, including any failures.
+            if (requestCode >= 0) {
+                SafeActivityOptions.abort(options);
+                return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
+            }
+            resultRecord = sourceRecord.resultTo;
+            if (resultRecord != null && !resultRecord.isInStackLocked()) {
+                resultRecord = null;
+            }
+            resultWho = sourceRecord.resultWho;
+            requestCode = sourceRecord.requestCode;
+            sourceRecord.resultTo = null;
+            if (resultRecord != null) {
+                resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
+            }
+            if (sourceRecord.launchedFromUid == callingUid) {
+                // The new activity is being launched from the same uid as the previous
+                // activity in the flow, and asking to forward its result back to the
+                // previous.  In this case the activity is serving as a trampoline between
+                // the two, so we also want to update its launchedFromPackage to be the
+                // same as the previous activity.  Note that this is safe, since we know
+                // these two packages come from the same uid; the caller could just as
+                // well have supplied that same package name itself.  This specifially
+                // deals with the case of an intent picker/chooser being launched in the app
+                // flow to redirect to an activity picked by the user, where we want the final
+                // activity to consider it to have been launched by the previous app activity.
+                callingPackage = sourceRecord.launchedFromPackage;
+            }
+        }
+
+        if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
+            // We couldn't find a class that can handle the given Intent.
+            // That's the end of that!
+            err = ActivityManager.START_INTENT_NOT_RESOLVED;
+        }
+
+        if (err == ActivityManager.START_SUCCESS && aInfo == null) {
+            // We couldn't find the specific class specified in the Intent.
+            // Also the end of the line.
+            err = ActivityManager.START_CLASS_NOT_FOUND;
+        }
+
+        if (err == ActivityManager.START_SUCCESS && sourceRecord != null
+                && sourceRecord.getTask().voiceSession != null) {
+            // If this activity is being launched as part of a voice session, we need
+            // to ensure that it is safe to do so.  If the upcoming activity will also
+            // be part of the voice session, we can only launch it if it has explicitly
+            // said it supports the VOICE category, or it is a part of the calling app.
+            if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0
+                    && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
+                try {
+                    intent.addCategory(Intent.CATEGORY_VOICE);
+                    if (!mService.getPackageManager().activitySupportsIntent(
+                            intent.getComponent(), intent, resolvedType)) {
+                        Slog.w(TAG,
+                                "Activity being started in current voice task does not support voice: "
+                                        + intent);
+                        err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+                    }
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failure checking voice capabilities", e);
+                    err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+                }
+            }
+        }
+
+        if (err == ActivityManager.START_SUCCESS && voiceSession != null) {
+            // If the caller is starting a new voice session, just make sure the target
+            // is actually allowing it to run this way.
+            try {
+                if (!mService.getPackageManager().activitySupportsIntent(intent.getComponent(),
+                        intent, resolvedType)) {
+                    Slog.w(TAG,
+                            "Activity being started in new voice task does not support: "
+                                    + intent);
+                    err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failure checking voice capabilities", e);
+                err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+            }
+        }
+
+        final ActivityStack resultStack = resultRecord == null ? null : resultRecord.getStack();
+
+        if (err != START_SUCCESS) {
+            if (resultRecord != null) {
+                resultStack.sendActivityResultLocked(
+                        -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
+            }
+            SafeActivityOptions.abort(options);
+            return err;
+        }
+
+        boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
+                requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
+                inTask != null, callerApp, resultRecord, resultStack);
+        abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
+                callingPid, resolvedType, aInfo.applicationInfo);
+
+        // Merge the two options bundles, while realCallerOptions takes precedence.
+        ActivityOptions checkedOptions = options != null
+                ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
+        if (allowPendingRemoteAnimationRegistryLookup) {
+            checkedOptions = mService.getActivityStartController()
+                    .getPendingRemoteAnimationRegistry()
+                    .overrideOptionsIfNeeded(callingPackage, checkedOptions);
+        }
+        if (mService.mController != null) {
+            try {
+                // The Intent we give to the watcher has the extra data
+                // stripped off, since it can contain private information.
+                Intent watchIntent = intent.cloneFilter();
+                abort |= !mService.mController.activityStarting(watchIntent,
+                        aInfo.applicationInfo.packageName);
+            } catch (RemoteException e) {
+                mService.mController = null;
+            }
+        }
+
+        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
+        if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid,
+                callingUid, checkedOptions)) {
+            // activity start was intercepted, e.g. because the target user is currently in quiet
+            // mode (turn off work) or the target application is suspended
+            intent = mInterceptor.mIntent;
+            rInfo = mInterceptor.mRInfo;
+            aInfo = mInterceptor.mAInfo;
+            resolvedType = mInterceptor.mResolvedType;
+            inTask = mInterceptor.mInTask;
+            callingPid = mInterceptor.mCallingPid;
+            callingUid = mInterceptor.mCallingUid;
+            checkedOptions = mInterceptor.mActivityOptions;
+        }
+
+        if (abort) {
+            if (resultRecord != null) {
+                resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
+                        RESULT_CANCELED, null);
+            }
+            // We pretend to the caller that it was really started, but
+            // they will just get a cancel result.
+            ActivityOptions.abort(checkedOptions);
+            return START_ABORTED;
+        }
+
+        // If permissions need a review before any of the app components can run, we
+        // launch the review activity and pass a pending intent to start the activity
+        // we are to launching now after the review is completed.
+        if (aInfo != null) {
+            if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+                    aInfo.packageName, userId)) {
+                IIntentSender target = mService.getIntentSenderLocked(
+                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+                        callingUid, userId, null, null, 0, new Intent[]{intent},
+                        new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
+                                | PendingIntent.FLAG_ONE_SHOT, null);
+
+                final int flags = intent.getFlags();
+                Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+                newIntent.setFlags(flags
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
+                newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+                if (resultRecord != null) {
+                    newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true);
+                }
+                intent = newIntent;
+
+                resolvedType = null;
+                callingUid = realCallingUid;
+                callingPid = realCallingPid;
+
+                rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, 0,
+                        computeResolveFilterUid(
+                                callingUid, realCallingUid, mRequest.filterCallingUid));
+                aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
+                        null /*profilerInfo*/);
+
+                if (DEBUG_PERMISSIONS_REVIEW) {
+                    final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+                    Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
+                            true, false) + "} from uid " + callingUid + " on display "
+                            + (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId));
+                }
+            }
+        }
+
+        // If we have an ephemeral app, abort the process of launching the resolved intent.
+        // Instead, launch the ephemeral installer. Once the installer is finished, it
+        // starts either the intent we resolved here [on install error] or the ephemeral
+        // app [on install success].
+        if (rInfo != null && rInfo.auxiliaryInfo != null) {
+            intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent,
+                    callingPackage, verificationBundle, resolvedType, userId);
+            resolvedType = null;
+            callingUid = realCallingUid;
+            callingPid = realCallingPid;
+
+            aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
+        }
+
+        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
+                callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
+                resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
+                mSupervisor, checkedOptions, sourceRecord);
+        if (outActivity != null) {
+            outActivity[0] = r;
+        }
+
+        if (r.appTimeTracker == null && sourceRecord != null) {
+            // If the caller didn't specify an explicit time tracker, we want to continue
+            // tracking under any it has.
+            r.appTimeTracker = sourceRecord.appTimeTracker;
+        }
+
+        final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+
+        // If we are starting an activity that is not from the same uid as the currently resumed
+        // one, check whether app switches are allowed.
+        if (voiceSession == null && (stack.getResumedActivity() == null
+                || stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {
+            if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
+                    realCallingPid, realCallingUid, "Activity start")) {
+                mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
+                        sourceRecord, startFlags, stack, callerApp));
+                ActivityOptions.abort(checkedOptions);
+                return ActivityManager.START_SWITCHES_CANCELED;
+            }
+        }
+
+        mService.onStartActivitySetDidAppSwitch();
+        mController.doPendingActivityLaunches(false);
+
+        maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r,
+                originatingPendingIntent);
+
+        return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
+                true /* doResume */, checkedOptions, inTask, outActivity);
+    }
+
+    private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,
+            Intent intent, WindowProcessController callerApp, ActivityRecord r,
+            PendingIntentRecord originatingPendingIntent) {
+        boolean callerAppHasForegroundActivity =
+                callerApp != null && callerApp.hasForegroundActivities();
+        if (!mService.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity
+                || r == null) {
+            // skip logging in this case
+            return;
+        }
+
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "logActivityStart");
+            final int callingUidProcState = mService.getUidStateLocked(callingUid);
+            final boolean callingUidHasAnyVisibleWindow =
+                    mService.mWindowManager.isAnyWindowVisibleForUid(callingUid);
+            final int realCallingUidProcState = (callingUid == realCallingUid)
+                    ? callingUidProcState
+                    : mService.getUidStateLocked(realCallingUid);
+            final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
+                    ? callingUidHasAnyVisibleWindow
+                    : mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid);
+            final String targetPackage = r.packageName;
+            final int targetUid = (r.appInfo != null) ? r.appInfo.uid : -1;
+            final int targetUidProcState = mService.getUidStateLocked(targetUid);
+            final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
+                    ? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid)
+                    : false;
+            final String targetWhitelistTag = (targetUid != -1)
+                    ? mService.getPendingTempWhitelistTagForUidLocked(targetUid)
+                    : null;
+
+            mSupervisor.getActivityMetricsLogger().logActivityStart(intent, callerApp, r,
+                    callingUid, callingPackage, callingUidProcState,
+                    callingUidHasAnyVisibleWindow,
+                    realCallingUid, realCallingUidProcState,
+                    realCallingUidHasAnyVisibleWindow,
+                    targetUid, targetPackage, targetUidProcState,
+                    targetUidHasAnyVisibleWindow, targetWhitelistTag,
+                    (originatingPendingIntent != null));
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    /**
+     * Creates a launch intent for the given auxiliary resolution data.
+     */
+    private @NonNull Intent createLaunchIntent(@Nullable AuxiliaryResolveInfo auxiliaryResponse,
+            Intent originalIntent, String callingPackage, Bundle verificationBundle,
+            String resolvedType, int userId) {
+        if (auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo) {
+            // request phase two resolution
+            mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo(
+                    auxiliaryResponse, originalIntent, resolvedType, callingPackage,
+                    verificationBundle, userId);
+        }
+        return InstantAppResolver.buildEphemeralInstallerIntent(
+                originalIntent,
+                InstantAppResolver.sanitizeIntent(originalIntent),
+                auxiliaryResponse == null ? null : auxiliaryResponse.failureIntent,
+                callingPackage,
+                verificationBundle,
+                resolvedType,
+                userId,
+                auxiliaryResponse == null ? null : auxiliaryResponse.installFailureActivity,
+                auxiliaryResponse == null ? null : auxiliaryResponse.token,
+                auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo,
+                auxiliaryResponse == null ? null : auxiliaryResponse.filters);
+    }
+
+    void postStartActivityProcessing(ActivityRecord r, int result,
+            ActivityStack startedActivityStack) {
+        if (ActivityManager.isStartResultFatalError(result)) {
+            return;
+        }
+
+        // We're waiting for an activity launch to finish, but that activity simply
+        // brought another activity to front. We must also handle the case where the task is already
+        // in the front as a result of the trampoline activity being in the same task (it will be
+        // considered focused as the trampoline will be finished). Let startActivityMayWait() know
+        // about this, so it waits for the new activity to become visible instead.
+        mSupervisor.reportWaitingActivityLaunchedIfNeeded(r, result);
+
+        if (startedActivityStack == null) {
+            return;
+        }
+
+        final int clearTaskFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK;
+        boolean clearedTask = (mLaunchFlags & clearTaskFlags) == clearTaskFlags
+                && mReuseTask != null;
+        if (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP || clearedTask) {
+            // The activity was already running so it wasn't started, but either brought to the
+            // front or the new intent was delivered to it since it was already in front. Notify
+            // anyone interested in this piece of information.
+            switch (startedActivityStack.getWindowingMode()) {
+                case WINDOWING_MODE_PINNED:
+                    mService.getTaskChangeNotificationController().notifyPinnedActivityRestartAttempt(
+                            clearedTask);
+                    break;
+                case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+                    final ActivityStack homeStack =
+                            startedActivityStack.getDisplay().getHomeStack();
+                    if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) {
+                        mService.mWindowManager.showRecentApps();
+                    }
+                    break;
+            }
+        }
+    }
+
+    private int startActivityMayWait(IApplicationThread caller, int callingUid,
+            String callingPackage, Intent intent, String resolvedType,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            IBinder resultTo, String resultWho, int requestCode, int startFlags,
+            ProfilerInfo profilerInfo, WaitResult outResult,
+            Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
+            int userId, TaskRecord inTask, String reason,
+            boolean allowPendingRemoteAnimationRegistryLookup,
+            PendingIntentRecord originatingPendingIntent) {
+        // Refuse possible leaked file descriptors
+        if (intent != null && intent.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+        mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
+        boolean componentSpecified = intent.getComponent() != null;
+
+        final int realCallingPid = Binder.getCallingPid();
+        final int realCallingUid = Binder.getCallingUid();
+
+        int callingPid;
+        if (callingUid >= 0) {
+            callingPid = -1;
+        } else if (caller == null) {
+            callingPid = realCallingPid;
+            callingUid = realCallingUid;
+        } else {
+            callingPid = callingUid = -1;
+        }
+
+        // Save a copy in case ephemeral needs it
+        final Intent ephemeralIntent = new Intent(intent);
+        // Don't modify the client's object!
+        intent = new Intent(intent);
+        if (componentSpecified
+                && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
+                && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
+                && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
+                && mService.getPackageManagerInternalLocked()
+                        .isInstantAppInstallerComponent(intent.getComponent())) {
+            // intercept intents targeted directly to the ephemeral installer the
+            // ephemeral installer should never be started with a raw Intent; instead
+            // adjust the intent so it looks like a "normal" instant app launch
+            intent.setComponent(null /*component*/);
+            componentSpecified = false;
+        }
+
+        ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
+                0 /* matchFlags */,
+                        computeResolveFilterUid(
+                                callingUid, realCallingUid, mRequest.filterCallingUid));
+        if (rInfo == null) {
+            UserInfo userInfo = mSupervisor.getUserInfo(userId);
+            if (userInfo != null && userInfo.isManagedProfile()) {
+                // Special case for managed profiles, if attempting to launch non-cryto aware
+                // app in a locked managed profile from an unlocked parent allow it to resolve
+                // as user will be sent via confirm credentials to unlock the profile.
+                UserManager userManager = UserManager.get(mService.mContext);
+                boolean profileLockedAndParentUnlockingOrUnlocked = false;
+                long token = Binder.clearCallingIdentity();
+                try {
+                    UserInfo parent = userManager.getProfileParent(userId);
+                    profileLockedAndParentUnlockingOrUnlocked = (parent != null)
+                            && userManager.isUserUnlockingOrUnlocked(parent.id)
+                            && !userManager.isUserUnlockingOrUnlocked(userId);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+                if (profileLockedAndParentUnlockingOrUnlocked) {
+                    rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
+                            PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                            computeResolveFilterUid(
+                                    callingUid, realCallingUid, mRequest.filterCallingUid));
+                }
+            }
+        }
+        // Collect information about the target of the Intent.
+        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
+
+        synchronized (mService.mGlobalLock) {
+            final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+            stack.mConfigWillChange = globalConfig != null
+                    && mService.getGlobalConfiguration().diff(globalConfig) != 0;
+            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                    "Starting activity when config will change = " + stack.mConfigWillChange);
+
+            final long origId = Binder.clearCallingIdentity();
+
+            if (aInfo != null &&
+                    (aInfo.applicationInfo.privateFlags
+                            & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 &&
+                    mService.mHasHeavyWeightFeature) {
+                // This may be a heavy-weight process!  Check to see if we already
+                // have another, different heavy-weight process running.
+                if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
+                    final WindowProcessController heavy = mService.mHeavyWeightProcess;
+                    if (heavy != null && (heavy.mInfo.uid != aInfo.applicationInfo.uid
+                            || !heavy.mName.equals(aInfo.processName))) {
+                        int appCallingUid = callingUid;
+                        if (caller != null) {
+                            WindowProcessController callerApp =
+                                    mService.getProcessController(caller);
+                            if (callerApp != null) {
+                                appCallingUid = callerApp.mInfo.uid;
+                            } else {
+                                Slog.w(TAG, "Unable to find app for caller " + caller
+                                        + " (pid=" + callingPid + ") when starting: "
+                                        + intent.toString());
+                                SafeActivityOptions.abort(options);
+                                return ActivityManager.START_PERMISSION_DENIED;
+                            }
+                        }
+
+                        IIntentSender target = mService.getIntentSenderLocked(
+                                ActivityManager.INTENT_SENDER_ACTIVITY, "android",
+                                appCallingUid, userId, null, null, 0, new Intent[] { intent },
+                                new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
+                                        | PendingIntent.FLAG_ONE_SHOT, null);
+
+                        Intent newIntent = new Intent();
+                        if (requestCode >= 0) {
+                            // Caller is requesting a result.
+                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
+                        }
+                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
+                                new IntentSender(target));
+                        heavy.updateIntentForHeavyWeightActivity(newIntent);
+                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
+                                aInfo.packageName);
+                        newIntent.setFlags(intent.getFlags());
+                        newIntent.setClassName("android",
+                                HeavyWeightSwitcherActivity.class.getName());
+                        intent = newIntent;
+                        resolvedType = null;
+                        caller = null;
+                        callingUid = Binder.getCallingUid();
+                        callingPid = Binder.getCallingPid();
+                        componentSpecified = true;
+                        rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId,
+                                0 /* matchFlags */, computeResolveFilterUid(
+                                        callingUid, realCallingUid, mRequest.filterCallingUid));
+                        aInfo = rInfo != null ? rInfo.activityInfo : null;
+                        if (aInfo != null) {
+                            aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
+                        }
+                    }
+                }
+            }
+
+            final ActivityRecord[] outRecord = new ActivityRecord[1];
+            int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
+                    voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
+                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
+                    ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
+                    allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent);
+
+            Binder.restoreCallingIdentity(origId);
+
+            if (stack.mConfigWillChange) {
+                // If the caller also wants to switch to a new configuration,
+                // do so now.  This allows a clean switch, as we are waiting
+                // for the current activity to pause (so we will not destroy
+                // it), and have not yet started the next activity.
+                mService.mAmInternal.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+                        "updateConfiguration()");
+                stack.mConfigWillChange = false;
+                if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+                        "Updating to new configuration after starting activity.");
+                mService.updateConfigurationLocked(globalConfig, null, false);
+            }
+
+            // Notify ActivityMetricsLogger that the activity has launched. ActivityMetricsLogger
+            // will then wait for the windows to be drawn and populate WaitResult.
+            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]);
+            if (outResult != null) {
+                outResult.result = res;
+
+                final ActivityRecord r = outRecord[0];
+
+                switch(res) {
+                    case START_SUCCESS: {
+                        mSupervisor.mWaitingActivityLaunched.add(outResult);
+                        do {
+                            try {
+                                mService.mGlobalLock.wait();
+                            } catch (InterruptedException e) {
+                            }
+                        } while (outResult.result != START_TASK_TO_FRONT
+                                && !outResult.timeout && outResult.who == null);
+                        if (outResult.result == START_TASK_TO_FRONT) {
+                            res = START_TASK_TO_FRONT;
+                        }
+                        break;
+                    }
+                    case START_DELIVERED_TO_TOP: {
+                        outResult.timeout = false;
+                        outResult.who = r.realActivity;
+                        outResult.totalTime = 0;
+                        break;
+                    }
+                    case START_TASK_TO_FRONT: {
+                        // ActivityRecord may represent a different activity, but it should not be
+                        // in the resumed state.
+                        if (r.nowVisible && r.isState(RESUMED)) {
+                            outResult.timeout = false;
+                            outResult.who = r.realActivity;
+                            outResult.totalTime = 0;
+                        } else {
+                            final long startTimeMs = SystemClock.uptimeMillis();
+                            mSupervisor.waitActivityVisible(r.realActivity, outResult, startTimeMs);
+                            // Note: the timeout variable is not currently not ever set.
+                            do {
+                                try {
+                                    mService.mGlobalLock.wait();
+                                } catch (InterruptedException e) {
+                                }
+                            } while (!outResult.timeout && outResult.who == null);
+                        }
+                        break;
+                    }
+                }
+            }
+
+            return res;
+        }
+    }
+
+    /**
+     * Compute the logical UID based on which the package manager would filter
+     * app components i.e. based on which the instant app policy would be applied
+     * because it is the logical calling UID.
+     *
+     * @param customCallingUid The UID on whose behalf to make the call.
+     * @param actualCallingUid The UID actually making the call.
+     * @param filterCallingUid The UID to be used to filter for instant apps.
+     * @return The logical UID making the call.
+     */
+    static int computeResolveFilterUid(int customCallingUid, int actualCallingUid,
+            int filterCallingUid) {
+        return filterCallingUid != UserHandle.USER_NULL
+                ? filterCallingUid
+                : (customCallingUid >= 0 ? customCallingUid : actualCallingUid);
+    }
+
+    private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
+                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
+                ActivityRecord[] outActivity) {
+        int result = START_CANCELED;
+        final ActivityStack startedActivityStack;
+        try {
+            mService.mWindowManager.deferSurfaceLayout();
+            result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
+                    startFlags, doResume, options, inTask, outActivity);
+        } finally {
+            final ActivityStack currentStack = r.getStack();
+            startedActivityStack = currentStack != null ? currentStack : mTargetStack;
+
+            if (ActivityManager.isStartResultSuccessful(result)) {
+                if (startedActivityStack != null) {
+                    // If there is no state change (e.g. a resumed activity is reparented to
+                    // top of another display) to trigger a visibility/configuration checking,
+                    // we have to update the configuration for changing to different display.
+                    final ActivityRecord currentTop =
+                            startedActivityStack.topRunningActivityLocked();
+                    if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
+                        mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(),
+                                true /* markFrozenIfConfigChanged */, false /* deferResume */);
+                    }
+                }
+            } else {
+                // If we are not able to proceed, disassociate the activity from the task.
+                // Leaving an activity in an incomplete state can lead to issues, such as
+                // performing operations without a window container.
+                final ActivityStack stack = mStartActivity.getStack();
+                if (stack != null) {
+                    stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
+                            null /* intentResultData */, "startActivity", true /* oomAdj */);
+                }
+            }
+            mService.mWindowManager.continueSurfaceLayout();
+        }
+
+        postStartActivityProcessing(r, result, startedActivityStack);
+
+        return result;
+    }
+
+    // Note: This method should only be called from {@link startActivity}.
+    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
+            ActivityRecord[] outActivity) {
+
+        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
+                voiceInteractor);
+        final int preferredWindowingMode = mLaunchParams.mWindowingMode;
+
+        // Do not start home activity if it cannot be launched on preferred display. We are not
+        // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
+        // fallback to launch on other displays.
+        if (r.isActivityTypeHome() && !mSupervisor.canStartHomeOnDisplay(r.info,
+                mPreferredDisplayId, true /* allowInstrumenting */)) {
+            Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId);
+            return START_CANCELED;
+        }
+
+        computeLaunchingTaskFlags();
+
+        computeSourceStack();
+
+        mIntent.setFlags(mLaunchFlags);
+
+        ActivityRecord reusedActivity = getReusableIntentActivity();
+
+        if (reusedActivity != null) {
+            // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
+            // still needs to be a lock task mode violation since the task gets cleared out and
+            // the device would otherwise leave the locked task.
+            if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(),
+                    (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
+                Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
+                return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
+
+            // True if we are clearing top and resetting of a standard (default) launch mode
+            // ({@code LAUNCH_MULTIPLE}) activity. The existing activity will be finished.
+            final boolean clearTopAndResetStandardLaunchMode =
+                    (mLaunchFlags & (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED))
+                            == (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+                    && mLaunchMode == LAUNCH_MULTIPLE;
+
+            // If mStartActivity does not have a task associated with it, associate it with the
+            // reused activity's task. Do not do so if we're clearing top and resetting for a
+            // standard launchMode activity.
+            if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) {
+                mStartActivity.setTask(reusedActivity.getTask());
+            }
+
+            if (reusedActivity.getTask().intent == null) {
+                // This task was started because of movement of the activity based on affinity...
+                // Now that we are actually launching it, we can assign the base intent.
+                reusedActivity.getTask().setIntent(mStartActivity);
+            }
+
+            // This code path leads to delivering a new intent, we want to make sure we schedule it
+            // as the first operation, in case the activity will be resumed as a result of later
+            // operations.
+            if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
+                    || isDocumentLaunchesIntoExisting(mLaunchFlags)
+                    || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
+                final TaskRecord task = reusedActivity.getTask();
+
+                // In this situation we want to remove all activities from the task up to the one
+                // being started. In most cases this means we are resetting the task to its initial
+                // state.
+                final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
+                        mLaunchFlags);
+
+                // The above code can remove {@code reusedActivity} from the task, leading to the
+                // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
+                // task reference is needed in the call below to
+                // {@link setTargetStackAndMoveToFrontIfNeeded}.
+                if (reusedActivity.getTask() == null) {
+                    reusedActivity.setTask(task);
+                }
+
+                if (top != null) {
+                    if (top.frontOfTask) {
+                        // Activity aliases may mean we use different intents for the top activity,
+                        // so make sure the task now has the identity of the new intent.
+                        top.getTask().setIntent(mStartActivity);
+                    }
+                    deliverNewIntent(top);
+                }
+            }
+
+            mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);
+
+            reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
+
+            final ActivityRecord outResult =
+                    outActivity != null && outActivity.length > 0 ? outActivity[0] : null;
+
+            // When there is a reused activity and the current result is a trampoline activity,
+            // set the reused activity as the result.
+            if (outResult != null && (outResult.finishing || outResult.noDisplay)) {
+                outActivity[0] = reusedActivity;
+            }
+
+            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
+                // We don't need to start a new activity, and the client said not to do anything
+                // if that is the case, so this is it!  And for paranoia, make sure we have
+                // correctly resumed the top activity.
+                resumeTargetStackIfNeeded();
+                return START_RETURN_INTENT_TO_CALLER;
+            }
+
+            if (reusedActivity != null) {
+                setTaskFromIntentActivity(reusedActivity);
+
+                if (!mAddingToTask && mReuseTask == null) {
+                    // We didn't do anything...  but it was needed (a.k.a., client don't use that
+                    // intent!)  And for paranoia, make sure we have correctly resumed the top activity.
+
+                    resumeTargetStackIfNeeded();
+                    if (outActivity != null && outActivity.length > 0) {
+                        outActivity[0] = reusedActivity;
+                    }
+
+                    return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
+                }
+            }
+        }
+
+        if (mStartActivity.packageName == null) {
+            final ActivityStack sourceStack = mStartActivity.resultTo != null
+                    ? mStartActivity.resultTo.getStack() : null;
+            if (sourceStack != null) {
+                sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
+                        mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
+                        null /* data */);
+            }
+            ActivityOptions.abort(mOptions);
+            return START_CLASS_NOT_FOUND;
+        }
+
+        // If the activity being launched is the same as the one currently at the top, then
+        // we need to check if it should only be launched once.
+        final ActivityStack topStack = mSupervisor.getTopDisplayFocusedStack();
+        final ActivityRecord topFocused = topStack.getTopActivity();
+        final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
+        final boolean dontStart = top != null && mStartActivity.resultTo == null
+                && top.realActivity.equals(mStartActivity.realActivity)
+                && top.userId == mStartActivity.userId
+                && top.attachedToProcess()
+                && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
+                || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
+                // This allows home activity to automatically launch on secondary display when
+                // display added, if home was the top activity on default display, instead of
+                // sending new intent to the home activity on default display.
+                && (!top.isActivityTypeHome() || top.getDisplayId() == mPreferredDisplayId);
+        if (dontStart) {
+            // For paranoia, make sure we have correctly resumed the top activity.
+            topStack.mLastPausedActivity = null;
+            if (mDoResume) {
+                mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            }
+            ActivityOptions.abort(mOptions);
+            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
+                // We don't need to start a new activity, and the client said not to do
+                // anything if that is the case, so this is it!
+                return START_RETURN_INTENT_TO_CALLER;
+            }
+
+            deliverNewIntent(top);
+
+            // Don't use mStartActivity.task to show the toast. We're not starting a new activity
+            // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
+            mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
+                    mPreferredDisplayId, topStack);
+
+            return START_DELIVERED_TO_TOP;
+        }
+
+        boolean newTask = false;
+        final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
+                ? mSourceRecord.getTask() : null;
+
+        // Should this be considered a new task?
+        int result = START_SUCCESS;
+        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
+                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+            newTask = true;
+            result = setTaskFromReuseOrCreateNewTask(taskToAffiliate);
+        } else if (mSourceRecord != null) {
+            result = setTaskFromSourceRecord();
+        } else if (mInTask != null) {
+            result = setTaskFromInTask();
+        } else {
+            // This not being started from an existing activity, and not part of a new task...
+            // just put it in the top task, though these days this case should never happen.
+            setTaskToCurrentTopOrCreateNewTask();
+        }
+        if (result != START_SUCCESS) {
+            return result;
+        }
+
+        mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
+                mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
+        mService.getPackageManagerInternalLocked().grantEphemeralAccess(
+                mStartActivity.userId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
+                UserHandle.getAppId(mCallingUid));
+        if (newTask) {
+            EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
+                    mStartActivity.getTask().taskId);
+        }
+        ActivityStack.logStartActivity(
+                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask());
+        mTargetStack.mLastPausedActivity = null;
+
+        mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity);
+
+        mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
+                mOptions);
+        if (mDoResume) {
+            final ActivityRecord topTaskActivity =
+                    mStartActivity.getTask().topRunningActivityLocked();
+            if (!mTargetStack.isFocusable()
+                    || (topTaskActivity != null && topTaskActivity.mTaskOverlay
+                    && mStartActivity != topTaskActivity)) {
+                // If the activity is not focusable, we can't resume it, but still would like to
+                // make sure it becomes visible as it starts (this will also trigger entry
+                // animation). An example of this are PIP activities.
+                // Also, we don't want to resume activities in a task that currently has an overlay
+                // as the starting activity just needs to be in the visible paused state until the
+                // over is removed.
+                mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                // Go ahead and tell window manager to execute app transition for this activity
+                // since the app transition will not be triggered through the resume channel.
+                mTargetStack.getDisplay().getWindowContainerController().executeAppTransition();
+            } else {
+                // If the target stack was not previously focusable (previous top running activity
+                // on that stack was not visible) then any prior calls to move the stack to the
+                // will not update the focused stack.  If starting the new activity now allows the
+                // task stack to be focusable, then ensure that we now update the focused stack
+                // accordingly.
+                if (mTargetStack.isFocusable()
+                        && !mSupervisor.isTopDisplayFocusedStack(mTargetStack)) {
+                    mTargetStack.moveToFront("startActivityUnchecked");
+                }
+                mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, mStartActivity,
+                        mOptions);
+            }
+        } else if (mStartActivity != null) {
+            mSupervisor.mRecentTasks.add(mStartActivity.getTask());
+        }
+        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+
+        mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
+                mPreferredDisplayId, mTargetStack);
+
+        return START_SUCCESS;
+    }
+
+    /**
+     * Resets the {@link ActivityStarter} state.
+     * @param clearRequest whether the request should be reset to default values.
+     */
+    void reset(boolean clearRequest) {
+        mStartActivity = null;
+        mIntent = null;
+        mCallingUid = -1;
+        mOptions = null;
+
+        mLaunchTaskBehind = false;
+        mLaunchFlags = 0;
+        mLaunchMode = INVALID_LAUNCH_MODE;
+
+        mLaunchParams.reset();
+
+        mNotTop = null;
+        mDoResume = false;
+        mStartFlags = 0;
+        mSourceRecord = null;
+        mPreferredDisplayId = INVALID_DISPLAY;
+
+        mInTask = null;
+        mAddingToTask = false;
+        mReuseTask = null;
+
+        mNewTaskInfo = null;
+        mNewTaskIntent = null;
+        mSourceStack = null;
+
+        mTargetStack = null;
+        mMovedToFront = false;
+        mNoAnimation = false;
+        mKeepCurTransition = false;
+        mAvoidMoveToFront = false;
+
+        mVoiceSession = null;
+        mVoiceInteractor = null;
+
+        mIntentDelivered = false;
+
+        if (clearRequest) {
+            mRequest.reset();
+        }
+    }
+
+    private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
+            boolean doResume, int startFlags, ActivityRecord sourceRecord,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+        reset(false /* clearRequest */);
+
+        mStartActivity = r;
+        mIntent = r.intent;
+        mOptions = options;
+        mCallingUid = r.launchedFromUid;
+        mSourceRecord = sourceRecord;
+        mVoiceSession = voiceSession;
+        mVoiceInteractor = voiceInteractor;
+
+        mLaunchParams.reset();
+
+        mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r,
+                sourceRecord, options, mLaunchParams);
+
+        if (mLaunchParams.hasPreferredDisplay()) {
+            mPreferredDisplayId = mLaunchParams.mPreferredDisplayId;
+        } else {
+            mPreferredDisplayId = DEFAULT_DISPLAY;
+        }
+
+        mLaunchMode = r.launchMode;
+
+        mLaunchFlags = adjustLaunchFlagsToDocumentMode(
+                r, LAUNCH_SINGLE_INSTANCE == mLaunchMode,
+                LAUNCH_SINGLE_TASK == mLaunchMode, mIntent.getFlags());
+        mLaunchTaskBehind = r.mLaunchTaskBehind
+                && !isLaunchModeOneOf(LAUNCH_SINGLE_TASK, LAUNCH_SINGLE_INSTANCE)
+                && (mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
+
+        sendNewTaskResultRequestIfNeeded();
+
+        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
+            mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
+        }
+
+        // If we are actually going to launch in to a new task, there are some cases where
+        // we further want to do multiple task.
+        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+            if (mLaunchTaskBehind
+                    || r.info.documentLaunchMode == DOCUMENT_LAUNCH_ALWAYS) {
+                mLaunchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK;
+            }
+        }
+
+        // We'll invoke onUserLeaving before onPause only if the launching
+        // activity did not explicitly state that this is an automated launch.
+        mSupervisor.mUserLeaving = (mLaunchFlags & FLAG_ACTIVITY_NO_USER_ACTION) == 0;
+        if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
+                "startActivity() => mUserLeaving=" + mSupervisor.mUserLeaving);
+
+        // If the caller has asked not to resume at this point, we make note
+        // of this in the record so that we can skip it when trying to find
+        // the top running activity.
+        mDoResume = doResume;
+        if (!doResume || !r.okToShowLocked()) {
+            r.delayedResume = true;
+            mDoResume = false;
+        }
+
+        if (mOptions != null) {
+            if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
+                r.mTaskOverlay = true;
+                if (!mOptions.canTaskOverlayResume()) {
+                    final TaskRecord task = mSupervisor.anyTaskForIdLocked(
+                            mOptions.getLaunchTaskId());
+                    final ActivityRecord top = task != null ? task.getTopActivity() : null;
+                    if (top != null && !top.isState(RESUMED)) {
+
+                        // The caller specifies that we'd like to be avoided to be moved to the
+                        // front, so be it!
+                        mDoResume = false;
+                        mAvoidMoveToFront = true;
+                    }
+                }
+            } else if (mOptions.getAvoidMoveToFront()) {
+                mDoResume = false;
+                mAvoidMoveToFront = true;
+            }
+        }
+
+        mNotTop = (mLaunchFlags & FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
+
+        mInTask = inTask;
+        // In some flows in to this function, we retrieve the task record and hold on to it
+        // without a lock before calling back in to here...  so the task at this point may
+        // not actually be in recents.  Check for that, and if it isn't in recents just
+        // consider it invalid.
+        if (inTask != null && !inTask.inRecents) {
+            Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
+            mInTask = null;
+        }
+
+        mStartFlags = startFlags;
+        // If the onlyIfNeeded flag is set, then we can do this if the activity being launched
+        // is the same as the one making the call...  or, as a special case, if we do not know
+        // the caller then we count the current top activity as the caller.
+        if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
+            ActivityRecord checkedCaller = sourceRecord;
+            if (checkedCaller == null) {
+                checkedCaller = mSupervisor.getTopDisplayFocusedStack()
+                        .topRunningNonDelayedActivityLocked(mNotTop);
+            }
+            if (!checkedCaller.realActivity.equals(r.realActivity)) {
+                // Caller is not the same as launcher, so always needed.
+                mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED;
+            }
+        }
+
+        mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
+    }
+
+    private void sendNewTaskResultRequestIfNeeded() {
+        final ActivityStack sourceStack = mStartActivity.resultTo != null
+                ? mStartActivity.resultTo.getStack() : null;
+        if (sourceStack != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+            // For whatever reason this activity is being launched into a new task...
+            // yet the caller has requested a result back.  Well, that is pretty messed up,
+            // so instead immediately send back a cancel and let the new task continue launched
+            // as normal without a dependency on its originator.
+            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
+            sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
+                    mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
+                    null /* data */);
+            mStartActivity.resultTo = null;
+        }
+    }
+
+    private void computeLaunchingTaskFlags() {
+        // If the caller is not coming from another activity, but has given us an explicit task into
+        // which they would like us to launch the new activity, then let's see about doing that.
+        if (mSourceRecord == null && mInTask != null && mInTask.getStack() != null) {
+            final Intent baseIntent = mInTask.getBaseIntent();
+            final ActivityRecord root = mInTask.getRootActivity();
+            if (baseIntent == null) {
+                ActivityOptions.abort(mOptions);
+                throw new IllegalArgumentException("Launching into task without base intent: "
+                        + mInTask);
+            }
+
+            // If this task is empty, then we are adding the first activity -- it
+            // determines the root, and must be launching as a NEW_TASK.
+            if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
+                if (!baseIntent.getComponent().equals(mStartActivity.intent.getComponent())) {
+                    ActivityOptions.abort(mOptions);
+                    throw new IllegalArgumentException("Trying to launch singleInstance/Task "
+                            + mStartActivity + " into different task " + mInTask);
+                }
+                if (root != null) {
+                    ActivityOptions.abort(mOptions);
+                    throw new IllegalArgumentException("Caller with mInTask " + mInTask
+                            + " has root " + root + " but target is singleInstance/Task");
+                }
+            }
+
+            // If task is empty, then adopt the interesting intent launch flags in to the
+            // activity being started.
+            if (root == null) {
+                final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK
+                        | FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+                mLaunchFlags = (mLaunchFlags & ~flagsOfInterest)
+                        | (baseIntent.getFlags() & flagsOfInterest);
+                mIntent.setFlags(mLaunchFlags);
+                mInTask.setIntent(mStartActivity);
+                mAddingToTask = true;
+
+                // If the task is not empty and the caller is asking to start it as the root of
+                // a new task, then we don't actually want to start this on the task. We will
+                // bring the task to the front, and possibly give it a new intent.
+            } else if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+                mAddingToTask = false;
+
+            } else {
+                mAddingToTask = true;
+            }
+
+            mReuseTask = mInTask;
+        } else {
+            mInTask = null;
+            // Launch ResolverActivity in the source task, so that it stays in the task bounds
+            // when in freeform workspace.
+            // Also put noDisplay activities in the source task. These by itself can be placed
+            // in any task/stack, however it could launch other activities like ResolverActivity,
+            // and we want those to stay in the original task.
+            if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
+                    && mSourceRecord.inFreeformWindowingMode())  {
+                mAddingToTask = true;
+            }
+        }
+
+        if (mInTask == null) {
+            if (mSourceRecord == null) {
+                // This activity is not being started from another...  in this
+                // case we -always- start a new task.
+                if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
+                    Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
+                            "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
+                    mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
+                }
+            } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
+                // The original activity who is starting us is running as a single
+                // instance...  this new activity it is starting must go on its
+                // own task.
+                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
+            } else if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
+                // The activity being started is a single instance...  it always
+                // gets launched into its own task.
+                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
+            }
+        }
+    }
+
+    private void computeSourceStack() {
+        if (mSourceRecord == null) {
+            mSourceStack = null;
+            return;
+        }
+        if (!mSourceRecord.finishing) {
+            mSourceStack = mSourceRecord.getStack();
+            return;
+        }
+
+        // If the source is finishing, we can't further count it as our source. This is because the
+        // task it is associated with may now be empty and on its way out, so we don't want to
+        // blindly throw it in to that task.  Instead we will take the NEW_TASK flow and try to find
+        // a task for it. But save the task information so it can be used when creating the new task.
+        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) {
+            Slog.w(TAG, "startActivity called from finishing " + mSourceRecord
+                    + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
+            mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
+            mNewTaskInfo = mSourceRecord.info;
+
+            // It is not guaranteed that the source record will have a task associated with it. For,
+            // example, if this method is being called for processing a pending activity launch, it
+            // is possible that the activity has been removed from the task after the launch was
+            // enqueued.
+            final TaskRecord sourceTask = mSourceRecord.getTask();
+            mNewTaskIntent = sourceTask != null ? sourceTask.intent : null;
+        }
+        mSourceRecord = null;
+        mSourceStack = null;
+    }
+
+    /**
+     * Decide whether the new activity should be inserted into an existing task. Returns null
+     * if not or an ActivityRecord with the task into which the new activity should be added.
+     */
+    private ActivityRecord getReusableIntentActivity() {
+        // We may want to try to place the new activity in to an existing task.  We always
+        // do this if the target activity is singleTask or singleInstance; we will also do
+        // this if NEW_TASK has been requested, and there is not an additional qualifier telling
+        // us to still place it in a new task: multi task, always doc mode, or being asked to
+        // launch this as a new task behind the current one.
+        boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
+                (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
+                || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);
+        // If bring to front is requested, and no result is requested and we have not been given
+        // an explicit task to launch in to, and we can find a task that was started with this
+        // same component, then instead of launching bring that one to the front.
+        putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
+        ActivityRecord intentActivity = null;
+        if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
+            final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
+            intentActivity = task != null ? task.getTopActivity() : null;
+        } else if (putIntoExistingTask) {
+            if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
+                // There can be one and only one instance of single instance activity in the
+                // history, and it is always in its own unique task, so we do a special search.
+               intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
+                       mStartActivity.isActivityTypeHome());
+            } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
+                // For the launch adjacent case we only want to put the activity in an existing
+                // task if the activity already exists in the history.
+                intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
+                        !(LAUNCH_SINGLE_TASK == mLaunchMode));
+            } else {
+                // Otherwise find the best task to put the activity in.
+                intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
+            }
+        }
+
+        if (mStartActivity.isActivityTypeHome() && intentActivity != null
+                && intentActivity.getDisplayId() != mPreferredDisplayId) {
+            // Do not reuse home activity on other displays.
+            intentActivity = null;
+        }
+
+        return intentActivity;
+    }
+
+    /**
+     * Figure out which task and activity to bring to front when we have found an existing matching
+     * activity record in history. May also clear the task if needed.
+     * @param intentActivity Existing matching activity.
+     * @return {@link ActivityRecord} brought to front.
+     */
+    private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) {
+        mTargetStack = intentActivity.getStack();
+        mTargetStack.mLastPausedActivity = null;
+        // If the target task is not in the front, then we need to bring it to the front...
+        // except...  well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
+        // the same behavior as if a new instance was being started, which means not bringing it
+        // to the front if the caller is not itself in the front.
+        final boolean differentTopTask;
+        if (mPreferredDisplayId == mTargetStack.mDisplayId) {
+            final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack();
+            final ActivityRecord curTop = (focusStack == null)
+                    ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
+            final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
+            differentTopTask = topTask != null
+                    && (topTask != intentActivity.getTask() || topTask != focusStack.topTask());
+        } else {
+            // The existing task should always be different from those in other displays.
+            differentTopTask = true;
+        }
+
+        if (differentTopTask && !mAvoidMoveToFront) {
+            mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+            if (mSourceRecord == null || (mSourceStack.getTopActivity() != null &&
+                    mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) {
+                // We really do want to push this one into the user's face, right now.
+                if (mLaunchTaskBehind && mSourceRecord != null) {
+                    intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
+                }
+
+                // If the launch flags carry both NEW_TASK and CLEAR_TASK, the task's activities
+                // will be cleared soon by ActivityStarter in setTaskFromIntentActivity().
+                // So no point resuming any of the activities here, it just wastes one extra
+                // resuming, plus enter AND exit transitions.
+                // Here we only want to bring the target stack forward. Transition will be applied
+                // to the new activity that's started after the old ones are gone.
+                final boolean willClearTask =
+                        (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+                if (!willClearTask) {
+                    final ActivityStack launchStack = getLaunchStack(
+                            mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions);
+                    final TaskRecord intentTask = intentActivity.getTask();
+                    if (launchStack == null || launchStack == mTargetStack) {
+                        // We only want to move to the front, if we aren't going to launch on a
+                        // different stack. If we launch on a different stack, we will put the
+                        // task on top there.
+                        mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
+                                mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
+                        mMovedToFront = true;
+                    } else if (launchStack.inSplitScreenWindowingMode()) {
+                        if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
+                            // If we want to launch adjacent and mTargetStack is not the computed
+                            // launch stack - move task to top of computed stack.
+                            intentTask.reparent(launchStack, ON_TOP,
+                                    REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
+                                    "launchToSide");
+                        } else {
+                            // TODO: This should be reevaluated in MW v2.
+                            // We choose to move task to front instead of launching it adjacent
+                            // when specific stack was requested explicitly and it appeared to be
+                            // adjacent stack, but FLAG_ACTIVITY_LAUNCH_ADJACENT was not set.
+                            mTargetStack.moveTaskToFrontLocked(intentTask,
+                                    mNoAnimation, mOptions, mStartActivity.appTimeTracker,
+                                    "bringToFrontInsteadOfAdjacentLaunch");
+                        }
+                        mMovedToFront = launchStack != launchStack.getDisplay()
+                                .getTopStackInWindowingMode(launchStack.getWindowingMode());
+                    } else if (launchStack.mDisplayId != mTargetStack.mDisplayId) {
+                        // Target and computed stacks are on different displays and we've
+                        // found a matching task - move the existing instance to that display and
+                        // move it to front.
+                        intentActivity.getTask().reparent(launchStack, ON_TOP,
+                                REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
+                                "reparentToDisplay");
+                        mMovedToFront = true;
+                    } else if (launchStack.isActivityTypeHome()
+                            && !mTargetStack.isActivityTypeHome()) {
+                        // It is possible for the home activity to be in another stack initially.
+                        // For example, the activity may have been initially started with an intent
+                        // which placed it in the fullscreen stack. To ensure the proper handling of
+                        // the activity based on home stack assumptions, we must move it over.
+                        intentActivity.getTask().reparent(launchStack, ON_TOP,
+                                REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
+                                "reparentingHome");
+                        mMovedToFront = true;
+                    }
+                    mOptions = null;
+
+                    // We are moving a task to the front, use starting window to hide initial drawn
+                    // delay.
+                    intentActivity.showStartingWindow(null /* prev */, false /* newTask */,
+                            true /* taskSwitch */);
+                }
+            }
+        }
+        // Need to update mTargetStack because if task was moved out of it, the original stack may
+        // be destroyed.
+        mTargetStack = intentActivity.getStack();
+        if (!mMovedToFront && mDoResume) {
+            if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack
+                    + " from " + intentActivity);
+            mTargetStack.moveToFront("intentActivityFound");
+        }
+
+        mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
+                WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack);
+
+        // If the caller has requested that the target task be reset, then do so.
+        if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+            return mTargetStack.resetTaskIfNeededLocked(intentActivity, mStartActivity);
+        }
+        return intentActivity;
+    }
+
+    private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
+        if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+                == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
+            // The caller has requested to completely replace any existing task with its new
+            // activity. Well that should not be too hard...
+            // Note: we must persist the {@link TaskRecord} first as intentActivity could be
+            // removed from calling performClearTaskLocked (For example, if it is being brought out
+            // of history or if it is finished immediately), thus disassociating the task. Also note
+            // that mReuseTask is reset as a result of {@link TaskRecord#performClearTaskLocked}
+            // launching another activity.
+            // TODO(b/36119896):  We shouldn't trigger activity launches in this path since we are
+            // already launching one.
+            final TaskRecord task = intentActivity.getTask();
+            task.performClearTaskLocked();
+            mReuseTask = task;
+            mReuseTask.setIntent(mStartActivity);
+        } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
+                || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
+            ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
+                    mLaunchFlags);
+            if (top == null) {
+                // A special case: we need to start the activity because it is not currently
+                // running, and the caller has asked to clear the current task to have this
+                // activity at the top.
+                mAddingToTask = true;
+
+                // We are no longer placing the activity in the task we previously thought we were.
+                mStartActivity.setTask(null);
+                // Now pretend like this activity is being started by the top of its task, so it
+                // is put in the right place.
+                mSourceRecord = intentActivity;
+                final TaskRecord task = mSourceRecord.getTask();
+                if (task != null && task.getStack() == null) {
+                    // Target stack got cleared when we all activities were removed above.
+                    // Go ahead and reset it.
+                    mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
+                            mLaunchFlags, mOptions);
+                    mTargetStack.addTask(task,
+                            !mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
+                }
+            }
+        } else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) {
+            // In this case the top activity on the task is the same as the one being launched,
+            // so we take that as a request to bring the task to the foreground. If the top
+            // activity in the task is the root activity, deliver this new intent to it if it
+            // desires.
+            if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
+                        || LAUNCH_SINGLE_TOP == mLaunchMode)
+                    && intentActivity.realActivity.equals(mStartActivity.realActivity)) {
+                if (intentActivity.frontOfTask) {
+                    intentActivity.getTask().setIntent(mStartActivity);
+                }
+                deliverNewIntent(intentActivity);
+            } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
+                // In this case we are launching the root activity of the task, but with a
+                // different intent. We should start a new instance on top.
+                mAddingToTask = true;
+                mSourceRecord = intentActivity;
+            }
+        } else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
+            // In this case an activity is being launched in to an existing task, without
+            // resetting that task. This is typically the situation of launching an activity
+            // from a notification or shortcut. We want to place the new activity on top of the
+            // current task.
+            mAddingToTask = true;
+            mSourceRecord = intentActivity;
+        } else if (!intentActivity.getTask().rootWasReset) {
+            // In this case we are launching into an existing task that has not yet been started
+            // from its front door. The current task has been brought to the front. Ideally,
+            // we'd probably like to place this new task at the bottom of its stack, but that's
+            // a little hard to do with the current organization of the code so for now we'll
+            // just drop it.
+            intentActivity.getTask().setIntent(mStartActivity);
+        }
+    }
+
+    private void resumeTargetStackIfNeeded() {
+        if (mDoResume) {
+            mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, null, mOptions);
+        } else {
+            ActivityOptions.abort(mOptions);
+        }
+        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+    }
+
+    private int setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) {
+        mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);
+
+        // Do no move the target stack to front yet, as we might bail if
+        // isLockTaskModeViolation fails below.
+
+        if (mReuseTask == null) {
+            final TaskRecord task = mTargetStack.createTaskRecord(
+                    mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
+                    mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
+                    mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
+                    mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord,
+                    mOptions);
+            addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
+            updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);
+
+            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
+                    + " in new task " + mStartActivity.getTask());
+        } else {
+            addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
+        }
+
+        if (taskToAffiliate != null) {
+            mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
+        }
+
+        if (mService.getLockTaskController().isLockTaskModeViolation(mStartActivity.getTask())) {
+            Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
+            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+        }
+
+        if (mDoResume) {
+            mTargetStack.moveToFront("reuseOrNewTask");
+        }
+        return START_SUCCESS;
+    }
+
+    private void deliverNewIntent(ActivityRecord activity) {
+        if (mIntentDelivered) {
+            return;
+        }
+
+        ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTask());
+        activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
+                mStartActivity.launchedFromPackage);
+        mIntentDelivered = true;
+    }
+
+    private int setTaskFromSourceRecord() {
+        if (mService.getLockTaskController().isLockTaskModeViolation(mSourceRecord.getTask())) {
+            Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
+            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+        }
+
+        final TaskRecord sourceTask = mSourceRecord.getTask();
+        final ActivityStack sourceStack = mSourceRecord.getStack();
+        // We only want to allow changing stack in two cases:
+        // 1. If the target task is not the top one. Otherwise we would move the launching task to
+        //    the other side, rather than show two side by side.
+        // 2. If activity is not allowed on target display.
+        final int targetDisplayId = mTargetStack != null ? mTargetStack.mDisplayId
+                : sourceStack.mDisplayId;
+        final boolean moveStackAllowed = sourceStack.topTask() != sourceTask
+                || !mStartActivity.canBeLaunchedOnDisplay(targetDisplayId);
+        if (moveStackAllowed) {
+            mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.getTask(),
+                    mOptions);
+            // If target stack is not found now - we can't just rely on the source stack, as it may
+            // be not suitable. Let's check other displays.
+            if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) {
+                // Can't use target display, lets find a stack on the source display.
+                mTargetStack = mSupervisor.getValidLaunchStackOnDisplay(
+                        sourceStack.mDisplayId, mStartActivity, mOptions);
+            }
+            if (mTargetStack == null) {
+                // There are no suitable stacks on the target and source display(s). Look on all
+                // displays.
+                mTargetStack = mSupervisor.getNextValidLaunchStackLocked(
+                        mStartActivity, -1 /* currentFocus */);
+            }
+        }
+
+        if (mTargetStack == null) {
+            mTargetStack = sourceStack;
+        } else if (mTargetStack != sourceStack) {
+            sourceTask.reparent(mTargetStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+                    DEFER_RESUME, "launchToSide");
+        }
+
+        final TaskRecord topTask = mTargetStack.topTask();
+        if (topTask != sourceTask && !mAvoidMoveToFront) {
+            mTargetStack.moveTaskToFrontLocked(sourceTask, mNoAnimation, mOptions,
+                    mStartActivity.appTimeTracker, "sourceTaskToFront");
+        } else if (mDoResume) {
+            mTargetStack.moveToFront("sourceStackToFront");
+        }
+
+        if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+            // In this case, we are adding the activity to an existing task, but the caller has
+            // asked to clear that task if the activity is already running.
+            ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
+            mKeepCurTransition = true;
+            if (top != null) {
+                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
+                deliverNewIntent(top);
+                // For paranoia, make sure we have correctly resumed the top activity.
+                mTargetStack.mLastPausedActivity = null;
+                if (mDoResume) {
+                    mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                }
+                ActivityOptions.abort(mOptions);
+                return START_DELIVERED_TO_TOP;
+            }
+        } else if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
+            // In this case, we are launching an activity in our own task that may already be
+            // running somewhere in the history, and we want to shuffle it to the front of the
+            // stack if so.
+            final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity);
+            if (top != null) {
+                final TaskRecord task = top.getTask();
+                task.moveActivityToFrontLocked(top);
+                top.updateOptionsLocked(mOptions);
+                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
+                deliverNewIntent(top);
+                mTargetStack.mLastPausedActivity = null;
+                if (mDoResume) {
+                    mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                }
+                return START_DELIVERED_TO_TOP;
+            }
+        }
+
+        // An existing activity is starting this new activity, so we want to keep the new one in
+        // the same task as the one that is starting it.
+        addOrReparentStartingActivity(sourceTask, "setTaskFromSourceRecord");
+        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
+                + " in existing task " + mStartActivity.getTask() + " from source " + mSourceRecord);
+        return START_SUCCESS;
+    }
+
+    private int setTaskFromInTask() {
+        // The caller is asking that the new activity be started in an explicit
+        // task it has provided to us.
+        if (mService.getLockTaskController().isLockTaskModeViolation(mInTask)) {
+            Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
+            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+        }
+
+        mTargetStack = mInTask.getStack();
+
+        // Check whether we should actually launch the new activity in to the task,
+        // or just reuse the current activity on top.
+        ActivityRecord top = mInTask.getTopActivity();
+        if (top != null && top.realActivity.equals(mStartActivity.realActivity)
+                && top.userId == mStartActivity.userId) {
+            if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
+                    || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK)) {
+                mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions,
+                        mStartActivity.appTimeTracker, "inTaskToFront");
+                if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
+                    // We don't need to start a new activity, and the client said not to do
+                    // anything if that is the case, so this is it!
+                    return START_RETURN_INTENT_TO_CALLER;
+                }
+                deliverNewIntent(top);
+                return START_DELIVERED_TO_TOP;
+            }
+        }
+
+        if (!mAddingToTask) {
+            mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions,
+                    mStartActivity.appTimeTracker, "inTaskToFront");
+            // We don't actually want to have this activity added to the task, so just
+            // stop here but still tell the caller that we consumed the intent.
+            ActivityOptions.abort(mOptions);
+            return START_TASK_TO_FRONT;
+        }
+
+        if (!mLaunchParams.mBounds.isEmpty()) {
+            // TODO: Shouldn't we already know what stack to use by the time we get here?
+            ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
+            if (stack != mInTask.getStack()) {
+                mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
+                        DEFER_RESUME, "inTaskToFront");
+                mTargetStack = mInTask.getStack();
+            }
+
+            updateBounds(mInTask, mLaunchParams.mBounds);
+        }
+
+        mTargetStack.moveTaskToFrontLocked(
+                mInTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "inTaskToFront");
+
+        addOrReparentStartingActivity(mInTask, "setTaskFromInTask");
+        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
+                + " in explicit task " + mStartActivity.getTask());
+
+        return START_SUCCESS;
+    }
+
+    @VisibleForTesting
+    void updateBounds(TaskRecord task, Rect bounds) {
+        if (bounds.isEmpty()) {
+            return;
+        }
+
+        final ActivityStack stack = task.getStack();
+        if (stack != null && stack.resizeStackWithLaunchBounds()) {
+            mService.resizeStack(
+                    stack.mStackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
+        } else {
+            task.updateOverrideConfiguration(bounds);
+        }
+    }
+
+    private void setTaskToCurrentTopOrCreateNewTask() {
+        mTargetStack = computeStackFocus(mStartActivity, false, mLaunchFlags, mOptions);
+        if (mDoResume) {
+            mTargetStack.moveToFront("addingToTopTask");
+        }
+        final ActivityRecord prev = mTargetStack.getTopActivity();
+        final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord(
+                mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info,
+                mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions);
+        addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask");
+        mTargetStack.positionChildWindowContainerAtTop(task);
+        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
+                + " in new guessed " + mStartActivity.getTask());
+    }
+
+    private void addOrReparentStartingActivity(TaskRecord parent, String reason) {
+        if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
+            parent.addActivityToTop(mStartActivity);
+        } else {
+            mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason);
+        }
+    }
+
+    private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance,
+            boolean launchSingleTask, int launchFlags) {
+        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
+                (launchSingleInstance || launchSingleTask)) {
+            // We have a conflict between the Intent and the Activity manifest, manifest wins.
+            Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
+                    "\"singleInstance\" or \"singleTask\"");
+            launchFlags &=
+                    ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
+        } else {
+            switch (r.info.documentLaunchMode) {
+                case ActivityInfo.DOCUMENT_LAUNCH_NONE:
+                    break;
+                case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
+                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+                    break;
+                case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
+                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+                    break;
+                case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
+                    launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
+                    break;
+            }
+        }
+        return launchFlags;
+    }
+
+    private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags,
+            ActivityOptions aOptions) {
+        final TaskRecord task = r.getTask();
+        ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
+        if (stack != null) {
+            return stack;
+        }
+
+        final ActivityStack currentStack = task != null ? task.getStack() : null;
+        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+        if (currentStack != null) {
+            if (focusedStack != currentStack) {
+                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                        "computeStackFocus: Setting " + "focused stack to r=" + r
+                                + " task=" + task);
+            } else {
+                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                        "computeStackFocus: Focused stack already=" + focusedStack);
+            }
+            return currentStack;
+        }
+
+        if (canLaunchIntoFocusedStack(r, newTask)) {
+            if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                    "computeStackFocus: Have a focused stack=" + focusedStack);
+            return focusedStack;
+        }
+
+        if (mPreferredDisplayId != DEFAULT_DISPLAY) {
+            // Try to put the activity in a stack on a secondary display.
+            stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r, aOptions);
+            if (stack == null) {
+                // If source display is not suitable - look for topmost valid stack in the system.
+                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                        "computeStackFocus: Can't launch on mPreferredDisplayId="
+                                + mPreferredDisplayId + ", looking on all displays.");
+                stack = mSupervisor.getNextValidLaunchStackLocked(r, mPreferredDisplayId);
+            }
+        }
+        if (stack == null) {
+            stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
+        }
+        if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+                + r + " stackId=" + stack.mStackId);
+        return stack;
+    }
+
+    /** Check if provided activity record can launch in currently focused stack. */
+    // TODO: This method can probably be consolidated into getLaunchStack() below.
+    private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
+        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+        final boolean canUseFocusedStack;
+        if (focusedStack.isActivityTypeAssistant()) {
+            canUseFocusedStack = r.isActivityTypeAssistant();
+        } else {
+            switch (focusedStack.getWindowingMode()) {
+                case WINDOWING_MODE_FULLSCREEN:
+                    // The fullscreen stack can contain any task regardless of if the task is
+                    // resizeable or not. So, we let the task go in the fullscreen task if it is the
+                    // focus stack.
+                    canUseFocusedStack = true;
+                    break;
+                case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+                case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
+                    // Any activity which supports split screen can go in the docked stack.
+                    canUseFocusedStack = r.supportsSplitScreenWindowingMode();
+                    break;
+                case WINDOWING_MODE_FREEFORM:
+                    // Any activity which supports freeform can go in the freeform stack.
+                    canUseFocusedStack = r.supportsFreeform();
+                    break;
+                default:
+                    // Dynamic stacks behave similarly to the fullscreen stack and can contain any
+                    // resizeable task.
+                    canUseFocusedStack = !focusedStack.isOnHomeDisplay()
+                            && r.canBeLaunchedOnDisplay(focusedStack.mDisplayId);
+            }
+        }
+        return canUseFocusedStack && !newTask
+                // Using the focus stack isn't important enough to override the preferred display.
+                && (mPreferredDisplayId == focusedStack.mDisplayId);
+    }
+
+    private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task,
+            ActivityOptions aOptions) {
+        // We are reusing a task, keep the stack!
+        if (mReuseTask != null) {
+            return mReuseTask.getStack();
+        }
+
+        if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0)
+                 || mPreferredDisplayId != DEFAULT_DISPLAY) {
+            // We don't pass in the default display id into the get launch stack call so it can do a
+            // full resolution.
+            final int candidateDisplay =
+                    mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY;
+            return mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP, candidateDisplay);
+        }
+        // Otherwise handle adjacent launch.
+
+        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+        // The parent activity doesn't want to launch the activity on top of itself, but
+        // instead tries to put it onto other side in side-by-side mode.
+        final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
+
+        if (parentStack != focusedStack) {
+            // If task's parent stack is not focused - use it during adjacent launch.
+            return parentStack;
+        } else {
+            if (focusedStack != null && task == focusedStack.topTask()) {
+                // If task is already on top of focused stack - use it. We don't want to move the
+                // existing focused task to adjacent stack, just deliver new intent in this case.
+                return focusedStack;
+            }
+
+            if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
+                // If parent was in docked stack, the natural place to launch another activity
+                // will be fullscreen, so it can appear alongside the docked window.
+                final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
+                return parentStack.getDisplay().getOrCreateStack(
+                        WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
+            } else {
+                // If the parent is not in the docked stack, we check if there is docked window
+                // and if yes, we will launch into that stack. If not, we just put the new
+                // activity into parent's stack, because we can't find a better place.
+                final ActivityStack dockedStack =
+                        mSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+                if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
+                    // There is a docked stack, but it isn't visible, so we can't launch into that.
+                    return mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
+                } else {
+                    return dockedStack;
+                }
+            }
+        }
+    }
+
+    private boolean isLaunchModeOneOf(int mode1, int mode2) {
+        return mode1 == mLaunchMode || mode2 == mLaunchMode;
+    }
+
+    static boolean isDocumentLaunchesIntoExisting(int flags) {
+        return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
+                (flags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0;
+    }
+
+    ActivityStarter setIntent(Intent intent) {
+        mRequest.intent = intent;
+        return this;
+    }
+
+    @VisibleForTesting
+    Intent getIntent() {
+        return mRequest.intent;
+    }
+
+    ActivityStarter setReason(String reason) {
+        mRequest.reason = reason;
+        return this;
+    }
+
+    ActivityStarter setCaller(IApplicationThread caller) {
+        mRequest.caller = caller;
+        return this;
+    }
+
+    ActivityStarter setEphemeralIntent(Intent intent) {
+        mRequest.ephemeralIntent = intent;
+        return this;
+    }
+
+
+    ActivityStarter setResolvedType(String type) {
+        mRequest.resolvedType = type;
+        return this;
+    }
+
+    ActivityStarter setActivityInfo(ActivityInfo info) {
+        mRequest.activityInfo = info;
+        return this;
+    }
+
+    ActivityStarter setResolveInfo(ResolveInfo info) {
+        mRequest.resolveInfo = info;
+        return this;
+    }
+
+    ActivityStarter setVoiceSession(IVoiceInteractionSession voiceSession) {
+        mRequest.voiceSession = voiceSession;
+        return this;
+    }
+
+    ActivityStarter setVoiceInteractor(IVoiceInteractor voiceInteractor) {
+        mRequest.voiceInteractor = voiceInteractor;
+        return this;
+    }
+
+    ActivityStarter setResultTo(IBinder resultTo) {
+        mRequest.resultTo = resultTo;
+        return this;
+    }
+
+    ActivityStarter setResultWho(String resultWho) {
+        mRequest.resultWho = resultWho;
+        return this;
+    }
+
+    ActivityStarter setRequestCode(int requestCode) {
+        mRequest.requestCode = requestCode;
+        return this;
+    }
+
+    ActivityStarter setCallingPid(int pid) {
+        mRequest.callingPid = pid;
+        return this;
+    }
+
+    ActivityStarter setCallingUid(int uid) {
+        mRequest.callingUid = uid;
+        return this;
+    }
+
+    ActivityStarter setCallingPackage(String callingPackage) {
+        mRequest.callingPackage = callingPackage;
+        return this;
+    }
+
+    ActivityStarter setRealCallingPid(int pid) {
+        mRequest.realCallingPid = pid;
+        return this;
+    }
+
+    ActivityStarter setRealCallingUid(int uid) {
+        mRequest.realCallingUid = uid;
+        return this;
+    }
+
+    ActivityStarter setStartFlags(int startFlags) {
+        mRequest.startFlags = startFlags;
+        return this;
+    }
+
+    ActivityStarter setActivityOptions(SafeActivityOptions options) {
+        mRequest.activityOptions = options;
+        return this;
+    }
+
+    ActivityStarter setActivityOptions(Bundle bOptions) {
+        return setActivityOptions(SafeActivityOptions.fromBundle(bOptions));
+    }
+
+    ActivityStarter setIgnoreTargetSecurity(boolean ignoreTargetSecurity) {
+        mRequest.ignoreTargetSecurity = ignoreTargetSecurity;
+        return this;
+    }
+
+    ActivityStarter setFilterCallingUid(int filterCallingUid) {
+        mRequest.filterCallingUid = filterCallingUid;
+        return this;
+    }
+
+    ActivityStarter setComponentSpecified(boolean componentSpecified) {
+        mRequest.componentSpecified = componentSpecified;
+        return this;
+    }
+
+    ActivityStarter setOutActivity(ActivityRecord[] outActivity) {
+        mRequest.outActivity = outActivity;
+        return this;
+    }
+
+    ActivityStarter setInTask(TaskRecord inTask) {
+        mRequest.inTask = inTask;
+        return this;
+    }
+
+    ActivityStarter setWaitResult(WaitResult result) {
+        mRequest.waitResult = result;
+        return this;
+    }
+
+    ActivityStarter setProfilerInfo(ProfilerInfo info) {
+        mRequest.profilerInfo = info;
+        return this;
+    }
+
+    ActivityStarter setGlobalConfiguration(Configuration config) {
+        mRequest.globalConfig = config;
+        return this;
+    }
+
+    ActivityStarter setUserId(int userId) {
+        mRequest.userId = userId;
+        return this;
+    }
+
+    ActivityStarter setMayWait(int userId) {
+        mRequest.mayWait = true;
+        mRequest.userId = userId;
+
+        return this;
+    }
+
+    ActivityStarter setAllowPendingRemoteAnimationRegistryLookup(boolean allowLookup) {
+        mRequest.allowPendingRemoteAnimationRegistryLookup = allowLookup;
+        return this;
+    }
+
+    ActivityStarter setOriginatingPendingIntent(PendingIntentRecord originatingPendingIntent) {
+        mRequest.originatingPendingIntent = originatingPendingIntent;
+        return this;
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        prefix = prefix + "  ";
+        pw.print(prefix);
+        pw.print("mCurrentUser=");
+        pw.println(mSupervisor.mCurrentUser);
+        pw.print(prefix);
+        pw.print("mLastStartReason=");
+        pw.println(mLastStartReason);
+        pw.print(prefix);
+        pw.print("mLastStartActivityTimeMs=");
+        pw.println(DateFormat.getDateTimeInstance().format(new Date(mLastStartActivityTimeMs)));
+        pw.print(prefix);
+        pw.print("mLastStartActivityResult=");
+        pw.println(mLastStartActivityResult);
+        ActivityRecord r = mLastStartActivityRecord[0];
+        if (r != null) {
+            pw.print(prefix);
+            pw.println("mLastStartActivityRecord:");
+            r.dump(pw, prefix + "  ");
+        }
+        if (mStartActivity != null) {
+            pw.print(prefix);
+            pw.println("mStartActivity:");
+            mStartActivity.dump(pw, prefix + "  ");
+        }
+        if (mIntent != null) {
+            pw.print(prefix);
+            pw.print("mIntent=");
+            pw.println(mIntent);
+        }
+        if (mOptions != null) {
+            pw.print(prefix);
+            pw.print("mOptions=");
+            pw.println(mOptions);
+        }
+        pw.print(prefix);
+        pw.print("mLaunchSingleTop=");
+        pw.print(LAUNCH_SINGLE_TOP == mLaunchMode);
+        pw.print(" mLaunchSingleInstance=");
+        pw.print(LAUNCH_SINGLE_INSTANCE == mLaunchMode);
+        pw.print(" mLaunchSingleTask=");
+        pw.println(LAUNCH_SINGLE_TASK == mLaunchMode);
+        pw.print(prefix);
+        pw.print("mLaunchFlags=0x");
+        pw.print(Integer.toHexString(mLaunchFlags));
+        pw.print(" mDoResume=");
+        pw.print(mDoResume);
+        pw.print(" mAddingToTask=");
+        pw.println(mAddingToTask);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java
new file mode 100644
index 0000000..7f09a07
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+/**
+ * Common class for the various debug {@link android.util.Log} output configuration relating to
+ * activities.
+ */
+public class ActivityTaskManagerDebugConfig {
+    // All output logs relating to acitvities use the {@link #TAG_ATM} string for tagging their log
+    // output. This makes it easy to identify the origin of the log message when sifting
+    // through a large amount of log output from multiple sources. However, it also makes trying
+    // to figure-out the origin of a log message while debugging the activity manager a little
+    // painful. By setting this constant to true, log messages from the activity manager package
+    // will be tagged with their class names instead fot the generic tag.
+    static final boolean TAG_WITH_CLASS_NAME = false;
+
+    // While debugging it is sometimes useful to have the category name of the log appended to the
+    // base log tag to make sifting through logs with the same base tag easier. By setting this
+    // constant to true, the category name of the log point will be appended to the log tag.
+    private static final boolean APPEND_CATEGORY_NAME = false;
+
+    // Default log tag for the activities.
+    static final String TAG_ATM = "ActivityTaskManager";
+
+    // Enable all debug log categories.
+    static final boolean DEBUG_ALL = false;
+
+    // Enable all debug log categories for activities.
+    private static final boolean DEBUG_ALL_ACTIVITIES = DEBUG_ALL || false;
+
+    static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false;
+    public static final boolean DEBUG_CONFIGURATION = DEBUG_ALL || false;
+    static final boolean DEBUG_CONTAINERS = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_FOCUS = false;
+    static final boolean DEBUG_IMMERSIVE = DEBUG_ALL || false;
+    static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false;
+    static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
+    static final boolean DEBUG_RECENTS = DEBUG_ALL || false;
+    static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false;
+    static final boolean DEBUG_SAVED_STATE = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_STACK = DEBUG_ALL || false;
+    static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;
+    public static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
+    static final boolean DEBUG_TASKS = DEBUG_ALL || false;
+    static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
+    static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
+    static final boolean DEBUG_APP = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_IDLE = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_RELEASE = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
+    static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
+    static final boolean DEBUG_RESULTS = DEBUG_ALL || false;
+    public static final boolean DEBUG_CLEANUP = DEBUG_ALL || false;
+    public static final boolean DEBUG_METRICS = DEBUG_ALL || false;
+
+    static final String POSTFIX_APP = APPEND_CATEGORY_NAME ? "_App" : "";
+    static final String POSTFIX_CLEANUP = (APPEND_CATEGORY_NAME) ? "_Cleanup" : "";
+    static final String POSTFIX_IDLE = APPEND_CATEGORY_NAME ? "_Idle" : "";
+    static final String POSTFIX_RELEASE = APPEND_CATEGORY_NAME ? "_Release" : "";
+    static final String POSTFIX_USER_LEAVING = APPEND_CATEGORY_NAME ? "_UserLeaving" : "";
+    static final String POSTFIX_ADD_REMOVE = APPEND_CATEGORY_NAME ? "_AddRemove" : "";
+    public static final String POSTFIX_CONFIGURATION = APPEND_CATEGORY_NAME ? "_Configuration" : "";
+    static final String POSTFIX_CONTAINERS = APPEND_CATEGORY_NAME ? "_Containers" : "";
+    static final String POSTFIX_FOCUS = APPEND_CATEGORY_NAME ? "_Focus" : "";
+    static final String POSTFIX_IMMERSIVE = APPEND_CATEGORY_NAME ? "_Immersive" : "";
+    public static final String POSTFIX_LOCKTASK = APPEND_CATEGORY_NAME ? "_LockTask" : "";
+    static final String POSTFIX_PAUSE = APPEND_CATEGORY_NAME ? "_Pause" : "";
+    static final String POSTFIX_RECENTS = APPEND_CATEGORY_NAME ? "_Recents" : "";
+    static final String POSTFIX_SAVED_STATE = APPEND_CATEGORY_NAME ? "_SavedState" : "";
+    static final String POSTFIX_STACK = APPEND_CATEGORY_NAME ? "_Stack" : "";
+    static final String POSTFIX_STATES = APPEND_CATEGORY_NAME ? "_States" : "";
+    public static final String POSTFIX_SWITCH = APPEND_CATEGORY_NAME ? "_Switch" : "";
+    static final String POSTFIX_TASKS = APPEND_CATEGORY_NAME ? "_Tasks" : "";
+    static final String POSTFIX_TRANSITION = APPEND_CATEGORY_NAME ? "_Transition" : "";
+    static final String POSTFIX_VISIBILITY = APPEND_CATEGORY_NAME ? "_Visibility" : "";
+    static final String POSTFIX_RESULTS = APPEND_CATEGORY_NAME ? "_Results" : "";
+}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 443d6fe..d665592 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -22,6 +22,7 @@
 import android.app.AppProtoEnums;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
+import android.app.ProfilerInfo;
 import android.content.ComponentName;
 import android.content.IIntentSender;
 import android.content.Intent;
@@ -33,22 +34,17 @@
 import android.os.SystemClock;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.SparseIntArray;
-
 import android.util.proto.ProtoOutputStream;
+
 import com.android.internal.app.IVoiceInteractor;
-import com.android.server.am.ActivityServiceConnectionsHolder;
 import com.android.server.am.PendingIntentRecord;
-import com.android.server.am.SafeActivityOptions;
-import com.android.server.am.TaskRecord;
 import com.android.server.am.UserState;
-import com.android.server.am.WindowProcessController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.List;
 import java.util.Set;
-import java.util.function.Predicate;
 
 /**
  * Activity Task manager local system service interface.
@@ -229,8 +225,9 @@
      * @param callback Callback to run after activity visibilities have been reevaluated. This can
      *                 be used from window manager so that when the callback is called, it's
      *                 guaranteed that all apps have their visibility updated accordingly.
+     * @param displayId The id of the display where the keyguard flags changed.
      */
-    public abstract void notifyKeyguardFlagsChanged(@Nullable Runnable callback);
+    public abstract void notifyKeyguardFlagsChanged(@Nullable Runnable callback, int displayId);
 
     /**
      * Called when the trusted state of Keyguard has changed.
@@ -348,6 +345,8 @@
     /** @return The intent used to launch the home activity. */
     public abstract Intent getHomeIntent();
     public abstract boolean startHomeActivity(int userId, String reason);
+    /** Start home activities on all displays that support system decorations. */
+    public abstract boolean startHomeOnAllDisplays(int userId, String reason);
     /** @return true if the given process is the factory test process. */
     public abstract boolean isFactoryTestProcess(WindowProcessController wpc);
     public abstract void updateTopComponentForFactoryTest();
@@ -410,6 +409,9 @@
             String[] args, int opti, boolean dumpAll, boolean dumpVisibleStacksOnly,
             boolean dumpFocusedStackOnly);
 
+    /** Dump the current state for inclusion in oom dump. */
+    public abstract void dumpForOom(PrintWriter pw);
+
     /** @return true if it the activity management system is okay with GC running now. */
     public abstract boolean canGcNow();
 
@@ -447,4 +449,28 @@
 
     public abstract void onUidAddedToPendingTempWhitelist(int uid, String tag);
     public abstract void onUidRemovedFromPendingTempWhitelist(int uid);
+
+    /** Handle app crash event in {@link android.app.IActivityController} if there is one. */
+    public abstract boolean handleAppCrashInActivityController(String processName, int pid,
+            String shortMsg, String longMsg, long timeMillis, String stackTrace,
+            Runnable killCrashingAppCallback);
+
+    public abstract void removeRecentTasksByPackageName(String packageName, int userId);
+    public abstract void cleanupRecentTasksForUser(int userId);
+    public abstract void loadRecentTasksForUser(int userId);
+    public abstract void onPackagesSuspendedChanged(String[] packages, boolean suspended,
+            int userId);
+    /** Flush recent tasks to disk. */
+    public abstract void flushRecentTasks();
+
+    public abstract WindowProcessController getHomeProcess();
+    public abstract WindowProcessController getPreviousProcess();
+
+    public abstract void clearLockedTasks(String reason);
+    public abstract void updateUserConfiguration();
+    public abstract boolean canShowErrorDialogs();
+
+    public abstract void setProfileApp(String profileApp);
+    public abstract void setProfileProc(WindowProcessController wpc);
+    public abstract void setProfilerInfo(ProfilerInfo profilerInfo);
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
new file mode 100644
index 0000000..433c05a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -0,0 +1,6761 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.Manifest.permission.BIND_VOICE_INTERACTION;
+import static android.Manifest.permission.CHANGE_CONFIGURATION;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+import static android.Manifest.permission.READ_FRAME_BUFFER;
+import static android.Manifest.permission.REMOVE_TASKS;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.Manifest.permission.STOP_APP_SWITCHES;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ApplicationInfo.FLAG_FACTORY_TEST;
+import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
+import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
+import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.content.pm.PackageManager.FEATURE_PC;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
+import static android.os.Build.VERSION_CODES.N;
+import static android.os.FactoryTest.FACTORY_TEST_HIGH_LEVEL;
+import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
+import static android.os.FactoryTest.FACTORY_TEST_OFF;
+import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
+import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
+import static android.provider.Settings.System.FONT_SCALE;
+import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
+
+import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
+import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
+import static com.android.server.am.ActivityManagerService.dumpStackTraces;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONTROLLER;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.GOING_TO_SLEEP;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.HOME_PROC;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.LAUNCHING_ACTIVITY;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto
+        .PREVIOUS_PROC_VISIBLE_TIME_MS;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage
+        .MODE;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage
+        .PACKAGE;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
+import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IMMERSIVE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IMMERSIVE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
+import static com.android.server.wm.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
+import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
+import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
+import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
+import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.ActivityThread;
+import android.app.AlertDialog;
+import android.app.AppGlobals;
+import android.app.Dialog;
+import android.app.IActivityController;
+import android.app.IActivityTaskManager;
+import android.app.IApplicationThread;
+import android.app.IAssistDataReceiver;
+import android.app.INotificationManager;
+import android.app.ITaskStackListener;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.PictureInPictureParams;
+import android.app.ProfilerInfo;
+import android.app.RemoteAction;
+import android.app.WaitResult;
+import android.app.WindowConfiguration;
+import android.app.admin.DevicePolicyCache;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.metrics.LogMaker;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.FactoryTest;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IUserManager;
+import android.os.LocaleList;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UpdateLock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.WorkSource;
+import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
+import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.VoiceInteractionManagerInternal;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
+import android.text.format.Time;
+import android.util.ArrayMap;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.StatsLog;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.view.IRecentsAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.view.WindowManager;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.AssistUtils;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ProcessMap;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.TransferPipe;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
+import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.KeyguardDismissCallback;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.AppOpsService;
+import com.android.server.AttributeCache;
+import com.android.server.DisplayThread;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
+import com.android.server.UiThread;
+import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.am.ActivityManagerServiceDumpActivitiesProto;
+import com.android.server.am.ActivityManagerServiceDumpProcessesProto;
+import com.android.server.am.AppTimeTracker;
+import com.android.server.am.BaseErrorDialog;
+import com.android.server.am.EventLogTags;
+import com.android.server.am.PendingIntentController;
+import com.android.server.am.PendingIntentRecord;
+import com.android.server.am.UserState;
+import com.android.server.firewall.IntentFirewall;
+import com.android.server.pm.UserManagerService;
+import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.vr.VrManagerInternal;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.ref.WeakReference;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * System service for managing activities and their containers (task, stacks, displays,... ).
+ *
+ * {@hide}
+ */
+public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
+    private static final String TAG_STACK = TAG + POSTFIX_STACK;
+    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+    private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
+    private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
+    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
+    private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
+    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
+
+    // How long we wait until we timeout on key dispatching.
+    public static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
+    // How long we wait until we timeout on key dispatching during instrumentation.
+    static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;
+
+    /** Used to indicate that an app transition should be animated. */
+    static final boolean ANIMATE = true;
+
+    /** Hardware-reported OpenGLES version. */
+    final int GL_ES_VERSION;
+
+    public static final String DUMP_ACTIVITIES_CMD = "activities" ;
+    public static final String DUMP_ACTIVITIES_SHORT_CMD = "a" ;
+    public static final String DUMP_LASTANR_CMD = "lastanr" ;
+    public static final String DUMP_LASTANR_TRACES_CMD = "lastanr-traces" ;
+    public static final String DUMP_STARTER_CMD = "starter" ;
+    public static final String DUMP_CONTAINERS_CMD = "containers" ;
+    public static final String DUMP_RECENTS_CMD = "recents" ;
+    public static final String DUMP_RECENTS_SHORT_CMD = "r" ;
+
+    /** This activity is not being relaunched, or being relaunched for a non-resize reason. */
+    public static final int RELAUNCH_REASON_NONE = 0;
+    /** This activity is being relaunched due to windowing mode change. */
+    public static final int RELAUNCH_REASON_WINDOWING_MODE_RESIZE = 1;
+    /** This activity is being relaunched due to a free-resize operation. */
+    public static final int RELAUNCH_REASON_FREE_RESIZE = 2;
+
+    Context mContext;
+
+    /**
+     * This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can
+     * change at runtime. Use mContext for non-UI purposes.
+     */
+    final Context mUiContext;
+    final ActivityThread mSystemThread;
+    H mH;
+    UiHandler mUiHandler;
+    ActivityManagerInternal mAmInternal;
+    UriGrantsManagerInternal mUgmInternal;
+    private PackageManagerInternal mPmInternal;
+    private ActivityTaskManagerInternal mInternal;
+    PowerManagerInternal mPowerManagerInternal;
+    private UsageStatsManagerInternal mUsageStatsInternal;
+
+    PendingIntentController mPendingIntentController;
+    IntentFirewall mIntentFirewall;
+
+    /* Global service lock used by the package the owns this service. */
+    final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
+    ActivityStackSupervisor mStackSupervisor;
+    WindowManagerService mWindowManager;
+    private UserManagerService mUserManager;
+    private AppOpsService mAppOpsService;
+    /** All active uids in the system. */
+    private final SparseArray<Integer> mActiveUids = new SparseArray<>();
+    private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>();
+    /** All processes currently running that might have a window organized by name. */
+    final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>();
+    /** All processes we currently have running mapped by pid */
+    final SparseArray<WindowProcessController> mPidMap = new SparseArray<>();
+    /** This is the process holding what we currently consider to be the "home" activity. */
+    WindowProcessController mHomeProcess;
+    /** The currently running heavy-weight process, if any. */
+    WindowProcessController mHeavyWeightProcess = null;
+    boolean mHasHeavyWeightFeature;
+    /**
+     * This is the process holding the activity the user last visited that is in a different process
+     * from the one they are currently in.
+     */
+    WindowProcessController mPreviousProcess;
+    /** The time at which the previous process was last visible. */
+    long mPreviousProcessVisibleTime;
+
+    /** List of intents that were used to start the most recent tasks. */
+    private RecentTasks mRecentTasks;
+    /** State of external calls telling us if the device is awake or asleep. */
+    private boolean mKeyguardShown = false;
+
+    // Wrapper around VoiceInteractionServiceManager
+    private AssistUtils mAssistUtils;
+
+    // VoiceInteraction session ID that changes for each new request except when
+    // being called for multi-window assist in a single session.
+    private int mViSessionId = 1000;
+
+    // How long to wait in getAssistContextExtras for the activity and foreground services
+    // to respond with the result.
+    private static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
+
+    // How long top wait when going through the modern assist (which doesn't need to block
+    // on getting this result before starting to launch its UI).
+    private static final int PENDING_ASSIST_EXTRAS_LONG_TIMEOUT = 2000;
+
+    // How long to wait in getAutofillAssistStructure() for the activity to respond with the result.
+    private static final int PENDING_AUTOFILL_ASSIST_STRUCTURE_TIMEOUT = 2000;
+
+    private final ArrayList<PendingAssistExtras> mPendingAssistExtras = new ArrayList<>();
+
+    // Keeps track of the active voice interaction service component, notified from
+    // VoiceInteractionManagerService
+    ComponentName mActiveVoiceInteractionServiceComponent;
+
+    VrController mVrController;
+    KeyguardController mKeyguardController;
+    private final ClientLifecycleManager mLifecycleManager;
+    private TaskChangeNotificationController mTaskChangeNotificationController;
+    /** The controller for all operations related to locktask. */
+    private LockTaskController mLockTaskController;
+    private ActivityStartController mActivityStartController;
+
+    boolean mSuppressResizeConfigChanges;
+
+    private final UpdateConfigurationResult mTmpUpdateConfigurationResult =
+            new UpdateConfigurationResult();
+
+    static final class UpdateConfigurationResult {
+        // Configuration changes that were updated.
+        int changes;
+        // If the activity was relaunched to match the new configuration.
+        boolean activityRelaunched;
+
+        void reset() {
+            changes = 0;
+            activityRelaunched = false;
+        }
+    }
+
+    /** Current sequencing integer of the configuration, for skipping old configurations. */
+    private int mConfigurationSeq;
+    // To cache the list of supported system locales
+    private String[] mSupportedSystemLocales = null;
+
+    /**
+     * Temp object used when global and/or display override configuration is updated. It is also
+     * sent to outer world instead of {@link #getGlobalConfiguration} because we don't trust
+     * anyone...
+     */
+    private Configuration mTempConfig = new Configuration();
+
+    /** Temporary to avoid allocations. */
+    final StringBuilder mStringBuilder = new StringBuilder(256);
+
+    // Amount of time after a call to stopAppSwitches() during which we will
+    // prevent further untrusted switches from happening.
+    private static final long APP_SWITCH_DELAY_TIME = 5 * 1000;
+
+    /**
+     * The time at which we will allow normal application switches again,
+     * after a call to {@link #stopAppSwitches()}.
+     */
+    private long mAppSwitchesAllowedTime;
+    /**
+     * This is set to true after the first switch after mAppSwitchesAllowedTime
+     * is set; any switches after that will clear the time.
+     */
+    private boolean mDidAppSwitch;
+
+    IActivityController mController = null;
+    boolean mControllerIsAMonkey = false;
+
+    final int mFactoryTest;
+
+    /** Used to control how we initialize the service. */
+    ComponentName mTopComponent;
+    String mTopAction = Intent.ACTION_MAIN;
+    String mTopData;
+
+    /** Profiling app information. */
+    String mProfileApp = null;
+    WindowProcessController mProfileProc = null;
+    ProfilerInfo mProfilerInfo = null;
+
+    /**
+     * Dump of the activity state at the time of the last ANR. Cleared after
+     * {@link WindowManagerService#LAST_ANR_LIFETIME_DURATION_MSECS}
+     */
+    String mLastANRState;
+
+    /**
+     * Used to retain an update lock when the foreground activity is in
+     * immersive mode.
+     */
+    private final UpdateLock mUpdateLock = new UpdateLock("immersive");
+
+    /**
+     * Packages that are being allowed to perform unrestricted app switches.  Mapping is
+     * User -> Type -> uid.
+     */
+    final SparseArray<ArrayMap<String, Integer>> mAllowAppSwitchUids = new SparseArray<>();
+
+    /** The dimensions of the thumbnails in the Recents UI. */
+    private int mThumbnailWidth;
+    private int mThumbnailHeight;
+    private float mFullscreenThumbnailScale;
+
+    /**
+     * Flag that indicates if multi-window is enabled.
+     *
+     * For any particular form of multi-window to be enabled, generic multi-window must be enabled
+     * in {@link com.android.internal.R.bool#config_supportsMultiWindow} config or
+     * {@link Settings.Global#DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES} development option set.
+     * At least one of the forms of multi-window must be enabled in order for this flag to be
+     * initialized to 'true'.
+     *
+     * @see #mSupportsSplitScreenMultiWindow
+     * @see #mSupportsFreeformWindowManagement
+     * @see #mSupportsPictureInPicture
+     * @see #mSupportsMultiDisplay
+     */
+    boolean mSupportsMultiWindow;
+    boolean mSupportsSplitScreenMultiWindow;
+    boolean mSupportsFreeformWindowManagement;
+    boolean mSupportsPictureInPicture;
+    boolean mSupportsMultiDisplay;
+    boolean mForceResizableActivities;
+
+    final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
+
+    // VR Vr2d Display Id.
+    int mVr2dDisplayId = INVALID_DISPLAY;
+
+    /**
+     * Set while we are wanting to sleep, to prevent any
+     * activities from being started/resumed.
+     *
+     * TODO(b/33594039): Clarify the actual state transitions represented by mSleeping.
+     *
+     * Currently mSleeping is set to true when transitioning into the sleep state, and remains true
+     * while in the sleep state until there is a pending transition out of sleep, in which case
+     * mSleeping is set to false, and remains false while awake.
+     *
+     * Whether mSleeping can quickly toggled between true/false without the device actually
+     * display changing states is undefined.
+     */
+    private boolean mSleeping = false;
+
+    /**
+     * The process state used for processes that are running the top activities.
+     * This changes between TOP and TOP_SLEEPING to following mSleeping.
+     */
+    int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+
+    // Whether we should show our dialogs (ANR, crash, etc) or just perform their default action
+    // automatically. Important for devices without direct input devices.
+    private boolean mShowDialogs = true;
+
+    /** Set if we are shutting down the system, similar to sleeping. */
+    boolean mShuttingDown = false;
+
+    /**
+     * We want to hold a wake lock while running a voice interaction session, since
+     * this may happen with the screen off and we need to keep the CPU running to
+     * be able to continue to interact with the user.
+     */
+    PowerManager.WakeLock mVoiceWakeLock;
+
+    /**
+     * Set while we are running a voice interaction. This overrides sleeping while it is active.
+     */
+    IVoiceInteractionSession mRunningVoice;
+
+    /**
+     * The last resumed activity. This is identical to the current resumed activity most
+     * of the time but could be different when we're pausing one activity before we resume
+     * another activity.
+     */
+    ActivityRecord mLastResumedActivity;
+
+    /**
+     * The activity that is currently being traced as the active resumed activity.
+     *
+     * @see #updateResumedAppTrace
+     */
+    private @Nullable ActivityRecord mTracedResumedActivity;
+
+    /** If non-null, we are tracking the time the user spends in the currently focused app. */
+    AppTimeTracker mCurAppTimeTracker;
+
+    private AppWarnings mAppWarnings;
+
+    /**
+     * Packages that the user has asked to have run in screen size
+     * compatibility mode instead of filling the screen.
+     */
+    CompatModePackages mCompatModePackages;
+
+    private FontScaleSettingObserver mFontScaleSettingObserver;
+
+    private final class FontScaleSettingObserver extends ContentObserver {
+        private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE);
+        private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS);
+
+        public FontScaleSettingObserver() {
+            super(mH);
+            final ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(mFontScaleUri, false, this, UserHandle.USER_ALL);
+            resolver.registerContentObserver(mHideErrorDialogsUri, false, this,
+                    UserHandle.USER_ALL);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
+            if (mFontScaleUri.equals(uri)) {
+                updateFontScaleIfNeeded(userId);
+            } else if (mHideErrorDialogsUri.equals(uri)) {
+                synchronized (mGlobalLock) {
+                    updateShouldShowDialogsLocked(getGlobalConfiguration());
+                }
+            }
+        }
+    }
+
+    ActivityTaskManagerService(Context context) {
+        mContext = context;
+        mFactoryTest = FactoryTest.getMode();
+        mSystemThread = ActivityThread.currentActivityThread();
+        mUiContext = mSystemThread.getSystemUiContext();
+        mLifecycleManager = new ClientLifecycleManager();
+        GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
+    }
+
+    public void onSystemReady() {
+        synchronized (mGlobalLock) {
+            mHasHeavyWeightFeature = mContext.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_CANT_SAVE_STATE);
+            mAssistUtils = new AssistUtils(mContext);
+            mVrController.onSystemReady();
+            mRecentTasks.onSystemReadyLocked();
+        }
+    }
+
+    public void onInitPowerManagement() {
+        synchronized (mGlobalLock) {
+            mStackSupervisor.initPowerManagement();
+            final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+            mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+            mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
+            mVoiceWakeLock.setReferenceCounted(false);
+        }
+    }
+
+    public void installSystemProviders() {
+        mFontScaleSettingObserver = new FontScaleSettingObserver();
+    }
+
+    public void retrieveSettings(ContentResolver resolver) {
+        final boolean freeformWindowManagement =
+                mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
+                        || Settings.Global.getInt(
+                        resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+
+        final boolean supportsMultiWindow = ActivityTaskManager.supportsMultiWindow(mContext);
+        final boolean supportsPictureInPicture = supportsMultiWindow &&
+                mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
+        final boolean supportsSplitScreenMultiWindow =
+                ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
+        final boolean supportsMultiDisplay = mContext.getPackageManager()
+                .hasSystemFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
+        final boolean alwaysFinishActivities =
+                Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
+        final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
+        final boolean forceResizable = Settings.Global.getInt(
+                resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
+        final boolean isPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
+
+        // Transfer any global setting for forcing RTL layout, into a System Property
+        SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
+
+        final Configuration configuration = new Configuration();
+        Settings.System.getConfiguration(resolver, configuration);
+        if (forceRtl) {
+            // This will take care of setting the correct layout direction flags
+            configuration.setLayoutDirection(configuration.locale);
+        }
+
+        synchronized (mGlobalLock) {
+            mForceResizableActivities = forceResizable;
+            final boolean multiWindowFormEnabled = freeformWindowManagement
+                    || supportsSplitScreenMultiWindow
+                    || supportsPictureInPicture
+                    || supportsMultiDisplay;
+            if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) {
+                mSupportsMultiWindow = true;
+                mSupportsFreeformWindowManagement = freeformWindowManagement;
+                mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
+                mSupportsPictureInPicture = supportsPictureInPicture;
+                mSupportsMultiDisplay = supportsMultiDisplay;
+            } else {
+                mSupportsMultiWindow = false;
+                mSupportsFreeformWindowManagement = false;
+                mSupportsSplitScreenMultiWindow = false;
+                mSupportsPictureInPicture = false;
+                mSupportsMultiDisplay = false;
+            }
+            mWindowManager.setForceResizableTasks(mForceResizableActivities);
+            mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
+            mWindowManager.setSupportsFreeformWindowManagement(mSupportsFreeformWindowManagement);
+            mWindowManager.setIsPc(isPc);
+            // This happens before any activities are started, so we can change global configuration
+            // in-place.
+            updateConfigurationLocked(configuration, null, true);
+            final Configuration globalConfig = getGlobalConfiguration();
+            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Initial config: " + globalConfig);
+
+            // Load resources only after the current configuration has been set.
+            final Resources res = mContext.getResources();
+            mThumbnailWidth = res.getDimensionPixelSize(
+                    com.android.internal.R.dimen.thumbnail_width);
+            mThumbnailHeight = res.getDimensionPixelSize(
+                    com.android.internal.R.dimen.thumbnail_height);
+
+            if ((globalConfig.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
+                mFullscreenThumbnailScale = (float) res
+                        .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
+                        (float) globalConfig.screenWidthDp;
+            } else {
+                mFullscreenThumbnailScale = res.getFraction(
+                        com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
+            }
+        }
+    }
+
+    public WindowManagerGlobalLock getGlobalLock() {
+        return mGlobalLock;
+    }
+
+    public void setActivityManagerService(IntentFirewall intentFirewall,
+            PendingIntentController intentController) {
+        mH = new H();
+        mUiHandler = new UiHandler();
+        mIntentFirewall = intentFirewall;
+        final File systemDir = SystemServiceManager.ensureSystemDir();
+        mAppWarnings = new AppWarnings(this, mUiContext, mH, mUiHandler, systemDir);
+        mCompatModePackages = new CompatModePackages(this, systemDir, mH);
+        mPendingIntentController = intentController;
+
+        mTempConfig.setToDefaults();
+        mTempConfig.setLocales(LocaleList.getDefault());
+        mConfigurationSeq = mTempConfig.seq = 1;
+        mStackSupervisor = createStackSupervisor();
+        mStackSupervisor.onConfigurationChanged(mTempConfig);
+
+        mTaskChangeNotificationController =
+                new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
+        mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mH);
+        mActivityStartController = new ActivityStartController(this);
+        mRecentTasks = createRecentTasks();
+        mStackSupervisor.setRecentTasks(mRecentTasks);
+        mVrController = new VrController(mGlobalLock);
+        mKeyguardController = mStackSupervisor.getKeyguardController();
+    }
+
+    public void onActivityManagerInternalAdded() {
+        synchronized (mGlobalLock) {
+            mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+            mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
+        }
+    }
+
+    int increaseConfigurationSeqLocked() {
+        mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
+        return mConfigurationSeq;
+    }
+
+    protected ActivityStackSupervisor createStackSupervisor() {
+        final ActivityStackSupervisor supervisor = new ActivityStackSupervisor(this, mH.getLooper());
+        supervisor.initialize();
+        return supervisor;
+    }
+
+    public void setWindowManager(WindowManagerService wm) {
+        synchronized (mGlobalLock) {
+            mWindowManager = wm;
+            mLockTaskController.setWindowManager(wm);
+            mStackSupervisor.setWindowManager(wm);
+        }
+    }
+
+    public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
+        synchronized (mGlobalLock) {
+            mUsageStatsInternal = usageStatsManager;
+        }
+    }
+
+    UserManagerService getUserManager() {
+        if (mUserManager == null) {
+            IBinder b = ServiceManager.getService(Context.USER_SERVICE);
+            mUserManager = (UserManagerService) IUserManager.Stub.asInterface(b);
+        }
+        return mUserManager;
+    }
+
+    AppOpsService getAppOpsService() {
+        if (mAppOpsService == null) {
+            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+            mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b);
+        }
+        return mAppOpsService;
+    }
+
+    boolean hasUserRestriction(String restriction, int userId) {
+        return getUserManager().hasUserRestriction(restriction, userId);
+    }
+
+    protected RecentTasks createRecentTasks() {
+        return new RecentTasks(this, mStackSupervisor);
+    }
+
+    RecentTasks getRecentTasks() {
+        return mRecentTasks;
+    }
+
+    ClientLifecycleManager getLifecycleManager() {
+        return mLifecycleManager;
+    }
+
+    ActivityStartController getActivityStartController() {
+        return mActivityStartController;
+    }
+
+    TaskChangeNotificationController getTaskChangeNotificationController() {
+        return mTaskChangeNotificationController;
+    }
+
+    LockTaskController getLockTaskController() {
+        return mLockTaskController;
+    }
+
+    /**
+     * Return the global configuration used by the process corresponding to the input pid. This is
+     * usually the global configuration with some overrides specific to that process.
+     */
+    Configuration getGlobalConfigurationForCallingPid() {
+        final int pid = Binder.getCallingPid();
+        if (pid == MY_PID || pid < 0) {
+            return getGlobalConfiguration();
+        }
+        synchronized (mGlobalLock) {
+            final WindowProcessController app = mPidMap.get(pid);
+            return app != null ? app.getConfiguration() : getGlobalConfiguration();
+        }
+    }
+
+    /**
+     * Return the device configuration info used by the process corresponding to the input pid.
+     * The value is consistent with the global configuration for the process.
+     */
+    @Override
+    public ConfigurationInfo getDeviceConfigurationInfo() {
+        ConfigurationInfo config = new ConfigurationInfo();
+        synchronized (mGlobalLock) {
+            final Configuration globalConfig = getGlobalConfigurationForCallingPid();
+            config.reqTouchScreen = globalConfig.touchscreen;
+            config.reqKeyboardType = globalConfig.keyboard;
+            config.reqNavigation = globalConfig.navigation;
+            if (globalConfig.navigation == Configuration.NAVIGATION_DPAD
+                    || globalConfig.navigation == Configuration.NAVIGATION_TRACKBALL) {
+                config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
+            }
+            if (globalConfig.keyboard != Configuration.KEYBOARD_UNDEFINED
+                    && globalConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
+                config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
+            }
+            config.reqGlEsVersion = GL_ES_VERSION;
+        }
+        return config;
+    }
+
+    private void start() {
+        mInternal = new LocalService();
+        LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
+    }
+
+    public static final class Lifecycle extends SystemService {
+        private final ActivityTaskManagerService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+            mService = new ActivityTaskManagerService(context);
+        }
+
+        @Override
+        public void onStart() {
+            publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
+            mService.start();
+        }
+
+        public ActivityTaskManagerService getService() {
+            return mService;
+        }
+    }
+
+    @Override
+    public final int startActivity(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
+        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
+                resultWho, requestCode, startFlags, profilerInfo, bOptions,
+                UserHandle.getCallingUserId());
+    }
+
+    @Override
+    public final int startActivities(IApplicationThread caller, String callingPackage,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
+            int userId) {
+        final String reason = "startActivities";
+        enforceNotIsolatedCaller(reason);
+        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
+        // TODO: Switch to user app stacks here.
+        return getActivityStartController().startActivities(caller, -1, callingPackage, intents,
+                resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId, reason,
+                null /* originatingPendingIntent */);
+    }
+
+    @Override
+    public int startActivityAsUser(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
+                resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
+                true /*validateIncomingUser*/);
+    }
+
+    int startActivityAsUser(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
+            boolean validateIncomingUser) {
+        enforceNotIsolatedCaller("startActivityAsUser");
+
+        userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
+                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
+
+        // TODO: Switch to user app stacks here.
+        return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
+                .setCaller(caller)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setProfilerInfo(profilerInfo)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
+                .execute();
+
+    }
+
+    @Override
+    public int startActivityIntentSender(IApplicationThread caller, IIntentSender target,
+            IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) {
+        enforceNotIsolatedCaller("startActivityIntentSender");
+        // Refuse possible leaked file descriptors
+        if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+
+        if (!(target instanceof PendingIntentRecord)) {
+            throw new IllegalArgumentException("Bad PendingIntent object");
+        }
+
+        PendingIntentRecord pir = (PendingIntentRecord)target;
+
+        synchronized (mGlobalLock) {
+            // If this is coming from the currently resumed activity, it is
+            // effectively saying that app switches are allowed at this point.
+            final ActivityStack stack = getTopDisplayFocusedStack();
+            if (stack.mResumedActivity != null &&
+                    stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
+                mAppSwitchesAllowedTime = 0;
+            }
+        }
+        return pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null,
+                resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
+    }
+
+    @Override
+    public boolean startNextMatchingActivity(IBinder callingActivity, Intent intent,
+            Bundle bOptions) {
+        // Refuse possible leaked file descriptors
+        if (intent != null && intent.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+        SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
+
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(callingActivity);
+            if (r == null) {
+                SafeActivityOptions.abort(options);
+                return false;
+            }
+            if (!r.attachedToProcess()) {
+                // The caller is not running...  d'oh!
+                SafeActivityOptions.abort(options);
+                return false;
+            }
+            intent = new Intent(intent);
+            // The caller is not allowed to change the data.
+            intent.setDataAndType(r.intent.getData(), r.intent.getType());
+            // And we are resetting to find the next component...
+            intent.setComponent(null);
+
+            final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
+
+            ActivityInfo aInfo = null;
+            try {
+                List<ResolveInfo> resolves =
+                        AppGlobals.getPackageManager().queryIntentActivities(
+                                intent, r.resolvedType,
+                                PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS,
+                                UserHandle.getCallingUserId()).getList();
+
+                // Look for the original activity in the list...
+                final int N = resolves != null ? resolves.size() : 0;
+                for (int i=0; i<N; i++) {
+                    ResolveInfo rInfo = resolves.get(i);
+                    if (rInfo.activityInfo.packageName.equals(r.packageName)
+                            && rInfo.activityInfo.name.equals(r.info.name)) {
+                        // We found the current one...  the next matching is
+                        // after it.
+                        i++;
+                        if (i<N) {
+                            aInfo = resolves.get(i).activityInfo;
+                        }
+                        if (debug) {
+                            Slog.v(TAG, "Next matching activity: found current " + r.packageName
+                                    + "/" + r.info.name);
+                            Slog.v(TAG, "Next matching activity: next is " + ((aInfo == null)
+                                    ? "null" : aInfo.packageName + "/" + aInfo.name));
+                        }
+                        break;
+                    }
+                }
+            } catch (RemoteException e) {
+            }
+
+            if (aInfo == null) {
+                // Nobody who is next!
+                SafeActivityOptions.abort(options);
+                if (debug) Slog.d(TAG, "Next matching activity: nothing found");
+                return false;
+            }
+
+            intent.setComponent(new ComponentName(
+                    aInfo.applicationInfo.packageName, aInfo.name));
+            intent.setFlags(intent.getFlags()&~(
+                    Intent.FLAG_ACTIVITY_FORWARD_RESULT|
+                            Intent.FLAG_ACTIVITY_CLEAR_TOP|
+                            Intent.FLAG_ACTIVITY_MULTIPLE_TASK|
+                            FLAG_ACTIVITY_NEW_TASK));
+
+            // Okay now we need to start the new activity, replacing the currently running activity.
+            // This is a little tricky because we want to start the new one as if the current one is
+            // finished, but not finish the current one first so that there is no flicker.
+            // And thus...
+            final boolean wasFinishing = r.finishing;
+            r.finishing = true;
+
+            // Propagate reply information over to the new activity.
+            final ActivityRecord resultTo = r.resultTo;
+            final String resultWho = r.resultWho;
+            final int requestCode = r.requestCode;
+            r.resultTo = null;
+            if (resultTo != null) {
+                resultTo.removeResultsLocked(r, resultWho, requestCode);
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+            // TODO(b/64750076): Check if calling pid should really be -1.
+            final int res = getActivityStartController()
+                    .obtainStarter(intent, "startNextMatchingActivity")
+                    .setCaller(r.app.getThread())
+                    .setResolvedType(r.resolvedType)
+                    .setActivityInfo(aInfo)
+                    .setResultTo(resultTo != null ? resultTo.appToken : null)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setCallingPid(-1)
+                    .setCallingUid(r.launchedFromUid)
+                    .setCallingPackage(r.launchedFromPackage)
+                    .setRealCallingPid(-1)
+                    .setRealCallingUid(r.launchedFromUid)
+                    .setActivityOptions(options)
+                    .execute();
+            Binder.restoreCallingIdentity(origId);
+
+            r.finishing = wasFinishing;
+            if (res != ActivityManager.START_SUCCESS) {
+                return false;
+            }
+            return true;
+        }
+    }
+
+    @Override
+    public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+        final WaitResult res = new WaitResult();
+        synchronized (mGlobalLock) {
+            enforceNotIsolatedCaller("startActivityAndWait");
+            userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                    userId, "startActivityAndWait");
+            // TODO: Switch to user app stacks here.
+            getActivityStartController().obtainStarter(intent, "startActivityAndWait")
+                    .setCaller(caller)
+                    .setCallingPackage(callingPackage)
+                    .setResolvedType(resolvedType)
+                    .setResultTo(resultTo)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setStartFlags(startFlags)
+                    .setActivityOptions(bOptions)
+                    .setMayWait(userId)
+                    .setProfilerInfo(profilerInfo)
+                    .setWaitResult(res)
+                    .execute();
+        }
+        return res;
+    }
+
+    @Override
+    public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, Configuration config, Bundle bOptions, int userId) {
+        synchronized (mGlobalLock) {
+            enforceNotIsolatedCaller("startActivityWithConfig");
+            userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+                    "startActivityWithConfig");
+            // TODO: Switch to user app stacks here.
+            return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
+                    .setCaller(caller)
+                    .setCallingPackage(callingPackage)
+                    .setResolvedType(resolvedType)
+                    .setResultTo(resultTo)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setStartFlags(startFlags)
+                    .setGlobalConfiguration(config)
+                    .setActivityOptions(bOptions)
+                    .setMayWait(userId)
+                    .execute();
+        }
+    }
+
+    @Override
+    public final int startActivityAsCaller(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, boolean ignoreTargetSecurity,
+            int userId) {
+
+        // This is very dangerous -- it allows you to perform a start activity (including
+        // permission grants) as any app that may launch one of your own activities.  So
+        // we will only allow this to be done from activities that are part of the core framework,
+        // and then only when they are running as the system.
+        final ActivityRecord sourceRecord;
+        final int targetUid;
+        final String targetPackage;
+        final boolean isResolver;
+        synchronized (mGlobalLock) {
+            if (resultTo == null) {
+                throw new SecurityException("Must be called from an activity");
+            }
+            sourceRecord = mStackSupervisor.isInAnyStackLocked(resultTo);
+            if (sourceRecord == null) {
+                throw new SecurityException("Called with bad activity token: " + resultTo);
+            }
+            if (!sourceRecord.info.packageName.equals("android")) {
+                throw new SecurityException(
+                        "Must be called from an activity that is declared in the android package");
+            }
+            if (sourceRecord.app == null) {
+                throw new SecurityException("Called without a process attached to activity");
+            }
+            if (UserHandle.getAppId(sourceRecord.app.mUid) != SYSTEM_UID) {
+                // This is still okay, as long as this activity is running under the
+                // uid of the original calling activity.
+                if (sourceRecord.app.mUid != sourceRecord.launchedFromUid) {
+                    throw new SecurityException(
+                            "Calling activity in uid " + sourceRecord.app.mUid
+                                    + " must be system uid or original calling uid "
+                                    + sourceRecord.launchedFromUid);
+                }
+            }
+            if (ignoreTargetSecurity) {
+                if (intent.getComponent() == null) {
+                    throw new SecurityException(
+                            "Component must be specified with ignoreTargetSecurity");
+                }
+                if (intent.getSelector() != null) {
+                    throw new SecurityException(
+                            "Selector not allowed with ignoreTargetSecurity");
+                }
+            }
+            targetUid = sourceRecord.launchedFromUid;
+            targetPackage = sourceRecord.launchedFromPackage;
+            isResolver = sourceRecord.isResolverOrChildActivity();
+        }
+
+        if (userId == UserHandle.USER_NULL) {
+            userId = UserHandle.getUserId(sourceRecord.app.mUid);
+        }
+
+        // TODO: Switch to user app stacks here.
+        try {
+            return getActivityStartController().obtainStarter(intent, "startActivityAsCaller")
+                    .setCallingUid(targetUid)
+                    .setCallingPackage(targetPackage)
+                    .setResolvedType(resolvedType)
+                    .setResultTo(resultTo)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setStartFlags(startFlags)
+                    .setActivityOptions(bOptions)
+                    .setMayWait(userId)
+                    .setIgnoreTargetSecurity(ignoreTargetSecurity)
+                    .setFilterCallingUid(isResolver ? 0 /* system */ : targetUid)
+                    .execute();
+        } catch (SecurityException e) {
+            // XXX need to figure out how to propagate to original app.
+            // A SecurityException here is generally actually a fault of the original
+            // calling activity (such as a fairly granting permissions), so propagate it
+            // back to them.
+            /*
+            StringBuilder msg = new StringBuilder();
+            msg.append("While launching");
+            msg.append(intent.toString());
+            msg.append(": ");
+            msg.append(e.getMessage());
+            */
+            throw e;
+        }
+    }
+
+    int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
+        return mAmInternal.handleIncomingUser(callingPid, callingUid, userId, false /* allowAll */,
+                ALLOW_FULL_ONLY, name, null /* callerPackage */);
+    }
+
+    @Override
+    public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
+            Intent intent, String resolvedType, IVoiceInteractionSession session,
+            IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions, int userId) {
+        mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
+        if (session == null || interactor == null) {
+            throw new NullPointerException("null session or interactor");
+        }
+        userId = handleIncomingUser(callingPid, callingUid, userId, "startVoiceActivity");
+        // TODO: Switch to user app stacks here.
+        return getActivityStartController().obtainStarter(intent, "startVoiceActivity")
+                .setCallingUid(callingUid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setVoiceSession(session)
+                .setVoiceInteractor(interactor)
+                .setStartFlags(startFlags)
+                .setProfilerInfo(profilerInfo)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
+                .execute();
+    }
+
+    @Override
+    public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
+            Intent intent, String resolvedType, Bundle bOptions, int userId) {
+        mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
+        userId = handleIncomingUser(callingPid, callingUid, userId, "startAssistantActivity");
+
+        return getActivityStartController().obtainStarter(intent, "startAssistantActivity")
+                .setCallingUid(callingUid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
+                .execute();
+    }
+
+    @Override
+    public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
+            IRecentsAnimationRunner recentsAnimationRunner) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
+        final int callingPid = Binder.getCallingPid();
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
+                final int recentsUid = mRecentTasks.getRecentsComponentUid();
+
+                // Start a new recents animation
+                final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
+                        getActivityStartController(), mWindowManager, callingPid);
+                anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
+                        recentsUid, assistDataReceiver);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public final int startActivityFromRecents(int taskId, Bundle bOptions) {
+        enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
+                "startActivityFromRecents()");
+
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
+                        safeOptions);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    /**
+     * This is the internal entry point for handling Activity.finish().
+     *
+     * @param token The Binder token referencing the Activity we want to finish.
+     * @param resultCode Result code, if any, from this Activity.
+     * @param resultData Result data (Intent), if any, from this Activity.
+     * @param finishTask Whether to finish the task associated with this Activity.
+     *
+     * @return Returns true if the activity successfully finished, or false if it is still running.
+     */
+    @Override
+    public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
+            int finishTask) {
+        // Refuse possible leaked file descriptors
+        if (resultData != null && resultData.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return true;
+            }
+            // Keep track of the root activity of the task before we finish it
+            TaskRecord tr = r.getTask();
+            ActivityRecord rootR = tr.getRootActivity();
+            if (rootR == null) {
+                Slog.w(TAG, "Finishing task with all activities already finished");
+            }
+            // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can
+            // finish.
+            if (getLockTaskController().activityBlockedFromFinish(r)) {
+                return false;
+            }
+
+            // TODO: There is a dup. of this block of code in ActivityStack.navigateUpToLocked
+            // We should consolidate.
+            if (mController != null) {
+                // Find the first activity that is not finishing.
+                ActivityRecord next = r.getStack().topRunningActivityLocked(token, 0);
+                if (next != null) {
+                    // ask watcher if this is allowed
+                    boolean resumeOK = true;
+                    try {
+                        resumeOK = mController.activityResuming(next.packageName);
+                    } catch (RemoteException e) {
+                        mController = null;
+                        Watchdog.getInstance().setActivityController(null);
+                    }
+
+                    if (!resumeOK) {
+                        Slog.i(TAG, "Not finishing activity because controller resumed");
+                        return false;
+                    }
+                }
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                boolean res;
+                final boolean finishWithRootActivity =
+                        finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
+                if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
+                        || (finishWithRootActivity && r == rootR)) {
+                    // If requested, remove the task that is associated to this activity only if it
+                    // was the root activity in the task. The result code and data is ignored
+                    // because we don't support returning them across task boundaries. Also, to
+                    // keep backwards compatibility we remove the task from recents when finishing
+                    // task with root activity.
+                    res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
+                            finishWithRootActivity, "finish-activity");
+                    if (!res) {
+                        Slog.i(TAG, "Removing task failed to finish activity");
+                    }
+                    // Explicitly dismissing the activity so reset its relaunch flag.
+                    r.mRelaunchReason = RELAUNCH_REASON_NONE;
+                } else {
+                    res = tr.getStack().requestFinishActivityLocked(token, resultCode,
+                            resultData, "app-request", true);
+                    if (!res) {
+                        Slog.i(TAG, "Failed to finish by app-request");
+                    }
+                }
+                return res;
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public boolean finishActivityAffinity(IBinder token) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+
+                // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
+                // can finish.
+                final TaskRecord task = r.getTask();
+                if (getLockTaskController().activityBlockedFromFinish(r)) {
+                    return false;
+                }
+                return task.getStack().finishActivityAffinityLocked(r);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            WindowProcessController proc = null;
+            synchronized (mGlobalLock) {
+                ActivityStack stack = ActivityRecord.getStackLocked(token);
+                if (stack == null) {
+                    return;
+                }
+                final ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token,
+                        false /* fromTimeout */, false /* processPausingActivities */, config);
+                if (r != null) {
+                    proc = r.app;
+                }
+                if (stopProfiling && proc != null) {
+                    proc.clearProfilerIfNeeded();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public final void activityResumed(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        synchronized (mGlobalLock) {
+            ActivityRecord.activityResumedLocked(token);
+            mWindowManager.notifyAppResumedFinished(token);
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public final void activityPaused(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        synchronized (mGlobalLock) {
+            ActivityStack stack = ActivityRecord.getStackLocked(token);
+            if (stack != null) {
+                stack.activityPausedLocked(token, false);
+            }
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public final void activityStopped(IBinder token, Bundle icicle,
+            PersistableBundle persistentState, CharSequence description) {
+        if (DEBUG_ALL) Slog.v(TAG, "Activity stopped: token=" + token);
+
+        // Refuse possible leaked file descriptors
+        if (icicle != null && icicle.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Bundle");
+        }
+
+        final long origId = Binder.clearCallingIdentity();
+
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.activityStoppedLocked(icicle, persistentState, description);
+            }
+        }
+
+        mAmInternal.trimApplications();
+
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public final void activityDestroyed(IBinder token) {
+        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
+        synchronized (mGlobalLock) {
+            ActivityStack stack = ActivityRecord.getStackLocked(token);
+            if (stack != null) {
+                stack.activityDestroyedLocked(token, "activityDestroyed");
+            }
+        }
+    }
+
+    @Override
+    public final void activityRelaunched(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        synchronized (mGlobalLock) {
+            mStackSupervisor.activityRelaunchedLocked(token);
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    public final void activitySlept(IBinder token) {
+        if (DEBUG_ALL) Slog.v(TAG, "Activity slept: token=" + token);
+
+        final long origId = Binder.clearCallingIdentity();
+
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                mStackSupervisor.activitySleptLocked(r);
+            }
+        }
+
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public void setRequestedOrientation(IBinder token, int requestedOrientation) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setRequestedOrientation(requestedOrientation);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public int getRequestedOrientation(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+            }
+            return r.getRequestedOrientation();
+        }
+    }
+
+    @Override
+    public void setImmersive(IBinder token, boolean immersive) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                throw new IllegalArgumentException();
+            }
+            r.immersive = immersive;
+
+            // update associated state if we're frontmost
+            if (r.isResumedActivityOnDisplay()) {
+                if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
+                applyUpdateLockStateLocked(r);
+            }
+        }
+    }
+
+    void applyUpdateLockStateLocked(ActivityRecord r) {
+        // Modifications to the UpdateLock state are done on our handler, outside
+        // the activity manager's locks.  The new state is determined based on the
+        // state *now* of the relevant activity record.  The object is passed to
+        // the handler solely for logging detail, not to be consulted/modified.
+        final boolean nextState = r != null && r.immersive;
+        mH.post(() -> {
+            if (mUpdateLock.isHeld() != nextState) {
+                if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE,
+                        "Applying new update lock state '" + nextState + "' for " + r);
+                if (nextState) {
+                    mUpdateLock.acquire();
+                } else {
+                    mUpdateLock.release();
+                }
+            }
+        });
+    }
+
+    @Override
+    public boolean isImmersive(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                throw new IllegalArgumentException();
+            }
+            return r.immersive;
+        }
+    }
+
+    @Override
+    public boolean isTopActivityImmersive() {
+        enforceNotIsolatedCaller("isTopActivityImmersive");
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
+            return (r != null) ? r.immersive : false;
+        }
+    }
+
+    @Override
+    public void overridePendingTransition(IBinder token, String packageName,
+            int enterAnim, int exitAnim) {
+        synchronized (mGlobalLock) {
+            ActivityRecord self = ActivityRecord.isInStackLocked(token);
+            if (self == null) {
+                return;
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+
+            if (self.isState(
+                    ActivityStack.ActivityState.RESUMED, ActivityStack.ActivityState.PAUSING)) {
+                self.getDisplay().getWindowContainerController().overridePendingAppTransition(
+                        packageName, enterAnim, exitAnim, null);
+            }
+
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public int getFrontActivityScreenCompatMode() {
+        enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
+            if (r == null) {
+                return ActivityManager.COMPAT_MODE_UNKNOWN;
+            }
+            return mCompatModePackages.computeCompatModeLocked(r.info.applicationInfo);
+        }
+    }
+
+    @Override
+    public void setFrontActivityScreenCompatMode(int mode) {
+        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+                "setFrontActivityScreenCompatMode");
+        ApplicationInfo ai;
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
+            if (r == null) {
+                Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
+                return;
+            }
+            ai = r.info.applicationInfo;
+            mCompatModePackages.setPackageScreenCompatModeLocked(ai, mode);
+        }
+    }
+
+    @Override
+    public int getLaunchedFromUid(IBinder activityToken) {
+        ActivityRecord srec;
+        synchronized (mGlobalLock) {
+            srec = ActivityRecord.forTokenLocked(activityToken);
+        }
+        if (srec == null) {
+            return -1;
+        }
+        return srec.launchedFromUid;
+    }
+
+    @Override
+    public String getLaunchedFromPackage(IBinder activityToken) {
+        ActivityRecord srec;
+        synchronized (mGlobalLock) {
+            srec = ActivityRecord.forTokenLocked(activityToken);
+        }
+        if (srec == null) {
+            return null;
+        }
+        return srec.launchedFromPackage;
+    }
+
+    @Override
+    public boolean convertFromTranslucent(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+                final boolean translucentChanged = r.changeWindowTranslucency(true);
+                if (translucentChanged) {
+                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                }
+                mWindowManager.setAppFullscreen(token, true);
+                return translucentChanged;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public boolean convertToTranslucent(IBinder token, Bundle options) {
+        SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+                final TaskRecord task = r.getTask();
+                int index = task.mActivities.lastIndexOf(r);
+                if (index > 0) {
+                    ActivityRecord under = task.mActivities.get(index - 1);
+                    under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
+                }
+                final boolean translucentChanged = r.changeWindowTranslucency(false);
+                if (translucentChanged) {
+                    r.getStack().convertActivityToTranslucent(r);
+                }
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mWindowManager.setAppFullscreen(token, false);
+                return translucentChanged;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void notifyActivityDrawn(IBinder token) {
+        if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
+        synchronized (mGlobalLock) {
+            ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token);
+            if (r != null) {
+                r.getStack().notifyActivityDrawnLocked(r);
+            }
+        }
+    }
+
+    @Override
+    public void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            r.reportFullyDrawnLocked(restoredFromBundle);
+        }
+    }
+
+    @Override
+    public int getActivityDisplayId(IBinder activityToken) throws RemoteException {
+        synchronized (mGlobalLock) {
+            final ActivityStack stack = ActivityRecord.getStackLocked(activityToken);
+            if (stack != null && stack.mDisplayId != INVALID_DISPLAY) {
+                return stack.mDisplayId;
+            }
+            return DEFAULT_DISPLAY;
+        }
+    }
+
+    @Override
+    public ActivityManager.StackInfo getFocusedStackInfo() throws RemoteException {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                ActivityStack focusedStack = getTopDisplayFocusedStack();
+                if (focusedStack != null) {
+                    return mStackSupervisor.getStackInfo(focusedStack.mStackId);
+                }
+                return null;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void setFocusedStack(int stackId) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
+        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                if (stack == null) {
+                    Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
+                    return;
+                }
+                final ActivityRecord r = stack.topRunningActivityLocked();
+                if (r != null && r.moveFocusableActivityToTop("setFocusedStack")) {
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public void setFocusedTask(int taskId) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
+        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_ONLY);
+                if (task == null) {
+                    return;
+                }
+                final ActivityRecord r = task.topRunningActivityLocked();
+                if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) {
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public boolean removeTask(int taskId) {
+        enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS,
+                        "remove-task");
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void removeAllVisibleRecentTasks() {
+        enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                getRecentTasks().removeAllVisibleTasks();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public boolean shouldUpRecreateTask(IBinder token, String destAffinity) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord srec = ActivityRecord.forTokenLocked(token);
+            if (srec != null) {
+                return srec.getStack().shouldUpRecreateTaskLocked(srec, destAffinity);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
+            Intent resultData) {
+
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r != null) {
+                return r.getStack().navigateUpToLocked(r, destIntent, resultCode, resultData);
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Attempts to move a task backwards in z-order (the order of activities within the task is
+     * unchanged).
+     *
+     * There are several possible results of this call:
+     * - if the task is locked, then we will show the lock toast
+     * - if there is a task behind the provided task, then that task is made visible and resumed as
+     *   this task is moved to the back
+     * - otherwise, if there are no other tasks in the stack:
+     *     - if this task is in the pinned stack, then we remove the stack completely, which will
+     *       have the effect of moving the task to the top or bottom of the fullscreen stack
+     *       (depending on whether it is visible)
+     *     - otherwise, we simply return home and hide this task
+     *
+     * @param token A reference to the activity we wish to move
+     * @param nonRoot If false then this only works if the activity is the root
+     *                of a task; if true it will work for any activity in a task.
+     * @return Returns true if the move completed, false if not.
+     */
+    @Override
+    public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
+        enforceNotIsolatedCaller("moveActivityTaskToBack");
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task != null) {
+                    return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public Rect getTaskBounds(int taskId) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()");
+        long ident = Binder.clearCallingIdentity();
+        Rect rect = new Rect();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+                if (task == null) {
+                    Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
+                    return rect;
+                }
+                if (task.getStack() != null) {
+                    // Return the bounds from window manager since it will be adjusted for various
+                    // things like the presense of a docked stack for tasks that aren't resizeable.
+                    task.getWindowContainerBounds(rect);
+                } else {
+                    // Task isn't in window manager yet since it isn't associated with a stack.
+                    // Return the persist value from activity manager
+                    if (!task.matchParentBounds()) {
+                        rect.set(task.getBounds());
+                    } else if (task.mLastNonFullscreenBounds != null) {
+                        rect.set(task.mLastNonFullscreenBounds);
+                    }
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return rect;
+    }
+
+    @Override
+    public ActivityManager.TaskDescription getTaskDescription(int id) {
+        synchronized (mGlobalLock) {
+            enforceCallerIsRecentsOrHasPermission(
+                    MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
+            final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
+                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+            if (tr != null) {
+                return tr.lastTaskDescription;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
+        if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
+                    toTop, ANIMATE, null /* initialBounds */, true /* showRecents */);
+            return;
+        }
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_ONLY);
+                if (task == null) {
+                    Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
+                    return;
+                }
+
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId
+                        + " to windowingMode=" + windowingMode + " toTop=" + toTop);
+
+                if (!task.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+                            + " non-standard task " + taskId + " to windowing mode="
+                            + windowingMode);
+                }
+
+                final ActivityStack stack = task.getStack();
+                if (toTop) {
+                    stack.moveToFront("setTaskWindowingMode", task);
+                }
+                stack.setWindowingMode(windowingMode);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public String getCallingPackage(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = getCallingRecordLocked(token);
+            return r != null ? r.info.packageName : null;
+        }
+    }
+
+    @Override
+    public ComponentName getCallingActivity(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = getCallingRecordLocked(token);
+            return r != null ? r.intent.getComponent() : null;
+        }
+    }
+
+    private ActivityRecord getCallingRecordLocked(IBinder token) {
+        ActivityRecord r = ActivityRecord.isInStackLocked(token);
+        if (r == null) {
+            return null;
+        }
+        return r.resultTo;
+    }
+
+    @Override
+    public void unhandledBack() {
+        mAmInternal.enforceCallingPermission(android.Manifest.permission.FORCE_BACK, "unhandledBack()");
+
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                getTopDisplayFocusedStack().unhandledBackLocked();
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    /**
+     * TODO: Add mController hook
+     */
+    @Override
+    public void moveTaskToFront(int taskId, int flags, Bundle bOptions) {
+        mAmInternal.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()");
+
+        if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
+        synchronized (mGlobalLock) {
+            moveTaskToFrontLocked(taskId, flags, SafeActivityOptions.fromBundle(bOptions),
+                    false /* fromRecents */);
+        }
+    }
+
+    void moveTaskToFrontLocked(int taskId, int flags, SafeActivityOptions options,
+            boolean fromRecents) {
+
+        if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+                Binder.getCallingUid(), -1, -1, "Task to front")) {
+            SafeActivityOptions.abort(options);
+            return;
+        }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+            if (task == null) {
+                Slog.d(TAG, "Could not find task for id: "+ taskId);
+                SafeActivityOptions.abort(options);
+                return;
+            }
+            if (getLockTaskController().isLockTaskModeViolation(task)) {
+                Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
+                SafeActivityOptions.abort(options);
+                return;
+            }
+            ActivityOptions realOptions = options != null
+                    ? options.getOptions(mStackSupervisor)
+                    : null;
+            mStackSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
+                    false /* forceNonResizable */);
+
+            final ActivityRecord topActivity = task.getTopActivity();
+            if (topActivity != null) {
+
+                // We are reshowing a task, use a starting window to hide the initial draw delay
+                // so the transition can start earlier.
+                topActivity.showStartingWindow(null /* prev */, false /* newTask */,
+                        true /* taskSwitch */, fromRecents);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
+            int callingPid, int callingUid, String name) {
+        if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
+            return true;
+        }
+
+        if (getRecentTasks().isCallerRecents(sourceUid)) {
+            return true;
+        }
+
+        int perm = checkComponentPermission(STOP_APP_SWITCHES, sourcePid, sourceUid, -1, true);
+        if (perm == PackageManager.PERMISSION_GRANTED) {
+            return true;
+        }
+        if (checkAllowAppSwitchUid(sourceUid)) {
+            return true;
+        }
+
+        // If the actual IPC caller is different from the logical source, then
+        // also see if they are allowed to control app switches.
+        if (callingUid != -1 && callingUid != sourceUid) {
+            perm = checkComponentPermission(STOP_APP_SWITCHES, callingPid, callingUid, -1, true);
+            if (perm == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            }
+            if (checkAllowAppSwitchUid(callingUid)) {
+                return true;
+            }
+        }
+
+        Slog.w(TAG, name + " request from " + sourceUid + " stopped");
+        return false;
+    }
+
+    private boolean checkAllowAppSwitchUid(int uid) {
+        ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(UserHandle.getUserId(uid));
+        if (types != null) {
+            for (int i = types.size() - 1; i >= 0; i--) {
+                if (types.valueAt(i).intValue() == uid) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void setActivityController(IActivityController controller, boolean imAMonkey) {
+        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+                "setActivityController()");
+        synchronized (mGlobalLock) {
+            mController = controller;
+            mControllerIsAMonkey = imAMonkey;
+            Watchdog.getInstance().setActivityController(controller);
+        }
+    }
+
+    public boolean isControllerAMonkey() {
+        synchronized (mGlobalLock) {
+            return mController != null && mControllerIsAMonkey;
+        }
+    }
+
+    @Override
+    public int getTaskForActivity(IBinder token, boolean onlyRoot) {
+        synchronized (mGlobalLock) {
+            return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
+        }
+    }
+
+    @Override
+    public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
+        return getFilteredTasks(maxNum, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED);
+    }
+
+    @Override
+    public List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum,
+            @WindowConfiguration.ActivityType int ignoreActivityType,
+            @WindowConfiguration.WindowingMode int ignoreWindowingMode) {
+        final int callingUid = Binder.getCallingUid();
+        ArrayList<ActivityManager.RunningTaskInfo> list = new ArrayList<>();
+
+        synchronized (mGlobalLock) {
+            if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum);
+
+            final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
+                    callingUid);
+            mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
+                    ignoreWindowingMode, callingUid, allowed);
+        }
+
+        return list;
+    }
+
+    @Override
+    public final void finishSubActivity(IBinder token, String resultWho, int requestCode) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.getStack().finishSubActivityLocked(r, resultWho, requestCode);
+            }
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public boolean willActivityBeVisible(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityStack stack = ActivityRecord.getStackLocked(token);
+            if (stack != null) {
+                return stack.willActivityBeVisibleLocked(token);
+            }
+            return false;
+        }
+    }
+
+    @Override
+    public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
+                    return;
+                }
+
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
+                        + " to stackId=" + stackId + " toTop=" + toTop);
+
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                if (stack == null) {
+                    throw new IllegalStateException(
+                            "moveTaskToStack: No stack for stackId=" + stackId);
+                }
+                if (!stack.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
+                            + taskId + " to stack " + stackId);
+                }
+                if (stack.inSplitScreenPrimaryWindowingMode()) {
+                    mWindowManager.setDockedStackCreateState(
+                            SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
+                }
+                task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
+                        "moveTaskToStack");
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode,
+            boolean preserveWindows, boolean animate, int animationDuration) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                if (animate) {
+                    final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
+                    if (stack == null) {
+                        Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
+                        return;
+                    }
+                    if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
+                        throw new IllegalArgumentException("Stack: " + stackId
+                                + " doesn't support animated resize.");
+                    }
+                    stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
+                            animationDuration, false /* fromFullscreen */);
+                } else {
+                    final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                    if (stack == null) {
+                        Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
+                        return;
+                    }
+                    mStackSupervisor.resizeStackLocked(stack, destBounds,
+                            null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+                            preserveWindows, allowResizeInDockedMode, !DEFER_RESUME);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Moves the specified task to the primary-split-screen stack.
+     *
+     * @param taskId Id of task to move.
+     * @param createMode The mode the primary split screen stack should be created in if it doesn't
+     *                   exist already. See
+     *                   {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
+     *                   and
+     *                   {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
+     * @param toTop If the task and stack should be moved to the top.
+     * @param animate Whether we should play an animation for the moving the task.
+     * @param initialBounds If the primary stack gets created, it will use these bounds for the
+     *                      stack. Pass {@code null} to use default bounds.
+     * @param showRecents If the recents activity should be shown on the other side of the task
+     *                    going into split-screen mode.
+     */
+    @Override
+    public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
+            boolean toTop, boolean animate, Rect initialBounds, boolean showRecents) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "setTaskWindowingModeSplitScreenPrimary()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_ONLY);
+                if (task == null) {
+                    Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
+                    return false;
+                }
+                if (DEBUG_STACK) Slog.d(TAG_STACK,
+                        "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
+                                + " to createMode=" + createMode + " toTop=" + toTop);
+                if (!task.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+                            + " non-standard task " + taskId + " to split-screen windowing mode");
+                }
+
+                mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+                final int windowingMode = task.getWindowingMode();
+                final ActivityStack stack = task.getStack();
+                if (toTop) {
+                    stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
+                }
+                stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
+                        false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */);
+                return windowingMode != task.getWindowingMode();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    /**
+     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+     */
+    @Override
+    public void removeStacksInWindowingModes(int[] windowingModes) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "removeStacksInWindowingModes()");
+
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mStackSupervisor.removeStacksInWindowingModes(windowingModes);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void removeStacksWithActivityTypes(int[] activityTypes) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "removeStacksWithActivityTypes()");
+
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mStackSupervisor.removeStacksWithActivityTypes(activityTypes);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
+            int userId) {
+        final int callingUid = Binder.getCallingUid();
+        userId = handleIncomingUser(Binder.getCallingPid(), callingUid, userId, "getRecentTasks");
+        final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
+                callingUid);
+        final boolean detailed = checkGetTasksPermission(
+                android.Manifest.permission.GET_DETAILED_TASKS, Binder.getCallingPid(),
+                UserHandle.getAppId(callingUid))
+                == PackageManager.PERMISSION_GRANTED;
+
+        synchronized (mGlobalLock) {
+            return mRecentTasks.getRecentTasks(maxNum, flags, allowed, detailed, userId,
+                    callingUid);
+        }
+    }
+
+    @Override
+    public List<ActivityManager.StackInfo> getAllStackInfos() {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.getAllStackInfosLocked();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.getStackInfo(windowingMode, activityType);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
+        final long callingUid = Binder.getCallingUid();
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                // Cancel the recents animation synchronously (do not hold the WM lock)
+                mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
+                        ? REORDER_MOVE_TO_ORIGINAL_POSITION
+                        : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void startLockTaskModeByToken(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r == null) {
+                return;
+            }
+            startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */);
+        }
+    }
+
+    @Override
+    public void startSystemLockTaskMode(int taskId) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
+        // This makes inner call to look as if it was initiated by system.
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_ONLY);
+                if (task == null) {
+                    return;
+                }
+
+                // When starting lock task mode the stack must be in front and focused
+                task.getStack().moveToFront("startSystemLockTaskMode");
+                startLockTaskModeLocked(task, true /* isSystemCaller */);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void stopLockTaskModeByToken(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r == null) {
+                return;
+            }
+            stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */);
+        }
+    }
+
+    /**
+     * This API should be called by SystemUI only when user perform certain action to dismiss
+     * lock task mode. We should only dismiss pinned lock task mode in this case.
+     */
+    @Override
+    public void stopSystemLockTaskMode() throws RemoteException {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
+        stopLockTaskModeInternal(null, true /* isSystemCaller */);
+    }
+
+    private void startLockTaskModeLocked(@Nullable TaskRecord task, boolean isSystemCaller) {
+        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task);
+        if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+            return;
+        }
+
+        final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
+        if (stack == null || task != stack.topTask()) {
+            throw new IllegalArgumentException("Invalid task, not in foreground");
+        }
+
+        // {@code isSystemCaller} is used to distinguish whether this request is initiated by the
+        // system or a specific app.
+        // * System-initiated requests will only start the pinned mode (screen pinning)
+        // * App-initiated requests
+        //   - will put the device in fully locked mode (LockTask), if the app is whitelisted
+        //   - will start the pinned mode, otherwise
+        final int callingUid = Binder.getCallingUid();
+        long ident = Binder.clearCallingIdentity();
+        try {
+            // When a task is locked, dismiss the pinned stack if it exists
+            mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+
+            getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void stopLockTaskModeInternal(@Nullable TaskRecord task, boolean isSystemCaller) {
+        final int callingUid = Binder.getCallingUid();
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                getLockTaskController().stopLockTaskMode(task, isSystemCaller, callingUid);
+            }
+            // Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
+            // task and jumping straight into a call in the case of emergency call back.
+            TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+            if (tm != null) {
+                tm.showInCallScreen(false);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void updateLockTaskPackages(int userId, String[] packages) {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != 0 && callingUid != SYSTEM_UID) {
+            mAmInternal.enforceCallingPermission(Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
+                    "updateLockTaskPackages()");
+        }
+        synchronized (this) {
+            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":"
+                    + Arrays.toString(packages));
+            getLockTaskController().updateLockTaskPackages(userId, packages);
+        }
+    }
+
+    @Override
+    public boolean isInLockTaskMode() {
+        return getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+    }
+
+    @Override
+    public int getLockTaskModeState() {
+        synchronized (mGlobalLock) {
+            return getLockTaskController().getLockTaskModeState();
+        }
+    }
+
+    @Override
+    public void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.setTaskDescription(td);
+                final TaskRecord task = r.getTask();
+                task.updateTaskDescription();
+                mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td);
+            }
+        }
+    }
+
+    @Override
+    public Bundle getActivityOptions(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r != null) {
+                    final ActivityOptions activityOptions = r.takeOptionsLocked();
+                    return activityOptions == null ? null : activityOptions.toBundle();
+                }
+                return null;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public List<IBinder> getAppTasks(String callingPackage) {
+        int callingUid = Binder.getCallingUid();
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mRecentTasks.getAppTasksList(callingUid, callingPackage);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void finishVoiceTask(IVoiceInteractionSession session) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                // TODO: VI Consider treating local voice interactions and voice tasks
+                // differently here
+                mStackSupervisor.finishVoiceTask(session);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+
+    }
+
+    @Override
+    public boolean isTopOfTask(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            return r != null && r.getTask().getTopActivity() == r;
+        }
+    }
+
+    @Override
+    public void notifyLaunchTaskBehindComplete(IBinder token) {
+        mStackSupervisor.scheduleLaunchTaskBehindComplete(token);
+    }
+
+    @Override
+    public void notifyEnterAnimationComplete(IBinder token) {
+        mH.post(() -> {
+            synchronized (mGlobalLock) {
+                ActivityRecord r = ActivityRecord.forTokenLocked(token);
+                if (r != null && r.attachedToProcess()) {
+                    try {
+                        r.app.getThread().scheduleEnterAnimationComplete(r.appToken);
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+
+        });
+    }
+
+    /** Called from an app when assist data is ready. */
+    @Override
+    public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
+            AssistContent content, Uri referrer) {
+        PendingAssistExtras pae = (PendingAssistExtras) token;
+        synchronized (pae) {
+            pae.result = extras;
+            pae.structure = structure;
+            pae.content = content;
+            if (referrer != null) {
+                pae.extras.putParcelable(Intent.EXTRA_REFERRER, referrer);
+            }
+            if (structure != null) {
+                structure.setHomeActivity(pae.isHome);
+            }
+            pae.haveResult = true;
+            pae.notifyAll();
+            if (pae.intent == null && pae.receiver == null) {
+                // Caller is just waiting for the result.
+                return;
+            }
+        }
+        // We are now ready to launch the assist activity.
+        IAssistDataReceiver sendReceiver = null;
+        Bundle sendBundle = null;
+        synchronized (mGlobalLock) {
+            buildAssistBundleLocked(pae, extras);
+            boolean exists = mPendingAssistExtras.remove(pae);
+            mUiHandler.removeCallbacks(pae);
+            if (!exists) {
+                // Timed out.
+                return;
+            }
+
+            if ((sendReceiver = pae.receiver) != null) {
+                // Caller wants result sent back to them.
+                sendBundle = new Bundle();
+                sendBundle.putBundle(ASSIST_KEY_DATA, pae.extras);
+                sendBundle.putParcelable(ASSIST_KEY_STRUCTURE, pae.structure);
+                sendBundle.putParcelable(ASSIST_KEY_CONTENT, pae.content);
+                sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
+            }
+        }
+        if (sendReceiver != null) {
+            try {
+                sendReceiver.onHandleAssistData(sendBundle);
+            } catch (RemoteException e) {
+            }
+            return;
+        }
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (TextUtils.equals(pae.intent.getAction(),
+                    android.service.voice.VoiceInteractionService.SERVICE_INTERFACE)) {
+                pae.intent.putExtras(pae.extras);
+                mContext.startServiceAsUser(pae.intent, new UserHandle(pae.userHandle));
+            } else {
+                pae.intent.replaceExtras(pae.extras);
+                pae.intent.setFlags(FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_SINGLE_TOP
+                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                mInternal.closeSystemDialogs("assist");
+
+                try {
+                    mContext.startActivityAsUser(pae.intent, new UserHandle(pae.userHandle));
+                } catch (ActivityNotFoundException e) {
+                    Slog.w(TAG, "No activity to handle assist action.", e);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public int addAppTask(IBinder activityToken, Intent intent,
+            ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException {
+        final int callingUid = Binder.getCallingUid();
+        final long callingIdent = Binder.clearCallingIdentity();
+
+        try {
+            synchronized (mGlobalLock) {
+                ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
+                if (r == null) {
+                    throw new IllegalArgumentException("Activity does not exist; token="
+                            + activityToken);
+                }
+                ComponentName comp = intent.getComponent();
+                if (comp == null) {
+                    throw new IllegalArgumentException("Intent " + intent
+                            + " must specify explicit component");
+                }
+                if (thumbnail.getWidth() != mThumbnailWidth
+                        || thumbnail.getHeight() != mThumbnailHeight) {
+                    throw new IllegalArgumentException("Bad thumbnail size: got "
+                            + thumbnail.getWidth() + "x" + thumbnail.getHeight() + ", require "
+                            + mThumbnailWidth + "x" + mThumbnailHeight);
+                }
+                if (intent.getSelector() != null) {
+                    intent.setSelector(null);
+                }
+                if (intent.getSourceBounds() != null) {
+                    intent.setSourceBounds(null);
+                }
+                if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0) {
+                    if ((intent.getFlags()&Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS) == 0) {
+                        // The caller has added this as an auto-remove task...  that makes no
+                        // sense, so turn off auto-remove.
+                        intent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
+                    }
+                }
+                final ActivityInfo ainfo = AppGlobals.getPackageManager().getActivityInfo(comp,
+                        STOCK_PM_FLAGS, UserHandle.getUserId(callingUid));
+                if (ainfo.applicationInfo.uid != callingUid) {
+                    throw new SecurityException(
+                            "Can't add task for another application: target uid="
+                                    + ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
+                }
+
+                final ActivityStack stack = r.getStack();
+                final TaskRecord task = stack.createTaskRecord(
+                        mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent,
+                        null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
+                if (!mRecentTasks.addToBottom(task)) {
+                    // The app has too many tasks already and we can't add any more
+                    stack.removeTask(task, "addAppTask", REMOVE_TASK_MODE_DESTROYING);
+                    return INVALID_TASK_ID;
+                }
+                task.lastTaskDescription.copyFrom(description);
+
+                // TODO: Send the thumbnail to WM to store it.
+
+                return task.taskId;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingIdent);
+        }
+    }
+
+    @Override
+    public Point getAppTaskThumbnailSize() {
+        synchronized (mGlobalLock) {
+            return new Point(mThumbnailWidth, mThumbnailHeight);
+        }
+    }
+
+    @Override
+    public void setTaskResizeable(int taskId, int resizeableMode) {
+        synchronized (mGlobalLock) {
+            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+                    taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+            if (task == null) {
+                Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
+                return;
+            }
+            task.setResizeMode(resizeableMode);
+        }
+    }
+
+    @Override
+    public void resizeTask(int taskId, Rect bounds, int resizeMode) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_ONLY);
+                if (task == null) {
+                    Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
+                    return;
+                }
+                // Place the task in the right stack if it isn't there already based on
+                // the requested bounds.
+                // The stack transition logic is:
+                // - a null bounds on a freeform task moves that task to fullscreen
+                // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
+                //   that task to freeform
+                // - otherwise the task is not moved
+                ActivityStack stack = task.getStack();
+                if (!task.getWindowConfiguration().canResizeTask()) {
+                    throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
+                }
+                if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+                    stack = stack.getDisplay().getOrCreateStack(
+                            WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
+                } else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+                    stack = stack.getDisplay().getOrCreateStack(
+                            WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP);
+                }
+
+                // Reparent the task to the right stack if necessary
+                boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
+                if (stack != task.getStack()) {
+                    // Defer resume until the task is resized below
+                    task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
+                            DEFER_RESUME, "resizeTask");
+                    preserveWindow = false;
+                }
+
+                // After reparenting (which only resizes the task to the stack bounds), resize the
+                // task to the actual bounds provided
+                task.resize(bounds, resizeMode, preserveWindow, !DEFER_RESUME);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public boolean releaseActivityInstance(IBinder token) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+                return r.getStack().safelyDestroyActivityLocked(r, "app-req");
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void releaseSomeActivities(IApplicationThread appInt) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                final WindowProcessController app = getProcessController(appInt);
+                mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
+            int[] secondaryDisplaysShowing) {
+        if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires permission "
+                    + android.Manifest.permission.DEVICE_POWER);
+        }
+
+        synchronized (mGlobalLock) {
+            long ident = Binder.clearCallingIdentity();
+            if (mKeyguardShown != keyguardShowing) {
+                mKeyguardShown = keyguardShowing;
+                final Message msg = PooledLambda.obtainMessage(
+                        ActivityManagerInternal::reportCurKeyguardUsageEvent, mAmInternal,
+                        keyguardShowing);
+                mH.sendMessage(msg);
+            }
+            try {
+                mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
+                        secondaryDisplaysShowing);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        mH.post(() -> {
+            for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+                mScreenObservers.get(i).onKeyguardStateChanged(keyguardShowing);
+            }
+        });
+    }
+
+    public void onScreenAwakeChanged(boolean isAwake) {
+        mH.post(() -> {
+            for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+                mScreenObservers.get(i).onAwakeStateChanged(isAwake);
+            }
+        });
+    }
+
+    @Override
+    public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
+        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, "getTaskDescriptionIcon");
+
+        final File passedIconFile = new File(filePath);
+        final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
+                passedIconFile.getName());
+        if (!legitIconFile.getPath().equals(filePath)
+                || !filePath.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
+            throw new IllegalArgumentException("Bad file path: " + filePath
+                    + " passed for userId " + userId);
+        }
+        return mRecentTasks.getTaskDescriptionIcon(filePath);
+    }
+
+    @Override
+    public void startInPlaceAnimationOnFrontMostApplication(Bundle opts) {
+        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(opts);
+        final ActivityOptions activityOptions = safeOptions != null
+                ? safeOptions.getOptions(mStackSupervisor)
+                : null;
+        if (activityOptions == null
+                || activityOptions.getAnimationType() != ActivityOptions.ANIM_CUSTOM_IN_PLACE
+                || activityOptions.getCustomInPlaceResId() == 0) {
+            throw new IllegalArgumentException("Expected in-place ActivityOption " +
+                    "with valid animation");
+        }
+        // Get top display of front most application.
+        final ActivityStack focusedStack = getTopDisplayFocusedStack();
+        if (focusedStack != null) {
+            final DisplayWindowController dwc =
+                    focusedStack.getDisplay().getWindowContainerController();
+            dwc.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
+            dwc.overridePendingAppTransitionInPlace(activityOptions.getPackageName(),
+                    activityOptions.getCustomInPlaceResId());
+            dwc.executeAppTransition();
+        }
+    }
+
+    @Override
+    public void removeStack(int stackId) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                if (stack == null) {
+                    Slog.w(TAG, "removeStack: No stack with id=" + stackId);
+                    return;
+                }
+                if (!stack.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException(
+                            "Removing non-standard stack is not allowed.");
+                }
+                mStackSupervisor.removeStack(stack);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void moveStackToDisplay(int stackId, int displayId) {
+        mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
+
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveStackToDisplay: moving stackId=" + stackId
+                        + " to displayId=" + displayId);
+                mStackSupervisor.moveStackToDisplayLocked(stackId, displayId, ON_TOP);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void exitFreeformMode(IBinder token) {
+        synchronized (mGlobalLock) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+                if (r == null) {
+                    throw new IllegalArgumentException(
+                            "exitFreeformMode: No activity record matching token=" + token);
+                }
+
+                final ActivityStack stack = r.getStack();
+                if (stack == null || !stack.inFreeformWindowingMode()) {
+                    throw new IllegalStateException(
+                            "exitFreeformMode: You can only go fullscreen from freeform.");
+                }
+
+                stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    /** Sets the task stack listener that gets callbacks when a task stack changes. */
+    @Override
+    public void registerTaskStackListener(ITaskStackListener listener) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "registerTaskStackListener()");
+        mTaskChangeNotificationController.registerTaskStackListener(listener);
+    }
+
+    /** Unregister a task stack listener so that it stops receiving callbacks. */
+    @Override
+    public void unregisterTaskStackListener(ITaskStackListener listener) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "unregisterTaskStackListener()");
+        mTaskChangeNotificationController.unregisterTaskStackListener(listener);
+    }
+
+    @Override
+    public boolean requestAssistContextExtras(int requestType, IAssistDataReceiver receiver,
+            Bundle receiverExtras, IBinder activityToken, boolean focused, boolean newSessionId) {
+        return enqueueAssistContext(requestType, null, null, receiver, receiverExtras,
+                activityToken, focused, newSessionId, UserHandle.getCallingUserId(), null,
+                PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) != null;
+    }
+
+    @Override
+    public boolean requestAutofillData(IAssistDataReceiver receiver, Bundle receiverExtras,
+            IBinder activityToken, int flags) {
+        return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_AUTOFILL, null, null,
+                receiver, receiverExtras, activityToken, true, true, UserHandle.getCallingUserId(),
+                null, PENDING_AUTOFILL_ASSIST_STRUCTURE_TIMEOUT, flags) != null;
+    }
+
+    @Override
+    public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle,
+            Bundle args) {
+        return enqueueAssistContext(requestType, intent, hint, null, null, null,
+                true /* focused */, true /* newSessionId */, userHandle, args,
+                PENDING_ASSIST_EXTRAS_TIMEOUT, 0) != null;
+    }
+
+    @Override
+    public Bundle getAssistContextExtras(int requestType) {
+        PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null,
+                null, null, true /* focused */, true /* newSessionId */,
+                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT, 0);
+        if (pae == null) {
+            return null;
+        }
+        synchronized (pae) {
+            while (!pae.haveResult) {
+                try {
+                    pae.wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+        synchronized (mGlobalLock) {
+            buildAssistBundleLocked(pae, pae.result);
+            mPendingAssistExtras.remove(pae);
+            mUiHandler.removeCallbacks(pae);
+        }
+        return pae.extras;
+    }
+
+    /**
+     * Binder IPC calls go through the public entry point.
+     * This can be called with or without the global lock held.
+     */
+    private static int checkCallingPermission(String permission) {
+        return checkPermission(
+                permission, Binder.getCallingPid(), UserHandle.getAppId(Binder.getCallingUid()));
+    }
+
+    /** This can be called with or without the global lock held. */
+    private void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
+        if (!getRecentTasks().isCallerRecents(Binder.getCallingUid())) {
+            mAmInternal.enforceCallingPermission(permission, func);
+        }
+    }
+
+    @VisibleForTesting
+    int checkGetTasksPermission(String permission, int pid, int uid) {
+        return checkPermission(permission, pid, uid);
+    }
+
+    static int checkPermission(String permission, int pid, int uid) {
+        if (permission == null) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+        return checkComponentPermission(permission, pid, uid, -1, true);
+    }
+
+    public static int checkComponentPermission(String permission, int pid, int uid,
+            int owningUid, boolean exported) {
+        return ActivityManagerService.checkComponentPermission(
+                permission, pid, uid, owningUid, exported);
+    }
+
+    boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
+        if (getRecentTasks().isCallerRecents(callingUid)) {
+            // Always allow the recents component to get tasks
+            return true;
+        }
+
+        boolean allowed = checkGetTasksPermission(android.Manifest.permission.REAL_GET_TASKS,
+                callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+        if (!allowed) {
+            if (checkGetTasksPermission(android.Manifest.permission.GET_TASKS,
+                    callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) {
+                // Temporary compatibility: some existing apps on the system image may
+                // still be requesting the old permission and not switched to the new
+                // one; if so, we'll still allow them full access.  This means we need
+                // to see if they are holding the old permission and are a system app.
+                try {
+                    if (AppGlobals.getPackageManager().isUidPrivileged(callingUid)) {
+                        allowed = true;
+                        if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
+                                + " is using old GET_TASKS but privileged; allowing");
+                    }
+                } catch (RemoteException e) {
+                }
+            }
+            if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
+                    + " does not hold REAL_GET_TASKS; limiting output");
+        }
+        return allowed;
+    }
+
+    private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
+            IAssistDataReceiver receiver, Bundle receiverExtras, IBinder activityToken,
+            boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
+            int flags) {
+        mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
+                "enqueueAssistContext()");
+
+        synchronized (mGlobalLock) {
+            ActivityRecord activity = getTopDisplayFocusedStack().getTopActivity();
+            if (activity == null) {
+                Slog.w(TAG, "getAssistContextExtras failed: no top activity");
+                return null;
+            }
+            if (!activity.attachedToProcess()) {
+                Slog.w(TAG, "getAssistContextExtras failed: no process for " + activity);
+                return null;
+            }
+            if (focused) {
+                if (activityToken != null) {
+                    ActivityRecord caller = ActivityRecord.forTokenLocked(activityToken);
+                    if (activity != caller) {
+                        Slog.w(TAG, "enqueueAssistContext failed: caller " + caller
+                                + " is not current top " + activity);
+                        return null;
+                    }
+                }
+            } else {
+                activity = ActivityRecord.forTokenLocked(activityToken);
+                if (activity == null) {
+                    Slog.w(TAG, "enqueueAssistContext failed: activity for token=" + activityToken
+                            + " couldn't be found");
+                    return null;
+                }
+                if (!activity.attachedToProcess()) {
+                    Slog.w(TAG, "enqueueAssistContext failed: no process for " + activity);
+                    return null;
+                }
+            }
+
+            PendingAssistExtras pae;
+            Bundle extras = new Bundle();
+            if (args != null) {
+                extras.putAll(args);
+            }
+            extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName);
+            extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.mUid);
+
+            pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras,
+                    userHandle);
+            pae.isHome = activity.isActivityTypeHome();
+
+            // Increment the sessionId if necessary
+            if (newSessionId) {
+                mViSessionId++;
+            }
+            try {
+                activity.app.getThread().requestAssistContextExtras(activity.appToken, pae,
+                        requestType, mViSessionId, flags);
+                mPendingAssistExtras.add(pae);
+                mUiHandler.postDelayed(pae, timeout);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "getAssistContextExtras failed: crash calling " + activity);
+                return null;
+            }
+            return pae;
+        }
+    }
+
+    private void buildAssistBundleLocked(PendingAssistExtras pae, Bundle result) {
+        if (result != null) {
+            pae.extras.putBundle(Intent.EXTRA_ASSIST_CONTEXT, result);
+        }
+        if (pae.hint != null) {
+            pae.extras.putBoolean(pae.hint, true);
+        }
+    }
+
+    private void pendingAssistExtrasTimedOut(PendingAssistExtras pae) {
+        IAssistDataReceiver receiver;
+        synchronized (mGlobalLock) {
+            mPendingAssistExtras.remove(pae);
+            receiver = pae.receiver;
+        }
+        if (receiver != null) {
+            // Caller wants result sent back to them.
+            Bundle sendBundle = new Bundle();
+            // At least return the receiver extras
+            sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
+            try {
+                pae.receiver.onHandleAssistData(sendBundle);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    public class PendingAssistExtras extends Binder implements Runnable {
+        public final ActivityRecord activity;
+        public boolean isHome;
+        public final Bundle extras;
+        public final Intent intent;
+        public final String hint;
+        public final IAssistDataReceiver receiver;
+        public final int userHandle;
+        public boolean haveResult = false;
+        public Bundle result = null;
+        public AssistStructure structure = null;
+        public AssistContent content = null;
+        public Bundle receiverExtras;
+
+        public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent,
+                String _hint, IAssistDataReceiver _receiver, Bundle _receiverExtras,
+                int _userHandle) {
+            activity = _activity;
+            extras = _extras;
+            intent = _intent;
+            hint = _hint;
+            receiver = _receiver;
+            receiverExtras = _receiverExtras;
+            userHandle = _userHandle;
+        }
+
+        @Override
+        public void run() {
+            Slog.w(TAG, "getAssistContextExtras failed: timeout retrieving from " + activity);
+            synchronized (this) {
+                haveResult = true;
+                notifyAll();
+            }
+            pendingAssistExtrasTimedOut(this);
+        }
+    }
+
+    @Override
+    public boolean isAssistDataAllowedOnCurrentActivity() {
+        int userId;
+        synchronized (mGlobalLock) {
+            final ActivityStack focusedStack = getTopDisplayFocusedStack();
+            if (focusedStack == null || focusedStack.isActivityTypeAssistant()) {
+                return false;
+            }
+
+            final ActivityRecord activity = focusedStack.getTopActivity();
+            if (activity == null) {
+                return false;
+            }
+            userId = activity.userId;
+        }
+        return !DevicePolicyCache.getInstance().getScreenCaptureDisabled(userId);
+    }
+
+    @Override
+    public boolean showAssistFromActivity(IBinder token, Bundle args) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                ActivityRecord caller = ActivityRecord.forTokenLocked(token);
+                ActivityRecord top = getTopDisplayFocusedStack().getTopActivity();
+                if (top != caller) {
+                    Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+                            + " is not current top " + top);
+                    return false;
+                }
+                if (!top.nowVisible) {
+                    Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+                            + " is not visible");
+                    return false;
+                }
+            }
+            return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION, null,
+                    token);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public boolean isRootVoiceInteraction(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return false;
+            }
+            return r.rootVoiceInteraction;
+        }
+    }
+
+    private void onLocalVoiceInteractionStartedLocked(IBinder activity,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+        ActivityRecord activityToCallback = ActivityRecord.forTokenLocked(activity);
+        if (activityToCallback == null) return;
+        activityToCallback.setVoiceSessionLocked(voiceSession);
+
+        // Inform the activity
+        try {
+            activityToCallback.app.getThread().scheduleLocalVoiceInteractionStarted(activity,
+                    voiceInteractor);
+            long token = Binder.clearCallingIdentity();
+            try {
+                startRunningVoiceLocked(voiceSession, activityToCallback.appInfo.uid);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            // TODO: VI Should we cache the activity so that it's easier to find later
+            // rather than scan through all the stacks and activities?
+        } catch (RemoteException re) {
+            activityToCallback.clearVoiceSessionLocked();
+            // TODO: VI Should this terminate the voice session?
+        }
+    }
+
+    private void startRunningVoiceLocked(IVoiceInteractionSession session, int targetUid) {
+        Slog.d(TAG, "<<<  startRunningVoiceLocked()");
+        mVoiceWakeLock.setWorkSource(new WorkSource(targetUid));
+        if (mRunningVoice == null || mRunningVoice.asBinder() != session.asBinder()) {
+            boolean wasRunningVoice = mRunningVoice != null;
+            mRunningVoice = session;
+            if (!wasRunningVoice) {
+                mVoiceWakeLock.acquire();
+                updateSleepIfNeededLocked();
+            }
+        }
+    }
+
+    void finishRunningVoiceLocked() {
+        if (mRunningVoice != null) {
+            mRunningVoice = null;
+            mVoiceWakeLock.release();
+            updateSleepIfNeededLocked();
+        }
+    }
+
+    @Override
+    public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake) {
+        synchronized (mGlobalLock) {
+            if (mRunningVoice != null && mRunningVoice.asBinder() == session.asBinder()) {
+                if (keepAwake) {
+                    mVoiceWakeLock.acquire();
+                } else {
+                    mVoiceWakeLock.release();
+                }
+            }
+        }
+    }
+
+    @Override
+    public ComponentName getActivityClassForToken(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return null;
+            }
+            return r.intent.getComponent();
+        }
+    }
+
+    @Override
+    public String getPackageForToken(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return null;
+            }
+            return r.packageName;
+        }
+    }
+
+    @Override
+    public void showLockTaskEscapeMessage(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r == null) {
+                return;
+            }
+            getLockTaskController().showLockTaskToast();
+        }
+    }
+
+    @Override
+    public void keyguardGoingAway(int flags) {
+        enforceNotIsolatedCaller("keyguardGoingAway");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mKeyguardController.keyguardGoingAway(flags);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Try to place task to provided position. The final position might be different depending on
+     * current user and stacks state. The task will be moved to target stack if it's currently in
+     * different stack.
+     */
+    @Override
+    public void positionTaskInStack(int taskId, int stackId, int position) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
+        synchronized (mGlobalLock) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task="
+                        + taskId + " in stackId=" + stackId + " at position=" + position);
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    throw new IllegalArgumentException("positionTaskInStack: no task for id="
+                            + taskId);
+                }
+
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+
+                if (stack == null) {
+                    throw new IllegalArgumentException("positionTaskInStack: no stack for id="
+                            + stackId);
+                }
+                if (!stack.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException("positionTaskInStack: Attempt to change"
+                            + " the position of task " + taskId + " in/to non-standard stack");
+                }
+
+                // TODO: Have the callers of this API call a separate reparent method if that is
+                // what they intended to do vs. having this method also do reparenting.
+                if (task.getStack() == stack) {
+                    // Change position in current stack.
+                    stack.positionChildAt(task, position);
+                } else {
+                    // Reparent to new stack.
+                    task.reparent(stack, position, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
+                            !DEFER_RESUME, "positionTaskInStack");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
+            int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
+        if (DEBUG_CONFIGURATION) Slog.v(TAG, "Report configuration: " + token + " "
+                + horizontalSizeConfiguration + " " + verticalSizeConfigurations);
+        synchronized (mGlobalLock) {
+            ActivityRecord record = ActivityRecord.isInStackLocked(token);
+            if (record == null) {
+                throw new IllegalArgumentException("reportSizeConfigurations: ActivityRecord not "
+                        + "found for: " + token);
+            }
+            record.setSizeConfigurations(horizontalSizeConfiguration,
+                    verticalSizeConfigurations, smallestSizeConfigurations);
+        }
+    }
+
+    /**
+     * Dismisses split-screen multi-window mode.
+     * @param toTop If true the current primary split-screen stack will be placed or left on top.
+     */
+    @Override
+    public void dismissSplitScreenMode(boolean toTop) {
+        enforceCallerIsRecentsOrHasPermission(
+                MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityStack stack =
+                        mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+                if (stack == null) {
+                    Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
+                    return;
+                }
+
+                if (toTop) {
+                    // Caller wants the current split-screen primary stack to be the top stack after
+                    // it goes fullscreen, so move it to the front.
+                    stack.moveToFront("dismissSplitScreenMode");
+                } else if (mStackSupervisor.isTopDisplayFocusedStack(stack)) {
+                    // In this case the current split-screen primary stack shouldn't be the top
+                    // stack after it goes fullscreen, but it current has focus, so we move the
+                    // focus to the top-most split-screen secondary stack next to it.
+                    final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
+                            WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                    if (otherStack != null) {
+                        otherStack.moveToFront("dismissSplitScreenMode_other");
+                    }
+                }
+
+                stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Dismisses Pip
+     * @param animate True if the dismissal should be animated.
+     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
+     *                          default animation duration should be used.
+     */
+    @Override
+    public void dismissPip(boolean animate, int animationDuration) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final PinnedActivityStack stack =
+                        mStackSupervisor.getDefaultDisplay().getPinnedStack();
+                if (stack == null) {
+                    Slog.w(TAG, "dismissPip: pinned stack not found.");
+                    return;
+                }
+                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
+                    throw new IllegalArgumentException("Stack: " + stack
+                            + " doesn't support animated resize.");
+                }
+                if (animate) {
+                    stack.animateResizePinnedStack(null /* sourceHintBounds */,
+                            null /* destBounds */, animationDuration, false /* fromFullscreen */);
+                } else {
+                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, true /* onTop */);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()");
+        synchronized (mGlobalLock) {
+            mSuppressResizeConfigChanges = suppress;
+        }
+    }
+
+    /**
+     * NOTE: For the pinned stack, this method is usually called after the bounds animation has
+     *       animated the stack to the fullscreen, but can also be called if we are relaunching an
+     *       activity and clearing the task at the same time.
+     */
+    @Override
+    // TODO: API should just be about changing windowing modes...
+    public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "moveTasksToFullscreenStack()");
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
+                if (stack != null){
+                    if (!stack.isActivityTypeStandardOrUndefined()) {
+                        throw new IllegalArgumentException(
+                                "You can't move tasks from non-standard stacks.");
+                    }
+                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, onTop);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    /**
+     * Moves the top activity in the input stackId to the pinned stack.
+     *
+     * @param stackId Id of stack to move the top activity to pinned stack.
+     * @param bounds Bounds to use for pinned stack.
+     *
+     * @return True if the top activity of the input stack was successfully moved to the pinned
+     *          stack.
+     */
+    @Override
+    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "moveTopActivityToPinnedStack()");
+        synchronized (mGlobalLock) {
+            if (!mSupportsPictureInPicture) {
+                throw new IllegalStateException("moveTopActivityToPinnedStack:"
+                        + "Device doesn't support picture-in-picture mode");
+            }
+
+            long ident = Binder.clearCallingIdentity();
+            try {
+                return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public boolean isInMultiWindowMode(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+                // An activity is consider to be in multi-window mode if its task isn't fullscreen.
+                return r.inMultiWindowMode();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public boolean isInPictureInPictureMode(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return isInPictureInPictureMode(ActivityRecord.forTokenLocked(token));
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    private boolean isInPictureInPictureMode(ActivityRecord r) {
+        if (r == null || r.getStack() == null || !r.inPinnedWindowingMode()
+                || r.getStack().isInStackLocked(r) == null) {
+            return false;
+        }
+
+        // If we are animating to fullscreen then we have already dispatched the PIP mode
+        // changed, so we should reflect that check here as well.
+        final PinnedActivityStack stack = r.getStack();
+        final PinnedStackWindowController windowController = stack.getWindowContainerController();
+        return !windowController.isAnimatingBoundsToFullscreen();
+    }
+
+    @Override
+    public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
+                        "enterPictureInPictureMode", token, params);
+
+                // If the activity is already in picture in picture mode, then just return early
+                if (isInPictureInPictureMode(r)) {
+                    return true;
+                }
+
+                // Activity supports picture-in-picture, now check that we can enter PiP at this
+                // point, if it is
+                if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode",
+                        false /* beforeStopping */)) {
+                    return false;
+                }
+
+                final Runnable enterPipRunnable = () -> {
+                    synchronized (mGlobalLock) {
+                        // Only update the saved args from the args that are set
+                        r.pictureInPictureArgs.copyOnlySet(params);
+                        final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
+                        final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
+                        // Adjust the source bounds by the insets for the transition down
+                        final Rect sourceBounds = new Rect(
+                                r.pictureInPictureArgs.getSourceRectHint());
+                        mStackSupervisor.moveActivityToPinnedStackLocked(
+                                r, sourceBounds, aspectRatio, "enterPictureInPictureMode");
+                        final PinnedActivityStack stack = r.getStack();
+                        stack.setPictureInPictureAspectRatio(aspectRatio);
+                        stack.setPictureInPictureActions(actions);
+                        MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
+                                r.shortComponentName, r.supportsEnterPipOnTaskSwitch);
+                        logPictureInPictureArgs(params);
+                    }
+                };
+
+                if (isKeyguardLocked()) {
+                    // If the keyguard is showing or occluded, then try and dismiss it before
+                    // entering picture-in-picture (this will prompt the user to authenticate if the
+                    // device is currently locked).
+                    dismissKeyguard(token, new KeyguardDismissCallback() {
+                        @Override
+                        public void onDismissSucceeded() {
+                            mH.post(enterPipRunnable);
+                        }
+                    }, null /* message */);
+                } else {
+                    // Enter picture in picture immediately otherwise
+                    enterPipRunnable.run();
+                }
+                return true;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
+                        "setPictureInPictureParams", token, params);
+
+                // Only update the saved args from the args that are set
+                r.pictureInPictureArgs.copyOnlySet(params);
+                if (r.inPinnedWindowingMode()) {
+                    // If the activity is already in picture-in-picture, update the pinned stack now
+                    // if it is not already expanding to fullscreen. Otherwise, the arguments will
+                    // be used the next time the activity enters PiP
+                    final PinnedActivityStack stack = r.getStack();
+                    if (!stack.isAnimatingBoundsToFullscreen()) {
+                        stack.setPictureInPictureAspectRatio(
+                                r.pictureInPictureArgs.getAspectRatio());
+                        stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
+                    }
+                }
+                logPictureInPictureArgs(params);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public int getMaxNumPictureInPictureActions(IBinder token) {
+        // Currently, this is a static constant, but later, we may change this to be dependent on
+        // the context of the activity
+        return 3;
+    }
+
+    private void logPictureInPictureArgs(PictureInPictureParams params) {
+        if (params.hasSetActions()) {
+            MetricsLogger.histogram(mContext, "tron_varz_picture_in_picture_actions_count",
+                    params.getActions().size());
+        }
+        if (params.hasSetAspectRatio()) {
+            LogMaker lm = new LogMaker(MetricsEvent.ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED);
+            lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, params.getAspectRatio());
+            MetricsLogger.action(lm);
+        }
+    }
+
+    /**
+     * Checks the state of the system and the activity associated with the given {@param token} to
+     * verify that picture-in-picture is supported for that activity.
+     *
+     * @return the activity record for the given {@param token} if all the checks pass.
+     */
+    private ActivityRecord ensureValidPictureInPictureActivityParamsLocked(String caller,
+            IBinder token, PictureInPictureParams params) {
+        if (!mSupportsPictureInPicture) {
+            throw new IllegalStateException(caller
+                    + ": Device doesn't support picture-in-picture mode.");
+        }
+
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        if (r == null) {
+            throw new IllegalStateException(caller
+                    + ": Can't find activity for token=" + token);
+        }
+
+        if (!r.supportsPictureInPicture()) {
+            throw new IllegalStateException(caller
+                    + ": Current activity does not support picture-in-picture.");
+        }
+
+        if (params.hasSetAspectRatio()
+                && !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
+                params.getAspectRatio())) {
+            final float minAspectRatio = mContext.getResources().getFloat(
+                    com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
+            final float maxAspectRatio = mContext.getResources().getFloat(
+                    com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
+            throw new IllegalArgumentException(String.format(caller
+                            + ": Aspect ratio is too extreme (must be between %f and %f).",
+                    minAspectRatio, maxAspectRatio));
+        }
+
+        // Truncate the number of actions if necessary
+        params.truncateActions(getMaxNumPictureInPictureActions(token));
+
+        return r;
+    }
+
+    @Override
+    public IBinder getUriPermissionOwnerForActivity(IBinder activityToken) {
+        enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
+            if (r == null) {
+                throw new IllegalArgumentException("Activity does not exist; token="
+                        + activityToken);
+            }
+            return r.getUriPermissionsLocked().getExternalToken();
+        }
+    }
+
+    @Override
+    public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
+            Rect tempDockedTaskInsetBounds,
+            Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds,
+                        tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds,
+                        PRESERVE_WINDOWS);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void setSplitScreenResizing(boolean resizing) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.setSplitScreenResizing(resizing);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Check that we have the features required for VR-related API calls, and throw an exception if
+     * not.
+     */
+    public void enforceSystemHasVrFeature() {
+        if (!mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
+            throw new UnsupportedOperationException("VR mode not supported on this device!");
+        }
+    }
+
+    @Override
+    public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
+        enforceSystemHasVrFeature();
+
+        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+
+        ActivityRecord r;
+        synchronized (mGlobalLock) {
+            r = ActivityRecord.isInStackLocked(token);
+        }
+
+        if (r == null) {
+            throw new IllegalArgumentException();
+        }
+
+        int err;
+        if ((err = vrService.hasVrPackage(packageName, r.userId)) !=
+                VrManagerInternal.NO_ERROR) {
+            return err;
+        }
+
+        // Clear the binder calling uid since this path may call moveToTask().
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                r.requestedVrComponent = (enabled) ? packageName : null;
+
+                // Update associated state if this activity is currently focused
+                if (r.isResumedActivityOnDisplay()) {
+                    applyUpdateVrModeLocked(r);
+                }
+                return 0;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
+        Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
+        synchronized (mGlobalLock) {
+            ActivityRecord activity = getTopDisplayFocusedStack().getTopActivity();
+            if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
+                throw new SecurityException("Only focused activity can call startVoiceInteraction");
+            }
+            if (mRunningVoice != null || activity.getTask().voiceSession != null
+                    || activity.voiceSession != null) {
+                Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction");
+                return;
+            }
+            if (activity.pendingVoiceInteractionStart) {
+                Slog.w(TAG, "Pending start of voice interaction already.");
+                return;
+            }
+            activity.pendingVoiceInteractionStart = true;
+        }
+        LocalServices.getService(VoiceInteractionManagerInternal.class)
+                .startLocalVoiceInteraction(callingActivity, options);
+    }
+
+    @Override
+    public void stopLocalVoiceInteraction(IBinder callingActivity) {
+        LocalServices.getService(VoiceInteractionManagerInternal.class)
+                .stopLocalVoiceInteraction(callingActivity);
+    }
+
+    @Override
+    public boolean supportsLocalVoiceInteraction() {
+        return LocalServices.getService(VoiceInteractionManagerInternal.class)
+                .supportsLocalVoiceInteraction();
+    }
+
+    /** Notifies all listeners when the pinned stack animation starts. */
+    @Override
+    public void notifyPinnedStackAnimationStarted() {
+        mTaskChangeNotificationController.notifyPinnedStackAnimationStarted();
+    }
+
+    /** Notifies all listeners when the pinned stack animation ends. */
+    @Override
+    public void notifyPinnedStackAnimationEnded() {
+        mTaskChangeNotificationController.notifyPinnedStackAnimationEnded();
+    }
+
+    @Override
+    public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.resizePinnedStackLocked(pinnedBounds, tempPinnedTaskBounds);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public boolean updateDisplayOverrideConfiguration(Configuration values, int displayId) {
+        mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateDisplayOverrideConfiguration()");
+
+        synchronized (mGlobalLock) {
+            // Check if display is initialized in AM.
+            if (!mStackSupervisor.isDisplayAdded(displayId)) {
+                // Call might come when display is not yet added or has already been removed.
+                if (DEBUG_CONFIGURATION) {
+                    Slog.w(TAG, "Trying to update display configuration for non-existing displayId="
+                            + displayId);
+                }
+                return false;
+            }
+
+            if (values == null && mWindowManager != null) {
+                // sentinel: fetch the current configuration from the window manager
+                values = mWindowManager.computeNewConfiguration(displayId);
+            }
+
+            if (mWindowManager != null) {
+                final Message msg = PooledLambda.obtainMessage(
+                        ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal, displayId);
+                mH.sendMessage(msg);
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                if (values != null) {
+                    Settings.System.clearConfiguration(values);
+                }
+                updateDisplayOverrideConfigurationLocked(values, null /* starting */,
+                        false /* deferResume */, displayId, mTmpUpdateConfigurationResult);
+                return mTmpUpdateConfigurationResult.changes != 0;
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public boolean updateConfiguration(Configuration values) {
+        mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
+
+        synchronized (mGlobalLock) {
+            if (values == null && mWindowManager != null) {
+                // sentinel: fetch the current configuration from the window manager
+                values = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
+            }
+
+            if (mWindowManager != null) {
+                final Message msg = PooledLambda.obtainMessage(
+                        ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal,
+                        DEFAULT_DISPLAY);
+                mH.sendMessage(msg);
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                if (values != null) {
+                    Settings.System.clearConfiguration(values);
+                }
+                updateConfigurationLocked(values, null, false, false /* persistent */,
+                        UserHandle.USER_NULL, false /* deferResume */,
+                        mTmpUpdateConfigurationResult);
+                return mTmpUpdateConfigurationResult.changes != 0;
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
+            CharSequence message) {
+        if (message != null) {
+            mAmInternal.enforceCallingPermission(
+                    Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard()");
+        }
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mKeyguardController.dismissKeyguard(token, callback, message);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public void cancelTaskWindowTransition(int taskId) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "cancelTaskWindowTransition()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_ONLY);
+                if (task == null) {
+                    Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
+                    return;
+                }
+                task.cancelWindowTransition();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
+        enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            final TaskRecord task;
+            synchronized (mGlobalLock) {
+                task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+                if (task == null) {
+                    Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
+                    return null;
+                }
+            }
+            // Don't call this while holding the lock as this operation might hit the disk.
+            return task.getSnapshot(reducedResolution);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void setDisablePreviewScreenshots(IBinder token, boolean disable) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                Slog.w(TAG, "setDisablePreviewScreenshots: Unable to find activity for token="
+                        + token);
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setDisablePreviewScreenshots(disable);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    /** Return the user id of the last resumed activity. */
+    @Override
+    public @UserIdInt
+    int getLastResumedActivityUserId() {
+        mAmInternal.enforceCallingPermission(
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL, "getLastResumedActivityUserId()");
+        synchronized (mGlobalLock) {
+            if (mLastResumedActivity == null) {
+                return getCurrentUserId();
+            }
+            return mLastResumedActivity.userId;
+        }
+    }
+
+    @Override
+    public void updateLockTaskFeatures(int userId, int flags) {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != 0 && callingUid != SYSTEM_UID) {
+            mAmInternal.enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
+                    "updateLockTaskFeatures()");
+        }
+        synchronized (mGlobalLock) {
+            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowing features " + userId + ":0x" +
+                    Integer.toHexString(flags));
+            getLockTaskController().updateLockTaskFeatures(userId, flags);
+        }
+    }
+
+    @Override
+    public void setShowWhenLocked(IBinder token, boolean showWhenLocked) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setShowWhenLocked(showWhenLocked);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void setTurnScreenOn(IBinder token, boolean turnScreenOn) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setTurnScreenOn(turnScreenOn);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
+        mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+                "registerRemoteAnimations");
+        definition.setCallingPid(Binder.getCallingPid());
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.registerRemoteAnimations(definition);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void registerRemoteAnimationForNextActivityStart(String packageName,
+            RemoteAnimationAdapter adapter) {
+        mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+                "registerRemoteAnimationForNextActivityStart");
+        adapter.setCallingPid(Binder.getCallingPid());
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                getActivityStartController().registerRemoteAnimationForNextActivityStart(
+                        packageName, adapter);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
+    @Override
+    public void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                mAppWarnings.alwaysShowUnsupportedCompileSdkWarning(activity);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void setVrThread(int tid) {
+        enforceSystemHasVrFeature();
+        synchronized (mGlobalLock) {
+            final int pid = Binder.getCallingPid();
+            final WindowProcessController wpc = mPidMap.get(pid);
+            mVrController.setVrThreadLocked(tid, pid, wpc);
+        }
+    }
+
+    @Override
+    public void setPersistentVrThread(int tid) {
+        if (checkCallingPermission(Manifest.permission.RESTRICTED_VR_ACCESS)
+                != PERMISSION_GRANTED) {
+            final String msg = "Permission Denial: setPersistentVrThread() from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " requires " + Manifest.permission.RESTRICTED_VR_ACCESS;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        enforceSystemHasVrFeature();
+        synchronized (mGlobalLock) {
+            final int pid = Binder.getCallingPid();
+            final WindowProcessController proc = mPidMap.get(pid);
+            mVrController.setPersistentVrThreadLocked(tid, pid, proc);
+        }
+    }
+
+    @Override
+    public void stopAppSwitches() {
+        enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "stopAppSwitches");
+        synchronized (mGlobalLock) {
+            mAppSwitchesAllowedTime = SystemClock.uptimeMillis() + APP_SWITCH_DELAY_TIME;
+            mDidAppSwitch = false;
+            getActivityStartController().schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
+        }
+    }
+
+    @Override
+    public void resumeAppSwitches() {
+        enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
+        synchronized (mGlobalLock) {
+            // Note that we don't execute any pending app switches... we will
+            // let those wait until either the timeout, or the next start
+            // activity request.
+            mAppSwitchesAllowedTime = 0;
+        }
+    }
+
+    void onStartActivitySetDidAppSwitch() {
+        if (mDidAppSwitch) {
+            // This is the second allowed switch since we stopped switches, so now just generally
+            // allow switches. Use case:
+            // - user presses home (switches disabled, switch to home, mDidAppSwitch now true);
+            // - user taps a home icon (coming from home so allowed, we hit here and now allow
+            // anyone to switch again).
+            mAppSwitchesAllowedTime = 0;
+        } else {
+            mDidAppSwitch = true;
+        }
+    }
+
+    /** @return whether the system should disable UI modes incompatible with VR mode. */
+    boolean shouldDisableNonVrUiLocked() {
+        return mVrController.shouldDisableNonVrUiLocked();
+    }
+
+    private void applyUpdateVrModeLocked(ActivityRecord r) {
+        // VR apps are expected to run in a main display. If an app is turning on VR for
+        // itself, but lives in a dynamic stack, then make sure that it is moved to the main
+        // fullscreen stack before enabling VR Mode.
+        // TODO: The goal of this code is to keep the VR app on the main display. When the
+        // stack implementation changes in the future, keep in mind that the use of the fullscreen
+        // stack is a means to move the activity to the main display and a moveActivityToDisplay()
+        // option would be a better choice here.
+        if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
+            Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
+                    + " to main stack for VR");
+            final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+                    WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
+            moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
+        }
+        mH.post(() -> {
+            if (!mVrController.onVrModeChanged(r)) {
+                return;
+            }
+            synchronized (mGlobalLock) {
+                final boolean disableNonVrUi = mVrController.shouldDisableNonVrUiLocked();
+                mWindowManager.disableNonVrUi(disableNonVrUi);
+                if (disableNonVrUi) {
+                    // If we are in a VR mode where Picture-in-Picture mode is unsupported,
+                    // then remove the pinned stack.
+                    mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+                }
+            }
+        });
+    }
+
+    @Override
+    public int getPackageScreenCompatMode(String packageName) {
+        enforceNotIsolatedCaller("getPackageScreenCompatMode");
+        synchronized (mGlobalLock) {
+            return mCompatModePackages.getPackageScreenCompatModeLocked(packageName);
+        }
+    }
+
+    @Override
+    public void setPackageScreenCompatMode(String packageName, int mode) {
+        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+                "setPackageScreenCompatMode");
+        synchronized (mGlobalLock) {
+            mCompatModePackages.setPackageScreenCompatModeLocked(packageName, mode);
+        }
+    }
+
+    @Override
+    public boolean getPackageAskScreenCompat(String packageName) {
+        enforceNotIsolatedCaller("getPackageAskScreenCompat");
+        synchronized (mGlobalLock) {
+            return mCompatModePackages.getPackageAskCompatModeLocked(packageName);
+        }
+    }
+
+    @Override
+    public void setPackageAskScreenCompat(String packageName, boolean ask) {
+        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+                "setPackageAskScreenCompat");
+        synchronized (mGlobalLock) {
+            mCompatModePackages.setPackageAskCompatModeLocked(packageName, ask);
+        }
+    }
+
+    public static String relaunchReasonToString(int relaunchReason) {
+        switch (relaunchReason) {
+            case RELAUNCH_REASON_WINDOWING_MODE_RESIZE:
+                return "window_resize";
+            case RELAUNCH_REASON_FREE_RESIZE:
+                return "free_resize";
+            default:
+                return null;
+        }
+    }
+
+    ActivityStack getTopDisplayFocusedStack() {
+        return mStackSupervisor.getTopDisplayFocusedStack();
+    }
+
+    /** Pokes the task persister. */
+    void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
+        mRecentTasks.notifyTaskPersisterLocked(task, flush);
+    }
+
+    boolean isKeyguardLocked() {
+        return mKeyguardController.isKeyguardLocked();
+    }
+
+    void dumpLastANRLocked(PrintWriter pw) {
+        pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
+        if (mLastANRState == null) {
+            pw.println("  <no ANR has occurred since boot>");
+        } else {
+            pw.println(mLastANRState);
+        }
+    }
+
+    void dumpLastANRTracesLocked(PrintWriter pw) {
+        pw.println("ACTIVITY MANAGER LAST ANR TRACES (dumpsys activity lastanr-traces)");
+
+        final File[] files = new File(ANR_TRACE_DIR).listFiles();
+        if (ArrayUtils.isEmpty(files)) {
+            pw.println("  <no ANR has occurred since boot>");
+            return;
+        }
+        // Find the latest file.
+        File latest = null;
+        for (File f : files) {
+            if ((latest == null) || (latest.lastModified() < f.lastModified())) {
+                latest = f;
+            }
+        }
+        pw.print("File: ");
+        pw.print(latest.getName());
+        pw.println();
+        try (BufferedReader in = new BufferedReader(new FileReader(latest))) {
+            String line;
+            while ((line = in.readLine()) != null) {
+                pw.println(line);
+            }
+        } catch (IOException e) {
+            pw.print("Unable to read: ");
+            pw.print(e);
+            pw.println();
+        }
+    }
+
+    void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
+        dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage,
+                "ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)");
+    }
+
+    void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) {
+        pw.println(header);
+
+        boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient,
+                dumpPackage);
+        boolean needSep = printedAnything;
+
+        boolean printed = ActivityStackSupervisor.printThisActivity(pw,
+                mStackSupervisor.getTopResumedActivity(),  dumpPackage, needSep,
+                "  ResumedActivity: ");
+        if (printed) {
+            printedAnything = true;
+            needSep = false;
+        }
+
+        if (dumpPackage == null) {
+            if (needSep) {
+                pw.println();
+            }
+            printedAnything = true;
+            mStackSupervisor.dump(pw, "  ");
+        }
+
+        if (!printedAnything) {
+            pw.println("  (nothing)");
+        }
+    }
+
+    void dumpActivityContainersLocked(PrintWriter pw) {
+        pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
+        mStackSupervisor.dumpChildrenNames(pw, " ");
+        pw.println(" ");
+    }
+
+    void dumpActivityStarterLocked(PrintWriter pw, String dumpPackage) {
+        pw.println("ACTIVITY MANAGER STARTER (dumpsys activity starter)");
+        getActivityStartController().dump(pw, "", dumpPackage);
+    }
+
+    /**
+     * There are three things that cmd can be:
+     *  - a flattened component name that matches an existing activity
+     *  - the cmd arg isn't the flattened component name of an existing activity:
+     *    dump all activity whose component contains the cmd as a substring
+     *  - A hex number of the ActivityRecord object instance.
+     *
+     *  @param dumpVisibleStacksOnly dump activity with {@param name} only if in a visible stack
+     *  @param dumpFocusedStackOnly dump activity with {@param name} only if in the focused stack
+     */
+    protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+            int opti, boolean dumpAll, boolean dumpVisibleStacksOnly, boolean dumpFocusedStackOnly) {
+        ArrayList<ActivityRecord> activities;
+
+        synchronized (mGlobalLock) {
+            activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
+                    dumpFocusedStackOnly);
+        }
+
+        if (activities.size() <= 0) {
+            return false;
+        }
+
+        String[] newArgs = new String[args.length - opti];
+        System.arraycopy(args, opti, newArgs, 0, args.length - opti);
+
+        TaskRecord lastTask = null;
+        boolean needSep = false;
+        for (int i = activities.size() - 1; i >= 0; i--) {
+            ActivityRecord r = activities.get(i);
+            if (needSep) {
+                pw.println();
+            }
+            needSep = true;
+            synchronized (mGlobalLock) {
+                final TaskRecord task = r.getTask();
+                if (lastTask != task) {
+                    lastTask = task;
+                    pw.print("TASK "); pw.print(lastTask.affinity);
+                    pw.print(" id="); pw.print(lastTask.taskId);
+                    pw.print(" userId="); pw.println(lastTask.userId);
+                    if (dumpAll) {
+                        lastTask.dump(pw, "  ");
+                    }
+                }
+            }
+            dumpActivity("  ", fd, pw, activities.get(i), newArgs, dumpAll);
+        }
+        return true;
+    }
+
+    /**
+     * Invokes IApplicationThread.dumpActivity() on the thread of the specified activity if
+     * there is a thread associated with the activity.
+     */
+    private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
+            final ActivityRecord r, String[] args, boolean dumpAll) {
+        String innerPrefix = prefix + "  ";
+        synchronized (mGlobalLock) {
+            pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName);
+            pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r)));
+            pw.print(" pid=");
+            if (r.hasProcess()) pw.println(r.app.getPid());
+            else pw.println("(not running)");
+            if (dumpAll) {
+                r.dump(pw, innerPrefix);
+            }
+        }
+        if (r.attachedToProcess()) {
+            // flush anything that is already in the PrintWriter since the thread is going
+            // to write to the file descriptor directly
+            pw.flush();
+            try {
+                TransferPipe tp = new TransferPipe();
+                try {
+                    r.app.getThread().dumpActivity(tp.getWriteFd(),
+                            r.appToken, innerPrefix, args);
+                    tp.go(fd);
+                } finally {
+                    tp.kill();
+                }
+            } catch (IOException e) {
+                pw.println(innerPrefix + "Failure while dumping the activity: " + e);
+            } catch (RemoteException e) {
+                pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
+            }
+        }
+    }
+
+    void writeSleepStateToProto(ProtoOutputStream proto) {
+        for (ActivityTaskManagerInternal.SleepToken st : mStackSupervisor.mSleepTokens) {
+            proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS,
+                    st.toString());
+        }
+
+        if (mRunningVoice != null) {
+            final long vrToken = proto.start(
+                    ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
+            proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION,
+                    mRunningVoice.toString());
+            mVoiceWakeLock.writeToProto(
+                    proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK);
+            proto.end(vrToken);
+        }
+
+        proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEPING, mSleeping);
+        proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SHUTTING_DOWN,
+                mShuttingDown);
+        mVrController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
+    }
+
+    int getCurrentUserId() {
+        return mAmInternal.getCurrentUserId();
+    }
+
+    private void enforceNotIsolatedCaller(String caller) {
+        if (UserHandle.isIsolated(Binder.getCallingUid())) {
+            throw new SecurityException("Isolated process not allowed to call " + caller);
+        }
+    }
+
+    public Configuration getConfiguration() {
+        Configuration ci;
+        synchronized(mGlobalLock) {
+            ci = new Configuration(getGlobalConfigurationForCallingPid());
+            ci.userSetLocale = false;
+        }
+        return ci;
+    }
+
+    /**
+     * Current global configuration information. Contains general settings for the entire system,
+     * also corresponds to the merged configuration of the default display.
+     */
+    Configuration getGlobalConfiguration() {
+        return mStackSupervisor.getConfiguration();
+    }
+
+    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean initLocale) {
+        return updateConfigurationLocked(values, starting, initLocale, false /* deferResume */);
+    }
+
+    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean initLocale, boolean deferResume) {
+        // pass UserHandle.USER_NULL as userId because we don't persist configuration for any user
+        return updateConfigurationLocked(values, starting, initLocale, false /* persistent */,
+                UserHandle.USER_NULL, deferResume);
+    }
+
+    public void updatePersistentConfiguration(Configuration values, @UserIdInt int userId) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                updateConfigurationLocked(values, null, false, true, userId,
+                        false /* deferResume */);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean initLocale, boolean persistent, int userId, boolean deferResume) {
+        return updateConfigurationLocked(values, starting, initLocale, persistent, userId,
+                deferResume, null /* result */);
+    }
+
+    /**
+     * Do either or both things: (1) change the current configuration, and (2)
+     * make sure the given activity is running with the (now) current
+     * configuration.  Returns true if the activity has been left running, or
+     * false if <var>starting</var> is being destroyed to match the new
+     * configuration.
+     *
+     * @param userId is only used when persistent parameter is set to true to persist configuration
+     *               for that particular user
+     */
+    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean initLocale, boolean persistent, int userId, boolean deferResume,
+            ActivityTaskManagerService.UpdateConfigurationResult result) {
+        int changes = 0;
+        boolean kept = true;
+
+        if (mWindowManager != null) {
+            mWindowManager.deferSurfaceLayout();
+        }
+        try {
+            if (values != null) {
+                changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
+                        deferResume);
+            }
+
+            kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+        } finally {
+            if (mWindowManager != null) {
+                mWindowManager.continueSurfaceLayout();
+            }
+        }
+
+        if (result != null) {
+            result.changes = changes;
+            result.activityRelaunched = !kept;
+        }
+        return kept;
+    }
+
+    /** Update default (global) configuration and notify listeners about changes. */
+    private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
+            boolean persistent, int userId, boolean deferResume) {
+        mTempConfig.setTo(getGlobalConfiguration());
+        final int changes = mTempConfig.updateFrom(values);
+        if (changes == 0) {
+            // Since calling to Activity.setRequestedOrientation leads to freezing the window with
+            // setting WindowManagerService.mWaitingForConfig to true, it is important that we call
+            // performDisplayOverrideConfigUpdate in order to send the new display configuration
+            // (even if there are no actual changes) to unfreeze the window.
+            performDisplayOverrideConfigUpdate(values, deferResume, DEFAULT_DISPLAY);
+            return 0;
+        }
+
+        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
+                "Updating global configuration to: " + values);
+
+        EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
+        StatsLog.write(StatsLog.RESOURCE_CONFIGURATION_CHANGED,
+                values.colorMode,
+                values.densityDpi,
+                values.fontScale,
+                values.hardKeyboardHidden,
+                values.keyboard,
+                values.keyboardHidden,
+                values.mcc,
+                values.mnc,
+                values.navigation,
+                values.navigationHidden,
+                values.orientation,
+                values.screenHeightDp,
+                values.screenLayout,
+                values.screenWidthDp,
+                values.smallestScreenWidthDp,
+                values.touchscreen,
+                values.uiMode);
+
+
+        if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
+            final LocaleList locales = values.getLocales();
+            int bestLocaleIndex = 0;
+            if (locales.size() > 1) {
+                if (mSupportedSystemLocales == null) {
+                    mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
+                }
+                bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
+            }
+            SystemProperties.set("persist.sys.locale",
+                    locales.get(bestLocaleIndex).toLanguageTag());
+            LocaleList.setDefault(locales, bestLocaleIndex);
+
+            final Message m = PooledLambda.obtainMessage(
+                    ActivityTaskManagerService::sendLocaleToMountDaemonMsg, this,
+                    locales.get(bestLocaleIndex));
+            mH.sendMessage(m);
+        }
+
+        mTempConfig.seq = increaseConfigurationSeqLocked();
+
+        // Update stored global config and notify everyone about the change.
+        mStackSupervisor.onConfigurationChanged(mTempConfig);
+
+        Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
+        // TODO(multi-display): Update UsageEvents#Event to include displayId.
+        mUsageStatsInternal.reportConfigurationChange(mTempConfig, mAmInternal.getCurrentUserId());
+
+        // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
+        updateShouldShowDialogsLocked(mTempConfig);
+
+        AttributeCache ac = AttributeCache.instance();
+        if (ac != null) {
+            ac.updateConfiguration(mTempConfig);
+        }
+
+        // Make sure all resources in our process are updated right now, so that anyone who is going
+        // to retrieve resource values after we return will be sure to get the new ones. This is
+        // especially important during boot, where the first config change needs to guarantee all
+        // resources have that config before following boot code is executed.
+        mSystemThread.applyConfigurationToResources(mTempConfig);
+
+        // We need another copy of global config because we're scheduling some calls instead of
+        // running them in place. We need to be sure that object we send will be handled unchanged.
+        final Configuration configCopy = new Configuration(mTempConfig);
+        if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
+            final Message msg = PooledLambda.obtainMessage(
+                    ActivityTaskManagerService::sendPutConfigurationForUserMsg,
+                    this, userId, configCopy);
+            mH.sendMessage(msg);
+        }
+
+        for (int i = mPidMap.size() - 1; i >= 0; i--) {
+            final int pid = mPidMap.keyAt(i);
+            final WindowProcessController app = mPidMap.get(pid);
+            if (DEBUG_CONFIGURATION) {
+                Slog.v(TAG_CONFIGURATION, "Update process config of "
+                        + app.mName + " to new config " + configCopy);
+            }
+            app.onConfigurationChanged(configCopy);
+        }
+
+        final Message msg = PooledLambda.obtainMessage(
+                ActivityManagerInternal::broadcastGlobalConfigurationChanged,
+                mAmInternal, changes, initLocale);
+        mH.sendMessage(msg);
+
+        // Override configuration of the default display duplicates global config, so we need to
+        // update it also. This will also notify WindowManager about changes.
+        performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
+                DEFAULT_DISPLAY);
+
+        return changes;
+    }
+
+    boolean updateDisplayOverrideConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean deferResume, int displayId) {
+        return updateDisplayOverrideConfigurationLocked(values, starting, deferResume /* deferResume */,
+                displayId, null /* result */);
+    }
+
+    /**
+     * Updates override configuration specific for the selected display. If no config is provided,
+     * new one will be computed in WM based on current display info.
+     */
+    boolean updateDisplayOverrideConfigurationLocked(Configuration values,
+            ActivityRecord starting, boolean deferResume, int displayId,
+            ActivityTaskManagerService.UpdateConfigurationResult result) {
+        int changes = 0;
+        boolean kept = true;
+
+        if (mWindowManager != null) {
+            mWindowManager.deferSurfaceLayout();
+        }
+        try {
+            if (values != null) {
+                if (displayId == DEFAULT_DISPLAY) {
+                    // Override configuration of the default display duplicates global config, so
+                    // we're calling global config update instead for default display. It will also
+                    // apply the correct override config.
+                    changes = updateGlobalConfigurationLocked(values, false /* initLocale */,
+                            false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume);
+                } else {
+                    changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId);
+                }
+            }
+
+            kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+        } finally {
+            if (mWindowManager != null) {
+                mWindowManager.continueSurfaceLayout();
+            }
+        }
+
+        if (result != null) {
+            result.changes = changes;
+            result.activityRelaunched = !kept;
+        }
+        return kept;
+    }
+
+    private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
+            int displayId) {
+        mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+        final int changes = mTempConfig.updateFrom(values);
+        if (changes != 0) {
+            Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
+                    + mTempConfig + " for displayId=" + displayId);
+            mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId);
+
+            final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
+            if (isDensityChange && displayId == DEFAULT_DISPLAY) {
+                mAppWarnings.onDensityChanged();
+
+                // Post message to start process to avoid possible deadlock of calling into AMS with
+                // the ATMS lock held.
+                final Message msg = PooledLambda.obtainMessage(
+                        ActivityManagerInternal::killAllBackgroundProcessesExcept, mAmInternal,
+                        N, ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+                mH.sendMessage(msg);
+            }
+        }
+        return changes;
+    }
+
+    private void updateEventDispatchingLocked(boolean booted) {
+        mWindowManager.setEventDispatching(booted && !mShuttingDown);
+    }
+
+    private void sendPutConfigurationForUserMsg(int userId, Configuration config) {
+        final ContentResolver resolver = mContext.getContentResolver();
+        Settings.System.putConfigurationForUser(resolver, config, userId);
+    }
+
+    private void sendLocaleToMountDaemonMsg(Locale l) {
+        try {
+            IBinder service = ServiceManager.getService("mount");
+            IStorageManager storageManager = IStorageManager.Stub.asInterface(service);
+            Log.d(TAG, "Storing locale " + l.toLanguageTag() + " for decryption UI");
+            storageManager.setField(StorageManager.SYSTEM_LOCALE_KEY, l.toLanguageTag());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error storing locale for decryption UI", e);
+        }
+    }
+
+    boolean isActivityStartsLoggingEnabled() {
+        return mAmInternal.isActivityStartsLoggingEnabled();
+    }
+
+    void enableScreenAfterBoot(boolean booted) {
+        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
+                SystemClock.uptimeMillis());
+        mWindowManager.enableScreenAfterBoot();
+
+        synchronized (mGlobalLock) {
+            updateEventDispatchingLocked(booted);
+        }
+    }
+
+    static long getInputDispatchingTimeoutLocked(ActivityRecord r) {
+        if (r == null || !r.hasProcess()) {
+            return KEY_DISPATCHING_TIMEOUT_MS;
+        }
+        return getInputDispatchingTimeoutLocked(r.app);
+    }
+
+    private static long getInputDispatchingTimeoutLocked(WindowProcessController r) {
+        return r != null ? r.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
+    }
+
+    /**
+     * Decide based on the configuration whether we should show the ANR,
+     * crash, etc dialogs.  The idea is that if there is no affordance to
+     * press the on-screen buttons, or the user experience would be more
+     * greatly impacted than the crash itself, we shouldn't show the dialog.
+     *
+     * A thought: SystemUI might also want to get told about this, the Power
+     * dialog / global actions also might want different behaviors.
+     */
+    private void updateShouldShowDialogsLocked(Configuration config) {
+        final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
+                && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
+                && config.navigation == Configuration.NAVIGATION_NONAV);
+        int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
+        final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
+                && !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER)
+                && modeType != Configuration.UI_MODE_TYPE_TELEVISION
+                && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
+        final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(),
+                HIDE_ERROR_DIALOGS, 0) != 0;
+        mShowDialogs = inputMethodExists && uiModeSupportsDialogs && !hideDialogsSet;
+    }
+
+    private void updateFontScaleIfNeeded(@UserIdInt int userId) {
+        final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(),
+                FONT_SCALE, 1.0f, userId);
+
+        synchronized (this) {
+            if (getGlobalConfiguration().fontScale == scaleFactor) {
+                return;
+            }
+
+            final Configuration configuration
+                    = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
+            configuration.fontScale = scaleFactor;
+            updatePersistentConfiguration(configuration, userId);
+        }
+    }
+
+    // Actually is sleeping or shutting down or whatever else in the future
+    // is an inactive state.
+    boolean isSleepingOrShuttingDownLocked() {
+        return isSleepingLocked() || mShuttingDown;
+    }
+
+    boolean isSleepingLocked() {
+        return mSleeping;
+    }
+
+    /** Update AMS states when an activity is resumed. */
+    void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
+        final TaskRecord task = r.getTask();
+        if (task.isActivityTypeStandard()) {
+            if (mCurAppTimeTracker != r.appTimeTracker) {
+                // We are switching app tracking.  Complete the current one.
+                if (mCurAppTimeTracker != null) {
+                    mCurAppTimeTracker.stop();
+                    mH.obtainMessage(
+                            REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
+                    mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
+                    mCurAppTimeTracker = null;
+                }
+                if (r.appTimeTracker != null) {
+                    mCurAppTimeTracker = r.appTimeTracker;
+                    startTimeTrackingFocusedActivityLocked();
+                }
+            } else {
+                startTimeTrackingFocusedActivityLocked();
+            }
+        } else {
+            r.appTimeTracker = null;
+        }
+        // TODO: VI Maybe r.task.voiceInteractor || r.voiceInteractor != null
+        // TODO: Probably not, because we don't want to resume voice on switching
+        // back to this activity
+        if (task.voiceInteractor != null) {
+            startRunningVoiceLocked(task.voiceSession, r.info.applicationInfo.uid);
+        } else {
+            finishRunningVoiceLocked();
+
+            if (mLastResumedActivity != null) {
+                final IVoiceInteractionSession session;
+
+                final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask();
+                if (lastResumedActivityTask != null
+                        && lastResumedActivityTask.voiceSession != null) {
+                    session = lastResumedActivityTask.voiceSession;
+                } else {
+                    session = mLastResumedActivity.voiceSession;
+                }
+
+                if (session != null) {
+                    // We had been in a voice interaction session, but now focused has
+                    // move to something different.  Just finish the session, we can't
+                    // return to it and retain the proper state and synchronization with
+                    // the voice interaction service.
+                    finishVoiceTask(session);
+                }
+            }
+        }
+
+        if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) {
+            mAmInternal.sendForegroundProfileChanged(r.userId);
+        }
+        updateResumedAppTrace(r);
+        mLastResumedActivity = r;
+
+        r.getDisplay().setFocusedApp(r, true);
+
+        applyUpdateLockStateLocked(r);
+        applyUpdateVrModeLocked(r);
+
+        EventLogTags.writeAmSetResumedActivity(
+                r == null ? -1 : r.userId,
+                r == null ? "NULL" : r.shortComponentName,
+                reason);
+    }
+
+    ActivityTaskManagerInternal.SleepToken acquireSleepToken(String tag, int displayId) {
+        synchronized (mGlobalLock) {
+            final ActivityTaskManagerInternal.SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
+            updateSleepIfNeededLocked();
+            return token;
+        }
+    }
+
+    void updateSleepIfNeededLocked() {
+        final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
+        final boolean wasSleeping = mSleeping;
+        boolean updateOomAdj = false;
+
+        if (!shouldSleep) {
+            // If wasSleeping is true, we need to wake up activity manager state from when
+            // we started sleeping. In either case, we need to apply the sleep tokens, which
+            // will wake up stacks or put them to sleep as appropriate.
+            if (wasSleeping) {
+                mSleeping = false;
+                StatsLog.write(StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
+                        StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__AWAKE);
+                startTimeTrackingFocusedActivityLocked();
+                mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+                mStackSupervisor.comeOutOfSleepIfNeededLocked();
+            }
+            mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
+            if (wasSleeping) {
+                updateOomAdj = true;
+            }
+        } else if (!mSleeping && shouldSleep) {
+            mSleeping = true;
+            StatsLog.write(StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
+                    StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__ASLEEP);
+            if (mCurAppTimeTracker != null) {
+                mCurAppTimeTracker.stop();
+            }
+            mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
+            mStackSupervisor.goingToSleepLocked();
+            updateResumedAppTrace(null /* resumed */);
+            updateOomAdj = true;
+        }
+        if (updateOomAdj) {
+            mH.post(mAmInternal::updateOomAdj);
+        }
+    }
+
+    void updateOomAdj() {
+        mH.post(mAmInternal::updateOomAdj);
+    }
+
+    void updateCpuStats() {
+        mH.post(mAmInternal::updateCpuStats);
+    }
+
+    void updateUsageStats(ActivityRecord component, boolean resumed) {
+        final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats,
+                mAmInternal, component.realActivity, component.app.mUid, component.userId, resumed);
+        mH.sendMessage(m);
+    }
+
+    void setBooting(boolean booting) {
+        mAmInternal.setBooting(booting);
+    }
+
+    boolean isBooting() {
+        return mAmInternal.isBooting();
+    }
+
+    void setBooted(boolean booted) {
+        mAmInternal.setBooted(booted);
+    }
+
+    boolean isBooted() {
+        return mAmInternal.isBooted();
+    }
+
+    void postFinishBooting(boolean finishBooting, boolean enableScreen) {
+        mH.post(() -> {
+            if (finishBooting) {
+                mAmInternal.finishBooting();
+            }
+            if (enableScreen) {
+                mInternal.enableScreenAfterBoot(isBooted());
+            }
+        });
+    }
+
+    void setHeavyWeightProcess(ActivityRecord root) {
+        mHeavyWeightProcess = root.app;
+        final Message m = PooledLambda.obtainMessage(
+                ActivityTaskManagerService::postHeavyWeightProcessNotification, this,
+                root.app, root.intent, root.userId);
+        mH.sendMessage(m);
+    }
+
+    void clearHeavyWeightProcessIfEquals(WindowProcessController proc) {
+        if (mHeavyWeightProcess == null || mHeavyWeightProcess != proc) {
+            return;
+        }
+
+        mHeavyWeightProcess = null;
+        final Message m = PooledLambda.obtainMessage(
+                ActivityTaskManagerService::cancelHeavyWeightProcessNotification, this,
+                proc.mUserId);
+        mH.sendMessage(m);
+    }
+
+    private void cancelHeavyWeightProcessNotification(int userId) {
+        final INotificationManager inm = NotificationManager.getService();
+        if (inm == null) {
+            return;
+        }
+        try {
+            inm.cancelNotificationWithTag("android", null,
+                    SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, userId);
+        } catch (RuntimeException e) {
+            Slog.w(TAG, "Error canceling notification for service", e);
+        } catch (RemoteException e) {
+        }
+
+    }
+
+    private void postHeavyWeightProcessNotification(
+            WindowProcessController proc, Intent intent, int userId) {
+        if (proc == null) {
+            return;
+        }
+
+        final INotificationManager inm = NotificationManager.getService();
+        if (inm == null) {
+            return;
+        }
+
+        try {
+            Context context = mContext.createPackageContext(proc.mInfo.packageName, 0);
+            String text = mContext.getString(R.string.heavy_weight_notification,
+                    context.getApplicationInfo().loadLabel(context.getPackageManager()));
+            Notification notification =
+                    new Notification.Builder(context,
+                            SystemNotificationChannels.HEAVY_WEIGHT_APP)
+                            .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+                            .setWhen(0)
+                            .setOngoing(true)
+                            .setTicker(text)
+                            .setColor(mContext.getColor(
+                                    com.android.internal.R.color.system_notification_accent_color))
+                            .setContentTitle(text)
+                            .setContentText(
+                                    mContext.getText(R.string.heavy_weight_notification_detail))
+                            .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
+                                    intent, PendingIntent.FLAG_CANCEL_CURRENT, null,
+                                    new UserHandle(userId)))
+                            .build();
+            try {
+                inm.enqueueNotificationWithTag("android", "android", null,
+                        SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, notification, userId);
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Error showing notification for heavy-weight app", e);
+            } catch (RemoteException e) {
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.w(TAG, "Unable to create context for heavy notification", e);
+        }
+
+    }
+
+    IIntentSender getIntentSenderLocked(int type, String packageName, int callingUid, int userId,
+            IBinder token, String resultWho, int requestCode, Intent[] intents,
+            String[] resolvedTypes, int flags, Bundle bOptions) {
+
+        ActivityRecord activity = null;
+        if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
+            activity = ActivityRecord.isInStackLocked(token);
+            if (activity == null) {
+                Slog.w(TAG, "Failed createPendingResult: activity " + token + " not in any stack");
+                return null;
+            }
+            if (activity.finishing) {
+                Slog.w(TAG, "Failed createPendingResult: activity " + activity + " is finishing");
+                return null;
+            }
+        }
+
+        final PendingIntentRecord rec = mPendingIntentController.getIntentSender(type, packageName,
+                callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
+                bOptions);
+        final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
+        if (noCreate) {
+            return rec;
+        }
+        if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
+            if (activity.pendingResults == null) {
+                activity.pendingResults = new HashSet<>();
+            }
+            activity.pendingResults.add(rec.ref);
+        }
+        return rec;
+    }
+
+    // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
+    private void startTimeTrackingFocusedActivityLocked() {
+        final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
+        if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
+            mCurAppTimeTracker.start(resumedActivity.packageName);
+        }
+    }
+
+    private void updateResumedAppTrace(@Nullable ActivityRecord resumed) {
+        if (mTracedResumedActivity != null) {
+            Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER,
+                    constructResumedTraceName(mTracedResumedActivity.packageName), 0);
+        }
+        if (resumed != null) {
+            Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+                    constructResumedTraceName(resumed.packageName), 0);
+        }
+        mTracedResumedActivity = resumed;
+    }
+
+    private String constructResumedTraceName(String packageName) {
+        return "focused app: " + packageName;
+    }
+
+    /** Applies latest configuration and/or visibility updates if needed. */
+    private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
+        boolean kept = true;
+        final ActivityStack mainStack = mStackSupervisor.getTopDisplayFocusedStack();
+        // mainStack is null during startup.
+        if (mainStack != null) {
+            if (changes != 0 && starting == null) {
+                // If the configuration changed, and the caller is not already
+                // in the process of starting an activity, then find the top
+                // activity to check if its configuration needs to change.
+                starting = mainStack.topRunningActivityLocked();
+            }
+
+            if (starting != null) {
+                kept = starting.ensureActivityConfiguration(changes,
+                        false /* preserveWindow */);
+                // And we need to make sure at this point that all other activities
+                // are made visible with the correct configuration.
+                mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
+                        !PRESERVE_WINDOWS);
+            }
+        }
+
+        return kept;
+    }
+
+    void scheduleAppGcsLocked() {
+        mH.post(() -> mAmInternal.scheduleAppGcs());
+    }
+
+    CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
+        return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
+    }
+
+    /**
+     * Returns the PackageManager. Used by classes hosted by {@link ActivityTaskManagerService}. The
+     * PackageManager could be unavailable at construction time and therefore needs to be accessed
+     * on demand.
+     */
+    IPackageManager getPackageManager() {
+        return AppGlobals.getPackageManager();
+    }
+
+    PackageManagerInternal getPackageManagerInternalLocked() {
+        if (mPmInternal == null) {
+            mPmInternal = LocalServices.getService(PackageManagerInternal.class);
+        }
+        return mPmInternal;
+    }
+
+    AppWarnings getAppWarningsLocked() {
+        return mAppWarnings;
+    }
+
+    Intent getHomeIntent() {
+        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
+        intent.setComponent(mTopComponent);
+        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
+        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
+            intent.addCategory(Intent.CATEGORY_HOME);
+        }
+        return intent;
+    }
+
+    ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
+        if (info == null) return null;
+        ApplicationInfo newInfo = new ApplicationInfo(info);
+        newInfo.initForUser(userId);
+        return newInfo;
+    }
+
+    WindowProcessController getProcessController(String processName, int uid) {
+        if (uid == SYSTEM_UID) {
+            // The system gets to run in any process. If there are multiple processes with the same
+            // uid, just pick the first (this should never happen).
+            final SparseArray<WindowProcessController> procs =
+                    mProcessNames.getMap().get(processName);
+            if (procs == null) return null;
+            final int procCount = procs.size();
+            for (int i = 0; i < procCount; i++) {
+                final int procUid = procs.keyAt(i);
+                if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
+                    // Don't use an app process or different user process for system component.
+                    continue;
+                }
+                return procs.valueAt(i);
+            }
+        }
+
+        return mProcessNames.get(processName, uid);
+    }
+
+    WindowProcessController getProcessController(IApplicationThread thread) {
+        if (thread == null) {
+            return null;
+        }
+
+        final IBinder threadBinder = thread.asBinder();
+        final ArrayMap<String, SparseArray<WindowProcessController>> pmap = mProcessNames.getMap();
+        for (int i = pmap.size()-1; i >= 0; i--) {
+            final SparseArray<WindowProcessController> procs = pmap.valueAt(i);
+            for (int j = procs.size() - 1; j >= 0; j--) {
+                final WindowProcessController proc = procs.valueAt(j);
+                if (proc.hasThread() && proc.getThread().asBinder() == threadBinder) {
+                    return proc;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    int getUidStateLocked(int uid) {
+        return mActiveUids.get(uid, PROCESS_STATE_NONEXISTENT);
+    }
+
+    /**
+     * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on
+     * the whitelist
+     */
+    String getPendingTempWhitelistTagForUidLocked(int uid) {
+        return mPendingTempWhitelist.get(uid);
+    }
+
+    void logAppTooSlow(WindowProcessController app, long startTime, String msg) {
+        if (true || Build.IS_USER) {
+            return;
+        }
+
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        StrictMode.allowThreadDiskWrites();
+        try {
+            File tracesDir = new File("/data/anr");
+            File tracesFile = null;
+            try {
+                tracesFile = File.createTempFile("app_slow", null, tracesDir);
+
+                StringBuilder sb = new StringBuilder();
+                Time tobj = new Time();
+                tobj.set(System.currentTimeMillis());
+                sb.append(tobj.format("%Y-%m-%d %H:%M:%S"));
+                sb.append(": ");
+                TimeUtils.formatDuration(SystemClock.uptimeMillis()-startTime, sb);
+                sb.append(" since ");
+                sb.append(msg);
+                FileOutputStream fos = new FileOutputStream(tracesFile);
+                fos.write(sb.toString().getBytes());
+                if (app == null) {
+                    fos.write("\n*** No application process!".getBytes());
+                }
+                fos.close();
+                FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw-
+            } catch (IOException e) {
+                Slog.w(TAG, "Unable to prepare slow app traces file: " + tracesFile, e);
+                return;
+            }
+
+            if (app != null && app.getPid() > 0) {
+                ArrayList<Integer> firstPids = new ArrayList<Integer>();
+                firstPids.add(app.getPid());
+                dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null);
+            }
+
+            File lastTracesFile = null;
+            File curTracesFile = null;
+            for (int i=9; i>=0; i--) {
+                String name = String.format(Locale.US, "slow%02d.txt", i);
+                curTracesFile = new File(tracesDir, name);
+                if (curTracesFile.exists()) {
+                    if (lastTracesFile != null) {
+                        curTracesFile.renameTo(lastTracesFile);
+                    } else {
+                        curTracesFile.delete();
+                    }
+                }
+                lastTracesFile = curTracesFile;
+            }
+            tracesFile.renameTo(curTracesFile);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+    }
+
+    final class H extends Handler {
+        static final int REPORT_TIME_TRACKER_MSG = 1;
+        static final int FIRST_ACTIVITY_STACK_MSG = 100;
+        static final int FIRST_SUPERVISOR_STACK_MSG = 200;
+
+        public H() {
+            super(DisplayThread.get().getLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case REPORT_TIME_TRACKER_MSG: {
+                    AppTimeTracker tracker = (AppTimeTracker) msg.obj;
+                    tracker.deliverResult(mContext);
+                } break;
+            }
+        }
+    }
+
+    final class UiHandler extends Handler {
+        static final int DISMISS_DIALOG_UI_MSG = 1;
+
+        public UiHandler() {
+            super(UiThread.get().getLooper(), null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case DISMISS_DIALOG_UI_MSG: {
+                    final Dialog d = (Dialog) msg.obj;
+                    d.dismiss();
+                    break;
+                }
+            }
+        }
+    }
+
+    final class LocalService extends ActivityTaskManagerInternal {
+        @Override
+        public SleepToken acquireSleepToken(String tag, int displayId) {
+            Preconditions.checkNotNull(tag);
+            return ActivityTaskManagerService.this.acquireSleepToken(tag, displayId);
+        }
+
+        @Override
+        public ComponentName getHomeActivityForUser(int userId) {
+            synchronized (mGlobalLock) {
+                ActivityRecord homeActivity =
+                        mStackSupervisor.getDefaultDisplayHomeActivityForUser(userId);
+                return homeActivity == null ? null : homeActivity.realActivity;
+            }
+        }
+
+        @Override
+        public void onLocalVoiceInteractionStarted(IBinder activity,
+                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+            synchronized (mGlobalLock) {
+                onLocalVoiceInteractionStartedLocked(activity, voiceSession, voiceInteractor);
+            }
+        }
+
+        @Override
+        public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
+                        reasons, timestamp);
+            }
+        }
+
+        @Override
+        public void notifyAppTransitionFinished() {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.notifyAppTransitionDone();
+            }
+        }
+
+        @Override
+        public void notifyAppTransitionCancelled() {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.notifyAppTransitionDone();
+            }
+        }
+
+        @Override
+        public List<IBinder> getTopVisibleActivities() {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.getTopVisibleActivities();
+            }
+        }
+
+        @Override
+        public void notifyDockedStackMinimizedChanged(boolean minimized) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.setDockedStackMinimized(minimized);
+            }
+        }
+
+        @Override
+        public int startActivitiesAsPackage(String packageName, int userId, Intent[] intents,
+                Bundle bOptions) {
+            Preconditions.checkNotNull(intents, "intents");
+            final String[] resolvedTypes = new String[intents.length];
+
+            // UID of the package on user userId.
+            // "= 0" is needed because otherwise catch(RemoteException) would make it look like
+            // packageUid may not be initialized.
+            int packageUid = 0;
+            final long ident = Binder.clearCallingIdentity();
+
+            try {
+                for (int i = 0; i < intents.length; i++) {
+                    resolvedTypes[i] =
+                            intents[i].resolveTypeIfNeeded(mContext.getContentResolver());
+                }
+
+                packageUid = AppGlobals.getPackageManager().getPackageUid(
+                        packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
+            } catch (RemoteException e) {
+                // Shouldn't happen.
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+
+            synchronized (mGlobalLock) {
+                return getActivityStartController().startActivitiesInPackage(
+                        packageUid, packageName,
+                        intents, resolvedTypes, null /* resultTo */,
+                        SafeActivityOptions.fromBundle(bOptions), userId,
+                        false /* validateIncomingUser */, null /* originatingPendingIntent */);
+            }
+        }
+
+        @Override
+        public int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
+                String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+                boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent) {
+            synchronized (mGlobalLock) {
+                return getActivityStartController().startActivitiesInPackage(uid, callingPackage,
+                        intents, resolvedTypes, resultTo, options, userId, validateIncomingUser,
+                        originatingPendingIntent);
+            }
+        }
+
+        @Override
+        public int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
+                String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
+                String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
+                int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
+                PendingIntentRecord originatingPendingIntent) {
+            synchronized (mGlobalLock) {
+                return getActivityStartController().startActivityInPackage(uid, realCallingPid,
+                        realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho,
+                        requestCode, startFlags, options, userId, inTask, reason,
+                        validateIncomingUser, originatingPendingIntent);
+            }
+        }
+
+        @Override
+        public int startActivityAsUser(IApplicationThread caller, String callerPacakge,
+                Intent intent, Bundle options, int userId) {
+            return ActivityTaskManagerService.this.startActivityAsUser(
+                    caller, callerPacakge, intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, userId,
+                    false /*validateIncomingUser*/);
+        }
+
+        @Override
+        public void notifyKeyguardFlagsChanged(@Nullable Runnable callback, int displayId) {
+            synchronized (mGlobalLock) {
+
+                // We might change the visibilities here, so prepare an empty app transition which
+                // might be overridden later if we actually change visibilities.
+                final DisplayWindowController dwc = mStackSupervisor.getActivityDisplay(displayId)
+                        .getWindowContainerController();
+                final boolean wasTransitionSet = dwc.getPendingAppTransition() != TRANSIT_NONE;
+                if (!wasTransitionSet) {
+                    dwc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */);
+                }
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+
+                // If there was a transition set already we don't want to interfere with it as we
+                // might be starting it too early.
+                if (!wasTransitionSet) {
+                    dwc.executeAppTransition();
+                }
+            }
+            if (callback != null) {
+                callback.run();
+            }
+        }
+
+        @Override
+        public void notifyKeyguardTrustedChanged() {
+            synchronized (mGlobalLock) {
+                if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
+                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                }
+            }
+        }
+
+        /**
+         * Called after virtual display Id is updated by
+         * {@link com.android.server.vr.Vr2dDisplay} with a specific
+         * {@param vrVr2dDisplayId}.
+         */
+        @Override
+        public void setVr2dDisplayId(int vr2dDisplayId) {
+            if (DEBUG_STACK) Slog.d(TAG, "setVr2dDisplayId called for: " + vr2dDisplayId);
+            synchronized (mGlobalLock) {
+                mVr2dDisplayId = vr2dDisplayId;
+            }
+        }
+
+        @Override
+        public void setFocusedActivity(IBinder token) {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+                if (r == null) {
+                    throw new IllegalArgumentException(
+                            "setFocusedActivity: No activity record matching token=" + token);
+                }
+                if (r.moveFocusableActivityToTop("setFocusedActivity")) {
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                }
+            }
+        }
+
+        @Override
+        public void registerScreenObserver(ScreenObserver observer) {
+            mScreenObservers.add(observer);
+        }
+
+        @Override
+        public boolean isCallerRecents(int callingUid) {
+            return getRecentTasks().isCallerRecents(callingUid);
+        }
+
+        @Override
+        public boolean isRecentsComponentHomeActivity(int userId) {
+            return getRecentTasks().isRecentsComponentHomeActivity(userId);
+        }
+
+        @Override
+        public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+            ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeStackPosition);
+        }
+
+        @Override
+        public void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
+            ActivityTaskManagerService.this.enforceCallerIsRecentsOrHasPermission(permission, func);
+        }
+
+        @Override
+        public void notifyActiveVoiceInteractionServiceChanged(ComponentName component) {
+            synchronized (mGlobalLock) {
+                mActiveVoiceInteractionServiceComponent = component;
+            }
+        }
+
+        @Override
+        public void setAllowAppSwitches(@NonNull String type, int uid, int userId) {
+            if (!mAmInternal.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) {
+                return;
+            }
+            synchronized (mGlobalLock) {
+                ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(userId);
+                if (types == null) {
+                    if (uid < 0) {
+                        return;
+                    }
+                    types = new ArrayMap<>();
+                    mAllowAppSwitchUids.put(userId, types);
+                }
+                if (uid < 0) {
+                    types.remove(type);
+                } else {
+                    types.put(type, uid);
+                }
+            }
+        }
+
+        @Override
+        public void onUserStopped(int userId) {
+            synchronized (mGlobalLock) {
+                getRecentTasks().unloadUserDataFromMemoryLocked(userId);
+                mAllowAppSwitchUids.remove(userId);
+            }
+        }
+
+        @Override
+        public boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
+            synchronized (mGlobalLock) {
+                return ActivityTaskManagerService.this.isGetTasksAllowed(
+                        caller, callingPid, callingUid);
+            }
+        }
+
+        @Override
+        public void onProcessAdded(WindowProcessController proc) {
+            synchronized (mGlobalLock) {
+                mProcessNames.put(proc.mName, proc.mUid, proc);
+            }
+        }
+
+        @Override
+        public void onProcessRemoved(String name, int uid) {
+            synchronized (mGlobalLock) {
+                mProcessNames.remove(name, uid);
+            }
+        }
+
+        @Override
+        public void onCleanUpApplicationRecord(WindowProcessController proc) {
+            synchronized (mGlobalLock) {
+                if (proc == mHomeProcess) {
+                    mHomeProcess = null;
+                }
+                if (proc == mPreviousProcess) {
+                    mPreviousProcess = null;
+                }
+            }
+        }
+
+        @Override
+        public int getTopProcessState() {
+            synchronized (mGlobalLock) {
+                return mTopProcessState;
+            }
+        }
+
+        @Override
+        public boolean isHeavyWeightProcess(WindowProcessController proc) {
+            synchronized (mGlobalLock) {
+                return proc == mHeavyWeightProcess;
+            }
+        }
+
+        @Override
+        public void clearHeavyWeightProcessIfEquals(WindowProcessController proc) {
+            synchronized (mGlobalLock) {
+                ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(proc);
+            }
+        }
+
+        @Override
+        public void finishHeavyWeightApp() {
+            synchronized (mGlobalLock) {
+                if (mHeavyWeightProcess != null) {
+                    mHeavyWeightProcess.finishActivities();
+                }
+                ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(
+                        mHeavyWeightProcess);
+            }
+        }
+
+        @Override
+        public boolean isSleeping() {
+            synchronized (mGlobalLock) {
+                return isSleepingLocked();
+            }
+        }
+
+        @Override
+        public boolean isShuttingDown() {
+            synchronized (mGlobalLock) {
+                return mShuttingDown;
+            }
+        }
+
+        @Override
+        public boolean shuttingDown(boolean booted, int timeout) {
+            synchronized (mGlobalLock) {
+                mShuttingDown = true;
+                mStackSupervisor.prepareForShutdownLocked();
+                updateEventDispatchingLocked(booted);
+                notifyTaskPersisterLocked(null, true);
+                return mStackSupervisor.shutdownLocked(timeout);
+            }
+        }
+
+        @Override
+        public void enableScreenAfterBoot(boolean booted) {
+            synchronized (mGlobalLock) {
+                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
+                        SystemClock.uptimeMillis());
+                mWindowManager.enableScreenAfterBoot();
+                updateEventDispatchingLocked(booted);
+            }
+        }
+
+        @Override
+        public boolean showStrictModeViolationDialog() {
+            synchronized (mGlobalLock) {
+                return mShowDialogs && !mSleeping && !mShuttingDown;
+            }
+        }
+
+        @Override
+        public void showSystemReadyErrorDialogsIfNeeded() {
+            synchronized (mGlobalLock) {
+                try {
+                    if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
+                        Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your"
+                                + " data partition or your device will be unstable.");
+                        mUiHandler.post(() -> {
+                            if (mShowDialogs) {
+                                AlertDialog d = new BaseErrorDialog(mUiContext);
+                                d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+                                d.setCancelable(false);
+                                d.setTitle(mUiContext.getText(R.string.android_system_label));
+                                d.setMessage(mUiContext.getText(R.string.system_error_wipe_data));
+                                d.setButton(DialogInterface.BUTTON_POSITIVE,
+                                        mUiContext.getText(R.string.ok),
+                                        mUiHandler.obtainMessage(DISMISS_DIALOG_UI_MSG, d));
+                                d.show();
+                            }
+                        });
+                    }
+                } catch (RemoteException e) {
+                }
+
+                if (!Build.isBuildConsistent()) {
+                    Slog.e(TAG, "Build fingerprint is not consistent, warning user");
+                    mUiHandler.post(() -> {
+                        if (mShowDialogs) {
+                            AlertDialog d = new BaseErrorDialog(mUiContext);
+                            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+                            d.setCancelable(false);
+                            d.setTitle(mUiContext.getText(R.string.android_system_label));
+                            d.setMessage(mUiContext.getText(R.string.system_error_manufacturer));
+                            d.setButton(DialogInterface.BUTTON_POSITIVE,
+                                    mUiContext.getText(R.string.ok),
+                                    mUiHandler.obtainMessage(DISMISS_DIALOG_UI_MSG, d));
+                            d.show();
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onProcessMapped(int pid, WindowProcessController proc) {
+            synchronized (mGlobalLock) {
+                mPidMap.put(pid, proc);
+            }
+        }
+
+        @Override
+        public void onProcessUnMapped(int pid) {
+            synchronized (mGlobalLock) {
+                mPidMap.remove(pid);
+            }
+        }
+
+        @Override
+        public void onPackageDataCleared(String name) {
+            synchronized (mGlobalLock) {
+                mCompatModePackages.handlePackageDataClearedLocked(name);
+                mAppWarnings.onPackageDataCleared(name);
+            }
+        }
+
+        @Override
+        public void onPackageUninstalled(String name) {
+            synchronized (mGlobalLock) {
+                mAppWarnings.onPackageUninstalled(name);
+                mCompatModePackages.handlePackageUninstalledLocked(name);
+            }
+        }
+
+        @Override
+        public void onPackageAdded(String name, boolean replacing) {
+            synchronized (mGlobalLock) {
+                mCompatModePackages.handlePackageAddedLocked(name, replacing);
+            }
+        }
+
+        @Override
+        public void onPackageReplaced(ApplicationInfo aInfo) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
+            }
+        }
+
+        @Override
+        public CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
+            synchronized (mGlobalLock) {
+                return compatibilityInfoForPackageLocked(ai);
+            }
+        }
+
+        /**
+         * Set the corresponding display information for the process global configuration. To be
+         * called when we need to show IME on a different display.
+         *
+         * @param pid The process id associated with the IME window.
+         * @param displayId The ID of the display showing the IME.
+         */
+        @Override
+        public void onImeWindowSetOnDisplay(final int pid, final int displayId) {
+            if (pid == MY_PID || pid < 0) {
+                if (DEBUG_CONFIGURATION) {
+                    Slog.w(TAG,
+                            "Trying to update display configuration for system/invalid process.");
+                }
+                return;
+            }
+            mH.post(() -> {
+                synchronized (mGlobalLock) {
+                    final ActivityDisplay activityDisplay =
+                            mStackSupervisor.getActivityDisplay(displayId);
+                    if (activityDisplay == null) {
+                        // Call might come when display is not yet added or has been removed.
+                        if (DEBUG_CONFIGURATION) {
+                            Slog.w(TAG, "Trying to update display configuration for non-existing "
+                                    + "displayId=" + displayId);
+                        }
+                        return;
+                    }
+                    final WindowProcessController process = mPidMap.get(pid);
+                    if (process == null) {
+                        if (DEBUG_CONFIGURATION) {
+                            Slog.w(TAG, "Trying to update display configuration for invalid "
+                                    + "process, pid=" + pid);
+                        }
+                        return;
+                    }
+                    process.registerDisplayConfigurationListenerLocked(activityDisplay);
+                }
+            });
+
+        }
+
+        @Override
+        public void sendActivityResult(int callingUid, IBinder activityToken, String resultWho,
+                int requestCode, int resultCode, Intent data) {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
+                if (r != null && r.getStack() != null) {
+                    r.getStack().sendActivityResultLocked(callingUid, r, resultWho, requestCode,
+                            resultCode, data);
+                }
+            }
+        }
+
+        @Override
+        public void clearPendingResultForActivity(IBinder activityToken,
+                WeakReference<PendingIntentRecord> pir) {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
+                if (r != null && r.pendingResults != null) {
+                    r.pendingResults.remove(pir);
+                }
+            }
+        }
+
+        @Override
+        public IIntentSender getIntentSender(int type, String packageName,
+                int callingUid, int userId, IBinder token, String resultWho,
+                int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
+                Bundle bOptions) {
+            synchronized (mGlobalLock) {
+                return getIntentSenderLocked(type, packageName, callingUid, userId, token,
+                        resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
+            }
+        }
+
+        @Override
+        public ActivityServiceConnectionsHolder getServiceConnectionsHolder(IBinder token) {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return null;
+                }
+                if (r.mServiceConnectionsHolder == null) {
+                    r.mServiceConnectionsHolder = new ActivityServiceConnectionsHolder(
+                            ActivityTaskManagerService.this, r);
+                }
+
+                return r.mServiceConnectionsHolder;
+            }
+        }
+
+        @Override
+        public Intent getHomeIntent() {
+            synchronized (mGlobalLock) {
+                return ActivityTaskManagerService.this.getHomeIntent();
+            }
+        }
+
+        @Override
+        public boolean startHomeActivity(int userId, String reason) {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY);
+            }
+        }
+
+        @Override
+        public boolean startHomeOnAllDisplays(int userId, String reason) {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.startHomeOnAllDisplays(userId, reason);
+            }
+        }
+
+        @Override
+        public boolean isFactoryTestProcess(WindowProcessController wpc) {
+            synchronized (mGlobalLock) {
+                if (mFactoryTest == FACTORY_TEST_OFF) {
+                    return false;
+                }
+                if (mFactoryTest == FACTORY_TEST_LOW_LEVEL && mTopComponent != null
+                        && wpc.mName.equals(mTopComponent.getPackageName())) {
+                    return true;
+                }
+                return mFactoryTest == FACTORY_TEST_HIGH_LEVEL
+                        && (wpc.mInfo.flags & FLAG_FACTORY_TEST) != 0;
+            }
+        }
+
+        @Override
+        public void updateTopComponentForFactoryTest() {
+            synchronized (mGlobalLock) {
+                if (mFactoryTest != FACTORY_TEST_LOW_LEVEL) {
+                    return;
+                }
+                final ResolveInfo ri = mContext.getPackageManager()
+                        .resolveActivity(new Intent(Intent.ACTION_FACTORY_TEST), STOCK_PM_FLAGS);
+                final CharSequence errorMsg;
+                if (ri != null) {
+                    final ActivityInfo ai = ri.activityInfo;
+                    final ApplicationInfo app = ai.applicationInfo;
+                    if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                        mTopAction = Intent.ACTION_FACTORY_TEST;
+                        mTopData = null;
+                        mTopComponent = new ComponentName(app.packageName, ai.name);
+                        errorMsg = null;
+                    } else {
+                        errorMsg = mContext.getResources().getText(
+                                com.android.internal.R.string.factorytest_not_system);
+                    }
+                } else {
+                    errorMsg = mContext.getResources().getText(
+                            com.android.internal.R.string.factorytest_no_action);
+                }
+                if (errorMsg == null) {
+                    return;
+                }
+
+                mTopAction = null;
+                mTopData = null;
+                mTopComponent = null;
+                mUiHandler.post(() -> {
+                    Dialog d = new FactoryErrorDialog(mUiContext, errorMsg);
+                    d.show();
+                    mAmInternal.ensureBootCompleted();
+                });
+            }
+        }
+
+        @Override
+        public void handleAppDied(WindowProcessController wpc, boolean restarting,
+                Runnable finishInstrumentationCallback) {
+            synchronized (mGlobalLock) {
+                // Remove this application's activities from active lists.
+                boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(wpc);
+
+                wpc.clearRecentTasks();
+                wpc.clearActivities();
+
+                if (wpc.isInstrumenting()) {
+                    finishInstrumentationCallback.run();
+                }
+
+                mWindowManager.deferSurfaceLayout();
+                try {
+                    if (!restarting && hasVisibleActivities
+                            && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) {
+                        // If there was nothing to resume, and we are not already restarting this
+                        // process, but there is a visible activity that is hosted by the process...
+                        // then make sure all visible activities are running, taking care of
+                        // restarting this process.
+                        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                    }
+                } finally {
+                    mWindowManager.continueSurfaceLayout();
+                }
+            }
+        }
+
+        @Override
+        public void closeSystemDialogs(String reason) {
+            enforceNotIsolatedCaller("closeSystemDialogs");
+
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                synchronized (mGlobalLock) {
+                    // Only allow this from foreground processes, so that background
+                    // applications can't abuse it to prevent system UI from being shown.
+                    if (uid >= FIRST_APPLICATION_UID) {
+                        final WindowProcessController proc = mPidMap.get(pid);
+                        if (!proc.isPerceptible()) {
+                            Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
+                                    + " from background process " + proc);
+                            return;
+                        }
+                    }
+                    mWindowManager.closeSystemDialogs(reason);
+
+                    mStackSupervisor.closeSystemDialogsLocked();
+                }
+                // Call into AM outside the synchronized block.
+                mAmInternal.broadcastCloseSystemDialogs(reason);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+
+        @Override
+        public void cleanupDisabledPackageComponents(
+                String packageName, Set<String> disabledClasses, int userId, boolean booted) {
+            synchronized (mGlobalLock) {
+                // Clean-up disabled activities.
+                if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
+                        packageName, disabledClasses, true, false, userId) && booted) {
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mStackSupervisor.scheduleIdleLocked();
+                }
+
+                // Clean-up disabled tasks
+                getRecentTasks().cleanupDisabledPackageTasksLocked(
+                        packageName, disabledClasses, userId);
+            }
+        }
+
+        @Override
+        public boolean onForceStopPackage(String packageName, boolean doit, boolean evenPersistent,
+                int userId) {
+            synchronized (mGlobalLock) {
+
+                boolean didSomething =
+                        getActivityStartController().clearPendingActivityLaunches(packageName);
+                didSomething |= mStackSupervisor.finishDisabledPackageActivitiesLocked(packageName,
+                        null, doit, evenPersistent, userId);
+                return didSomething;
+            }
+        }
+
+        @Override
+        public void resumeTopActivities(boolean scheduleIdle) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                if (scheduleIdle) {
+                    mStackSupervisor.scheduleIdleLocked();
+                }
+            }
+        }
+
+        @Override
+        public void preBindApplication(WindowProcessController wpc) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(wpc.mInfo);
+            }
+        }
+
+        @Override
+        public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.attachApplicationLocked(wpc);
+            }
+        }
+
+        @Override
+        public void notifyLockedProfile(@UserIdInt int userId, int currentUserId) {
+            try {
+                if (!AppGlobals.getPackageManager().isUidPrivileged(Binder.getCallingUid())) {
+                    throw new SecurityException("Only privileged app can call notifyLockedProfile");
+                }
+            } catch (RemoteException ex) {
+                throw new SecurityException("Fail to check is caller a privileged app", ex);
+            }
+
+            synchronized (mGlobalLock) {
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    if (mAmInternal.shouldConfirmCredentials(userId)) {
+                        if (mKeyguardController.isKeyguardLocked()) {
+                            // Showing launcher to avoid user entering credential twice.
+                            startHomeActivity(currentUserId, "notifyLockedProfile");
+                        }
+                        mStackSupervisor.lockAllProfileTasks(userId);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+
+        @Override
+        public void startConfirmDeviceCredentialIntent(Intent intent, Bundle options) {
+            mAmInternal.enforceCallingPermission(
+                    MANAGE_ACTIVITY_STACKS, "startConfirmDeviceCredentialIntent");
+
+            synchronized (mGlobalLock) {
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
+                            FLAG_ACTIVITY_TASK_ON_HOME);
+                    ActivityOptions activityOptions = options != null
+                            ? new ActivityOptions(options) : ActivityOptions.makeBasic();
+                    final ActivityRecord homeActivity =
+                            mStackSupervisor.getDefaultDisplayHomeActivity();
+                    if (homeActivity != null) {
+                        activityOptions.setLaunchTaskId(homeActivity.getTask().taskId);
+                    }
+                    mContext.startActivityAsUser(intent, activityOptions.toBundle(),
+                            UserHandle.CURRENT);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+
+        @Override
+        public void writeActivitiesToProto(ProtoOutputStream proto) {
+            synchronized (mGlobalLock) {
+                // The output proto of "activity --proto activities"
+                // is ActivityManagerServiceDumpActivitiesProto
+                mStackSupervisor.writeToProto(proto,
+                        ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
+            }
+        }
+
+        @Override
+        public void saveANRState(String reason) {
+            synchronized (mGlobalLock) {
+                final StringWriter sw = new StringWriter();
+                final PrintWriter pw = new FastPrintWriter(sw, false, 1024);
+                pw.println("  ANR time: " + DateFormat.getDateTimeInstance().format(new Date()));
+                if (reason != null) {
+                    pw.println("  Reason: " + reason);
+                }
+                pw.println();
+                getActivityStartController().dump(pw, "  ", null);
+                pw.println();
+                pw.println("-------------------------------------------------------------------------------");
+                dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
+                        true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */,
+                        "" /* header */);
+                pw.println();
+                pw.close();
+
+                mLastANRState = sw.toString();
+            }
+        }
+
+        @Override
+        public void clearSavedANRState() {
+            synchronized (mGlobalLock) {
+                mLastANRState = null;
+            }
+        }
+
+        @Override
+        public void dump(String cmd, FileDescriptor fd, PrintWriter pw, String[] args, int opti,
+                boolean dumpAll, boolean dumpClient, String dumpPackage) {
+            synchronized (mGlobalLock) {
+                if (DUMP_ACTIVITIES_CMD.equals(cmd) || DUMP_ACTIVITIES_SHORT_CMD.equals(cmd)) {
+                    dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+                } else if (DUMP_LASTANR_CMD.equals(cmd)) {
+                    dumpLastANRLocked(pw);
+                } else if (DUMP_LASTANR_TRACES_CMD.equals(cmd)) {
+                    dumpLastANRTracesLocked(pw);
+                } else if (DUMP_STARTER_CMD.equals(cmd)) {
+                    dumpActivityStarterLocked(pw, dumpPackage);
+                } else if (DUMP_CONTAINERS_CMD.equals(cmd)) {
+                    dumpActivityContainersLocked(pw);
+                } else if (DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd)) {
+                    if (getRecentTasks() != null) {
+                        getRecentTasks().dump(pw, dumpAll, dumpPackage);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean dumpForProcesses(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+                String dumpPackage, int dumpAppId, boolean needSep, boolean testPssMode,
+                int wakefulness) {
+            synchronized (mGlobalLock) {
+                if (mHomeProcess != null && (dumpPackage == null
+                        || mHomeProcess.mPkgList.contains(dumpPackage))) {
+                    if (needSep) {
+                        pw.println();
+                        needSep = false;
+                    }
+                    pw.println("  mHomeProcess: " + mHomeProcess);
+                }
+                if (mPreviousProcess != null && (dumpPackage == null
+                        || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+                    if (needSep) {
+                        pw.println();
+                        needSep = false;
+                    }
+                    pw.println("  mPreviousProcess: " + mPreviousProcess);
+                }
+                if (dumpAll && (mPreviousProcess == null || dumpPackage == null
+                        || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+                    StringBuilder sb = new StringBuilder(128);
+                    sb.append("  mPreviousProcessVisibleTime: ");
+                    TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb);
+                    pw.println(sb);
+                }
+                if (mHeavyWeightProcess != null && (dumpPackage == null
+                        || mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
+                    if (needSep) {
+                        pw.println();
+                        needSep = false;
+                    }
+                    pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
+                }
+                if (dumpPackage == null) {
+                    pw.println("  mGlobalConfiguration: " + getGlobalConfiguration());
+                    mStackSupervisor.dumpDisplayConfigs(pw, "  ");
+                }
+                if (dumpAll) {
+                    if (dumpPackage == null) {
+                        pw.println("  mConfigWillChange: "
+                                + getTopDisplayFocusedStack().mConfigWillChange);
+                    }
+                    if (mCompatModePackages.getPackages().size() > 0) {
+                        boolean printed = false;
+                        for (Map.Entry<String, Integer> entry
+                                : mCompatModePackages.getPackages().entrySet()) {
+                            String pkg = entry.getKey();
+                            int mode = entry.getValue();
+                            if (dumpPackage != null && !dumpPackage.equals(pkg)) {
+                                continue;
+                            }
+                            if (!printed) {
+                                pw.println("  mScreenCompatPackages:");
+                                printed = true;
+                            }
+                            pw.println("    " + pkg + ": " + mode);
+                        }
+                    }
+                }
+
+                if (dumpPackage == null) {
+                    pw.println("  mWakefulness="
+                            + PowerManagerInternal.wakefulnessToString(wakefulness));
+                    pw.println("  mSleepTokens=" + mStackSupervisor.mSleepTokens);
+                    if (mRunningVoice != null) {
+                        pw.println("  mRunningVoice=" + mRunningVoice);
+                        pw.println("  mVoiceWakeLock" + mVoiceWakeLock);
+                    }
+                    pw.println("  mSleeping=" + mSleeping);
+                    pw.println("  mShuttingDown=" + mShuttingDown + " mTestPssMode=" + testPssMode);
+                    pw.println("  mVrController=" + mVrController);
+                }
+                if (mCurAppTimeTracker != null) {
+                    mCurAppTimeTracker.dumpWithHeader(pw, "  ", true);
+                }
+                if (mAllowAppSwitchUids.size() > 0) {
+                    boolean printed = false;
+                    for (int i = 0; i < mAllowAppSwitchUids.size(); i++) {
+                        ArrayMap<String, Integer> types = mAllowAppSwitchUids.valueAt(i);
+                        for (int j = 0; j < types.size(); j++) {
+                            if (dumpPackage == null ||
+                                    UserHandle.getAppId(types.valueAt(j).intValue()) == dumpAppId) {
+                                if (needSep) {
+                                    pw.println();
+                                    needSep = false;
+                                }
+                                if (!printed) {
+                                    pw.println("  mAllowAppSwitchUids:");
+                                    printed = true;
+                                }
+                                pw.print("    User ");
+                                pw.print(mAllowAppSwitchUids.keyAt(i));
+                                pw.print(": Type ");
+                                pw.print(types.keyAt(j));
+                                pw.print(" = ");
+                                UserHandle.formatUid(pw, types.valueAt(j).intValue());
+                                pw.println();
+                            }
+                        }
+                    }
+                }
+                if (dumpPackage == null) {
+                    if (mController != null) {
+                        pw.println("  mController=" + mController
+                                + " mControllerIsAMonkey=" + mControllerIsAMonkey);
+                    }
+                    pw.println("  mGoingToSleep=" + mStackSupervisor.mGoingToSleep);
+                    pw.println("  mLaunchingActivity=" + mStackSupervisor.mLaunchingActivity);
+                }
+
+                return needSep;
+            }
+        }
+
+        @Override
+        public void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage) {
+            synchronized (mGlobalLock) {
+                if (dumpPackage == null) {
+                    getGlobalConfiguration().writeToProto(proto, GLOBAL_CONFIGURATION);
+                    proto.write(CONFIG_WILL_CHANGE, getTopDisplayFocusedStack().mConfigWillChange);
+                    writeSleepStateToProto(proto);
+                    if (mController != null) {
+                        final long token = proto.start(CONTROLLER);
+                        proto.write(CONTROLLER, mController.toString());
+                        proto.write(IS_A_MONKEY, mControllerIsAMonkey);
+                        proto.end(token);
+                    }
+                    mStackSupervisor.mGoingToSleep.writeToProto(proto, GOING_TO_SLEEP);
+                    mStackSupervisor.mLaunchingActivity.writeToProto(proto, LAUNCHING_ACTIVITY);
+                }
+
+                if (mHomeProcess != null && (dumpPackage == null
+                        || mHomeProcess.mPkgList.contains(dumpPackage))) {
+                    mHomeProcess.writeToProto(proto, HOME_PROC);
+                }
+
+                if (mPreviousProcess != null && (dumpPackage == null
+                        || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+                    mPreviousProcess.writeToProto(proto, PREVIOUS_PROC);
+                    proto.write(PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
+                }
+
+                if (mHeavyWeightProcess != null && (dumpPackage == null
+                        || mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
+                    mHeavyWeightProcess.writeToProto(proto, HEAVY_WEIGHT_PROC);
+                }
+
+                for (Map.Entry<String, Integer> entry
+                        : mCompatModePackages.getPackages().entrySet()) {
+                    String pkg = entry.getKey();
+                    int mode = entry.getValue();
+                    if (dumpPackage == null || dumpPackage.equals(pkg)) {
+                        long compatToken = proto.start(SCREEN_COMPAT_PACKAGES);
+                        proto.write(PACKAGE, pkg);
+                        proto.write(MODE, mode);
+                        proto.end(compatToken);
+                    }
+                }
+
+                if (mCurAppTimeTracker != null) {
+                    mCurAppTimeTracker.writeToProto(proto, CURRENT_TRACKER, true);
+                }
+
+            }
+        }
+
+        @Override
+        public boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
+                String[] args, int opti, boolean dumpAll, boolean dumpVisibleStacksOnly,
+                boolean dumpFocusedStackOnly) {
+            synchronized (mGlobalLock) {
+                return ActivityTaskManagerService.this.dumpActivity(fd, pw, name, args, opti,
+                        dumpAll, dumpVisibleStacksOnly, dumpFocusedStackOnly);
+            }
+        }
+
+        @Override
+        public void dumpForOom(PrintWriter pw) {
+            synchronized (mGlobalLock) {
+                pw.println("  mHomeProcess: " + mHomeProcess);
+                pw.println("  mPreviousProcess: " + mPreviousProcess);
+                if (mHeavyWeightProcess != null) {
+                    pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
+                }
+            }
+        }
+
+        @Override
+        public boolean canGcNow() {
+            synchronized (mGlobalLock) {
+                return isSleeping() || mStackSupervisor.allResumedActivitiesIdle();
+            }
+        }
+
+        @Override
+        public WindowProcessController getTopApp() {
+            synchronized (mGlobalLock) {
+                final ActivityRecord top = mStackSupervisor.getTopResumedActivity();
+                return top != null ? top.app : null;
+            }
+        }
+
+        @Override
+        public void rankTaskLayersIfNeeded() {
+            synchronized (mGlobalLock) {
+                if (mStackSupervisor != null) {
+                    mStackSupervisor.rankTaskLayersIfNeeded();
+                }
+            }
+        }
+
+        @Override
+        public void scheduleDestroyAllActivities(String reason) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.scheduleDestroyAllActivities(null, reason);
+            }
+        }
+
+        @Override
+        public void removeUser(int userId) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.removeUserLocked(userId);
+            }
+        }
+
+        @Override
+        public boolean switchUser(int userId, UserState userState) {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.switchUserLocked(userId, userState);
+            }
+        }
+
+        @Override
+        public void onHandleAppCrash(WindowProcessController wpc) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.handleAppCrashLocked(wpc);
+            }
+        }
+
+        @Override
+        public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.finishTopCrashedActivitiesLocked(crashedApp, reason);
+            }
+        }
+
+        @Override
+        public void onUidActive(int uid, int procState) {
+            synchronized (mGlobalLock) {
+                mActiveUids.put(uid, procState);
+            }
+        }
+
+        @Override
+        public void onUidInactive(int uid) {
+            synchronized (mGlobalLock) {
+                mActiveUids.remove(uid);
+            }
+        }
+
+        @Override
+        public void onActiveUidsCleared() {
+            synchronized (mGlobalLock) {
+                mActiveUids.clear();
+            }
+        }
+
+        @Override
+        public void onUidProcStateChanged(int uid, int procState) {
+            synchronized (mGlobalLock) {
+                if (mActiveUids.get(uid) != null) {
+                    mActiveUids.put(uid, procState);
+                }
+            }
+        }
+
+        @Override
+        public void onUidAddedToPendingTempWhitelist(int uid, String tag) {
+            synchronized (mGlobalLock) {
+                mPendingTempWhitelist.put(uid, tag);
+            }
+        }
+
+        @Override
+        public void onUidRemovedFromPendingTempWhitelist(int uid) {
+            synchronized (mGlobalLock) {
+                mPendingTempWhitelist.remove(uid);
+            }
+        }
+
+        @Override
+        public boolean handleAppCrashInActivityController(String processName, int pid,
+                String shortMsg, String longMsg, long timeMillis, String stackTrace,
+                Runnable killCrashingAppCallback) {
+            synchronized (mGlobalLock) {
+                if (mController == null) {
+                    return false;
+                }
+
+                try {
+                    if (!mController.appCrashed(processName, pid, shortMsg, longMsg, timeMillis,
+                            stackTrace)) {
+                        killCrashingAppCallback.run();
+                        return true;
+                    }
+                } catch (RemoteException e) {
+                    mController = null;
+                    Watchdog.getInstance().setActivityController(null);
+                }
+                return false;
+            }
+        }
+
+        @Override
+        public void removeRecentTasksByPackageName(String packageName, int userId) {
+            synchronized (mGlobalLock) {
+                mRecentTasks.removeTasksByPackageName(packageName, userId);
+            }
+        }
+
+        @Override
+        public void cleanupRecentTasksForUser(int userId) {
+            synchronized (mGlobalLock) {
+                mRecentTasks.cleanupLocked(userId);
+            }
+        }
+
+        @Override
+        public void loadRecentTasksForUser(int userId) {
+            synchronized (mGlobalLock) {
+                mRecentTasks.loadUserRecentsLocked(userId);
+            }
+        }
+
+        @Override
+        public void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) {
+            synchronized (mGlobalLock) {
+                mRecentTasks.onPackagesSuspendedChanged(packages, suspended, userId);
+            }
+        }
+
+        @Override
+        public void flushRecentTasks() {
+            mRecentTasks.flush();
+        }
+
+        @Override
+        public WindowProcessController getHomeProcess() {
+            synchronized (mGlobalLock) {
+                return mHomeProcess;
+            }
+        }
+
+        @Override
+        public WindowProcessController getPreviousProcess() {
+            synchronized (mGlobalLock) {
+                return mPreviousProcess;
+            }
+        }
+
+        @Override
+        public void clearLockedTasks(String reason) {
+            synchronized (mGlobalLock) {
+                getLockTaskController().clearLockedTasks(reason);
+            }
+        }
+
+        @Override
+        public void updateUserConfiguration() {
+            synchronized (mGlobalLock) {
+                final Configuration configuration = new Configuration(getGlobalConfiguration());
+                final int currentUserId = mAmInternal.getCurrentUserId();
+                Settings.System.adjustConfigurationForUser(mContext.getContentResolver(),
+                        configuration, currentUserId, Settings.System.canWrite(mContext));
+                updateConfigurationLocked(configuration, null /* starting */,
+                        false /* initLocale */, false /* persistent */, currentUserId,
+                        false /* deferResume */);
+            }
+        }
+
+        @Override
+        public boolean canShowErrorDialogs() {
+            synchronized (mGlobalLock) {
+                return mShowDialogs && !mSleeping && !mShuttingDown
+                        && !mKeyguardController.isKeyguardOrAodShowing(DEFAULT_DISPLAY)
+                        && !hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
+                        mAmInternal.getCurrentUserId())
+                        && !(UserManager.isDeviceInDemoMode(mContext)
+                        && mAmInternal.getCurrentUser().isDemo());
+            }
+        }
+
+        @Override
+        public void setProfileApp(String profileApp) {
+            synchronized (mGlobalLock) {
+                mProfileApp = profileApp;
+            }
+        }
+
+        @Override
+        public void setProfileProc(WindowProcessController wpc) {
+            synchronized (mGlobalLock) {
+                mProfileProc = wpc;
+            }
+        }
+
+        @Override
+        public void setProfilerInfo(ProfilerInfo profilerInfo) {
+            synchronized (mGlobalLock) {
+                mProfilerInfo = profilerInfo;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
new file mode 100644
index 0000000..04fef02
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+
+import android.app.ActivityManager;
+import android.app.IAppTask;
+import android.app.IApplicationThread;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.UserHandle;
+
+/**
+ * An implementation of IAppTask, that allows an app to manage its own tasks via
+ * {@link android.app.ActivityManager.AppTask}.  We keep track of the callingUid to ensure that
+ * only the process that calls getAppTasks() can call the AppTask methods.
+ */
+class AppTaskImpl extends IAppTask.Stub {
+    private ActivityTaskManagerService mService;
+
+    private int mTaskId;
+    private int mCallingUid;
+
+    public AppTaskImpl(ActivityTaskManagerService service, int taskId, int callingUid) {
+        mService = service;
+        mTaskId = taskId;
+        mCallingUid = callingUid;
+    }
+
+    private void checkCaller() {
+        if (mCallingUid != Binder.getCallingUid()) {
+            throw new SecurityException("Caller " + mCallingUid
+                    + " does not match caller of getAppTasks(): " + Binder.getCallingUid());
+        }
+    }
+
+    @Override
+    public void finishAndRemoveTask() {
+        checkCaller();
+
+        synchronized (mService.mGlobalLock) {
+            long origId = Binder.clearCallingIdentity();
+            try {
+                // We remove the task from recents to preserve backwards
+                if (!mService.mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
+                        REMOVE_FROM_RECENTS, "finish-and-remove-task")) {
+                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public ActivityManager.RecentTaskInfo getTaskInfo() {
+        checkCaller();
+
+        synchronized (mService.mGlobalLock) {
+            long origId = Binder.clearCallingIdentity();
+            try {
+                TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+                if (tr == null) {
+                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+                }
+                return mService.getRecentTasks().createRecentTaskInfo(tr);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void moveToFront() {
+        checkCaller();
+        // Will bring task to front if it already has a root activity.
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mService.mGlobalLock) {
+                mService.mStackSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId,
+                        null);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public int startActivity(IBinder whoThread, String callingPackage,
+            Intent intent, String resolvedType, Bundle bOptions) {
+        checkCaller();
+
+        int callingUser = UserHandle.getCallingUserId();
+        TaskRecord tr;
+        IApplicationThread appThread;
+        synchronized (mService.mGlobalLock) {
+            tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+            if (tr == null) {
+                throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+            }
+            appThread = IApplicationThread.Stub.asInterface(whoThread);
+            if (appThread == null) {
+                throw new IllegalArgumentException("Bad app thread " + appThread);
+            }
+        }
+
+        return mService.getActivityStartController().obtainStarter(intent, "AppTaskImpl")
+                .setCaller(appThread)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setActivityOptions(bOptions)
+                .setMayWait(callingUser)
+                .setInTask(tr)
+                .execute();
+    }
+
+    @Override
+    public void setExcludeFromRecents(boolean exclude) {
+        checkCaller();
+
+        synchronized (mService.mGlobalLock) {
+            long origId = Binder.clearCallingIdentity();
+            try {
+                TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+                if (tr == null) {
+                    throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
+                }
+                Intent intent = tr.getBaseIntent();
+                if (exclude) {
+                    intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                } else {
+                    intent.setFlags(intent.getFlags()
+                            & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index a9d0978..089640b 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -166,6 +166,7 @@
 
     private final Context mContext;
     private final WindowManagerService mService;
+    private final DisplayContent mDisplayContent;
 
     private @TransitionType int mNextAppTransition = TRANSIT_UNSET;
     private @TransitionFlags int mNextAppTransitionFlags = 0;
@@ -257,10 +258,11 @@
     final Handler mHandler;
     final Runnable mHandleAppTransitionTimeoutRunnable = () -> handleAppTransitionTimeout();
 
-    AppTransition(Context context, WindowManagerService service) {
+    AppTransition(Context context, WindowManagerService service, DisplayContent displayContent) {
         mContext = context;
         mService = service;
         mHandler = new Handler(service.mH.getLooper());
+        mDisplayContent = displayContent;
         mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.linear_out_slow_in);
         mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
@@ -426,7 +428,7 @@
                         ? topOpeningAnim.getStatusBarTransitionsStartTime()
                         : SystemClock.uptimeMillis(),
                 AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
-        mService.getDefaultDisplayContentLocked().getDockedDividerController()
+        mDisplayContent.getDockedDividerController()
                 .notifyAppTransitionStarting(openingApps, transit);
 
         if (mRemoteAnimationController != null) {
@@ -1922,7 +1924,7 @@
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Failed to fetch app transition specs: " + e);
                 }
-                synchronized (mService.mWindowMap) {
+                synchronized (mService.mGlobalLock) {
                     mNextAppTransitionAnimationsSpecsPending = false;
                     overridePendingAppTransitionMultiThumb(specs,
                             mNextAppTransitionFutureCallback, null /* finishedCallback */,
@@ -2142,7 +2144,8 @@
                 + " transit=" + appTransitionToString(transit)
                 + " " + this
                 + " alwaysKeepCurrent=" + alwaysKeepCurrent
-                + " Callers=" + Debug.getCallers(3));
+                + " displayId=" + mDisplayContent.getDisplayId()
+                + " Callers=" + Debug.getCallers(5));
         final boolean allowSetCrashing = !isKeyguardTransit(mNextAppTransition)
                 && transit == TRANSIT_CRASHING_ACTIVITY_CLOSE;
         if (forceOverride || isKeyguardTransit(transit) || !isTransitionSet()
@@ -2217,15 +2220,19 @@
     }
 
     private void handleAppTransitionTimeout() {
-        synchronized (mService.mWindowMap) {
-            if (isTransitionSet() || !mService.mOpeningApps.isEmpty()
-                    || !mService.mClosingApps.isEmpty()) {
+        synchronized (mService.mGlobalLock) {
+            final DisplayContent dc = mDisplayContent;
+            if (dc == null) {
+                return;
+            }
+            if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty()) {
                 if (DEBUG_APP_TRANSITIONS) {
                     Slog.v(TAG_WM, "*** APP TRANSITION TIMEOUT."
+                            + " displayId=" + dc.getDisplayId()
                             + " isTransitionSet()="
-                            + mService.mAppTransition.isTransitionSet()
-                            + " mOpeningApps.size()=" + mService.mOpeningApps.size()
-                            + " mClosingApps.size()=" + mService.mClosingApps.size());
+                            + dc.mAppTransition.isTransitionSet()
+                            + " mOpeningApps.size()=" + dc.mOpeningApps.size()
+                            + " mClosingApps.size()=" + dc.mClosingApps.size());
                 }
                 setTimeout();
                 mService.mWindowPlacerLocked.performSurfacePlacement();
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
new file mode 100644
index 0000000..94a47dd
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
+
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
+import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
+
+import android.app.WindowConfiguration;
+import android.os.Trace;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import android.view.Display;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.view.animation.Animation;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.function.Predicate;
+
+
+/**
+ * Checks for app transition readiness, resolves animation attributes and performs visibility
+ * change for apps that animate as part of an app transition.
+ */
+public class AppTransitionController {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransitionController" : TAG_WM;
+    private final WindowManagerService mService;
+    private final DisplayContent mDisplayContent;
+    private final WallpaperController mWallpaperControllerLocked;
+
+    private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
+
+    AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
+        mService = service;
+        mDisplayContent = displayContent;
+        mWallpaperControllerLocked = new WallpaperController(mService);
+    }
+
+    /**
+     * Handle application transition for given display.
+     */
+    void handleAppTransitionReady() {
+        final int appsCount = mDisplayContent.mOpeningApps.size();
+        if (!transitionGoodToGo(appsCount, mTempTransitionReasons)) {
+            return;
+        }
+        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
+
+        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
+        int transit = mDisplayContent.mAppTransition.getAppTransition();
+        if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
+            transit = WindowManager.TRANSIT_UNSET;
+        }
+        mDisplayContent.mSkipAppTransitionAnimation = false;
+        mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
+
+        mDisplayContent.mAppTransition.removeAppTransitionTimeoutCallbacks();
+
+        mService.mRoot.mWallpaperMayChange = false;
+
+        int i;
+        for (i = 0; i < appsCount; i++) {
+            final AppWindowToken wtoken = mDisplayContent.mOpeningApps.valueAt(i);
+            // Clearing the mAnimatingExit flag before entering animation. It's set to true if app
+            // window is removed, or window relayout to invisible. This also affects window
+            // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the
+            // transition selection depends on wallpaper target visibility.
+            wtoken.clearAnimatingFlags();
+        }
+
+        // Adjust wallpaper before we pull the lower/upper target, since pending changes
+        // (like the clearAnimatingFlags() above) might affect wallpaper target result.
+        // Or, the opening app window should be a wallpaper target.
+        mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(mDisplayContent,
+                mDisplayContent.mOpeningApps);
+
+        // Determine if closing and opening app token sets are wallpaper targets, in which case
+        // special animations are needed.
+        final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null;
+        final boolean openingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mOpeningApps)
+                && hasWallpaperTarget;
+        final boolean closingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mClosingApps)
+                && hasWallpaperTarget;
+
+        transit = maybeUpdateTransitToTranslucentAnim(transit);
+        transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
+                closingAppHasWallpaper);
+
+        // Find the layout params of the top-most application window in the tokens, which is
+        // what will control the animation theme. If all closing windows are obscured, then there is
+        // no need to do an animation. This is the case, for example, when this transition is being
+        // done behind a dream window.
+        final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
+                mDisplayContent.mClosingApps);
+        final boolean allowAnimations = mService.mPolicy.allowAppAnimationsLw();
+        final AppWindowToken animLpToken = allowAnimations
+                ? findAnimLayoutParamsToken(transit, activityTypes)
+                : null;
+        final AppWindowToken topOpeningApp = allowAnimations
+                ? getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */)
+                : null;
+        final AppWindowToken topClosingApp = allowAnimations
+                ? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */)
+                : null;
+        final WindowManager.LayoutParams animLp = getAnimLp(animLpToken);
+        overrideWithRemoteAnimationIfSet(animLpToken, transit, activityTypes);
+
+        final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
+                || containsVoiceInteraction(mDisplayContent.mOpeningApps);
+
+        final int layoutRedo;
+        mService.mSurfaceAnimationRunner.deferStartingAnimations();
+        try {
+            processApplicationsAnimatingInPlace(transit);
+
+            handleClosingApps(transit, animLp, voiceInteraction);
+            handleOpeningApps(transit, animLp, voiceInteraction);
+
+            mDisplayContent.mAppTransition.setLastAppTransition(transit, topOpeningApp,
+                    topClosingApp);
+
+            final int flags = mDisplayContent.mAppTransition.getTransitFlags();
+            layoutRedo = mDisplayContent.mAppTransition.goodToGo(transit, topOpeningApp,
+                    topClosingApp, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps);
+            handleNonAppWindowsInTransition(transit, flags);
+            mDisplayContent.mAppTransition.postAnimationCallback();
+            mDisplayContent.mAppTransition.clear();
+        } finally {
+            mService.mSurfaceAnimationRunner.continueStartingAnimations();
+        }
+
+        mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);
+
+        mDisplayContent.mOpeningApps.clear();
+        mDisplayContent.mClosingApps.clear();
+        mDisplayContent.mUnknownAppVisibilityController.clear();
+
+        // This has changed the visibility of windows, so perform
+        // a new layout to get them all up-to-date.
+        mDisplayContent.setLayoutNeeded();
+
+        mDisplayContent.computeImeTarget(true /* updateImeTarget */);
+
+        mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING,
+                mTempTransitionReasons.clone()).sendToTarget();
+
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+
+        mDisplayContent.pendingLayoutChanges |=
+                layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
+    }
+
+    private static WindowManager.LayoutParams getAnimLp(AppWindowToken wtoken) {
+        final WindowState mainWindow = wtoken != null ? wtoken.findMainWindow() : null;
+        return mainWindow != null ? mainWindow.mAttrs : null;
+    }
+
+    /**
+     * Overrides the pending transition with the remote animation defined for the transition in the
+     * set of defined remote animations in the app window token.
+     */
+    private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit,
+            ArraySet<Integer> activityTypes) {
+        if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) {
+            // The crash transition has higher priority than any involved remote animations.
+            return;
+        }
+        if (animLpToken == null) {
+            return;
+        }
+        final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition();
+        if (definition == null) {
+            return;
+        }
+        final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
+        if (adapter != null) {
+            animLpToken.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(
+                    adapter);
+        }
+    }
+
+    /**
+     * @return The window token that determines the animation theme.
+     */
+    private AppWindowToken findAnimLayoutParamsToken(@WindowManager.TransitionType int transit,
+            ArraySet<Integer> activityTypes) {
+        AppWindowToken result;
+        final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
+        final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
+
+        // Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
+        result = lookForHighestTokenWithFilter(closingApps, openingApps,
+                w -> w.getRemoteAnimationDefinition() != null
+                        && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
+        if (result != null) {
+            return result;
+        }
+        result = lookForHighestTokenWithFilter(closingApps, openingApps,
+                w -> w.fillsParent() && w.findMainWindow() != null);
+        if (result != null) {
+            return result;
+        }
+        return lookForHighestTokenWithFilter(closingApps, openingApps,
+                w -> w.findMainWindow() != null);
+    }
+
+    /**
+     * @return The set of {@link WindowConfiguration.ActivityType}s contained in the set of apps in
+     *         {@code array1} and {@code array2}.
+     */
+    private static ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1,
+            ArraySet<AppWindowToken> array2) {
+        final ArraySet<Integer> result = new ArraySet<>();
+        for (int i = array1.size() - 1; i >= 0; i--) {
+            result.add(array1.valueAt(i).getActivityType());
+        }
+        for (int i = array2.size() - 1; i >= 0; i--) {
+            result.add(array2.valueAt(i).getActivityType());
+        }
+        return result;
+    }
+
+    private static AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1,
+            ArraySet<AppWindowToken> array2, Predicate<AppWindowToken> filter) {
+        final int array1count = array1.size();
+        final int count = array1count + array2.size();
+        int bestPrefixOrderIndex = Integer.MIN_VALUE;
+        AppWindowToken bestToken = null;
+        for (int i = 0; i < count; i++) {
+            final AppWindowToken wtoken = i < array1count
+                    ? array1.valueAt(i)
+                    : array2.valueAt(i - array1count);
+            final int prefixOrderIndex = wtoken.getPrefixOrderIndex();
+            if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) {
+                bestPrefixOrderIndex = prefixOrderIndex;
+                bestToken = wtoken;
+            }
+        }
+        return bestToken;
+    }
+
+    private boolean containsVoiceInteraction(ArraySet<AppWindowToken> apps) {
+        for (int i = apps.size() - 1; i >= 0; i--) {
+            if (apps.valueAt(i).mVoiceInteraction) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
+        final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
+        final int appsCount = openingApps.size();
+        for (int i = 0; i < appsCount; i++) {
+            AppWindowToken wtoken = openingApps.valueAt(i);
+            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
+
+            if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) {
+                // This token isn't going to be animating. Add it to the list of tokens to
+                // be notified of app transition complete since the notification will not be
+                // sent be the app window animator.
+                mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
+            }
+            wtoken.updateReportedVisibilityLocked();
+            wtoken.waitingToShow = false;
+            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+                    ">>> OPEN TRANSACTION handleAppTransitionReady()");
+            mService.openSurfaceTransaction();
+            try {
+                wtoken.showAllWindowsLocked();
+            } finally {
+                mService.closeSurfaceTransaction("handleAppTransitionReady");
+                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+                        "<<< CLOSE TRANSACTION handleAppTransitionReady()");
+            }
+
+            if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) {
+                wtoken.attachThumbnailAnimation();
+            } else if (mDisplayContent.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) {
+                wtoken.attachCrossProfileAppsThumbnailAnimation();
+            }
+        }
+    }
+
+    private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
+        final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
+        final int appsCount = closingApps.size();
+        for (int i = 0; i < appsCount; i++) {
+            AppWindowToken wtoken = closingApps.valueAt(i);
+
+            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
+            // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
+            //       animating?
+            wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
+            wtoken.updateReportedVisibilityLocked();
+            // Force the allDrawn flag, because we want to start
+            // this guy's animations regardless of whether it's
+            // gotten drawn.
+            wtoken.allDrawn = true;
+            wtoken.deferClearAllDrawn = false;
+            // Ensure that apps that are mid-starting are also scheduled to have their
+            // starting windows removed after the animation is complete
+            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit
+                    && wtoken.getController() != null) {
+                wtoken.getController().removeStartingWindow();
+            }
+
+            if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) {
+                wtoken.attachThumbnailAnimation();
+            }
+        }
+    }
+
+    private void handleNonAppWindowsInTransition(int transit, int flags) {
+        if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
+            if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
+                    && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0) {
+                Animation anim = mService.mPolicy.createKeyguardWallpaperExit(
+                        (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
+                if (anim != null) {
+                    mDisplayContent.mWallpaperController.startWallpaperAnimation(anim);
+                }
+            }
+        }
+        if (transit == TRANSIT_KEYGUARD_GOING_AWAY
+                || transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
+            mDisplayContent.startKeyguardExitOnNonAppWindows(
+                    transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+                    (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
+        }
+    }
+
+    private boolean transitionGoodToGo(int appsCount, SparseIntArray outReasons) {
+        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                "Checking " + appsCount + " opening apps (frozen="
+                        + mService.mDisplayFrozen + " timeout="
+                        + mDisplayContent.mAppTransition.isTimeout() + ")...");
+        final ScreenRotationAnimation screenRotationAnimation =
+                mService.mAnimator.getScreenRotationAnimationLocked(
+                        Display.DEFAULT_DISPLAY);
+
+        outReasons.clear();
+        if (!mDisplayContent.mAppTransition.isTimeout()) {
+            // Imagine the case where we are changing orientation due to an app transition, but a
+            // previous orientation change is still in progress. We won't process the orientation
+            // change for our transition because we need to wait for the rotation animation to
+            // finish.
+            // If we start the app transition at this point, we will interrupt it halfway with a
+            // new rotation animation after the old one finally finishes. It's better to defer the
+            // app transition.
+            if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() &&
+                    mService.getDefaultDisplayContentLocked().rotationNeedsUpdate()) {
+                if (DEBUG_APP_TRANSITIONS) {
+                    Slog.v(TAG, "Delaying app transition for screen rotation animation to finish");
+                }
+                return false;
+            }
+            for (int i = 0; i < appsCount; i++) {
+                AppWindowToken wtoken = mDisplayContent.mOpeningApps.valueAt(i);
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                        "Check opening app=" + wtoken + ": allDrawn="
+                                + wtoken.allDrawn + " startingDisplayed="
+                                + wtoken.startingDisplayed + " startingMoved="
+                                + wtoken.startingMoved + " isRelaunching()="
+                                + wtoken.isRelaunching() + " startingWindow="
+                                + wtoken.startingWindow);
+
+
+                final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching();
+                if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
+                    return false;
+                }
+                final int windowingMode = wtoken.getWindowingMode();
+                if (allDrawn) {
+                    outReasons.put(windowingMode,  APP_TRANSITION_WINDOWS_DRAWN);
+                } else {
+                    outReasons.put(windowingMode,
+                            wtoken.startingData instanceof SplashScreenStartingData
+                                    ? APP_TRANSITION_SPLASH_SCREEN
+                                    : APP_TRANSITION_SNAPSHOT);
+                }
+            }
+
+            // We also need to wait for the specs to be fetched, if needed.
+            if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) {
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true");
+                return false;
+            }
+
+            if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
+                if (DEBUG_APP_TRANSITIONS) {
+                    Slog.v(TAG, "unknownApps is not empty: "
+                            + mDisplayContent.mUnknownAppVisibilityController.getDebugMessage());
+                }
+                return false;
+            }
+
+            // If the wallpaper is visible, we need to check it's ready too.
+            boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
+                    mWallpaperControllerLocked.wallpaperTransitionReady();
+            if (wallpaperReady) {
+                return true;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper,
+            boolean closingAppHasWallpaper) {
+        // Given no app transition pass it through instead of a wallpaper transition.
+        // Never convert the crashing transition.
+        // Never update the transition for the wallpaper if we are just docking from recents
+        if (transit == TRANSIT_NONE || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE
+                || transit == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+            return transit;
+        }
+
+        final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
+        final boolean showWallpaper = wallpaperTarget != null
+                && (wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
+        // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
+        // don't consider upgrading to wallpaper transition.
+        final WindowState oldWallpaper =
+                (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper)
+                        ? null
+                        : wallpaperTarget;
+        final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
+        final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
+        final AppWindowToken topOpeningApp = getTopApp(mDisplayContent.mOpeningApps,
+                false /* ignoreHidden */);
+        final AppWindowToken topClosingApp = getTopApp(mDisplayContent.mClosingApps,
+                true /* ignoreHidden */);
+
+        boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps);
+        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                "New wallpaper target=" + wallpaperTarget
+                        + ", oldWallpaper=" + oldWallpaper
+                        + ", openingApps=" + openingApps
+                        + ", closingApps=" + closingApps);
+
+        if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
+            transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                    "New transit: " + AppTransition.appTransitionToString(transit));
+        }
+        // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
+        // relies on the fact that we always execute a Keyguard transition after preparing one.
+        else if (!isKeyguardGoingAwayTransit(transit)) {
+            if (closingAppHasWallpaper && openingAppHasWallpaper) {
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!");
+                switch (transit) {
+                    case TRANSIT_ACTIVITY_OPEN:
+                    case TRANSIT_TASK_OPEN:
+                    case TRANSIT_TASK_TO_FRONT:
+                        transit = TRANSIT_WALLPAPER_INTRA_OPEN;
+                        break;
+                    case TRANSIT_ACTIVITY_CLOSE:
+                    case TRANSIT_TASK_CLOSE:
+                    case TRANSIT_TASK_TO_BACK:
+                        transit = TRANSIT_WALLPAPER_INTRA_CLOSE;
+                        break;
+                }
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                        "New transit: " + AppTransition.appTransitionToString(transit));
+            } else if (oldWallpaper != null && !mDisplayContent.mOpeningApps.isEmpty()
+                    && !openingApps.contains(oldWallpaper.mAppToken)
+                    && closingApps.contains(oldWallpaper.mAppToken)
+                    && topClosingApp == oldWallpaper.mAppToken) {
+                // We are transitioning from an activity with a wallpaper to one without.
+                transit = TRANSIT_WALLPAPER_CLOSE;
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
+                        + AppTransition.appTransitionToString(transit));
+            } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()
+                    && openingApps.contains(wallpaperTarget.mAppToken)
+                    && topOpeningApp == wallpaperTarget.mAppToken
+                    && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) {
+                // We are transitioning from an activity without
+                // a wallpaper to now showing the wallpaper
+                transit = TRANSIT_WALLPAPER_OPEN;
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
+                        + AppTransition.appTransitionToString(transit));
+            }
+        }
+        return transit;
+    }
+
+    /**
+     * There are cases where we open/close a new task/activity, but in reality only a translucent
+     * activity on top of existing activities is opening/closing. For that one, we have a different
+     * animation because non of the task/activity animations actually work well with translucent
+     * apps.
+     *
+     * @param transit The current transition type.
+     * @return The current transition type or
+     *         {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE}/
+     *         {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the
+     *         situation.
+     */
+    @VisibleForTesting
+    int maybeUpdateTransitToTranslucentAnim(int transit) {
+        final boolean taskOrActivity = AppTransition.isTaskTransit(transit)
+                || AppTransition.isActivityTransit(transit);
+        boolean allOpeningVisible = true;
+        boolean allTranslucentOpeningApps = !mDisplayContent.mOpeningApps.isEmpty();
+        for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) {
+            final AppWindowToken token = mDisplayContent.mOpeningApps.valueAt(i);
+            if (!token.isVisible()) {
+                allOpeningVisible = false;
+                if (token.fillsParent()) {
+                    allTranslucentOpeningApps = false;
+                }
+            }
+        }
+        boolean allTranslucentClosingApps = !mDisplayContent.mClosingApps.isEmpty();
+        for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) {
+            if (mDisplayContent.mClosingApps.valueAt(i).fillsParent()) {
+                allTranslucentClosingApps = false;
+                break;
+            }
+        }
+
+        if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) {
+            return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
+        }
+        if (taskOrActivity && allTranslucentOpeningApps && mDisplayContent.mClosingApps.isEmpty()) {
+            return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
+        }
+        return transit;
+    }
+
+    private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) {
+        for (int i = apps.size() - 1; i >= 0; i--) {
+            if (apps.valueAt(i).windowsCanBeWallpaperTarget()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Finds the top app in a list of apps, using its {@link AppWindowToken#getPrefixOrderIndex} to
+     * compare z-order.
+     *
+     * @param apps The list of apps to search.
+     * @param ignoreHidden If set to true, ignores apps that are {@link AppWindowToken#isHidden}.
+     * @return The top {@link AppWindowToken}.
+     */
+    private AppWindowToken getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden) {
+        int topPrefixOrderIndex = Integer.MIN_VALUE;
+        AppWindowToken topApp = null;
+        for (int i = apps.size() - 1; i >= 0; i--) {
+            final AppWindowToken app = apps.valueAt(i);
+            if (ignoreHidden && app.isHidden()) {
+                continue;
+            }
+            final int prefixOrderIndex = app.getPrefixOrderIndex();
+            if (prefixOrderIndex > topPrefixOrderIndex) {
+                topPrefixOrderIndex = prefixOrderIndex;
+                topApp = app;
+            }
+        }
+        return topApp;
+    }
+
+    private void processApplicationsAnimatingInPlace(int transit) {
+        if (transit == TRANSIT_TASK_IN_PLACE) {
+            // Find the focused window
+            final WindowState win = mDisplayContent.findFocusedWindow();
+            if (win != null) {
+                final AppWindowToken wtoken = win.mAppToken;
+                if (DEBUG_APP_TRANSITIONS)
+                    Slog.v(TAG, "Now animating app in place " + wtoken);
+                wtoken.cancelAnimation();
+                wtoken.applyAnimationLocked(null, transit, false, false);
+                wtoken.updateReportedVisibilityLocked();
+                wtoken.showAllWindowsLocked();
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
new file mode 100644
index 0000000..0436857
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import android.annotation.UiThread;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.AtomicFile;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * Manages warning dialogs shown during application lifecycle.
+ */
+class AppWarnings {
+    private static final String TAG = "AppWarnings";
+    private static final String CONFIG_FILE_NAME = "packages-warnings.xml";
+
+    public static final int FLAG_HIDE_DISPLAY_SIZE = 0x01;
+    public static final int FLAG_HIDE_COMPILE_SDK = 0x02;
+    public static final int FLAG_HIDE_DEPRECATED_SDK = 0x04;
+
+    private final HashMap<String, Integer> mPackageFlags = new HashMap<>();
+
+    private final ActivityTaskManagerService mAtm;
+    private final Context mUiContext;
+    private final ConfigHandler mHandler;
+    private final UiHandler mUiHandler;
+    private final AtomicFile mConfigFile;
+
+    private UnsupportedDisplaySizeDialog mUnsupportedDisplaySizeDialog;
+    private UnsupportedCompileSdkDialog mUnsupportedCompileSdkDialog;
+    private DeprecatedTargetSdkVersionDialog mDeprecatedTargetSdkVersionDialog;
+
+    /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
+    private HashSet<ComponentName> mAlwaysShowUnsupportedCompileSdkWarningActivities =
+            new HashSet<>();
+
+    /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
+    void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) {
+        mAlwaysShowUnsupportedCompileSdkWarningActivities.add(activity);
+    }
+
+    /**
+     * Creates a new warning dialog manager.
+     * <p>
+     * <strong>Note:</strong> Must be called from the ActivityManagerService thread.
+     *
+     * @param atm
+     * @param uiContext
+     * @param handler
+     * @param uiHandler
+     * @param systemDir
+     */
+    public AppWarnings(ActivityTaskManagerService atm, Context uiContext, Handler handler,
+            Handler uiHandler, File systemDir) {
+        mAtm = atm;
+        mUiContext = uiContext;
+        mHandler = new ConfigHandler(handler.getLooper());
+        mUiHandler = new UiHandler(uiHandler.getLooper());
+        mConfigFile = new AtomicFile(new File(systemDir, CONFIG_FILE_NAME), "warnings-config");
+
+        readConfigFromFileAmsThread();
+    }
+
+    /**
+     * Shows the "unsupported display size" warning, if necessary.
+     *
+     * @param r activity record for which the warning may be displayed
+     */
+    public void showUnsupportedDisplaySizeDialogIfNeeded(ActivityRecord r) {
+        final Configuration globalConfig = mAtm.getGlobalConfiguration();
+        if (globalConfig.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE
+                && r.appInfo.requiresSmallestWidthDp > globalConfig.smallestScreenWidthDp) {
+            mUiHandler.showUnsupportedDisplaySizeDialog(r);
+        }
+    }
+
+    /**
+     * Shows the "unsupported compile SDK" warning, if necessary.
+     *
+     * @param r activity record for which the warning may be displayed
+     */
+    public void showUnsupportedCompileSdkDialogIfNeeded(ActivityRecord r) {
+        if (r.appInfo.compileSdkVersion == 0 || r.appInfo.compileSdkVersionCodename == null) {
+            // We don't know enough about this package. Abort!
+            return;
+        }
+
+        // TODO(b/75318890): Need to move this to when the app actually crashes.
+        if (/*ActivityManager.isRunningInTestHarness()
+                &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(r.realActivity)) {
+            // Don't show warning if we are running in a test harness and we don't have to always
+            // show for this activity.
+            return;
+        }
+
+        // If the application was built against an pre-release SDK that's older than the current
+        // platform OR if the current platform is pre-release and older than the SDK against which
+        // the application was built OR both are pre-release with the same SDK_INT but different
+        // codenames (e.g. simultaneous pre-release development), then we're likely to run into
+        // compatibility issues. Warn the user and offer to check for an update.
+        final int compileSdk = r.appInfo.compileSdkVersion;
+        final int platformSdk = Build.VERSION.SDK_INT;
+        final boolean isCompileSdkPreview = !"REL".equals(r.appInfo.compileSdkVersionCodename);
+        final boolean isPlatformSdkPreview = !"REL".equals(Build.VERSION.CODENAME);
+        if ((isCompileSdkPreview && compileSdk < platformSdk)
+                || (isPlatformSdkPreview && platformSdk < compileSdk)
+                || (isCompileSdkPreview && isPlatformSdkPreview && platformSdk == compileSdk
+                    && !Build.VERSION.CODENAME.equals(r.appInfo.compileSdkVersionCodename))) {
+            mUiHandler.showUnsupportedCompileSdkDialog(r);
+        }
+    }
+
+    /**
+     * Shows the "deprecated target sdk" warning, if necessary.
+     *
+     * @param r activity record for which the warning may be displayed
+     */
+    public void showDeprecatedTargetDialogIfNeeded(ActivityRecord r) {
+        if (r.appInfo.targetSdkVersion < Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT) {
+            mUiHandler.showDeprecatedTargetDialog(r);
+        }
+    }
+
+    /**
+     * Called when an activity is being started.
+     *
+     * @param r record for the activity being started
+     */
+    public void onStartActivity(ActivityRecord r) {
+        showUnsupportedCompileSdkDialogIfNeeded(r);
+        showUnsupportedDisplaySizeDialogIfNeeded(r);
+        showDeprecatedTargetDialogIfNeeded(r);
+    }
+
+    /**
+     * Called when an activity was previously started and is being resumed.
+     *
+     * @param r record for the activity being resumed
+     */
+    public void onResumeActivity(ActivityRecord r) {
+        showUnsupportedDisplaySizeDialogIfNeeded(r);
+    }
+
+    /**
+     * Called by ActivityManagerService when package data has been cleared.
+     *
+     * @param name the package whose data has been cleared
+     */
+    public void onPackageDataCleared(String name) {
+        removePackageAndHideDialogs(name);
+    }
+
+    /**
+     * Called by ActivityManagerService when a package has been uninstalled.
+     *
+     * @param name the package that has been uninstalled
+     */
+    public void onPackageUninstalled(String name) {
+        removePackageAndHideDialogs(name);
+    }
+
+    /**
+     * Called by ActivityManagerService when the default display density has changed.
+     */
+    public void onDensityChanged() {
+        mUiHandler.hideUnsupportedDisplaySizeDialog();
+    }
+
+    /**
+     * Does what it says on the tin.
+     */
+    private void removePackageAndHideDialogs(String name) {
+        mUiHandler.hideDialogsForPackage(name);
+
+        synchronized (mPackageFlags) {
+            mPackageFlags.remove(name);
+            mHandler.scheduleWrite();
+        }
+    }
+
+    /**
+     * Hides the "unsupported display size" warning.
+     * <p>
+     * <strong>Note:</strong> Must be called on the UI thread.
+     */
+    @UiThread
+    private void hideUnsupportedDisplaySizeDialogUiThread() {
+        if (mUnsupportedDisplaySizeDialog != null) {
+            mUnsupportedDisplaySizeDialog.dismiss();
+            mUnsupportedDisplaySizeDialog = null;
+        }
+    }
+
+    /**
+     * Shows the "unsupported display size" warning for the given application.
+     * <p>
+     * <strong>Note:</strong> Must be called on the UI thread.
+     *
+     * @param ar record for the activity that triggered the warning
+     */
+    @UiThread
+    private void showUnsupportedDisplaySizeDialogUiThread(ActivityRecord ar) {
+        if (mUnsupportedDisplaySizeDialog != null) {
+            mUnsupportedDisplaySizeDialog.dismiss();
+            mUnsupportedDisplaySizeDialog = null;
+        }
+        if (ar != null && !hasPackageFlag(
+                ar.packageName, FLAG_HIDE_DISPLAY_SIZE)) {
+            mUnsupportedDisplaySizeDialog = new UnsupportedDisplaySizeDialog(
+                    AppWarnings.this, mUiContext, ar.info.applicationInfo);
+            mUnsupportedDisplaySizeDialog.show();
+        }
+    }
+
+    /**
+     * Shows the "unsupported compile SDK" warning for the given application.
+     * <p>
+     * <strong>Note:</strong> Must be called on the UI thread.
+     *
+     * @param ar record for the activity that triggered the warning
+     */
+    @UiThread
+    private void showUnsupportedCompileSdkDialogUiThread(ActivityRecord ar) {
+        if (mUnsupportedCompileSdkDialog != null) {
+            mUnsupportedCompileSdkDialog.dismiss();
+            mUnsupportedCompileSdkDialog = null;
+        }
+        if (ar != null && !hasPackageFlag(
+                ar.packageName, FLAG_HIDE_COMPILE_SDK)) {
+            mUnsupportedCompileSdkDialog = new UnsupportedCompileSdkDialog(
+                    AppWarnings.this, mUiContext, ar.info.applicationInfo);
+            mUnsupportedCompileSdkDialog.show();
+        }
+    }
+
+    /**
+     * Shows the "deprecated target sdk version" warning for the given application.
+     * <p>
+     * <strong>Note:</strong> Must be called on the UI thread.
+     *
+     * @param ar record for the activity that triggered the warning
+     */
+    @UiThread
+    private void showDeprecatedTargetSdkDialogUiThread(ActivityRecord ar) {
+        if (mDeprecatedTargetSdkVersionDialog != null) {
+            mDeprecatedTargetSdkVersionDialog.dismiss();
+            mDeprecatedTargetSdkVersionDialog = null;
+        }
+        if (ar != null && !hasPackageFlag(
+                ar.packageName, FLAG_HIDE_DEPRECATED_SDK)) {
+            mDeprecatedTargetSdkVersionDialog = new DeprecatedTargetSdkVersionDialog(
+                    AppWarnings.this, mUiContext, ar.info.applicationInfo);
+            mDeprecatedTargetSdkVersionDialog.show();
+        }
+    }
+
+    /**
+     * Dismisses all warnings for the given package.
+     * <p>
+     * <strong>Note:</strong> Must be called on the UI thread.
+     *
+     * @param name the package for which warnings should be dismissed, or {@code null} to dismiss
+     *             all warnings
+     */
+    @UiThread
+    private void hideDialogsForPackageUiThread(String name) {
+        // Hides the "unsupported display" dialog if necessary.
+        if (mUnsupportedDisplaySizeDialog != null && (name == null || name.equals(
+                mUnsupportedDisplaySizeDialog.getPackageName()))) {
+            mUnsupportedDisplaySizeDialog.dismiss();
+            mUnsupportedDisplaySizeDialog = null;
+        }
+
+        // Hides the "unsupported compile SDK" dialog if necessary.
+        if (mUnsupportedCompileSdkDialog != null && (name == null || name.equals(
+                mUnsupportedCompileSdkDialog.getPackageName()))) {
+            mUnsupportedCompileSdkDialog.dismiss();
+            mUnsupportedCompileSdkDialog = null;
+        }
+
+        // Hides the "deprecated target sdk version" dialog if necessary.
+        if (mDeprecatedTargetSdkVersionDialog != null && (name == null || name.equals(
+                mDeprecatedTargetSdkVersionDialog.getPackageName()))) {
+            mDeprecatedTargetSdkVersionDialog.dismiss();
+            mDeprecatedTargetSdkVersionDialog = null;
+        }
+    }
+
+    /**
+     * Returns the value of the flag for the given package.
+     *
+     * @param name the package from which to retrieve the flag
+     * @param flag the bitmask for the flag to retrieve
+     * @return {@code true} if the flag is enabled, {@code false} otherwise
+     */
+    boolean hasPackageFlag(String name, int flag) {
+        return (getPackageFlags(name) & flag) == flag;
+    }
+
+    /**
+     * Sets the flag for the given package to the specified value.
+     *
+     * @param name the package on which to set the flag
+     * @param flag the bitmask for flag to set
+     * @param enabled the value to set for the flag
+     */
+    void setPackageFlag(String name, int flag, boolean enabled) {
+        synchronized (mPackageFlags) {
+            final int curFlags = getPackageFlags(name);
+            final int newFlags = enabled ? (curFlags | flag) : (curFlags & ~flag);
+            if (curFlags != newFlags) {
+                if (newFlags != 0) {
+                    mPackageFlags.put(name, newFlags);
+                } else {
+                    mPackageFlags.remove(name);
+                }
+                mHandler.scheduleWrite();
+            }
+        }
+    }
+
+    /**
+     * Returns the bitmask of flags set for the specified package.
+     */
+    private int getPackageFlags(String name) {
+        synchronized (mPackageFlags) {
+            return mPackageFlags.getOrDefault(name, 0);
+        }
+    }
+
+    /**
+     * Handles messages on the system process UI thread.
+     */
+    private final class UiHandler extends Handler {
+        private static final int MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG = 1;
+        private static final int MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG = 2;
+        private static final int MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG = 3;
+        private static final int MSG_HIDE_DIALOGS_FOR_PACKAGE = 4;
+        private static final int MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG = 5;
+
+        public UiHandler(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG: {
+                    final ActivityRecord ar = (ActivityRecord) msg.obj;
+                    showUnsupportedDisplaySizeDialogUiThread(ar);
+                } break;
+                case MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG: {
+                    hideUnsupportedDisplaySizeDialogUiThread();
+                } break;
+                case MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG: {
+                    final ActivityRecord ar = (ActivityRecord) msg.obj;
+                    showUnsupportedCompileSdkDialogUiThread(ar);
+                } break;
+                case MSG_HIDE_DIALOGS_FOR_PACKAGE: {
+                    final String name = (String) msg.obj;
+                    hideDialogsForPackageUiThread(name);
+                } break;
+                case MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG: {
+                    final ActivityRecord ar = (ActivityRecord) msg.obj;
+                    showDeprecatedTargetSdkDialogUiThread(ar);
+                } break;
+            }
+        }
+
+        public void showUnsupportedDisplaySizeDialog(ActivityRecord r) {
+            removeMessages(MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG);
+            obtainMessage(MSG_SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG, r).sendToTarget();
+        }
+
+        public void hideUnsupportedDisplaySizeDialog() {
+            removeMessages(MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG);
+            sendEmptyMessage(MSG_HIDE_UNSUPPORTED_DISPLAY_SIZE_DIALOG);
+        }
+
+        public void showUnsupportedCompileSdkDialog(ActivityRecord r) {
+            removeMessages(MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG);
+            obtainMessage(MSG_SHOW_UNSUPPORTED_COMPILE_SDK_DIALOG, r).sendToTarget();
+        }
+
+        public void showDeprecatedTargetDialog(ActivityRecord r) {
+            removeMessages(MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG);
+            obtainMessage(MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG, r).sendToTarget();
+        }
+
+        public void hideDialogsForPackage(String name) {
+            obtainMessage(MSG_HIDE_DIALOGS_FOR_PACKAGE, name).sendToTarget();
+        }
+    }
+
+    /**
+     * Handles messages on the ActivityTaskManagerService thread.
+     */
+    private final class ConfigHandler extends Handler {
+        private static final int MSG_WRITE = 1;
+
+        private static final int DELAY_MSG_WRITE = 10000;
+
+        public ConfigHandler(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_WRITE:
+                    writeConfigToFileAmsThread();
+                    break;
+            }
+        }
+
+        public void scheduleWrite() {
+            removeMessages(MSG_WRITE);
+            sendEmptyMessageDelayed(MSG_WRITE, DELAY_MSG_WRITE);
+        }
+    }
+
+    /**
+     * Writes the configuration file.
+     * <p>
+     * <strong>Note:</strong> Should be called from the ActivityManagerService thread unless you
+     * don't care where you're doing I/O operations. But you <i>do</i> care, don't you?
+     */
+    private void writeConfigToFileAmsThread() {
+        // Create a shallow copy so that we don't have to synchronize on config.
+        final HashMap<String, Integer> packageFlags;
+        synchronized (mPackageFlags) {
+            packageFlags = new HashMap<>(mPackageFlags);
+        }
+
+        FileOutputStream fos = null;
+        try {
+            fos = mConfigFile.startWrite();
+
+            final XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(fos, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            out.startTag(null, "packages");
+
+            for (Map.Entry<String, Integer> entry : packageFlags.entrySet()) {
+                String pkg = entry.getKey();
+                int mode = entry.getValue();
+                if (mode == 0) {
+                    continue;
+                }
+                out.startTag(null, "package");
+                out.attribute(null, "name", pkg);
+                out.attribute(null, "flags", Integer.toString(mode));
+                out.endTag(null, "package");
+            }
+
+            out.endTag(null, "packages");
+            out.endDocument();
+
+            mConfigFile.finishWrite(fos);
+        } catch (java.io.IOException e1) {
+            Slog.w(TAG, "Error writing package metadata", e1);
+            if (fos != null) {
+                mConfigFile.failWrite(fos);
+            }
+        }
+    }
+
+    /**
+     * Reads the configuration file and populates the package flags.
+     * <p>
+     * <strong>Note:</strong> Must be called from the constructor (and thus on the
+     * ActivityManagerService thread) since we don't synchronize on config.
+     */
+    private void readConfigFromFileAmsThread() {
+        FileInputStream fis = null;
+
+        try {
+            fis = mConfigFile.openRead();
+
+            final XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, StandardCharsets.UTF_8.name());
+
+            int eventType = parser.getEventType();
+            while (eventType != XmlPullParser.START_TAG &&
+                    eventType != XmlPullParser.END_DOCUMENT) {
+                eventType = parser.next();
+            }
+            if (eventType == XmlPullParser.END_DOCUMENT) {
+                return;
+            }
+
+            String tagName = parser.getName();
+            if ("packages".equals(tagName)) {
+                eventType = parser.next();
+                do {
+                    if (eventType == XmlPullParser.START_TAG) {
+                        tagName = parser.getName();
+                        if (parser.getDepth() == 2) {
+                            if ("package".equals(tagName)) {
+                                final String name = parser.getAttributeValue(null, "name");
+                                if (name != null) {
+                                    final String flags = parser.getAttributeValue(
+                                            null, "flags");
+                                    int flagsInt = 0;
+                                    if (flags != null) {
+                                        try {
+                                            flagsInt = Integer.parseInt(flags);
+                                        } catch (NumberFormatException e) {
+                                        }
+                                    }
+                                    mPackageFlags.put(name, flagsInt);
+                                }
+                            }
+                        }
+                    }
+                    eventType = parser.next();
+                } while (eventType != XmlPullParser.END_DOCUMENT);
+            }
+        } catch (XmlPullParserException e) {
+            Slog.w(TAG, "Error reading package metadata", e);
+        } catch (java.io.IOException e) {
+            if (fis != null) Slog.w(TAG, "Error reading package metadata", e);
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (java.io.IOException e1) {
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 330c54c..830c2e6 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -16,11 +16,21 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_NONE;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-
 import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 import static android.view.WindowManager.TRANSIT_UNSET;
+
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -30,14 +40,20 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.app.ActivityManager.TaskSnapshot;
+import android.app.ActivityOptions;
+import android.content.Intent;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.graphics.GraphicBuffer;
+import android.graphics.Rect;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.util.Slog;
+import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.IApplicationToken;
 import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
@@ -120,7 +136,7 @@
             final StartingData startingData;
             final AppWindowToken container;
 
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 if (mContainer == null) {
                     if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
                             + " add starting window");
@@ -154,7 +170,7 @@
             }
             if (surface != null) {
                 boolean abort = false;
-                synchronized (mWindowMap) {
+                synchronized (mGlobalLock) {
                     // If the window was successfully added, then
                     // we need to remove it.
                     if (container.removed || container.startingData == null) {
@@ -204,7 +220,7 @@
         super(listener, service);
         mHandler = new H(service.mH.getLooper());
         mToken = token;
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
             if (atoken != null) {
                 // TODO: Should this throw an exception instead?
@@ -241,7 +257,7 @@
     }
 
     public void removeContainer(int displayId) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent dc = mRoot.getDisplayContent(displayId);
             if (dc == null) {
                 Slog.w(TAG_WM, "removeAppToken: Attempted to remove binder token: "
@@ -259,7 +275,7 @@
     }
 
     public void reparent(TaskWindowContainerController taskController, int position) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, "reparent: moving app token=" + mToken
                     + " to task=" + taskController + " at " + position);
             if (mContainer == null) {
@@ -279,7 +295,7 @@
 
     public Configuration setOrientation(int requestedOrientation, int displayId,
             Configuration displayConfig, boolean freezeScreenIfNeeded) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 Slog.w(TAG_WM,
                         "Attempted to set orientation of non-existing app token: " + mToken);
@@ -295,7 +311,7 @@
     }
 
     public int getOrientation() {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 return SCREEN_ORIENTATION_UNSPECIFIED;
             }
@@ -305,7 +321,7 @@
     }
 
     public void setDisablePreviewScreenshots(boolean disable) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
                         + " token: " + mToken);
@@ -316,7 +332,7 @@
     }
 
     public void setVisibility(boolean visible, boolean deferHidingClient) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
                         + mToken);
@@ -324,6 +340,7 @@
             }
 
             final AppWindowToken wtoken = mContainer;
+            final AppTransition appTransition = mContainer.getDisplayContent().mAppTransition;
 
             // Don't set visibility to false if we were already not visible. This prevents WM from
             // adding the app to the closing app list which doesn't make sense for something that is
@@ -344,12 +361,13 @@
             }
 
             if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility("
-                    + mToken + ", visible=" + visible + "): " + mService.mAppTransition
+                    + mToken + ", visible=" + visible + "): " + appTransition
                     + " hidden=" + wtoken.isHidden() + " hiddenRequested="
                     + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
 
-            mService.mOpeningApps.remove(wtoken);
-            mService.mClosingApps.remove(wtoken);
+            final DisplayContent displayContent = mContainer.getDisplayContent();
+            displayContent.mOpeningApps.remove(wtoken);
+            displayContent.mClosingApps.remove(wtoken);
             wtoken.waitingToShow = false;
             wtoken.hiddenRequested = !visible;
             wtoken.mDeferHidingClient = deferHidingClient;
@@ -360,12 +378,12 @@
                 // if made visible again.
                 wtoken.removeDeadWindows();
             } else {
-                if (!mService.mAppTransition.isTransitionSet()
-                        && mService.mAppTransition.isReady()) {
+                if (!appTransition.isTransitionSet()
+                        && appTransition.isReady()) {
                     // Add the app mOpeningApps if transition is unset but ready. This means
                     // we're doing a screen freeze, and the unfreeze will wait for all opening
                     // apps to be ready.
-                    mService.mOpeningApps.add(wtoken);
+                    displayContent.mOpeningApps.add(wtoken);
                 }
                 wtoken.startingMoved = false;
                 // If the token is currently hidden (should be the common case), or has been
@@ -395,16 +413,16 @@
 
             // If we are preparing an app transition, then delay changing
             // the visibility of this token until we execute that transition.
-            if (wtoken.okToAnimate() && mService.mAppTransition.isTransitionSet()) {
+            if (wtoken.okToAnimate() && appTransition.isTransitionSet()) {
                 wtoken.inPendingTransaction = true;
                 if (visible) {
-                    mService.mOpeningApps.add(wtoken);
+                    displayContent.mOpeningApps.add(wtoken);
                     wtoken.mEnteringAnimation = true;
                 } else {
-                    mService.mClosingApps.add(wtoken);
+                    displayContent.mClosingApps.add(wtoken);
                     wtoken.mEnteringAnimation = false;
                 }
-                if (mService.mAppTransition.getAppTransition()
+                if (appTransition.getAppTransition()
                         == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
                     // We're launchingBehind, add the launching activity to mOpeningApps.
                     final WindowState win = mContainer.getDisplayContent().findFocusedWindow();
@@ -415,7 +433,7 @@
                                     + " adding " + focusedToken + " to mOpeningApps");
                             // Force animation to be loaded.
                             focusedToken.setHidden(true);
-                            mService.mOpeningApps.add(focusedToken);
+                            displayContent.mOpeningApps.add(focusedToken);
                         }
                     }
                 }
@@ -432,9 +450,10 @@
      * of Keyguard flags it's going to set on its windows.
      */
     public void notifyUnknownVisibilityLaunched() {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer != null) {
-                mService.mUnknownAppVisibilityController.notifyLaunched(mContainer);
+                mContainer.getDisplayContent().mUnknownAppVisibilityController.notifyLaunched(
+                        mContainer);
             }
         }
     }
@@ -443,7 +462,7 @@
             CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
             IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
             boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken
                     + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
                     + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
@@ -547,7 +566,8 @@
     private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
             boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
             TaskSnapshot snapshot) {
-        if (mService.mAppTransition.getAppTransition() == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+        if (mContainer.getDisplayContent().mAppTransition.getAppTransition()
+                == TRANSIT_DOCK_TASK_FROM_RECENTS) {
             // TODO(b/34099271): Remove this statement to add back the starting window and figure
             // out why it causes flickering, the starting window appears over the thumbnail while
             // the docked from recents transition occurs
@@ -592,7 +612,7 @@
     }
 
     public void removeStartingWindow() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer.startingWindow == null) {
                 if (mContainer.startingData != null) {
                     // Starting window has not been added yet, but it is scheduled to be added.
@@ -645,7 +665,7 @@
     }
 
     public void pauseKeyDispatching() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer != null) {
                 mContainer.getDisplayContent().getInputMonitor().pauseDispatchingLw(mContainer);
             }
@@ -653,7 +673,7 @@
     }
 
     public void resumeKeyDispatching() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer != null) {
                 mContainer.getDisplayContent().getInputMonitor().resumeDispatchingLw(mContainer);
             }
@@ -661,7 +681,7 @@
     }
 
     public void notifyAppResumed(boolean wasStopped) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + mToken);
                 return;
@@ -671,7 +691,7 @@
     }
 
     public void notifyAppStopping() {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
                         + mToken);
@@ -682,7 +702,7 @@
     }
 
     public void notifyAppStopped() {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: "
                         + mToken);
@@ -693,7 +713,7 @@
     }
 
     public void startFreezingScreen(int configChanges) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 Slog.w(TAG_WM,
                         "Attempted to freeze screen with non-existing app token: " + mContainer);
@@ -710,7 +730,7 @@
     }
 
     public void stopFreezingScreen(boolean force) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 return;
             }
@@ -721,7 +741,7 @@
     }
 
     public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
                         + " token: " + mToken);
@@ -753,12 +773,112 @@
     }
 
     /**
+     * Apply override app transition base on options & animation type.
+     */
+    public void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
+        synchronized (mGlobalLock) {
+            final int animationType = pendingOptions.getAnimationType();
+            final DisplayContent displayContent = mContainer.getDisplayContent();
+            switch (animationType) {
+                case ANIM_CUSTOM:
+                    displayContent.mAppTransition.overridePendingAppTransition(
+                            pendingOptions.getPackageName(),
+                            pendingOptions.getCustomEnterResId(),
+                            pendingOptions.getCustomExitResId(),
+                            pendingOptions.getOnAnimationStartListener());
+                    break;
+                case ANIM_CLIP_REVEAL:
+                    displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
+                            pendingOptions.getStartX(), pendingOptions.getStartY(),
+                            pendingOptions.getWidth(), pendingOptions.getHeight());
+                    if (intent.getSourceBounds() == null) {
+                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                                pendingOptions.getStartY(),
+                                pendingOptions.getStartX() + pendingOptions.getWidth(),
+                                pendingOptions.getStartY() + pendingOptions.getHeight()));
+                    }
+                    break;
+                case ANIM_SCALE_UP:
+                    displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
+                            pendingOptions.getStartX(), pendingOptions.getStartY(),
+                            pendingOptions.getWidth(), pendingOptions.getHeight());
+                    if (intent.getSourceBounds() == null) {
+                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                                pendingOptions.getStartY(),
+                                pendingOptions.getStartX() + pendingOptions.getWidth(),
+                                pendingOptions.getStartY() + pendingOptions.getHeight()));
+                    }
+                    break;
+                case ANIM_THUMBNAIL_SCALE_UP:
+                case ANIM_THUMBNAIL_SCALE_DOWN:
+                    final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
+                    final GraphicBuffer buffer = pendingOptions.getThumbnail();
+                    displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
+                            pendingOptions.getStartX(), pendingOptions.getStartY(),
+                            pendingOptions.getOnAnimationStartListener(),
+                            scaleUp);
+                    if (intent.getSourceBounds() == null && buffer != null) {
+                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                                pendingOptions.getStartY(),
+                                pendingOptions.getStartX() + buffer.getWidth(),
+                                pendingOptions.getStartY() + buffer.getHeight()));
+                    }
+                    break;
+                case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
+                case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
+                    final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
+                    final IAppTransitionAnimationSpecsFuture specsFuture =
+                            pendingOptions.getSpecsFuture();
+                    if (specsFuture != null) {
+                        // TODO(multidisplay): Shouldn't be really used anymore from next CL.
+                        displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
+                                specsFuture, pendingOptions.getOnAnimationStartListener(),
+                                animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
+                    } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
+                            && specs != null) {
+                        displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
+                                specs, pendingOptions.getOnAnimationStartListener(),
+                                pendingOptions.getAnimationFinishedListener(), false);
+                    } else {
+                        displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
+                                pendingOptions.getThumbnail(),
+                                pendingOptions.getStartX(), pendingOptions.getStartY(),
+                                pendingOptions.getWidth(), pendingOptions.getHeight(),
+                                pendingOptions.getOnAnimationStartListener(),
+                                (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
+                        if (intent.getSourceBounds() == null) {
+                            intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                                    pendingOptions.getStartY(),
+                                    pendingOptions.getStartX() + pendingOptions.getWidth(),
+                                    pendingOptions.getStartY() + pendingOptions.getHeight()));
+                        }
+                    }
+                    break;
+                case ANIM_OPEN_CROSS_PROFILE_APPS:
+                    displayContent.mAppTransition
+                            .overridePendingAppTransitionStartCrossProfileApps();
+                    break;
+                case ANIM_REMOTE_ANIMATION:
+                    // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL.
+                    displayContent.mAppTransition.overridePendingAppTransitionRemote(
+                            pendingOptions.getRemoteAnimationAdapter());
+                    break;
+                case ANIM_NONE:
+                    break;
+                default:
+                    Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
+                    break;
+            }
+        }
+    }
+
+    /**
      * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP.
      * This information helps AWT know that the app is in the process of pausing before it gets the
      * signal on the WM side.
      */
     public void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index ad92f81..729f89b 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -96,7 +96,7 @@
         anim.scaleCurrentDuration(mAppToken.mService.getTransitionAnimationScaleLocked());
         mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter(
                 new WindowAnimationSpec(anim, position,
-                        mAppToken.mService.mAppTransition.canSkipFirstFrame()),
+                        mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame()),
                 mAppToken.mService.mSurfaceAnimationRunner), false /* hidden */);
     }
 
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index e38e229..9baafcb 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -111,6 +111,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.function.Consumer;
 
 class AppTokenList extends ArrayList<AppWindowToken> {
 }
@@ -500,14 +501,14 @@
                 setClientHidden(!visible);
             }
 
-            if (!mService.mClosingApps.contains(this) && !mService.mOpeningApps.contains(this)) {
+            if (!getDisplayContent().mClosingApps.contains(this)
+                    && !getDisplayContent().mOpeningApps.contains(this)) {
                 // The token is not closing nor opening, so even if there is an animation set, that
                 // doesn't mean that it goes through the normal app transition cycle so we have
                 // to inform the docked controller about visibility change.
                 // TODO(multi-display): notify docked divider on all displays where visibility was
                 // affected.
-                mService.getDefaultDisplayContentLocked().getDockedDividerController()
-                        .notifyAppVisibilityChanged();
+                getDisplayContent().getDockedDividerController().notifyAppVisibilityChanged();
 
                 // Take the screenshot before possibly hiding the WSA, otherwise the screenshot
                 // will not be taken.
@@ -524,7 +525,7 @@
             // no animation but there will still be a transition set.
             // We still need to delay hiding the surface such that it
             // can be synchronized with showing the next surface in the transition.
-            if (isHidden() && !delayed && !mService.mAppTransition.isTransitionSet()) {
+            if (isHidden() && !delayed && !getDisplayContent().mAppTransition.isTransitionSet()) {
                 SurfaceControl.openTransaction();
                 for (int i = mChildren.size() - 1; i >= 0; i--) {
                     mChildren.get(i).mWinAnimator.hide("immediately hidden");
@@ -630,14 +631,14 @@
 
         boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
 
-        mService.mOpeningApps.remove(this);
-        mService.mUnknownAppVisibilityController.appRemovedOrHidden(this);
+        getDisplayContent().mOpeningApps.remove(this);
+        getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
         mService.mTaskSnapshotController.onAppRemoved(this);
         waitingToShow = false;
-        if (mService.mClosingApps.contains(this)) {
+        if (getDisplayContent().mClosingApps.contains(this)) {
             delayed = true;
-        } else if (mService.mAppTransition.isTransitionSet()) {
-            mService.mClosingApps.add(this);
+        } else if (getDisplayContent().mAppTransition.isTransitionSet()) {
+            getDisplayContent().mClosingApps.add(this);
             delayed = true;
         }
 
@@ -652,10 +653,10 @@
         }
 
         // If this window was animating, then we need to ensure that the app transition notifies
-        // that animations have completed in WMS.handleAnimatingStoppedAndTransitionLocked(), so
-        // add to that list now
+        // that animations have completed in DisplayContent.handleAnimatingStoppedAndTransition(),
+        // so add to that list now
         if (isSelfAnimating()) {
-            mService.mNoAnimationNotifyOnTransitionFinished.add(token);
+            getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token);
         }
 
         final TaskStack stack = getStack();
@@ -795,7 +796,7 @@
             if (task == null) {
                 // It is possible we have been marked as a closing app earlier. We must remove ourselves
                 // from this list so we do not participate in any future animations.
-                mService.mClosingApps.remove(this);
+                getDisplayContent().mClosingApps.remove(this);
             } else if (mLastParent != null && mLastParent.mStack != null) {
                 task.mStack.mExitingAppTokens.remove(this);
             }
@@ -1219,7 +1220,7 @@
         if (tStartingWindow != null && fromToken.startingSurface != null) {
             // In this case, the starting icon has already been displayed, so start
             // letting windows get shown immediately without any more transitions.
-            mService.mSkipAppTransitionAnimation = true;
+            getDisplayContent().mSkipAppTransitionAnimation = true;
 
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Moving existing starting " + tStartingWindow
                     + " from " + fromToken + " to " + this);
@@ -1269,7 +1270,7 @@
                 // When transferring an animation, we no longer need to apply an animation to the
                 // the token we transfer the animation over. Thus, remove the animation from
                 // pending opening apps.
-                mService.mOpeningApps.remove(this);
+                getDisplayContent().mOpeningApps.remove(this);
 
                 mService.updateFocusedWindowLocked(
                         UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
@@ -1323,8 +1324,8 @@
         // The {@link AppWindowToken} should only specify an orientation when it is not closing or
         // going to the bottom. Allowing closing {@link AppWindowToken} to participate can lead to
         // an Activity in another task being started in the wrong orientation during the transition.
-        if (!(sendingToBottom || mService.mClosingApps.contains(this))
-                && (isVisible() || mService.mOpeningApps.contains(this))) {
+        if (!(sendingToBottom || getDisplayContent().mClosingApps.contains(this))
+                && (isVisible() || getDisplayContent().mOpeningApps.contains(this))) {
             return mOrientation;
         }
 
@@ -1398,7 +1399,7 @@
             setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "checkAppWindowsReadyToShow");
 
             // We can now show all of the drawn windows!
-            if (!mService.mOpeningApps.contains(this) && canShowWindows()) {
+            if (!getDisplayContent().mOpeningApps.contains(this) && canShowWindows()) {
                 showAllWindowsLocked();
             }
         }
@@ -1572,6 +1573,11 @@
         return forAllWindowsUnchecked(callback, traverseTopToBottom);
     }
 
+    @Override
+    void forAllAppWindows(Consumer<AppWindowToken> callback) {
+        callback.accept(this);
+    }
+
     boolean forAllWindowsUnchecked(ToBooleanFunction<WindowState> callback,
             boolean traverseTopToBottom) {
         return super.forAllWindows(callback, traverseTopToBottom);
@@ -1629,7 +1635,8 @@
         final boolean containsShowWhenLocked = containsShowWhenLockedWindow();
         if (containsDismissKeyguard != mLastContainsDismissKeyguardWindow
                 || containsShowWhenLocked != mLastContainsShowWhenLockedWindow) {
-            mService.notifyKeyguardFlagsChanged(null /* callback */);
+            mService.notifyKeyguardFlagsChanged(null /* callback */,
+                    getDisplayContent().getDisplayId());
         }
         mLastContainsDismissKeyguardWindow = containsDismissKeyguard;
         mLastContainsShowWhenLockedWindow = containsShowWhenLocked;
@@ -1787,19 +1794,20 @@
             getAnimationBounds(mTmpPoint, mTmpRect);
 
             // Delaying animation start isn't compatible with remote animations at all.
-            if (mService.mAppTransition.getRemoteAnimationController() != null
+            if (getDisplayContent().mAppTransition.getRemoteAnimationController() != null
                     && !mSurfaceAnimator.isAnimationStartDelayed()) {
-                adapter = mService.mAppTransition.getRemoteAnimationController()
+                adapter = getDisplayContent().mAppTransition.getRemoteAnimationController()
                         .createAnimationAdapter(this, mTmpPoint, mTmpRect);
             } else {
-                final int appStackClipMode = mService.mAppTransition.getAppStackClipMode();
+                final int appStackClipMode =
+                        getDisplayContent().mAppTransition.getAppStackClipMode();
                 mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM);
 
                 final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
                 if (a != null) {
                     adapter = new LocalAnimationAdapter(
                             new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
-                                    mService.mAppTransition.canSkipFirstFrame(),
+                                    getDisplayContent().mAppTransition.canSkipFirstFrame(),
                                     appStackClipMode,
                                     true /* isAppAnimation */),
                             mService.mSurfaceAnimationRunner);
@@ -1807,7 +1815,7 @@
                         mNeedsZBoost = true;
                     }
                     mTransit = transit;
-                    mTransitFlags = mService.mAppTransition.getTransitFlags();
+                    mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
                 } else {
                     adapter = null;
                 }
@@ -1877,7 +1885,7 @@
                 + " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter
                 + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
         final Configuration displayConfig = displayContent.getConfiguration();
-        final Animation a = mService.mAppTransition.loadAnimation(lp, transit, enter,
+        final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter,
                 displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets,
                 surfaceInsets, stableInsets, isVoiceInteraction, freeform, getTask().mTaskId);
         if (a != null) {
@@ -2017,7 +2025,7 @@
         final ArrayList<WindowState> children = new ArrayList<>(mChildren);
         children.forEach(WindowState::onExitAnimationDone);
 
-        mService.mAppTransition.notifyAppTransitionFinishedLocked(token);
+        getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token);
         scheduleAnimation();
     }
 
@@ -2048,8 +2056,9 @@
     }
 
     boolean isWaitingForTransitionStart() {
-        return mService.mAppTransition.isTransitionSet()
-                && (mService.mOpeningApps.contains(this) || mService.mClosingApps.contains(this));
+        return getDisplayContent().mAppTransition.isTransitionSet()
+                && (getDisplayContent().mOpeningApps.contains(this)
+                    || getDisplayContent().mClosingApps.contains(this));
     }
 
     public int getTransit() {
@@ -2066,7 +2075,7 @@
         }
         final int taskId = getTask().mTaskId;
         final GraphicBuffer thumbnailHeader =
-                mService.mAppTransition.getAppTransitionThumbnailHeader(taskId);
+                getDisplayContent().mAppTransition.getAppTransitionThumbnailHeader(taskId);
         if (thumbnailHeader == null) {
             if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "No thumbnail header bitmap for: " + taskId);
             return;
@@ -2095,14 +2104,14 @@
                 ? R.drawable.ic_account_circle
                 : R.drawable.ic_corp_badge;
         final GraphicBuffer thumbnail =
-                mService.mAppTransition
+                getDisplayContent().mAppTransition
                         .createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
         if (thumbnail == null) {
             return;
         }
         mThumbnail = new AppWindowThumbnail(getPendingTransaction(), this, thumbnail);
         final Animation animation =
-                mService.mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
+                getDisplayContent().mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
                         win.getFrameLw());
         mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left,
                 frame.top));
@@ -2119,7 +2128,7 @@
                 new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
         final Rect insets = win != null ? win.getContentInsets() : null;
         final Configuration displayConfig = mDisplayContent.getConfiguration();
-        return mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(
+        return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked(
                 appRect, insets, thumbnailHeader, getTask().mTaskId, displayConfig.uiMode,
                 displayConfig.orientation);
     }
@@ -2357,4 +2366,11 @@
         return forAllWindows(ws -> ws.mAttrs.getColorMode() != COLOR_MODE_DEFAULT,
                 true /* topToBottom */);
     }
+
+    void removeFromPendingTransition() {
+        if (isWaitingForTransitionStart() && mDisplayContent != null) {
+            mDisplayContent.mOpeningApps.remove(this);
+            mDisplayContent.mClosingApps.remove(this);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AssistDataReceiverProxy.java b/services/core/java/com/android/server/wm/AssistDataReceiverProxy.java
new file mode 100644
index 0000000..6756273
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AssistDataReceiverProxy.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.app.IAssistDataReceiver;
+import android.graphics.Bitmap;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
+
+/**
+ * Proxies assist data to the given receiver, skipping all callbacks if the receiver dies.
+ */
+class AssistDataReceiverProxy implements AssistDataRequesterCallbacks,
+        Binder.DeathRecipient {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "AssistDataReceiverProxy" : TAG_ATM;
+
+    private String mCallerPackage;
+    private IAssistDataReceiver mReceiver;
+
+    public AssistDataReceiverProxy(IAssistDataReceiver receiver, String callerPackage) {
+        mReceiver = receiver;
+        mCallerPackage = callerPackage;
+        linkToDeath();
+    }
+
+    @Override
+    public boolean canHandleReceivedAssistDataLocked() {
+        // We are forwarding, so we can always receive this data
+        return true;
+    }
+
+    @Override
+    public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
+        if (mReceiver != null) {
+            try {
+                mReceiver.onHandleAssistData(data);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to proxy assist data to receiver in package="
+                        + mCallerPackage, e);
+            }
+        }
+    }
+
+    @Override
+    public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
+        if (mReceiver != null) {
+            try {
+                mReceiver.onHandleAssistScreenshot(screenshot);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to proxy assist screenshot to receiver in package="
+                        + mCallerPackage, e);
+            }
+        }
+    }
+
+    @Override
+    public void onAssistRequestCompleted() {
+        unlinkToDeath();
+    }
+
+    @Override
+    public void binderDied() {
+        unlinkToDeath();
+    }
+
+    private void linkToDeath() {
+        try {
+            mReceiver.asBinder().linkToDeath(this, 0);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Could not link to client death", e);
+        }
+    }
+
+    private void unlinkToDeath() {
+        if (mReceiver != null) {
+            mReceiver.asBinder().unlinkToDeath(this, 0);
+        }
+        mReceiver = null;
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index fff1fa4..e358ad5 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -16,21 +16,18 @@
 
 package com.android.server.wm;
 
-import static android.graphics.PixelFormat.OPAQUE;
-import static android.view.SurfaceControl.FX_SURFACE_DIM;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
-import java.io.PrintWriter;
-
 import android.graphics.Matrix;
-import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.util.Slog;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
 
+import java.io.PrintWriter;
+
 /**
  * Four black surfaces put together to make a black frame.
  */
@@ -59,6 +56,7 @@
             transaction.setLayerStack(surface, dc.getDisplayId());
             transaction.setAlpha(surface, 1);
             transaction.setLayer(surface, layer);
+            transaction.setPosition(surface, left, top);
             transaction.show(surface);
             if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
                             "  BLACK " + surface + ": CREATE layer=" + layer);
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
new file mode 100644
index 0000000..7430f0f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import android.annotation.NonNull;
+import android.app.IApplicationThread;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.ActivityLifecycleItem;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * Class that is able to combine multiple client lifecycle transition requests and/or callbacks,
+ * and execute them as a single transaction.
+ *
+ * @see ClientTransaction
+ */
+class ClientLifecycleManager {
+    // TODO(lifecycler): Implement building transactions or global transaction.
+    // TODO(lifecycler): Use object pools for transactions and transaction items.
+
+    /**
+     * Schedule a transaction, which may consist of multiple callbacks and a lifecycle request.
+     * @param transaction A sequence of client transaction items.
+     * @throws RemoteException
+     *
+     * @see ClientTransaction
+     */
+    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
+        final IApplicationThread client = transaction.getClient();
+        transaction.schedule();
+        if (!(client instanceof Binder)) {
+            // If client is not an instance of Binder - it's a remote call and at this point it is
+            // safe to recycle the object. All objects used for local calls will be recycled after
+            // the transaction is executed on client in ActivityThread.
+            transaction.recycle();
+        }
+    }
+
+    /**
+     * Schedule a single lifecycle request or callback to client activity.
+     * @param client Target client.
+     * @param activityToken Target activity token.
+     * @param stateRequest A request to move target activity to a desired lifecycle state.
+     * @throws RemoteException
+     *
+     * @see ClientTransactionItem
+     */
+    void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
+            @NonNull ActivityLifecycleItem stateRequest) throws RemoteException {
+        final ClientTransaction clientTransaction = transactionWithState(client, activityToken,
+                stateRequest);
+        scheduleTransaction(clientTransaction);
+    }
+
+    /**
+     * Schedule a single callback delivery to client activity.
+     * @param client Target client.
+     * @param activityToken Target activity token.
+     * @param callback A request to deliver a callback.
+     * @throws RemoteException
+     *
+     * @see ClientTransactionItem
+     */
+    void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
+            @NonNull ClientTransactionItem callback) throws RemoteException {
+        final ClientTransaction clientTransaction = transactionWithCallback(client, activityToken,
+                callback);
+        scheduleTransaction(clientTransaction);
+    }
+
+    /**
+     * Schedule a single callback delivery to client application.
+     * @param client Target client.
+     * @param callback A request to deliver a callback.
+     * @throws RemoteException
+     *
+     * @see ClientTransactionItem
+     */
+    void scheduleTransaction(@NonNull IApplicationThread client,
+            @NonNull ClientTransactionItem callback) throws RemoteException {
+        final ClientTransaction clientTransaction = transactionWithCallback(client,
+                null /* activityToken */, callback);
+        scheduleTransaction(clientTransaction);
+    }
+
+    /**
+     * @return A new instance of {@link ClientTransaction} with a single lifecycle state request.
+     *
+     * @see ClientTransaction
+     * @see ClientTransactionItem
+     */
+    private static ClientTransaction transactionWithState(@NonNull IApplicationThread client,
+            @NonNull IBinder activityToken, @NonNull ActivityLifecycleItem stateRequest) {
+        final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken);
+        clientTransaction.setLifecycleStateRequest(stateRequest);
+        return clientTransaction;
+    }
+
+    /**
+     * @return A new instance of {@link ClientTransaction} with a single callback invocation.
+     *
+     * @see ClientTransaction
+     * @see ClientTransactionItem
+     */
+    private static ClientTransaction transactionWithCallback(@NonNull IApplicationThread client,
+            IBinder activityToken, @NonNull ClientTransactionItem callback) {
+        final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken);
+        clientTransaction.addCallback(callback);
+        return clientTransaction;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
new file mode 100644
index 0000000..c8f8e82
--- /dev/null
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2014 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.server.wm;
+
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+public final class CompatModePackages {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_ATM;
+    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
+
+    private final ActivityTaskManagerService mService;
+    private final AtomicFile mFile;
+
+    // Compatibility state: no longer ask user to select the mode.
+    private static final int COMPAT_FLAG_DONT_ASK = 1<<0;
+    // Compatibility state: compatibility mode is enabled.
+    private static final int COMPAT_FLAG_ENABLED = 1<<1;
+
+    private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
+
+    private static final int MSG_WRITE = 300;
+
+    private final CompatHandler mHandler;
+
+    private final class CompatHandler extends Handler {
+        public CompatHandler(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_WRITE:
+                    saveCompatModes();
+                    break;
+            }
+        }
+    };
+
+    public CompatModePackages(ActivityTaskManagerService service, File systemDir, Handler handler) {
+        mService = service;
+        mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"), "compat-mode");
+        mHandler = new CompatHandler(handler.getLooper());
+
+        FileInputStream fis = null;
+        try {
+            fis = mFile.openRead();
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, StandardCharsets.UTF_8.name());
+            int eventType = parser.getEventType();
+            while (eventType != XmlPullParser.START_TAG &&
+                    eventType != XmlPullParser.END_DOCUMENT) {
+                eventType = parser.next();
+            }
+            if (eventType == XmlPullParser.END_DOCUMENT) {
+                return;
+            }
+
+            String tagName = parser.getName();
+            if ("compat-packages".equals(tagName)) {
+                eventType = parser.next();
+                do {
+                    if (eventType == XmlPullParser.START_TAG) {
+                        tagName = parser.getName();
+                        if (parser.getDepth() == 2) {
+                            if ("pkg".equals(tagName)) {
+                                String pkg = parser.getAttributeValue(null, "name");
+                                if (pkg != null) {
+                                    String mode = parser.getAttributeValue(null, "mode");
+                                    int modeInt = 0;
+                                    if (mode != null) {
+                                        try {
+                                            modeInt = Integer.parseInt(mode);
+                                        } catch (NumberFormatException e) {
+                                        }
+                                    }
+                                    mPackages.put(pkg, modeInt);
+                                }
+                            }
+                        }
+                    }
+                    eventType = parser.next();
+                } while (eventType != XmlPullParser.END_DOCUMENT);
+            }
+        } catch (XmlPullParserException e) {
+            Slog.w(TAG, "Error reading compat-packages", e);
+        } catch (java.io.IOException e) {
+            if (fis != null) Slog.w(TAG, "Error reading compat-packages", e);
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (java.io.IOException e1) {
+                }
+            }
+        }
+    }
+
+    public HashMap<String, Integer> getPackages() {
+        return mPackages;
+    }
+
+    private int getPackageFlags(String packageName) {
+        Integer flags = mPackages.get(packageName);
+        return flags != null ? flags : 0;
+    }
+
+    public void handlePackageDataClearedLocked(String packageName) {
+        // User has explicitly asked to clear all associated data.
+        removePackage(packageName);
+    }
+
+    public void handlePackageUninstalledLocked(String packageName) {
+        // Clear settings when app is uninstalled since this is an explicit
+        // signal from the user to remove the app and all associated data.
+        removePackage(packageName);
+    }
+
+    private void removePackage(String packageName) {
+        if (mPackages.containsKey(packageName)) {
+            mPackages.remove(packageName);
+            scheduleWrite();
+        }
+    }
+
+    public void handlePackageAddedLocked(String packageName, boolean updated) {
+        ApplicationInfo ai = null;
+        try {
+            ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
+        } catch (RemoteException e) {
+        }
+        if (ai == null) {
+            return;
+        }
+        CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
+        final boolean mayCompat = !ci.alwaysSupportsScreen()
+                && !ci.neverSupportsScreen();
+
+        if (updated) {
+            // Update -- if the app no longer can run in compat mode, clear
+            // any current settings for it.
+            if (!mayCompat && mPackages.containsKey(packageName)) {
+                mPackages.remove(packageName);
+                scheduleWrite();
+            }
+        }
+    }
+
+    private void scheduleWrite() {
+        mHandler.removeMessages(MSG_WRITE);
+        Message msg = mHandler.obtainMessage(MSG_WRITE);
+        mHandler.sendMessageDelayed(msg, 10000);
+    }
+
+    public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
+        final Configuration globalConfig = mService.getGlobalConfiguration();
+        CompatibilityInfo ci = new CompatibilityInfo(ai, globalConfig.screenLayout,
+                globalConfig.smallestScreenWidthDp,
+                (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
+        //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci);
+        return ci;
+    }
+
+    public int computeCompatModeLocked(ApplicationInfo ai) {
+        final boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
+        final Configuration globalConfig = mService.getGlobalConfiguration();
+        final CompatibilityInfo info = new CompatibilityInfo(ai, globalConfig.screenLayout,
+                globalConfig.smallestScreenWidthDp, enabled);
+        if (info.alwaysSupportsScreen()) {
+            return ActivityManager.COMPAT_MODE_NEVER;
+        }
+        if (info.neverSupportsScreen()) {
+            return ActivityManager.COMPAT_MODE_ALWAYS;
+        }
+        return enabled ? ActivityManager.COMPAT_MODE_ENABLED
+                : ActivityManager.COMPAT_MODE_DISABLED;
+    }
+
+    public boolean getPackageAskCompatModeLocked(String packageName) {
+        return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
+    }
+
+    public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
+        setPackageFlagLocked(packageName, COMPAT_FLAG_DONT_ASK, ask);
+    }
+
+    private void setPackageFlagLocked(String packageName, int flag, boolean set) {
+        final int curFlags = getPackageFlags(packageName);
+        final int newFlags = set ? (curFlags & ~flag) : (curFlags | flag);
+        if (curFlags != newFlags) {
+            if (newFlags != 0) {
+                mPackages.put(packageName, newFlags);
+            } else {
+                mPackages.remove(packageName);
+            }
+            scheduleWrite();
+        }
+    }
+
+    public int getPackageScreenCompatModeLocked(String packageName) {
+        ApplicationInfo ai = null;
+        try {
+            ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
+        } catch (RemoteException e) {
+        }
+        if (ai == null) {
+            return ActivityManager.COMPAT_MODE_UNKNOWN;
+        }
+        return computeCompatModeLocked(ai);
+    }
+
+    public void setPackageScreenCompatModeLocked(String packageName, int mode) {
+        ApplicationInfo ai = null;
+        try {
+            ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0);
+        } catch (RemoteException e) {
+        }
+        if (ai == null) {
+            Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
+            return;
+        }
+        setPackageScreenCompatModeLocked(ai, mode);
+    }
+
+    void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
+        final String packageName = ai.packageName;
+
+        int curFlags = getPackageFlags(packageName);
+
+        boolean enable;
+        switch (mode) {
+            case ActivityManager.COMPAT_MODE_DISABLED:
+                enable = false;
+                break;
+            case ActivityManager.COMPAT_MODE_ENABLED:
+                enable = true;
+                break;
+            case ActivityManager.COMPAT_MODE_TOGGLE:
+                enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
+                break;
+            default:
+                Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
+                return;
+        }
+
+        int newFlags = curFlags;
+        if (enable) {
+            newFlags |= COMPAT_FLAG_ENABLED;
+        } else {
+            newFlags &= ~COMPAT_FLAG_ENABLED;
+        }
+
+        CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
+        if (ci.alwaysSupportsScreen()) {
+            Slog.w(TAG, "Ignoring compat mode change of " + packageName
+                    + "; compatibility never needed");
+            newFlags = 0;
+        }
+        if (ci.neverSupportsScreen()) {
+            Slog.w(TAG, "Ignoring compat mode change of " + packageName
+                    + "; compatibility always needed");
+            newFlags = 0;
+        }
+
+        if (newFlags != curFlags) {
+            if (newFlags != 0) {
+                mPackages.put(packageName, newFlags);
+            } else {
+                mPackages.remove(packageName);
+            }
+
+            // Need to get compatibility info in new state.
+            ci = compatibilityInfoForPackageLocked(ai);
+
+            scheduleWrite();
+
+            final ActivityStack stack = mService.getTopDisplayFocusedStack();
+            ActivityRecord starting = stack.restartPackage(packageName);
+
+            // Tell all processes that loaded this package about the change.
+            for (int i = mService.mPidMap.size() - 1; i >= 0; i--) {
+                final WindowProcessController app = mService.mPidMap.valueAt(i);
+                if (!app.mPkgList.contains(packageName)) {
+                    continue;
+                }
+                try {
+                    if (app.hasThread()) {
+                        if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
+                                + app.mName + " new compat " + ci);
+                        app.getThread().updatePackageCompatibilityInfo(packageName, ci);
+                    }
+                } catch (Exception e) {
+                }
+            }
+
+            if (starting != null) {
+                starting.ensureActivityConfiguration(0 /* globalChanges */,
+                        false /* preserveWindow */);
+                // And we need to make sure at this point that all other activities
+                // are made visible with the correct configuration.
+                stack.ensureActivitiesVisibleLocked(starting, 0, !PRESERVE_WINDOWS);
+            }
+        }
+    }
+
+    private void saveCompatModes() {
+        HashMap<String, Integer> pkgs;
+        synchronized (mService.mGlobalLock) {
+            pkgs = new HashMap<>(mPackages);
+        }
+
+        FileOutputStream fos = null;
+
+        try {
+            fos = mFile.startWrite();
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(fos, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            out.startTag(null, "compat-packages");
+
+            final IPackageManager pm = AppGlobals.getPackageManager();
+            final Configuration globalConfig = mService.getGlobalConfiguration();
+            final int screenLayout = globalConfig.screenLayout;
+            final int smallestScreenWidthDp = globalConfig.smallestScreenWidthDp;
+            final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
+            while (it.hasNext()) {
+                Map.Entry<String, Integer> entry = it.next();
+                String pkg = entry.getKey();
+                int mode = entry.getValue();
+                if (mode == 0) {
+                    continue;
+                }
+                ApplicationInfo ai = null;
+                try {
+                    ai = pm.getApplicationInfo(pkg, 0, 0);
+                } catch (RemoteException e) {
+                }
+                if (ai == null) {
+                    continue;
+                }
+                CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout,
+                        smallestScreenWidthDp, false);
+                if (info.alwaysSupportsScreen()) {
+                    continue;
+                }
+                if (info.neverSupportsScreen()) {
+                    continue;
+                }
+                out.startTag(null, "pkg");
+                out.attribute(null, "name", pkg);
+                out.attribute(null, "mode", Integer.toString(mode));
+                out.endTag(null, "pkg");
+            }
+
+            out.endTag(null, "compat-packages");
+            out.endDocument();
+
+            mFile.finishWrite(fos);
+        } catch (java.io.IOException e1) {
+            Slog.w(TAG, "Error writing compat packages", e1);
+            if (fos != null) {
+                mFile.failWrite(fos);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DeprecatedTargetSdkVersionDialog.java b/services/core/java/com/android/server/wm/DeprecatedTargetSdkVersionDialog.java
new file mode 100644
index 0000000..37244bd
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DeprecatedTargetSdkVersionDialog.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.internal.R;
+import com.android.server.utils.AppInstallerUtil;
+
+public class DeprecatedTargetSdkVersionDialog {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "DeprecatedTargetSdkVersionDialog" : TAG_ATM;
+
+    private final AlertDialog mDialog;
+    private final String mPackageName;
+
+    public DeprecatedTargetSdkVersionDialog(final AppWarnings manager, Context context,
+            ApplicationInfo appInfo) {
+        mPackageName = appInfo.packageName;
+
+        final PackageManager pm = context.getPackageManager();
+        final CharSequence label = appInfo.loadSafeLabel(pm,
+                PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE
+                        | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
+        final CharSequence message = context.getString(R.string.deprecated_target_sdk_message);
+
+        final AlertDialog.Builder builder = new AlertDialog.Builder(context)
+                .setPositiveButton(R.string.ok, (dialog, which) ->
+                    manager.setPackageFlag(
+                            mPackageName, AppWarnings.FLAG_HIDE_DEPRECATED_SDK, true))
+                .setMessage(message)
+                .setTitle(label);
+
+        // If we might be able to update the app, show a button.
+        final Intent installerIntent = AppInstallerUtil.createIntent(context, appInfo.packageName);
+        if (installerIntent != null) {
+            builder.setNeutralButton(R.string.deprecated_target_sdk_app_store,
+                    (dialog, which) -> {
+                        context.startActivity(installerIntent);
+                    });
+        }
+
+        // Ensure the content view is prepared.
+        mDialog = builder.create();
+        mDialog.create();
+
+        final Window window = mDialog.getWindow();
+        window.setType(WindowManager.LayoutParams.TYPE_PHONE);
+
+        // DO NOT MODIFY. Used by CTS to verify the dialog is displayed.
+        window.getAttributes().setTitle("DeprecatedTargetSdkVersionDialog");
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public void show() {
+        Log.w(TAG, "Showing SDK deprecation warning for package " + mPackageName);
+        mDialog.show();
+    }
+
+    public void dismiss() {
+        mDialog.dismiss();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ca23360..fece980 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -65,6 +65,7 @@
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.DisplayContentProto.ABOVE_APP_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
 import static com.android.server.wm.DisplayContentProto.BELOW_APP_WINDOWS;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
@@ -80,6 +81,7 @@
 import static com.android.server.wm.DisplayContentProto.SURFACE_SIZE;
 import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
@@ -119,7 +121,9 @@
 import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
 
+import android.animation.AnimationHandler;
 import android.annotation.CallSuper;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
@@ -137,6 +141,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
+import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.Slog;
@@ -152,6 +157,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
+import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -162,6 +168,8 @@
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -183,6 +191,18 @@
         implements WindowManagerPolicy.DisplayContentInfo {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
 
+    /** The default scaling mode that scales content automatically. */
+    static final int FORCE_SCALING_MODE_AUTO = 0;
+    /** For {@link #setForcedScalingMode} to apply flag {@link Display#FLAG_SCALING_DISABLED}. */
+    static final int FORCE_SCALING_MODE_DISABLED = 1;
+
+    @IntDef(prefix = { "FORCE_SCALING_MODE_" }, value = {
+            FORCE_SCALING_MODE_AUTO,
+            FORCE_SCALING_MODE_DISABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ForceScalingMode {}
+
     /** Unique identifier of this stack. */
     private final int mDisplayId;
 
@@ -212,6 +232,21 @@
     private boolean mTmpInitial;
     private int mMaxUiWidth;
 
+    final AppTransition mAppTransition;
+    final AppTransitionController mAppTransitionController;
+    boolean mSkipAppTransitionAnimation = false;
+
+    final ArraySet<AppWindowToken> mOpeningApps = new ArraySet<>();
+    final ArraySet<AppWindowToken> mClosingApps = new ArraySet<>();
+    final UnknownAppVisibilityController mUnknownAppVisibilityController;
+    BoundsAnimationController mBoundsAnimationController;
+
+    /**
+     * List of clients without a transtiton animation that we notify once we are done
+     * transitioning since they won't be notified through the app window animator.
+     */
+    final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>();
+
     // Mapping from a token IBinder to a WindowToken object on this display.
     private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();
 
@@ -237,6 +272,11 @@
      * @see WindowManagerService#setForcedDisplayDensityForUser(int, int, int)
      */
     int mBaseDisplayDensity = 0;
+
+    /**
+     * Whether to disable display scaling. This can be set via shell command "adb shell wm scaling".
+     * @see WindowManagerService#setForcedDisplayScalingMode(int, int)
+     */
     boolean mDisplayScalingDisabled;
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
     private final Display mDisplay;
@@ -274,7 +314,7 @@
      * Last applied orientation of the display.
      * Constants as per {@link android.content.pm.ActivityInfo.ScreenOrientation}.
      *
-     * @see WindowManagerService#updateOrientationFromAppTokensLocked(boolean, int)
+     * @see #updateOrientationFromAppTokens()
      */
     private int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
 
@@ -800,6 +840,15 @@
         mDividerControllerLocked = new DockedStackDividerController(service, this);
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
 
+        mAppTransition = new AppTransition(service.mContext, service, this);
+        mAppTransition.registerListenerLocked(service.mActivityManagerAppTransitionNotifier);
+        mAppTransitionController = new AppTransitionController(service, this);
+        mUnknownAppVisibilityController = new UnknownAppVisibilityController(service, this);
+
+        AnimationHandler animationHandler = new AnimationHandler();
+        mBoundsAnimationController = new BoundsAnimationController(service.mContext,
+                mAppTransition, SurfaceAnimationThread.getHandler(), animationHandler);
+
         // We use this as our arbitrary surface size for buffer-less parents
         // that don't impose cropping on their children. It may need to be larger
         // than the display size because fullscreen windows can be shifted offscreen
@@ -836,6 +885,7 @@
         // {@link DisplayContent} ready for use.
         mDisplayReady = true;
 
+        mService.mAnimator.addDisplayLocked(mDisplayId);
         mInputMonitor = new InputMonitor(service, mDisplayId);
 
         if (mService.mInputManager != null) {
@@ -995,18 +1045,10 @@
         return mLastOrientation;
     }
 
-    void setLastOrientation(int orientation) {
-        mLastOrientation = orientation;
-    }
-
     boolean getAltOrientation() {
         return mAltOrientation;
     }
 
-    void setAltOrientation(boolean altOrientation) {
-        mAltOrientation = altOrientation;
-    }
-
     int getLastWindowForcedOrientation() {
         return mLastWindowForcedOrientation;
     }
@@ -1059,6 +1101,34 @@
         return true;
     }
 
+    /** Notify the configuration change of this display. */
+    void sendNewConfiguration() {
+        mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
+    }
+
+    /**
+     * Determine the new desired orientation of this display.
+     *
+     * The orientation is computed from non-application windows first. If none of the
+     * non-application windows specify orientation, the orientation is computed from application
+     * tokens.
+     *
+     * @return {@code true} if the orientation is changed.
+     */
+    boolean updateOrientationFromAppTokens() {
+        return updateOrientationFromAppTokens(false /* forceUpdate */);
+    }
+
+    boolean updateOrientationFromAppTokens(boolean forceUpdate) {
+        final int req = getOrientation();
+        if (req != mLastOrientation || forceUpdate) {
+            mLastOrientation = req;
+            mDisplayRotation.setCurrentOrientation(req);
+            return updateRotationUnchecked(forceUpdate);
+        }
+        return false;
+    }
+
     /**
      * Update rotation of the display and send configuration if the rotation is changed.
      *
@@ -1067,7 +1137,7 @@
     boolean updateRotationAndSendNewConfigIfNeeded() {
         final boolean changed = updateRotationUnchecked(false /* forceUpdate */);
         if (changed) {
-            mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
+            sendNewConfiguration();
         }
         return changed;
     }
@@ -1083,14 +1153,19 @@
     }
 
     /**
-     * Update rotation of the display with an option to force the update.
+     * Update rotation of the DisplayContent with an option to force the update. This updates
+     * the container's perception of rotation and, depending on the top activities, will freeze
+     * the screen or start seamless rotation. The display itself gets rotated in
+     * {@link #applyRotationLocked} during {@link WindowManagerService#sendNewConfiguration}.
+     *
      * @param forceUpdate Force the rotation update. Sometimes in WM we might skip updating
      *                    orientation because we're waiting for some rotation to finish or display
      *                    to unfreeze, which results in configuration of the previously visible
      *                    activity being applied to a newly visible one. Forcing the rotation
      *                    update allows to workaround this issue.
      * @return {@code true} if the rotation has been changed.  In this case YOU MUST CALL
-     *         {@link WindowManagerService#sendNewConfiguration(int)} TO UNFREEZE THE SCREEN.
+     *         {@link WindowManagerService#sendNewConfiguration(int)} TO COMPLETE THE ROTATION AND
+     *         UNFREEZE THE SCREEN.
      */
     boolean updateRotationUnchecked(boolean forceUpdate) {
         ScreenRotationAnimation screenRotationAnimation;
@@ -1188,7 +1263,7 @@
             mService.mWaitingForConfig = true;
         }
 
-        setRotation(rotation);
+        mRotation = rotation;
         mAltOrientation = altOrientation;
 
         mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
@@ -1202,18 +1277,29 @@
         if (!rotateSeamlessly) {
             mService.startFreezingDisplayLocked(anim[0], anim[1], this);
             // startFreezingDisplayLocked can reset the ScreenRotationAnimation.
-            screenRotationAnimation = mService.mAnimator.getScreenRotationAnimationLocked(
-                    mDisplayId);
         } else {
             // The screen rotation animation uses a screenshot to freeze the screen
             // while windows resize underneath.
             // When we are rotating seamlessly, we allow the elements to transition
             // to their rotated state independently and without a freeze required.
-            screenRotationAnimation = null;
-
             mService.startSeamlessRotation();
         }
 
+        return true;
+    }
+
+    /**
+     * Applies the rotation transaction. This must be called after {@link #updateRotationUnchecked}
+     * (if it returned {@code true}) to actually finish the rotation.
+     *
+     * @param oldRotation the rotation we are coming from.
+     * @param rotation the rotation to apply.
+     */
+    void applyRotationLocked(final int oldRotation, final int rotation) {
+        mDisplayRotation.setRotation(rotation);
+        final boolean rotateSeamlessly = mService.isRotatingSeamlessly();
+        ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly
+                ? null : mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
         // We need to update our screen size information to match the new rotation. If the rotation
         // has actually changed then this method will return true and, according to the comment at
         // the top of the method, the caller is obligated to call computeNewConfigurationLocked().
@@ -1274,8 +1360,6 @@
                 && isDefaultDisplay) {
             mService.mAccessibilityController.onRotationChangedLocked(this);
         }
-
-        return true;
     }
 
     void configureDisplayPolicy() {
@@ -1385,7 +1469,6 @@
                     mCompatDisplayMetrics);
         }
 
-        updateBounds();
         return mDisplayInfo;
     }
 
@@ -1419,11 +1502,14 @@
      */
     void computeScreenConfiguration(Configuration config) {
         final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode);
+        calculateBounds(displayInfo, mTmpBounds);
+        config.windowConfiguration.setBounds(mTmpBounds);
 
         final int dw = displayInfo.logicalWidth;
         final int dh = displayInfo.logicalHeight;
         config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
         config.windowConfiguration.setWindowingMode(getWindowingMode());
+        config.windowConfiguration.setRotation(displayInfo.rotation);
 
         final float density = mDisplayMetrics.density;
         config.screenWidthDp =
@@ -1455,7 +1541,7 @@
         config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
         config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
         config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, config.uiMode, dw,
-                dh, mDisplayId);
+                dh, displayInfo.displayCutout, mDisplayId);
         config.densityDpi = displayInfo.logicalDensityDpi;
 
         config.colorMode =
@@ -1532,7 +1618,7 @@
     }
 
     private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh,
-            int displayId) {
+            DisplayCutout displayCutout, int displayId) {
         mTmpDisplayMetrics.setTo(mDisplayMetrics);
         final DisplayMetrics tmpDm = mTmpDisplayMetrics;
         final int unrotDw, unrotDh;
@@ -1544,22 +1630,22 @@
             unrotDh = dh;
         }
         int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, unrotDh,
-                displayId);
+                displayCutout, displayId);
         sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, unrotDw,
-                displayId);
+                displayCutout, displayId);
         sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, unrotDh,
-                displayId);
+                displayCutout, displayId);
         sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, unrotDw,
-                displayId);
+                displayCutout, displayId);
         return sw;
     }
 
     private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode,
-            DisplayMetrics dm, int dw, int dh, int displayId) {
+            DisplayMetrics dm, int dw, int dh, DisplayCutout displayCutout, int displayId) {
         dm.noncompatWidthPixels = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
-                displayId, mDisplayInfo.displayCutout);
+                displayId, displayCutout);
         dm.noncompatHeightPixels = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation,
-                uiMode, displayId, mDisplayInfo.displayCutout);
+                uiMode, displayId, displayCutout);
         float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
         int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f);
         if (curSize == 0 || size < curSize) {
@@ -1597,24 +1683,24 @@
                 unrotDw);
         int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
         sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode,
-                displayId);
+                displayInfo.displayCutout, displayId);
         sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode,
-                displayId);
+                displayInfo.displayCutout, displayId);
         sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode,
-                displayId);
+                displayInfo.displayCutout, displayId);
         sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode,
-                displayId);
+                displayInfo.displayCutout, displayId);
         outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
         outConfig.screenLayout = sl;
     }
 
     private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh,
-            int uiMode, int displayId) {
+            int uiMode, DisplayCutout displayCutout, int displayId) {
         // Get the app screen size at this rotation.
         int w = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayId,
-                mDisplayInfo.displayCutout);
+                displayCutout);
         int h = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayId,
-                mDisplayInfo.displayCutout);
+                displayCutout);
 
         // Compute the screen layout size class for this rotation.
         int longSize = w;
@@ -1788,11 +1874,26 @@
     public void onConfigurationChanged(Configuration newParentConfig) {
         super.onConfigurationChanged(newParentConfig);
 
+        // If there was no pinned stack, we still need to notify the controller of the display info
+        // update as a result of the config change.
+        if (mPinnedStackControllerLocked != null && !hasPinnedStack()) {
+            mPinnedStackControllerLocked.onDisplayInfoChanged(getDisplayInfo());
+        }
+
         // The display size information is heavily dependent on the resources in the current
         // configuration, so we need to reconfigure it every time the configuration changes.
-        // See {@link PhoneWindowManager#setInitialDisplaySize}...sigh...
+        // See {@link #configureDisplayPolicy}...sigh...
         mService.reconfigureDisplayLocked(this);
 
+    }
+
+    /**
+     * Updates the resources used by docked/pinned controllers. This needs to be called at the
+     * beginning of a configuration update cascade since the metrics from these resources are used
+     * for bounds calculations. Since ActivityDisplay initiates the configuration update, this
+     * should be called from there instead of DisplayContent's onConfigurationChanged.
+     */
+    void preOnConfigurationChanged() {
         final DockedStackDividerController dividerController = getDockedDividerController();
 
         if (dividerController != null) {
@@ -1806,26 +1907,6 @@
         }
     }
 
-    /**
-     * Callback used to trigger bounds update after configuration change and get ids of stacks whose
-     * bounds were updated.
-     */
-    void updateStackBoundsAfterConfigChange(@NonNull List<TaskStack> changedStackList) {
-        for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
-            final TaskStack stack = mTaskStackContainers.getChildAt(i);
-            if (stack.updateBoundsAfterConfigChange()) {
-                changedStackList.add(stack);
-            }
-        }
-
-        // If there was no pinned stack, we still need to notify the controller of the display info
-        // update as a result of the config change.  We do this here to consolidate the flow between
-        // changes when there is and is not a stack.
-        if (!hasPinnedStack()) {
-            mPinnedStackControllerLocked.onDisplayInfoChanged();
-        }
-    }
-
     @Override
     boolean fillsParent() {
         return true;
@@ -2027,6 +2108,68 @@
         updateBounds();
     }
 
+    /**
+     * Forces this display to use the specified density.
+     *
+     * @param density The density in DPI to use. If the value equals to initial density, the setting
+     *                will be cleared.
+     * @param userId The target user to apply. Only meaningful when this is default display. If the
+     *               user id is {@link UserHandle#USER_CURRENT}, it means to apply current settings
+     *               so only need to configure display.
+     */
+    void setForcedDensity(int density, int userId) {
+        final boolean clear = density == mInitialDisplayDensity;
+        final boolean updateCurrent = userId == UserHandle.USER_CURRENT;
+        if (mService.mCurrentUserId == userId || updateCurrent) {
+            mBaseDisplayDensity = density;
+            mService.reconfigureDisplayLocked(this);
+        }
+        if (updateCurrent) {
+            // We are applying existing settings so no need to save it again.
+            return;
+        }
+
+        if (density == mInitialDisplayDensity) {
+            density = 0;
+        }
+        mService.mDisplaySettings.setForcedDensity(this, density, userId);
+    }
+
+    /** @param mode {@link #FORCE_SCALING_MODE_AUTO} or {@link #FORCE_SCALING_MODE_DISABLED}. */
+    void setForcedScalingMode(@ForceScalingMode int mode) {
+        if (mode != FORCE_SCALING_MODE_DISABLED) {
+            mode = FORCE_SCALING_MODE_AUTO;
+        }
+
+        mDisplayScalingDisabled = (mode != FORCE_SCALING_MODE_AUTO);
+        Slog.i(TAG_WM, "Using display scaling mode: " + (mDisplayScalingDisabled ? "off" : "auto"));
+        mService.reconfigureDisplayLocked(this);
+
+        mService.mDisplaySettings.setForcedScalingMode(this, mode);
+    }
+
+    /** If the given width and height equal to initial size, the setting will be cleared. */
+    void setForcedSize(int width, int height) {
+        final boolean clear = mInitialDisplayWidth == width && mInitialDisplayHeight == height;
+        if (!clear) {
+            // Set some sort of reasonable bounds on the size of the display that we will try
+            // to emulate.
+            final int minSize = 200;
+            final int maxScale = 2;
+            width = Math.min(Math.max(width, minSize), mInitialDisplayWidth * maxScale);
+            height = Math.min(Math.max(height, minSize), mInitialDisplayHeight * maxScale);
+        }
+
+        Slog.i(TAG_WM, "Using new display size: " + width + "x" + height);
+        updateBaseDisplayMetrics(width, height, mBaseDisplayDensity);
+        mService.reconfigureDisplayLocked(this);
+
+        if (clear) {
+            width = height = 0;
+        }
+        mService.mDisplaySettings.setForcedSize(this, width, height);
+    }
+
     void getStableRect(Rect out) {
         out.set(mDisplayFrames.mStable);
     }
@@ -2051,6 +2194,9 @@
                     + " to its current displayId=" + mDisplayId);
         }
 
+        // Clean up all pending transitions when stack reparent to another display.
+        stack.forAllAppWindows(AppWindowToken::removeFromPendingTransition);
+
         prevDc.mTaskStackContainers.removeChild(stack);
         mTaskStackContainers.addStackToDisplay(stack, onTop);
     }
@@ -2210,6 +2356,13 @@
     void removeImmediately() {
         mRemovingDisplay = true;
         try {
+            // Clear all transitions & screen frozen states when removing display.
+            mOpeningApps.clear();
+            mClosingApps.clear();
+            mUnknownAppVisibilityController.clear();
+            mAppTransition.removeAppTransitionTimeoutCallbacks();
+            handleAnimatingStoppedAndTransition();
+            mService.stopFreezingDisplayLocked();
             super.removeImmediately();
             if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
             if (mPointerEventDispatcher != null && mTapDetector != null) {
@@ -2221,11 +2374,12 @@
             mWindowingLayer.release();
             mOverlayLayer.release();
         } finally {
+            mDisplayReady = false;
             mRemovingDisplay = false;
         }
 
         mInputMonitor.onRemoved();
-        mService.onDisplayRemoved(mDisplayId);
+        mService.mWindowPlacerLocked.requestTraversal();
     }
 
     /** Returns true if a removal action is still being deferred. */
@@ -2351,11 +2505,15 @@
 
     void rotateBounds(int oldRotation, int newRotation, Rect bounds) {
         getBounds(mTmpRect, newRotation);
+        rotateBounds(mTmpRect, oldRotation, newRotation, bounds);
+    }
 
+    void rotateBounds(Rect parentBounds, int oldRotation, int newRotation, Rect bounds) {
         // Compute a transform matrix to undo the coordinate space transformation,
         // and present the window at the same physical position it previously occupied.
         final int deltaRotation = deltaRotation(newRotation, oldRotation);
-        createRotationMatrix(deltaRotation, mTmpRect.width(), mTmpRect.height(), mTmpMatrix);
+        createRotationMatrix(
+                deltaRotation, parentBounds.width(), parentBounds.height(), mTmpMatrix);
 
         mTmpRectF.set(bounds);
         mTmpMatrix.mapRect(mTmpRectF);
@@ -2430,6 +2588,7 @@
             screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
         }
         mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
+        mAppTransition.writeToProto(proto, APP_TRANSITION);
         proto.write(SURFACE_SIZE, mSurfaceSize);
         if (mFocusedApp != null) {
             mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
@@ -2914,11 +3073,10 @@
                 }
 
                 if (highestTarget != null) {
-                    final AppTransition appTransition = mService.mAppTransition;
-                    if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, appTransition + " " + highestTarget
+                    if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, mAppTransition + " " + highestTarget
                             + " animating=" + highestTarget.isAnimating());
 
-                    if (appTransition.isTransitionSet()) {
+                    if (mAppTransition.isTransitionSet()) {
                         // If we are currently setting up for an animation, hold everything until we
                         // can find out what will happen.
                         setInputMethodTarget(highestTarget, true);
@@ -3009,6 +3167,18 @@
                 pw.println();
             }
         }
+
+        if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty()) {
+            pw.println();
+            if (mOpeningApps.size() > 0) {
+                pw.print("  mOpeningApps="); pw.println(mOpeningApps);
+            }
+            if (mClosingApps.size() > 0) {
+                pw.print("  mClosingApps="); pw.println(mClosingApps);
+            }
+        }
+
+        mUnknownAppVisibilityController.dump(pw, "  ");
     }
 
     void dumpWindowAnimators(PrintWriter pw, String subPrefix) {
@@ -3214,9 +3384,9 @@
 
             if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
                 if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
-                if (mService.updateOrientationFromAppTokensLocked(mDisplayId)) {
+                if (updateOrientationFromAppTokens()) {
                     setLayoutNeeded();
-                    mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
+                    sendNewConfiguration();
                 }
             }
 
@@ -3271,27 +3441,27 @@
     }
 
     private void updateBounds() {
-        calculateBounds(mTmpBounds);
+        calculateBounds(mDisplayInfo, mTmpBounds);
         setBounds(mTmpBounds);
     }
 
     // Determines the current display bounds based on the current state
-    private void calculateBounds(Rect out) {
+    private void calculateBounds(DisplayInfo displayInfo, Rect out) {
         // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked.
-        final int orientation = mDisplayInfo.rotation;
-        boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270);
+        final int rotation = displayInfo.rotation;
+        boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
         final int physWidth = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
         final int physHeight = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
-        int width = mDisplayInfo.logicalWidth;
+        int width = displayInfo.logicalWidth;
         int left = (physWidth - width) / 2;
-        int height = mDisplayInfo.logicalHeight;
+        int height = displayInfo.logicalHeight;
         int top = (physHeight - height) / 2;
         out.set(left, top, left + width, top + height);
     }
 
     @Override
     public void getBounds(Rect out) {
-        calculateBounds(out);
+        calculateBounds(mDisplayInfo, out);
     }
 
     private void getBounds(Rect out, int orientation) {
@@ -3905,7 +4075,7 @@
                 final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens;
                 for (int j = appTokens.size() - 1; j >= 0; --j) {
                     final AppWindowToken token = appTokens.get(j);
-                    if (!token.hasVisible && !mService.mClosingApps.contains(token)
+                    if (!token.hasVisible && !mClosingApps.contains(token)
                             && (!token.mIsExiting || token.isEmpty())) {
                         // Make sure there is no animation running on this token, so any windows
                         // associated with it will be removed as soon as their animations are
@@ -4203,8 +4373,8 @@
             // Only allow force setting the orientation when all unknown visibilities have been
             // resolved, as otherwise we just may be starting another occluding activity.
             final boolean isUnoccluding =
-                    mService.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
-                            && mService.mUnknownAppVisibilityController.allResolved();
+                    mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
+                            && mUnknownAppVisibilityController.allResolved();
             if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
                 return mLastKeyguardForcedOrientation;
             }
@@ -4444,4 +4614,56 @@
             mPointerEventDispatcher.unregisterInputEventListener(listener);
         }
     }
+
+    void prepareAppTransition(@WindowManager.TransitionType int transit,
+            boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags,
+            boolean forceOverride) {
+        final boolean prepared = mAppTransition.prepareAppTransitionLocked(
+                transit, alwaysKeepCurrent, flags, forceOverride);
+        if (prepared && okToAnimate()) {
+            mSkipAppTransitionAnimation = false;
+        }
+    }
+
+    void executeAppTransition() {
+        if (mAppTransition.isTransitionSet()) {
+            if (DEBUG_APP_TRANSITIONS) {
+                Slog.w(TAG_WM, "Execute app transition: " + mAppTransition + ", displayId: "
+                        + mDisplayId + " Callers=" + Debug.getCallers(5));
+            }
+            mAppTransition.setReady();
+            mService.mWindowPlacerLocked.requestTraversal();
+        }
+    }
+
+    /**
+     * Update pendingLayoutChanges after app transition has finished.
+     */
+    void handleAnimatingStoppedAndTransition() {
+        int changes = 0;
+
+        mAppTransition.setIdle();
+
+        for (int i = mNoAnimationNotifyOnTransitionFinished.size() - 1; i >= 0; i--) {
+            final IBinder token = mNoAnimationNotifyOnTransitionFinished.get(i);
+            mAppTransition.notifyAppTransitionFinishedLocked(token);
+        }
+        mNoAnimationNotifyOnTransitionFinished.clear();
+
+        mWallpaperController.hideDeferredWallpapersIfNeeded();
+
+        onAppTransitionDone();
+
+        changes |= FINISH_LAYOUT_REDO_LAYOUT;
+        if (DEBUG_WALLPAPER_LIGHT) {
+            Slog.v(TAG_WM, "Wallpaper layer changed: assigning layers + relayout");
+        }
+        computeImeTarget(true /* updateImeTarget */);
+        mService.mRoot.mWallpaperMayChange = true;
+        // Since the window list has been rebuilt, focus might have to be recomputed since the
+        // actual order of windows might have changed again.
+        mService.mFocusMayChange = true;
+
+        pendingLayoutChanges |= changes;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 847cff9..9f98dc5 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -269,7 +269,6 @@
         if (changed) {
             mService.updateRotation(true /* alwaysSendConfiguration */,
                     false /* forceRelayout */);
-            mService.mDisplaySettings.writeSettingsLocked();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplaySettings.java b/services/core/java/com/android/server/wm/DisplaySettings.java
index 28dc008..44956ab 100644
--- a/services/core/java/com/android/server/wm/DisplaySettings.java
+++ b/services/core/java/com/android/server/wm/DisplaySettings.java
@@ -16,12 +16,14 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO;
+import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.app.WindowConfiguration;
-import android.graphics.Rect;
 import android.os.Environment;
+import android.provider.Settings;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml;
@@ -33,6 +35,7 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.DisplayContent.ForceScalingMode;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -65,17 +68,24 @@
         private int mWindowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
         private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
         private int mUserRotation = Surface.ROTATION_0;
+        private int mForcedWidth;
+        private int mForcedHeight;
+        private int mForcedDensity;
+        private int mForcedScalingMode = FORCE_SCALING_MODE_AUTO;
 
         private Entry(String _name) {
             mName = _name;
         }
 
+        /** @return {@code true} if all values are default. */
         private boolean isEmpty() {
             return mOverscanLeft == 0 && mOverscanTop == 0 && mOverscanRight == 0
                     && mOverscanBottom == 0
                     && mWindowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
                     && mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
-                    && mUserRotation == Surface.ROTATION_0;
+                    && mUserRotation == Surface.ROTATION_0
+                    && mForcedWidth == 0 && mForcedHeight == 0 && mForcedDensity == 0
+                    && mForcedScalingMode == FORCE_SCALING_MODE_AUTO;
         }
     }
 
@@ -87,64 +97,84 @@
     DisplaySettings(WindowManagerService service, File folder) {
         mService = service;
         mFile = new AtomicFile(new File(folder, "display_settings.xml"), "wm-displays");
+        readSettings();
     }
 
-    private Entry getEntry(String name, String uniqueId) {
+    private Entry getEntry(DisplayInfo displayInfo) {
         // Try to get the entry with the unique if possible.
         // Else, fall back on the display name.
         Entry entry;
-        if (uniqueId == null || (entry = mEntries.get(uniqueId)) == null) {
-            entry = mEntries.get(name);
+        if (displayInfo.uniqueId == null || (entry = mEntries.get(displayInfo.uniqueId)) == null) {
+            entry = mEntries.get(displayInfo.name);
         }
         return entry;
     }
 
-    private Entry getOrCreateEntry(String uniqueId, String name) {
-        Entry entry = getEntry(uniqueId, name);
-        if (entry == null) {
-            entry = new Entry(uniqueId);
-            mEntries.put(uniqueId, entry);
-        }
-        return entry;
+    private Entry getOrCreateEntry(DisplayInfo displayInfo) {
+        final Entry entry = getEntry(displayInfo);
+        return entry != null ? entry : new Entry(displayInfo.uniqueId);
     }
 
-    private void removeEntryIfEmpty(String uniqueId, String name) {
-        final Entry entry = getEntry(uniqueId, name);
-        if (entry.isEmpty()) {
-            mEntries.remove(uniqueId);
-            mEntries.remove(name);
-        }
-    }
-
-    private void getOverscanLocked(String name, String uniqueId, Rect outRect) {
-        final Entry entry = getEntry(name, uniqueId);
-        if (entry != null) {
-            outRect.left = entry.mOverscanLeft;
-            outRect.top = entry.mOverscanTop;
-            outRect.right = entry.mOverscanRight;
-            outRect.bottom = entry.mOverscanBottom;
-        } else {
-            outRect.set(0, 0, 0, 0);
-        }
-    }
-
-    void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
-            int bottom) {
-        Entry entry = mEntries.get(uniqueId);
-        if (left == 0 && top == 0 && right == 0 && bottom == 0 && entry == null) {
-            // All default value, no action needed.
-            return;
-        }
-        entry = getOrCreateEntry(uniqueId, name);
+    void setOverscanLocked(DisplayInfo displayInfo, int left, int top, int right, int bottom) {
+        final Entry entry = getOrCreateEntry(displayInfo);
         entry.mOverscanLeft = left;
         entry.mOverscanTop = top;
         entry.mOverscanRight = right;
         entry.mOverscanBottom = bottom;
-        removeEntryIfEmpty(uniqueId, name);
+        writeSettingsIfNeeded(entry, displayInfo);
     }
 
-    private int getWindowingModeLocked(String name, String uniqueId, int displayId) {
-        final Entry entry = getEntry(name, uniqueId);
+    void setUserRotation(DisplayContent displayContent, int rotationMode, int rotation) {
+        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final Entry entry = getOrCreateEntry(displayInfo);
+        entry.mUserRotationMode = rotationMode;
+        entry.mUserRotation = rotation;
+        writeSettingsIfNeeded(entry, displayInfo);
+    }
+
+    void setForcedSize(DisplayContent displayContent, int width, int height) {
+        if (displayContent.isDefaultDisplay) {
+            final String sizeString = (width == 0 || height == 0) ? "" : (width + "," + height);
+            Settings.Global.putString(mService.mContext.getContentResolver(),
+                    Settings.Global.DISPLAY_SIZE_FORCED, sizeString);
+            return;
+        }
+
+        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final Entry entry = getOrCreateEntry(displayInfo);
+        entry.mForcedWidth = width;
+        entry.mForcedHeight = height;
+        writeSettingsIfNeeded(entry, displayInfo);
+    }
+
+    void setForcedDensity(DisplayContent displayContent, int density, int userId) {
+        if (displayContent.isDefaultDisplay) {
+            final String densityString = density == 0 ? "" : Integer.toString(density);
+            Settings.Secure.putStringForUser(mService.mContext.getContentResolver(),
+                    Settings.Secure.DISPLAY_DENSITY_FORCED, densityString, userId);
+            return;
+        }
+
+        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final Entry entry = getOrCreateEntry(displayInfo);
+        entry.mForcedDensity = density;
+        writeSettingsIfNeeded(entry, displayInfo);
+    }
+
+    void setForcedScalingMode(DisplayContent displayContent, @ForceScalingMode int mode) {
+        if (displayContent.isDefaultDisplay) {
+            Settings.Global.putInt(mService.mContext.getContentResolver(),
+                    Settings.Global.DISPLAY_SCALING_FORCE, mode);
+            return;
+        }
+
+        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final Entry entry = getOrCreateEntry(displayInfo);
+        entry.mForcedScalingMode = mode;
+        writeSettingsIfNeeded(entry, displayInfo);
+    }
+
+    private int getWindowingModeLocked(Entry entry, int displayId) {
         int windowingMode = entry != null ? entry.mWindowingMode
                 : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
         // This display used to be in freeform, but we don't support freeform anymore, so fall
@@ -168,54 +198,35 @@
         return windowingMode;
     }
 
-    void setUserRotation(DisplayContent dc, int rotationMode, int rotation) {
+    void applySettingsToDisplayLocked(DisplayContent dc) {
         final DisplayInfo displayInfo = dc.getDisplayInfo();
+        final Entry entry = getEntry(displayInfo);
 
-        final String uniqueId = displayInfo.uniqueId;
-        final String name = displayInfo.name;
-        Entry entry = getEntry(displayInfo.name, uniqueId);
-        if (rotationMode == WindowManagerPolicy.USER_ROTATION_FREE
-                && rotation == Surface.ROTATION_0 && entry == null) {
-            // All default values. No action needed.
+        // Setting windowing mode first, because it may override overscan values later.
+        dc.setWindowingMode(getWindowingModeLocked(entry, dc.getDisplayId()));
+
+        if (entry == null) {
             return;
         }
 
-        entry = getOrCreateEntry(uniqueId, name);
-        entry.mUserRotationMode = rotationMode;
-        entry.mUserRotation = rotation;
-        removeEntryIfEmpty(uniqueId, name);
+        displayInfo.overscanLeft = entry.mOverscanLeft;
+        displayInfo.overscanTop = entry.mOverscanTop;
+        displayInfo.overscanRight = entry.mOverscanRight;
+        displayInfo.overscanBottom = entry.mOverscanBottom;
+
+        dc.getDisplayRotation().restoreUserRotation(entry.mUserRotationMode, entry.mUserRotation);
+
+        if (entry.mForcedDensity != 0) {
+            dc.mBaseDisplayDensity = entry.mForcedDensity;
+        }
+        if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) {
+            dc.updateBaseDisplayMetrics(entry.mForcedWidth, entry.mForcedHeight,
+                    dc.mBaseDisplayDensity);
+        }
+        dc.mDisplayScalingDisabled = entry.mForcedScalingMode == FORCE_SCALING_MODE_DISABLED;
     }
 
-    private void restoreUserRotation(DisplayContent dc) {
-        final DisplayInfo info = dc.getDisplayInfo();
-
-        final Entry entry = getEntry(info.name, info.uniqueId);
-        final int userRotationMode = entry != null ? entry.mUserRotationMode
-                : WindowManagerPolicy.USER_ROTATION_FREE;
-        final int userRotation = entry != null ? entry.mUserRotation
-                : Surface.ROTATION_0;
-
-        dc.getDisplayRotation().restoreUserRotation(userRotationMode, userRotation);
-    }
-
-    void applySettingsToDisplayLocked(DisplayContent dc) {
-        final DisplayInfo displayInfo = dc.getDisplayInfo();
-
-        // Setting windowing mode first, because it may override overscan values later.
-        dc.setWindowingMode(getWindowingModeLocked(displayInfo.name, displayInfo.uniqueId,
-                dc.getDisplayId()));
-
-        final Rect rect = new Rect();
-        getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect);
-        displayInfo.overscanLeft = rect.left;
-        displayInfo.overscanTop = rect.top;
-        displayInfo.overscanRight = rect.right;
-        displayInfo.overscanBottom = rect.bottom;
-
-        restoreUserRotation(dc);
-    }
-
-    void readSettingsLocked() {
+    private void readSettings() {
         FileInputStream stream;
         try {
             stream = mFile.openRead();
@@ -306,12 +317,29 @@
                     WindowManagerPolicy.USER_ROTATION_FREE);
             entry.mUserRotation = getIntAttribute(parser, "userRotation",
                     Surface.ROTATION_0);
+            entry.mForcedWidth = getIntAttribute(parser, "forcedWidth");
+            entry.mForcedHeight = getIntAttribute(parser, "forcedHeight");
+            entry.mForcedDensity = getIntAttribute(parser, "forcedDensity");
+            entry.mForcedScalingMode = getIntAttribute(parser, "forcedScalingMode",
+                    FORCE_SCALING_MODE_AUTO);
             mEntries.put(name, entry);
         }
         XmlUtils.skipCurrentTag(parser);
     }
 
-    void writeSettingsLocked() {
+    private void writeSettingsIfNeeded(Entry changedEntry, DisplayInfo displayInfo) {
+        if (changedEntry.isEmpty()) {
+            boolean removed = mEntries.remove(displayInfo.uniqueId) != null;
+            // Legacy name might have been in used, so we need to clear it.
+            removed |= mEntries.remove(displayInfo.name) != null;
+            if (!removed) {
+                // The entry didn't exist so nothing is changed and no need to update the file.
+                return;
+            }
+        } else {
+            mEntries.put(displayInfo.uniqueId, changedEntry);
+        }
+
         FileOutputStream stream;
         try {
             stream = mFile.startWrite();
@@ -351,6 +379,17 @@
                 if (entry.mUserRotation != Surface.ROTATION_0) {
                     out.attribute(null, "userRotation", Integer.toString(entry.mUserRotation));
                 }
+                if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) {
+                    out.attribute(null, "forcedWidth", Integer.toString(entry.mForcedWidth));
+                    out.attribute(null, "forcedHeight", Integer.toString(entry.mForcedHeight));
+                }
+                if (entry.mForcedDensity != 0) {
+                    out.attribute(null, "forcedDensity", Integer.toString(entry.mForcedDensity));
+                }
+                if (entry.mForcedScalingMode != FORCE_SCALING_MODE_AUTO) {
+                    out.attribute(null, "forcedScalingMode",
+                            Integer.toString(entry.mForcedScalingMode));
+                }
                 out.endTag(null, "display");
             }
 
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
index ab87759..632494b 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -16,6 +16,10 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
@@ -23,10 +27,17 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 
 import android.content.res.Configuration;
+import android.graphics.GraphicBuffer;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.util.Slog;
+import android.view.AppTransitionAnimationSpec;
 import android.view.Display;
+import android.view.WindowManager;
+import android.view.WindowManager.TransitionType;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 /**
  * Controller for the display container. This is created by activity manager to link activity
@@ -41,7 +52,7 @@
         super(listener, WindowManagerService.getInstance());
         mDisplayId = display.getDisplayId();
 
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 mRoot.createDisplayContent(display, this /* controller */);
@@ -56,9 +67,15 @@
         }
     }
 
+    @VisibleForTesting
+    public DisplayWindowController(Display display, WindowManagerService service) {
+        super(null, service);
+        mDisplayId = display.getDisplayId();
+    }
+
     @Override
     public void removeContainer() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if(mContainer == null) {
                 if (DEBUG_DISPLAY) Slog.i(TAG_WM, "removeDisplay: could not find displayId="
                         + mDisplayId);
@@ -71,9 +88,34 @@
 
     @Override
     public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
-        // TODO: The container receives override configuration changes through other means. enabling
-        // callbacks through the controller causes layout issues. Investigate consolidating
-        // override configuration propagation to just here.
+        synchronized (mGlobalLock) {
+            if (mContainer != null) {
+                mContainer.mService.setNewDisplayOverrideConfiguration(overrideConfiguration,
+                        mContainer);
+            }
+        }
+    }
+
+    /**
+     * Updates the docked/pinned controller resources to the current system context.
+     */
+    public void preOnConfigurationChanged() {
+        synchronized (mGlobalLock) {
+            if (mContainer != null) {
+                mContainer.preOnConfigurationChanged();
+            }
+        }
+    }
+
+  /**
+   * @see DisplayContent#applyRotationLocked(int, int)
+   */
+    public void applyRotation(int oldRotation, int newRotation) {
+        synchronized (mGlobalLock) {
+            if (mContainer != null) {
+                mContainer.applyRotationLocked(oldRotation, newRotation);
+            }
+        }
     }
 
     public int getDisplayId() {
@@ -81,11 +123,27 @@
     }
 
     /**
+     * Called when the corresponding display receives
+     * {@link android.hardware.display.DisplayManager.DisplayListener#onDisplayChanged(int)}.
+     */
+    public void onDisplayChanged() {
+        synchronized (mGlobalLock) {
+            if (mContainer == null) {
+                if (DEBUG_DISPLAY) Slog.i(TAG_WM, "onDisplayChanged: could not find display="
+                        + mDisplayId);
+                return;
+            }
+            mContainer.updateDisplayInfo();
+            mService.mWindowPlacerLocked.requestTraversal();
+        }
+    }
+
+    /**
      * Positions the task stack at the given position in the task stack container.
      */
     public void positionChildAt(StackWindowController child, int position,
             boolean includingParents) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (DEBUG_STACK) Slog.i(TAG_WM, "positionTaskStackAt: positioning stack=" + child
                     + " at " + position);
             if (mContainer == null) {
@@ -107,7 +165,7 @@
      * attempt to update the IME target before all information about the Windows have been updated.
      */
     public void deferUpdateImeTarget() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent dc = mRoot.getDisplayContent(mDisplayId);
             if (dc != null) {
                 dc.deferUpdateImeTarget();
@@ -119,7 +177,7 @@
      * Resumes updating the IME target after deferring. See {@link #deferUpdateImeTarget()}
      */
     public void continueUpdateImeTarget() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent dc = mRoot.getDisplayContent(mDisplayId);
             if (dc != null) {
                 dc.continueUpdateImeTarget();
@@ -134,7 +192,7 @@
      * @param moveFocusNow Specifies if we should update the focused window immediately.
      */
     public void setFocusedApp(IBinder token, boolean moveFocusNow) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "setFocusedApp: could not find displayId="
                         + mDisplayId);
@@ -163,6 +221,124 @@
         }
     }
 
+    public void prepareAppTransition(@WindowManager.TransitionType int transit,
+            boolean alwaysKeepCurrent) {
+        prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
+    }
+
+    /**
+     * @param transit What kind of transition is happening. Use one of the constants
+     *                AppTransition.TRANSIT_*.
+     * @param alwaysKeepCurrent If true and a transition is already set, new transition will NOT
+     *                          be set.
+     * @param flags Additional flags for the app transition, Use a combination of the constants
+     *              AppTransition.TRANSIT_FLAG_*.
+     * @param forceOverride Always override the transit, not matter what was set previously.
+     */
+    public void prepareAppTransition(@WindowManager.TransitionType int transit,
+            boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags,
+            boolean forceOverride) {
+        synchronized (mGlobalLock) {
+            mRoot.getDisplayContent(mDisplayId).prepareAppTransition(transit, alwaysKeepCurrent,
+                    flags, forceOverride);
+        }
+    }
+
+    public void executeAppTransition() {
+        synchronized (mGlobalLock) {
+            mRoot.getDisplayContent(mDisplayId).executeAppTransition();
+        }
+    }
+
+    public void overridePendingAppTransition(String packageName,
+            int enterAnim, int exitAnim, IRemoteCallback startedCallback) {
+        synchronized (mGlobalLock) {
+            mRoot.getDisplayContent(mDisplayId).mAppTransition.overridePendingAppTransition(
+                    packageName, enterAnim, exitAnim, startedCallback);
+        }
+    }
+
+    public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
+            int startHeight) {
+        synchronized (mGlobalLock) {
+            mRoot.getDisplayContent(mDisplayId).mAppTransition.overridePendingAppTransitionScaleUp(
+                    startX, startY, startWidth, startHeight);
+        }
+    }
+
+    public void overridePendingAppTransitionClipReveal(int startX, int startY,
+            int startWidth, int startHeight) {
+        synchronized (mGlobalLock) {
+            mRoot.getDisplayContent(mDisplayId)
+                    .mAppTransition.overridePendingAppTransitionClipReveal(startX, startY,
+                    startWidth, startHeight);
+        }
+    }
+
+    public void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX,
+            int startY, IRemoteCallback startedCallback, boolean scaleUp) {
+        synchronized (mGlobalLock) {
+            mRoot.getDisplayContent(mDisplayId)
+                    .mAppTransition.overridePendingAppTransitionThumb(srcThumb, startX, startY,
+                    startedCallback, scaleUp);
+        }
+    }
+
+    public void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX,
+            int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
+            boolean scaleUp) {
+        synchronized (mGlobalLock) {
+            mRoot.getDisplayContent(mDisplayId)
+                    .mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX,
+                    startY, targetWidth, targetHeight, startedCallback, scaleUp);
+        }
+    }
+
+    public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
+            IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
+            boolean scaleUp) {
+        synchronized (mGlobalLock) {
+            mRoot.getDisplayContent(mDisplayId)
+                    .mAppTransition.overridePendingAppTransitionMultiThumb(specs,
+                    onAnimationStartedCallback, onAnimationFinishedCallback, scaleUp);
+        }
+    }
+
+    public void overridePendingAppTransitionStartCrossProfileApps() {
+        synchronized (mGlobalLock) {
+            mRoot.getDisplayContent(mDisplayId)
+                    .mAppTransition.overridePendingAppTransitionStartCrossProfileApps();
+        }
+    }
+
+    public void overridePendingAppTransitionInPlace(String packageName, int anim) {
+        synchronized (mGlobalLock) {
+            mRoot.getDisplayContent(mDisplayId)
+                    .mAppTransition.overrideInPlaceAppTransition(packageName, anim);
+        }
+    }
+
+    /**
+     * Get Pending App transition of display.
+     *
+     * @return The pending app transition of the display.
+     */
+    public @TransitionType int getPendingAppTransition() {
+        synchronized (mGlobalLock) {
+            return mRoot.getDisplayContent(mDisplayId).mAppTransition.getAppTransition();
+        }
+    }
+
+    /**
+     * Check if pending app transition is for activity / task launch.
+     */
+    public boolean isNextTransitionForward() {
+        final int transit = getPendingAppTransition();
+        return transit == TRANSIT_ACTIVITY_OPEN
+                || transit == TRANSIT_TASK_OPEN
+                || transit == TRANSIT_TASK_TO_FRONT;
+    }
+
     @Override
     public String toString() {
         return "{DisplayWindowController displayId=" + mDisplayId + "}";
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index ac93848..6daf2f5 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -27,14 +27,15 @@
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
+import static android.view.WindowManager.TRANSIT_NONE;
+
 import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
 import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
-import static android.view.WindowManager.TRANSIT_NONE;
+import static com.android.server.wm.DockedStackDividerControllerProto.MINIMIZED_DOCK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
 import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
-import static com.android.server.wm.DockedStackDividerControllerProto.MINIMIZED_DOCK;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -170,7 +171,7 @@
             final int orientation = mTmpRect2.width() <= mTmpRect2.height()
                     ? ORIENTATION_PORTRAIT
                     : ORIENTATION_LANDSCAPE;
-            final int dockSide = getDockSide(mTmpRect, mTmpRect2, orientation);
+            final int dockSide = getDockSide(mTmpRect, mTmpRect2, orientation, rotation);
             final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide,
                     getContentWidth());
 
@@ -201,7 +202,7 @@
      * @param orientation the origination of device
      * @return current docked side
      */
-    int getDockSide(Rect bounds, Rect displayRect, int orientation) {
+    int getDockSide(Rect bounds, Rect displayRect, int orientation, int rotation) {
         if (orientation == Configuration.ORIENTATION_PORTRAIT) {
             // Portrait mode, docked either at the top or the bottom.
             final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top);
@@ -210,7 +211,8 @@
             } else if (diff < 0) {
                 return DOCKED_BOTTOM;
             }
-            return canPrimaryStackDockTo(DOCKED_TOP) ? DOCKED_TOP : DOCKED_BOTTOM;
+            return canPrimaryStackDockTo(DOCKED_TOP, displayRect, rotation)
+                    ? DOCKED_TOP : DOCKED_BOTTOM;
         } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
             // Landscape mode, docked either on the left or on the right.
             final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left);
@@ -219,7 +221,8 @@
             } else if (diff < 0) {
                 return DOCKED_RIGHT;
             }
-            return canPrimaryStackDockTo(DOCKED_LEFT) ? DOCKED_LEFT : DOCKED_RIGHT;
+            return canPrimaryStackDockTo(DOCKED_LEFT, displayRect, rotation)
+                    ? DOCKED_LEFT : DOCKED_RIGHT;
         }
         return DOCKED_INVALID;
     }
@@ -462,10 +465,9 @@
      * @param dockSide the side to see if it is valid
      * @return true if the side provided is valid
      */
-    boolean canPrimaryStackDockTo(int dockSide) {
-        final DisplayInfo di = mDisplayContent.getDisplayInfo();
-        return mService.mPolicy.isDockSideAllowed(dockSide, mOriginalDockedSide, di.logicalWidth,
-                di.logicalHeight, di.rotation);
+    boolean canPrimaryStackDockTo(int dockSide, Rect parentRect, int rotation) {
+        return mService.mPolicy.isDockSideAllowed(dockSide, mOriginalDockedSide,
+                parentRect.width(), parentRect.height(), rotation);
     }
 
     void notifyDockedStackExistsChanged(boolean exists) {
@@ -531,7 +533,7 @@
             final TaskStack stack =
                     mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
             final long transitionDuration = isAnimationMaximizing()
-                    ? mService.mAppTransition.getLastClipRevealTransitionDuration()
+                    ? mDisplayContent.mAppTransition.getLastClipRevealTransitionDuration()
                     : DEFAULT_APP_TRANSITION_DURATION;
             mAnimationDuration = (long)
                     (transitionDuration * mService.getTransitionAnimationScaleLocked());
@@ -820,7 +822,7 @@
                 mService.mWaitingForDrawnCallback.run();
             }
             mService.mWaitingForDrawnCallback = () -> {
-                synchronized (mService.mWindowMap) {
+                synchronized (mService.mGlobalLock) {
                     mAnimationStartDelayed = false;
                     if (mDelayedImeWin != null) {
                         mDelayedImeWin.endDelayingAnimationStart();
@@ -950,7 +952,7 @@
             return naturalAmount;
         }
         final int minimizeDistance = stack.getMinimizeDistance();
-        float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation()
+        final float startPrime = mDisplayContent.mAppTransition.getLastClipRevealMaxTranslation()
                 / (float) minimizeDistance;
         final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime;
         final float t2 = Math.min(t / mMaximizeMeetFraction, 1);
@@ -963,12 +965,12 @@
      */
     private float getClipRevealMeetFraction(TaskStack stack) {
         if (!isAnimationMaximizing() || stack == null ||
-                !mService.mAppTransition.hadClipRevealAnimation()) {
+                !mDisplayContent.mAppTransition.hadClipRevealAnimation()) {
             return 1f;
         }
         final int minimizeDistance = stack.getMinimizeDistance();
-        final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation())
-                / (float) minimizeDistance;
+        final float fraction = Math.abs(mDisplayContent.mAppTransition
+                .getLastClipRevealMaxTranslation()) / (float) minimizeDistance;
         final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN)
                 / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN)));
         return CLIP_REVEAL_MEET_EARLIEST
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index f42e979..ce8c979 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -104,7 +104,7 @@
         final boolean callbackResult = mCallback.get().prePerformDrag(window, dragToken,
                 touchSource, touchX, touchY, thumbCenterX, thumbCenterY, data);
         try {
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 try {
                     if (!callbackResult) {
                         Slog.w(TAG_WM, "IDragDropCallback rejects the performDrag request");
@@ -209,7 +209,7 @@
 
         mCallback.get().preReportDropResult(window, consumed);
         try {
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 if (mDragState == null) {
                     // Most likely the drop recipient ANRed and we ended the drag
                     // out from under it.  Log the issue and move on.
@@ -248,7 +248,7 @@
 
         mCallback.get().preCancelDragAndDrop(dragToken);
         try {
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 if (mDragState == null) {
                     Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
                     throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
@@ -277,7 +277,7 @@
      * @param newY Y coordinate value in dp in the screen coordinate
      */
     void handleMotionEvent(boolean keepHandling, float newX, float newY) {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             if (!dragDropActiveLocked()) {
                 // The drag has ended but the clean-up message has not been processed by
                 // window manager. Drop events that occur after this until window manager
@@ -352,7 +352,7 @@
                         Slog.w(TAG_WM, "Timeout ending drag to win " + win);
                     }
 
-                    synchronized (mService.mWindowMap) {
+                    synchronized (mService.mGlobalLock) {
                         // !!! TODO: ANR the drag-receiving app
                         if (mDragState != null) {
                             mDragState.mDragResult = false;
@@ -368,14 +368,14 @@
                     final DragState.InputInterceptor interceptor =
                             (DragState.InputInterceptor) msg.obj;
                     if (interceptor == null) return;
-                    synchronized (mService.mWindowMap) {
+                    synchronized (mService.mGlobalLock) {
                         interceptor.tearDown();
                     }
                     break;
                 }
 
                 case MSG_ANIMATION_END: {
-                    synchronized (mService.mWindowMap) {
+                    synchronized (mService.mGlobalLock) {
                         if (mDragState == null) {
                             Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
                                     "plyaing animation");
diff --git a/services/core/java/com/android/server/wm/FactoryErrorDialog.java b/services/core/java/com/android/server/wm/FactoryErrorDialog.java
new file mode 100644
index 0000000..88b5475
--- /dev/null
+++ b/services/core/java/com/android/server/wm/FactoryErrorDialog.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2006 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.server.wm;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Handler;
+import android.os.Message;
+import android.view.WindowManager;
+
+import com.android.server.am.BaseErrorDialog;
+
+final class FactoryErrorDialog extends BaseErrorDialog {
+    public FactoryErrorDialog(Context context, CharSequence msg) {
+        super(context);
+        setCancelable(false);
+        setTitle(context.getText(com.android.internal.R.string.factorytest_failed));
+        setMessage(msg);
+        setButton(DialogInterface.BUTTON_POSITIVE,
+                context.getText(com.android.internal.R.string.factorytest_reboot),
+                mHandler.obtainMessage(0));
+        WindowManager.LayoutParams attrs = getWindow().getAttributes();
+        attrs.setTitle("Factory Error");
+        getWindow().setAttributes(attrs);
+    }
+    
+    public void onStop() {
+    }
+
+    private final Handler mHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            throw new RuntimeException("Rebooting from failed factory test");
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 10d77e5..f823caa 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -53,7 +53,7 @@
             return;
         }
 
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             WindowState windowState = (WindowState) inputWindowHandle.windowState;
             if (windowState != null) {
                 Slog.i(TAG_WM, "WINDOW DIED " + windowState);
@@ -74,7 +74,7 @@
         AppWindowToken appWindowToken = null;
         WindowState windowState = null;
         boolean aboveSystem = false;
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             if (inputWindowHandle != null) {
                 windowState = (WindowState) inputWindowHandle.windowState;
                 if (windowState != null) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index ed3e6c6..0e4ab53 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -96,7 +96,7 @@
 
         @Override
         public void dismiss() {
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 if (mInputMonitor.destroyInputConsumer(mWindowHandle.name)) {
                     mInputEventReceiver.dispose();
                 }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
new file mode 100644
index 0000000..c91af73
--- /dev/null
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2016 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.server.wm;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_UNSET;
+import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
+import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
+import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+
+import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
+import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING;
+import static com.android.server.am.KeyguardOccludedProto.DISPLAY_ID;
+import static com.android.server.am.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
+ * currently visible.
+ * <p>
+ * Note that everything in this class should only be accessed with the AM lock being held.
+ */
+class KeyguardController {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_ATM;
+
+    private final ActivityStackSupervisor mStackSupervisor;
+    private WindowManagerService mWindowManager;
+    private boolean mKeyguardShowing;
+    private boolean mAodShowing;
+    private boolean mKeyguardGoingAway;
+    private boolean mDismissalRequested;
+    private int[] mSecondaryDisplayIdsShowing;
+    private int mBeforeUnoccludeTransit;
+    private int mVisibilityTransactionDepth;
+    private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
+    private final ActivityTaskManagerService mService;
+
+    KeyguardController(ActivityTaskManagerService service,
+            ActivityStackSupervisor stackSupervisor) {
+        mService = service;
+        mStackSupervisor = stackSupervisor;
+    }
+
+    void setWindowManager(WindowManagerService windowManager) {
+        mWindowManager = windowManager;
+    }
+
+    /**
+     * @return true if either Keyguard or AOD are showing, not going away, and not being occluded
+     *         on the given display, false otherwise
+     */
+    boolean isKeyguardOrAodShowing(int displayId) {
+        return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway
+                && (displayId == DEFAULT_DISPLAY
+                        ? !isDisplayOccluded(DEFAULT_DISPLAY)
+                        : isShowingOnSecondaryDisplay(displayId));
+    }
+
+    /**
+     * @return true if Keyguard is showing, not going away, and not being occluded on the given
+     *         display, false otherwise
+     */
+    boolean isKeyguardShowing(int displayId) {
+        return mKeyguardShowing && !mKeyguardGoingAway
+                && (displayId == DEFAULT_DISPLAY
+                        ? !isDisplayOccluded(DEFAULT_DISPLAY)
+                        : isShowingOnSecondaryDisplay(displayId));
+    }
+
+    /**
+     * @return true if Keyguard is either showing or occluded, but not going away
+     */
+    boolean isKeyguardLocked() {
+        return mKeyguardShowing && !mKeyguardGoingAway;
+    }
+
+    /**
+     * @return {@code true} if the keyguard is going away, {@code false} otherwise.
+     */
+    boolean isKeyguardGoingAway() {
+        // Also check keyguard showing in case value is stale.
+        return mKeyguardGoingAway && mKeyguardShowing;
+    }
+
+    /**
+     * Update the Keyguard showing state.
+     */
+    void setKeyguardShown(boolean keyguardShowing, boolean aodShowing,
+            int[] secondaryDisplaysShowing) {
+        boolean showingChanged = keyguardShowing != mKeyguardShowing || aodShowing != mAodShowing;
+        // If keyguard is going away, but SystemUI aborted the transition, need to reset state.
+        showingChanged |= mKeyguardGoingAway && keyguardShowing;
+        if (!showingChanged && Arrays.equals(secondaryDisplaysShowing,
+                mSecondaryDisplayIdsShowing)) {
+            return;
+        }
+        mKeyguardShowing = keyguardShowing;
+        mAodShowing = aodShowing;
+        mSecondaryDisplayIdsShowing = secondaryDisplaysShowing;
+        mWindowManager.setAodShowing(aodShowing);
+        if (showingChanged) {
+            dismissDockedStackIfNeeded();
+            setKeyguardGoingAway(false);
+            // TODO(b/113840485): Check usage for non-default display
+            mWindowManager.setKeyguardOrAodShowingOnDefaultDisplay(
+                    isKeyguardOrAodShowing(DEFAULT_DISPLAY));
+            if (keyguardShowing) {
+                mDismissalRequested = false;
+            }
+        }
+        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        updateKeyguardSleepToken();
+    }
+
+    private boolean isShowingOnSecondaryDisplay(int displayId) {
+        if (mSecondaryDisplayIdsShowing == null) return false;
+        for (int showingId : mSecondaryDisplayIdsShowing) {
+            if (displayId == showingId) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Called when Keyguard is going away.
+     *
+     * @param flags See {@link WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
+     *              etc.
+     */
+    void keyguardGoingAway(int flags) {
+        if (!mKeyguardShowing) {
+            return;
+        }
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway");
+        mWindowManager.deferSurfaceLayout();
+        try {
+            setKeyguardGoingAway(true);
+            mStackSupervisor.getDefaultDisplay().getWindowContainerController()
+                    .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
+                            false /* alwaysKeepCurrent */, convertTransitFlags(flags),
+                            false /* forceOverride */);
+            updateKeyguardSleepToken();
+
+            // Some stack visibility might change (e.g. docked stack)
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+            mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
+            mWindowManager.executeAppTransition();
+        } finally {
+            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
+            mWindowManager.continueSurfaceLayout();
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence message) {
+        final ActivityRecord activityRecord = ActivityRecord.forTokenLocked(token);
+        if (activityRecord == null || !activityRecord.visibleIgnoringKeyguard) {
+            failCallback(callback);
+            return;
+        }
+        Slog.i(TAG, "Activity requesting to dismiss Keyguard: " + activityRecord);
+
+        // If the client has requested to dismiss the keyguard and the Activity has the flag to
+        // turn the screen on, wakeup the screen if it's the top Activity.
+        if (activityRecord.getTurnScreenOnFlag() && activityRecord.isTopRunningActivity()) {
+            mStackSupervisor.wakeUp("dismissKeyguard");
+        }
+
+        mWindowManager.dismissKeyguard(callback, message);
+    }
+
+    private void setKeyguardGoingAway(boolean keyguardGoingAway) {
+        mKeyguardGoingAway = keyguardGoingAway;
+        mWindowManager.setKeyguardGoingAway(keyguardGoingAway);
+    }
+
+    private void failCallback(IKeyguardDismissCallback callback) {
+        try {
+            callback.onDismissError();
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to call callback", e);
+        }
+    }
+
+    private int convertTransitFlags(int keyguardGoingAwayFlags) {
+        int result = 0;
+        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) {
+            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+        }
+        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0) {
+            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+        }
+        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0) {
+            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+        }
+        return result;
+    }
+
+    /**
+     * Starts a batch of visibility updates.
+     */
+    void beginActivityVisibilityUpdate() {
+        mVisibilityTransactionDepth++;
+    }
+
+    /**
+     * Ends a batch of visibility updates. After all batches are done, this method makes sure to
+     * update lockscreen occluded/dismiss state if needed.
+     */
+    void endActivityVisibilityUpdate() {
+        mVisibilityTransactionDepth--;
+        if (mVisibilityTransactionDepth == 0) {
+            visibilitiesUpdated();
+        }
+    }
+
+    /**
+     * @return True if we may show an activity while Keyguard is showing because we are in the
+     *         process of dismissing it anyways, false otherwise.
+     */
+    boolean canShowActivityWhileKeyguardShowing(ActivityRecord r, boolean dismissKeyguard) {
+
+        // Allow to show it when we are about to dismiss Keyguard. This isn't allowed if r is
+        // already the dismissing activity, in which case we don't allow it to repeatedly dismiss
+        // Keyguard.
+        return dismissKeyguard && canDismissKeyguard() && !mAodShowing
+                && (mDismissalRequested
+                || getDisplay(r.getDisplayId()).mDismissingKeyguardActivity != r);
+    }
+
+    /**
+     * @return True if we may show an activity while Keyguard is occluded, false otherwise.
+     */
+    boolean canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked) {
+        return showWhenLocked || dismissKeyguard && !mWindowManager.isKeyguardSecure();
+    }
+
+    private void visibilitiesUpdated() {
+        boolean requestDismissKeyguard = false;
+        for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
+            final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+            final KeyguardDisplayState state = getDisplay(display.mDisplayId);
+            state.visibilitiesUpdated(this, display);
+            requestDismissKeyguard |= state.mRequestDismissKeyguard;
+        }
+
+        // Dismissing Keyguard happens globally using the information from all displays.
+        if (requestDismissKeyguard) {
+            handleDismissKeyguard();
+        }
+    }
+
+    /**
+     * Called when occluded state changed.
+     */
+    private void handleOccludedChanged() {
+        mWindowManager.onKeyguardOccludedChanged(isDisplayOccluded(DEFAULT_DISPLAY));
+        if (isKeyguardLocked()) {
+            mWindowManager.deferSurfaceLayout();
+            try {
+                mStackSupervisor.getDefaultDisplay().getWindowContainerController()
+                        .prepareAppTransition(resolveOccludeTransit(),
+                                false /* alwaysKeepCurrent */, 0 /* flags */,
+                                true /* forceOverride */);
+                updateKeyguardSleepToken();
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mWindowManager.executeAppTransition();
+            } finally {
+                mWindowManager.continueSurfaceLayout();
+            }
+        }
+        dismissDockedStackIfNeeded();
+    }
+
+    /**
+     * Called when somebody wants to dismiss the Keyguard via the flag.
+     */
+    private void handleDismissKeyguard() {
+        // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
+        // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
+        // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
+        if (mWindowManager.isKeyguardSecure()) {
+            mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
+            mDismissalRequested = true;
+
+            // If we are about to unocclude the Keyguard, but we can dismiss it without security,
+            // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
+            final DisplayWindowController dwc =
+                    mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+            if (mKeyguardShowing && canDismissKeyguard()
+                    && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
+                dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
+                        0 /* flags */, true /* forceOverride */);
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mWindowManager.executeAppTransition();
+            }
+        }
+    }
+
+    private boolean isDisplayOccluded(int displayId) {
+        return getDisplay(displayId).mOccluded;
+    }
+
+    /**
+     * @return true if Keyguard can be currently dismissed without entering credentials.
+     */
+    boolean canDismissKeyguard() {
+        return mWindowManager.isKeyguardTrusted() || !mWindowManager.isKeyguardSecure();
+    }
+
+    private int resolveOccludeTransit() {
+        final DisplayWindowController dwc =
+                mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+        if (mBeforeUnoccludeTransit != TRANSIT_UNSET
+                && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
+                // TODO(b/113840485): Handle app transition for individual display.
+                && isDisplayOccluded(DEFAULT_DISPLAY)) {
+
+            // Reuse old transit in case we are occluding Keyguard again, meaning that we never
+            // actually occclude/unocclude Keyguard, but just run a normal transition.
+            return mBeforeUnoccludeTransit;
+            // TODO(b/113840485): Handle app transition for individual display.
+        } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) {
+
+            // Save transit in case we dismiss/occlude Keyguard shortly after.
+            mBeforeUnoccludeTransit = dwc.getPendingAppTransition();
+            return TRANSIT_KEYGUARD_UNOCCLUDE;
+        } else {
+            return TRANSIT_KEYGUARD_OCCLUDE;
+        }
+    }
+
+    private void dismissDockedStackIfNeeded() {
+        // TODO(b/113840485): Handle docked stack for individual display.
+        if (mKeyguardShowing && isDisplayOccluded(DEFAULT_DISPLAY)) {
+            // The lock screen is currently showing, but is occluded by a window that can
+            // show on top of the lock screen. In this can we want to dismiss the docked
+            // stack since it will be complicated/risky to try to put the activity on top
+            // of the lock screen in the right fullscreen configuration.
+            final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+            if (stack == null) {
+                return;
+            }
+            mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
+                    stack.isFocusedStackOnDisplay());
+        }
+    }
+
+    private void updateKeyguardSleepToken() {
+        for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
+            final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+            final KeyguardDisplayState state = getDisplay(display.mDisplayId);
+            if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) {
+                state.acquiredSleepToken();
+            } else if (!isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken != null) {
+                state.releaseSleepToken();
+            }
+        }
+    }
+
+    private KeyguardDisplayState getDisplay(int displayId) {
+        KeyguardDisplayState state = mDisplayStates.get(displayId);
+        if (state == null) {
+            state = new KeyguardDisplayState(mService, displayId);
+            mDisplayStates.append(displayId, state);
+        }
+        return state;
+    }
+
+    void onDisplayRemoved(int displayId) {
+        final KeyguardDisplayState state = mDisplayStates.get(displayId);
+        if (state != null) {
+            state.onRemoved();
+            mDisplayStates.remove(displayId);
+        }
+    }
+
+    /** Represents Keyguard state per individual display. */
+    private static class KeyguardDisplayState {
+        private final int mDisplayId;
+        private boolean mOccluded;
+        private ActivityRecord mDismissingKeyguardActivity;
+        private boolean mRequestDismissKeyguard;
+        private final ActivityTaskManagerService mService;
+        private SleepToken mSleepToken;
+
+        KeyguardDisplayState(ActivityTaskManagerService service, int displayId) {
+            mService = service;
+            mDisplayId = displayId;
+        }
+
+        void onRemoved() {
+            mDismissingKeyguardActivity = null;
+            releaseSleepToken();
+        }
+
+        void acquiredSleepToken() {
+            if (mSleepToken == null) {
+                mSleepToken = mService.acquireSleepToken("keyguard", mDisplayId);
+            }
+        }
+
+        void releaseSleepToken() {
+            if (mSleepToken != null) {
+                mSleepToken.release();
+                mSleepToken = null;
+            }
+        }
+
+        void visibilitiesUpdated(KeyguardController controller, ActivityDisplay display) {
+            final boolean lastOccluded = mOccluded;
+            final ActivityRecord lastDismissActivity = mDismissingKeyguardActivity;
+            mRequestDismissKeyguard = false;
+            mOccluded = false;
+            mDismissingKeyguardActivity = null;
+
+            // Only the top activity of the focused stack on each display may control it's
+            // occluded state.
+            final ActivityStack focusedStack = display.getFocusedStack();
+            if (focusedStack != null) {
+                final ActivityRecord topDismissing =
+                        focusedStack.getTopDismissingKeyguardActivity();
+                mOccluded = focusedStack.topActivityOccludesKeyguard() || (topDismissing != null
+                                && focusedStack.topRunningActivityLocked() == topDismissing
+                                && controller.canShowWhileOccluded(
+                                true /* dismissKeyguard */,
+                                false /* showWhenLocked */));
+                if (focusedStack.getTopDismissingKeyguardActivity() != null) {
+                    mDismissingKeyguardActivity = focusedStack.getTopDismissingKeyguardActivity();
+                }
+                mOccluded |= controller.mWindowManager.isShowingDream();
+            }
+
+            // TODO(b/113840485): Handle app transition for individual display, and apply occluded
+            // state change to secondary displays.
+            // For now, only default display can change occluded.
+            if (lastOccluded != mOccluded && mDisplayId == DEFAULT_DISPLAY) {
+                controller.handleOccludedChanged();
+            }
+            if (lastDismissActivity != mDismissingKeyguardActivity && !mOccluded
+                    && mDismissingKeyguardActivity != null
+                    && controller.mWindowManager.isKeyguardSecure()) {
+                mRequestDismissKeyguard = true;
+            }
+        }
+
+        void dumpStatus(PrintWriter pw, String prefix) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append(prefix);
+            sb.append("  Occluded=").append(mOccluded)
+                    .append(" DismissingKeyguardActivity=")
+                    .append(mDismissingKeyguardActivity)
+                    .append(" at display=")
+                    .append(mDisplayId);
+            pw.println(sb.toString());
+        }
+
+        void writeToProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+            proto.write(DISPLAY_ID, mDisplayId);
+            proto.write(KEYGUARD_OCCLUDED, mOccluded);
+            proto.end(token);
+        }
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        pw.println(prefix + "KeyguardController:");
+        pw.println(prefix + "  mKeyguardShowing=" + mKeyguardShowing);
+        pw.println(prefix + "  mAodShowing=" + mAodShowing);
+        pw.println(prefix + "  mKeyguardGoingAway=" + mKeyguardGoingAway);
+        dumpDisplayStates(pw, prefix);
+        pw.println(prefix + "  mDismissalRequested=" + mDismissalRequested);
+        pw.println(prefix + "  mVisibilityTransactionDepth=" + mVisibilityTransactionDepth);
+    }
+
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(KEYGUARD_SHOWING, mKeyguardShowing);
+        writeDisplayStatesToProto(proto, KEYGUARD_OCCLUDED_STATES);
+        proto.end(token);
+    }
+
+    private void dumpDisplayStates(PrintWriter pw, String prefix) {
+        for (int i = 0; i < mDisplayStates.size(); i++) {
+            mDisplayStates.valueAt(i).dumpStatus(pw, prefix);
+        }
+    }
+
+    private void writeDisplayStatesToProto(ProtoOutputStream proto, long fieldId) {
+        for (int i = 0; i < mDisplayStates.size(); i++) {
+            mDisplayStates.valueAt(i).writeToProto(proto, fieldId);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
new file mode 100644
index 0000000..252f47c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
+
+import android.annotation.IntDef;
+import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo.WindowLayout;
+import android.graphics.Rect;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link LaunchParamsController} calculates the {@link LaunchParams} by coordinating between
+ * registered {@link LaunchParamsModifier}s.
+ */
+class LaunchParamsController {
+    private final ActivityTaskManagerService mService;
+    private final List<LaunchParamsModifier> mModifiers = new ArrayList<>();
+
+    // Temporary {@link LaunchParams} for internal calculations. This is kept separate from
+    // {@code mTmpCurrent} and {@code mTmpResult} to prevent clobbering values.
+    private final LaunchParams mTmpParams = new LaunchParams();
+
+    private final LaunchParams mTmpCurrent = new LaunchParams();
+    private final LaunchParams mTmpResult = new LaunchParams();
+
+    LaunchParamsController(ActivityTaskManagerService service) {
+       mService = service;
+    }
+
+    /**
+     * Creates a {@link LaunchParamsController} with default registered
+     * {@link LaunchParamsModifier}s.
+     */
+    void registerDefaultModifiers(ActivityStackSupervisor supervisor) {
+        // {@link TaskLaunchParamsModifier} handles window layout preferences.
+        registerModifier(new TaskLaunchParamsModifier(supervisor));
+    }
+
+    /**
+     * Returns the {@link LaunchParams} calculated by the registered modifiers
+     * @param task      The {@link TaskRecord} currently being positioned.
+     * @param layout    The specified {@link WindowLayout}.
+     * @param activity  The {@link ActivityRecord} currently being positioned.
+     * @param source    The {@link ActivityRecord} from which activity was started from.
+     * @param options   The {@link ActivityOptions} specified for the activity.
+     * @param result    The resulting params.
+     */
+    void calculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
+                   ActivityRecord source, ActivityOptions options, LaunchParams result) {
+        result.reset();
+
+        // We start at the last registered {@link LaunchParamsModifier} as this represents
+        // The modifier closest to the product level. Moving back through the list moves closer to
+        // the platform logic.
+        for (int i = mModifiers.size() - 1; i >= 0; --i) {
+            mTmpCurrent.set(result);
+            mTmpResult.reset();
+            final LaunchParamsModifier modifier = mModifiers.get(i);
+
+            switch(modifier.onCalculate(task, layout, activity, source, options, mTmpCurrent,
+                    mTmpResult)) {
+                case RESULT_SKIP:
+                    // Do not apply any results when we are told to skip
+                    continue;
+                case RESULT_DONE:
+                    // Set result and return immediately.
+                    result.set(mTmpResult);
+                    return;
+                case RESULT_CONTINUE:
+                    // Set result and continue
+                    result.set(mTmpResult);
+                    break;
+            }
+        }
+
+        if (activity != null && activity.requestedVrComponent != null) {
+            // Check if the Activity is a VR activity. If so, it should be launched in main display.
+            result.mPreferredDisplayId = DEFAULT_DISPLAY;
+        } else if (mService.mVr2dDisplayId != INVALID_DISPLAY) {
+            // Get the virtual display ID from ActivityTaskManagerService. If that's set we
+            // should always use that.
+            result.mPreferredDisplayId = mService.mVr2dDisplayId;
+        }
+    }
+
+    /**
+     * A convenience method for laying out a task.
+     * @return {@code true} if bounds were set on the task. {@code false} otherwise.
+     */
+    boolean layoutTask(TaskRecord task, WindowLayout layout) {
+        return layoutTask(task, layout, null /*activity*/, null /*source*/, null /*options*/);
+    }
+
+    boolean layoutTask(TaskRecord task, WindowLayout layout, ActivityRecord activity,
+            ActivityRecord source, ActivityOptions options) {
+        calculate(task, layout, activity, source, options, mTmpParams);
+
+        // No changes, return.
+        if (mTmpParams.isEmpty()) {
+            return false;
+        }
+
+        mService.mWindowManager.deferSurfaceLayout();
+
+        try {
+            if (mTmpParams.hasPreferredDisplay()
+                    && mTmpParams.mPreferredDisplayId != task.getStack().getDisplay().mDisplayId) {
+                mService.moveStackToDisplay(task.getStackId(), mTmpParams.mPreferredDisplayId);
+            }
+
+            if (mTmpParams.hasWindowingMode()
+                    && mTmpParams.mWindowingMode != task.getStack().getWindowingMode()) {
+                task.getStack().setWindowingMode(mTmpParams.mWindowingMode);
+            }
+
+            if (!mTmpParams.mBounds.isEmpty()) {
+                task.updateOverrideConfiguration(mTmpParams.mBounds);
+                return true;
+            } else {
+                return false;
+            }
+        } finally {
+            mService.mWindowManager.continueSurfaceLayout();
+        }
+    }
+
+    /**
+     * Adds a modifier to participate in future bounds calculation. Note that the last registered
+     * {@link LaunchParamsModifier} will be the first to calculate the bounds.
+     */
+    void registerModifier(LaunchParamsModifier modifier) {
+        if (mModifiers.contains(modifier)) {
+            return;
+        }
+
+        mModifiers.add(modifier);
+    }
+
+    /**
+     * A container for holding launch related fields.
+     */
+    static class LaunchParams {
+        /** The bounds within the parent container. */
+        final Rect mBounds = new Rect();
+
+        /** The id of the display the {@link TaskRecord} would prefer to be on. */
+        int mPreferredDisplayId;
+
+        /** The windowing mode to be in. */
+        int mWindowingMode;
+
+        /** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */
+        void reset() {
+            mBounds.setEmpty();
+            mPreferredDisplayId = INVALID_DISPLAY;
+            mWindowingMode = WINDOWING_MODE_UNDEFINED;
+        }
+
+        /** Copies the values set on the passed in {@link LaunchParams}. */
+        void set(LaunchParams params) {
+            mBounds.set(params.mBounds);
+            mPreferredDisplayId = params.mPreferredDisplayId;
+            mWindowingMode = params.mWindowingMode;
+        }
+
+        /** Returns {@code true} if no values have been explicitly set. */
+        boolean isEmpty() {
+            return mBounds.isEmpty() && mPreferredDisplayId == INVALID_DISPLAY
+                    && mWindowingMode == WINDOWING_MODE_UNDEFINED;
+        }
+
+        boolean hasWindowingMode() {
+            return mWindowingMode != WINDOWING_MODE_UNDEFINED;
+        }
+
+        boolean hasPreferredDisplay() {
+            return mPreferredDisplayId != INVALID_DISPLAY;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            LaunchParams that = (LaunchParams) o;
+
+            if (mPreferredDisplayId != that.mPreferredDisplayId) return false;
+            if (mWindowingMode != that.mWindowingMode) return false;
+            return mBounds != null ? mBounds.equals(that.mBounds) : that.mBounds == null;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = mBounds != null ? mBounds.hashCode() : 0;
+            result = 31 * result + mPreferredDisplayId;
+            result = 31 * result + mWindowingMode;
+            return result;
+        }
+    }
+
+    /**
+     * An interface implemented by those wanting to participate in bounds calculation.
+     */
+    interface LaunchParamsModifier {
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE})
+        @interface Result {}
+
+        /** Returned when the modifier does not want to influence the bounds calculation */
+        int RESULT_SKIP = 0;
+        /**
+         * Returned when the modifier has changed the bounds and would like its results to be the
+         * final bounds applied.
+         */
+        int RESULT_DONE = 1;
+        /**
+         * Returned when the modifier has changed the bounds but is okay with other modifiers
+         * influencing the bounds.
+         */
+        int RESULT_CONTINUE = 2;
+
+        /**
+         * Returns the launch params that the provided activity launch params should be overridden
+         * to. {@link LaunchParamsModifier} can use this for various purposes, including: 1)
+         * Providing default bounds if the launch bounds have not been provided. 2) Repositioning
+         * the task so it doesn't get placed over an existing task. 3) Resizing the task so that its
+         * dimensions match the activity's requested orientation.
+         *
+         * @param task          Can be: 1) the target task in which the source activity wants to
+         *                      launch the target activity; 2) a newly created task that Android
+         *                      gives a chance to override its launching bounds; 3) {@code null} if
+         *                      this is called to override an activity's launching bounds.
+         * @param layout        Desired layout when activity is first launched.
+         * @param activity      Activity that is being started. This can be {@code null} on
+         *                      re-parenting an activity to a new task (e.g. for
+         *                      Picture-In-Picture). Tasks being created because an activity was
+         *                      launched should have this be non-null.
+         * @param source        the Activity that launched a new task. Could be {@code null}.
+         * @param options       {@link ActivityOptions} used to start the activity with.
+         * @param currentParams launching params after the process of last {@link
+         *                      LaunchParamsModifier}.
+         * @param outParams     the result params to be set.
+         * @return see {@link LaunchParamsModifier.Result}
+         */
+        @Result
+        int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
+                ActivityRecord source, ActivityOptions options, LaunchParams currentParams,
+                LaunchParams outParams);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/LaunchWarningWindow.java b/services/core/java/com/android/server/wm/LaunchWarningWindow.java
new file mode 100644
index 0000000..1c0093e
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LaunchWarningWindow.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 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.server.wm;
+
+import com.android.internal.R;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.util.TypedValue;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public final class LaunchWarningWindow extends Dialog {
+    public LaunchWarningWindow(Context context, ActivityRecord cur, ActivityRecord next) {
+        super(context, R.style.Theme_Toast);
+
+        requestWindowFeature(Window.FEATURE_LEFT_ICON);
+        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+        
+        setContentView(R.layout.launch_warning);
+        setTitle(context.getText(R.string.launch_warning_title));
+
+        TypedValue out = new TypedValue();
+        getContext().getTheme().resolveAttribute(android.R.attr.alertDialogIcon, out, true);
+        getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, out.resourceId);
+
+        ImageView icon = (ImageView)findViewById(R.id.replace_app_icon);
+        icon.setImageDrawable(next.info.applicationInfo.loadIcon(context.getPackageManager()));
+        TextView text = (TextView)findViewById(R.id.replace_message);
+        text.setText(context.getResources().getString(R.string.launch_warning_replace,
+                next.info.applicationInfo.loadLabel(context.getPackageManager()).toString()));
+        icon = (ImageView)findViewById(R.id.original_app_icon);
+        icon.setImageDrawable(cur.info.applicationInfo.loadIcon(context.getPackageManager()));
+        text = (TextView)findViewById(R.id.original_message);
+        text.setText(context.getResources().getString(R.string.launch_warning_original,
+                cur.info.applicationInfo.loadLabel(context.getPackageManager()).toString()));
+    }
+}
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
new file mode 100644
index 0000000..41d0777
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -0,0 +1,900 @@
+/*
+ * Copyright 2017, 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.server.wm;
+
+import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Context.DEVICE_POLICY_SERVICE;
+import static android.content.Context.STATUS_BAR_SERVICE;
+import static android.content.Intent.ACTION_CALL_EMERGENCY;
+import static android.os.UserHandle.USER_ALL;
+import static android.os.UserHandle.USER_CURRENT;
+import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.StatusBarManager;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.IDevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Helper class that deals with all things related to task locking. This includes the screen pinning
+ * mode that can be launched via System UI as well as the fully locked mode that can be achieved
+ * on fully managed devices.
+ *
+ * Note: All methods in this class should only be called with the ActivityTaskManagerService lock
+ * held.
+ *
+ * @see Activity#startLockTask()
+ * @see Activity#stopLockTask()
+ */
+public class LockTaskController {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "LockTaskController" : TAG_ATM;
+    private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
+
+    @VisibleForTesting
+    static final int STATUS_BAR_MASK_LOCKED = StatusBarManager.DISABLE_MASK
+            & (~StatusBarManager.DISABLE_EXPAND)
+            & (~StatusBarManager.DISABLE_NOTIFICATION_TICKER)
+            & (~StatusBarManager.DISABLE_SYSTEM_INFO)
+            & (~StatusBarManager.DISABLE_BACK);
+    @VisibleForTesting
+    static final int STATUS_BAR_MASK_PINNED = StatusBarManager.DISABLE_MASK
+            & (~StatusBarManager.DISABLE_BACK)
+            & (~StatusBarManager.DISABLE_HOME)
+            & (~StatusBarManager.DISABLE_RECENT);
+
+    private static final SparseArray<Pair<Integer, Integer>> STATUS_BAR_FLAG_MAP_LOCKED;
+    static {
+        STATUS_BAR_FLAG_MAP_LOCKED = new SparseArray<>();
+
+        STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO,
+                new Pair<>(StatusBarManager.DISABLE_CLOCK, StatusBarManager.DISABLE2_SYSTEM_ICONS));
+
+        STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS,
+                new Pair<>(StatusBarManager.DISABLE_NOTIFICATION_ICONS
+                        | StatusBarManager.DISABLE_NOTIFICATION_ALERTS,
+                        StatusBarManager.DISABLE2_NOTIFICATION_SHADE));
+
+        STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_HOME,
+                new Pair<>(StatusBarManager.DISABLE_HOME, StatusBarManager.DISABLE2_NONE));
+
+        STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW,
+                new Pair<>(StatusBarManager.DISABLE_RECENT, StatusBarManager.DISABLE2_NONE));
+
+        STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
+                new Pair<>(StatusBarManager.DISABLE_NONE,
+                        StatusBarManager.DISABLE2_GLOBAL_ACTIONS));
+    }
+
+    /** Tag used for disabling of keyguard */
+    private static final String LOCK_TASK_TAG = "Lock-to-App";
+
+    private final IBinder mToken = new Binder();
+    private final ActivityStackSupervisor mSupervisor;
+    private final Context mContext;
+
+    // The following system services cannot be final, because they do not exist when this class
+    // is instantiated during device boot
+    @VisibleForTesting
+    IStatusBarService mStatusBarService;
+    @VisibleForTesting
+    IDevicePolicyManager mDevicePolicyManager;
+    @VisibleForTesting
+    WindowManagerService mWindowManager;
+    @VisibleForTesting
+    LockPatternUtils mLockPatternUtils;
+    @VisibleForTesting
+    TelecomManager mTelecomManager;
+
+    /**
+     * The chain of tasks in LockTask mode, in the order of when they first entered LockTask mode.
+     *
+     * The first task in the list, which started the current LockTask session, is called the root
+     * task. It coincides with the Home task in a typical multi-app kiosk deployment. When there are
+     * more than one locked tasks, the root task can't be finished. Nor can it be moved to the back
+     * of the stack by {@link ActivityStack#moveTaskToBackLocked(int)};
+     *
+     * Calling {@link Activity#stopLockTask()} on the root task will finish all tasks but itself in
+     * this list, and the device will exit LockTask mode.
+     *
+     * The list is empty if LockTask is inactive.
+     */
+    private final ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>();
+
+    /**
+     * Packages that are allowed to be launched into the lock task mode for each user.
+     */
+    private final SparseArray<String[]> mLockTaskPackages = new SparseArray<>();
+
+    /**
+     * Features that are allowed by DPC to show during LockTask mode.
+     */
+    private final SparseIntArray mLockTaskFeatures = new SparseIntArray();
+
+    /**
+     * Store the current lock task mode. Possible values:
+     * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
+     * {@link ActivityManager#LOCK_TASK_MODE_PINNED}
+     */
+    private int mLockTaskModeState = LOCK_TASK_MODE_NONE;
+
+    /**
+     * This is ActivityStackSupervisor's Handler.
+     */
+    private final Handler mHandler;
+
+    LockTaskController(Context context, ActivityStackSupervisor supervisor,
+            Handler handler) {
+        mContext = context;
+        mSupervisor = supervisor;
+        mHandler = handler;
+    }
+
+    /**
+     * Set the window manager instance used in this class. This is necessary, because the window
+     * manager does not exist during instantiation of this class.
+     */
+    void setWindowManager(WindowManagerService windowManager) {
+        mWindowManager = windowManager;
+    }
+
+    /**
+     * @return the current lock task state. This can be any of
+     * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
+     * {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
+     */
+    int getLockTaskModeState() {
+        return mLockTaskModeState;
+    }
+
+    /**
+     * @return whether the given task is locked at the moment. Locked tasks cannot be moved to the
+     * back of the stack.
+     */
+    @VisibleForTesting
+    boolean isTaskLocked(TaskRecord task) {
+        return mLockTaskModeTasks.contains(task);
+    }
+
+    /**
+     * @return {@code true} whether this task first started the current LockTask session.
+     */
+    private boolean isRootTask(TaskRecord task) {
+        return mLockTaskModeTasks.indexOf(task) == 0;
+    }
+
+    /**
+     * @return whether the given activity is blocked from finishing, because it is the only activity
+     * of the last locked task and finishing it would mean that lock task mode is ended illegally.
+     */
+    boolean activityBlockedFromFinish(ActivityRecord activity) {
+        final TaskRecord task = activity.getTask();
+        if (activity == task.getRootActivity()
+                && activity == task.getTopActivity()
+                && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
+                && isRootTask(task)) {
+            Slog.i(TAG, "Not finishing task in lock task mode");
+            showLockTaskToast();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return whether the given task can be moved to the back of the stack with
+     * {@link ActivityStack#moveTaskToBackLocked(int)}
+     * @see #mLockTaskModeTasks
+     */
+    boolean canMoveTaskToBack(TaskRecord task) {
+        if (isRootTask(task)) {
+            showLockTaskToast();
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * @return whether the requested task is allowed to be locked (either whitelisted, or declares
+     * lockTaskMode="always" in the manifest).
+     */
+    boolean isTaskWhitelisted(TaskRecord task) {
+        switch(task.mLockTaskAuth) {
+            case LOCK_TASK_AUTH_WHITELISTED:
+            case LOCK_TASK_AUTH_LAUNCHABLE:
+            case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
+                return true;
+            case LOCK_TASK_AUTH_PINNABLE:
+            case LOCK_TASK_AUTH_DONT_LOCK:
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * @return whether the requested task is disallowed to be launched.
+     */
+    boolean isLockTaskModeViolation(TaskRecord task) {
+        return isLockTaskModeViolation(task, false);
+    }
+
+    /**
+     * @param isNewClearTask whether the task would be cleared as part of the operation.
+     * @return whether the requested task is disallowed to be launched.
+     */
+    boolean isLockTaskModeViolation(TaskRecord task, boolean isNewClearTask) {
+        if (isLockTaskModeViolationInternal(task, isNewClearTask)) {
+            showLockTaskToast();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return the root task of the lock task.
+     */
+    TaskRecord getRootTask() {
+        if (mLockTaskModeTasks.isEmpty()) {
+            return null;
+        }
+        return mLockTaskModeTasks.get(0);
+    }
+
+    private boolean isLockTaskModeViolationInternal(TaskRecord task, boolean isNewClearTask) {
+        // TODO: Double check what's going on here. If the task is already in lock task mode, it's
+        // likely whitelisted, so will return false below.
+        if (isTaskLocked(task) && !isNewClearTask) {
+            // If the task is already at the top and won't be cleared, then allow the operation
+            return false;
+        }
+
+        // Allow recents activity if enabled by policy
+        if (task.isActivityTypeRecents() && isRecentsAllowed(task.userId)) {
+            return false;
+        }
+
+        // Allow emergency calling when the device is protected by a locked keyguard
+        if (isKeyguardAllowed(task.userId) && isEmergencyCallTask(task)) {
+            return false;
+        }
+
+        return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty());
+    }
+
+    private boolean isRecentsAllowed(int userId) {
+        return (getLockTaskFeaturesForUser(userId)
+                & DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW) != 0;
+    }
+
+    private boolean isKeyguardAllowed(int userId) {
+        return (getLockTaskFeaturesForUser(userId)
+                & DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0;
+    }
+
+    private boolean isEmergencyCallTask(TaskRecord task) {
+        final Intent intent = task.intent;
+        if (intent == null) {
+            return false;
+        }
+
+        // 1. The emergency keypad activity launched on top of the keyguard
+        if (EMERGENCY_DIALER_COMPONENT.equals(intent.getComponent())) {
+            return true;
+        }
+
+        // 2. The intent sent by the keypad, which is handled by Telephony
+        if (ACTION_CALL_EMERGENCY.equals(intent.getAction())) {
+            return true;
+        }
+
+        // 3. Telephony then starts the default package for making the call
+        final TelecomManager tm = getTelecomManager();
+        final String dialerPackage = tm != null ? tm.getSystemDialerPackage() : null;
+        if (dialerPackage != null && dialerPackage.equals(intent.getComponent().getPackageName())) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Stop the current lock task mode.
+     *
+     * This is called by {@link ActivityManagerService} and performs various checks before actually
+     * finishing the locked task.
+     *
+     * @param task the task that requested the end of lock task mode ({@code null} for quitting app
+     *             pinning mode)
+     * @param isSystemCaller indicates whether this request comes from the system via
+     *                       {@link ActivityTaskManagerService#stopSystemLockTaskMode()}. If
+     *                       {@code true}, it means the user intends to stop pinned mode through UI;
+     *                       otherwise, it's called by an app and we need to stop locked or pinned
+     *                       mode, subject to checks.
+     * @param callingUid the caller that requested the end of lock task mode.
+     * @throws IllegalArgumentException if the calling task is invalid (e.g., {@code null} or not in
+     *                                  foreground)
+     * @throws SecurityException if the caller is not authorized to stop the lock task mode, i.e. if
+     *                           they differ from the one that launched lock task mode.
+     */
+    void stopLockTaskMode(@Nullable TaskRecord task, boolean isSystemCaller, int callingUid) {
+        if (mLockTaskModeState == LOCK_TASK_MODE_NONE) {
+            return;
+        }
+
+        if (isSystemCaller) {
+            if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
+                clearLockedTasks("stopAppPinning");
+            } else {
+                Slog.e(TAG_LOCKTASK, "Attempted to stop LockTask with isSystemCaller=true");
+                showLockTaskToast();
+            }
+
+        } else {
+            // Ensure calling activity is not null
+            if (task == null) {
+                throw new IllegalArgumentException("can't stop LockTask for null task");
+            }
+
+            // Ensure the same caller for startLockTaskMode and stopLockTaskMode.
+            // It is possible lockTaskMode was started by the system process because
+            // android:lockTaskMode is set to a locking value in the application manifest
+            // instead of the app calling startLockTaskMode. In this case
+            // {@link TaskRecord.mLockTaskUid} will be 0, so we compare the callingUid to the
+            // {@link TaskRecord.effectiveUid} instead. Also caller with
+            // {@link MANAGE_ACTIVITY_STACKS} can stop any lock task.
+            if (callingUid != task.mLockTaskUid
+                    && (task.mLockTaskUid != 0 || callingUid != task.effectiveUid)) {
+                throw new SecurityException("Invalid uid, expected " + task.mLockTaskUid
+                        + " callingUid=" + callingUid + " effectiveUid=" + task.effectiveUid);
+            }
+
+            // We don't care if it's pinned or locked mode; this will stop it anyways.
+            clearLockedTask(task);
+        }
+    }
+
+    /**
+     * Clear all locked tasks and request the end of LockTask mode.
+     *
+     * This method is called by UserController when starting a new foreground user, and,
+     * unlike {@link #stopLockTaskMode(TaskRecord, boolean, int)}, it doesn't perform the checks.
+     */
+    void clearLockedTasks(String reason) {
+        if (DEBUG_LOCKTASK) Slog.i(TAG_LOCKTASK, "clearLockedTasks: " + reason);
+        if (!mLockTaskModeTasks.isEmpty()) {
+            clearLockedTask(mLockTaskModeTasks.get(0));
+        }
+    }
+
+    /**
+     * Clear one locked task from LockTask mode.
+     *
+     * If the requested task is the root task (see {@link #mLockTaskModeTasks}), then all locked
+     * tasks are cleared. Otherwise, only the requested task is cleared. LockTask mode is stopped
+     * when the last locked task is cleared.
+     *
+     * @param task the task to be cleared from LockTask mode.
+     */
+    void clearLockedTask(final TaskRecord task) {
+        if (task == null || mLockTaskModeTasks.isEmpty()) return;
+
+        if (task == mLockTaskModeTasks.get(0)) {
+            // We're removing the root task while there are other locked tasks. Therefore we should
+            // clear all locked tasks in reverse order.
+            for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx > 0; --taskNdx) {
+                clearLockedTask(mLockTaskModeTasks.get(taskNdx));
+            }
+        }
+
+        removeLockedTask(task);
+        if (mLockTaskModeTasks.isEmpty()) {
+            return;
+        }
+        task.performClearTaskLocked();
+        mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+    }
+
+    /**
+     * Remove the given task from the locked task list. If this was the last task in the list,
+     * lock task mode is stopped.
+     */
+    private void removeLockedTask(final TaskRecord task) {
+        if (!mLockTaskModeTasks.remove(task)) {
+            return;
+        }
+        if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: removed " + task);
+        if (mLockTaskModeTasks.isEmpty()) {
+            if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task +
+                    " last task, reverting locktask mode. Callers=" + Debug.getCallers(3));
+            mHandler.post(() -> performStopLockTask(task.userId));
+        }
+    }
+
+    // This method should only be called on the handler thread
+    private void performStopLockTask(int userId) {
+        // When lock task ends, we enable the status bars.
+        try {
+            setStatusBarState(LOCK_TASK_MODE_NONE, userId);
+            setKeyguardState(LOCK_TASK_MODE_NONE, userId);
+            if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
+                lockKeyguardIfNeeded();
+            }
+            if (getDevicePolicyManager() != null) {
+                getDevicePolicyManager().notifyLockTaskModeChanged(false, null, userId);
+            }
+            if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
+                getStatusBarService().showPinningEnterExitToast(false /* entering */);
+            }
+            mWindowManager.onLockTaskStateChanged(LOCK_TASK_MODE_NONE);
+        } catch (RemoteException ex) {
+            throw new RuntimeException(ex);
+        } finally {
+            mLockTaskModeState = LOCK_TASK_MODE_NONE;
+        }
+    }
+
+    /**
+     * Show the lock task violation toast. Currently we only show toast for screen pinning mode, and
+     * no-op if the device is in locked mode.
+     */
+    void showLockTaskToast() {
+        if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
+            try {
+                getStatusBarService().showPinningEscapeToast();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to send pinning escape toast", e);
+            }
+        }
+    }
+
+    // Starting lock task
+
+    /**
+     * Method to start lock task mode on a given task.
+     *
+     * @param task the task that should be locked.
+     * @param isSystemCaller indicates whether this request was initiated by the system via
+     *                       {@link ActivityTaskManagerService#startSystemLockTaskMode(int)}. If
+     *                       {@code true}, this intends to start pinned mode; otherwise, we look
+     *                       at the calling task's mLockTaskAuth to decide which mode to start.
+     * @param callingUid the caller that requested the launch of lock task mode.
+     */
+    void startLockTaskMode(@NonNull TaskRecord task, boolean isSystemCaller, int callingUid) {
+        if (!isSystemCaller) {
+            task.mLockTaskUid = callingUid;
+            if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
+                // startLockTask() called by app, but app is not part of lock task whitelist. Show
+                // app pinning request. We will come back here with isSystemCaller true.
+                if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user");
+                StatusBarManagerInternal statusBarManager = LocalServices.getService(
+                        StatusBarManagerInternal.class);
+                if (statusBarManager != null) {
+                    statusBarManager.showScreenPinningRequest(task.taskId);
+                }
+                return;
+            }
+        }
+
+        // System can only initiate screen pinning, not full lock task mode
+        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
+                isSystemCaller ? "Locking pinned" : "Locking fully");
+        setLockTaskMode(task, isSystemCaller ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED,
+                "startLockTask", true);
+    }
+
+    /**
+     * Start lock task mode on the given task.
+     * @param lockTaskModeState whether fully locked or pinned mode.
+     * @param andResume whether the task should be brought to foreground as part of the operation.
+     */
+    private void setLockTaskMode(@NonNull TaskRecord task, int lockTaskModeState,
+                                 String reason, boolean andResume) {
+        // Should have already been checked, but do it again.
+        if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
+                    "setLockTaskMode: Can't lock due to auth");
+            return;
+        }
+        if (isLockTaskModeViolation(task)) {
+            Slog.e(TAG_LOCKTASK, "setLockTaskMode: Attempt to start an unauthorized lock task.");
+            return;
+        }
+
+        final Intent taskIntent = task.intent;
+        if (mLockTaskModeTasks.isEmpty() && taskIntent != null) {
+            mSupervisor.mRecentTasks.onLockTaskModeStateChanged(lockTaskModeState, task.userId);
+            // Start lock task on the handler thread
+            mHandler.post(() -> performStartLockTask(
+                    taskIntent.getComponent().getPackageName(),
+                    task.userId,
+                    lockTaskModeState));
+        }
+        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskMode: Locking to " + task +
+                " Callers=" + Debug.getCallers(4));
+
+        if (!mLockTaskModeTasks.contains(task)) {
+            mLockTaskModeTasks.add(task);
+        }
+
+        if (task.mLockTaskUid == -1) {
+            task.mLockTaskUid = task.effectiveUid;
+        }
+
+        if (andResume) {
+            mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
+                    lockTaskModeState != LOCK_TASK_MODE_NONE);
+            mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            final ActivityStack stack = task.getStack();
+            if (stack != null) {
+                stack.getDisplay().getWindowContainerController().executeAppTransition();
+            }
+        } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
+            mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
+                    DEFAULT_DISPLAY, task.getStack(), true /* forceNonResizable */);
+        }
+    }
+
+    // This method should only be called on the handler thread
+    private void performStartLockTask(String packageName, int userId, int lockTaskModeState) {
+        // When lock task starts, we disable the status bars.
+        try {
+            if (lockTaskModeState == LOCK_TASK_MODE_PINNED) {
+                getStatusBarService().showPinningEnterExitToast(true /* entering */);
+            }
+            mWindowManager.onLockTaskStateChanged(lockTaskModeState);
+            mLockTaskModeState = lockTaskModeState;
+            setStatusBarState(lockTaskModeState, userId);
+            setKeyguardState(lockTaskModeState, userId);
+            if (getDevicePolicyManager() != null) {
+                getDevicePolicyManager().notifyLockTaskModeChanged(true, packageName, userId);
+            }
+        } catch (RemoteException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Update packages that are allowed to be launched in lock task mode.
+     * @param userId Which user this whitelist is associated with
+     * @param packages The whitelist of packages allowed in lock task mode
+     * @see #mLockTaskPackages
+     */
+    void updateLockTaskPackages(int userId, String[] packages) {
+        mLockTaskPackages.put(userId, packages);
+
+        boolean taskChanged = false;
+        for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord lockedTask = mLockTaskModeTasks.get(taskNdx);
+            final boolean wasWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
+                    || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
+            lockedTask.setLockTaskAuth();
+            final boolean isWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
+                    || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
+
+            if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED
+                    || lockedTask.userId != userId
+                    || !wasWhitelisted || isWhitelisted) {
+                continue;
+            }
+
+            // Terminate locked tasks that have recently lost whitelist authorization.
+            if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
+                    lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString());
+            removeLockedTask(lockedTask);
+            lockedTask.performClearTaskLocked();
+            taskChanged = true;
+        }
+
+        for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+            mSupervisor.getChildAt(displayNdx).onLockTaskPackagesUpdated();
+        }
+
+        final ActivityRecord r = mSupervisor.topRunningActivityLocked();
+        final TaskRecord task = (r != null) ? r.getTask() : null;
+        if (mLockTaskModeTasks.isEmpty() && task!= null
+                && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
+            // This task must have just been authorized.
+            if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK,
+                    "onLockTaskPackagesUpdated: starting new locktask task=" + task);
+            setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated", false);
+            taskChanged = true;
+        }
+
+        if (taskChanged) {
+            mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        }
+    }
+
+    boolean isPackageWhitelisted(int userId, String pkg) {
+        if (pkg == null) {
+            return false;
+        }
+        String[] whitelist;
+        whitelist = mLockTaskPackages.get(userId);
+        if (whitelist == null) {
+            return false;
+        }
+        for (String whitelistedPkg : whitelist) {
+            if (pkg.equals(whitelistedPkg)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Update the UI features that are enabled for LockTask mode.
+     * @param userId Which user these feature flags are associated with
+     * @param flags Bitfield of feature flags
+     * @see DevicePolicyManager#setLockTaskFeatures(ComponentName, int)
+     */
+    void updateLockTaskFeatures(int userId, int flags) {
+        int oldFlags = getLockTaskFeaturesForUser(userId);
+        if (flags == oldFlags) {
+            return;
+        }
+
+        mLockTaskFeatures.put(userId, flags);
+        if (!mLockTaskModeTasks.isEmpty() && userId == mLockTaskModeTasks.get(0).userId) {
+            mHandler.post(() -> {
+                if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
+                    setStatusBarState(mLockTaskModeState, userId);
+                    setKeyguardState(mLockTaskModeState, userId);
+                }
+            });
+        }
+    }
+
+    /**
+     * Helper method for configuring the status bar disabled state.
+     * Should only be called on the handler thread to avoid race.
+     */
+    private void setStatusBarState(int lockTaskModeState, int userId) {
+        IStatusBarService statusBar = getStatusBarService();
+        if (statusBar == null) {
+            Slog.e(TAG, "Can't find StatusBarService");
+            return;
+        }
+
+        // Default state, when lockTaskModeState == LOCK_TASK_MODE_NONE
+        int flags1 = StatusBarManager.DISABLE_NONE;
+        int flags2 = StatusBarManager.DISABLE2_NONE;
+
+        if (lockTaskModeState == LOCK_TASK_MODE_PINNED) {
+            flags1 = STATUS_BAR_MASK_PINNED;
+
+        } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) {
+            int lockTaskFeatures = getLockTaskFeaturesForUser(userId);
+            Pair<Integer, Integer> statusBarFlags = getStatusBarDisableFlags(lockTaskFeatures);
+            flags1 = statusBarFlags.first;
+            flags2 = statusBarFlags.second;
+        }
+
+        try {
+            statusBar.disable(flags1, mToken, mContext.getPackageName());
+            statusBar.disable2(flags2, mToken, mContext.getPackageName());
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to set status bar flags", e);
+        }
+    }
+
+    /**
+     * Helper method for configuring the keyguard disabled state.
+     * Should only be called on the handler thread to avoid race.
+     */
+    private void setKeyguardState(int lockTaskModeState, int userId) {
+        if (lockTaskModeState == LOCK_TASK_MODE_NONE) {
+            mWindowManager.reenableKeyguard(mToken);
+
+        } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) {
+            if (isKeyguardAllowed(userId)) {
+                mWindowManager.reenableKeyguard(mToken);
+            } else {
+                // If keyguard is not secure and it is locked, dismiss the keyguard before
+                // disabling it, which avoids the platform to think the keyguard is still on.
+                if (mWindowManager.isKeyguardLocked() && !mWindowManager.isKeyguardSecure()) {
+                    mWindowManager.dismissKeyguard(new IKeyguardDismissCallback.Stub() {
+                        @Override
+                        public void onDismissError() throws RemoteException {
+                            Slog.i(TAG, "setKeyguardState: failed to dismiss keyguard");
+                        }
+
+                        @Override
+                        public void onDismissSucceeded() throws RemoteException {
+                            mHandler.post(
+                                    () -> mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG));
+                        }
+
+                        @Override
+                        public void onDismissCancelled() throws RemoteException {
+                            Slog.i(TAG, "setKeyguardState: dismiss cancelled");
+                        }
+                    }, null);
+                } else {
+                    mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
+                }
+            }
+
+        } else { // lockTaskModeState == LOCK_TASK_MODE_PINNED
+            mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
+        }
+    }
+
+    /**
+     * Helper method for locking the device immediately. This may be necessary when the device
+     * leaves the pinned mode.
+     */
+    private void lockKeyguardIfNeeded() {
+        try {
+            boolean shouldLockKeyguard = Settings.Secure.getIntForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
+                    USER_CURRENT) != 0;
+            if (shouldLockKeyguard) {
+                mWindowManager.lockNow(null);
+                mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
+                getLockPatternUtils().requireCredentialEntry(USER_ALL);
+            }
+        } catch (Settings.SettingNotFoundException e) {
+            // No setting, don't lock.
+        }
+    }
+
+    /**
+     * Translates from LockTask feature flags to StatusBarManager disable and disable2 flags.
+     * @param lockTaskFlags Bitfield of flags as per
+     *                      {@link DevicePolicyManager#setLockTaskFeatures(ComponentName, int)}
+     * @return A {@link Pair} of {@link StatusBarManager#disable(int)} and
+     *         {@link StatusBarManager#disable2(int)} flags
+     */
+    @VisibleForTesting
+    Pair<Integer, Integer> getStatusBarDisableFlags(int lockTaskFlags) {
+        // Everything is disabled by default
+        int flags1 = StatusBarManager.DISABLE_MASK;
+        int flags2 = StatusBarManager.DISABLE2_MASK;
+        for (int i = STATUS_BAR_FLAG_MAP_LOCKED.size() - 1; i >= 0; i--) {
+            Pair<Integer, Integer> statusBarFlags = STATUS_BAR_FLAG_MAP_LOCKED.valueAt(i);
+            if ((STATUS_BAR_FLAG_MAP_LOCKED.keyAt(i) & lockTaskFlags) != 0) {
+                flags1 &= ~statusBarFlags.first;
+                flags2 &= ~statusBarFlags.second;
+            }
+        }
+        // Some flags are not used for LockTask purposes, so we mask them
+        flags1 &= STATUS_BAR_MASK_LOCKED;
+        return new Pair<>(flags1, flags2);
+    }
+
+    /**
+     * Gets the cached value of LockTask feature flags for a specific user.
+     */
+    private int getLockTaskFeaturesForUser(int userId) {
+        return mLockTaskFeatures.get(userId, DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
+    }
+
+    // Should only be called on the handler thread
+    @Nullable
+    private IStatusBarService getStatusBarService() {
+        if (mStatusBarService == null) {
+            mStatusBarService = IStatusBarService.Stub.asInterface(
+                    ServiceManager.checkService(STATUS_BAR_SERVICE));
+            if (mStatusBarService == null) {
+                Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
+            }
+        }
+        return mStatusBarService;
+    }
+
+    // Should only be called on the handler thread
+    @Nullable
+    private IDevicePolicyManager getDevicePolicyManager() {
+        if (mDevicePolicyManager == null) {
+            mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
+                    ServiceManager.checkService(DEVICE_POLICY_SERVICE));
+            if (mDevicePolicyManager == null) {
+                Slog.w(TAG, "warning: no DEVICE_POLICY_SERVICE");
+            }
+        }
+        return mDevicePolicyManager;
+    }
+
+    @NonNull
+    private LockPatternUtils getLockPatternUtils() {
+        if (mLockPatternUtils == null) {
+            // We don't preserve the LPU object to save memory
+            return new LockPatternUtils(mContext);
+        }
+        return mLockPatternUtils;
+    }
+
+    @Nullable
+    private TelecomManager getTelecomManager() {
+        if (mTelecomManager == null) {
+            // We don't preserve the TelecomManager object to save memory
+            return mContext.getSystemService(TelecomManager.class);
+        }
+        return mTelecomManager;
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        pw.println(prefix + "LockTaskController");
+        prefix = prefix + "  ";
+        pw.println(prefix + "mLockTaskModeState=" + lockTaskModeToString());
+        pw.println(prefix + "mLockTaskModeTasks=");
+        for (int i = 0; i < mLockTaskModeTasks.size(); ++i) {
+            pw.println(prefix + "  #" + i + " " + mLockTaskModeTasks.get(i));
+        }
+        pw.println(prefix + "mLockTaskPackages (userId:packages)=");
+        for (int i = 0; i < mLockTaskPackages.size(); ++i) {
+            pw.println(prefix + "  u" + mLockTaskPackages.keyAt(i)
+                    + ":" + Arrays.toString(mLockTaskPackages.valueAt(i)));
+        }
+    }
+
+    private String lockTaskModeToString() {
+        switch (mLockTaskModeState) {
+            case LOCK_TASK_MODE_LOCKED:
+                return "LOCKED";
+            case LOCK_TASK_MODE_PINNED:
+                return "PINNED";
+            case LOCK_TASK_MODE_NONE:
+                return "NONE";
+            default: return "unknown=" + mLockTaskModeState;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/PendingRemoteAnimationRegistry.java b/services/core/java/com/android/server/wm/PendingRemoteAnimationRegistry.java
new file mode 100644
index 0000000..dcb9a6a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PendingRemoteAnimationRegistry.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.os.Handler;
+import android.util.ArrayMap;
+import android.view.RemoteAnimationAdapter;
+
+import com.android.server.am.ActivityManagerService;
+
+/**
+ * Registry to keep track of remote animations to be run for activity starts from a certain package.
+ *
+ * @see ActivityManagerService#registerRemoteAnimationForNextActivityStart
+ */
+class PendingRemoteAnimationRegistry {
+
+    private static final long TIMEOUT_MS = 3000;
+
+    private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
+    private final Handler mHandler;
+    private final ActivityTaskManagerService mService;
+
+    PendingRemoteAnimationRegistry(ActivityTaskManagerService service, Handler handler) {
+        mService = service;
+        mHandler = handler;
+    }
+
+    /**
+     * Adds a remote animation to be run for all activity starts originating from a certain package.
+     */
+    void addPendingAnimation(String packageName, RemoteAnimationAdapter adapter) {
+        mEntries.put(packageName, new Entry(packageName, adapter));
+    }
+
+    /**
+     * Overrides the activity options with a registered remote animation for a certain calling
+     * package if such a remote animation is registered.
+     */
+    ActivityOptions overrideOptionsIfNeeded(String callingPackage,
+            @Nullable ActivityOptions options) {
+        final Entry entry = mEntries.get(callingPackage);
+        if (entry == null) {
+            return options;
+        }
+        if (options == null) {
+            options = ActivityOptions.makeRemoteAnimation(entry.adapter);
+        } else {
+            options.setRemoteAnimationAdapter(entry.adapter);
+        }
+        mEntries.remove(callingPackage);
+        return options;
+    }
+
+    private class Entry {
+        final String packageName;
+        final RemoteAnimationAdapter adapter;
+
+        Entry(String packageName, RemoteAnimationAdapter adapter) {
+            this.packageName = packageName;
+            this.adapter = adapter;
+            mHandler.postDelayed(() -> {
+                synchronized (mService.mGlobalLock) {
+                    final Entry entry = mEntries.get(packageName);
+                    if (entry == this) {
+                        mEntries.remove(packageName);
+                    }
+                }
+            }, TIMEOUT_MS);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/PersisterQueue.java b/services/core/java/com/android/server/wm/PersisterQueue.java
new file mode 100644
index 0000000..1cfc7ac
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PersisterQueue.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.function.Predicate;
+
+/**
+ * The common threading logic for persisters to use so that they can run in the same threads.
+ * Methods in this class are synchronized on its instance, so caller could also synchronize on
+ * its instance to perform modifications in items.
+ */
+class PersisterQueue {
+    private static final String TAG = "PersisterQueue";
+    private static final boolean DEBUG = false;
+
+    /** When not flushing don't write out files faster than this */
+    private static final long INTER_WRITE_DELAY_MS = 500;
+
+    /**
+     * When not flushing delay this long before writing the first file out. This gives the next task
+     * being launched a chance to load its resources without this occupying IO bandwidth.
+     */
+    private static final long PRE_TASK_DELAY_MS = 3000;
+
+    /** The maximum number of entries to keep in the queue before draining it automatically. */
+    private static final int MAX_WRITE_QUEUE_LENGTH = 6;
+
+    /** Special value for mWriteTime to mean don't wait, just write */
+    private static final long FLUSH_QUEUE = -1;
+
+    /** An {@link WriteQueueItem} that doesn't do anything. Used to trigger {@link
+     * Listener#onPreProcessItem}. */
+    static final WriteQueueItem EMPTY_ITEM = () -> { };
+
+    private final long mInterWriteDelayMs;
+    private final long mPreTaskDelayMs;
+    private final LazyTaskWriterThread mLazyTaskWriterThread;
+    private final ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<>();
+
+    private final ArrayList<Listener> mListeners = new ArrayList<>();
+
+    /**
+     * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
+     * until the image queue is drained and all tasks needing persisting are written to disk. There
+     * is no delay between writes. == 0 We are Idle. Next writes will be delayed by
+     * #PRE_TASK_DELAY_MS. > 0 We are Actively writing. Next write will be at this time. Subsequent
+     * writes will be delayed by #INTER_WRITE_DELAY_MS.
+     */
+    private long mNextWriteTime = 0;
+
+    PersisterQueue() {
+        this(INTER_WRITE_DELAY_MS, PRE_TASK_DELAY_MS);
+    }
+
+    /** Used for tests to reduce waiting time. */
+    @VisibleForTesting
+    PersisterQueue(long interWriteDelayMs, long preTaskDelayMs) {
+        if (interWriteDelayMs < 0 || preTaskDelayMs < 0) {
+            throw new IllegalArgumentException("Both inter-write delay and pre-task delay need to"
+                    + "be non-negative. inter-write delay: " + interWriteDelayMs
+                    + "ms pre-task delay: " + preTaskDelayMs);
+        }
+        mInterWriteDelayMs = interWriteDelayMs;
+        mPreTaskDelayMs = preTaskDelayMs;
+        mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
+    }
+
+    synchronized void startPersisting() {
+        if (!mLazyTaskWriterThread.isAlive()) {
+            mLazyTaskWriterThread.start();
+        }
+    }
+
+    /** Stops persisting thread. Should only be used in tests. */
+    @VisibleForTesting
+    void stopPersisting() throws InterruptedException {
+        if (!mLazyTaskWriterThread.isAlive()) {
+            return;
+        }
+
+        synchronized (this) {
+            mLazyTaskWriterThread.interrupt();
+        }
+        mLazyTaskWriterThread.join();
+    }
+
+    synchronized void addItem(WriteQueueItem item, boolean flush) {
+        mWriteQueue.add(item);
+
+        if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
+            mNextWriteTime = FLUSH_QUEUE;
+        } else if (mNextWriteTime == 0) {
+            mNextWriteTime = SystemClock.uptimeMillis() + mPreTaskDelayMs;
+        }
+        notify();
+    }
+
+    synchronized <T extends WriteQueueItem> T findLastItem(Predicate<T> predicate, Class<T> clazz) {
+        for (int i = mWriteQueue.size() - 1; i >= 0; --i) {
+            WriteQueueItem writeQueueItem = mWriteQueue.get(i);
+            if (clazz.isInstance(writeQueueItem)) {
+                T item = clazz.cast(writeQueueItem);
+                if (predicate.test(item)) {
+                    return item;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    synchronized <T extends WriteQueueItem> void removeItems(Predicate<T> predicate,
+            Class<T> clazz) {
+        for (int i = mWriteQueue.size() - 1; i >= 0; --i) {
+            WriteQueueItem writeQueueItem = mWriteQueue.get(i);
+            if (clazz.isInstance(writeQueueItem)) {
+                T item = clazz.cast(writeQueueItem);
+                if (predicate.test(item)) {
+                    if (DEBUG) Slog.d(TAG, "Removing " + item + " from write queue.");
+                    mWriteQueue.remove(i);
+                }
+            }
+        }
+    }
+
+    synchronized void flush() {
+        mNextWriteTime = FLUSH_QUEUE;
+        notifyAll();
+        do {
+            try {
+                wait();
+            } catch (InterruptedException e) {
+            }
+        } while (mNextWriteTime == FLUSH_QUEUE);
+    }
+
+    void yieldIfQueueTooDeep() {
+        boolean stall = false;
+        synchronized (this) {
+            if (mNextWriteTime == FLUSH_QUEUE) {
+                stall = true;
+            }
+        }
+        if (stall) {
+            Thread.yield();
+        }
+    }
+
+    void addListener(Listener listener) {
+        mListeners.add(listener);
+    }
+
+    @VisibleForTesting
+    boolean removeListener(Listener listener) {
+        return mListeners.remove(listener);
+    }
+
+    private void processNextItem() throws InterruptedException {
+        // This part is extracted into a method so that the GC can clearly see the end of the
+        // scope of the variable 'item'.  If this part was in the loop in LazyTaskWriterThread, the
+        // last item it processed would always "leak".
+        // See https://b.corp.google.com/issues/64438652#comment7
+
+        // If mNextWriteTime, then don't delay between each call to saveToXml().
+        final WriteQueueItem item;
+        synchronized (this) {
+            if (mNextWriteTime != FLUSH_QUEUE) {
+                // The next write we don't have to wait so long.
+                mNextWriteTime = SystemClock.uptimeMillis() + mInterWriteDelayMs;
+                if (DEBUG) {
+                    Slog.d(TAG, "Next write time may be in " + mInterWriteDelayMs
+                            + " msec. (" + mNextWriteTime + ")");
+                }
+            }
+
+            while (mWriteQueue.isEmpty()) {
+                if (mNextWriteTime != 0) {
+                    mNextWriteTime = 0; // idle.
+                    notify(); // May need to wake up flush().
+                }
+                // Make sure we exit this thread correctly when interrupted before going to
+                // indefinite wait.
+                if (Thread.currentThread().isInterrupted()) {
+                    throw new InterruptedException();
+                }
+                if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
+                wait();
+                // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
+                // from now.
+            }
+            item = mWriteQueue.remove(0);
+
+            long now = SystemClock.uptimeMillis();
+            if (DEBUG) {
+                Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" + mNextWriteTime
+                        + " mWriteQueue.size=" + mWriteQueue.size());
+            }
+            while (now < mNextWriteTime) {
+                if (DEBUG) {
+                    Slog.d(TAG, "LazyTaskWriter: waiting " + (mNextWriteTime - now));
+                }
+                wait(mNextWriteTime - now);
+                now = SystemClock.uptimeMillis();
+            }
+
+            // Got something to do.
+        }
+
+        item.process();
+    }
+
+    interface WriteQueueItem {
+        void process();
+    }
+
+    interface Listener {
+        /**
+         * Called before {@link PersisterQueue} tries to process next item.
+         *
+         * Note if the queue is empty, this callback will be called before the indefinite wait. This
+         * will be called once when {@link PersisterQueue} starts the internal thread before the
+         * indefinite wait.
+         *
+         * This callback is called w/o locking the instance of {@link PersisterQueue}.
+         *
+         * @param queueEmpty {@code true} if the queue is empty, which indicates {@link
+         * PersisterQueue} is likely to enter indefinite wait; or {@code false} if there is still
+         * item to process.
+         */
+        void onPreProcessItem(boolean queueEmpty);
+    }
+
+    private class LazyTaskWriterThread extends Thread {
+
+        private LazyTaskWriterThread(String name) {
+            super(name);
+        }
+
+        @Override
+        public void run() {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+            try {
+                while (true) {
+                    final boolean probablyDone;
+                    synchronized (PersisterQueue.this) {
+                        probablyDone = mWriteQueue.isEmpty();
+                    }
+
+                    for (int i = mListeners.size() - 1; i >= 0; --i) {
+                        mListeners.get(i).onPreProcessItem(probablyDone);
+                    }
+
+                    processNextItem();
+                }
+            } catch (InterruptedException e) {
+                Slog.e(TAG, "Persister thread is exiting. Should never happen in prod, but"
+                        + "it's OK in tests.");
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/PinnedActivityStack.java b/services/core/java/com/android/server/wm/PinnedActivityStack.java
new file mode 100644
index 0000000..af18077
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PinnedActivityStack.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
+import android.app.RemoteAction;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * State and management of the pinned stack of activities.
+ */
+class PinnedActivityStack extends ActivityStack<PinnedStackWindowController>
+        implements PinnedStackWindowListener {
+
+    PinnedActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
+            boolean onTop) {
+        super(display, stackId, supervisor, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, onTop);
+    }
+
+    @Override
+    PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
+            Rect outBounds) {
+        return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
+                mStackSupervisor.mWindowManager);
+    }
+
+    Rect getDefaultPictureInPictureBounds(float aspectRatio) {
+        return getWindowContainerController().getPictureInPictureBounds(aspectRatio,
+                null /* currentStackBounds */);
+    }
+
+    void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration,
+            boolean fromFullscreen) {
+        if (skipResizeAnimation(toBounds == null /* toFullscreen */)) {
+            mService.moveTasksToFullscreenStack(mStackId, true /* onTop */);
+        } else {
+            getWindowContainerController().animateResizePinnedStack(toBounds, sourceHintBounds,
+                    animationDuration, fromFullscreen);
+        }
+    }
+
+    private boolean skipResizeAnimation(boolean toFullscreen) {
+        if (!toFullscreen) {
+            return false;
+        }
+        final Configuration parentConfig = getParent().getConfiguration();
+        final ActivityRecord top = topRunningNonOverlayTaskActivity();
+        return top != null && !top.isConfigurationCompatible(parentConfig);
+    }
+
+    void setPictureInPictureAspectRatio(float aspectRatio) {
+        getWindowContainerController().setPictureInPictureAspectRatio(aspectRatio);
+    }
+
+    void setPictureInPictureActions(List<RemoteAction> actions) {
+        getWindowContainerController().setPictureInPictureActions(actions);
+    }
+
+    boolean isAnimatingBoundsToFullscreen() {
+        return getWindowContainerController().isAnimatingBoundsToFullscreen();
+    }
+
+    /**
+     * Returns whether to defer the scheduling of the multi-window mode.
+     */
+    boolean deferScheduleMultiWindowModeChanged() {
+        // For the pinned stack, the deferring of the multi-window mode changed is tied to the
+        // transition animation into picture-in-picture, and is called once the animation completes,
+        // or is interrupted in a way that would leave the stack in a non-fullscreen state.
+        // @see BoundsAnimationController
+        // @see BoundsAnimationControllerTests
+        return mWindowContainerController.deferScheduleMultiWindowModeChanged();
+    }
+
+    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+            boolean forceUpdate) {
+        // It is guaranteed that the activities requiring the update will be in the pinned stack at
+        // this point (either reparented before the animation into PiP, or before reparenting after
+        // the animation out of PiP)
+        synchronized (mService) {
+            ArrayList<TaskRecord> tasks = getAllTasks();
+            for (int i = 0; i < tasks.size(); i++ ) {
+                mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
+                        forceUpdate);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index b64d4f8..d21f67d 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -20,10 +20,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.util.TypedValue.COMPLEX_UNIT_DIP;
 
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.PinnedStackControllerProto.DEFAULT_BOUNDS;
 import static com.android.server.wm.PinnedStackControllerProto.MOVEMENT_BOUNDS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.app.RemoteAction;
 import android.content.pm.ParceledListSlice;
@@ -142,7 +142,7 @@
 
         @Override
         public int getDisplayRotation() {
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 return mDisplayInfo.rotation;
             }
         }
@@ -288,7 +288,7 @@
      * will apply the default bounds to the provided snap fraction.
      */
     Rect getDefaultBounds(float snapFraction) {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             final Rect insetBounds = new Rect();
             getInsetBounds(insetBounds);
 
@@ -314,8 +314,8 @@
      * onTaskStackBoundsChanged() to be called.  But we still should update our known display info
      * with the new state so that we can update SystemUI.
      */
-    synchronized void onDisplayInfoChanged() {
-        mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
+    synchronized void onDisplayInfoChanged(DisplayInfo displayInfo) {
+        mDisplayInfo.copyFrom(displayInfo);
         notifyMovementBoundsChanged(false /* fromImeAdjustment */, false /* fromShelfAdjustment */);
     }
 
@@ -324,7 +324,7 @@
      * new orientation of the device if necessary.
      */
     boolean onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
             if (mDisplayInfo.equals(displayInfo)) {
                 // We are already in the right orientation, ignore
@@ -481,7 +481,7 @@
      */
     private void notifyMovementBoundsChanged(boolean fromImeAdjustment,
             boolean fromShelfAdjustment) {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             if (mPinnedStackListener == null) {
                 return;
             }
@@ -513,7 +513,7 @@
      * @return the bounds on the screen that the PIP can be visible in.
      */
     private void getInsetBounds(Rect outRect) {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             mService.mPolicy.getStableInsetsLw(mDisplayInfo.rotation, mDisplayInfo.logicalWidth,
                     mDisplayInfo.logicalHeight, mDisplayInfo.displayCutout, mTmpInsets);
             outRect.set(mTmpInsets.left + mScreenEdgeInsets.x, mTmpInsets.top + mScreenEdgeInsets.y,
@@ -527,7 +527,7 @@
      *         controller.
      */
     private Rect getMovementBounds(Rect stackBounds) {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             return getMovementBounds(stackBounds, true /* adjustForIme */,
                     true /* adjustForShelf */);
         }
@@ -538,7 +538,7 @@
      *         controller.
      */
     private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme, boolean adjustForShelf) {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             final Rect movementBounds = new Rect();
             getInsetBounds(movementBounds);
 
@@ -554,7 +554,7 @@
      * Applies the minimized offsets to the given stack bounds.
      */
     private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
             mService.getStableInsetsLocked(mDisplayContent.getDisplayId(), mStableInsets);
             mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize,
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 02fbfba..bbdcc62 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -47,7 +47,7 @@
      *         default bounds.
      */
     public Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (!mService.mSupportsPictureInPicture || mContainer == null) {
                 return null;
             }
@@ -78,7 +78,7 @@
      */
     public void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds,
             int animationDuration, boolean fromFullscreen) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 throw new IllegalArgumentException("Pinned stack container not found :(");
             }
@@ -117,11 +117,12 @@
             final Rect finalToBounds = toBounds;
             final @SchedulePipModeChangedState int finalSchedulePipModeChangedState =
                 schedulePipModeChangedState;
-            mService.mBoundsAnimationController.getHandler().post(() -> {
+            final DisplayContent displayContent = mContainer.getDisplayContent();
+            displayContent.mBoundsAnimationController.getHandler().post(() -> {
                 if (mContainer == null) {
                     return;
                 }
-                mService.mBoundsAnimationController.animateBounds(mContainer, fromBounds,
+                displayContent.mBoundsAnimationController.animateBounds(mContainer, fromBounds,
                         finalToBounds, animationDuration, finalSchedulePipModeChangedState,
                         fromFullscreen, toFullscreen);
             });
@@ -132,7 +133,7 @@
      * Sets the current picture-in-picture aspect ratio.
      */
     public void setPictureInPictureAspectRatio(float aspectRatio) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (!mService.mSupportsPictureInPicture || mContainer == null) {
                 return;
             }
@@ -159,7 +160,7 @@
      * Sets the current picture-in-picture actions.
      */
     public void setPictureInPictureActions(List<RemoteAction> actions) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (!mService.mSupportsPictureInPicture || mContainer == null) {
                 return;
             }
@@ -173,7 +174,7 @@
      * from fullscreen to non-fullscreen bounds.
      */
     public boolean deferScheduleMultiWindowModeChanged() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             return mContainer.deferScheduleMultiWindowModeChanged();
         }
     }
@@ -182,7 +183,7 @@
      * @return whether the bounds are currently animating to fullscreen.
      */
     public boolean isAnimatingBoundsToFullscreen() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             return mContainer.isAnimatingBoundsToFullscreen();
         }
     }
@@ -191,7 +192,7 @@
      * @return whether the stack can be resized from the bounds animation.
      */
     public boolean pinnedStackResizeDisallowed() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             return mContainer.pinnedStackResizeDisallowed();
         }
     }
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
new file mode 100644
index 0000000..c995d3f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -0,0 +1,1621 @@
+/*
+ * Copyright (C) 2014 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.server.wm;
+
+import static android.app.ActivityManager.FLAG_AND_UNLOCKED;
+import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
+import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.os.Process.SYSTEM_UID;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.wm.TaskRecord.TaskActivitiesReport;
+
+import com.google.android.collect.Sets;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Class for managing the recent tasks list. The list is ordered by most recent (index 0) to the
+ * least recent.
+ *
+ * The trimming logic can be boiled down to the following.  For recent task list with a number of
+ * tasks, the visible tasks are an interleaving subset of tasks that would normally be presented to
+ * the user. Non-visible tasks are not considered for trimming. Of the visible tasks, only a
+ * sub-range are presented to the user, based on the device type, last task active time, or other
+ * task state. Tasks that are not in the visible range and are not returnable from the SystemUI
+ * (considering the back stack) are considered trimmable. If the device does not support recent
+ * tasks, then trimming is completely disabled.
+ *
+ * eg.
+ * L = [TTTTTTTTTTTTTTTTTTTTTTTTTT] // list of tasks
+ *     [VVV  VV   VVVV  V V V     ] // Visible tasks
+ *     [RRR  RR   XXXX  X X X     ] // Visible range tasks, eg. if the device only shows 5 tasks,
+ *                                  // 'X' tasks are trimmed.
+ */
+class RecentTasks {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_ATM;
+    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
+    private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+
+    private static final int DEFAULT_INITIAL_CAPACITY = 5;
+
+    // Whether or not to move all affiliated tasks to the front when one of the tasks is launched
+    private static final boolean MOVE_AFFILIATED_TASKS_TO_FRONT = false;
+
+    // Comparator to sort by taskId
+    private static final Comparator<TaskRecord> TASK_ID_COMPARATOR =
+            (lhs, rhs) -> rhs.taskId - lhs.taskId;
+
+    // Placeholder variables to keep track of activities/apps that are no longer avialble while
+    // iterating through the recents list
+    private static final ActivityInfo NO_ACTIVITY_INFO_TOKEN = new ActivityInfo();
+    private static final ApplicationInfo NO_APPLICATION_INFO_TOKEN = new ApplicationInfo();
+
+    /**
+     * Callbacks made when manipulating the list.
+     */
+    interface Callbacks {
+        /**
+         * Called when a task is added to the recent tasks list.
+         */
+        void onRecentTaskAdded(TaskRecord task);
+
+        /**
+         * Called when a task is removed from the recent tasks list.
+         */
+        void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess);
+    }
+
+    /**
+     * Save recent tasks information across reboots.
+     */
+    private final TaskPersister mTaskPersister;
+    private final ActivityTaskManagerService mService;
+    private final ActivityStackSupervisor mSupervisor;
+
+    /**
+     * Keeps track of the static recents package/component which is granted additional permissions
+     * to call recents-related APIs.
+     */
+    private int mRecentsUid = -1;
+    private ComponentName mRecentsComponent = null;
+
+    /**
+     * Mapping of user id -> whether recent tasks have been loaded for that user.
+     */
+    private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(
+            DEFAULT_INITIAL_CAPACITY);
+
+    /**
+     * Stores for each user task ids that are taken by tasks residing in persistent storage. These
+     * tasks may or may not currently be in memory.
+     */
+    private final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>(
+            DEFAULT_INITIAL_CAPACITY);
+
+    // List of all active recent tasks
+    private final ArrayList<TaskRecord> mTasks = new ArrayList<>();
+    private final ArrayList<Callbacks> mCallbacks = new ArrayList<>();
+
+    // These values are generally loaded from resources, but can be set dynamically in the tests
+    private boolean mHasVisibleRecentTasks;
+    private int mGlobalMaxNumTasks;
+    private int mMinNumVisibleTasks;
+    private int mMaxNumVisibleTasks;
+    private long mActiveTasksSessionDurationMs;
+
+    // Mainly to avoid object recreation on multiple calls.
+    private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<>();
+    private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>();
+    private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>();
+    private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray();
+    private final TaskActivitiesReport mTmpReport = new TaskActivitiesReport();
+
+    @VisibleForTesting
+    RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) {
+        mService = service;
+        mSupervisor = mService.mStackSupervisor;
+        mTaskPersister = taskPersister;
+        mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
+        mHasVisibleRecentTasks = true;
+    }
+
+    RecentTasks(ActivityTaskManagerService service, ActivityStackSupervisor stackSupervisor) {
+        final File systemDir = Environment.getDataSystemDirectory();
+        final Resources res = service.mContext.getResources();
+        mService = service;
+        mSupervisor = mService.mStackSupervisor;
+        mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this);
+        mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
+        mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
+        loadParametersFromResources(res);
+    }
+
+    @VisibleForTesting
+    void setParameters(int minNumVisibleTasks, int maxNumVisibleTasks,
+            long activeSessionDurationMs) {
+        mMinNumVisibleTasks = minNumVisibleTasks;
+        mMaxNumVisibleTasks = maxNumVisibleTasks;
+        mActiveTasksSessionDurationMs = activeSessionDurationMs;
+    }
+
+    @VisibleForTesting
+    void setGlobalMaxNumTasks(int globalMaxNumTasks) {
+        mGlobalMaxNumTasks = globalMaxNumTasks;
+    }
+
+    /**
+     * Loads the parameters from the system resources.
+     */
+    @VisibleForTesting
+    void loadParametersFromResources(Resources res) {
+        if (ActivityManager.isLowRamDeviceStatic()) {
+            mMinNumVisibleTasks = res.getInteger(
+                    com.android.internal.R.integer.config_minNumVisibleRecentTasks_lowRam);
+            mMaxNumVisibleTasks = res.getInteger(
+                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks_lowRam);
+        } else if (SystemProperties.getBoolean("ro.recents.grid", false)) {
+            mMinNumVisibleTasks = res.getInteger(
+                    com.android.internal.R.integer.config_minNumVisibleRecentTasks_grid);
+            mMaxNumVisibleTasks = res.getInteger(
+                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks_grid);
+        } else {
+            mMinNumVisibleTasks = res.getInteger(
+                    com.android.internal.R.integer.config_minNumVisibleRecentTasks);
+            mMaxNumVisibleTasks = res.getInteger(
+                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks);
+        }
+        final int sessionDurationHrs = res.getInteger(
+                com.android.internal.R.integer.config_activeTaskDurationHours);
+        mActiveTasksSessionDurationMs = (sessionDurationHrs > 0)
+                ? TimeUnit.HOURS.toMillis(sessionDurationHrs)
+                : -1;
+    }
+
+    /**
+     * Loads the static recents component.  This is called after the system is ready, but before
+     * any dependent services (like SystemUI) is started.
+     */
+    void loadRecentsComponent(Resources res) {
+        final String rawRecentsComponent = res.getString(
+                com.android.internal.R.string.config_recentsComponentName);
+        if (TextUtils.isEmpty(rawRecentsComponent)) {
+            return;
+        }
+
+        final ComponentName cn = ComponentName.unflattenFromString(rawRecentsComponent);
+        if (cn != null) {
+            try {
+                final ApplicationInfo appInfo = AppGlobals.getPackageManager()
+                        .getApplicationInfo(cn.getPackageName(), 0, mService.mContext.getUserId());
+                if (appInfo != null) {
+                    mRecentsUid = appInfo.uid;
+                    mRecentsComponent = cn;
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Could not load application info for recents component: " + cn);
+            }
+        }
+    }
+
+    /**
+     * @return whether the current caller has the same uid as the recents component.
+     */
+    boolean isCallerRecents(int callingUid) {
+        return UserHandle.isSameApp(callingUid, mRecentsUid);
+    }
+
+    /**
+     * @return whether the given component is the recents component and shares the same uid as the
+     *         recents component.
+     */
+    boolean isRecentsComponent(ComponentName cn, int uid) {
+        return cn.equals(mRecentsComponent) && UserHandle.isSameApp(uid, mRecentsUid);
+    }
+
+    /**
+     * @return whether the home app is also the active handler of recent tasks.
+     */
+    boolean isRecentsComponentHomeActivity(int userId) {
+        final ComponentName defaultHomeActivity = mService.getPackageManagerInternalLocked()
+                .getDefaultHomeActivity(userId);
+        return defaultHomeActivity != null && mRecentsComponent != null &&
+                defaultHomeActivity.getPackageName().equals(mRecentsComponent.getPackageName());
+    }
+
+    /**
+     * @return the recents component.
+     */
+    ComponentName getRecentsComponent() {
+        return mRecentsComponent;
+    }
+
+    /**
+     * @return the uid for the recents component.
+     */
+    int getRecentsComponentUid() {
+        return mRecentsUid;
+    }
+
+    void registerCallback(Callbacks callback) {
+        mCallbacks.add(callback);
+    }
+
+    void unregisterCallback(Callbacks callback) {
+        mCallbacks.remove(callback);
+    }
+
+    private void notifyTaskAdded(TaskRecord task) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            mCallbacks.get(i).onRecentTaskAdded(task);
+        }
+    }
+
+    private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed, killProcess);
+        }
+    }
+
+    /**
+     * Loads the persistent recentTasks for {@code userId} into this list from persistent storage.
+     * Does nothing if they are already loaded.
+     *
+     * @param userId the user Id
+     */
+    void loadUserRecentsLocked(int userId) {
+        if (mUsersWithRecentsLoaded.get(userId)) {
+            // User already loaded, return early
+            return;
+        }
+
+        // Load the task ids if not loaded.
+        loadPersistedTaskIdsForUserLocked(userId);
+
+        // Check if any tasks are added before recents is loaded
+        final SparseBooleanArray preaddedTasks = new SparseBooleanArray();
+        for (final TaskRecord task : mTasks) {
+            if (task.userId == userId && shouldPersistTaskLocked(task)) {
+                preaddedTasks.put(task.taskId, true);
+            }
+        }
+
+        Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
+        mTasks.addAll(mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks));
+        cleanupLocked(userId);
+        mUsersWithRecentsLoaded.put(userId, true);
+
+        // If we have tasks added before loading recents, we need to update persistent task IDs.
+        if (preaddedTasks.size() > 0) {
+            syncPersistentTaskIdsLocked();
+        }
+    }
+
+    private void loadPersistedTaskIdsForUserLocked(int userId) {
+        // An empty instead of a null set here means that no persistent taskIds were present
+        // on file when we loaded them.
+        if (mPersistedTaskIds.get(userId) == null) {
+            mPersistedTaskIds.put(userId, mTaskPersister.loadPersistedTaskIdsForUser(userId));
+            Slog.i(TAG, "Loaded persisted task ids for user " + userId);
+        }
+    }
+
+    /**
+     * @return whether the {@param taskId} is currently in use for the given user.
+     */
+    boolean containsTaskId(int taskId, int userId) {
+        loadPersistedTaskIdsForUserLocked(userId);
+        return mPersistedTaskIds.get(userId).get(taskId);
+    }
+
+    /**
+     * @return all the task ids for the user with the given {@param userId}.
+     */
+    SparseBooleanArray getTaskIdsForUser(int userId) {
+        loadPersistedTaskIdsForUserLocked(userId);
+        return mPersistedTaskIds.get(userId);
+    }
+
+    /**
+     * Kicks off the task persister to write any pending tasks to disk.
+     */
+    void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
+        final ActivityStack stack = task != null ? task.getStack() : null;
+        if (stack != null && stack.isHomeOrRecentsStack()) {
+            // Never persist the home or recents stack.
+            return;
+        }
+        syncPersistentTaskIdsLocked();
+        mTaskPersister.wakeup(task, flush);
+    }
+
+    private void syncPersistentTaskIdsLocked() {
+        for (int i = mPersistedTaskIds.size() - 1; i >= 0; i--) {
+            int userId = mPersistedTaskIds.keyAt(i);
+            if (mUsersWithRecentsLoaded.get(userId)) {
+                // Recents are loaded only after task ids are loaded. Therefore, the set of taskids
+                // referenced here should not be null.
+                mPersistedTaskIds.valueAt(i).clear();
+            }
+        }
+        for (int i = mTasks.size() - 1; i >= 0; i--) {
+            final TaskRecord task = mTasks.get(i);
+            if (shouldPersistTaskLocked(task)) {
+                // Set of persisted taskIds for task.userId should not be null here
+                // TODO Investigate why it can happen. For now initialize with an empty set
+                if (mPersistedTaskIds.get(task.userId) == null) {
+                    Slog.wtf(TAG, "No task ids found for userId " + task.userId + ". task=" + task
+                            + " mPersistedTaskIds=" + mPersistedTaskIds);
+                    mPersistedTaskIds.put(task.userId, new SparseBooleanArray());
+                }
+                mPersistedTaskIds.get(task.userId).put(task.taskId, true);
+            }
+        }
+    }
+
+    private static boolean shouldPersistTaskLocked(TaskRecord task) {
+        final ActivityStack stack = task.getStack();
+        return task.isPersistable && (stack == null || !stack.isHomeOrRecentsStack());
+    }
+
+    void onSystemReadyLocked() {
+        loadRecentsComponent(mService.mContext.getResources());
+        mTasks.clear();
+        mTaskPersister.onSystemReady();
+    }
+
+    Bitmap getTaskDescriptionIcon(String path) {
+        return mTaskPersister.getTaskDescriptionIcon(path);
+    }
+
+    void saveImage(Bitmap image, String path) {
+        mTaskPersister.saveImage(image, path);
+    }
+
+    void flush() {
+        synchronized (mService.mGlobalLock) {
+            syncPersistentTaskIdsLocked();
+        }
+        mTaskPersister.flush();
+    }
+
+    /**
+     * Returns all userIds for which recents from persistent storage are loaded into this list.
+     *
+     * @return an array of userIds.
+     */
+    int[] usersWithRecentsLoadedLocked() {
+        int[] usersWithRecentsLoaded = new int[mUsersWithRecentsLoaded.size()];
+        int len = 0;
+        for (int i = 0; i < usersWithRecentsLoaded.length; i++) {
+            int userId = mUsersWithRecentsLoaded.keyAt(i);
+            if (mUsersWithRecentsLoaded.valueAt(i)) {
+                usersWithRecentsLoaded[len++] = userId;
+            }
+        }
+        if (len < usersWithRecentsLoaded.length) {
+            // should never happen.
+            return Arrays.copyOf(usersWithRecentsLoaded, len);
+        }
+        return usersWithRecentsLoaded;
+    }
+
+    /**
+     * Removes recent tasks and any other state kept in memory for the passed in user. Does not
+     * touch the information present on persistent storage.
+     *
+     * @param userId the id of the user
+     */
+    void unloadUserDataFromMemoryLocked(int userId) {
+        if (mUsersWithRecentsLoaded.get(userId)) {
+            Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
+            mUsersWithRecentsLoaded.delete(userId);
+            removeTasksForUserLocked(userId);
+        }
+        mPersistedTaskIds.delete(userId);
+        mTaskPersister.unloadUserDataFromMemory(userId);
+    }
+
+    /** Remove recent tasks for a user. */
+    private void removeTasksForUserLocked(int userId) {
+        if(userId <= 0) {
+            Slog.i(TAG, "Can't remove recent task on user " + userId);
+            return;
+        }
+
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            TaskRecord tr = mTasks.get(i);
+            if (tr.userId == userId) {
+                if(DEBUG_TASKS) Slog.i(TAG_TASKS,
+                        "remove RecentTask " + tr + " when finishing user" + userId);
+                remove(tr);
+            }
+        }
+    }
+
+    void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) {
+        final Set<String> packageNames = Sets.newHashSet(packages);
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final TaskRecord tr = mTasks.get(i);
+            if (tr.realActivity != null
+                    && packageNames.contains(tr.realActivity.getPackageName())
+                    && tr.userId == userId
+                    && tr.realActivitySuspended != suspended) {
+               tr.realActivitySuspended = suspended;
+               if (suspended) {
+                   mSupervisor.removeTaskByIdLocked(tr.taskId, false,
+                           REMOVE_FROM_RECENTS, "suspended-package");
+               }
+               notifyTaskPersisterLocked(tr, false);
+            }
+        }
+    }
+
+    void onLockTaskModeStateChanged(int lockTaskModeState, int userId) {
+        if (lockTaskModeState != ActivityManager.LOCK_TASK_MODE_LOCKED) {
+            return;
+        }
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final TaskRecord tr = mTasks.get(i);
+            if (tr.userId == userId && !mService.getLockTaskController().isTaskWhitelisted(tr)) {
+                remove(tr);
+            }
+        }
+    }
+
+    void removeTasksByPackageName(String packageName, int userId) {
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final TaskRecord tr = mTasks.get(i);
+            final String taskPackageName =
+                    tr.getBaseIntent().getComponent().getPackageName();
+            if (tr.userId != userId) continue;
+            if (!taskPackageName.equals(packageName)) continue;
+
+            mSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS,
+                    "remove-package-task");
+        }
+    }
+
+    void removeAllVisibleTasks() {
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final TaskRecord tr = mTasks.get(i);
+            if (isVisibleRecentTask(tr)) {
+                mTasks.remove(i);
+                notifyTaskRemoved(tr, true /* wasTrimmed */, true /* killProcess */);
+            }
+        }
+    }
+
+    void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses,
+            int userId) {
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final TaskRecord tr = mTasks.get(i);
+            if (userId != UserHandle.USER_ALL && tr.userId != userId) {
+                continue;
+            }
+
+            ComponentName cn = tr.intent != null ? tr.intent.getComponent() : null;
+            final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
+                    && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
+            if (sameComponent) {
+                mSupervisor.removeTaskByIdLocked(tr.taskId, false,
+                        REMOVE_FROM_RECENTS, "disabled-package");
+            }
+        }
+    }
+
+    /**
+     * Update the recent tasks lists: make sure tasks should still be here (their
+     * applications / activities still exist), update their availability, fix-up ordering
+     * of affiliations.
+     */
+    void cleanupLocked(int userId) {
+        int recentsCount = mTasks.size();
+        if (recentsCount == 0) {
+            // Happens when called from the packagemanager broadcast before boot,
+            // or just any empty list.
+            return;
+        }
+
+        // Clear the temp lists
+        mTmpAvailActCache.clear();
+        mTmpAvailAppCache.clear();
+
+        final IPackageManager pm = AppGlobals.getPackageManager();
+        for (int i = recentsCount - 1; i >= 0; i--) {
+            final TaskRecord task = mTasks.get(i);
+            if (userId != UserHandle.USER_ALL && task.userId != userId) {
+                // Only look at tasks for the user ID of interest.
+                continue;
+            }
+            if (task.autoRemoveRecents && task.getTopActivity() == null) {
+                // This situation is broken, and we should just get rid of it now.
+                remove(task);
+                Slog.w(TAG, "Removing auto-remove without activity: " + task);
+                continue;
+            }
+            // Check whether this activity is currently available.
+            if (task.realActivity != null) {
+                ActivityInfo ai = mTmpAvailActCache.get(task.realActivity);
+                if (ai == null) {
+                    try {
+                        // At this first cut, we're only interested in
+                        // activities that are fully runnable based on
+                        // current system state.
+                        ai = pm.getActivityInfo(task.realActivity,
+                                PackageManager.MATCH_DEBUG_TRIAGED_MISSING
+                                        | ActivityManagerService.STOCK_PM_FLAGS, userId);
+                    } catch (RemoteException e) {
+                        // Will never happen.
+                        continue;
+                    }
+                    if (ai == null) {
+                        ai = NO_ACTIVITY_INFO_TOKEN;
+                    }
+                    mTmpAvailActCache.put(task.realActivity, ai);
+                }
+                if (ai == NO_ACTIVITY_INFO_TOKEN) {
+                    // This could be either because the activity no longer exists, or the
+                    // app is temporarily gone. For the former we want to remove the recents
+                    // entry; for the latter we want to mark it as unavailable.
+                    ApplicationInfo app = mTmpAvailAppCache
+                            .get(task.realActivity.getPackageName());
+                    if (app == null) {
+                        try {
+                            app = pm.getApplicationInfo(task.realActivity.getPackageName(),
+                                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+                        } catch (RemoteException e) {
+                            // Will never happen.
+                            continue;
+                        }
+                        if (app == null) {
+                            app = NO_APPLICATION_INFO_TOKEN;
+                        }
+                        mTmpAvailAppCache.put(task.realActivity.getPackageName(), app);
+                    }
+                    if (app == NO_APPLICATION_INFO_TOKEN
+                            || (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+                        // Doesn't exist any more! Good-bye.
+                        remove(task);
+                        Slog.w(TAG, "Removing no longer valid recent: " + task);
+                        continue;
+                    } else {
+                        // Otherwise just not available for now.
+                        if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
+                                "Making recent unavailable: " + task);
+                        task.isAvailable = false;
+                    }
+                } else {
+                    if (!ai.enabled || !ai.applicationInfo.enabled
+                            || (ai.applicationInfo.flags
+                                    & ApplicationInfo.FLAG_INSTALLED) == 0) {
+                        if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
+                                "Making recent unavailable: " + task
+                                        + " (enabled=" + ai.enabled + "/"
+                                        + ai.applicationInfo.enabled
+                                        + " flags="
+                                        + Integer.toHexString(ai.applicationInfo.flags)
+                                        + ")");
+                        task.isAvailable = false;
+                    } else {
+                        if (DEBUG_RECENTS && !task.isAvailable) Slog.d(TAG_RECENTS,
+                                "Making recent available: " + task);
+                        task.isAvailable = true;
+                    }
+                }
+            }
+        }
+
+        // Verify the affiliate chain for each task.
+        int i = 0;
+        recentsCount = mTasks.size();
+        while (i < recentsCount) {
+            i = processNextAffiliateChainLocked(i);
+        }
+        // recent tasks are now in sorted, affiliated order.
+    }
+
+    /**
+     * @return whether the given {@param task} can be added to the list without causing another
+     * task to be trimmed as a result of that add.
+     */
+    private boolean canAddTaskWithoutTrim(TaskRecord task) {
+        return findRemoveIndexForAddTask(task) == -1;
+    }
+
+    /**
+     * Returns the list of {@link ActivityManager.AppTask}s.
+     */
+    ArrayList<IBinder> getAppTasksList(int callingUid, String callingPackage) {
+        final ArrayList<IBinder> list = new ArrayList<>();
+        final int size = mTasks.size();
+        for (int i = 0; i < size; i++) {
+            final TaskRecord tr = mTasks.get(i);
+            // Skip tasks that do not match the caller.  We don't need to verify
+            // callingPackage, because we are also limiting to callingUid and know
+            // that will limit to the correct security sandbox.
+            if (tr.effectiveUid != callingUid) {
+                continue;
+            }
+            Intent intent = tr.getBaseIntent();
+            if (intent == null || !callingPackage.equals(intent.getComponent().getPackageName())) {
+                continue;
+            }
+            AppTaskImpl taskImpl = new AppTaskImpl(mService, tr.taskId, callingUid);
+            list.add(taskImpl.asBinder());
+        }
+        return list;
+    }
+
+    @VisibleForTesting
+    Set<Integer> getProfileIds(int userId) {
+        Set<Integer> userIds = new ArraySet<>();
+        final List<UserInfo> profiles = mService.getUserManager().getProfiles(userId,
+                false /* enabledOnly */);
+        for (int i = profiles.size() - 1; i >= 0; --i) {
+            userIds.add(profiles.get(i).id);
+        }
+        return userIds;
+    }
+
+    @VisibleForTesting
+    UserInfo getUserInfo(int userId) {
+        return mService.getUserManager().getUserInfo(userId);
+    }
+
+    @VisibleForTesting
+    int[] getCurrentProfileIds() {
+        return mService.mAmInternal.getCurrentProfileIds();
+    }
+
+    /**
+     * @return the list of recent tasks for presentation.
+     */
+    ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
+            boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
+        return new ParceledListSlice<>(getRecentTasksImpl(maxNum, flags, getTasksAllowed,
+                getDetailedTasks, userId, callingUid));
+    }
+
+
+    /**
+     * @return the list of recent tasks for presentation.
+     */
+    ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags,
+            boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
+        final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
+
+        if (!mService.mAmInternal.isUserRunning(userId, FLAG_AND_UNLOCKED)) {
+            Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
+            return new ArrayList<>();
+        }
+        loadUserRecentsLocked(userId);
+
+        final Set<Integer> includedUsers = getProfileIds(userId);
+        includedUsers.add(Integer.valueOf(userId));
+
+        final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>();
+        final int size = mTasks.size();
+        int numVisibleTasks = 0;
+        for (int i = 0; i < size; i++) {
+            final TaskRecord tr = mTasks.get(i);
+
+            if (isVisibleRecentTask(tr)) {
+                numVisibleTasks++;
+                if (isInVisibleRange(tr, numVisibleTasks)) {
+                    // Fall through
+                } else {
+                    // Not in visible range
+                    continue;
+                }
+            } else {
+                // Not visible
+                continue;
+            }
+
+            // Skip remaining tasks once we reach the requested size
+            if (res.size() >= maxNum) {
+                continue;
+            }
+
+            // Only add calling user or related users recent tasks
+            if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
+                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
+                continue;
+            }
+
+            if (tr.realActivitySuspended) {
+                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
+                continue;
+            }
+
+            // Return the entry if desired by the caller.  We always return
+            // the first entry, because callers always expect this to be the
+            // foreground app.  We may filter others if the caller has
+            // not supplied RECENT_WITH_EXCLUDED and there is some reason
+            // we should exclude the entry.
+
+            if (i == 0
+                    || withExcluded
+                    || (tr.intent == null)
+                    || ((tr.intent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                    == 0)) {
+                if (!getTasksAllowed) {
+                    // If the caller doesn't have the GET_TASKS permission, then only
+                    // allow them to see a small subset of tasks -- their own and home.
+                    if (!tr.isActivityTypeHome() && tr.effectiveUid != callingUid) {
+                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
+                        continue;
+                    }
+                }
+                if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
+                    // Don't include auto remove tasks that are finished or finishing.
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                            "Skipping, auto-remove without activity: " + tr);
+                    continue;
+                }
+                if ((flags & RECENT_IGNORE_UNAVAILABLE) != 0 && !tr.isAvailable) {
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                            "Skipping, unavail real act: " + tr);
+                    continue;
+                }
+
+                if (!tr.mUserSetupComplete) {
+                    // Don't include task launched while user is not done setting-up.
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                            "Skipping, user setup not complete: " + tr);
+                    continue;
+                }
+
+                final ActivityManager.RecentTaskInfo rti = createRecentTaskInfo(tr);
+                if (!getDetailedTasks) {
+                    rti.baseIntent.replaceExtras((Bundle)null);
+                }
+
+                res.add(rti);
+            }
+        }
+        return res;
+    }
+
+    /**
+     * @return the list of persistable task ids.
+     */
+    void getPersistableTaskIds(ArraySet<Integer> persistentTaskIds) {
+        final int size = mTasks.size();
+        for (int i = 0; i < size; i++) {
+            final TaskRecord task = mTasks.get(i);
+            if (TaskPersister.DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task
+                    + " persistable=" + task.isPersistable);
+            final ActivityStack stack = task.getStack();
+            if ((task.isPersistable || task.inRecents)
+                    && (stack == null || !stack.isHomeOrRecentsStack())) {
+                if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
+                persistentTaskIds.add(task.taskId);
+            } else {
+                if (TaskPersister.DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task="
+                        + task);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    ArrayList<TaskRecord> getRawTasks() {
+        return mTasks;
+    }
+
+    /**
+     * @return ids of tasks that are presented in Recents UI.
+     */
+    SparseBooleanArray getRecentTaskIds() {
+        final SparseBooleanArray res = new SparseBooleanArray();
+        final int size = mTasks.size();
+        int numVisibleTasks = 0;
+        for (int i = 0; i < size; i++) {
+            final TaskRecord tr = mTasks.get(i);
+            if (isVisibleRecentTask(tr)) {
+                numVisibleTasks++;
+                if (isInVisibleRange(tr, numVisibleTasks)) {
+                    res.put(tr.taskId, true);
+                }
+            }
+        }
+        return res;
+    }
+
+    /**
+     * @return the task in the task list with the given {@param id} if one exists.
+     */
+    TaskRecord getTask(int id) {
+        final int recentsCount = mTasks.size();
+        for (int i = 0; i < recentsCount; i++) {
+            TaskRecord tr = mTasks.get(i);
+            if (tr.taskId == id) {
+                return tr;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Add a new task to the recent tasks list.
+     */
+    void add(TaskRecord task) {
+        if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task);
+
+        final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId
+                || task.mNextAffiliateTaskId != INVALID_TASK_ID
+                || task.mPrevAffiliateTaskId != INVALID_TASK_ID;
+
+        int recentsCount = mTasks.size();
+        // Quick case: never add voice sessions.
+        // TODO: VI what about if it's just an activity?
+        // Probably nothing to do here
+        if (task.voiceSession != null) {
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                    "addRecent: not adding voice interaction " + task);
+            return;
+        }
+        // Another quick case: check if the top-most recent task is the same.
+        if (!isAffiliated && recentsCount > 0 && mTasks.get(0) == task) {
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: already at top: " + task);
+            return;
+        }
+        // Another quick case: check if this is part of a set of affiliated
+        // tasks that are at the top.
+        if (isAffiliated && recentsCount > 0 && task.inRecents
+                && task.mAffiliatedTaskId == mTasks.get(0).mAffiliatedTaskId) {
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + mTasks.get(0)
+                    + " at top when adding " + task);
+            return;
+        }
+
+        boolean needAffiliationFix = false;
+
+        // Slightly less quick case: the task is already in recents, so all we need
+        // to do is move it.
+        if (task.inRecents) {
+            int taskIndex = mTasks.indexOf(task);
+            if (taskIndex >= 0) {
+                if (!isAffiliated || !MOVE_AFFILIATED_TASKS_TO_FRONT) {
+                    // Simple case: this is not an affiliated task, so we just move it to the front.
+                    mTasks.remove(taskIndex);
+                    mTasks.add(0, task);
+                    notifyTaskPersisterLocked(task, false);
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
+                            + " from " + taskIndex);
+                    return;
+                } else {
+                    // More complicated: need to keep all affiliated tasks together.
+                    if (moveAffiliatedTasksToFront(task, taskIndex)) {
+                        // All went well.
+                        return;
+                    }
+
+                    // Uh oh...  something bad in the affiliation chain, try to rebuild
+                    // everything and then go through our general path of adding a new task.
+                    needAffiliationFix = true;
+                }
+            } else {
+                Slog.wtf(TAG, "Task with inRecent not in recents: " + task);
+                needAffiliationFix = true;
+            }
+        }
+
+        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task);
+        removeForAddTask(task);
+
+        task.inRecents = true;
+        if (!isAffiliated || needAffiliationFix) {
+            // If this is a simple non-affiliated task, or we had some failure trying to
+            // handle it as part of an affilated task, then just place it at the top.
+            mTasks.add(0, task);
+            notifyTaskAdded(task);
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task);
+        } else if (isAffiliated) {
+            // If this is a new affiliated task, then move all of the affiliated tasks
+            // to the front and insert this new one.
+            TaskRecord other = task.mNextAffiliate;
+            if (other == null) {
+                other = task.mPrevAffiliate;
+            }
+            if (other != null) {
+                int otherIndex = mTasks.indexOf(other);
+                if (otherIndex >= 0) {
+                    // Insert new task at appropriate location.
+                    int taskIndex;
+                    if (other == task.mNextAffiliate) {
+                        // We found the index of our next affiliation, which is who is
+                        // before us in the list, so add after that point.
+                        taskIndex = otherIndex+1;
+                    } else {
+                        // We found the index of our previous affiliation, which is who is
+                        // after us in the list, so add at their position.
+                        taskIndex = otherIndex;
+                    }
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                            "addRecent: new affiliated task added at " + taskIndex + ": " + task);
+                    mTasks.add(taskIndex, task);
+                    notifyTaskAdded(task);
+
+                    // Now move everything to the front.
+                    if (moveAffiliatedTasksToFront(task, taskIndex)) {
+                        // All went well.
+                        return;
+                    }
+
+                    // Uh oh...  something bad in the affiliation chain, try to rebuild
+                    // everything and then go through our general path of adding a new task.
+                    needAffiliationFix = true;
+                } else {
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                            "addRecent: couldn't find other affiliation " + other);
+                    needAffiliationFix = true;
+                }
+            } else {
+                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                        "addRecent: adding affiliated task without next/prev:" + task);
+                needAffiliationFix = true;
+            }
+        }
+
+        if (needAffiliationFix) {
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations");
+            cleanupLocked(task.userId);
+        }
+
+        // Trim the set of tasks to the active set
+        trimInactiveRecentTasks();
+    }
+
+    /**
+     * Add the task to the bottom if possible.
+     */
+    boolean addToBottom(TaskRecord task) {
+        if (!canAddTaskWithoutTrim(task)) {
+            // Adding this task would cause the task to be removed (since it's appended at
+            // the bottom and would be trimmed) so just return now
+            return false;
+        }
+
+        add(task);
+        return true;
+    }
+
+    /**
+     * Remove a task from the recent tasks list.
+     */
+    void remove(TaskRecord task) {
+        mTasks.remove(task);
+        notifyTaskRemoved(task, false /* wasTrimmed */, false /* killProcess */);
+    }
+
+    /**
+     * Trims the recents task list to the global max number of recents.
+     */
+    private void trimInactiveRecentTasks() {
+        int recentsCount = mTasks.size();
+
+        // Remove from the end of the list until we reach the max number of recents
+        while (recentsCount > mGlobalMaxNumTasks) {
+            final TaskRecord tr = mTasks.remove(recentsCount - 1);
+            notifyTaskRemoved(tr, true /* wasTrimmed */, false /* killProcess */);
+            recentsCount--;
+            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming over max-recents task=" + tr
+                    + " max=" + mGlobalMaxNumTasks);
+        }
+
+        // Remove any tasks that belong to currently quiet profiles
+        final int[] profileUserIds = getCurrentProfileIds();
+        mTmpQuietProfileUserIds.clear();
+        for (int userId : profileUserIds) {
+            final UserInfo userInfo = getUserInfo(userId);
+            if (userInfo != null && userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) {
+                mTmpQuietProfileUserIds.put(userId, true);
+            }
+            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "User: " + userInfo
+                    + " quiet=" + mTmpQuietProfileUserIds.get(userId));
+        }
+
+        // Remove any inactive tasks, calculate the latest set of visible tasks
+        int numVisibleTasks = 0;
+        for (int i = 0; i < mTasks.size();) {
+            final TaskRecord task = mTasks.get(i);
+
+            if (isActiveRecentTask(task, mTmpQuietProfileUserIds)) {
+                if (!mHasVisibleRecentTasks) {
+                    // Keep all active tasks if visible recent tasks is not supported
+                    i++;
+                    continue;
+                }
+
+                if (!isVisibleRecentTask(task)) {
+                    // Keep all active-but-invisible tasks
+                    i++;
+                    continue;
+                } else {
+                    numVisibleTasks++;
+                    if (isInVisibleRange(task, numVisibleTasks) || !isTrimmable(task)) {
+                        // Keep visible tasks in range
+                        i++;
+                        continue;
+                    } else {
+                        // Fall through to trim visible tasks that are no longer in range and
+                        // trimmable
+                        if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
+                                "Trimming out-of-range visible task=" + task);
+                    }
+                }
+            } else {
+                // Fall through to trim inactive tasks
+                if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming inactive task=" + task);
+            }
+
+            // Task is no longer active, trim it from the list
+            mTasks.remove(task);
+            notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */);
+            notifyTaskPersisterLocked(task, false /* flush */);
+        }
+    }
+
+    /**
+     * @return whether the given task should be considered active.
+     */
+    private boolean isActiveRecentTask(TaskRecord task, SparseBooleanArray quietProfileUserIds) {
+        if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isActiveRecentTask: task=" + task
+                + " globalMax=" + mGlobalMaxNumTasks);
+
+        if (quietProfileUserIds.get(task.userId)) {
+            // Quiet profile user's tasks are never active
+            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\tisQuietProfileTask=true");
+            return false;
+        }
+
+        if (task.mAffiliatedTaskId != INVALID_TASK_ID && task.mAffiliatedTaskId != task.taskId) {
+            // Keep the task active if its affiliated task is also active
+            final TaskRecord affiliatedTask = getTask(task.mAffiliatedTaskId);
+            if (affiliatedTask != null) {
+                if (!isActiveRecentTask(affiliatedTask, quietProfileUserIds)) {
+                    if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
+                            "\taffiliatedWithTask=" + affiliatedTask + " is not active");
+                    return false;
+                }
+            }
+        }
+
+        // All other tasks are considered active
+        return true;
+    }
+
+    /**
+     * @return whether the given active task should be presented to the user through SystemUI.
+     */
+    private boolean isVisibleRecentTask(TaskRecord task) {
+        if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isVisibleRecentTask: task=" + task
+                + " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks
+                + " sessionDuration=" + mActiveTasksSessionDurationMs
+                + " inactiveDuration=" + task.getInactiveDuration()
+                + " activityType=" + task.getActivityType()
+                + " windowingMode=" + task.getWindowingMode()
+                + " intentFlags=" + task.getBaseIntent().getFlags());
+
+        switch (task.getActivityType()) {
+            case ACTIVITY_TYPE_HOME:
+            case ACTIVITY_TYPE_RECENTS:
+                // Ignore certain activity types completely
+                return false;
+            case ACTIVITY_TYPE_ASSISTANT:
+                // Ignore assistant that chose to be excluded from Recents, even if it's a top
+                // task.
+                if ((task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                        == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) {
+                    return false;
+                }
+        }
+
+        // Ignore certain windowing modes
+        switch (task.getWindowingMode()) {
+            case WINDOWING_MODE_PINNED:
+                return false;
+            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+                if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().topTask());
+                final ActivityStack stack = task.getStack();
+                if (stack != null && stack.topTask() == task) {
+                    // Only the non-top task of the primary split screen mode is visible
+                    return false;
+                }
+        }
+
+        // If we're in lock task mode, ignore the root task
+        if (task == mService.getLockTaskController().getRootTask()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * @return whether the given visible task is within the policy range.
+     */
+    private boolean isInVisibleRange(TaskRecord task, int numVisibleTasks) {
+        // Keep the last most task even if it is excluded from recents
+        final boolean isExcludeFromRecents =
+                (task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                        == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+        if (isExcludeFromRecents) {
+            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true");
+            return numVisibleTasks == 1;
+        }
+
+        if (mMinNumVisibleTasks >= 0 && numVisibleTasks <= mMinNumVisibleTasks) {
+            // Always keep up to the min number of recent tasks, after that fall through to the
+            // checks below
+            return true;
+        }
+
+        if (mMaxNumVisibleTasks >= 0) {
+            // Always keep up to the max number of recent tasks, but return false afterwards
+            return numVisibleTasks <= mMaxNumVisibleTasks;
+        }
+
+        if (mActiveTasksSessionDurationMs > 0) {
+            // Keep the task if the inactive time is within the session window, this check must come
+            // after the checks for the min/max visible task range
+            if (task.getInactiveDuration() <= mActiveTasksSessionDurationMs) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * @return whether the given task can be trimmed even if it is outside the visible range.
+     */
+    protected boolean isTrimmable(TaskRecord task) {
+        final ActivityStack stack = task.getStack();
+
+        // No stack for task, just trim it
+        if (stack == null) {
+            return true;
+        }
+
+        // Ignore tasks from different displays
+        // TODO (b/115289124): No Recents on non-default displays.
+        if (stack.mDisplayId != DEFAULT_DISPLAY) {
+            return false;
+        }
+
+        // Trim tasks that are in stacks that are behind the home stack
+        final ActivityDisplay display = stack.getDisplay();
+        return display.getIndexOf(stack) < display.getIndexOf(display.getHomeStack());
+    }
+
+    /**
+     * If needed, remove oldest existing entries in recents that are for the same kind
+     * of task as the given one.
+     */
+    private void removeForAddTask(TaskRecord task) {
+        final int removeIndex = findRemoveIndexForAddTask(task);
+        if (removeIndex == -1) {
+            // Nothing to trim
+            return;
+        }
+
+        // There is a similar task that will be removed for the addition of {@param task}, but it
+        // can be the same task, and if so, the task will be re-added in add(), so skip the
+        // callbacks here.
+        final TaskRecord removedTask = mTasks.remove(removeIndex);
+        if (removedTask != task) {
+            notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */);
+            if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask
+                    + " for addition of task=" + task);
+        }
+        notifyTaskPersisterLocked(removedTask, false /* flush */);
+    }
+
+    /**
+     * Find the task that would be removed if the given {@param task} is added to the recent tasks
+     * list (if any).
+     */
+    private int findRemoveIndexForAddTask(TaskRecord task) {
+        final int recentsCount = mTasks.size();
+        final Intent intent = task.intent;
+        final boolean document = intent != null && intent.isDocument();
+        int maxRecents = task.maxRecents - 1;
+        for (int i = 0; i < recentsCount; i++) {
+            final TaskRecord tr = mTasks.get(i);
+            if (task != tr) {
+                if (!hasCompatibleActivityTypeAndWindowingMode(task, tr)
+                        || task.userId != tr.userId) {
+                    continue;
+                }
+                final Intent trIntent = tr.intent;
+                final boolean sameAffinity =
+                        task.affinity != null && task.affinity.equals(tr.affinity);
+                final boolean sameIntent = intent != null && intent.filterEquals(trIntent);
+                boolean multiTasksAllowed = false;
+                final int flags = intent.getFlags();
+                if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0
+                        && (flags & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
+                    multiTasksAllowed = true;
+                }
+                final boolean trIsDocument = trIntent != null && trIntent.isDocument();
+                final boolean bothDocuments = document && trIsDocument;
+                if (!sameAffinity && !sameIntent && !bothDocuments) {
+                    continue;
+                }
+
+                if (bothDocuments) {
+                    // Do these documents belong to the same activity?
+                    final boolean sameActivity = task.realActivity != null
+                            && tr.realActivity != null
+                            && task.realActivity.equals(tr.realActivity);
+                    if (!sameActivity) {
+                        // If the document is open in another app or is not the same document, we
+                        // don't need to trim it.
+                        continue;
+                    } else if (maxRecents > 0) {
+                        --maxRecents;
+                        if (!sameIntent || multiTasksAllowed) {
+                            // We don't want to trim if we are not over the max allowed entries and
+                            // the tasks are not of the same intent filter, or multiple entries for
+                            // the task is allowed.
+                            continue;
+                        }
+                    }
+                    // Hit the maximum number of documents for this task. Fall through
+                    // and remove this document from recents.
+                } else if (document || trIsDocument) {
+                    // Only one of these is a document. Not the droid we're looking for.
+                    continue;
+                }
+            }
+            return i;
+        }
+        return -1;
+    }
+
+    // Extract the affiliates of the chain containing recent at index start.
+    private int processNextAffiliateChainLocked(int start) {
+        final TaskRecord startTask = mTasks.get(start);
+        final int affiliateId = startTask.mAffiliatedTaskId;
+
+        // Quick identification of isolated tasks. I.e. those not launched behind.
+        if (startTask.taskId == affiliateId && startTask.mPrevAffiliate == null &&
+                startTask.mNextAffiliate == null) {
+            // There is still a slim chance that there are other tasks that point to this task
+            // and that the chain is so messed up that this task no longer points to them but
+            // the gain of this optimization outweighs the risk.
+            startTask.inRecents = true;
+            return start + 1;
+        }
+
+        // Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents.
+        mTmpRecents.clear();
+        for (int i = mTasks.size() - 1; i >= start; --i) {
+            final TaskRecord task = mTasks.get(i);
+            if (task.mAffiliatedTaskId == affiliateId) {
+                mTasks.remove(i);
+                mTmpRecents.add(task);
+            }
+        }
+
+        // Sort them all by taskId. That is the order they were create in and that order will
+        // always be correct.
+        Collections.sort(mTmpRecents, TASK_ID_COMPARATOR);
+
+        // Go through and fix up the linked list.
+        // The first one is the end of the chain and has no next.
+        final TaskRecord first = mTmpRecents.get(0);
+        first.inRecents = true;
+        if (first.mNextAffiliate != null) {
+            Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate);
+            first.setNextAffiliate(null);
+            notifyTaskPersisterLocked(first, false);
+        }
+        // Everything in the middle is doubly linked from next to prev.
+        final int tmpSize = mTmpRecents.size();
+        for (int i = 0; i < tmpSize - 1; ++i) {
+            final TaskRecord next = mTmpRecents.get(i);
+            final TaskRecord prev = mTmpRecents.get(i + 1);
+            if (next.mPrevAffiliate != prev) {
+                Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate +
+                        " setting prev=" + prev);
+                next.setPrevAffiliate(prev);
+                notifyTaskPersisterLocked(next, false);
+            }
+            if (prev.mNextAffiliate != next) {
+                Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate +
+                        " setting next=" + next);
+                prev.setNextAffiliate(next);
+                notifyTaskPersisterLocked(prev, false);
+            }
+            prev.inRecents = true;
+        }
+        // The last one is the beginning of the list and has no prev.
+        final TaskRecord last = mTmpRecents.get(tmpSize - 1);
+        if (last.mPrevAffiliate != null) {
+            Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate);
+            last.setPrevAffiliate(null);
+            notifyTaskPersisterLocked(last, false);
+        }
+
+        // Insert the group back into mTmpTasks at start.
+        mTasks.addAll(start, mTmpRecents);
+        mTmpRecents.clear();
+
+        // Let the caller know where we left off.
+        return start + tmpSize;
+    }
+
+    private boolean moveAffiliatedTasksToFront(TaskRecord task, int taskIndex) {
+        int recentsCount = mTasks.size();
+        TaskRecord top = task;
+        int topIndex = taskIndex;
+        while (top.mNextAffiliate != null && topIndex > 0) {
+            top = top.mNextAffiliate;
+            topIndex--;
+        }
+        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at "
+                + topIndex + " from intial " + taskIndex);
+        // Find the end of the chain, doing a sanity check along the way.
+        boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId;
+        int endIndex = topIndex;
+        TaskRecord prev = top;
+        while (endIndex < recentsCount) {
+            TaskRecord cur = mTasks.get(endIndex);
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @"
+                    + endIndex + " " + cur);
+            if (cur == top) {
+                // Verify start of the chain.
+                if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) {
+                    Slog.wtf(TAG, "Bad chain @" + endIndex
+                            + ": first task has next affiliate: " + prev);
+                    sane = false;
+                    break;
+                }
+            } else {
+                // Verify middle of the chain's next points back to the one before.
+                if (cur.mNextAffiliate != prev
+                        || cur.mNextAffiliateTaskId != prev.taskId) {
+                    Slog.wtf(TAG, "Bad chain @" + endIndex
+                            + ": middle task " + cur + " @" + endIndex
+                            + " has bad next affiliate "
+                            + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId
+                            + ", expected " + prev);
+                    sane = false;
+                    break;
+                }
+            }
+            if (cur.mPrevAffiliateTaskId == INVALID_TASK_ID) {
+                // Chain ends here.
+                if (cur.mPrevAffiliate != null) {
+                    Slog.wtf(TAG, "Bad chain @" + endIndex
+                            + ": last task " + cur + " has previous affiliate "
+                            + cur.mPrevAffiliate);
+                    sane = false;
+                }
+                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex);
+                break;
+            } else {
+                // Verify middle of the chain's prev points to a valid item.
+                if (cur.mPrevAffiliate == null) {
+                    Slog.wtf(TAG, "Bad chain @" + endIndex
+                            + ": task " + cur + " has previous affiliate "
+                            + cur.mPrevAffiliate + " but should be id "
+                            + cur.mPrevAffiliate);
+                    sane = false;
+                    break;
+                }
+            }
+            if (cur.mAffiliatedTaskId != task.mAffiliatedTaskId) {
+                Slog.wtf(TAG, "Bad chain @" + endIndex
+                        + ": task " + cur + " has affiliated id "
+                        + cur.mAffiliatedTaskId + " but should be "
+                        + task.mAffiliatedTaskId);
+                sane = false;
+                break;
+            }
+            prev = cur;
+            endIndex++;
+            if (endIndex >= recentsCount) {
+                Slog.wtf(TAG, "Bad chain ran off index " + endIndex
+                        + ": last task " + prev);
+                sane = false;
+                break;
+            }
+        }
+        if (sane) {
+            if (endIndex < taskIndex) {
+                Slog.wtf(TAG, "Bad chain @" + endIndex
+                        + ": did not extend to task " + task + " @" + taskIndex);
+                sane = false;
+            }
+        }
+        if (sane) {
+            // All looks good, we can just move all of the affiliated tasks
+            // to the top.
+            for (int i=topIndex; i<=endIndex; i++) {
+                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task
+                        + " from " + i + " to " + (i-topIndex));
+                TaskRecord cur = mTasks.remove(i);
+                mTasks.add(i - topIndex, cur);
+            }
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks  " +  topIndex
+                    + " to " + endIndex);
+            return true;
+        }
+
+        // Whoops, couldn't do it.
+        return false;
+    }
+
+    void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) {
+        pw.println("ACTIVITY MANAGER RECENT TASKS (dumpsys activity recents)");
+        pw.println("mRecentsUid=" + mRecentsUid);
+        pw.println("mRecentsComponent=" + mRecentsComponent);
+        if (mTasks.isEmpty()) {
+            return;
+        }
+
+        // Dump raw recent task list
+        boolean printedAnything = false;
+        boolean printedHeader = false;
+        final int size = mTasks.size();
+        for (int i = 0; i < size; i++) {
+            final TaskRecord tr = mTasks.get(i);
+            if (dumpPackage != null && (tr.realActivity == null ||
+                    !dumpPackage.equals(tr.realActivity.getPackageName()))) {
+                continue;
+            }
+
+            if (!printedHeader) {
+                pw.println("  Recent tasks:");
+                printedHeader = true;
+                printedAnything = true;
+            }
+            pw.print("  * Recent #"); pw.print(i); pw.print(": ");
+            pw.println(tr);
+            if (dumpAll) {
+                tr.dump(pw, "    ");
+            }
+        }
+
+        // Dump visible recent task list
+        if (mHasVisibleRecentTasks) {
+            // Reset the header flag for the next block
+            printedHeader = false;
+            ArrayList<ActivityManager.RecentTaskInfo> tasks = getRecentTasksImpl(Integer.MAX_VALUE,
+                    0, true /* getTasksAllowed */, false /* getDetailedTasks */,
+                    mService.getCurrentUserId(), SYSTEM_UID);
+            for (int i = 0; i < tasks.size(); i++) {
+                final ActivityManager.RecentTaskInfo taskInfo = tasks.get(i);
+                if (!printedHeader) {
+                    if (printedAnything) {
+                        // Separate from the last block if it printed
+                        pw.println();
+                    }
+                    pw.println("  Visible recent tasks (most recent first):");
+                    printedHeader = true;
+                    printedAnything = true;
+                }
+
+                pw.print("  * RecentTaskInfo #"); pw.print(i); pw.print(": ");
+                taskInfo.dump(pw, "    ");
+            }
+        }
+
+        if (!printedAnything) {
+            pw.println("  (nothing)");
+        }
+    }
+
+    /**
+     * Creates a new RecentTaskInfo from a TaskRecord.
+     */
+    ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) {
+        ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
+        tr.fillTaskInfo(rti, mTmpReport);
+        // Fill in some deprecated values
+        rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
+        rti.persistentId = rti.taskId;
+        return rti;
+    }
+
+    /**
+     * @return Whether the activity types and windowing modes of the two tasks are considered
+     *         compatible. This is necessary because we currently don't persist the activity type
+     *         or the windowing mode with the task, so they can be undefined when restored.
+     */
+    private boolean hasCompatibleActivityTypeAndWindowingMode(TaskRecord t1, TaskRecord t2) {
+        final int activityType = t1.getActivityType();
+        final int windowingMode = t1.getWindowingMode();
+        final boolean isUndefinedType = activityType == ACTIVITY_TYPE_UNDEFINED;
+        final boolean isUndefinedMode = windowingMode == WINDOWING_MODE_UNDEFINED;
+        final int otherActivityType = t2.getActivityType();
+        final int otherWindowingMode = t2.getWindowingMode();
+        final boolean isOtherUndefinedType = otherActivityType == ACTIVITY_TYPE_UNDEFINED;
+        final boolean isOtherUndefinedMode = otherWindowingMode == WINDOWING_MODE_UNDEFINED;
+
+        // An activity type and windowing mode is compatible if they are the exact same type/mode,
+        // or if one of the type/modes is undefined
+        final boolean isCompatibleType = activityType == otherActivityType
+                || isUndefinedType || isOtherUndefinedType;
+        final boolean isCompatibleMode = windowingMode == otherWindowingMode
+                || isUndefinedMode || isOtherUndefinedMode;
+
+        return isCompatibleType && isCompatibleMode;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
new file mode 100644
index 0000000..067b01a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
+import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
+import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
+
+import android.app.ActivityOptions;
+import android.app.AppOpsManager;
+import android.app.IAssistDataReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.Slog;
+import android.view.IRecentsAnimationRunner;
+
+import com.android.server.am.AssistDataRequester;
+import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
+
+/**
+ * Manages the recents animation, including the reordering of the stacks for the transition and
+ * cleanup. See {@link com.android.server.wm.RecentsAnimationController}.
+ */
+class RecentsAnimation implements RecentsAnimationCallbacks,
+        ActivityDisplay.OnStackOrderChangedListener {
+    private static final String TAG = RecentsAnimation.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    private final ActivityTaskManagerService mService;
+    private final ActivityStackSupervisor mStackSupervisor;
+    private final ActivityStartController mActivityStartController;
+    private final WindowManagerService mWindowManager;
+    private final ActivityDisplay mDefaultDisplay;
+    private final int mCallingPid;
+
+    private int mTargetActivityType;
+    private AssistDataRequester mAssistDataRequester;
+
+    // The stack to restore the target stack behind when the animation is finished
+    private ActivityStack mRestoreTargetBehindStack;
+
+    RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor,
+            ActivityStartController activityStartController, WindowManagerService wm,
+            int callingPid) {
+        mService = atm;
+        mStackSupervisor = stackSupervisor;
+        mDefaultDisplay = stackSupervisor.getDefaultDisplay();
+        mActivityStartController = activityStartController;
+        mWindowManager = wm;
+        mCallingPid = callingPid;
+    }
+
+    void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
+            ComponentName recentsComponent, int recentsUid,
+            IAssistDataReceiver assistDataReceiver) {
+        if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + intent
+                + " assistDataReceiver=" + assistDataReceiver);
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
+
+        // TODO(multi-display) currently only support recents animation in default display.
+        final DisplayWindowController dwc =
+                mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+        if (!mWindowManager.canStartRecentsAnimation()) {
+            notifyAnimationCancelBeforeStart(recentsAnimationRunner);
+            if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition="
+                        + dwc.getPendingAppTransition());
+            return;
+        }
+
+        // If the activity is associated with the recents stack, then try and get that first
+        mTargetActivityType = intent.getComponent() != null
+                && recentsComponent.equals(intent.getComponent())
+                        ? ACTIVITY_TYPE_RECENTS
+                        : ACTIVITY_TYPE_HOME;
+        final ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
+                mTargetActivityType);
+        ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent());
+        final boolean hasExistingActivity = targetActivity != null;
+        if (hasExistingActivity) {
+            final ActivityDisplay display = targetActivity.getDisplay();
+            mRestoreTargetBehindStack = display.getStackAbove(targetStack);
+            if (mRestoreTargetBehindStack == null) {
+                notifyAnimationCancelBeforeStart(recentsAnimationRunner);
+                if (DEBUG) Slog.d(TAG, "No stack above target stack=" + targetStack);
+                return;
+            }
+        }
+
+        // Send launch hint if we are actually launching the target. If it's already visible
+        // (shouldn't happen in general) we don't need to send it.
+        if (targetActivity == null || !targetActivity.visible) {
+            mStackSupervisor.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */,
+                    targetActivity);
+        }
+
+        mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
+
+        mService.mH.post(() -> mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, true));
+
+        mWindowManager.deferSurfaceLayout();
+        try {
+            // Kick off the assist data request in the background before showing the target activity
+            if (assistDataReceiver != null) {
+                final AppOpsManager appOpsManager = (AppOpsManager)
+                        mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
+                final AssistDataReceiverProxy proxy = new AssistDataReceiverProxy(
+                        assistDataReceiver, recentsComponent.getPackageName());
+                mAssistDataRequester = new AssistDataRequester(mService.mContext,
+                        mWindowManager, appOpsManager, proxy, this, OP_ASSIST_STRUCTURE, OP_NONE);
+                mAssistDataRequester.requestAssistData(mStackSupervisor.getTopVisibleActivities(),
+                        true /* fetchData */, false /* fetchScreenshots */,
+                        true /* allowFetchData */, false /* allowFetchScreenshots */,
+                        recentsUid, recentsComponent.getPackageName());
+            }
+
+            if (hasExistingActivity) {
+                // Move the recents activity into place for the animation if it is not top most
+                mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
+                if (DEBUG) Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
+                            + mDefaultDisplay.getStackAbove(targetStack));
+
+                // If there are multiple tasks in the target stack (ie. the home stack, with 3p
+                // and default launchers coexisting), then move the task to the top as a part of
+                // moving the stack to the front
+                if (targetStack.topTask() != targetActivity.getTask()) {
+                    targetStack.addTask(targetActivity.getTask(), true /* toTop */,
+                            "startRecentsActivity");
+                }
+            } else {
+                // No recents activity
+                ActivityOptions options = ActivityOptions.makeBasic();
+                options.setLaunchActivityType(mTargetActivityType);
+                options.setAvoidMoveToFront();
+                intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION);
+
+                mActivityStartController
+                        .obtainStarter(intent, "startRecentsActivity_noTargetActivity")
+                        .setCallingUid(recentsUid)
+                        .setCallingPackage(recentsComponent.getPackageName())
+                        .setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
+                        .setMayWait(mService.getCurrentUserId())
+                        .execute();
+                mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+                mWindowManager.executeAppTransition();
+
+                targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
+                        mTargetActivityType).getTopActivity();
+
+                // TODO: Maybe wait for app to draw in this particular case?
+
+                if (DEBUG) Slog.d(TAG, "Started intent=" + intent);
+            }
+
+            // Mark the target activity as launch-behind to bump its visibility for the
+            // duration of the gesture that is driven by the recents component
+            targetActivity.mLaunchTaskBehind = true;
+
+            // Fetch all the surface controls and pass them to the client to get the animation
+            // started. Cancel any existing recents animation running synchronously (do not hold the
+            // WM lock)
+            mWindowManager.cancelRecentsAnimationSynchronously(REORDER_MOVE_TO_ORIGINAL_POSITION,
+                    "startRecentsActivity");
+            mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
+                    this, mDefaultDisplay.mDisplayId,
+                    mStackSupervisor.mRecentTasks.getRecentTaskIds());
+
+            // If we updated the launch-behind state, update the visibility of the activities after
+            // we fetch the visible tasks to be controlled by the animation
+            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+
+            mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
+                    targetActivity);
+
+            // Register for stack order changes
+            mDefaultDisplay.registerStackOrderChangedListener(this);
+        } catch (Exception e) {
+            Slog.e(TAG, "Failed to start recents activity", e);
+            throw e;
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
+        synchronized (mService.mGlobalLock) {
+            if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller="
+                    + mWindowManager.getRecentsAnimationController()
+                    + " reorderMode=" + reorderMode);
+
+            // Cancel the associated assistant data request
+            if (mAssistDataRequester != null) {
+                mAssistDataRequester.cancel();
+                mAssistDataRequester = null;
+            }
+
+            // Unregister for stack order changes
+            mDefaultDisplay.unregisterStackOrderChangedListener(this);
+
+            if (mWindowManager.getRecentsAnimationController() == null) return;
+
+            // Just to be sure end the launch hint in case the target activity was never launched.
+            // However, if we're keeping the activity and making it visible, we can leave it on.
+            if (reorderMode != REORDER_KEEP_IN_PLACE) {
+                mStackSupervisor.sendPowerHintForLaunchEndIfNeeded();
+            }
+
+            mService.mH.post(
+                    () -> mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, false));
+
+            mWindowManager.inSurfaceTransaction(() -> {
+                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+                        "RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
+                mWindowManager.deferSurfaceLayout();
+                try {
+                    mWindowManager.cleanupRecentsAnimation(reorderMode);
+
+                    final ActivityStack targetStack = mDefaultDisplay.getStack(
+                            WINDOWING_MODE_UNDEFINED, mTargetActivityType);
+                    final ActivityRecord targetActivity = targetStack != null
+                            ? targetStack.getTopActivity()
+                            : null;
+                    if (DEBUG) Slog.d(TAG, "onAnimationFinished(): targetStack=" + targetStack
+                            + " targetActivity=" + targetActivity
+                            + " mRestoreTargetBehindStack=" + mRestoreTargetBehindStack);
+                    if (targetActivity == null) {
+                        return;
+                    }
+
+                    // Restore the launched-behind state
+                    targetActivity.mLaunchTaskBehind = false;
+
+                    if (reorderMode == REORDER_MOVE_TO_TOP) {
+                        // Bring the target stack to the front
+                        mStackSupervisor.mNoAnimActivities.add(targetActivity);
+                        targetStack.moveToFront("RecentsAnimation.onAnimationFinished()");
+                        if (DEBUG) {
+                            final ActivityStack topStack = getTopNonAlwaysOnTopStack();
+                            if (topStack != targetStack) {
+                                Slog.w(TAG, "Expected target stack=" + targetStack
+                                        + " to be top most but found stack=" + topStack);
+                            }
+                        }
+                    } else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){
+                        // Restore the target stack to its previous position
+                        final ActivityDisplay display = targetActivity.getDisplay();
+                        display.moveStackBehindStack(targetStack, mRestoreTargetBehindStack);
+                        if (DEBUG) {
+                            final ActivityStack aboveTargetStack =
+                                    mDefaultDisplay.getStackAbove(targetStack);
+                            if (mRestoreTargetBehindStack != null
+                                    && aboveTargetStack != mRestoreTargetBehindStack) {
+                                Slog.w(TAG, "Expected target stack=" + targetStack
+                                        + " to restored behind stack=" + mRestoreTargetBehindStack
+                                        + " but it is behind stack=" + aboveTargetStack);
+                            }
+                        }
+                    } else {
+                        // Keep target stack in place, nothing changes, so ignore the transition
+                        // logic below
+                        return;
+                    }
+
+                    mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+
+                    // No reason to wait for the pausing activity in this case, as the hiding of
+                    // surfaces needs to be done immediately.
+                    mWindowManager.executeAppTransition();
+
+                    // After reordering the stacks, reset the minimized state. At this point, either
+                    // the target activity is now top-most and we will stay minimized (if in
+                    // split-screen), or we will have returned to the app, and the minimized state
+                    // should be reset
+                    mWindowManager.checkSplitScreenMinimizedChanged(true /* animate */);
+                } catch (Exception e) {
+                    Slog.e(TAG, "Failed to clean up recents activity", e);
+                    throw e;
+                } finally {
+                    mWindowManager.continueSurfaceLayout();
+                    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                }
+            });
+        }
+    }
+
+    @Override
+    public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode,
+            boolean runSychronously) {
+        if (runSychronously) {
+            finishAnimation(reorderMode);
+        } else {
+            mService.mH.post(() -> finishAnimation(reorderMode));
+        }
+    }
+
+    @Override
+    public void onStackOrderChanged() {
+        // If the activity display stack order changes, cancel any running recents animation in
+        // place
+        mWindowManager.cancelRecentsAnimationSynchronously(REORDER_KEEP_IN_PLACE,
+                "stackOrderChanged");
+    }
+
+    /**
+     * Called only when the animation should be canceled prior to starting.
+     */
+    private void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
+        try {
+            recentsAnimationRunner.onAnimationCanceled();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to cancel recents animation before start", e);
+        }
+    }
+
+    /**
+     * @return The top stack that is not always-on-top.
+     */
+    private ActivityStack getTopNonAlwaysOnTopStack() {
+        for (int i = mDefaultDisplay.getChildCount() - 1; i >= 0; i--) {
+            final ActivityStack s = mDefaultDisplay.getChildAt(i);
+            if (s.getWindowConfiguration().isAlwaysOnTop()) {
+                continue;
+            }
+            return s;
+        }
+        return null;
+    }
+
+    /**
+     * @return the top activity in the {@param targetStack} matching the {@param component}, or just
+     * the top activity of the top task if no task matches the component.
+     */
+    private ActivityRecord getTargetActivity(ActivityStack targetStack, ComponentName component) {
+        if (targetStack == null) {
+            return null;
+        }
+
+        for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
+            final TaskRecord task = (TaskRecord) targetStack.getChildAt(i);
+            if (task.getBaseIntent().getComponent().equals(component)) {
+                return task.getTopActivity();
+            }
+        }
+        return targetStack.getTopActivity();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 5c80759..c4fbee9 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -467,7 +467,8 @@
         // so if we are actually transitioning there, notify again here
         if (mTargetAppToken != null) {
             if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
-                mService.mAppTransition.notifyAppTransitionFinishedLocked(mTargetAppToken.token);
+                mService.mRoot.getDisplayContent(mDisplayId)
+                        .mAppTransition.notifyAppTransitionFinishedLocked(mTargetAppToken.token);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 8ec0a01..f5acdcc 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -182,7 +182,7 @@
         if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): mPendingAnimations="
                 + mPendingAnimations.size());
         mHandler.removeCallbacks(mTimeoutRunnable);
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             unlinkToDeathOfRunner();
             releaseFinishedCallback();
             mService.openSurfaceTransaction();
@@ -317,7 +317,7 @@
         }
 
         private int getMode() {
-            if (mService.mOpeningApps.contains(mAppWindowToken)) {
+            if (mAppWindowToken.getDisplayContent().mOpeningApps.contains(mAppWindowToken)) {
                 return RemoteAnimationTarget.MODE_OPENING;
             } else {
                 return RemoteAnimationTarget.MODE_CLOSING;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index c8977be..8c0c073 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -42,7 +42,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.H.WINDOW_FREEZE_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
@@ -55,6 +54,7 @@
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
 
 import android.annotation.CallSuper;
+import android.annotation.NonNull;
 import android.content.res.Configuration;
 import android.hardware.power.V1_0.PowerHint;
 import android.os.Binder;
@@ -76,12 +76,10 @@
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
-import com.android.internal.util.ArrayUtils;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.List;
 import java.util.function.Consumer;
 
 /** Root {@link WindowContainer} for the device. */
@@ -116,9 +114,6 @@
     boolean mOrientationChangeComplete = true;
     boolean mWallpaperActionPending = false;
 
-    private final ArrayList<TaskStack> mTmpStackList = new ArrayList();
-    private final ArrayList<Integer> mTmpStackIds = new ArrayList<>();
-
     final WallpaperController mWallpaperController;
 
     private final Handler mHandler;
@@ -314,57 +309,33 @@
     }
 
     /**
-     * Set new display override config and return array of ids of stacks that were changed during
-     * update. If called for the default display, global configuration will also be updated. Stacks
-     * that are marked for deferred removal are excluded from the returned array.
+     * Set new display override config. If called for the default display, global configuration
+     * will also be updated.
      */
-    int[] setDisplayOverrideConfigurationIfNeeded(Configuration newConfiguration, int displayId) {
-        final DisplayContent displayContent = getDisplayContent(displayId);
-        if (displayContent == null) {
-            throw new IllegalArgumentException("Display not found for id: " + displayId);
-        }
+    void setDisplayOverrideConfigurationIfNeeded(Configuration newConfiguration,
+            @NonNull DisplayContent displayContent) {
 
         final Configuration currentConfig = displayContent.getOverrideConfiguration();
         final boolean configChanged = currentConfig.diff(newConfiguration) != 0;
         if (!configChanged) {
-            return null;
+            return;
         }
 
         displayContent.onOverrideConfigurationChanged(newConfiguration);
 
-        mTmpStackList.clear();
-        if (displayId == DEFAULT_DISPLAY) {
+        if (displayContent.getDisplayId() == DEFAULT_DISPLAY) {
             // Override configuration of the default display duplicates global config. In this case
             // we also want to update the global config.
-            setGlobalConfigurationIfNeeded(newConfiguration, mTmpStackList);
-        } else {
-            updateStackBoundsAfterConfigChange(displayId, mTmpStackList);
+            setGlobalConfigurationIfNeeded(newConfiguration);
         }
-
-        mTmpStackIds.clear();
-        final int stackCount = mTmpStackList.size();
-
-        for (int i = 0; i < stackCount; ++i) {
-            final TaskStack stack = mTmpStackList.get(i);
-
-            // We only include stacks that are not marked for removal as they do not exist outside
-            // of WindowManager at this point.
-            if (!stack.mDeferRemoval) {
-                mTmpStackIds.add(stack.mStackId);
-            }
-        }
-
-        return mTmpStackIds.isEmpty() ? null : ArrayUtils.convertToIntArray(mTmpStackIds);
     }
 
-    private void setGlobalConfigurationIfNeeded(Configuration newConfiguration,
-            List<TaskStack> changedStacks) {
+    private void setGlobalConfigurationIfNeeded(Configuration newConfiguration) {
         final boolean configChanged = getConfiguration().diff(newConfiguration) != 0;
         if (!configChanged) {
             return;
         }
         onConfigurationChanged(newConfiguration);
-        updateStackBoundsAfterConfigChange(changedStacks);
     }
 
     @Override
@@ -375,24 +346,6 @@
         forAllDisplays(mDisplayContentConfigChangesConsumer);
     }
 
-    /**
-     * Callback used to trigger bounds update after configuration change and get ids of stacks whose
-     * bounds were updated.
-     */
-    private void updateStackBoundsAfterConfigChange(List<TaskStack> changedStacks) {
-        final int numDisplays = mChildren.size();
-        for (int i = 0; i < numDisplays; ++i) {
-            final DisplayContent dc = mChildren.get(i);
-            dc.updateStackBoundsAfterConfigChange(changedStacks);
-        }
-    }
-
-    /** Same as {@link #updateStackBoundsAfterConfigChange()} but only for a specific display. */
-    private void updateStackBoundsAfterConfigChange(int displayId, List<TaskStack> changedStacks) {
-        final DisplayContent dc = getDisplayContent(displayId);
-        dc.updateStackBoundsAfterConfigChange(changedStacks);
-    }
-
     private void prepareFreezingTaskBounds() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             mChildren.get(i).prepareFreezingTaskBounds();
@@ -576,17 +529,15 @@
         mSustainedPerformanceModeCurrent = false;
         mService.mTransactionSequence++;
 
-        // TODO(multi-display):
+        // TODO(multi-display): recents animation & wallpaper need support multi-display.
         final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
-        final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo();
-        final int defaultDw = defaultInfo.logicalWidth;
-        final int defaultDh = defaultInfo.logicalHeight;
+        final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
 
         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                 ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
         mService.openSurfaceTransaction();
         try {
-            applySurfaceChangesTransaction(recoveringMemory, defaultDw, defaultDh);
+            applySurfaceChangesTransaction(recoveringMemory);
         } catch (RuntimeException e) {
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
         } finally {
@@ -594,39 +545,13 @@
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                     "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
         }
-
         mService.mAnimator.executeAfterPrepareSurfacesRunnables();
 
-        final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
-
-        // If we are ready to perform an app transition, check through all of the app tokens to be
-        // shown and see if they are ready to go.
-        if (mService.mAppTransition.isReady()) {
-            // This needs to be split into two expressions, as handleAppTransitionReadyLocked may
-            // modify dc.pendingLayoutChanges, which would get lost when writing
-            // defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked()
-            final int layoutChanges = surfacePlacer.handleAppTransitionReadyLocked();
-            defaultDisplay.pendingLayoutChanges |= layoutChanges;
-            if (DEBUG_LAYOUT_REPEATS)
-                surfacePlacer.debugLayoutRepeats("after handleAppTransitionReadyLocked",
-                        defaultDisplay.pendingLayoutChanges);
-        }
-
-        if (!isAppAnimating() && mService.mAppTransition.isRunning()) {
-            // We have finished the animation of an app transition. To do this, we have delayed a
-            // lot of operations like showing and hiding apps, moving apps in Z-order, etc. The app
-            // token list reflects the correct Z-order, but the window list may now be out of sync
-            // with it. So here we will just rebuild the entire app window list. Fun!
-            defaultDisplay.pendingLayoutChanges |=
-                    mService.handleAnimatingStoppedAndTransitionLocked();
-            if (DEBUG_LAYOUT_REPEATS)
-                surfacePlacer.debugLayoutRepeats("after handleAnimStopAndXitionLock",
-                        defaultDisplay.pendingLayoutChanges);
-        }
+        checkAppTransitionReady(surfacePlacer);
 
         // Defer starting the recents animation until the wallpaper has drawn
         final RecentsAnimationController recentsAnimationController =
-            mService.getRecentsAnimationController();
+                mService.getRecentsAnimationController();
         if (recentsAnimationController != null) {
             recentsAnimationController.checkAnimationReady(mWallpaperController);
         }
@@ -732,8 +657,8 @@
             mUpdateRotation = updateRotationUnchecked();
         }
 
-        if (mService.mWaitingForDrawnCallback != null ||
-                (mOrientationChangeComplete && !defaultDisplay.isLayoutNeeded()
+        if (mService.mWaitingForDrawnCallback != null
+                || (mOrientationChangeComplete && !isLayoutNeeded()
                         && !mUpdateRotation)) {
             mService.checkDrawnWindowsLocked();
         }
@@ -741,7 +666,7 @@
         final int N = mService.mPendingRemove.size();
         if (N > 0) {
             if (mService.mPendingRemoveTmp.length < N) {
-                mService.mPendingRemoveTmp = new WindowState[N+10];
+                mService.mPendingRemoveTmp = new WindowState[N + 10];
             }
             mService.mPendingRemove.toArray(mService.mPendingRemoveTmp);
             mService.mPendingRemove.clear();
@@ -783,12 +708,47 @@
                 "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
     }
 
-    private void applySurfaceChangesTransaction(boolean recoveringMemory, int defaultDw,
-            int defaultDh) {
+    private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
+        // Trace all displays app transition by Z-order for pending layout change.
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final DisplayContent curDisplay = mChildren.get(i);
+
+            // If we are ready to perform an app transition, check through all of the app tokens
+            // to be shown and see if they are ready to go.
+            if (curDisplay.mAppTransition.isReady()) {
+                // handleAppTransitionReady may modify curDisplay.pendingLayoutChanges.
+                curDisplay.mAppTransitionController.handleAppTransitionReady();
+                if (DEBUG_LAYOUT_REPEATS) {
+                    surfacePlacer.debugLayoutRepeats("after handleAppTransitionReady",
+                            curDisplay.pendingLayoutChanges);
+                }
+            }
+
+            if (!curDisplay.isAppAnimating() && curDisplay.mAppTransition.isRunning()) {
+                // We have finished the animation of an app transition. To do this, we have
+                // delayed a lot of operations like showing and hiding apps, moving apps in
+                // Z-order, etc.
+                // The app token list reflects the correct Z-order, but the window list may now
+                // be out of sync with it. So here we will just rebuild the entire app window
+                // list. Fun!
+                curDisplay.handleAnimatingStoppedAndTransition();
+                if (DEBUG_LAYOUT_REPEATS) {
+                    surfacePlacer.debugLayoutRepeats("after handleAnimStopAndXitionLock",
+                            curDisplay.pendingLayoutChanges);
+                }
+            }
+        }
+    }
+
+    private void applySurfaceChangesTransaction(boolean recoveringMemory) {
         mHoldScreenWindow = null;
         mObscuringWindow = null;
 
         // TODO(multi-display): Support these features on secondary screens.
+        final DisplayContent defaultDc = mService.getDefaultDisplayContentLocked();
+        final DisplayInfo defaultInfo = defaultDc.getDisplayInfo();
+        final int defaultDw = defaultInfo.logicalWidth;
+        final int defaultDh = defaultInfo.logicalHeight;
         if (mService.mWatermark != null) {
             mService.mWatermark.positionSurface(defaultDw, defaultDh);
         }
@@ -904,10 +864,8 @@
         boolean changed = false;
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             final DisplayContent displayContent = mChildren.get(i);
-            if (displayContent.updateRotationUnchecked()) {
+            if (displayContent.updateRotationAndSendNewConfigIfNeeded()) {
                 changed = true;
-                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
-                        .sendToTarget();
             }
         }
         return changed;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainerController.java b/services/core/java/com/android/server/wm/RootWindowContainerController.java
index 93be6e9..1176220 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainerController.java
@@ -25,7 +25,7 @@
 
     public RootWindowContainerController(RootWindowContainerListener listener) {
         super(listener, WindowManagerService.getInstance());
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mRoot.setController(this);
         }
     }
@@ -39,7 +39,7 @@
 
     /** Move the display to the given position. */
     public void positionChildAt(DisplayWindowController child, int position) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mContainer.positionChildAt(position, child.mContainer);
         }
     }
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
new file mode 100644
index 0000000..34282cd
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration.ActivityType;
+import android.app.WindowConfiguration.WindowingMode;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+
+/**
+ * Class for resolving the set of running tasks in the system.
+ */
+class RunningTasks {
+
+    // Comparator to sort by last active time (descending)
+    private static final Comparator<TaskRecord> LAST_ACTIVE_TIME_COMPARATOR =
+            (o1, o2) -> Long.signum(o2.lastActiveTime - o1.lastActiveTime);
+
+    private final TaskRecord.TaskActivitiesReport mTmpReport =
+            new TaskRecord.TaskActivitiesReport();
+    private final TreeSet<TaskRecord> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR);
+    private final ArrayList<TaskRecord> mTmpStackTasks = new ArrayList<>();
+
+    void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType,
+            @WindowingMode int ignoreWindowingMode, ArrayList<ActivityDisplay> activityDisplays,
+            int callingUid, boolean allowed) {
+        // Return early if there are no tasks to fetch
+        if (maxNum <= 0) {
+            return;
+        }
+
+        // Gather all of the tasks across all of the tasks, and add them to the sorted set
+        mTmpSortedSet.clear();
+        final int numDisplays = activityDisplays.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final ActivityDisplay display = activityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                mTmpStackTasks.clear();
+                stack.getRunningTasks(mTmpStackTasks, ignoreActivityType, ignoreWindowingMode,
+                        callingUid, allowed);
+                mTmpSortedSet.addAll(mTmpStackTasks);
+            }
+        }
+
+        // Take the first {@param maxNum} tasks and create running task infos for them
+        final Iterator<TaskRecord> iter = mTmpSortedSet.iterator();
+        while (iter.hasNext()) {
+            if (maxNum == 0) {
+                break;
+            }
+
+            final TaskRecord task = iter.next();
+            list.add(createRunningTaskInfo(task));
+            maxNum--;
+        }
+    }
+
+    /**
+     * Constructs a {@link RunningTaskInfo} from a given {@param task}.
+     */
+    private RunningTaskInfo createRunningTaskInfo(TaskRecord task) {
+        final RunningTaskInfo rti = new RunningTaskInfo();
+        task.fillTaskInfo(rti, mTmpReport);
+        // Fill in some deprecated values
+        rti.id = rti.taskId;
+        return rti;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
new file mode 100644
index 0000000..ac90283
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.view.RemoteAnimationAdapter;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Wraps {@link ActivityOptions}, records binder identity, and checks permission when retrieving
+ * the inner options. Also supports having two set of options: Once from the original caller, and
+ * once from the caller that is overriding it, which happens when sending a {@link PendingIntent}.
+ */
+public class SafeActivityOptions {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_ATM;
+
+    private final int mOriginalCallingPid;
+    private final int mOriginalCallingUid;
+    private int mRealCallingPid;
+    private int mRealCallingUid;
+    private final @Nullable ActivityOptions mOriginalOptions;
+    private @Nullable ActivityOptions mCallerOptions;
+
+    /**
+     * Constructs a new instance from a bundle and records {@link Binder#getCallingPid}/
+     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
+     * this object.
+     *
+     * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
+     */
+    public static SafeActivityOptions fromBundle(Bundle bOptions) {
+        return bOptions != null
+                ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions))
+                : null;
+    }
+
+    /**
+     * Constructs a new instance and records {@link Binder#getCallingPid}/
+     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
+     * this object.
+     *
+     * @param options The options to wrap.
+     */
+    public SafeActivityOptions(@Nullable ActivityOptions options) {
+        mOriginalCallingPid = Binder.getCallingPid();
+        mOriginalCallingUid = Binder.getCallingUid();
+        mOriginalOptions = options;
+    }
+
+    /**
+     * Overrides options with options from a caller and records {@link Binder#getCallingPid}/
+     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
+     * method.
+     */
+    public void setCallerOptions(@Nullable ActivityOptions options) {
+        mRealCallingPid = Binder.getCallingPid();
+        mRealCallingUid = Binder.getCallingUid();
+        mCallerOptions = options;
+    }
+
+    /**
+     * Performs permission check and retrieves the options.
+     *
+     * @param r The record of the being started activity.
+     */
+    ActivityOptions getOptions(ActivityRecord r) throws SecurityException {
+        return getOptions(r.intent, r.info, r.app, r.mStackSupervisor);
+    }
+
+    /**
+     * Performs permission check and retrieves the options when options are not being used to launch
+     * a specific activity (i.e. a task is moved to front).
+     */
+    ActivityOptions getOptions(ActivityStackSupervisor supervisor) throws SecurityException {
+        return getOptions(null, null, null, supervisor);
+    }
+
+    /**
+     * Performs permission check and retrieves the options.
+     *
+     * @param intent The intent that is being launched.
+     * @param aInfo The info of the activity being launched.
+     * @param callerApp The record of the caller.
+     */
+    ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
+            @Nullable WindowProcessController callerApp,
+            ActivityStackSupervisor supervisor) throws SecurityException {
+        if (mOriginalOptions != null) {
+            checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions,
+                    mOriginalCallingPid, mOriginalCallingUid);
+            setCallingPidForRemoteAnimationAdapter(mOriginalOptions, mOriginalCallingPid);
+        }
+        if (mCallerOptions != null) {
+            checkPermissions(intent, aInfo, callerApp, supervisor, mCallerOptions,
+                    mRealCallingPid, mRealCallingUid);
+            setCallingPidForRemoteAnimationAdapter(mCallerOptions, mRealCallingPid);
+        }
+        return mergeActivityOptions(mOriginalOptions, mCallerOptions);
+    }
+
+    private void setCallingPidForRemoteAnimationAdapter(ActivityOptions options, int callingPid) {
+        final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
+        if (adapter == null) {
+            return;
+        }
+        if (callingPid == Process.myPid()) {
+            Slog.wtf(TAG, "Safe activity options constructed after clearing calling id");
+            return;
+        }
+        adapter.setCallingPid(callingPid);
+    }
+
+    /**
+     * @see ActivityOptions#popAppVerificationBundle
+     */
+    Bundle popAppVerificationBundle() {
+        return mOriginalOptions != null ? mOriginalOptions.popAppVerificationBundle() : null;
+    }
+
+    private void abort() {
+        if (mOriginalOptions != null) {
+            ActivityOptions.abort(mOriginalOptions);
+        }
+        if (mCallerOptions != null) {
+            ActivityOptions.abort(mCallerOptions);
+        }
+    }
+
+    static void abort(@Nullable SafeActivityOptions options) {
+        if (options != null) {
+            options.abort();
+        }
+    }
+
+    /**
+     * Merges two activity options into one, with {@code options2} taking precedence in case of a
+     * conflict.
+     */
+    @VisibleForTesting
+    @Nullable ActivityOptions mergeActivityOptions(@Nullable ActivityOptions options1,
+            @Nullable ActivityOptions options2) {
+        if (options1 == null) {
+            return options2;
+        }
+        if (options2 == null) {
+            return options1;
+        }
+        final Bundle b1 = options1.toBundle();
+        final Bundle b2 = options2.toBundle();
+        b1.putAll(b2);
+        return ActivityOptions.fromBundle(b1);
+    }
+
+    private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
+            @Nullable WindowProcessController callerApp, ActivityStackSupervisor supervisor,
+            ActivityOptions options, int callingPid, int callingUid) {
+        // If a launch task id is specified, then ensure that the caller is the recents
+        // component or has the START_TASKS_FROM_RECENTS permission
+        if (options.getLaunchTaskId() != INVALID_TASK_ID
+                && !supervisor.mRecentTasks.isCallerRecents(callingUid)) {
+            final int startInTaskPerm = ActivityTaskManagerService.checkPermission(
+                    START_TASKS_FROM_RECENTS, callingPid, callingUid);
+            if (startInTaskPerm == PERMISSION_DENIED) {
+                final String msg = "Permission Denial: starting " + getIntentString(intent)
+                        + " from " + callerApp + " (pid=" + callingPid
+                        + ", uid=" + callingUid + ") with launchTaskId="
+                        + options.getLaunchTaskId();
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
+        }
+        // Check if someone tries to launch an activity on a private display with a different
+        // owner.
+        final int launchDisplayId = options.getLaunchDisplayId();
+        if (aInfo != null && launchDisplayId != INVALID_DISPLAY
+                && !supervisor.isCallerAllowedToLaunchOnDisplay(callingPid, callingUid,
+                        launchDisplayId, aInfo)) {
+            final String msg = "Permission Denial: starting " + getIntentString(intent)
+                    + " from " + callerApp + " (pid=" + callingPid
+                    + ", uid=" + callingUid + ") with launchDisplayId="
+                    + launchDisplayId;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        // Check if someone tries to launch an unwhitelisted activity into LockTask mode.
+        final boolean lockTaskMode = options.getLockTaskMode();
+        if (aInfo != null && lockTaskMode
+                && !supervisor.mService.getLockTaskController().isPackageWhitelisted(
+                        UserHandle.getUserId(callingUid), aInfo.packageName)) {
+            final String msg = "Permission Denial: starting " + getIntentString(intent)
+                    + " from " + callerApp + " (pid=" + callingPid
+                    + ", uid=" + callingUid + ") with lockTaskMode=true";
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+
+        // Check permission for remote animations
+        final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
+        if (adapter != null && supervisor.mService.checkPermission(
+                CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
+                        != PERMISSION_GRANTED) {
+            final String msg = "Permission Denial: starting " + getIntentString(intent)
+                    + " from " + callerApp + " (pid=" + callingPid
+                    + ", uid=" + callingUid + ") with remoteAnimationAdapter";
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+    }
+
+    private String getIntentString(Intent intent) {
+        return intent != null ? intent.toString() : "(no intent)";
+    }
+}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index acc9c03..b411fad 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -143,7 +143,7 @@
 
     @Override
     public void binderDied() {
-        synchronized(mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             mCallback.asBinder().unlinkToDeath(this, 0);
             mClientDead = true;
             killSessionLocked();
@@ -229,14 +229,14 @@
 
     @Override
     public void setInTouchMode(boolean mode) {
-        synchronized(mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             mService.mInTouchMode = mode;
         }
     }
 
     @Override
     public boolean getInTouchMode() {
-        synchronized(mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             return mService.mInTouchMode;
         }
     }
@@ -244,7 +244,7 @@
     @Override
     public boolean performHapticFeedback(IWindow window, int effectId,
             boolean always) {
-        synchronized(mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             long ident = Binder.clearCallingIdentity();
             try {
                 return mService.mPolicy.performHapticFeedbackLw(
@@ -317,7 +317,7 @@
 
     @Override
     public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
-        synchronized(mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             long ident = Binder.clearCallingIdentity();
             try {
                 mService.mRoot.mWallpaperController.setWindowWallpaperPosition(
@@ -331,14 +331,14 @@
 
     @Override
     public void wallpaperOffsetsComplete(IBinder window) {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             mService.mRoot.mWallpaperController.wallpaperOffsetsComplete(window);
         }
     }
 
     @Override
     public void setWallpaperDisplayOffset(IBinder window, int x, int y) {
-        synchronized(mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             long ident = Binder.clearCallingIdentity();
             try {
                 mService.mRoot.mWallpaperController.setWindowWallpaperDisplayOffset(
@@ -352,7 +352,7 @@
     @Override
     public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
             int z, Bundle extras, boolean sync) {
-        synchronized(mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             long ident = Binder.clearCallingIdentity();
             try {
                 return mService.mRoot.mWallpaperController.sendWindowWallpaperCommand(
@@ -366,14 +366,14 @@
 
     @Override
     public void wallpaperCommandComplete(IBinder window, Bundle result) {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             mService.mRoot.mWallpaperController.wallpaperCommandComplete(window);
         }
     }
 
     @Override
     public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
-        synchronized(mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             final long identity = Binder.clearCallingIdentity();
             try {
                 mService.onRectangleOnScreenRequested(token, rectangle);
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 1fd2c0f..d8e1ebf 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -16,6 +16,11 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
 import android.app.WindowConfiguration;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -31,11 +36,6 @@
 
 import java.lang.ref.WeakReference;
 
-import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
 /**
  * Controller for the stack container. This is created by activity manager to link activity stacks
  * to the stack container they use in window manager.
@@ -67,7 +67,7 @@
         mStackId = stackId;
         mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
 
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent dc = mRoot.getDisplayContent(displayId);
             if (dc == null) {
                 throw new IllegalArgumentException("Trying to add stackId=" + stackId
@@ -81,7 +81,7 @@
 
     @Override
     public void removeContainer() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer != null) {
                 mContainer.removeIfPossible();
                 super.removeContainer();
@@ -90,7 +90,7 @@
     }
 
     public void reparent(int displayId, Rect outStackBounds, boolean onTop) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 throw new IllegalArgumentException("Trying to move unknown stackId=" + mStackId
                         + " to displayId=" + displayId);
@@ -108,7 +108,7 @@
     }
 
     public void positionChildAt(TaskWindowContainerController child, int position) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (DEBUG_STACK) Slog.i(TAG_WM, "positionChildAt: positioning task=" + child
                     + " at " + position);
             if (child.mContainer == null) {
@@ -132,7 +132,7 @@
             return;
         }
 
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             final Task childTask = child.mContainer;
             if (childTask == null) {
                 Slog.e(TAG_WM, "positionChildAtTop: task=" + child + " not found");
@@ -140,10 +140,11 @@
             }
             mContainer.positionChildAt(POSITION_TOP, childTask, includingParents);
 
-            if (mService.mAppTransition.isTransitionSet()) {
+            final DisplayContent displayContent = mContainer.getDisplayContent();
+            if (displayContent.mAppTransition.isTransitionSet()) {
                 childTask.setSendingToBottom(false);
             }
-            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+            displayContent.layoutAndAssignWindowLayersIfNeeded();
         }
     }
 
@@ -154,7 +155,7 @@
             return;
         }
 
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             final Task childTask = child.mContainer;
             if (childTask == null) {
                 Slog.e(TAG_WM, "positionChildAtBottom: task=" + child + " not found");
@@ -162,7 +163,7 @@
             }
             mContainer.positionChildAt(POSITION_BOTTOM, childTask, includingParents);
 
-            if (mService.mAppTransition.isTransitionSet()) {
+            if (mContainer.getDisplayContent().mAppTransition.isTransitionSet()) {
                 childTask.setSendingToBottom(true);
             }
             mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
@@ -178,7 +179,7 @@
      */
     public void resize(Rect bounds, SparseArray<Rect> taskBounds,
             SparseArray<Rect> taskTempInsetBounds) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 throw new IllegalArgumentException("resizeStack: stack " + this + " not found.");
             }
@@ -193,7 +194,7 @@
     }
 
     public void onPipAnimationEndResize() {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             mContainer.onPipAnimationEndResize();
         }
     }
@@ -203,7 +204,7 @@
      */
    public void getStackDockedModeBounds(Rect currentTempTaskBounds, Rect outStackBounds,
            Rect outTempTaskBounds, boolean ignoreVisibility) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer != null) {
                 mContainer.getStackDockedModeBoundsLocked(currentTempTaskBounds, outStackBounds,
                         outTempTaskBounds, ignoreVisibility);
@@ -215,7 +216,7 @@
     }
 
     public void prepareFreezingTaskBounds() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 throw new IllegalArgumentException("prepareFreezingTaskBounds: stack " + this
                         + " not found.");
@@ -225,7 +226,7 @@
     }
 
     public void getRawBounds(Rect outBounds) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer.matchParentBounds()) {
                 outBounds.setEmpty();
             } else {
@@ -235,7 +236,7 @@
     }
 
     public void getBounds(Rect outBounds) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer != null) {
                 mContainer.getBounds(outBounds);
                 return;
@@ -244,12 +245,6 @@
         }
     }
 
-    public void getBoundsForNewConfiguration(Rect outBounds) {
-        synchronized(mWindowMap) {
-            mContainer.getBoundsForNewConfiguration(outBounds);
-        }
-    }
-
     /**
      * Adjusts the screen size in dp's for the {@param config} for the given params. The provided
      * params represent the desired state of a configuration change. Since this utility is used
@@ -260,7 +255,7 @@
             Rect nonDecorBounds, Rect stableBounds, boolean overrideWidth,
             boolean overrideHeight, float density, Configuration config,
             Configuration parentConfig, int windowingMode) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final TaskStack stack = mContainer;
             final DisplayContent displayContent = stack.getDisplayContent();
             final DisplayInfo di = displayContent.getDisplayInfo();
@@ -377,6 +372,14 @@
         mHandler.obtainMessage(H.REQUEST_RESIZE, bounds).sendToTarget();
     }
 
+    /** @see TaskStack.updateBoundsForConfigChange(Configuration, Configuration, Rect) */
+    public boolean updateBoundsForConfigChange(
+            Configuration parentConfig, Configuration prevConfig, Rect outBounds) {
+        synchronized (mGlobalLock) {
+            return mContainer.updateBoundsForConfigChange(parentConfig, prevConfig, outBounds);
+        }
+    }
+
     @Override
     public String toString() {
         return "{StackWindowController stackId=" + mStackId + "}";
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index ba3d091..3ea615a 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -71,7 +71,7 @@
     private OnAnimationFinishedCallback getFinishedCallback(
             @Nullable Runnable animationFinishedCallback) {
         return anim -> {
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
                 if (target != null) {
                     target.mInnerAnimationFinishedCallback.onAnimationFinished(anim);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6aa0e01..30eca89 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -45,6 +45,7 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
+import android.view.Display;
 import android.view.Surface;
 import android.view.SurfaceControl;
 
@@ -68,7 +69,12 @@
     // Bounds used to calculate the insets.
     private final Rect mTempInsetBounds = new Rect();
 
-    // Device rotation as of the last time {@link #mBounds} was set.
+    /** ID of the display which rotation {@link #mRotation} has. */
+    private int mLastRotationDisplayId = Display.INVALID_DISPLAY;
+    /**
+     * Display rotation as of the last time {@link #setBounds(Rect)} was called or this task was
+     * moved to a new display.
+     */
     private int mRotation;
 
     // For comparison with DisplayContent bounds.
@@ -221,7 +227,7 @@
         }
     }
 
-    /** @see com.android.server.am.ActivityTaskManagerService#positionTaskInStack(int, int, int). */
+    /** @see ActivityTaskManagerService#positionTaskInStack(int, int, int). */
     void positionAt(int position) {
         mStack.positionChildAt(position, this, false /* includingParents */);
     }
@@ -284,12 +290,7 @@
         if (displayContent != null) {
             rotation = displayContent.getDisplayInfo().rotation;
         } else if (bounds == null) {
-            // Can't set to fullscreen if we don't have a display to get bounds from...
-            return BOUNDS_CHANGE_NONE;
-        }
-
-        if (equivalentOverrideBounds(bounds)) {
-            return BOUNDS_CHANGE_NONE;
+            return super.setBounds(bounds);
         }
 
         final int boundsChange = super.setBounds(bounds);
@@ -510,8 +511,20 @@
             setBounds(null);
             return;
         }
+        final int displayId = displayContent.getDisplayId();
         final int newRotation = displayContent.getDisplayInfo().rotation;
+        if (displayId != mLastRotationDisplayId) {
+            // This task is on a display that it wasn't on. There is no point to keep the relative
+            // position if display rotations for old and new displays are different. Just keep these
+            // values.
+            mLastRotationDisplayId = displayId;
+            mRotation = newRotation;
+            return;
+        }
+
         if (mRotation == newRotation) {
+            // Rotation didn't change. We don't need to adjust the bounds to keep the relative
+            // position.
             return;
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
new file mode 100644
index 0000000..3b3feac
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2016 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.server.wm;
+
+import android.app.ActivityManager.TaskSnapshot;
+import android.app.ITaskStackListener;
+import android.app.ActivityManager.TaskDescription;
+import android.content.ComponentName;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+
+class TaskChangeNotificationController {
+    private static final int LOG_STACK_STATE_MSG = 1;
+    private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2;
+    private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3;
+    private static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4;
+    private static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5;
+    private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6;
+    private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7;
+    private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8;
+    private static final int NOTIFY_TASK_REMOVED_LISTENERS_MSG = 9;
+    private static final int NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG = 10;
+    private static final int NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG = 11;
+    private static final int NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS = 12;
+    private static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
+    private static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14;
+    private static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15;
+    private static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16;
+    private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17;
+    private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18;
+
+    // Delay in notifying task stack change listeners (in millis)
+    private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
+
+    // Global lock used by the service the instantiate objects of this class.
+    private final Object mServiceLock;
+    private final ActivityStackSupervisor mStackSupervisor;
+    private final Handler mHandler;
+
+    // Task stack change listeners in a remote process.
+    private final RemoteCallbackList<ITaskStackListener> mRemoteTaskStackListeners =
+            new RemoteCallbackList<>();
+
+    /*
+     * Task stack change listeners in a local process. Tracked separately so that they can be
+     * called on the same thread.
+     */
+    private final ArrayList<ITaskStackListener> mLocalTaskStackListeners = new ArrayList<>();
+
+    private final TaskStackConsumer mNotifyTaskStackChanged = (l, m) -> {
+        l.onTaskStackChanged();
+    };
+
+    private final TaskStackConsumer mNotifyTaskCreated = (l, m) -> {
+        l.onTaskCreated(m.arg1, (ComponentName) m.obj);
+    };
+
+    private final TaskStackConsumer mNotifyTaskRemoved = (l, m) -> {
+        l.onTaskRemoved(m.arg1);
+    };
+
+    private final TaskStackConsumer mNotifyTaskMovedToFront = (l, m) -> {
+        l.onTaskMovedToFront(m.arg1);
+    };
+
+    private final TaskStackConsumer mNotifyTaskDescriptionChanged = (l, m) -> {
+        l.onTaskDescriptionChanged(m.arg1, (TaskDescription) m.obj);
+    };
+
+    private final TaskStackConsumer mNotifyActivityRequestedOrientationChanged = (l, m) -> {
+        l.onActivityRequestedOrientationChanged(m.arg1, m.arg2);
+    };
+
+    private final TaskStackConsumer mNotifyTaskRemovalStarted = (l, m) -> {
+        l.onTaskRemovalStarted(m.arg1);
+    };
+
+    private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> {
+        l.onActivityPinned((String) m.obj /* packageName */, m.sendingUid /* userId */,
+                m.arg1 /* taskId */, m.arg2 /* stackId */);
+    };
+
+    private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> {
+        l.onActivityUnpinned();
+    };
+
+    private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> {
+        l.onPinnedActivityRestartAttempt(m.arg1 != 0);
+    };
+
+    private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> {
+        l.onPinnedStackAnimationStarted();
+    };
+
+    private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> {
+        l.onPinnedStackAnimationEnded();
+    };
+
+    private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> {
+        l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2);
+    };
+
+    private final TaskStackConsumer mNotifyActivityDismissingDockedStack = (l, m) -> {
+        l.onActivityDismissingDockedStack();
+    };
+
+    private final TaskStackConsumer mNotifyActivityLaunchOnSecondaryDisplayFailed = (l, m) -> {
+        l.onActivityLaunchOnSecondaryDisplayFailed();
+    };
+
+    private final TaskStackConsumer mNotifyTaskProfileLocked = (l, m) -> {
+        l.onTaskProfileLocked(m.arg1, m.arg2);
+    };
+
+    private final TaskStackConsumer mNotifyTaskSnapshotChanged = (l, m) -> {
+        l.onTaskSnapshotChanged(m.arg1, (TaskSnapshot) m.obj);
+    };
+
+    @FunctionalInterface
+    public interface TaskStackConsumer {
+        void accept(ITaskStackListener t, Message m) throws RemoteException;
+    }
+
+    private class MainHandler extends Handler {
+        public MainHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case LOG_STACK_STATE_MSG: {
+                    synchronized (mServiceLock) {
+                        mStackSupervisor.logStackState();
+                    }
+                    break;
+                }
+                case NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyTaskStackChanged, msg);
+                    break;
+                case NOTIFY_TASK_ADDED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyTaskCreated, msg);
+                    break;
+                case NOTIFY_TASK_REMOVED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyTaskRemoved, msg);
+                    break;
+                case NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyTaskMovedToFront, msg);
+                    break;
+                case NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyTaskDescriptionChanged, msg);
+                    break;
+                case NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS:
+                    forAllRemoteListeners(mNotifyActivityRequestedOrientationChanged, msg);
+                    break;
+                case NOTIFY_TASK_REMOVAL_STARTED_LISTENERS:
+                    forAllRemoteListeners(mNotifyTaskRemovalStarted, msg);
+                    break;
+                case NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyActivityPinned, msg);
+                    break;
+                case NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyActivityUnpinned, msg);
+                    break;
+                case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg);
+                    break;
+                case NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyPinnedStackAnimationStarted, msg);
+                    break;
+                case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyPinnedStackAnimationEnded, msg);
+                    break;
+                case NOTIFY_FORCED_RESIZABLE_MSG:
+                    forAllRemoteListeners(mNotifyActivityForcedResizable, msg);
+                    break;
+                case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG:
+                    forAllRemoteListeners(mNotifyActivityDismissingDockedStack, msg);
+                    break;
+                case NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG:
+                    forAllRemoteListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg);
+                    break;
+                case NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyTaskProfileLocked, msg);
+                    break;
+                case NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyTaskSnapshotChanged, msg);
+                    break;
+            }
+        }
+    }
+
+    public TaskChangeNotificationController(Object serviceLock,
+            ActivityStackSupervisor stackSupervisor, Handler handler) {
+        mServiceLock = serviceLock;
+        mStackSupervisor = stackSupervisor;
+        mHandler = new MainHandler(handler.getLooper());
+    }
+
+    public void registerTaskStackListener(ITaskStackListener listener) {
+        synchronized (mServiceLock) {
+            if (listener != null) {
+                if (Binder.getCallingPid() == android.os.Process.myPid()) {
+                    if (!mLocalTaskStackListeners.contains(listener)) {
+                        mLocalTaskStackListeners.add(listener);
+                    }
+                } else {
+                    mRemoteTaskStackListeners.register(listener);
+                }
+            }
+        }
+    }
+
+    public void unregisterTaskStackListener(ITaskStackListener listener) {
+        synchronized (mServiceLock) {
+            if (listener != null) {
+                if (Binder.getCallingPid() == android.os.Process.myPid()) {
+                    mLocalTaskStackListeners.remove(listener);
+                } else {
+                    mRemoteTaskStackListeners.unregister(listener);
+                }
+            }
+        }
+    }
+
+    private void forAllRemoteListeners(TaskStackConsumer callback, Message message) {
+        synchronized (mServiceLock) {
+            for (int i = mRemoteTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
+                try {
+                    // Make a one-way callback to the listener
+                    callback.accept(mRemoteTaskStackListeners.getBroadcastItem(i), message);
+                } catch (RemoteException e) {
+                    // Handled by the RemoteCallbackList.
+                }
+            }
+            mRemoteTaskStackListeners.finishBroadcast();
+        }
+    }
+
+    private void forAllLocalListeners(TaskStackConsumer callback, Message message) {
+        synchronized (mServiceLock) {
+            for (int i = mLocalTaskStackListeners.size() - 1; i >= 0; i--) {
+                try {
+                    callback.accept(mLocalTaskStackListeners.get(i), message);
+                } catch (RemoteException e) {
+                    // Never thrown since this is called locally.
+                }
+            }
+        }
+    }
+
+    /** Notifies all listeners when the task stack has changed. */
+    void notifyTaskStackChanged() {
+        mHandler.sendEmptyMessage(LOG_STACK_STATE_MSG);
+        mHandler.removeMessages(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
+        forAllLocalListeners(mNotifyTaskStackChanged, msg);
+        // Only the main task stack change notification requires a delay.
+        mHandler.sendMessageDelayed(msg, NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY);
+    }
+
+    /** Notifies all listeners when an Activity is pinned. */
+    void notifyActivityPinned(ActivityRecord r) {
+        mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
+        final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
+                r.getTask().taskId, r.getStackId(), r.packageName);
+        msg.sendingUid = r.userId;
+        forAllLocalListeners(mNotifyActivityPinned, msg);
+        msg.sendToTarget();
+    }
+
+    /** Notifies all listeners when an Activity is unpinned. */
+    void notifyActivityUnpinned() {
+        mHandler.removeMessages(NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG);
+        final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG);
+        forAllLocalListeners(mNotifyActivityUnpinned, msg);
+        msg.sendToTarget();
+    }
+
+    /**
+     * Notifies all listeners when an attempt was made to start an an activity that is already
+     * running in the pinned stack and the activity was not actually started, but the task is
+     * either brought to the front or a new Intent is delivered to it.
+     */
+    void notifyPinnedActivityRestartAttempt(boolean clearedTask) {
+        mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
+        final Message msg =
+                mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG,
+                        clearedTask ? 1 : 0, 0);
+        forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg);
+        msg.sendToTarget();
+    }
+
+    /** Notifies all listeners when the pinned stack animation starts. */
+    void notifyPinnedStackAnimationStarted() {
+        mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
+        final Message msg =
+                mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
+        forAllLocalListeners(mNotifyPinnedStackAnimationStarted, msg);
+        msg.sendToTarget();
+    }
+
+    /** Notifies all listeners when the pinned stack animation ends. */
+    void notifyPinnedStackAnimationEnded() {
+        mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
+        final Message msg =
+                mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
+        forAllLocalListeners(mNotifyPinnedStackAnimationEnded, msg);
+        msg.sendToTarget();
+    }
+
+    void notifyActivityDismissingDockedStack() {
+        mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
+        final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
+        forAllLocalListeners(mNotifyActivityDismissingDockedStack, msg);
+        msg.sendToTarget();
+    }
+
+    void notifyActivityForcedResizable(int taskId, int reason, String packageName) {
+        mHandler.removeMessages(NOTIFY_FORCED_RESIZABLE_MSG);
+        final Message msg = mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, taskId, reason,
+                packageName);
+        forAllLocalListeners(mNotifyActivityForcedResizable, msg);
+        msg.sendToTarget();
+    }
+
+    void notifyActivityLaunchOnSecondaryDisplayFailed() {
+        mHandler.removeMessages(NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG);
+        final Message msg = mHandler.obtainMessage(
+                NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG);
+        forAllLocalListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg);
+        msg.sendToTarget();
+    }
+
+    void notifyTaskCreated(int taskId, ComponentName componentName) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_ADDED_LISTENERS_MSG,
+                taskId, 0 /* unused */, componentName);
+        forAllLocalListeners(mNotifyTaskCreated, msg);
+        msg.sendToTarget();
+    }
+
+    void notifyTaskRemoved(int taskId) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_REMOVED_LISTENERS_MSG,
+                taskId, 0 /* unused */);
+        forAllLocalListeners(mNotifyTaskRemoved, msg);
+        msg.sendToTarget();
+    }
+
+    void notifyTaskMovedToFront(int taskId) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG,
+                taskId, 0 /* unused */);
+        forAllLocalListeners(mNotifyTaskMovedToFront, msg);
+        msg.sendToTarget();
+    }
+
+    void notifyTaskDescriptionChanged(int taskId, TaskDescription taskDescription) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG,
+                taskId, 0 /* unused */, taskDescription);
+        forAllLocalListeners(mNotifyTaskDescriptionChanged, msg);
+        msg.sendToTarget();
+
+    }
+
+    void notifyActivityRequestedOrientationChanged(int taskId, int orientation) {
+        final Message msg = mHandler.obtainMessage(
+                NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS, taskId, orientation);
+        forAllLocalListeners(mNotifyActivityRequestedOrientationChanged, msg);
+        msg.sendToTarget();
+    }
+
+    /**
+     * Notify listeners that the task is about to be finished before its surfaces are removed from
+     * the window manager. This allows interested parties to perform relevant animations before
+     * the window disappears.
+     */
+    void notifyTaskRemovalStarted(int taskId) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_REMOVAL_STARTED_LISTENERS, taskId,
+                0 /* unused */);
+        forAllLocalListeners(mNotifyTaskRemovalStarted, msg);
+        msg.sendToTarget();
+
+    }
+
+    /**
+     * Notify listeners that the task has been put in a locked state because one or more of the
+     * activities inside it belong to a managed profile user that has been locked.
+     */
+    void notifyTaskProfileLocked(int taskId, int userId) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG, taskId,
+                userId);
+        forAllLocalListeners(mNotifyTaskProfileLocked, msg);
+        msg.sendToTarget();
+    }
+
+    /**
+     * Notify listeners that the snapshot of a task has changed.
+     */
+    void notifyTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG,
+                taskId, 0, snapshot);
+        forAllLocalListeners(mNotifyTaskSnapshotChanged, msg);
+        msg.sendToTarget();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
new file mode 100644
index 0000000..b7804e8
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -0,0 +1,810 @@
+/*
+ * Copyright (C) 2015 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.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT;
+import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.app.WindowConfiguration;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.Slog;
+import android.view.Gravity;
+
+import com.android.server.wm.LaunchParamsController.LaunchParams;
+import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The class that defines the default launch params for tasks.
+ */
+class TaskLaunchParamsModifier implements LaunchParamsModifier {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskLaunchParamsModifier" : TAG_ATM;
+    private static final boolean DEBUG = false;
+
+    // A mask for SUPPORTS_SCREEN that indicates the activity supports resize.
+    private static final int SUPPORTS_SCREEN_RESIZEABLE_MASK =
+            ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES
+                    | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
+                    | ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
+                    | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS
+                    | ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES
+                    | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
+
+    // Screen size of Nexus 5x
+    private static final int DEFAULT_PORTRAIT_PHONE_WIDTH_DP = 412;
+    private static final int DEFAULT_PORTRAIT_PHONE_HEIGHT_DP = 732;
+
+    // Allowance of size matching.
+    private static final int EPSILON = 2;
+
+    // Cascade window offset.
+    private static final int CASCADING_OFFSET_DP = 75;
+
+    // Threshold how close window corners have to be to call them colliding.
+    private static final int BOUNDS_CONFLICT_THRESHOLD = 4;
+
+    // Divide display size by this number to get each step to adjust bounds to avoid conflict.
+    private static final int STEP_DENOMINATOR = 16;
+
+    // We always want to step by at least this.
+    private static final int MINIMAL_STEP = 1;
+
+    private final ActivityStackSupervisor mSupervisor;
+    private final Rect mTmpBounds = new Rect();
+    private final int[] mTmpDirections = new int[2];
+
+    private StringBuilder mLogBuilder;
+
+    TaskLaunchParamsModifier(ActivityStackSupervisor supervisor) {
+        mSupervisor = supervisor;
+    }
+
+    @Override
+    public int onCalculate(TaskRecord task, ActivityInfo.WindowLayout layout,
+                           ActivityRecord activity, ActivityRecord source, ActivityOptions options,
+                           LaunchParams currentParams, LaunchParams outParams) {
+        initLogBuilder(task, activity);
+        final int result = calculate(task, layout, activity, source, options, currentParams,
+                outParams);
+        outputLog();
+        return result;
+    }
+
+    private int calculate(TaskRecord task, ActivityInfo.WindowLayout layout,
+            ActivityRecord activity, ActivityRecord source, ActivityOptions options,
+            LaunchParams currentParams, LaunchParams outParams) {
+        final ActivityRecord root;
+        if (task != null) {
+            root = task.getRootActivity() == null ? activity : task.getRootActivity();
+        } else {
+            root = activity;
+        }
+
+        // TODO: Investigate whether we can safely ignore all cases where we don't have root
+        // activity available. Note we can't know if the bounds are valid if we're not sure of the
+        // requested orientation of the root activity. Therefore if we found such a case we may need
+        // to pass the activity into this modifier in that case.
+        if (root == null) {
+            // There is a case that can lead us here. The caller is moving the top activity that is
+            // in a task that has multiple activities to PIP mode. For that the caller is creating a
+            // new task to host the activity so that we only move the top activity to PIP mode and
+            // keep other activities in the previous task. There is no point to apply the launch
+            // logic in this case.
+            return RESULT_SKIP;
+        }
+
+        // STEP 1: Determine the display to launch the activity/task.
+        final int displayId = getPreferredLaunchDisplay(task, options, source, currentParams);
+        outParams.mPreferredDisplayId = displayId;
+        ActivityDisplay display = mSupervisor.getActivityDisplay(displayId);
+        if (DEBUG) {
+            appendLog("display-id=" + outParams.mPreferredDisplayId + " display-windowing-mode="
+                    + display.getWindowingMode());
+        }
+
+        // STEP 2: Resolve launch windowing mode.
+        // STEP 2.1: Determine if any parameter has specified initial bounds. That might be the
+        // launch bounds from activity options, or size/gravity passed in layout. It also treats the
+        // launch windowing mode in options as a suggestion for future resolution.
+        int launchMode = options != null ? options.getLaunchWindowingMode()
+                : WINDOWING_MODE_UNDEFINED;
+        // hasInitialBounds is set if either activity options or layout has specified bounds. If
+        // that's set we'll skip some adjustments later to avoid overriding the initial bounds.
+        boolean hasInitialBounds = false;
+        final boolean canApplyFreeformPolicy = canApplyFreeformWindowPolicy(display, launchMode);
+        if (mSupervisor.canUseActivityOptionsLaunchBounds(options)
+                && (canApplyFreeformPolicy || canApplyPipWindowPolicy(launchMode))) {
+            hasInitialBounds = true;
+            launchMode = launchMode == WINDOWING_MODE_UNDEFINED
+                    ? WINDOWING_MODE_FREEFORM
+                    : launchMode;
+            outParams.mBounds.set(options.getLaunchBounds());
+            if (DEBUG) appendLog("activity-options-bounds=" + outParams.mBounds);
+        } else if (launchMode == WINDOWING_MODE_PINNED) {
+            // System controls PIP window's bounds, so don't apply launch bounds.
+            if (DEBUG) appendLog("empty-window-layout-for-pip");
+        } else if (launchMode == WINDOWING_MODE_FULLSCREEN) {
+            if (DEBUG) appendLog("activity-options-fullscreen=" + outParams.mBounds);
+        } else if (layout != null && canApplyFreeformPolicy) {
+            getLayoutBounds(display, root, layout, mTmpBounds);
+            if (!mTmpBounds.isEmpty()) {
+                launchMode = WINDOWING_MODE_FREEFORM;
+                outParams.mBounds.set(mTmpBounds);
+                hasInitialBounds = true;
+                if (DEBUG) appendLog("bounds-from-layout=" + outParams.mBounds);
+            } else {
+                if (DEBUG) appendLog("empty-window-layout");
+            }
+        }
+
+        // STEP 2.2: Check if previous modifier or the controller (referred as "callers" below) has
+        // some opinions on launch mode and launch bounds. If they have opinions and there is no
+        // initial bounds set in parameters. Note the check on display ID is also input param
+        // related because we always defer to callers' suggestion if there is no specific display ID
+        // in options or from source activity.
+        //
+        // If opinions from callers don't need any further resolution, we try to honor that as is as
+        // much as possible later.
+
+        // Flag to indicate if current param needs no further resolution. It's true it current
+        // param isn't freeform mode, or it already has launch bounds.
+        boolean fullyResolvedCurrentParam = false;
+        // We inherit launch params from previous modifiers or LaunchParamsController if options,
+        // layout and display conditions are not contradictory to their suggestions. It's important
+        // to carry over their values because LaunchParamsController doesn't automatically do that.
+        if (!currentParams.isEmpty() && !hasInitialBounds
+                && (!currentParams.hasPreferredDisplay()
+                    || displayId == currentParams.mPreferredDisplayId)) {
+            if (currentParams.hasWindowingMode()) {
+                launchMode = currentParams.mWindowingMode;
+                fullyResolvedCurrentParam = (launchMode != WINDOWING_MODE_FREEFORM);
+                if (DEBUG) {
+                    appendLog("inherit-" + WindowConfiguration.windowingModeToString(launchMode));
+                }
+            }
+
+            if (!currentParams.mBounds.isEmpty()) {
+                outParams.mBounds.set(currentParams.mBounds);
+                fullyResolvedCurrentParam = true;
+                if (DEBUG) appendLog("inherit-bounds=" + outParams.mBounds);
+            }
+        }
+
+        // STEP 2.3: Adjust launch parameters as needed for freeform display. We enforce the policy
+        // that legacy (pre-D) apps and those apps that can't handle multiple screen density well
+        // are forced to be maximized. The rest of this step is to define the default policy when
+        // there is no initial bounds or a fully resolved current params from callers. Right now we
+        // launch all possible tasks/activities that can handle freeform into freeform mode.
+        if (display.inFreeformWindowingMode()) {
+            if (launchMode == WINDOWING_MODE_PINNED) {
+                if (DEBUG) appendLog("picture-in-picture");
+            } else if (isTaskForcedMaximized(root)) {
+                // We're launching an activity that probably can't handle resizing nicely, so force
+                // it to be maximized even someone suggests launching it in freeform using launch
+                // options.
+                launchMode = WINDOWING_MODE_FULLSCREEN;
+                outParams.mBounds.setEmpty();
+                if (DEBUG) appendLog("forced-maximize");
+            } else if (fullyResolvedCurrentParam) {
+                // Don't adjust launch mode if that's inherited, except when we're launching an
+                // activity that should be forced to maximize.
+                if (DEBUG) appendLog("skip-adjustment-fully-resolved-params");
+            } else if (launchMode != WINDOWING_MODE_FREEFORM
+                    && (isNOrGreater(root) || isPreNResizeable(root))) {
+                // We're launching a pre-N and post-D activity that supports resizing, or a post-N
+                // activity. They can handle freeform nicely so launch them in freeform.
+                // Use undefined because we know we're in a freeform display.
+                launchMode = WINDOWING_MODE_UNDEFINED;
+                if (DEBUG) appendLog("should-be-freeform");
+            }
+        } else {
+            if (DEBUG) appendLog("non-freeform-display");
+        }
+        // If launch mode matches display windowing mode, let it inherit from display.
+        outParams.mWindowingMode = launchMode == display.getWindowingMode()
+                ? WINDOWING_MODE_UNDEFINED : launchMode;
+
+        // STEP 3: Determine final launch bounds based on resolved windowing mode and activity
+        // requested orientation. We set bounds to empty for fullscreen mode and keep bounds as is
+        // for all other windowing modes that's not freeform mode. One can read comments in
+        // relevant methods to further understand this step.
+        //
+        // We skip making adjustments if the params are fully resolved from previous results and
+        // trust that they are valid.
+        if (!fullyResolvedCurrentParam) {
+            final int resolvedMode = (launchMode != WINDOWING_MODE_UNDEFINED) ? launchMode
+                    : display.getWindowingMode();
+            if (source != null && source.inFreeformWindowingMode()
+                    && resolvedMode == WINDOWING_MODE_FREEFORM
+                    && outParams.mBounds.isEmpty()
+                    && source.getDisplayId() == display.mDisplayId) {
+                // Set bounds to be not very far from source activity.
+                cascadeBounds(source.getBounds(), display, outParams.mBounds);
+            }
+            getTaskBounds(root, display, layout, resolvedMode, hasInitialBounds, outParams.mBounds);
+        }
+
+        return RESULT_CONTINUE;
+    }
+
+    private int getPreferredLaunchDisplay(@Nullable TaskRecord task,
+            @Nullable ActivityOptions options, ActivityRecord source, LaunchParams currentParams) {
+        int displayId = INVALID_DISPLAY;
+        final int optionLaunchId = options != null ? options.getLaunchDisplayId() : INVALID_DISPLAY;
+        if (optionLaunchId != INVALID_DISPLAY) {
+            if (DEBUG) appendLog("display-from-option=" + optionLaunchId);
+            displayId = optionLaunchId;
+        }
+
+        if (displayId == INVALID_DISPLAY && source != null) {
+            final int sourceDisplayId = source.getDisplayId();
+            if (DEBUG) appendLog("display-from-source=" + sourceDisplayId);
+            displayId = sourceDisplayId;
+        }
+
+        ActivityStack stack =
+                (displayId == INVALID_DISPLAY && task != null) ? task.getStack() : null;
+        if (stack != null) {
+            if (DEBUG) appendLog("display-from-task=" + stack.mDisplayId);
+            displayId = stack.mDisplayId;
+        }
+
+        if (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) == null) {
+            displayId = INVALID_DISPLAY;
+        }
+        displayId = (displayId == INVALID_DISPLAY) ? currentParams.mPreferredDisplayId : displayId;
+
+        displayId = (displayId == INVALID_DISPLAY) ? DEFAULT_DISPLAY : displayId;
+
+        return displayId;
+    }
+
+    private boolean canApplyFreeformWindowPolicy(@NonNull ActivityDisplay display, int launchMode) {
+        return mSupervisor.mService.mSupportsFreeformWindowManagement
+                && (display.inFreeformWindowingMode() || launchMode == WINDOWING_MODE_FREEFORM);
+    }
+
+    private boolean canApplyPipWindowPolicy(int launchMode) {
+        return mSupervisor.mService.mSupportsPictureInPicture
+                && launchMode == WINDOWING_MODE_PINNED;
+    }
+
+    private void getLayoutBounds(@NonNull ActivityDisplay display, @NonNull ActivityRecord root,
+            @NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect outBounds) {
+        final int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
+        final int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+        if (!windowLayout.hasSpecifiedSize() && verticalGravity == 0 && horizontalGravity == 0) {
+            outBounds.setEmpty();
+            return;
+        }
+
+        final Rect bounds = display.getBounds();
+        final int defaultWidth = bounds.width();
+        final int defaultHeight = bounds.height();
+
+        int width;
+        int height;
+        if (!windowLayout.hasSpecifiedSize()) {
+            outBounds.setEmpty();
+            getTaskBounds(root, display, windowLayout, WINDOWING_MODE_FREEFORM,
+                    /* hasInitialBounds */ false, outBounds);
+            width = outBounds.width();
+            height = outBounds.height();
+        } else {
+            width = defaultWidth;
+            if (windowLayout.width > 0 && windowLayout.width < defaultWidth) {
+                width = windowLayout.width;
+            } else if (windowLayout.widthFraction > 0 && windowLayout.widthFraction < 1.0f) {
+                width = (int) (width * windowLayout.widthFraction);
+            }
+
+            height = defaultHeight;
+            if (windowLayout.height > 0 && windowLayout.height < defaultHeight) {
+                height = windowLayout.height;
+            } else if (windowLayout.heightFraction > 0 && windowLayout.heightFraction < 1.0f) {
+                height = (int) (height * windowLayout.heightFraction);
+            }
+        }
+
+        final float fractionOfHorizontalOffset;
+        switch (horizontalGravity) {
+            case Gravity.LEFT:
+                fractionOfHorizontalOffset = 0f;
+                break;
+            case Gravity.RIGHT:
+                fractionOfHorizontalOffset = 1f;
+                break;
+            default:
+                fractionOfHorizontalOffset = 0.5f;
+        }
+
+        final float fractionOfVerticalOffset;
+        switch (verticalGravity) {
+            case Gravity.TOP:
+                fractionOfVerticalOffset = 0f;
+                break;
+            case Gravity.BOTTOM:
+                fractionOfVerticalOffset = 1f;
+                break;
+            default:
+                fractionOfVerticalOffset = 0.5f;
+        }
+
+        outBounds.set(0, 0, width, height);
+        final int xOffset = (int) (fractionOfHorizontalOffset * (defaultWidth - width));
+        final int yOffset = (int) (fractionOfVerticalOffset * (defaultHeight - height));
+        outBounds.offset(xOffset, yOffset);
+    }
+
+    /**
+     * Returns if task is forced to maximize.
+     *
+     * There are several cases where we force a task to maximize:
+     * 1) Root activity is targeting pre-Donut, which by default can't handle multiple screen
+     *    densities, so resizing will likely cause issues;
+     * 2) Root activity doesn't declare any flag that it supports any screen density, so resizing
+     *    may also cause issues;
+     * 3) Root activity is not resizeable, for which we shouldn't allow user resize it.
+     *
+     * @param root the root activity to check against.
+     * @return {@code true} if it should be forced to maximize; {@code false} otherwise.
+     */
+    private boolean isTaskForcedMaximized(@NonNull ActivityRecord root) {
+        if (root.appInfo.targetSdkVersion < Build.VERSION_CODES.DONUT
+                || (root.appInfo.flags & SUPPORTS_SCREEN_RESIZEABLE_MASK) == 0) {
+            return true;
+        }
+
+        return !root.isResizeable();
+    }
+
+    private boolean isNOrGreater(@NonNull ActivityRecord root) {
+        return root.appInfo.targetSdkVersion >= Build.VERSION_CODES.N;
+    }
+
+    /**
+     * Resolves activity requested orientation to 4 categories:
+     * 1) {@link ActivityInfo#SCREEN_ORIENTATION_LOCKED} indicating app wants to lock down
+     *    orientation;
+     * 2) {@link ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE} indicating app wants to be in landscape;
+     * 3) {@link ActivityInfo#SCREEN_ORIENTATION_PORTRAIT} indicating app wants to be in portrait;
+     * 4) {@link ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} indicating app can handle any
+     *    orientation.
+     *
+     * @param activity the activity to check
+     * @return corresponding resolved orientation value.
+     */
+    private int resolveOrientation(@NonNull ActivityRecord activity) {
+        int orientation = activity.info.screenOrientation;
+        switch (orientation) {
+            case SCREEN_ORIENTATION_NOSENSOR:
+            case SCREEN_ORIENTATION_LOCKED:
+                orientation = SCREEN_ORIENTATION_LOCKED;
+                break;
+            case SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+            case SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+            case SCREEN_ORIENTATION_USER_LANDSCAPE:
+            case SCREEN_ORIENTATION_LANDSCAPE:
+                if (DEBUG) appendLog("activity-requested-landscape");
+                orientation = SCREEN_ORIENTATION_LANDSCAPE;
+                break;
+            case SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+            case SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+            case SCREEN_ORIENTATION_USER_PORTRAIT:
+            case SCREEN_ORIENTATION_PORTRAIT:
+                if (DEBUG) appendLog("activity-requested-portrait");
+                orientation = SCREEN_ORIENTATION_PORTRAIT;
+                break;
+            default:
+                orientation = SCREEN_ORIENTATION_UNSPECIFIED;
+        }
+
+        return orientation;
+    }
+
+    private boolean isPreNResizeable(ActivityRecord root) {
+        return root.appInfo.targetSdkVersion < Build.VERSION_CODES.N && root.isResizeable();
+    }
+
+    private void cascadeBounds(@NonNull Rect srcBounds, @NonNull ActivityDisplay display,
+            @NonNull Rect outBounds) {
+        outBounds.set(srcBounds);
+        float density = (float) display.getConfiguration().densityDpi / DENSITY_DEFAULT;
+        final int defaultOffset = (int) (CASCADING_OFFSET_DP * density + 0.5f);
+
+        display.getBounds(mTmpBounds);
+        final int dx = Math.min(defaultOffset, Math.max(0, mTmpBounds.right - srcBounds.right));
+        final int dy = Math.min(defaultOffset, Math.max(0, mTmpBounds.bottom - srcBounds.bottom));
+        outBounds.offset(dx, dy);
+    }
+
+    private void getTaskBounds(@NonNull ActivityRecord root, @NonNull ActivityDisplay display,
+            @NonNull ActivityInfo.WindowLayout layout, int resolvedMode, boolean hasInitialBounds,
+            @NonNull Rect inOutBounds) {
+        if (resolvedMode == WINDOWING_MODE_FULLSCREEN) {
+            // We don't handle letterboxing here. Letterboxing will be handled by valid checks
+            // later.
+            inOutBounds.setEmpty();
+            if (DEBUG) appendLog("maximized-bounds");
+            return;
+        }
+
+        if (resolvedMode != WINDOWING_MODE_FREEFORM) {
+            // We don't apply freeform bounds adjustment to other windowing modes.
+            if (DEBUG) {
+                appendLog("skip-bounds-" + WindowConfiguration.windowingModeToString(resolvedMode));
+            }
+            return;
+        }
+
+        final int orientation = resolveOrientation(root, display, inOutBounds);
+        if (orientation != SCREEN_ORIENTATION_PORTRAIT
+                && orientation != SCREEN_ORIENTATION_LANDSCAPE) {
+            throw new IllegalStateException(
+                    "Orientation must be one of portrait or landscape, but it's "
+                    + ActivityInfo.screenOrientationToString(orientation));
+        }
+
+        // First we get the default size we want.
+        getDefaultFreeformSize(display, layout, orientation, mTmpBounds);
+        if (hasInitialBounds || sizeMatches(inOutBounds, mTmpBounds)) {
+            // We're here because either input parameters specified initial bounds, or the suggested
+            // bounds have the same size of the default freeform size. We should use the suggested
+            // bounds if possible -- so if app can handle the orientation we just use it, and if not
+            // we transpose the suggested bounds in-place.
+            if (orientation == orientationFromBounds(inOutBounds)) {
+                if (DEBUG) appendLog("freeform-size-orientation-match=" + inOutBounds);
+            } else {
+                // Meh, orientation doesn't match. Let's rotate inOutBounds in-place.
+                centerBounds(display, inOutBounds.height(), inOutBounds.width(), inOutBounds);
+                if (DEBUG) appendLog("freeform-orientation-mismatch=" + inOutBounds);
+            }
+        } else {
+            // We are here either because there is no suggested bounds, or the suggested bounds is
+            // a cascade from source activity. We should use the default freeform size and center it
+            // to the center of suggested bounds (or the display if no suggested bounds). The
+            // default size might be too big to center to source activity bounds in display, so we
+            // may need to move it back to the display.
+            centerBounds(display, mTmpBounds.width(), mTmpBounds.height(), inOutBounds);
+            adjustBoundsToFitInDisplay(display, inOutBounds);
+            if (DEBUG) appendLog("freeform-size-mismatch=" + inOutBounds);
+        }
+
+        // Lastly we adjust bounds to avoid conflicts with other tasks as much as possible.
+        adjustBoundsToAvoidConflict(display, inOutBounds);
+    }
+
+    private int resolveOrientation(@NonNull ActivityRecord root, @NonNull ActivityDisplay display,
+            @NonNull Rect bounds) {
+        int orientation = resolveOrientation(root);
+
+        if (orientation == SCREEN_ORIENTATION_LOCKED) {
+            orientation = bounds.isEmpty() ? display.getConfiguration().orientation
+                    : orientationFromBounds(bounds);
+            if (DEBUG) {
+                appendLog(bounds.isEmpty() ? "locked-orientation-from-display=" + orientation
+                        : "locked-orientation-from-bounds=" + bounds);
+            }
+        }
+
+        if (orientation == SCREEN_ORIENTATION_UNSPECIFIED) {
+            orientation = bounds.isEmpty() ? SCREEN_ORIENTATION_PORTRAIT
+                    : orientationFromBounds(bounds);
+            if (DEBUG) {
+                appendLog(bounds.isEmpty() ? "default-portrait"
+                        : "orientation-from-bounds=" + bounds);
+            }
+        }
+
+        return orientation;
+    }
+
+    private void getDefaultFreeformSize(@NonNull ActivityDisplay display,
+            @NonNull ActivityInfo.WindowLayout layout, int orientation, @NonNull Rect bounds) {
+        // Default size, which is letterboxing/pillarboxing in display. That's to say the large
+        // dimension of default size is the small dimension of display size, and the small dimension
+        // of default size is calculated to keep the same aspect ratio as the display's.
+        Rect displayBounds = display.getBounds();
+        final int portraitHeight = Math.min(displayBounds.width(), displayBounds.height());
+        final int otherDimension = Math.max(displayBounds.width(), displayBounds.height());
+        final int portraitWidth = (portraitHeight * portraitHeight) / otherDimension;
+        final int defaultWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitHeight
+                : portraitWidth;
+        final int defaultHeight = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitWidth
+                : portraitHeight;
+
+        // Get window size based on Nexus 5x screen, we assume that this is enough to show content
+        // of activities.
+        final float density = (float) display.getConfiguration().densityDpi / DENSITY_DEFAULT;
+        final int phonePortraitWidth = (int) (DEFAULT_PORTRAIT_PHONE_WIDTH_DP * density + 0.5f);
+        final int phonePortraitHeight = (int) (DEFAULT_PORTRAIT_PHONE_HEIGHT_DP * density + 0.5f);
+        final int phoneWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? phonePortraitHeight
+                : phonePortraitWidth;
+        final int phoneHeight = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? phonePortraitWidth
+                : phonePortraitHeight;
+
+        // Minimum layout requirements.
+        final int layoutMinWidth = (layout == null) ? -1 : layout.minWidth;
+        final int layoutMinHeight = (layout == null) ? -1 : layout.minHeight;
+
+        // Final result.
+        final int width = Math.min(defaultWidth, Math.max(phoneWidth, layoutMinWidth));
+        final int height = Math.min(defaultHeight, Math.max(phoneHeight, layoutMinHeight));
+
+        bounds.set(0, 0, width, height);
+    }
+
+    /**
+     * Gets centered bounds of width x height. If inOutBounds is not empty, the result bounds
+     * centers at its center or display's center if inOutBounds is empty.
+     */
+    private void centerBounds(@NonNull ActivityDisplay display, int width, int height,
+            @NonNull Rect inOutBounds) {
+        if (inOutBounds.isEmpty()) {
+            display.getBounds(inOutBounds);
+        }
+        final int left = inOutBounds.centerX() - width / 2;
+        final int top = inOutBounds.centerY() - height / 2;
+        inOutBounds.set(left, top, left + width, top + height);
+    }
+
+    private void adjustBoundsToFitInDisplay(@NonNull ActivityDisplay display,
+            @NonNull Rect inOutBounds) {
+        final Rect displayBounds = display.getBounds();
+
+        if (displayBounds.width() < inOutBounds.width()
+                || displayBounds.height() < inOutBounds.height()) {
+            // There is no way for us to fit the bounds in the display without changing width
+            // or height. Don't even try it.
+            return;
+        }
+
+        final int dx;
+        if (inOutBounds.right > displayBounds.right) {
+            // Right edge is out of display.
+            dx = displayBounds.right - inOutBounds.right;
+        } else if (inOutBounds.left < displayBounds.left) {
+            // Left edge is out of display.
+            dx = displayBounds.left - inOutBounds.left;
+        } else {
+            // Vertical edges are all in display.
+            dx = 0;
+        }
+
+        final int dy;
+        if (inOutBounds.top < displayBounds.top) {
+            // Top edge is out of display.
+            dy = displayBounds.top - inOutBounds.top;
+        } else if (inOutBounds.bottom > displayBounds.bottom) {
+            // Bottom edge is out of display.
+            dy = displayBounds.bottom - inOutBounds.bottom;
+        } else {
+            // Horizontal edges are all in display.
+            dy = 0;
+        }
+        inOutBounds.offset(dx, dy);
+    }
+
+    /**
+     * Adjusts input bounds to avoid conflict with existing tasks in the display.
+     *
+     * If the input bounds conflict with existing tasks, this method scans the bounds in a series of
+     * directions to find a location where the we can put the bounds in display without conflict
+     * with any other tasks.
+     *
+     * It doesn't try to adjust bounds that's not fully in the given display.
+     *
+     * @param display the display which tasks are to check
+     * @param inOutBounds the bounds used to input initial bounds and output result bounds
+     */
+    private void adjustBoundsToAvoidConflict(@NonNull ActivityDisplay display,
+            @NonNull Rect inOutBounds) {
+        final Rect displayBounds = display.getBounds();
+        if (!displayBounds.contains(inOutBounds)) {
+            // The initial bounds are already out of display. The scanning algorithm below doesn't
+            // work so well with them.
+            return;
+        }
+
+        final List<TaskRecord> tasksToCheck = new ArrayList<>();
+        for (int i = 0; i < display.getChildCount(); ++i) {
+            ActivityStack<?> stack = display.getChildAt(i);
+            if (!stack.inFreeformWindowingMode()) {
+                continue;
+            }
+
+            for (int j = 0; j < stack.getChildCount(); ++j) {
+                tasksToCheck.add(stack.getChildAt(j));
+            }
+        }
+
+        if (!boundsConflict(tasksToCheck, inOutBounds)) {
+            // Current proposal doesn't conflict with any task. Early return to avoid unnecessary
+            // calculation.
+            return;
+        }
+
+        calculateCandidateShiftDirections(displayBounds, inOutBounds);
+        for (int direction : mTmpDirections) {
+            if (direction == Gravity.NO_GRAVITY) {
+                // We exhausted candidate directions, give up.
+                break;
+            }
+
+            mTmpBounds.set(inOutBounds);
+            while (boundsConflict(tasksToCheck, mTmpBounds) && displayBounds.contains(mTmpBounds)) {
+                shiftBounds(direction, displayBounds, mTmpBounds);
+            }
+
+            if (!boundsConflict(tasksToCheck, mTmpBounds) && displayBounds.contains(mTmpBounds)) {
+                // Found a candidate. Just use this.
+                inOutBounds.set(mTmpBounds);
+                if (DEBUG) appendLog("avoid-bounds-conflict=" + inOutBounds);
+                return;
+            }
+
+            // Didn't find a conflict free bounds here. Try the next candidate direction.
+        }
+
+        // We failed to find a conflict free location. Just keep the original result.
+    }
+
+    /**
+     * Determines scanning directions and their priorities to avoid bounds conflict.
+     *
+     * @param availableBounds bounds that the result must be in
+     * @param initialBounds initial bounds when start scanning
+     */
+    private void calculateCandidateShiftDirections(@NonNull Rect availableBounds,
+            @NonNull Rect initialBounds) {
+        for (int i = 0; i < mTmpDirections.length; ++i) {
+            mTmpDirections[i] = Gravity.NO_GRAVITY;
+        }
+
+        final int oneThirdWidth = (2 * availableBounds.left + availableBounds.right) / 3;
+        final int twoThirdWidth = (availableBounds.left + 2 * availableBounds.right) / 3;
+        final int centerX = initialBounds.centerX();
+        if (centerX < oneThirdWidth) {
+            // Too close to left, just scan to the right.
+            mTmpDirections[0] = Gravity.RIGHT;
+            return;
+        } else if (centerX > twoThirdWidth) {
+            // Too close to right, just scan to the left.
+            mTmpDirections[0] = Gravity.LEFT;
+            return;
+        }
+
+        final int oneThirdHeight = (2 * availableBounds.top + availableBounds.bottom) / 3;
+        final int twoThirdHeight = (availableBounds.top + 2 * availableBounds.bottom) / 3;
+        final int centerY = initialBounds.centerY();
+        if (centerY < oneThirdHeight || centerY > twoThirdHeight) {
+            // Too close to top or bottom boundary and we're in the middle horizontally, scan
+            // horizontally in both directions.
+            mTmpDirections[0] = Gravity.RIGHT;
+            mTmpDirections[1] = Gravity.LEFT;
+            return;
+        }
+
+        // We're in the center region both horizontally and vertically. Scan in both directions of
+        // primary diagonal.
+        mTmpDirections[0] = Gravity.BOTTOM | Gravity.RIGHT;
+        mTmpDirections[1] = Gravity.TOP | Gravity.LEFT;
+    }
+
+    private boolean boundsConflict(@NonNull List<TaskRecord> tasks, @NonNull Rect bounds) {
+        for (TaskRecord task : tasks) {
+            final Rect taskBounds = task.getBounds();
+            final boolean leftClose = Math.abs(taskBounds.left - bounds.left)
+                    < BOUNDS_CONFLICT_THRESHOLD;
+            final boolean topClose = Math.abs(taskBounds.top - bounds.top)
+                    < BOUNDS_CONFLICT_THRESHOLD;
+            final boolean rightClose = Math.abs(taskBounds.right - bounds.right)
+                    < BOUNDS_CONFLICT_THRESHOLD;
+            final boolean bottomClose = Math.abs(taskBounds.bottom - bounds.bottom)
+                    < BOUNDS_CONFLICT_THRESHOLD;
+
+            if ((leftClose && topClose) || (leftClose && bottomClose) || (rightClose && topClose)
+                    || (rightClose && bottomClose)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private void shiftBounds(int direction, @NonNull Rect availableRect,
+            @NonNull Rect inOutBounds) {
+        final int horizontalOffset;
+        switch (direction & Gravity.HORIZONTAL_GRAVITY_MASK) {
+            case Gravity.LEFT:
+                horizontalOffset = -Math.max(MINIMAL_STEP,
+                        availableRect.width() / STEP_DENOMINATOR);
+                break;
+            case Gravity.RIGHT:
+                horizontalOffset = Math.max(MINIMAL_STEP, availableRect.width() / STEP_DENOMINATOR);
+                break;
+            default:
+                horizontalOffset = 0;
+        }
+
+        final int verticalOffset;
+        switch (direction & Gravity.VERTICAL_GRAVITY_MASK) {
+            case Gravity.TOP:
+                verticalOffset = -Math.max(MINIMAL_STEP, availableRect.height() / STEP_DENOMINATOR);
+                break;
+            case Gravity.BOTTOM:
+                verticalOffset = Math.max(MINIMAL_STEP, availableRect.height() / STEP_DENOMINATOR);
+                break;
+            default:
+                verticalOffset = 0;
+        }
+
+        inOutBounds.offset(horizontalOffset, verticalOffset);
+    }
+
+    private void initLogBuilder(TaskRecord task, ActivityRecord activity) {
+        if (DEBUG) {
+            mLogBuilder = new StringBuilder("TaskLaunchParamsModifier:task=" + task
+                    + " activity=" + activity);
+        }
+    }
+
+    private void appendLog(String log) {
+        if (DEBUG) mLogBuilder.append(" ").append(log);
+    }
+
+    private void outputLog() {
+        if (DEBUG) Slog.d(TAG, mLogBuilder.toString());
+    }
+
+    private static int orientationFromBounds(Rect bounds) {
+        return bounds.width() > bounds.height() ? SCREEN_ORIENTATION_LANDSCAPE
+                : SCREEN_ORIENTATION_PORTRAIT;
+    }
+
+    private static boolean sizeMatches(Rect left, Rect right) {
+        return (Math.abs(right.width() - left.width()) < EPSILON)
+                && (Math.abs(right.height() - left.height()) < EPSILON);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
new file mode 100644
index 0000000..9705d42
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2014 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.server.wm;
+
+import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+
+import android.annotation.NonNull;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Debug;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.SystemClock;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Persister that saves recent tasks into disk.
+ */
+public class TaskPersister implements PersisterQueue.Listener {
+    static final String TAG = "TaskPersister";
+    static final boolean DEBUG = false;
+    static final String IMAGE_EXTENSION = ".png";
+
+    private static final String TASKS_DIRNAME = "recent_tasks";
+    private static final String TASK_FILENAME_SUFFIX = "_task.xml";
+    private static final String IMAGES_DIRNAME = "recent_images";
+    private static final String PERSISTED_TASK_IDS_FILENAME = "persisted_taskIds.txt";
+
+    private static final String TAG_TASK = "task";
+
+    private final ActivityTaskManagerService mService;
+    private final ActivityStackSupervisor mStackSupervisor;
+    private final RecentTasks mRecentTasks;
+    private final SparseArray<SparseBooleanArray> mTaskIdsInFile = new SparseArray<>();
+    private final File mTaskIdsDir;
+    // To lock file operations in TaskPersister
+    private final Object mIoLock = new Object();
+    private final PersisterQueue mPersisterQueue;
+
+    private final ArraySet<Integer> mTmpTaskIds = new ArraySet<>();
+
+    TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
+            ActivityTaskManagerService service, RecentTasks recentTasks) {
+
+        final File legacyImagesDir = new File(systemDir, IMAGES_DIRNAME);
+        if (legacyImagesDir.exists()) {
+            if (!FileUtils.deleteContents(legacyImagesDir) || !legacyImagesDir.delete()) {
+                Slog.i(TAG, "Failure deleting legacy images directory: " + legacyImagesDir);
+            }
+        }
+
+        final File legacyTasksDir = new File(systemDir, TASKS_DIRNAME);
+        if (legacyTasksDir.exists()) {
+            if (!FileUtils.deleteContents(legacyTasksDir) || !legacyTasksDir.delete()) {
+                Slog.i(TAG, "Failure deleting legacy tasks directory: " + legacyTasksDir);
+            }
+        }
+
+        mTaskIdsDir = new File(Environment.getDataDirectory(), "system_de");
+        mStackSupervisor = stackSupervisor;
+        mService = service;
+        mRecentTasks = recentTasks;
+        mPersisterQueue = new PersisterQueue();
+        mPersisterQueue.addListener(this);
+    }
+
+    @VisibleForTesting
+    TaskPersister(File workingDir) {
+        mTaskIdsDir = workingDir;
+        mStackSupervisor = null;
+        mService = null;
+        mRecentTasks = null;
+        mPersisterQueue = new PersisterQueue();
+        mPersisterQueue.addListener(this);
+    }
+
+    void onSystemReady() {
+        mPersisterQueue.startPersisting();
+    }
+
+    private void removeThumbnails(TaskRecord task) {
+        mPersisterQueue.removeItems(
+                item -> {
+                    File file = new File(item.mFilePath);
+                    return file.getName().startsWith(Integer.toString(task.taskId));
+                },
+                ImageWriteQueueItem.class);
+    }
+
+    @NonNull
+    SparseBooleanArray loadPersistedTaskIdsForUser(int userId) {
+        if (mTaskIdsInFile.get(userId) != null) {
+            return mTaskIdsInFile.get(userId).clone();
+        }
+        final SparseBooleanArray persistedTaskIds = new SparseBooleanArray();
+        synchronized (mIoLock) {
+            BufferedReader reader = null;
+            String line;
+            try {
+                reader = new BufferedReader(new FileReader(getUserPersistedTaskIdsFile(userId)));
+                while ((line = reader.readLine()) != null) {
+                    for (String taskIdString : line.split("\\s+")) {
+                        int id = Integer.parseInt(taskIdString);
+                        persistedTaskIds.put(id, true);
+                    }
+                }
+            } catch (FileNotFoundException e) {
+                // File doesn't exist. Ignore.
+            } catch (Exception e) {
+                Slog.e(TAG, "Error while reading taskIds file for user " + userId, e);
+            } finally {
+                IoUtils.closeQuietly(reader);
+            }
+        }
+        mTaskIdsInFile.put(userId, persistedTaskIds);
+        return persistedTaskIds.clone();
+    }
+
+
+    @VisibleForTesting
+    void writePersistedTaskIdsForUser(@NonNull SparseBooleanArray taskIds, int userId) {
+        if (userId < 0) {
+            return;
+        }
+        final File persistedTaskIdsFile = getUserPersistedTaskIdsFile(userId);
+        synchronized (mIoLock) {
+            BufferedWriter writer = null;
+            try {
+                writer = new BufferedWriter(new FileWriter(persistedTaskIdsFile));
+                for (int i = 0; i < taskIds.size(); i++) {
+                    if (taskIds.valueAt(i)) {
+                        writer.write(String.valueOf(taskIds.keyAt(i)));
+                        writer.newLine();
+                    }
+                }
+            } catch (Exception e) {
+                Slog.e(TAG, "Error while writing taskIds file for user " + userId, e);
+            } finally {
+                IoUtils.closeQuietly(writer);
+            }
+        }
+    }
+
+    void unloadUserDataFromMemory(int userId) {
+        mTaskIdsInFile.delete(userId);
+    }
+
+    void wakeup(TaskRecord task, boolean flush) {
+        synchronized (mPersisterQueue) {
+            if (task != null) {
+                final TaskWriteQueueItem item = mPersisterQueue.findLastItem(
+                        queueItem -> task == queueItem.mTask, TaskWriteQueueItem.class);
+                if (item != null && !task.inRecents) {
+                    removeThumbnails(task);
+                }
+
+                if (item == null && task.isPersistable) {
+                    mPersisterQueue.addItem(new TaskWriteQueueItem(task, mService), flush);
+                }
+            } else {
+                // Dummy. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is
+                // notified.
+                mPersisterQueue.addItem(PersisterQueue.EMPTY_ITEM, flush);
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " Callers="
+                        + Debug.getCallers(4));
+            }
+        }
+
+        mPersisterQueue.yieldIfQueueTooDeep();
+    }
+
+    void flush() {
+        mPersisterQueue.flush();
+    }
+
+    void saveImage(Bitmap image, String filePath) {
+        synchronized (mPersisterQueue) {
+            final ImageWriteQueueItem item = mPersisterQueue.findLastItem(
+                    queueItem -> queueItem.mFilePath.equals(filePath), ImageWriteQueueItem.class);
+            if (item != null) {
+                // replace the Bitmap with the new one.
+                item.mImage = image;
+            } else {
+                mPersisterQueue.addItem(new ImageWriteQueueItem(filePath, image),
+                        /* flush */ false);
+            }
+            if (DEBUG) Slog.d(TAG, "saveImage: filePath=" + filePath + " now=" +
+                    SystemClock.uptimeMillis() + " Callers=" + Debug.getCallers(4));
+        }
+
+        mPersisterQueue.yieldIfQueueTooDeep();
+    }
+
+    Bitmap getTaskDescriptionIcon(String filePath) {
+        // See if it is in the write queue
+        final Bitmap icon = getImageFromWriteQueue(filePath);
+        if (icon != null) {
+            return icon;
+        }
+        return restoreImage(filePath);
+    }
+
+    private Bitmap getImageFromWriteQueue(String filePath) {
+        final ImageWriteQueueItem item = mPersisterQueue.findLastItem(
+                queueItem -> queueItem.mFilePath.equals(filePath), ImageWriteQueueItem.class);
+        return item != null ? item.mImage : null;
+    }
+
+    private String fileToString(File file) {
+        final String newline = System.lineSeparator();
+        try {
+            BufferedReader reader = new BufferedReader(new FileReader(file));
+            StringBuffer sb = new StringBuffer((int) file.length() * 2);
+            String line;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line + newline);
+            }
+            reader.close();
+            return sb.toString();
+        } catch (IOException ioe) {
+            Slog.e(TAG, "Couldn't read file " + file.getName());
+            return null;
+        }
+    }
+
+    private TaskRecord taskIdToTask(int taskId, ArrayList<TaskRecord> tasks) {
+        if (taskId < 0) {
+            return null;
+        }
+        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = tasks.get(taskNdx);
+            if (task.taskId == taskId) {
+                return task;
+            }
+        }
+        Slog.e(TAG, "Restore affiliation error looking for taskId=" + taskId);
+        return null;
+    }
+
+    List<TaskRecord> restoreTasksForUserLocked(final int userId, SparseBooleanArray preaddedTasks) {
+        final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
+        ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
+
+        File userTasksDir = getUserTasksDir(userId);
+
+        File[] recentFiles = userTasksDir.listFiles();
+        if (recentFiles == null) {
+            Slog.e(TAG, "restoreTasksForUserLocked: Unable to list files from " + userTasksDir);
+            return tasks;
+        }
+
+        for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
+            File taskFile = recentFiles[taskNdx];
+            if (DEBUG) {
+                Slog.d(TAG, "restoreTasksForUserLocked: userId=" + userId
+                        + ", taskFile=" + taskFile.getName());
+            }
+
+            if (!taskFile.getName().endsWith(TASK_FILENAME_SUFFIX)) {
+                continue;
+            }
+            try {
+                final int taskId = Integer.parseInt(taskFile.getName().substring(
+                        0 /* beginIndex */,
+                        taskFile.getName().length() - TASK_FILENAME_SUFFIX.length()));
+                if (preaddedTasks.get(taskId, false)) {
+                    Slog.w(TAG, "Task #" + taskId +
+                            " has already been created so we don't restore again");
+                    continue;
+                }
+            } catch (NumberFormatException e) {
+                Slog.w(TAG, "Unexpected task file name", e);
+                continue;
+            }
+
+            BufferedReader reader = null;
+            boolean deleteFile = false;
+            try {
+                reader = new BufferedReader(new FileReader(taskFile));
+                final XmlPullParser in = Xml.newPullParser();
+                in.setInput(reader);
+
+                int event;
+                while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+                        event != XmlPullParser.END_TAG) {
+                    final String name = in.getName();
+                    if (event == XmlPullParser.START_TAG) {
+                        if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: START_TAG name=" + name);
+                        if (TAG_TASK.equals(name)) {
+                            final TaskRecord task = TaskRecord.restoreFromXml(in, mStackSupervisor);
+                            if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: restored task="
+                                    + task);
+                            if (task != null) {
+                                // XXX Don't add to write queue... there is no reason to write
+                                // out the stuff we just read, if we don't write it we will
+                                // read the same thing again.
+                                // mWriteQueue.add(new TaskWriteQueueItem(task));
+
+                                final int taskId = task.taskId;
+                                if (mStackSupervisor.anyTaskForIdLocked(taskId,
+                                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
+                                    // Should not happen.
+                                    Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
+                                } else if (userId != task.userId) {
+                                    // Should not happen.
+                                    Slog.wtf(TAG, "Task with userId " + task.userId + " found in "
+                                            + userTasksDir.getAbsolutePath());
+                                } else {
+                                    // Looks fine.
+                                    mStackSupervisor.setNextTaskIdForUserLocked(taskId, userId);
+                                    task.isPersistable = true;
+                                    tasks.add(task);
+                                    recoveredTaskIds.add(taskId);
+                                }
+                            } else {
+                                Slog.e(TAG, "restoreTasksForUserLocked: Unable to restore taskFile="
+                                        + taskFile + ": " + fileToString(taskFile));
+                            }
+                        } else {
+                            Slog.wtf(TAG, "restoreTasksForUserLocked: Unknown xml event=" + event
+                                    + " name=" + name);
+                        }
+                    }
+                    XmlUtils.skipCurrentTag(in);
+                }
+            } catch (Exception e) {
+                Slog.wtf(TAG, "Unable to parse " + taskFile + ". Error ", e);
+                Slog.e(TAG, "Failing file: " + fileToString(taskFile));
+                deleteFile = true;
+            } finally {
+                IoUtils.closeQuietly(reader);
+                if (deleteFile) {
+                    if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
+                    taskFile.delete();
+                }
+            }
+        }
+
+        if (!DEBUG) {
+            removeObsoleteFiles(recoveredTaskIds, userTasksDir.listFiles());
+        }
+
+        // Fix up task affiliation from taskIds
+        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = tasks.get(taskNdx);
+            task.setPrevAffiliate(taskIdToTask(task.mPrevAffiliateTaskId, tasks));
+            task.setNextAffiliate(taskIdToTask(task.mNextAffiliateTaskId, tasks));
+        }
+
+        Collections.sort(tasks, new Comparator<TaskRecord>() {
+            @Override
+            public int compare(TaskRecord lhs, TaskRecord rhs) {
+                final long diff = rhs.mLastTimeMoved - lhs.mLastTimeMoved;
+                if (diff < 0) {
+                    return -1;
+                } else if (diff > 0) {
+                    return +1;
+                } else {
+                    return 0;
+                }
+            }
+        });
+        return tasks;
+    }
+
+    @Override
+    public void onPreProcessItem(boolean queueEmpty) {
+        // We can't lock mService while locking the queue, but we don't want to
+        // call removeObsoleteFiles before every item, only the last time
+        // before going to sleep. The risk is that we call removeObsoleteFiles()
+        // successively.
+        if (queueEmpty) {
+            if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
+            mTmpTaskIds.clear();
+            synchronized (mService.mGlobalLock) {
+                if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
+                mRecentTasks.getPersistableTaskIds(mTmpTaskIds);
+                mService.mWindowManager.removeObsoleteTaskFiles(mTmpTaskIds,
+                        mRecentTasks.usersWithRecentsLoadedLocked());
+            }
+            removeObsoleteFiles(mTmpTaskIds);
+        }
+        writeTaskIdsFiles();
+    }
+
+    private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
+        if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: persistentTaskIds=" + persistentTaskIds +
+                " files=" + files);
+        if (files == null) {
+            Slog.e(TAG, "File error accessing recents directory (directory doesn't exist?).");
+            return;
+        }
+        for (int fileNdx = 0; fileNdx < files.length; ++fileNdx) {
+            File file = files[fileNdx];
+            String filename = file.getName();
+            final int taskIdEnd = filename.indexOf('_');
+            if (taskIdEnd > 0) {
+                final int taskId;
+                try {
+                    taskId = Integer.parseInt(filename.substring(0, taskIdEnd));
+                    if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: Found taskId=" + taskId);
+                } catch (Exception e) {
+                    Slog.wtf(TAG, "removeObsoleteFiles: Can't parse file=" + file.getName());
+                    file.delete();
+                    continue;
+                }
+                if (!persistentTaskIds.contains(taskId)) {
+                    if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: deleting file=" + file.getName());
+                    file.delete();
+                }
+            }
+        }
+    }
+
+    private void writeTaskIdsFiles() {
+        SparseArray<SparseBooleanArray> changedTaskIdsPerUser = new SparseArray<>();
+        synchronized (mService.mGlobalLock) {
+            for (int userId : mRecentTasks.usersWithRecentsLoadedLocked()) {
+                SparseBooleanArray taskIdsToSave = mRecentTasks.getTaskIdsForUser(userId);
+                SparseBooleanArray persistedIdsInFile = mTaskIdsInFile.get(userId);
+                if (persistedIdsInFile != null && persistedIdsInFile.equals(taskIdsToSave)) {
+                    continue;
+                } else {
+                    SparseBooleanArray taskIdsToSaveCopy = taskIdsToSave.clone();
+                    mTaskIdsInFile.put(userId, taskIdsToSaveCopy);
+                    changedTaskIdsPerUser.put(userId, taskIdsToSaveCopy);
+                }
+            }
+        }
+        for (int i = 0; i < changedTaskIdsPerUser.size(); i++) {
+            writePersistedTaskIdsForUser(changedTaskIdsPerUser.valueAt(i),
+                    changedTaskIdsPerUser.keyAt(i));
+        }
+    }
+
+    private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) {
+        int[] candidateUserIds;
+        synchronized (mService.mGlobalLock) {
+            // Remove only from directories of the users who have recents in memory synchronized
+            // with persistent storage.
+            candidateUserIds = mRecentTasks.usersWithRecentsLoadedLocked();
+        }
+        for (int userId : candidateUserIds) {
+            removeObsoleteFiles(persistentTaskIds, getUserImagesDir(userId).listFiles());
+            removeObsoleteFiles(persistentTaskIds, getUserTasksDir(userId).listFiles());
+        }
+    }
+
+    static Bitmap restoreImage(String filename) {
+        if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename);
+        return BitmapFactory.decodeFile(filename);
+    }
+
+    private File getUserPersistedTaskIdsFile(int userId) {
+        File userTaskIdsDir = new File(mTaskIdsDir, String.valueOf(userId));
+        if (!userTaskIdsDir.exists() && !userTaskIdsDir.mkdirs()) {
+            Slog.e(TAG, "Error while creating user directory: " + userTaskIdsDir);
+        }
+        return new File(userTaskIdsDir, PERSISTED_TASK_IDS_FILENAME);
+    }
+
+    static File getUserTasksDir(int userId) {
+        File userTasksDir = new File(Environment.getDataSystemCeDirectory(userId), TASKS_DIRNAME);
+
+        if (!userTasksDir.exists()) {
+            if (!userTasksDir.mkdir()) {
+                Slog.e(TAG, "Failure creating tasks directory for user " + userId + ": "
+                        + userTasksDir);
+            }
+        }
+        return userTasksDir;
+    }
+
+    static File getUserImagesDir(int userId) {
+        return new File(Environment.getDataSystemCeDirectory(userId), IMAGES_DIRNAME);
+    }
+
+    private static boolean createParentDirectory(String filePath) {
+        File parentDir = new File(filePath).getParentFile();
+        return parentDir.exists() || parentDir.mkdirs();
+    }
+
+    private static class TaskWriteQueueItem implements PersisterQueue.WriteQueueItem {
+        private final ActivityTaskManagerService mService;
+        private final TaskRecord mTask;
+
+        TaskWriteQueueItem(TaskRecord task, ActivityTaskManagerService service) {
+            mTask = task;
+            mService = service;
+        }
+
+        private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
+            if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
+            final XmlSerializer xmlSerializer = new FastXmlSerializer();
+            StringWriter stringWriter = new StringWriter();
+            xmlSerializer.setOutput(stringWriter);
+
+            if (DEBUG) {
+                xmlSerializer.setFeature(
+                        "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            }
+
+            // save task
+            xmlSerializer.startDocument(null, true);
+
+            xmlSerializer.startTag(null, TAG_TASK);
+            task.saveToXml(xmlSerializer);
+            xmlSerializer.endTag(null, TAG_TASK);
+
+            xmlSerializer.endDocument();
+            xmlSerializer.flush();
+
+            return stringWriter;
+        }
+
+        @Override
+        public void process() {
+            // Write out one task.
+            StringWriter stringWriter = null;
+            TaskRecord task = mTask;
+            if (DEBUG) Slog.d(TAG, "Writing task=" + task);
+            synchronized (mService.mGlobalLock) {
+                if (task.inRecents) {
+                    // Still there.
+                    try {
+                        if (DEBUG) Slog.d(TAG, "Saving task=" + task);
+                        stringWriter = saveToXml(task);
+                    } catch (IOException e) {
+                    } catch (XmlPullParserException e) {
+                    }
+                }
+            }
+            if (stringWriter != null) {
+                // Write out xml file while not holding mService lock.
+                FileOutputStream file = null;
+                AtomicFile atomicFile = null;
+                try {
+                    atomicFile = new AtomicFile(new File(
+                            getUserTasksDir(task.userId),
+                            String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
+                    file = atomicFile.startWrite();
+                    file.write(stringWriter.toString().getBytes());
+                    file.write('\n');
+                    atomicFile.finishWrite(file);
+                } catch (IOException e) {
+                    if (file != null) {
+                        atomicFile.failWrite(file);
+                    }
+                    Slog.e(TAG,
+                            "Unable to open " + atomicFile + " for persisting. " + e);
+                }
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "TaskWriteQueueItem{task=" + mTask + "}";
+        }
+    }
+
+    private static class ImageWriteQueueItem implements PersisterQueue.WriteQueueItem {
+        final String mFilePath;
+        Bitmap mImage;
+
+        ImageWriteQueueItem(String filePath, Bitmap image) {
+            mFilePath = filePath;
+            mImage = image;
+        }
+
+        @Override
+        public void process() {
+            final String filePath = mFilePath;
+            if (!createParentDirectory(filePath)) {
+                Slog.e(TAG, "Error while creating images directory for file: " + filePath);
+                return;
+            }
+            final Bitmap bitmap = mImage;
+            if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
+            FileOutputStream imageFile = null;
+            try {
+                imageFile = new FileOutputStream(new File(filePath));
+                bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
+            } catch (Exception e) {
+                Slog.e(TAG, "saveImage: unable to save " + filePath, e);
+            } finally {
+                IoUtils.closeQuietly(imageFile);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "ImageWriteQueueItem{path=" + mFilePath
+                    + ", image=(" + mImage.getWidth() + "x" + mImage.getHeight() + ")}";
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index d2696c0..66ebc9b 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -159,7 +159,7 @@
                         if (DEBUG_TASK_POSITIONING){
                             Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
                         }
-                        synchronized (mService.mWindowMap) {
+                        synchronized (mService.mGlobalLock) {
                             mDragEnded = notifyMoveLocked(newX, newY);
                             mTask.getDimBounds(mTmpRect);
                         }
@@ -192,7 +192,7 @@
 
                 if (mDragEnded) {
                     final boolean wasResizing = mResizing;
-                    synchronized (mService.mWindowMap) {
+                    synchronized (mService.mGlobalLock) {
                         endDragLocked();
                         mTask.getDimBounds(mTmpRect);
                     }
@@ -397,7 +397,7 @@
         // bounds yet. This will guarantee that the app starts the backdrop renderer before
         // configuration changes which could cause an activity restart.
         if (mResizing) {
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 notifyMoveLocked(startX, startY);
             }
 
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 25148d1..f2d3dca 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -61,7 +61,7 @@
 
     boolean startMovingTask(IWindow window, float startX, float startY) {
         WindowState win = null;
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             win = mService.windowForClientLocked(null, window, false);
             // win shouldn't be null here, pass it down to startPositioningLocked
             // to get warning if it's null.
@@ -79,7 +79,7 @@
     void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
         mHandler.post(() -> {
             int taskId = -1;
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 final Task task = displayContent.findTaskForResizePoint(x, y);
                 if (task != null) {
                     if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
@@ -152,7 +152,7 @@
         mHandler.post(() -> {
             if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
 
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 if (mTaskPositioner != null) {
                     mTaskPositioner.unregister();
                     mTaskPositioner = null;
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
new file mode 100644
index 0000000..d4acb18
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -0,0 +1,2543 @@
+/*
+ * Copyright (C) 2006 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.server.wm;
+
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
+import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING_TO_TOP;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.TaskRecordProto.ACTIVITIES;
+import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
+import static com.android.server.am.TaskRecordProto.BOUNDS;
+import static com.android.server.am.TaskRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.TaskRecordProto.FULLSCREEN;
+import static com.android.server.am.TaskRecordProto.ID;
+import static com.android.server.am.TaskRecordProto.LAST_NON_FULLSCREEN_BOUNDS;
+import static com.android.server.am.TaskRecordProto.MIN_HEIGHT;
+import static com.android.server.am.TaskRecordProto.MIN_WIDTH;
+import static com.android.server.am.TaskRecordProto.ORIG_ACTIVITY;
+import static com.android.server.am.TaskRecordProto.REAL_ACTIVITY;
+import static com.android.server.am.TaskRecordProto.RESIZE_MODE;
+import static com.android.server.am.TaskRecordProto.STACK_ID;
+import static java.lang.Integer.MAX_VALUE;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManager.TaskDescription;
+import android.app.ActivityManager.TaskSnapshot;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.AppGlobals;
+import android.app.TaskInfo;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Debug;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.util.XmlUtils;
+import com.android.server.wm.ActivityStack.ActivityState;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Objects;
+
+// TODO: Make package private again once move to WM package is complete.
+public class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_ATM;
+    private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
+    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
+    private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
+    private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+
+    private static final String ATTR_TASKID = "task_id";
+    private static final String TAG_INTENT = "intent";
+    private static final String TAG_AFFINITYINTENT = "affinity_intent";
+    private static final String ATTR_REALACTIVITY = "real_activity";
+    private static final String ATTR_REALACTIVITY_SUSPENDED = "real_activity_suspended";
+    private static final String ATTR_ORIGACTIVITY = "orig_activity";
+    private static final String TAG_ACTIVITY = "activity";
+    private static final String ATTR_AFFINITY = "affinity";
+    private static final String ATTR_ROOT_AFFINITY = "root_affinity";
+    private static final String ATTR_ROOTHASRESET = "root_has_reset";
+    private static final String ATTR_AUTOREMOVERECENTS = "auto_remove_recents";
+    private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
+    private static final String ATTR_USERID = "user_id";
+    private static final String ATTR_USER_SETUP_COMPLETE = "user_setup_complete";
+    private static final String ATTR_EFFECTIVE_UID = "effective_uid";
+    @Deprecated
+    private static final String ATTR_TASKTYPE = "task_type";
+    private static final String ATTR_LASTDESCRIPTION = "last_description";
+    private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
+    private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
+    private static final String ATTR_TASK_AFFILIATION = "task_affiliation";
+    private static final String ATTR_PREV_AFFILIATION = "prev_affiliation";
+    private static final String ATTR_NEXT_AFFILIATION = "next_affiliation";
+    private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
+    private static final String ATTR_CALLING_UID = "calling_uid";
+    private static final String ATTR_CALLING_PACKAGE = "calling_package";
+    private static final String ATTR_SUPPORTS_PICTURE_IN_PICTURE = "supports_picture_in_picture";
+    private static final String ATTR_RESIZE_MODE = "resize_mode";
+    private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
+    private static final String ATTR_MIN_WIDTH = "min_width";
+    private static final String ATTR_MIN_HEIGHT = "min_height";
+    private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version";
+
+    // Current version of the task record we persist. Used to check if we need to run any upgrade
+    // code.
+    private static final int PERSIST_TASK_VERSION = 1;
+
+    private static final int INVALID_MIN_SIZE = -1;
+
+    /**
+     * The modes to control how the stack is moved to the front when calling
+     * {@link TaskRecord#reparent}.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            REPARENT_MOVE_STACK_TO_FRONT,
+            REPARENT_KEEP_STACK_AT_FRONT,
+            REPARENT_LEAVE_STACK_IN_PLACE
+    })
+    @interface ReparentMoveStackMode {}
+    // Moves the stack to the front if it was not at the front
+    static final int REPARENT_MOVE_STACK_TO_FRONT = 0;
+    // Only moves the stack to the front if it was focused or front most already
+    static final int REPARENT_KEEP_STACK_AT_FRONT = 1;
+    // Do not move the stack as a part of reparenting
+    static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
+
+    /**
+     * The factory used to create {@link TaskRecord}. This allows OEM subclass {@link TaskRecord}.
+     */
+    private static TaskRecordFactory sTaskRecordFactory;
+
+    final int taskId;       // Unique identifier for this task.
+    String affinity;        // The affinity name for this task, or null; may change identity.
+    String rootAffinity;    // Initial base affinity, or null; does not change from initial root.
+    final IVoiceInteractionSession voiceSession;    // Voice interaction session driving task
+    final IVoiceInteractor voiceInteractor;         // Associated interactor to provide to app
+    Intent intent;          // The original intent that started the task. Note that this value can
+                            // be null.
+    Intent affinityIntent;  // Intent of affinity-moved activity that started this task.
+    int effectiveUid;       // The current effective uid of the identity of this task.
+    ComponentName origActivity; // The non-alias activity component of the intent.
+    ComponentName realActivity; // The actual activity component that started the task.
+    boolean realActivitySuspended; // True if the actual activity component that started the
+                                   // task is suspended.
+    boolean inRecents;      // Actually in the recents list?
+    long lastActiveTime;    // Last time this task was active in the current device session,
+                            // including sleep. This time is initialized to the elapsed time when
+                            // restored from disk.
+    boolean isAvailable;    // Is the activity available to be launched?
+    boolean rootWasReset;   // True if the intent at the root of the task had
+                            // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
+    boolean autoRemoveRecents;  // If true, we should automatically remove the task from
+                                // recents when activity finishes
+    boolean askedCompatMode;// Have asked the user about compat mode for this task.
+    boolean hasBeenVisible; // Set if any activities in the task have been visible to the user.
+
+    String stringName;      // caching of toString() result.
+    int userId;             // user for which this task was created
+    boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity
+                                // was changed.
+
+    int numFullscreen;      // Number of fullscreen activities.
+
+    int mResizeMode;        // The resize mode of this task and its activities.
+                            // Based on the {@link ActivityInfo#resizeMode} of the root activity.
+    private boolean mSupportsPictureInPicture;  // Whether or not this task and its activities
+            // support PiP. Based on the {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE} flag
+            // of the root activity.
+    /** Can't be put in lockTask mode. */
+    final static int LOCK_TASK_AUTH_DONT_LOCK = 0;
+    /** Can enter app pinning with user approval. Can never start over existing lockTask task. */
+    final static int LOCK_TASK_AUTH_PINNABLE = 1;
+    /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */
+    final static int LOCK_TASK_AUTH_LAUNCHABLE = 2;
+    /** Can enter lockTask without user approval. Can start over existing lockTask task. */
+    final static int LOCK_TASK_AUTH_WHITELISTED = 3;
+    /** Priv-app that starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing
+     * lockTask task. */
+    final static int LOCK_TASK_AUTH_LAUNCHABLE_PRIV = 4;
+    int mLockTaskAuth = LOCK_TASK_AUTH_PINNABLE;
+
+    int mLockTaskUid = -1;  // The uid of the application that called startLockTask().
+
+    // This represents the last resolved activity values for this task
+    // NOTE: This value needs to be persisted with each task
+    TaskDescription lastTaskDescription = new TaskDescription();
+
+    /** List of all activities in the task arranged in history order */
+    final ArrayList<ActivityRecord> mActivities;
+
+    /** Current stack. Setter must always be used to update the value. */
+    private ActivityStack mStack;
+
+    /** The process that had previously hosted the root activity of this task.
+     * Used to know that we should try harder to keep this process around, in case the
+     * user wants to return to it. */
+    private WindowProcessController mRootProcess;
+
+    /** Takes on same value as first root activity */
+    boolean isPersistable = false;
+    int maxRecents;
+
+    /** Only used for persistable tasks, otherwise 0. The last time this task was moved. Used for
+     * determining the order when restoring. Sign indicates whether last task movement was to front
+     * (positive) or back (negative). Absolute value indicates time. */
+    long mLastTimeMoved = System.currentTimeMillis();
+
+    /** If original intent did not allow relinquishing task identity, save that information */
+    private boolean mNeverRelinquishIdentity = true;
+
+    // Used in the unique case where we are clearing the task in order to reuse it. In that case we
+    // do not want to delete the stack when the task goes empty.
+    private boolean mReuseTask = false;
+
+    CharSequence lastDescription; // Last description captured for this item.
+
+    int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
+    int mAffiliatedTaskColor; // color of the parent task affiliation.
+    TaskRecord mPrevAffiliate; // previous task in affiliated chain.
+    int mPrevAffiliateTaskId = INVALID_TASK_ID; // previous id for persistence.
+    TaskRecord mNextAffiliate; // next task in affiliated chain.
+    int mNextAffiliateTaskId = INVALID_TASK_ID; // next id for persistence.
+
+    // For relaunching the task from recents as though it was launched by the original launcher.
+    int mCallingUid;
+    String mCallingPackage;
+
+    final ActivityTaskManagerService mService;
+
+    private final Rect mTmpStableBounds = new Rect();
+    private final Rect mTmpNonDecorBounds = new Rect();
+    private final Rect mTmpRect = new Rect();
+
+    // Last non-fullscreen bounds the task was launched in or resized to.
+    // The information is persisted and used to determine the appropriate stack to launch the
+    // task into on restore.
+    Rect mLastNonFullscreenBounds = null;
+    // Minimal width and height of this task when it's resizeable. -1 means it should use the
+    // default minimal width/height.
+    int mMinWidth;
+    int mMinHeight;
+
+    // Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
+    // This number will be assigned when we evaluate OOM scores for all visible tasks.
+    int mLayerRank = -1;
+
+    /** Helper object used for updating override configuration. */
+    private Configuration mTmpConfig = new Configuration();
+
+    private TaskWindowContainerController mWindowContainerController;
+
+    /**
+     * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
+     * ActivityInfo, Intent, TaskDescription)} instead.
+     */
+    TaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info, Intent _intent,
+            IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
+        mService = service;
+        userId = UserHandle.getUserId(info.applicationInfo.uid);
+        taskId = _taskId;
+        lastActiveTime = SystemClock.elapsedRealtime();
+        mAffiliatedTaskId = _taskId;
+        voiceSession = _voiceSession;
+        voiceInteractor = _voiceInteractor;
+        isAvailable = true;
+        mActivities = new ArrayList<>();
+        mCallingUid = info.applicationInfo.uid;
+        mCallingPackage = info.packageName;
+        setIntent(_intent, info);
+        setMinDimensions(info);
+        touchActiveTime();
+        mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
+    }
+
+    /**
+     * Don't use constructor directly.
+     * Use {@link #create(ActivityTaskManagerService, int, ActivityInfo,
+     * Intent, IVoiceInteractionSession, IVoiceInteractor)} instead.
+     */
+    TaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info, Intent _intent,
+            TaskDescription _taskDescription) {
+        mService = service;
+        userId = UserHandle.getUserId(info.applicationInfo.uid);
+        taskId = _taskId;
+        lastActiveTime = SystemClock.elapsedRealtime();
+        mAffiliatedTaskId = _taskId;
+        voiceSession = null;
+        voiceInteractor = null;
+        isAvailable = true;
+        mActivities = new ArrayList<>();
+        mCallingUid = info.applicationInfo.uid;
+        mCallingPackage = info.packageName;
+        setIntent(_intent, info);
+        setMinDimensions(info);
+
+        isPersistable = true;
+        // Clamp to [1, max].
+        maxRecents = Math.min(Math.max(info.maxRecents, 1),
+                ActivityTaskManager.getMaxAppRecentsLimitStatic());
+
+        lastTaskDescription = _taskDescription;
+        touchActiveTime();
+        mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
+    }
+
+    /**
+     * Don't use constructor directly. This is only used by XML parser.
+     */
+    TaskRecord(ActivityTaskManagerService service, int _taskId, Intent _intent,
+            Intent _affinityIntent, String _affinity, String _rootAffinity,
+            ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
+            boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId,
+            int _effectiveUid, String _lastDescription, ArrayList<ActivityRecord> activities,
+            long lastTimeMoved, boolean neverRelinquishIdentity,
+            TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId,
+            int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
+            int resizeMode, boolean supportsPictureInPicture, boolean _realActivitySuspended,
+            boolean userSetupComplete, int minWidth, int minHeight) {
+        mService = service;
+        taskId = _taskId;
+        intent = _intent;
+        affinityIntent = _affinityIntent;
+        affinity = _affinity;
+        rootAffinity = _rootAffinity;
+        voiceSession = null;
+        voiceInteractor = null;
+        realActivity = _realActivity;
+        realActivitySuspended = _realActivitySuspended;
+        origActivity = _origActivity;
+        rootWasReset = _rootWasReset;
+        isAvailable = true;
+        autoRemoveRecents = _autoRemoveRecents;
+        askedCompatMode = _askedCompatMode;
+        userId = _userId;
+        mUserSetupComplete = userSetupComplete;
+        effectiveUid = _effectiveUid;
+        lastActiveTime = SystemClock.elapsedRealtime();
+        lastDescription = _lastDescription;
+        mActivities = activities;
+        mLastTimeMoved = lastTimeMoved;
+        mNeverRelinquishIdentity = neverRelinquishIdentity;
+        lastTaskDescription = _lastTaskDescription;
+        mAffiliatedTaskId = taskAffiliation;
+        mAffiliatedTaskColor = taskAffiliationColor;
+        mPrevAffiliateTaskId = prevTaskId;
+        mNextAffiliateTaskId = nextTaskId;
+        mCallingUid = callingUid;
+        mCallingPackage = callingPackage;
+        mResizeMode = resizeMode;
+        mSupportsPictureInPicture = supportsPictureInPicture;
+        mMinWidth = minWidth;
+        mMinHeight = minHeight;
+        mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
+    }
+
+    TaskWindowContainerController getWindowContainerController() {
+        return mWindowContainerController;
+    }
+
+    void createWindowContainer(boolean onTop, boolean showForAllUsers) {
+        if (mWindowContainerController != null) {
+            throw new IllegalArgumentException("Window container=" + mWindowContainerController
+                    + " already created for task=" + this);
+        }
+
+        final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
+        setWindowContainerController(new TaskWindowContainerController(taskId, this,
+                getStack().getWindowContainerController(), userId, bounds,
+                mResizeMode, mSupportsPictureInPicture, onTop,
+                showForAllUsers, lastTaskDescription));
+    }
+
+    /**
+     * Should only be invoked from {@link #createWindowContainer(boolean, boolean)}.
+     */
+    @VisibleForTesting
+    protected void setWindowContainerController(TaskWindowContainerController controller) {
+        if (mWindowContainerController != null) {
+            throw new IllegalArgumentException("Window container=" + mWindowContainerController
+                    + " already created for task=" + this);
+        }
+
+        mWindowContainerController = controller;
+    }
+
+    void removeWindowContainer() {
+        mService.getLockTaskController().clearLockedTask(this);
+        mWindowContainerController.removeContainer();
+        if (!getWindowConfiguration().persistTaskBounds()) {
+            // Reset current bounds for task whose bounds shouldn't be persisted so it uses
+            // default configuration the next time it launches.
+            updateOverrideConfiguration(null);
+        }
+        mService.getTaskChangeNotificationController().notifyTaskRemoved(taskId);
+        mWindowContainerController = null;
+    }
+
+    @Override
+    public void onSnapshotChanged(TaskSnapshot snapshot) {
+        mService.getTaskChangeNotificationController().notifyTaskSnapshotChanged(taskId, snapshot);
+    }
+
+    void setResizeMode(int resizeMode) {
+        if (mResizeMode == resizeMode) {
+            return;
+        }
+        mResizeMode = resizeMode;
+        mWindowContainerController.setResizeable(resizeMode);
+        mService.mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+    }
+
+    void setTaskDockedResizing(boolean resizing) {
+        mWindowContainerController.setTaskDockedResizing(resizing);
+    }
+
+    // TODO: Consolidate this with the resize() method below.
+    @Override
+    public void requestResize(Rect bounds, int resizeMode) {
+        mService.resizeTask(taskId, bounds, resizeMode);
+    }
+
+    boolean resize(Rect bounds, int resizeMode, boolean preserveWindow, boolean deferResume) {
+        mService.mWindowManager.deferSurfaceLayout();
+
+        try {
+            if (!isResizeable()) {
+                Slog.w(TAG, "resizeTask: task " + this + " not resizeable.");
+                return true;
+            }
+
+            // If this is a forced resize, let it go through even if the bounds is not changing,
+            // as we might need a relayout due to surface size change (to/from fullscreen).
+            final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
+            if (equivalentOverrideBounds(bounds) && !forced) {
+                // Nothing to do here...
+                return true;
+            }
+
+            if (mWindowContainerController == null) {
+                // Task doesn't exist in window manager yet (e.g. was restored from recents).
+                // All we can do for now is update the bounds so it can be used when the task is
+                // added to window manager.
+                updateOverrideConfiguration(bounds);
+                if (!inFreeformWindowingMode()) {
+                    // re-restore the task so it can have the proper stack association.
+                    mService.mStackSupervisor.restoreRecentTaskLocked(this, null, !ON_TOP);
+                }
+                return true;
+            }
+
+            if (!canResizeToBounds(bounds)) {
+                throw new IllegalArgumentException("resizeTask: Can not resize task=" + this
+                        + " to bounds=" + bounds + " resizeMode=" + mResizeMode);
+            }
+
+            // Do not move the task to another stack here.
+            // This method assumes that the task is already placed in the right stack.
+            // we do not mess with that decision and we only do the resize!
+
+            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + taskId);
+
+            final boolean updatedConfig = updateOverrideConfiguration(bounds);
+            // This variable holds information whether the configuration didn't change in a significant
+
+            // way and the activity was kept the way it was. If it's false, it means the activity
+            // had
+            // to be relaunched due to configuration change.
+            boolean kept = true;
+            if (updatedConfig) {
+                final ActivityRecord r = topRunningActivityLocked();
+                if (r != null && !deferResume) {
+                    kept = r.ensureActivityConfiguration(0 /* globalChanges */,
+                            preserveWindow);
+                    // Preserve other windows for resizing because if resizing happens when there
+                    // is a dialog activity in the front, the activity that still shows some
+                    // content to the user will become black and cause flickers. Note in most cases
+                    // this won't cause tons of irrelevant windows being preserved because only
+                    // activities in this task may experience a bounds change. Configs for other
+                    // activities stay the same.
+                    mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0,
+                            preserveWindow);
+                    if (!kept) {
+                        mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    }
+                }
+            }
+            mWindowContainerController.resize(kept, forced);
+
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            return kept;
+        } finally {
+            mService.mWindowManager.continueSurfaceLayout();
+        }
+    }
+
+    // TODO: Investigate combining with the resize() method above.
+    void resizeWindowContainer() {
+        mWindowContainerController.resize(false /* relayout */, false /* forced */);
+    }
+
+    void getWindowContainerBounds(Rect bounds) {
+        mWindowContainerController.getBounds(bounds);
+    }
+
+    /**
+     * Convenience method to reparent a task to the top or bottom position of the stack.
+     */
+    boolean reparent(ActivityStack preferredStack, boolean toTop,
+            @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+            String reason) {
+        return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate, deferResume,
+                true /* schedulePictureInPictureModeChange */, reason);
+    }
+
+    /**
+     * Convenience method to reparent a task to the top or bottom position of the stack, with
+     * an option to skip scheduling the picture-in-picture mode change.
+     */
+    boolean reparent(ActivityStack preferredStack, boolean toTop,
+            @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+            boolean schedulePictureInPictureModeChange, String reason) {
+        return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate,
+                deferResume, schedulePictureInPictureModeChange, reason);
+    }
+
+    /** Convenience method to reparent a task to a specific position of the stack. */
+    boolean reparent(ActivityStack preferredStack, int position,
+            @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+            String reason) {
+        return reparent(preferredStack, position, moveStackMode, animate, deferResume,
+                true /* schedulePictureInPictureModeChange */, reason);
+    }
+
+    /**
+     * Reparents the task into a preferred stack, creating it if necessary.
+     *
+     * @param preferredStack the target stack to move this task
+     * @param position the position to place this task in the new stack
+     * @param animate whether or not we should wait for the new window created as a part of the
+     *            reparenting to be drawn and animated in
+     * @param moveStackMode whether or not to move the stack to the front always, only if it was
+     *            previously focused & in front, or never
+     * @param deferResume whether or not to update the visibility of other tasks and stacks that may
+     *            have changed as a result of this reparenting
+     * @param schedulePictureInPictureModeChange specifies whether or not to schedule the PiP mode
+     *            change. Callers may set this to false if they are explicitly scheduling PiP mode
+     *            changes themselves, like during the PiP animation
+     * @param reason the caller of this reparenting
+     * @return whether the task was reparented
+     */
+    // TODO: Inspect all call sites and change to just changing windowing mode of the stack vs.
+    // re-parenting the task. Can only be done when we are no longer using static stack Ids.
+    boolean reparent(ActivityStack preferredStack, int position,
+            @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+            boolean schedulePictureInPictureModeChange, String reason) {
+        final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
+        final WindowManagerService windowManager = mService.mWindowManager;
+        final ActivityStack sourceStack = getStack();
+        final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack,
+                position == MAX_VALUE);
+        if (toStack == sourceStack) {
+            return false;
+        }
+        if (!canBeLaunchedOnDisplay(toStack.mDisplayId)) {
+            return false;
+        }
+
+        final int toStackWindowingMode = toStack.getWindowingMode();
+        final ActivityRecord topActivity = getTopActivity();
+
+        final boolean mightReplaceWindow = topActivity != null
+                && replaceWindowsOnTaskMove(getWindowingMode(), toStackWindowingMode);
+        if (mightReplaceWindow) {
+            // We are about to relaunch the activity because its configuration changed due to
+            // being maximized, i.e. size change. The activity will first remove the old window
+            // and then add a new one. This call will tell window manager about this, so it can
+            // preserve the old window until the new one is drawn. This prevents having a gap
+            // between the removal and addition, in which no window is visible. We also want the
+            // entrance of the new window to be properly animated.
+            // Note here we always set the replacing window first, as the flags might be needed
+            // during the relaunch. If we end up not doing any relaunch, we clear the flags later.
+            windowManager.setWillReplaceWindow(topActivity.appToken, animate);
+        }
+
+        windowManager.deferSurfaceLayout();
+        boolean kept = true;
+        try {
+            final ActivityRecord r = topRunningActivityLocked();
+            final boolean wasFocused = r != null && supervisor.isTopDisplayFocusedStack(sourceStack)
+                    && (topRunningActivityLocked() == r);
+            final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r;
+            final boolean wasPaused = r != null && sourceStack.mPausingActivity == r;
+
+            // In some cases the focused stack isn't the front stack. E.g. pinned stack.
+            // Whenever we are moving the top activity from the front stack we want to make sure to
+            // move the stack to the front.
+            final boolean wasFront = r != null && sourceStack.isTopStackOnDisplay()
+                    && (sourceStack.topRunningActivityLocked() == r);
+
+            // Adjust the position for the new parent stack as needed.
+            position = toStack.getAdjustedPositionForTask(this, position, null /* starting */);
+
+            // Must reparent first in window manager to avoid a situation where AM can delete the
+            // we are coming from in WM before we reparent because it became empty.
+            mWindowContainerController.reparent(toStack.getWindowContainerController(), position,
+                    moveStackMode == REPARENT_MOVE_STACK_TO_FRONT);
+
+            final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
+                    || (moveStackMode == REPARENT_KEEP_STACK_AT_FRONT && (wasFocused || wasFront));
+            // Move the task
+            sourceStack.removeTask(this, reason, moveStackToFront
+                    ? REMOVE_TASK_MODE_MOVING_TO_TOP : REMOVE_TASK_MODE_MOVING);
+            toStack.addTask(this, position, false /* schedulePictureInPictureModeChange */, reason);
+
+            if (schedulePictureInPictureModeChange) {
+                // Notify of picture-in-picture mode changes
+                supervisor.scheduleUpdatePictureInPictureModeIfNeeded(this, sourceStack);
+            }
+
+            // TODO: Ensure that this is actually necessary here
+            // Notify the voice session if required
+            if (voiceSession != null) {
+                try {
+                    voiceSession.taskStarted(intent, taskId);
+                } catch (RemoteException e) {
+                }
+            }
+
+            // If the task had focus before (or we're requested to move focus), move focus to the
+            // new stack by moving the stack to the front.
+            if (r != null) {
+                toStack.moveToFrontAndResumeStateIfNeeded(r, moveStackToFront, wasResumed,
+                        wasPaused, reason);
+            }
+            if (!animate) {
+                mService.mStackSupervisor.mNoAnimActivities.add(topActivity);
+            }
+
+            // We might trigger a configuration change. Save the current task bounds for freezing.
+            // TODO: Should this call be moved inside the resize method in WM?
+            toStack.prepareFreezingTaskBounds();
+
+            // Make sure the task has the appropriate bounds/size for the stack it is in.
+            final boolean toStackSplitScreenPrimary =
+                    toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+            final Rect configBounds = getOverrideBounds();
+            if ((toStackWindowingMode == WINDOWING_MODE_FULLSCREEN
+                    || toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
+                    && !Objects.equals(configBounds, toStack.getOverrideBounds())) {
+                kept = resize(toStack.getOverrideBounds(), RESIZE_MODE_SYSTEM, !mightReplaceWindow,
+                        deferResume);
+            } else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) {
+                Rect bounds = getLaunchBounds();
+                if (bounds == null) {
+                    mService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
+                    bounds = configBounds;
+                }
+                kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
+            } else if (toStackSplitScreenPrimary || toStackWindowingMode == WINDOWING_MODE_PINNED) {
+                if (toStackSplitScreenPrimary && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) {
+                    // Move recents to front so it is not behind home stack when going into docked
+                    // mode
+                    mService.mStackSupervisor.moveRecentsStackToFront(reason);
+                }
+                kept = resize(toStack.getOverrideBounds(), RESIZE_MODE_SYSTEM, !mightReplaceWindow,
+                        deferResume);
+            }
+        } finally {
+            windowManager.continueSurfaceLayout();
+        }
+
+        if (mightReplaceWindow) {
+            // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
+            // window), we need to clear the replace window settings. Otherwise, we schedule a
+            // timeout to remove the old window if the replacing window is not coming in time.
+            windowManager.scheduleClearWillReplaceWindows(topActivity.appToken, !kept);
+        }
+
+        if (!deferResume) {
+            // The task might have already been running and its visibility needs to be synchronized
+            // with the visibility of the stack / windows.
+            supervisor.ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
+            supervisor.resumeFocusedStacksTopActivitiesLocked();
+        }
+
+        // TODO: Handle incorrect request to move before the actual move, not after.
+        supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(),
+                DEFAULT_DISPLAY, toStack);
+
+        return (preferredStack == toStack);
+    }
+
+    /**
+     * @return True if the windows of tasks being moved to the target stack from the source stack
+     * should be replaced, meaning that window manager will keep the old window around until the new
+     * is ready.
+     */
+    private static boolean replaceWindowsOnTaskMove(
+            int sourceWindowingMode, int targetWindowingMode) {
+        return sourceWindowingMode == WINDOWING_MODE_FREEFORM
+                || targetWindowingMode == WINDOWING_MODE_FREEFORM;
+    }
+
+    void cancelWindowTransition() {
+        mWindowContainerController.cancelWindowTransition();
+    }
+
+    /**
+     * DO NOT HOLD THE ACTIVITY MANAGER LOCK WHEN CALLING THIS METHOD!
+     */
+    TaskSnapshot getSnapshot(boolean reducedResolution) {
+
+        // TODO: Move this to {@link TaskWindowContainerController} once recent tasks are more
+        // synchronized between AM and WM.
+        return mService.mWindowManager.getTaskSnapshot(taskId, userId, reducedResolution);
+    }
+
+    void touchActiveTime() {
+        lastActiveTime = SystemClock.elapsedRealtime();
+    }
+
+    long getInactiveDuration() {
+        return SystemClock.elapsedRealtime() - lastActiveTime;
+    }
+
+    /** Sets the original intent, and the calling uid and package. */
+    void setIntent(ActivityRecord r) {
+        mCallingUid = r.launchedFromUid;
+        mCallingPackage = r.launchedFromPackage;
+        setIntent(r.intent, r.info);
+        setLockTaskAuth(r);
+    }
+
+    /** Sets the original intent, _without_ updating the calling uid or package. */
+    private void setIntent(Intent _intent, ActivityInfo info) {
+        if (intent == null) {
+            mNeverRelinquishIdentity =
+                    (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
+        } else if (mNeverRelinquishIdentity) {
+            return;
+        }
+
+        affinity = info.taskAffinity;
+        if (intent == null) {
+            // If this task already has an intent associated with it, don't set the root
+            // affinity -- we don't want it changing after initially set, but the initially
+            // set value may be null.
+            rootAffinity = affinity;
+        }
+        effectiveUid = info.applicationInfo.uid;
+        stringName = null;
+
+        if (info.targetActivity == null) {
+            if (_intent != null) {
+                // If this Intent has a selector, we want to clear it for the
+                // recent task since it is not relevant if the user later wants
+                // to re-launch the app.
+                if (_intent.getSelector() != null || _intent.getSourceBounds() != null) {
+                    _intent = new Intent(_intent);
+                    _intent.setSelector(null);
+                    _intent.setSourceBounds(null);
+                }
+            }
+            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Setting Intent of " + this + " to " + _intent);
+            intent = _intent;
+            realActivity = _intent != null ? _intent.getComponent() : null;
+            origActivity = null;
+        } else {
+            ComponentName targetComponent = new ComponentName(
+                    info.packageName, info.targetActivity);
+            if (_intent != null) {
+                Intent targetIntent = new Intent(_intent);
+                targetIntent.setComponent(targetComponent);
+                targetIntent.setSelector(null);
+                targetIntent.setSourceBounds(null);
+                if (DEBUG_TASKS) Slog.v(TAG_TASKS,
+                        "Setting Intent of " + this + " to target " + targetIntent);
+                intent = targetIntent;
+                realActivity = targetComponent;
+                origActivity = _intent.getComponent();
+            } else {
+                intent = null;
+                realActivity = targetComponent;
+                origActivity = new ComponentName(info.packageName, info.name);
+            }
+        }
+
+        final int intentFlags = intent == null ? 0 : intent.getFlags();
+        if ((intentFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+            // Once we are set to an Intent with this flag, we count this
+            // task as having a true root activity.
+            rootWasReset = true;
+        }
+        userId = UserHandle.getUserId(info.applicationInfo.uid);
+        mUserSetupComplete = Settings.Secure.getIntForUser(mService.mContext.getContentResolver(),
+                USER_SETUP_COMPLETE, 0, userId) != 0;
+        if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
+            // If the activity itself has requested auto-remove, then just always do it.
+            autoRemoveRecents = true;
+        } else if ((intentFlags & (FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS))
+                == FLAG_ACTIVITY_NEW_DOCUMENT) {
+            // If the caller has not asked for the document to be retained, then we may
+            // want to turn on auto-remove, depending on whether the target has set its
+            // own document launch mode.
+            if (info.documentLaunchMode != ActivityInfo.DOCUMENT_LAUNCH_NONE) {
+                autoRemoveRecents = false;
+            } else {
+                autoRemoveRecents = true;
+            }
+        } else {
+            autoRemoveRecents = false;
+        }
+        mResizeMode = info.resizeMode;
+        mSupportsPictureInPicture = info.supportsPictureInPicture();
+    }
+
+    /** Sets the original minimal width and height. */
+    private void setMinDimensions(ActivityInfo info) {
+        if (info != null && info.windowLayout != null) {
+            mMinWidth = info.windowLayout.minWidth;
+            mMinHeight = info.windowLayout.minHeight;
+        } else {
+            mMinWidth = INVALID_MIN_SIZE;
+            mMinHeight = INVALID_MIN_SIZE;
+        }
+    }
+
+    /**
+     * Return true if the input activity has the same intent filter as the intent this task
+     * record is based on (normally the root activity intent).
+     */
+    boolean isSameIntentFilter(ActivityRecord r) {
+        final Intent intent = new Intent(r.intent);
+        // Correct the activity intent for aliasing. The task record intent will always be based on
+        // the real activity that will be launched not the alias, so we need to use an intent with
+        // the component name pointing to the real activity not the alias in the activity record.
+        intent.setComponent(r.realActivity);
+        return intent.filterEquals(this.intent);
+    }
+
+    boolean returnsToHomeStack() {
+        final int returnHomeFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME;
+        return intent != null && (intent.getFlags() & returnHomeFlags) == returnHomeFlags;
+    }
+
+    void setPrevAffiliate(TaskRecord prevAffiliate) {
+        mPrevAffiliate = prevAffiliate;
+        mPrevAffiliateTaskId = prevAffiliate == null ? INVALID_TASK_ID : prevAffiliate.taskId;
+    }
+
+    void setNextAffiliate(TaskRecord nextAffiliate) {
+        mNextAffiliate = nextAffiliate;
+        mNextAffiliateTaskId = nextAffiliate == null ? INVALID_TASK_ID : nextAffiliate.taskId;
+    }
+
+    <T extends ActivityStack> T getStack() {
+        return (T) mStack;
+    }
+
+    /**
+     * Must be used for setting parent stack because it performs configuration updates.
+     * Must be called after adding task as a child to the stack.
+     */
+    void setStack(ActivityStack stack) {
+        if (stack != null && !stack.isInStackLocked(this)) {
+            throw new IllegalStateException("Task must be added as a Stack child first.");
+        }
+        final ActivityStack oldStack = mStack;
+        mStack = stack;
+
+        // If the new {@link TaskRecord} is from a different {@link ActivityStack}, remove this
+        // {@link ActivityRecord} from its current {@link ActivityStack}.
+
+        if (oldStack != mStack) {
+            for (int i = getChildCount() - 1; i >= 0; --i) {
+                final ActivityRecord activity = getChildAt(i);
+
+                if (oldStack != null) {
+                    oldStack.onActivityRemovedFromStack(activity);
+                }
+
+                if (mStack != null) {
+                    stack.onActivityAddedToStack(activity);
+                }
+            }
+        }
+
+        onParentChanged();
+    }
+
+    /**
+     * @return Id of current stack, {@link INVALID_STACK_ID} if no stack is set.
+     */
+    int getStackId() {
+        return mStack != null ? mStack.mStackId : INVALID_STACK_ID;
+    }
+
+    @Override
+    protected int getChildCount() {
+        return mActivities.size();
+    }
+
+    @Override
+    protected ActivityRecord getChildAt(int index) {
+        return mActivities.get(index);
+    }
+
+    @Override
+    protected ConfigurationContainer getParent() {
+        return mStack;
+    }
+
+    @Override
+    protected void onParentChanged() {
+        super.onParentChanged();
+        mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+    }
+
+    // Close up recents linked list.
+    private void closeRecentsChain() {
+        if (mPrevAffiliate != null) {
+            mPrevAffiliate.setNextAffiliate(mNextAffiliate);
+        }
+        if (mNextAffiliate != null) {
+            mNextAffiliate.setPrevAffiliate(mPrevAffiliate);
+        }
+        setPrevAffiliate(null);
+        setNextAffiliate(null);
+    }
+
+    void removedFromRecents() {
+        closeRecentsChain();
+        if (inRecents) {
+            inRecents = false;
+            mService.notifyTaskPersisterLocked(this, false);
+        }
+
+        clearRootProcess();
+
+        // TODO: Use window container controller once tasks are better synced between AM and WM
+        mService.mWindowManager.notifyTaskRemovedFromRecents(taskId, userId);
+    }
+
+    void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) {
+        closeRecentsChain();
+        mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId;
+        mAffiliatedTaskColor = taskToAffiliateWith.mAffiliatedTaskColor;
+        // Find the end
+        while (taskToAffiliateWith.mNextAffiliate != null) {
+            final TaskRecord nextRecents = taskToAffiliateWith.mNextAffiliate;
+            if (nextRecents.mAffiliatedTaskId != mAffiliatedTaskId) {
+                Slog.e(TAG, "setTaskToAffiliateWith: nextRecents=" + nextRecents + " affilTaskId="
+                        + nextRecents.mAffiliatedTaskId + " should be " + mAffiliatedTaskId);
+                if (nextRecents.mPrevAffiliate == taskToAffiliateWith) {
+                    nextRecents.setPrevAffiliate(null);
+                }
+                taskToAffiliateWith.setNextAffiliate(null);
+                break;
+            }
+            taskToAffiliateWith = nextRecents;
+        }
+        taskToAffiliateWith.setNextAffiliate(this);
+        setPrevAffiliate(taskToAffiliateWith);
+        setNextAffiliate(null);
+    }
+
+    /** Returns the intent for the root activity for this task */
+    Intent getBaseIntent() {
+        return intent != null ? intent : affinityIntent;
+    }
+
+    /** Returns the first non-finishing activity from the root. */
+    ActivityRecord getRootActivity() {
+        for (int i = 0; i < mActivities.size(); i++) {
+            final ActivityRecord r = mActivities.get(i);
+            if (r.finishing) {
+                continue;
+            }
+            return r;
+        }
+        return null;
+    }
+
+    ActivityRecord getTopActivity() {
+        return getTopActivity(true /* includeOverlays */);
+    }
+
+    ActivityRecord getTopActivity(boolean includeOverlays) {
+        for (int i = mActivities.size() - 1; i >= 0; --i) {
+            final ActivityRecord r = mActivities.get(i);
+            if (r.finishing || (!includeOverlays && r.mTaskOverlay)) {
+                continue;
+            }
+            return r;
+        }
+        return null;
+    }
+
+    ActivityRecord topRunningActivityLocked() {
+        if (mStack != null) {
+            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+                ActivityRecord r = mActivities.get(activityNdx);
+                if (!r.finishing && r.okToShowLocked()) {
+                    return r;
+                }
+            }
+        }
+        return null;
+    }
+
+    boolean isVisible() {
+        for (int i = mActivities.size() - 1; i >= 0; --i) {
+            final ActivityRecord r = mActivities.get(i);
+            if (r.visible) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
+        if (mStack != null) {
+            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+                ActivityRecord r = mActivities.get(activityNdx);
+                if (!r.finishing && r.okToShowLocked() && r.visibleIgnoringKeyguard) {
+                    outActivities.add(r);
+                }
+            }
+        }
+    }
+
+    ActivityRecord topRunningActivityWithStartingWindowLocked() {
+        if (mStack != null) {
+            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+                ActivityRecord r = mActivities.get(activityNdx);
+                if (r.mStartingWindowState != STARTING_WINDOW_SHOWN
+                        || r.finishing || !r.okToShowLocked()) {
+                    continue;
+                }
+                return r;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Return the number of running activities, and the number of non-finishing/initializing
+     * activities in the provided {@param reportOut} respectively.
+     */
+    void getNumRunningActivities(TaskActivitiesReport reportOut) {
+        reportOut.reset();
+        for (int i = mActivities.size() - 1; i >= 0; --i) {
+            final ActivityRecord r = mActivities.get(i);
+            if (r.finishing) {
+                continue;
+            }
+
+            reportOut.base = r;
+
+            // Increment the total number of non-finishing activities
+            reportOut.numActivities++;
+
+            if (reportOut.top == null || (reportOut.top.isState(ActivityState.INITIALIZING))) {
+                reportOut.top = r;
+                // Reset the number of running activities until we hit the first non-initializing
+                // activity
+                reportOut.numRunning = 0;
+            }
+            if (r.attachedToProcess()) {
+                // Increment the number of actually running activities
+                reportOut.numRunning++;
+            }
+        }
+    }
+
+    boolean okToShowLocked() {
+        // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
+        // okay to show the activity when locked.
+        return mService.mStackSupervisor.isCurrentProfileLocked(userId)
+                || topRunningActivityLocked() != null;
+    }
+
+    /** Call after activity movement or finish to make sure that frontOfTask is set correctly */
+    final void setFrontOfTask() {
+        boolean foundFront = false;
+        final int numActivities = mActivities.size();
+        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
+            final ActivityRecord r = mActivities.get(activityNdx);
+            if (foundFront || r.finishing) {
+                r.frontOfTask = false;
+            } else {
+                r.frontOfTask = true;
+                // Set frontOfTask false for every following activity.
+                foundFront = true;
+            }
+        }
+        if (!foundFront && numActivities > 0) {
+            // All activities of this task are finishing. As we ought to have a frontOfTask
+            // activity, make the bottom activity front.
+            mActivities.get(0).frontOfTask = true;
+        }
+    }
+
+    /**
+     * Reorder the history stack so that the passed activity is brought to the front.
+     */
+    final void moveActivityToFrontLocked(ActivityRecord newTop) {
+        if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
+                "Removing and adding activity " + newTop
+                + " to stack at top callers=" + Debug.getCallers(4));
+
+        mActivities.remove(newTop);
+        mActivities.add(newTop);
+
+        // Make sure window manager is aware of the position change.
+        mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController);
+        updateEffectiveIntent();
+
+        setFrontOfTask();
+    }
+
+    void addActivityAtBottom(ActivityRecord r) {
+        addActivityAtIndex(0, r);
+    }
+
+    void addActivityToTop(ActivityRecord r) {
+        addActivityAtIndex(mActivities.size(), r);
+    }
+
+    @Override
+    /*@WindowConfiguration.ActivityType*/
+    public int getActivityType() {
+        final int applicationType = super.getActivityType();
+        if (applicationType != ACTIVITY_TYPE_UNDEFINED || mActivities.isEmpty()) {
+            return applicationType;
+        }
+        return mActivities.get(0).getActivityType();
+    }
+
+    /**
+     * Adds an activity {@param r} at the given {@param index}. The activity {@param r} must either
+     * be in the current task or unparented to any task.
+     */
+    void addActivityAtIndex(int index, ActivityRecord r) {
+        TaskRecord task = r.getTask();
+        if (task != null && task != this) {
+            throw new IllegalArgumentException("Can not add r=" + " to task=" + this
+                    + " current parent=" + task);
+        }
+
+        r.setTask(this);
+
+        // Remove r first, and if it wasn't already in the list and it's fullscreen, count it.
+        if (!mActivities.remove(r) && r.fullscreen) {
+            // Was not previously in list.
+            numFullscreen++;
+        }
+        // Only set this based on the first activity
+        if (mActivities.isEmpty()) {
+            if (r.getActivityType() == ACTIVITY_TYPE_UNDEFINED) {
+                // Normally non-standard activity type for the activity record will be set when the
+                // object is created, however we delay setting the standard application type until
+                // this point so that the task can set the type for additional activities added in
+                // the else condition below.
+                r.setActivityType(ACTIVITY_TYPE_STANDARD);
+            }
+            setActivityType(r.getActivityType());
+            isPersistable = r.isPersistable();
+            mCallingUid = r.launchedFromUid;
+            mCallingPackage = r.launchedFromPackage;
+            // Clamp to [1, max].
+            maxRecents = Math.min(Math.max(r.info.maxRecents, 1),
+                    ActivityTaskManager.getMaxAppRecentsLimitStatic());
+        } else {
+            // Otherwise make all added activities match this one.
+            r.setActivityType(getActivityType());
+        }
+
+        final int size = mActivities.size();
+
+        if (index == size && size > 0) {
+            final ActivityRecord top = mActivities.get(size - 1);
+            if (top.mTaskOverlay) {
+                // Place below the task overlay activity since the overlay activity should always
+                // be on top.
+                index--;
+            }
+        }
+
+        index = Math.min(size, index);
+        mActivities.add(index, r);
+
+        updateEffectiveIntent();
+        if (r.isPersistable()) {
+            mService.notifyTaskPersisterLocked(this, false);
+        }
+
+        // Sync. with window manager
+        updateOverrideConfigurationFromLaunchBounds();
+        final AppWindowContainerController appController = r.getWindowContainerController();
+        if (appController != null) {
+            // Only attempt to move in WM if the child has a controller. It is possible we haven't
+            // created controller for the activity we are starting yet.
+            mWindowContainerController.positionChildAt(appController, index);
+        }
+
+        // Make sure the list of display UID whitelists is updated
+        // now that this record is in a new task.
+        mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+    }
+
+    /**
+     * Removes the specified activity from this task.
+     * @param r The {@link ActivityRecord} to remove.
+     * @return true if this was the last activity in the task.
+     */
+    boolean removeActivity(ActivityRecord r) {
+        return removeActivity(r, false /* reparenting */);
+    }
+
+    boolean removeActivity(ActivityRecord r, boolean reparenting) {
+        if (r.getTask() != this) {
+            throw new IllegalArgumentException(
+                    "Activity=" + r + " does not belong to task=" + this);
+        }
+
+        r.setTask(null /* task */, reparenting /* reparenting */);
+
+        if (mActivities.remove(r) && r.fullscreen) {
+            // Was previously in list.
+            numFullscreen--;
+        }
+        if (r.isPersistable()) {
+            mService.notifyTaskPersisterLocked(this, false);
+        }
+
+        if (inPinnedWindowingMode()) {
+            // We normally notify listeners of task stack changes on pause, however pinned stack
+            // activities are normally in the paused state so no notification will be sent there
+            // before the activity is removed. We send it here so instead.
+            mService.getTaskChangeNotificationController().notifyTaskStackChanged();
+        }
+
+        if (mActivities.isEmpty()) {
+            return !mReuseTask;
+        }
+        updateEffectiveIntent();
+        return false;
+    }
+
+    /**
+     * @return whether or not there are ONLY task overlay activities in the stack.
+     *         If {@param excludeFinishing} is set, then ignore finishing activities in the check.
+     *         If there are no task overlay activities, this call returns false.
+     */
+    boolean onlyHasTaskOverlayActivities(boolean excludeFinishing) {
+        int count = 0;
+        for (int i = mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = mActivities.get(i);
+            if (excludeFinishing && r.finishing) {
+                continue;
+            }
+            if (!r.mTaskOverlay) {
+                return false;
+            }
+            count++;
+        }
+        return count > 0;
+    }
+
+    boolean autoRemoveFromRecents() {
+        // We will automatically remove the task either if it has explicitly asked for
+        // this, or it is empty and has never contained an activity that got shown to
+        // the user.
+        return autoRemoveRecents || (mActivities.isEmpty() && !hasBeenVisible);
+    }
+
+    /**
+     * Completely remove all activities associated with an existing
+     * task starting at a specified index.
+     */
+    final void performClearTaskAtIndexLocked(int activityNdx, boolean pauseImmediately,
+            String reason) {
+        int numActivities = mActivities.size();
+        for ( ; activityNdx < numActivities; ++activityNdx) {
+            final ActivityRecord r = mActivities.get(activityNdx);
+            if (r.finishing) {
+                continue;
+            }
+            if (mStack == null) {
+                // Task was restored from persistent storage.
+                r.takeFromHistory();
+                mActivities.remove(activityNdx);
+                --activityNdx;
+                --numActivities;
+            } else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED, null,
+                    reason, false, pauseImmediately)) {
+                --activityNdx;
+                --numActivities;
+            }
+        }
+    }
+
+    /**
+     * Completely remove all activities associated with an existing task.
+     */
+    void performClearTaskLocked() {
+        mReuseTask = true;
+        performClearTaskAtIndexLocked(0, !PAUSE_IMMEDIATELY, "clear-task-all");
+        mReuseTask = false;
+    }
+
+    ActivityRecord performClearTaskForReuseLocked(ActivityRecord newR, int launchFlags) {
+        mReuseTask = true;
+        final ActivityRecord result = performClearTaskLocked(newR, launchFlags);
+        mReuseTask = false;
+        return result;
+    }
+
+    /**
+     * Perform clear operation as requested by
+     * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
+     * stack to the given task, then look for
+     * an instance of that activity in the stack and, if found, finish all
+     * activities on top of it and return the instance.
+     *
+     * @param newR Description of the new activity being started.
+     * @return Returns the old activity that should be continued to be used,
+     * or null if none was found.
+     */
+    final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
+        int numActivities = mActivities.size();
+        for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
+            ActivityRecord r = mActivities.get(activityNdx);
+            if (r.finishing) {
+                continue;
+            }
+            if (r.realActivity.equals(newR.realActivity)) {
+                // Here it is!  Now finish everything in front...
+                final ActivityRecord ret = r;
+
+                for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
+                    r = mActivities.get(activityNdx);
+                    if (r.finishing) {
+                        continue;
+                    }
+                    ActivityOptions opts = r.takeOptionsLocked();
+                    if (opts != null) {
+                        ret.updateOptionsLocked(opts);
+                    }
+                    if (mStack != null && mStack.finishActivityLocked(
+                            r, Activity.RESULT_CANCELED, null, "clear-task-stack", false)) {
+                        --activityNdx;
+                        --numActivities;
+                    }
+                }
+
+                // Finally, if this is a normal launch mode (that is, not
+                // expecting onNewIntent()), then we will finish the current
+                // instance of the activity so a new fresh one can be started.
+                if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
+                        && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0
+                        && !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
+                    if (!ret.finishing) {
+                        if (mStack != null) {
+                            mStack.finishActivityLocked(
+                                    ret, Activity.RESULT_CANCELED, null, "clear-task-top", false);
+                        }
+                        return null;
+                    }
+                }
+
+                return ret;
+            }
+        }
+
+        return null;
+    }
+
+    void removeTaskActivitiesLocked(boolean pauseImmediately, String reason) {
+        // Just remove the entire task.
+        performClearTaskAtIndexLocked(0, pauseImmediately, reason);
+    }
+
+    String lockTaskAuthToString() {
+        switch (mLockTaskAuth) {
+            case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK";
+            case LOCK_TASK_AUTH_PINNABLE: return "LOCK_TASK_AUTH_PINNABLE";
+            case LOCK_TASK_AUTH_LAUNCHABLE: return "LOCK_TASK_AUTH_LAUNCHABLE";
+            case LOCK_TASK_AUTH_WHITELISTED: return "LOCK_TASK_AUTH_WHITELISTED";
+            case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return "LOCK_TASK_AUTH_LAUNCHABLE_PRIV";
+            default: return "unknown=" + mLockTaskAuth;
+        }
+    }
+
+    void setLockTaskAuth() {
+        setLockTaskAuth(getRootActivity());
+    }
+
+    private void setLockTaskAuth(@Nullable ActivityRecord r) {
+        if (r == null) {
+            mLockTaskAuth = LOCK_TASK_AUTH_PINNABLE;
+            return;
+        }
+
+        final String pkg = (realActivity != null) ? realActivity.getPackageName() : null;
+        final LockTaskController lockTaskController = mService.getLockTaskController();
+        switch (r.lockTaskLaunchMode) {
+            case LOCK_TASK_LAUNCH_MODE_DEFAULT:
+                mLockTaskAuth = lockTaskController.isPackageWhitelisted(userId, pkg)
+                        ? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
+                break;
+
+            case LOCK_TASK_LAUNCH_MODE_NEVER:
+                mLockTaskAuth = LOCK_TASK_AUTH_DONT_LOCK;
+                break;
+
+            case LOCK_TASK_LAUNCH_MODE_ALWAYS:
+                mLockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+                break;
+
+            case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
+                mLockTaskAuth = lockTaskController.isPackageWhitelisted(userId, pkg)
+                        ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
+                break;
+        }
+        if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this +
+                " mLockTaskAuth=" + lockTaskAuthToString());
+    }
+
+    private boolean isResizeable(boolean checkSupportsPip) {
+        return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
+                || (checkSupportsPip && mSupportsPictureInPicture));
+    }
+
+    boolean isResizeable() {
+        return isResizeable(true /* checkSupportsPip */);
+    }
+
+    @Override
+    public boolean supportsSplitScreenWindowingMode() {
+        // A task can not be docked even if it is considered resizeable because it only supports
+        // picture-in-picture mode but has a non-resizeable resizeMode
+        return super.supportsSplitScreenWindowingMode()
+                && mService.mSupportsSplitScreenMultiWindow
+                && (mService.mForceResizableActivities
+                        || (isResizeable(false /* checkSupportsPip */)
+                                && !ActivityInfo.isPreserveOrientationMode(mResizeMode)));
+    }
+
+    /**
+     * Check whether this task can be launched on the specified display.
+     *
+     * @param displayId Target display id.
+     * @return {@code true} if either it is the default display or this activity can be put on a
+     *         secondary display.
+     */
+    boolean canBeLaunchedOnDisplay(int displayId) {
+        return mService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
+                -1 /* don't check PID */, -1 /* don't check UID */, null /* activityInfo */);
+    }
+
+    /**
+     * Check that a given bounds matches the application requested orientation.
+     *
+     * @param bounds The bounds to be tested.
+     * @return True if the requested bounds are okay for a resizing request.
+     */
+    private boolean canResizeToBounds(Rect bounds) {
+        if (bounds == null || !inFreeformWindowingMode()) {
+            // Note: If not on the freeform workspace, we ignore the bounds.
+            return true;
+        }
+        final boolean landscape = bounds.width() > bounds.height();
+        final Rect configBounds = getOverrideBounds();
+        if (mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION) {
+            return configBounds.isEmpty()
+                    || landscape == (configBounds.width() > configBounds.height());
+        }
+        return (mResizeMode != RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY || !landscape)
+                && (mResizeMode != RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY || landscape);
+    }
+
+    /**
+     * @return {@code true} if the task is being cleared for the purposes of being reused.
+     */
+    boolean isClearingToReuseTask() {
+        return mReuseTask;
+    }
+
+    /**
+     * Find the activity in the history stack within the given task.  Returns
+     * the index within the history at which it's found, or < 0 if not found.
+     */
+    final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
+        final ComponentName realActivity = r.realActivity;
+        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+            ActivityRecord candidate = mActivities.get(activityNdx);
+            if (candidate.finishing) {
+                continue;
+            }
+            if (candidate.realActivity.equals(realActivity)) {
+                return candidate;
+            }
+        }
+        return null;
+    }
+
+    /** Updates the last task description values. */
+    void updateTaskDescription() {
+        // Traverse upwards looking for any break between main task activities and
+        // utility activities.
+        int activityNdx;
+        final int numActivities = mActivities.size();
+        final boolean relinquish = numActivities != 0 &&
+                (mActivities.get(0).info.flags & FLAG_RELINQUISH_TASK_IDENTITY) != 0;
+        for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
+                ++activityNdx) {
+            final ActivityRecord r = mActivities.get(activityNdx);
+            if (relinquish && (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
+                // This will be the top activity for determining taskDescription. Pre-inc to
+                // overcome initial decrement below.
+                ++activityNdx;
+                break;
+            }
+            if (r.intent != null &&
+                    (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+                break;
+            }
+        }
+        if (activityNdx > 0) {
+            // Traverse downwards starting below break looking for set label, icon.
+            // Note that if there are activities in the task but none of them set the
+            // recent activity values, then we do not fall back to the last set
+            // values in the TaskRecord.
+            String label = null;
+            String iconFilename = null;
+            int iconResource = -1;
+            int colorPrimary = 0;
+            int colorBackground = 0;
+            int statusBarColor = 0;
+            int navigationBarColor = 0;
+            boolean topActivity = true;
+            for (--activityNdx; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = mActivities.get(activityNdx);
+                if (r.mTaskOverlay) {
+                    continue;
+                }
+                if (r.taskDescription != null) {
+                    if (label == null) {
+                        label = r.taskDescription.getLabel();
+                    }
+                    if (iconResource == -1) {
+                        iconResource = r.taskDescription.getIconResource();
+                    }
+                    if (iconFilename == null) {
+                        iconFilename = r.taskDescription.getIconFilename();
+                    }
+                    if (colorPrimary == 0) {
+                        colorPrimary = r.taskDescription.getPrimaryColor();
+                    }
+                    if (topActivity) {
+                        colorBackground = r.taskDescription.getBackgroundColor();
+                        statusBarColor = r.taskDescription.getStatusBarColor();
+                        navigationBarColor = r.taskDescription.getNavigationBarColor();
+                    }
+                }
+                topActivity = false;
+            }
+            lastTaskDescription = new TaskDescription(label, null, iconResource, iconFilename,
+                    colorPrimary, colorBackground, statusBarColor, navigationBarColor);
+            if (mWindowContainerController != null) {
+                mWindowContainerController.setTaskDescription(lastTaskDescription);
+            }
+            // Update the task affiliation color if we are the parent of the group
+            if (taskId == mAffiliatedTaskId) {
+                mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor();
+            }
+        }
+    }
+
+    int findEffectiveRootIndex() {
+        int effectiveNdx = 0;
+        final int topActivityNdx = mActivities.size() - 1;
+        for (int activityNdx = 0; activityNdx <= topActivityNdx; ++activityNdx) {
+            final ActivityRecord r = mActivities.get(activityNdx);
+            if (r.finishing) {
+                continue;
+            }
+            effectiveNdx = activityNdx;
+            if ((r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
+                break;
+            }
+        }
+        return effectiveNdx;
+    }
+
+    void updateEffectiveIntent() {
+        final int effectiveRootIndex = findEffectiveRootIndex();
+        final ActivityRecord r = mActivities.get(effectiveRootIndex);
+        setIntent(r);
+
+        // Update the task description when the activities change
+        updateTaskDescription();
+    }
+
+    private void adjustForMinimalTaskDimensions(Rect bounds) {
+        if (bounds == null) {
+            return;
+        }
+        int minWidth = mMinWidth;
+        int minHeight = mMinHeight;
+        // If the task has no requested minimal size, we'd like to enforce a minimal size
+        // so that the user can not render the task too small to manipulate. We don't need
+        // to do this for the pinned stack as the bounds are controlled by the system.
+        if (!inPinnedWindowingMode()) {
+            final int defaultMinSizeDp =
+                    mService.mStackSupervisor.mDefaultMinSizeOfResizeableTaskDp;
+            final ActivityDisplay display =
+                    mService.mStackSupervisor.getActivityDisplay(mStack.mDisplayId);
+            final float density =
+                    (float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+            final int defaultMinSize = (int) (defaultMinSizeDp * density);
+
+            if (minWidth == INVALID_MIN_SIZE) {
+                minWidth = defaultMinSize;
+            }
+            if (minHeight == INVALID_MIN_SIZE) {
+                minHeight = defaultMinSize;
+            }
+        }
+        final boolean adjustWidth = minWidth > bounds.width();
+        final boolean adjustHeight = minHeight > bounds.height();
+        if (!(adjustWidth || adjustHeight)) {
+            return;
+        }
+
+        final Rect configBounds = getOverrideBounds();
+        if (adjustWidth) {
+            if (!configBounds.isEmpty() && bounds.right == configBounds.right) {
+                bounds.left = bounds.right - minWidth;
+            } else {
+                // Either left bounds match, or neither match, or the previous bounds were
+                // fullscreen and we default to keeping left.
+                bounds.right = bounds.left + minWidth;
+            }
+        }
+        if (adjustHeight) {
+            if (!configBounds.isEmpty() && bounds.bottom == configBounds.bottom) {
+                bounds.top = bounds.bottom - minHeight;
+            } else {
+                // Either top bounds match, or neither match, or the previous bounds were
+                // fullscreen and we default to keeping top.
+                bounds.bottom = bounds.top + minHeight;
+            }
+        }
+    }
+
+    /**
+     * @return a new Configuration for this Task, given the provided {@param bounds} and
+     *         {@param insetBounds}.
+     */
+    Configuration computeNewOverrideConfigurationForBounds(Rect bounds, Rect insetBounds) {
+        // Compute a new override configuration for the given bounds, if fullscreen bounds
+        // (bounds == null), then leave the override config unset
+        final Configuration newOverrideConfig = new Configuration();
+        if (bounds != null) {
+            newOverrideConfig.setTo(getOverrideConfiguration());
+            mTmpRect.set(bounds);
+            adjustForMinimalTaskDimensions(mTmpRect);
+            computeOverrideConfiguration(newOverrideConfig, mTmpRect, insetBounds,
+                    mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
+        }
+
+        return newOverrideConfig;
+    }
+
+    /**
+     * Update task's override configuration based on the bounds.
+     * @param bounds The bounds of the task.
+     * @return True if the override configuration was updated.
+     */
+    boolean updateOverrideConfiguration(Rect bounds) {
+        return updateOverrideConfiguration(bounds, null /* insetBounds */);
+    }
+
+    void setLastNonFullscreenBounds(Rect bounds) {
+        if (mLastNonFullscreenBounds == null) {
+            mLastNonFullscreenBounds = new Rect(bounds);
+        } else {
+            mLastNonFullscreenBounds.set(bounds);
+        }
+    }
+
+    /**
+     * Update task's override configuration based on the bounds.
+     * @param bounds The bounds of the task.
+     * @param insetBounds The bounds used to calculate the system insets, which is used here to
+     *                    subtract the navigation bar/status bar size from the screen size reported
+     *                    to the application. See {@link IActivityTaskManager#resizeDockedStack}.
+     * @return True if the override configuration was updated.
+     */
+    boolean updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
+        if (equivalentOverrideBounds(bounds)) {
+            return false;
+        }
+        final Rect currentBounds = getOverrideBounds();
+
+        mTmpConfig.setTo(getOverrideConfiguration());
+        final Configuration newConfig = getOverrideConfiguration();
+
+        final boolean matchParentBounds = bounds == null || bounds.isEmpty();
+        final boolean persistBounds = getWindowConfiguration().persistTaskBounds();
+        if (matchParentBounds) {
+            if (!currentBounds.isEmpty() && persistBounds) {
+                setLastNonFullscreenBounds(currentBounds);
+            }
+            setBounds(null);
+            newConfig.unset();
+        } else {
+            mTmpRect.set(bounds);
+            adjustForMinimalTaskDimensions(mTmpRect);
+            setBounds(mTmpRect);
+
+            if (mStack == null || persistBounds) {
+                setLastNonFullscreenBounds(getOverrideBounds());
+            }
+            computeOverrideConfiguration(newConfig, mTmpRect, insetBounds,
+                    mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
+        }
+        onOverrideConfigurationChanged(newConfig);
+        return !mTmpConfig.equals(newConfig);
+    }
+
+    /**
+     * This should be called when an child activity changes state. This should only
+     * be called from
+     * {@link ActivityRecord#setState(ActivityState, String)} .
+     * @param record The {@link ActivityRecord} whose state has changed.
+     * @param state The new state.
+     * @param reason The reason for the change.
+     */
+    void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) {
+        final ActivityStack parent = getStack();
+
+        if (parent != null) {
+            parent.onActivityStateChanged(record, state, reason);
+        }
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newParentConfig) {
+        final boolean wasInMultiWindowMode = inMultiWindowMode();
+        super.onConfigurationChanged(newParentConfig);
+        if (wasInMultiWindowMode != inMultiWindowMode()) {
+            mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this);
+        }
+        // TODO: Should also take care of Pip mode changes here.
+    }
+
+    /** Clears passed config and fills it with new override values. */
+    // TODO(b/36505427): TaskRecord.computeOverrideConfiguration() is a utility method that doesn't
+    // depend on task or stacks, but uses those object to get the display to base the calculation
+    // on. Probably best to centralize calculations like this in ConfigurationContainer.
+    void computeOverrideConfiguration(Configuration config, Rect bounds, Rect insetBounds,
+            boolean overrideWidth, boolean overrideHeight) {
+        mTmpNonDecorBounds.set(bounds);
+        mTmpStableBounds.set(bounds);
+
+        config.unset();
+        final Configuration parentConfig = getParent().getConfiguration();
+
+        final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+
+        if (mStack != null) {
+            final StackWindowController stackController = mStack.getWindowContainerController();
+            stackController.adjustConfigurationForBounds(bounds, insetBounds,
+                    mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density,
+                    config, parentConfig, getWindowingMode());
+        } else {
+            throw new IllegalArgumentException("Expected stack when calculating override config");
+        }
+
+        config.orientation = (config.screenWidthDp <= config.screenHeightDp)
+                ? Configuration.ORIENTATION_PORTRAIT
+                : Configuration.ORIENTATION_LANDSCAPE;
+
+        // For calculating screen layout, we need to use the non-decor inset screen area for the
+        // calculation for compatibility reasons, i.e. screen area without system bars that could
+        // never go away in Honeycomb.
+        final int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
+        final int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
+        // We're only overriding LONG, SIZE and COMPAT parts of screenLayout, so we start override
+        // calculation with partial default.
+        // Reducing the screen layout starting from its parent config.
+        final int sl = parentConfig.screenLayout &
+                (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
+        final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
+        final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
+        config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
+    }
+
+    Rect updateOverrideConfigurationFromLaunchBounds() {
+        final Rect bounds = getLaunchBounds();
+        updateOverrideConfiguration(bounds);
+        if (bounds != null && !bounds.isEmpty()) {
+            // TODO: Review if we actually want to do this - we are setting the launch bounds
+            // directly here.
+            bounds.set(getOverrideBounds());
+        }
+        return bounds;
+    }
+
+    /** Updates the task's bounds and override configuration to match what is expected for the
+     * input stack. */
+    void updateOverrideConfigurationForStack(ActivityStack inStack) {
+        if (mStack != null && mStack == inStack) {
+            return;
+        }
+
+        if (inStack.inFreeformWindowingMode()) {
+            if (!isResizeable()) {
+                throw new IllegalArgumentException("Can not position non-resizeable task="
+                        + this + " in stack=" + inStack);
+            }
+            if (!matchParentBounds()) {
+                return;
+            }
+            if (mLastNonFullscreenBounds != null) {
+                updateOverrideConfiguration(mLastNonFullscreenBounds);
+            } else {
+                mService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
+            }
+        } else {
+            updateOverrideConfiguration(inStack.getOverrideBounds());
+        }
+    }
+
+    /** Returns the bounds that should be used to launch this task. */
+    Rect getLaunchBounds() {
+        if (mStack == null) {
+            return null;
+        }
+
+        final int windowingMode = getWindowingMode();
+        if (!isActivityTypeStandardOrUndefined()
+                || windowingMode == WINDOWING_MODE_FULLSCREEN
+                || (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) {
+            return isResizeable() ? mStack.getOverrideBounds() : null;
+        } else if (!getWindowConfiguration().persistTaskBounds()) {
+            return mStack.getOverrideBounds();
+        }
+        return mLastNonFullscreenBounds;
+    }
+
+    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
+        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+            final ActivityRecord r = mActivities.get(activityNdx);
+            if (r.visible) {
+                r.showStartingWindow(null /* prev */, false /* newTask */, taskSwitch);
+            }
+        }
+    }
+
+    void setRootProcess(WindowProcessController proc) {
+        clearRootProcess();
+        if (intent != null &&
+                (intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0) {
+            mRootProcess = proc;
+            mRootProcess.addRecentTask(this);
+        }
+    }
+
+    void clearRootProcess() {
+        if (mRootProcess != null) {
+            mRootProcess.removeRecentTask(this);
+            mRootProcess = null;
+        }
+    }
+
+    void clearAllPendingOptions() {
+        for (int i = getChildCount() - 1; i >= 0; i--) {
+            getChildAt(i).clearOptionsLocked(false /* withAbort */);
+        }
+    }
+
+    /**
+     * Fills in a {@link TaskInfo} with information from this task.
+     * @param info the {@link TaskInfo} to fill in
+     * @param reuseActivitiesReport a temporary activities report that we can reuse to fetch the
+     *                              running activities
+     */
+    void fillTaskInfo(TaskInfo info, TaskActivitiesReport reuseActivitiesReport) {
+        getNumRunningActivities(reuseActivitiesReport);
+        info.userId = userId;
+        info.stackId = getStackId();
+        info.taskId = taskId;
+        info.isRunning = getTopActivity() != null;
+        info.baseIntent = new Intent(getBaseIntent());
+        info.baseActivity = reuseActivitiesReport.base != null
+                ? reuseActivitiesReport.base.intent.getComponent()
+                : null;
+        info.topActivity = reuseActivitiesReport.top != null
+                ? reuseActivitiesReport.top.intent.getComponent()
+                : null;
+        info.origActivity = origActivity;
+        info.realActivity = realActivity;
+        info.numActivities = reuseActivitiesReport.numActivities;
+        info.lastActiveTime = lastActiveTime;
+        info.taskDescription = new ActivityManager.TaskDescription(lastTaskDescription);
+        info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
+        info.resizeMode = mResizeMode;
+        info.configuration.setTo(getConfiguration());
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.print("userId="); pw.print(userId);
+                pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
+                pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
+                pw.print(" mUserSetupComplete="); pw.print(mUserSetupComplete);
+                pw.print(" mCallingPackage="); pw.println(mCallingPackage);
+        if (affinity != null || rootAffinity != null) {
+            pw.print(prefix); pw.print("affinity="); pw.print(affinity);
+            if (affinity == null || !affinity.equals(rootAffinity)) {
+                pw.print(" root="); pw.println(rootAffinity);
+            } else {
+                pw.println();
+            }
+        }
+        if (voiceSession != null || voiceInteractor != null) {
+            pw.print(prefix); pw.print("VOICE: session=0x");
+            pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
+            pw.print(" interactor=0x");
+            pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor)));
+        }
+        if (intent != null) {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append(prefix); sb.append("intent={");
+            intent.toShortString(sb, false, true, false, true);
+            sb.append('}');
+            pw.println(sb.toString());
+        }
+        if (affinityIntent != null) {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append(prefix); sb.append("affinityIntent={");
+            affinityIntent.toShortString(sb, false, true, false, true);
+            sb.append('}');
+            pw.println(sb.toString());
+        }
+        if (origActivity != null) {
+            pw.print(prefix); pw.print("origActivity=");
+            pw.println(origActivity.flattenToShortString());
+        }
+        if (realActivity != null) {
+            pw.print(prefix); pw.print("realActivity=");
+            pw.println(realActivity.flattenToShortString());
+        }
+        if (autoRemoveRecents || isPersistable || !isActivityTypeStandard() || numFullscreen != 0) {
+            pw.print(prefix); pw.print("autoRemoveRecents="); pw.print(autoRemoveRecents);
+                    pw.print(" isPersistable="); pw.print(isPersistable);
+                    pw.print(" numFullscreen="); pw.print(numFullscreen);
+                    pw.print(" activityType="); pw.println(getActivityType());
+        }
+        if (rootWasReset || mNeverRelinquishIdentity || mReuseTask
+                || mLockTaskAuth != LOCK_TASK_AUTH_PINNABLE) {
+            pw.print(prefix); pw.print("rootWasReset="); pw.print(rootWasReset);
+                    pw.print(" mNeverRelinquishIdentity="); pw.print(mNeverRelinquishIdentity);
+                    pw.print(" mReuseTask="); pw.print(mReuseTask);
+                    pw.print(" mLockTaskAuth="); pw.println(lockTaskAuthToString());
+        }
+        if (mAffiliatedTaskId != taskId || mPrevAffiliateTaskId != INVALID_TASK_ID
+                || mPrevAffiliate != null || mNextAffiliateTaskId != INVALID_TASK_ID
+                || mNextAffiliate != null) {
+            pw.print(prefix); pw.print("affiliation="); pw.print(mAffiliatedTaskId);
+                    pw.print(" prevAffiliation="); pw.print(mPrevAffiliateTaskId);
+                    pw.print(" (");
+                    if (mPrevAffiliate == null) {
+                        pw.print("null");
+                    } else {
+                        pw.print(Integer.toHexString(System.identityHashCode(mPrevAffiliate)));
+                    }
+                    pw.print(") nextAffiliation="); pw.print(mNextAffiliateTaskId);
+                    pw.print(" (");
+                    if (mNextAffiliate == null) {
+                        pw.print("null");
+                    } else {
+                        pw.print(Integer.toHexString(System.identityHashCode(mNextAffiliate)));
+                    }
+                    pw.println(")");
+        }
+        pw.print(prefix); pw.print("Activities="); pw.println(mActivities);
+        if (!askedCompatMode || !inRecents || !isAvailable) {
+            pw.print(prefix); pw.print("askedCompatMode="); pw.print(askedCompatMode);
+                    pw.print(" inRecents="); pw.print(inRecents);
+                    pw.print(" isAvailable="); pw.println(isAvailable);
+        }
+        if (lastDescription != null) {
+            pw.print(prefix); pw.print("lastDescription="); pw.println(lastDescription);
+        }
+        if (mRootProcess != null) {
+            pw.print(prefix); pw.print("mRootProcess="); pw.println(mRootProcess);
+        }
+        pw.print(prefix); pw.print("stackId="); pw.println(getStackId());
+        pw.print(prefix + "hasBeenVisible=" + hasBeenVisible);
+                pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode));
+                pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture);
+                pw.print(" isResizeable=" + isResizeable());
+                pw.print(" lastActiveTime=" + lastActiveTime);
+                pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        if (stringName != null) {
+            sb.append(stringName);
+            sb.append(" U=");
+            sb.append(userId);
+            sb.append(" StackId=");
+            sb.append(getStackId());
+            sb.append(" sz=");
+            sb.append(mActivities.size());
+            sb.append('}');
+            return sb.toString();
+        }
+        sb.append("TaskRecord{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(" #");
+        sb.append(taskId);
+        if (affinity != null) {
+            sb.append(" A=");
+            sb.append(affinity);
+        } else if (intent != null) {
+            sb.append(" I=");
+            sb.append(intent.getComponent().flattenToShortString());
+        } else if (affinityIntent != null && affinityIntent.getComponent() != null) {
+            sb.append(" aI=");
+            sb.append(affinityIntent.getComponent().flattenToShortString());
+        } else {
+            sb.append(" ??");
+        }
+        stringName = sb.toString();
+        return toString();
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
+        proto.write(ID, taskId);
+        for (int i = mActivities.size() - 1; i >= 0; i--) {
+            ActivityRecord activity = mActivities.get(i);
+            activity.writeToProto(proto, ACTIVITIES);
+        }
+        proto.write(STACK_ID, mStack.mStackId);
+        if (mLastNonFullscreenBounds != null) {
+            mLastNonFullscreenBounds.writeToProto(proto, LAST_NON_FULLSCREEN_BOUNDS);
+        }
+        if (realActivity != null) {
+            proto.write(REAL_ACTIVITY, realActivity.flattenToShortString());
+        }
+        if (origActivity != null) {
+            proto.write(ORIG_ACTIVITY, origActivity.flattenToShortString());
+        }
+        proto.write(ACTIVITY_TYPE, getActivityType());
+        proto.write(RESIZE_MODE, mResizeMode);
+        // TODO: Remove, no longer needed with windowingMode.
+        proto.write(FULLSCREEN, matchParentBounds());
+
+        if (!matchParentBounds()) {
+            final Rect bounds = getOverrideBounds();
+            bounds.writeToProto(proto, BOUNDS);
+        }
+        proto.write(MIN_WIDTH, mMinWidth);
+        proto.write(MIN_HEIGHT, mMinHeight);
+        proto.end(token);
+    }
+
+    /**
+     * See {@link #getNumRunningActivities(TaskActivitiesReport)}.
+     */
+    static class TaskActivitiesReport {
+        int numRunning;
+        int numActivities;
+        ActivityRecord top;
+        ActivityRecord base;
+
+        void reset() {
+            numRunning = numActivities = 0;
+            top = base = null;
+        }
+    }
+
+    /**
+     * Saves this {@link TaskRecord} to XML using given serializer.
+     */
+    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+        if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
+
+        out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
+        if (realActivity != null) {
+            out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
+        }
+        out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended));
+        if (origActivity != null) {
+            out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
+        }
+        // Write affinity, and root affinity if it is different from affinity.
+        // We use the special string "@" for a null root affinity, so we can identify
+        // later whether we were given a root affinity or should just make it the
+        // same as the affinity.
+        if (affinity != null) {
+            out.attribute(null, ATTR_AFFINITY, affinity);
+            if (!affinity.equals(rootAffinity)) {
+                out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
+            }
+        } else if (rootAffinity != null) {
+            out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
+        }
+        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
+        out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
+        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
+        out.attribute(null, ATTR_USERID, String.valueOf(userId));
+        out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
+        out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
+        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
+        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
+        if (lastDescription != null) {
+            out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
+        }
+        if (lastTaskDescription != null) {
+            lastTaskDescription.saveToXml(out);
+        }
+        out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
+        out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
+        out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
+        out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
+        out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
+        out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
+        out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
+        out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
+                String.valueOf(mSupportsPictureInPicture));
+        if (mLastNonFullscreenBounds != null) {
+            out.attribute(
+                    null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString());
+        }
+        out.attribute(null, ATTR_MIN_WIDTH, String.valueOf(mMinWidth));
+        out.attribute(null, ATTR_MIN_HEIGHT, String.valueOf(mMinHeight));
+        out.attribute(null, ATTR_PERSIST_TASK_VERSION, String.valueOf(PERSIST_TASK_VERSION));
+
+        if (affinityIntent != null) {
+            out.startTag(null, TAG_AFFINITYINTENT);
+            affinityIntent.saveToXml(out);
+            out.endTag(null, TAG_AFFINITYINTENT);
+        }
+
+        if (intent != null) {
+            out.startTag(null, TAG_INTENT);
+            intent.saveToXml(out);
+            out.endTag(null, TAG_INTENT);
+        }
+
+        final ArrayList<ActivityRecord> activities = mActivities;
+        final int numActivities = activities.size();
+        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
+            final ActivityRecord r = activities.get(activityNdx);
+            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
+                    ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
+                            | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) &&
+                            activityNdx > 0) {
+                // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
+                break;
+            }
+            out.startTag(null, TAG_ACTIVITY);
+            r.saveToXml(out);
+            out.endTag(null, TAG_ACTIVITY);
+        }
+    }
+
+    @VisibleForTesting
+    static TaskRecordFactory getTaskRecordFactory() {
+        if (sTaskRecordFactory == null) {
+            setTaskRecordFactory(new TaskRecordFactory());
+        }
+        return sTaskRecordFactory;
+    }
+
+    static void setTaskRecordFactory(TaskRecordFactory factory) {
+        sTaskRecordFactory = factory;
+    }
+
+    static TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
+            Intent intent, IVoiceInteractionSession voiceSession,
+            IVoiceInteractor voiceInteractor) {
+        return getTaskRecordFactory().create(
+                service, taskId, info, intent, voiceSession, voiceInteractor);
+    }
+
+    static TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
+            Intent intent, TaskDescription taskDescription) {
+        return getTaskRecordFactory().create(service, taskId, info, intent, taskDescription);
+    }
+
+    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+            throws IOException, XmlPullParserException {
+        return getTaskRecordFactory().restoreFromXml(in, stackSupervisor);
+    }
+
+    /**
+     * A factory class used to create {@link TaskRecord} or its subclass if any. This can be
+     * specified when system boots by setting it with
+     * {@link #setTaskRecordFactory(TaskRecordFactory)}.
+     */
+    static class TaskRecordFactory {
+
+        TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
+                Intent intent, IVoiceInteractionSession voiceSession,
+                IVoiceInteractor voiceInteractor) {
+            return new TaskRecord(
+                    service, taskId, info, intent, voiceSession, voiceInteractor);
+        }
+
+        TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
+                Intent intent, TaskDescription taskDescription) {
+            return new TaskRecord(service, taskId, info, intent, taskDescription);
+        }
+
+        /**
+         * Should only be used when we're restoring {@link TaskRecord} from storage.
+         */
+        TaskRecord create(ActivityTaskManagerService service, int taskId, Intent intent,
+                Intent affinityIntent, String affinity, String rootAffinity,
+                ComponentName realActivity, ComponentName origActivity, boolean rootWasReset,
+                boolean autoRemoveRecents, boolean askedCompatMode, int userId,
+                int effectiveUid, String lastDescription, ArrayList<ActivityRecord> activities,
+                long lastTimeMoved, boolean neverRelinquishIdentity,
+                TaskDescription lastTaskDescription, int taskAffiliation, int prevTaskId,
+                int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
+                int resizeMode, boolean supportsPictureInPicture, boolean realActivitySuspended,
+                boolean userSetupComplete, int minWidth, int minHeight) {
+            return new TaskRecord(service, taskId, intent, affinityIntent, affinity,
+                    rootAffinity, realActivity, origActivity, rootWasReset, autoRemoveRecents,
+                    askedCompatMode, userId, effectiveUid, lastDescription, activities,
+                    lastTimeMoved, neverRelinquishIdentity, lastTaskDescription, taskAffiliation,
+                    prevTaskId, nextTaskId, taskAffiliationColor, callingUid, callingPackage,
+                    resizeMode, supportsPictureInPicture, realActivitySuspended, userSetupComplete,
+                    minWidth, minHeight);
+        }
+
+        TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+                throws IOException, XmlPullParserException {
+            Intent intent = null;
+            Intent affinityIntent = null;
+            ArrayList<ActivityRecord> activities = new ArrayList<>();
+            ComponentName realActivity = null;
+            boolean realActivitySuspended = false;
+            ComponentName origActivity = null;
+            String affinity = null;
+            String rootAffinity = null;
+            boolean hasRootAffinity = false;
+            boolean rootHasReset = false;
+            boolean autoRemoveRecents = false;
+            boolean askedCompatMode = false;
+            int taskType = 0;
+            int userId = 0;
+            boolean userSetupComplete = true;
+            int effectiveUid = -1;
+            String lastDescription = null;
+            long lastTimeOnTop = 0;
+            boolean neverRelinquishIdentity = true;
+            int taskId = INVALID_TASK_ID;
+            final int outerDepth = in.getDepth();
+            TaskDescription taskDescription = new TaskDescription();
+            int taskAffiliation = INVALID_TASK_ID;
+            int taskAffiliationColor = 0;
+            int prevTaskId = INVALID_TASK_ID;
+            int nextTaskId = INVALID_TASK_ID;
+            int callingUid = -1;
+            String callingPackage = "";
+            int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+            boolean supportsPictureInPicture = false;
+            Rect lastNonFullscreenBounds = null;
+            int minWidth = INVALID_MIN_SIZE;
+            int minHeight = INVALID_MIN_SIZE;
+            int persistTaskVersion = 0;
+
+            for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
+                final String attrName = in.getAttributeName(attrNdx);
+                final String attrValue = in.getAttributeValue(attrNdx);
+                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
+                        attrName + " value=" + attrValue);
+                switch (attrName) {
+                    case ATTR_TASKID:
+                        if (taskId == INVALID_TASK_ID) taskId = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_REALACTIVITY:
+                        realActivity = ComponentName.unflattenFromString(attrValue);
+                        break;
+                    case ATTR_REALACTIVITY_SUSPENDED:
+                        realActivitySuspended = Boolean.valueOf(attrValue);
+                        break;
+                    case ATTR_ORIGACTIVITY:
+                        origActivity = ComponentName.unflattenFromString(attrValue);
+                        break;
+                    case ATTR_AFFINITY:
+                        affinity = attrValue;
+                        break;
+                    case ATTR_ROOT_AFFINITY:
+                        rootAffinity = attrValue;
+                        hasRootAffinity = true;
+                        break;
+                    case ATTR_ROOTHASRESET:
+                        rootHasReset = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_AUTOREMOVERECENTS:
+                        autoRemoveRecents = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_ASKEDCOMPATMODE:
+                        askedCompatMode = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_USERID:
+                        userId = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_USER_SETUP_COMPLETE:
+                        userSetupComplete = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_EFFECTIVE_UID:
+                        effectiveUid = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_TASKTYPE:
+                        taskType = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_LASTDESCRIPTION:
+                        lastDescription = attrValue;
+                        break;
+                    case ATTR_LASTTIMEMOVED:
+                        lastTimeOnTop = Long.parseLong(attrValue);
+                        break;
+                    case ATTR_NEVERRELINQUISH:
+                        neverRelinquishIdentity = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_TASK_AFFILIATION:
+                        taskAffiliation = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_PREV_AFFILIATION:
+                        prevTaskId = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_NEXT_AFFILIATION:
+                        nextTaskId = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_TASK_AFFILIATION_COLOR:
+                        taskAffiliationColor = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_CALLING_UID:
+                        callingUid = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_CALLING_PACKAGE:
+                        callingPackage = attrValue;
+                        break;
+                    case ATTR_RESIZE_MODE:
+                        resizeMode = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_SUPPORTS_PICTURE_IN_PICTURE:
+                        supportsPictureInPicture = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_NON_FULLSCREEN_BOUNDS:
+                        lastNonFullscreenBounds = Rect.unflattenFromString(attrValue);
+                        break;
+                    case ATTR_MIN_WIDTH:
+                        minWidth = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_MIN_HEIGHT:
+                        minHeight = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_PERSIST_TASK_VERSION:
+                        persistTaskVersion = Integer.parseInt(attrValue);
+                        break;
+                    default:
+                        if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
+                            taskDescription.restoreFromXml(attrName, attrValue);
+                        } else {
+                            Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
+                        }
+                }
+            }
+
+            int event;
+            while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+                    (event != XmlPullParser.END_TAG || in.getDepth() >= outerDepth)) {
+                if (event == XmlPullParser.START_TAG) {
+                    final String name = in.getName();
+                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
+                            "TaskRecord: START_TAG name=" + name);
+                    if (TAG_AFFINITYINTENT.equals(name)) {
+                        affinityIntent = Intent.restoreFromXml(in);
+                    } else if (TAG_INTENT.equals(name)) {
+                        intent = Intent.restoreFromXml(in);
+                    } else if (TAG_ACTIVITY.equals(name)) {
+                        ActivityRecord activity =
+                                ActivityRecord.restoreFromXml(in, stackSupervisor);
+                        if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
+                                activity);
+                        if (activity != null) {
+                            activities.add(activity);
+                        }
+                    } else {
+                        handleUnknownTag(name, in);
+                    }
+                }
+            }
+            if (!hasRootAffinity) {
+                rootAffinity = affinity;
+            } else if ("@".equals(rootAffinity)) {
+                rootAffinity = null;
+            }
+            if (effectiveUid <= 0) {
+                Intent checkIntent = intent != null ? intent : affinityIntent;
+                effectiveUid = 0;
+                if (checkIntent != null) {
+                    IPackageManager pm = AppGlobals.getPackageManager();
+                    try {
+                        ApplicationInfo ai = pm.getApplicationInfo(
+                                checkIntent.getComponent().getPackageName(),
+                                PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                        | PackageManager.MATCH_DISABLED_COMPONENTS, userId);
+                        if (ai != null) {
+                            effectiveUid = ai.uid;
+                        }
+                    } catch (RemoteException e) {
+                    }
+                }
+                Slog.w(TAG, "Updating task #" + taskId + " for " + checkIntent
+                        + ": effectiveUid=" + effectiveUid);
+            }
+
+            if (persistTaskVersion < 1) {
+                // We need to convert the resize mode of home activities saved before version one if
+                // they are marked as RESIZE_MODE_RESIZEABLE to
+                // RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION since we didn't have that differentiation
+                // before version 1 and the system didn't resize home activities before then.
+                if (taskType == 1 /* old home type */ && resizeMode == RESIZE_MODE_RESIZEABLE) {
+                    resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+                }
+            } else {
+                // This activity has previously marked itself explicitly as both resizeable and
+                // supporting picture-in-picture.  Since there is no longer a requirement for
+                // picture-in-picture activities to be resizeable, we can mark this simply as
+                // resizeable and supporting picture-in-picture separately.
+                if (resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED) {
+                    resizeMode = RESIZE_MODE_RESIZEABLE;
+                    supportsPictureInPicture = true;
+                }
+            }
+
+            final TaskRecord task = create(stackSupervisor.mService,
+                    taskId, intent, affinityIntent,
+                    affinity, rootAffinity, realActivity, origActivity, rootHasReset,
+                    autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
+                    activities, lastTimeOnTop, neverRelinquishIdentity, taskDescription,
+                    taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
+                    callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
+                    userSetupComplete, minWidth, minHeight);
+            task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
+            task.setBounds(lastNonFullscreenBounds);
+
+            for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
+                activities.get(activityNdx).setTask(task);
+            }
+
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
+            return task;
+        }
+
+        void handleUnknownTag(String name, XmlPullParser in)
+                throws IOException, XmlPullParserException {
+            Slog.e(TAG, "restoreTask: Unexpected name=" + name);
+            XmlUtils.skipCurrentTag(in);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index 7bf4edb..8175c4a 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -58,7 +58,7 @@
     @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
             boolean reducedResolution) {
 
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             // Try the running cache.
             final CacheEntry entry = mRunningCache.get(taskId);
             if (entry != null) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index ef63b9b..b84d20d 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -129,8 +129,8 @@
         mPersister.start();
     }
 
-    void onTransitionStarting() {
-        handleClosingApps(mService.mClosingApps);
+    void onTransitionStarting(DisplayContent displayContent) {
+        handleClosingApps(displayContent.mClosingApps);
     }
 
     /**
@@ -436,7 +436,7 @@
         // We can't take a snapshot when screen is off, so take a snapshot now!
         mHandler.post(() -> {
             try {
-                synchronized (mService.mWindowMap) {
+                synchronized (mService.mGlobalLock) {
                     mTmpTasks.clear();
                     mService.mRoot.forAllTasks(task -> {
                         if (task.isVisible()) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 67d2be8..a7b0272 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -149,7 +149,7 @@
         final int windowFlags;
         final int windowPrivateFlags;
         final int currentOrientation;
-        synchronized (service.mWindowMap) {
+        synchronized (service.mGlobalLock) {
             final WindowState mainWindow = token.findMainWindow();
             final Task task = token.getTask();
             if (task == null) {
@@ -248,7 +248,7 @@
 
     @Override
     public void remove() {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             final long now = SystemClock.uptimeMillis();
             if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) {
                 mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
@@ -288,7 +288,7 @@
         } else {
             drawSizeMatchSnapshot(buffer);
         }
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             mShownTime = SystemClock.uptimeMillis();
             mHasDrawn = true;
         }
@@ -422,7 +422,7 @@
                 case MSG_REPORT_DRAW:
                     final boolean hasDrawn;
                     final TaskSnapshotSurface surface = (TaskSnapshotSurface) msg.obj;
-                    synchronized (surface.mService.mWindowMap) {
+                    synchronized (surface.mService.mGlobalLock) {
                         hasDrawn = surface.mHasDrawn;
                     }
                     if (hasDrawn) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 00caceb..4dc2b8e 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -23,6 +23,9 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -32,6 +35,7 @@
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
+
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS;
 import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
@@ -60,13 +64,16 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Surface;
 import android.view.SurfaceControl;
+
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
 import com.android.server.EventLogTags;
+
 import java.io.PrintWriter;
 
 public class TaskStack extends WindowContainer<Task> implements
@@ -99,7 +106,7 @@
      */
     private final Rect mFullyAdjustedImeBounds = new Rect();
 
-    // Device rotation as of the last time {@link #mBounds} was set.
+    // Display rotation as of the last time {@link #mBounds} was set.
     private int mRotation;
 
     /** Density as of last time {@link #mBounds} was set. */
@@ -140,10 +147,6 @@
     private Rect mBoundsAnimationTarget = new Rect();
     private Rect mBoundsAnimationSourceHintBounds = new Rect();
 
-    // Temporary storage for the new bounds that should be used after the configuration change.
-    // Will be cleared once the client retrieves the new bounds via getBoundsForNewConfiguration().
-    private final Rect mBoundsAfterRotation = new Rect();
-
     Rect mPreAnimationBounds = new Rect();
 
     private Dimmer mDimmer = new Dimmer(this);
@@ -289,8 +292,9 @@
     private int setBounds(Rect existing, Rect bounds) {
         int rotation = Surface.ROTATION_0;
         int density = DENSITY_DPI_UNDEFINED;
-        if (mDisplayContent != null) {
-            mDisplayContent.getBounds(mTmpRect);
+        WindowContainer parent = getParent();
+        if (parent != null) {
+            parent.getBounds(mTmpRect);
             rotation = mDisplayContent.getDisplayInfo().rotation;
             density = mDisplayContent.getDisplayInfo().logicalDensityDpi;
         }
@@ -440,20 +444,33 @@
         // If the rotation or density didn't match, we'll update it in onConfigurationChanged.
     }
 
-    /** @return true if bounds were updated to some non-empty value. */
-    boolean updateBoundsAfterConfigChange() {
-        if (mDisplayContent == null) {
-            // If the stack is already detached we're not updating anything,
-            // as it's going away soon anyway.
-            return false;
-        }
-
-        if (inPinnedWindowingMode()) {
-            getAnimationOrCurrentBounds(mTmpRect2);
+    /**
+     * Updates the passed-in {@code inOutBounds} based on how it would change when this container's
+     * override configuration is applied to the specified {@code parentConfig} and
+     * {@code prevConfig}. This gets run *after* the override configuration is updated, so it's
+     * safe to rely on wm hierarchy state in here (though eventually this dependence should be
+     * removed).
+     *
+     * This does NOT modify this TaskStack's configuration. However, it does, for the time-being,
+     * update various controller state (pinned/docked).
+     *
+     * @param parentConfig a parent configuration to compute relative to.
+     * @param prevConfig the full configuration used to produce the incoming {@code inOutBounds}.
+     * @param inOutBounds the bounds to update (both input and output).
+     * @return true if bounds were updated to some non-empty value. */
+    boolean updateBoundsForConfigChange(
+            Configuration parentConfig, Configuration prevConfig, Rect inOutBounds) {
+        if (getOverrideWindowingMode() == WINDOWING_MODE_PINNED) {
+            if ((mBoundsAnimatingRequested || mBoundsAnimating)
+                    && !mBoundsAnimationTarget.isEmpty()) {
+                getFinalAnimationBounds(mTmpRect2);
+            } else {
+                mTmpRect2.set(prevConfig.windowConfiguration.getBounds());
+            }
             boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
                     mTmpRect2, mTmpRect3);
             if (updated) {
-                mBoundsAfterRotation.set(mTmpRect3);
+                inOutBounds.set(mTmpRect3);
 
                 // Once we've set the bounds based on the rotation of the old bounds in the new
                 // orientation, clear the animation target bounds since they are obsolete, and
@@ -461,72 +478,75 @@
                 mBoundsAnimationTarget.setEmpty();
                 mBoundsAnimationSourceHintBounds.setEmpty();
                 mCancelCurrentBoundsAnimation = true;
-                return true;
             }
+            return updated;
         }
 
-        final int newRotation = getDisplayInfo().rotation;
-        final int newDensity = getDisplayInfo().logicalDensityDpi;
+        final int newRotation = parentConfig.windowConfiguration.getRotation();
+        final int newDensity = parentConfig.densityDpi;
 
-        if (mRotation == newRotation && mDensity == newDensity) {
-            // Nothing to do here as we already update the state in updateDisplayInfo.
+        if (prevConfig.windowConfiguration.getRotation() == newRotation
+                && prevConfig.densityDpi == newDensity) {
             return false;
         }
 
         if (matchParentBounds()) {
-            // Update stack bounds again since rotation changed since updateDisplayInfo().
-            setBounds(null);
-            // Return false since we don't need the client to resize.
             return false;
         }
 
-        mTmpRect2.set(getRawBounds());
-        mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
-        if (inSplitScreenPrimaryWindowingMode()) {
-            repositionPrimarySplitScreenStackAfterRotation(mTmpRect2);
-            snapDockedStackAfterRotation(mTmpRect2);
-            final int newDockSide = getDockSide(mTmpRect2);
+        mDisplayContent.rotateBounds(parentConfig.windowConfiguration.getBounds(),
+                prevConfig.windowConfiguration.getRotation(), newRotation, inOutBounds);
+        if (getOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                || getOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+            boolean primary = getOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+            repositionSplitScreenStackAfterRotation(parentConfig, primary, inOutBounds);
+            final DisplayCutout cutout = mDisplayContent.getDisplayInfo().displayCutout;
+            snapDockedStackAfterRotation(parentConfig, cutout, inOutBounds);
+            if (primary) {
+                final int newDockSide = getDockSide(mDisplayContent, parentConfig, inOutBounds);
 
-            // Update the dock create mode and clear the dock create bounds, these
-            // might change after a rotation and the original values will be invalid.
-            mService.setDockedStackCreateStateLocked(
-                    (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
-                            ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
-                            : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
-                    null);
-            mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
+                // Update the dock create mode and clear the dock create bounds, these
+                // might change after a rotation and the original values will be invalid.
+                mService.setDockedStackCreateStateLocked(
+                        (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
+                                ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
+                                : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
+                        null);
+                mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
+            }
         }
 
-        mBoundsAfterRotation.set(mTmpRect2);
         return true;
     }
 
-    void getBoundsForNewConfiguration(Rect outBounds) {
-        outBounds.set(mBoundsAfterRotation);
-        mBoundsAfterRotation.setEmpty();
-    }
-
     /**
      * Some primary split screen sides are not allowed by the policy. This method queries the policy
      * and moves the primary stack around if needed.
      *
-     * @param inOutBounds the bounds of the primary stack to adjust
+     * @param parentConfig the configuration of the stack's parent.
+     * @param primary true if adjusting the primary docked stack, false for secondary.
+     * @param inOutBounds the bounds of the stack to adjust.
      */
-    private void repositionPrimarySplitScreenStackAfterRotation(Rect inOutBounds) {
-        int dockSide = getDockSide(inOutBounds);
-        if (mDisplayContent.getDockedDividerController().canPrimaryStackDockTo(dockSide)) {
+    void repositionSplitScreenStackAfterRotation(Configuration parentConfig, boolean primary,
+            Rect inOutBounds) {
+        final int dockSide = getDockSide(mDisplayContent, parentConfig, inOutBounds);
+        final int otherDockSide = DockedDividerUtils.invertDockSide(dockSide);
+        final int primaryDockSide = primary ? dockSide : otherDockSide;
+        if (mDisplayContent.getDockedDividerController()
+                .canPrimaryStackDockTo(primaryDockSide,
+                        parentConfig.windowConfiguration.getBounds(),
+                        parentConfig.windowConfiguration.getRotation())) {
             return;
         }
-        mDisplayContent.getBounds(mTmpRect);
-        dockSide = DockedDividerUtils.invertDockSide(dockSide);
-        switch (dockSide) {
+        final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
+        switch (otherDockSide) {
             case DOCKED_LEFT:
                 int movement = inOutBounds.left;
                 inOutBounds.left -= movement;
                 inOutBounds.right -= movement;
                 break;
             case DOCKED_RIGHT:
-                movement = mTmpRect.right - inOutBounds.right;
+                movement = parentBounds.right - inOutBounds.right;
                 inOutBounds.left += movement;
                 inOutBounds.right += movement;
                 break;
@@ -536,7 +556,7 @@
                 inOutBounds.bottom -= movement;
                 break;
             case DOCKED_BOTTOM:
-                movement = mTmpRect.bottom - inOutBounds.bottom;
+                movement = parentBounds.bottom - inOutBounds.bottom;
                 inOutBounds.top += movement;
                 inOutBounds.bottom += movement;
                 break;
@@ -546,22 +566,22 @@
     /**
      * Snaps the bounds after rotation to the closest snap target for the docked stack.
      */
-    private void snapDockedStackAfterRotation(Rect outBounds) {
+    void snapDockedStackAfterRotation(Configuration parentConfig, DisplayCutout displayCutout,
+            Rect outBounds) {
 
         // Calculate the current position.
-        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
         final int dividerSize = mDisplayContent.getDockedDividerController().getContentWidth();
-        final int dockSide = getDockSide(outBounds);
+        final int dockSide = getDockSide(parentConfig, outBounds);
         final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
                 dockSide, dividerSize);
-        final int displayWidth = displayInfo.logicalWidth;
-        final int displayHeight = displayInfo.logicalHeight;
+        final int displayWidth = parentConfig.windowConfiguration.getBounds().width();
+        final int displayHeight = parentConfig.windowConfiguration.getBounds().height();
 
         // Snap the position to a target.
-        final int rotation = displayInfo.rotation;
-        final int orientation = mDisplayContent.getConfiguration().orientation;
+        final int rotation = parentConfig.windowConfiguration.getRotation();
+        final int orientation = parentConfig.orientation;
         mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight,
-                displayInfo.displayCutout, outBounds);
+                displayCutout, outBounds);
         final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
                 mService.mContext.getResources(), displayWidth, displayHeight,
                 dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds,
@@ -570,7 +590,7 @@
 
         // Recalculate the bounds based on the position of the target.
         DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
-                outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight,
+                outBounds, displayWidth, displayHeight,
                 dividerSize);
     }
 
@@ -1470,27 +1490,27 @@
      * information which side of the screen was the dock anchored.
      */
     int getDockSide() {
-        return getDockSide(getRawBounds());
+        return getDockSide(mDisplayContent.getConfiguration(), getRawBounds());
     }
 
     int getDockSideForDisplay(DisplayContent dc) {
-        return getDockSide(dc, getRawBounds());
+        return getDockSide(dc, dc.getConfiguration(), getRawBounds());
     }
 
-    private int getDockSide(Rect bounds) {
+    int getDockSide(Configuration parentConfig, Rect bounds) {
         if (mDisplayContent == null) {
             return DOCKED_INVALID;
         }
-        return getDockSide(mDisplayContent, bounds);
+        return getDockSide(mDisplayContent, parentConfig, bounds);
     }
 
-    private int getDockSide(DisplayContent dc, Rect bounds) {
+    private int getDockSide(DisplayContent dc, Configuration parentConfig, Rect bounds) {
         if (!inSplitScreenWindowingMode()) {
             return DOCKED_INVALID;
         }
-        dc.getBounds(mTmpRect);
-        final int orientation = dc.getConfiguration().orientation;
-        return dc.getDockedDividerController().getDockSide(bounds, mTmpRect, orientation);
+        return dc.getDockedDividerController().getDockSide(bounds,
+                parentConfig.windowConfiguration.getBounds(),
+                parentConfig.orientation, parentConfig.windowConfiguration.getRotation());
     }
 
     boolean hasTaskForUser(int userId) {
@@ -1619,7 +1639,7 @@
 
     public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) {
         // Hold the lock since this is called from the BoundsAnimator running on the UiThread
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             if (mCancelCurrentBoundsAnimation) {
                 return false;
             }
@@ -1638,13 +1658,13 @@
             return;
         }
 
-        mService.mBoundsAnimationController.onAllWindowsDrawn();
+        getDisplayContent().mBoundsAnimationController.onAllWindowsDrawn();
     }
 
     @Override  // AnimatesBounds
     public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
         // Hold the lock since this is called from the BoundsAnimator running on the UiThread
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             if (!isAttached()) {
                 // Don't run the animation if the stack is already detached
                 return false;
@@ -1723,7 +1743,7 @@
 
     @Override
     public boolean isAttached() {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             return mDisplayContent != null;
         }
     }
@@ -1732,7 +1752,7 @@
      * Called immediately prior to resizing the tasks at the end of the pinned stack animation.
      */
     public void onPipAnimationEndResize() {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             mBoundsAnimating = false;
             for (int i = 0; i < mChildren.size(); i++) {
                 final Task t = mChildren.get(i);
@@ -1744,7 +1764,7 @@
 
     @Override
     public boolean shouldDeferStartOnMoveToFullscreen() {
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             // Workaround for the recents animation -- normally we need to wait for the new activity
             // to show before starting the PiP animation, but because we start and show the home
             // activity early for the recents animation prior to the PiP animation starting, there
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 52f8510..53d2cb0 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -47,7 +47,7 @@
         mDisplayContent = displayContent;
         mHandler = new Handler(mService.mH.getLooper());
         mMoveDisplayToTop = () -> {
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 mDisplayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
                         mDisplayContent, true /* includingParents */);
             }
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 8b634b1..59b2055 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -65,7 +65,7 @@
         mTaskId = taskId;
         mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
 
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (DEBUG_STACK) Slog.i(TAG_WM, "TaskWindowContainerController: taskId=" + taskId
                     + " stack=" + stackController + " bounds=" + bounds);
 
@@ -93,7 +93,7 @@
 
     @Override
     public void removeContainer() {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 if (DEBUG_STACK) Slog.i(TAG_WM, "removeTask: could not find taskId=" + mTaskId);
                 return;
@@ -108,7 +108,7 @@
     }
 
     public void positionChildAt(AppWindowContainerController childController, int position) {
-        synchronized(mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             final AppWindowToken aToken = childController.mContainer;
             if (aToken == null) {
                 Slog.w(TAG_WM,
@@ -125,7 +125,7 @@
     }
 
     public void reparent(StackWindowController stackController, int position, boolean moveParents) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (DEBUG_STACK) Slog.i(TAG_WM, "reparent: moving taskId=" + mTaskId
                     + " to stack=" + stackController + " at " + position);
             if (mContainer == null) {
@@ -144,7 +144,7 @@
     }
 
     public void setResizeable(int resizeMode) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer != null) {
                 mContainer.setResizeable(resizeMode);
             }
@@ -152,7 +152,7 @@
     }
 
     public void resize(boolean relayout, boolean forced) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 throw new IllegalArgumentException("resizeTask: taskId " + mTaskId + " not found.");
             }
@@ -165,7 +165,7 @@
     }
 
     public void getBounds(Rect bounds) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer != null) {
                 mContainer.getBounds(bounds);
                 return;
@@ -180,7 +180,7 @@
      * @param resizing Whether to put the task into drag resize mode.
      */
     public void setTaskDockedResizing(boolean resizing) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 Slog.w(TAG_WM, "setTaskDockedResizing: taskId " + mTaskId + " not found.");
                 return;
@@ -190,7 +190,7 @@
     }
 
     public void cancelWindowTransition() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 Slog.w(TAG_WM, "cancelWindowTransition: taskId " + mTaskId + " not found.");
                 return;
@@ -200,7 +200,7 @@
     }
 
     public void setTaskDescription(TaskDescription taskDescription) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 Slog.w(TAG_WM, "setTaskDescription: taskId " + mTaskId + " not found.");
                 return;
@@ -210,7 +210,7 @@
     }
 
     public boolean isDragResizing() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             return mContainer.isDragResizing();
         }
     }
diff --git a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
index eb751fa..01abcab 100644
--- a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
+++ b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
@@ -60,8 +60,11 @@
 
     private final WindowManagerService mService;
 
-    UnknownAppVisibilityController(WindowManagerService service) {
+    private final DisplayContent mDisplayContent;
+
+    UnknownAppVisibilityController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
+        mDisplayContent = displayContent;
     }
 
     boolean allResolved() {
@@ -128,7 +131,8 @@
         int state = mUnknownApps.get(appWindow);
         if (state == UNKNOWN_STATE_WAITING_RELAYOUT) {
             mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE);
-            mService.notifyKeyguardFlagsChanged(this::notifyVisibilitiesUpdated);
+            mService.notifyKeyguardFlagsChanged(this::notifyVisibilitiesUpdated,
+                    appWindow.getDisplayContent().getDisplayId());
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/UnsupportedCompileSdkDialog.java b/services/core/java/com/android/server/wm/UnsupportedCompileSdkDialog.java
new file mode 100644
index 0000000..6a878b9
--- /dev/null
+++ b/services/core/java/com/android/server/wm/UnsupportedCompileSdkDialog.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+
+import com.android.internal.R;
+import com.android.server.utils.AppInstallerUtil;
+
+public class UnsupportedCompileSdkDialog {
+    private final AlertDialog mDialog;
+    private final String mPackageName;
+
+    public UnsupportedCompileSdkDialog(final AppWarnings manager, Context context,
+            ApplicationInfo appInfo) {
+        mPackageName = appInfo.packageName;
+
+        final PackageManager pm = context.getPackageManager();
+        final CharSequence label = appInfo.loadSafeLabel(pm,
+                PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE
+                        | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
+        final CharSequence message = context.getString(R.string.unsupported_compile_sdk_message,
+                label);
+
+        final AlertDialog.Builder builder = new AlertDialog.Builder(context)
+                .setPositiveButton(R.string.ok, null)
+                .setMessage(message)
+                .setView(R.layout.unsupported_compile_sdk_dialog_content);
+
+        // If we might be able to update the app, show a button.
+        final Intent installerIntent = AppInstallerUtil.createIntent(context, appInfo.packageName);
+        if (installerIntent != null) {
+                builder.setNeutralButton(R.string.unsupported_compile_sdk_check_update,
+                        (dialog, which) -> context.startActivity(installerIntent));
+        }
+
+        // Ensure the content view is prepared.
+        mDialog = builder.create();
+        mDialog.create();
+
+        final Window window = mDialog.getWindow();
+        window.setType(WindowManager.LayoutParams.TYPE_PHONE);
+
+        // DO NOT MODIFY. Used by CTS to verify the dialog is displayed.
+        window.getAttributes().setTitle("UnsupportedCompileSdkDialog");
+
+        final CheckBox alwaysShow = mDialog.findViewById(R.id.ask_checkbox);
+        alwaysShow.setChecked(true);
+        alwaysShow.setOnCheckedChangeListener((buttonView, isChecked) -> manager.setPackageFlag(
+                mPackageName, AppWarnings.FLAG_HIDE_COMPILE_SDK, !isChecked));
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public void show() {
+        mDialog.show();
+    }
+
+    public void dismiss() {
+        mDialog.dismiss();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/UnsupportedDisplaySizeDialog.java b/services/core/java/com/android/server/wm/UnsupportedDisplaySizeDialog.java
new file mode 100644
index 0000000..4a800c4
--- /dev/null
+++ b/services/core/java/com/android/server/wm/UnsupportedDisplaySizeDialog.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011 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.server.wm;
+
+import com.android.internal.R;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+
+public class UnsupportedDisplaySizeDialog {
+    private final AlertDialog mDialog;
+    private final String mPackageName;
+
+    public UnsupportedDisplaySizeDialog(final AppWarnings manager, Context context,
+            ApplicationInfo appInfo) {
+        mPackageName = appInfo.packageName;
+
+        final PackageManager pm = context.getPackageManager();
+        final CharSequence label = appInfo.loadSafeLabel(pm,
+                PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE
+                        | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
+        final CharSequence message = context.getString(
+                R.string.unsupported_display_size_message, label);
+
+        mDialog = new AlertDialog.Builder(context)
+                .setPositiveButton(R.string.ok, null)
+                .setMessage(message)
+                .setView(R.layout.unsupported_display_size_dialog_content)
+                .create();
+
+        // Ensure the content view is prepared.
+        mDialog.create();
+
+        final Window window = mDialog.getWindow();
+        window.setType(WindowManager.LayoutParams.TYPE_PHONE);
+
+        // DO NOT MODIFY. Used by CTS to verify the dialog is displayed.
+        window.getAttributes().setTitle("UnsupportedDisplaySizeDialog");
+
+        final CheckBox alwaysShow = mDialog.findViewById(R.id.ask_checkbox);
+        alwaysShow.setChecked(true);
+        alwaysShow.setOnCheckedChangeListener((buttonView, isChecked) -> manager.setPackageFlag(
+                mPackageName, AppWarnings.FLAG_HIDE_DISPLAY_SIZE, !isChecked));
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public void show() {
+        mDialog.show();
+    }
+
+    public void dismiss() {
+        mDialog.dismiss();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/VrController.java b/services/core/java/com/android/server/wm/VrController.java
new file mode 100644
index 0000000..abe40a7
--- /dev/null
+++ b/services/core/java/com/android/server/wm/VrController.java
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import android.content.ComponentName;
+import android.os.Process;
+import android.service.vr.IPersistentVrStateCallbacks;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
+
+import com.android.server.LocalServices;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.am.ProcessList;
+import com.android.server.am.VrControllerProto;
+import com.android.server.vr.VrManagerInternal;
+
+/**
+ * Helper class for {@link ActivityManagerService} responsible for VrMode-related ActivityManager
+ * functionality.
+ *
+ * <p>Specifically, this class is responsible for:
+ * <ul>
+ * <li>Adjusting the scheduling of VR render threads while in VR mode.
+ * <li>Handling ActivityManager calls to set a VR or a 'persistent' VR thread.
+ * <li>Tracking the state of ActivityManagerService's view of VR-related behavior flags.
+ * </ul>
+ *
+ * <p>This is NOT the class that manages the system VR mode lifecycle. The class responsible for
+ * handling everything related to VR mode state changes (e.g. the lifecycles of the associated
+ * VrListenerService, VrStateCallbacks, VR HAL etc.) is VrManagerService.
+ *
+ * <p>This class is exclusively for use by ActivityManagerService. Do not add callbacks or other
+ * functionality to this for things that belong in VrManagerService.
+ */
+final class VrController {
+    private static final String TAG = "VrController";
+
+    // VR state flags.
+    private static final int FLAG_NON_VR_MODE = 0;
+    private static final int FLAG_VR_MODE = 1;
+    private static final int FLAG_PERSISTENT_VR_MODE = 2;
+
+    // Keep the enum lists in sync
+    private static int[] ORIG_ENUMS = new int[] {
+            FLAG_NON_VR_MODE,
+            FLAG_VR_MODE,
+            FLAG_PERSISTENT_VR_MODE,
+    };
+    private static int[] PROTO_ENUMS = new int[] {
+            VrControllerProto.FLAG_NON_VR_MODE,
+            VrControllerProto.FLAG_VR_MODE,
+            VrControllerProto.FLAG_PERSISTENT_VR_MODE,
+    };
+
+    // Invariants maintained for mVrState
+    //
+    //   Always true:
+    //      - Only a single VR-related thread will have elevated scheduling priorities at a time
+    //        across all threads in all processes (and for all possible running modes).
+    //
+    //   Always true while FLAG_PERSISTENT_VR_MODE is set:
+    //      - An application has set a flag to run in persistent VR mode the next time VR mode is
+    //        entered. The device may or may not be in VR mode.
+    //      - mVrState will contain FLAG_PERSISTENT_VR_MODE
+    //      - An application may set a persistent VR thread that gains elevated scheduling
+    //        priorities via a call to setPersistentVrThread.
+    //      - Calls to set a regular (non-persistent) VR thread via setVrThread will fail, and
+    //        thread that had previously elevated its scheduling priority in this way is returned
+    //        to its normal scheduling priority.
+    //
+    //   Always true while FLAG_VR_MODE is set:
+    //      - The current top application is running in VR mode.
+    //      - mVrState will contain FLAG_VR_MODE
+    //
+    //   While FLAG_VR_MODE is set without FLAG_PERSISTENT_VR_MODE:
+    //      - The current top application may set one of its threads to run at an elevated
+    //        scheduling priority via a call to setVrThread.
+    //
+    //   While FLAG_VR_MODE is set with FLAG_PERSISTENT_VR_MODE:
+    //      - The current top application may NOT set one of its threads to run at an elevated
+    //        scheduling priority via a call to setVrThread (instead, the persistent VR thread will
+    //        be kept if an application has set one).
+    //
+    //   While mVrState == FLAG_NON_VR_MODE:
+    //      - Calls to setVrThread will fail.
+    //      - Calls to setPersistentVrThread will fail.
+    //      - No threads will have elevated scheduling priority for VR.
+    //
+    private int mVrState = FLAG_NON_VR_MODE;
+
+    // The single VR render thread on the device that is given elevated scheduling priority.
+    private int mVrRenderThreadTid = 0;
+
+    private final Object mGlobalAmLock;
+
+    private final IPersistentVrStateCallbacks mPersistentVrModeListener =
+            new IPersistentVrStateCallbacks.Stub() {
+        @Override
+        public void onPersistentVrStateChanged(boolean enabled) {
+            synchronized(mGlobalAmLock) {
+                // Note: This is the only place where mVrState should have its
+                // FLAG_PERSISTENT_VR_MODE setting changed.
+                if (enabled) {
+                    setVrRenderThreadLocked(0, ProcessList.SCHED_GROUP_TOP_APP, true);
+                    mVrState |= FLAG_PERSISTENT_VR_MODE;
+                } else {
+                    setPersistentVrRenderThreadLocked(0, true);
+                    mVrState &= ~FLAG_PERSISTENT_VR_MODE;
+                }
+            }
+        }
+    };
+
+    /**
+     * Create new VrController instance.
+     *
+     * @param globalAmLock the global ActivityManagerService lock.
+     */
+    public VrController(final Object globalAmLock) {
+        mGlobalAmLock = globalAmLock;
+    }
+
+    /**
+     * Called when ActivityManagerService receives its systemReady call during boot.
+     */
+    public void onSystemReady() {
+        VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class);
+        if (vrManagerInternal != null) {
+            vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
+        }
+    }
+
+    /**
+     * Called when ActivityManagerService's TOP_APP process has changed.
+     *
+     * <p>Note: This must be called with the global ActivityManagerService lock held.
+     *
+     * @param proc is the WindowProcessController of the process that entered or left the TOP_APP
+     *            scheduling group.
+     */
+    public void onTopProcChangedLocked(WindowProcessController proc) {
+        final int curSchedGroup = proc.getCurrentSchedulingGroup();
+        if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+            setVrRenderThreadLocked(proc.mVrThreadTid, curSchedGroup, true);
+        } else {
+            if (proc.mVrThreadTid == mVrRenderThreadTid) {
+                clearVrRenderThreadLocked(true);
+            }
+        }
+    }
+
+    /**
+     * Called when ActivityManagerService is switching VR mode for the TOP_APP process.
+     *
+     * @param record the ActivityRecord of the activity changing the system VR mode.
+     * @return {@code true} if the VR state changed.
+     */
+    public boolean onVrModeChanged(ActivityRecord record) {
+        // This message means that the top focused activity enabled VR mode (or an activity
+        // that previously set this has become focused).
+        VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+        if (vrService == null) {
+            // VR mode isn't supported on this device.
+            return false;
+        }
+        boolean vrMode;
+        ComponentName requestedPackage;
+        ComponentName callingPackage;
+        int userId;
+        int processId = -1;
+        boolean changed = false;
+        synchronized (mGlobalAmLock) {
+            vrMode = record.requestedVrComponent != null;
+            requestedPackage = record.requestedVrComponent;
+            userId = record.userId;
+            callingPackage = record.info.getComponentName();
+
+            // Tell the VrController that a VR mode change is requested.
+            changed = changeVrModeLocked(vrMode, record.app);
+
+            if (record.app != null) {
+                processId = record.app.getPid();
+            }
+        }
+
+        // Tell VrManager that a VR mode changed is requested, VrManager will handle
+        // notifying all non-AM dependencies if needed.
+        vrService.setVrMode(vrMode, requestedPackage, userId, processId, callingPackage);
+        return changed;
+    }
+
+    /**
+     * Called to set an application's VR thread.
+     *
+     * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set,
+     * or the scheduling group of the thread is not for the current top app.  If this succeeds, any
+     * previous VR thread will be returned to a normal sheduling priority; if this fails, the
+     * scheduling for the previous thread will be unaffected.
+     *
+     * <p>Note: This must be called with the global ActivityManagerService lock and the
+     *     mPidsSelfLocked object locks held.
+     *
+     * @param tid the tid of the thread to set, or 0 to unset the current thread.
+     * @param pid the pid of the process owning the thread to set.
+     * @param proc the WindowProcessController of the process owning the thread to set.
+     */
+    public void setVrThreadLocked(int tid, int pid, WindowProcessController proc) {
+        if (hasPersistentVrFlagSet()) {
+            Slog.w(TAG, "VR thread cannot be set in persistent VR mode!");
+            return;
+        }
+        if (proc == null) {
+           Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!");
+           return;
+        }
+        if (tid != 0) {
+            enforceThreadInProcess(tid, pid);
+        }
+        if (!inVrMode()) {
+            Slog.w(TAG, "VR thread cannot be set when not in VR mode!");
+        } else {
+            setVrRenderThreadLocked(tid, proc.getCurrentSchedulingGroup(), false);
+        }
+        proc.mVrThreadTid = (tid > 0) ? tid : 0;
+    }
+
+    /**
+     * Called to set an application's persistent VR thread.
+     *
+     * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds,
+     * any previous VR thread will be returned to a normal sheduling priority; if this fails,
+     * the scheduling for the previous thread will be unaffected.
+     *
+     * <p>Note: This must be called with the global ActivityManagerService lock and the
+     *     mPidsSelfLocked object locks held.
+     *
+     * @param tid the tid of the thread to set, or 0 to unset the current thread.
+     * @param pid the pid of the process owning the thread to set.
+     * @param proc the process owning the thread to set.
+     */
+    public void setPersistentVrThreadLocked(int tid, int pid, WindowProcessController proc) {
+        if (!hasPersistentVrFlagSet()) {
+            Slog.w(TAG, "Persistent VR thread may only be set in persistent VR mode!");
+            return;
+        }
+        if (proc == null) {
+           Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!");
+           return;
+        }
+        if (tid != 0) {
+            enforceThreadInProcess(tid, pid);
+        }
+        setPersistentVrRenderThreadLocked(tid, false);
+    }
+
+    /**
+     * Return {@code true} when UI features incompatible with VR mode should be disabled.
+     *
+     * <p>Note: This must be called with the global ActivityManagerService lock held.
+     */
+    public boolean shouldDisableNonVrUiLocked() {
+        return mVrState != FLAG_NON_VR_MODE;
+    }
+
+    /**
+     * Called when to update this VrController instance's state when the system VR mode is being
+     * changed.
+     *
+     * <p>Note: This must be called with the global ActivityManagerService lock held.
+     *
+     * @param vrMode {@code true} if the system VR mode is being enabled.
+     * @param proc the WindowProcessController of the process enabling the system VR mode.
+     *
+     * @return {@code true} if our state changed.
+     */
+    private boolean changeVrModeLocked(boolean vrMode, WindowProcessController proc) {
+        final int oldVrState = mVrState;
+
+        // This is the only place where mVrState should have its FLAG_VR_MODE setting
+        // changed.
+        if (vrMode) {
+            mVrState |= FLAG_VR_MODE;
+        } else {
+            mVrState &= ~FLAG_VR_MODE;
+        }
+
+        boolean changed = (oldVrState != mVrState);
+
+        if (changed) {
+            if (proc != null) {
+                if (proc.mVrThreadTid > 0) {
+                    setVrRenderThreadLocked(
+                            proc.mVrThreadTid, proc.getCurrentSchedulingGroup(), false);
+                }
+            } else {
+              clearVrRenderThreadLocked(false);
+            }
+        }
+        return changed;
+    }
+
+    /**
+     * Set the given thread as the new VR thread, and give it special scheduling priority.
+     *
+     * <p>If the current thread is this thread, do nothing. If the current thread is different from
+     * the given thread, the current thread will be returned to a normal scheduling priority.
+     *
+     * @param newTid the tid of the thread to set, or 0 to unset the current thread.
+     * @param suppressLogs {@code true} if any error logging should be disabled.
+     *
+     * @return the tid of the thread configured to run at the scheduling priority for VR
+     *          mode after this call completes (this may be the previous thread).
+     */
+    private int updateVrRenderThreadLocked(int newTid, boolean suppressLogs) {
+        if (mVrRenderThreadTid == newTid) {
+            return mVrRenderThreadTid;
+        }
+
+        if (mVrRenderThreadTid > 0) {
+            ActivityManagerService.scheduleAsRegularPriority(mVrRenderThreadTid, suppressLogs);
+            mVrRenderThreadTid = 0;
+        }
+
+        if (newTid > 0) {
+            mVrRenderThreadTid = newTid;
+            ActivityManagerService.scheduleAsFifoPriority(mVrRenderThreadTid, suppressLogs);
+        }
+        return mVrRenderThreadTid;
+    }
+
+    /**
+     * Set special scheduling for the given application persistent VR thread, if allowed.
+     *
+     * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds,
+     * any previous VR thread will be returned to a normal sheduling priority; if this fails,
+     * the scheduling for the previous thread will be unaffected.
+     *
+     * @param newTid the tid of the thread to set, or 0 to unset the current thread.
+     * @param suppressLogs {@code true} if any error logging should be disabled.
+     *
+     * @return the tid of the thread configured to run at the scheduling priority for VR
+     *          mode after this call completes (this may be the previous thread).
+     */
+    private int setPersistentVrRenderThreadLocked(int newTid, boolean suppressLogs) {
+       if (!hasPersistentVrFlagSet()) {
+            if (!suppressLogs) {
+                Slog.w(TAG, "Failed to set persistent VR thread, "
+                        + "system not in persistent VR mode.");
+            }
+            return mVrRenderThreadTid;
+        }
+        return updateVrRenderThreadLocked(newTid, suppressLogs);
+    }
+
+    /**
+     * Set special scheduling for the given application VR thread, if allowed.
+     *
+     * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set,
+     * or the scheduling group of the thread is not for the current top app.  If this succeeds, any
+     * previous VR thread will be returned to a normal sheduling priority; if this fails, the
+     * scheduling for the previous thread will be unaffected.
+     *
+     * @param newTid the tid of the thread to set, or 0 to unset the current thread.
+     * @param schedGroup the current scheduling group of the thread to set.
+     * @param suppressLogs {@code true} if any error logging should be disabled.
+     *
+     * @return the tid of the thread configured to run at the scheduling priority for VR
+     *          mode after this call completes (this may be the previous thread).
+     */
+    private int setVrRenderThreadLocked(int newTid, int schedGroup, boolean suppressLogs) {
+        boolean inVr = inVrMode();
+        boolean inPersistentVr = hasPersistentVrFlagSet();
+        if (!inVr || inPersistentVr || schedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+            if (!suppressLogs) {
+               String reason = "caller is not the current top application.";
+               if (!inVr) {
+                   reason = "system not in VR mode.";
+               } else if (inPersistentVr) {
+                   reason = "system in persistent VR mode.";
+               }
+               Slog.w(TAG, "Failed to set VR thread, " + reason);
+            }
+            return mVrRenderThreadTid;
+        }
+        return updateVrRenderThreadLocked(newTid, suppressLogs);
+    }
+
+    /**
+     * Unset any special scheduling used for the current VR render thread, and return it to normal
+     * scheduling priority.
+     *
+     * @param suppressLogs {@code true} if any error logging should be disabled.
+     */
+    private void clearVrRenderThreadLocked(boolean suppressLogs) {
+        updateVrRenderThreadLocked(0, suppressLogs);
+    }
+
+    /**
+     * Check that the given tid is running in the process for the given pid, and throw an exception
+     * if not.
+     */
+    private void enforceThreadInProcess(int tid, int pid) {
+        if (!Process.isThreadInProcess(pid, tid)) {
+            throw new IllegalArgumentException("VR thread does not belong to process");
+        }
+    }
+
+    /**
+     * True when the system is in VR mode.
+     */
+    private boolean inVrMode() {
+        return (mVrState & FLAG_VR_MODE) != 0;
+    }
+
+    /**
+     * True when the persistent VR mode flag has been set.
+     *
+     * Note: Currently this does not necessarily mean that the system is in VR mode.
+     */
+    private boolean hasPersistentVrFlagSet() {
+        return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0;
+    }
+
+    @Override
+    public String toString() {
+      return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid);
+    }
+
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, VrControllerProto.VR_MODE,
+                mVrState, ORIG_ENUMS, PROTO_ENUMS);
+        proto.write(VrControllerProto.RENDER_THREAD_ID, mVrRenderThreadTid);
+        proto.end(token);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index a448f97..29e1b20 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -16,16 +16,14 @@
 
 package com.android.server.wm;
 
-import com.android.internal.util.ToBooleanFunction;
-
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
 
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
@@ -50,6 +48,8 @@
 import android.view.WindowManager;
 import android.view.animation.Animation;
 
+import com.android.internal.util.ToBooleanFunction;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
@@ -263,7 +263,8 @@
                 && (mWallpaperTarget != winGoingAway || mPrevWallpaperTarget != null)) {
             return;
         }
-        if (mService.mAppTransition.isRunning()) {
+        if (mWallpaperTarget != null
+                && mWallpaperTarget.getDisplayContent().mAppTransition.isRunning()) {
             // Defer hiding the wallpaper when app transition is running until the animations
             // are done.
             mDeferredHideWallpaper = winGoingAway;
@@ -339,7 +340,7 @@
                             try {
                                 if (DEBUG_WALLPAPER) Slog.v(TAG,
                                         "Waiting for offset complete...");
-                                mService.mWindowMap.wait(WALLPAPER_TIMEOUT);
+                                mService.mGlobalLock.wait(WALLPAPER_TIMEOUT);
                             } catch (InterruptedException e) {
                             }
                             if (DEBUG_WALLPAPER) Slog.v(TAG, "Offset complete!");
@@ -451,7 +452,7 @@
         if (mWaitingOnWallpaper != null &&
                 mWaitingOnWallpaper.mClient.asBinder() == window) {
             mWaitingOnWallpaper = null;
-            mService.mWindowMap.notifyAll();
+            mService.mGlobalLock.notifyAll();
         }
     }
 
@@ -459,7 +460,7 @@
         if (mWaitingOnWallpaper != null &&
                 mWaitingOnWallpaper.mClient.asBinder() == window) {
             mWaitingOnWallpaper = null;
-            mService.mWindowMap.notifyAll();
+            mService.mGlobalLock.notifyAll();
         }
     }
 
@@ -549,9 +550,9 @@
             // is not. If they're both hidden, still use the new target.
             mWallpaperTarget = prevWallpaperTarget;
         } else if (newTargetHidden == oldTargetHidden
-                && !mService.mOpeningApps.contains(wallpaperTarget.mAppToken)
-                && (mService.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
-                || mService.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
+                && !dc.mOpeningApps.contains(wallpaperTarget.mAppToken)
+                && (dc.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
+                || dc.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
             // If they're both hidden (or both not hidden), prefer the one that's currently in
             // opening or closing app list, this allows transition selection logic to better
             // determine the wallpaper status of opening/closing apps.
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index ad0b8ec..46bb981 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -94,7 +94,7 @@
                 () -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
 
         mAnimationFrameCallback = frameTimeNs -> {
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 mAnimationFrameCallbackScheduled = false;
             }
             animate(frameTimeNs);
@@ -105,9 +105,6 @@
         // Create the DisplayContentsAnimator object by retrieving it if the associated
         // {@link DisplayContent} exists.
         getDisplayContentsAnimatorLocked(displayId);
-        if (displayId == DEFAULT_DISPLAY) {
-            mInitialized = true;
-        }
     }
 
     void removeDisplayLocked(final int displayId) {
@@ -122,6 +119,10 @@
         mDisplayContentsAnimators.delete(displayId);
     }
 
+    void ready() {
+        mInitialized = true;
+    }
+
     /**
      * DO NOT HOLD THE WINDOW MANAGER LOCK WHILE CALLING THIS METHOD. Reason: the method closes
      * an animation transaction, that might be blocking until the next sf-vsync, so we want to make
@@ -129,7 +130,7 @@
      */
     private void animate(long frameTimeNs) {
 
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             if (!mInitialized) {
                 return;
             }
@@ -138,7 +139,7 @@
             scheduleAnimation();
         }
 
-        synchronized (mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
             mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
             mAnimating = false;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 46999a2..abc3826 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -826,6 +826,12 @@
         wrapper.release();
     }
 
+    void forAllAppWindows(Consumer<AppWindowToken> callback) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            mChildren.get(i).forAllAppWindows(callback);
+        }
+    }
+
     /**
      * For all tasks at or below this container call the callback.
      *
diff --git a/services/core/java/com/android/server/wm/WindowContainerController.java b/services/core/java/com/android/server/wm/WindowContainerController.java
index eb23faf..7cb89c5 100644
--- a/services/core/java/com/android/server/wm/WindowContainerController.java
+++ b/services/core/java/com/android/server/wm/WindowContainerController.java
@@ -32,7 +32,7 @@
 
     final WindowManagerService mService;
     final RootWindowContainer mRoot;
-    final WindowHashMap mWindowMap;
+    final WindowManagerGlobalLock mGlobalLock;
 
     // The window container this controller owns.
     E mContainer;
@@ -43,7 +43,7 @@
         mListener = listener;
         mService = service;
         mRoot = mService != null ? mService.mRoot : null;
-        mWindowMap = mService != null ? mService.mWindowMap : null;
+        mGlobalLock = mService != null ? mService.mGlobalLock : null;
     }
 
     void setContainer(E container) {
@@ -73,7 +73,7 @@
 
     @Override
     public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mContainer == null) {
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerGlobalLock.java b/services/core/java/com/android/server/wm/WindowManagerGlobalLock.java
new file mode 100644
index 0000000..7ce11ee
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowManagerGlobalLock.java
@@ -0,0 +1,9 @@
+package com.android.server.wm;
+
+/**
+ * Class that is used to generate an instance of the WM global lock. We are only doing this because
+ * we need a class for the pattern used in frameworks/base/services/core/Android.bp for CPU boost
+ * in the WM critical section.
+ */
+public class WindowManagerGlobalLock {
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5642b1f..ec68f76 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -67,12 +67,10 @@
 import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
 import static com.android.server.LockGuard.INDEX_WINDOW;
 import static com.android.server.LockGuard.installLock;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.KeyguardDisableHandler.KEYGUARD_POLICY_CHANGED;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
@@ -86,7 +84,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -96,7 +93,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerServiceDumpProto.APP_TRANSITION;
 import static com.android.server.wm.WindowManagerServiceDumpProto.DISPLAY_FROZEN;
 import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_APP;
 import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_WINDOW;
@@ -108,7 +104,6 @@
 
 import android.Manifest;
 import android.Manifest.permission;
-import android.animation.AnimationHandler;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -134,7 +129,6 @@
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
-import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -188,7 +182,6 @@
 import android.util.TimeUtils;
 import android.util.TypedValue;
 import android.util.proto.ProtoOutputStream;
-import android.view.AppTransitionAnimationSpec;
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
@@ -221,7 +214,6 @@
 import android.view.WindowContentFrameStats;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
-import android.view.WindowManager.TransitionFlags;
 import android.view.WindowManager.TransitionType;
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
@@ -266,7 +258,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
-import java.util.List;
 
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
@@ -446,18 +437,11 @@
      */
     final ArraySet<Session> mSessions = new ArraySet<>();
 
-    /**
-     * Mapping from an IWindow IBinder to the server's Window object.
-     * This is also used as the lock for all of our state.
-     * NOTE: Never call into methods that lock ActivityManagerService while holding this object.
-     */
+    /** Mapping from an IWindow IBinder to the server's Window object. */
     final WindowHashMap mWindowMap = new WindowHashMap();
 
-    /**
-     * List of window tokens that have finished starting their application,
-     * and now need to have the policy remove their windows.
-     */
-    final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
+    /** Global service lock used by the package the owns this service. */
+    final WindowManagerGlobalLock mGlobalLock;
 
     /**
      * List of app window tokens that are waiting for replacing windows. If the
@@ -612,14 +596,6 @@
     // changes the orientation.
     private final PowerManager.WakeLock mScreenFrozenLock;
 
-    final AppTransition mAppTransition;
-    boolean mSkipAppTransitionAnimation = false;
-
-    final ArraySet<AppWindowToken> mOpeningApps = new ArraySet<>();
-    final ArraySet<AppWindowToken> mClosingApps = new ArraySet<>();
-
-    final UnknownAppVisibilityController mUnknownAppVisibilityController =
-            new UnknownAppVisibilityController(this);
     final TaskSnapshotController mTaskSnapshotController;
 
     boolean mIsTouchDevice;
@@ -751,7 +727,6 @@
      * up when the animation finishes.
      */
     final ArrayMap<AnimationAdapter, SurfaceAnimator> mAnimationTransferMap = new ArrayMap<>();
-    final BoundsAnimationController mBoundsAnimationController;
 
     private WindowContentFrameStats mTempWindowRenderStats;
 
@@ -779,10 +754,6 @@
     // For example, when this flag is true, there will be no wallpaper service.
     final boolean mOnlyCore;
 
-    // List of clients without a transtiton animation that we notify once we are done transitioning
-    // since they won't be notified through the app window animator.
-    final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>();
-
     static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
             new WindowManagerThreadPriorityBooster();
 
@@ -802,7 +773,7 @@
     void openSurfaceTransaction() {
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 SurfaceControl.openTransaction();
             }
         } finally {
@@ -817,7 +788,7 @@
     void closeSurfaceTransaction(String where) {
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction");
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 try {
                     traceStateLocked(where);
                 } finally {
@@ -884,9 +855,11 @@
     }
 
     public static WindowManagerService main(final Context context, final InputManagerService im,
-            final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy) {
+            final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
+            final WindowManagerGlobalLock globalLock) {
         DisplayThread.getHandler().runWithScissors(() ->
-                sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy),
+                sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
+                        globalLock),
                 0);
         return sInstance;
     }
@@ -908,8 +881,10 @@
     }
 
     private WindowManagerService(Context context, InputManagerService inputManager,
-            boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy) {
+            boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
+            WindowManagerGlobalLock globalLock) {
         installLock(this, INDEX_WINDOW);
+        mGlobalLock = globalLock;
         mContext = context;
         mAllowBootMessages = showBootMsgs;
         mOnlyCore = onlyCore;
@@ -930,7 +905,6 @@
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mDisplaySettings = new DisplaySettings(this);
-        mDisplaySettings.readSettingsLocked();
 
         mPolicy = policy;
         mAnimator = new WindowAnimator(this);
@@ -960,7 +934,7 @@
 
                 @Override
                 public void onLowPowerModeChanged(PowerSaveState result) {
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         final boolean enabled = result.batterySaverEnabled;
                         if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) {
                             mAnimationsDisabled = enabled;
@@ -976,13 +950,6 @@
                 PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN");
         mScreenFrozenLock.setReferenceCounted(false);
 
-        mAppTransition = new AppTransition(context, this);
-        mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
-
-        final AnimationHandler animationHandler = new AnimationHandler();
-        mBoundsAnimationController = new BoundsAnimationController(context, mAppTransition,
-                AnimationThread.getHandler(), animationHandler);
-
         mActivityManager = ActivityManager.getService();
         mActivityTaskManager = ActivityTaskManager.getService();
         mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
@@ -1114,7 +1081,7 @@
         final int callingUid = Binder.getCallingUid();
         final int type = attrs.type;
 
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (!mDisplayReady) {
                 throw new IllegalStateException("Display has not been initialialized");
             }
@@ -1485,7 +1452,7 @@
             if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "
                     + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));
 
-            if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(displayId)) {
+            if (win.isVisibleOrAdding() && displayContent.updateOrientationFromAppTokens()) {
                 reportNewConfig = true;
             }
         }
@@ -1578,11 +1545,13 @@
         Rect frame = replacedWindow.getVisibleFrameLw();
         // We treat this as if this activity was opening, so we can trigger the app transition
         // animation and piggy-back on existing transition animation infrastructure.
-        mOpeningApps.add(atoken);
-        prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT);
-        mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
+        final DisplayContent dc = atoken.getDisplayContent();
+        dc.mOpeningApps.add(atoken);
+        dc.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT,
+                0 /* flags */, false /* forceOverride */);
+        dc.mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
                 frame.width(), frame.height());
-        executeAppTransition();
+        dc.executeAppTransition();
         return true;
     }
 
@@ -1591,10 +1560,12 @@
         // unfreeze wait for the apps to be drawn.
         // Note that if the display unfroze already because app unfreeze timed out,
         // we don't set up the transition anymore and just let it go.
-        if (mDisplayFrozen && !mOpeningApps.contains(atoken) && atoken.isRelaunching()) {
-            mOpeningApps.add(atoken);
-            prepareAppTransition(WindowManager.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT);
-            executeAppTransition();
+        final DisplayContent dc = atoken.getDisplayContent();
+        if (mDisplayFrozen && !dc.mOpeningApps.contains(atoken) && atoken.isRelaunching()) {
+            dc.mOpeningApps.add(atoken);
+            dc.prepareAppTransition(WindowManager.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT, 0 /* flags */,
+                    false /* forceOverride */);
+            dc.executeAppTransition();
         }
     }
 
@@ -1620,7 +1591,7 @@
             throw new SecurityException("Only system can call refreshScreenCaptureDisabled.");
         }
 
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             // Update secure surface for all windows belonging to this user.
             mRoot.setSecureSurfaceState(userId,
                     DevicePolicyCache.getInstance().getScreenCaptureDisabled(userId));
@@ -1628,7 +1599,7 @@
     }
 
     void removeWindow(Session session, IWindow client) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             WindowState win = windowForClientLocked(session, client, false);
             if (win == null) {
                 return;
@@ -1709,13 +1680,13 @@
     }
 
     private void updateHiddenWhileSuspendedState(ArraySet<String> packages, boolean suspended) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mRoot.updateHiddenWhileSuspendedState(packages, suspended);
         }
     }
 
     private void updateAppOpsState() {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             mRoot.updateAppOpsState();
         }
     }
@@ -1746,7 +1717,7 @@
     void setTransparentRegionWindow(Session session, IWindow client, Region region) {
         long origId = Binder.clearCallingIdentity();
         try {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 WindowState w = windowForClientLocked(session, client, false);
                 if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
                         "transparentRegionHint=" + region, false);
@@ -1764,7 +1735,7 @@
             Rect visibleInsets, Region touchableRegion) {
         long origId = Binder.clearCallingIdentity();
         try {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 WindowState w = windowForClientLocked(session, client, false);
                 if (DEBUG_LAYOUT) Slog.d(TAG, "setInsetsWindow " + w
                         + ", contentInsets=" + w.mGivenContentInsets + " -> " + contentInsets
@@ -1799,7 +1770,7 @@
 
     public void getWindowDisplayFrame(Session session, IWindow client,
             Rect outDisplayFrame) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             WindowState win = windowForClientLocked(session, client, false);
             if (win == null) {
                 outDisplayFrame.setEmpty();
@@ -1810,7 +1781,7 @@
     }
 
     public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mAccessibilityController != null) {
                 WindowState window = mWindowMap.get(token);
                 //TODO (multidisplay): Magnification is supported only for the default display.
@@ -1822,14 +1793,14 @@
     }
 
     public IWindowId getWindowId(IBinder token) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             WindowState window = mWindowMap.get(token);
             return window != null ? window.mWindowId : null;
         }
     }
 
     public void pokeDrawLock(Session session, IBinder token) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             WindowState window = windowForClientLocked(session, token, false);
             if (window != null) {
                 window.pokeDrawLockLw(mDrawLockTimeoutMillis);
@@ -1854,7 +1825,7 @@
 
         long origId = Binder.clearCallingIdentity();
         final int displayId;
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             WindowState win = windowForClientLocked(session, client, false);
             if (win == null) {
                 return 0;
@@ -2088,12 +2059,12 @@
             }
 
             if (win.mAppToken != null) {
-                mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken);
+                dc.mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken);
             }
 
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                     "relayoutWindow: updateOrientationFromAppTokens");
-            configChanged = updateOrientationFromAppTokensLocked(displayId);
+            configChanged = dc.updateOrientationFromAppTokens();
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
             if (toBeDisplayed && win.mIsWallpaper) {
@@ -2248,7 +2219,7 @@
         final long origId = Binder.clearCallingIdentity();
 
         try {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 WindowState win = windowForClientLocked(session, client, false);
                 if (win == null) {
                     return false;
@@ -2263,7 +2234,7 @@
     void finishDrawingWindow(Session session, IWindow client) {
         final long origId = Binder.clearCallingIdentity();
         try {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 WindowState win = windowForClientLocked(session, client, false);
                 if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "finishDrawingWindow: " + win + " mDrawState="
                         + (win != null ? win.mWinAnimator.drawStateToString() : "null"));
@@ -2303,7 +2274,7 @@
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent dc = mRoot.getDisplayContent(displayId);
             if (dc == null) {
                 Slog.w(TAG_WM, "addWindowToken: Attempted to add token: " + binder
@@ -2335,7 +2306,7 @@
 
         final long origId = Binder.clearCallingIdentity();
         try {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 final DisplayContent dc = mRoot.getDisplayContent(displayId);
                 if (dc == null) {
                     Slog.w(TAG_WM, "removeWindowToken: Attempted to remove token: " + binder
@@ -2373,7 +2344,7 @@
         final Configuration config;
         final long ident = Binder.clearCallingIdentity();
         try {
-            synchronized(mWindowMap) {
+            synchronized (mGlobalLock) {
                 config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded,
                         displayId, forceUpdate);
             }
@@ -2384,6 +2355,15 @@
         return config;
     }
 
+    /**
+     * Update orientation of the target display, returning a non-null new Configuration if it has
+     * changed from the current orientation. If a non-null configuration is returned, someone must
+     * call {@link #setNewDisplayOverrideConfiguration(Configuration, int)} to tell the window
+     * manager it can unfreeze the screen. This will typically be done by calling
+     * {@link #sendNewConfiguration(int)}.
+     *
+     * @see android.view.IWindowManager#updateOrientationFromAppTokens(Configuration, IBinder, int)
+     */
     private Configuration updateOrientationFromAppTokensLocked(Configuration currentConfig,
             IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {
         if (!mDisplayReady) {
@@ -2391,7 +2371,8 @@
         }
         Configuration config = null;
 
-        if (updateOrientationFromAppTokensLocked(displayId, forceUpdate)) {
+        final DisplayContent dc = mRoot.getDisplayContent(displayId);
+        if (dc != null && dc.updateOrientationFromAppTokens(forceUpdate)) {
             // If we changed the orientation but mOrientationChangeComplete is already true,
             // we used seamless rotation, and we don't need to freeze the screen.
             if (freezeThisOneIfNeeded != null && !mRoot.mOrientationChangeComplete) {
@@ -2426,168 +2407,35 @@
         return config;
     }
 
-    /**
-     * Determine the new desired orientation of the display, returning a non-null new Configuration
-     * if it has changed from the current orientation.  IF TRUE IS RETURNED SOMEONE MUST CALL
-     * {@link #setNewDisplayOverrideConfiguration(Configuration, int)} TO TELL THE WINDOW MANAGER IT
-     * CAN UNFREEZE THE SCREEN.  This will typically be done for you if you call
-     * {@link #sendNewConfiguration(int)}.
-     *
-     * The orientation is computed from non-application windows first. If none of the
-     * non-application windows specify orientation, the orientation is computed from application
-     * tokens.
-     * @see android.view.IWindowManager#updateOrientationFromAppTokens(Configuration, IBinder, int)
-     */
-    boolean updateOrientationFromAppTokensLocked(int displayId) {
-        return updateOrientationFromAppTokensLocked(displayId, false /* forceUpdate */);
-    }
-
-    boolean updateOrientationFromAppTokensLocked(int displayId, boolean forceUpdate) {
-        long ident = Binder.clearCallingIdentity();
-        try {
-            final DisplayContent dc = mRoot.getDisplayContent(displayId);
-            final int req = dc.getOrientation();
-            if (req != dc.getLastOrientation() || forceUpdate) {
-                dc.setLastOrientation(req);
-                //send a message to Policy indicating orientation change to take
-                //action like disabling/enabling sensors etc.,
-                dc.getDisplayRotation().setCurrentOrientation(req);
-                return dc.updateRotationUnchecked(forceUpdate);
-            }
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public int[] setNewDisplayOverrideConfiguration(Configuration overrideConfig, int displayId) {
-        if (!checkCallingPermission(MANAGE_APP_TOKENS, "setNewDisplayOverrideConfiguration()")) {
-            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+    void setNewDisplayOverrideConfiguration(Configuration overrideConfig, DisplayContent dc) {
+        if (mWaitingForConfig) {
+            mWaitingForConfig = false;
+            mLastFinishedFreezeSource = "new-config";
         }
 
-        synchronized(mWindowMap) {
-            if (mWaitingForConfig) {
-                mWaitingForConfig = false;
-                mLastFinishedFreezeSource = "new-config";
-            }
-
-            return mRoot.setDisplayOverrideConfigurationIfNeeded(overrideConfig, displayId);
-        }
+        mRoot.setDisplayOverrideConfigurationIfNeeded(overrideConfig, dc);
     }
 
+    // TODO(multi-display): remove when no default display use case.
+    // (i.e. KeyguardController / RecentsAnimation)
     @Override
     public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent) {
-        prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
-    }
-
-    /**
-     * @param transit What kind of transition is happening. Use one of the constants
-     *                AppTransition.TRANSIT_*.
-     * @param alwaysKeepCurrent If true and a transition is already set, new transition will NOT
-     *                          be set.
-     * @param flags Additional flags for the app transition, Use a combination of the constants
-     *              AppTransition.TRANSIT_FLAG_*.
-     * @param forceOverride Always override the transit, not matter what was set previously.
-     */
-    public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent,
-            @TransitionFlags int flags, boolean forceOverride) {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
-        synchronized(mWindowMap) {
-            boolean prepared = mAppTransition.prepareAppTransitionLocked(transit, alwaysKeepCurrent,
-                    flags, forceOverride);
-            // TODO (multidisplay): associate app transitions with displays
-            final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY);
-            if (prepared && dc != null && dc.okToAnimate()) {
-                mSkipAppTransitionAnimation = false;
-            }
-        }
-    }
-
-    @Override
-    public @TransitionType int getPendingAppTransition() {
-        return mAppTransition.getAppTransition();
-    }
-
-    @Override
-    public void overridePendingAppTransition(String packageName,
-            int enterAnim, int exitAnim, IRemoteCallback startedCallback) {
-        synchronized(mWindowMap) {
-            mAppTransition.overridePendingAppTransition(packageName, enterAnim, exitAnim,
-                    startedCallback);
-        }
-    }
-
-    @Override
-    public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
-            int startHeight) {
-        synchronized(mWindowMap) {
-            mAppTransition.overridePendingAppTransitionScaleUp(startX, startY, startWidth,
-                    startHeight);
-        }
-    }
-
-    @Override
-    public void overridePendingAppTransitionClipReveal(int startX, int startY,
-            int startWidth, int startHeight) {
-        synchronized(mWindowMap) {
-            mAppTransition.overridePendingAppTransitionClipReveal(startX, startY, startWidth,
-                    startHeight);
-        }
-    }
-
-    @Override
-    public void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX,
-            int startY, IRemoteCallback startedCallback, boolean scaleUp) {
-        synchronized(mWindowMap) {
-            mAppTransition.overridePendingAppTransitionThumb(srcThumb, startX, startY,
-                    startedCallback, scaleUp);
-        }
-    }
-
-    @Override
-    public void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX,
-            int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
-            boolean scaleUp) {
-        synchronized(mWindowMap) {
-            mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX, startY,
-                    targetWidth, targetHeight, startedCallback, scaleUp);
-        }
-    }
-
-    @Override
-    public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
-            IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
-            boolean scaleUp) {
-        synchronized (mWindowMap) {
-            mAppTransition.overridePendingAppTransitionMultiThumb(specs, onAnimationStartedCallback,
-                    onAnimationFinishedCallback, scaleUp);
-
-        }
-    }
-
-    public void overridePendingAppTransitionStartCrossProfileApps() {
-        synchronized (mWindowMap) {
-            mAppTransition.overridePendingAppTransitionStartCrossProfileApps();
-        }
-    }
-
-    @Override
-    public void overridePendingAppTransitionInPlace(String packageName, int anim) {
-        synchronized(mWindowMap) {
-            mAppTransition.overrideInPlaceAppTransition(packageName, anim);
-        }
+        getDefaultDisplayContentLocked().prepareAppTransition(transit,
+                alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
     }
 
     @Override
     public void overridePendingAppTransitionMultiThumbFuture(
             IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
             boolean scaleUp) {
-        synchronized(mWindowMap) {
-            mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture, callback,
-                    scaleUp);
+        synchronized (mGlobalLock) {
+            // TODO(multi-display): sysui using this api only support default display.
+            mRoot.getDisplayContent(DEFAULT_DISPLAY)
+                    .mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture,
+                    callback, scaleUp);
         }
     }
 
@@ -2598,8 +2446,10 @@
             throw new SecurityException(
                     "Requires CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission");
         }
-        synchronized (mWindowMap) {
-            mAppTransition.overridePendingAppTransitionRemote(remoteAnimationAdapter);
+        synchronized (mGlobalLock) {
+            // TODO(multi-display): sysui using this api only support default display.
+            mRoot.getDisplayContent(DEFAULT_DISPLAY)
+                    .mAppTransition.overridePendingAppTransitionRemote(remoteAnimationAdapter);
         }
     }
 
@@ -2608,30 +2458,24 @@
         // TODO: Remove once clients are updated.
     }
 
+    // TODO(multi-display): remove when no default display use case.
+    // (i.e. KeyguardController / RecentsAnimation)
     @Override
     public void executeAppTransition() {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "executeAppTransition()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
-
-        synchronized(mWindowMap) {
-            if (DEBUG_APP_TRANSITIONS) Slog.w(TAG_WM, "Execute app transition: " + mAppTransition
-                    + " Callers=" + Debug.getCallers(5));
-            if (mAppTransition.isTransitionSet()) {
-                mAppTransition.setReady();
-                mWindowPlacerLocked.requestTraversal();
-            }
-        }
+        getDefaultDisplayContentLocked().executeAppTransition();
     }
 
     public void initializeRecentsAnimation(int targetActivityType,
             IRecentsAnimationRunner recentsAnimationRunner,
             RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId,
             SparseBooleanArray recentTaskIds) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mRecentsAnimationController = new RecentsAnimationController(this,
                     recentsAnimationRunner, callbacks, displayId);
-            mAppTransition.updateBooster();
+            mRoot.getDisplayContent(displayId).mAppTransition.updateBooster();
             mRecentsAnimationController.initialize(targetActivityType, recentTaskIds);
         }
     }
@@ -2650,8 +2494,9 @@
      *         {@link RecentsAnimation#startRecentsActivity}.
      */
     public boolean canStartRecentsAnimation() {
-        synchronized (mWindowMap) {
-            if (mAppTransition.isTransitionSet()) {
+        synchronized (mGlobalLock) {
+            // TODO(multi-display): currently only default display support recent activity
+            if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
                 return false;
             }
             return true;
@@ -2673,18 +2518,19 @@
     }
 
     public void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mRecentsAnimationController != null) {
                 final RecentsAnimationController controller = mRecentsAnimationController;
                 mRecentsAnimationController = null;
                 controller.cleanupAnimation(reorderMode);
-                mAppTransition.updateBooster();
+                // TODO(mult-display): currently only default display support recents animation.
+                getDefaultDisplayContentLocked().mAppTransition.updateBooster();
             }
         }
     }
 
     public void setAppFullscreen(IBinder token, boolean toOpaque) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final AppWindowToken atoken = mRoot.getAppWindowToken(token);
             if (atoken != null) {
                 atoken.setFillsParent(toOpaque);
@@ -2695,7 +2541,7 @@
     }
 
     public void setWindowOpaque(IBinder token, boolean isOpaque) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             setWindowOpaqueLocked(token, isOpaque);
         }
     }
@@ -2711,7 +2557,7 @@
     }
 
     public void setDockedStackCreateState(int mode, Rect bounds) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             setDockedStackCreateStateLocked(mode, bounds);
         }
     }
@@ -2722,7 +2568,7 @@
     }
 
     public void checkSplitScreenMinimizedChanged(boolean animate) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent displayContent = getDefaultDisplayContentLocked();
             displayContent.getDockedDividerController().checkMinimizeChanged(animate);
         }
@@ -2736,7 +2582,7 @@
 
     @Override
     public void getStackBounds(int windowingMode, int activityType, Rect bounds) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final TaskStack stack = mRoot.getStack(windowingMode, activityType);
             if (stack != null) {
                 stack.getBounds(bounds);
@@ -2748,7 +2594,8 @@
 
     @Override
     public void notifyShowingDreamChanged() {
-        notifyKeyguardFlagsChanged(null /* callback */);
+        // TODO(multi-display): support show dream in multi-display.
+        notifyKeyguardFlagsChanged(null /* callback */, DEFAULT_DISPLAY);
     }
 
     @Override
@@ -2779,21 +2626,16 @@
     /**
      * Starts deferring layout passes. Useful when doing multiple changes but to optimize
      * performance, only one layout pass should be done. This can be called multiple times, and
-     * layouting will be resumed once the last caller has called {@link #continueSurfaceLayout}
+     * layouting will be resumed once the last caller has called
+     * {@link #continueSurfaceLayout}.
      */
-    public void deferSurfaceLayout() {
-        synchronized (mWindowMap) {
-            mWindowPlacerLocked.deferLayout();
-        }
+    void deferSurfaceLayout() {
+        mWindowPlacerLocked.deferLayout();
     }
 
-    /**
-     * Resumes layout passes after deferring them. See {@link #deferSurfaceLayout()}
-     */
-    public void continueSurfaceLayout() {
-        synchronized (mWindowMap) {
-            mWindowPlacerLocked.continueLayout();
-        }
+    /** Resumes layout passes after deferring them. See {@link #deferSurfaceLayout()} */
+    void continueSurfaceLayout() {
+        mWindowPlacerLocked.continueLayout();
     }
 
     /**
@@ -2801,7 +2643,7 @@
      *         {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set
      */
     public boolean containsShowWhenLockedWindow(IBinder token) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final AppWindowToken wtoken = mRoot.getAppWindowToken(token);
             return wtoken != null && wtoken.containsShowWhenLockedWindow();
         }
@@ -2812,7 +2654,7 @@
      *         {@link LayoutParams#FLAG_DISMISS_KEYGUARD} set
      */
     public boolean containsDismissKeyguardWindow(IBinder token) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final AppWindowToken wtoken = mRoot.getAppWindowToken(token);
             return wtoken != null && wtoken.containsDismissKeyguardWindow();
         }
@@ -2823,27 +2665,28 @@
      * reevaluate the visibilities of the activities.
      * @param callback Runnable to be called when activity manager is done reevaluating visibilities
      */
-    void notifyKeyguardFlagsChanged(@Nullable Runnable callback) {
+    void notifyKeyguardFlagsChanged(@Nullable Runnable callback, int displayId) {
         final Runnable wrappedCallback = callback != null
-                ? () -> { synchronized (mWindowMap) { callback.run(); } }
+                ? () -> { synchronized (mGlobalLock) { callback.run(); } }
                 : null;
-        mH.obtainMessage(H.NOTIFY_KEYGUARD_FLAGS_CHANGED, wrappedCallback).sendToTarget();
+        mH.obtainMessage(H.NOTIFY_KEYGUARD_FLAGS_CHANGED, displayId, 0,
+                wrappedCallback).sendToTarget();
     }
 
     public boolean isKeyguardTrusted() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             return mPolicy.isKeyguardTrustedLw();
         }
     }
 
     public void setKeyguardGoingAway(boolean keyguardGoingAway) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mKeyguardGoingAway = keyguardGoingAway;
         }
     }
 
     public void setKeyguardOrAodShowingOnDefaultDisplay(boolean showing) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mKeyguardOrAodShowingOnDefaultDisplay = showing;
         }
     }
@@ -2859,7 +2702,7 @@
             throw new SecurityException("Requires FREEZE_SCREEN permission");
         }
 
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (!mClientFreezingScreen) {
                 mClientFreezingScreen = true;
                 final long origId = Binder.clearCallingIdentity();
@@ -2881,7 +2724,7 @@
             throw new SecurityException("Requires FREEZE_SCREEN permission");
         }
 
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mClientFreezingScreen) {
                 mClientFreezingScreen = false;
                 mLastFinishedFreezeSource = "client";
@@ -2984,7 +2827,7 @@
     }
 
     public boolean isShowingDream() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             return mPolicy.isShowingDreamLw();
         }
     }
@@ -2994,13 +2837,13 @@
         if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) {
             throw new SecurityException("Requires CONTROL_KEYGUARD permission");
         }
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             mPolicy.dismissKeyguardLw(callback, message);
         }
     }
 
     public void onKeyguardOccludedChanged(boolean occluded) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mPolicy.onKeyguardOccludedChangedLw(occluded);
         }
     }
@@ -3012,7 +2855,7 @@
             throw new SecurityException("Requires INTERACT_ACROSS_USERS_FULL permission");
         }
         mPolicy.setSwitchingUser(switching);
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mSwitchingUser = switching;
         }
     }
@@ -3023,7 +2866,7 @@
 
     @Override
     public void closeSystemDialogs(String reason) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             mRoot.closeSystemDialogs(reason);
         }
     }
@@ -3107,7 +2950,7 @@
 
     @Override
     public float getCurrentAnimatorScale() {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             return mAnimationsDisabled ? 0 : mAnimatorDurationScaleSetting;
         }
     }
@@ -3118,7 +2961,7 @@
 
     @Override
     public void registerPointerEventListener(PointerEventListener listener, int displayId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             if (displayContent != null) {
                 displayContent.registerPointerEventListener(listener);
@@ -3128,7 +2971,7 @@
 
     @Override
     public void unregisterPointerEventListener(PointerEventListener listener, int displayId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             if (displayContent != null) {
                 displayContent.unregisterPointerEventListener(listener);
@@ -3207,16 +3050,15 @@
     }
 
     public void setCurrentProfileIds(final int[] currentProfileIds) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mCurrentProfileIds = currentProfileIds;
         }
     }
 
     public void setCurrentUser(final int newUserId, final int[] currentProfileIds) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mCurrentUserId = newUserId;
             mCurrentProfileIds = currentProfileIds;
-            mAppTransition.setCurrentUser(newUserId);
             mPolicy.setCurrentUserLw(newUserId);
 
             // If keyguard was disabled, re-enable it
@@ -3235,13 +3077,15 @@
             displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(
                     stack != null && stack.hasTaskForUser(newUserId));
 
+            mRoot.forAllDisplays(dc -> dc.mAppTransition.setCurrentUser(newUserId));
+
             // If the display is already prepared, update the density.
             // Otherwise, we'll update it when it's prepared.
             if (mDisplayReady) {
                 final int forcedDensity = getForcedDisplayDensityForUserLocked(newUserId);
                 final int targetDensity = forcedDensity != 0 ? forcedDensity
                         : displayContent.mInitialDisplayDensity;
-                setForcedDisplayDensityLocked(displayContent, targetDensity);
+                displayContent.setForcedDensity(targetDensity, UserHandle.USER_CURRENT);
             }
         }
     }
@@ -3256,7 +3100,7 @@
     }
 
     public void enableScreenAfterBoot() {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (DEBUG_BOOT) {
                 RuntimeException here = new RuntimeException("here");
                 here.fillInStackTrace();
@@ -3282,7 +3126,7 @@
 
     @Override
     public void enableScreenIfNeeded() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             enableScreenIfNeededLocked();
         }
     }
@@ -3306,7 +3150,7 @@
     }
 
     public void performBootTimeout() {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mDisplayEnabled) {
                 return;
             }
@@ -3324,7 +3168,7 @@
     }
 
     private void performEnableScreen() {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: mDisplayEnabled=" + mDisplayEnabled
                     + " mForceDisplayEnabled=" + mForceDisplayEnabled
                     + " mShowingBootMessages=" + mShowingBootMessages
@@ -3411,7 +3255,7 @@
 
     public void showBootMessage(final CharSequence msg, final boolean always) {
         boolean first = false;
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if (DEBUG_BOOT) {
                 RuntimeException here = new RuntimeException("here");
                 here.fillInStackTrace();
@@ -3457,7 +3301,7 @@
 
     @Override
     public void setInTouchMode(boolean mode) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             mInTouchMode = mode;
         }
     }
@@ -3467,7 +3311,7 @@
                 && mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_windowShowCircularMask)) {
             final int currentUserId;
-            synchronized(mWindowMap) {
+            synchronized (mGlobalLock) {
                 currentUserId = mCurrentUserId;
             }
             // Device configuration calls for a circular display mask, but we only enable the mask
@@ -3492,7 +3336,7 @@
     }
 
     public void showCircularMask(boolean visible) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
 
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
                     ">>> OPEN TRANSACTION showCircularMask(visible=" + visible + ")");
@@ -3526,7 +3370,7 @@
     }
 
     public void showEmulatorDisplayOverlay() {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
 
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
                     ">>> OPEN TRANSACTION showEmulatorDisplayOverlay");
@@ -3567,7 +3411,7 @@
 
     private void showStrictModeViolation(int arg, int pid) {
         final boolean on = arg != 0;
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             // Ignoring requests to enable the red border from clients which aren't on screen.
             // (e.g. Broadcast Receivers in the background..)
             if (on && !mRoot.canShowStrictModeViolation(pid)) {
@@ -3606,7 +3450,7 @@
         }
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 return mRoot.mWallpaperController.screenshotWallpaperLocked();
             }
         } finally {
@@ -3626,7 +3470,7 @@
         }
 
         final Bitmap bm;
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(DEFAULT_DISPLAY);
             if (displayContent == null) {
                 if (DEBUG_SCREENSHOT) {
@@ -3663,7 +3507,7 @@
      *                       model.
      */
     public void removeObsoleteTaskFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mTaskSnapshotController.removeObsoleteTaskFiles(persistentTaskIds, runningUserIds);
         }
     }
@@ -3693,7 +3537,7 @@
 
         long origId = Binder.clearCallingIdentity();
         try {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 final DisplayContent display = mRoot.getDisplayContent(displayId);
                 if (display == null) {
                     Slog.w(TAG, "Trying to freeze rotation for a missing display.");
@@ -3729,7 +3573,7 @@
 
         long origId = Binder.clearCallingIdentity();
         try {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 final DisplayContent display = mRoot.getDisplayContent(displayId);
                 if (display == null) {
                     Slog.w(TAG, "Trying to thaw rotation for a missing display.");
@@ -3751,7 +3595,7 @@
 
     @Override
     public boolean isDisplayRotationFrozen(int displayId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent display = mRoot.getDisplayContent(displayId);
             if (display == null) {
                 Slog.w(TAG, "Trying to thaw rotation for a missing display.");
@@ -3783,7 +3627,7 @@
         long origId = Binder.clearCallingIdentity();
 
         try {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 boolean layoutNeeded = false;
                 final int displayCount = mRoot.mChildren.size();
                 for (int i = 0; i < displayCount; ++i) {
@@ -3797,8 +3641,7 @@
                         layoutNeeded = true;
                     }
                     if (rotationChanged || alwaysSendConfiguration) {
-                        mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
-                                .sendToTarget();
+                        displayContent.sendNewConfiguration();
                     }
                 }
 
@@ -3817,14 +3660,14 @@
 
     @Override
     public WindowManagerPolicy.DisplayContentInfo getDefaultDisplayContentInfo() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             return getDefaultDisplayContentLocked();
         }
     }
 
     @Override
     public int getDefaultDisplayRotation() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             return getDefaultDisplayContentLocked().getRotation();
         }
     }
@@ -3832,7 +3675,7 @@
     @Override
     public int watchRotation(IRotationWatcher watcher, int displayId) {
         final DisplayContent displayContent;
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             displayContent = mRoot.getDisplayContent(displayId);
         }
         if (displayContent == null) {
@@ -3844,7 +3687,7 @@
         IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
             @Override
             public void binderDied() {
-                synchronized (mWindowMap) {
+                synchronized (mGlobalLock) {
                     for (int i=0; i<mRotationWatchers.size(); i++) {
                         if (watcherBinder == mRotationWatchers.get(i).mWatcher.asBinder()) {
                             RotationWatcher removed = mRotationWatchers.remove(i);
@@ -3859,7 +3702,7 @@
             }
         };
 
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             try {
                 watcher.asBinder().linkToDeath(dr, 0);
                 mRotationWatchers.add(new RotationWatcher(watcher, dr, displayId));
@@ -3874,7 +3717,7 @@
     @Override
     public void removeRotationWatcher(IRotationWatcher watcher) {
         final IBinder watcherBinder = watcher.asBinder();
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             for (int i=0; i<mRotationWatchers.size(); i++) {
                 RotationWatcher rotationWatcher = mRotationWatchers.get(i);
                 if (watcherBinder == rotationWatcher.mWatcher.asBinder()) {
@@ -3892,7 +3735,7 @@
     @Override
     public boolean registerWallpaperVisibilityListener(IWallpaperVisibilityListener listener,
             int displayId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             if (displayContent == null) {
                 throw new IllegalArgumentException("Trying to register visibility event "
@@ -3906,7 +3749,7 @@
     @Override
     public void unregisterWallpaperVisibilityListener(IWallpaperVisibilityListener listener,
             int displayId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mWallpaperVisibilityListeners
                     .unregisterWallpaperVisibilityListener(listener, displayId);
         }
@@ -3914,7 +3757,7 @@
 
     @Override
     public int getPreferredOptionsPanelGravity(int displayId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             if (displayContent == null) {
                 return Gravity.CENTER | Gravity.BOTTOM;
@@ -4032,7 +3875,7 @@
         boolean result = true;
 
         final ArrayList<WindowState> windows = new ArrayList();
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mRoot.forAllWindows(w -> {
                 windows.add(w);
             }, false /* traverseTopToBottom */);
@@ -4214,20 +4057,20 @@
     }
 
     public void addWindowChangeListener(WindowChangeListener listener) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             mWindowChangeListeners.add(listener);
         }
     }
 
     public void removeWindowChangeListener(WindowChangeListener listener) {
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             mWindowChangeListeners.remove(listener);
         }
     }
 
     private void notifyWindowsChanged() {
         WindowChangeListener[] windowChangeListeners;
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if(mWindowChangeListeners.isEmpty()) {
                 return;
             }
@@ -4242,7 +4085,7 @@
 
     private void notifyFocusChanged() {
         WindowChangeListener[] windowChangeListeners;
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             if(mWindowChangeListeners.isEmpty()) {
                 return;
             }
@@ -4261,7 +4104,7 @@
             return getFocusedWindow();
         }
 
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             return mRoot.getWindow((w) -> System.identityHashCode(w) == hashCode);
         }
     }
@@ -4280,7 +4123,7 @@
                 // E.g. changing device rotation by 180 degrees. Go ahead and perform surface
                 // placement to unfreeze the display since we froze it when the rotation was updated
                 // in DisplayContent#updateRotationUnchecked.
-                synchronized (mWindowMap) {
+                synchronized (mGlobalLock) {
                     if (mWaitingForConfig) {
                         mWaitingForConfig = false;
                         mLastFinishedFreezeSource = "config-unchanged";
@@ -4297,7 +4140,7 @@
     }
 
     public Configuration computeNewConfiguration(int displayId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             return computeNewConfigurationLocked(displayId);
         }
     }
@@ -4315,7 +4158,7 @@
     void notifyHardKeyboardStatusChange() {
         final boolean available;
         final WindowManagerInternal.OnHardKeyboardStatusChangeListener listener;
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             listener = mHardKeyboardStatusChangeListener;
             available = mHardKeyboardAvailable;
         }
@@ -4337,7 +4180,7 @@
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mEventDispatchingEnabled = enabled;
             if (mDisplayEnabled) {
                 mInputManagerCallback.setEventDispatchingLw(enabled);
@@ -4346,7 +4189,7 @@
     }
 
     private WindowState getFocusedWindow() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             return getFocusedWindowLocked();
         }
     }
@@ -4415,31 +4258,15 @@
     }
 
     public void displayReady() {
-        final int displayCount = mRoot.mChildren.size();
-        for (int i = 0; i < displayCount; ++i) {
-            final DisplayContent display = mRoot.mChildren.get(i);
-            displayReady(display.getDisplayId());
-        }
-
-
-        synchronized(mWindowMap) {
-            final DisplayContent displayContent = getDefaultDisplayContentLocked();
+        synchronized (mGlobalLock) {
             if (mMaxUiWidth > 0) {
-                displayContent.setMaxUiWidth(mMaxUiWidth);
+                mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth));
             }
-            readForcedDisplayPropertiesLocked(displayContent);
+            applyForcedPropertiesForDefaultDisplay();
+            mAnimator.ready();
             mDisplayReady = true;
-        }
-
-        try {
-            mActivityTaskManager.updateConfiguration(null);
-        } catch (RemoteException e) {
-        }
-
-        synchronized(mWindowMap) {
             mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TOUCHSCREEN);
-            getDefaultDisplayContentLocked().configureDisplayPolicy();
         }
 
         try {
@@ -4450,17 +4277,6 @@
         updateCircularDisplayMaskIfNeeded();
     }
 
-    private void displayReady(int displayId) {
-        synchronized(mWindowMap) {
-            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-            if (displayContent != null) {
-                mAnimator.addDisplayLocked(displayId);
-                displayContent.initializeDisplayBaseInfo();
-                reconfigureDisplayLocked(displayContent);
-            }
-        }
-    }
-
     public void systemReady() {
         mPolicy.systemReady();
         mTaskSnapshotController.systemReady();
@@ -4566,7 +4382,7 @@
 
                     AccessibilityController accessibilityController = null;
 
-                    synchronized(mWindowMap) {
+                    synchronized (mGlobalLock) {
                         // TODO(multidisplay): Accessibility supported only of default desiplay.
                         if (mAccessibilityController != null && displayContent.isDefaultDisplay) {
                             accessibilityController = mAccessibilityController;
@@ -4610,7 +4426,7 @@
                     final DisplayContent displayContent = (DisplayContent) msg.obj;
                     ArrayList<WindowState> losers;
 
-                    synchronized(mWindowMap) {
+                    synchronized (mGlobalLock) {
                         losers = displayContent.mLosingFocus;
                         displayContent.mLosingFocus = new ArrayList<>();
                     }
@@ -4625,7 +4441,7 @@
 
                 case WINDOW_FREEZE_TIMEOUT: {
                     final DisplayContent displayContent = (DisplayContent) msg.obj;
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         displayContent.onWindowFreezeTimeout();
                     }
                     break;
@@ -4673,7 +4489,7 @@
                 }
 
                 case FORCE_GC: {
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         // Since we're holding both mWindowMap and mAnimator we don't need to
                         // hold mAnimator.mLayoutToAnim.
                         if (mAnimator.isAnimating() || mAnimator.isAnimationScheduled()) {
@@ -4698,7 +4514,7 @@
                 }
 
                 case APP_FREEZE_TIMEOUT: {
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         Slog.w(TAG_WM, "App freeze timeout expired.");
                         mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
                         for (int i = mAppFreezeListeners.size() - 1; i >=0 ; --i) {
@@ -4709,7 +4525,7 @@
                 }
 
                 case CLIENT_FREEZE_TIMEOUT: {
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         if (mClientFreezingScreen) {
                             mClientFreezingScreen = false;
                             mLastFinishedFreezeSource = "client-timeout";
@@ -4720,15 +4536,17 @@
                 }
 
                 case SEND_NEW_CONFIGURATION: {
-                    removeMessages(SEND_NEW_CONFIGURATION, msg.obj);
-                    final int displayId = (Integer) msg.obj;
-                    if (mRoot.getDisplayContent(displayId) != null) {
-                        sendNewConfiguration(displayId);
+                    final DisplayContent displayContent = (DisplayContent) msg.obj;
+                    removeMessages(SEND_NEW_CONFIGURATION, displayContent);
+                    if (displayContent.isReady()) {
+                        sendNewConfiguration(displayContent.getDisplayId());
                     } else {
                         // Message could come after display has already been removed.
                         if (DEBUG_CONFIGURATION) {
-                            Slog.w(TAG, "Trying to send configuration to non-existing displayId="
-                                    + displayId);
+                            final String reason = displayContent.getParent() == null
+                                    ? "detached" : "unready";
+                            Slog.w(TAG, "Trying to send configuration to " + reason + " display="
+                                    + displayContent);
                         }
                     }
                     break;
@@ -4736,7 +4554,7 @@
 
                 case REPORT_WINDOWS_CHANGE: {
                     if (mWindowsChanged) {
-                        synchronized (mWindowMap) {
+                        synchronized (mGlobalLock) {
                             mWindowsChanged = false;
                         }
                         notifyWindowsChanged();
@@ -4756,7 +4574,7 @@
 
                 case WAITING_FOR_DRAWN_TIMEOUT: {
                     Runnable callback = null;
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         Slog.w(TAG_WM, "Timeout waiting for drawn: undrawn=" + mWaitingForDrawn);
                         mWaitingForDrawn.clear();
                         callback = mWaitingForDrawnCallback;
@@ -4791,7 +4609,7 @@
                     break;
                 case ALL_WINDOWS_DRAWN: {
                     Runnable callback;
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         callback = mWaitingForDrawnCallback;
                         mWaitingForDrawnCallback = null;
                     }
@@ -4812,7 +4630,7 @@
                     } else {
                         ArrayList<IWindowSessionCallback> callbacks
                                 = new ArrayList<IWindowSessionCallback>();
-                        synchronized (mWindowMap) {
+                        synchronized (mGlobalLock) {
                             for (int i=0; i<mSessions.size(); i++) {
                                 callbacks.add(mSessions.valueAt(i).mCallback);
                             }
@@ -4829,7 +4647,7 @@
                 break;
                 case CHECK_IF_BOOT_ANIMATION_FINISHED: {
                     final boolean bootAnimationComplete;
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         if (DEBUG_BOOT) Slog.i(TAG_WM, "CHECK_IF_BOOT_ANIMATION_FINISHED:");
                         bootAnimationComplete = checkBootAnimationCompleteLocked();
                     }
@@ -4839,14 +4657,14 @@
                 }
                 break;
                 case RESET_ANR_MESSAGE: {
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         mLastANRState = null;
                     }
                     mAtmInternal.clearSavedANRState();
                 }
                 break;
                 case WALLPAPER_DRAW_PENDING_TIMEOUT: {
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         if (mRoot.mWallpaperController.processWallpaperDrawPendingTimeout()) {
                             mWindowPlacerLocked.performSurfacePlacement();
                         }
@@ -4854,7 +4672,7 @@
                 }
                 break;
                 case UPDATE_DOCKED_STACK_DIVIDER: {
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         final DisplayContent displayContent = getDefaultDisplayContentLocked();
                         displayContent.getDockedDividerController().reevaluateVisibility(false);
                         displayContent.adjustForImeIfNeeded();
@@ -4862,7 +4680,7 @@
                 }
                 break;
                 case WINDOW_REPLACEMENT_TIMEOUT: {
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         for (int i = mWindowReplacementTimeouts.size() - 1; i >= 0; i--) {
                             final AppWindowToken token = mWindowReplacementTimeouts.get(i);
                             token.onWindowReplacementTimeout();
@@ -4886,7 +4704,7 @@
                 break;
                 case WINDOW_HIDE_TIMEOUT: {
                     final WindowState window = (WindowState) msg.obj;
-                    synchronized(mWindowMap) {
+                    synchronized (mGlobalLock) {
                         // TODO: This is all about fixing b/21693547
                         // where partially initialized Toasts get stuck
                         // around and keep the screen on. We'd like
@@ -4910,20 +4728,20 @@
                 }
                 break;
                 case RESTORE_POINTER_ICON: {
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         restorePointerIconLocked((DisplayContent)msg.obj, msg.arg1, msg.arg2);
                     }
                 }
                 break;
                 case SEAMLESS_ROTATION_TIMEOUT: {
                     final DisplayContent displayContent = (DisplayContent) msg.obj;
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         displayContent.onSeamlessRotationTimeout();
                     }
                 }
                 break;
                 case NOTIFY_KEYGUARD_FLAGS_CHANGED: {
-                    mAtmInternal.notifyKeyguardFlagsChanged((Runnable) msg.obj);
+                    mAtmInternal.notifyKeyguardFlagsChanged((Runnable) msg.obj, msg.arg1);
                 }
                 break;
                 case NOTIFY_KEYGUARD_TRUSTED_CHANGED: {
@@ -4939,7 +4757,7 @@
                 }
                 break;
                 case ANIMATION_FAILSAFE: {
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         if (mRecentsAnimationController != null) {
                             mRecentsAnimationController.scheduleFailsafe();
                         }
@@ -4947,7 +4765,7 @@
                 }
                 break;
                 case RECOMPUTE_FOCUS: {
-                    synchronized (mWindowMap) {
+                    synchronized (mGlobalLock) {
                         updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
                                 true /* updateInputWindows */);
                     }
@@ -4985,7 +4803,7 @@
 
     @Override
     public void getInitialDisplaySize(int displayId, Point size) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
                 size.x = displayContent.mInitialDisplayWidth;
@@ -4996,7 +4814,7 @@
 
     @Override
     public void getBaseDisplaySize(int displayId, Point size) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
                 size.x = displayContent.mBaseDisplayWidth;
@@ -5016,21 +4834,10 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            synchronized(mWindowMap) {
-                // Set some sort of reasonable bounds on the size of the display that we
-                // will try to emulate.
-                final int MIN_WIDTH = 200;
-                final int MIN_HEIGHT = 200;
-                final int MAX_SCALE = 2;
+            synchronized (mGlobalLock) {
                 final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                 if (displayContent != null) {
-                    width = Math.min(Math.max(width, MIN_WIDTH),
-                            displayContent.mInitialDisplayWidth * MAX_SCALE);
-                    height = Math.min(Math.max(height, MIN_HEIGHT),
-                            displayContent.mInitialDisplayHeight * MAX_SCALE);
-                    setForcedDisplaySizeLocked(displayContent, width, height);
-                    Settings.Global.putString(mContext.getContentResolver(),
-                            Settings.Global.DISPLAY_SIZE_FORCED, width + "," + height);
+                    displayContent.setForcedSize(width, height);
                 }
             }
         } finally {
@@ -5049,15 +4856,10 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            synchronized(mWindowMap) {
+            synchronized (mGlobalLock) {
                 final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                 if (displayContent != null) {
-                    if (mode < 0 || mode > 1) {
-                        mode = 0;
-                    }
-                    setForcedDisplayScalingModeLocked(displayContent, mode);
-                    Settings.Global.putInt(mContext.getContentResolver(),
-                            Settings.Global.DISPLAY_SCALING_FORCE, mode);
+                    displayContent.setForcedScalingMode(mode);
                 }
             }
         } finally {
@@ -5065,13 +4867,9 @@
         }
     }
 
-    private void setForcedDisplayScalingModeLocked(DisplayContent displayContent, int mode) {
-        Slog.i(TAG_WM, "Using display scaling mode: " + (mode == 0 ? "auto" : "off"));
-        displayContent.mDisplayScalingDisabled = (mode != 0);
-        reconfigureDisplayLocked(displayContent);
-    }
-
-    private void readForcedDisplayPropertiesLocked(final DisplayContent displayContent) {
+    /** The global settings only apply to default display. */
+    private void applyForcedPropertiesForDefaultDisplay() {
+        final DisplayContent displayContent = getDefaultDisplayContentLocked();
         // Display size.
         String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.DISPLAY_SIZE_FORCED);
@@ -5111,13 +4909,6 @@
         }
     }
 
-    // displayContent must not be null
-    private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) {
-        Slog.i(TAG_WM, "Using new display size: " + width + "x" + height);
-        displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity);
-        reconfigureDisplayLocked(displayContent);
-    }
-
     @Override
     public void clearForcedDisplaySize(int displayId) {
         if (mContext.checkCallingOrSelfPermission(
@@ -5129,13 +4920,11 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            synchronized(mWindowMap) {
+            synchronized (mGlobalLock) {
                 final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                 if (displayContent != null) {
-                    setForcedDisplaySizeLocked(displayContent, displayContent.mInitialDisplayWidth,
+                    displayContent.setForcedSize(displayContent.mInitialDisplayWidth,
                             displayContent.mInitialDisplayHeight);
-                    Settings.Global.putString(mContext.getContentResolver(),
-                            Settings.Global.DISPLAY_SIZE_FORCED, "");
                 }
             }
         } finally {
@@ -5145,7 +4934,7 @@
 
     @Override
     public int getInitialDisplayDensity(int displayId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
                 return displayContent.mInitialDisplayDensity;
@@ -5156,7 +4945,7 @@
 
     @Override
     public int getBaseDisplayDensity(int displayId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
                 return displayContent.mBaseDisplayDensity;
@@ -5179,14 +4968,11 @@
                 null);
         final long ident = Binder.clearCallingIdentity();
         try {
-            synchronized(mWindowMap) {
+            synchronized (mGlobalLock) {
                 final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-                if (displayContent != null && mCurrentUserId == targetUserId) {
-                    setForcedDisplayDensityLocked(displayContent, density);
+                if (displayContent != null) {
+                    displayContent.setForcedDensity(density, targetUserId);
                 }
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        Settings.Secure.DISPLAY_DENSITY_FORCED,
-                        Integer.toString(density), targetUserId);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -5207,14 +4993,12 @@
                 null);
         final long ident = Binder.clearCallingIdentity();
         try {
-            synchronized(mWindowMap) {
+            synchronized (mGlobalLock) {
                 final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-                if (displayContent != null && mCurrentUserId == callingUserId) {
-                    setForcedDisplayDensityLocked(displayContent,
-                            displayContent.mInitialDisplayDensity);
+                if (displayContent != null) {
+                    displayContent.setForcedDensity(displayContent.mInitialDisplayDensity,
+                            callingUserId);
                 }
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        Settings.Secure.DISPLAY_DENSITY_FORCED, "", callingUserId);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -5241,18 +5025,6 @@
         return 0;
     }
 
-    /**
-     * Forces the given display to the use the specified density.
-     *
-     * @param displayContent the display to modify
-     * @param density the density in DPI to use
-     */
-    private void setForcedDisplayDensityLocked(@NonNull DisplayContent displayContent,
-            int density) {
-        displayContent.mBaseDisplayDensity = density;
-        reconfigureDisplayLocked(displayContent);
-    }
-
     void reconfigureDisplayLocked(@NonNull DisplayContent displayContent) {
         if (!displayContent.isReady()) {
             return;
@@ -5260,8 +5032,7 @@
         displayContent.configureDisplayPolicy();
         displayContent.setLayoutNeeded();
 
-        final int displayId = displayContent.getDisplayId();
-        boolean configChanged = updateOrientationFromAppTokensLocked(displayId);
+        boolean configChanged = displayContent.updateOrientationFromAppTokens();
         final Configuration currentDisplayConfig = displayContent.getConfiguration();
         mTempConfiguration.setTo(currentDisplayConfig);
         displayContent.computeScreenConfiguration(mTempConfiguration);
@@ -5271,7 +5042,7 @@
             mWaitingForConfig = true;
             startFreezingDisplayLocked(0 /* exitAnim */,
                     0 /* enterAnim */, displayContent);
-            mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+            displayContent.sendNewConfiguration();
         }
 
         mWindowPlacerLocked.performSurfacePlacement();
@@ -5287,7 +5058,7 @@
         }
         final long ident = Binder.clearCallingIdentity();
         try {
-            synchronized(mWindowMap) {
+            synchronized (mGlobalLock) {
                 DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                 if (displayContent != null) {
                     setOverscanLocked(displayContent, left, top, right, bottom);
@@ -5306,9 +5077,7 @@
         displayInfo.overscanRight = right;
         displayInfo.overscanBottom = bottom;
 
-        mDisplaySettings.setOverscanLocked(displayInfo.uniqueId, displayInfo.name, left, top,
-                right, bottom);
-        mDisplaySettings.writeSettingsLocked();
+        mDisplaySettings.setOverscanLocked(displayInfo, left, top, right, bottom);
 
         reconfigureDisplayLocked(displayContent);
     }
@@ -5382,39 +5151,6 @@
         }
     }
 
-    /**
-     * @return bitmap indicating if another pass through layout must be made.
-     */
-    int handleAnimatingStoppedAndTransitionLocked() {
-        int changes = 0;
-
-        mAppTransition.setIdle();
-
-        for (int i = mNoAnimationNotifyOnTransitionFinished.size() - 1; i >= 0; i--) {
-            final IBinder token = mNoAnimationNotifyOnTransitionFinished.get(i);
-            mAppTransition.notifyAppTransitionFinishedLocked(token);
-        }
-        mNoAnimationNotifyOnTransitionFinished.clear();
-
-        // TODO: multi-display.
-        final DisplayContent dc = getDefaultDisplayContentLocked();
-
-        dc.mWallpaperController.hideDeferredWallpapersIfNeeded();
-
-        dc.onAppTransitionDone();
-
-        changes |= FINISH_LAYOUT_REDO_LAYOUT;
-        if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG_WM,
-                "Wallpaper layer changed: assigning layers + relayout");
-        dc.computeImeTarget(true /* updateImeTarget */);
-        mRoot.mWallpaperMayChange = true;
-        // Since the window list has been rebuilt, focus might have to be recomputed since the
-        // actual order of windows might have changed again.
-        mFocusMayChange = true;
-
-        return changes;
-    }
-
     void checkDrawnWindowsLocked() {
         if (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) {
             return;
@@ -5475,7 +5211,7 @@
     }
 
     void requestTraversal() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mWindowPlacerLocked.requestTraversal();
         }
     }
@@ -5527,8 +5263,8 @@
 
         mInputManagerCallback.freezeInputDispatchingLw();
 
-        if (mAppTransition.isTransitionSet()) {
-            mAppTransition.freeze();
+        if (displayContent.mAppTransition.isTransitionSet()) {
+            displayContent.mAppTransition.freeze();
         }
 
         if (PROFILE_ORIENTATION) {
@@ -5563,15 +5299,16 @@
             return;
         }
 
+        final DisplayContent dc = mRoot.getDisplayContent(mFrozenDisplayId);
         if (mWaitingForConfig || mAppsFreezingScreen > 0
                 || mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
-                || mClientFreezingScreen || !mOpeningApps.isEmpty()) {
+                || mClientFreezingScreen || (dc != null && !dc.mOpeningApps.isEmpty())) {
             if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
                 "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig
                 + ", mAppsFreezingScreen=" + mAppsFreezingScreen
                 + ", mWindowsFreezingScreen=" + mWindowsFreezingScreen
                 + ", mClientFreezingScreen=" + mClientFreezingScreen
-                + ", mOpeningApps.size()=" + mOpeningApps.size());
+                + ", mOpeningApps.size()=" + (dc != null ? dc.mOpeningApps.size() : 0));
             return;
         }
 
@@ -5609,7 +5346,6 @@
         if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
                 && screenRotationAnimation.hasScreenshot()) {
             if (DEBUG_ORIENTATION) Slog.i(TAG_WM, "**** Dismissing screen rotation animation");
-            // TODO(multidisplay): rotation on main screen only.
             DisplayInfo displayInfo = displayContent.getDisplayInfo();
             // Get rotation animation again, with new top window
             if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
@@ -5639,7 +5375,7 @@
         // to avoid inconsistent states.  However, something interesting
         // could have actually changed during that time so re-evaluate it
         // now to catch that.
-        configChanged = updateOrientationFromAppTokensLocked(displayId);
+        configChanged = displayContent != null && displayContent.updateOrientationFromAppTokens();
 
         // A little kludge: a lot could have happened while the
         // display was frozen, so now that we are coming back we
@@ -5651,13 +5387,13 @@
 
         mScreenFrozenLock.release();
 
-        if (updateRotation) {
+        if (updateRotation && displayContent != null && updateRotation) {
             if (DEBUG_ORIENTATION) Slog.d(TAG_WM, "Performing post-rotate rotation");
             configChanged |= displayContent.updateRotationUnchecked();
         }
 
         if (configChanged) {
-            mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+            displayContent.sendNewConfiguration();
         }
         mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN);
     }
@@ -5723,7 +5459,7 @@
     public void setRecentsVisibility(boolean visible) {
         mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
                 "setRecentsVisibility()");
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mPolicy.setRecentsVisibilityLw(visible);
         }
     }
@@ -5736,7 +5472,7 @@
                     + android.Manifest.permission.STATUS_BAR);
         }
 
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mPolicy.setPipVisibilityLw(visible);
         }
     }
@@ -5745,7 +5481,7 @@
     public void setShelfHeight(boolean visible, int shelfHeight) {
         mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
                 "setShelfHeight()");
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             getDefaultDisplayContentLocked().getPinnedStackController().setAdjustedForShelf(visible,
                     shelfHeight);
         }
@@ -5759,7 +5495,7 @@
                     + android.Manifest.permission.STATUS_BAR);
         }
 
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mLastStatusBarVisibility = visibility;
             visibility = mPolicy.adjustSystemUiVisibilityLw(visibility);
             updateStatusBarVisibilityLocked(visibility);
@@ -5773,7 +5509,7 @@
                     + android.Manifest.permission.STATUS_BAR);
         }
 
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mPolicy.setNavBarVirtualKeyHapticFeedbackEnabledLw(enabled);
         }
     }
@@ -5798,7 +5534,7 @@
 
     @Override
     public void reevaluateStatusBarVisibility() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             int visibility = mPolicy.adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
             if (updateStatusBarVisibilityLocked(visibility)) {
                 mWindowPlacerLocked.requestTraversal();
@@ -5814,7 +5550,7 @@
     @Override
     @WindowManagerPolicy.NavigationBarPosition
     public int getNavBarPosition() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             // Perform layout if it was scheduled before to make sure that we get correct nav bar
             // position when doing rotations.
             final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
@@ -5827,7 +5563,7 @@
     @Override
     public WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name,
             InputEventReceiver.Factory inputEventReceiverFactory, int displayId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             if (displayContent != null) {
                 return displayContent.getInputMonitor().createInputConsumer(looper, name,
@@ -5841,7 +5577,7 @@
     @Override
     public void createInputConsumer(IBinder token, String name, int displayId,
             InputChannel inputChannel) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             DisplayContent display = mRoot.getDisplayContent(displayId);
             if (display != null) {
                 display.getInputMonitor().createInputConsumer(token, name, inputChannel,
@@ -5852,7 +5588,7 @@
 
     @Override
     public boolean destroyInputConsumer(String name, int displayId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             DisplayContent display = mRoot.getDisplayContent(displayId);
             if (display != null) {
                 return display.getInputMonitor().destroyInputConsumer(name);
@@ -5866,7 +5602,7 @@
         if (mContext.checkCallingOrSelfPermission(RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) {
             throw new SecurityException("getCurrentImeTouchRegion is restricted to VR services");
         }
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final Region r = new Region();
             // TODO(b/111080190): this method is only return the recent focused IME touch region,
             // For Multi-Session IME, will need to add API for given display Id to
@@ -5907,7 +5643,7 @@
                 "clearWindowContentFrameStats()")) {
             throw new SecurityException("Requires FRAME_STATS permission");
         }
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             WindowState windowState = mWindowMap.get(token);
             if (windowState == null) {
                 return false;
@@ -5926,7 +5662,7 @@
                 "getWindowContentFrameStats()")) {
             throw new SecurityException("Requires FRAME_STATS permission");
         }
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             WindowState windowState = mWindowMap.get(token);
             if (windowState == null) {
                 return null;
@@ -5947,7 +5683,7 @@
     }
 
     public void notifyAppRelaunching(IBinder token) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
             if (appWindow != null) {
                 appWindow.startRelaunching();
@@ -5956,7 +5692,7 @@
     }
 
     public void notifyAppRelaunchingFinished(IBinder token) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
             if (appWindow != null) {
                 appWindow.finishRelaunching();
@@ -5965,7 +5701,7 @@
     }
 
     public void notifyAppRelaunchesCleared(IBinder token) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
             if (appWindow != null) {
                 appWindow.clearRelaunching();
@@ -5974,10 +5710,11 @@
     }
 
     public void notifyAppResumedFinished(IBinder token) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
             if (appWindow != null) {
-                mUnknownAppVisibilityController.notifyAppResumedFinished(appWindow);
+                appWindow.getDisplayContent().mUnknownAppVisibilityController
+                        .notifyAppResumedFinished(appWindow);
             }
         }
     }
@@ -5986,7 +5723,7 @@
      * Returns true if the callingUid has any window currently visible to the user.
      */
     public boolean isAnyWindowVisibleForUid(int callingUid) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             return mRoot.forAllWindows(w -> {
                 return w.getOwningUid() == callingUid && w.isVisible();
             }, true /* traverseTopToBottom */);
@@ -6000,7 +5737,7 @@
      * container may not exist when this happens.
      */
     public void notifyTaskRemovedFromRecents(int taskId, int userId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mTaskSnapshotController.notifyTaskRemovedFromRecents(taskId, userId);
         }
     }
@@ -6023,15 +5760,6 @@
     private void dumpTokensLocked(PrintWriter pw, boolean dumpAll) {
         pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)");
         mRoot.dumpTokens(pw, dumpAll);
-        if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty()) {
-            pw.println();
-            if (mOpeningApps.size() > 0) {
-                pw.print("  mOpeningApps="); pw.println(mOpeningApps);
-            }
-            if (mClosingApps.size() > 0) {
-                pw.print("  mClosingApps="); pw.println(mClosingApps);
-            }
-        }
     }
 
     private void dumpSessionsLocked(PrintWriter pw, boolean dumpAll) {
@@ -6068,7 +5796,6 @@
         final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
         proto.write(ROTATION, defaultDisplayContent.getRotation());
         proto.write(LAST_ORIENTATION, defaultDisplayContent.getLastOrientation());
-        mAppTransition.writeToProto(proto, APP_TRANSITION);
     }
 
     void traceStateLocked(String where) {
@@ -6201,7 +5928,6 @@
                 pw.println();
 
         mInputManagerCallback.dump(pw, "  ");
-        mUnknownAppVisibilityController.dump(pw, "  ");
         mTaskSnapshotController.dump(pw, "  ");
 
         if (dumpAll) {
@@ -6240,9 +5966,6 @@
                     pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
                     pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
                     pw.print(" animator="); pw.println(mAnimatorDurationScaleSetting);
-            pw.print("  mSkipAppTransitionAnimation=");pw.println(mSkipAppTransitionAnimation);
-            pw.println("  mLayoutToAnim:");
-            mAppTransition.dump(pw, "    ");
             if (mRecentsAnimationController != null) {
                 pw.print("  mRecentsAnimationController="); pw.println(mRecentsAnimationController);
                 mRecentsAnimationController.dump(pw, "    ");
@@ -6256,7 +5979,7 @@
         if ("apps".equals(name) || "visible".equals(name) || "visible-apps".equals(name)) {
             final boolean appsOnly = name.contains("apps");
             final boolean visibleOnly = name.contains("visible");
-            synchronized(mWindowMap) {
+            synchronized (mGlobalLock) {
                 if (appsOnly) {
                     mRoot.dumpDisplayContents(pw);
                 }
@@ -6269,7 +5992,7 @@
                 }, true /* traverseTopToBottom */);
             }
         } else {
-            synchronized(mWindowMap) {
+            synchronized (mGlobalLock) {
                 mRoot.getWindowsByName(windows, name);
             }
         }
@@ -6278,7 +6001,7 @@
             return false;
         }
 
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             dumpWindowsLocked(pw, dumpAll, windows);
         }
         return true;
@@ -6385,7 +6108,7 @@
 
         if (useProto) {
             final ProtoOutputStream proto = new ProtoOutputStream(fd);
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 writeToProtoLocked(proto, false /* trim */);
             }
             proto.flush();
@@ -6396,47 +6119,47 @@
             String cmd = args[opti];
             opti++;
             if ("lastanr".equals(cmd) || "l".equals(cmd)) {
-                synchronized(mWindowMap) {
+                synchronized (mGlobalLock) {
                     dumpLastANRLocked(pw);
                 }
                 return;
             } else if ("policy".equals(cmd) || "p".equals(cmd)) {
-                synchronized(mWindowMap) {
+                synchronized (mGlobalLock) {
                     dumpPolicyLocked(pw, args, true);
                 }
                 return;
             } else if ("animator".equals(cmd) || "a".equals(cmd)) {
-                synchronized(mWindowMap) {
+                synchronized (mGlobalLock) {
                     dumpAnimatorLocked(pw, args, true);
                 }
                 return;
             } else if ("sessions".equals(cmd) || "s".equals(cmd)) {
-                synchronized(mWindowMap) {
+                synchronized (mGlobalLock) {
                     dumpSessionsLocked(pw, true);
                 }
                 return;
             } else if ("displays".equals(cmd) || "d".equals(cmd)) {
-                synchronized(mWindowMap) {
+                synchronized (mGlobalLock) {
                     mRoot.dumpDisplayContents(pw);
                 }
                 return;
             } else if ("tokens".equals(cmd) || "t".equals(cmd)) {
-                synchronized(mWindowMap) {
+                synchronized (mGlobalLock) {
                     dumpTokensLocked(pw, true);
                 }
                 return;
             } else if ("windows".equals(cmd) || "w".equals(cmd)) {
-                synchronized(mWindowMap) {
+                synchronized (mGlobalLock) {
                     dumpWindowsLocked(pw, true, null);
                 }
                 return;
             } else if ("all".equals(cmd) || "a".equals(cmd)) {
-                synchronized(mWindowMap) {
+                synchronized (mGlobalLock) {
                     dumpWindowsLocked(pw, true, null);
                 }
                 return;
             } else if ("containers".equals(cmd)) {
-                synchronized(mWindowMap) {
+                synchronized (mGlobalLock) {
                     mRoot.dumpChildrenNames(pw, " ");
                     pw.println(" ");
                     mRoot.forAllWindows(w -> {pw.println(w);}, true /* traverseTopToBottom */);
@@ -6452,7 +6175,7 @@
             }
         }
 
-        synchronized(mWindowMap) {
+        synchronized (mGlobalLock) {
             pw.println();
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
@@ -6497,7 +6220,7 @@
     // Called by the heartbeat to ensure locks are not held indefnitely (for deadlock detection).
     @Override
     public void monitor() {
-        synchronized (mWindowMap) { }
+        synchronized (mGlobalLock) { }
     }
 
     // There is an inherent assumption that this will never return null.
@@ -6507,25 +6230,8 @@
         return mRoot.getDisplayContent(DEFAULT_DISPLAY);
     }
 
-    public void onDisplayAdded(int displayId) {
-        synchronized (mWindowMap) {
-            final Display display = mDisplayManager.getDisplay(displayId);
-            if (display != null) {
-                displayReady(displayId);
-            }
-            mWindowPlacerLocked.requestTraversal();
-        }
-    }
-
-    public void onDisplayRemoved(int displayId) {
-        synchronized (mWindowMap) {
-            mAnimator.removeDisplayLocked(displayId);
-            mWindowPlacerLocked.requestTraversal();
-        }
-    }
-
     public void onOverlayChanged() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mRoot.forAllDisplays(displayContent -> {
                 mPolicy.onOverlayChangedLw(displayContent);
                 displayContent.updateDisplayInfo();
@@ -6535,7 +6241,7 @@
     }
 
     public void onDisplayChanged(int displayId) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             if (displayContent != null) {
                 displayContent.updateDisplayInfo();
@@ -6546,7 +6252,7 @@
 
     @Override
     public Object getWindowManagerLock() {
-        return mWindowMap;
+        return mGlobalLock;
     }
 
     /**
@@ -6555,7 +6261,7 @@
      * @param token Application token for which the activity will be relaunched.
      */
     public void setWillReplaceWindow(IBinder token, boolean animate) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
             if (appWindowToken == null) {
                 Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
@@ -6584,7 +6290,7 @@
     // TODO: The s at the end of the method name is the only difference with the name of the method
     // above. We should combine them or find better names.
     void setWillReplaceWindows(IBinder token, boolean childrenOnly) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
             if (appWindowToken == null) {
                 Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
@@ -6617,7 +6323,7 @@
      * @param replacing Whether the window is being replaced or not.
      */
     public void scheduleClearWillReplaceWindows(IBinder token, boolean replacing) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
             if (appWindowToken == null) {
                 Slog.w(TAG_WM, "Attempted to reset replacing window on non-existing app token "
@@ -6643,7 +6349,7 @@
 
     @Override
     public int getDockedStackSide() {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final TaskStack dockedStack = getDefaultDisplayContentLocked()
                     .getSplitScreenPrimaryStackIgnoringVisibility();
             return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide();
@@ -6651,7 +6357,7 @@
     }
 
     public void setDockedStackResizing(boolean resizing) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing);
             requestTraversal();
         }
@@ -6659,7 +6365,7 @@
 
     @Override
     public void setDockedStackDividerTouchRegion(Rect touchRegion) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent dc = getDefaultDisplayContentLocked();
             dc.getDockedDividerController().setTouchRegion(touchRegion);
             dc.updateTouchExcludeRegion();
@@ -6668,32 +6374,32 @@
 
     @Override
     public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer(
                     visible, targetWindowingMode, alpha);
         }
     }
 
     public void setForceResizableTasks(boolean forceResizableTasks) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mForceResizableTasks = forceResizableTasks;
         }
     }
 
     public void setSupportsPictureInPicture(boolean supportsPictureInPicture) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mSupportsPictureInPicture = supportsPictureInPicture;
         }
     }
 
     public void setSupportsFreeformWindowManagement(boolean supportsFreeformWindowManagement) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mSupportsFreeformWindowManagement = supportsFreeformWindowManagement;
         }
     }
 
     public void setIsPc(boolean isPc) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mIsPc = isPc;
         }
     }
@@ -6706,7 +6412,7 @@
     public void registerDockedStackListener(IDockedStackListener listener) {
         mAtmInternal.enforceCallerIsRecentsOrHasPermission(REGISTER_WINDOW_MANAGER_LISTENERS,
                 "registerDockedStackListener()");
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             // TODO(multi-display): The listener is registered on the default display only.
             getDefaultDisplayContentLocked().mDividerControllerLocked.registerDockedStackListener(
                     listener);
@@ -6722,7 +6428,7 @@
         if (!mSupportsPictureInPicture) {
             return;
         }
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             displayContent.getPinnedStackController().registerPinnedStackListener(listener);
         }
@@ -6741,7 +6447,7 @@
 
     @Override
     public void getStableInsets(int displayId, Rect outInsets) throws RemoteException {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             getStableInsetsLocked(displayId, outInsets);
         }
     }
@@ -6800,7 +6506,7 @@
             mouseY = mMousePositionTracker.mLatestMouseY;
         }
 
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mDragDropController.dragDropActiveLocked()) {
                 // Drag cursor overrides the app cursor.
                 return;
@@ -6855,7 +6561,7 @@
      */
     void updateTapExcludeRegion(IWindow client, int regionId, int left, int top, int width,
             int height) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             final WindowState callingWin = windowForClientLocked(null, client, false);
             if (callingWin == null) {
                 Slog.w(TAG_WM, "Bad requesting window " + client);
@@ -6869,7 +6575,7 @@
     public void dontOverrideDisplayInfo(int displayId) {
         final long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 final DisplayContent dc = getDisplayContentOrCreate(displayId, null);
                 if (dc == null) {
                     throw new IllegalArgumentException(
@@ -6935,7 +6641,7 @@
 
         @Override
         public void setMagnificationSpec(MagnificationSpec spec) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 if (mAccessibilityController != null) {
                     mAccessibilityController.setMagnificationSpecLocked(spec);
                 } else {
@@ -6949,7 +6655,7 @@
 
         @Override
         public void setForceShowMagnifiableBounds(boolean show) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 if (mAccessibilityController != null) {
                     mAccessibilityController.setForceShowMagnifiableBoundsLocked(show);
                 } else {
@@ -6960,7 +6666,7 @@
 
         @Override
         public void getMagnificationRegion(@NonNull Region magnificationRegion) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 if (mAccessibilityController != null) {
                     mAccessibilityController.getMagnificationRegionLocked(magnificationRegion);
                 } else {
@@ -6971,7 +6677,7 @@
 
         @Override
         public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 WindowState windowState = mWindowMap.get(windowToken);
                 if (windowState == null) {
                     return null;
@@ -6991,7 +6697,7 @@
 
         @Override
         public void setMagnificationCallbacks(@Nullable MagnificationCallbacks callbacks) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 if (mAccessibilityController == null) {
                     mAccessibilityController = new AccessibilityController(
                             WindowManagerService.this);
@@ -7005,7 +6711,7 @@
 
         @Override
         public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 if (mAccessibilityController == null) {
                     mAccessibilityController = new AccessibilityController(
                             WindowManagerService.this);
@@ -7024,7 +6730,7 @@
 
         @Override
         public IBinder getFocusedWindowToken() {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 WindowState windowState = getFocusedWindowLocked();
                 if (windowState != null) {
                     return windowState.mClient.asBinder();
@@ -7050,7 +6756,7 @@
 
         @Override
         public void getWindowFrame(IBinder token, Rect outBounds) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 WindowState windowState = mWindowMap.get(token);
                 if (windowState != null) {
                     outBounds.set(windowState.getFrameLw());
@@ -7063,7 +6769,7 @@
         @Override
         public void waitForAllWindowsDrawn(Runnable callback, long timeout) {
             boolean allWindowsDrawn = false;
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 mWaitingForDrawnCallback = callback;
                 getDefaultDisplayContentLocked().waitForAllWindowsDrawn();
                 mWindowPlacerLocked.requestTraversal();
@@ -7087,7 +6793,7 @@
 
         @Override
         public void removeWindowToken(IBinder binder, boolean removeWindows, int displayId) {
-            synchronized(mWindowMap) {
+            synchronized (mGlobalLock) {
                 if (removeWindows) {
                     final DisplayContent dc = mRoot.getDisplayContent(displayId);
                     if (dc == null) {
@@ -7109,16 +6815,18 @@
             }
         }
 
+        // TODO(multi-display): currently only used by PWM to notify keyguard transitions as well
+        // forwarding it to SystemUI for synchronizing status and navigation bar animations.
         @Override
         public void registerAppTransitionListener(AppTransitionListener listener) {
-            synchronized (mWindowMap) {
-                mAppTransition.registerListenerLocked(listener);
+            synchronized (mGlobalLock) {
+                getDefaultDisplayContentLocked().mAppTransition.registerListenerLocked(listener);
             }
         }
 
         @Override
         public int getInputMethodWindowVisibleHeight(int displayId) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 final DisplayContent dc = mRoot.getDisplayContent(displayId);
                 return dc.mDisplayFrames.getInputMethodWindowVisibleHeight();
             }
@@ -7142,7 +6850,7 @@
 
         @Override
         public boolean isHardKeyboardAvailable() {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 return mHardKeyboardAvailable;
             }
         }
@@ -7150,14 +6858,14 @@
         @Override
         public void setOnHardKeyboardStatusChangeListener(
                 OnHardKeyboardStatusChangeListener listener) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 mHardKeyboardStatusChangeListener = listener;
             }
         }
 
         @Override
         public boolean isStackVisible(int windowingMode) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 final DisplayContent dc = getDefaultDisplayContentLocked();
                 return dc.isStackVisible(windowingMode);
             }
@@ -7165,7 +6873,7 @@
 
         @Override
         public boolean isDockedDividerResizing() {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 return getDefaultDisplayContentLocked().getDockedDividerController().isResizing();
             }
         }
@@ -7173,7 +6881,7 @@
         @Override
         public void computeWindowsForAccessibility() {
             final AccessibilityController accessibilityController;
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 accessibilityController = mAccessibilityController;
             }
             if (accessibilityController != null) {
@@ -7186,7 +6894,7 @@
             if (DEBUG_DISPLAY) {
                 Slog.d(TAG, "setVr2dDisplayId called for: " + vr2dDisplayId);
             }
-            synchronized (WindowManagerService.this) {
+            synchronized (mGlobalLock) {
                 mVr2dDisplayId = vr2dDisplayId;
             }
         }
@@ -7203,7 +6911,7 @@
 
         @Override
         public int getWindowOwnerUserId(IBinder token) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 WindowState window = mWindowMap.get(token);
                 if (window != null) {
                     return UserHandle.getUserId(window.mOwnerUid);
@@ -7214,7 +6922,7 @@
 
         @Override
         public boolean isUidFocused(int uid) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
                     final DisplayContent displayContent = mRoot.getChildAt(i);
                     if (displayContent.mCurrentFocus != null
@@ -7231,7 +6939,7 @@
             if (displayId == Display.INVALID_DISPLAY) {
                 return false;
             }
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 final DisplayContent displayContent = mRoot.getTopFocusedDisplayContent();
                 if (displayContent == null
                         || displayContent.getDisplayId() != displayId
@@ -7266,7 +6974,7 @@
             if (displayId == Display.INVALID_DISPLAY) {
                 return false;
             }
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                 return displayContent != null && displayContent.hasAccess(uid);
             }
@@ -7274,7 +6982,7 @@
 
         @Override
         public int getDisplayIdForWindow(IBinder windowToken) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 final WindowState window = mWindowMap.get(windowToken);
                 if (window != null) {
                     return window.getDisplayContent().getDisplayId();
@@ -7285,7 +6993,7 @@
 
         @Override
         public void onProcessConfigurationChanged(int pid, Configuration newConfig) {
-            synchronized (mWindowMap) {
+            synchronized (mGlobalLock) {
                 Configuration currentConfig = mProcessConfigurations.get(pid);
                 if (currentConfig == null) {
                     currentConfig = new Configuration(newConfig);
@@ -7311,10 +7019,6 @@
      * WARNING: This interrupts surface updates, be careful! Don't
      * execute within the transaction for longer than you would
      * execute on an animation thread.
-     * WARNING: This holds the WindowManager lock, so if exec will acquire
-     * the ActivityManager lock, you should hold it BEFORE calling this
-     * otherwise there is a risk of deadlock if another thread holding the AM
-     * lock waits on the WM lock.
      * WARNING: This method contains locks known to the State of California
      * to cause Deadlocks and other conditions.
      *
@@ -7335,25 +7039,18 @@
      * deferSurfaceLayout may be a little too broad, in particular the total
      * enclosure of startActivityUnchecked which could run for quite some time.
      */
-    public void inSurfaceTransaction(Runnable exec) {
-        // We hold the WindowManger lock to ensure relayoutWindow
-        // does not return while a Surface transaction is opening.
-        // The client depends on us to have resized the surface
-        // by that point (b/36462635)
-
-        synchronized (mWindowMap) {
-            SurfaceControl.openTransaction();
-            try {
-                exec.run();
-            } finally {
-                SurfaceControl.closeTransaction();
-            }
+    void inSurfaceTransaction(Runnable exec) {
+        SurfaceControl.openTransaction();
+        try {
+            exec.run();
+        } finally {
+            SurfaceControl.closeTransaction();
         }
     }
 
     /** Called to inform window manager if non-Vr UI shoul be disabled or not. */
     public void disableNonVrUi(boolean disable) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             // Allow alert window notifications to be shown if non-vr UI is enabled.
             final boolean showAlertWindowNotifications = !disable;
             if (showAlertWindowNotifications == mShowAlertWindowNotifications) {
@@ -7423,6 +7120,10 @@
         mRotatingSeamlessly = true;
     }
 
+    boolean isRotatingSeamlessly() {
+        return mRotatingSeamlessly;
+    }
+
     void finishSeamlessRotation() {
         mRotatingSeamlessly = false;
     }
@@ -7437,7 +7138,7 @@
      *                      {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
      */
     public void onLockTaskStateChanged(int lockTaskState) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             mPolicy.onLockTaskStateChangedLw(lockTaskState);
         }
     }
@@ -7448,7 +7149,7 @@
      * ensure the new value takes effect.
      */
     public void setAodShowing(boolean aodShowing) {
-        synchronized (mWindowMap) {
+        synchronized (mGlobalLock) {
             if (mPolicy.setAodShowing(aodShowing)) {
                 mWindowPlacerLocked.performSurfacePlacement();
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index bf2d0df..bf77ba8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -233,9 +233,11 @@
     private int runDisplayScaling(PrintWriter pw) throws RemoteException {
         String scalingStr = getNextArgRequired();
         if ("auto".equals(scalingStr)) {
-            mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 0);
+            mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr),
+                    DisplayContent.FORCE_SCALING_MODE_AUTO);
         } else if ("off".equals(scalingStr)) {
-            mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 1);
+            mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr),
+                    DisplayContent.FORCE_SCALING_MODE_DISABLED);
         } else {
             getErrPrintWriter().println("Error: scaling must be 'auto' or 'off'");
             return -1;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
new file mode 100644
index 0000000..4c9788d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -0,0 +1,931 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService
+        .INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
+import static com.android.server.wm.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.app.IApplicationThread;
+import android.app.ProfilerInfo;
+import android.app.servertransaction.ConfigurationChangeItem;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.Watchdog;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * The Activity Manager (AM) package manages the lifecycle of processes in the system through
+ * ProcessRecord. However, it is important for the Window Manager (WM) package to be aware
+ * of the processes and their state since it affects how WM manages windows and activities. This
+ * class that allows the ProcessRecord object in the AM package to communicate important
+ * changes to its state to the WM package in a structured way. WM package also uses
+ * {@link WindowProcessListener} to request changes to the process state on the AM side.
+ * Note that public calls into this class are assumed to be originating from outside the
+ * window manager so the window manager lock is held and appropriate permissions are checked before
+ * calls are allowed to proceed.
+ */
+public class WindowProcessController extends ConfigurationContainer<ConfigurationContainer>
+        implements ConfigurationContainerListener {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowProcessController" : TAG_ATM;
+    private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
+    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
+
+    // all about the first app in the process
+    final ApplicationInfo mInfo;
+    final String mName;
+    final int mUid;
+    // The process of this application; 0 if none
+    private volatile int mPid;
+    // user of process.
+    final int mUserId;
+    // The owner of this window process controller object. Mainly for identification when we
+    // communicate back to the activity manager side.
+    public final Object mOwner;
+    // List of packages running in the process
+    final ArraySet<String> mPkgList = new ArraySet<>();
+    private final WindowProcessListener mListener;
+    private final ActivityTaskManagerService mAtm;
+    // The actual proc...  may be null only if 'persistent' is true (in which case we are in the
+    // process of launching the app)
+    private volatile IApplicationThread mThread;
+    // Currently desired scheduling class
+    private volatile int mCurSchedGroup;
+    // Currently computed process state
+    private volatile int mCurProcState = PROCESS_STATE_NONEXISTENT;
+    // Last reported process state;
+    private volatile int mRepProcState = PROCESS_STATE_NONEXISTENT;
+    // are we in the process of crashing?
+    private volatile boolean mCrashing;
+    // does the app have a not responding dialog?
+    private volatile boolean mNotResponding;
+    // always keep this application running?
+    private volatile boolean mPersistent;
+    // The ABI this process was launched with
+    private volatile String mRequiredAbi;
+    // Running any services that are foreground?
+    private volatile boolean mHasForegroundServices;
+    // Running any activities that are foreground?
+    private volatile boolean mHasForegroundActivities;
+    // Are there any client services with activities?
+    private volatile boolean mHasClientActivities;
+    // Is this process currently showing a non-activity UI that the user is interacting with?
+    // E.g. The status bar when it is expanded, but not when it is minimized. When true the process
+    // will be set to use the ProcessList#SCHED_GROUP_TOP_APP scheduling group to boost performance.
+    private volatile boolean mHasTopUi;
+    // Is the process currently showing a non-activity UI that overlays on-top of activity UIs on
+    // screen. E.g. display a window of type
+    // android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY When true the process will
+    // oom adj score will be set to ProcessList#PERCEPTIBLE_APP_ADJ at minimum to reduce the chance
+    // of the process getting killed.
+    private volatile boolean mHasOverlayUi;
+    // Want to clean up resources from showing UI?
+    private volatile boolean mPendingUiClean;
+    // The time we sent the last interaction event
+    private volatile long mInteractionEventTime;
+    // When we became foreground for interaction purposes
+    private volatile long mFgInteractionTime;
+    // When (uptime) the process last became unimportant
+    private volatile long mWhenUnimportant;
+    // was app launched for debugging?
+    private volatile boolean mDebugging;
+    // Active instrumentation running in process?
+    private volatile boolean mInstrumenting;
+    // This process it perceptible by the user.
+    private volatile boolean mPerceptible;
+    // Set to true when process was launched with a wrapper attached
+    private volatile boolean mUsingWrapper;
+
+    // Thread currently set for VR scheduling
+    int mVrThreadTid;
+
+    // all activities running in the process
+    private final ArrayList<ActivityRecord> mActivities = new ArrayList<>();
+    // any tasks this process had run root activities in
+    private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<>();
+
+    // Last configuration that was reported to the process.
+    private final Configuration mLastReportedConfiguration;
+    // Registered display id as a listener to override config change
+    private int mDisplayId;
+
+    public WindowProcessController(ActivityTaskManagerService atm, ApplicationInfo info,
+            String name, int uid, int userId, Object owner, WindowProcessListener listener) {
+        mInfo = info;
+        mName = name;
+        mUid = uid;
+        mUserId = userId;
+        mOwner = owner;
+        mListener = listener;
+        mAtm = atm;
+        mLastReportedConfiguration = new Configuration();
+        mDisplayId = INVALID_DISPLAY;
+        if (atm != null) {
+            onConfigurationChanged(atm.getGlobalConfiguration());
+        }
+    }
+
+    public void setPid(int pid) {
+        mPid = pid;
+    }
+
+    public int getPid() {
+        return mPid;
+    }
+
+    public void setThread(IApplicationThread thread) {
+        mThread = thread;
+    }
+
+    IApplicationThread getThread() {
+        return mThread;
+    }
+
+    boolean hasThread() {
+        return mThread != null;
+    }
+
+    public void setCurrentSchedulingGroup(int curSchedGroup) {
+        mCurSchedGroup = curSchedGroup;
+    }
+
+    int getCurrentSchedulingGroup() {
+        return mCurSchedGroup;
+    }
+
+    public void setCurrentProcState(int curProcState) {
+        mCurProcState = curProcState;
+    }
+
+    int getCurrentProcState() {
+        return mCurProcState;
+    }
+
+    public void setReportedProcState(int repProcState) {
+        mRepProcState = repProcState;
+    }
+
+    int getReportedProcState() {
+        return mRepProcState;
+    }
+
+    public void setCrashing(boolean crashing) {
+        mCrashing = crashing;
+    }
+
+    boolean isCrashing() {
+        return mCrashing;
+    }
+
+    public void setNotResponding(boolean notResponding) {
+        mNotResponding = notResponding;
+    }
+
+    boolean isNotResponding() {
+        return mNotResponding;
+    }
+
+    public void setPersistent(boolean persistent) {
+        mPersistent = persistent;
+    }
+
+    boolean isPersistent() {
+        return mPersistent;
+    }
+
+    public void setHasForegroundServices(boolean hasForegroundServices) {
+        mHasForegroundServices = hasForegroundServices;
+    }
+
+    boolean hasForegroundServices() {
+        return mHasForegroundServices;
+    }
+
+    public void setHasForegroundActivities(boolean hasForegroundActivities) {
+        mHasForegroundActivities = hasForegroundActivities;
+    }
+
+    boolean hasForegroundActivities() {
+        return mHasForegroundActivities;
+    }
+
+    public void setHasClientActivities(boolean hasClientActivities) {
+        mHasClientActivities = hasClientActivities;
+    }
+
+    boolean hasClientActivities() {
+        return mHasClientActivities;
+    }
+
+    public void setHasTopUi(boolean hasTopUi) {
+        mHasTopUi = hasTopUi;
+    }
+
+    boolean hasTopUi() {
+        return mHasTopUi;
+    }
+
+    public void setHasOverlayUi(boolean hasOverlayUi) {
+        mHasOverlayUi = hasOverlayUi;
+    }
+
+    boolean hasOverlayUi() {
+        return mHasOverlayUi;
+    }
+
+    public void setPendingUiClean(boolean hasPendingUiClean) {
+        mPendingUiClean = hasPendingUiClean;
+    }
+
+    boolean hasPendingUiClean() {
+        return mPendingUiClean;
+    }
+
+    void postPendingUiCleanMsg(boolean pendingUiClean) {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Message m = PooledLambda.obtainMessage(
+                WindowProcessListener::setPendingUiClean, mListener, pendingUiClean);
+        mAtm.mH.sendMessage(m);
+    }
+
+    public void setInteractionEventTime(long interactionEventTime) {
+        mInteractionEventTime = interactionEventTime;
+    }
+
+    long getInteractionEventTime() {
+        return mInteractionEventTime;
+    }
+
+    public void setFgInteractionTime(long fgInteractionTime) {
+        mFgInteractionTime = fgInteractionTime;
+    }
+
+    long getFgInteractionTime() {
+        return mFgInteractionTime;
+    }
+
+    public void setWhenUnimportant(long whenUnimportant) {
+        mWhenUnimportant = whenUnimportant;
+    }
+
+    long getWhenUnimportant() {
+        return mWhenUnimportant;
+    }
+
+    public void setRequiredAbi(String requiredAbi) {
+        mRequiredAbi = requiredAbi;
+    }
+
+    String getRequiredAbi() {
+        return mRequiredAbi;
+    }
+
+    public void setDebugging(boolean debugging) {
+        mDebugging = debugging;
+    }
+
+    boolean isDebugging() {
+        return mDebugging;
+    }
+
+    public void setUsingWrapper(boolean usingWrapper) {
+        mUsingWrapper = usingWrapper;
+    }
+
+    boolean isUsingWrapper() {
+        return mUsingWrapper;
+    }
+
+    public void setInstrumenting(boolean instrumenting) {
+        mInstrumenting = instrumenting;
+    }
+
+    boolean isInstrumenting() {
+        return mInstrumenting;
+    }
+
+    public void setPerceptible(boolean perceptible) {
+        mPerceptible = perceptible;
+    }
+
+    boolean isPerceptible() {
+        return mPerceptible;
+    }
+
+    @Override
+    protected int getChildCount() {
+        return 0;
+    }
+
+    @Override
+    protected ConfigurationContainer getChildAt(int index) {
+        return null;
+    }
+
+    @Override
+    protected ConfigurationContainer getParent() {
+        return null;
+    }
+
+    public void addPackage(String packageName) {
+        synchronized (mAtm.mGlobalLock) {
+            mPkgList.add(packageName);
+        }
+    }
+
+    public void clearPackageList() {
+        synchronized (mAtm.mGlobalLock) {
+            mPkgList.clear();
+        }
+    }
+
+    void addActivityIfNeeded(ActivityRecord r) {
+        if (mActivities.contains(r)) {
+            return;
+        }
+        mActivities.add(r);
+    }
+
+    void removeActivity(ActivityRecord r) {
+        mActivities.remove(r);
+    }
+
+    public void clearActivities() {
+        synchronized (mAtm.mGlobalLock) {
+            mActivities.clear();
+        }
+    }
+
+    public boolean hasActivities() {
+        synchronized (mAtm.mGlobalLock) {
+            return !mActivities.isEmpty();
+        }
+    }
+
+    public boolean hasVisibleActivities() {
+        synchronized (mAtm.mGlobalLock) {
+            for (int i = mActivities.size() - 1; i >= 0; --i) {
+                final ActivityRecord r = mActivities.get(i);
+                if (r.visible) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public boolean hasActivitiesOrRecentTasks() {
+        synchronized (mAtm.mGlobalLock) {
+            return !mActivities.isEmpty() || !mRecentTasks.isEmpty();
+        }
+    }
+
+    public void stopFreezingActivities() {
+        synchronized (mAtm.mGlobalLock) {
+            int i = mActivities.size();
+            while (i > 0) {
+                i--;
+                mActivities.get(i).stopFreezingScreenLocked(true);
+            }
+        }
+    }
+
+    public void finishActivities() {
+        synchronized (mAtm.mGlobalLock) {
+            ArrayList<ActivityRecord> activities = new ArrayList<>(mActivities);
+            for (int i = 0; i < activities.size(); i++) {
+                final ActivityRecord r = activities.get(i);
+                if (!r.finishing && r.isInStackLocked()) {
+                    r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
+                            null, "finish-heavy", true);
+                }
+            }
+        }
+    }
+
+    public boolean isInterestingToUser() {
+        synchronized (mAtm.mGlobalLock) {
+            final int size = mActivities.size();
+            for (int i = 0; i < size; i++) {
+                ActivityRecord r = mActivities.get(i);
+                if (r.isInterestingToUserLocked()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public boolean hasRunningActivity(String packageName) {
+        synchronized (mAtm.mGlobalLock) {
+            for (int i = mActivities.size() - 1; i >= 0; --i) {
+                final ActivityRecord r = mActivities.get(i);
+                if (packageName.equals(r.packageName)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public void clearPackagePreferredForHomeActivities() {
+        synchronized (mAtm.mGlobalLock) {
+            for (int i = mActivities.size() - 1; i >= 0; --i) {
+                final ActivityRecord r = mActivities.get(i);
+                if (r.isActivityTypeHome()) {
+                    Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
+                    try {
+                        ActivityThread.getPackageManager()
+                                .clearPackagePreferredActivities(r.packageName);
+                    } catch (RemoteException c) {
+                        // pm is in same process, this will never happen.
+                    }
+                }
+            }
+        }
+    }
+
+    boolean hasStartedActivity(ActivityRecord launchedActivity) {
+        for (int i = mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord activity = mActivities.get(i);
+            if (launchedActivity == activity) {
+                continue;
+            }
+            if (!activity.stopped) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    public void updateIntentForHeavyWeightActivity(Intent intent) {
+        synchronized (mAtm.mGlobalLock) {
+            if (mActivities.isEmpty()) {
+                return;
+            }
+            ActivityRecord hist = mActivities.get(0);
+            intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName);
+            intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTask().taskId);
+        }
+    }
+
+    boolean shouldKillProcessForRemovedTask(TaskRecord tr) {
+        for (int k = 0; k < mActivities.size(); k++) {
+            final ActivityRecord activity = mActivities.get(k);
+            if (!activity.stopped) {
+                // Don't kill process(es) that has an activity not stopped.
+                return false;
+            }
+            final TaskRecord otherTask = activity.getTask();
+            if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
+                // Don't kill process(es) that has an activity in a different task that is
+                // also in recents.
+                return false;
+            }
+        }
+        return true;
+    }
+
+    ArraySet<TaskRecord> getReleaseSomeActivitiesTasks() {
+        // Examine all activities currently running in the process.
+        TaskRecord firstTask = null;
+        // Tasks is non-null only if two or more tasks are found.
+        ArraySet<TaskRecord> tasks = null;
+        if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + this);
+        for (int i = 0; i < mActivities.size(); i++) {
+            final ActivityRecord r = mActivities.get(i);
+            // First, if we find an activity that is in the process of being destroyed,
+            // then we just aren't going to do anything for now; we want things to settle
+            // down before we try to prune more activities.
+            if (r.finishing || r.isState(DESTROYING, DESTROYED)) {
+                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
+                return null;
+            }
+            // Don't consider any activies that are currently not in a state where they
+            // can be destroyed.
+            if (r.visible || !r.stopped || !r.haveState
+                    || r.isState(RESUMED, PAUSING, PAUSED, STOPPING)) {
+                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
+                continue;
+            }
+
+            final TaskRecord task = r.getTask();
+            if (task != null) {
+                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
+                        + " from " + r);
+                if (firstTask == null) {
+                    firstTask = task;
+                } else if (firstTask != task) {
+                    if (tasks == null) {
+                        tasks = new ArraySet<>();
+                        tasks.add(firstTask);
+                    }
+                    tasks.add(task);
+                }
+            }
+        }
+
+        return tasks;
+    }
+
+    public interface ComputeOomAdjCallback {
+        void onVisibleActivity();
+        void onPausedActivity();
+        void onStoppingActivity(boolean finishing);
+        void onOtherActivity();
+    }
+
+    public int computeOomAdjFromActivities(int minTaskLayer, ComputeOomAdjCallback callback) {
+        synchronized (mAtm.mGlobalLock) {
+            final int activitiesSize = mActivities.size();
+            for (int j = 0; j < activitiesSize; j++) {
+                final ActivityRecord r = mActivities.get(j);
+                if (r.app != this) {
+                    Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
+                            + " instead of expected " + this);
+                    if (r.app == null || (r.app.mUid == mUid)) {
+                        // Only fix things up when they look sane
+                        r.setProcess(this);
+                    } else {
+                        continue;
+                    }
+                }
+                if (r.visible) {
+                    callback.onVisibleActivity();
+                    final TaskRecord task = r.getTask();
+                    if (task != null && minTaskLayer > 0) {
+                        final int layer = task.mLayerRank;
+                        if (layer >= 0 && minTaskLayer > layer) {
+                            minTaskLayer = layer;
+                        }
+                    }
+                    break;
+                } else if (r.isState(PAUSING, PAUSED)) {
+                    callback.onPausedActivity();
+                } else if (r.isState(STOPPING)) {
+                    callback.onStoppingActivity(r.finishing);
+                } else {
+                    callback.onOtherActivity();
+                }
+            }
+        }
+
+        return minTaskLayer;
+    }
+
+    public int computeRelaunchReason() {
+        synchronized (mAtm.mGlobalLock) {
+            final int activitiesSize = mActivities.size();
+            for (int i = activitiesSize - 1; i >= 0; i--) {
+                final ActivityRecord r = mActivities.get(i);
+                if (r.mRelaunchReason != RELAUNCH_REASON_NONE) {
+                    return r.mRelaunchReason;
+                }
+            }
+        }
+        return RELAUNCH_REASON_NONE;
+    }
+
+    public long getInputDispatchingTimeout() {
+        synchronized (mAtm.mGlobalLock) {
+            return isInstrumenting() || isUsingWrapper()
+                    ? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS : KEY_DISPATCHING_TIMEOUT_MS;
+        }
+    }
+
+    void clearProfilerIfNeeded() {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        mAtm.mH.sendMessage(PooledLambda.obtainMessage(
+                WindowProcessListener::clearProfilerIfNeeded, mListener));
+    }
+
+    void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
+            boolean activityChange, boolean updateOomAdj) {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Message m = PooledLambda.obtainMessage(WindowProcessListener::updateProcessInfo,
+                mListener, updateServiceConnectionActivities, updateLru, activityChange,
+                updateOomAdj);
+        mAtm.mH.sendMessage(m);
+    }
+
+    void updateServiceConnectionActivities() {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        mAtm.mH.sendMessage(PooledLambda.obtainMessage(
+                WindowProcessListener::updateServiceConnectionActivities, mListener));
+    }
+
+    void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Message m = PooledLambda.obtainMessage(
+                WindowProcessListener::setPendingUiCleanAndForceProcessStateUpTo,
+                mListener, newState);
+        mAtm.mH.sendMessage(m);
+    }
+
+    void setRemoved(boolean removed) {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Message m = PooledLambda.obtainMessage(
+                WindowProcessListener::setRemoved, mListener, removed);
+        mAtm.mH.sendMessage(m);
+    }
+
+    void clearWaitingToKill() {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Message m = PooledLambda.obtainMessage(
+                WindowProcessListener::clearWaitingToKill, mListener);
+        mAtm.mH.sendMessage(m);
+    }
+
+    void addPackage(String pkg, long versionCode) {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Message m = PooledLambda.obtainMessage(
+                WindowProcessListener::addPackage, mListener, pkg, versionCode);
+        mAtm.mH.sendMessage(m);
+    }
+
+    ProfilerInfo onStartActivity(int topProcessState) {
+        ProfilerInfo profilerInfo = null;
+        boolean setProfileProc = false;
+        if (mAtm.mProfileApp != null
+                && mAtm.mProfileApp.equals(mName)) {
+            if (mAtm.mProfileProc == null || mAtm.mProfileProc == this) {
+                setProfileProc = true;
+                final ProfilerInfo profilerInfoSvc = mAtm.mProfilerInfo;
+                if (profilerInfoSvc != null && profilerInfoSvc.profileFile != null) {
+                    if (profilerInfoSvc.profileFd != null) {
+                        try {
+                            profilerInfoSvc.profileFd = profilerInfoSvc.profileFd.dup();
+                        } catch (IOException e) {
+                            profilerInfoSvc.closeFd();
+                        }
+                    }
+
+                    profilerInfo = new ProfilerInfo(profilerInfoSvc);
+                }
+            }
+        }
+
+
+        if (mListener != null) {
+            // Posting on handler so WM lock isn't held when we call into AM.
+            final Message m = PooledLambda.obtainMessage(WindowProcessListener::onStartActivity,
+                    mListener, topProcessState, setProfileProc);
+            mAtm.mH.sendMessage(m);
+        }
+
+        return profilerInfo;
+    }
+
+    public void appDied() {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Message m = PooledLambda.obtainMessage(
+                WindowProcessListener::appDied, mListener);
+        mAtm.mH.sendMessage(m);
+    }
+
+    void registerDisplayConfigurationListenerLocked(ActivityDisplay activityDisplay) {
+        if (activityDisplay == null) {
+            return;
+        }
+        // A process can only register to one display to listener to the override configuration
+        // change. Unregister existing listener if it has one before register the new one.
+        unregisterDisplayConfigurationListenerLocked();
+        mDisplayId = activityDisplay.mDisplayId;
+        activityDisplay.registerConfigurationChangeListener(this);
+    }
+
+    private void unregisterDisplayConfigurationListenerLocked() {
+        if (mDisplayId == INVALID_DISPLAY) {
+            return;
+        }
+        final ActivityDisplay activityDisplay =
+                mAtm.mStackSupervisor.getActivityDisplay(mDisplayId);
+        if (activityDisplay != null) {
+            mAtm.mStackSupervisor.getActivityDisplay(
+                    mDisplayId).unregisterConfigurationChangeListener(this);
+        }
+        mDisplayId = INVALID_DISPLAY;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newGlobalConfig) {
+        super.onConfigurationChanged(newGlobalConfig);
+        updateConfiguration();
+    }
+
+    @Override
+    public void onOverrideConfigurationChanged(Configuration newOverrideConfig) {
+        super.onOverrideConfigurationChanged(newOverrideConfig);
+        updateConfiguration();
+    }
+
+    private void updateConfiguration() {
+        final Configuration config = getConfiguration();
+        if (mLastReportedConfiguration.diff(config) == 0) {
+            // Nothing changed.
+            return;
+        }
+
+        try {
+            if (mThread == null) {
+                return;
+            }
+            if (DEBUG_CONFIGURATION) {
+                Slog.v(TAG_CONFIGURATION, "Sending to proc " + mName
+                        + " new config " + config);
+            }
+            config.seq = mAtm.increaseConfigurationSeqLocked();
+            mAtm.getLifecycleManager().scheduleTransaction(mThread,
+                    ConfigurationChangeItem.obtain(config));
+            setLastReportedConfiguration(config);
+        } catch (Exception e) {
+            Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
+        }
+    }
+
+    private void setLastReportedConfiguration(Configuration config) {
+        mLastReportedConfiguration.setTo(config);
+    }
+
+    Configuration getLastReportedConfiguration() {
+        return mLastReportedConfiguration;
+    }
+
+    /** Returns the total time (in milliseconds) spent executing in both user and system code. */
+    public long getCpuTime() {
+        return (mListener != null) ? mListener.getCpuTime() : 0;
+    }
+
+    void addRecentTask(TaskRecord task) {
+        mRecentTasks.add(task);
+    }
+
+    void removeRecentTask(TaskRecord task) {
+        mRecentTasks.remove(task);
+    }
+
+    public boolean hasRecentTasks() {
+        synchronized (mAtm.mGlobalLock) {
+            return !mRecentTasks.isEmpty();
+        }
+    }
+
+    public void clearRecentTasks() {
+        synchronized (mAtm.mGlobalLock) {
+            for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
+                mRecentTasks.get(i).clearRootProcess();
+            }
+            mRecentTasks.clear();
+        }
+    }
+
+    public void appEarlyNotResponding(String annotation, Runnable killAppCallback) {
+        synchronized (mAtm.mGlobalLock) {
+            if (mAtm.mController == null) {
+                return;
+            }
+
+            try {
+                // 0 == continue, -1 = kill process immediately
+                int res = mAtm.mController.appEarlyNotResponding(mName, mPid, annotation);
+                if (res < 0 && mPid != MY_PID) {
+                    killAppCallback.run();
+                }
+            } catch (RemoteException e) {
+                mAtm.mController = null;
+                Watchdog.getInstance().setActivityController(null);
+            }
+        }
+    }
+
+    public boolean appNotResponding(String info, Runnable killAppCallback,
+            Runnable serviceTimeoutCallback) {
+        synchronized (mAtm.mGlobalLock) {
+            if (mAtm.mController == null) {
+                return false;
+            }
+
+            try {
+                // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
+                int res = mAtm.mController.appNotResponding(mName, mPid, info);
+                if (res != 0) {
+                    if (res < 0 && mPid != MY_PID) {
+                        killAppCallback.run();
+                    } else {
+                        serviceTimeoutCallback.run();
+                    }
+                    return true;
+                }
+            } catch (RemoteException e) {
+                mAtm.mController = null;
+                Watchdog.getInstance().setActivityController(null);
+            }
+            return false;
+        }
+    }
+
+    public void onTopProcChanged() {
+        synchronized (mAtm.mGlobalLock) {
+            mAtm.mVrController.onTopProcChangedLocked(this);
+        }
+    }
+
+    public boolean isHomeProcess() {
+        synchronized (mAtm.mGlobalLock) {
+            return this == mAtm.mHomeProcess;
+        }
+    }
+
+    public boolean isPreviousProcess() {
+        synchronized (mAtm.mGlobalLock) {
+            return this == mAtm.mPreviousProcess;
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        synchronized (mAtm.mGlobalLock) {
+            if (mActivities.size() > 0) {
+                pw.print(prefix); pw.println("Activities:");
+                for (int i = 0; i < mActivities.size(); i++) {
+                    pw.print(prefix); pw.print("  - "); pw.println(mActivities.get(i));
+                }
+            }
+
+            if (mRecentTasks.size() > 0) {
+                pw.println(prefix + "Recent Tasks:");
+                for (int i = 0; i < mRecentTasks.size(); i++) {
+                    pw.println(prefix + "  - " + mRecentTasks.get(i));
+                }
+            }
+
+            if (mVrThreadTid != 0) {
+                pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
+            }
+        }
+        pw.println(prefix + " Configuration=" + getConfiguration());
+        pw.println(prefix + " OverrideConfiguration=" + getOverrideConfiguration());
+        pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration);
+    }
+
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        if (mListener != null) {
+            mListener.writeToProto(proto, fieldId);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowProcessListener.java b/services/core/java/com/android/server/wm/WindowProcessListener.java
new file mode 100644
index 0000000..bce5e2d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowProcessListener.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import android.util.proto.ProtoOutputStream;
+
+/**
+ * Interface used by the owner/creator of a process that owns windows to listen to changes from the
+ * WM side.
+ * @see WindowProcessController
+ */
+public interface WindowProcessListener {
+
+    /** Clear the profiler record if we are currently profiling this process. */
+    void clearProfilerIfNeeded();
+
+    /** Update the service connection for this process based on activities it might have. */
+    void updateServiceConnectionActivities();
+
+    /** Set or clear flag that we would like to clean-up UI resources for this process. */
+    void setPendingUiClean(boolean pendingUiClean);
+
+    /**
+     * Set flag that we would like to clean-up UI resources for this process and force new process
+     * state.
+     */
+    void setPendingUiCleanAndForceProcessStateUpTo(int newState);
+
+    /** Update the process information. */
+    void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
+            boolean activityChange, boolean updateOomAdj);
+
+    /** Set process package been removed from device. */
+    void setRemoved(boolean removed);
+
+    /** Returns the total time (in milliseconds) spent executing in both user and system code. */
+    long getCpuTime();
+
+    /** Clears the waiting to kill reason for this process. */
+    void clearWaitingToKill();
+
+    /** Adds the package to the process. */
+    void addPackage(String pkg, long versionCode);
+
+    /** Called when we are in the process on starting an activity. */
+    void onStartActivity(int topProcessState, boolean setProfileProc);
+
+    /** App died :(...oh well */
+    void appDied();
+    void writeToProto(ProtoOutputStream proto, long fieldId);
+}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f16008d..99f65c3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -103,7 +103,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
@@ -1364,7 +1363,7 @@
     @Override
     boolean hasContentToDisplay() {
         if (!mAppFreezing && isDrawnLw() && (mViewVisibility == View.VISIBLE
-                || (isAnimating() && !mService.mAppTransition.isTransitionSet()))) {
+                || (isAnimating() && !getDisplayContent().mAppTransition.isTransitionSet()))) {
             return true;
         }
 
@@ -1473,7 +1472,7 @@
      * of a transition that has not yet been started.
      */
     boolean isReadyForDisplay() {
-        if (mToken.waitingToShow && mService.mAppTransition.isTransitionSet()) {
+        if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet()) {
             return false;
         }
         final boolean parentAndClientVisible = !isParentWindowHidden()
@@ -1942,8 +1941,11 @@
             removeImmediately();
             // Removing a visible window will effect the computed orientation
             // So just update orientation if needed.
-            if (wasVisible && mService.updateOrientationFromAppTokensLocked(displayId)) {
-                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+            if (wasVisible) {
+                final DisplayContent displayContent = getDisplayContent();
+                if (displayContent.updateOrientationFromAppTokens()) {
+                    displayContent.sendNewConfiguration();
+                }
             }
             mService.updateFocusedWindowLocked(isFocused()
                             ? UPDATE_FOCUS_REMOVING_FOCUS
@@ -2314,7 +2316,7 @@
         public void binderDied() {
             try {
                 boolean resetSplitScreenResizing = false;
-                synchronized(mService.mWindowMap) {
+                synchronized (mService.mGlobalLock) {
                     final WindowState win = mService.windowForClientLocked(mSession, mClient, false);
                     Slog.i(TAG, "WIN DEATH: " + win);
                     if (win != null) {
@@ -2949,7 +2951,12 @@
         // isDragResizing() or isDragResizeChanged() is true.
         boolean resizing = isDragResizing() || isDragResizeChanged();
         if (getWindowConfiguration().useWindowFrameForBackdrop() || !resizing) {
-            return frame;
+            // Surface position is now inherited from parent, and BackdropFrameRenderer uses
+            // backdrop frame to position content. Thus we just keep the size of backdrop frame, and
+            // remove the offset to avoid double offset from display origin.
+            mTmpRect.set(frame);
+            mTmpRect.offsetTo(0, 0);
+            return mTmpRect;
         }
         final DisplayInfo displayInfo = getDisplayInfo();
         mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
@@ -2979,7 +2986,7 @@
     }
 
     public void registerFocusObserver(IWindowFocusObserver observer) {
-        synchronized(mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             if (mFocusCallbacks == null) {
                 mFocusCallbacks = new RemoteCallbackList<IWindowFocusObserver>();
             }
@@ -2988,7 +2995,7 @@
     }
 
     public void unregisterFocusObserver(IWindowFocusObserver observer) {
-        synchronized(mService.mWindowMap) {
+        synchronized (mService.mGlobalLock) {
             if (mFocusCallbacks != null) {
                 mFocusCallbacks.unregister(observer);
             }
@@ -4435,7 +4442,7 @@
         public boolean isFocused() {
             final WindowState outer = mOuter.get();
             if (outer != null) {
-                synchronized (outer.mService.mWindowMap) {
+                synchronized (outer.mService.mGlobalLock) {
                     return outer.isFocused();
                 }
             }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 2beb788..838d2a1 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1364,7 +1364,8 @@
                         break;
                 }
                 if (attr >= 0) {
-                    a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr, TRANSIT_NONE);
+                    a = mWin.getDisplayContent().mAppTransition.loadAnimationAttr(
+                            mWin.mAttrs, attr, TRANSIT_NONE);
                 }
             }
             if (DEBUG_ANIM) Slog.v(TAG,
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index e13a70a..7d25b8c 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -16,62 +16,18 @@
 
 package com.android.server.wm;
 
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
-import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT;
-import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
-import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
-import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
 import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE;
 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 
-import android.app.WindowConfiguration;
 import android.os.Debug;
 import android.os.Trace;
-import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseIntArray;
-import android.view.Display;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationDefinition;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.view.WindowManager.TransitionType;
-import android.view.animation.Animation;
-
-import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
-import java.util.function.Predicate;
 
 /**
  * Positions windows and their surfaces.
@@ -105,7 +61,7 @@
         mService = service;
         mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
         mPerformSurfacePlacement = () -> {
-            synchronized (mService.mWindowMap) {
+            synchronized (mService.mGlobalLock) {
                 performSurfacePlacement();
             }
         };
@@ -233,550 +189,6 @@
         return mInLayout;
     }
 
-    /**
-     * @return bitmap indicating if another pass through layout must be made.
-     */
-    int handleAppTransitionReadyLocked() {
-        int appsCount = mService.mOpeningApps.size();
-        if (!transitionGoodToGo(appsCount, mTempTransitionReasons)) {
-            return 0;
-        }
-        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
-
-        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
-        int transit = mService.mAppTransition.getAppTransition();
-        if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
-            transit = WindowManager.TRANSIT_UNSET;
-        }
-        mService.mSkipAppTransitionAnimation = false;
-        mService.mNoAnimationNotifyOnTransitionFinished.clear();
-
-        mService.mAppTransition.removeAppTransitionTimeoutCallbacks();
-
-        final DisplayContent displayContent = mService.getDefaultDisplayContentLocked();
-
-        mService.mRoot.mWallpaperMayChange = false;
-
-        int i;
-        for (i = 0; i < appsCount; i++) {
-            final AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
-            // Clearing the mAnimatingExit flag before entering animation. It's set to true if app
-            // window is removed, or window relayout to invisible. This also affects window
-            // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the
-            // transition selection depends on wallpaper target visibility.
-            wtoken.clearAnimatingFlags();
-        }
-
-        // Adjust wallpaper before we pull the lower/upper target, since pending changes
-        // (like the clearAnimatingFlags() above) might affect wallpaper target result.
-        // Or, the opening app window should be a wallpaper target.
-        mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(displayContent,
-                mService.mOpeningApps);
-
-        // Determine if closing and opening app token sets are wallpaper targets, in which case
-        // special animations are needed.
-        final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null;
-        final boolean openingAppHasWallpaper = canBeWallpaperTarget(mService.mOpeningApps)
-                && hasWallpaperTarget;
-        final boolean closingAppHasWallpaper = canBeWallpaperTarget(mService.mClosingApps)
-                && hasWallpaperTarget;
-
-        transit = maybeUpdateTransitToTranslucentAnim(transit);
-        transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
-                closingAppHasWallpaper);
-
-        // Find the layout params of the top-most application window in the tokens, which is
-        // what will control the animation theme. If all closing windows are obscured, then there is
-        // no need to do an animation. This is the case, for example, when this transition is being
-        // done behind a dream window.
-        final ArraySet<Integer> activityTypes = collectActivityTypes(mService.mOpeningApps,
-                mService.mClosingApps);
-        final boolean allowAnimations = mService.mPolicy.allowAppAnimationsLw();
-        final AppWindowToken animLpToken = allowAnimations
-                ? findAnimLayoutParamsToken(transit, activityTypes)
-                : null;
-        final AppWindowToken topOpeningApp = allowAnimations
-                ? getTopApp(mService.mOpeningApps, false /* ignoreHidden */)
-                : null;
-        final AppWindowToken topClosingApp = allowAnimations
-                ? getTopApp(mService.mClosingApps, false /* ignoreHidden */)
-                : null;
-        final LayoutParams animLp = getAnimLp(animLpToken);
-        overrideWithRemoteAnimationIfSet(animLpToken, transit, activityTypes);
-
-        final boolean voiceInteraction = containsVoiceInteraction(mService.mOpeningApps)
-                || containsVoiceInteraction(mService.mOpeningApps);
-
-        final int layoutRedo;
-        mService.mSurfaceAnimationRunner.deferStartingAnimations();
-        try {
-            processApplicationsAnimatingInPlace(transit);
-
-            handleClosingApps(transit, animLp, voiceInteraction);
-            handleOpeningApps(transit, animLp, voiceInteraction);
-
-            mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp);
-
-            final int flags = mService.mAppTransition.getTransitFlags();
-            layoutRedo = mService.mAppTransition.goodToGo(transit, topOpeningApp, topClosingApp,
-                    mService.mOpeningApps, mService.mClosingApps);
-            handleNonAppWindowsInTransition(transit, flags);
-            mService.mAppTransition.postAnimationCallback();
-            mService.mAppTransition.clear();
-        } finally {
-            mService.mSurfaceAnimationRunner.continueStartingAnimations();
-        }
-
-        mService.mTaskSnapshotController.onTransitionStarting();
-
-        mService.mOpeningApps.clear();
-        mService.mClosingApps.clear();
-        mService.mUnknownAppVisibilityController.clear();
-
-        // This has changed the visibility of windows, so perform
-        // a new layout to get them all up-to-date.
-        displayContent.setLayoutNeeded();
-
-        // TODO(multidisplay): IMEs are only supported on the default display.
-        final DisplayContent dc = mService.getDefaultDisplayContentLocked();
-        dc.computeImeTarget(true /* updateImeTarget */);
-        mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
-                true /*updateInputWindows*/);
-        mService.mFocusMayChange = false;
-
-        mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING,
-                mTempTransitionReasons.clone()).sendToTarget();
-
-        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
-
-        return layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
-    }
-
-    private static LayoutParams getAnimLp(AppWindowToken wtoken) {
-        final WindowState mainWindow = wtoken != null ? wtoken.findMainWindow() : null;
-        return mainWindow != null ? mainWindow.mAttrs : null;
-    }
-
-    /**
-     * Overrides the pending transition with the remote animation defined for the transition in the
-     * set of defined remote animations in the app window token.
-     */
-    private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit,
-            ArraySet<Integer> activityTypes) {
-        if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) {
-            // The crash transition has higher priority than any involved remote animations.
-            return;
-        }
-        if (animLpToken == null) {
-            return;
-        }
-        final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition();
-        if (definition != null) {
-            final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
-            if (adapter != null) {
-                mService.mAppTransition.overridePendingAppTransitionRemote(adapter);
-            }
-        }
-    }
-
-    /**
-     * @return The window token that determines the animation theme.
-     */
-    private AppWindowToken findAnimLayoutParamsToken(@TransitionType int transit,
-            ArraySet<Integer> activityTypes) {
-        AppWindowToken result;
-
-        // Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
-        result = lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps,
-                w -> w.getRemoteAnimationDefinition() != null
-                        && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
-        if (result != null) {
-            return result;
-        }
-        result = lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps,
-                w -> w.fillsParent() && w.findMainWindow() != null);
-        if (result != null) {
-            return result;
-        }
-        return lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps,
-                w -> w.findMainWindow() != null);
-    }
-
-    /**
-     * @return The set of {@link WindowConfiguration.ActivityType}s contained in the set of apps in
-     *         {@code array1} and {@code array2}.
-     */
-    private ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1,
-            ArraySet<AppWindowToken> array2) {
-        final ArraySet<Integer> result = new ArraySet<>();
-        for (int i = array1.size() - 1; i >= 0; i--) {
-            result.add(array1.valueAt(i).getActivityType());
-        }
-        for (int i = array2.size() - 1; i >= 0; i--) {
-            result.add(array2.valueAt(i).getActivityType());
-        }
-        return result;
-    }
-
-    private AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1,
-            ArraySet<AppWindowToken> array2, Predicate<AppWindowToken> filter) {
-        final int array1count = array1.size();
-        final int count = array1count + array2.size();
-        int bestPrefixOrderIndex = Integer.MIN_VALUE;
-        AppWindowToken bestToken = null;
-        for (int i = 0; i < count; i++) {
-            final AppWindowToken wtoken = i < array1count
-                    ? array1.valueAt(i)
-                    : array2.valueAt(i - array1count);
-            final int prefixOrderIndex = wtoken.getPrefixOrderIndex();
-            if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) {
-                bestPrefixOrderIndex = prefixOrderIndex;
-                bestToken = wtoken;
-            }
-        }
-        return bestToken;
-    }
-
-    private boolean containsVoiceInteraction(ArraySet<AppWindowToken> apps) {
-        for (int i = apps.size() - 1; i >= 0; i--) {
-            if (apps.valueAt(i).mVoiceInteraction) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
-        final int appsCount = mService.mOpeningApps.size();
-        for (int i = 0; i < appsCount; i++) {
-            AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
-            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
-
-            if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) {
-                // This token isn't going to be animating. Add it to the list of tokens to
-                // be notified of app transition complete since the notification will not be
-                // sent be the app window animator.
-                mService.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
-            }
-            wtoken.updateReportedVisibilityLocked();
-            wtoken.waitingToShow = false;
-            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
-                    ">>> OPEN TRANSACTION handleAppTransitionReadyLocked()");
-            mService.openSurfaceTransaction();
-            try {
-                wtoken.showAllWindowsLocked();
-            } finally {
-                mService.closeSurfaceTransaction("handleAppTransitionReadyLocked");
-                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
-                        "<<< CLOSE TRANSACTION handleAppTransitionReadyLocked()");
-            }
-
-            if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) {
-                wtoken.attachThumbnailAnimation();
-            } else if (mService.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) {
-                wtoken.attachCrossProfileAppsThumbnailAnimation();
-            }
-        }
-    }
-
-    private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
-        final int appsCount;
-        appsCount = mService.mClosingApps.size();
-        for (int i = 0; i < appsCount; i++) {
-            AppWindowToken wtoken = mService.mClosingApps.valueAt(i);
-
-            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
-            // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
-            //       animating?
-            wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
-            wtoken.updateReportedVisibilityLocked();
-            // Force the allDrawn flag, because we want to start
-            // this guy's animations regardless of whether it's
-            // gotten drawn.
-            wtoken.allDrawn = true;
-            wtoken.deferClearAllDrawn = false;
-            // Ensure that apps that are mid-starting are also scheduled to have their
-            // starting windows removed after the animation is complete
-            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit
-                    && wtoken.getController() != null) {
-                wtoken.getController().removeStartingWindow();
-            }
-
-            if (mService.mAppTransition.isNextAppTransitionThumbnailDown()) {
-                wtoken.attachThumbnailAnimation();
-            }
-        }
-    }
-
-    private void handleNonAppWindowsInTransition(int transit, int flags) {
-        if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
-            if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
-                    && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0) {
-                Animation anim = mService.mPolicy.createKeyguardWallpaperExit(
-                        (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
-                if (anim != null) {
-                    mService.getDefaultDisplayContentLocked().mWallpaperController
-                            .startWallpaperAnimation(anim);
-                }
-            }
-        }
-        if (transit == TRANSIT_KEYGUARD_GOING_AWAY
-                || transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
-            mService.getDefaultDisplayContentLocked().startKeyguardExitOnNonAppWindows(
-                    transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
-                    (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
-        }
-    }
-
-    private boolean transitionGoodToGo(int appsCount, SparseIntArray outReasons) {
-        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                "Checking " + appsCount + " opening apps (frozen="
-                        + mService.mDisplayFrozen + " timeout="
-                        + mService.mAppTransition.isTimeout() + ")...");
-        final ScreenRotationAnimation screenRotationAnimation =
-            mService.mAnimator.getScreenRotationAnimationLocked(
-                    Display.DEFAULT_DISPLAY);
-
-        outReasons.clear();
-        if (!mService.mAppTransition.isTimeout()) {
-            // Imagine the case where we are changing orientation due to an app transition, but a previous
-            // orientation change is still in progress. We won't process the orientation change
-            // for our transition because we need to wait for the rotation animation to finish.
-            // If we start the app transition at this point, we will interrupt it halfway with a new rotation
-            // animation after the old one finally finishes. It's better to defer the
-            // app transition.
-            if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() &&
-                    mService.getDefaultDisplayContentLocked().rotationNeedsUpdate()) {
-                if (DEBUG_APP_TRANSITIONS) {
-                    Slog.v(TAG, "Delaying app transition for screen rotation animation to finish");
-                }
-                return false;
-            }
-            for (int i = 0; i < appsCount; i++) {
-                AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
-                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                        "Check opening app=" + wtoken + ": allDrawn="
-                        + wtoken.allDrawn + " startingDisplayed="
-                        + wtoken.startingDisplayed + " startingMoved="
-                        + wtoken.startingMoved + " isRelaunching()="
-                        + wtoken.isRelaunching() + " startingWindow="
-                        + wtoken.startingWindow);
-
-
-                final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching();
-                if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
-                    return false;
-                }
-                final int windowingMode = wtoken.getWindowingMode();
-                if (allDrawn) {
-                    outReasons.put(windowingMode,  APP_TRANSITION_WINDOWS_DRAWN);
-                } else {
-                    outReasons.put(windowingMode,
-                            wtoken.startingData instanceof SplashScreenStartingData
-                                    ? APP_TRANSITION_SPLASH_SCREEN
-                                    : APP_TRANSITION_SNAPSHOT);
-                }
-            }
-
-            // We also need to wait for the specs to be fetched, if needed.
-            if (mService.mAppTransition.isFetchingAppTransitionsSpecs()) {
-                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true");
-                return false;
-            }
-
-            if (!mService.mUnknownAppVisibilityController.allResolved()) {
-                if (DEBUG_APP_TRANSITIONS) {
-                    Slog.v(TAG, "unknownApps is not empty: "
-                            + mService.mUnknownAppVisibilityController.getDebugMessage());
-                }
-                return false;
-            }
-
-            // If the wallpaper is visible, we need to check it's ready too.
-            boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
-                    mWallpaperControllerLocked.wallpaperTransitionReady();
-            if (wallpaperReady) {
-                return true;
-            }
-            return false;
-        }
-        return true;
-    }
-
-    private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper,
-            boolean closingAppHasWallpaper) {
-        // Given no app transition pass it through instead of a wallpaper transition.
-        // Never convert the crashing transition.
-        // Never update the transition for the wallpaper if we are just docking from recents
-        if (transit == TRANSIT_NONE || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE
-                || transit == TRANSIT_DOCK_TASK_FROM_RECENTS) {
-            return transit;
-        }
-
-        final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
-        final boolean showWallpaper = wallpaperTarget != null
-                && (wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
-        // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
-        // don't consider upgrading to wallpaper transition.
-        final WindowState oldWallpaper =
-                (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper)
-                        ? null
-                        : wallpaperTarget;
-        final ArraySet<AppWindowToken> openingApps = mService.mOpeningApps;
-        final ArraySet<AppWindowToken> closingApps = mService.mClosingApps;
-        final AppWindowToken topOpeningApp = getTopApp(mService.mOpeningApps,
-                false /* ignoreHidden */);
-        final AppWindowToken topClosingApp = getTopApp(mService.mClosingApps,
-                true /* ignoreHidden */);
-
-        boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps);
-        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                "New wallpaper target=" + wallpaperTarget
-                        + ", oldWallpaper=" + oldWallpaper
-                        + ", openingApps=" + openingApps
-                        + ", closingApps=" + closingApps);
-
-        if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
-            transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
-            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                    "New transit: " + AppTransition.appTransitionToString(transit));
-        }
-        // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
-        // relies on the fact that we always execute a Keyguard transition after preparing one.
-        else if (!isKeyguardGoingAwayTransit(transit)) {
-            if (closingAppHasWallpaper && openingAppHasWallpaper) {
-                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!");
-                switch (transit) {
-                    case TRANSIT_ACTIVITY_OPEN:
-                    case TRANSIT_TASK_OPEN:
-                    case TRANSIT_TASK_TO_FRONT:
-                        transit = TRANSIT_WALLPAPER_INTRA_OPEN;
-                        break;
-                    case TRANSIT_ACTIVITY_CLOSE:
-                    case TRANSIT_TASK_CLOSE:
-                    case TRANSIT_TASK_TO_BACK:
-                        transit = TRANSIT_WALLPAPER_INTRA_CLOSE;
-                        break;
-                }
-                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                        "New transit: " + AppTransition.appTransitionToString(transit));
-            } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty()
-                    && !openingApps.contains(oldWallpaper.mAppToken)
-                    && closingApps.contains(oldWallpaper.mAppToken)
-                    && topClosingApp == oldWallpaper.mAppToken) {
-                // We are transitioning from an activity with a wallpaper to one without.
-                transit = TRANSIT_WALLPAPER_CLOSE;
-                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
-                        + AppTransition.appTransitionToString(transit));
-            } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()
-                    && openingApps.contains(wallpaperTarget.mAppToken)
-                    && topOpeningApp == wallpaperTarget.mAppToken
-                    && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) {
-                // We are transitioning from an activity without
-                // a wallpaper to now showing the wallpaper
-                transit = TRANSIT_WALLPAPER_OPEN;
-                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
-                        + AppTransition.appTransitionToString(transit));
-            }
-        }
-        return transit;
-    }
-
-    /**
-     * There are cases where we open/close a new task/activity, but in reality only a translucent
-     * activity on top of existing activities is opening/closing. For that one, we have a different
-     * animation because non of the task/activity animations actually work well with translucent
-     * apps.
-     *
-     * @param transit The current transition type.
-     * @return The current transition type or
-     *         {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE}/
-     *         {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the
-     *         situation.
-     */
-    @VisibleForTesting
-    int maybeUpdateTransitToTranslucentAnim(int transit) {
-        final boolean taskOrActivity = AppTransition.isTaskTransit(transit)
-                || AppTransition.isActivityTransit(transit);
-        boolean allOpeningVisible = true;
-        boolean allTranslucentOpeningApps = !mService.mOpeningApps.isEmpty();
-        for (int i = mService.mOpeningApps.size() - 1; i >= 0; i--) {
-            final AppWindowToken token = mService.mOpeningApps.valueAt(i);
-            if (!token.isVisible()) {
-                allOpeningVisible = false;
-                if (token.fillsParent()) {
-                    allTranslucentOpeningApps = false;
-                }
-            }
-        }
-        boolean allTranslucentClosingApps = !mService.mClosingApps.isEmpty();
-        for (int i = mService.mClosingApps.size() - 1; i >= 0; i--) {
-            if (mService.mClosingApps.valueAt(i).fillsParent()) {
-                allTranslucentClosingApps = false;
-                break;
-            }
-        }
-
-        if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) {
-            return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
-        }
-        if (taskOrActivity && allTranslucentOpeningApps && mService.mClosingApps.isEmpty()) {
-            return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
-        }
-        return transit;
-    }
-
-    private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) {
-        for (int i = apps.size() - 1; i >= 0; i--) {
-            if (apps.valueAt(i).windowsCanBeWallpaperTarget()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Finds the top app in a list of apps, using its {@link AppWindowToken#getPrefixOrderIndex} to
-     * compare z-order.
-     *
-     * @param apps The list of apps to search.
-     * @param ignoreHidden If set to true, ignores apps that are {@link AppWindowToken#isHidden}.
-     * @return The top {@link AppWindowToken}.
-     */
-    private AppWindowToken getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden) {
-        int topPrefixOrderIndex = Integer.MIN_VALUE;
-        AppWindowToken topApp = null;
-        for (int i = apps.size() - 1; i >= 0; i--) {
-            final AppWindowToken app = apps.valueAt(i);
-            if (ignoreHidden && app.isHidden()) {
-                continue;
-            }
-            final int prefixOrderIndex = app.getPrefixOrderIndex();
-            if (prefixOrderIndex > topPrefixOrderIndex) {
-                topPrefixOrderIndex = prefixOrderIndex;
-                topApp = app;
-            }
-        }
-        return topApp;
-    }
-
-    private void processApplicationsAnimatingInPlace(int transit) {
-        if (transit == TRANSIT_TASK_IN_PLACE) {
-            // TODO (b/111362605): non-default-display transition.
-            // Find the focused window
-            final WindowState win = mService.getDefaultDisplayContentLocked().findFocusedWindow();
-            if (win != null) {
-                final AppWindowToken wtoken = win.mAppToken;
-                if (DEBUG_APP_TRANSITIONS)
-                    Slog.v(TAG, "Now animating app in place " + wtoken);
-                wtoken.cancelAnimation();
-                wtoken.applyAnimationLocked(null, transit, false, false);
-                wtoken.updateReportedVisibilityLocked();
-                wtoken.showAllWindowsLocked();
-            }
-        }
-    }
-
     void requestTraversal() {
         if (!mTraversalScheduled) {
             mTraversalScheduled = true;
diff --git a/services/core/jni/com_android_server_connectivity_Vpn.cpp b/services/core/jni/com_android_server_connectivity_Vpn.cpp
index b6bea11..836d6d8 100644
--- a/services/core/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/core/jni/com_android_server_connectivity_Vpn.cpp
@@ -57,7 +57,7 @@
 
 static int create_interface(int mtu)
 {
-    int tun = open("/dev/tun", O_RDWR | O_NONBLOCK);
+    int tun = open("/dev/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC);
 
     ifreq ifr4;
     memset(&ifr4, 0, sizeof(ifr4));
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index b47dbfa..a4983a9 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -40,7 +40,7 @@
 #include <cinttypes>
 #include <iomanip>
 
-static jobject mCallbacksObj = NULL;
+static jobject mCallbacksObj = nullptr;
 
 static jmethodID method_reportLocation;
 static jmethodID method_reportStatus;
@@ -717,7 +717,7 @@
 
     std::vector<uint8_t> navigationData = message.data;
     uint8_t* data = &(navigationData[0]);
-    if (dataLength == 0 || data == NULL) {
+    if (dataLength == 0 || data == nullptr) {
       ALOGE("Invalid Navigation Message found: data=%p, length=%zd", data,
             dataLength);
       return Void();
@@ -749,7 +749,7 @@
     Return<void> GnssMeasurementCb(const IGnssMeasurementCallback_V1_0::GnssData& data) override;
  private:
     void translateGnssMeasurement_V1_0(
-            JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurement,
+            const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurement,
             JavaObject& object);
     jobjectArray translateGnssMeasurements(
             JNIEnv* env,
@@ -772,7 +772,7 @@
     clock = translateGnssClock(env, &data.clock);
 
     measurementArray = translateGnssMeasurements(
-        env, data.measurements.data(), NULL, data.measurements.size());
+        env, data.measurements.data(), nullptr, data.measurements.size());
     setMeasurementData(env, clock, measurementArray);
 
     env->DeleteLocalRef(clock);
@@ -789,7 +789,7 @@
 
     clock = translateGnssClock(env, &data.clock);
     measurementArray = translateGnssMeasurements(
-        env, NULL, data.measurements.data(), data.measurementCount);
+        env, nullptr, data.measurements.data(), data.measurementCount);
     setMeasurementData(env, clock, measurementArray);
 
     env->DeleteLocalRef(clock);
@@ -799,7 +799,7 @@
 
 // preallocate object as: JavaObject object(env, "android/location/GnssMeasurement");
 void GnssMeasurementCallback::translateGnssMeasurement_V1_0(
-        JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurement,
+        const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurement,
         JavaObject& object) {
     uint32_t flags = static_cast<uint32_t>(measurement->flags);
 
@@ -816,7 +816,7 @@
         measurement->pseudorangeRateUncertaintyMps);
     SET(AccumulatedDeltaRangeState,
         (static_cast<int32_t>(measurement->accumulatedDeltaRangeState) &
-        !ADR_STATE_HALF_CYCLE_REPORTED)); // Half Cycle state not reported from Hardware in V1_0
+        ~ADR_STATE_HALF_CYCLE_REPORTED)); // Half Cycle state not reported from Hardware in V1_0
     SET(AccumulatedDeltaRangeMeters, measurement->accumulatedDeltaRangeM);
     SET(AccumulatedDeltaRangeUncertaintyMeters,
         measurement->accumulatedDeltaRangeUncertaintyM);
@@ -883,26 +883,26 @@
          const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurements_v1_0,
          size_t count) {
     if (count == 0) {
-        return NULL;
+        return nullptr;
     }
 
     jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
     jobjectArray gnssMeasurementArray = env->NewObjectArray(
             count,
             gnssMeasurementClass,
-            NULL /* initialElement */);
+            nullptr /* initialElement */);
 
     for (uint16_t i = 0; i < count; ++i) {
         JavaObject object(env, "android/location/GnssMeasurement");
-        if (measurements_v1_1 != NULL) {
-            translateGnssMeasurement_V1_0(env, &(measurements_v1_1[i].v1_0), object);
+        if (measurements_v1_1 != nullptr) {
+            translateGnssMeasurement_V1_0(&(measurements_v1_1[i].v1_0), object);
 
             // Set the V1_1 flag, and mark that new field has valid information for Java Layer
             SET(AccumulatedDeltaRangeState,
                     (static_cast<int32_t>(measurements_v1_1[i].accumulatedDeltaRangeState) |
                     ADR_STATE_HALF_CYCLE_REPORTED));
         } else {
-            translateGnssMeasurement_V1_0(env, &(measurements_v1_0[i]), object);
+            translateGnssMeasurement_V1_0(&(measurements_v1_0[i]), object);
         }
 
         env->SetObjectArrayElement(gnssMeasurementArray, i, object.get());
@@ -987,14 +987,12 @@
 Return<void> AGnssCallback::agnssStatusIpV6Cb(
         const IAGnssCallback::AGnssStatusIpV6& agps_status) {
     JNIEnv* env = getJniEnv();
-    jbyteArray byteArray = NULL;
-    bool isSupported = false;
+    jbyteArray byteArray = nullptr;
 
     byteArray = env->NewByteArray(16);
-    if (byteArray != NULL) {
+    if (byteArray != nullptr) {
         env->SetByteArrayRegion(byteArray, 0, 16,
                                 (const jbyte*)(agps_status.ipV6Addr.data()));
-        isSupported = true;
     } else {
         ALOGE("Unable to allocate byte array for IPv6 address.");
     }
@@ -1006,7 +1004,7 @@
         ALOGD("AGPS IP is v6: %s", str);
     }
 
-    jsize byteArrayLength = byteArray != NULL ? env->GetArrayLength(byteArray) : 0;
+    jsize byteArrayLength = byteArray != nullptr ? env->GetArrayLength(byteArray) : 0;
     ALOGV("Passing AGPS IP addr: size %d", byteArrayLength);
     env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus,
                         agps_status.type, agps_status.status, byteArray);
@@ -1023,7 +1021,7 @@
 Return<void> AGnssCallback::agnssStatusIpV4Cb(
         const IAGnssCallback::AGnssStatusIpV4& agps_status) {
     JNIEnv* env = getJniEnv();
-    jbyteArray byteArray = NULL;
+    jbyteArray byteArray = nullptr;
 
     uint32_t ipAddr = agps_status.ipV4Addr;
     byteArray = convertToIpV4(ipAddr);
@@ -1039,7 +1037,7 @@
     }
 
     jsize byteArrayLength =
-      byteArray != NULL ? env->GetArrayLength(byteArray) : 0;
+      byteArray != nullptr ? env->GetArrayLength(byteArray) : 0;
     ALOGV("Passing AGPS IP addr: size %d", byteArrayLength);
     env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus,
                       agps_status.type, agps_status.status, byteArray);
@@ -1054,14 +1052,14 @@
 
 jbyteArray AGnssCallback::convertToIpV4(uint32_t ip) {
     if (INADDR_NONE == ip) {
-        return NULL;
+        return nullptr;
     }
 
     JNIEnv* env = getJniEnv();
     jbyteArray byteArray = env->NewByteArray(4);
-    if (byteArray == NULL) {
+    if (byteArray == nullptr) {
         ALOGE("Unable to allocate byte array for IPv4 address");
-        return NULL;
+        return nullptr;
     }
 
     jbyte ipv4[4];
@@ -1341,7 +1339,7 @@
 
     sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback();
     if (gnssXtraIface == nullptr) {
-        ALOGE("Unable to initialize GNSS Xtra interface\n");
+        ALOGI("Unable to initialize GNSS Xtra interface\n");
     } else {
         result = gnssXtraIface->setCallback(gnssXtraCbIface);
         if (!result.isOk() || !result) {
@@ -1483,7 +1481,7 @@
         return;
     }
 
-    const char *setid = env->GetStringUTFChars(setid_string, NULL);
+    const char *setid = env->GetStringUTFChars(setid_string, nullptr);
     agnssRilIface->setSetId((IAGnssRil::SetIDType)type, setid);
     env->ReleaseStringUTFChars(setid_string, setid);
 }
@@ -1579,12 +1577,12 @@
         ALOGE("no AGPS interface in agps_data_conn_open");
         return;
     }
-    if (apn == NULL) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+    if (apn == nullptr) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", nullptr);
         return;
     }
 
-    const char *apnStr = env->GetStringUTFChars(apn, NULL);
+    const char *apnStr = env->GetStringUTFChars(apn, nullptr);
 
     auto result = agnssIface->dataConnOpen(apnStr, static_cast<IAGnss::ApnIpType>(apnIpType));
     if (!result.isOk() || !result){
@@ -1626,7 +1624,7 @@
         return;
     }
 
-    const char *c_hostname = env->GetStringUTFChars(hostname, NULL);
+    const char *c_hostname = env->GetStringUTFChars(hostname, nullptr);
     auto result = agnssIface->setServer(static_cast<IAGnssCallback::AGnssType>(type),
                                        c_hostname,
                                        port);
@@ -1649,7 +1647,7 @@
 
 static jstring android_location_GnssLocationProvider_get_internal_state(JNIEnv* env,
                                                                        jobject /* obj */) {
-    jstring result = NULL;
+    jstring result = nullptr;
     /*
      * TODO(b/33089503) : Create a jobject to represent GnssDebug.
      */
@@ -1736,7 +1734,7 @@
             ALOGE("updateNetworkState failed");
         }
 
-        const char *c_apn = env->GetStringUTFChars(apn, NULL);
+        const char *c_apn = env->GetStringUTFChars(apn, nullptr);
         result = agnssRilIface->updateNetworkAvailability(available, c_apn);
         if (!result.isOk() || !result) {
             ALOGE("updateNetworkAvailability failed");
@@ -2019,15 +2017,15 @@
     }
 
     jint *constellation_array = env->GetIntArrayElements(constellations, 0);
-    if (NULL == constellation_array) {
-        ALOGI("GetIntArrayElements returns NULL.");
+    if (nullptr == constellation_array) {
+        ALOGI("GetIntArrayElements returns nullptr.");
         return JNI_FALSE;
     }
     jsize length = env->GetArrayLength(constellations);
 
     jint *sv_id_array = env->GetIntArrayElements(sv_ids, 0);
-    if (NULL == sv_id_array) {
-        ALOGI("GetIntArrayElements returns NULL.");
+    if (nullptr == sv_id_array) {
+        ALOGI("GetIntArrayElements returns nullptr.");
         return JNI_FALSE;
     }
 
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index d0f173b..ec94e3c9 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -66,6 +66,30 @@
     jbyte* mElements;
 };
 
+jbyteArray constructFsveritySignedData(JNIEnv* env, jobject /* clazz */, jbyteArray digest) {
+#if HAS_FSVERITY
+    const int kSha256Bytes = 32;
+    auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest_disk) + kSha256Bytes);
+    fsverity_digest_disk* data = reinterpret_cast<fsverity_digest_disk*>(raii->getRaw());
+
+    data->digest_algorithm = FS_VERITY_ALG_SHA256;
+    data->digest_size = kSha256Bytes;
+    if (env->GetArrayLength(digest) != kSha256Bytes) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid hash size of %d",
+                          env->GetArrayLength(digest));
+        return 0;
+    }
+    const jbyte* src = env->GetByteArrayElements(digest, nullptr);
+    memcpy(data->digest, src, kSha256Bytes);
+
+    return raii->release();
+#else
+    LOG_ALWAYS_FATAL("fs-verity is used while not enabled");
+    return 0;
+#endif  // HAS_FSVERITY
+}
+
+
 jbyteArray constructFsverityDescriptor(JNIEnv* env, jobject /* clazz */, jlong fileSize) {
 #if HAS_FSVERITY
     auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_descriptor));
@@ -122,6 +146,7 @@
 }
 
 const JNINativeMethod sMethods[] = {
+    { "constructFsveritySignedDataNative", "([B)[B", (void *)constructFsveritySignedData },
     { "constructFsverityDescriptorNative", "(J)[B", (void *)constructFsverityDescriptor },
     { "constructFsverityExtensionNative", "(SI)[B", (void *)constructFsverityExtension },
     { "constructFsverityFooterNative", "(I)[B", (void *)constructFsverityFooter },
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index 4514492..e9b2d7f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -53,8 +53,8 @@
 
     private final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() {
         @Override
-        public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
-                long timestamp, int uid) {
+        public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+                String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
             if (!mIsLoggingEnabled.get()) {
                 return;
             }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index bbc4f44..933eac6 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -16,6 +16,12 @@
 
 package com.android.server;
 
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
+import static android.os.IServiceManager.DUMP_FLAG_PROTO;
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.app.ActivityThread;
 import android.app.INotificationManager;
 import android.app.usage.UsageStatsManagerInternal;
@@ -64,10 +70,12 @@
 import com.android.internal.util.EmergencyAffordanceManager;
 import com.android.internal.widget.ILockSettings;
 import com.android.server.am.ActivityManagerService;
-import com.android.server.am.ActivityTaskManagerService;
 import com.android.server.appbinding.AppBindingService;
 import com.android.server.audio.AudioService;
 import com.android.server.biometrics.BiometricService;
+import com.android.server.biometrics.face.FaceService;
+import com.android.server.biometrics.fingerprint.FingerprintService;
+import com.android.server.biometrics.iris.IrisService;
 import com.android.server.broadcastradio.BroadcastRadioService;
 import com.android.server.camera.CameraServiceProxy;
 import com.android.server.clipboard.ClipboardService;
@@ -78,8 +86,6 @@
 import com.android.server.display.DisplayManagerService;
 import com.android.server.dreams.DreamManagerService;
 import com.android.server.emergency.EmergencyAffordanceService;
-import com.android.server.biometrics.face.FaceService;
-import com.android.server.biometrics.fingerprint.FingerprintService;
 import com.android.server.hdmi.HdmiControlService;
 import com.android.server.input.InputManagerService;
 import com.android.server.inputmethod.InputMethodManagerService;
@@ -87,8 +93,8 @@
 import com.android.server.lights.LightsService;
 import com.android.server.media.MediaResourceMonitorService;
 import com.android.server.media.MediaRouterService;
-import com.android.server.media.MediaUpdateService;
 import com.android.server.media.MediaSessionService;
+import com.android.server.media.MediaUpdateService;
 import com.android.server.media.projection.MediaProjectionManagerService;
 import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.net.NetworkStatsService;
@@ -109,6 +115,7 @@
 import com.android.server.policy.PhoneWindowManager;
 import com.android.server.power.PowerManagerService;
 import com.android.server.power.ShutdownThread;
+import com.android.server.power.ThermalManagerService;
 import com.android.server.restrictions.RestrictionsManagerService;
 import com.android.server.security.KeyAttestationApplicationIdProviderService;
 import com.android.server.security.KeyChainSystemService;
@@ -127,6 +134,8 @@
 import com.android.server.usage.UsageStatsService;
 import com.android.server.vr.VrManagerService;
 import com.android.server.webkit.WebViewUpdateService;
+import com.android.server.wm.ActivityTaskManagerService;
+import com.android.server.wm.WindowManagerGlobalLock;
 import com.android.server.wm.WindowManagerService;
 
 import dalvik.system.VMRuntime;
@@ -138,12 +147,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Future;
 
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
-import static android.os.IServiceManager.DUMP_FLAG_PROTO;
-import static android.view.Display.DEFAULT_DISPLAY;
-
 public final class SystemServer {
     private static final String TAG = "SystemServer";
 
@@ -229,6 +232,8 @@
             "com.android.server.wallpaper.WallpaperManagerService$Lifecycle";
     private static final String AUTO_FILL_MANAGER_SERVICE_CLASS =
             "com.android.server.autofill.AutofillManagerService";
+    private static final String INTELLIGENCE_MANAGER_SERVICE_CLASS =
+            "com.android.server.intelligence.IntelligenceManagerService";
     private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS =
             "com.android.server.timezone.RulesManagerService$Lifecycle";
     private static final String IOT_SERVICE_CLASS =
@@ -243,6 +248,8 @@
             "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
     private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS =
             "com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
+    private static final String ADB_SERVICE_CLASS =
+            "com.android.server.adb.AdbService$Lifecycle";
 
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
@@ -270,6 +277,7 @@
     // TODO: remove all of these references by improving dependency resolution and boot phases
     private PowerManagerService mPowerManagerService;
     private ActivityManagerService mActivityManagerService;
+    private WindowManagerGlobalLock mWindowManagerGlobalLock;
     private WebViewUpdateService mWebViewUpdateService;
     private DisplayManagerService mDisplayManagerService;
     private PackageManagerService mPackageManagerService;
@@ -592,6 +600,7 @@
                 mSystemServiceManager, atm);
         mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
         mActivityManagerService.setInstaller(installer);
+        mWindowManagerGlobalLock = atm.getGlobalLock();
         traceEnd();
 
         // Power manager needs to be started early because other services need it.
@@ -602,6 +611,10 @@
         mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
         traceEnd();
 
+        traceBeginAndSlog("StartThermalManager");
+        mSystemServiceManager.startService(ThermalManagerService.class);
+        traceEnd();
+
         // Now that the power manager has been started, let the activity manager
         // initialize power management features.
         traceBeginAndSlog("InitPowerManagement");
@@ -792,6 +805,8 @@
 
         boolean disableSystemTextClassifier = SystemProperties.getBoolean(
                 "config.disable_systemtextclassifier", false);
+        boolean disableIntelligence = SystemProperties.getBoolean(
+                "config.disable_intelligence", false);
         boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime",
                 false);
         boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
@@ -914,7 +929,7 @@
             ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
             mSensorServiceStart = null;
             wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
-                    new PhoneWindowManager());
+                    new PhoneWindowManager(), mWindowManagerGlobalLock);
             ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                     DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
             ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
@@ -1372,6 +1387,15 @@
                 traceEnd();
             }
 
+            // Start ADB Debugging Service
+            traceBeginAndSlog("StartAdbService");
+            try {
+                mSystemServiceManager.startService(ADB_SERVICE_CLASS);
+            } catch (Throwable e) {
+                Slog.e(TAG, "Failure starting AdbService");
+            }
+            traceEnd();
+
             if (!isWatch) {
                 traceBeginAndSlog("StartSerialService");
                 try {
@@ -1578,6 +1602,8 @@
 
             final boolean hasFeatureFace
                     = mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE);
+            final boolean hasFeatureIris
+                    = mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS);
             final boolean hasFeatureFingerprint
                     = mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
 
@@ -1587,15 +1613,21 @@
                 traceEnd();
             }
 
+            if (hasFeatureIris) {
+                traceBeginAndSlog("StartIrisSensor");
+                mSystemServiceManager.startService(IrisService.class);
+                traceEnd();
+            }
+
             if (hasFeatureFingerprint) {
                 traceBeginAndSlog("StartFingerprintSensor");
                 mSystemServiceManager.startService(FingerprintService.class);
                 traceEnd();
             }
 
-            if (hasFeatureFace || hasFeatureFingerprint) {
+            if (hasFeatureFace || hasFeatureIris || hasFeatureFingerprint) {
                 // Start this service after all biometric services.
-                traceBeginAndSlog("StartBiometricPromptService");
+                traceBeginAndSlog("StartBiometricService");
                 mSystemServiceManager.startService(BiometricService.class);
                 traceEnd();
             }
@@ -1717,6 +1749,12 @@
             traceEnd();
         }
 
+        if (!disableIntelligence) {
+            traceBeginAndSlog("StartIntelligenceService");
+            mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS);
+            traceEnd();
+        }
+
         traceBeginAndSlog("AppServiceManager");
         mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
         traceEnd();
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
new file mode 100644
index 0000000..2cc3ef8
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 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.server.backup.encryption.chunking;
+
+import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
+import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+
+import android.platform.test.annotations.Presubmit;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import java.security.SecureRandom;
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class ChunkEncryptorTest {
+    private static final String MAC_ALGORITHM = "HmacSHA256";
+    private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+    private static final int GCM_NONCE_LENGTH_BYTES = 12;
+    private static final int GCM_TAG_LENGTH_BYTES = 16;
+    private static final String CHUNK_PLAINTEXT =
+            "A little Learning is a dang'rous Thing;\n"
+                    + "Drink deep, or taste not the Pierian Spring:\n"
+                    + "There shallow Draughts intoxicate the Brain,\n"
+                    + "And drinking largely sobers us again.";
+    private static final byte[] PLAINTEXT_BYTES = CHUNK_PLAINTEXT.getBytes(UTF_8);
+    private static final byte[] NONCE_1 = "0123456789abc".getBytes(UTF_8);
+    private static final byte[] NONCE_2 = "123456789abcd".getBytes(UTF_8);
+
+    private static final byte[][] NONCES = new byte[][] {NONCE_1, NONCE_2};
+
+    @Mock private SecureRandom mSecureRandomMock;
+    private SecretKey mSecretKey;
+    private ChunkHash mPlaintextHash;
+    private ChunkEncryptor mChunkEncryptor;
+
+    @Before
+    public void setUp() throws Exception {
+        mSecretKey = generateAesKey();
+        ChunkHasher chunkHasher = new ChunkHasher(mSecretKey);
+        mPlaintextHash = chunkHasher.computeHash(PLAINTEXT_BYTES);
+        mSecureRandomMock = mock(SecureRandom.class);
+        mChunkEncryptor = new ChunkEncryptor(mSecretKey, mSecureRandomMock);
+
+        // Return NONCE_1, then NONCE_2 for invocations of mSecureRandomMock.nextBytes().
+        doAnswer(
+                        new Answer<Void>() {
+                            private int mInvocation = 0;
+
+                            @Override
+                            public Void answer(InvocationOnMock invocation) {
+                                byte[] nonceDestination = invocation.getArgument(0);
+                                System.arraycopy(
+                                        NONCES[this.mInvocation],
+                                        0,
+                                        nonceDestination,
+                                        0,
+                                        GCM_NONCE_LENGTH_BYTES);
+                                this.mInvocation++;
+                                return null;
+                            }
+                        })
+                .when(mSecureRandomMock)
+                .nextBytes(any(byte[].class));
+    }
+
+    @Test
+    public void encrypt_withHash_resultContainsHashAsKey() throws Exception {
+        EncryptedChunk chunk = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
+
+        assertThat(chunk.key()).isEqualTo(mPlaintextHash);
+    }
+
+    @Test
+    public void encrypt_generatesHmacOfPlaintext() throws Exception {
+        EncryptedChunk chunk = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
+
+        byte[] generatedHash = chunk.key().getHash();
+        Mac mac = Mac.getInstance(MAC_ALGORITHM);
+        mac.init(mSecretKey);
+        byte[] plaintextHmac = mac.doFinal(PLAINTEXT_BYTES);
+        assertThat(generatedHash).isEqualTo(plaintextHmac);
+    }
+
+    @Test
+    public void encrypt_whenInvokedAgain_generatesNewNonce() throws Exception {
+        EncryptedChunk chunk1 = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
+
+        EncryptedChunk chunk2 = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
+
+        assertThat(chunk1.nonce()).isNotEqualTo(chunk2.nonce());
+    }
+
+    @Test
+    public void encrypt_whenInvokedAgain_generatesNewCiphertext() throws Exception {
+        EncryptedChunk chunk1 = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
+
+        EncryptedChunk chunk2 = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
+
+        assertThat(chunk1.encryptedBytes()).isNotEqualTo(chunk2.encryptedBytes());
+    }
+
+    @Test
+    public void encrypt_generates12ByteNonce() throws Exception {
+        EncryptedChunk encryptedChunk = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
+
+        byte[] nonce = encryptedChunk.nonce();
+        assertThat(nonce).hasLength(GCM_NONCE_LENGTH_BYTES);
+    }
+
+    @Test
+    public void encrypt_decryptedResultCorrespondsToPlaintext() throws Exception {
+        EncryptedChunk chunk = mChunkEncryptor.encrypt(mPlaintextHash, PLAINTEXT_BYTES);
+
+        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+        cipher.init(
+                Cipher.DECRYPT_MODE,
+                mSecretKey,
+                new GCMParameterSpec(GCM_TAG_LENGTH_BYTES * 8, chunk.nonce()));
+        byte[] decrypted = cipher.doFinal(chunk.encryptedBytes());
+        assertThat(decrypted).isEqualTo(PLAINTEXT_BYTES);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
new file mode 100644
index 0000000..11796c0
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 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.server.backup.encryption.chunking;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class ChunkHasherTest {
+    private static final String KEY_ALGORITHM = "AES";
+    private static final String MAC_ALGORITHM = "HmacSHA256";
+
+    private static final byte[] TEST_KEY = {100, 120};
+    private static final byte[] TEST_DATA = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
+
+    private SecretKey mSecretKey;
+    private ChunkHasher mChunkHasher;
+
+    @Before
+    public void setUp() throws Exception {
+        mSecretKey = new SecretKeySpec(TEST_KEY, KEY_ALGORITHM);
+        mChunkHasher = new ChunkHasher(mSecretKey);
+    }
+
+    @Test
+    public void computeHash_returnsHmacForData() throws Exception {
+        ChunkHash chunkHash = mChunkHasher.computeHash(TEST_DATA);
+
+        byte[] hash = chunkHash.getHash();
+        Mac mac = Mac.getInstance(MAC_ALGORITHM);
+        mac.init(mSecretKey);
+        byte[] expectedHash = mac.doFinal(TEST_DATA);
+        assertThat(hash).isEqualTo(expectedHash);
+    }
+
+    @Test
+    public void computeHash_generates256BitHmac() throws Exception {
+        int expectedLength = 256 / Byte.SIZE;
+
+        byte[] hash = mChunkHasher.computeHash(TEST_DATA).getHash();
+
+        assertThat(hash).hasLength(expectedLength);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
new file mode 100644
index 0000000..8b54e1e
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 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.server.backup.encryption.chunking;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import com.google.common.primitives.Bytes;
+import java.io.ByteArrayOutputStream;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class RawBackupWriterTest {
+    private static final byte[] TEST_BYTES = {1, 2, 3, 4, 5, 6};
+
+    private BackupWriter mWriter;
+    private ByteArrayOutputStream mOutput;
+
+    @Before
+    public void setUp() {
+        mOutput = new ByteArrayOutputStream();
+        mWriter = new RawBackupWriter(mOutput);
+    }
+
+    @Test
+    public void writeBytes_writesToOutputStream() throws Exception {
+        mWriter.writeBytes(TEST_BYTES);
+
+        assertThat(mOutput.toByteArray())
+                .asList()
+                .containsExactlyElementsIn(Bytes.asList(TEST_BYTES))
+                .inOrder();
+    }
+
+    @Test
+    public void writeChunk_throwsUnsupportedOperationException() throws Exception {
+        assertThrows(UnsupportedOperationException.class, () -> mWriter.writeChunk(0, 0));
+    }
+
+    @Test
+    public void getBytesWritten_returnsTotalSum() throws Exception {
+        mWriter.writeBytes(TEST_BYTES);
+        mWriter.writeBytes(TEST_BYTES);
+
+        long bytesWritten = mWriter.getBytesWritten();
+
+        assertThat(bytesWritten).isEqualTo(2 * TEST_BYTES.length);
+    }
+
+    @Test
+    public void flush_flushesOutputStream() throws Exception {
+        mOutput = mock(ByteArrayOutputStream.class);
+        mWriter = new RawBackupWriter(mOutput);
+
+        mWriter.flush();
+
+        verify(mOutput).flush();
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java b/services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java
new file mode 100644
index 0000000..83e8461
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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.server.backup.testing;
+
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+/** Helpers for crypto code tests. */
+public class CryptoTestUtils {
+    private static final String KEY_ALGORITHM = "AES";
+    private static final int KEY_SIZE_BITS = 256;
+
+    private CryptoTestUtils() {}
+
+    public static SecretKey generateAesKey() throws NoSuchAlgorithmException {
+        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
+        keyGenerator.init(KEY_SIZE_BITS);
+        return keyGenerator.generateKey();
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index f85ffc8..3979a8e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -27,7 +27,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_LONG_TIME;
@@ -40,14 +39,15 @@
 import static com.android.server.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.atLeastOnce;
 
 import android.app.ActivityManager;
 import android.app.AlarmManager;
@@ -61,7 +61,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
@@ -92,7 +91,6 @@
     private static final String TEST_CALLING_PACKAGE = "com.android.framework.test-package";
     private static final int SYSTEM_UI_UID = 123456789;
     private static final int TEST_CALLING_UID = 12345;
-    private static final long DEFAULT_TIMEOUT = 5_000;
 
     private AlarmManagerService mService;
     @Mock
@@ -126,10 +124,11 @@
             mElapsed = millisElapsed;
         }
 
-        synchronized long expire() {
+        synchronized void expire() throws InterruptedException {
             mExpired = true;
-            notify();
-            return mElapsed;
+            notifyAll();
+            // Now wait for the alarm thread to finish execution.
+            wait();
         }
     }
 
@@ -146,6 +145,7 @@
         @Override
         int waitForAlarm() {
             synchronized (mTestTimer) {
+                mTestTimer.notifyAll();
                 if (!mTestTimer.mExpired) {
                     try {
                         mTestTimer.wait();
@@ -268,15 +268,16 @@
         final PendingIntent alarmPi = getNewMockPendingIntent();
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
 
-        mNowElapsedTest = mTestTimer.expire();
+        mNowElapsedTest = mTestTimer.getElapsed();
+        mTestTimer.expire();
 
         final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor =
                 ArgumentCaptor.forClass(PendingIntent.OnFinished.class);
-        verify(alarmPi, timeout(DEFAULT_TIMEOUT)).send(any(Context.class), eq(0),
-                any(Intent.class), onFinishedCaptor.capture(), any(Handler.class), isNull(), any());
-        verify(mWakeLock, timeout(DEFAULT_TIMEOUT)).acquire();
+        verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class),
+                onFinishedCaptor.capture(), any(Handler.class), isNull(), any());
+        verify(mWakeLock).acquire();
         onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null);
-        verify(mWakeLock, timeout(DEFAULT_TIMEOUT)).release();
+        verify(mWakeLock).release();
     }
 
     @Test
@@ -351,57 +352,58 @@
 
         when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
                 anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
-        mNowElapsedTest = mTestTimer.expire();
-        verify(mUsageStatsManagerInternal, timeout(DEFAULT_TIMEOUT).atLeastOnce())
+
+        mNowElapsedTest = mTestTimer.getElapsed();
+        mTestTimer.expire();
+
+        verify(mUsageStatsManagerInternal, atLeastOnce())
                 .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
                         eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong());
         final long expectedNextTrigger = mNowElapsedTest
                 + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_WORKING_SET);
-        assertTrue("Incorrect next alarm trigger. Expected " + expectedNextTrigger + " found: "
-                + mTestTimer.getElapsed(), pollingCheck(DEFAULT_TIMEOUT,
-                () -> (mTestTimer.getElapsed() == expectedNextTrigger)));
+        assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
     }
 
     @Test
-    public void testStandbyBucketDelay_frequent() {
+    public void testStandbyBucketDelay_frequent() throws Exception {
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
         assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
 
         when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
                 anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT);
-        mNowElapsedTest = mTestTimer.expire();
-        verify(mUsageStatsManagerInternal, timeout(DEFAULT_TIMEOUT).atLeastOnce())
+        mNowElapsedTest = mTestTimer.getElapsed();
+        mTestTimer.expire();
+
+        verify(mUsageStatsManagerInternal, atLeastOnce())
                 .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
                         eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong());
         final long expectedNextTrigger = mNowElapsedTest
                 + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_FREQUENT);
-        assertTrue("Incorrect next alarm trigger. Expected " + expectedNextTrigger + " found: "
-                + mTestTimer.getElapsed(), pollingCheck(DEFAULT_TIMEOUT,
-                () -> (mTestTimer.getElapsed() == expectedNextTrigger)));
+        assertEquals("Incorrect next alarm trigger.", expectedNextTrigger, mTestTimer.getElapsed());
     }
 
     @Test
-    public void testStandbyBucketDelay_rare() {
+    public void testStandbyBucketDelay_rare() throws Exception {
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
         assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
 
         when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
                 anyLong())).thenReturn(STANDBY_BUCKET_RARE);
-        mNowElapsedTest = mTestTimer.expire();
-        verify(mUsageStatsManagerInternal, timeout(DEFAULT_TIMEOUT).atLeastOnce())
+        mNowElapsedTest = mTestTimer.getElapsed();
+        mTestTimer.expire();
+
+        verify(mUsageStatsManagerInternal, atLeastOnce())
                 .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
                         eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong());
         final long expectedNextTrigger = mNowElapsedTest
                 + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_RARE);
-        assertTrue("Incorrect next alarm trigger. Expected " + expectedNextTrigger + " found: "
-                + mTestTimer.getElapsed(), pollingCheck(DEFAULT_TIMEOUT,
-                () -> (mTestTimer.getElapsed() == expectedNextTrigger)));
+        assertEquals("Incorrect next alarm trigger.", expectedNextTrigger, mTestTimer.getElapsed());
     }
 
     @Test
-    public void testAlarmRestrictedInBatterSaver() throws PendingIntent.CanceledException {
+    public void testAlarmRestrictedInBatterSaver() throws Exception {
         final ArgumentCaptor<AppStateTracker.Listener> listenerArgumentCaptor =
                 ArgumentCaptor.forClass(AppStateTracker.Listener.class);
         verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
@@ -416,8 +418,9 @@
                 mService.mPendingBackgroundAlarms;
         assertNull(restrictedAlarms.get(TEST_CALLING_UID));
 
-        mNowElapsedTest = mTestTimer.expire();
-        pollingCheck(DEFAULT_TIMEOUT, () -> (restrictedAlarms.get(TEST_CALLING_UID) != null));
+        mNowElapsedTest = mTestTimer.getElapsed();
+        mTestTimer.expire();
+        assertNotNull(restrictedAlarms.get(TEST_CALLING_UID));
 
         listenerArgumentCaptor.getValue().unblockAlarmsForUid(TEST_CALLING_UID);
         verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class), any(),
@@ -431,25 +434,4 @@
             mMockingSession.finishMocking();
         }
     }
-
-    private boolean pollingCheck(long timeout, Condition condition) {
-        final long deadline = SystemClock.uptimeMillis() + timeout;
-        boolean interrupted = false;
-        while (!condition.check() && SystemClock.uptimeMillis() < deadline) {
-            try {
-                Thread.sleep(500);
-            } catch (InterruptedException ie) {
-                interrupted = true;
-            }
-        }
-        if (interrupted) {
-            Thread.currentThread().interrupt();
-        }
-        return condition.check();
-    }
-
-    @FunctionalInterface
-    interface Condition {
-        boolean check();
-    }
 }
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 878179b..e2f8995 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -33,7 +33,8 @@
     testables \
     testng \
     ub-uiautomator\
-    platformprotosnano
+    platformprotosnano \
+    hamcrest-library
 
 LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
 
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 863e487..fa17b61 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -206,11 +206,11 @@
             </intent-filter>
         </activity-alias>
 
-        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityA" />
-        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityB" />
-        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
-        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
-        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
 
         <receiver android:name="com.android.server.appwidget.DummyAppWidget">
             <intent-filter>
diff --git a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
index 8022532..69d8e25 100644
--- a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
@@ -28,9 +28,9 @@
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.os.storage.StorageManagerInternal;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.SparseArray;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -38,9 +38,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
-import java.util.List;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class StorageManagerServiceTest {
@@ -83,29 +80,14 @@
 
         when(mUmi.getUserIds()).thenReturn(new int[] { 0 });
 
-        {
-            final SparseArray<String> res = new SparseArray<>();
-            res.put(UID_COLORS, NAME_COLORS);
-            when(mPmi.getAppsWithSharedUserIds()).thenReturn(res);
-        }
+        when(mPmi.getSharedUserIdForPackage(eq(PKG_GREY))).thenReturn(null);
+        when(mPmi.getSharedUserIdForPackage(eq(PKG_RED))).thenReturn(NAME_COLORS);
+        when(mPmi.getSharedUserIdForPackage(eq(PKG_BLUE))).thenReturn(NAME_COLORS);
 
-        {
-            final List<ApplicationInfo> res = new ArrayList<>();
-            res.add(buildApplicationInfo(PKG_GREY, UID_GREY));
-            res.add(buildApplicationInfo(PKG_RED, UID_COLORS));
-            res.add(buildApplicationInfo(PKG_BLUE, UID_COLORS));
-            when(mPm.getInstalledApplicationsAsUser(anyInt(), anyInt())).thenReturn(res);
-        }
-
-        when(mPmi.getPackageUid(eq(PKG_GREY), anyInt(), anyInt())).thenReturn(UID_GREY);
-        when(mPmi.getPackageUid(eq(PKG_RED), anyInt(), anyInt())).thenReturn(UID_COLORS);
-        when(mPmi.getPackageUid(eq(PKG_BLUE), anyInt(), anyInt())).thenReturn(UID_COLORS);
-
-        when(mPm.getPackagesForUid(eq(UID_GREY))).thenReturn(new String[] { PKG_GREY });
-        when(mPm.getPackagesForUid(eq(UID_COLORS))).thenReturn(new String[] { PKG_RED, PKG_BLUE });
+        when(mPmi.getPackagesForSharedUserId(eq(NAME_COLORS), anyInt()))
+                .thenReturn(new String[] { PKG_RED, PKG_BLUE });
 
         mService = new StorageManagerService(mContext);
-        mService.collectPackagesInfo();
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
index 72c22fd..29a920a 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
@@ -22,9 +22,14 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.accounts.Account;
 import android.content.Context;
 import android.database.Cursor;
+import android.database.sqlite.SQLiteStatement;
 import android.os.Build;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Pair;
@@ -37,7 +42,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
 import java.io.File;
+import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -64,8 +73,11 @@
     private File deDb;
     private File ceDb;
 
+    @Mock private PrintWriter mockWriter;
+
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
         Context context = InstrumentationRegistry.getContext();
         preNDb = new File(context.getCacheDir(), PREN_DB);
         ceDb = new File(context.getCacheDir(), CE_DB);
@@ -444,4 +456,33 @@
         assertTrue(mAccountsDb.deleteDeAccount(accId)); // Trigger should remove visibility.
         assertNull(mAccountsDb.findAccountVisibility(account, packageName1));
     }
+
+    @Test
+    public void testDumpDebugTable() {
+        long accId = 10;
+        long insertionPoint = mAccountsDb.reserveDebugDbInsertionPoint();
+
+        SQLiteStatement logStatement = mAccountsDb.getStatementForLogging();
+
+        logStatement.bindLong(1, accId);
+        logStatement.bindString(2, "action");
+        logStatement.bindString(3, "date");
+        logStatement.bindLong(4, 10);
+        logStatement.bindString(5, "table");
+        logStatement.bindLong(6, insertionPoint);
+        logStatement.execute();
+
+        mAccountsDb.dumpDebugTable(mockWriter);
+
+        verify(mockWriter, times(3)).println(anyString());
+    }
+
+    @Test
+    public void testReserveDebugDbInsertionPoint() {
+        long insertionPoint = mAccountsDb.reserveDebugDbInsertionPoint();
+        long insertionPoint2 = mAccountsDb.reserveDebugDbInsertionPoint();
+
+        assertEquals(0, insertionPoint);
+        assertEquals(1, insertionPoint2);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
deleted file mode 100644
index 0da5742..0000000
--- a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
-
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doReturn;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link ActivityDisplay} class.
- *
- * Build/Install/Run:
- *  atest WmTests:ActivityDisplayTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class ActivityDisplayTests extends ActivityTestsBase {
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        setupActivityTaskManagerService();
-    }
-
-    @Test
-    public void testLastFocusedStackIsUpdatedWhenMovingStack() {
-        // Create a stack at bottom.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack stack = display.createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, !ON_TOP);
-        final ActivityStack prevFocusedStack = display.getFocusedStack();
-
-        stack.moveToFront("moveStackToFront");
-        // After moving the stack to front, the previous focused should be the last focused.
-        assertTrue(stack.isFocusedStackOnDisplay());
-        assertEquals(prevFocusedStack, display.getLastFocusedStack());
-
-        stack.moveToBack("moveStackToBack", null /* task */);
-        // After moving the stack to back, the stack should be the last focused.
-        assertEquals(stack, display.getLastFocusedStack());
-    }
-
-    /**
-     * This test simulates the picture-in-picture menu activity launches an activity to fullscreen
-     * stack. The fullscreen stack should be the top focused for resuming correctly.
-     */
-    @Test
-    public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
-        // Create a pinned stack and move to front.
-        final ActivityStack pinnedStack = mSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final TaskRecord pinnedTask = new TaskBuilder(mService.mStackSupervisor)
-                .setStack(pinnedStack).build();
-        new ActivityBuilder(mService).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
-                .setTask(pinnedTask).build();
-        pinnedStack.moveToFront("movePinnedStackToFront");
-
-        // The focused stack should be the pinned stack.
-        assertTrue(pinnedStack.isFocusedStackOnDisplay());
-
-        // Create a fullscreen stack and move to front.
-        final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt(
-                mSupervisor.getDefaultDisplay());
-        fullscreenStack.moveToFront("moveFullscreenStackToFront");
-
-        // The focused stack should be the fullscreen stack.
-        assertTrue(fullscreenStack.isFocusedStackOnDisplay());
-    }
-
-    /**
-     * Test {@link ActivityDisplay#mPreferredTopFocusableStack} will be cleared when the stack is
-     * removed or moved to back, and the focused stack will be according to z-order.
-     */
-    @Test
-    public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
-        // Create a display which only contains 2 stacks.
-        final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
-        final ActivityStack stack1 = createFullscreenStackWithSimpleActivityAt(display);
-        final ActivityStack stack2 = createFullscreenStackWithSimpleActivityAt(display);
-
-        // Put stack1 and stack2 on top.
-        stack1.moveToFront("moveStack1ToFront");
-        stack2.moveToFront("moveStack2ToFront");
-        assertTrue(stack2.isFocusedStackOnDisplay());
-
-        // Stack1 should be focused after moving stack2 to back.
-        stack2.moveToBack("moveStack2ToBack", null /* task */);
-        assertTrue(stack1.isFocusedStackOnDisplay());
-
-        // Stack2 should be focused after removing stack1.
-        display.removeChild(stack1);
-        assertTrue(stack2.isFocusedStackOnDisplay());
-    }
-
-    private ActivityStack createFullscreenStackWithSimpleActivityAt(ActivityDisplay display) {
-        final ActivityStack fullscreenStack = display.createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final TaskRecord fullscreenTask = new TaskBuilder(mService.mStackSupervisor)
-                .setStack(fullscreenStack).build();
-        new ActivityBuilder(mService).setTask(fullscreenTask).build();
-        return fullscreenStack;
-    }
-
-    /**
-     * Verifies the correct activity is returned when querying the top running activity.
-     */
-    @Test
-    public void testTopRunningActivity() {
-        // Create stack to hold focus.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final ActivityStack stack = mSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(stack).build();
-
-        // Make sure the top running activity is not affected when keyguard is not locked.
-        assertTopRunningActivity(activity, display);
-
-        // Check to make sure activity not reported when it cannot show on lock and lock is on.
-        doReturn(true).when(keyguard).isKeyguardLocked();
-        assertEquals(activity, display.topRunningActivity());
-        assertNull(display.topRunningActivity(true /* considerKeyguardState */));
-
-        // Change focus to stack with activity.
-        stack.moveToFront("focusChangeToTestStack");
-        assertEquals(stack, display.getFocusedStack());
-        assertEquals(activity, display.topRunningActivity());
-        assertNull(display.topRunningActivity(true /* considerKeyguardState */));
-
-        // Add activity that should be shown on the keyguard.
-        final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .setStack(stack)
-                .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
-                .build();
-
-        // Ensure the show when locked activity is returned.
-        assertTopRunningActivity(showWhenLockedActivity, display);
-
-        // Change focus back to empty stack.
-        emptyStack.moveToFront("focusChangeToEmptyStack");
-        assertEquals(emptyStack, display.getFocusedStack());
-        // If there is no running activity in focused stack, the running activity in next focusable
-        // stack should be returned.
-        assertTopRunningActivity(showWhenLockedActivity, display);
-    }
-
-    private static void assertTopRunningActivity(ActivityRecord top, ActivityDisplay display) {
-        assertEquals(top, display.topRunningActivity());
-        assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index 8c27e25..e4fe599 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.am;
@@ -23,30 +23,18 @@
 import android.os.SystemClock;
 
 import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 /**
  * Test class for {@link ActivityManagerInternal}.
  *
- * To run the tests, use
- *
- * runtest -c com.android.server.am.ActivityManagerInternalTest frameworks-services
- *
- * or the following steps:
- *
- * Build: m FrameworksServicesTests
- * Install: adb install -r \
- *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -e class com.android.server.am.ActivityManagerInternalTest -w \
- *     com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:ActivityManagerInternalTest
  */
-@RunWith(AndroidJUnit4.class)
 public class ActivityManagerInternalTest {
     private static final int TEST_UID1 = 111;
     private static final int TEST_UID2 = 112;
@@ -59,6 +47,7 @@
 
     private ActivityManagerService mAms;
     private ActivityManagerInternal mAmi;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -147,18 +136,19 @@
 
         private final Object mLock;
         private Runnable mRunnable;
-        boolean mNotified;
+        public boolean mNotified;
 
-        public CustomThread(Object lock) {
+        CustomThread(Object lock) {
             mLock = lock;
         }
 
-        public CustomThread(Object lock, Runnable runnable) {
+        CustomThread(Object lock, Runnable runnable) {
             super(runnable);
             mLock = lock;
             mRunnable = runnable;
         }
 
+        @SuppressWarnings("WaitNotInLoop")
         @Override
         public void run() {
             if (mRunnable != null) {
@@ -168,7 +158,7 @@
                     try {
                         mLock.wait();
                     } catch (InterruptedException e) {
-                        Thread.currentThread().interrupted();
+                        Thread.interrupted();
                     }
                 }
             }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 349c0a37..767eb60 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.am;
@@ -67,14 +67,12 @@
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.AppOpsService;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -90,21 +88,11 @@
 /**
  * Test class for {@link ActivityManagerService}.
  *
- * To run the tests, use
- *
- * runtest -c com.android.server.am.ActivityManagerServiceTest frameworks-services
- *
- * or the following steps:
- *
- * Build: m FrameworksServicesTests
- * Install: adb install -r \
- *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -e class com.android.server.am.ActivityManagerServiceTest -w \
- *     com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:ActivityManagerServiceTest
  */
 @SmallTest
 @FlakyTest(bugId = 113616538)
-@RunWith(AndroidJUnit4.class)
 public class ActivityManagerServiceTest {
     private static final String TAG = ActivityManagerServiceTest.class.getSimpleName();
 
@@ -149,6 +137,7 @@
         mHandlerThread.quit();
     }
 
+    @SuppressWarnings("GuardedBy")
     @MediumTest
     @Test
     public void incrementProcStateSeqAndNotifyAppsLocked() throws Exception {
@@ -175,6 +164,7 @@
                 true); // expectNotify
 
         // Explicitly setting the seq counter for more verification.
+        // @SuppressWarnings("GuardedBy")
         mAms.mProcessList.mProcStateSeqCounter = 42;
 
         // Uid state is not moving from background to foreground or vice versa.
@@ -260,13 +250,14 @@
         uidRec.hasInternetPermission = true;
         mAms.mActiveUids.put(uid, uidRec);
 
-        final ProcessRecord appRec = new ProcessRecord(mAms, new ApplicationInfo(), TAG, uid, null);
+        final ProcessRecord appRec = new ProcessRecord(mAms, new ApplicationInfo(), TAG, uid);
         appRec.thread = Mockito.mock(IApplicationThread.class);
         mAms.mProcessList.mLruProcesses.add(appRec);
 
         return uidRec;
     }
 
+    @SuppressWarnings("GuardedBy")
     private void verifySeqCounterAndInteractions(UidRecord uidRec, int prevState, int curState,
             int expectedGlobalCounter, int expectedCurProcStateSeq, int expectedBlockState,
             boolean expectNotify) throws Exception {
@@ -277,6 +268,7 @@
         uidRec.setCurProcState(curState);
         mAms.incrementProcStateSeqAndNotifyAppsLocked();
 
+        // @SuppressWarnings("GuardedBy")
         assertEquals(expectedGlobalCounter, mAms.mProcessList.mProcStateSeqCounter);
         assertEquals(expectedCurProcStateSeq, uidRec.curProcStateSeq);
 
@@ -561,8 +553,8 @@
             ActivityManager.PROCESS_STATE_SERVICE,
             ActivityManager.PROCESS_STATE_RECEIVER
         };
-        final ArrayList<UidRecord.ChangeItem> pendingItemsForUids
-                = new ArrayList<>(changesForPendingItems.length);
+        final ArrayList<UidRecord.ChangeItem> pendingItemsForUids =
+                new ArrayList<>(changesForPendingItems.length);
         for (int i = 0; i < changesForPendingItems.length; ++i) {
             final UidRecord.ChangeItem item = new UidRecord.ChangeItem();
             item.uid = i;
@@ -621,7 +613,7 @@
         }
         mAms.mPendingUidChanges.addAll(pendingItemsForUids);
         mAms.dispatchUidsChanged();
-        assertEquals("validateUids should be empty, validateUids: " + mAms.mValidateUids,
+        assertEquals("validateUids should be empty, size=" + mAms.mValidateUids.size(),
                 0, mAms.mValidateUids.size());
     }
 
@@ -775,7 +767,7 @@
         mAms.mActiveUids.clear();
     }
 
-    private class TestHandler extends Handler {
+    private static class TestHandler extends Handler {
         private static final long WAIT_FOR_MSG_TIMEOUT_MS = 4000; // 4 sec
         private static final long WAIT_FOR_MSG_INTERVAL_MS = 400; // 0.4 sec
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index 2dfb375..8965152 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -27,33 +27,33 @@
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.filters.FlakyTest;
+
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.List;
 
-import androidx.test.filters.FlakyTest;
-
 /**
  * Tests for {@link ActivityManager}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.am.ActivityManagerTest
+ *  atest FrameworksServicesTests:ActivityManagerTest
  */
-@Presubmit
 @FlakyTest(detail = "Promote to presubmit if stable")
+@Presubmit
 public class ActivityManagerTest {
 
-    private IActivityManager service;
+    private IActivityManager mService;
 
     @Before
     public void setUp() throws Exception {
-        service = ActivityManager.getService();
+        mService = ActivityManager.getService();
     }
 
     @Test
     public void testTaskIdsForRunningUsers() throws RemoteException {
-        int[] runningUserIds = service.getRunningUserIds();
+        int[] runningUserIds = mService.getRunningUserIds();
         assertThat(runningUserIds).isNotEmpty();
         for (int userId : runningUserIds) {
             testTaskIdsForUser(userId);
@@ -61,7 +61,7 @@
     }
 
     private void testTaskIdsForUser(int userId) throws RemoteException {
-        List<?> recentTasks = service.getRecentTasks(100, 0, userId).getList();
+        List<?> recentTasks = mService.getRecentTasks(100, 0, userId).getList();
         if (recentTasks != null) {
             for (Object elem : recentTasks) {
                 assertThat(elem).isInstanceOf(RecentTaskInfo.class);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityOptionsTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityOptionsTest.java
deleted file mode 100644
index d15bff4..0000000
--- a/services/tests/servicestests/src/com/android/server/am/ActivityOptionsTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
-
-import static org.junit.Assert.assertEquals;
-
-import android.app.ActivityOptions;
-import android.os.Bundle;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * atest FrameworksServicesTests:ActivityOptionsTest
- */
-@MediumTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class ActivityOptionsTest {
-
-    @Test
-    public void testMerge_NoClobber() {
-        // Construct some options with set values
-        ActivityOptions opts = ActivityOptions.makeBasic();
-        opts.setLaunchDisplayId(Integer.MAX_VALUE);
-        opts.setLaunchActivityType(ACTIVITY_TYPE_STANDARD);
-        opts.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        opts.setAvoidMoveToFront();
-        opts.setLaunchTaskId(Integer.MAX_VALUE);
-        opts.setLockTaskEnabled(true);
-        opts.setRotationAnimationHint(ROTATION_ANIMATION_ROTATE);
-        opts.setTaskOverlay(true, true);
-        opts.setSplitScreenCreateMode(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
-        Bundle optsBundle = opts.toBundle();
-
-        // Try and merge the constructed options with a new set of options
-        optsBundle.putAll(ActivityOptions.makeBasic().toBundle());
-
-        // Ensure the set values are not clobbered
-        ActivityOptions restoredOpts = ActivityOptions.fromBundle(optsBundle);
-        assertEquals(Integer.MAX_VALUE, restoredOpts.getLaunchDisplayId());
-        assertEquals(ACTIVITY_TYPE_STANDARD, restoredOpts.getLaunchActivityType());
-        assertEquals(WINDOWING_MODE_FULLSCREEN, restoredOpts.getLaunchWindowingMode());
-        assertEquals(true, restoredOpts.getAvoidMoveToFront());
-        assertEquals(Integer.MAX_VALUE, restoredOpts.getLaunchTaskId());
-        assertEquals(true, restoredOpts.getLockTaskMode());
-        assertEquals(ROTATION_ANIMATION_ROTATE, restoredOpts.getRotationAnimationHint());
-        assertEquals(true, restoredOpts.getTaskOverlay());
-        assertEquals(true, restoredOpts.canTaskOverlayResume());
-        assertEquals(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
-                restoredOpts.getSplitScreenCreateMode());
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
deleted file mode 100644
index ffc7fa2..0000000
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
-
-import static junit.framework.TestCase.assertNotNull;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityOptions;
-import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.PauseActivityItem;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.util.MutableBoolean;
-
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
-
-/**
- * Tests for the {@link ActivityRecord} class.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.am.ActivityRecordTests
- */
-@MediumTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class ActivityRecordTests extends ActivityTestsBase {
-    private TestActivityStack mStack;
-    private TaskRecord mTask;
-    private ActivityRecord mActivity;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        setupActivityTaskManagerService();
-        mStack = mSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
-        mActivity = new ActivityBuilder(mService).setTask(mTask).build();
-    }
-
-    @Test
-    public void testStackCleanupOnClearingTask() throws Exception {
-        mActivity.setTask(null);
-        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
-    }
-
-    @Test
-    public void testStackCleanupOnActivityRemoval() throws Exception {
-        mTask.removeActivity(mActivity);
-        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(),  1);
-    }
-
-    @Test
-    public void testStackCleanupOnTaskRemoval() throws Exception {
-        mStack.removeTask(mTask, null /*reason*/, REMOVE_TASK_MODE_MOVING);
-        // Stack should be gone on task removal.
-        assertNull(mService.mStackSupervisor.getStack(mStack.mStackId));
-    }
-
-    @Test
-    public void testNoCleanupMovingActivityInSameStack() throws Exception {
-        final TaskRecord newTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack)
-                .build();
-        mActivity.reparent(newTask, 0, null /*reason*/);
-        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
-    }
-
-    @Test
-    public void testPausingWhenVisibleFromStopped() throws Exception {
-        final MutableBoolean pauseFound = new MutableBoolean(false);
-        doAnswer((InvocationOnMock invocationOnMock) -> {
-            final ClientTransaction transaction = invocationOnMock.getArgument(0);
-            if (transaction.getLifecycleStateRequest() instanceof PauseActivityItem) {
-                pauseFound.value = true;
-            }
-            return null;
-        }).when(mActivity.app.getThread()).scheduleTransaction(any());
-
-        mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
-
-        // The activity is in the focused stack so it should not move to paused.
-        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
-        assertTrue(mActivity.isState(STOPPED));
-        assertFalse(pauseFound.value);
-
-        // Clear focused stack
-        final ActivityDisplay display = mActivity.mStackSupervisor.getDefaultDisplay();
-        when(display.getFocusedStack()).thenReturn(null);
-
-        // In the unfocused stack, the activity should move to paused.
-        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
-        assertTrue(mActivity.isState(PAUSING));
-        assertTrue(pauseFound.value);
-
-        // Make sure that the state does not change for current non-stopping states.
-        mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped");
-
-        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
-
-        assertTrue(mActivity.isState(INITIALIZING));
-
-        // Make sure the state does not change if we are not the current top activity.
-        mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind");
-
-        // Make sure that the state does not change when we have an activity becoming translucent
-        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
-        mStack.mTranslucentActivityWaiting = topActivity;
-        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
-
-        assertTrue(mActivity.isState(STOPPED));
-    }
-
-    @Test
-    public void testPositionLimitedAspectRatioNavBarBottom() throws Exception {
-        verifyPositionWithLimitedAspectRatio(NAV_BAR_BOTTOM, new Rect(0, 0, 1000, 2000), 1.5f,
-                new Rect(0, 0, 1000, 1500));
-    }
-
-    @Test
-    public void testPositionLimitedAspectRatioNavBarLeft() throws Exception {
-        verifyPositionWithLimitedAspectRatio(NAV_BAR_LEFT, new Rect(0, 0, 2000, 1000), 1.5f,
-                new Rect(500, 0, 2000, 1000));
-    }
-
-    @Test
-    public void testPositionLimitedAspectRatioNavBarRight() throws Exception {
-        verifyPositionWithLimitedAspectRatio(NAV_BAR_RIGHT, new Rect(0, 0, 2000, 1000), 1.5f,
-                new Rect(0, 0, 1500, 1000));
-    }
-
-    private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds,
-            float aspectRatio, Rect expectedActivityBounds) {
-        // Verify with nav bar on the right.
-        when(mService.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition);
-        mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds);
-        mActivity.info.maxAspectRatio = aspectRatio;
-        mActivity.ensureActivityConfiguration(
-                0 /* globalChanges */, false /* preserveWindow */);
-        assertEquals(expectedActivityBounds, mActivity.getBounds());
-    }
-
-    @Test
-    public void testCanBeLaunchedOnDisplay() throws Exception {
-        mService.mSupportsMultiWindow = true;
-        final ActivityRecord activity = new ActivityBuilder(mService).build();
-
-        // An activity can be launched on default display.
-        assertTrue(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY));
-        // An activity cannot be launched on a non-existent display.
-        assertFalse(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY + 1));
-    }
-
-    @Test
-    public void testsApplyOptionsLocked() {
-        ActivityOptions activityOptions = ActivityOptions.makeBasic();
-
-        // Set and apply options for ActivityRecord. Pending options should be cleared
-        mActivity.updateOptionsLocked(activityOptions);
-        mActivity.applyOptionsLocked();
-        assertNull(mActivity.pendingOptions);
-
-        // Set options for two ActivityRecords in same Task. Apply one ActivityRecord options.
-        // Pending options should be cleared for both ActivityRecords
-        ActivityRecord activity2 = new ActivityBuilder(mService).setTask(mTask).build();
-        activity2.updateOptionsLocked(activityOptions);
-        mActivity.updateOptionsLocked(activityOptions);
-        mActivity.applyOptionsLocked();
-        assertNull(mActivity.pendingOptions);
-        assertNull(activity2.pendingOptions);
-
-        // Set options for two ActivityRecords in separate Tasks. Apply one ActivityRecord options.
-        // Pending options should be cleared for only ActivityRecord that was applied
-        TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
-        activity2 = new ActivityBuilder(mService).setTask(task2).build();
-        activity2.updateOptionsLocked(activityOptions);
-        mActivity.updateOptionsLocked(activityOptions);
-        mActivity.applyOptionsLocked();
-        assertNull(mActivity.pendingOptions);
-        assertNotNull(activity2.pendingOptions);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
deleted file mode 100644
index 2c993d3..0000000
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
-import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.contains;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.app.ActivityOptions;
-import android.app.WaitResult;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-
-/**
- * Tests for the {@link ActivityStackSupervisor} class.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.am.ActivityStackSupervisorTests
- */
-@MediumTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class ActivityStackSupervisorTests extends ActivityTestsBase {
-    private ActivityStack mFullscreenStack;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        setupActivityTaskManagerService();
-        mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-    }
-
-    /**
-     * This test ensures that we do not try to restore a task based off an invalid task id. We
-     * should expect {@code null} to be returned in this case.
-     */
-    @Test
-    public void testRestoringInvalidTask() throws Exception {
-        ((TestActivityDisplay) mSupervisor.getDefaultDisplay()).removeAllTasks();
-        TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/,
-                MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
-        assertNull(task);
-    }
-
-    /**
-     * This test ensures that an existing task in the pinned stack is moved to the fullscreen
-     * activity stack when a new task is added.
-     */
-    @Test
-    public void testReplacingTaskInPinnedStack() throws Exception {
-        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(mFullscreenStack).build();
-        final TaskRecord firstTask = firstActivity.getTask();
-
-        final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(mFullscreenStack).build();
-        final TaskRecord secondTask = secondActivity.getTask();
-
-        mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
-
-        // Ensure full screen stack has both tasks.
-        ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
-
-        // Move first activity to pinned stack.
-        final Rect sourceBounds = new Rect();
-        mSupervisor.moveActivityToPinnedStackLocked(firstActivity, sourceBounds,
-                0f /*aspectRatio*/, "initialMove");
-
-        final ActivityDisplay display = mFullscreenStack.getDisplay();
-        ActivityStack pinnedStack = display.getPinnedStack();
-        // Ensure a task has moved over.
-        ensureStackPlacement(pinnedStack, firstTask);
-        ensureStackPlacement(mFullscreenStack, secondTask);
-
-        // Move second activity to pinned stack.
-        mSupervisor.moveActivityToPinnedStackLocked(secondActivity, sourceBounds,
-                0f /*aspectRatio*/, "secondMove");
-
-        // Need to get stacks again as a new instance might have been created.
-        pinnedStack = display.getPinnedStack();
-        mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-        // Ensure stacks have swapped tasks.
-        ensureStackPlacement(pinnedStack, secondTask);
-        ensureStackPlacement(mFullscreenStack, firstTask);
-    }
-
-    private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
-        final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
-        assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
-
-        if (tasks == null) {
-            return;
-        }
-
-        for (TaskRecord task : tasks) {
-            assertTrue(stackTasks.contains(task));
-        }
-    }
-
-    /**
-     * Ensures that an activity is removed from the stopping activities list once it is resumed.
-     */
-    @Test
-    public void testStoppingActivityRemovedWhenResumed() throws Exception {
-        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(mFullscreenStack).build();
-        mSupervisor.mStoppingActivities.add(firstActivity);
-
-        firstActivity.completeResumeLocked();
-
-        assertFalse(mSupervisor.mStoppingActivities.contains(firstActivity));
-    }
-
-    /**
-     * Ensures that waiting results are notified of launches.
-     */
-    @Test
-    public void testReportWaitingActivityLaunchedIfNeeded() throws Exception {
-        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(mFullscreenStack).build();
-
-        // #notifyAll will be called on the ActivityManagerService. we must hold the object lock
-        // when this happens.
-        synchronized (mSupervisor.mService.mGlobalLock) {
-            final WaitResult taskToFrontWait = new WaitResult();
-            mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
-            mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT);
-
-            assertTrue(mSupervisor.mWaitingActivityLaunched.isEmpty());
-            assertEquals(taskToFrontWait.result, START_TASK_TO_FRONT);
-            assertEquals(taskToFrontWait.who, null);
-
-            final WaitResult deliverToTopWait = new WaitResult();
-            mSupervisor.mWaitingActivityLaunched.add(deliverToTopWait);
-            mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity,
-                    START_DELIVERED_TO_TOP);
-
-            assertTrue(mSupervisor.mWaitingActivityLaunched.isEmpty());
-            assertEquals(deliverToTopWait.result, START_DELIVERED_TO_TOP);
-            assertEquals(deliverToTopWait.who, firstActivity.realActivity);
-        }
-    }
-
-    @Test
-    public void testApplySleepTokensLocked() throws Exception {
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final ActivityStack stack = mock(ActivityStack.class);
-        display.addChild(stack, 0 /* position */);
-
-        // Make sure we wake and resume in the case the display is turning on and the keyguard is
-        // not showing.
-        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
-                false /* displayShouldSleep */, true /* isFocusedStack */,
-                false /* keyguardShowing */, true /* expectWakeFromSleep */,
-                true /* expectResumeTopActivity */);
-
-        // Make sure we wake and don't resume when the display is turning on and the keyguard is
-        // showing.
-        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
-                false /* displayShouldSleep */, true /* isFocusedStack */,
-                true /* keyguardShowing */, true /* expectWakeFromSleep */,
-                false /* expectResumeTopActivity */);
-
-        // Make sure we wake and don't resume when the display is turning on and the keyguard is
-        // not showing as unfocused.
-        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
-                false /* displayShouldSleep */, false /* isFocusedStack */,
-                false /* keyguardShowing */, true /* expectWakeFromSleep */,
-                false /* expectResumeTopActivity */);
-
-        // Should not do anything if the display state hasn't changed.
-        verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
-                false /* displayShouldSleep */, true /* isFocusedStack */,
-                false /* keyguardShowing */, false /* expectWakeFromSleep */,
-                false /* expectResumeTopActivity */);
-    }
-
-    private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
-            ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
-            boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
-            boolean expectResumeTopActivity) {
-        reset(stack);
-
-        doReturn(displayShouldSleep).when(display).shouldSleep();
-        doReturn(displaySleeping).when(display).isSleeping();
-        doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
-
-        doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
-        doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
-        mSupervisor.applySleepTokensLocked(true);
-        verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
-        verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
-                null /* target */, null /* targetOptions */);
-    }
-
-    /**
-     * Verifies that removal of activity with task and stack is done correctly.
-     */
-    @Test
-    public void testRemovingStackOnAppCrash() throws Exception {
-        final ActivityDisplay defaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
-        final int originalStackCount = defaultDisplay.getChildCount();
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(stack).build();
-
-        assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
-
-        // Let's pretend that the app has crashed.
-        firstActivity.app.setThread(null);
-        mService.mStackSupervisor.finishTopCrashedActivitiesLocked(firstActivity.app, "test");
-
-        // Verify that the stack was removed.
-        assertEquals(originalStackCount, defaultDisplay.getChildCount());
-    }
-
-    @Test
-    public void testFocusability() throws Exception {
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(stack).build();
-
-        // Under split screen primary we should be focusable when not minimized
-        mService.mStackSupervisor.setDockedStackMinimized(false);
-        assertTrue(stack.isFocusable());
-        assertTrue(activity.isFocusable());
-
-        // Under split screen primary we should not be focusable when minimized
-        mService.mStackSupervisor.setDockedStackMinimized(true);
-        assertFalse(stack.isFocusable());
-        assertFalse(activity.isFocusable());
-
-        final ActivityStack pinnedStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(pinnedStack).build();
-
-        // We should not be focusable when in pinned mode
-        assertFalse(pinnedStack.isFocusable());
-        assertFalse(pinnedActivity.isFocusable());
-
-        // Add flag forcing focusability.
-        pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
-
-        // We should not be focusable when in pinned mode
-        assertTrue(pinnedStack.isFocusable());
-        assertTrue(pinnedActivity.isFocusable());
-
-        // Without the overridding activity, stack should not be focusable.
-        pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability",
-                REMOVE_TASK_MODE_DESTROYING);
-        assertFalse(pinnedStack.isFocusable());
-    }
-
-    /**
-     * Verify that split-screen primary stack will be chosen if activity is launched that targets
-     * split-screen secondary, but a matching existing instance is found on top of split-screen
-     * primary stack.
-     */
-    @Test
-    public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() throws Exception {
-        // Create primary split-screen stack with a task and an activity.
-        final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
-                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
-                        true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
-        final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
-
-        // Find a launch stack for the top activity in split-screen primary, while requesting
-        // split-screen secondary.
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
-        final ActivityStack result = mSupervisor.getLaunchStack(r, options, task, true /* onTop */);
-
-        // Assert that the primary stack is returned.
-        assertEquals(primaryStack, result);
-    }
-
-    /**
-     * Verify split-screen primary stack & task can resized by
-     * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
-     */
-    @Test
-    public void testResizeDockedStackForSplitScreenPrimary() throws Exception {
-        final Rect TASK_SIZE = new Rect(0, 0, 600, 600);
-        final Rect STACK_SIZE = new Rect(0, 0, 300, 300);
-
-        // Create primary split-screen stack with a task.
-        final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
-                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
-                        true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
-
-        // Resize dock stack.
-        mService.resizeDockedStack(STACK_SIZE, TASK_SIZE, null, null, null);
-
-        // Verify dock stack & its task bounds if is equal as resized result.
-        assertEquals(primaryStack.getBounds(), STACK_SIZE);
-        assertEquals(task.getBounds(), TASK_SIZE);
-    }
-
-    /**
-     * Verify that home stack would be moved to front when the top activity is Recents.
-     */
-    @Test
-    public void testFindTaskToMoveToFrontWhenRecentsOnTop() throws Exception {
-        // Create stack/task on default display.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
-
-        // Create Recents on top of the display.
-        final ActivityStack stack = display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
-        new ActivityBuilder(mService).setTask(task).build();
-
-        final String reason = "findTaskToMoveToFront";
-        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
-                false);
-
-        verify(display).moveHomeStackToFront(contains(reason));
-    }
-
-    /**
-     * Verify that home stack won't be moved to front if the top activity on other display is
-     * Recents.
-     */
-    @Test
-    public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() throws Exception {
-        // Create stack/task on default display.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
-
-        // Create Recents on secondary display.
-        final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
-                ActivityDisplay.POSITION_TOP);
-        final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
-        new ActivityBuilder(mService).setTask(task).build();
-
-        final String reason = "findTaskToMoveToFront";
-        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
-                false);
-
-        verify(display, never()).moveHomeStackToFront(contains(reason));
-    }
-
-    /**
-     * Verify if a stack is not at the topmost position, it should be able to resume its activity if
-     * the stack is the top focused.
-     */
-    @Test
-    public void testResumeActivityWhenNonTopmostStackIsTopFocused() throws Exception {
-        // Create a stack at bottom.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, false /* onTop */));
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
-        final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
-        display.positionChildAtBottom(targetStack);
-
-        // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
-        // is the current top focused stack.
-        assertFalse(targetStack.isTopStackOnDisplay());
-        doReturn(targetStack).when(mSupervisor).getTopDisplayFocusedStack();
-
-        // Use the stack as target to resume.
-        mSupervisor.resumeFocusedStacksTopActivitiesLocked(
-                targetStack, activity, null /* targetOptions */);
-
-        // Verify the target stack should resume its activity.
-        verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
-                eq(activity), eq(null /* targetOptions */));
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
deleted file mode 100644
index 53f67af..0000000
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ /dev/null
@@ -1,777 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
-import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-import android.content.pm.ActivityInfo;
-import android.os.UserHandle;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link ActivityStack} class.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.am.ActivityStackTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class ActivityStackTests extends ActivityTestsBase {
-    private ActivityDisplay mDefaultDisplay;
-    private ActivityStack mStack;
-    private TaskRecord mTask;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        setupActivityTaskManagerService();
-        mDefaultDisplay = mSupervisor.getDefaultDisplay();
-        mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */));
-        mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
-    }
-
-    @Test
-    public void testEmptyTaskCleanupOnRemove() throws Exception {
-        assertNotNull(mTask.getWindowContainerController());
-        mStack.removeTask(mTask, "testEmptyTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
-        assertNull(mTask.getWindowContainerController());
-    }
-
-    @Test
-    public void testOccupiedTaskCleanupOnRemove() throws Exception {
-        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
-        assertNotNull(mTask.getWindowContainerController());
-        mStack.removeTask(mTask, "testOccupiedTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
-        assertNotNull(mTask.getWindowContainerController());
-    }
-
-    @Test
-    public void testResumedActivity() throws Exception {
-        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
-        assertEquals(mStack.getResumedActivity(), null);
-        r.setState(RESUMED, "testResumedActivity");
-        assertEquals(mStack.getResumedActivity(), r);
-        r.setState(PAUSING, "testResumedActivity");
-        assertEquals(mStack.getResumedActivity(), null);
-    }
-
-    @Test
-    public void testResumedActivityFromTaskReparenting() {
-        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
-        // Ensure moving task between two stacks updates resumed activity
-        r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
-        assertEquals(mStack.getResumedActivity(), r);
-
-        final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        mTask.reparent(destStack, true /* toTop */, TaskRecord.REPARENT_KEEP_STACK_AT_FRONT,
-                false /* animate */, true /* deferResume*/,
-                "testResumedActivityFromTaskReparenting");
-
-        assertEquals(mStack.getResumedActivity(), null);
-        assertEquals(destStack.getResumedActivity(), r);
-    }
-
-    @Test
-    public void testResumedActivityFromActivityReparenting() {
-        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
-        // Ensure moving task between two stacks updates resumed activity
-        r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
-        assertEquals(mStack.getResumedActivity(), r);
-
-        final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();
-
-        mTask.removeActivity(r);
-        destTask.addActivityToTop(r);
-
-        assertEquals(mStack.getResumedActivity(), null);
-        assertEquals(destStack.getResumedActivity(), r);
-    }
-
-    @Test
-    public void testPrimarySplitScreenRestoresWhenMovedToBack() throws Exception {
-        // Create primary splitscreen stack. This will create secondary stacks and places the
-        // existing fullscreen stack on the bottom.
-        final ActivityStack primarySplitScreen = mDefaultDisplay.createStack(
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        // Assert windowing mode.
-        assertEquals(primarySplitScreen.getWindowingMode(), WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-
-        // Move primary to back.
-        primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack",
-                null /* task */);
-
-        // Assert that stack is at the bottom.
-        assertEquals(mDefaultDisplay.getIndexOf(primarySplitScreen), 0);
-
-        // Ensure no longer in splitscreen.
-        assertEquals(primarySplitScreen.getWindowingMode(), WINDOWING_MODE_FULLSCREEN);
-
-        // Ensure that the override mode is restored to undefined
-        assertEquals(primarySplitScreen.getOverrideWindowingMode(), WINDOWING_MODE_UNDEFINED);
-    }
-
-    @Test
-    public void testPrimarySplitScreenRestoresPreviousWhenMovedToBack() throws Exception {
-        // This time, start with a fullscreen activitystack
-        final ActivityStack primarySplitScreen = mDefaultDisplay.createStack(
-            WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        primarySplitScreen.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-
-        // Assert windowing mode.
-        assertEquals(primarySplitScreen.getWindowingMode(), WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-
-        // Move primary to back.
-        primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack",
-            null /* task */);
-
-        // Assert that stack is at the bottom.
-        assertEquals(mDefaultDisplay.getIndexOf(primarySplitScreen), 0);
-
-        // Ensure that the override mode is restored to what it was (fullscreen)
-        assertEquals(primarySplitScreen.getOverrideWindowingMode(), WINDOWING_MODE_FULLSCREEN);
-    }
-
-    @Test
-    public void testStackInheritsDisplayWindowingMode() throws Exception {
-        final ActivityStack primarySplitScreen = mDefaultDisplay.createStack(
-            WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
-        assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
-
-        mDefaultDisplay.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertEquals(WINDOWING_MODE_FREEFORM, primarySplitScreen.getWindowingMode());
-        assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
-    }
-
-    @Test
-    public void testStackOverridesDisplayWindowingMode() throws Exception {
-        final ActivityStack primarySplitScreen = mDefaultDisplay.createStack(
-            WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
-        assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
-
-        primarySplitScreen.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        // setting windowing mode should still work even though resolved mode is already fullscreen
-        assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getOverrideWindowingMode());
-
-        mDefaultDisplay.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
-    }
-
-    @Test
-    public void testStopActivityWhenActivityDestroyed() throws Exception {
-        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
-        r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
-        mStack.moveToFront("testStopActivityWithDestroy");
-        mStack.stopActivityLocked(r);
-        // Mostly testing to make sure there is a crash in the call part, so if we get here we are
-        // good-to-go!
-    }
-
-    @Test
-    public void testFindTaskWithOverlay() throws Exception {
-        final ActivityRecord r = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .setStack(mStack)
-                .setUid(0)
-                .build();
-        final TaskRecord task = r.getTask();
-        // Overlay must be for a different user to prevent recognizing a matching top activity
-        final ActivityRecord taskOverlay = new ActivityBuilder(mService).setTask(task)
-                .setUid(UserHandle.PER_USER_RANGE * 2).build();
-        taskOverlay.mTaskOverlay = true;
-
-        final ActivityStackSupervisor.FindTaskResult result =
-                new ActivityStackSupervisor.FindTaskResult();
-        mStack.findTaskLocked(r, result);
-
-        assertEquals(task.getTopActivity(false /* includeOverlays */), r);
-        assertEquals(task.getTopActivity(true /* includeOverlays */), taskOverlay);
-        assertNotNull(result.mRecord);
-    }
-
-    @Test
-    public void testShouldBeVisible_Fullscreen() throws Exception {
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        assertTrue(homeStack.shouldBeVisible(null /* starting */));
-        assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
-
-        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        // Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack
-        // should be visible since it is always on-top.
-        fullscreenStack.setIsTranslucent(false);
-        assertFalse(homeStack.shouldBeVisible(null /* starting */));
-        assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
-        assertTrue(fullscreenStack.shouldBeVisible(null /* starting */));
-
-        // Home stack should be visible behind a translucent fullscreen stack.
-        fullscreenStack.setIsTranslucent(true);
-        assertTrue(homeStack.shouldBeVisible(null /* starting */));
-        assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
-    }
-
-    @Test
-    public void testShouldBeVisible_SplitScreen() throws Exception {
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        // Home stack should always be fullscreen for this test.
-        homeStack.setSupportsSplitScreen(false);
-        final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        // Home stack shouldn't be visible if both halves of split-screen are opaque.
-        splitScreenPrimary.setIsTranslucent(false);
-        splitScreenSecondary.setIsTranslucent(false);
-        assertFalse(homeStack.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
-
-        // Home stack should be visible if one of the halves of split-screen is translucent.
-        splitScreenPrimary.setIsTranslucent(true);
-        assertTrue(homeStack.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
-
-        final TestActivityStack splitScreenSecondary2 = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        // First split-screen secondary shouldn't be visible behind another opaque split-split
-        // secondary.
-        splitScreenSecondary2.setIsTranslucent(false);
-        assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
-
-        // First split-screen secondary should be visible behind another translucent split-screen
-        // secondary.
-        splitScreenSecondary2.setIsTranslucent(true);
-        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
-
-        final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
-
-        // Split-screen stacks shouldn't be visible behind an opaque fullscreen stack.
-        assistantStack.setIsTranslucent(false);
-        assertTrue(assistantStack.shouldBeVisible(null /* starting */));
-        assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
-        assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
-        assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
-
-        // Split-screen stacks should be visible behind a translucent fullscreen stack.
-        assistantStack.setIsTranslucent(true);
-        assertTrue(assistantStack.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
-
-        // Assistant stack shouldn't be visible behind translucent split-screen stack
-        assistantStack.setIsTranslucent(false);
-        splitScreenPrimary.setIsTranslucent(true);
-        splitScreenSecondary2.setIsTranslucent(true);
-        splitScreenSecondary2.moveToFront("testShouldBeVisible_SplitScreen");
-        splitScreenPrimary.moveToFront("testShouldBeVisible_SplitScreen");
-        assertFalse(assistantStack.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
-    }
-
-    @Test
-    public void testShouldBeVisible_Finishing() throws Exception {
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack translucentStack = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        translucentStack.setIsTranslucent(true);
-
-        assertTrue(homeStack.shouldBeVisible(null /* starting */));
-        assertTrue(translucentStack.shouldBeVisible(null /* starting */));
-
-        final ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
-        topRunningHomeActivity.finishing = true;
-        final ActivityRecord topRunningTranslucentActivity =
-                translucentStack.topRunningActivityLocked();
-        topRunningTranslucentActivity.finishing = true;
-
-        // Home shouldn't be visible since its activity is marked as finishing and it isn't the top
-        // of the stack list.
-        assertFalse(homeStack.shouldBeVisible(null /* starting */));
-        // Home should be visible if we are starting an activity within it.
-        assertTrue(homeStack.shouldBeVisible(topRunningHomeActivity /* starting */));
-        // The translucent stack should be visible since it is the top of the stack list even though
-        // it has its activity marked as finishing.
-        assertTrue(translucentStack.shouldBeVisible(null /* starting */));
-    }
-
-    @Test
-    public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
-        mDefaultDisplay.removeChild(mStack);
-
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        homeStack.setIsTranslucent(false);
-        fullscreenStack.setIsTranslucent(false);
-
-        // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
-        int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
-        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == fullscreenStack);
-        mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
-        assertTrue(mDefaultDisplay.getIndexOf(homeStack) == homeStackIndex);
-    }
-
-    @Test
-    public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
-        mDefaultDisplay.removeChild(mStack);
-
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        homeStack.setIsTranslucent(false);
-        fullscreenStack.setIsTranslucent(true);
-
-        // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
-        int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
-        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == fullscreenStack);
-        mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
-        assertTrue(mDefaultDisplay.getIndexOf(homeStack) == homeStackIndex);
-    }
-
-    @Test
-    public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
-        mDefaultDisplay.removeChild(mStack);
-
-        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-
-        homeStack.setIsTranslucent(false);
-        fullscreenStack.setIsTranslucent(false);
-
-        // Ensure we don't move the home stack if it is already on top
-        int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
-        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == null);
-        mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
-        assertTrue(mDefaultDisplay.getIndexOf(homeStack) == homeStackIndex);
-    }
-
-    @Test
-    public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
-        mDefaultDisplay.removeChild(mStack);
-
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final TestActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        homeStack.setIsTranslucent(false);
-        fullscreenStack1.setIsTranslucent(false);
-        fullscreenStack2.setIsTranslucent(false);
-
-        // Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the
-        // pinned stack
-        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == fullscreenStack1);
-        mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
-        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == fullscreenStack2);
-    }
-
-    @Test
-    public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
-        mDefaultDisplay.removeChild(mStack);
-
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-
-        homeStack.setIsTranslucent(false);
-        fullscreenStack1.setIsTranslucent(false);
-        fullscreenStack2.setIsTranslucent(true);
-
-        // Ensure that we move the home stack behind the bottom most non-translucent fullscreen
-        // stack
-        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == fullscreenStack1);
-        mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
-        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == fullscreenStack1);
-    }
-
-    @Test
-    public void testMoveHomeStackBehindStack_BehindHomeStack() {
-        mDefaultDisplay.removeChild(mStack);
-
-        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-
-        homeStack.setIsTranslucent(false);
-        fullscreenStack1.setIsTranslucent(false);
-        fullscreenStack2.setIsTranslucent(false);
-
-        // Ensure we don't move the home stack behind itself
-        int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
-        mDefaultDisplay.moveStackBehindStack(homeStack, homeStack);
-        assertTrue(mDefaultDisplay.getIndexOf(homeStack) == homeStackIndex);
-    }
-
-    @Test
-    public void testMoveHomeStackBehindStack() {
-        mDefaultDisplay.removeChild(mStack);
-
-        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final TestActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final TestActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-
-        mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack1);
-        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == fullscreenStack1);
-        mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack2);
-        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == fullscreenStack2);
-        mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack4);
-        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == fullscreenStack4);
-        mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack2);
-        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == fullscreenStack2);
-    }
-
-    @Test
-    public void testSetAlwaysOnTop() {
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == pinnedStack);
-
-        final TestActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        alwaysOnTopStack.setAlwaysOnTop(true);
-        assertTrue(alwaysOnTopStack.isAlwaysOnTop());
-        // Ensure (non-pinned) always on top stack is put below pinned stack.
-        assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack) == pinnedStack);
-
-        final TestActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        // Ensure non always on top stack is put below always on top stacks.
-        assertTrue(mDefaultDisplay.getStackAbove(nonAlwaysOnTopStack) == alwaysOnTopStack);
-
-        final TestActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        alwaysOnTopStack2.setAlwaysOnTop(true);
-        assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
-        // Ensure newly created always on top stack is placed above other all always on top stacks.
-        assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == pinnedStack);
-
-        alwaysOnTopStack2.setAlwaysOnTop(false);
-        // Ensure, when always on top is turned off for a stack, the stack is put just below all
-        // other always on top stacks.
-        assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == alwaysOnTopStack);
-        alwaysOnTopStack2.setAlwaysOnTop(true);
-
-        // Ensure always on top state changes properly when windowing mode changes.
-        alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        assertFalse(alwaysOnTopStack2.isAlwaysOnTop());
-        assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == alwaysOnTopStack);
-        alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
-        assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == pinnedStack);
-    }
-
-    @Test
-    public void testSplitScreenMoveToFront() throws Exception {
-        final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(
-                mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-        final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
-
-        splitScreenPrimary.setIsTranslucent(false);
-        splitScreenSecondary.setIsTranslucent(false);
-        assistantStack.setIsTranslucent(false);
-
-        assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
-        assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
-        assertTrue(assistantStack.shouldBeVisible(null /* starting */));
-
-        splitScreenSecondary.moveToFront("testSplitScreenMoveToFront");
-
-        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
-        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
-        assertFalse(assistantStack.shouldBeVisible(null /* starting */));
-    }
-
-    private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
-            ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
-        final T stack;
-        if (activityType == ACTIVITY_TYPE_HOME) {
-            // Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
-            stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
-            if (onTop) {
-                mDefaultDisplay.positionChildAtTop(stack, false /* includingParents */);
-            } else {
-                mDefaultDisplay.positionChildAtBottom(stack);
-            }
-        } else {
-            stack = display.createStack(windowingMode, activityType, onTop);
-            final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
-                    .setCreateTask(true).build();
-        }
-        return stack;
-    }
-
-    @Test
-    public void testFinishDisabledPackageActivities() throws Exception {
-        final ActivityRecord firstActivity = new ActivityBuilder(mService).setTask(mTask).build();
-        final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(mTask).build();
-
-        // Making the second activity a task overlay without an app means it will be removed from
-        // the task's activities as well once first activity is removed.
-        secondActivity.mTaskOverlay = true;
-        secondActivity.app = null;
-
-        assertEquals(mTask.mActivities.size(), 2);
-
-        mStack.finishDisabledPackageActivitiesLocked(firstActivity.packageName, null,
-                true /* doit */, true /* evenPersistent */, UserHandle.USER_ALL);
-
-        assertTrue(mTask.mActivities.isEmpty());
-        assertTrue(mStack.getAllTasks().isEmpty());
-    }
-
-    @Test
-    public void testHandleAppDied() throws Exception {
-        final ActivityRecord firstActivity = new ActivityBuilder(mService).setTask(mTask).build();
-        final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(mTask).build();
-
-        // Making the first activity a task overlay means it will be removed from the task's
-        // activities as well once second activity is removed as handleAppDied processes the
-        // activity list in reverse.
-        firstActivity.mTaskOverlay = true;
-        firstActivity.app = null;
-
-        // second activity will be immediately removed as it has no state.
-        secondActivity.haveState = false;
-
-        assertEquals(mTask.mActivities.size(), 2);
-
-        mStack.handleAppDiedLocked(secondActivity.app);
-
-        assertTrue(mTask.mActivities.isEmpty());
-        assertTrue(mStack.getAllTasks().isEmpty());
-    }
-
-    @Test
-    public void testFinishCurrentActivity() {
-        // Create 2 activities on a new display.
-        final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
-        final ActivityStack stack1 = createStackForShouldBeVisibleTest(display,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityStack stack2 = createStackForShouldBeVisibleTest(display,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        // There is still an activity1 in stack1 so the activity2 should be added to finishing list
-        // that will be destroyed until idle.
-        final ActivityRecord activity2 = finishCurrentActivity(stack2);
-        assertEquals(FINISHING, activity2.getState());
-        assertTrue(mSupervisor.mFinishingActivities.contains(activity2));
-
-        // The display becomes empty. Since there is no next activity to be idle, the activity
-        // should be destroyed immediately with updating configuration to restore original state.
-        final ActivityRecord activity1 = finishCurrentActivity(stack1);
-        assertEquals(DESTROYING, activity1.getState());
-        verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */,
-                eq(display.mDisplayId), anyBoolean(), anyBoolean());
-    }
-
-    private ActivityRecord finishCurrentActivity(ActivityStack stack) {
-        final ActivityRecord activity = stack.topRunningActivityLocked();
-        assertNotNull(activity);
-        activity.setState(PAUSED, "finishCurrentActivity");
-        activity.makeFinishingLocked();
-        stack.finishCurrentActivityLocked(activity, ActivityStack.FINISH_AFTER_VISIBLE,
-                false /* oomAdj */, "finishCurrentActivity");
-        return activity;
-    }
-
-    @Test
-    public void testShouldSleepActivities() throws Exception {
-        // When focused activity and keyguard is going away, we should not sleep regardless
-        // of the display state
-        verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
-                true /* displaySleeping */, false /* expected*/);
-
-        // When not the focused stack, defer to display sleeping state.
-        verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/,
-                true /* displaySleeping */, true /* expected*/);
-
-        // If keyguard is going away, defer to the display sleeping state.
-        verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
-                true /* displaySleeping */, true /* expected*/);
-        verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
-                false /* displaySleeping */, false /* expected*/);
-    }
-
-    @Test
-    public void testStackOrderChangedOnRemoveStack() throws Exception {
-        StackOrderChangedListener listener = new StackOrderChangedListener();
-        mDefaultDisplay.registerStackOrderChangedListener(listener);
-        try {
-            mDefaultDisplay.removeChild(mStack);
-        } finally {
-            mDefaultDisplay.unregisterStackOrderChangedListener(listener);
-        }
-        assertTrue(listener.changed);
-    }
-
-    @Test
-    public void testStackOrderChangedOnAddPositionStack() throws Exception {
-        mDefaultDisplay.removeChild(mStack);
-
-        StackOrderChangedListener listener = new StackOrderChangedListener();
-        mDefaultDisplay.registerStackOrderChangedListener(listener);
-        try {
-            mDefaultDisplay.addChild(mStack, 0);
-        } finally {
-            mDefaultDisplay.unregisterStackOrderChangedListener(listener);
-        }
-        assertTrue(listener.changed);
-    }
-
-    @Test
-    public void testStackOrderChangedOnPositionStack() throws Exception {
-        StackOrderChangedListener listener = new StackOrderChangedListener();
-        try {
-            final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
-                    mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                    true /* onTop */);
-            mDefaultDisplay.registerStackOrderChangedListener(listener);
-            mDefaultDisplay.positionChildAtBottom(fullscreenStack1);
-        } finally {
-            mDefaultDisplay.unregisterStackOrderChangedListener(listener);
-        }
-        assertTrue(listener.changed);
-    }
-
-    private void verifyShouldSleepActivities(boolean focusedStack,
-            boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
-        final ActivityDisplay display = mock(ActivityDisplay.class);
-        final KeyguardController keyguardController = mSupervisor.getKeyguardController();
-
-        doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
-        doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
-        doReturn(displaySleeping).when(display).isSleeping();
-        doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay();
-
-        assertEquals(expected, mStack.shouldSleepActivities());
-    }
-
-    private class StackOrderChangedListener implements ActivityDisplay.OnStackOrderChangedListener {
-        boolean changed = false;
-
-        @Override
-        public void onStackOrderChanged() {
-            changed = true;
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
deleted file mode 100644
index 9192d6b..0000000
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.app.IApplicationThread;
-import android.content.Intent;
-import android.os.UserHandle;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
-import com.android.server.am.ActivityStarter.Factory;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Random;
-
-/**
- * Tests for the {@link ActivityStartController} class.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:ActivityStartControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class ActivityStartControllerTests extends ActivityTestsBase {
-    private ActivityTaskManagerService mService;
-    private ActivityStartController mController;
-    private Factory mFactory;
-    private ActivityStarter mStarter;
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        mService = createActivityTaskManagerService();
-        mFactory = mock(Factory.class);
-        mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory);
-        mStarter = spy(new ActivityStarter(mController, mService,
-                mService.mStackSupervisor, mock(ActivityStartInterceptor.class)));
-        doReturn(mStarter).when(mFactory).obtain();
-    }
-
-    /**
-     * Ensures that pending launches are processed.
-     */
-    @Test
-    public void testPendingActivityLaunches() {
-        final Random random = new Random();
-
-        final ActivityRecord activity = new ActivityBuilder(mService).build();
-        final ActivityRecord source = new ActivityBuilder(mService).build();
-        final int startFlags = random.nextInt();
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final WindowProcessController wpc = new WindowProcessController(mService,
-                mService.mContext.getApplicationInfo(), "name", 12345,
-                UserHandle.getUserId(12345), mock(Object.class),
-                mock(WindowProcessListener.class), null);
-        wpc.setThread(mock(IApplicationThread.class));
-
-        mController.addPendingActivityLaunch(
-                new PendingActivityLaunch(activity, source, startFlags, stack, wpc));
-        final boolean resume = random.nextBoolean();
-        mController.doPendingActivityLaunches(resume);
-
-        verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null),
-                eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null));
-    }
-
-
-    /**
-     * Ensures instances are recycled after execution.
-     */
-    @Test
-    public void testRecycling() throws Exception {
-        final Intent intent = new Intent();
-        final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
-                mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
-        optionStarter
-                .setIntent(intent)
-                .setReason("Test")
-                .execute();
-        verify(mFactory, times(1)).recycle(eq(optionStarter));
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
deleted file mode 100644
index 270d394..0000000
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright 2017, 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.server.am;
-
-import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
-
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManagerInternal;
-import android.app.KeyguardManager;
-import android.app.admin.DevicePolicyManagerInternal;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.SuspendDialogInfo;
-import android.content.pm.UserInfo;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
-import android.testing.DexmakerShareClassLoaderRule;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.app.HarmfulAppWarningActivity;
-import com.android.internal.app.SuspendedAppActivity;
-import com.android.internal.app.UnlaunchableAppActivity;
-import com.android.server.LocalServices;
-import com.android.server.pm.PackageManagerService;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Unit tests for {@link ActivityStartInterceptorTest}.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.am.ActivityStartInterceptorTest
- */
-@Presubmit
-@SmallTest
-public class ActivityStartInterceptorTest {
-    private static final int TEST_USER_ID = 1;
-    private static final int TEST_REAL_CALLING_UID = 2;
-    private static final int TEST_REAL_CALLING_PID = 3;
-    private static final String TEST_CALLING_PACKAGE = "com.test.caller";
-    private static final int TEST_START_FLAGS = 4;
-    private static final Intent ADMIN_SUPPORT_INTENT =
-            new Intent("com.test.ADMIN_SUPPORT");
-    private static final Intent CONFIRM_CREDENTIALS_INTENT =
-            new Intent("com.test.CONFIRM_CREDENTIALS");
-    private static final UserInfo PARENT_USER_INFO = new UserInfo(0 /* userId */, "parent",
-            0 /* flags */);
-    private static final String TEST_PACKAGE_NAME = "com.test.package";
-
-    @Rule
-    public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
-            new DexmakerShareClassLoaderRule();
-
-    @Mock
-    private Context mContext;
-    @Mock
-    private ActivityManagerService mAm;
-    @Mock
-    private ActivityTaskManagerService mService;
-    @Mock
-    private ActivityStackSupervisor mSupervisor;
-    @Mock
-    private DevicePolicyManagerInternal mDevicePolicyManager;
-    @Mock
-    private PackageManagerInternal mPackageManagerInternal;
-    @Mock
-    private UserManager mUserManager;
-    @Mock
-    private KeyguardManager mKeyguardManager;
-    @Mock
-    private PackageManagerService mPackageManager;
-    @Mock
-    private ActivityManagerInternal mAmInternal;
-
-    private ActivityStartInterceptor mInterceptor;
-    private ActivityInfo mAInfo = new ActivityInfo();
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mService.mAmInternal = mAmInternal;
-        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext);
-        mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
-                TEST_START_FLAGS, TEST_CALLING_PACKAGE);
-
-        // Mock ActivityManagerInternal
-        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
-        LocalServices.addService(ActivityManagerInternal.class, mAmInternal);
-
-        // Mock DevicePolicyManagerInternal
-        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
-        LocalServices.addService(DevicePolicyManagerInternal.class,
-                mDevicePolicyManager);
-        when(mDevicePolicyManager.createShowAdminSupportIntent(TEST_USER_ID, true))
-                .thenReturn(ADMIN_SUPPORT_INTENT);
-        when(mService.getPackageManagerInternalLocked()).thenReturn(mPackageManagerInternal);
-
-        // Mock UserManager
-        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
-        when(mUserManager.getProfileParent(TEST_USER_ID)).thenReturn(PARENT_USER_INFO);
-
-        // Mock KeyguardManager
-        when(mContext.getSystemService(Context.KEYGUARD_SERVICE)).thenReturn(mKeyguardManager);
-        when(mKeyguardManager.createConfirmDeviceCredentialIntent(
-                nullable(CharSequence.class), nullable(CharSequence.class), eq(TEST_USER_ID))).
-                thenReturn(CONFIRM_CREDENTIALS_INTENT);
-
-        // Mock PackageManager
-        when(mService.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
-                .thenReturn(null);
-
-        // Initialise activity info
-        mAInfo.applicationInfo = new ApplicationInfo();
-        mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
-    }
-
-    @Test
-    public void testSuspendedByAdminPackage() {
-        // GIVEN the package we're about to launch is currently suspended
-        mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
-
-        when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
-                .thenReturn(PLATFORM_PACKAGE_NAME);
-
-        // THEN calling intercept returns true
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
-
-        // THEN the returned intent is the admin support intent
-        assertEquals(ADMIN_SUPPORT_INTENT, mInterceptor.mIntent);
-    }
-
-    @Test
-    public void testSuspendedPackage() {
-        mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
-        final String suspendingPackage = "com.test.suspending.package";
-        final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
-                .setMessage("Test Message")
-                .setIcon(0x11110001)
-                .build();
-        when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
-                .thenReturn(suspendingPackage);
-        when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, TEST_USER_ID))
-                .thenReturn(dialogInfo);
-        // THEN calling intercept returns true
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
-
-        // Check intent parameters
-        assertEquals(dialogInfo,
-                mInterceptor.mIntent.getParcelableExtra(SuspendedAppActivity.EXTRA_DIALOG_INFO));
-        assertEquals(suspendingPackage,
-                mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_SUSPENDING_PACKAGE));
-        assertEquals(TEST_PACKAGE_NAME,
-                mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_SUSPENDED_PACKAGE));
-        assertEquals(TEST_USER_ID, mInterceptor.mIntent.getIntExtra(Intent.EXTRA_USER_ID, -1000));
-    }
-
-    @Test
-    public void testInterceptQuietProfile() {
-        // GIVEN that the user the activity is starting as is currently in quiet mode
-        when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
-
-        // THEN calling intercept returns true
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
-
-        // THEN the returned intent is the quiet mode intent
-        assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
-                .filterEquals(mInterceptor.mIntent));
-    }
-
-    @Test
-    public void testWorkChallenge() {
-        // GIVEN that the user the activity is starting as is currently locked
-        when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
-
-        // THEN calling intercept returns true
-        mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null);
-
-        // THEN the returned intent is the quiet mode intent
-        assertTrue(CONFIRM_CREDENTIALS_INTENT.filterEquals(mInterceptor.mIntent));
-    }
-
-    @Test
-    public void testHarmfulAppWarning() {
-        // GIVEN the package we're about to launch has a harmful app warning set
-        when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
-                .thenReturn("This app is bad");
-
-        // THEN calling intercept returns true
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
-
-        // THEN the returned intent is the harmful app warning intent
-        assertTrue(mInterceptor.mIntent.getComponent().getClassName().equals(
-                HarmfulAppWarningActivity.class.getName()));
-    }
-
-    @Test
-    public void testNoInterception() {
-        // GIVEN that none of the interception conditions are met
-
-        // THEN calling intercept returns false
-        assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
deleted file mode 100644
index ba64b51..0000000
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ /dev/null
@@ -1,677 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.ActivityManager.START_ABORTED;
-import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
-import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
-import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
-import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
-import static android.app.ActivityManager.START_NOT_VOICE_COMPATIBLE;
-import static android.app.ActivityManager.START_PERMISSION_DENIED;
-import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
-import static android.app.ActivityManager.START_SUCCESS;
-import static android.app.ActivityManager.START_SWITCHES_CANCELED;
-import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
-
-import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
-import static com.android.server.am.ActivityDisplay.POSITION_TOP;
-import static com.android.server.am.ActivityTaskManagerService.ANIMATE;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyObject;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.app.ActivityOptions;
-import android.app.IApplicationThread;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ActivityInfo.WindowLayout;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.platform.test.annotations.Presubmit;
-import android.service.voice.IVoiceInteractionSession;
-import android.view.Gravity;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
-import com.android.server.am.TaskRecord.TaskRecordFactory;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link ActivityStarter} class.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:ActivityStarterTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class ActivityStarterTests extends ActivityTestsBase {
-    private ActivityStarter mStarter;
-    private ActivityStartController mController;
-    private ActivityMetricsLogger mActivityMetricsLogger;
-
-    private static final int PRECONDITION_NO_CALLER_APP = 1;
-    private static final int PRECONDITION_NO_INTENT_COMPONENT = 1 << 1;
-    private static final int PRECONDITION_NO_ACTIVITY_INFO = 1 << 2;
-    private static final int PRECONDITION_SOURCE_PRESENT = 1 << 3;
-    private static final int PRECONDITION_REQUEST_CODE = 1 << 4;
-    private static final int PRECONDITION_SOURCE_VOICE_SESSION = 1 << 5;
-    private static final int PRECONDITION_NO_VOICE_SESSION_SUPPORT = 1 << 6;
-    private static final int PRECONDITION_DIFFERENT_UID = 1 << 7;
-    private static final int PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION = 1 << 8;
-    private static final int PRECONDITION_CANNOT_START_ANY_ACTIVITY = 1 << 9;
-    private static final int PRECONDITION_DISALLOW_APP_SWITCHING = 1 << 10;
-
-    private static final int FAKE_CALLING_UID = 666;
-    private static final int FAKE_REAL_CALLING_UID = 667;
-    private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude";
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        setupActivityTaskManagerService();
-        mController = mock(ActivityStartController.class);
-        mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
-        clearInvocations(mActivityMetricsLogger);
-        mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
-                mock(ActivityStartInterceptor.class));
-    }
-
-    @Test
-    public void testUpdateLaunchBounds() throws Exception {
-        // When in a non-resizeable stack, the task bounds should be updated.
-        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
-                .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
-                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
-                .build();
-        final Rect bounds = new Rect(10, 10, 100, 100);
-
-        mStarter.updateBounds(task, bounds);
-        assertEquals(task.getOverrideBounds(), bounds);
-        assertEquals(new Rect(), task.getStack().getOverrideBounds());
-
-        // When in a resizeable stack, the stack bounds should be updated as well.
-        final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
-                .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
-                        WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
-                .build();
-        assertTrue(task2.getStack() instanceof PinnedActivityStack);
-        mStarter.updateBounds(task2, bounds);
-
-        verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
-                eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
-
-        // In the case of no animation, the stack and task bounds should be set immediately.
-        if (!ANIMATE) {
-            assertEquals(task2.getStack().getOverrideBounds(), bounds);
-            assertEquals(task2.getOverrideBounds(), bounds);
-        } else {
-            assertEquals(task2.getOverrideBounds(), new Rect());
-        }
-    }
-
-    @Test
-    public void testStartActivityPreconditions() throws Exception {
-        verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED);
-        verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT,
-                START_INTENT_NOT_RESOLVED);
-        verifyStartActivityPreconditions(PRECONDITION_NO_ACTIVITY_INFO, START_CLASS_NOT_FOUND);
-        verifyStartActivityPreconditions(PRECONDITION_SOURCE_PRESENT | PRECONDITION_REQUEST_CODE,
-                Intent.FLAG_ACTIVITY_FORWARD_RESULT, START_FORWARD_AND_REQUEST_CONFLICT);
-        verifyStartActivityPreconditions(
-                PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
-                        | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID,
-                START_NOT_VOICE_COMPATIBLE);
-        verifyStartActivityPreconditions(
-                PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
-                        | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID
-                        | PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION,
-                START_NOT_VOICE_COMPATIBLE);
-        verifyStartActivityPreconditions(PRECONDITION_CANNOT_START_ANY_ACTIVITY, START_ABORTED);
-        verifyStartActivityPreconditions(PRECONDITION_DISALLOW_APP_SWITCHING,
-                START_SWITCHES_CANCELED);
-    }
-
-    private static boolean containsConditions(int preconditions, int mask) {
-        return (preconditions & mask) == mask;
-    }
-
-    private void verifyStartActivityPreconditions(int preconditions, int expectedResult) {
-        verifyStartActivityPreconditions(preconditions, 0 /*launchFlags*/, expectedResult);
-    }
-
-    /**
-     * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
-     * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
-     * and the launch flags specified in the intent. The method constructs a call to
-     * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches
-     * the expected. It is important to note that the method also checks side effects of the start,
-     * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios.
-     * @param preconditions A bitmask representing the preconditions for the launch
-     * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
-     * @param expectedResult The expected result from the launch.
-     */
-    private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
-            int expectedResult) {
-        final ActivityTaskManagerService service = mService;
-        final IPackageManager packageManager = mock(IPackageManager.class);
-        final ActivityStartController controller = mock(ActivityStartController.class);
-
-        final ActivityStarter starter = new ActivityStarter(controller, service,
-                service.mStackSupervisor, mock(ActivityStartInterceptor.class));
-        prepareStarter(launchFlags);
-        final IApplicationThread caller = mock(IApplicationThread.class);
-
-        final WindowProcessController wpc =
-                containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
-                ? null : new WindowProcessController(
-                        service, mock(ApplicationInfo.class),null, 0, -1, null, null, null);
-        doReturn(wpc).when(service).getProcessController(anyObject());
-
-        final Intent intent = new Intent();
-        intent.setFlags(launchFlags);
-
-        final ActivityInfo aInfo = containsConditions(preconditions, PRECONDITION_NO_ACTIVITY_INFO)
-                ?  null : new ActivityInfo();
-
-        IVoiceInteractionSession voiceSession =
-                containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
-                ? mock(IVoiceInteractionSession.class) : null;
-
-        // Create source token
-        final ActivityBuilder builder = new ActivityBuilder(service).setTask(
-                new TaskBuilder(service.mStackSupervisor).setVoiceSession(voiceSession).build());
-
-        if (aInfo != null) {
-            aInfo.applicationInfo = new ApplicationInfo();
-            aInfo.applicationInfo.packageName =
-                    ActivityBuilder.getDefaultComponent().getPackageName();
-        }
-
-        // Offset uid by one from {@link ActivityInfo} to simulate different uids.
-        if (containsConditions(preconditions, PRECONDITION_DIFFERENT_UID)) {
-            builder.setUid(aInfo.applicationInfo.uid + 1);
-        }
-
-        final ActivityRecord source = builder.build();
-
-        if (!containsConditions(preconditions, PRECONDITION_NO_INTENT_COMPONENT)) {
-            intent.setComponent(source.realActivity);
-        }
-
-        if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
-            doReturn(false).when(service).checkAppSwitchAllowedLocked(
-                    anyInt(), anyInt(), anyInt(), anyInt(), any());
-        }
-
-        if (containsConditions(preconditions,PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
-            doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission(
-                    any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
-                    anyBoolean(), anyBoolean(), any(), any(), any());
-        }
-
-        try {
-            if (containsConditions(preconditions,
-                    PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION)) {
-                doAnswer((inv) -> {
-                    throw new RemoteException();
-                }).when(packageManager).activitySupportsIntent(eq(source.realActivity), eq(intent),
-                        any());
-            } else {
-                doReturn(!containsConditions(preconditions, PRECONDITION_NO_VOICE_SESSION_SUPPORT))
-                        .when(packageManager).activitySupportsIntent(eq(source.realActivity),
-                        eq(intent), any());
-            }
-        } catch (RemoteException e) {
-        }
-
-        final IBinder resultTo = containsConditions(preconditions, PRECONDITION_SOURCE_PRESENT)
-                || containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
-                ? source.appToken : null;
-
-        final int requestCode = containsConditions(preconditions, PRECONDITION_REQUEST_CODE)
-                ? 1 : 0;
-
-        final int result = starter.setCaller(caller)
-                .setIntent(intent)
-                .setActivityInfo(aInfo)
-                .setResultTo(resultTo)
-                .setRequestCode(requestCode)
-                .setReason("testLaunchActivityPermissionDenied")
-                .execute();
-
-        // In some cases the expected result internally is different than the published result. We
-        // must use ActivityStarter#getExternalResult to translate.
-        assertEquals(ActivityStarter.getExternalResult(expectedResult), result);
-
-        // Ensure that {@link ActivityOptions} are aborted with unsuccessful result.
-        if (expectedResult != START_SUCCESS) {
-            final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
-                    mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
-            final ActivityOptions options = spy(ActivityOptions.makeBasic());
-
-            final int optionResult = optionStarter.setCaller(caller)
-                    .setIntent(intent)
-                    .setActivityInfo(aInfo)
-                    .setResultTo(resultTo)
-                    .setRequestCode(requestCode)
-                    .setReason("testLaunchActivityPermissionDenied")
-                    .setActivityOptions(new SafeActivityOptions(options))
-                    .execute();
-            verify(options, times(1)).abort();
-        }
-    }
-
-    private ActivityStarter prepareStarter(@Intent.Flags int launchFlags) {
-        return prepareStarter(launchFlags, true /* mockGetLaunchStack */);
-    }
-
-    /**
-     * Creates a {@link ActivityStarter} with default parameters and necessary mocks.
-     *
-     * @param launchFlags The intent flags to launch activity.
-     * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for
-     *                           always launching to the testing stack. Set to false when allowing
-     *                           the activity can be launched to any stack that is decided by real
-     *                           implementation.
-     * @return A {@link ActivityStarter} with default setup.
-     */
-    private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
-            boolean mockGetLaunchStack) {
-        // always allow test to start activity.
-        doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
-                any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
-                anyBoolean(), anyBoolean(), any(), any(), any());
-
-        // instrument the stack and task used.
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
-                .setCreateStack(false)
-                .build();
-
-        // use factory that only returns spy task.
-        final TaskRecordFactory factory = mock(TaskRecordFactory.class);
-        TaskRecord.setTaskRecordFactory(factory);
-
-        // return task when created.
-        doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any());
-
-        if (mockGetLaunchStack) {
-            // Direct starter to use spy stack.
-            doReturn(stack).when(mService.mStackSupervisor)
-                    .getLaunchStack(any(), any(), any(), anyBoolean());
-            doReturn(stack).when(mService.mStackSupervisor)
-                    .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
-        }
-
-        // Set up mock package manager internal and make sure no unmocked methods are called
-        PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class,
-                invocation -> {
-                    throw new RuntimeException("Not stubbed");
-                });
-        doReturn(mockPackageManager).when(mService).getPackageManagerInternalLocked();
-
-        // Never review permissions
-        doReturn(false).when(mockPackageManager).isPermissionsReviewRequired(any(), anyInt());
-        doNothing().when(mockPackageManager).grantEphemeralAccess(
-                anyInt(), any(), anyInt(), anyInt());
-
-        final Intent intent = new Intent();
-        intent.addFlags(launchFlags);
-        intent.setComponent(ActivityBuilder.getDefaultComponent());
-
-        final ActivityInfo info = new ActivityInfo();
-
-        info.applicationInfo = new ApplicationInfo();
-        info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
-
-        return new ActivityStarter(mController, mService,
-                mService.mStackSupervisor, mock(ActivityStartInterceptor.class))
-                .setIntent(intent)
-                .setActivityInfo(info);
-    }
-
-    /**
-     * Ensures that values specified at launch time are passed to {@link LaunchParamsModifier}
-     * when we are laying out a new task.
-     */
-    @Test
-    public void testCreateTaskLayout() {
-        // modifier for validating passed values.
-        final LaunchParamsModifier modifier = mock(LaunchParamsModifier.class);
-        mService.mStackSupervisor.getLaunchParamsController().registerModifier(modifier);
-
-        // add custom values to activity info to make unique.
-        final ActivityInfo info = new ActivityInfo();
-        final Rect launchBounds = new Rect(0, 0, 20, 30);
-
-        final WindowLayout windowLayout =
-                new WindowLayout(10, .5f, 20, 1.0f, Gravity.NO_GRAVITY, 1, 1);
-
-        info.windowLayout = windowLayout;
-        info.applicationInfo = new ApplicationInfo();
-        info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
-
-        // create starter.
-        final ActivityStarter optionStarter = prepareStarter(0 /* launchFlags */);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchBounds(launchBounds);
-
-        // run starter.
-        optionStarter
-                .setReason("testCreateTaskLayout")
-                .setActivityInfo(info)
-                .setActivityOptions(new SafeActivityOptions(options))
-                .execute();
-
-        // verify that values are passed to the modifier. Values are passed twice -- once for
-        // setting initial state, another when task is created.
-        verify(modifier, times(2)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
-                any(), any());
-    }
-
-    /**
-     * This test ensures that if the intent is being delivered to a
-     */
-    @Test
-    public void testSplitScreenDeliverToTop() {
-        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-
-        final ActivityRecord focusActivity = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .build();
-
-        focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-
-        final ActivityRecord reusableActivity = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .build();
-
-        // Create reusable activity after entering split-screen so that it is the top secondary
-        // stack.
-        reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-
-        // Set focus back to primary.
-        final ActivityStack focusStack = focusActivity.getStack();
-        focusStack.moveToFront("testSplitScreenDeliverToTop");
-
-        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
-
-        final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
-
-        // Ensure result is delivering intent to top.
-        assertEquals(result, START_DELIVERED_TO_TOP);
-    }
-
-    /**
-     * This test ensures that if the intent is being delivered to a split-screen unfocused task
-     * reports it is brought to front instead of delivering to top.
-     */
-    @Test
-    public void testSplitScreenTaskToFront() {
-        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-
-        // Create reusable activity here first. Setting the windowing mode of the primary stack
-        // will move the existing standard full screen stack to secondary, putting this one on the
-        // bottom.
-        final ActivityRecord reusableActivity = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .build();
-
-        reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-
-        final ActivityRecord focusActivity = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .build();
-
-        // Enter split-screen. Primary stack should have focus.
-        focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-
-        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
-
-        final int result = starter.setReason("testSplitScreenMoveToFront").execute();
-
-        // Ensure result is moving task to front.
-        assertEquals(result, START_TASK_TO_FRONT);
-    }
-
-    /**
-     * Tests activity is cleaned up properly in a task mode violation.
-     */
-    @Test
-    public void testTaskModeViolation() {
-        final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
-        ((TestActivityDisplay) display).removeAllTasks();
-        assertNoTasks(display);
-
-        final ActivityStarter starter = prepareStarter(0);
-
-        final LockTaskController lockTaskController = mService.getLockTaskController();
-        doReturn(true).when(lockTaskController).isLockTaskModeViolation(any());
-
-        final int result = starter.setReason("testTaskModeViolation").execute();
-
-        assertEquals(START_RETURN_LOCK_TASK_MODE_VIOLATION, result);
-        assertNoTasks(display);
-    }
-
-    private void assertNoTasks(ActivityDisplay display) {
-        for (int i = display.getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = display.getChildAt(i);
-            assertTrue(stack.getAllTasks().isEmpty());
-        }
-    }
-
-    /**
-     * This test ensures that activity starts are not being logged when the logging is disabled.
-     */
-    @Test
-    public void testActivityStartsLogging_noLoggingWhenDisabled() {
-        doReturn(false).when(mService).isActivityStartsLoggingEnabled();
-        doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
-
-        ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK);
-        starter.setReason("testActivityStartsLogging_noLoggingWhenDisabled").execute();
-
-        // verify logging wasn't done
-        verify(mActivityMetricsLogger, never()).logActivityStart(any(), any(), any(), anyInt(),
-                any(), anyInt(), anyBoolean(), anyInt(), anyInt(), anyBoolean(), anyInt(), any(),
-                anyInt(), anyBoolean(), any(), anyBoolean());
-    }
-
-    /**
-     * This test ensures that activity starts are being logged when the logging is enabled.
-     */
-    @Test
-    public void testActivityStartsLogging_logsWhenEnabled() {
-        // note: conveniently this package doesn't have any activity visible
-        doReturn(true).when(mService).isActivityStartsLoggingEnabled();
-        doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
-
-        ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
-                .setCallingUid(FAKE_CALLING_UID)
-                .setRealCallingUid(FAKE_REAL_CALLING_UID)
-                .setCallingPackage(FAKE_CALLING_PACKAGE)
-                .setOriginatingPendingIntent(null);
-
-        starter.setReason("testActivityStartsLogging_logsWhenEnabled").execute();
-
-        // verify the above activity start was logged
-        verify(mActivityMetricsLogger, times(1)).logActivityStart(any(), any(), any(),
-                eq(FAKE_CALLING_UID), eq(FAKE_CALLING_PACKAGE), anyInt(), anyBoolean(),
-                eq(FAKE_REAL_CALLING_UID), anyInt(), anyBoolean(), anyInt(),
-                eq(ActivityBuilder.getDefaultComponent().getPackageName()), anyInt(), anyBoolean(),
-                any(), eq(false));
-    }
-
-    /**
-     * This test ensures that when starting an existing single task activity on secondary display
-     * which is not the top focused display, it should deliver new intent to the activity and not
-     * create a new stack.
-     */
-    @Test
-    public void testDeliverIntentToTopActivityOfNonTopDisplay() {
-        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
-                false /* mockGetLaunchStack */);
-
-        // Create a secondary display at bottom.
-        final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
-        mSupervisor.addChild(secondaryDisplay, POSITION_BOTTOM);
-        final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        // Create an activity record on the top of secondary display.
-        final ActivityRecord topActivityOnSecondaryDisplay = createSingleTaskActivityOn(stack);
-
-        // Put an activity on default display as the top focused activity.
-        new ActivityBuilder(mService).setCreateTask(true).build();
-
-        // Start activity with the same intent as {@code topActivityOnSecondaryDisplay}
-        // on secondary display.
-        final ActivityOptions options = ActivityOptions.makeBasic()
-                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
-        final int result = starter.setReason("testDeliverIntentToTopActivityOfNonTopDisplay")
-                .setIntent(topActivityOnSecondaryDisplay.intent)
-                .setActivityOptions(options.toBundle())
-                .execute();
-
-        // Ensure result is delivering intent to top.
-        assertEquals(START_DELIVERED_TO_TOP, result);
-
-        // Ensure secondary display only creates one stack.
-        verify(secondaryDisplay, times(1)).createStack(anyInt(), anyInt(), anyBoolean());
-    }
-
-    /**
-     * This test ensures that when starting an existing non-top single task activity on secondary
-     * display which is the top focused display, it should bring the task to front without creating
-     * unused stack.
-     */
-    @Test
-    public void testBringTaskToFrontOnSecondaryDisplay() {
-        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
-                false /* mockGetLaunchStack */);
-
-        // Create a secondary display with an activity.
-        final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
-        mSupervisor.addChild(secondaryDisplay, POSITION_TOP);
-        final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
-                secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
-                        ACTIVITY_TYPE_STANDARD, false /* onTop */));
-
-        // Create another activity on top of the secondary display.
-        final ActivityStack topStack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TaskRecord topTask = new TaskBuilder(mSupervisor).setStack(topStack).build();
-        new ActivityBuilder(mService).setTask(topTask).build();
-
-        // Start activity with the same intent as {@code singleTaskActivity} on secondary display.
-        final ActivityOptions options = ActivityOptions.makeBasic()
-                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
-        final int result = starter.setReason("testBringTaskToFrontOnSecondaryDisplay")
-                .setIntent(singleTaskActivity.intent)
-                .setActivityOptions(options.toBundle())
-                .execute();
-
-        // Ensure result is moving existing task to front.
-        assertEquals(START_TASK_TO_FRONT, result);
-
-        // Ensure secondary display only creates two stacks.
-        verify(secondaryDisplay, times(2)).createStack(anyInt(), anyInt(), anyBoolean());
-    }
-
-    private ActivityRecord createSingleTaskActivityOn(ActivityStack stack) {
-        final ComponentName componentName = ComponentName.createRelative(
-                DEFAULT_COMPONENT_PACKAGE_NAME,
-                DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity");
-        final TaskRecord taskRecord = new TaskBuilder(mSupervisor)
-                .setComponent(componentName)
-                .setStack(stack)
-                .build();
-        return new ActivityBuilder(mService)
-                .setComponent(componentName)
-                .setLaunchMode(LAUNCH_SINGLE_TASK)
-                .setTask(taskRecord)
-                .build();
-    }
-
-    /**
-     * This test ensures that a reused top activity in the top focused stack is able to be
-     * reparented to another display.
-     */
-    @Test
-    public void testReparentTopFocusedActivityToSecondaryDisplay() {
-        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
-                false /* mockGetLaunchStack */);
-
-        // Create a secondary display at bottom.
-        final TestActivityDisplay secondaryDisplay = addNewActivityDisplayAt(POSITION_BOTTOM);
-        secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
-
-        // Put an activity on default display as the top focused activity.
-        final ActivityRecord topActivity = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .setLaunchMode(LAUNCH_SINGLE_TASK)
-                .build();
-
-        // Start activity with the same intent as {@code topActivity} on secondary display.
-        final ActivityOptions options = ActivityOptions.makeBasic()
-                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
-        starter.setReason("testReparentTopFocusedActivityToSecondaryDisplay")
-                .setIntent(topActivity.intent)
-                .setActivityOptions(options.toBundle())
-                .execute();
-
-        // Ensure the activity is moved to secondary display.
-        assertEquals(secondaryDisplay, topActivity.getDisplay());
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
deleted file mode 100644
index 01d51e4..0000000
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ /dev/null
@@ -1,758 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-
-import android.app.ActivityManagerInternal;
-import android.app.ActivityOptions;
-import android.content.pm.PackageManagerInternal;
-import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.server.wm.DisplayWindowController;
-
-import org.junit.Rule;
-import org.mockito.invocation.InvocationOnMock;
-
-import android.app.IApplicationThread;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerGlobal;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Process;
-import android.os.UserHandle;
-import android.service.voice.IVoiceInteractionSession;
-import android.testing.DexmakerShareClassLoaderRule;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.internal.app.IVoiceInteractor;
-import com.android.server.AppOpsService;
-import com.android.server.AttributeCache;
-import com.android.server.ServiceThread;
-import com.android.server.wm.AppWindowContainerController;
-import com.android.server.wm.PinnedStackWindowController;
-import com.android.server.wm.RootWindowContainerController;
-import com.android.server.wm.StackWindowController;
-import com.android.server.wm.TaskWindowContainerController;
-import com.android.server.wm.WindowManagerService;
-import com.android.server.wm.WindowTestUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * A base class to handle common operations in activity related unit tests.
- */
-public class ActivityTestsBase {
-    private static boolean sOneTimeSetupDone = false;
-
-    private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
-
-    @Rule
-    public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
-            new DexmakerShareClassLoaderRule();
-
-    private final Context mContext = InstrumentationRegistry.getContext();
-    final TestInjector mTestInjector = new TestInjector();
-
-    ActivityTaskManagerService mService;
-    ActivityStackSupervisor mSupervisor;
-
-    // Default package name
-    static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";
-
-    // Default base activity name
-    private static final String DEFAULT_COMPONENT_CLASS_NAME = ".BarActivity";
-
-    @Before
-    public void setUp() throws Exception {
-        if (!sOneTimeSetupDone) {
-            sOneTimeSetupDone = true;
-            MockitoAnnotations.initMocks(this);
-            AttributeCache.init(mContext);
-        }
-        mTestInjector.setUp();
-    }
-
-    @After
-    public void tearDown() {
-        mTestInjector.tearDown();
-    }
-
-    protected ActivityTaskManagerService createActivityTaskManagerService() {
-        final TestActivityTaskManagerService atm =
-                spy(new TestActivityTaskManagerService(mContext));
-        setupActivityManagerService(atm);
-        return atm;
-    }
-
-    void setupActivityTaskManagerService() {
-        mService = createActivityTaskManagerService();
-        mSupervisor = mService.mStackSupervisor;
-    }
-
-    ActivityManagerService createActivityManagerService() {
-        final TestActivityTaskManagerService atm =
-                spy(new TestActivityTaskManagerService(mContext));
-        return setupActivityManagerService(atm);
-    }
-
-    ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
-        final TestActivityManagerService am = spy(new TestActivityManagerService(mTestInjector));
-        setupActivityManagerService(am, atm);
-        return am;
-    }
-
-    /** Creates a {@link TestActivityDisplay}. */
-    TestActivityDisplay createNewActivityDisplay() {
-        return TestActivityDisplay.create(mSupervisor, sNextDisplayId++);
-    }
-
-    /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
-    TestActivityDisplay addNewActivityDisplayAt(int position) {
-        final TestActivityDisplay display = createNewActivityDisplay();
-        mSupervisor.addChild(display, position);
-        return display;
-    }
-
-    void setupActivityManagerService(
-            TestActivityManagerService am, TestActivityTaskManagerService atm) {
-        atm.setActivityManagerService(am, am.mHandlerThread.getLooper(), am.mIntentFirewall,
-                am.mPendingIntentController);
-        atm.mAmInternal = am.getLocalService();
-        am.mAtmInternal = atm.getLocalService();
-        // Makes sure the supervisor is using with the spy object.
-        atm.mStackSupervisor.setService(atm);
-        doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
-        PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
-        doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
-        doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
-        doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
-        am.mActivityTaskManager = atm;
-        am.mWindowManager = prepareMockWindowManager();
-        atm.setWindowManager(am.mWindowManager);
-
-        // Put a home stack on the default display, so that we'll always have something focusable.
-        final TestActivityStackSupervisor supervisor =
-                (TestActivityStackSupervisor) atm.mStackSupervisor;
-        supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
-        final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
-                .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
-        new ActivityBuilder(atm).setTask(task).build();
-    }
-
-    /**
-     * Builder for creating new activities.
-     */
-    protected static class ActivityBuilder {
-        // An id appended to the end of the component name to make it unique
-        private static int sCurrentActivityId = 0;
-
-        private final ActivityTaskManagerService mService;
-
-        private ComponentName mComponent;
-        private TaskRecord mTaskRecord;
-        private int mUid;
-        private boolean mCreateTask;
-        private ActivityStack mStack;
-        private int mActivityFlags;
-        private int mLaunchMode;
-
-        ActivityBuilder(ActivityTaskManagerService service) {
-            mService = service;
-        }
-
-        ActivityBuilder setComponent(ComponentName component) {
-            mComponent = component;
-            return this;
-        }
-
-        static ComponentName getDefaultComponent() {
-            return ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
-                    DEFAULT_COMPONENT_PACKAGE_NAME);
-        }
-
-        ActivityBuilder setTask(TaskRecord task) {
-            mTaskRecord = task;
-            return this;
-        }
-
-        ActivityBuilder setActivityFlags(int flags) {
-            mActivityFlags = flags;
-            return this;
-        }
-
-        ActivityBuilder setLaunchMode(int launchMode) {
-            mLaunchMode = launchMode;
-            return this;
-        }
-
-        ActivityBuilder setStack(ActivityStack stack) {
-            mStack = stack;
-            return this;
-        }
-
-        ActivityBuilder setCreateTask(boolean createTask) {
-            mCreateTask = createTask;
-            return this;
-        }
-
-        ActivityBuilder setUid(int uid) {
-            mUid = uid;
-            return this;
-        }
-
-        ActivityRecord build() {
-            if (mComponent == null) {
-                final int id = sCurrentActivityId++;
-                mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
-                        DEFAULT_COMPONENT_CLASS_NAME + id);
-            }
-
-            if (mCreateTask) {
-                mTaskRecord = new TaskBuilder(mService.mStackSupervisor)
-                        .setComponent(mComponent)
-                        .setStack(mStack).build();
-            }
-
-            Intent intent = new Intent();
-            intent.setComponent(mComponent);
-            final ActivityInfo aInfo = new ActivityInfo();
-            aInfo.applicationInfo = new ApplicationInfo();
-            aInfo.applicationInfo.packageName = mComponent.getPackageName();
-            aInfo.applicationInfo.uid = mUid;
-            aInfo.flags |= mActivityFlags;
-            aInfo.launchMode = mLaunchMode;
-
-            final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
-                    0 /* launchedFromPid */, 0, null, intent, null,
-                    aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
-                    0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
-                    mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
-            activity.mWindowContainerController = mock(AppWindowContainerController.class);
-
-            if (mTaskRecord != null) {
-                mTaskRecord.addActivityToTop(activity);
-            }
-
-            final WindowProcessController wpc = new WindowProcessController(mService,
-                    mService.mContext.getApplicationInfo(), "name", 12345,
-                    UserHandle.getUserId(12345), mock(Object.class),
-                    mock(WindowProcessListener.class), null);
-            wpc.setThread(mock(IApplicationThread.class));
-            activity.setProcess(wpc);
-            return activity;
-        }
-    }
-
-    /**
-     * Builder for creating new tasks.
-     */
-    protected static class TaskBuilder {
-        // Default package name
-        static final String DEFAULT_PACKAGE = "com.bar";
-
-        private final ActivityStackSupervisor mSupervisor;
-
-        private ComponentName mComponent;
-        private String mPackage;
-        private int mFlags = 0;
-        // Task id 0 is reserved in ARC for the home app.
-        private int mTaskId = 1;
-        private int mUserId = 0;
-        private IVoiceInteractionSession mVoiceSession;
-        private boolean mCreateStack = true;
-
-        private ActivityStack mStack;
-
-        TaskBuilder(ActivityStackSupervisor supervisor) {
-            mSupervisor = supervisor;
-        }
-
-        TaskBuilder setComponent(ComponentName component) {
-            mComponent = component;
-            return this;
-        }
-
-        TaskBuilder setPackage(String packageName) {
-            mPackage = packageName;
-            return this;
-        }
-
-        /**
-         * Set to {@code true} by default, set to {@code false} to prevent the task from
-         * automatically creating a parent stack.
-         */
-        TaskBuilder setCreateStack(boolean createStack) {
-            mCreateStack = createStack;
-            return this;
-        }
-
-        TaskBuilder setVoiceSession(IVoiceInteractionSession session) {
-            mVoiceSession = session;
-            return this;
-        }
-
-        TaskBuilder setFlags(int flags) {
-            mFlags = flags;
-            return this;
-        }
-
-        TaskBuilder setTaskId(int taskId) {
-            mTaskId = taskId;
-            return this;
-        }
-
-        TaskBuilder setUserId(int userId) {
-            mUserId = userId;
-            return this;
-        }
-
-        TaskBuilder setStack(ActivityStack stack) {
-            mStack = stack;
-            return this;
-        }
-
-        TaskRecord build() {
-            if (mStack == null && mCreateStack) {
-                mStack = mSupervisor.getDefaultDisplay().createStack(
-                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-            }
-
-            final ActivityInfo aInfo = new ActivityInfo();
-            aInfo.applicationInfo = new ApplicationInfo();
-            aInfo.applicationInfo.packageName = mPackage;
-
-            Intent intent = new Intent();
-            if (mComponent == null) {
-                mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
-                        DEFAULT_COMPONENT_CLASS_NAME);
-            }
-
-            intent.setComponent(mComponent);
-            intent.setFlags(mFlags);
-
-            final TestTaskRecord task = new TestTaskRecord(mSupervisor.mService, mTaskId, aInfo,
-                    intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/);
-            task.userId = mUserId;
-
-            if (mStack != null) {
-                mStack.moveToFront("test");
-                mStack.addTask(task, true, "creating test task");
-                task.setStack(mStack);
-                task.setWindowContainerController();
-            }
-
-            task.touchActiveTime();
-
-            return task;
-        }
-
-        private static class TestTaskRecord extends TaskRecord {
-            TestTaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info,
-                       Intent _intent, IVoiceInteractionSession _voiceSession,
-                       IVoiceInteractor _voiceInteractor) {
-                super(service, _taskId, info, _intent, _voiceSession, _voiceInteractor);
-            }
-
-            @Override
-            void createWindowContainer(boolean onTop, boolean showForAllUsers) {
-                setWindowContainerController();
-            }
-
-            private void setWindowContainerController() {
-                setWindowContainerController(mock(TaskWindowContainerController.class));
-            }
-        }
-    }
-
-    protected static class TestActivityTaskManagerService extends ActivityTaskManagerService {
-        private LockTaskController mLockTaskController;
-        private ActivityTaskManagerInternal mInternal;
-        private PackageManagerInternal mPmInternal;
-
-        TestActivityTaskManagerService(Context context) {
-            super(context);
-            mSupportsMultiWindow = true;
-            mSupportsMultiDisplay = true;
-            mSupportsSplitScreenMultiWindow = true;
-            mSupportsFreeformWindowManagement = true;
-            mSupportsPictureInPicture = true;
-            mUgmInternal = mock(UriGrantsManagerInternal.class);
-        }
-
-        @Override
-        int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
-            return userId;
-        }
-
-        @Override
-        public LockTaskController getLockTaskController() {
-            if (mLockTaskController == null) {
-                mLockTaskController = spy(super.getLockTaskController());
-            }
-
-            return mLockTaskController;
-        }
-
-        @Override
-        void updateUsageStats(ActivityRecord component, boolean resumed) {
-        }
-
-        @Override
-        final protected ActivityStackSupervisor createStackSupervisor() {
-            final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
-            final KeyguardController keyguardController = mock(KeyguardController.class);
-
-            // Invoked during {@link ActivityStack} creation.
-            doNothing().when(supervisor).updateUIDsPresentOnDisplay();
-            // Always keep things awake.
-            doReturn(true).when(supervisor).hasAwakeDisplay();
-            // Called when moving activity to pinned stack.
-            doNothing().when(supervisor).ensureActivitiesVisibleLocked(any(), anyInt(), anyBoolean());
-            // Do not schedule idle timeouts
-            doNothing().when(supervisor).scheduleIdleTimeoutLocked(any());
-            // unit test version does not handle launch wake lock
-            doNothing().when(supervisor).acquireLaunchWakelock();
-            doReturn(keyguardController).when(supervisor).getKeyguardController();
-
-            supervisor.initialize();
-
-            return supervisor;
-        }
-
-        protected ActivityStackSupervisor createTestSupervisor() {
-            return new TestActivityStackSupervisor(this, mH.getLooper());
-        }
-
-        ActivityTaskManagerInternal getLocalService() {
-            if (mInternal == null) {
-                mInternal = new ActivityTaskManagerService.LocalService();
-            }
-            return mInternal;
-        }
-
-        PackageManagerInternal getPackageManagerInternalLocked() {
-            if (mPmInternal == null) {
-                mPmInternal = mock(PackageManagerInternal.class);
-                doReturn(false).when(mPmInternal).isPermissionsReviewRequired(anyString(), anyInt());
-            }
-            return mPmInternal;
-        }
-    }
-
-    private static class TestInjector extends ActivityManagerService.Injector {
-        private ServiceThread mHandlerThread;
-
-        @Override
-        public Context getContext() {
-            return InstrumentationRegistry.getContext();
-        }
-
-        @Override
-        public AppOpsService getAppOpsService(File file, Handler handler) {
-            return null;
-        }
-
-        @Override
-        public Handler getUiHandler(ActivityManagerService service) {
-            return mHandlerThread.getThreadHandler();
-        }
-
-        @Override
-        public boolean isNetworkRestrictedForUid(int uid) {
-            return false;
-        }
-
-        void setUp() {
-            mHandlerThread = new ServiceThread("ActivityTestsThread",
-                    Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
-            mHandlerThread.start();
-        }
-
-        void tearDown() {
-            mHandlerThread.quitSafely();
-        }
-    }
-
-    /**
-     * An {@link ActivityManagerService} subclass which provides a test
-     * {@link ActivityStackSupervisor}.
-     */
-    static class TestActivityManagerService extends ActivityManagerService {
-
-        private ActivityManagerInternal mInternal;
-
-        TestActivityManagerService(TestInjector testInjector) {
-            super(testInjector, testInjector.mHandlerThread);
-            mUgmInternal = mock(UriGrantsManagerInternal.class);
-        }
-
-        @Override
-        Configuration getGlobalConfiguration() {
-            return mContext.getResources().getConfiguration();
-        }
-
-        ActivityManagerInternal getLocalService() {
-            if (mInternal == null) {
-                mInternal = new LocalService();
-            }
-            return mInternal;
-        }
-    }
-
-    /**
-     * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
-     * setup not available in the test environment. Also specifies an injector for
-     */
-    protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
-        private ActivityDisplay mDisplay;
-        private KeyguardController mKeyguardController;
-
-        public TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
-            super(service, looper);
-            mDisplayManager =
-                    (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
-            mWindowManager = prepareMockWindowManager();
-            mKeyguardController = mock(KeyguardController.class);
-            setWindowContainerController(mock(RootWindowContainerController.class));
-        }
-
-        @Override
-        public void initialize() {
-            super.initialize();
-            mDisplay = spy(TestActivityDisplay.create(this, DEFAULT_DISPLAY));
-            addChild(mDisplay, ActivityDisplay.POSITION_TOP);
-        }
-
-        @Override
-        public KeyguardController getKeyguardController() {
-            return mKeyguardController;
-        }
-
-        @Override
-        ActivityDisplay getDefaultDisplay() {
-            return mDisplay;
-        }
-
-        @Override
-        void setWindowManager(WindowManagerService wm) {
-            mWindowManager = wm;
-        }
-    }
-
-    protected static class TestActivityDisplay extends ActivityDisplay {
-        private final ActivityStackSupervisor mSupervisor;
-
-        static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
-            if (displayId == DEFAULT_DISPLAY) {
-                return new TestActivityDisplay(supervisor,
-                        supervisor.mDisplayManager.getDisplay(displayId));
-            }
-            final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
-                    new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
-            return new TestActivityDisplay(supervisor, display);
-        }
-
-        TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
-            super(supervisor, display);
-            // Normally this comes from display-properties as exposed by WM. Without that, just
-            // hard-code to FULLSCREEN for tests.
-            setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-            mSupervisor = supervisor;
-        }
-
-        @Override
-        <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
-                int stackId, boolean onTop) {
-            if (windowingMode == WINDOWING_MODE_PINNED) {
-                return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop) {
-                    @Override
-                    Rect getDefaultPictureInPictureBounds(float aspectRatio) {
-                        return new Rect(50, 50, 100, 100);
-                    }
-
-                    @Override
-                    PinnedStackWindowController createStackWindowController(int displayId,
-                            boolean onTop, Rect outBounds) {
-                        return mock(PinnedStackWindowController.class);
-                    }
-                };
-            } else {
-                return (T) new TestActivityStack(
-                        this, stackId, mSupervisor, windowingMode, activityType, onTop);
-            }
-        }
-
-        @Override
-        protected DisplayWindowController createWindowContainerController() {
-            return mock(DisplayWindowController.class);
-        }
-
-        void removeAllTasks() {
-            for (int i = 0; i < getChildCount(); i++) {
-                final ActivityStack stack = getChildAt(i);
-                for (TaskRecord task : (List<TaskRecord>) stack.getAllTasks()) {
-                    stack.removeTask(task, "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
-                }
-            }
-        }
-    }
-
-    private static WindowManagerService prepareMockWindowManager() {
-        final WindowManagerService service = WindowTestUtils.getMockWindowManagerService();
-
-        doAnswer((InvocationOnMock invocationOnMock) -> {
-            final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
-            if (runnable != null) {
-                runnable.run();
-            }
-            return null;
-        }).when(service).inSurfaceTransaction(any());
-
-        return service;
-    }
-
-    /**
-     * Overridden {@link ActivityStack} that tracks test metrics, such as the number of times a
-     * method is called. Note that its functionality depends on the implementations of the
-     * construction arguments.
-     */
-    protected static class TestActivityStack<T extends StackWindowController>
-            extends ActivityStack<T> {
-        private int mOnActivityRemovedFromStackCount = 0;
-        private T mContainerController;
-
-        static final int IS_TRANSLUCENT_UNSET = 0;
-        static final int IS_TRANSLUCENT_FALSE = 1;
-        static final int IS_TRANSLUCENT_TRUE = 2;
-        private int mIsTranslucent = IS_TRANSLUCENT_UNSET;
-
-        static final int SUPPORTS_SPLIT_SCREEN_UNSET = 0;
-        static final int SUPPORTS_SPLIT_SCREEN_FALSE = 1;
-        static final int SUPPORTS_SPLIT_SCREEN_TRUE = 2;
-        private int mSupportsSplitScreen = SUPPORTS_SPLIT_SCREEN_UNSET;
-
-        TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
-                int windowingMode, int activityType, boolean onTop) {
-            super(display, stackId, supervisor, windowingMode, activityType, onTop);
-        }
-
-        @Override
-        void onActivityRemovedFromStack(ActivityRecord r) {
-            mOnActivityRemovedFromStackCount++;
-            super.onActivityRemovedFromStack(r);
-        }
-
-        // Returns the number of times {@link #onActivityRemovedFromStack} has been called
-        int onActivityRemovedFromStackInvocationCount() {
-            return mOnActivityRemovedFromStackCount;
-        }
-
-        @Override
-        protected T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
-            mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController();
-
-            // Primary pinned stacks require a non-empty out bounds to be set or else all tasks
-            // will be moved to the full screen stack.
-            if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                outBounds.set(0, 0, 100, 100);
-            }
-            return mContainerController;
-        }
-
-        @Override
-        T getWindowContainerController() {
-            return mContainerController;
-        }
-
-        void setIsTranslucent(boolean isTranslucent) {
-            mIsTranslucent = isTranslucent ? IS_TRANSLUCENT_TRUE : IS_TRANSLUCENT_FALSE;
-        }
-
-        @Override
-        boolean isStackTranslucent(ActivityRecord starting) {
-            switch (mIsTranslucent) {
-                case IS_TRANSLUCENT_TRUE:
-                    return true;
-                case IS_TRANSLUCENT_FALSE:
-                    return false;
-                case IS_TRANSLUCENT_UNSET:
-                default:
-                    return super.isStackTranslucent(starting);
-            }
-        }
-
-        void setSupportsSplitScreen(boolean supportsSplitScreen) {
-            mSupportsSplitScreen = supportsSplitScreen
-                    ? SUPPORTS_SPLIT_SCREEN_TRUE : SUPPORTS_SPLIT_SCREEN_FALSE;
-        }
-
-        @Override
-        public boolean supportsSplitScreenWindowingMode() {
-            switch (mSupportsSplitScreen) {
-                case SUPPORTS_SPLIT_SCREEN_TRUE:
-                    return true;
-                case SUPPORTS_SPLIT_SCREEN_FALSE:
-                    return false;
-                case SUPPORTS_SPLIT_SCREEN_UNSET:
-                default:
-                    return super.supportsSplitScreenWindowingMode();
-            }
-        }
-
-        @Override
-        void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
-                                 boolean newTask, boolean keepCurTransition,
-                                 ActivityOptions options) {
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
index 06d41f1..05cb48b 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -16,27 +16,26 @@
 
 package com.android.server.am;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import android.content.Context;
 import android.os.Handler;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.AppOpsService;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.io.File;
 
 /**
- * runtest -c com.android.server.am.AppErrorDialogTest frameworks-services
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:AppErrorDialogTest
  */
-@RunWith(AndroidJUnit4.class)
 @SmallTest
 @FlakyTest(bugId = 113616538)
 public class AppErrorDialogTest {
@@ -46,7 +45,7 @@
 
     @Before
     public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
+        mContext = getInstrumentation().getTargetContext();
         mService = new ActivityManagerService(new ActivityManagerService.Injector() {
             @Override
             public AppOpsService getAppOpsService(File file, Handler handler) {
@@ -67,9 +66,9 @@
 
     @Test
     @UiThreadTest
-    public void testCreateWorks() throws Exception {
+    public void testCreateWorks() {
         AppErrorDialog.Data data = new AppErrorDialog.Data();
-        data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345, null);
+        data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345);
         data.result = new AppErrorResult();
 
         AppErrorDialog dialog = new AppErrorDialog(mContext, mService, data);
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
deleted file mode 100644
index 1b823ff..0000000
--- a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_ERRORED;
-import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT;
-import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
-import static android.graphics.Bitmap.Config.ARGB_8888;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-
-import android.app.AppOpsManager;
-import android.app.IActivityManager;
-import android.app.IActivityTaskManager;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.util.Log;
-import android.view.IWindowManager;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Note: Currently, we only support fetching the screenshot for the current application, so the
- * screenshot checks are hardcoded accordingly.
- *
- * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
- */
-@MediumTest
-@FlakyTest(bugId = 113616538)
-@RunWith(AndroidJUnit4.class)
-public class AssistDataRequesterTest extends ActivityTestsBase {
-
-    private static final String TAG = AssistDataRequesterTest.class.getSimpleName();
-
-    private static final boolean CURRENT_ACTIVITY_ASSIST_ALLOWED = true;
-    private static final boolean CALLER_ASSIST_STRUCTURE_ALLOWED = true;
-    private static final boolean CALLER_ASSIST_SCREENSHOT_ALLOWED = true;
-    private static final boolean FETCH_DATA = true;
-    private static final boolean FETCH_SCREENSHOTS = true;
-    private static final boolean ALLOW_FETCH_DATA = true;
-    private static final boolean ALLOW_FETCH_SCREENSHOTS = true;
-
-    private static final int TEST_UID = 0;
-    private static final String TEST_PACKAGE = "";
-
-    private Context mContext;
-    private AssistDataRequester mDataRequester;
-    private Callbacks mCallbacks;
-    private Object mCallbacksLock;
-    private Handler mHandler;
-    private IActivityManager mAm;
-    private IActivityTaskManager mAtm;
-    private IWindowManager mWm;
-    private AppOpsManager mAppOpsManager;
-
-    /**
-     * The requests to fetch assist data are done incrementally from the text thread, and we
-     * immediately post onto the main thread handler below, which would immediately make the
-     * callback and decrement the pending counts. In order to assert the pending counts, we defer
-     * the callbacks on the test-side until after we flip the gate, after which we can drain the
-     * main thread handler and make assertions on the actual callbacks
-     */
-    private CountDownLatch mGate;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        mAm = mock(IActivityManager.class);
-        mAtm = mock(IActivityTaskManager.class);
-        mWm = mock(IWindowManager.class);
-        mAppOpsManager = mock(AppOpsManager.class);
-        mContext =  InstrumentationRegistry.getContext();
-        mHandler = new Handler(Looper.getMainLooper());
-        mCallbacksLock = new Object();
-        mCallbacks = new Callbacks();
-        mDataRequester = new AssistDataRequester(mContext, mWm, mAppOpsManager, mCallbacks,
-                mCallbacksLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
-
-        // Gate the continuation of the assist data callbacks until we are ready within the tests
-        mGate = new CountDownLatch(1);
-        doAnswer(invocation -> {
-            mHandler.post(() -> {
-                try {
-                    mGate.await(10, TimeUnit.SECONDS);
-                    mDataRequester.onHandleAssistData(new Bundle());
-                } catch (InterruptedException e) {
-                    Log.e(TAG, "Failed to wait", e);
-                }
-            });
-            return true;
-        }).when(mAtm).requestAssistContextExtras(anyInt(), any(), any(), any(), anyBoolean(),
-                anyBoolean());
-        doAnswer(invocation -> {
-            mHandler.post(() -> {
-                try {
-                    mGate.await(10, TimeUnit.SECONDS);
-                    mDataRequester.onHandleAssistScreenshot(Bitmap.createBitmap(1, 1, ARGB_8888));
-                } catch (InterruptedException e) {
-                    Log.e(TAG, "Failed to wait", e);
-                }
-            });
-            return true;
-        }).when(mWm).requestAssistScreenshot(any());
-    }
-
-    private void setupMocks(boolean currentActivityAssistAllowed, boolean assistStructureAllowed,
-            boolean assistScreenshotAllowed) throws Exception {
-        doReturn(currentActivityAssistAllowed).when(mAtm).isAssistDataAllowedOnCurrentActivity();
-        doReturn(assistStructureAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
-                .checkOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString());
-        doReturn(assistScreenshotAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
-                .checkOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString());
-    }
-
-    @Test
-    public void testRequestData() throws Exception {
-        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
-                CALLER_ASSIST_SCREENSHOT_ALLOWED);
-
-        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
-        assertReceivedDataCount(5, 5, 1, 1);
-    }
-
-    @Test
-    public void testEmptyActivities_expectNoCallbacks() throws Exception {
-        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
-                CALLER_ASSIST_SCREENSHOT_ALLOWED);
-
-        mDataRequester.requestAssistData(createActivityList(0), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
-        assertReceivedDataCount(0, 0, 0, 0);
-    }
-
-    @Test
-    public void testCurrentAppDisallow_expectNullCallbacks() throws Exception {
-        setupMocks(!CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
-                CALLER_ASSIST_SCREENSHOT_ALLOWED);
-
-        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
-        assertReceivedDataCount(0, 1, 0, 1);
-    }
-
-    @Test
-    public void testProcessPendingData() throws Exception {
-        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
-                CALLER_ASSIST_SCREENSHOT_ALLOWED);
-
-        mCallbacks.canHandleReceivedData = false;
-        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
-        assertTrue(mDataRequester.getPendingDataCount() == 5);
-        assertTrue(mDataRequester.getPendingScreenshotCount() == 1);
-        mGate.countDown();
-        waitForIdle(mHandler);
-
-        // Callbacks still not ready to receive, but all pending data is received
-        assertTrue(mDataRequester.getPendingDataCount() == 0);
-        assertTrue(mDataRequester.getPendingScreenshotCount() == 0);
-        assertTrue(mCallbacks.receivedData.isEmpty());
-        assertTrue(mCallbacks.receivedScreenshots.isEmpty());
-        assertFalse(mCallbacks.requestCompleted);
-
-        mCallbacks.canHandleReceivedData = true;
-        mDataRequester.processPendingAssistData();
-        // Since we are posting the callback for the request-complete, flush the handler as well
-        mGate.countDown();
-        waitForIdle(mHandler);
-        assertTrue(mCallbacks.receivedData.size() == 5);
-        assertTrue(mCallbacks.receivedScreenshots.size() == 1);
-        assertTrue(mCallbacks.requestCompleted);
-
-        // Clear the state and ensure that we only process pending data once
-        mCallbacks.reset();
-        mDataRequester.processPendingAssistData();
-        assertTrue(mCallbacks.receivedData.isEmpty());
-        assertTrue(mCallbacks.receivedScreenshots.isEmpty());
-    }
-
-    @Test
-    public void testNoFetchData_expectNoDataCallbacks() throws Exception {
-        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
-                CALLER_ASSIST_SCREENSHOT_ALLOWED);
-
-        mDataRequester.requestAssistData(createActivityList(5), !FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
-        assertReceivedDataCount(0, 0, 0, 1);
-    }
-
-    @Test
-    public void testDisallowAssistStructure_expectNullDataCallbacks() throws Exception {
-        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, !CALLER_ASSIST_STRUCTURE_ALLOWED,
-                CALLER_ASSIST_SCREENSHOT_ALLOWED);
-
-        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
-        // Expect a single null data when the appops is denied
-        assertReceivedDataCount(0, 1, 0, 1);
-    }
-
-    @Test
-    public void testDisallowAssistContextExtras_expectNullDataCallbacks() throws Exception {
-        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
-                CALLER_ASSIST_SCREENSHOT_ALLOWED);
-        doReturn(false).when(mAtm).requestAssistContextExtras(anyInt(), any(), any(), any(),
-                anyBoolean(), anyBoolean());
-
-        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
-        // Expect a single null data when requestAssistContextExtras() fails
-        assertReceivedDataCount(0, 1, 0, 1);
-    }
-
-    @Test
-    public void testNoFetchScreenshots_expectNoScreenshotCallbacks() throws Exception {
-        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
-                CALLER_ASSIST_SCREENSHOT_ALLOWED);
-
-        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, !FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
-        assertReceivedDataCount(5, 5, 0, 0);
-    }
-
-    @Test
-    public void testDisallowAssistScreenshot_expectNullScreenshotCallback() throws Exception {
-        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
-                !CALLER_ASSIST_SCREENSHOT_ALLOWED);
-
-        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
-        // Expect a single null screenshot when the appops is denied
-        assertReceivedDataCount(5, 5, 0, 1);
-    }
-
-    @Test
-    public void testCanNotHandleReceivedData_expectNoCallbacks() throws Exception {
-        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, !CALLER_ASSIST_STRUCTURE_ALLOWED,
-                !CALLER_ASSIST_SCREENSHOT_ALLOWED);
-
-        mCallbacks.canHandleReceivedData = false;
-        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
-        mGate.countDown();
-        waitForIdle(mHandler);
-        assertTrue(mCallbacks.receivedData.isEmpty());
-        assertTrue(mCallbacks.receivedScreenshots.isEmpty());
-    }
-
-    @Test
-    public void testRequestDataNoneAllowed_expectNullCallbacks() throws Exception {
-        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
-                CALLER_ASSIST_SCREENSHOT_ALLOWED);
-
-        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                !ALLOW_FETCH_DATA, !ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
-        assertReceivedDataCount(0, 1, 0, 1);
-    }
-
-    private void assertReceivedDataCount(int numPendingData, int numReceivedData,
-            int numPendingScreenshots, int numReceivedScreenshots) throws Exception {
-        assertTrue("Expected " + numPendingData + " pending data, got "
-                        + mDataRequester.getPendingDataCount(),
-                mDataRequester.getPendingDataCount() == numPendingData);
-        assertTrue("Expected " + numPendingScreenshots + " pending screenshots, got "
-                        + mDataRequester.getPendingScreenshotCount(),
-                mDataRequester.getPendingScreenshotCount() == numPendingScreenshots);
-        assertFalse("Expected request NOT completed", mCallbacks.requestCompleted);
-        mGate.countDown();
-        waitForIdle(mHandler);
-        assertTrue("Expected " + numReceivedData + " data, received "
-                        + mCallbacks.receivedData.size(),
-                mCallbacks.receivedData.size() == numReceivedData);
-        assertTrue("Expected " + numReceivedScreenshots + " screenshots, received "
-                        + mCallbacks.receivedScreenshots.size(),
-                mCallbacks.receivedScreenshots.size() == numReceivedScreenshots);
-        assertTrue("Expected request completed", mCallbacks.requestCompleted);
-    }
-
-    private List<IBinder> createActivityList(int size) {
-        ArrayList<IBinder> activities = new ArrayList<>();
-        for (int i = 0; i < size; i++) {
-            activities.add(mock(IBinder.class));
-        }
-        return activities;
-    }
-
-    public void waitForIdle(Handler h) throws Exception {
-        if (Looper.myLooper() == h.getLooper()) {
-            throw new RuntimeException("This method can not be called from the waiting looper");
-        }
-        CountDownLatch latch = new CountDownLatch(1);
-        h.post(() -> latch.countDown());
-        latch.await(2, TimeUnit.SECONDS);
-    }
-
-    private class Callbacks implements AssistDataRequesterCallbacks {
-
-        boolean canHandleReceivedData = true;
-        boolean requestCompleted = false;
-        ArrayList<Bundle> receivedData = new ArrayList<>();
-        ArrayList<Bitmap> receivedScreenshots = new ArrayList<>();
-
-        void reset() {
-            canHandleReceivedData = true;
-            receivedData.clear();
-            receivedScreenshots.clear();
-        }
-
-        @Override
-        public boolean canHandleReceivedAssistDataLocked() {
-            return canHandleReceivedData;
-        }
-
-        @Override
-        public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
-            receivedData.add(data);
-        }
-
-        @Override
-        public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
-            receivedScreenshots.add(screenshot);
-        }
-
-        @Override
-        public void onAssistRequestCompleted() {
-            mHandler.post(() -> {
-                try {
-                    mGate.await(10, TimeUnit.SECONDS);
-                    requestCompleted = true;
-                } catch (InterruptedException e) {
-                    Log.e(TAG, "Failed to wait", e);
-                }
-            });
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index 75f7c4c..0889265 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -27,10 +27,8 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -40,11 +38,10 @@
  * Test class for {@link BroadcastRecord}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.am.BroadcastRecordTest
+ *  atest FrameworksServicesTests:BroadcastRecordTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class BroadcastRecordTest {
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/am/ClientLifecycleManagerTests.java b/services/tests/servicestests/src/com/android/server/am/ClientLifecycleManagerTests.java
deleted file mode 100644
index b4ad183..0000000
--- a/services/tests/servicestests/src/com/android/server/am/ClientLifecycleManagerTests.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.android.server.am;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.app.IApplicationThread;
-import android.app.servertransaction.ClientTransaction;
-import android.os.Binder;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class ClientLifecycleManagerTests {
-
-    @Test
-    public void testScheduleAndRecycleBinderClientTransaction() throws Exception {
-        ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.class),
-                new Binder()));
-
-        ClientLifecycleManager clientLifecycleManager = new ClientLifecycleManager();
-        clientLifecycleManager.scheduleTransaction(item);
-
-        verify(item, times(1)).recycle();
-    }
-
-    @Test
-    public void testScheduleNoRecycleNonBinderClientTransaction() throws Exception {
-        ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.Stub.class),
-                new Binder()));
-
-        ClientLifecycleManager clientLifecycleManager = new ClientLifecycleManager();
-        clientLifecycleManager.scheduleTransaction(item);
-
-        verify(item, times(0)).recycle();
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
index 719e0ed..9626990 100644
--- a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -11,15 +11,18 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.am;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.android.server.am.ActivityManagerService.Injector;
 
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -28,9 +31,7 @@
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.AppOpsService;
@@ -39,7 +40,6 @@
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -48,20 +48,10 @@
 /**
  * Test class for {@link CoreSettingsObserver}.
  *
- * To run the tests, use
- *
- * runtest -c com.android.server.am.CoreSettingsObserverTest frameworks-services
- *
- * or the following steps:
- *
- * Build: m FrameworksServicesTests
- * Install: adb install -r \
- *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -e class com.android.server.am.CoreSettingsObserverTest -w \
- *     com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:CoreSettingsObserverTest
  */
 @SmallTest
-@RunWith(AndroidJUnit4.class)
 public class CoreSettingsObserverTest {
     private static final String TEST_SETTING_SECURE_INT = "secureInt";
     private static final String TEST_SETTING_GLOBAL_FLOAT = "globalFloat";
@@ -94,7 +84,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        final Context originalContext = InstrumentationRegistry.getContext();
+        final Context originalContext = getInstrumentation().getTargetContext();
         when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo());
         mContentResolver = new MockContentResolver(mContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
@@ -124,12 +114,12 @@
     public void testPopulateSettings_settingNotSet() {
         final Bundle settingsBundle = getPopulatedBundle();
 
-        assertFalse("Bundle should not contain " + TEST_SETTING_SECURE_INT,
-                settingsBundle.containsKey(TEST_SETTING_SECURE_INT));
-        assertFalse("Bundle should not contain " + TEST_SETTING_GLOBAL_FLOAT,
-                settingsBundle.containsKey(TEST_SETTING_GLOBAL_FLOAT));
-        assertFalse("Bundle should not contain " + TEST_SETTING_SYSTEM_STRING,
-                settingsBundle.containsKey(TEST_SETTING_SYSTEM_STRING));
+        assertWithMessage("Bundle should not contain " + TEST_SETTING_SECURE_INT)
+                .that(settingsBundle.keySet()).doesNotContain(TEST_SETTING_SECURE_INT);
+        assertWithMessage("Bundle should not contain " + TEST_SETTING_GLOBAL_FLOAT)
+                .that(settingsBundle.keySet()).doesNotContain(TEST_SETTING_GLOBAL_FLOAT);
+        assertWithMessage("Bundle should not contain " + TEST_SETTING_SYSTEM_STRING)
+                .that(settingsBundle.keySet()).doesNotContain(TEST_SETTING_SYSTEM_STRING);
     }
 
     @Test
@@ -150,8 +140,8 @@
         Settings.Global.putString(mContentResolver, TEST_SETTING_GLOBAL_FLOAT, null);
         settingsBundle = getPopulatedBundle();
 
-        assertFalse("Bundle should not contain " + TEST_SETTING_GLOBAL_FLOAT,
-                settingsBundle.containsKey(TEST_SETTING_GLOBAL_FLOAT));
+        assertWithMessage("Bundle should not contain " + TEST_SETTING_GLOBAL_FLOAT)
+                .that(settingsBundle.keySet()).doesNotContain(TEST_SETTING_GLOBAL_FLOAT);
         assertEquals("Unexpected value of " + TEST_SETTING_SECURE_INT,
                 TEST_INT, settingsBundle.getInt(TEST_SETTING_SECURE_INT));
         assertEquals("Unexpected value of " + TEST_SETTING_SYSTEM_STRING,
@@ -170,6 +160,7 @@
             return mContext;
         }
 
+        @Override
         public AppOpsService getAppOpsService(File file, Handler handler) {
             return null;
         }
diff --git a/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java b/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
index 765aaad..c162c3b 100644
--- a/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
@@ -11,34 +11,37 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.am;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
 import android.content.ContentResolver;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.test.FakeSettingsProvider;
 
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.HashMap;
 import java.util.Map;
 
 /**
  * Tests for {@link GlobalSettingsToPropertiesMapper}
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:GlobalSettingsToPropertiesMapperTest
  */
-@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class GlobalSettingsToPropertiesMapperTest {
     private static final String[][] TEST_MAPPING = new String[][] {
@@ -51,7 +54,7 @@
     @Before
     public void setup() {
         // Use FakeSettingsProvider to not affect global state
-        mMockContentResolver = new MockContentResolver(InstrumentationRegistry.getContext());
+        mMockContentResolver = new MockContentResolver(getInstrumentation().getTargetContext());
         mMockContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         mTestMapper = new TestMapper(mMockContentResolver);
     }
@@ -63,21 +66,21 @@
 
         mTestMapper.updatePropertiesFromGlobalSettings();
         String propValue = mTestMapper.systemPropertiesGet("TestProperty");
-        Assert.assertEquals("testValue", propValue);
+        assertEquals("testValue", propValue);
 
         Settings.Global.putString(mMockContentResolver,
                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
         mTestMapper.updatePropertyFromSetting(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
                 "TestProperty");
         propValue = mTestMapper.systemPropertiesGet("TestProperty");
-        Assert.assertEquals("testValue2", propValue);
+        assertEquals("testValue2", propValue);
 
         Settings.Global.putString(mMockContentResolver,
                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
         mTestMapper.updatePropertyFromSetting(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
                 "TestProperty");
         propValue = mTestMapper.systemPropertiesGet("TestProperty");
-        Assert.assertEquals("", propValue);
+        assertEquals("", propValue);
     }
 
     @Test
@@ -85,7 +88,7 @@
         // Test that empty property will not not be set if setting is not set
         mTestMapper.updatePropertiesFromGlobalSettings();
         String propValue = mTestMapper.systemPropertiesGet("TestProperty");
-        Assert.assertNull("Property should not be set if setting is null", propValue);
+        assertNull("Property should not be set if setting is null", propValue);
     }
 
     private static class TestMapper extends GlobalSettingsToPropertiesMapper {
@@ -108,6 +111,5 @@
             mProps.put(key, value);
         }
     }
-
 }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
deleted file mode 100644
index 2fb10e1..0000000
--- a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.app.ActivityOptions;
-import android.content.pm.ActivityInfo.WindowLayout;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.am.LaunchParamsController.LaunchParams;
-import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for exercising {@link LaunchParamsController}.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:LaunchParamsControllerTests
- */
-@MediumTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class LaunchParamsControllerTests extends ActivityTestsBase {
-    private ActivityTaskManagerService mService;
-    private LaunchParamsController mController;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        mService = createActivityTaskManagerService();
-        mController = new LaunchParamsController(mService);
-    }
-
-    /**
-     * Makes sure positioners get values passed to controller.
-     */
-    @Test
-    public void testArgumentPropagation() {
-        final LaunchParamsModifier
-                positioner = mock(LaunchParamsModifier.class);
-        mController.registerModifier(positioner);
-
-        final ActivityRecord record = new ActivityBuilder(mService).build();
-        final ActivityRecord source = new ActivityBuilder(mService).build();
-        final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
-        final ActivityOptions options = mock(ActivityOptions.class);
-
-        mController.calculate(record.getTask(), layout, record, source, options,
-                new LaunchParams());
-        verify(positioner, times(1)).onCalculate(eq(record.getTask()), eq(layout), eq(record),
-                eq(source), eq(options), any(), any());
-    }
-
-    /**
-     * Ensures positioners further down the chain are not called when RESULT_DONE is returned.
-     */
-    @Test
-    public void testEarlyExit() {
-        final LaunchParamsModifier
-                ignoredPositioner = mock(LaunchParamsModifier.class);
-        final LaunchParamsModifier earlyExitPositioner =
-                (task, layout, activity, source, options, currentParams, outParams) -> RESULT_DONE;
-
-        mController.registerModifier(ignoredPositioner);
-        mController.registerModifier(earlyExitPositioner);
-
-        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
-                null /*source*/, null /*options*/, new LaunchParams());
-        verify(ignoredPositioner, never()).onCalculate(any(), any(), any(), any(), any(),
-                any(), any());
-    }
-
-    /**
-     * Ensures that positioners are called in the correct order.
-     */
-    @Test
-    public void testRegistration() {
-        LaunchParamsModifier earlyExitPositioner =
-                new InstrumentedPositioner(RESULT_DONE, new LaunchParams());
-
-        final LaunchParamsModifier firstPositioner = spy(earlyExitPositioner);
-
-        mController.registerModifier(firstPositioner);
-
-        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
-                null /*source*/, null /*options*/, new LaunchParams());
-        verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
-                any());
-
-        final LaunchParamsModifier secondPositioner = spy(earlyExitPositioner);
-
-        mController.registerModifier(secondPositioner);
-
-        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
-                null /*source*/, null /*options*/, new LaunchParams());
-        verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
-                any());
-        verify(secondPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
-                any());
-    }
-
-    /**
-     * Makes sure positioners further down the registration chain are called.
-     */
-    @Test
-    public void testPassThrough() {
-        final LaunchParamsModifier
-                positioner1 = mock(LaunchParamsModifier.class);
-        final LaunchParams params = new LaunchParams();
-        params.mWindowingMode = WINDOWING_MODE_FREEFORM;
-        params.mBounds.set(0, 0, 30, 20);
-        params.mPreferredDisplayId = 3;
-
-        final InstrumentedPositioner positioner2 = new InstrumentedPositioner(RESULT_CONTINUE,
-                params);
-
-        mController.registerModifier(positioner1);
-        mController.registerModifier(positioner2);
-
-        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
-                null /*options*/, new LaunchParams());
-
-        verify(positioner1, times(1)).onCalculate(any(), any(), any(), any(), any(),
-                eq(positioner2.getLaunchParams()), any());
-    }
-
-    /**
-     * Ensures skipped results are not propagated.
-     */
-    @Test
-    public void testSkip() {
-        final LaunchParams params1 = new LaunchParams();
-        params1.mBounds.set(0, 0, 10, 10);
-        final InstrumentedPositioner positioner1 = new InstrumentedPositioner(RESULT_SKIP, params1);
-
-        final LaunchParams params2 = new LaunchParams();
-        params2.mBounds.set(0, 0, 20, 30);
-        final InstrumentedPositioner positioner2 =
-                new InstrumentedPositioner(RESULT_CONTINUE, params2);
-
-        mController.registerModifier(positioner1);
-        mController.registerModifier(positioner2);
-
-        final LaunchParams
-                result = new LaunchParams();
-
-        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
-                null /*options*/, result);
-
-        assertEquals(result, positioner2.getLaunchParams());
-    }
-
-    /**
-     * Tests preferred display id calculation for VR.
-     */
-    @Test
-    public void testVrPreferredDisplay() {
-        final int vr2dDisplayId = 1;
-        mService.mVr2dDisplayId = vr2dDisplayId;
-
-        final LaunchParams result = new LaunchParams();
-        final ActivityRecord vrActivity = new ActivityBuilder(mService).build();
-        vrActivity.requestedVrComponent = vrActivity.realActivity;
-
-        // VR activities should always land on default display.
-        mController.calculate(null /*task*/, null /*layout*/, vrActivity /*activity*/,
-                null /*source*/, null /*options*/, result);
-        assertEquals(DEFAULT_DISPLAY, result.mPreferredDisplayId);
-
-        // Otherwise, always lands on VR 2D display.
-        final ActivityRecord vr2dActivity = new ActivityBuilder(mService).build();
-        mController.calculate(null /*task*/, null /*layout*/, vr2dActivity /*activity*/,
-                null /*source*/, null /*options*/, result);
-        assertEquals(vr2dDisplayId, result.mPreferredDisplayId);
-        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
-                null /*options*/, result);
-        assertEquals(vr2dDisplayId, result.mPreferredDisplayId);
-
-        mService.mVr2dDisplayId = INVALID_DISPLAY;
-    }
-
-    /**
-     * Ensures that {@link LaunchParamsModifier} requests specifying display id during
-     * layout are honored.
-     */
-    @Test
-    public void testLayoutTaskPreferredDisplayChange() {
-        final LaunchParams params = new LaunchParams();
-        params.mPreferredDisplayId = 2;
-        final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
-        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor).build();
-
-        mController.registerModifier(positioner);
-
-        doNothing().when(mService).moveStackToDisplay(anyInt(), anyInt());
-        mController.layoutTask(task, null /* windowLayout */);
-        verify(mService, times(1)).moveStackToDisplay(eq(task.getStackId()),
-                eq(params.mPreferredDisplayId));
-    }
-
-    /**
-     * Ensures that {@link LaunchParamsModifier} requests specifying windowingMode during
-     * layout are honored.
-     */
-    @Test
-    public void testLayoutTaskWindowingModeChange() {
-        final LaunchParams params = new LaunchParams();
-        final int windowingMode = WINDOWING_MODE_FREEFORM;
-        params.mWindowingMode = windowingMode;
-        final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
-        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor).build();
-
-        mController.registerModifier(positioner);
-
-        final int beforeWindowMode = task.getStack().getWindowingMode();
-        assertNotEquals(beforeWindowMode, windowingMode);
-
-        mController.layoutTask(task, null /* windowLayout */);
-
-        final int afterWindowMode = task.getStack().getWindowingMode();
-        assertEquals(afterWindowMode, windowingMode);
-    }
-
-    public static class InstrumentedPositioner implements
-            LaunchParamsModifier {
-
-        final private int mReturnVal;
-        final private LaunchParams mParams;
-
-        InstrumentedPositioner(int returnVal, LaunchParams params) {
-            mReturnVal = returnVal;
-            mParams = params;
-        }
-
-        @Override
-        public int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
-                   ActivityRecord source, ActivityOptions options,
-                   LaunchParams currentParams, LaunchParams outParams) {
-            outParams.set(mParams);
-            return mReturnVal;
-        }
-
-        LaunchParams getLaunchParams() {
-            return mParams;
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
deleted file mode 100644
index 863a0d8..0000000
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ /dev/null
@@ -1,681 +0,0 @@
-/*
- * Copyright 2017, 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.server.am;
-
-import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
-import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.StatusBarManager.DISABLE2_MASK;
-import static android.app.StatusBarManager.DISABLE2_NONE;
-import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
-import static android.app.StatusBarManager.DISABLE_HOME;
-import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
-import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
-import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
-import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
-import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
-import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
-import static android.os.Process.SYSTEM_UID;
-import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
-
-import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_LOCKED;
-import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_PINNED;
-
-import static org.junit.Assert.*;
-import static org.mockito.ArgumentMatchers.*;
-import static org.mockito.Mockito.*;
-
-import android.app.StatusBarManager;
-import android.app.admin.DevicePolicyManager;
-import android.app.admin.IDevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.UserHandle;
-import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
-import android.telecom.TelecomManager;
-import android.testing.DexmakerShareClassLoaderRule;
-import android.util.Pair;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.server.LocalServices;
-import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.WindowManagerService;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.verification.VerificationMode;
-
-/**
- * Unit tests for {@link LockTaskController}.
- *
- * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.am.LockTaskControllerTest
- */
-@Presubmit
-@SmallTest
-public class LockTaskControllerTest {
-    private static final String TEST_PACKAGE_NAME = "com.test.package";
-    private static final String TEST_PACKAGE_NAME_2 = "com.test.package2";
-    private static final String TEST_CLASS_NAME = ".TestClass";
-    private static final int TEST_USER_ID = 123;
-    private static final int TEST_UID = 10467;
-
-    @Rule
-    public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
-            new DexmakerShareClassLoaderRule();
-
-    @Mock private ActivityStackSupervisor mSupervisor;
-    @Mock private IDevicePolicyManager mDevicePolicyManager;
-    @Mock private IStatusBarService mStatusBarService;
-    @Mock private WindowManagerService mWindowManager;
-    @Mock private LockPatternUtils mLockPatternUtils;
-    @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
-    @Mock private TelecomManager mTelecomManager;
-    @Mock private RecentTasks mRecentTasks;
-
-    private LockTaskController mLockTaskController;
-    private Context mContext;
-    private String mLockToAppSetting;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        mContext = InstrumentationRegistry.getTargetContext();
-        mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(),
-                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED);
-
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
-        mSupervisor.mRecentTasks = mRecentTasks;
-
-        mLockTaskController = new LockTaskController(mContext, mSupervisor,
-                new ImmediatelyExecuteHandler());
-        mLockTaskController.setWindowManager(mWindowManager);
-        mLockTaskController.mStatusBarService = mStatusBarService;
-        mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
-        mLockTaskController.mTelecomManager = mTelecomManager;
-        mLockTaskController.mLockPatternUtils = mLockPatternUtils;
-
-        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
-        LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        Settings.Secure.putString(mContext.getContentResolver(),
-                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting);
-    }
-
-    @Test
-    public void testPreconditions() {
-        // GIVEN nothing has happened
-
-        // THEN current lock task mode should be NONE
-        assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
-    }
-
-    @Test
-    public void testStartLockTaskMode_once() throws Exception {
-        // GIVEN a task record with whitelisted auth
-        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-
-        // WHEN calling setLockTaskMode for LOCKED mode without resuming
-        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
-
-        // THEN the lock task mode state should be LOCKED
-        assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
-        // THEN the task should be locked
-        assertTrue(mLockTaskController.isTaskLocked(tr));
-
-        // THEN lock task mode should be started
-        verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
-    }
-
-    @Test
-    public void testStartLockTaskMode_twice() throws Exception {
-        // GIVEN two task records with whitelisted auth
-        TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-
-        // WHEN calling setLockTaskMode for LOCKED mode on both tasks
-        mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
-        mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
-
-        // THEN the lock task mode state should be LOCKED
-        assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
-        // THEN neither of the tasks should be able to move to back of stack
-        assertTrue(mLockTaskController.isTaskLocked(tr1));
-        assertTrue(mLockTaskController.isTaskLocked(tr2));
-
-        // THEN lock task mode should be started
-        verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
-    }
-
-    @Test
-    public void testStartLockTaskMode_pinningRequest() throws Exception {
-        // GIVEN a task record that is not whitelisted, i.e. with pinned auth
-        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
-
-        // WHEN calling startLockTaskMode
-        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
-
-        // THEN a pinning request should be shown
-        verify(mStatusBarManagerInternal).showScreenPinningRequest(anyInt());
-    }
-
-    @Test
-    public void testStartLockTaskMode_pinnedBySystem() throws Exception {
-        // GIVEN a task record with pinned auth
-        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
-
-        // WHEN the system calls startLockTaskMode
-        mLockTaskController.startLockTaskMode(tr, true, SYSTEM_UID);
-
-        // THEN the lock task mode state should be PINNED
-        assertEquals(LOCK_TASK_MODE_PINNED, mLockTaskController.getLockTaskModeState());
-        // THEN the task should be locked
-        assertTrue(mLockTaskController.isTaskLocked(tr));
-
-        // THEN lock task mode should be started
-        verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
-        // THEN screen pinning toast should be shown
-        verify(mStatusBarService).showPinningEnterExitToast(true /* entering */);
-    }
-
-    @Test
-    public void testLockTaskViolation() throws Exception {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
-
-        // THEN it's not a lock task violation to try and launch this task without clearing
-        assertFalse(mLockTaskController.isLockTaskModeViolation(tr, false));
-
-        // THEN it's a lock task violation to launch another task that is not whitelisted
-        assertTrue(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
-                TaskRecord.LOCK_TASK_AUTH_PINNABLE)));
-        // THEN it's a lock task violation to launch another task that is disallowed from lock task
-        assertTrue(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
-                TaskRecord.LOCK_TASK_AUTH_DONT_LOCK)));
-
-        // THEN it's no a lock task violation to launch another task that is whitelisted
-        assertFalse(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
-                TaskRecord.LOCK_TASK_AUTH_WHITELISTED)));
-        assertFalse(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
-                TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE)));
-        // THEN it's not a lock task violation to launch another task that is priv launchable
-        assertFalse(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
-                TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV)));
-    }
-
-    @Test
-    public void testLockTaskViolation_emergencyCall() throws Exception {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
-
-        // GIVEN tasks necessary for emergency calling
-        TaskRecord keypad = getTaskRecord(new Intent().setComponent(EMERGENCY_DIALER_COMPONENT),
-                TaskRecord.LOCK_TASK_AUTH_PINNABLE);
-        TaskRecord callAction = getTaskRecord(new Intent(Intent.ACTION_CALL_EMERGENCY),
-                TaskRecord.LOCK_TASK_AUTH_PINNABLE);
-        TaskRecord dialer = getTaskRecord("com.example.dialer", TaskRecord.LOCK_TASK_AUTH_PINNABLE);
-        when(mTelecomManager.getSystemDialerPackage())
-                .thenReturn(dialer.intent.getComponent().getPackageName());
-
-        // GIVEN keyguard is allowed for lock task mode
-        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD);
-
-        // THEN the above tasks should all be allowed
-        assertFalse(mLockTaskController.isLockTaskModeViolation(keypad));
-        assertFalse(mLockTaskController.isLockTaskModeViolation(callAction));
-        assertFalse(mLockTaskController.isLockTaskModeViolation(dialer));
-
-        // GIVEN keyguard is disallowed for lock task mode (default)
-        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE);
-
-        // THEN the above tasks should all be blocked
-        assertTrue(mLockTaskController.isLockTaskModeViolation(keypad));
-        assertTrue(mLockTaskController.isLockTaskModeViolation(callAction));
-        assertTrue(mLockTaskController.isLockTaskModeViolation(dialer));
-    }
-
-    @Test
-    public void testStopLockTaskMode() throws Exception {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
-
-        // WHEN the same caller calls stopLockTaskMode
-        mLockTaskController.stopLockTaskMode(tr, false, TEST_UID);
-
-        // THEN the lock task mode should be NONE
-        assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
-        // THEN the task should no longer be locked
-        assertFalse(mLockTaskController.isTaskLocked(tr));
-        // THEN lock task mode should have been finished
-        verifyLockTaskStopped(times(1));
-    }
-
-    @Test(expected = SecurityException.class)
-    public void testStopLockTaskMode_differentCaller() throws Exception {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
-
-        // WHEN a different caller calls stopLockTaskMode
-        mLockTaskController.stopLockTaskMode(tr, false, TEST_UID + 1);
-
-        // THEN security exception should be thrown, because different caller tried to unlock
-    }
-
-    @Test
-    public void testStopLockTaskMode_systemCaller() throws Exception {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
-
-        // WHEN system calls stopLockTaskMode
-        mLockTaskController.stopLockTaskMode(tr, true, SYSTEM_UID);
-
-        // THEN lock task mode should still be active
-        assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
-    }
-
-    @Test
-    public void testStopLockTaskMode_twoTasks() throws Exception {
-        // GIVEN two task records with whitelisted auth that is in lock task mode
-        TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
-        mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
-
-        // WHEN calling stopLockTaskMode
-        mLockTaskController.stopLockTaskMode(tr2, false, TEST_UID);
-
-        // THEN the lock task mode should still be active
-        assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
-        // THEN the first task should still be locked
-        assertTrue(mLockTaskController.isTaskLocked(tr1));
-        // THEN the top task should no longer be locked
-        assertFalse(mLockTaskController.isTaskLocked(tr2));
-        // THEN lock task mode should not have been finished
-        verifyLockTaskStopped(never());
-    }
-
-    @Test
-    public void testStopLockTaskMode_rootTask() throws Exception {
-        // GIVEN two task records with whitelisted auth that is in lock task mode
-        TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
-        mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
-
-        // WHEN calling stopLockTaskMode on the root task
-        mLockTaskController.stopLockTaskMode(tr1, false, TEST_UID);
-
-        // THEN the lock task mode should be inactive
-        assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
-        // THEN the first task should no longer be locked
-        assertFalse(mLockTaskController.isTaskLocked(tr1));
-        // THEN the top task should no longer be locked
-        assertFalse(mLockTaskController.isTaskLocked(tr2));
-        // THEN lock task mode should be finished
-        verifyLockTaskStopped(times(1));
-    }
-
-    @Test
-    public void testStopLockTaskMode_pinned() throws Exception {
-        // GIVEN one task records that is in pinned mode
-        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
-        mLockTaskController.startLockTaskMode(tr, true, SYSTEM_UID);
-        // GIVEN that the keyguard is required to show after unlocking
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1);
-
-        // reset invocation counter
-        reset(mStatusBarService);
-
-        // WHEN calling stopLockTask
-        mLockTaskController.stopLockTaskMode(null, true, SYSTEM_UID);
-
-        // THEN the lock task mode should no longer be active
-        assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
-        // THEN the task should no longer be locked
-        assertFalse(mLockTaskController.isTaskLocked(tr));
-        // THEN lock task mode should have been finished
-        verifyLockTaskStopped(times(1));
-        // THEN the keyguard should be shown
-        verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL);
-        // THEN screen pinning toast should be shown
-        verify(mStatusBarService).showPinningEnterExitToast(false /* entering */);
-    }
-
-    @Test
-    public void testClearLockedTasks() throws Exception {
-        // GIVEN two task records with whitelisted auth that is in lock task mode
-        TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
-        mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
-
-        // WHEN calling stopLockTaskMode on the root task
-        mLockTaskController.clearLockedTasks("testClearLockedTasks");
-
-        // THEN the lock task mode should be inactive
-        assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
-        // THEN the first task should no longer be locked
-        assertFalse(mLockTaskController.isTaskLocked(tr1));
-        // THEN the top task should no longer be locked
-        assertFalse(mLockTaskController.isTaskLocked(tr2));
-        // THEN lock task mode should be finished
-        verifyLockTaskStopped(times(1));
-    }
-
-    @Test
-    public void testUpdateLockTaskPackages() throws Exception {
-        String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
-        String[] whitelist2 = {TEST_PACKAGE_NAME};
-
-        // No package is whitelisted initially
-        for (String pkg : whitelist1) {
-            assertFalse("Package shouldn't be whitelisted: " + pkg,
-                    mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg));
-            assertFalse("Package shouldn't be whitelisted for user 0: " + pkg,
-                    mLockTaskController.isPackageWhitelisted(0, pkg));
-        }
-
-        // Apply whitelist
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist1);
-
-        // Assert the whitelist is applied to the correct user
-        for (String pkg : whitelist1) {
-            assertTrue("Package should be whitelisted: " + pkg,
-                    mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg));
-            assertFalse("Package shouldn't be whitelisted for user 0: " + pkg,
-                    mLockTaskController.isPackageWhitelisted(0, pkg));
-        }
-
-        // Update whitelist
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist2);
-
-        // Assert the new whitelist is applied
-        assertTrue("Package should remain whitelisted: " + TEST_PACKAGE_NAME,
-                mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME));
-        assertFalse("Package should no longer be whitelisted: " + TEST_PACKAGE_NAME_2,
-                mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME_2));
-    }
-
-    @Test
-    public void testUpdateLockTaskPackages_taskRemoved() throws Exception {
-        // GIVEN two tasks which are whitelisted initially
-        TaskRecord tr1 = getTaskRecordForUpdate(TEST_PACKAGE_NAME, true);
-        TaskRecord tr2 = getTaskRecordForUpdate(TEST_PACKAGE_NAME_2, false);
-        String[] whitelist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
-
-        // GIVEN the tasks are launched into LockTask mode
-        mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
-        mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
-        assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
-        assertTrue(mLockTaskController.isTaskLocked(tr1));
-        assertTrue(mLockTaskController.isTaskLocked(tr2));
-        verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
-
-        // WHEN removing one package from whitelist
-        whitelist = new String[] {TEST_PACKAGE_NAME};
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
-
-        // THEN the task running that package should be stopped
-        verify(tr2).performClearTaskLocked();
-        assertFalse(mLockTaskController.isTaskLocked(tr2));
-        // THEN the other task should remain locked
-        assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
-        assertTrue(mLockTaskController.isTaskLocked(tr1));
-        verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
-
-        // WHEN removing the last package from whitelist
-        whitelist = new String[] {};
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
-
-        // THEN the last task should be cleared, and the system should quit LockTask mode
-        verify(tr1).performClearTaskLocked();
-        assertFalse(mLockTaskController.isTaskLocked(tr1));
-        assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
-        verifyLockTaskStopped(times(1));
-    }
-
-    @Test
-    public void testUpdateLockTaskFeatures() throws Exception {
-        // GIVEN a locked task
-        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
-
-        // THEN lock task mode should be started with default status bar masks
-        verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
-
-        // reset invocation counter
-        reset(mStatusBarService);
-
-        // WHEN home button is enabled for lock task mode
-        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_HOME);
-
-        // THEN status bar should be updated to reflect this change
-        int expectedFlags = STATUS_BAR_MASK_LOCKED
-                & ~DISABLE_HOME;
-        int expectedFlags2 = DISABLE2_MASK;
-        verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
-                eq(mContext.getPackageName()));
-        verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
-                eq(mContext.getPackageName()));
-
-        // reset invocation counter
-        reset(mStatusBarService);
-
-        // WHEN notifications are enabled for lock task mode
-        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NOTIFICATIONS);
-
-        // THEN status bar should be updated to reflect this change
-        expectedFlags = STATUS_BAR_MASK_LOCKED
-                & ~DISABLE_NOTIFICATION_ICONS
-                & ~DISABLE_NOTIFICATION_ALERTS;
-        expectedFlags2 = DISABLE2_MASK
-                & ~DISABLE2_NOTIFICATION_SHADE;
-        verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
-                eq(mContext.getPackageName()));
-        verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
-                eq(mContext.getPackageName()));
-    }
-
-    @Test
-    public void testUpdateLockTaskFeatures_differentUser() throws Exception {
-        // GIVEN a locked task
-        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
-
-        // THEN lock task mode should be started with default status bar masks
-        verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
-
-        // reset invocation counter
-        reset(mStatusBarService);
-
-        // WHEN home button is enabled for lock task mode for another user
-        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID + 1, LOCK_TASK_FEATURE_HOME);
-
-        // THEN status bar shouldn't change
-        verify(mStatusBarService, never()).disable(anyInt(), any(IBinder.class),
-                eq(mContext.getPackageName()));
-        verify(mStatusBarService, never()).disable2(anyInt(), any(IBinder.class),
-                eq(mContext.getPackageName()));
-    }
-
-    @Test
-    public void testUpdateLockTaskFeatures_keyguard() throws Exception {
-        // GIVEN a locked task
-        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
-        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
-
-        // THEN keyguard should be disabled
-        verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString());
-
-        // WHEN keyguard is enabled for lock task mode
-        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD);
-
-        // THEN keyguard should be enabled
-        verify(mWindowManager).reenableKeyguard(any(IBinder.class));
-
-        // WHEN keyguard is disabled again for lock task mode
-        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE);
-
-        // THEN keyguard should be disabled
-        verify(mWindowManager, times(2)).disableKeyguard(any(IBinder.class), anyString());
-    }
-
-    @Test
-    public void testGetStatusBarDisableFlags() {
-        // Note that we don't enumerate all StatusBarManager flags, but only choose a subset to test
-
-        // WHEN nothing is enabled
-        Pair<Integer, Integer> flags = mLockTaskController.getStatusBarDisableFlags(
-                LOCK_TASK_FEATURE_NONE);
-        // THEN unsupported feature flags should still be untouched
-        assertTrue((~STATUS_BAR_MASK_LOCKED & flags.first) == 0);
-        // THEN everything else should be disabled
-        assertTrue((StatusBarManager.DISABLE_CLOCK & flags.first) != 0);
-        assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0);
-
-        // WHEN only home button is enabled
-        flags = mLockTaskController.getStatusBarDisableFlags(
-                LOCK_TASK_FEATURE_HOME);
-        // THEN unsupported feature flags should still be untouched
-        assertTrue((~STATUS_BAR_MASK_LOCKED & flags.first) == 0);
-        // THEN home button should indeed be enabled
-        assertTrue((StatusBarManager.DISABLE_HOME & flags.first) == 0);
-        // THEN other feature flags should remain disabled
-        assertTrue((StatusBarManager.DISABLE2_NOTIFICATION_SHADE & flags.second) != 0);
-
-        // WHEN only global actions menu and notifications are enabled
-        flags = mLockTaskController.getStatusBarDisableFlags(
-                DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS
-                        | DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS);
-        // THEN unsupported feature flags should still be untouched
-        assertTrue((~STATUS_BAR_MASK_LOCKED & flags.first) == 0);
-        // THEN notifications should be enabled
-        assertTrue((StatusBarManager.DISABLE_NOTIFICATION_ICONS & flags.first) == 0);
-        assertTrue((StatusBarManager.DISABLE_NOTIFICATION_ALERTS & flags.first) == 0);
-        assertTrue((StatusBarManager.DISABLE2_NOTIFICATION_SHADE & flags.second) == 0);
-        // THEN global actions should be enabled
-        assertTrue((StatusBarManager.DISABLE2_GLOBAL_ACTIONS & flags.second) == 0);
-        // THEN quick settings should still be disabled
-        assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0);
-    }
-
-    private TaskRecord getTaskRecord(int lockTaskAuth) {
-        return getTaskRecord(TEST_PACKAGE_NAME, lockTaskAuth);
-    }
-
-    private TaskRecord getTaskRecord(String pkg, int lockTaskAuth) {
-        final Intent intent = new Intent()
-                .setComponent(ComponentName.createRelative(pkg, TEST_CLASS_NAME));
-        return getTaskRecord(intent, lockTaskAuth);
-    }
-
-    private TaskRecord getTaskRecord(Intent intent, int lockTaskAuth) {
-        TaskRecord tr = mock(TaskRecord.class);
-        tr.mLockTaskAuth = lockTaskAuth;
-        tr.intent = intent;
-        tr.userId = TEST_USER_ID;
-        return tr;
-    }
-
-    /**
-     * @param isAppAware {@code true} if the app has marked if_whitelisted in its manifest
-     */
-    private TaskRecord getTaskRecordForUpdate(String pkg, boolean isAppAware) {
-        final int authIfWhitelisted = isAppAware
-                ? TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE
-                : TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
-        TaskRecord tr = getTaskRecord(pkg, authIfWhitelisted);
-        doAnswer((invocation) -> {
-            boolean isWhitelisted =
-                    mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg);
-            tr.mLockTaskAuth = isWhitelisted
-                    ? authIfWhitelisted
-                    : TaskRecord.LOCK_TASK_AUTH_PINNABLE;
-            return null;
-        }).when(tr).setLockTaskAuth();
-        return tr;
-    }
-
-    private void verifyLockTaskStarted(int statusBarMask, int statusBarMask2) throws Exception {
-        // THEN the keyguard should have been disabled
-        verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString());
-        // THEN the status bar should have been disabled
-        verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class),
-                eq(mContext.getPackageName()));
-        verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class),
-                eq(mContext.getPackageName()));
-        // THEN recents should have been notified
-        verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID));
-        // THEN the DO/PO should be informed about the operation
-        verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME,
-                TEST_USER_ID);
-    }
-
-    private void verifyLockTaskStopped(VerificationMode mode) throws Exception {
-        // THEN the keyguard should have been disabled
-        verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class));
-        // THEN the status bar should have been disabled
-        verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE),
-                any(IBinder.class), eq(mContext.getPackageName()));
-        verify(mStatusBarService, mode).disable2(eq(StatusBarManager.DISABLE2_NONE),
-                any(IBinder.class), eq(mContext.getPackageName()));
-        // THEN the DO/PO should be informed about the operation
-        verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(false, null, TEST_USER_ID);
-    }
-
-    /**
-     * Special handler implementation that executes any message / runnable posted immediately on the
-     * thread that it's posted on rather than enqueuing them on its looper.
-     */
-    private static class ImmediatelyExecuteHandler extends Handler {
-        @Override
-        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
-            if (msg.getCallback() != null) {
-                msg.getCallback().run();
-            }
-            return true;
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index 9a283fe..c7409d7 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static com.android.server.am.MemoryStatUtil.BYTES_IN_KILOBYTE;
+import static com.android.server.am.MemoryStatUtil.JIFFY_NANOS;
 import static com.android.server.am.MemoryStatUtil.MemoryStat;
 import static com.android.server.am.MemoryStatUtil.PAGE_SIZE;
 import static com.android.server.am.MemoryStatUtil.parseMemoryMaxUsageFromMemCg;
@@ -28,14 +29,15 @@
 import static org.junit.Assert.assertNull;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.Collections;
 
-@RunWith(AndroidJUnit4.class)
+/**
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:MemoryStatUtilTest
+ */
 @SmallTest
 public class MemoryStatUtilTest {
     private static final String MEMORY_STAT_CONTENTS = String.join(
@@ -96,7 +98,7 @@
             "-2",
             "117",
             "0",
-            "2206",
+            "2222", // this in start time (in ticks per second)
             "1257177088",
             "3", // this is RSS (number of pages)
             "4294967295",
@@ -129,51 +131,51 @@
             "0");
 
     private static final String PROC_STATUS_CONTENTS = "Name:\tandroid.youtube\n"
-        + "State:\tS (sleeping)\n"
-        + "Tgid:\t12088\n"
-        + "Pid:\t12088\n"
-        + "PPid:\t723\n"
-        + "TracerPid:\t0\n"
-        + "Uid:\t10083\t10083\t10083\t10083\n"
-        + "Gid:\t10083\t10083\t10083\t10083\n"
-        + "Ngid:\t0\n"
-        + "FDSize:\t128\n"
-        + "Groups:\t3003 9997 20083 50083 \n"
-        + "VmPeak:\t 4546844 kB\n"
-        + "VmSize:\t 4542636 kB\n"
-        + "VmLck:\t       0 kB\n"
-        + "VmPin:\t       0 kB\n"
-        + "VmHWM:\t  137668 kB\n" // RSS high watermark
-        + "VmRSS:\t  126776 kB\n"
-        + "RssAnon:\t   37860 kB\n"
-        + "RssFile:\t   88764 kB\n"
-        + "RssShmem:\t     152 kB\n"
-        + "VmData:\t 4125112 kB\n"
-        + "VmStk:\t    8192 kB\n"
-        + "VmExe:\t      24 kB\n"
-        + "VmLib:\t  102432 kB\n"
-        + "VmPTE:\t    1300 kB\n"
-        + "VmPMD:\t      36 kB\n"
-        + "VmSwap:\t       0 kB\n"
-        + "Threads:\t95\n"
-        + "SigQ:\t0/13641\n"
-        + "SigPnd:\t0000000000000000\n"
-        + "ShdPnd:\t0000000000000000\n"
-        + "SigBlk:\t0000000000001204\n"
-        + "SigIgn:\t0000000000000001\n"
-        + "SigCgt:\t00000006400084f8\n"
-        + "CapInh:\t0000000000000000\n"
-        + "CapPrm:\t0000000000000000\n"
-        + "CapEff:\t0000000000000000\n"
-        + "CapBnd:\t0000000000000000\n"
-        + "CapAmb:\t0000000000000000\n"
-        + "Seccomp:\t2\n"
-        + "Cpus_allowed:\tff\n"
-        + "Cpus_allowed_list:\t0-7\n"
-        + "Mems_allowed:\t1\n"
-        + "Mems_allowed_list:\t0\n"
-        + "voluntary_ctxt_switches:\t903\n"
-        + "nonvoluntary_ctxt_switches:\t104\n";
+            + "State:\tS (sleeping)\n"
+            + "Tgid:\t12088\n"
+            + "Pid:\t12088\n"
+            + "PPid:\t723\n"
+            + "TracerPid:\t0\n"
+            + "Uid:\t10083\t10083\t10083\t10083\n"
+            + "Gid:\t10083\t10083\t10083\t10083\n"
+            + "Ngid:\t0\n"
+            + "FDSize:\t128\n"
+            + "Groups:\t3003 9997 20083 50083 \n"
+            + "VmPeak:\t 4546844 kB\n"
+            + "VmSize:\t 4542636 kB\n"
+            + "VmLck:\t       0 kB\n"
+            + "VmPin:\t       0 kB\n"
+            + "VmHWM:\t  137668 kB\n" // RSS high watermark
+            + "VmRSS:\t  126776 kB\n"
+            + "RssAnon:\t   37860 kB\n"
+            + "RssFile:\t   88764 kB\n"
+            + "RssShmem:\t     152 kB\n"
+            + "VmData:\t 4125112 kB\n"
+            + "VmStk:\t    8192 kB\n"
+            + "VmExe:\t      24 kB\n"
+            + "VmLib:\t  102432 kB\n"
+            + "VmPTE:\t    1300 kB\n"
+            + "VmPMD:\t      36 kB\n"
+            + "VmSwap:\t       0 kB\n"
+            + "Threads:\t95\n"
+            + "SigQ:\t0/13641\n"
+            + "SigPnd:\t0000000000000000\n"
+            + "ShdPnd:\t0000000000000000\n"
+            + "SigBlk:\t0000000000001204\n"
+            + "SigIgn:\t0000000000000001\n"
+            + "SigCgt:\t00000006400084f8\n"
+            + "CapInh:\t0000000000000000\n"
+            + "CapPrm:\t0000000000000000\n"
+            + "CapEff:\t0000000000000000\n"
+            + "CapBnd:\t0000000000000000\n"
+            + "CapAmb:\t0000000000000000\n"
+            + "Seccomp:\t2\n"
+            + "Cpus_allowed:\tff\n"
+            + "Cpus_allowed_list:\t0-7\n"
+            + "Mems_allowed:\t1\n"
+            + "Mems_allowed_list:\t0\n"
+            + "voluntary_ctxt_switches:\t903\n"
+            + "nonvoluntary_ctxt_switches:\t104\n";
 
     @Test
     public void testParseMemoryStatFromMemcg_parsesCorrectValues() {
@@ -219,6 +221,7 @@
         assertEquals(3 * PAGE_SIZE, stat.rssInBytes);
         assertEquals(0, stat.cacheInBytes);
         assertEquals(0, stat.swapInBytes);
+        assertEquals(2222 * JIFFY_NANOS, stat.startTimeNanos);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java b/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
deleted file mode 100644
index 1c4e0f6..0000000
--- a/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import android.app.ActivityOptions;
-import android.platform.test.annotations.Presubmit;
-import android.view.RemoteAnimationAdapter;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.testutils.OffsettableClock;
-import com.android.server.testutils.TestHandler;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * atest PendingRemoteAnimationRegistryTest
- */
-@SmallTest
-@Presubmit
-@FlakyTest
-@RunWith(AndroidJUnit4.class)
-public class PendingRemoteAnimationRegistryTest extends ActivityTestsBase {
-
-    @Mock RemoteAnimationAdapter mAdapter;
-    private PendingRemoteAnimationRegistry mRegistry;
-    private final OffsettableClock mClock = new OffsettableClock.Stopped();
-    private TestHandler mHandler;
-    private ActivityManagerService mService;
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-        MockitoAnnotations.initMocks(this);
-        mService = createActivityManagerService();
-        mService.mHandlerThread.getThreadHandler().runWithScissors(() -> {
-            mHandler = new TestHandler(null, mClock);
-        }, 0);
-        mRegistry = new PendingRemoteAnimationRegistry(mService.mActivityTaskManager, mHandler);
-    }
-
-    @Test
-    public void testOverrideActivityOptions() {
-        mRegistry.addPendingAnimation("com.android.test", mAdapter);
-        ActivityOptions opts = ActivityOptions.makeBasic();
-        opts = mRegistry.overrideOptionsIfNeeded("com.android.test", opts);
-        assertEquals(mAdapter, opts.getRemoteAnimationAdapter());
-    }
-
-    @Test
-    public void testOverrideActivityOptions_null() {
-        mRegistry.addPendingAnimation("com.android.test", mAdapter);
-        final ActivityOptions opts = mRegistry.overrideOptionsIfNeeded("com.android.test", null);
-        assertNotNull(opts);
-        assertEquals(mAdapter, opts.getRemoteAnimationAdapter());
-    }
-
-    @Test
-    public void testTimeout() {
-        mRegistry.addPendingAnimation("com.android.test", mAdapter);
-        mClock.fastForward(5000);
-        mHandler.timeAdvance();
-        assertNull(mRegistry.overrideOptionsIfNeeded("com.android.test", null));
-    }
-
-    @Test
-    public void testTimeout_overridenEntry() {
-        mRegistry.addPendingAnimation("com.android.test", mAdapter);
-        mClock.fastForward(2500);
-        mHandler.timeAdvance();
-        mRegistry.addPendingAnimation("com.android.test", mAdapter);
-        mClock.fastForward(1000);
-        mHandler.timeAdvance();
-        final ActivityOptions opts = mRegistry.overrideOptionsIfNeeded("com.android.test", null);
-        assertEquals(mAdapter, opts.getRemoteAnimationAdapter());
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/PersisterQueueTests.java b/services/tests/servicestests/src/com/android/server/am/PersisterQueueTests.java
deleted file mode 100644
index d7794b0..0000000
--- a/services/tests/servicestests/src/com/android/server/am/PersisterQueueTests.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertSame;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.os.SystemClock;
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Predicate;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-/**
- * atest PersisterQueueTests
- */
-@RunWith(AndroidJUnit4.class)
-@MediumTest
-@Presubmit
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
-public class PersisterQueueTests implements PersisterQueue.Listener {
-    private static final long INTER_WRITE_DELAY_MS = 50;
-    private static final long PRE_TASK_DELAY_MS = 300;
-    // We allow at most 1s more than the expected timeout.
-    private static final long TIMEOUT_ALLOWANCE = 100;
-
-    private static final Predicate<MatchingTestItem> TEST_ITEM_PREDICATE = item -> item.mMatching;
-
-    private AtomicInteger mItemCount;
-    private CountDownLatch mSetUpLatch;
-    private volatile CountDownLatch mLatch;
-    private List<Boolean> mProbablyDoneResults;
-
-    private PersisterQueue mTarget;
-
-    @Before
-    public void setUp() throws Exception {
-        mItemCount = new AtomicInteger(0);
-        mProbablyDoneResults = new ArrayList<>();
-        mSetUpLatch = new CountDownLatch(1);
-
-        mTarget = new PersisterQueue(INTER_WRITE_DELAY_MS, PRE_TASK_DELAY_MS);
-        mTarget.addListener(this);
-        mTarget.startPersisting();
-
-        assertTrue("Target didn't call callback on start up.",
-                mSetUpLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mTarget.stopPersisting();
-    }
-
-    @Test
-    public void testCallCallbackOnStartUp() throws Exception {
-        // The onPreProcessItem() must be called on start up.
-        assertEquals(1, mProbablyDoneResults.size());
-        // The last one must be called with probably done being true.
-        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(0));
-    }
-
-    @Test
-    public void testProcessOneItem() throws Exception {
-        mLatch = new CountDownLatch(1);
-
-        final long dispatchTime = SystemClock.uptimeMillis();
-        mTarget.addItem(new TestItem(), false);
-        assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
-        final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
-        assertTrue("Target didn't wait enough time before processing item. duration: "
-                        + processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS + "ms",
-                processDuration >= PRE_TASK_DELAY_MS);
-
-        // Once before processing this item, once after that.
-        assertEquals(2, mProbablyDoneResults.size());
-        // The last one must be called with probably done being true.
-        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
-    }
-
-    @Test
-    public void testProcessOneItem_Flush() throws Exception {
-        mLatch = new CountDownLatch(1);
-
-        final long dispatchTime = SystemClock.uptimeMillis();
-        mTarget.addItem(new TestItem(), true);
-        assertTrue("Target didn't call callback enough times.",
-                mLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
-        final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
-        assertTrue("Target didn't process item immediately when flushing. duration: "
-                        + processDuration + "ms pretask delay: "
-                        + PRE_TASK_DELAY_MS + "ms",
-                processDuration < PRE_TASK_DELAY_MS);
-
-        // Once before processing this item, once after that.
-        assertEquals(2, mProbablyDoneResults.size());
-        // The last one must be called with probably done being true.
-        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
-    }
-
-    @Test
-    public void testProcessTwoItems() throws Exception {
-        mLatch = new CountDownLatch(2);
-
-        final long dispatchTime = SystemClock.uptimeMillis();
-        mTarget.addItem(new TestItem(), false);
-        mTarget.addItem(new TestItem(), false);
-        assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS + TIMEOUT_ALLOWANCE,
-                        TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process all items.", 2, mItemCount.get());
-        final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
-        assertTrue("Target didn't wait enough time before processing item. duration: "
-                        + processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS
-                        + "ms inter write delay: " + INTER_WRITE_DELAY_MS + "ms",
-                processDuration >= PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS);
-
-        // Once before processing this item, once after that.
-        assertEquals(3, mProbablyDoneResults.size());
-        // The first one must be called with probably done being false.
-        assertFalse("The first probablyDone must be false.", mProbablyDoneResults.get(1));
-        // The last one must be called with probably done being true.
-        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
-    }
-
-    @Test
-    public void testProcessTwoItems_OneAfterAnother() throws Exception {
-        // First item
-        mLatch = new CountDownLatch(1);
-        long dispatchTime = SystemClock.uptimeMillis();
-        mTarget.addItem(new TestItem(), false);
-        assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        long processDuration = SystemClock.uptimeMillis() - dispatchTime;
-        assertTrue("Target didn't wait enough time before processing item."
-                        + processDuration + "ms pretask delay: "
-                        + PRE_TASK_DELAY_MS + "ms",
-                processDuration >= PRE_TASK_DELAY_MS);
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
-
-        // Second item
-        mLatch = new CountDownLatch(1);
-        dispatchTime = SystemClock.uptimeMillis();
-        // Synchronize on the instance to make sure we schedule the item after it starts to wait for
-        // task indefinitely.
-        synchronized (mTarget) {
-            mTarget.addItem(new TestItem(), false);
-        }
-        assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process all items.", 2, mItemCount.get());
-        processDuration = SystemClock.uptimeMillis() - dispatchTime;
-        assertTrue("Target didn't wait enough time before processing item."
-                        + processDuration + "ms pre task delay: "
-                        + PRE_TASK_DELAY_MS + "ms",
-                processDuration >= PRE_TASK_DELAY_MS);
-
-        // Once before processing this item, once after that.
-        assertEquals(3, mProbablyDoneResults.size());
-        // The last one must be called with probably done being true.
-        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
-    }
-
-    @Test
-    public void testFindLastItemNotReturnDifferentType() throws Exception {
-        synchronized (mTarget) {
-            mTarget.addItem(new TestItem(), false);
-            assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
-        }
-    }
-
-    @Test
-    public void testFindLastItemNotReturnMismatchItem() throws Exception {
-        synchronized (mTarget) {
-            mTarget.addItem(new MatchingTestItem(false), false);
-            assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
-        }
-    }
-
-    @Test
-    public void testFindLastItemReturnMatchedItem() throws Exception {
-        synchronized (mTarget) {
-            final MatchingTestItem item = new MatchingTestItem(true);
-            mTarget.addItem(item, false);
-            assertSame(item, mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
-        }
-    }
-
-    @Test
-    public void testRemoveItemsNotRemoveDifferentType() throws Exception {
-        mLatch = new CountDownLatch(1);
-        synchronized (mTarget) {
-            mTarget.addItem(new TestItem(), false);
-            mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
-        }
-        assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
-    }
-
-    @Test
-    public void testRemoveItemsNotRemoveMismatchedItem() throws Exception {
-        mLatch = new CountDownLatch(1);
-        synchronized (mTarget) {
-            mTarget.addItem(new MatchingTestItem(false), false);
-            mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
-        }
-        assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
-    }
-
-    @Test
-    public void testRemoveItemsRemoveMatchedItem() throws Exception {
-        mLatch = new CountDownLatch(1);
-        synchronized (mTarget) {
-            mTarget.addItem(new TestItem(), false);
-            mTarget.addItem(new MatchingTestItem(true), false);
-            mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
-        }
-        assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
-    }
-
-    @Test
-    public void testFlushWaitSynchronously() {
-        final long dispatchTime = SystemClock.uptimeMillis();
-        mTarget.addItem(new TestItem(), false);
-        mTarget.addItem(new TestItem(), false);
-        mTarget.flush();
-        assertEquals("Flush should wait until all items are processed before return.",
-                2, mItemCount.get());
-        final long processTime = SystemClock.uptimeMillis() - dispatchTime;
-        assertTrue("Flush should trigger immediate flush without delays. processTime: "
-                + processTime, processTime < TIMEOUT_ALLOWANCE);
-    }
-
-    @Override
-    public void onPreProcessItem(boolean queueEmpty) {
-        mProbablyDoneResults.add(queueEmpty);
-
-        final CountDownLatch latch = mLatch;
-        if (latch != null) {
-            latch.countDown();
-        }
-
-        mSetUpLatch.countDown();
-    }
-
-    private class TestItem implements PersisterQueue.WriteQueueItem {
-        @Override
-        public void process() {
-            mItemCount.getAndIncrement();
-        }
-    }
-
-    private class MatchingTestItem extends TestItem {
-        private boolean mMatching;
-
-        private MatchingTestItem(boolean matching) {
-            mMatching = matching;
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
deleted file mode 100644
index 27e8c63..0000000
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ /dev/null
@@ -1,1046 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-
-import android.app.ActivityManager.RecentTaskInfo;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityTaskManager;
-import android.app.WindowConfiguration;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.UserInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.platform.test.annotations.Presubmit;
-import android.util.MutableLong;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.am.RecentTasks.Callbacks;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static java.lang.Integer.MAX_VALUE;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-
-/**
- * atest FrameworksServicesTests:RecentTasksTest
- */
-@MediumTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class RecentTasksTest extends ActivityTestsBase {
-    private static final int TEST_USER_0_ID = 0;
-    private static final int TEST_USER_1_ID = 10;
-    private static final int TEST_QUIET_USER_ID = 20;
-    private static final UserInfo DEFAULT_USER_INFO = new UserInfo();
-    private static final UserInfo QUIET_USER_INFO = new UserInfo();
-    private static int LAST_TASK_ID = 1;
-    private static int LAST_STACK_ID = 1;
-    private static int INVALID_STACK_ID = 999;
-
-    private Context mContext = InstrumentationRegistry.getContext();
-    private TestActivityTaskManagerService mService;
-    private ActivityDisplay mDisplay;
-    private ActivityDisplay mOtherDisplay;
-    private ActivityStack mStack;
-    private ActivityStack mHomeStack;
-    private TestTaskPersister mTaskPersister;
-    private TestRecentTasks mRecentTasks;
-    private TestRunningTasks mRunningTasks;
-
-    private ArrayList<TaskRecord> mTasks;
-    private ArrayList<TaskRecord> mSameDocumentTasks;
-
-    private CallbacksRecorder mCallbacksRecorder;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
-        mService = spy(new MyTestActivityTaskManagerService(mContext));
-        final TestActivityManagerService am = spy(new MyTestActivityManagerService());
-        setupActivityManagerService(am, mService);
-        mRecentTasks = (TestRecentTasks) mService.getRecentTasks();
-        mRecentTasks.loadParametersFromResources(mContext.getResources());
-        mHomeStack = mService.mStackSupervisor.getDefaultDisplay().getOrCreateStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mCallbacksRecorder = new CallbacksRecorder();
-        mRecentTasks.registerCallback(mCallbacksRecorder);
-        QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE;
-
-        mTasks = new ArrayList<>();
-        mTasks.add(createTaskBuilder(".Task1").build());
-        mTasks.add(createTaskBuilder(".Task2").build());
-        mTasks.add(createTaskBuilder(".Task3").build());
-        mTasks.add(createTaskBuilder(".Task4").build());
-        mTasks.add(createTaskBuilder(".Task5").build());
-
-        mSameDocumentTasks = new ArrayList<>();
-        mSameDocumentTasks.add(createDocumentTask(".DocumentTask1"));
-        mSameDocumentTasks.add(createDocumentTask(".DocumentTask1"));
-    }
-
-    @Test
-    public void testCallbacks() throws Exception {
-        // Add some tasks
-        mRecentTasks.add(mTasks.get(0));
-        mRecentTasks.add(mTasks.get(1));
-        assertTrue(mCallbacksRecorder.added.contains(mTasks.get(0))
-                && mCallbacksRecorder.added.contains(mTasks.get(1)));
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.isEmpty());
-        mCallbacksRecorder.clear();
-
-        // Remove some tasks
-        mRecentTasks.remove(mTasks.get(0));
-        mRecentTasks.remove(mTasks.get(1));
-        assertTrue(mCallbacksRecorder.added.isEmpty());
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.contains(mTasks.get(0)));
-        assertTrue(mCallbacksRecorder.removed.contains(mTasks.get(1)));
-        mCallbacksRecorder.clear();
-
-        // Remove the callback, ensure we don't get any calls
-        mRecentTasks.unregisterCallback(mCallbacksRecorder);
-        mRecentTasks.add(mTasks.get(0));
-        mRecentTasks.remove(mTasks.get(0));
-        assertTrue(mCallbacksRecorder.added.isEmpty());
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.isEmpty());
-    }
-
-    @Test
-    public void testAddTasksNoMultiple_expectNoTrim() throws Exception {
-        // Add same non-multiple-task document tasks will remove the task (to re-add it) but not
-        // trim it
-        TaskRecord documentTask1 = createDocumentTask(".DocumentTask1");
-        TaskRecord documentTask2 = createDocumentTask(".DocumentTask1");
-        mRecentTasks.add(documentTask1);
-        mRecentTasks.add(documentTask2);
-        assertTrue(mCallbacksRecorder.added.contains(documentTask1));
-        assertTrue(mCallbacksRecorder.added.contains(documentTask2));
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.contains(documentTask1));
-    }
-
-    @Test
-    public void testAddTasksMaxTaskRecents_expectNoTrim() throws Exception {
-        // Add a task hitting max-recents for that app will remove the task (to add the next one)
-        // but not trim it
-        TaskRecord documentTask1 = createDocumentTask(".DocumentTask1");
-        TaskRecord documentTask2 = createDocumentTask(".DocumentTask1");
-        documentTask1.maxRecents = 1;
-        documentTask2.maxRecents = 1;
-        mRecentTasks.add(documentTask1);
-        mRecentTasks.add(documentTask2);
-        assertTrue(mCallbacksRecorder.added.contains(documentTask1));
-        assertTrue(mCallbacksRecorder.added.contains(documentTask2));
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.contains(documentTask1));
-    }
-
-    @Test
-    public void testAddTasksSameTask_expectNoTrim() throws Exception {
-        // Add a task that is already in the task list does not trigger any callbacks, it just
-        // moves in the list
-        TaskRecord documentTask1 = createDocumentTask(".DocumentTask1");
-        mRecentTasks.add(documentTask1);
-        mRecentTasks.add(documentTask1);
-        assertTrue(mCallbacksRecorder.added.size() == 1);
-        assertTrue(mCallbacksRecorder.added.contains(documentTask1));
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.isEmpty());
-    }
-
-    @Test
-    public void testAddTasksMultipleDocumentTasks_expectNoTrim() throws Exception {
-        // Add same multiple-task document tasks does not trim the first tasks
-        TaskRecord documentTask1 = createDocumentTask(".DocumentTask1",
-                FLAG_ACTIVITY_MULTIPLE_TASK);
-        TaskRecord documentTask2 = createDocumentTask(".DocumentTask1",
-                FLAG_ACTIVITY_MULTIPLE_TASK);
-        mRecentTasks.add(documentTask1);
-        mRecentTasks.add(documentTask2);
-        assertTrue(mCallbacksRecorder.added.size() == 2);
-        assertTrue(mCallbacksRecorder.added.contains(documentTask1));
-        assertTrue(mCallbacksRecorder.added.contains(documentTask2));
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.isEmpty());
-    }
-
-    @Test
-    public void testAddTasksMultipleTasks_expectRemovedNoTrim() throws Exception {
-        // Add multiple same-affinity non-document tasks, ensure that it removes the other task,
-        // but that it does not trim it
-        TaskRecord task1 = createTaskBuilder(".Task1")
-                .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
-                .build();
-        TaskRecord task2 = createTaskBuilder(".Task1")
-                .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
-                .build();
-        mRecentTasks.add(task1);
-        assertTrue(mCallbacksRecorder.added.size() == 1);
-        assertTrue(mCallbacksRecorder.added.contains(task1));
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.isEmpty());
-        mCallbacksRecorder.clear();
-        mRecentTasks.add(task2);
-        assertTrue(mCallbacksRecorder.added.size() == 1);
-        assertTrue(mCallbacksRecorder.added.contains(task2));
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.size() == 1);
-        assertTrue(mCallbacksRecorder.removed.contains(task1));
-    }
-
-    @Test
-    public void testAddTasksDifferentStacks_expectNoRemove() throws Exception {
-        // Adding the same task with different activity types should not trigger removal of the
-        // other task
-        TaskRecord task1 = createTaskBuilder(".Task1")
-                .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
-                .setStack(mHomeStack).build();
-        TaskRecord task2 = createTaskBuilder(".Task1")
-                .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
-                .setStack(mStack).build();
-        mRecentTasks.add(task1);
-        mRecentTasks.add(task2);
-        assertTrue(mCallbacksRecorder.added.size() == 2);
-        assertTrue(mCallbacksRecorder.added.contains(task1));
-        assertTrue(mCallbacksRecorder.added.contains(task2));
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.isEmpty());
-    }
-
-    @Test
-    public void testAddTaskCompatibleActivityType_expectRemove() throws Exception {
-        // Test with undefined activity type since the type is not persisted by the task persister
-        // and we want to ensure that a new task will match a restored task
-        TaskRecord task1 = createTaskBuilder(".Task1")
-                .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
-                .build();
-        setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED);
-        assertTrue(task1.getActivityType() == ACTIVITY_TYPE_UNDEFINED);
-        mRecentTasks.add(task1);
-        mCallbacksRecorder.clear();
-
-        TaskRecord task2 = createTaskBuilder(".Task1")
-                .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
-                .build();
-        assertTrue(task2.getActivityType() == ACTIVITY_TYPE_STANDARD);
-        mRecentTasks.add(task2);
-        assertTrue(mCallbacksRecorder.added.size() == 1);
-        assertTrue(mCallbacksRecorder.added.contains(task2));
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.size() == 1);
-        assertTrue(mCallbacksRecorder.removed.contains(task1));
-    }
-
-    @Test
-    public void testAddTaskCompatibleActivityTypeDifferentUser_expectNoRemove() throws Exception {
-        TaskRecord task1 = createTaskBuilder(".Task1")
-                .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
-                .setUserId(TEST_USER_0_ID)
-                .build();
-        setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED);
-        assertTrue(task1.getActivityType() == ACTIVITY_TYPE_UNDEFINED);
-        mRecentTasks.add(task1);
-        mCallbacksRecorder.clear();
-
-        TaskRecord task2 = createTaskBuilder(".Task1")
-                .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
-                .setUserId(TEST_USER_1_ID)
-                .build();
-        assertTrue(task2.getActivityType() == ACTIVITY_TYPE_STANDARD);
-        mRecentTasks.add(task2);
-        assertTrue(mCallbacksRecorder.added.size() == 1);
-        assertTrue(mCallbacksRecorder.added.contains(task2));
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.isEmpty());
-    }
-
-    @Test
-    public void testAddTaskCompatibleWindowingMode_expectRemove() throws Exception {
-        TaskRecord task1 = createTaskBuilder(".Task1")
-                .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
-                .build();
-        setTaskWindowingMode(task1, WINDOWING_MODE_UNDEFINED);
-        assertTrue(task1.getWindowingMode() == WINDOWING_MODE_UNDEFINED);
-        mRecentTasks.add(task1);
-        mCallbacksRecorder.clear();
-
-        TaskRecord task2 = createTaskBuilder(".Task1")
-                .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
-                .build();
-        setTaskWindowingMode(task2, WINDOWING_MODE_FULLSCREEN);
-        assertTrue(task2.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
-        mRecentTasks.add(task2);
-
-        assertTrue(mCallbacksRecorder.added.size() == 1);
-        assertTrue(mCallbacksRecorder.added.contains(task2));
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.size() == 1);
-        assertTrue(mCallbacksRecorder.removed.contains(task1));
-    }
-
-    @Test
-    public void testAddTaskIncompatibleWindowingMode_expectNoRemove() throws Exception {
-        TaskRecord task1 = createTaskBuilder(".Task1")
-                .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
-                .build();
-        setTaskWindowingMode(task1, WINDOWING_MODE_FULLSCREEN);
-        assertTrue(task1.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
-        mRecentTasks.add(task1);
-
-        TaskRecord task2 = createTaskBuilder(".Task1")
-                .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
-                .build();
-        setTaskWindowingMode(task2, WINDOWING_MODE_PINNED);
-        assertTrue(task2.getWindowingMode() == WINDOWING_MODE_PINNED);
-        mRecentTasks.add(task2);
-
-        assertTrue(mCallbacksRecorder.added.size() == 2);
-        assertTrue(mCallbacksRecorder.added.contains(task1));
-        assertTrue(mCallbacksRecorder.added.contains(task2));
-        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
-        assertTrue(mCallbacksRecorder.removed.isEmpty());
-    }
-
-    @Test
-    public void testUsersTasks() throws Exception {
-        mRecentTasks.setOnlyTestVisibleRange();
-
-        // Setup some tasks for the users
-        mTaskPersister.userTaskIdsOverride = new SparseBooleanArray();
-        mTaskPersister.userTaskIdsOverride.put(1, true);
-        mTaskPersister.userTaskIdsOverride.put(2, true);
-        mTaskPersister.userTasksOverride = new ArrayList<>();
-        mTaskPersister.userTasksOverride.add(createTaskBuilder(".UserTask1").build());
-        mTaskPersister.userTasksOverride.add(createTaskBuilder(".UserTask2").build());
-
-        // Assert no user tasks are initially loaded
-        assertTrue(mRecentTasks.usersWithRecentsLoadedLocked().length == 0);
-
-        // Load user 0 tasks
-        mRecentTasks.loadUserRecentsLocked(TEST_USER_0_ID);
-        assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID));
-        assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
-        assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
-
-        // Load user 1 tasks
-        mRecentTasks.loadUserRecentsLocked(TEST_USER_1_ID);
-        assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID));
-        assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_1_ID));
-        assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
-        assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
-        assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_1_ID));
-        assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_1_ID));
-
-        // Unload user 1 tasks
-        mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_1_ID);
-        assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID));
-        assertFalse(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_1_ID));
-        assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
-        assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
-
-        // Unload user 0 tasks
-        mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_0_ID);
-        assertFalse(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID));
-        assertFalse(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_1_ID));
-    }
-
-    @Test
-    public void testOrderedIteration() throws Exception {
-        mRecentTasks.setOnlyTestVisibleRange();
-        TaskRecord task1 = createTaskBuilder(".Task1").build();
-        task1.lastActiveTime = new Random().nextInt();
-        TaskRecord task2 = createTaskBuilder(".Task1").build();
-        task2.lastActiveTime = new Random().nextInt();
-        TaskRecord task3 = createTaskBuilder(".Task1").build();
-        task3.lastActiveTime = new Random().nextInt();
-        TaskRecord task4 = createTaskBuilder(".Task1").build();
-        task4.lastActiveTime = new Random().nextInt();
-        mRecentTasks.add(task1);
-        mRecentTasks.add(task2);
-        mRecentTasks.add(task3);
-        mRecentTasks.add(task4);
-
-        MutableLong prevLastActiveTime = new MutableLong(0);
-        final ArrayList<TaskRecord> tasks = mRecentTasks.getRawTasks();
-        for (int i = 0; i < tasks.size(); i++) {
-            final TaskRecord task = tasks.get(i);
-            assertTrue(task.lastActiveTime >= prevLastActiveTime.value);
-            prevLastActiveTime.value = task.lastActiveTime;
-        }
-    }
-
-    @Test
-    public void testTrimToGlobalMaxNumRecents() throws Exception {
-        mRecentTasks.setOnlyTestVisibleRange();
-
-        // Limit the global maximum number of recent tasks to a fixed size
-        mRecentTasks.setGlobalMaxNumTasks(2 /* globalMaxNumTasks */);
-
-        // Add N+1 tasks
-        mRecentTasks.add(mTasks.get(0));
-        mRecentTasks.add(mTasks.get(1));
-        mRecentTasks.add(mTasks.get(2));
-
-        // Ensure that the last task was trimmed as an inactive task
-        assertTrimmed(mTasks.get(0));
-    }
-
-    @Test
-    public void testTrimQuietProfileTasks() throws Exception {
-        mRecentTasks.setOnlyTestVisibleRange();
-        TaskRecord qt1 = createTaskBuilder(".QuietTask1").setUserId(TEST_QUIET_USER_ID).build();
-        TaskRecord qt2 = createTaskBuilder(".QuietTask2").setUserId(TEST_QUIET_USER_ID).build();
-        mRecentTasks.add(qt1);
-        mRecentTasks.add(qt2);
-
-        mRecentTasks.add(mTasks.get(0));
-        mRecentTasks.add(mTasks.get(1));
-
-        // Ensure that the quiet user's tasks was trimmed once the new tasks were added
-        assertTrimmed(qt1, qt2);
-    }
-
-    @Test
-    public void testSessionDuration() throws Exception {
-        mRecentTasks.setOnlyTestVisibleRange();
-        mRecentTasks.setParameters(-1 /* min */, -1 /* max */, 50 /* ms */);
-
-        TaskRecord t1 = createTaskBuilder(".Task1").build();
-        t1.touchActiveTime();
-        mRecentTasks.add(t1);
-
-        // Force a small sleep just beyond the session duration
-        SystemClock.sleep(75);
-
-        TaskRecord t2 = createTaskBuilder(".Task2").build();
-        t2.touchActiveTime();
-        mRecentTasks.add(t2);
-
-        // Assert that the old task has been removed due to being out of the active session
-        assertTrimmed(t1);
-    }
-
-    @Test
-    public void testVisibleTasks_excludedFromRecents() throws Exception {
-        mRecentTasks.setOnlyTestVisibleRange();
-        mRecentTasks.setParameters(-1 /* min */, 4 /* max */, -1 /* ms */);
-
-        TaskRecord excludedTask1 = createTaskBuilder(".ExcludedTask1")
-                .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
-                .build();
-        TaskRecord excludedTask2 = createTaskBuilder(".ExcludedTask2")
-                .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
-                .build();
-
-        mRecentTasks.add(excludedTask1);
-        mRecentTasks.add(mTasks.get(0));
-        mRecentTasks.add(mTasks.get(1));
-        mRecentTasks.add(mTasks.get(2));
-        mRecentTasks.add(excludedTask2);
-
-        // The last excluded task should be trimmed, while the first-most excluded task should not
-        assertTrimmed(excludedTask1);
-    }
-
-    @Test
-    public void testVisibleTasks_minNum() throws Exception {
-        mRecentTasks.setOnlyTestVisibleRange();
-        mRecentTasks.setParameters(5 /* min */, -1 /* max */, 25 /* ms */);
-
-        for (int i = 0; i < 4; i++) {
-            final TaskRecord task = mTasks.get(i);
-            task.touchActiveTime();
-            mRecentTasks.add(task);
-        }
-
-        // Force a small sleep just beyond the session duration
-        SystemClock.sleep(50);
-
-        // Add a new task to trigger tasks to be trimmed
-        mRecentTasks.add(mTasks.get(4));
-
-        // Ensure that there are a minimum number of tasks regardless of session length
-        assertNoTasksTrimmed();
-    }
-
-    @Test
-    public void testVisibleTasks_maxNum() throws Exception {
-        mRecentTasks.setOnlyTestVisibleRange();
-        mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */);
-
-        for (int i = 0; i < 5; i++) {
-            final TaskRecord task = mTasks.get(i);
-            task.touchActiveTime();
-            mRecentTasks.add(task);
-        }
-
-        // Ensure that only the last number of max tasks are kept
-        assertTrimmed(mTasks.get(0), mTasks.get(1));
-    }
-
-    @Test
-    public void testBackStackTasks_expectNoTrim() throws Exception {
-        mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
-
-        final MyTestActivityStackSupervisor supervisor =
-                (MyTestActivityStackSupervisor) mService.mStackSupervisor;
-        final ActivityStack homeStack = mDisplay.getHomeStack();
-        final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
-
-        // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
-        // the tasks belong in stacks above the home stack
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task1").setStack(aboveHomeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setStack(aboveHomeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task3").setStack(aboveHomeStack).build());
-
-        assertNoTasksTrimmed();
-    }
-
-    @Test
-    public void testBehindHomeStackTasks_expectTaskTrimmed() throws Exception {
-        mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
-
-        final MyTestActivityStackSupervisor supervisor =
-                (MyTestActivityStackSupervisor) mService.mStackSupervisor;
-        final ActivityStack behindHomeStack = new MyTestActivityStack(mDisplay, supervisor);
-        final ActivityStack homeStack = mDisplay.getHomeStack();
-        final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
-
-        // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
-        // the home stack is trimmed once a new task is added
-        final TaskRecord behindHomeTask = createTaskBuilder(".Task1")
-                .setStack(behindHomeStack)
-                .build();
-        mRecentTasks.add(behindHomeTask);
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setStack(aboveHomeStack).build());
-
-        assertTrimmed(behindHomeTask);
-    }
-
-    @Test
-    public void testOtherDisplayTasks_expectNoTrim() throws Exception {
-        mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
-
-        final MyTestActivityStackSupervisor supervisor =
-                (MyTestActivityStackSupervisor) mService.mStackSupervisor;
-        final ActivityStack homeStack = mDisplay.getHomeStack();
-        final ActivityStack otherDisplayStack = new MyTestActivityStack(mOtherDisplay, supervisor);
-
-        // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
-        // removed
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task1").setStack(otherDisplayStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setStack(otherDisplayStack).build());
-        mRecentTasks.add(createTaskBuilder(".HomeTask2").setStack(homeStack).build());
-
-        assertNoTasksTrimmed();
-    }
-
-    @Test
-    public void testRemovePackageByName() throws Exception {
-        // Add a number of tasks with the same package name
-        mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".Task1").build());
-        mRecentTasks.add(createTaskBuilder("com.android.pkg2", ".Task2").build());
-        mRecentTasks.add(createTaskBuilder("com.android.pkg3", ".Task3").build());
-        mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".Task4").build());
-        mRecentTasks.removeTasksByPackageName("com.android.pkg1", TEST_USER_0_ID);
-
-        final ArrayList<TaskRecord> tasks = mRecentTasks.getRawTasks();
-        for (int i = 0; i < tasks.size(); i++) {
-            if (tasks.get(i).intent.getComponent().getPackageName().equals("com.android.pkg1")) {
-                fail("Expected com.android.pkg1 tasks to be removed");
-            }
-        }
-    }
-
-    @Test
-    public void testRemoveAllVisibleTasks() throws Exception {
-        mRecentTasks.setParameters(-1 /* min */, 3 /* max */, 100 /* ms */);
-
-        // Create some set of tasks, some of which are visible and some are not
-        TaskRecord t1 = createTaskBuilder("com.android.pkg1", ".Task1").build();
-        mRecentTasks.add(t1);
-        mRecentTasks.add(setTaskActivityType(
-                createTaskBuilder("com.android.pkg1", ".HomeTask").build(),
-                ACTIVITY_TYPE_HOME));
-        TaskRecord t2 = createTaskBuilder("com.android.pkg2", ".Task2").build();
-        mRecentTasks.add(t2);
-        mRecentTasks.add(setTaskWindowingMode(
-                createTaskBuilder("com.android.pkg1", ".PipTask").build(),
-                WINDOWING_MODE_PINNED));
-        TaskRecord t3 = createTaskBuilder("com.android.pkg3", ".Task3").build();
-        mRecentTasks.add(t3);
-
-        // Create some more tasks that are out of visible range, but are still visible
-        TaskRecord t4 = createTaskBuilder("com.android.pkg3", ".Task4").build();
-        mRecentTasks.add(t4);
-        TaskRecord t5 = createTaskBuilder("com.android.pkg3", ".Task5").build();
-        mRecentTasks.add(t5);
-
-        // Create some more tasks that are out of the active session range, but are still visible
-        TaskRecord t6 = createTaskBuilder("com.android.pkg3", ".Task6").build();
-        t6.lastActiveTime = SystemClock.elapsedRealtime() - 200;
-        mRecentTasks.add(t6);
-        TaskRecord t7 = createTaskBuilder("com.android.pkg3", ".Task7").build();
-        t7.lastActiveTime = SystemClock.elapsedRealtime() - 200;
-        mRecentTasks.add(t7);
-
-        // Remove all the visible tasks and ensure that they are removed
-        mRecentTasks.removeAllVisibleTasks();
-        assertTrimmed(t1, t2, t3, t4, t5, t6, t7);
-    }
-
-    @Test
-    public void testNotRecentsComponent_denyApiAccess() throws Exception {
-        doReturn(PackageManager.PERMISSION_DENIED).when(mService)
-                .checkGetTasksPermission(anyString(), anyInt(), anyInt());
-        // Expect the following methods to fail due to recents component not being set
-        mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
-        testRecentTasksApis(false /* expectNoSecurityException */);
-        // Don't throw for the following tests
-        mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY);
-        testGetTasksApis(false /* expectNoSecurityException */);
-    }
-
-    @Test
-    public void testRecentsComponent_allowApiAccessWithoutPermissions() throws Exception {
-        doReturn(PackageManager.PERMISSION_DENIED).when(mService)
-                .checkGetTasksPermission(anyString(), anyInt(), anyInt());
-
-        // Set the recents component and ensure that the following calls do not fail
-        mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.GRANT);
-        testRecentTasksApis(true /* expectNoSecurityException */);
-        testGetTasksApis(true /* expectNoSecurityException */);
-    }
-
-    private void testRecentTasksApis(boolean expectCallable) {
-        assertSecurityException(expectCallable, () -> mService.removeStack(INVALID_STACK_ID));
-        assertSecurityException(expectCallable,
-                () -> mService.removeStacksInWindowingModes(new int[] {WINDOWING_MODE_UNDEFINED}));
-        assertSecurityException(expectCallable,
-                () -> mService.removeStacksWithActivityTypes(new int[] {ACTIVITY_TYPE_UNDEFINED}));
-        assertSecurityException(expectCallable, () -> mService.removeTask(0));
-        assertSecurityException(expectCallable,
-                () -> mService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
-        assertSecurityException(expectCallable,
-                () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
-        assertSecurityException(expectCallable,
-                () -> mService.setTaskWindowingModeSplitScreenPrimary(0,
-                        SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
-        assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
-        assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
-        assertSecurityException(expectCallable,
-                () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
-        assertSecurityException(expectCallable,
-                () -> mService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
-        assertSecurityException(expectCallable,
-                () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
-                        new Rect()));
-        assertSecurityException(expectCallable,
-                () -> mService.resizePinnedStack(new Rect(), new Rect()));
-        assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
-        assertSecurityException(expectCallable,
-                () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
-        assertSecurityException(expectCallable, () -> {
-            try {
-                mService.getFocusedStackInfo();
-            } catch (RemoteException e) {
-                // Ignore
-            }
-        });
-        assertSecurityException(expectCallable,
-                () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
-        assertSecurityException(expectCallable,
-                () -> mService.startActivityFromRecents(0, new Bundle()));
-        assertSecurityException(expectCallable,() -> mService.getTaskSnapshot(0, true));
-        assertSecurityException(expectCallable,() -> mService.registerTaskStackListener(null));
-        assertSecurityException(expectCallable,() -> mService.unregisterTaskStackListener(null));
-        assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
-        assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
-        assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
-                null));
-        assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation(true));
-        assertSecurityException(expectCallable, () -> mService.stopAppSwitches());
-        assertSecurityException(expectCallable, () -> mService.resumeAppSwitches());
-    }
-
-    private void testGetTasksApis(boolean expectCallable) {
-        mService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
-        mService.getTasks(MAX_VALUE);
-        if (expectCallable) {
-            assertTrue(mRecentTasks.lastAllowed);
-            assertTrue(mRunningTasks.lastAllowed);
-        } else {
-            assertFalse(mRecentTasks.lastAllowed);
-            assertFalse(mRunningTasks.lastAllowed);
-        }
-    }
-
-    private TaskBuilder createTaskBuilder(String className) {
-        return createTaskBuilder(mContext.getPackageName(), className);
-    }
-
-    private TaskBuilder createTaskBuilder(String packageName, String className) {
-        return new TaskBuilder(mService.mStackSupervisor)
-                .setComponent(new ComponentName(packageName, className))
-                .setStack(mStack)
-                .setTaskId(LAST_TASK_ID++)
-                .setUserId(TEST_USER_0_ID);
-    }
-
-    private TaskRecord createDocumentTask(String className) {
-        return createDocumentTask(className, 0);
-    }
-
-    private TaskRecord createDocumentTask(String className, int flags) {
-        TaskRecord task = createTaskBuilder(className)
-                .setFlags(FLAG_ACTIVITY_NEW_DOCUMENT | flags)
-                .build();
-        task.affinity = null;
-        task.maxRecents = ActivityTaskManager.getMaxAppRecentsLimitStatic();
-        return task;
-    }
-
-    private TaskRecord setTaskActivityType(TaskRecord task,
-            @WindowConfiguration.ActivityType int activityType) {
-        Configuration config1 = new Configuration();
-        config1.windowConfiguration.setActivityType(activityType);
-        task.onConfigurationChanged(config1);
-        return task;
-    }
-
-    private TaskRecord setTaskWindowingMode(TaskRecord task,
-            @WindowConfiguration.WindowingMode int windowingMode) {
-        Configuration config1 = new Configuration();
-        config1.windowConfiguration.setWindowingMode(windowingMode);
-        task.onConfigurationChanged(config1);
-        return task;
-    }
-
-    private boolean arrayContainsUser(int[] userIds, int targetUserId) {
-        Arrays.sort(userIds);
-        return Arrays.binarySearch(userIds, targetUserId) >= 0;
-    }
-
-    private void assertNoTasksTrimmed() {
-        assertTrimmed();
-    }
-
-    private void assertTrimmed(TaskRecord... tasks) {
-        final ArrayList<TaskRecord> trimmed = mCallbacksRecorder.trimmed;
-        final ArrayList<TaskRecord> removed = mCallbacksRecorder.removed;
-        assertTrue("Expected " + tasks.length + " trimmed tasks, got " + trimmed.size(),
-                trimmed.size() == tasks.length);
-        assertTrue("Expected " + tasks.length + " removed tasks, got " + removed.size(),
-                removed.size() == tasks.length);
-        for (TaskRecord task : tasks) {
-            assertTrue("Expected trimmed task: " + task, trimmed.contains(task));
-            assertTrue("Expected removed task: " + task, removed.contains(task));
-        }
-    }
-
-    private void assertSecurityException(boolean expectCallable, Runnable runnable) {
-        boolean noSecurityException = true;
-        try {
-            runnable.run();
-        } catch (SecurityException se) {
-            noSecurityException = false;
-        } catch (Exception e) {
-            // We only care about SecurityExceptions, fall through here
-            e.printStackTrace();
-        }
-        if (noSecurityException != expectCallable) {
-            fail("Expected callable: " + expectCallable + " but got no security exception: "
-                    + noSecurityException);
-        }
-    }
-
-    private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
-        MyTestActivityTaskManagerService(Context context) {
-            super(context);
-        }
-
-        @Override
-        protected RecentTasks createRecentTasks() {
-            return new TestRecentTasks(this, mTaskPersister);
-        }
-
-        @Override
-        protected ActivityStackSupervisor createTestSupervisor() {
-            return new MyTestActivityStackSupervisor(this, mH.getLooper());
-        }
-
-    }
-
-    private class MyTestActivityManagerService extends TestActivityManagerService {
-        MyTestActivityManagerService() {
-            super(mTestInjector);
-        }
-
-        @Override
-        public boolean isUserRunning(int userId, int flags) {
-            return true;
-        }
-    }
-
-    private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor {
-        public MyTestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
-            super(service, looper);
-        }
-
-        @Override
-        public void initialize() {
-            super.initialize();
-            mDisplay = getActivityDisplay(DEFAULT_DISPLAY);
-            mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1);
-            addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
-            addChild(mDisplay, ActivityDisplay.POSITION_TOP);
-        }
-
-        @Override
-        RunningTasks createRunningTasks() {
-            mRunningTasks = new TestRunningTasks();
-            return mRunningTasks;
-        }
-    }
-
-    private class MyTestActivityStack extends TestActivityStack {
-        private ActivityDisplay mDisplay = null;
-
-        MyTestActivityStack(ActivityDisplay display, ActivityStackSupervisor supervisor) {
-            super(display, LAST_STACK_ID++, supervisor, WINDOWING_MODE_FULLSCREEN,
-                    ACTIVITY_TYPE_STANDARD, true);
-            mDisplay = display;
-        }
-
-        @Override
-        ActivityDisplay getDisplay() {
-            if (mDisplay != null) {
-                return mDisplay;
-            }
-            return super.getDisplay();
-        }
-    }
-
-    private static class CallbacksRecorder implements Callbacks {
-        ArrayList<TaskRecord> added = new ArrayList<>();
-        ArrayList<TaskRecord> trimmed = new ArrayList<>();
-        ArrayList<TaskRecord> removed = new ArrayList<>();
-
-        void clear() {
-            added.clear();
-            trimmed.clear();
-            removed.clear();
-        }
-
-        @Override
-        public void onRecentTaskAdded(TaskRecord task) {
-            added.add(task);
-        }
-
-        @Override
-        public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
-            if (wasTrimmed) {
-                trimmed.add(task);
-            }
-            removed.add(task);
-        }
-    }
-
-    private static class TestTaskPersister extends TaskPersister {
-        SparseBooleanArray userTaskIdsOverride;
-        ArrayList<TaskRecord> userTasksOverride;
-
-        TestTaskPersister(File workingDir) {
-            super(workingDir);
-        }
-
-        @Override
-        SparseBooleanArray loadPersistedTaskIdsForUser(int userId) {
-            if (userTaskIdsOverride != null) {
-                return userTaskIdsOverride;
-            }
-            return super.loadPersistedTaskIdsForUser(userId);
-        }
-
-        @Override
-        List<TaskRecord> restoreTasksForUserLocked(int userId, SparseBooleanArray preaddedTasks) {
-            if (userTasksOverride != null) {
-                return userTasksOverride;
-            }
-            return super.restoreTasksForUserLocked(userId, preaddedTasks);
-        }
-    }
-
-    private static class TestRecentTasks extends RecentTasks {
-        static final int GRANT = 0;
-        static final int DENY = 1;
-        static final int DENY_THROW_SECURITY_EXCEPTION = 2;
-
-        private boolean mOverrideIsCallerRecents;
-        private boolean mIsTrimmableOverride;
-        private int mIsCallerRecentsPolicy;
-
-        boolean lastAllowed;
-
-        TestRecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) {
-            super(service, taskPersister);
-        }
-
-        @Override
-        Set<Integer> getProfileIds(int userId) {
-            Set<Integer> profileIds = new HashSet<>();
-            profileIds.add(TEST_USER_0_ID);
-            profileIds.add(TEST_QUIET_USER_ID);
-            return profileIds;
-        }
-
-        @Override
-        UserInfo getUserInfo(int userId) {
-            switch (userId) {
-                case TEST_USER_0_ID:
-                case TEST_USER_1_ID:
-                    return DEFAULT_USER_INFO;
-                case TEST_QUIET_USER_ID:
-                    return QUIET_USER_INFO;
-            }
-            return null;
-        }
-
-        @Override
-        int[] getCurrentProfileIds() {
-            return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID };
-        }
-
-        @Override
-        boolean isCallerRecents(int callingUid) {
-            if (mOverrideIsCallerRecents) {
-                switch (mIsCallerRecentsPolicy) {
-                    case GRANT:
-                        return true;
-                    case DENY:
-                        return false;
-                    case DENY_THROW_SECURITY_EXCEPTION:
-                        throw new SecurityException();
-                }
-            }
-            return super.isCallerRecents(callingUid);
-        }
-
-        void setIsCallerRecentsOverride(int policy) {
-            mOverrideIsCallerRecents = true;
-            mIsCallerRecentsPolicy = policy;
-        }
-
-        /**
-         * To simplify the setup for some tests, the caller can request that we only rely on the
-         * visible range test to determine what is trimmable. In this case, we don't try to
-         * use the stack order to determine additionally if the task is trimmable when it is not
-         * in the visible range.
-         */
-        void setOnlyTestVisibleRange() {
-            mIsTrimmableOverride = true;
-        }
-
-        @Override
-        ParceledListSlice<RecentTaskInfo> getRecentTasks(int maxNum, int flags,
-                boolean getTasksAllowed,
-                boolean getDetailedTasks, int userId, int callingUid) {
-            lastAllowed = getTasksAllowed;
-            return super.getRecentTasks(maxNum, flags, getTasksAllowed, getDetailedTasks, userId,
-                    callingUid);
-        }
-
-        @Override
-        protected boolean isTrimmable(TaskRecord task) {
-            return mIsTrimmableOverride || super.isTrimmable(task);
-        }
-    }
-
-    private static class TestRunningTasks extends RunningTasks {
-        boolean lastAllowed;
-
-        @Override
-        void getTasks(int maxNum, List<RunningTaskInfo> list, int ignoreActivityType,
-                int ignoreWindowingMode, ArrayList<ActivityDisplay> activityDisplays,
-                int callingUid, boolean allowed) {
-            lastAllowed = allowed;
-            super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, activityDisplays,
-                    callingUid, allowed);
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java b/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
deleted file mode 100644
index f15b5f7..0000000
--- a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-
-import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.platform.test.annotations.Presubmit;
-import android.view.IRecentsAnimationRunner;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * atest FrameworksServicesTests:RecentsAnimationTest
- */
-@MediumTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class RecentsAnimationTest extends ActivityTestsBase {
-
-    private Context mContext = InstrumentationRegistry.getContext();
-    private TestActivityTaskManagerService mService;
-    private ComponentName mRecentsComponent;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
-        mService = spy(new MyTestActivityTaskManagerService(mContext));
-        setupActivityManagerService(mService);
-    }
-
-    @Test
-    public void testCancelAnimationOnStackOrderChange() throws Exception {
-        ActivityStack fullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        ActivityStack recentsStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        ActivityRecord recentsActivity = new ActivityBuilder(mService)
-                .setComponent(mRecentsComponent)
-                .setCreateTask(true)
-                .setStack(recentsStack)
-                .build();
-        ActivityStack fullscreenStack2 = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        ActivityRecord fsActivity = new ActivityBuilder(mService)
-                .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
-                .setCreateTask(true)
-                .setStack(fullscreenStack2)
-                .build();
-        doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
-
-        // Start the recents animation
-        Intent recentsIntent = new Intent();
-        recentsIntent.setComponent(mRecentsComponent);
-        mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
-
-        fullscreenStack.moveToFront("Activity start");
-
-        // Ensure that the recents animation was canceled
-        verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
-                eq(REORDER_KEEP_IN_PLACE), any());
-    }
-
-    private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
-        MyTestActivityTaskManagerService(Context context) {
-            super(context);
-        }
-
-        @Override
-        protected RecentTasks createRecentTasks() {
-            RecentTasks recents = mock(RecentTasks.class);
-            doReturn(mRecentsComponent).when(recents).getRecentsComponent();
-            System.out.println(mRecentsComponent);
-            return recents;
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
deleted file mode 100644
index aa3046f..0000000
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
-
-import static org.junit.Assert.assertTrue;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.platform.test.annotations.Presubmit;
-import android.util.SparseArray;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-
-/**
- * atest FrameworksServicesTests:RunningTasksTest
- */
-@MediumTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class RunningTasksTest extends ActivityTestsBase {
-
-    private Context mContext = InstrumentationRegistry.getContext();
-
-    private RunningTasks mRunningTasks;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        setupActivityTaskManagerService();
-        mRunningTasks = new RunningTasks();
-    }
-
-    @Test
-    public void testCollectTasksByLastActiveTime() throws Exception {
-        // Create a number of stacks with tasks (of incrementing active time)
-        final ArrayList<ActivityDisplay> displays = new ArrayList<>();
-        final ActivityDisplay display = TestActivityDisplay.create(mSupervisor, DEFAULT_DISPLAY);
-        displays.add(display);
-
-        final int numStacks = 2;
-        for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
-            final ActivityStack stack = new TestActivityStack(display, stackIndex, mSupervisor,
-                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true);
-            display.addChild(stack, POSITION_BOTTOM);
-        }
-
-        final int numTasks = 10;
-        int activeTime = 0;
-        for (int i = 0; i < numTasks; i++) {
-            createTask(display.getChildAt(i % numStacks), ".Task" + i, i, activeTime++);
-        }
-
-        // Ensure that the latest tasks were returned in order of decreasing last active time,
-        // collected from all tasks across all the stacks
-        final int numFetchTasks = 5;
-        ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
-        mRunningTasks.getTasks(5, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
-                displays, -1 /* callingUid */, true /* allowed */);
-        assertTrue(tasks.size() == numFetchTasks);
-        for (int i = 0; i < numFetchTasks; i++) {
-            assertTrue(tasks.get(i).id == (numTasks - i - 1));
-        }
-
-        // Ensure that requesting more than the total number of tasks only returns the subset
-        // and does not crash
-        tasks.clear();
-        mRunningTasks.getTasks(100, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
-                displays, -1 /* callingUid */, true /* allowed */);
-        assertTrue(tasks.size() == numTasks);
-        for (int i = 0; i < numTasks; i++) {
-            assertTrue(tasks.get(i).id == (numTasks - i - 1));
-        }
-    }
-
-    /**
-     * Create a task with a single activity in it, with the given last active time.
-     */
-    private TaskRecord createTask(ActivityStack stack, String className, int taskId,
-            int lastActiveTime) {
-        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
-                .setComponent(new ComponentName(mContext.getPackageName(), className))
-                .setTaskId(taskId)
-                .setStack(stack)
-                .build();
-        task.lastActiveTime = lastActiveTime;
-        final ActivityRecord activity = new ActivityBuilder(mService)
-                .setTask(task)
-                .setComponent(new ComponentName(mContext.getPackageName(), ".TaskActivity"))
-                .build();
-        return task;
-    }
-}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java b/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java
deleted file mode 100644
index 8e4e7e6..0000000
--- a/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import static org.junit.Assert.assertEquals;
-
-import android.app.ActivityOptions;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@MediumTest
-@Presubmit
-@FlakyTest
-@RunWith(AndroidJUnit4.class)
-public class SafeActivityOptionsTest {
-
-    @Test
-    public void testMerge() {
-        final ActivityOptions opts1 = ActivityOptions.makeBasic();
-        opts1.setLaunchDisplayId(5);
-        final ActivityOptions opts2 = ActivityOptions.makeBasic();
-        opts2.setLaunchDisplayId(6);
-        final SafeActivityOptions options = new SafeActivityOptions(opts1);
-        final ActivityOptions result = options.mergeActivityOptions(opts1, opts2);
-        assertEquals(6, result.getLaunchDisplayId());
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
deleted file mode 100644
index 169204f..0000000
--- a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
+++ /dev/null
@@ -1,1101 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.util.DisplayMetrics.DENSITY_DEFAULT;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityOptions;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Build;
-import android.platform.test.annotations.Presubmit;
-import android.view.Gravity;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.filters.FlakyTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.am.LaunchParamsController.LaunchParams;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Locale;
-
-/**
- * Tests for default task bounds.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:TaskLaunchParamsModifierTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
-public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
-
-    private ActivityRecord mActivity;
-
-    private TaskLaunchParamsModifier mTarget;
-
-    private LaunchParams mCurrent;
-    private LaunchParams mResult;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        setupActivityTaskManagerService();
-        mService.mSupportsFreeformWindowManagement = true;
-        when(mSupervisor.canUseActivityOptionsLaunchBounds(any())).thenCallRealMethod();
-
-        mActivity = new ActivityBuilder(mService).build();
-        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1;
-        mActivity.info.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
-
-        mTarget = new TaskLaunchParamsModifier(mSupervisor);
-
-        mCurrent = new LaunchParams();
-        mCurrent.reset();
-        mResult = new LaunchParams();
-        mResult.reset();
-    }
-
-    @Test
-    public void testReturnsSkipWithEmptyActivity() {
-        final TaskRecord task = new TaskBuilder(mSupervisor).build();
-        assertEquals(RESULT_SKIP, mTarget.onCalculate(task, /* layout */ null,
-                /* activity */ null, /* source */ null, /* options */ null, mCurrent, mResult));
-    }
-
-    // =============================
-    // Display ID Related Tests
-    // =============================
-    @Test
-    public void testDefaultToPrimaryDisplay() {
-        createNewActivityDisplay(WINDOWING_MODE_FREEFORM);
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(DEFAULT_DISPLAY, mResult.mPreferredDisplayId);
-    }
-
-    @Test
-    public void testUsesPreviousDisplayIdIfSet() {
-        createNewActivityDisplay(WINDOWING_MODE_FREEFORM);
-        final TestActivityDisplay display = createNewActivityDisplay(WINDOWING_MODE_FULLSCREEN);
-
-        mCurrent.mPreferredDisplayId = display.mDisplayId;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(display.mDisplayId, mResult.mPreferredDisplayId);
-    }
-
-    @Test
-    public void testUsesSourcesDisplayIdIfSet() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FULLSCREEN);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        ActivityRecord source = createSourceActivity(fullscreenDisplay);
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, source, /* options */ null, mCurrent, mResult));
-
-        assertEquals(fullscreenDisplay.mDisplayId, mResult.mPreferredDisplayId);
-    }
-
-    @Test
-    public void testUsesOptionsDisplayIdIfSet() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FULLSCREEN);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-        ActivityRecord source = createSourceActivity(freeformDisplay);
-
-        ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(fullscreenDisplay.mDisplayId);
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, source, options, mCurrent, mResult));
-
-        assertEquals(fullscreenDisplay.mDisplayId, mResult.mPreferredDisplayId);
-    }
-
-    // =====================================
-    // Launch Windowing Mode Related Tests
-    // =====================================
-    @Test
-    public void testBoundsInOptionsInfersFreeformOnFreeformDisplay() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchBounds(new Rect(0, 0, 100, 100));
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
-                WINDOWING_MODE_FREEFORM);
-    }
-
-    @Test
-    public void testBoundsInOptionsInfersFreeformWithResizeableActivity() {
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchBounds(new Rect(0, 0, 100, 100));
-
-        mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
-                WINDOWING_MODE_FULLSCREEN);
-    }
-
-    @Test
-    public void testKeepsPictureInPictureLaunchModeInOptions() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_PINNED);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_PINNED, mResult.mWindowingMode,
-                WINDOWING_MODE_FREEFORM);
-    }
-
-    @Test
-    public void testKeepsPictureInPictureLaunchModeWithBoundsInOptions() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_PINNED);
-        options.setLaunchBounds(new Rect(0, 0, 100, 100));
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_PINNED, mResult.mWindowingMode,
-                WINDOWING_MODE_FREEFORM);
-    }
-
-    @Test
-    public void testKeepsFullscreenLaunchModeInOptionsOnNonFreeformDisplay() {
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
-        mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
-                WINDOWING_MODE_FULLSCREEN);
-    }
-
-    @Test
-    public void testNonEmptyLayoutInfersFreeformOnFreeformDisplay() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setWidth(120).setHeight(80).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
-                WINDOWING_MODE_FREEFORM);
-    }
-
-    @Test
-    public void testNonEmptyLayoutInfersFreeformWithEmptySize() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setGravity(Gravity.LEFT).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
-                WINDOWING_MODE_FREEFORM);
-    }
-
-    @Test
-    public void testNonEmptyLayoutUsesFullscreenWithResizeableActivity() {
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setWidth(120).setHeight(80).build();
-
-        mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
-                WINDOWING_MODE_FULLSCREEN);
-    }
-
-    @Test
-    public void testRespectsFullyResolvedCurrentParam_Fullscreen() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-        mCurrent.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
-                WINDOWING_MODE_FREEFORM);
-    }
-
-    @Test
-    public void testRespectsModeFromFullyResolvedCurrentParam_NonEmptyBounds() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
-        mCurrent.mBounds.set(0, 0, 200, 100);
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
-                WINDOWING_MODE_FREEFORM);
-    }
-
-    @Test
-    public void testForceMaximizesPreDApp() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
-        options.setLaunchBounds(new Rect(0, 0, 200, 100));
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
-        mCurrent.mBounds.set(0, 0, 200, 100);
-
-        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.CUPCAKE;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
-                WINDOWING_MODE_FREEFORM);
-    }
-
-    @Test
-    public void testForceMaximizesAppWithoutMultipleDensitySupport() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
-        options.setLaunchBounds(new Rect(0, 0, 200, 100));
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
-        mCurrent.mBounds.set(0, 0, 200, 100);
-
-        mActivity.appInfo.flags = 0;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
-                WINDOWING_MODE_FREEFORM);
-    }
-
-    @Test
-    public void testForceMaximizesUnresizeableApp() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
-        options.setLaunchBounds(new Rect(0, 0, 200, 100));
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
-        mCurrent.mBounds.set(0, 0, 200, 100);
-
-        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
-                WINDOWING_MODE_FREEFORM);
-    }
-
-    @Test
-    public void testSkipsForceMaximizingAppsOnNonFreeformDisplay() {
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
-        options.setLaunchBounds(new Rect(0, 0, 200, 100));
-
-        mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
-        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
-        mCurrent.mBounds.set(0, 0, 200, 100);
-
-        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
-                WINDOWING_MODE_FULLSCREEN);
-    }
-
-    @Test
-    public void testUsesFullscreenOnNonFreeformDisplay() {
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(DEFAULT_DISPLAY);
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
-                WINDOWING_MODE_FULLSCREEN);
-    }
-
-    @Test
-    public void testUsesFreeformByDefaultForPostNApp() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
-                WINDOWING_MODE_FREEFORM);
-    }
-
-    @Test
-    public void testUsesFreeformByDefaultForPreNResizeableAppWithoutOrientationRequest() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-
-        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
-                WINDOWING_MODE_FREEFORM);
-    }
-
-    @Test
-    public void testSkipsFreeformForPreNResizeableAppOnNonFullscreenDisplay() {
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(DEFAULT_DISPLAY);
-
-        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
-                WINDOWING_MODE_FULLSCREEN);
-    }
-
-    // ================================
-    // Launching Bounds Related Tests
-    // ===============================
-    @Test
-    public void testKeepsBoundsWithPictureInPictureLaunchModeInOptions() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_PINNED);
-
-        final Rect expected = new Rect(0, 0, 100, 100);
-        options.setLaunchBounds(expected);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquals(expected, mResult.mBounds);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_LeftGravity() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setGravity(Gravity.LEFT).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(0, mResult.mBounds.left);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_TopGravity() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setGravity(Gravity.TOP).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(0, mResult.mBounds.top);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_TopLeftGravity() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setGravity(Gravity.TOP | Gravity.LEFT).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(0, mResult.mBounds.left);
-        assertEquals(0, mResult.mBounds.top);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_RightGravity() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setGravity(Gravity.RIGHT).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(1920, mResult.mBounds.right);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_BottomGravity() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setGravity(Gravity.BOTTOM).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(1080, mResult.mBounds.bottom);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_BottomRightGravity() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setGravity(Gravity.BOTTOM | Gravity.RIGHT).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(1920, mResult.mBounds.right);
-        assertEquals(1080, mResult.mBounds.bottom);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsOnFreeformDisplay_CenterToDisplay() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setWidth(120).setHeight(80).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(new Rect(900, 500, 1020, 580), mResult.mBounds);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsOnFreeformDisplay_LeftGravity() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setWidth(120).setHeight(80).setGravity(Gravity.LEFT).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(new Rect(0, 500, 120, 580), mResult.mBounds);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsOnFreeformDisplay_TopGravity() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setWidth(120).setHeight(80).setGravity(Gravity.TOP).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(new Rect(900, 0, 1020, 80), mResult.mBounds);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsOnFreeformDisplay_TopLeftGravity() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setWidth(120).setHeight(80).setGravity(Gravity.TOP | Gravity.LEFT).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(new Rect(0, 0, 120, 80), mResult.mBounds);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsOnFreeformDisplay_RightGravity() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setWidth(120).setHeight(80).setGravity(Gravity.RIGHT).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(new Rect(1800, 500, 1920, 580), mResult.mBounds);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsOnFreeformDisplay_BottomGravity() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setWidth(120).setHeight(80).setGravity(Gravity.BOTTOM).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(new Rect(900, 1000, 1020, 1080), mResult.mBounds);
-    }
-
-    @Test
-    public void testNonEmptyLayoutBoundsOnFreeformDisplay_RightBottomGravity() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setWidth(120).setHeight(80).setGravity(Gravity.BOTTOM | Gravity.RIGHT).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(new Rect(1800, 1000, 1920, 1080), mResult.mBounds);
-    }
-
-    @Test
-    public void testNonEmptyLayoutFractionBoundsOnFreeformDisplay() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setWidthFraction(0.0625f).setHeightFraction(0.1f).build();
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(new Rect(900, 486, 1020, 594), mResult.mBounds);
-    }
-
-    @Test
-    public void testRespectBoundsFromFullyResolvedCurrentParam_NonEmptyBounds() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
-        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
-        mCurrent.mBounds.set(0, 0, 200, 100);
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(new Rect(0, 0, 200, 100), mResult.mBounds);
-    }
-
-    @Test
-    public void testDefaultSizeSmallerThanBigScreen() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-
-        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        final int resultWidth = mResult.mBounds.width();
-        final int displayWidth = freeformDisplay.getBounds().width();
-        assertTrue("Result width " + resultWidth + " is not smaller than " + displayWidth,
-                resultWidth < displayWidth);
-
-        final int resultHeight = mResult.mBounds.height();
-        final int displayHeight = freeformDisplay.getBounds().height();
-        assertTrue("Result width " + resultHeight + " is not smaller than "
-                        + displayHeight, resultHeight < displayHeight);
-    }
-
-    @Test
-    public void testDefaultFreeformSizeCenteredToDisplay() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-
-        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        final Rect displayBounds = freeformDisplay.getBounds();
-        assertEquals("Distance to left and right should be equal.",
-                mResult.mBounds.left - displayBounds.left,
-                displayBounds.right - mResult.mBounds.right);
-        assertEquals("Distance to top and bottom should be equal.",
-                mResult.mBounds.top - displayBounds.top,
-                displayBounds.bottom - mResult.mBounds.bottom);
-    }
-
-    @Test
-    public void testCascadesToSourceSizeForFreeform() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-
-        final ActivityRecord source = createSourceActivity(freeformDisplay);
-        source.setBounds(0, 0, 412, 732);
-
-        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, source, options, mCurrent, mResult));
-
-        final Rect displayBounds = freeformDisplay.getBounds();
-        assertTrue("Left bounds should be larger than 0.", mResult.mBounds.left > 0);
-        assertTrue("Top bounds should be larger than 0.", mResult.mBounds.top > 0);
-        assertTrue("Bounds should be centered at somewhere in the left half, but it's "
-                + "centerX is " + mResult.mBounds.centerX(),
-                mResult.mBounds.centerX() < displayBounds.centerX());
-        assertTrue("Bounds should be centered at somewhere in the top half, but it's "
-                        + "centerY is " + mResult.mBounds.centerY(),
-                mResult.mBounds.centerY() < displayBounds.centerY());
-    }
-
-    @Test
-    public void testAdjustBoundsToFitDisplay_TopLeftOutOfDisplay() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-
-        final ActivityRecord source = createSourceActivity(freeformDisplay);
-        source.setBounds(0, 0, 200, 400);
-
-        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, source, options, mCurrent, mResult));
-
-        final Rect displayBounds = freeformDisplay.getBounds();
-        assertTrue("display bounds doesn't contain result. display bounds: "
-                + displayBounds + " result: " + mResult.mBounds,
-                displayBounds.contains(mResult.mBounds));
-    }
-
-    @Test
-    public void testAdjustBoundsToFitDisplay_BottomRightOutOfDisplay() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-
-        final ActivityRecord source = createSourceActivity(freeformDisplay);
-        source.setBounds(1720, 680, 1920, 1080);
-
-        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, source, options, mCurrent, mResult));
-
-        final Rect displayBounds = freeformDisplay.getBounds();
-        assertTrue("display bounds doesn't contain result. display bounds: "
-                        + displayBounds + " result: " + mResult.mBounds,
-                displayBounds.contains(mResult.mBounds));
-    }
-
-    @Test
-    public void testAdjustBoundsToFitDisplay_LargerThanDisplay() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        Configuration overrideConfig = new Configuration();
-        overrideConfig.setTo(mSupervisor.getOverrideConfiguration());
-        overrideConfig.setLayoutDirection(new Locale("ar"));
-        mSupervisor.onConfigurationChanged(overrideConfig);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-
-        final ActivityRecord source = createSourceActivity(freeformDisplay);
-        source.setBounds(1720, 680, 1920, 1080);
-
-        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, source, options, mCurrent, mResult));
-
-        final Rect displayBounds = freeformDisplay.getBounds();
-        assertTrue("display bounds doesn't contain result. display bounds: "
-                        + displayBounds + " result: " + mResult.mBounds,
-                displayBounds.contains(mResult.mBounds));
-    }
-
-    @Test
-    public void testRespectsLayoutMinDimensions() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setMinWidth(500).setMinHeight(800).build();
-
-        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, options, mCurrent, mResult));
-
-        assertEquals(500, mResult.mBounds.width());
-        assertEquals(800, mResult.mBounds.height());
-    }
-
-    @Test
-    public void testRotatesInPlaceInitialBoundsMismatchOrientation() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-        options.setLaunchBounds(new Rect(100, 100, 500, 300));
-
-        mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquals(new Rect(200, 0, 400, 400), mResult.mBounds);
-    }
-
-    @Test
-    public void testShiftsToRightForCloseToLeftBoundsWhenConflict() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        addFreeformTaskTo(freeformDisplay, new Rect(50, 50, 100, 150));
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-        options.setLaunchBounds(new Rect(50, 50, 500, 300));
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquals(new Rect(170, 50, 620, 300), mResult.mBounds);
-    }
-
-    @Test
-    public void testShiftsToLeftForCloseToRightBoundsWhenConflict() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        addFreeformTaskTo(freeformDisplay, new Rect(1720, 50, 1830, 150));
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-        options.setLaunchBounds(new Rect(1720, 50, 1850, 300));
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquals(new Rect(1600, 50, 1730, 300), mResult.mBounds);
-    }
-
-    @Test
-    public void testShiftsToRightFirstForHorizontallyCenteredAndCloseToTopWhenConflict() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        addFreeformTaskTo(freeformDisplay, new Rect(0, 0, 100, 300));
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-        options.setLaunchBounds(new Rect(0, 0, 1800, 200));
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquals(new Rect(120, 0, 1920, 200), mResult.mBounds);
-    }
-
-    @Test
-    public void testShiftsToLeftNoSpaceOnRightForHorizontallyCenteredAndCloseToTopWhenConflict() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        addFreeformTaskTo(freeformDisplay, new Rect(120, 0, 240, 300));
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-        options.setLaunchBounds(new Rect(120, 0, 1860, 200));
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquals(new Rect(0, 0, 1740, 200), mResult.mBounds);
-    }
-
-    @Test
-    public void testShiftsToBottomRightFirstForCenteredBoundsWhenConflict() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        addFreeformTaskTo(freeformDisplay, new Rect(120, 0, 240, 100));
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-        options.setLaunchBounds(new Rect(120, 0, 1800, 1013));
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquals(new Rect(240, 67, 1920, 1080), mResult.mBounds);
-    }
-
-    @Test
-    public void testShiftsToTopLeftIfNoSpaceOnBottomRightForCenteredBoundsWhenConflict() {
-        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
-                WINDOWING_MODE_FREEFORM);
-
-        addFreeformTaskTo(freeformDisplay, new Rect(120, 67, 240, 100));
-
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
-        options.setLaunchBounds(new Rect(120, 67, 1800, 1020));
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
-                mActivity, /* source */ null, options, mCurrent, mResult));
-
-        assertEquals(new Rect(0, 0, 1680,
-                953), mResult.mBounds);
-    }
-
-    private TestActivityDisplay createNewActivityDisplay(int windowingMode) {
-        final TestActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
-        display.setWindowingMode(windowingMode);
-        display.setBounds(/* left */ 0, /* top */ 0, /* right */ 1920, /* bottom */ 1080);
-        display.getConfiguration().densityDpi = DENSITY_DEFAULT;
-        return display;
-    }
-
-    private ActivityRecord createSourceActivity(TestActivityDisplay display) {
-        final TestActivityStack stack = display.createStack(display.getWindowingMode(),
-                ACTIVITY_TYPE_STANDARD, true);
-        return new ActivityBuilder(mService).setStack(stack).setCreateTask(true).build();
-    }
-
-    private void addFreeformTaskTo(TestActivityDisplay display, Rect bounds) {
-        final TestActivityStack stack = display.createStack(display.getWindowingMode(),
-                ACTIVITY_TYPE_STANDARD, true);
-        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
-        task.setBounds(bounds);
-    }
-
-    private void assertEquivalentWindowingMode(int expected, int actual, int parentWindowingMode) {
-        if (expected != parentWindowingMode) {
-            assertEquals(expected, actual);
-        } else {
-            assertEquals(WINDOWING_MODE_UNDEFINED, actual);
-        }
-    }
-
-    private static class WindowLayoutBuilder {
-        private int mWidth = -1;
-        private int mHeight = -1;
-        private float mWidthFraction = -1f;
-        private float mHeightFraction = -1f;
-        private int mGravity = Gravity.NO_GRAVITY;
-        private int mMinWidth = -1;
-        private int mMinHeight = -1;
-
-        private WindowLayoutBuilder setWidth(int width) {
-            mWidth = width;
-            return this;
-        }
-
-        private WindowLayoutBuilder setHeight(int height) {
-            mHeight = height;
-            return this;
-        }
-
-        private WindowLayoutBuilder setWidthFraction(float widthFraction) {
-            mWidthFraction = widthFraction;
-            return this;
-        }
-
-        private WindowLayoutBuilder setHeightFraction(float heightFraction) {
-            mHeightFraction = heightFraction;
-            return this;
-        }
-
-        private WindowLayoutBuilder setGravity(int gravity) {
-            mGravity = gravity;
-            return this;
-        }
-
-        private WindowLayoutBuilder setMinWidth(int minWidth) {
-            mMinWidth = minWidth;
-            return this;
-        }
-
-        private WindowLayoutBuilder setMinHeight(int minHeight) {
-            mMinHeight = minHeight;
-            return this;
-        }
-
-        private ActivityInfo.WindowLayout build() {
-            return new ActivityInfo.WindowLayout(mWidth, mWidthFraction, mHeight, mHeightFraction,
-                    mGravity, mMinWidth, mMinHeight);
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
deleted file mode 100644
index 48bfe1d..0000000
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2016 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.server.am;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import static androidx.test.InstrumentationRegistry.getTargetContext;
-
-import android.content.pm.UserInfo;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
-import android.util.SparseBooleanArray;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import androidx.test.filters.FlakyTest;
-
-/**
- * Tests for {@link TaskPersister}.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:TaskPersisterTest
- */
-@Presubmit
-@FlakyTest(detail = "Promote to presubmit if stable")
-public class TaskPersisterTest {
-    private static final String TEST_USER_NAME = "AM-Test-User";
-
-    private TaskPersister mTaskPersister;
-    private int testUserId;
-    private UserManager mUserManager;
-
-    @Before
-    public void setUp() throws Exception {
-        mUserManager = UserManager.get(getTargetContext());
-        mTaskPersister = new TaskPersister(getTargetContext().getFilesDir());
-        // In ARC, the maximum number of supported users is one, which is different from the ones of
-        // most phones (more than 4). This prevents TaskPersisterTest from creating another user for
-        // test. However, since guest users can be added as much as possible, we create guest user
-        // in the test.
-        testUserId = createUser(TEST_USER_NAME, UserInfo.FLAG_GUEST);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mTaskPersister.unloadUserDataFromMemory(testUserId);
-        removeUser(testUserId);
-    }
-
-    private int getRandomTaskIdForUser(int userId) {
-        int taskId = (int) (Math.random() * UserHandle.PER_USER_RANGE);
-        taskId += UserHandle.PER_USER_RANGE * userId;
-        return taskId;
-    }
-
-    @Test
-    public void testTaskIdsPersistence() {
-        SparseBooleanArray taskIdsOnFile = new SparseBooleanArray();
-        for (int i = 0; i < 100; i++) {
-            taskIdsOnFile.put(getRandomTaskIdForUser(testUserId), true);
-        }
-        mTaskPersister.writePersistedTaskIdsForUser(taskIdsOnFile, testUserId);
-        SparseBooleanArray newTaskIdsOnFile = mTaskPersister
-                .loadPersistedTaskIdsForUser(testUserId);
-        assertEquals("TaskIds written differ from TaskIds read back from file",
-                taskIdsOnFile, newTaskIdsOnFile);
-    }
-
-    private int createUser(String name, int flags) {
-        UserInfo user = mUserManager.createUser(name, flags);
-        assertNotNull("Error while creating the test user: " + TEST_USER_NAME, user);
-        return user.id;
-    }
-
-    private void removeUser(int userId) {
-        boolean userRemoved = mUserManager.removeUser(userId);
-        assertTrue("Error while removing the test user: " + TEST_USER_NAME, userRemoved);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
deleted file mode 100644
index 27766d3..0000000
--- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.am;
-
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.service.voice.IVoiceInteractionSession;
-import android.util.Xml;
-
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.app.IVoiceInteractor;
-import com.android.server.am.TaskRecord.TaskRecordFactory;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.util.ArrayList;
-
-/**
- * Tests for exercising {@link TaskRecord}.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.am.TaskRecordTests
- */
-@MediumTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class TaskRecordTests extends ActivityTestsBase {
-
-    private static final String TASK_TAG = "task";
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-        TaskRecord.setTaskRecordFactory(null);
-        setupActivityTaskManagerService();
-    }
-
-    @Test
-    public void testRestoreWindowedTask() throws Exception {
-        final TaskRecord expected = createTaskRecord(64);
-        expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
-
-        final File serializedFile = serializeToFile(expected);
-
-        try {
-            final TaskRecord actual = restoreFromFile(serializedFile);
-            assertEquals(expected.taskId, actual.taskId);
-            assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
-        } finally {
-            serializedFile.delete();
-        }
-    }
-
-    @Test
-    public void testDefaultTaskFactoryNotNull() throws Exception {
-        assertNotNull(TaskRecord.getTaskRecordFactory());
-    }
-
-    @Test
-    public void testCreateTestRecordUsingCustomizedFactory() throws Exception {
-        TestTaskRecordFactory factory = new TestTaskRecordFactory();
-        TaskRecord.setTaskRecordFactory(factory);
-
-        assertFalse(factory.mCreated);
-
-        TaskRecord.create(null, 0, null, null, null, null);
-
-        assertTrue(factory.mCreated);
-    }
-
-    @Test
-    public void testReturnsToHomeStack() throws Exception {
-        final TaskRecord task = createTaskRecord(1);
-        assertFalse(task.returnsToHomeStack());
-        task.intent = null;
-        assertFalse(task.returnsToHomeStack());
-        task.intent = new Intent();
-        assertFalse(task.returnsToHomeStack());
-        task.intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
-        assertTrue(task.returnsToHomeStack());
-    }
-
-    private File serializeToFile(TaskRecord r) throws IOException, XmlPullParserException {
-        final File tmpFile = File.createTempFile(r.taskId + "_task_", "xml");
-
-        try (final OutputStream os = new FileOutputStream(tmpFile)) {
-            final XmlSerializer serializer = Xml.newSerializer();
-            serializer.setOutput(os, "UTF-8");
-            serializer.startDocument(null, true);
-            serializer.startTag(null, TASK_TAG);
-            r.saveToXml(serializer);
-            serializer.endTag(null, TASK_TAG);
-            serializer.endDocument();
-        }
-
-        return tmpFile;
-    }
-
-    private TaskRecord restoreFromFile(File file) throws IOException, XmlPullParserException {
-        try (final Reader reader = new BufferedReader(new FileReader(file))) {
-            final XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(reader);
-            assertEquals(XmlPullParser.START_TAG, parser.next());
-            assertEquals(TASK_TAG, parser.getName());
-            return TaskRecord.restoreFromXml(parser, mService.mStackSupervisor);
-        }
-    }
-
-    private TaskRecord createTaskRecord(int taskId) {
-        return new TaskRecord(mService, taskId, new Intent(), null, null, null,
-                ActivityBuilder.getDefaultComponent(), null, false, false, false, 0, 10050, null,
-                new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0, null, 0, false, false, false, 0, 0
-        );
-    }
-
-    private static class TestTaskRecordFactory extends TaskRecordFactory {
-        private boolean mCreated = false;
-
-        @Override
-        TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
-                Intent intent,
-                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
-            mCreated = true;
-            return null;
-        }
-
-        @Override
-        TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
-                Intent intent,
-                ActivityManager.TaskDescription taskDescription) {
-            mCreated = true;
-            return null;
-        }
-
-        @Override
-        TaskRecord create(ActivityTaskManagerService service, int taskId, Intent intent,
-                Intent affinityIntent, String affinity, String rootAffinity,
-                ComponentName realActivity,
-                ComponentName origActivity, boolean rootWasReset, boolean autoRemoveRecents,
-                boolean askedCompatMode, int userId, int effectiveUid, String lastDescription,
-                ArrayList<ActivityRecord> activities, long lastTimeMoved,
-                boolean neverRelinquishIdentity,
-                ActivityManager.TaskDescription lastTaskDescription,
-                int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
-                int callingUid, String callingPackage, int resizeMode,
-                boolean supportsPictureInPicture,
-                boolean realActivitySuspended, boolean userSetupComplete, int minWidth,
-                int minHeight) {
-            mCreated = true;
-            return null;
-        }
-
-        @Override
-        TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
-                throws IOException, XmlPullParserException {
-            mCreated = true;
-            return null;
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java b/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
deleted file mode 100644
index 3f7c714..0000000
--- a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright (C) 2016 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.server.am;
-
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityManager.TaskDescription;
-import android.app.ActivityTaskManager;
-import android.app.IActivityManager;
-import android.app.ITaskStackListener;
-import android.app.Instrumentation.ActivityMonitor;
-import android.app.TaskStackListener;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.os.RemoteException;
-import android.support.test.uiautomator.UiDevice;
-import android.text.TextUtils;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.annotations.GuardedBy;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class TaskStackChangedListenerTest {
-
-    private IActivityManager mService;
-    private ITaskStackListener mTaskStackListener;
-
-    private static final Object sLock = new Object();
-    @GuardedBy("sLock")
-    private static boolean sTaskStackChangedCalled;
-    private static boolean sActivityBResumed;
-
-    @Before
-    public void setUp() throws Exception {
-        mService = ActivityManager.getService();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        ActivityTaskManager.getService().unregisterTaskStackListener(mTaskStackListener);
-        mTaskStackListener = null;
-    }
-
-    @Test
-    public void testTaskStackChanged_afterFinish() throws Exception {
-        registerTaskStackChangedListener(new TaskStackListener() {
-            @Override
-            public void onTaskStackChanged() throws RemoteException {
-                synchronized (sLock) {
-                    sTaskStackChangedCalled = true;
-                }
-            }
-        });
-
-        Context ctx = InstrumentationRegistry.getContext();
-        ctx.startActivity(new Intent(ctx, ActivityA.class));
-        UiDevice.getInstance(getInstrumentation()).waitForIdle();
-        synchronized (sLock) {
-            Assert.assertTrue(sTaskStackChangedCalled);
-        }
-        Assert.assertTrue(sActivityBResumed);
-    }
-
-    @Test
-    public void testTaskDescriptionChanged() throws Exception {
-        final Object[] params = new Object[2];
-        final CountDownLatch latch = new CountDownLatch(1);
-        registerTaskStackChangedListener(new TaskStackListener() {
-            int taskId = -1;
-
-            @Override
-            public void onTaskCreated(int taskId, ComponentName componentName)
-                    throws RemoteException {
-                this.taskId = taskId;
-            }
-            @Override
-            public void onTaskDescriptionChanged(int taskId, TaskDescription td)
-                    throws RemoteException {
-                if (this.taskId == taskId && !TextUtils.isEmpty(td.getLabel())) {
-                    params[0] = taskId;
-                    params[1] = td;
-                    latch.countDown();
-                }
-            }
-        });
-        final Activity activity = startTestActivity(ActivityTaskDescriptionChange.class);
-        waitForCallback(latch);
-        assertEquals(activity.getTaskId(), params[0]);
-        assertEquals("Test Label", ((TaskDescription) params[1]).getLabel());
-    }
-
-    @Test
-    public void testActivityRequestedOrientationChanged() throws Exception {
-        final int[] params = new int[2];
-        final CountDownLatch latch = new CountDownLatch(1);
-        registerTaskStackChangedListener(new TaskStackListener() {
-            @Override
-            public void onActivityRequestedOrientationChanged(int taskId,
-                    int requestedOrientation) {
-                params[0] = taskId;
-                params[1] = requestedOrientation;
-                latch.countDown();
-            }
-        });
-        final Activity activity = startTestActivity(ActivityRequestedOrientationChange.class);
-        waitForCallback(latch);
-        assertEquals(activity.getTaskId(), params[0]);
-        assertEquals(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, params[1]);
-    }
-
-    /**
-     * Tests for onTaskCreated, onTaskMovedToFront, onTaskRemoved and onTaskRemovalStarted.
-     */
-    @Test
-    public void testTaskChangeCallBacks() throws Exception {
-        final Object[] params = new Object[2];
-        final CountDownLatch taskCreatedLaunchLatch = new CountDownLatch(1);
-        final CountDownLatch taskMovedToFrontLatch = new CountDownLatch(1);
-        final CountDownLatch taskRemovedLatch = new CountDownLatch(1);
-        final CountDownLatch taskRemovalStartedLatch = new CountDownLatch(1);
-        final CountDownLatch onDetachedFromWindowLatch = new CountDownLatch(1);
-        registerTaskStackChangedListener(new TaskStackListener() {
-            @Override
-            public void onTaskCreated(int taskId, ComponentName componentName)
-                    throws RemoteException {
-                params[0] = taskId;
-                params[1] = componentName;
-                taskCreatedLaunchLatch.countDown();
-            }
-
-            @Override
-            public void onTaskMovedToFront(int taskId) throws RemoteException {
-                params[0] = taskId;
-                taskMovedToFrontLatch.countDown();
-            }
-
-            @Override
-            public void onTaskRemovalStarted(int taskId) {
-                params[0] = taskId;
-                taskRemovalStartedLatch.countDown();
-            }
-
-            @Override
-            public void onTaskRemoved(int taskId) throws RemoteException {
-                params[0] = taskId;
-                taskRemovedLatch.countDown();
-            }
-        });
-
-        final ActivityTaskChangeCallbacks activity =
-                (ActivityTaskChangeCallbacks) startTestActivity(ActivityTaskChangeCallbacks.class);
-        activity.setDetachedFromWindowLatch(onDetachedFromWindowLatch);
-        final int id = activity.getTaskId();
-
-        // Test for onTaskCreated.
-        waitForCallback(taskCreatedLaunchLatch);
-        assertEquals(id, params[0]);
-        ComponentName componentName = (ComponentName) params[1];
-        assertEquals(ActivityTaskChangeCallbacks.class.getName(), componentName.getClassName());
-
-        // Test for onTaskMovedToFront.
-        assertEquals(1, taskMovedToFrontLatch.getCount());
-        mService.moveTaskToFront(id, 0, null);
-        waitForCallback(taskMovedToFrontLatch);
-        assertEquals(activity.getTaskId(), params[0]);
-
-        // Test for onTaskRemovalStarted.
-        assertEquals(1, taskRemovalStartedLatch.getCount());
-        activity.finishAndRemoveTask();
-        waitForCallback(taskRemovalStartedLatch);
-        // onTaskRemovalStarted happens before the activity's window is removed.
-        assertFalse(activity.onDetachedFromWindowCalled);
-        assertEquals(id, params[0]);
-
-        // Test for onTaskRemoved.
-        assertEquals(1, taskRemovedLatch.getCount());
-        waitForCallback(taskRemovedLatch);
-        assertEquals(id, params[0]);
-        waitForCallback(onDetachedFromWindowLatch);
-        assertTrue(activity.onDetachedFromWindowCalled);
-    }
-
-    /**
-     * Starts the provided activity and returns the started instance.
-     */
-    private TestActivity startTestActivity(Class<?> activityClass) throws InterruptedException {
-        final Context context = InstrumentationRegistry.getContext();
-        final ActivityMonitor monitor =
-                new ActivityMonitor(activityClass.getName(), null, false);
-        InstrumentationRegistry.getInstrumentation().addMonitor(monitor);
-        context.startActivity(new Intent(context, activityClass));
-        final TestActivity activity = (TestActivity)monitor.waitForActivityWithTimeout(1000);
-        if (activity == null) {
-            throw new RuntimeException("Timed out waiting for Activity");
-        }
-        activity.waitForResumeStateChange(true);
-        return activity;
-    }
-
-    private void registerTaskStackChangedListener(ITaskStackListener listener) throws Exception {
-        mTaskStackListener = listener;
-        ActivityTaskManager.getService().registerTaskStackListener(listener);
-    }
-
-    private void waitForCallback(CountDownLatch latch) {
-        try {
-        final boolean result = latch.await(2, TimeUnit.SECONDS);
-            if (!result) {
-                throw new RuntimeException("Timed out waiting for task stack change notification");
-            }
-        }catch (InterruptedException e) {}
-    }
-
-    public static class TestActivity extends Activity {
-        boolean mIsResumed = false;
-
-        @Override
-        protected void onPostResume() {
-            super.onPostResume();
-            synchronized (this) {
-                mIsResumed = true;
-                notifyAll();
-            }
-        }
-
-        @Override
-        protected void onPause() {
-            super.onPause();
-            synchronized (this) {
-                mIsResumed = false;
-                notifyAll();
-            }
-        }
-
-        /**
-         * If isResumed is {@code true}, sleep the thread until the activity is resumed.
-         * if {@code false}, sleep the thread until the activity is paused.
-         */
-        public void waitForResumeStateChange(boolean isResumed) throws InterruptedException {
-            synchronized (this) {
-                if (mIsResumed == isResumed) {
-                    return;
-                }
-                wait(5000);
-            }
-            assertTrue("The activity resume state change timed out", mIsResumed == isResumed);
-        }
-    }
-
-    public static class ActivityA extends TestActivity {
-
-        private boolean mActivityBLaunched = false;
-
-        @Override
-        protected void onPostResume() {
-            super.onPostResume();
-            if (mActivityBLaunched) {
-                return;
-            }
-            mActivityBLaunched = true;
-            finish();
-            startActivity(new Intent(this, ActivityB.class));
-        }
-    }
-
-    public static class ActivityB extends TestActivity {
-
-        @Override
-        protected void onPostResume() {
-            super.onPostResume();
-            synchronized (sLock) {
-                sTaskStackChangedCalled = false;
-            }
-            sActivityBResumed = true;
-            finish();
-        }
-    }
-
-    public static class ActivityRequestedOrientationChange extends TestActivity {
-        @Override
-        protected void onPostResume() {
-            super.onPostResume();
-            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-            finish();
-        }
-    }
-
-    public static class ActivityTaskDescriptionChange extends TestActivity {
-        @Override
-        protected void onPostResume() {
-            super.onPostResume();
-            setTaskDescription(new TaskDescription("Test Label"));
-            finish();
-        }
-    }
-
-    public static class ActivityTaskChangeCallbacks extends TestActivity {
-        boolean onDetachedFromWindowCalled = false;
-        CountDownLatch onDetachedFromWindowCountDownLatch;
-
-        @Override
-        public void onDetachedFromWindow() {
-            onDetachedFromWindowCalled = true;
-            onDetachedFromWindowCountDownLatch.countDown();
-        }
-
-        void setDetachedFromWindowLatch(CountDownLatch countDownLatch) {
-            onDetachedFromWindowCountDownLatch = countDownLatch;
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 75e1d0d..bd03a8d 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.am;
@@ -19,6 +19,8 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
 import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
 import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
@@ -49,8 +51,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import static androidx.test.InstrumentationRegistry.getTargetContext;
-
 import android.app.IUserSwitchObserver;
 import android.content.Context;
 import android.content.IIntentReceiver;
@@ -68,6 +68,9 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
 import com.android.server.pm.UserManagerService;
 import com.android.server.wm.WindowManagerService;
 
@@ -81,20 +84,18 @@
 import java.util.List;
 import java.util.Set;
 
-import androidx.test.filters.SmallTest;
-
 /**
  * Tests for {@link UserController}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.am.UserControllerTest
+ *  atest FrameworksServicesTests:UserControllerTest
  */
-@Presubmit
 @SmallTest
+@Presubmit
 public class UserControllerTest {
     private static final int TEST_USER_ID = 10;
     private static final int NONEXIST_USER_ID = 2;
-    private static String TAG = UserControllerTest.class.getSimpleName();
+    private static final String TAG = UserControllerTest.class.getSimpleName();
     private UserController mUserController;
     private TestInjector mInjector;
 
@@ -121,7 +122,7 @@
     @Before
     public void setUp() throws Exception {
         runWithDexmakerShareClassLoader(() -> {
-            mInjector = spy(new TestInjector(getTargetContext()));
+            mInjector = spy(new TestInjector(getInstrumentation().getTargetContext()));
             doNothing().when(mInjector).clearAllLockedTasks(anyString());
             doNothing().when(mInjector).startHomeActivity(anyInt(), anyString());
             doReturn(false).when(mInjector).stackSupervisorSwitchUser(anyInt(), any());
@@ -133,7 +134,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mInjector.handlerThread.quit();
+        mInjector.mHandlerThread.quit();
         validateMockitoUsage();
     }
 
@@ -148,6 +149,7 @@
         startForegroundUserAssertions();
     }
 
+    @FlakyTest(bugId = 118932054)
     @Test
     public void testStartUser_background() {
         mUserController.startUser(TEST_USER_ID, false /* foreground */);
@@ -169,8 +171,8 @@
 
     private void startUserAssertions(
             List<String> expectedActions, Set<Integer> expectedMessageCodes) {
-        assertEquals(expectedActions, getActions(mInjector.sentIntents));
-        Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
+        assertEquals(expectedActions, getActions(mInjector.mSentIntents));
+        Set<Integer> actualCodes = mInjector.mHandler.getMessageCodes();
         assertEquals("Unexpected message sent", expectedMessageCodes, actualCodes);
     }
 
@@ -180,7 +182,7 @@
 
     private void startForegroundUserAssertions() {
         startUserAssertions(START_FOREGROUND_USER_ACTIONS, START_FOREGROUND_USER_MESSAGE_CODES);
-        Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
+        Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
         assertNotNull(reportMsg);
         UserState userState = (UserState) reportMsg.obj;
         assertNotNull(userState);
@@ -211,19 +213,19 @@
         mUserController.registerUserSwitchObserver(observer, "mock");
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, true);
-        Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
+        Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
         assertNotNull(reportMsg);
         UserState userState = (UserState) reportMsg.obj;
         int oldUserId = reportMsg.arg1;
         int newUserId = reportMsg.arg2;
         // Call dispatchUserSwitch and verify that observer was called only once
-        mInjector.handler.clearAllRecordedMessages();
+        mInjector.mHandler.clearAllRecordedMessages();
         mUserController.dispatchUserSwitch(userState, oldUserId, newUserId);
         verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
         Set<Integer> expectedCodes = Collections.singleton(CONTINUE_USER_SWITCH_MSG);
-        Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
+        Set<Integer> actualCodes = mInjector.mHandler.getMessageCodes();
         assertEquals("Unexpected message sent", expectedCodes, actualCodes);
-        Message conMsg = mInjector.handler.getMessageForCode(CONTINUE_USER_SWITCH_MSG);
+        Message conMsg = mInjector.mHandler.getMessageForCode(CONTINUE_USER_SWITCH_MSG);
         assertNotNull(conMsg);
         userState = (UserState) conMsg.obj;
         assertNotNull(userState);
@@ -241,17 +243,17 @@
         mUserController.registerUserSwitchObserver(observer, "mock");
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, true);
-        Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
+        Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
         assertNotNull(reportMsg);
         UserState userState = (UserState) reportMsg.obj;
         int oldUserId = reportMsg.arg1;
         int newUserId = reportMsg.arg2;
         // Call dispatchUserSwitch and verify that observer was called only once
-        mInjector.handler.clearAllRecordedMessages();
+        mInjector.mHandler.clearAllRecordedMessages();
         mUserController.dispatchUserSwitch(userState, oldUserId, newUserId);
         verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
         // Verify that CONTINUE_USER_SWITCH_MSG is not sent (triggers timeout)
-        Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
+        Set<Integer> actualCodes = mInjector.mHandler.getMessageCodes();
         assertWithMessage("No messages should be sent").that(actualCodes).isEmpty();
     }
 
@@ -259,12 +261,12 @@
     public void testContinueUserSwitch() {
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, true);
-        Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
+        Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
         assertNotNull(reportMsg);
         UserState userState = (UserState) reportMsg.obj;
         int oldUserId = reportMsg.arg1;
         int newUserId = reportMsg.arg2;
-        mInjector.handler.clearAllRecordedMessages();
+        mInjector.mHandler.clearAllRecordedMessages();
         // Verify that continueUserSwitch worked as expected
         mUserController.continueUserSwitch(userState, oldUserId, newUserId);
         verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
@@ -276,12 +278,12 @@
         mUserController.mUserSwitchUiEnabled = false;
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, true);
-        Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
+        Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
         assertNotNull(reportMsg);
         UserState userState = (UserState) reportMsg.obj;
         int oldUserId = reportMsg.arg1;
         int newUserId = reportMsg.arg2;
-        mInjector.handler.clearAllRecordedMessages();
+        mInjector.mHandler.clearAllRecordedMessages();
         // Verify that continueUserSwitch worked as expected
         mUserController.continueUserSwitch(userState, oldUserId, newUserId);
         verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
@@ -290,9 +292,9 @@
 
     private void continueUserSwitchAssertions() {
         Set<Integer> expectedCodes = Collections.singleton(REPORT_USER_SWITCH_COMPLETE_MSG);
-        Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
+        Set<Integer> actualCodes = mInjector.mHandler.getMessageCodes();
         assertEquals("Unexpected message sent", expectedCodes, actualCodes);
-        Message msg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_COMPLETE_MSG);
+        Message msg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_COMPLETE_MSG);
         assertNotNull(msg);
         assertEquals("Unexpected userId", TEST_USER_ID, msg.arg1);
     }
@@ -305,10 +307,10 @@
         mUserController.registerUserSwitchObserver(observer, "mock");
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, true);
-        Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
+        Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
         assertNotNull(reportMsg);
         int newUserId = reportMsg.arg2;
-        mInjector.handler.clearAllRecordedMessages();
+        mInjector.mHandler.clearAllRecordedMessages();
         // Mockito can't reset only interactions, so just verify that this hasn't been
         // called with 'false' until after dispatchUserSwitchComplete.
         verify(mInjector.getWindowManager(), never()).setSwitchingUser(false);
@@ -321,7 +323,7 @@
 
     private void setUpUser(int userId, int flags) {
         UserInfo userInfo = new UserInfo(userId, "User" + userId, flags);
-        when(mInjector.userManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo);
+        when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo);
     }
 
     private static List<String> getActions(List<Intent> intents) {
@@ -334,45 +336,46 @@
 
     // Should be public to allow mocking
     private static class TestInjector extends UserController.Injector {
-        TestHandler handler;
-        TestHandler uiHandler;
-        HandlerThread handlerThread;
-        UserManagerService userManagerMock;
-        UserManagerInternal userManagerInternalMock;
-        WindowManagerService windowManagerMock;
-        private Context mCtx;
-        List<Intent> sentIntents = new ArrayList<>();
+        public final TestHandler mHandler;
+        public final HandlerThread mHandlerThread;
+        public final UserManagerService mUserManagerMock;
+        public final List<Intent> mSentIntents = new ArrayList<>();
+
+        private final TestHandler mUiHandler;
+        private final UserManagerInternal mUserManagerInternalMock;
+        private final WindowManagerService mWindowManagerMock;
+        private final Context mCtx;
 
         TestInjector(Context ctx) {
             super(null);
             mCtx = ctx;
-            handlerThread = new HandlerThread(TAG);
-            handlerThread.start();
-            handler = new TestHandler(handlerThread.getLooper());
-            uiHandler = new TestHandler(handlerThread.getLooper());
-            userManagerMock = mock(UserManagerService.class);
-            userManagerInternalMock = mock(UserManagerInternal.class);
-            windowManagerMock = mock(WindowManagerService.class);
+            mHandlerThread = new HandlerThread(TAG);
+            mHandlerThread.start();
+            mHandler = new TestHandler(mHandlerThread.getLooper());
+            mUiHandler = new TestHandler(mHandlerThread.getLooper());
+            mUserManagerMock = mock(UserManagerService.class);
+            mUserManagerInternalMock = mock(UserManagerInternal.class);
+            mWindowManagerMock = mock(WindowManagerService.class);
         }
 
         @Override
         protected Handler getHandler(Handler.Callback callback) {
-            return handler;
+            return mHandler;
         }
 
         @Override
         protected Handler getUiHandler(Handler.Callback callback) {
-            return uiHandler;
+            return mUiHandler;
         }
 
         @Override
         protected UserManagerService getUserManager() {
-            return userManagerMock;
+            return mUserManagerMock;
         }
 
         @Override
         UserManagerInternal getUserManagerInternal() {
-            return userManagerInternalMock;
+            return mUserManagerInternalMock;
         }
 
         @Override
@@ -388,7 +391,7 @@
 
         @Override
         WindowManagerService getWindowManager() {
-            return windowManagerMock;
+            return mWindowManagerMock;
         }
 
         @Override
@@ -402,7 +405,7 @@
                 String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered,
                 boolean sticky, int callingPid, int callingUid, int userId) {
             Log.i(TAG, "broadcastIntentLocked " + intent);
-            sentIntents.add(intent);
+            mSentIntents.add(intent);
             return 0;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index 92211ec..7c00299 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -118,8 +118,8 @@
     }
 
     @Test
-    public void initialize_forUserSystem_successfullyInitialized() {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+    public void initializeService_forUserSystem_successfullyInitialized() {
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
 
         assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
@@ -127,29 +127,29 @@
     // The BackupManagerService can only be initialized by USER_SYSTEM, so we check that if any
     // other user trying to initialize it leaves it non-active.
     @Test
-    public void initialize_forNonUserSystem_nonInitialized() {
-        mTrampoline.initialize(NON_USER_SYSTEM);
+    public void initializeService_forNonUserSystem_nonInitialized() {
+        mTrampoline.initializeService(NON_USER_SYSTEM);
 
         assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
     }
 
     @Test
-    public void initialize_globallyDisabled_nonInitialized() {
+    public void initializeService_globallyDisabled_nonInitialized() {
         TrampolineTestable.sBackupDisabled = true;
 
         TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
-        trampoline.initialize(UserHandle.USER_SYSTEM);
+        trampoline.initializeService(UserHandle.USER_SYSTEM);
 
         assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     // Verify that BackupManagerService is not initialized if suppress file exists.
     @Test
-    public void initialize_suppressFileExists_nonInitialized() {
+    public void initializeService_suppressFileExists_nonInitialized() {
         when(mSuppressFileMock.exists()).thenReturn(true);
 
         TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
-        trampoline.initialize(UserHandle.USER_SYSTEM);
+        trampoline.initializeService(UserHandle.USER_SYSTEM);
 
         assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
@@ -229,7 +229,7 @@
     @Test
     public void setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated()
             throws IOException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
 
         mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
@@ -243,7 +243,7 @@
     setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated_ioExceptionHandled()
             throws IOException {
         when(mSuppressFileMock.createNewFile()).thenThrow(new IOException());
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
 
         mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
@@ -269,7 +269,7 @@
 
     @Test
     public void dataChanged_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.dataChanged(PACKAGE_NAME);
         verify(mBackupManagerServiceMock).dataChanged(PACKAGE_NAME);
     }
@@ -282,7 +282,7 @@
 
     @Test
     public void clearBackupData_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
         verify(mBackupManagerServiceMock).clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
     }
@@ -295,7 +295,7 @@
 
     @Test
     public void agentConnected_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.agentConnected(PACKAGE_NAME, mAgentMock);
         verify(mBackupManagerServiceMock).agentConnected(PACKAGE_NAME, mAgentMock);
     }
@@ -308,7 +308,7 @@
 
     @Test
     public void agentDisconnected_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.agentDisconnected(PACKAGE_NAME);
         verify(mBackupManagerServiceMock).agentDisconnected(PACKAGE_NAME);
     }
@@ -321,7 +321,7 @@
 
     @Test
     public void restoreAtInstall_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.restoreAtInstall(PACKAGE_NAME, 123);
         verify(mBackupManagerServiceMock).restoreAtInstall(PACKAGE_NAME, 123);
     }
@@ -334,7 +334,7 @@
 
     @Test
     public void setBackupEnabled_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.setBackupEnabled(true);
         verify(mBackupManagerServiceMock).setBackupEnabled(true);
     }
@@ -347,7 +347,7 @@
 
     @Test
     public void setAutoRestore_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.setAutoRestore(true);
         verify(mBackupManagerServiceMock).setAutoRestore(true);
     }
@@ -360,7 +360,7 @@
 
     @Test
     public void setBackupProvisioned_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.setBackupProvisioned(true);
         verify(mBackupManagerServiceMock).setBackupProvisioned(true);
     }
@@ -373,7 +373,7 @@
 
     @Test
     public void isBackupEnabled_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.isBackupEnabled();
         verify(mBackupManagerServiceMock).isBackupEnabled();
     }
@@ -386,7 +386,7 @@
 
     @Test
     public void setBackupPassword_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
         verify(mBackupManagerServiceMock).setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
     }
@@ -399,7 +399,7 @@
 
     @Test
     public void hasBackupPassword_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.hasBackupPassword();
         verify(mBackupManagerServiceMock).hasBackupPassword();
     }
@@ -412,7 +412,7 @@
 
     @Test
     public void backupNow_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.backupNow();
         verify(mBackupManagerServiceMock).backupNow();
     }
@@ -427,7 +427,7 @@
 
     @Test
     public void adbBackup_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.adbBackup(mParcelFileDescriptorMock, true, true, true, true, true, true, true,
                 true,
                 PACKAGE_NAMES);
@@ -444,7 +444,7 @@
 
     @Test
     public void fullTransportBackup_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.fullTransportBackup(PACKAGE_NAMES);
         verify(mBackupManagerServiceMock).fullTransportBackup(PACKAGE_NAMES);
     }
@@ -457,7 +457,7 @@
 
     @Test
     public void adbRestore_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.adbRestore(mParcelFileDescriptorMock);
         verify(mBackupManagerServiceMock).adbRestore(mParcelFileDescriptorMock);
     }
@@ -472,7 +472,7 @@
 
     @Test
     public void acknowledgeFullBackupOrRestore_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.acknowledgeFullBackupOrRestore(123, true, CURRENT_PASSWORD, ENCRYPTION_PASSWORD,
                 mFullBackupRestoreObserverMock);
         verify(mBackupManagerServiceMock).acknowledgeAdbBackupOrRestore(123, true, CURRENT_PASSWORD,
@@ -489,7 +489,7 @@
     public void getCurrentTransport_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.getCurrentTransport()).thenReturn(TRANSPORT_NAME);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
 
         assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransport());
         verify(mBackupManagerServiceMock).getCurrentTransport();
@@ -505,7 +505,7 @@
     public void listAllTransports_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.listAllTransports()).thenReturn(TRANSPORTS);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertEquals(TRANSPORTS, mTrampoline.listAllTransports());
         verify(mBackupManagerServiceMock).listAllTransports();
     }
@@ -521,7 +521,7 @@
         when(mBackupManagerServiceMock.listAllTransportComponents()).thenReturn(
                 TRANSPORT_COMPONENTS);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponents());
         verify(mBackupManagerServiceMock).listAllTransportComponents();
     }
@@ -536,7 +536,7 @@
     public void getTransportWhitelist_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertEquals(TRANSPORTS, mTrampoline.getTransportWhitelist());
         verify(mBackupManagerServiceMock).getTransportWhitelist();
     }
@@ -552,7 +552,7 @@
     public void describeTransport_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.updateTransportAttributes(TRANSPORT_COMPONENT_NAME, TRANSPORT_NAME, null,
                 "Transport Destination", null, "Data Management");
         verify(mBackupManagerServiceMock).updateTransportAttributes(TRANSPORT_COMPONENT_NAME,
@@ -567,7 +567,7 @@
 
     @Test
     public void selectBackupTransport_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.selectBackupTransport(TRANSPORT_NAME);
         verify(mBackupManagerServiceMock).selectBackupTransport(TRANSPORT_NAME);
     }
@@ -634,7 +634,7 @@
 
     @Test
     public void selectBackupTransportAsync_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME, null);
         verify(mBackupManagerServiceMock).selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME,
                 null);
@@ -652,7 +652,7 @@
         when(mBackupManagerServiceMock.getConfigurationIntent(TRANSPORT_NAME)).thenReturn(
                 configurationIntentStub);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertEquals(configurationIntentStub, mTrampoline.getConfigurationIntent(TRANSPORT_NAME));
         verify(mBackupManagerServiceMock).getConfigurationIntent(TRANSPORT_NAME);
     }
@@ -668,7 +668,7 @@
         when(mBackupManagerServiceMock.getDestinationString(TRANSPORT_NAME)).thenReturn(
                 DESTINATION_STRING);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertEquals(DESTINATION_STRING, mTrampoline.getDestinationString(TRANSPORT_NAME));
         verify(mBackupManagerServiceMock).getDestinationString(TRANSPORT_NAME);
     }
@@ -685,7 +685,7 @@
         when(mBackupManagerServiceMock.getDataManagementIntent(TRANSPORT_NAME)).thenReturn(
                 dataManagementIntent);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertEquals(dataManagementIntent, mTrampoline.getDataManagementIntent(TRANSPORT_NAME));
         verify(mBackupManagerServiceMock).getDataManagementIntent(TRANSPORT_NAME);
     }
@@ -701,7 +701,7 @@
         when(mBackupManagerServiceMock.getDataManagementLabel(TRANSPORT_NAME)).thenReturn(
                 DATA_MANAGEMENT_LABEL);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertEquals(DATA_MANAGEMENT_LABEL, mTrampoline.getDataManagementLabel(TRANSPORT_NAME));
         verify(mBackupManagerServiceMock).getDataManagementLabel(TRANSPORT_NAME);
     }
@@ -714,7 +714,7 @@
 
     @Test
     public void beginRestoreSession_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
         verify(mBackupManagerServiceMock).beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
     }
@@ -727,7 +727,7 @@
 
     @Test
     public void opComplete_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.opComplete(1, 2);
         verify(mBackupManagerServiceMock).opComplete(1, 2);
     }
@@ -742,7 +742,7 @@
     public void getAvailableRestoreToken_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.getAvailableRestoreToken(PACKAGE_NAME)).thenReturn(123L);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertEquals(123, mTrampoline.getAvailableRestoreToken(PACKAGE_NAME));
         verify(mBackupManagerServiceMock).getAvailableRestoreToken(PACKAGE_NAME);
     }
@@ -757,7 +757,7 @@
     public void isAppEligibleForBackup_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.isAppEligibleForBackup(PACKAGE_NAME)).thenReturn(true);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertTrue(mTrampoline.isAppEligibleForBackup(PACKAGE_NAME));
         verify(mBackupManagerServiceMock).isAppEligibleForBackup(PACKAGE_NAME);
     }
@@ -774,7 +774,7 @@
         when(mBackupManagerServiceMock.requestBackup(PACKAGE_NAMES, mBackupObserverMock,
                 mBackupManagerMonitorMock, 123)).thenReturn(456);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertEquals(456, mTrampoline.requestBackup(PACKAGE_NAMES, mBackupObserverMock,
                 mBackupManagerMonitorMock, 123));
         verify(mBackupManagerServiceMock).requestBackup(PACKAGE_NAMES, mBackupObserverMock,
@@ -789,7 +789,7 @@
 
     @Test
     public void cancelBackups_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.cancelBackups();
         verify(mBackupManagerServiceMock).cancelBackups();
     }
@@ -805,7 +805,7 @@
         FullBackupJob fullBackupJob = new FullBackupJob();
         when(mBackupManagerServiceMock.beginFullBackup(fullBackupJob)).thenReturn(true);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         assertTrue(mTrampoline.beginFullBackup(fullBackupJob));
         verify(mBackupManagerServiceMock).beginFullBackup(fullBackupJob);
     }
@@ -818,7 +818,7 @@
 
     @Test
     public void endFullBackup_forwarded() throws RemoteException {
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.endFullBackup();
         verify(mBackupManagerServiceMock).endFullBackup();
     }
@@ -829,7 +829,7 @@
                 android.Manifest.permission.DUMP)).thenReturn(
                 PackageManager.PERMISSION_DENIED);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
 
         mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
 
@@ -853,7 +853,7 @@
                 android.Manifest.permission.DUMP)).thenReturn(
                 PackageManager.PERMISSION_GRANTED);
 
-        mTrampoline.initialize(UserHandle.USER_SYSTEM);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
 
         mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, null);
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 2b5b812..a847b6a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -38,12 +38,13 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.security.KeyChain;
-import android.support.annotation.NonNull;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.Pair;
 import android.view.IWindowManager;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.net.NetworkPolicyManagerInternal;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 213961c..be00bb6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -30,11 +30,12 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
-import android.support.annotation.NonNull;
 import android.test.mock.MockContext;
 import android.util.ArrayMap;
 import android.util.ExceptionUtils;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.util.FunctionalUtils;
 
 import org.junit.Assert;
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
index 4d0278f..e4c9cc3 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
@@ -16,9 +16,6 @@
 
 package com.android.server.inputmethod;
 
-import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR;
-import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_VIEW_HAS_FOCUS;
-
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.in;
 import static org.hamcrest.Matchers.not;
@@ -45,6 +42,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.inputmethod.StartInputFlags;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -1090,21 +1089,19 @@
         assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed(
                 Build.VERSION_CODES.O_MR1, 0));
         assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed(
-                Build.VERSION_CODES.O_MR1, CONTROL_WINDOW_VIEW_HAS_FOCUS));
+                Build.VERSION_CODES.O_MR1, StartInputFlags.VIEW_HAS_FOCUS));
         assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed(
                 Build.VERSION_CODES.O_MR1,
-                CONTROL_WINDOW_VIEW_HAS_FOCUS | CONTROL_WINDOW_IS_TEXT_EDITOR));
+                StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR));
 
         // On P+ devices, SOFT_INPUT_STATE_VISIBLE/SOFT_INPUT_STATE_ALWAYS_VISIBLE are allowed only
         // when there is a focused View and its View#onCheckIsTextEditor() returns true.
         assertFalse(InputMethodUtils.isSoftInputModeStateVisibleAllowed(
                 Build.VERSION_CODES.P, 0));
         assertFalse(InputMethodUtils.isSoftInputModeStateVisibleAllowed(
-                Build.VERSION_CODES.P, CONTROL_WINDOW_VIEW_HAS_FOCUS));
+                Build.VERSION_CODES.P, StartInputFlags.VIEW_HAS_FOCUS));
         assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed(
                 Build.VERSION_CODES.P,
-                CONTROL_WINDOW_VIEW_HAS_FOCUS | CONTROL_WINDOW_IS_TEXT_EDITOR));
-
+                StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR));
     }
-
 }
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 543f51cba..78751a1 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -27,6 +27,7 @@
 import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.HexDump;
@@ -53,6 +54,7 @@
  * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
  */
 @RunWith(AndroidJUnit4.class)
+@SmallTest
 public class JobStoreTest {
     private static final String TAG = "TaskStoreTest";
     private static final String TEST_PREFIX = "_test_";
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
index b5a354c..9c8a382 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
@@ -51,7 +51,9 @@
 @MediumTest
 public class NetworkWatchlistServiceTests {
 
-    private static final long NETWOR_EVENT_TIMEOUT_SEC = 1;
+    private static final long NETWORK_EVENT_TIMEOUT_SEC = 1;
+    private static final int TEST_NETID = 100;
+    private static final int TEST_EVENT_TYPE = 1;
     private static final String TEST_HOST = "testhost.com";
     private static final String TEST_IP = "7.6.8.9";
     private static final String[] TEST_IPS =
@@ -180,8 +182,9 @@
                     }
                 };
         mWatchlistService.mNetworkWatchlistHandler = testDnsHandler;
-        connectivityMetrics.callback.onDnsEvent(TEST_HOST, TEST_IPS, TEST_IPS.length, 123L, 456);
-        if (!testDnsLatch.await(NETWOR_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+        connectivityMetrics.callback.onDnsEvent(TEST_NETID, TEST_EVENT_TYPE, 0,
+                TEST_HOST, TEST_IPS, TEST_IPS.length, 123L, 456);
+        if (!testDnsLatch.await(NETWORK_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
             fail("Timed out waiting for network event");
         }
         assertEquals(TEST_HOST, dnsParams[0]);
@@ -206,7 +209,7 @@
                 };
         mWatchlistService.mNetworkWatchlistHandler = testConnectHandler;
         connectivityMetrics.callback.onConnectEvent(TEST_IP, 80, 123L, 456);
-        if (!testConnectLatch.await(NETWOR_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+        if (!testConnectLatch.await(NETWORK_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
             fail("Timed out waiting for network event");
         }
         assertNull(connectParams[0]);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 9af7b1c..58c4bbf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -15,7 +15,10 @@
  */
 package com.android.server.pm;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -26,6 +29,7 @@
 import android.content.pm.PackageParser;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -37,12 +41,12 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import libcore.io.IoUtils;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import libcore.io.IoUtils;
-
 import java.io.File;
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
@@ -460,6 +464,7 @@
         pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo()));
         pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo()));
         pkg.requestedPermissions.add("foo7");
+        pkg.implicitPermissions.add("foo25");
 
         pkg.protectedBroadcasts = new ArrayList<>();
         pkg.protectedBroadcasts.add("foo8");
@@ -488,6 +493,9 @@
 
         pkg.usesLibraryFiles = new String[] { "foo13"};
 
+        pkg.usesLibraryInfos = new ArrayList<>();
+        pkg.usesLibraryInfos.add(new SharedLibraryInfo(null, null, null, 0L, 0, null, null, null));
+
         pkg.mOriginalPackages = new ArrayList<>();
         pkg.mOriginalPackages.add("foo14");
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 416a616..bd42b73 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -401,15 +401,7 @@
         List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
         notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
 
-        PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
-        assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
-        assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
-        // We expect that all the contexts are unsupported.
-        String[] expectedContexts =
-                Collections.nCopies(secondaries.size(),
-                        PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT).toArray(new String[0]);
-        assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
-                /*isUsedByOtherApps*/false, mUser0, expectedContexts);
+        assertNoUseInfo(mBarUser0UnsupportedClassLoader);
     }
 
     @Test
@@ -438,27 +430,18 @@
     }
 
     @Test
-    public void testNotifyUnsupportedClassLoaderDoesNotChange() {
-        List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
+    public void testNotifyUnsupportedClassLoaderDoesNotChangeExisting() {
+        List<String> secondaries = mBarUser0.getSecondaryDexPaths();
+
+        notifyDexLoad(mBarUser0, secondaries, mUser0);
+        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
+        assertSecondaryUse(mBarUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
+
+        // Record bar secondaries again with an unsupported class loader. This should not change the
+        // context.
         notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
-
-        PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
-        assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
-        assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
-        // We expect that all the contexts are unsupported.
-        String[] expectedContexts =
-                Collections.nCopies(secondaries.size(),
-                        PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT).toArray(new String[0]);
-        assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
-                /*isUsedByOtherApps*/false, mUser0, expectedContexts);
-
-        // Record bar secondaries again with a different class loader. This will change the context.
-        // However, because the context was already marked as unsupported we should not chage it.
-        notifyDexLoad(mBarUser0DelegateLastClassLoader, secondaries, mUser0);
-        pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
-        assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
-                /*isUsedByOtherApps*/false, mUser0, expectedContexts);
-
+        pui = getPackageUseInfo(mBarUser0);
+        assertSecondaryUse(mBarUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 3e93dcf..7755e94 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -399,20 +399,6 @@
     }
 
     @Test
-    public void testRecordClassLoaderContextUnsupportedContext() {
-        // Record a secondary dex file.
-        assertTrue(record(mFooSecondary1User0));
-        // Now update its context.
-        TestData unsupportedContext = mFooSecondary1User0.updateClassLoaderContext(
-                PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
-        assertTrue(record(unsupportedContext));
-
-        assertPackageDexUsage(null, unsupportedContext);
-        writeAndReadBack();
-        assertPackageDexUsage(null, unsupportedContext);
-    }
-
-    @Test
     public void testRecordClassLoaderContextTransitionFromUnknown() {
         // Record a secondary dex file.
         TestData unknownContext = mFooSecondary1User0.updateClassLoaderContext(
@@ -440,29 +426,41 @@
         PackageDexUsage.DexUseInfo validContext = new DexUseInfo(isUsedByOtherApps, userId,
                 "valid_context", "arm");
         assertFalse(validContext.isUnknownClassLoaderContext());
-        assertFalse(validContext.isUnsupportedClassLoaderContext());
         assertFalse(validContext.isVariableClassLoaderContext());
 
-        PackageDexUsage.DexUseInfo unsupportedContext = new DexUseInfo(isUsedByOtherApps, userId,
-                PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT, "arm");
-        assertFalse(unsupportedContext.isUnknownClassLoaderContext());
-        assertTrue(unsupportedContext.isUnsupportedClassLoaderContext());
-        assertFalse(unsupportedContext.isVariableClassLoaderContext());
-
         PackageDexUsage.DexUseInfo variableContext = new DexUseInfo(isUsedByOtherApps, userId,
                 PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT, "arm");
         assertFalse(variableContext.isUnknownClassLoaderContext());
-        assertFalse(variableContext.isUnsupportedClassLoaderContext());
         assertTrue(variableContext.isVariableClassLoaderContext());
 
         PackageDexUsage.DexUseInfo unknownContext = new DexUseInfo(isUsedByOtherApps, userId,
                 PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT, "arm");
         assertTrue(unknownContext.isUnknownClassLoaderContext());
-        assertFalse(unknownContext.isUnsupportedClassLoaderContext());
         assertFalse(unknownContext.isVariableClassLoaderContext());
     }
 
     @Test
+    public void testUnsupportedClassLoaderDiscardedOnRead() throws Exception {
+        String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__2\n"
+                + mBarSecondary1User0.mPackageName + "\n"
+                + "#" + mBarSecondary1User0.mDexFile + "\n"
+                + "0,0," + mBarSecondary1User0.mLoaderIsa + "\n"
+                + "@\n"
+                + "=UnsupportedClassLoaderContext=\n"
+
+                + mFooSecondary1User0.mPackageName + "\n"
+                + "#" + mFooSecondary1User0.mDexFile + "\n"
+                + "0,0," + mFooSecondary1User0.mLoaderIsa + "\n"
+                + "@\n"
+                + mFooSecondary1User0.mClassLoaderContext + "\n";
+
+        mPackageDexUsage.read(new StringReader(content));
+
+        assertPackageDexUsage(mFooBaseUser0, mFooSecondary1User0);
+        assertPackageDexUsage(mBarBaseUser0);
+    }
+
+    @Test
     public void testReadVersion1() {
         String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
         // Equivalent to
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
deleted file mode 100644
index d34f951..0000000
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.policy;
-
-import android.annotation.Nullable;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.proto.ProtoOutputStream;
-import android.view.Display;
-import android.view.DisplayCutout;
-import android.view.IApplicationToken;
-import android.view.WindowManager;
-
-import com.android.server.wm.WindowFrames;
-import com.android.server.wm.utils.WmDisplayCutout;
-
-public class FakeWindowState implements WindowManagerPolicy.WindowState {
-
-    private WindowFrames mWindowFrames = new WindowFrames();
-
-    public WindowManager.LayoutParams attrs;
-    public int displayId;
-    public boolean isVoiceInteraction;
-    public boolean inMultiWindowMode;
-    public boolean visible = true;
-    public int surfaceLayer = 1;
-    public boolean isDimming = false;
-
-    public boolean policyVisible = true;
-
-    @Override
-    public int getOwningUid() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public String getOwningPackage() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public void computeFrameLw() {
-    }
-
-    @Override
-    public Rect getFrameLw() {
-        return mWindowFrames.mParentFrame;
-    }
-
-    @Override
-    public Rect getDisplayFrameLw() {
-        return mWindowFrames.mDisplayFrame;
-    }
-
-    @Override
-    public Rect getOverscanFrameLw() {
-        return mWindowFrames.mOverscanFrame;
-    }
-
-    @Override
-    public Rect getContentFrameLw() {
-        return mWindowFrames.mContentFrame;
-    }
-
-    @Override
-    public Rect getVisibleFrameLw() {
-        return mWindowFrames.mVisibleFrame;
-    }
-
-    public Rect getStableFrame() {
-        return mWindowFrames.mStableFrame;
-    }
-
-    public Rect getDecorFrame() {
-        return mWindowFrames.mDecorFrame;
-    }
-
-    @Override
-    public boolean getGivenInsetsPendingLw() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public Rect getGivenContentInsetsLw() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public Rect getGivenVisibleInsetsLw() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public WindowManager.LayoutParams getAttrs() {
-        return attrs;
-    }
-
-    @Override
-    public boolean getNeedsMenuLw(WindowManagerPolicy.WindowState bottom) {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public int getSystemUiVisibility() {
-        return attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility;
-    }
-
-    @Override
-    public int getSurfaceLayer() {
-        return surfaceLayer;
-    }
-
-    @Override
-    public int getBaseType() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public IApplicationToken getAppToken() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean isVoiceInteraction() {
-        return isVoiceInteraction;
-    }
-
-    @Override
-    public boolean hasAppShownWindows() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean isVisibleLw() {
-        return visible && policyVisible;
-    }
-
-    @Override
-    public boolean isDisplayedLw() {
-        return isVisibleLw();
-    }
-
-    @Override
-    public boolean isAnimatingLw() {
-        return false;
-    }
-
-    @Override
-    public boolean canAffectSystemUiFlags() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean isGoneForLayoutLw() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean isDrawnLw() {
-        return true;
-    }
-
-    @Override
-    public boolean hasDrawnLw() {
-        return true;
-    }
-
-    @Override
-    public boolean hideLw(boolean doAnimation) {
-        if (!policyVisible) {
-            return false;
-        }
-        policyVisible = false;
-        return true;
-    }
-
-    @Override
-    public boolean showLw(boolean doAnimation) {
-        if (policyVisible) {
-            return false;
-        }
-        policyVisible = true;
-        return true;
-    }
-
-    @Override
-    public boolean isAlive() {
-        return true;
-    }
-
-    @Override
-    public boolean isDefaultDisplay() {
-        return displayId == Display.DEFAULT_DISPLAY;
-    }
-
-    @Override
-    public boolean isDimming() {
-        return isDimming;
-    }
-
-    @Override
-    public int getWindowingMode() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean isInMultiWindowMode() {
-        return inMultiWindowMode;
-    }
-
-    @Override
-    public int getRotationAnimationHint() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean isInputMethodWindow() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public int getDisplayId() {
-        return displayId;
-    }
-
-    @Override
-    public boolean canAcquireSleepToken() {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public boolean canReceiveKeys() { return false; }
-
-    @Override
-    public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId){
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public WindowFrames getWindowFrames() {
-        return mWindowFrames;
-    }
-
-    @Override
-    public boolean isInputMethodTarget() {
-        return false;
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
deleted file mode 100644
index cce6ba7..0000000
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.policy;
-
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-
-import static org.hamcrest.Matchers.equalTo;
-
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ErrorCollector;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase {
-
-    @Rule
-    public final ErrorCollector mErrorCollector = new ErrorCollector();
-
-    @Before
-    public void setUp() throws Exception {
-        addStatusBar();
-        addNavigationBar();
-    }
-
-    @Test
-    public void portrait() throws Exception {
-        DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */);
-
-        verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
-        verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
-        verifyConsistency(di);
-    }
-
-    @Test
-    public void portrait_withCutout() throws Exception {
-        DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */);
-
-        verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
-        verifyNonDecorInsets(di, 0, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT);
-        verifyConsistency(di);
-    }
-
-    @Test
-    public void landscape() throws Exception {
-        DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */);
-
-        verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        verifyNonDecorInsets(di, 0, 0, NAV_BAR_HEIGHT, 0);
-        verifyConsistency(di);
-    }
-
-    @Test
-    public void landscape_withCutout() throws Exception {
-        DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */);
-
-        verifyStableInsets(di, DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        verifyNonDecorInsets(di, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT, 0);
-        verifyConsistency(di);
-    }
-
-    @Test
-    public void seascape() throws Exception {
-        DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */);
-
-        verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
-        verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, 0, 0);
-        verifyConsistency(di);
-    }
-
-    @Test
-    public void seascape_withCutout() throws Exception {
-        DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */);
-
-        verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
-        verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, DISPLAY_CUTOUT_HEIGHT, 0);
-        verifyConsistency(di);
-    }
-
-    @Test
-    public void upsideDown() throws Exception {
-        DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */);
-
-        verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
-        verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
-        verifyConsistency(di);
-    }
-
-    @Test
-    public void upsideDown_withCutout() throws Exception {
-        DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */);
-
-        verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
-        verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
-        verifyConsistency(di);
-    }
-
-    private void verifyStableInsets(DisplayInfo di, int left, int top, int right, int bottom) {
-        mErrorCollector.checkThat("stableInsets", getStableInsetsLw(di), equalTo(new Rect(
-                left, top, right, bottom)));
-    }
-
-    private void verifyNonDecorInsets(DisplayInfo di, int left, int top, int right, int bottom) {
-        mErrorCollector.checkThat("nonDecorInsets", getNonDecorInsetsLw(di), equalTo(new Rect(
-                left, top, right, bottom)));
-    }
-
-    private void verifyConsistency(DisplayInfo di) {
-        verifyConsistency("configDisplay", di, getStableInsetsLw(di),
-                getConfigDisplayWidth(di), getConfigDisplayHeight(di));
-        verifyConsistency("nonDecorDisplay", di, getNonDecorInsetsLw(di),
-                getNonDecorDisplayWidth(di), getNonDecorDisplayHeight(di));
-    }
-
-    private void verifyConsistency(String what, DisplayInfo di, Rect insets, int width,
-            int height) {
-        mErrorCollector.checkThat(what + ":width", width,
-                equalTo(di.logicalWidth - insets.left - insets.right));
-        mErrorCollector.checkThat(what + ":height", height,
-                equalTo(di.logicalHeight - insets.top - insets.bottom));
-    }
-
-    private Rect getStableInsetsLw(DisplayInfo di) {
-        Rect result = new Rect();
-        mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
-                di.displayCutout, result);
-        return result;
-    }
-
-    private Rect getNonDecorInsetsLw(DisplayInfo di) {
-        Rect result = new Rect();
-        mPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
-                di.displayCutout, result);
-        return result;
-    }
-
-    private int getNonDecorDisplayWidth(DisplayInfo di) {
-        return mPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight, di.rotation,
-                0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
-    }
-
-    private int getNonDecorDisplayHeight(DisplayInfo di) {
-        return mPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight, di.rotation,
-                0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
-    }
-
-    private int getConfigDisplayWidth(DisplayInfo di) {
-        return mPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight, di.rotation,
-                0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
-    }
-
-    private int getConfigDisplayHeight(DisplayInfo di) {
-        return mPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight, di.rotation,
-                0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
-    }
-}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
deleted file mode 100644
index fee761d..0000000
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.policy;
-
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
-import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.view.DisplayCutout;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
-
-    private FakeWindowState mAppWindow;
-
-    @Before
-    public void setUp() throws Exception {
-        mAppWindow = new FakeWindowState();
-        mAppWindow.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
-                TYPE_APPLICATION,
-                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
-                PixelFormat.TRANSLUCENT);
-
-        addStatusBar();
-        addNavigationBar();
-    }
-
-    @Test
-    public void layoutWindowLw_appDrawsBars() throws Exception {
-        mAppWindow.attrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
-        assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_appWontDrawBars() throws Exception {
-        mAppWindow.attrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
-    }
-
-    @Test
-    public void layoutWindowLw_appWontDrawBars_forceStatus() throws Exception {
-        mAppWindow.attrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
-    }
-
-    @Test
-    public void addingWindow_doesNotTamperWithSysuiFlags() {
-        mAppWindow.attrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        mPolicy.addWindow(mAppWindow);
-
-        assertEquals(0, mAppWindow.attrs.systemUiVisibility);
-        assertEquals(0, mAppWindow.attrs.subtreeSystemUiVisibility);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout() {
-        addDisplayCutout();
-
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withhDisplayCutout_never() {
-        addDisplayCutout();
-
-        mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
-        addDisplayCutout();
-
-        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
-        assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_fullscreen() {
-        addDisplayCutout();
-
-        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
-        addDisplayCutout();
-
-        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
-        mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
-    }
-
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_landscape() {
-        addDisplayCutout();
-        setRotation(ROTATION_90);
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getContentFrameLw(),
-                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mAppWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_seascape() {
-        addDisplayCutout();
-        setRotation(ROTATION_270);
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetBy(mAppWindow.getFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getStableFrame(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
-        assertInsetBy(mAppWindow.getContentFrameLw(),
-                NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
-        addDisplayCutout();
-        setRotation(ROTATION_90);
-
-        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getContentFrameLw(),
-                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
-        addDisplayCutout();
-
-        mAppWindow.attrs.flags = FLAG_LAYOUT_IN_SCREEN;
-        mAppWindow.attrs.type = TYPE_APPLICATION_OVERLAY;
-        mAppWindow.attrs.width = DISPLAY_WIDTH;
-        mAppWindow.attrs.height = DISPLAY_HEIGHT;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
-        addDisplayCutout();
-        setRotation(ROTATION_90);
-
-        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-        mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        mPolicy.addWindow(mAppWindow);
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
-
-        assertInsetBy(mAppWindow.getFrameLw(), 0, 0, 0, 0);
-        assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getContentFrameLw(),
-                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutHint_screenDecorWindow() {
-        addDisplayCutout();
-        mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR;
-
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
-        final Rect frame = new Rect();
-        final Rect content = new Rect();
-        final Rect stable = new Rect();
-        final Rect outsets = new Rect();
-        final DisplayCutout.ParcelableWrapper cutout = new DisplayCutout.ParcelableWrapper();
-        mPolicy.getLayoutHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames,
-                false /* floatingStack */, frame, content, stable, outsets, cutout);
-
-        assertThat(frame, equalTo(mFrames.mUnrestricted));
-        assertThat(content, equalTo(new Rect()));
-        assertThat(stable, equalTo(new Rect()));
-        assertThat(outsets, equalTo(new Rect()));
-        assertThat(cutout.get(), equalTo(DisplayCutout.NO_CUTOUT));
-    }
-
-    @Test
-    public void layoutHint_appWindow() {
-        // Initialize DisplayFrames
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
-        final Rect outFrame = new Rect();
-        final Rect outContentInsets = new Rect();
-        final Rect outStableInsets = new Rect();
-        final Rect outOutsets = new Rect();
-        final DisplayCutout.ParcelableWrapper outDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
-
-        mPolicy.getLayoutHintLw(mAppWindow.attrs, null, mFrames, false /* floatingStack */,
-                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
-
-        assertThat(outFrame, is(mFrames.mUnrestricted));
-        assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
-        assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
-        assertThat(outOutsets, is(new Rect()));
-        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
-    }
-
-    @Test
-    public void layoutHint_appWindowInTask() {
-        // Initialize DisplayFrames
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
-        final Rect taskBounds = new Rect(100, 100, 200, 200);
-
-        final Rect outFrame = new Rect();
-        final Rect outContentInsets = new Rect();
-        final Rect outStableInsets = new Rect();
-        final Rect outOutsets = new Rect();
-        final DisplayCutout.ParcelableWrapper outDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
-
-        mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, false /* floatingStack */,
-                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
-
-        assertThat(outFrame, is(taskBounds));
-        assertThat(outContentInsets, is(new Rect()));
-        assertThat(outStableInsets, is(new Rect()));
-        assertThat(outOutsets, is(new Rect()));
-        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
-    }
-
-    @Test
-    public void layoutHint_appWindowInTask_outsideContentFrame() {
-        // Initialize DisplayFrames
-        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
-        // Task is in the nav bar area (usually does not happen, but this is similar enough to the
-        // possible overlap with the IME)
-        final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
-                200, mFrames.mContent.bottom + 10);
-
-        final Rect outFrame = new Rect();
-        final Rect outContentInsets = new Rect();
-        final Rect outStableInsets = new Rect();
-        final Rect outOutsets = new Rect();
-        final DisplayCutout.ParcelableWrapper outDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
-
-        mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, true /* floatingStack */,
-                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
-
-        assertThat(outFrame, is(taskBounds));
-        assertThat(outContentInsets, is(new Rect()));
-        assertThat(outStableInsets, is(new Rect()));
-        assertThat(outOutsets, is(new Rect()));
-        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
-    }
-}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
deleted file mode 100644
index d92d7e0..0000000
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.policy;
-
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.graphics.PixelFormat;
-import android.platform.test.annotations.Presubmit;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class PhoneWindowManagerTest {
-
-    private static FakeWindowState createOpaqueFullscreen(boolean hasLightNavBar) {
-        final FakeWindowState state = new FakeWindowState();
-        state.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
-                TYPE_BASE_APPLICATION,
-                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
-                PixelFormat.OPAQUE);
-        state.attrs.subtreeSystemUiVisibility =
-                hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
-        return state;
-    }
-
-    private static FakeWindowState createDimmingDialogWindow(boolean canBeImTarget) {
-        final FakeWindowState state = new FakeWindowState();
-        state.attrs = new WindowManager.LayoutParams(WRAP_CONTENT, WRAP_CONTENT,
-                TYPE_APPLICATION,
-                FLAG_DIM_BEHIND  | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM),
-                PixelFormat.TRANSLUCENT);
-        state.isDimming = true;
-        return state;
-    }
-
-    private static FakeWindowState createInputMethodWindow(boolean visible, boolean drawNavBar,
-            boolean hasLightNavBar) {
-        final FakeWindowState state = new FakeWindowState();
-        state.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
-                TYPE_INPUT_METHOD,
-                FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN
-                        | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0),
-                PixelFormat.TRANSPARENT);
-        state.attrs.subtreeSystemUiVisibility =
-                hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
-        state.visible = visible;
-        state.policyVisible = visible;
-        return state;
-    }
-
-
-    @Test
-    public void testChooseNavigationColorWindowLw() throws Exception {
-        final FakeWindowState opaque = createOpaqueFullscreen(false);
-
-        final FakeWindowState dimmingImTarget = createDimmingDialogWindow(true);
-        final FakeWindowState dimmingNonImTarget = createDimmingDialogWindow(false);
-
-        final FakeWindowState visibleIme = createInputMethodWindow(true, true, false);
-        final FakeWindowState invisibleIme = createInputMethodWindow(false, true, false);
-        final FakeWindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false);
-
-        // If everything is null, return null
-        assertNull(null, PhoneWindowManager.chooseNavigationColorWindowLw(
-                null, null, null, NAV_BAR_BOTTOM));
-
-        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, opaque, null, NAV_BAR_BOTTOM));
-        assertEquals(dimmingImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, dimmingImTarget, null, NAV_BAR_BOTTOM));
-        assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM));
-
-        assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
-                null, null, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
-                null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
-                null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, opaque, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
-        assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
-
-        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
-        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
-        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, opaque, visibleIme, NAV_BAR_RIGHT));
-
-        // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color
-        // window.
-        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM));
-        assertEquals(dimmingImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
-        assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
-                opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
-    }
-
-    @Test
-    public void testUpdateLightNavigationBarLw() throws Exception {
-        final FakeWindowState opaqueDarkNavBar = createOpaqueFullscreen(false);
-        final FakeWindowState opaqueLightNavBar = createOpaqueFullscreen(true);
-
-        final FakeWindowState dimming = createDimmingDialogWindow(false);
-
-        final FakeWindowState imeDrawDarkNavBar = createInputMethodWindow(true,true, false);
-        final FakeWindowState imeDrawLightNavBar = createInputMethodWindow(true,true, true);
-
-        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
-                PhoneWindowManager.updateLightNavigationBarLw(
-                        SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null,
-                        null, null));
-
-        // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag.
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar));
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null,
-                opaqueDarkNavBar));
-        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
-                PhoneWindowManager.updateLightNavigationBarLw(0, opaqueLightNavBar,
-                        opaqueLightNavBar, null, opaqueLightNavBar));
-        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
-                PhoneWindowManager.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
-                        opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar));
-
-        // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                0, opaqueDarkNavBar, dimming, null, dimming));
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                0, opaqueLightNavBar, dimming, null, dimming));
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming));
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming));
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar,
-                dimming));
-
-        // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar,
-                imeDrawDarkNavBar));
-
-        // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins.
-        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
-                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar,
-                imeDrawDarkNavBar, imeDrawDarkNavBar));
-
-        // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
-        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
-                PhoneWindowManager.updateLightNavigationBarLw(0, opaqueDarkNavBar,
-                        opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
-    }
-
-    @Test
-    public void testIsDockSideAllowedDockTop() throws Exception {
-        // Docked top is always allowed
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                true /* navigationBarCanMove */));
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-    }
-
-    @Test
-    public void testIsDockSideAllowedDockBottom() throws Exception {
-        // Cannot dock bottom
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                true /* navigationBarCanMove */));
-    }
-
-    @Test
-    public void testIsDockSideAllowedNavigationBarMovable() throws Exception {
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                true /* navigationBarCanMove */));
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_LEFT,
-                true /* navigationBarCanMove */));
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_RIGHT,
-                true /* navigationBarCanMove */));
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                true /* navigationBarCanMove */));
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_RIGHT,
-                true /* navigationBarCanMove */));
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_LEFT,
-                true /* navigationBarCanMove */));
-    }
-
-    @Test
-    public void testIsDockSideAllowedNavigationBarNotMovable() throws Exception {
-        // Navigation bar is not movable such as tablets
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
-                false /* navigationBarCanMove */));
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
deleted file mode 100644
index e16f118..0000000
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.policy;
-
-import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
-import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-
-import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Matrix;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.os.IBinder;
-import android.os.UserHandle;
-import android.testing.TestableResources;
-import android.util.Pair;
-import android.view.Display;
-import android.view.DisplayCutout;
-import android.view.DisplayInfo;
-import android.view.Gravity;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IAccessibilityManager;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.server.policy.keyguard.KeyguardServiceDelegate;
-import com.android.server.wm.DisplayFrames;
-import com.android.server.wm.WindowTestUtils.TestDisplayContent;
-import com.android.server.wm.utils.WmDisplayCutout;
-
-import org.junit.Before;
-
-public class PhoneWindowManagerTestBase {
-    static final int DISPLAY_WIDTH = 500;
-    static final int DISPLAY_HEIGHT = 1000;
-
-    static final int STATUS_BAR_HEIGHT = 10;
-    static final int NAV_BAR_HEIGHT = 15;
-    static final int DISPLAY_CUTOUT_HEIGHT = 8;
-
-    TestablePhoneWindowManager mPolicy;
-    TestContextWrapper mContext;
-    DisplayFrames mFrames;
-
-    FakeWindowState mStatusBar;
-    FakeWindowState mNavigationBar;
-    private boolean mHasDisplayCutout;
-    private int mRotation = ROTATION_0;
-
-    @Before
-    public void setUpBase() throws Exception {
-        mContext = new TestContextWrapper(InstrumentationRegistry.getTargetContext());
-        mContext.getResourceMocker().addOverride(
-                com.android.internal.R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT);
-        mContext.getResourceMocker().addOverride(
-                com.android.internal.R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT);
-        mContext.getResourceMocker().addOverride(
-                com.android.internal.R.dimen.navigation_bar_height, NAV_BAR_HEIGHT);
-        mContext.getResourceMocker().addOverride(
-                com.android.internal.R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT);
-        mContext.getResourceMocker().addOverride(
-                com.android.internal.R.dimen.navigation_bar_width, NAV_BAR_HEIGHT);
-
-        mPolicy = TestablePhoneWindowManager.create(mContext);
-
-        updateDisplayFrames();
-    }
-
-    public void setRotation(int rotation) {
-        mRotation = rotation;
-        updateDisplayFrames();
-    }
-
-    private void updateDisplayFrames() {
-        Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
-                mHasDisplayCutout);
-        mFrames = new DisplayFrames(Display.DEFAULT_DISPLAY, info.first, info.second);
-    }
-
-    public void addStatusBar() {
-        mStatusBar = new FakeWindowState();
-        mStatusBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, STATUS_BAR_HEIGHT,
-                TYPE_STATUS_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT);
-        mStatusBar.attrs.gravity = Gravity.TOP;
-
-        mPolicy.addWindow(mStatusBar);
-        mPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT;
-    }
-
-    public void addNavigationBar() {
-        mNavigationBar = new FakeWindowState();
-        mNavigationBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, NAV_BAR_HEIGHT,
-                TYPE_NAVIGATION_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT);
-        mNavigationBar.attrs.gravity = Gravity.BOTTOM;
-
-        mPolicy.addWindow(mNavigationBar);
-        mPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
-    }
-
-    public void addDisplayCutout() {
-        mHasDisplayCutout = true;
-        updateDisplayFrames();
-    }
-
-    /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
-    public void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
-            int expectedInsetRight, int expectedInsetBottom) {
-        assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
-                mFrames.mDisplayWidth - expectedInsetRight,
-                mFrames.mDisplayHeight - expectedInsetBottom), actual);
-    }
-
-    /**
-     * Asserts that {@code actual} is inset by the given amounts from the full display rect.
-     *
-     * Convenience wrapper for when only the top and bottom inset are non-zero.
-     */
-    public void assertInsetByTopBottom(Rect actual, int expectedInsetTop, int expectedInsetBottom) {
-        assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
-    }
-
-    public static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
-        return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first;
-    }
-    public static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation,
-            boolean withDisplayCutout) {
-        DisplayInfo info = new DisplayInfo();
-        WmDisplayCutout cutout = null;
-
-        final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270;
-        info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
-        info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
-        info.rotation = rotation;
-        if (withDisplayCutout) {
-            cutout = WmDisplayCutout.computeSafeInsets(
-                    displayCutoutForRotation(rotation), info.logicalWidth,
-                    info.logicalHeight);
-            info.displayCutout = cutout.getDisplayCutout();
-        } else {
-            info.displayCutout = null;
-        }
-        return Pair.create(info, cutout);
-    }
-
-    private static DisplayCutout displayCutoutForRotation(int rotation) {
-        RectF rectF = new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
-
-        Matrix m = new Matrix();
-        transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
-        m.mapRect(rectF);
-
-        int pos = -1;
-        switch (rotation) {
-            case ROTATION_0:
-                pos = BOUNDS_POSITION_TOP;
-                break;
-            case ROTATION_90:
-                pos = BOUNDS_POSITION_LEFT;
-                break;
-            case ROTATION_180:
-                pos = BOUNDS_POSITION_BOTTOM;
-                break;
-            case ROTATION_270:
-                pos = BOUNDS_POSITION_RIGHT;
-                break;
-        }
-
-
-        return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top,
-                (int) rectF.right, (int) rectF.bottom, pos);
-    }
-
-    static class TestContextWrapper extends ContextWrapper {
-        private final TestableResources mResourceMocker;
-
-        public TestContextWrapper(Context targetContext) {
-            super(targetContext);
-            mResourceMocker = new TestableResources(targetContext.getResources());
-        }
-
-        @Override
-        public int checkPermission(String permission, int pid, int uid) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
-
-        @Override
-        public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
-
-        @Override
-        public Resources getResources() {
-            return mResourceMocker.getResources();
-        }
-
-        public TestableResources getResourceMocker() {
-            return mResourceMocker;
-        }
-    }
-
-    static class TestablePhoneWindowManager extends PhoneWindowManager {
-
-        public TestablePhoneWindowManager() {
-        }
-
-        @Override
-        void initializeHdmiState() {
-            // Do nothing.
-        }
-
-        @Override
-        Context getSystemUiContext() {
-            return mContext;
-        }
-
-        void addWindow(WindowState state) {
-            if (state instanceof FakeWindowState) {
-                ((FakeWindowState) state).surfaceLayer =
-                        getWindowLayerFromTypeLw(state.getAttrs().type,
-                                true /* canAddInternalSystemWindow */);
-            }
-            adjustWindowParamsLw(state, state.getAttrs(), true /* hasStatusBarPermission */);
-            assertEquals(WindowManagerGlobal.ADD_OKAY, prepareAddWindowLw(state, state.getAttrs()));
-        }
-
-        public static TestablePhoneWindowManager create(Context context) {
-            TestablePhoneWindowManager[] policy = new TestablePhoneWindowManager[1];
-            InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-                policy[0] = new TestablePhoneWindowManager();
-                policy[0].mContext = context;
-                policy[0].mKeyguardDelegate = mock(KeyguardServiceDelegate.class);
-                policy[0].mAccessibilityManager = new AccessibilityManager(context,
-                        mock(IAccessibilityManager.class), UserHandle.USER_CURRENT);
-                policy[0].mSystemGestures = mock(SystemGesturesPointerEventListener.class);
-
-                final TestDisplayContent displayContent = TestDisplayContent.create(context);
-                policy[0].setDefaultDisplay(displayContent);
-                policy[0].onConfigurationChanged(displayContent);
-            });
-            return policy[0];
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index caaa0bb..41d5691 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -129,6 +129,7 @@
 
     static class MyInjector extends AppStandbyController.Injector {
         long mElapsedRealtime;
+        boolean mIsAppIdleEnabled = true;
         boolean mIsCharging;
         List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>();
         boolean mDisplayOn;
@@ -160,7 +161,7 @@
 
         @Override
         boolean isAppIdleEnabled() {
-            return true;
+            return mIsAppIdleEnabled;
         }
 
         @Override
@@ -277,6 +278,13 @@
         }
     }
 
+    private void setAppIdleEnabled(AppStandbyController controller, boolean enabled) {
+        mInjector.mIsAppIdleEnabled = enabled;
+        if (controller != null) {
+            controller.setAppIdleEnabled(enabled);
+        }
+    }
+
     private AppStandbyController setupController() throws Exception {
         mInjector.mElapsedRealtime = 0;
         setupPm(mInjector.getContext().getPackageManager());
@@ -346,7 +354,7 @@
         public void onParoleStateChanged(boolean isParoleOn) {
             synchronized (this) {
                 // Only record information if it is being looked for
-                if (mLatch.getCount() > 0) {
+                if (mLatch != null && mLatch.getCount() > 0) {
                     mOnParole = isParoleOn;
                     mLastParoleChangeTime = getCurrentTime();
                     mLatch.countDown();
@@ -407,6 +415,74 @@
                 marginOfError);
     }
 
+    @Test
+    public void testEnabledState() throws Exception {
+        TestParoleListener paroleListener = new TestParoleListener();
+        mController.addListener(paroleListener);
+        long lastUpdateTime;
+
+        // Test that listeners are notified if enabled changes when the device is not in parole.
+        setChargingState(mController, false);
+
+        // Start off not enabled. Device is effectively on permanent parole.
+        setAppIdleEnabled(mController, false);
+
+        // Enable controller
+        paroleListener.rearmLatch();
+        setAppIdleEnabled(mController, true);
+        paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+        assertFalse(paroleListener.mOnParole);
+        lastUpdateTime = paroleListener.getLastParoleChangeTime();
+
+        paroleListener.rearmLatch();
+        setAppIdleEnabled(mController, true);
+        paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+        assertFalse(paroleListener.mOnParole);
+        // Make sure AppStandbyController doesn't notify listeners when there's no change.
+        assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
+
+        // Disable controller
+        paroleListener.rearmLatch();
+        setAppIdleEnabled(mController, false);
+        paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+        assertTrue(paroleListener.mOnParole);
+        lastUpdateTime = paroleListener.getLastParoleChangeTime();
+
+        paroleListener.rearmLatch();
+        setAppIdleEnabled(mController, false);
+        paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+        assertTrue(paroleListener.mOnParole);
+        // Make sure AppStandbyController doesn't notify listeners when there's no change.
+        assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
+
+
+        // Test that listeners aren't notified if enabled status changes when the device is already
+        // in parole.
+
+        // A device is in parole whenever it's charging.
+        setChargingState(mController, true);
+
+        // Start off not enabled.
+        paroleListener.rearmLatch();
+        setAppIdleEnabled(mController, false);
+        paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+        assertTrue(paroleListener.mOnParole);
+        lastUpdateTime = paroleListener.getLastParoleChangeTime();
+
+        // Test that toggling doesn't notify the listener.
+        paroleListener.rearmLatch();
+        setAppIdleEnabled(mController, true);
+        paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+        assertTrue(paroleListener.mOnParole);
+        assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
+
+        paroleListener.rearmLatch();
+        setAppIdleEnabled(mController, false);
+        paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+        assertTrue(paroleListener.mOnParole);
+        assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
+    }
+
     private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) {
         mInjector.mElapsedRealtime = elapsedTime;
         controller.checkIdleStates(USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index 047addd..793d6b0 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -61,6 +61,7 @@
 
     private static final long TIME_30_MIN = 30 * 60_000L;
     private static final long TIME_10_MIN = 10 * 60_000L;
+    private static final long TIME_1_MIN = 10 * 60_000L;
 
     private static final long MAX_OBSERVER_PER_UID = 10;
     private static final long MIN_TIME_LIMIT = 4_000L;
@@ -77,7 +78,8 @@
             PKG_GAME1, PKG_GAME2
     };
 
-    private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+    private CountDownLatch mLimitReachedLatch = new CountDownLatch(1);
+    private CountDownLatch mSessionEndLatch = new CountDownLatch(1);
 
     private AppTimeLimitController mController;
 
@@ -85,18 +87,24 @@
 
     private long mUptimeMillis;
 
-    AppTimeLimitController.OnLimitReachedListener mListener
-            = new AppTimeLimitController.OnLimitReachedListener() {
+    AppTimeLimitController.TimeLimitCallbackListener mListener =
+            new AppTimeLimitController.TimeLimitCallbackListener() {
+                @Override
+                public void onLimitReached(int observerId, int userId, long timeLimit,
+                        long timeElapsed,
+                        PendingIntent callbackIntent) {
+                    mLimitReachedLatch.countDown();
+                }
 
-        @Override
-        public void onLimitReached(int observerId, int userId, long timeLimit, long timeElapsed,
-                PendingIntent callbackIntent) {
-            mCountDownLatch.countDown();
-        }
-    };
+                @Override
+                public void onSessionEnd(int observerId, int userId, long timeElapsed,
+                        PendingIntent callbackIntent) {
+                    mSessionEndLatch.countDown();
+                }
+            };
 
     class MyAppTimeLimitController extends AppTimeLimitController {
-        MyAppTimeLimitController(AppTimeLimitController.OnLimitReachedListener listener,
+        MyAppTimeLimitController(AppTimeLimitController.TimeLimitCallbackListener listener,
                 Looper looper) {
             super(listener, looper);
         }
@@ -107,7 +115,12 @@
         }
 
         @Override
-        protected long getObserverPerUidLimit() {
+        protected long getAppUsageObserverPerUidLimit() {
+            return MAX_OBSERVER_PER_UID;
+        }
+
+        @Override
+        protected long getUsageSessionObserverPerUidLimit() {
             return MAX_OBSERVER_PER_UID;
         }
 
@@ -129,188 +142,551 @@
         mThread.quit();
     }
 
-    /** Verify observer is added */
+    /** Verify app usage observer is added */
     @Test
-    public void testAddObserver() {
-        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
-        addObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
-        assertTrue("Observer wasn't added", hasObserver(OBS_ID2));
-        assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
+    public void testAppUsageObserver_AddObserver() {
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
+        addAppUsageObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+        assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID2));
+        assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
     }
 
-    /** Verify observer is removed */
+    /** Verify usage session observer is added */
     @Test
-    public void testRemoveObserver() {
-        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
-        mController.removeObserver(UID, OBS_ID1, USER_ID);
-        assertFalse("Observer wasn't removed", hasObserver(OBS_ID1));
+    public void testUsageSessionObserver_AddObserver() {
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+        addUsageSessionObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN, TIME_1_MIN);
+        assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID2));
+        assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+    }
+
+    /** Verify app usage observer is removed */
+    @Test
+    public void testAppUsageObserver_RemoveObserver() {
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
+        mController.removeAppUsageObserver(UID, OBS_ID1, USER_ID);
+        assertFalse("Observer wasn't removed", hasAppUsageObserver(UID, OBS_ID1));
+    }
+
+    /** Verify usage session observer is removed */
+    @Test
+    public void testUsageSessionObserver_RemoveObserver() {
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+        mController.removeUsageSessionObserver(UID, OBS_ID1, USER_ID);
+        assertFalse("Observer wasn't removed", hasUsageSessionObserver(UID, OBS_ID1));
     }
 
     /** Re-adding an observer should result in only one copy */
     @Test
-    public void testObserverReAdd() {
-        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
-        addObserver(OBS_ID1, GROUP1, TIME_10_MIN);
+    public void testAppUsageObserver_ObserverReAdd() {
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_10_MIN);
         assertTrue("Observer wasn't added",
-                mController.getObserverGroup(OBS_ID1, USER_ID).timeLimit == TIME_10_MIN);
-        mController.removeObserver(UID, OBS_ID1, USER_ID);
-        assertFalse("Observer wasn't removed", hasObserver(OBS_ID1));
+                mController.getAppUsageGroup(UID, OBS_ID1).getTimeLimitMs() == TIME_10_MIN);
+        mController.removeAppUsageObserver(UID, OBS_ID1, USER_ID);
+        assertFalse("Observer wasn't removed", hasAppUsageObserver(UID, OBS_ID1));
+    }
+
+    /** Re-adding an observer should result in only one copy */
+    @Test
+    public void testUsageSessionObserver_ObserverReAdd() {
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_10_MIN, TIME_1_MIN);
+        assertTrue("Observer wasn't added",
+                mController.getSessionUsageGroup(UID, OBS_ID1).getTimeLimitMs() == TIME_10_MIN);
+        mController.removeUsageSessionObserver(UID, OBS_ID1, USER_ID);
+        assertFalse("Observer wasn't removed", hasUsageSessionObserver(UID, OBS_ID1));
+    }
+
+    /** Different type observers can be registered to the same observerId value */
+    @Test
+    public void testAllObservers_ExclusiveObserverIds() {
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_10_MIN);
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
+        assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+
+        AppTimeLimitController.UsageGroup appUsageGroup = mController.getAppUsageGroup(UID,
+                OBS_ID1);
+        AppTimeLimitController.UsageGroup sessionUsageGroup = mController.getSessionUsageGroup(UID,
+                OBS_ID1);
+
+        // Verify data still intact
+        assertEquals(TIME_10_MIN, appUsageGroup.getTimeLimitMs());
+        assertEquals(TIME_30_MIN, sessionUsageGroup.getTimeLimitMs());
     }
 
     /** Verify that usage across different apps within a group are added up */
     @Test
-    public void testAccumulation() throws Exception {
+    public void testAppUsageObserver_Accumulation() throws Exception {
         setTime(0L);
-        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        moveToForeground(PKG_SOC1);
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        startUsage(PKG_SOC1);
         // Add 10 mins
         setTime(TIME_10_MIN);
-        moveToBackground(PKG_SOC1);
+        stopUsage(PKG_SOC1);
 
-        long timeRemaining = mController.getObserverGroup(OBS_ID1, USER_ID).timeRemaining;
+        AppTimeLimitController.UsageGroup group = mController.getAppUsageGroup(UID, OBS_ID1);
+
+        long timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
         assertEquals(TIME_10_MIN * 2, timeRemaining);
 
-        moveToForeground(PKG_SOC1);
+        startUsage(PKG_SOC1);
         setTime(TIME_10_MIN * 2);
-        moveToBackground(PKG_SOC1);
+        stopUsage(PKG_SOC1);
 
-        timeRemaining = mController.getObserverGroup(OBS_ID1, USER_ID).timeRemaining;
+        timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
         assertEquals(TIME_10_MIN, timeRemaining);
 
         setTime(TIME_30_MIN);
 
-        assertFalse(mCountDownLatch.await(100L, TimeUnit.MILLISECONDS));
+        assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
 
         // Add a different package in the group
-        moveToForeground(PKG_GAME1);
+        startUsage(PKG_GAME1);
         setTime(TIME_30_MIN + TIME_10_MIN);
-        moveToBackground(PKG_GAME1);
+        stopUsage(PKG_GAME1);
 
-        assertEquals(0, mController.getObserverGroup(OBS_ID1, USER_ID).timeRemaining);
-        assertTrue(mCountDownLatch.await(100L, TimeUnit.MILLISECONDS));
+        assertEquals(0, group.getTimeLimitMs() - group.getUsageTimeMs());
+        assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+    }
+
+    /** Verify that usage across different apps within a group are added up */
+    @Test
+    public void testUsageSessionObserver_Accumulation() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        startUsage(PKG_SOC1);
+        // Add 10 mins
+        setTime(TIME_10_MIN);
+        stopUsage(PKG_SOC1);
+
+        AppTimeLimitController.UsageGroup group = mController.getSessionUsageGroup(UID, OBS_ID1);
+
+        long timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
+        assertEquals(TIME_10_MIN * 2, timeRemaining);
+
+        startUsage(PKG_SOC1);
+        setTime(TIME_10_MIN * 2);
+        stopUsage(PKG_SOC1);
+
+        timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
+        assertEquals(TIME_10_MIN, timeRemaining);
+
+        setTime(TIME_30_MIN);
+
+        assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
+        // Add a different package in the group
+        startUsage(PKG_GAME1);
+        setTime(TIME_30_MIN + TIME_10_MIN);
+        stopUsage(PKG_GAME1);
+
+        assertEquals(0, group.getTimeLimitMs() - group.getUsageTimeMs());
+        assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
     }
 
     /** Verify that time limit does not get triggered due to a different app */
     @Test
-    public void testTimeoutOtherApp() throws Exception {
+    public void testAppUsageObserver_TimeoutOtherApp() throws Exception {
         setTime(0L);
-        addObserver(OBS_ID1, GROUP1, 4_000L);
-        moveToForeground(PKG_SOC2);
-        assertFalse(mCountDownLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        addAppUsageObserver(OBS_ID1, GROUP1, 4_000L);
+        startUsage(PKG_SOC2);
+        assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
         setTime(6_000L);
-        moveToBackground(PKG_SOC2);
-        assertFalse(mCountDownLatch.await(100L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC2);
+        assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+    }
+
+    /** Verify that time limit does not get triggered due to a different app */
+    @Test
+    public void testUsageSessionObserver_TimeoutOtherApp() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, 4_000L, 1_000L);
+        startUsage(PKG_SOC2);
+        assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        setTime(6_000L);
+        stopUsage(PKG_SOC2);
+        assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
     }
 
     /** Verify the timeout message is delivered at the right time */
     @Test
-    public void testTimeout() throws Exception {
+    public void testAppUsageObserver_Timeout() throws Exception {
         setTime(0L);
-        addObserver(OBS_ID1, GROUP1, 4_000L);
-        moveToForeground(PKG_SOC1);
+        addAppUsageObserver(OBS_ID1, GROUP1, 4_000L);
+        startUsage(PKG_SOC1);
         setTime(6_000L);
-        assertTrue(mCountDownLatch.await(6_000L, TimeUnit.MILLISECONDS));
-        moveToBackground(PKG_SOC1);
+        assertTrue(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
         // Verify that the observer was removed
-        assertFalse(hasObserver(OBS_ID1));
+        assertFalse(hasAppUsageObserver(UID, OBS_ID1));
+    }
+
+    /** Verify the timeout message is delivered at the right time */
+    @Test
+    public void testUsageSessionObserver_Timeout() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, 4_000L, 1_000L);
+        startUsage(PKG_SOC1);
+        setTime(6_000L);
+        assertTrue(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Usage has stopped, Session should end in a second. Verify session end occurs in a second
+        // (+/- 100ms, which is hopefully not too slim a margin)
+        assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+        assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+        // Verify that the observer was not removed
+        assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
     }
 
     /** If an app was already running, make sure it is partially counted towards the time limit */
     @Test
-    public void testAlreadyRunning() throws Exception {
+    public void testAppUsageObserver_AlreadyRunning() throws Exception {
         setTime(TIME_10_MIN);
-        moveToForeground(PKG_GAME1);
+        startUsage(PKG_GAME1);
         setTime(TIME_30_MIN);
-        addObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
         setTime(TIME_30_MIN + TIME_10_MIN);
-        moveToBackground(PKG_GAME1);
-        assertFalse(mCountDownLatch.await(1000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_GAME1);
+        assertFalse(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
 
-        moveToForeground(PKG_GAME2);
+        startUsage(PKG_GAME2);
         setTime(TIME_30_MIN + TIME_30_MIN);
-        moveToBackground(PKG_GAME2);
-        assertTrue(mCountDownLatch.await(1000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_GAME2);
+        assertTrue(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
         // Verify that the observer was removed
-        assertFalse(hasObserver(OBS_ID2));
+        assertFalse(hasAppUsageObserver(UID, OBS_ID2));
+    }
+
+    /** If an app was already running, make sure it is partially counted towards the time limit */
+    @Test
+    public void testUsageSessionObserver_AlreadyRunning() throws Exception {
+        setTime(TIME_10_MIN);
+        startUsage(PKG_GAME1);
+        setTime(TIME_30_MIN);
+        addUsageSessionObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN, TIME_1_MIN);
+        setTime(TIME_30_MIN + TIME_10_MIN);
+        stopUsage(PKG_GAME1);
+        assertFalse(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
+
+        startUsage(PKG_GAME2);
+        setTime(TIME_30_MIN + TIME_30_MIN);
+        stopUsage(PKG_GAME2);
+        assertTrue(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
+        // Verify that the observer was removed
+        assertTrue(hasUsageSessionObserver(UID, OBS_ID2));
     }
 
     /** If watched app is already running, verify the timeout callback happens at the right time */
     @Test
-    public void testAlreadyRunningTimeout() throws Exception {
+    public void testAppUsageObserver_AlreadyRunningTimeout() throws Exception {
         setTime(0);
-        moveToForeground(PKG_SOC1);
+        startUsage(PKG_SOC1);
         setTime(TIME_10_MIN);
         // 10 second time limit
-        addObserver(OBS_ID1, GROUP_SOC, 10_000L);
+        addAppUsageObserver(OBS_ID1, GROUP_SOC, 10_000L);
         setTime(TIME_10_MIN + 5_000L);
         // Shouldn't call back in 6 seconds
-        assertFalse(mCountDownLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
         setTime(TIME_10_MIN + 10_000L);
         // Should call back by 11 seconds (6 earlier + 5 now)
-        assertTrue(mCountDownLatch.await(5_000L, TimeUnit.MILLISECONDS));
+        assertTrue(mLimitReachedLatch.await(5_000L, TimeUnit.MILLISECONDS));
         // Verify that the observer was removed
-        assertFalse(hasObserver(OBS_ID1));
+        assertFalse(hasAppUsageObserver(UID, OBS_ID1));
     }
 
-    /** Verify that App Time Limit Controller will limit the number of observerIds */
+    /** If watched app is already running, verify the timeout callback happens at the right time */
     @Test
-    public void testMaxObserverLimit() throws Exception {
+    public void testUsageSessionObserver_AlreadyRunningTimeout() throws Exception {
+        setTime(0);
+        startUsage(PKG_SOC1);
+        setTime(TIME_10_MIN);
+        // 10 second time limit
+        addUsageSessionObserver(OBS_ID1, GROUP_SOC, 10_000L, 1_000L);
+        setTime(TIME_10_MIN + 5_000L);
+        // Shouldn't call back in 6 seconds
+        assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        setTime(TIME_10_MIN + 10_000L);
+        // Should call back by 11 seconds (6 earlier + 5 now)
+        assertTrue(mLimitReachedLatch.await(5_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Usage has stopped, Session should end in a second. Verify session end occurs in a second
+        // (+/- 100ms, which is hopefully not too slim a margin)
+        assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+        assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+        // Verify that the observer was removed
+        assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
+    }
+
+    /**
+     * Verify that App Time Limit Controller will limit the number of observerIds for app usage
+     * observers
+     */
+    @Test
+    public void testAppUsageObserver_MaxObserverLimit() throws Exception {
         boolean receivedException = false;
         int ANOTHER_UID = UID + 1;
-        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID2, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID3, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID4, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID5, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID6, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID7, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID8, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID9, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID10, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID2, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID3, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID4, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID5, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID6, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID7, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID8, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID9, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID10, GROUP1, TIME_30_MIN);
         // Readding an observer should not cause an IllegalStateException
-        addObserver(OBS_ID5, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID5, GROUP1, TIME_30_MIN);
         // Adding an observer for a different uid shouldn't cause an IllegalStateException
-        mController.addObserver(ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, null, USER_ID);
+        mController.addAppUsageObserver(ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, null, USER_ID);
         try {
-            addObserver(OBS_ID11, GROUP1, TIME_30_MIN);
+            addAppUsageObserver(OBS_ID11, GROUP1, TIME_30_MIN);
         } catch (IllegalStateException ise) {
             receivedException = true;
         }
         assertTrue("Should have caused an IllegalStateException", receivedException);
     }
 
-    /** Verify that addObserver minimum time limit is one minute */
+    /**
+     * Verify that App Time Limit Controller will limit the number of observerIds for usage session
+     * observers
+     */
     @Test
-    public void testMinimumTimeLimit() throws Exception {
+    public void testUsageSessionObserver_MaxObserverLimit() throws Exception {
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        boolean receivedException = false;
+        int ANOTHER_UID = UID + 1;
+        addUsageSessionObserver(OBS_ID2, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID3, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID4, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID5, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID6, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID7, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID8, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID9, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID10, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        // Readding an observer should not cause an IllegalStateException
+        addUsageSessionObserver(OBS_ID5, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        // Adding an observer for a different uid shouldn't cause an IllegalStateException
+        mController.addUsageSessionObserver(ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, TIME_1_MIN,
+                null, null, USER_ID);
+        try {
+            addUsageSessionObserver(OBS_ID11, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        } catch (IllegalStateException ise) {
+            receivedException = true;
+        }
+        assertTrue("Should have caused an IllegalStateException", receivedException);
+    }
+
+    /** Verify that addAppUsageObserver minimum time limit is one minute */
+    @Test
+    public void testAppUsageObserver_MinimumTimeLimit() throws Exception {
         boolean receivedException = false;
         // adding an observer with a one minute time limit should not cause an exception
-        addObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT);
+        addAppUsageObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT);
         try {
-            addObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT - 1);
+            addAppUsageObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT - 1);
         } catch (IllegalArgumentException iae) {
             receivedException = true;
         }
         assertTrue("Should have caused an IllegalArgumentException", receivedException);
     }
 
-    private void moveToForeground(String packageName) {
-        mController.moveToForeground(packageName, "class", USER_ID);
+    /** Verify that addUsageSessionObserver minimum time limit is one minute */
+    @Test
+    public void testUsageSessionObserver_MinimumTimeLimit() throws Exception {
+        boolean receivedException = false;
+        // test also for session observers
+        addUsageSessionObserver(OBS_ID10, GROUP1, MIN_TIME_LIMIT, TIME_1_MIN);
+        try {
+            addUsageSessionObserver(OBS_ID10, GROUP1, MIN_TIME_LIMIT - 1, TIME_1_MIN);
+        } catch (IllegalArgumentException iae) {
+            receivedException = true;
+        }
+        assertTrue("Should have caused an IllegalArgumentException", receivedException);
     }
 
-    private void moveToBackground(String packageName) {
-        mController.moveToBackground(packageName, "class", USER_ID);
+    /** Verify that concurrent usage from multiple apps in the same group will counted correctly */
+    @Test
+    public void testAppUsageObserver_ConcurrentUsage() throws Exception {
+        setTime(0L);
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        AppTimeLimitController.UsageGroup group = mController.getAppUsageGroup(UID, OBS_ID1);
+        startUsage(PKG_SOC1);
+        // Add 10 mins
+        setTime(TIME_10_MIN);
+
+        // Add a different package in the group will first package is still in use
+        startUsage(PKG_GAME1);
+        setTime(TIME_10_MIN * 2);
+        // Stop first package usage
+        stopUsage(PKG_SOC1);
+
+        setTime(TIME_30_MIN);
+        stopUsage(PKG_GAME1);
+
+        assertEquals(TIME_30_MIN, group.getUsageTimeMs());
+        assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
     }
 
-    private void addObserver(int observerId, String[] packages, long timeLimit) {
-        mController.addObserver(UID, observerId, packages, timeLimit, null, USER_ID);
+    /** Verify that concurrent usage from multiple apps in the same group will counted correctly */
+    @Test
+    public void testUsageSessionObserver_ConcurrentUsage() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        AppTimeLimitController.UsageGroup group = mController.getSessionUsageGroup(UID, OBS_ID1);
+        startUsage(PKG_SOC1);
+        // Add 10 mins
+        setTime(TIME_10_MIN);
+
+        // Add a different package in the group will first package is still in use
+        startUsage(PKG_GAME1);
+        setTime(TIME_10_MIN * 2);
+        // Stop first package usage
+        stopUsage(PKG_SOC1);
+
+        setTime(TIME_30_MIN);
+        stopUsage(PKG_GAME1);
+
+        assertEquals(TIME_30_MIN, group.getUsageTimeMs());
+        assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
     }
 
-    /** Is there still an observer by that id */
-    private boolean hasObserver(int observerId) {
-        return mController.getObserverGroup(observerId, USER_ID) != null;
+    /** Verify that a session will continue if usage starts again within the session threshold */
+    @Test
+    public void testUsageSessionObserver_ContinueSession() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, 10_000L, 2_000L);
+        startUsage(PKG_SOC1);
+        setTime(6_000L);
+        stopUsage(PKG_SOC1);
+        // Wait momentarily, Session should not end
+        assertFalse(mSessionEndLatch.await(1_000L, TimeUnit.MILLISECONDS));
+
+        setTime(7_000L);
+        startUsage(PKG_SOC1);
+        setTime(10_500L);
+        stopUsage(PKG_SOC1);
+        // Total usage time has not reached the limit. Time limit callback should not fire yet
+        assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
+        setTime(10_600L);
+        startUsage(PKG_SOC1);
+        setTime(12_000L);
+        assertTrue(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Usage has stopped, Session should end in 2 seconds. Verify session end occurs
+        // (+/- 100ms, which is hopefully not too slim a margin)
+        assertFalse(mSessionEndLatch.await(1_900L, TimeUnit.MILLISECONDS));
+        assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+        // Verify that the observer was not removed
+        assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
+    }
+
+    /** Verify that a new session will start if next usage starts after the session threshold */
+    @Test
+    public void testUsageSessionObserver_NewSession() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, 10_000L, 1_000L);
+        startUsage(PKG_SOC1);
+        setTime(6_000L);
+        stopUsage(PKG_SOC1);
+        // Wait for longer than the session threshold. Session end callback should not be triggered
+        // because the usage timelimit hasn't been triggered.
+        assertFalse(mSessionEndLatch.await(1_500L, TimeUnit.MILLISECONDS));
+
+        setTime(7_500L);
+        // This should be the start of a new session
+        startUsage(PKG_SOC1);
+        setTime(16_000L);
+        stopUsage(PKG_SOC1);
+        // Total usage has exceed the timelimit, but current session time has not
+        assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
+        setTime(16_100L);
+        startUsage(PKG_SOC1);
+        setTime(18_000L);
+        assertTrue(mLimitReachedLatch.await(2000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Usage has stopped, Session should end in 2 seconds. Verify session end occurs
+        // (+/- 100ms, which is hopefully not too slim a margin)
+        assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+        assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+        // Verify that the observer was not removed
+        assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
+    }
+
+    /** Verify that the callbacks will be triggered for multiple sessions */
+    @Test
+    public void testUsageSessionObserver_RepeatSessions() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, 10_000L, 1_000L);
+        startUsage(PKG_SOC1);
+        setTime(9_000L);
+        stopUsage(PKG_SOC1);
+        // Stutter usage here, to reduce real world time needed trigger limit reached callback
+        startUsage(PKG_SOC1);
+        setTime(11_000L);
+        assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Usage has stopped, Session should end in 1 seconds. Verify session end occurs
+        // (+/- 100ms, which is hopefully not too slim a margin)
+        assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+        assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+
+        // Rearm the countdown latches
+        mLimitReachedLatch = new CountDownLatch(1);
+        mSessionEndLatch = new CountDownLatch(1);
+
+        // New session start
+        setTime(20_000L);
+        startUsage(PKG_SOC1);
+        setTime(29_000L);
+        stopUsage(PKG_SOC1);
+        startUsage(PKG_SOC1);
+        setTime(31_000L);
+        assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+        assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+        assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
+    }
+
+    private void startUsage(String packageName) {
+        mController.noteUsageStart(packageName, USER_ID);
+    }
+
+    private void stopUsage(String packageName) {
+        mController.noteUsageStop(packageName, USER_ID);
+    }
+
+    private void addAppUsageObserver(int observerId, String[] packages, long timeLimit) {
+        mController.addAppUsageObserver(UID, observerId, packages, timeLimit, null, USER_ID);
+    }
+
+    private void addUsageSessionObserver(int observerId, String[] packages, long timeLimit,
+            long sessionThreshold) {
+        mController.addUsageSessionObserver(UID, observerId, packages, timeLimit, sessionThreshold,
+                null, null, USER_ID);
+    }
+
+    /** Is there still an app usage observer by that id */
+    private boolean hasAppUsageObserver(int uid, int observerId) {
+        return mController.getAppUsageGroup(uid, observerId) != null;
+    }
+
+    /** Is there still an usage session observer by that id */
+    private boolean hasUsageSessionObserver(int uid, int observerId) {
+        return mController.getSessionUsageGroup(uid, observerId) != null;
     }
 
     private void setTime(long time) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
index b5fe8b1..a907161 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -29,11 +29,9 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -41,12 +39,11 @@
  * Tests for the {@link TaskStack} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.AnimatingAppWindowTokenRegistryTest
+ *  atest FrameworksServicesTests:AnimatingAppWindowTokenRegistryTest
  */
 @SmallTest
 @Presubmit
 @FlakyTest(detail = "Promote once confirmed non-flaky")
-@RunWith(AndroidJUnit4.class)
 public class AnimatingAppWindowTokenRegistryTest extends WindowTestsBase {
 
     @Mock
@@ -56,14 +53,14 @@
     Runnable mMockEndDeferFinishCallback1;
     @Mock
     Runnable mMockEndDeferFinishCallback2;
+
     @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
     }
 
     @Test
-    public void testDeferring() throws Exception {
+    public void testDeferring() {
         final AppWindowToken window1 = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD,
@@ -85,7 +82,7 @@
     }
 
     @Test
-    public void testContainerRemoved() throws Exception {
+    public void testContainerRemoved() {
         final AppWindowToken window1 = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD,
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java
new file mode 100644
index 0000000..5e12a95
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:AppTransitionControllerTest
+ */
+@SmallTest
+@Presubmit
+public class AppTransitionControllerTest extends WindowTestsBase {
+
+    private AppTransitionController mAppTransitionController;
+
+    @Before
+    public void setUp() throws Exception {
+        mAppTransitionController = new AppTransitionController(mWm, mDisplayContent);
+    }
+
+    @Test
+    public void testTranslucentOpen() {
+        synchronized (mWm.mGlobalLock) {
+            final AppWindowToken behind = createAppWindowToken(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            translucentOpening.setFillsParent(false);
+            translucentOpening.setHidden(true);
+            mDisplayContent.mOpeningApps.add(behind);
+            mDisplayContent.mOpeningApps.add(translucentOpening);
+            assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
+                    mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
+                            TRANSIT_TASK_OPEN));
+        }
+    }
+
+    @Test
+    public void testTranslucentClose() {
+        synchronized (mWm.mGlobalLock) {
+            final AppWindowToken behind = createAppWindowToken(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            translucentClosing.setFillsParent(false);
+            mDisplayContent.mClosingApps.add(translucentClosing);
+            assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
+                    mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
+                            TRANSIT_TASK_CLOSE));
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
index 3053c41..f12619c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
@@ -11,83 +11,179 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
 
-import android.content.Context;
+import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.IApplicationToken;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link AppTransition}.
  *
- * atest AppTransitionTests
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:AppTransitionTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
-public class AppTransitionTests {
+public class AppTransitionTests extends WindowTestsBase {
 
-    @Rule
-    public final WindowManagerServiceRule mRule = new WindowManagerServiceRule();
-    private WindowManagerService mWm;
+    private DisplayContent mDc;
 
     @Before
     public void setUp() throws Exception {
-        final Context context = InstrumentationRegistry.getTargetContext();
-        mWm = mRule.getWindowManagerService();
+        mDc = mWm.getDefaultDisplayContentLocked();
+        // For unit test,  we don't need to test performSurfacePlacement to prevent some
+        // abnormal interaction with surfaceflinger native side.
+        mWm.mRoot = spy(mWm.mRoot);
+        doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
     }
 
     @Test
-    public void testKeyguardOverride() throws Exception {
+    public void testKeyguardOverride() {
         mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
         mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
-        assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mWm.mAppTransition.getAppTransition());
+        assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition());
     }
 
     @Test
-    public void testKeyguardKeep() throws Exception {
+    public void testKeyguardKeep() {
         mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
         mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
-        assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mWm.mAppTransition.getAppTransition());
+        assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition());
     }
 
     @Test
-    public void testForceOverride() throws Exception {
+    public void testForceOverride() {
         mWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */);
-        mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */,
-                0 /* flags */, true /* forceOverride */);
-        assertEquals(TRANSIT_ACTIVITY_OPEN, mWm.mAppTransition.getAppTransition());
+        mDc.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
+                false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
+        assertEquals(TRANSIT_ACTIVITY_OPEN, mDc.mAppTransition.getAppTransition());
     }
 
     @Test
-    public void testCrashing() throws Exception {
+    public void testCrashing() {
         mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
         mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
-        assertEquals(TRANSIT_CRASHING_ACTIVITY_CLOSE, mWm.mAppTransition.getAppTransition());
+        assertEquals(TRANSIT_CRASHING_ACTIVITY_CLOSE, mDc.mAppTransition.getAppTransition());
     }
 
     @Test
-    public void testKeepKeyguard_withCrashing() throws Exception {
+    public void testKeepKeyguard_withCrashing() {
         mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
         mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
-        assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mWm.mAppTransition.getAppTransition());
+        assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition());
+    }
+
+    @Test
+    public void testAppTransitionStateForMultiDisplay() {
+        // Create 2 displays & presume both display the state is ON for ready to display & animate.
+        final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON);
+        final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON);
+
+        // Create 2 app window tokens to represent 2 activity window.
+        final WindowTestUtils.TestAppWindowToken token1 = createTestAppWindowToken(dc1,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        final WindowTestUtils.TestAppWindowToken token2 = createTestAppWindowToken(dc2,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+
+        // Set TestAppWindowContainerController & assign first app token state to be good to go.
+        final WindowTestUtils.TestAppWindowContainerController controller1 =
+                createAppWindowController(dc1, token1.appToken);
+        final WindowTestUtils.TestAppWindowContainerController controller2 =
+                createAppWindowController(dc1, token2.appToken);
+        controller1.setContainer(token1);
+        token1.allDrawn = true;
+        token1.startingDisplayed = true;
+        token1.startingMoved = true;
+        controller2.setContainer(token2);
+
+        // Simulate activity resume / finish flows to prepare app transition & set visibility,
+        // make sure transition is set as expected for each display.
+        dc1.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
+                false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
+        assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition());
+        dc2.getController().prepareAppTransition(TRANSIT_ACTIVITY_CLOSE,
+                false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
+        assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition());
+        // One activity window is visible for resuming & the other activity window is invisible
+        // for finishing in different display.
+        controller1.setVisibility(true, false);
+        controller2.setVisibility(false, false);
+
+        // Make sure each display is in animating stage.
+        assertTrue(dc1.mOpeningApps.size() > 0);
+        assertTrue(dc2.mClosingApps.size() > 0);
+        assertTrue(dc1.isAppAnimating());
+        assertTrue(dc2.isAppAnimating());
+    }
+
+    @Test
+    public void testCleanAppTransitionWhenTaskStackReparent() {
+        // Create 2 displays & presume both display the state is ON for ready to display & animate.
+        final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON);
+        final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON);
+
+        final TaskStack stack1 = createTaskStackOnDisplay(dc1);
+        final Task task1 = createTaskInStack(stack1, 0 /* userId */);
+        final WindowTestUtils.TestAppWindowToken token1 =
+                WindowTestUtils.createTestAppWindowToken(dc1);
+        task1.addChild(token1, 0);
+
+        // Simulate same app is during opening / closing transition set stage.
+        dc1.mClosingApps.add(token1);
+        assertTrue(dc1.mClosingApps.size() > 0);
+
+        dc1.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
+                false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
+        assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition());
+        assertTrue(dc1.mAppTransition.isTransitionSet());
+
+        dc1.mOpeningApps.add(token1);
+        assertTrue(dc1.mOpeningApps.size() > 0);
+
+        // Move stack to another display.
+        stack1.getController().reparent(dc2.getDisplayId(),  new Rect(), true);
+
+        // Verify if token are cleared from both pending transition list in former display.
+        assertFalse(dc1.mOpeningApps.contains(token1));
+        assertFalse(dc1.mOpeningApps.contains(token1));
+    }
+
+    private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
+            DisplayContent dc, IApplicationToken token) {
+        return createAppWindowController(
+                new WindowTestUtils.TestTaskWindowContainerController(
+                        createStackControllerOnDisplay(dc)), token);
+    }
+
+    private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
+            WindowTestUtils.TestTaskWindowContainerController taskController,
+            IApplicationToken token) {
+        return new WindowTestUtils.TestAppWindowContainerController(taskController, token);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index fcd8a39e4..415b5d9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -21,6 +21,8 @@
 import static android.content.res.Configuration.EMPTY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -29,10 +31,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController;
 
@@ -41,16 +41,17 @@
 /**
  * Test class for {@link AppWindowContainerController}.
  *
- * atest FrameworksServicesTests:com.android.server.wm.AppWindowContainerControllerTests
+ * atest FrameworksServicesTests:AppWindowContainerControllerTests
  */
+@FlakyTest(bugId = 74078662)
 @SmallTest
 @Presubmit
-@FlakyTest(bugId = 74078662)
-@org.junit.runner.RunWith(AndroidJUnit4.class)
 public class AppWindowContainerControllerTests extends WindowTestsBase {
 
+    private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
+
     @Test
-    public void testRemoveContainer() throws Exception {
+    public void testRemoveContainer() {
         final WindowTestUtils.TestAppWindowContainerController controller =
                 createAppWindowController();
 
@@ -68,7 +69,7 @@
     }
 
     @Test
-    public void testSetOrientation() throws Exception {
+    public void testSetOrientation() {
         final WindowTestUtils.TestAppWindowContainerController controller =
                 createAppWindowController();
 
@@ -84,7 +85,7 @@
         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
 
         // Reset display frozen state
-        sWm.mDisplayFrozen = false;
+        mWm.mDisplayFrozen = false;
     }
 
     private void assertHasStartingWindow(AppWindowToken atoken) {
@@ -103,10 +104,10 @@
     }
 
     @Test
-    public void testCreateRemoveStartingWindow() throws Exception {
+    public void testCreateRemoveStartingWindow() {
         final WindowTestUtils.TestAppWindowContainerController controller =
                 createAppWindowController();
-        controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+        controller.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false, false);
         waitUntilHandlersIdle();
@@ -118,34 +119,34 @@
     }
 
     @Test
-    public void testAddRemoveRace() throws Exception {
-
+    public void testAddRemoveRace() {
         // There was once a race condition between adding and removing starting windows
         for (int i = 0; i < 1000; i++) {
             final WindowTestUtils.TestAppWindowContainerController controller =
                     createAppWindowController();
-            controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+            controller.addStartingWindow(mPackageName,
                     android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                     false, false);
             controller.removeStartingWindow();
             waitUntilHandlersIdle();
             assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
 
-            controller.getAppWindowToken(mDisplayContent).getParent().getParent().removeImmediately();
+            controller.getAppWindowToken(
+                    mDisplayContent).getParent().getParent().removeImmediately();
         }
     }
 
     @Test
-    public void testTransferStartingWindow() throws Exception {
+    public void testTransferStartingWindow() {
         final WindowTestUtils.TestAppWindowContainerController controller1 =
                 createAppWindowController();
         final WindowTestUtils.TestAppWindowContainerController controller2 =
                 createAppWindowController();
-        controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+        controller1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false, false);
         waitUntilHandlersIdle();
-        controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+        controller2.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
                 true, true, false, true, false, false);
         waitUntilHandlersIdle();
@@ -154,19 +155,19 @@
     }
 
     @Test
-    public void testTransferStartingWindowWhileCreating() throws Exception {
+    public void testTransferStartingWindowWhileCreating() {
         final WindowTestUtils.TestAppWindowContainerController controller1 =
                 createAppWindowController();
         final WindowTestUtils.TestAppWindowContainerController controller2 =
                 createAppWindowController();
-        ((TestWindowManagerPolicy) sWm.mPolicy).setRunnableWhenAddingSplashScreen(() -> {
+        ((TestWindowManagerPolicy) mWm.mPolicy).setRunnableWhenAddingSplashScreen(() -> {
 
             // Surprise, ...! Transfer window in the middle of the creation flow.
-            controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+            controller2.addStartingWindow(mPackageName,
                     android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
                     true, true, false, true, false, false);
         });
-        controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+        controller1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false, false);
         waitUntilHandlersIdle();
@@ -175,7 +176,7 @@
     }
 
     @Test
-    public void testTryTransferStartingWindowFromHiddenAboveToken() throws Exception {
+    public void testTryTransferStartingWindowFromHiddenAboveToken() {
 
         // Add two tasks on top of each other.
         TestTaskWindowContainerController taskController =
@@ -186,7 +187,7 @@
                 createAppWindowController(taskController);
 
         // Add a starting window.
-        controllerTop.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+        controllerTop.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false, false);
         waitUntilHandlersIdle();
@@ -202,7 +203,7 @@
     }
 
     @Test
-    public void testReparent() throws Exception {
+    public void testReparent() {
         final StackWindowController stackController =
             createStackControllerOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTaskWindowContainerController taskController1 =
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index e3ab5cf..4522494 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -26,15 +26,14 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
 import android.view.SurfaceControl;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.server.wm.WindowTestUtils.TestAppWindowToken;
 
+import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -43,10 +42,9 @@
  * Animation related tests for the {@link AppWindowToken} class.
  *
  * Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.wm.AppWindowTokenAnimationTests
+ *  atest FrameworksServicesTests:AppWindowTokenAnimationTests
  */
 @SmallTest
-@RunWith(AndroidJUnit4.class)
 public class AppWindowTokenAnimationTests extends WindowTestsBase {
 
     private TestAppWindowToken mToken;
@@ -56,9 +54,8 @@
     @Mock
     private AnimationAdapter mSpec;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
 
         mToken = createTestAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN,
@@ -67,7 +64,7 @@
     }
 
     @Test
-    public void clipAfterAnim_boundsLayerIsCreated() throws Exception {
+    public void clipAfterAnim_boundsLayerIsCreated() {
         mToken.mNeedsAnimationBoundsLayer = true;
 
         mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
@@ -78,7 +75,7 @@
     }
 
     @Test
-    public void clipAfterAnim_boundsLayerIsDestroyed() throws Exception {
+    public void clipAfterAnim_boundsLayerIsDestroyed() {
         mToken.mNeedsAnimationBoundsLayer = true;
         mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash;
@@ -95,7 +92,7 @@
     }
 
     @Test
-    public void clipAfterAnimCancelled_boundsLayerIsDestroyed() throws Exception {
+    public void clipAfterAnimCancelled_boundsLayerIsDestroyed() {
         mToken.mNeedsAnimationBoundsLayer = true;
         mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash;
@@ -108,7 +105,7 @@
     }
 
     @Test
-    public void clipNoneAnim_boundsLayerIsNotCreated() throws Exception {
+    public void clipNoneAnim_boundsLayerIsNotCreated() {
         mToken.mNeedsAnimationBoundsLayer = false;
 
         mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index 7935ec1..552390d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -47,31 +47,27 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link AppWindowToken} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.AppWindowTokenTests
+ *  atest FrameworksServicesTests:AppWindowTokenTests
  */
+@FlakyTest(bugId = 68267650)
 @SmallTest
-// TODO: b/68267650
-// @Presubmit
-@RunWith(AndroidJUnit4.class)
+@Presubmit
 public class AppWindowTokenTests extends WindowTestsBase {
 
     TaskStack mStack;
     Task mTask;
     WindowTestUtils.TestAppWindowToken mToken;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-
         mStack = createTaskStackOnDisplay(mDisplayContent);
         mTask = createTaskInStack(mStack, 0 /* userId */);
         mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
@@ -81,7 +77,7 @@
 
     @Test
     @Presubmit
-    public void testAddWindow_Order() throws Exception {
+    public void testAddWindow_Order() {
         assertEquals(0, mToken.getWindowsCount());
 
         final WindowState win1 = createWindow(null, TYPE_APPLICATION, mToken, "win1");
@@ -107,7 +103,7 @@
 
     @Test
     @Presubmit
-    public void testFindMainWindow() throws Exception {
+    public void testFindMainWindow() {
         assertNull(mToken.findMainWindow());
 
         final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
@@ -123,7 +119,7 @@
 
     @Test
     @Presubmit
-    public void testGetTopFullscreenWindow() throws Exception {
+    public void testGetTopFullscreenWindow() {
         assertNull(mToken.getTopFullscreenWindow());
 
         final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
@@ -138,10 +134,10 @@
     }
 
     @Test
-    public void testLandscapeSeascapeRotationByApp() throws Exception {
+    public void testLandscapeSeascapeRotationByApp() {
         // Some plumbing to get the service ready for rotation updates.
-        sWm.mDisplayReady = true;
-        sWm.mDisplayEnabled = true;
+        mWm.mDisplayReady = true;
+        mWm.mDisplayEnabled = true;
 
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
@@ -151,26 +147,26 @@
 
         // Set initial orientation and update.
         mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
+        mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
                 mDisplayContent.getDisplayId());
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
         appWindow.resizeReported = false;
 
         // Update the orientation to perform 180 degree rotation and check that resize was reported.
         mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
-        sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
+        mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
                 mDisplayContent.getDisplayId());
-        sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
+        mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
         assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation());
         assertTrue(appWindow.resizeReported);
         appWindow.removeImmediately();
     }
 
     @Test
-    public void testLandscapeSeascapeRotationByPolicy() throws Exception {
+    public void testLandscapeSeascapeRotationByPolicy() {
         // Some plumbing to get the service ready for rotation updates.
-        sWm.mDisplayReady = true;
-        sWm.mDisplayEnabled = true;
+        mWm.mDisplayReady = true;
+        mWm.mDisplayEnabled = true;
 
         final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation());
         mDisplayContent.setDisplayRotation(spiedRotation);
@@ -194,15 +190,19 @@
 
     private void performRotation(DisplayRotation spiedRotation, int rotationToReport) {
         doReturn(rotationToReport).when(spiedRotation).rotationForOrientation(anyInt(), anyInt());
-        sWm.updateRotation(false, false);
+        int oldRotation = mDisplayContent.getRotation();
+        mWm.updateRotation(false, false);
+        // Must manually apply here since ATM doesn't know about the display during this test
+        // (meaning it can't perform the normal sendNewConfiguration flow).
+        mDisplayContent.applyRotationLocked(oldRotation, mDisplayContent.getRotation());
         // Prevent the next rotation from being deferred by animation.
-        sWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null);
-        sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
+        mWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null);
+        mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
     }
 
     @Test
     @Presubmit
-    public void testGetOrientation() throws Exception {
+    public void testGetOrientation() {
         mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
         mToken.setFillsParent(false);
@@ -220,7 +220,7 @@
 
     @Test
     @Presubmit
-    public void testKeyguardFlagsDuringRelaunch() throws Exception {
+    public void testKeyguardFlagsDuringRelaunch() {
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
@@ -246,7 +246,7 @@
 
     @Test
     @FlakyTest(detail = "Promote once confirmed non-flaky")
-    public void testStuckExitingWindow() throws Exception {
+    public void testStuckExitingWindow() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
         closingWindow.mAnimatingExit = true;
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index e6e08bb..1c5391e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -11,22 +11,24 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
 import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import android.animation.ValueAnimator;
 import android.content.Context;
@@ -35,16 +37,14 @@
 import android.os.Looper;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.BoundsAnimationController.BoundsAnimator;
 import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
 
+import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks
@@ -57,21 +57,20 @@
  * appropriately.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.BoundsAnimationControllerTests
+ *  atest FrameworksServicesTests:BoundsAnimationControllerTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class BoundsAnimationControllerTests extends WindowTestsBase {
 
     /**
      * Mock value animator to simulate updates with.
      */
-    private class MockValueAnimator extends ValueAnimator {
+    private static class MockValueAnimator extends ValueAnimator {
 
         private float mFraction;
 
-        public MockValueAnimator getWithValue(float fraction) {
+        MockValueAnimator getWithValue(float fraction) {
             mFraction = fraction;
             return this;
         }
@@ -85,12 +84,12 @@
     /**
      * Mock app transition to fire notifications to the bounds animator.
      */
-    private class MockAppTransition extends AppTransition {
+    private static class MockAppTransition extends AppTransition {
 
         private AppTransitionListener mListener;
 
-        MockAppTransition(Context context) {
-            super(context, sWm);
+        MockAppTransition(Context context, WindowManagerService wm, DisplayContent displayContent) {
+            super(context, wm, displayContent);
         }
 
         @Override
@@ -118,7 +117,7 @@
     /**
      * A test animate bounds user to track callbacks from the bounds animation.
      */
-    private class TestBoundsAnimationTarget implements BoundsAnimationTarget {
+    private static class TestBoundsAnimationTarget implements BoundsAnimationTarget {
 
         boolean mAwaitingAnimationStart;
         boolean mMovedToFullscreen;
@@ -191,21 +190,23 @@
     /**
      * Drives the animations, makes common assertions along the way.
      */
-    private class BoundsAnimationDriver {
+    private static class BoundsAnimationDriver {
 
-        private BoundsAnimationController mController;
-        private TestBoundsAnimationTarget mTarget;
+        private final BoundsAnimationController mController;
+        private final TestBoundsAnimationTarget mTarget;
+        private final MockValueAnimator mMockAnimator;
+
         private BoundsAnimator mAnimator;
-
         private Rect mFrom;
         private Rect mTo;
         private Rect mLargerBounds;
         private Rect mExpectedFinalBounds;
 
         BoundsAnimationDriver(BoundsAnimationController controller,
-                TestBoundsAnimationTarget target) {
+                TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) {
             mController = controller;
             mTarget = target;
+            mMockAnimator = mockValueAnimator;
         }
 
         BoundsAnimationDriver start(Rect from, Rect to) {
@@ -220,7 +221,7 @@
 
             // Started, not running
             assertTrue(mTarget.mAwaitingAnimationStart);
-            assertTrue(!mTarget.mAnimationStarted);
+            assertFalse(mTarget.mAnimationStarted);
 
             startImpl(from, to);
 
@@ -234,7 +235,7 @@
             }
 
             // Started and running
-            assertTrue(!mTarget.mAwaitingAnimationStart);
+            assertFalse(mTarget.mAwaitingAnimationStart);
             assertTrue(mTarget.mAnimationStarted);
 
             return this;
@@ -266,7 +267,7 @@
                 assertTrue(mTarget.mForcePipModeChangedCallback);
             } else {
                 // No animation start for replacing animation
-                assertTrue(!mTarget.mAnimationStarted);
+                assertFalse(mTarget.mAnimationStarted);
             }
             mTarget.mAnimationStarted = true;
             return this;
@@ -295,7 +296,7 @@
 
             // Animating to larger size
             if (mFrom.equals(mLargerBounds)) {
-                assertTrue(!mAnimator.animatingToLargerSize());
+                assertFalse(mAnimator.animatingToLargerSize());
             } else if (mTo.equals(mLargerBounds)) {
                 assertTrue(mAnimator.animatingToLargerSize());
             }
@@ -337,10 +338,10 @@
             mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(0.5f));
 
             // Not started, not running, cancel reset
-            assertTrue(!mTarget.mCancelRequested);
+            assertFalse(mTarget.mCancelRequested);
 
             // Stack/task bounds not updated
-            assertTrue(!mTarget.mBoundsUpdated);
+            assertFalse(mTarget.mBoundsUpdated);
 
             // Callback made
             assertTrue(mTarget.mAnimationEnded);
@@ -370,14 +371,10 @@
             return this;
         }
 
-        private Rect getLargerBounds(Rect r1, Rect r2) {
+        private static Rect getLargerBounds(Rect r1, Rect r2) {
             int r1Area = r1.width() * r1.height();
             int r2Area = r2.width() * r2.height();
-            if (r1Area <= r2Area) {
-                return r2;
-            } else {
-                return r1;
-            }
+            return (r1Area <= r2Area) ? r2 : r1;
         }
     }
 
@@ -393,32 +390,29 @@
 
     // Common
     private MockAppTransition mMockAppTransition;
-    private MockValueAnimator mMockAnimator;
     private TestBoundsAnimationTarget mTarget;
     private BoundsAnimationController mController;
     private BoundsAnimationDriver mDriver;
 
     // Temp
-    private Rect mTmpRect = new Rect();
+    private static final Rect sTmpRect = new Rect();
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-
-        final Context context = InstrumentationRegistry.getTargetContext();
+        final Context context = getInstrumentation().getTargetContext();
         final Handler handler = new Handler(Looper.getMainLooper());
-        mMockAppTransition = new MockAppTransition(context);
-        mMockAnimator = new MockValueAnimator();
+        mMockAppTransition = new MockAppTransition(context, mWm, mDisplayContent);
         mTarget = new TestBoundsAnimationTarget();
         mController = new BoundsAnimationController(context, mMockAppTransition, handler, null);
-        mDriver = new BoundsAnimationDriver(mController, mTarget);
+        final MockValueAnimator mockValueAnimator = new MockValueAnimator();
+        mDriver = new BoundsAnimationDriver(mController, mTarget, mockValueAnimator);
     }
 
     /** BASE TRANSITIONS **/
 
     @UiThreadTest
     @Test
-    public void testFullscreenToFloatingTransition() throws Exception {
+    public void testFullscreenToFloatingTransition() {
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0f)
@@ -430,7 +424,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToFullscreenTransition() throws Exception {
+    public void testFloatingToFullscreenTransition() {
         mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
                 .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
                 .update(0f)
@@ -442,7 +436,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToSmallerFloatingTransition() throws Exception {
+    public void testFloatingToSmallerFloatingTransition() {
         mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0f)
@@ -454,7 +448,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToLargerFloatingTransition() throws Exception {
+    public void testFloatingToLargerFloatingTransition() {
         mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0f)
@@ -468,7 +462,7 @@
 
     @UiThreadTest
     @Test
-    public void testFullscreenToFloatingCancelFromTarget() throws Exception {
+    public void testFullscreenToFloatingCancelFromTarget() {
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -478,7 +472,7 @@
 
     @UiThreadTest
     @Test
-    public void testFullscreenToFloatingCancelFromAnimationToSameBounds() throws Exception {
+    public void testFullscreenToFloatingCancelFromAnimationToSameBounds() {
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -489,7 +483,7 @@
 
     @UiThreadTest
     @Test
-    public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() throws Exception {
+    public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() {
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -501,7 +495,7 @@
 
     @UiThreadTest
     @Test
-    public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() throws Exception {
+    public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() {
         // When animating from fullscreen and the animation is interruped, we expect the animation
         // start callback to be made, with a forced pip mode change callback
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
@@ -516,7 +510,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToFullscreenCancelFromTarget() throws Exception {
+    public void testFloatingToFullscreenCancelFromTarget() {
         mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
                 .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -526,7 +520,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToFullscreenCancelFromAnimationToSameBounds() throws Exception {
+    public void testFloatingToFullscreenCancelFromAnimationToSameBounds() {
         mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
                 .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -537,7 +531,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() throws Exception {
+    public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() {
         mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
                 .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -551,7 +545,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToSmallerFloatingCancelFromTarget() throws Exception {
+    public void testFloatingToSmallerFloatingCancelFromTarget() {
         mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -561,7 +555,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToLargerFloatingCancelFromTarget() throws Exception {
+    public void testFloatingToLargerFloatingCancelFromTarget() {
         mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -573,7 +567,7 @@
 
     @UiThreadTest
     @Test
-    public void testBoundsAreCopied() throws Exception {
+    public void testBoundsAreCopied() {
         Rect from = new Rect(0, 0, 100, 100);
         Rect to = new Rect(25, 25, 75, 75);
         mDriver.start(from, to)
@@ -586,9 +580,9 @@
     /**
      * @return whether the task and stack bounds would be the same if they were at the same offset.
      */
-    private boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) {
-        mTmpRect.set(taskBounds);
-        mTmpRect.offsetTo(stackBounds.left, stackBounds.top);
-        return stackBounds.equals(mTmpRect);
+    private static boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) {
+        sTmpRect.set(taskBounds);
+        sTmpRect.offsetTo(stackBounds.left, stackBounds.top);
+        return stackBounds.equals(sTmpRect);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java
deleted file mode 100644
index 2b8214d..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.wm;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
-import static android.content.res.Configuration.EMPTY;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.content.res.Configuration;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Test class for {@link ConfigurationContainer}.
- *
- * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.ConfigurationContainerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class ConfigurationContainerTests {
-
-    @Test
-    public void testConfigurationInit() throws Exception {
-        // Check root container initial config.
-        final TestConfigurationContainer root = new TestConfigurationContainer();
-        assertEquals(EMPTY, root.getOverrideConfiguration());
-        assertEquals(EMPTY, root.getMergedOverrideConfiguration());
-        assertEquals(EMPTY, root.getConfiguration());
-
-        // Check child initial config.
-        final TestConfigurationContainer child1 = root.addChild();
-        assertEquals(EMPTY, child1.getOverrideConfiguration());
-        assertEquals(EMPTY, child1.getMergedOverrideConfiguration());
-        assertEquals(EMPTY, child1.getConfiguration());
-
-        // Check child initial config if root has overrides.
-        final Configuration rootOverrideConfig = new Configuration();
-        rootOverrideConfig.fontScale = 1.3f;
-        root.onOverrideConfigurationChanged(rootOverrideConfig);
-        final TestConfigurationContainer child2 = root.addChild();
-        assertEquals(EMPTY, child2.getOverrideConfiguration());
-        assertEquals(rootOverrideConfig, child2.getMergedOverrideConfiguration());
-        assertEquals(rootOverrideConfig, child2.getConfiguration());
-
-        // Check child initial config if root has parent config set.
-        final Configuration rootParentConfig = new Configuration();
-        rootParentConfig.fontScale = 0.8f;
-        rootParentConfig.orientation = SCREEN_ORIENTATION_LANDSCAPE;
-        root.onConfigurationChanged(rootParentConfig);
-        final Configuration rootFullConfig = new Configuration(rootParentConfig);
-        rootFullConfig.updateFrom(rootOverrideConfig);
-
-        final TestConfigurationContainer child3 = root.addChild();
-        assertEquals(EMPTY, child3.getOverrideConfiguration());
-        assertEquals(rootOverrideConfig, child3.getMergedOverrideConfiguration());
-        assertEquals(rootFullConfig, child3.getConfiguration());
-    }
-
-    @Test
-    public void testConfigurationChangeOnAddRemove() throws Exception {
-        // Init root's config.
-        final TestConfigurationContainer root = new TestConfigurationContainer();
-        final Configuration rootOverrideConfig = new Configuration();
-        rootOverrideConfig.fontScale = 1.3f;
-        root.onOverrideConfigurationChanged(rootOverrideConfig);
-
-        // Init child's config.
-        final TestConfigurationContainer child = root.addChild();
-        final Configuration childOverrideConfig = new Configuration();
-        childOverrideConfig.densityDpi = 320;
-        child.onOverrideConfigurationChanged(childOverrideConfig);
-        final Configuration mergedOverrideConfig = new Configuration(root.getConfiguration());
-        mergedOverrideConfig.updateFrom(childOverrideConfig);
-
-        // Check configuration update when child is removed from parent.
-        root.removeChild(child);
-        assertEquals(childOverrideConfig, child.getOverrideConfiguration());
-        assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
-        assertEquals(mergedOverrideConfig, child.getConfiguration());
-
-        // It may be paranoia... but let's check if parent's config didn't change after removal.
-        assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
-        assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
-        assertEquals(rootOverrideConfig, root.getConfiguration());
-
-        // Init different root
-        final TestConfigurationContainer root2 = new TestConfigurationContainer();
-        final Configuration rootOverrideConfig2 = new Configuration();
-        rootOverrideConfig2.fontScale = 1.1f;
-        root2.onOverrideConfigurationChanged(rootOverrideConfig2);
-
-        // Check configuration update when child is added to different parent.
-        mergedOverrideConfig.setTo(rootOverrideConfig2);
-        mergedOverrideConfig.updateFrom(childOverrideConfig);
-        root2.addChild(child);
-        assertEquals(childOverrideConfig, child.getOverrideConfiguration());
-        assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
-        assertEquals(mergedOverrideConfig, child.getConfiguration());
-    }
-
-    @Test
-    public void testConfigurationChangePropagation() throws Exception {
-        // Builds 3-level vertical hierarchy with one configuration container on each level.
-        // In addition to different overrides on each level, everyone in hierarchy will have one
-        // common overridden value - orientation;
-
-        // Init root's config.
-        final TestConfigurationContainer root = new TestConfigurationContainer();
-        final Configuration rootOverrideConfig = new Configuration();
-        rootOverrideConfig.fontScale = 1.3f;
-        rootOverrideConfig.orientation = SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-        root.onOverrideConfigurationChanged(rootOverrideConfig);
-
-        // Init children.
-        final TestConfigurationContainer child1 = root.addChild();
-        final Configuration childOverrideConfig1 = new Configuration();
-        childOverrideConfig1.densityDpi = 320;
-        childOverrideConfig1.orientation = SCREEN_ORIENTATION_LANDSCAPE;
-        child1.onOverrideConfigurationChanged(childOverrideConfig1);
-
-        final TestConfigurationContainer child2 = child1.addChild();
-        final Configuration childOverrideConfig2 = new Configuration();
-        childOverrideConfig2.screenWidthDp = 150;
-        childOverrideConfig2.orientation = SCREEN_ORIENTATION_PORTRAIT;
-        child2.onOverrideConfigurationChanged(childOverrideConfig2);
-
-        // Check configuration on all levels when root override is updated.
-        rootOverrideConfig.smallestScreenWidthDp = 200;
-        root.onOverrideConfigurationChanged(rootOverrideConfig);
-
-        final Configuration mergedOverrideConfig1 = new Configuration(rootOverrideConfig);
-        mergedOverrideConfig1.updateFrom(childOverrideConfig1);
-        final Configuration mergedConfig1 = new Configuration(mergedOverrideConfig1);
-
-        final Configuration mergedOverrideConfig2 = new Configuration(mergedOverrideConfig1);
-        mergedOverrideConfig2.updateFrom(childOverrideConfig2);
-        final Configuration mergedConfig2 = new Configuration(mergedOverrideConfig2);
-
-        assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
-        assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
-        assertEquals(rootOverrideConfig, root.getConfiguration());
-
-        assertEquals(childOverrideConfig1, child1.getOverrideConfiguration());
-        assertEquals(mergedOverrideConfig1, child1.getMergedOverrideConfiguration());
-        assertEquals(mergedConfig1, child1.getConfiguration());
-
-        assertEquals(childOverrideConfig2, child2.getOverrideConfiguration());
-        assertEquals(mergedOverrideConfig2, child2.getMergedOverrideConfiguration());
-        assertEquals(mergedConfig2, child2.getConfiguration());
-
-        // Check configuration on all levels when root parent config is updated.
-        final Configuration rootParentConfig = new Configuration();
-        rootParentConfig.screenHeightDp = 100;
-        rootParentConfig.orientation = SCREEN_ORIENTATION_REVERSE_PORTRAIT;
-        root.onConfigurationChanged(rootParentConfig);
-        final Configuration mergedRootConfig = new Configuration(rootParentConfig);
-        mergedRootConfig.updateFrom(rootOverrideConfig);
-
-        mergedConfig1.setTo(mergedRootConfig);
-        mergedConfig1.updateFrom(mergedOverrideConfig1);
-
-        mergedConfig2.setTo(mergedConfig1);
-        mergedConfig2.updateFrom(mergedOverrideConfig2);
-
-        assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
-        assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
-        assertEquals(mergedRootConfig, root.getConfiguration());
-
-        assertEquals(childOverrideConfig1, child1.getOverrideConfiguration());
-        assertEquals(mergedOverrideConfig1, child1.getMergedOverrideConfiguration());
-        assertEquals(mergedConfig1, child1.getConfiguration());
-
-        assertEquals(childOverrideConfig2, child2.getOverrideConfiguration());
-        assertEquals(mergedOverrideConfig2, child2.getMergedOverrideConfiguration());
-        assertEquals(mergedConfig2, child2.getConfiguration());
-    }
-
-    @Test
-    public void testSetWindowingMode() throws Exception {
-        final TestConfigurationContainer root = new TestConfigurationContainer();
-        root.setWindowingMode(WINDOWING_MODE_UNDEFINED);
-        final TestConfigurationContainer child = root.addChild();
-        child.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertEquals(WINDOWING_MODE_UNDEFINED, root.getWindowingMode());
-        assertEquals(WINDOWING_MODE_FREEFORM, child.getWindowingMode());
-
-        root.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        assertEquals(WINDOWING_MODE_FULLSCREEN, root.getWindowingMode());
-        assertEquals(WINDOWING_MODE_FREEFORM, child.getWindowingMode());
-    }
-
-    @Test
-    public void testSetActivityType() throws Exception {
-        final TestConfigurationContainer root = new TestConfigurationContainer();
-        root.setActivityType(ACTIVITY_TYPE_UNDEFINED);
-        final TestConfigurationContainer child = root.addChild();
-        child.setActivityType(ACTIVITY_TYPE_STANDARD);
-        assertEquals(ACTIVITY_TYPE_UNDEFINED, root.getActivityType());
-        assertEquals(ACTIVITY_TYPE_STANDARD, child.getActivityType());
-
-        boolean gotException = false;
-        try {
-            // Can't change activity type once set.
-            child.setActivityType(ACTIVITY_TYPE_HOME);
-        } catch (IllegalStateException e) {
-            gotException = true;
-        }
-        assertTrue("Can't change activity type once set.", gotException);
-
-        // TODO: Commenting out for now until we figure-out a good way to test these rules that
-        // should only apply to system process.
-        /*
-        gotException = false;
-        try {
-            // Parent can't change child's activity type once set.
-            root.setActivityType(ACTIVITY_TYPE_HOME);
-        } catch (IllegalStateException e) {
-            gotException = true;
-        }
-        assertTrue("Parent can't change activity type once set.", gotException);
-        assertEquals(ACTIVITY_TYPE_HOME, root.getActivityType());
-
-        final TestConfigurationContainer child2 = new TestConfigurationContainer();
-        child2.setActivityType(ACTIVITY_TYPE_RECENTS);
-
-        gotException = false;
-        try {
-            // Can't re-parent to a different activity type.
-            root.addChild(child2);
-        } catch (IllegalStateException e) {
-            gotException = true;
-        }
-        assertTrue("Can't re-parent to a different activity type.", gotException);
-        */
-
-    }
-
-    @Test
-    public void testRegisterConfigurationChangeListener() throws Exception {
-        final TestConfigurationContainer container = new TestConfigurationContainer();
-        final TestConfigurationChangeListener listener = new TestConfigurationChangeListener();
-        final Configuration config = new Configuration();
-        config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        config.windowConfiguration.setAppBounds(10, 10, 10, 10);
-        container.onOverrideConfigurationChanged(config);
-        container.registerConfigurationChangeListener(listener);
-        // Assert listener got the current config. of the container after it was registered.
-        assertEquals(config, listener.mOverrideConfiguration);
-        // Assert listener gets changes to override configuration.
-        container.onOverrideConfigurationChanged(EMPTY);
-        assertEquals(EMPTY, listener.mOverrideConfiguration);
-    }
-
-    /**
-     * Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed
-     * for testing.
-     */
-    private class TestConfigurationContainer
-            extends ConfigurationContainer<TestConfigurationContainer> {
-        private List<TestConfigurationContainer> mChildren = new ArrayList<>();
-        private TestConfigurationContainer mParent;
-
-        TestConfigurationContainer addChild(TestConfigurationContainer childContainer) {
-            childContainer.mParent = this;
-            childContainer.onParentChanged();
-            mChildren.add(childContainer);
-            return childContainer;
-        }
-
-        TestConfigurationContainer addChild() {
-            return addChild(new TestConfigurationContainer());
-        }
-
-        void removeChild(TestConfigurationContainer child) {
-            child.mParent = null;
-            child.onParentChanged();
-        }
-
-        @Override
-        protected int getChildCount() {
-            return mChildren.size();
-        }
-
-        @Override
-        protected TestConfigurationContainer getChildAt(int index) {
-            return mChildren.get(index);
-        }
-
-        @Override
-        protected ConfigurationContainer getParent() {
-            return mParent;
-        }
-    }
-
-    private class TestConfigurationChangeListener implements ConfigurationContainerListener {
-
-        final Configuration mOverrideConfiguration = new Configuration();
-
-        public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
-            mOverrideConfiguration.setTo(overrideConfiguration);
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
index 21555e3..b6a7cfb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -32,26 +32,22 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
-import androidx.test.runner.AndroidJUnit4;
-
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.wm.DimmerTests;
+ *  atest FrameworksServicesTests:DimmerTests;
  */
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class DimmerTests extends WindowTestsBase {
 
-    private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
+    private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         final SurfaceControl mControl = mock(SurfaceControl.class);
         final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
 
-        TestWindowContainer() {
-            super(sWm);
+        TestWindowContainer(WindowManagerService wm) {
+            super(wm);
         }
 
         @Override
@@ -65,13 +61,13 @@
         }
     }
 
-    private class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
+    private static class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
         final SurfaceSession mSession = new SurfaceSession();
         final SurfaceControl mHostControl = mock(SurfaceControl.class);
         final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class);
 
-        MockSurfaceBuildingContainer() {
-            super(sWm);
+        MockSurfaceBuildingContainer(WindowManagerService wm) {
+            super(wm);
         }
 
         class MockSurfaceBuilder extends SurfaceControl.Builder {
@@ -116,15 +112,14 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-        mHost = new MockSurfaceBuildingContainer();
+        mHost = new MockSurfaceBuildingContainer(mWm);
         mSurfaceAnimatorStarter = spy(new SurfaceAnimatorStarterImpl());
         mTransaction = mock(SurfaceControl.Transaction.class);
         mDimmer = new Dimmer(mHost, mSurfaceAnimatorStarter);
     }
 
     @Test
-    public void testDimAboveNoChildCreatesSurface() throws Exception {
+    public void testDimAboveNoChildCreatesSurface() {
         final float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, alpha);
 
@@ -137,7 +132,7 @@
     }
 
     @Test
-    public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() throws Exception {
+    public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() {
         float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, alpha);
         final SurfaceControl firstSurface = getDimLayer();
@@ -150,7 +145,7 @@
     }
 
     @Test
-    public void testUpdateDimsAppliesSize() throws Exception {
+    public void testUpdateDimsAppliesSize() {
         mDimmer.dimAbove(mTransaction, 0.8f);
 
         int width = 100;
@@ -163,7 +158,7 @@
     }
 
     @Test
-    public void testDimAboveNoChildNotReset() throws Exception {
+    public void testDimAboveNoChildNotReset() {
         mDimmer.dimAbove(mTransaction, 0.8f);
         SurfaceControl dimLayer = getDimLayer();
         mDimmer.resetDimStates();
@@ -174,8 +169,8 @@
     }
 
     @Test
-    public void testDimAboveWithChildCreatesSurfaceAboveChild() throws Exception {
-        TestWindowContainer child = new TestWindowContainer();
+    public void testDimAboveWithChildCreatesSurfaceAboveChild() {
+        TestWindowContainer child = new TestWindowContainer(mWm);
         mHost.addChild(child, 0);
 
         final float alpha = 0.8f;
@@ -189,8 +184,8 @@
     }
 
     @Test
-    public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() throws Exception {
-        TestWindowContainer child = new TestWindowContainer();
+    public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() {
+        TestWindowContainer child = new TestWindowContainer(mWm);
         mHost.addChild(child, 0);
 
         final float alpha = 0.8f;
@@ -204,8 +199,8 @@
     }
 
     @Test
-    public void testDimBelowWithChildSurfaceDestroyedWhenReset() throws Exception {
-        TestWindowContainer child = new TestWindowContainer();
+    public void testDimBelowWithChildSurfaceDestroyedWhenReset() {
+        TestWindowContainer child = new TestWindowContainer(mWm);
         mHost.addChild(child, 0);
 
         final float alpha = 0.8f;
@@ -220,8 +215,8 @@
     }
 
     @Test
-    public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() throws Exception {
-        TestWindowContainer child = new TestWindowContainer();
+    public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() {
+        TestWindowContainer child = new TestWindowContainer(mWm);
         mHost.addChild(child, 0);
 
         final float alpha = 0.8f;
@@ -236,9 +231,9 @@
     }
 
     @Test
-    public void testDimUpdateWhileDimming() throws Exception {
+    public void testDimUpdateWhileDimming() {
         Rect bounds = new Rect();
-        TestWindowContainer child = new TestWindowContainer();
+        TestWindowContainer child = new TestWindowContainer(mWm);
         mHost.addChild(child, 0);
 
         final float alpha = 0.8f;
@@ -258,8 +253,8 @@
     }
 
     @Test
-    public void testRemoveDimImmediately() throws Exception {
-        TestWindowContainer child = new TestWindowContainer();
+    public void testRemoveDimImmediately() {
+        TestWindowContainer child = new TestWindowContainer(mWm);
         mHost.addChild(child, 0);
 
         mDimmer.dimAbove(mTransaction, child, 1);
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index cb8ca7e..f4da4b3 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -53,7 +53,6 @@
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
-import android.util.SparseIntArray;
 import android.view.DisplayCutout;
 import android.view.Gravity;
 import android.view.MotionEvent;
@@ -61,12 +60,10 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -78,16 +75,15 @@
  * Tests for the {@link DisplayContent} class.
  *
  * Build/Install/Run:
- *  atest com.android.server.wm.DisplayContentTests
+ *  atest FrameworksServicesTests:DisplayContentTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class DisplayContentTests extends WindowTestsBase {
 
     @Test
     @FlakyTest(bugId = 77772044)
-    public void testForAllWindows() throws Exception {
+    public void testForAllWindows() {
         final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION,
                 mDisplayContent, "exiting app");
         final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken;
@@ -108,11 +104,11 @@
     }
 
     @Test
-    public void testForAllWindows_WithAppImeTarget() throws Exception {
+    public void testForAllWindows_WithAppImeTarget() {
         final WindowState imeAppTarget =
                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
 
-        sWm.mInputMethodTarget = imeAppTarget;
+        mWm.mInputMethodTarget = imeAppTarget;
 
         assertForAllWindowsOrder(Arrays.asList(
                 mWallpaperWindow,
@@ -128,8 +124,8 @@
     }
 
     @Test
-    public void testForAllWindows_WithChildWindowImeTarget() throws Exception {
-        sWm.mInputMethodTarget = mChildAppWindowAbove;
+    public void testForAllWindows_WithChildWindowImeTarget() {
+        mWm.mInputMethodTarget = mChildAppWindowAbove;
 
         assertForAllWindowsOrder(Arrays.asList(
                 mWallpaperWindow,
@@ -144,8 +140,8 @@
     }
 
     @Test
-    public void testForAllWindows_WithStatusBarImeTarget() throws Exception {
-        sWm.mInputMethodTarget = mStatusBarWindow;
+    public void testForAllWindows_WithStatusBarImeTarget() {
+        mWm.mInputMethodTarget = mStatusBarWindow;
 
         assertForAllWindowsOrder(Arrays.asList(
                 mWallpaperWindow,
@@ -160,7 +156,7 @@
     }
 
     @Test
-    public void testForAllWindows_WithInBetweenWindowToken() throws Exception {
+    public void testForAllWindows_WithInBetweenWindowToken() {
         // This window is set-up to be z-ordered between some windows that go in the same token like
         // the nav bar and status bar.
         final WindowState voiceInteractionWindow = createWindow(null, TYPE_VOICE_INTERACTION,
@@ -180,7 +176,7 @@
     }
 
     @Test
-    public void testComputeImeTarget() throws Exception {
+    public void testComputeImeTarget() {
         // Verify that an app window can be an ime target.
         final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
         appWin.setHasSurface(true);
@@ -203,7 +199,7 @@
      * container references updates.
      */
     @Test
-    public void testMoveStackBetweenDisplays() throws Exception {
+    public void testMoveStackBetweenDisplays() {
         // Create a second display.
         final DisplayContent dc = createNewDisplay();
 
@@ -233,8 +229,7 @@
      * This tests override configuration updates for display content.
      */
     @Test
-    public void testDisplayOverrideConfigUpdate() throws Exception {
-        final int displayId = mDisplayContent.getDisplayId();
+    public void testDisplayOverrideConfigUpdate() {
         final Configuration currentOverrideConfig = mDisplayContent.getOverrideConfiguration();
 
         // Create new, slightly changed override configuration and apply it to the display.
@@ -242,7 +237,7 @@
         newOverrideConfig.densityDpi += 120;
         newOverrideConfig.fontScale += 0.3;
 
-        sWm.setNewDisplayOverrideConfiguration(newOverrideConfig, displayId);
+        mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, mDisplayContent);
 
         // Check that override config is applied.
         assertEquals(newOverrideConfig, mDisplayContent.getOverrideConfiguration());
@@ -252,24 +247,25 @@
      * This tests global configuration updates when default display config is updated.
      */
     @Test
-    public void testDefaultDisplayOverrideConfigUpdate() throws Exception {
-        final Configuration currentConfig = mDisplayContent.getConfiguration();
+    public void testDefaultDisplayOverrideConfigUpdate() {
+        DisplayContent defaultDisplay = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
+        final Configuration currentConfig = defaultDisplay.getConfiguration();
 
         // Create new, slightly changed override configuration and apply it to the display.
         final Configuration newOverrideConfig = new Configuration(currentConfig);
         newOverrideConfig.densityDpi += 120;
         newOverrideConfig.fontScale += 0.3;
 
-        sWm.setNewDisplayOverrideConfiguration(newOverrideConfig, DEFAULT_DISPLAY);
+        mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, defaultDisplay);
 
         // Check that global configuration is updated, as we've updated default display's config.
-        Configuration globalConfig = sWm.mRoot.getConfiguration();
+        Configuration globalConfig = mWm.mRoot.getConfiguration();
         assertEquals(newOverrideConfig.densityDpi, globalConfig.densityDpi);
         assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
 
         // Return back to original values.
-        sWm.setNewDisplayOverrideConfiguration(currentConfig, DEFAULT_DISPLAY);
-        globalConfig = sWm.mRoot.getConfiguration();
+        mWm.setNewDisplayOverrideConfiguration(currentConfig, defaultDisplay);
+        globalConfig = mWm.mRoot.getConfiguration();
         assertEquals(currentConfig.densityDpi, globalConfig.densityDpi);
         assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
     }
@@ -278,8 +274,8 @@
      * Tests tapping on a stack in different display results in window gaining focus.
      */
     @Test
-    public void testInputEventBringsCorrectDisplayInFocus() throws Exception {
-        DisplayContent dc0 = sWm.getDefaultDisplayContentLocked();
+    public void testInputEventBringsCorrectDisplayInFocus() {
+        DisplayContent dc0 = mWm.getDefaultDisplayContentLocked();
         // Create a second display
         final DisplayContent dc1 = createNewDisplay();
 
@@ -303,51 +299,51 @@
         // tap on primary display.
         tapOnDisplay(dc0);
         // Check focus is on primary display.
-        assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
+        assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
                 dc0.findFocusedWindow());
 
         // Tap on secondary display.
         tapOnDisplay(dc1);
         // Check focus is on secondary.
-        assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
+        assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
                 dc1.findFocusedWindow());
     }
 
     @Test
-    public void testFocusedWindowMultipleDisplays() throws Exception {
+    public void testFocusedWindowMultipleDisplays() {
         // Create a focusable window and check that focus is calculated correctly
         final WindowState window1 =
                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1");
         updateFocusedWindow();
         assertTrue(window1.isFocused());
-        assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
+        assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
 
         // Check that a new display doesn't affect focus
         final DisplayContent dc = createNewDisplay();
         updateFocusedWindow();
         assertTrue(window1.isFocused());
-        assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
+        assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
 
         // Add a window to the second display, and it should be focused
         final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2");
         updateFocusedWindow();
         assertTrue(window1.isFocused());
         assertTrue(window2.isFocused());
-        assertEquals(window2, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
+        assertEquals(window2, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
 
         // Move the first window to the to including parents, and make sure focus is updated
         window1.getParent().positionChildAt(POSITION_TOP, window1, true);
         updateFocusedWindow();
         assertTrue(window1.isFocused());
         assertTrue(window2.isFocused());
-        assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
+        assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
     }
 
     /**
      * This tests setting the maximum ui width on a display.
      */
     @Test
-    public void testMaxUiWidth() throws Exception {
+    public void testMaxUiWidth() {
         // Prevent base display metrics for test from being updated to the value of real display.
         final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
         final int baseWidth = 1440;
@@ -439,8 +435,8 @@
     }
 
     @Test
-    public void testDisplayCutout_rot0() throws Exception {
-        synchronized (sWm.getWindowManagerLock()) {
+    public void testDisplayCutout_rot0() {
+        synchronized (mWm.getWindowManagerLock()) {
             final DisplayContent dc = createNewDisplay();
             dc.mInitialDisplayWidth = 200;
             dc.mInitialDisplayHeight = 400;
@@ -458,8 +454,8 @@
     }
 
     @Test
-    public void testDisplayCutout_rot90() throws Exception {
-        synchronized (sWm.getWindowManagerLock()) {
+    public void testDisplayCutout_rot90() {
+        synchronized (mWm.getWindowManagerLock()) {
             // Prevent mInitialDisplayCutout from being updated from real display (e.g. null
             // if the device has no cutout).
             final DisplayContent dc = createDisplayNoUpdateDisplayInfo();
@@ -476,7 +472,8 @@
 
             final Rect r1 = new Rect(left, top, right, bottom);
             final DisplayCutout cutout = new WmDisplayCutout(
-                    fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), null)
+                    fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP),
+                    null)
                     .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
 
             dc.mInitialDisplayCutout = cutout;
@@ -500,8 +497,8 @@
     }
 
     @Test
-    public void testLayoutSeq_assignedDuringLayout() throws Exception {
-        synchronized (sWm.getWindowManagerLock()) {
+    public void testLayoutSeq_assignedDuringLayout() {
+        synchronized (mWm.getWindowManagerLock()) {
 
             final DisplayContent dc = createNewDisplay();
             final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
@@ -533,7 +530,7 @@
         assertEquals("Visible keyguard must influence device orientation",
                 SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation());
 
-        sWm.setKeyguardGoingAway(true);
+        mWm.setKeyguardGoingAway(true);
         assertEquals("Keyguard that is going away must not influence device orientation",
                 SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
     }
@@ -543,10 +540,10 @@
         final DisplayContent dc = createNewDisplay();
 
         assertTrue(dc.mShouldOverrideDisplayConfiguration);
-        sWm.dontOverrideDisplayInfo(dc.getDisplayId());
+        mWm.dontOverrideDisplayInfo(dc.getDisplayId());
 
         assertFalse(dc.mShouldOverrideDisplayConfiguration);
-        verify(sWm.mDisplayManagerInternal, times(1))
+        verify(mWm.mDisplayManagerInternal, times(1))
                 .setDisplayInfoOverrideFromWindowManager(dc.getDisplayId(), null);
     }
 
@@ -572,7 +569,7 @@
     }
 
     private boolean isOptionsPanelAtRight(int displayId) {
-        return (sWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
+        return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
     }
 
     private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth,
@@ -583,8 +580,8 @@
     }
 
     private void updateFocusedWindow() {
-        synchronized (sWm.mWindowMap) {
-            sWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false);
+        synchronized (mWm.mGlobalLock) {
+            mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false);
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
index a028d5ee..f0c49c8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
@@ -12,15 +12,19 @@
  * 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.server.wm;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
 
 import android.app.WindowConfiguration;
 import android.platform.test.annotations.Presubmit;
@@ -28,15 +32,13 @@
 import android.view.DisplayInfo;
 import android.view.Surface;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.policy.WindowManagerPolicy;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.io.File;
 
@@ -44,14 +46,13 @@
  * Tests for the {@link DisplaySettings} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.DisplaySettingsTests
+ *  atest FrameworksServicesTests:DisplaySettingsTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class DisplaySettingsTests extends WindowTestsBase {
 
-    private File mTestFolder;
+    private static final File TEST_FOLDER = getInstrumentation().getTargetContext().getCacheDir();
     private DisplaySettings mTarget;
 
     private DisplayContent mPrimaryDisplay;
@@ -59,22 +60,23 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
+        deleteRecursively(TEST_FOLDER);
 
-        mTestFolder = InstrumentationRegistry.getContext().getCacheDir();
-        deleteRecursively(mTestFolder);
+        mWm.setSupportsFreeformWindowManagement(false);
+        mWm.setIsPc(false);
 
-        sWm.setSupportsFreeformWindowManagement(false);
-        sWm.setIsPc(false);
+        mTarget = new DisplaySettings(mWm, TEST_FOLDER);
 
-        mTarget = new DisplaySettings(sWm, mTestFolder);
-        mTarget.readSettingsLocked();
-
-        mPrimaryDisplay = sWm.getDefaultDisplayContentLocked();
-        mSecondaryDisplay = createNewDisplay();
+        mPrimaryDisplay = mWm.getDefaultDisplayContentLocked();
+        mSecondaryDisplay = mDisplayContent;
         assertNotEquals(Display.DEFAULT_DISPLAY, mSecondaryDisplay.getDisplayId());
     }
 
+    @After
+    public void tearDown() {
+        deleteRecursively(TEST_FOLDER);
+    }
+
     @Test
     public void testPrimaryDisplayDefaultToFullscreenWithoutFreeformSupport() {
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
@@ -85,7 +87,7 @@
 
     @Test
     public void testPrimaryDisplayDefaultToFullscreenWithFreeformSupportNonPc() {
-        sWm.setSupportsFreeformWindowManagement(true);
+        mWm.setSupportsFreeformWindowManagement(true);
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
@@ -95,8 +97,8 @@
 
     @Test
     public void testPrimaryDisplayDefaultToFreeformWithFreeformIsPc() {
-        sWm.setSupportsFreeformWindowManagement(true);
-        sWm.setIsPc(true);
+        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.setIsPc(true);
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
@@ -114,7 +116,7 @@
 
     @Test
     public void testSecondaryDisplayDefaultToFreeformWithFreeformSupportNonPc() {
-        sWm.setSupportsFreeformWindowManagement(true);
+        mWm.setSupportsFreeformWindowManagement(true);
 
         mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
 
@@ -124,8 +126,8 @@
 
     @Test
     public void testSecondaryDisplayDefaultToFreeformWithFreeformSupportIsPc() {
-        sWm.setSupportsFreeformWindowManagement(true);
-        sWm.setIsPc(true);
+        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.setIsPc(true);
 
         mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
 
@@ -134,6 +136,65 @@
     }
 
     @Test
+    public void testDefaultToOriginalMetrics() {
+        final int originalWidth = mSecondaryDisplay.mBaseDisplayWidth;
+        final int originalHeight = mSecondaryDisplay.mBaseDisplayHeight;
+        final int originalDensity = mSecondaryDisplay.mBaseDisplayDensity;
+        final boolean originalScalingDisabled = mSecondaryDisplay.mDisplayScalingDisabled;
+
+        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        assertEquals(originalWidth, mSecondaryDisplay.mBaseDisplayWidth);
+        assertEquals(originalHeight, mSecondaryDisplay.mBaseDisplayHeight);
+        assertEquals(originalDensity, mSecondaryDisplay.mBaseDisplayDensity);
+        assertEquals(originalScalingDisabled, mSecondaryDisplay.mDisplayScalingDisabled);
+    }
+
+    @Test
+    public void testSetForcedSize() {
+        final DisplayInfo originalInfo = new DisplayInfo(mSecondaryDisplay.getDisplayInfo());
+        // Provides the orginal display info to avoid changing initial display size.
+        doAnswer(invocation -> {
+            ((DisplayInfo) invocation.getArguments()[1]).copyFrom(originalInfo);
+            return null;
+        }).when(mWm.mDisplayManagerInternal).getNonOverrideDisplayInfo(anyInt(), any());
+
+        mTarget.setForcedSize(mSecondaryDisplay, 1000 /* width */, 2000 /* height */);
+        applySettingsToDisplayByNewInstance(mSecondaryDisplay);
+
+        assertEquals(1000 /* width */, mSecondaryDisplay.mBaseDisplayWidth);
+        assertEquals(2000 /* height */, mSecondaryDisplay.mBaseDisplayHeight);
+
+        mWm.clearForcedDisplaySize(mSecondaryDisplay.getDisplayId());
+        assertEquals(mSecondaryDisplay.mInitialDisplayWidth, mSecondaryDisplay.mBaseDisplayWidth);
+        assertEquals(mSecondaryDisplay.mInitialDisplayHeight, mSecondaryDisplay.mBaseDisplayHeight);
+    }
+
+    @Test
+    public void testSetForcedDensity() {
+        mTarget.setForcedDensity(mSecondaryDisplay, 600 /* density */, 0 /* userId */);
+        applySettingsToDisplayByNewInstance(mSecondaryDisplay);
+
+        assertEquals(600 /* density */, mSecondaryDisplay.mBaseDisplayDensity);
+
+        mWm.clearForcedDisplayDensityForUser(mSecondaryDisplay.getDisplayId(), 0 /* userId */);
+        assertEquals(mSecondaryDisplay.mInitialDisplayDensity,
+                mSecondaryDisplay.mBaseDisplayDensity);
+    }
+
+    @Test
+    public void testSetForcedScalingMode() {
+        mTarget.setForcedScalingMode(mSecondaryDisplay, DisplayContent.FORCE_SCALING_MODE_DISABLED);
+        applySettingsToDisplayByNewInstance(mSecondaryDisplay);
+
+        assertTrue(mSecondaryDisplay.mDisplayScalingDisabled);
+
+        mWm.setForcedDisplayScalingMode(mSecondaryDisplay.getDisplayId(),
+                DisplayContent.FORCE_SCALING_MODE_AUTO);
+        assertFalse(mSecondaryDisplay.mDisplayScalingDisabled);
+    }
+
+    @Test
     public void testDefaultToZeroOverscan() {
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
@@ -143,8 +204,7 @@
     @Test
     public void testPersistOverscanInSameInstance() {
         final DisplayInfo info = mPrimaryDisplay.getDisplayInfo();
-        mTarget.setOverscanLocked(info.uniqueId, info.name, 1 /* left */, 2 /* top */,
-                3 /* right */, 4 /* bottom */);
+        mTarget.setOverscanLocked(info, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
@@ -154,14 +214,9 @@
     @Test
     public void testPersistOverscanAcrossInstances() {
         final DisplayInfo info = mPrimaryDisplay.getDisplayInfo();
-        mTarget.setOverscanLocked(info.uniqueId, info.name, 1 /* left */, 2 /* top */,
-                3 /* right */, 4 /* bottom */);
-        mTarget.writeSettingsLocked();
+        mTarget.setOverscanLocked(info, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
 
-        DisplaySettings target = new DisplaySettings(sWm, mTestFolder);
-        target.readSettingsLocked();
-
-        target.applySettingsToDisplayLocked(mPrimaryDisplay);
+        applySettingsToDisplayByNewInstance(mPrimaryDisplay);
 
         assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
     }
@@ -208,12 +263,8 @@
     public void testPersistUserRotationModeAcrossInstances() {
         mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
                 Surface.ROTATION_270);
-        mTarget.writeSettingsLocked();
 
-        DisplaySettings target = new DisplaySettings(sWm, mTestFolder);
-        target.readSettingsLocked();
-
-        target.applySettingsToDisplayLocked(mSecondaryDisplay);
+        applySettingsToDisplayByNewInstance(mSecondaryDisplay);
 
         final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation();
         assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation.getUserRotationMode());
@@ -225,7 +276,7 @@
         mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
                 Surface.ROTATION_270);
 
-        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+        applySettingsToDisplayByNewInstance(mSecondaryDisplay);
 
         assertEquals(Surface.ROTATION_270,
                 mSecondaryDisplay.getDisplayRotation().getUserRotation());
@@ -241,17 +292,26 @@
         assertEquals(bottom, info.overscanBottom);
     }
 
+    /**
+     * This method helps to ensure read and write persistent settings successfully because the
+     * constructor of {@link DisplaySettings} should read the persistent file from the given path
+     * that also means the previous state must be written correctly.
+     */
+    private void applySettingsToDisplayByNewInstance(DisplayContent display) {
+        new DisplaySettings(mWm, TEST_FOLDER).applySettingsToDisplayLocked(display);
+    }
+
     private static boolean deleteRecursively(File file) {
+        boolean fullyDeleted = true;
         if (file.isFile()) {
             return file.delete();
+        } else if (file.isDirectory()) {
+            final File[] files = file.listFiles();
+            for (File child : files) {
+                fullyDeleted &= deleteRecursively(child);
+            }
+            fullyDeleted &= file.delete();
         }
-
-        boolean fullyDeleted = true;
-        final File[] files = file.listFiles();
-        for (File child : files) {
-            fullyDeleted &= deleteRecursively(child);
-        }
-        fullyDeleted &= file.delete();
         return fullyDeleted;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
index 3c8b2a0..55e766d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -40,23 +40,24 @@
 import android.view.SurfaceSession;
 import android.view.View;
 
-import com.android.internal.annotations.GuardedBy;
+import androidx.test.filters.SmallTest;
+
 import com.android.server.LocalServices;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import androidx.test.filters.SmallTest;
-
 /**
  * Tests for the {@link DragDropController} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.DragDropControllerTests
+ *  atest FrameworksServicesTests:DragDropControllerTests
  */
 @SmallTest
 @Presubmit
@@ -67,7 +68,6 @@
     private IBinder mToken;
 
     static class TestDragDropController extends DragDropController {
-        @GuardedBy("sWm.mWindowMap")
         private Runnable mCloseCallback;
 
         TestDragDropController(WindowManagerService service, Looper looper) {
@@ -107,31 +107,34 @@
         return window;
     }
 
-    @Override
-    @Before
-    public void setUp() throws Exception {
+    @BeforeClass
+    public static void setUpOnce() {
         final UserManagerInternal userManager = mock(UserManagerInternal.class);
         LocalServices.addService(UserManagerInternal.class, userManager);
+    }
 
-        super.setUp();
+    @AfterClass
+    public static void tearDownOnce() {
+        LocalServices.removeServiceForTest(UserManagerInternal.class);
+    }
 
-        mTarget = new TestDragDropController(sWm, sWm.mH.getLooper());
+    @Before
+    public void setUp() throws Exception {
+        mTarget = new TestDragDropController(mWm, mWm.mH.getLooper());
         mDisplayContent = spy(mDisplayContent);
         mWindow = createDropTargetWindow("Drag test window", 0);
         doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
-        when(sWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);
+        when(mWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);
 
-        synchronized (sWm.mWindowMap) {
-            sWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
+        synchronized (mWm.mGlobalLock) {
+            mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
         }
     }
 
-    @Override
     @After
     public void tearDown() throws Exception {
-        LocalServices.removeServiceForTest(UserManagerInternal.class);
         final CountDownLatch latch;
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             if (!mTarget.dragDropActiveLocked()) {
                 return;
             }
@@ -142,8 +145,6 @@
             mTarget.setOnClosedCallbackLocked(latch::countDown);
         }
         assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-
-        super.tearDown();
     }
 
     @Test
@@ -174,7 +175,7 @@
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
 
-            assertTrue(sWm.mInputManager.transferTouchFocus(null, null));
+            assertTrue(mWm.mInputManager.transferTouchFocus(null, null));
             mToken = mTarget.performDrag(
                     new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0,
                     data);
diff --git a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
index 7222a99..1fae317 100644
--- a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
 
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -16,17 +32,18 @@
 import android.view.IPinnedStackListener;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+/**
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:PinnedStackControllerTest
+ */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class PinnedStackControllerTest extends WindowTestsBase {
 
     @Mock private IPinnedStackListener mIPinnedStackListener;
@@ -34,15 +51,15 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
+
         when(mIPinnedStackListener.asBinder()).thenReturn(mIPinnedStackListenerStub);
     }
 
     @Test
     public void setShelfHeight_shelfVisibilityChangedTriggered() throws RemoteException {
-        sWm.mSupportsPictureInPicture = true;
-        sWm.registerPinnedStackListener(DEFAULT_DISPLAY, mIPinnedStackListener);
+        mWm.mSupportsPictureInPicture = true;
+        mWm.registerPinnedStackListener(DEFAULT_DISPLAY, mIPinnedStackListener);
 
         verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0);
         verify(mIPinnedStackListener).onShelfVisibilityChanged(false, 0);
@@ -55,7 +72,7 @@
 
         final int SHELF_HEIGHT = 300;
 
-        sWm.setShelfHeight(true, SHELF_HEIGHT);
+        mWm.setShelfHeight(true, SHELF_HEIGHT);
         verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT);
         verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false),
                 eq(true), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 088e229..fe5fc06 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -24,9 +24,9 @@
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
 
-import static org.junit.Assert.fail;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.verify;
@@ -42,22 +42,20 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 /**
- * atest FrameworksServicesTests:com.android.server.wm.RecentsAnimationControllerTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:RecentsAnimationControllerTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class RecentsAnimationControllerTest extends WindowTestsBase {
 
     @Mock SurfaceControl mMockLeash;
@@ -69,10 +67,10 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
+
         when(mMockRunner.asBinder()).thenReturn(new Binder());
-        mController = new RecentsAnimationController(sWm, mMockRunner, mAnimationCallbacks,
+        mController = new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
                 DEFAULT_DISPLAY);
     }
 
@@ -96,7 +94,7 @@
     }
 
     @Test
-    public void testCancelAfterRemove_expectIgnored() throws Exception {
+    public void testCancelAfterRemove_expectIgnored() {
         final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         AnimationAdapter adapter = mController.addAnimation(appWindow.getTask(),
@@ -114,10 +112,10 @@
         }
     }
 
-    @Test
     @FlakyTest(bugId = 117117823)
-    public void testIncludedApps_expectTargetAndVisible() throws Exception {
-        sWm.setRecentsAnimationController(mController);
+    @Test
+    public void testIncludedApps_expectTargetAndVisible() {
+        mWm.setRecentsAnimationController(mController);
         final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
         final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 7d19baa..fa53795 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -39,7 +39,6 @@
 import android.view.SurfaceControl.Transaction;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.testutils.OffsettableClock;
 import com.android.server.testutils.TestHandler;
@@ -47,17 +46,16 @@
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 /**
- * atest FrameworksServicesTests:com.android.server.wm.RemoteAnimationControllerTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:RemoteAnimationControllerTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class RemoteAnimationControllerTest extends WindowTestsBase {
 
     @Mock SurfaceControl mMockLeash;
@@ -71,27 +69,25 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
+
         when(mMockRunner.asBinder()).thenReturn(new Binder());
         mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
         mAdapter.setCallingPid(123);
-        sWm.mH.runWithScissors(() -> {
-            mHandler = new TestHandler(null, mClock);
-        }, 0);
-        mController = new RemoteAnimationController(sWm, mAdapter, mHandler);
+        mWm.mH.runWithScissors(() -> mHandler = new TestHandler(null, mClock), 0);
+        mController = new RemoteAnimationController(mWm, mAdapter, mHandler);
     }
 
     @Test
     public void testRun() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
-        sWm.mOpeningApps.add(win.mAppToken);
+        mDisplayContent.mOpeningApps.add(win.mAppToken);
         try {
             final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
                     new Point(50, 100), new Rect(50, 100, 150, 150));
             adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
             mController.goodToGo();
-            sWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
@@ -113,7 +109,7 @@
             finishedCaptor.getValue().onAnimationFinished();
             verify(mFinishedCallback).onAnimationFinished(eq(adapter));
         } finally {
-            sWm.mOpeningApps.clear();
+            mDisplayContent.mOpeningApps.clear();
         }
     }
 
@@ -146,7 +142,7 @@
 
     @Test
     public void testTimeout_scaled() throws Exception {
-        sWm.setAnimationScale(2, 5.0f);
+        mWm.setAnimationScale(2, 5.0f);
         try{
             final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
             final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
@@ -165,19 +161,19 @@
             verify(mMockRunner).onAnimationCancelled();
             verify(mFinishedCallback).onAnimationFinished(eq(adapter));
         } finally {
-            sWm.setAnimationScale(2, 1.0f);
+            mWm.setAnimationScale(2, 1.0f);
         }
 
     }
 
     @Test
-    public void testZeroAnimations() throws Exception {
+    public void testZeroAnimations() {
         mController.goodToGo();
         verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
     }
 
     @Test
-    public void testNotReallyStarted() throws Exception {
+    public void testNotReallyStarted() {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mController.createAnimationAdapter(win.mAppToken,
                 new Point(50, 100), new Rect(50, 100, 150, 150));
@@ -195,7 +191,7 @@
                 new Point(50, 100), new Rect(50, 100, 150, 150));
         adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
         mController.goodToGo();
-        sWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
         final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                 ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
         final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
@@ -206,7 +202,7 @@
     }
 
     @Test
-    public void testRemovedBeforeStarted() throws Exception {
+    public void testRemovedBeforeStarted() {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
                 new Point(50, 100), new Rect(50, 100, 150, 150));
diff --git a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
deleted file mode 100644
index c1db6a6..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.android.server.wm;
-
-import static org.junit.Assert.assertTrue;
-
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link RootWindowContainer} class.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.RootWindowContainerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class RootWindowContainerTests extends WindowTestsBase {
-    @Test
-    public void testSetDisplayOverrideConfigurationIfNeeded() throws Exception {
-        synchronized (sWm.mWindowMap) {
-            // Add first stack we expect to be updated with configuration change.
-            final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
-            stack.getOverrideConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 5, 5));
-
-            // Add second task that will be set for deferred removal that should not be returned
-            // with the configuration change.
-            final TaskStack deferredDeletedStack = createTaskStackOnDisplay(mDisplayContent);
-            deferredDeletedStack.getOverrideConfiguration().windowConfiguration.setBounds(
-                    new Rect(0, 0, 5, 5));
-            deferredDeletedStack.mDeferRemoval = true;
-
-            final Configuration override = new Configuration(
-                    mDisplayContent.getOverrideConfiguration());
-            override.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
-
-            // Set display override.
-            final int[] results = sWm.mRoot.setDisplayOverrideConfigurationIfNeeded(override,
-                    mDisplayContent.getDisplayId());
-
-            // Ensure only first stack is returned.
-            assertTrue(results.length == 1);
-            assertTrue(results[0] == stack.mStackId);
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
deleted file mode 100644
index ae40f7e..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.wm;
-
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.graphics.Color.RED;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Gravity.BOTTOM;
-import static android.view.Gravity.LEFT;
-import static android.view.Gravity.RIGHT;
-import static android.view.Gravity.TOP;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static org.junit.Assert.assertEquals;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
-import android.media.ImageReader;
-import android.os.Handler;
-import android.platform.test.annotations.Presubmit;
-import android.util.Pair;
-import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.widget.TextView;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.function.BooleanSupplier;
-
-/**
- * Tests for the {@link android.view.WindowManager.LayoutParams#PRIVATE_FLAG_IS_SCREEN_DECOR} flag.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.ScreenDecorWindowTests
- */
-// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags.
-// TODO: Test non-Activity windows.
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class ScreenDecorWindowTests {
-
-    private final Context mContext = InstrumentationRegistry.getTargetContext();
-    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
-
-    private WindowManager mWm;
-    private ArrayList<View> mWindows = new ArrayList<>();
-
-    private Activity mTestActivity;
-    private VirtualDisplay mDisplay;
-    private ImageReader mImageReader;
-
-    private int mDecorThickness;
-    private int mHalfDecorThickness;
-
-    @Before
-    public void setUp() {
-        final Pair<VirtualDisplay, ImageReader> result = createDisplay();
-        mDisplay = result.first;
-        mImageReader = result.second;
-        final Display display = mDisplay.getDisplay();
-        final Context dContext = mContext.createDisplayContext(display);
-        mWm = dContext.getSystemService(WindowManager.class);
-        mTestActivity = startActivityOnDisplay(TestActivity.class, display.getDisplayId());
-        final Point size = new Point();
-        mDisplay.getDisplay().getRealSize(size);
-        mDecorThickness = Math.min(size.x, size.y) / 3;
-        mHalfDecorThickness = mDecorThickness / 2;
-    }
-
-    @After
-    public void tearDown() {
-        while (!mWindows.isEmpty()) {
-            removeWindow(mWindows.get(0));
-        }
-        finishActivity(mTestActivity);
-        mDisplay.release();
-        mImageReader.close();
-    }
-
-    @Test
-    public void testScreenSides() throws Exception {
-        // Decor on top
-        final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
-        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
-
-        // Decor at the bottom
-        updateWindow(decorWindow, BOTTOM, MATCH_PARENT, mDecorThickness, 0, 0);
-        assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mDecorThickness);
-
-        // Decor to the left
-        updateWindow(decorWindow, LEFT, mDecorThickness, MATCH_PARENT, 0, 0);
-        assertInsetGreaterOrEqual(mTestActivity, LEFT, mDecorThickness);
-
-        // Decor to the right
-        updateWindow(decorWindow, RIGHT, mDecorThickness, MATCH_PARENT, 0, 0);
-        assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness);
-    }
-
-    @Test
-    public void testMultipleDecors() throws Exception {
-        // Test 2 decor windows on-top.
-        createDecorWindow(TOP, MATCH_PARENT, mHalfDecorThickness);
-        assertInsetGreaterOrEqual(mTestActivity, TOP, mHalfDecorThickness);
-        createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
-        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
-
-        // And one at the bottom.
-        createDecorWindow(BOTTOM, MATCH_PARENT, mHalfDecorThickness);
-        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
-        assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mHalfDecorThickness);
-    }
-
-    @Test
-    public void testFlagChange() throws Exception {
-        WindowInsets initialInsets = getInsets(mTestActivity);
-
-        final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
-        assertTopInsetEquals(mTestActivity, mDecorThickness);
-
-        updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
-                0, PRIVATE_FLAG_IS_SCREEN_DECOR);
-
-        // TODO: fix test and re-enable assertion.
-        // initialInsets was not actually immutable and just updated to the current insets,
-        // meaning this assertion never actually tested anything. Now that WindowInsets actually is
-        // immutable, it turns out the test was broken.
-        // assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
-
-        updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
-                PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_IS_SCREEN_DECOR);
-        assertTopInsetEquals(mTestActivity, mDecorThickness);
-    }
-
-    @Test
-    public void testRemoval() throws Exception {
-        WindowInsets initialInsets = getInsets(mTestActivity);
-
-        final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
-        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
-
-        removeWindow(decorWindow);
-        assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
-    }
-
-    private View createDecorWindow(int gravity, int width, int height) {
-        return createWindow("decorWindow", gravity, width, height, RED,
-                FLAG_LAYOUT_IN_SCREEN, PRIVATE_FLAG_IS_SCREEN_DECOR);
-    }
-
-    private View createWindow(String name, int gravity, int width, int height, int color, int flags,
-            int privateFlags) {
-
-        final View[] viewHolder = new View[1];
-        final int finalFlag = flags
-                | FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_TOUCHABLE;
-
-        // Needs to run on the UI thread.
-        Handler.getMain().runWithScissors(() -> {
-            final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                    width, height, TYPE_APPLICATION_OVERLAY, finalFlag, PixelFormat.OPAQUE);
-            lp.gravity = gravity;
-            lp.privateFlags |= privateFlags;
-
-            final TextView view = new TextView(mContext);
-            view.setText("ScreenDecorWindowTests - " + name);
-            view.setBackgroundColor(color);
-            mWm.addView(view, lp);
-            mWindows.add(view);
-            viewHolder[0] = view;
-        }, 0);
-
-        waitForIdle();
-        return viewHolder[0];
-    }
-
-    private void updateWindow(View v, int gravity, int width, int height,
-            int privateFlags, int privateFlagsMask) {
-        // Needs to run on the UI thread.
-        Handler.getMain().runWithScissors(() -> {
-            final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) v.getLayoutParams();
-            lp.gravity = gravity;
-            lp.width = width;
-            lp.height = height;
-            setPrivateFlags(lp, privateFlags, privateFlagsMask);
-
-            mWm.updateViewLayout(v, lp);
-        }, 0);
-
-        waitForIdle();
-    }
-
-    private void removeWindow(View v) {
-        Handler.getMain().runWithScissors(() -> mWm.removeView(v), 0);
-        mWindows.remove(v);
-        waitForIdle();
-    }
-
-    private WindowInsets getInsets(Activity a) {
-        return new WindowInsets(a.getWindow().getDecorView().getRootWindowInsets());
-    }
-
-    /**
-     * Set the flags of the window, as per the
-     * {@link WindowManager.LayoutParams WindowManager.LayoutParams}
-     * flags.
-     *
-     * @param flags The new window flags (see WindowManager.LayoutParams).
-     * @param mask Which of the window flag bits to modify.
-     */
-    public void setPrivateFlags(WindowManager.LayoutParams lp, int flags, int mask) {
-        lp.flags = (lp.flags & ~mask) | (flags & mask);
-    }
-
-    /**
-     * Asserts the top inset of {@param activity} is equal to {@param expected} waiting as needed.
-     */
-    private void assertTopInsetEquals(Activity activity, int expected) throws Exception {
-        waitForTopInsetEqual(activity, expected);
-        assertEquals(expected, getInsets(activity).getSystemWindowInsetTop());
-    }
-
-    private void waitForTopInsetEqual(Activity activity, int expected) {
-        waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected);
-    }
-
-    /**
-     * Asserts the inset at {@param side} of {@param activity} is equal to {@param expected}
-     * waiting as needed.
-     */
-    private void assertInsetGreaterOrEqual(Activity activity, int side, int expected)
-            throws Exception {
-        waitForInsetGreaterOrEqual(activity, side, expected);
-
-        final WindowInsets insets = getInsets(activity);
-        switch (side) {
-            case TOP: assertGreaterOrEqual(insets.getSystemWindowInsetTop(), expected); break;
-            case BOTTOM: assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), expected); break;
-            case LEFT: assertGreaterOrEqual(insets.getSystemWindowInsetLeft(), expected); break;
-            case RIGHT: assertGreaterOrEqual(insets.getSystemWindowInsetRight(), expected); break;
-        }
-    }
-
-    private void waitForInsetGreaterOrEqual(Activity activity, int side, int expected) {
-        waitFor(() -> {
-            final WindowInsets insets = getInsets(activity);
-            switch (side) {
-                case TOP: return insets.getSystemWindowInsetTop() >= expected;
-                case BOTTOM: return insets.getSystemWindowInsetBottom() >= expected;
-                case LEFT: return insets.getSystemWindowInsetLeft() >= expected;
-                case RIGHT: return insets.getSystemWindowInsetRight() >= expected;
-                default: return true;
-            }
-        });
-    }
-
-    /** Asserts that the first entry is greater than or equal to the second entry. */
-    private void assertGreaterOrEqual(int first, int second) throws Exception {
-        Assert.assertTrue("Excepted " + first + " >= " + second, first >= second);
-    }
-
-    private void waitFor(BooleanSupplier waitCondition) {
-        int retriesLeft = 5;
-        do {
-            if (waitCondition.getAsBoolean()) {
-                break;
-            }
-            try {
-                Thread.sleep(500);
-            } catch (InterruptedException e) {
-                // Well I guess we are not waiting...
-            }
-        } while (retriesLeft-- > 0);
-    }
-
-    private void finishActivity(Activity a) {
-        if (a == null) {
-            return;
-        }
-        a.finish();
-        waitForIdle();
-    }
-
-    private void waitForIdle() {
-        mInstrumentation.waitForIdleSync();
-    }
-
-    private Activity startActivityOnDisplay(Class<?> cls, int displayId) {
-        final Intent intent = new Intent(mContext, cls);
-        intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(displayId);
-        final Activity activity = mInstrumentation.startActivitySync(intent, options.toBundle());
-        waitForIdle();
-
-        assertEquals(displayId, activity.getDisplayId());
-        return activity;
-    }
-
-    private Pair<VirtualDisplay, ImageReader> createDisplay() {
-        final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
-        final DisplayInfo displayInfo = new DisplayInfo();
-        final Display defaultDisplay = dm.getDisplay(DEFAULT_DISPLAY);
-        defaultDisplay.getDisplayInfo(displayInfo);
-        final String name = "ScreenDecorWindowTests";
-        int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
-
-        final ImageReader imageReader = ImageReader.newInstance(
-                displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
-
-        final VirtualDisplay display = dm.createVirtualDisplay(name, displayInfo.logicalWidth,
-                displayInfo.logicalHeight, displayInfo.logicalDensityDpi, imageReader.getSurface(),
-                flags);
-
-        return Pair.create(display, imageReader);
-    }
-
-    public static class TestActivity extends Activity {
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
index 9f2645c..ce5b13c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -25,10 +25,8 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link StackWindowController}.
@@ -38,10 +36,9 @@
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class StackWindowControllerTests extends WindowTestsBase {
     @Test
-    public void testRemoveContainer() throws Exception {
+    public void testRemoveContainer() {
         final StackWindowController stackController =
                 createStackControllerOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -61,7 +58,7 @@
     }
 
     @Test
-    public void testRemoveContainer_deferRemoval() throws Exception {
+    public void testRemoveContainer_deferRemoval() {
         final StackWindowController stackController =
                 createStackControllerOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -87,7 +84,7 @@
     }
 
     @Test
-    public void testReparent() throws Exception {
+    public void testReparent() {
         // Create first stack on primary display.
         final StackWindowController stack1Controller =
                 createStackControllerOnDisplay(mDisplayContent);
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 4551c36..584f269 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -27,6 +27,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static java.util.concurrent.TimeUnit.SECONDS;
+
 import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
 import android.animation.ValueAnimator;
 import android.graphics.Matrix;
@@ -42,30 +44,27 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import static java.util.concurrent.TimeUnit.SECONDS;
-
 import java.util.concurrent.CountDownLatch;
 
 /**
  * Test class for {@link SurfaceAnimationRunner}.
  *
- * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimationRunnerTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:SurfaceAnimationRunnerTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class SurfaceAnimationRunnerTest extends WindowTestsBase {
 
     @Mock SurfaceControl mMockSurface;
@@ -79,7 +78,8 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
+        MockitoAnnotations.initMocks(this);
+
         mFinishCallbackLatch = new CountDownLatch(1);
         mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */, null,
                 mMockTransaction, mMockPowerManager);
@@ -112,7 +112,7 @@
     }
 
     @Test
-    public void testCancel_notStarted() throws Exception {
+    public void testCancel_notStarted() {
         mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
                 mMockTransaction, mMockPowerManager);
         mSurfaceAnimationRunner
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 7b5e8de..6833dc5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -35,14 +35,13 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.SurfaceAnimator.Animatable;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -50,11 +49,11 @@
 /**
  * Test class for {@link SurfaceAnimatorTest}.
  *
- * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimatorTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:SurfaceAnimatorTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class SurfaceAnimatorTest extends WindowTestsBase {
 
     @Mock AnimationAdapter mSpec;
@@ -68,15 +67,24 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
-        mAnimatable = new MyAnimatable();
-        mAnimatable2 = new MyAnimatable();
-        mDeferFinishAnimatable = new DeferFinishAnimatable();
+
+        mAnimatable = new MyAnimatable(mWm, mSession, mTransaction);
+        mAnimatable2 = new MyAnimatable(mWm, mSession, mTransaction);
+        mDeferFinishAnimatable = new DeferFinishAnimatable(mWm, mSession, mTransaction);
+    }
+
+    @After
+    public void tearDown() {
+        mAnimatable = null;
+        mAnimatable2 = null;
+        mDeferFinishAnimatable = null;
+        mSession.kill();
+        mSession = null;
     }
 
     @Test
-    public void testRunAnimation() throws Exception {
+    public void testRunAnimation() {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
                 OnAnimationFinishedCallback.class);
@@ -92,7 +100,7 @@
     }
 
     @Test
-    public void testOverrideAnimation() throws Exception {
+    public void testOverrideAnimation() {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         final SurfaceControl firstLeash = mAnimatable.mLeash;
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
@@ -117,7 +125,7 @@
     }
 
     @Test
-    public void testCancelAnimation() throws Exception {
+    public void testCancelAnimation() {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         assertAnimating(mAnimatable);
         mAnimatable.mSurfaceAnimator.cancelAnimation();
@@ -128,7 +136,7 @@
     }
 
     @Test
-    public void testDelayingAnimationStart() throws Exception {
+    public void testDelayingAnimationStart() {
         mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         verifyZeroInteractions(mSpec);
@@ -139,7 +147,7 @@
     }
 
     @Test
-    public void testDelayingAnimationStartAndCancelled() throws Exception {
+    public void testDelayingAnimationStartAndCancelled() {
         mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         mAnimatable.mSurfaceAnimator.cancelAnimation();
@@ -150,7 +158,7 @@
     }
 
     @Test
-    public void testTransferAnimation() throws Exception {
+    public void testTransferAnimation() {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
 
         final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
@@ -171,7 +179,7 @@
 
     @Test
     @FlakyTest(detail = "Promote once confirmed non-flaky")
-    public void testDeferFinish() throws Exception {
+    public void testDeferFinish() {
 
         // Start animation
         mDeferFinishAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec,
@@ -186,7 +194,7 @@
         assertAnimating(mDeferFinishAnimatable);
 
         // Now end defer finishing.
-        mDeferFinishAnimatable.endDeferFinishCallback.run();
+        mDeferFinishAnimatable.mEndDeferFinishCallback.run();
         assertNotAnimating(mAnimatable2);
         assertTrue(mDeferFinishAnimatable.mFinishedCallbackCalled);
         verify(mTransaction).destroy(eq(mDeferFinishAnimatable.mLeash));
@@ -202,26 +210,30 @@
         assertNull(animatable.mSurfaceAnimator.getAnimation());
     }
 
-    private class MyAnimatable implements Animatable {
+    private static class MyAnimatable implements Animatable {
 
+        private final SurfaceSession mSession;
+        private final Transaction mTransaction;
         final SurfaceControl mParent;
         final SurfaceControl mSurface;
         final SurfaceAnimator mSurfaceAnimator;
         SurfaceControl mLeash;
         boolean mFinishedCallbackCalled;
 
-        MyAnimatable() {
-            mParent = sWm.makeSurfaceBuilder(mSession)
+        MyAnimatable(WindowManagerService wm, SurfaceSession session, Transaction transaction) {
+            mSession = session;
+            mTransaction = transaction;
+            mParent = wm.makeSurfaceBuilder(mSession)
                     .setName("test surface parent")
                     .setSize(3000, 3000)
                     .build();
-            mSurface = sWm.makeSurfaceBuilder(mSession)
+            mSurface = wm.makeSurfaceBuilder(mSession)
                     .setName("test surface")
                     .setSize(1, 1)
                     .build();
             mFinishedCallbackCalled = false;
             mLeash = null;
-            mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, sWm);
+            mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, wm);
         }
 
         @Override
@@ -281,13 +293,18 @@
         private final Runnable mFinishedCallback = () -> mFinishedCallbackCalled = true;
     }
 
-    private class DeferFinishAnimatable extends MyAnimatable {
+    private static class DeferFinishAnimatable extends MyAnimatable {
 
-        Runnable endDeferFinishCallback;
+        Runnable mEndDeferFinishCallback;
+
+        DeferFinishAnimatable(WindowManagerService wm, SurfaceSession session,
+                Transaction transaction) {
+            super(wm, session, transaction);
+        }
 
         @Override
         public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
-            this.endDeferFinishCallback = endDeferFinishCallback;
+            mEndDeferFinishCallback = endDeferFinishCallback;
             return true;
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
index 17b7fc1..785b955 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -35,24 +35,22 @@
 import android.view.Display;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
 /**
  * Tests for the {@link TaskPositioner} class.
  *
- * runtest frameworks-services -c com.android.server.wm.TaskPositionerTests
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:TaskPositionerTests
  */
 @SmallTest
-@RunWith(AndroidJUnit4.class)
 public class TaskPositionerTests extends WindowTestsBase {
 
-    private final boolean DEBUGGING = false;
-    private final String TAG = "TaskPositionerTest";
+    private static final boolean DEBUGGING = false;
+    private static final String TAG = "TaskPositionerTest";
 
     private final static int MOUSE_DELTA_X = 5;
     private final static int MOUSE_DELTA_Y = 5;
@@ -65,8 +63,6 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-
         TaskPositioner.setFactory(null);
 
         final Display display = mDisplayContent.getDisplay();
@@ -77,7 +73,7 @@
         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm);
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
 
-        mPositioner = new TaskPositioner(sWm, Mockito.mock(IActivityTaskManager.class));
+        mPositioner = new TaskPositioner(mWm, Mockito.mock(IActivityTaskManager.class));
         mPositioner.register(mDisplayContent);
 
         mWindow = Mockito.spy(createWindow(null, TYPE_BASE_APPLICATION, "window"));
@@ -94,7 +90,7 @@
     }
 
     @Test
-    public void testOverrideFactory() throws Exception {
+    public void testOverrideFactory() {
         final boolean[] created = new boolean[1];
         created[0] = false;
         TaskPositioner.setFactory(new TaskPositioner.Factory() {
@@ -105,7 +101,7 @@
             }
         });
 
-        assertNull(TaskPositioner.create(sWm));
+        assertNull(TaskPositioner.create(mWm));
         assertTrue(created[0]);
     }
 
@@ -114,7 +110,7 @@
      * as does some basic tests (e.g. dragging in Y only will keep X stable).
      */
     @Test
-    public void testBasicFreeWindowResizing() throws Exception {
+    public void testBasicFreeWindowResizing() {
         final Rect r = new Rect(100, 220, 700, 520);
         final int midY = (r.top + r.bottom) / 2;
         mDimBounds.set(r);
@@ -175,7 +171,7 @@
      * This tests that by dragging any edge, the fixed / opposite edge(s) remains anchored.
      */
     @Test
-    public void testFreeWindowResizingTestAllEdges() throws Exception {
+    public void testFreeWindowResizingTestAllEdges() {
         final Rect r = new Rect(100, 220, 700, 520);
         final int midX = (r.left + r.right) / 2;
         final int midY = (r.top + r.bottom) / 2;
@@ -260,7 +256,7 @@
      * right things upon resizing when dragged from the top left corner.
      */
     @Test
-    public void testLandscapePreservedWindowResizingDragTopLeft() throws Exception {
+    public void testLandscapePreservedWindowResizingDragTopLeft() {
         final Rect r = new Rect(100, 220, 700, 520);
         mDimBounds.set(r);
 
@@ -298,7 +294,7 @@
      * right things upon resizing when dragged from the left corner.
      */
     @Test
-    public void testLandscapePreservedWindowResizingDragLeft() throws Exception {
+    public void testLandscapePreservedWindowResizingDragLeft() {
         final Rect r = new Rect(100, 220, 700, 520);
         final int midY = (r.top + r.bottom) / 2;
         mDimBounds.set(r);
@@ -339,7 +335,7 @@
      * right things upon resizing when dragged from the top corner.
      */
     @Test
-    public void testLandscapePreservedWindowResizingDragTop() throws Exception {
+    public void testLandscapePreservedWindowResizingDragTop() {
         final Rect r = new Rect(100, 220, 700, 520);
         final int midX = (r.left + r.right) / 2;
         mDimBounds.set(r);
@@ -376,7 +372,7 @@
      * right things upon resizing when dragged from the top left corner.
      */
     @Test
-    public void testPortraitPreservedWindowResizingDragTopLeft() throws Exception {
+    public void testPortraitPreservedWindowResizingDragTopLeft() {
         final Rect r = new Rect(330, 100, 630, 600);
         mDimBounds.set(r);
 
@@ -409,7 +405,7 @@
      * right things upon resizing when dragged from the left corner.
      */
     @Test
-    public void testPortraitPreservedWindowResizingDragLeft() throws Exception {
+    public void testPortraitPreservedWindowResizingDragLeft() {
         final Rect r = new Rect(330, 100, 630, 600);
         final int midY = (r.top + r.bottom) / 2;
         mDimBounds.set(r);
@@ -452,7 +448,7 @@
      * right things upon resizing when dragged from the top corner.
      */
     @Test
-    public void testPortraitPreservedWindowResizingDragTop() throws Exception {
+    public void testPortraitPreservedWindowResizingDragTop() {
         final Rect r = new Rect(330, 100, 630, 600);
         final int midX = (r.left + r.right) / 2;
         mDimBounds.set(r);
@@ -485,7 +481,7 @@
                 mPositioner.getWindowDragBounds());
     }
 
-    private void assertBoundsEquals(Rect expected, Rect actual) {
+    private static void assertBoundsEquals(Rect expected, Rect actual) {
         if (DEBUGGING) {
             if (!expected.equals(actual)) {
                 Log.e(TAG, "rect(" + actual.toString() + ") != isRect(" + actual.toString()
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
index f8e7403..00b4629 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -32,70 +32,66 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link TaskPositioningController} class.
  *
- * atest com.android.server.wm.TaskPositioningControllerTests
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:TaskPositioningControllerTests
  */
-@SmallTest
 @FlakyTest(bugId = 117924387)
-@RunWith(AndroidJUnit4.class)
+@SmallTest
 @Presubmit
 public class TaskPositioningControllerTests extends WindowTestsBase {
     private static final int TIMEOUT_MS = 1000;
+
     private TaskPositioningController mTarget;
     private WindowState mWindow;
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
+        assertNotNull(mWm.mTaskPositioningController);
+        mTarget = mWm.mTaskPositioningController;
 
-        assertNotNull(sWm.mTaskPositioningController);
-        mTarget = sWm.mTaskPositioningController;
-
-        when(sWm.mInputManager.transferTouchFocus(
+        when(mWm.mInputManager.transferTouchFocus(
                 any(InputChannel.class),
                 any(InputChannel.class))).thenReturn(true);
 
         mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
         mWindow.mInputChannel = new InputChannel();
-        synchronized (sWm.mWindowMap) {
-            sWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
+        synchronized (mWm.mGlobalLock) {
+            mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
         }
     }
 
     @Test
-    public void testStartAndFinishPositioning() throws Exception {
-        synchronized (sWm.mWindowMap) {
+    public void testStartAndFinishPositioning() {
+        synchronized (mWm.mGlobalLock) {
             assertFalse(mTarget.isPositioningLocked());
             assertNull(mTarget.getDragWindowHandleLocked());
         }
 
         assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0));
 
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             assertTrue(mTarget.isPositioningLocked());
             assertNotNull(mTarget.getDragWindowHandleLocked());
         }
 
         mTarget.finishTaskPositioning();
         // Wait until the looper processes finishTaskPositioning.
-        assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS));
+        assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS));
 
         assertFalse(mTarget.isPositioningLocked());
         assertNull(mTarget.getDragWindowHandleLocked());
     }
 
     @Test
-    public void testHandleTapOutsideTask() throws Exception {
-        synchronized (sWm.mWindowMap) {
-
+    public void testHandleTapOutsideTask() {
+        synchronized (mWm.mGlobalLock) {
             assertFalse(mTarget.isPositioningLocked());
             assertNull(mTarget.getDragWindowHandleLocked());
         }
@@ -106,16 +102,16 @@
 
         mTarget.handleTapOutsideTask(content, 0, 0);
         // Wait until the looper processes finishTaskPositioning.
-        assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS));
+        assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS));
 
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             assertTrue(mTarget.isPositioningLocked());
             assertNotNull(mTarget.getDragWindowHandleLocked());
         }
 
         mTarget.finishTaskPositioning();
         // Wait until the looper processes finishTaskPositioning.
-        assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS));
+        assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS));
 
         assertFalse(mTarget.isPositioningLocked());
         assertNull(mTarget.getDragWindowHandleLocked());
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index c9d8004..1c6afd54 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -24,32 +24,32 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link TaskSnapshotCache}.
  *
- * runtest frameworks-services -c com.android.server.wm.TaskSnapshotCacheTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:TaskSnapshotCacheTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
 
     private TaskSnapshotCache mCache;
 
+    @Override
     @Before
-    public void setUp() throws Exception {
+    public void setUp() {
         super.setUp();
-        mCache = new TaskSnapshotCache(sWm, mLoader);
+
+        mCache = new TaskSnapshotCache(mWm, mLoader);
     }
 
     @Test
-    public void testAppRemoved() throws Exception {
+    public void testAppRemoved() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mCache.putSnapshot(window.getTask(), createSnapshot());
         assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
@@ -60,7 +60,7 @@
     }
 
     @Test
-    public void testAppDied() throws Exception {
+    public void testAppDied() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mCache.putSnapshot(window.getTask(), createSnapshot());
         assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
@@ -71,7 +71,7 @@
     }
 
     @Test
-    public void testTaskRemoved() throws Exception {
+    public void testTaskRemoved() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mCache.putSnapshot(window.getTask(), createSnapshot());
         assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
@@ -82,32 +82,32 @@
     }
 
     @Test
-    public void testReduced_notCached() throws Exception {
+    public void testReduced_notCached() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
-        mPersister.persistSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId, createSnapshot());
+        mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
         mPersister.waitForQueueEmpty();
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 false /* restoreFromDisk */, false /* reducedResolution */));
 
         // Load it from disk
-        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 true /* restoreFromDisk */, true /* reducedResolution */));
 
         // Make sure it's not in the cache now.
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 false /* restoreFromDisk */, false /* reducedResolution */));
     }
 
     @Test
-    public void testRestoreFromDisk() throws Exception {
+    public void testRestoreFromDisk() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
-        mPersister.persistSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId, createSnapshot());
+        mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
         mPersister.waitForQueueEmpty();
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 false /* restoreFromDisk */, false /* reducedResolution */));
 
         // Load it from disk
-        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 true /* restoreFromDisk */, false /* reducedResolution */));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index efce063..d2c0765 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -20,7 +20,8 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 import static android.view.WindowManager.TRANSIT_UNSET;
 
-import static com.android.server.wm.TaskSnapshotController.*;
+import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_APP_THEME;
+import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL;
 
 import static junit.framework.Assert.assertEquals;
 
@@ -28,25 +29,23 @@
 import android.util.ArraySet;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.google.android.collect.Sets;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link TaskSnapshotController}.
  *
- * runtest frameworks-services -c com.android.server.wm.TaskSnapshotControllerTest
+ * Build/Install/Run:
+ *  *  atest FrameworksServicesTests:TaskSnapshotControllerTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class TaskSnapshotControllerTest extends WindowTestsBase {
 
     @Test
-    public void testGetClosingApps_closing() throws Exception {
+    public void testGetClosingApps_closing() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
         closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
@@ -54,13 +53,13 @@
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
         final ArraySet<Task> closingTasks = new ArraySet<>();
-        sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+        mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
         assertEquals(1, closingTasks.size());
         assertEquals(closingWindow.mAppToken.getTask(), closingTasks.valueAt(0));
     }
 
     @Test
-    public void testGetClosingApps_notClosing() throws Exception {
+    public void testGetClosingApps_notClosing() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
         final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
@@ -72,12 +71,12 @@
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
         final ArraySet<Task> closingTasks = new ArraySet<>();
-        sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+        mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
         assertEquals(0, closingTasks.size());
     }
 
     @Test
-    public void testGetClosingApps_skipClosingAppsSnapshotTasks() throws Exception {
+    public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
         closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
@@ -85,29 +84,29 @@
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
         final ArraySet<Task> closingTasks = new ArraySet<>();
-        sWm.mTaskSnapshotController.addSkipClosingAppSnapshotTasks(
+        mWm.mTaskSnapshotController.addSkipClosingAppSnapshotTasks(
                 Sets.newArraySet(closingWindow.mAppToken.getTask()));
-        sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+        mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
         assertEquals(0, closingTasks.size());
     }
 
     @Test
-    public void testGetSnapshotMode() throws Exception {
+    public void testGetSnapshotMode() {
         final WindowState disabledWindow = createWindow(null,
                 FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow");
         disabledWindow.mAppToken.setDisablePreviewScreenshots(true);
         assertEquals(SNAPSHOT_MODE_APP_THEME,
-                sWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask()));
+                mWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask()));
 
         final WindowState normalWindow = createWindow(null,
                 FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow");
         assertEquals(SNAPSHOT_MODE_REAL,
-                sWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask()));
+                mWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask()));
 
         final WindowState secureWindow = createWindow(null,
                 FIRST_APPLICATION_WINDOW, mDisplayContent, "secureWindow");
         secureWindow.mAttrs.flags |= FLAG_SECURE;
         assertEquals(SNAPSHOT_MODE_APP_THEME,
-                sWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask()));
+                mWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask()));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 600b2e5..b0eafee 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -34,23 +34,22 @@
 import android.view.View;
 
 import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.io.File;
+import java.util.function.Predicate;
 
 /**
  * Test class for {@link TaskSnapshotPersister} and {@link TaskSnapshotLoader}
  *
- * atest FrameworksServicesTests:TaskSnapshotPersisterLoaderTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:TaskSnapshotPersisterLoaderTest
  */
 @MediumTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase {
 
     private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
@@ -59,9 +58,9 @@
     public void testPersistAndLoadSnapshot() {
         mPersister.persistSnapshot(1 , mTestUserId, createSnapshot());
         mPersister.waitForQueueEmpty();
-        final File[] files = new File[] { new File(sFilesDir.getPath() + "/snapshots/1.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/1.jpg"),
-                new File(sFilesDir.getPath() + "/snapshots/1_reduced.jpg")};
+        final File[] files = new File[] { new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
         assertTrueForFiles(files, File::exists, " must exist");
         final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* reduced */);
         assertNotNull(snapshot);
@@ -70,9 +69,10 @@
         assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
     }
 
-    private void assertTrueForFiles(File[] files, Predicate<File> predicate, String message) {
+    private static void assertTrueForFiles(File[] files, Predicate<File> predicate,
+            String message) {
         for (File file : files) {
-            assertTrue(file.getName() + message, predicate.apply(file));
+            assertTrue(file.getName() + message, predicate.test(file));
         }
     }
 
@@ -81,9 +81,9 @@
         mPersister.persistSnapshot(1, mTestUserId, createSnapshot());
         mPersister.onTaskRemovedFromRecents(1, mTestUserId);
         mPersister.waitForQueueEmpty();
-        assertFalse(new File(sFilesDir.getPath() + "/snapshots/1.proto").exists());
-        assertFalse(new File(sFilesDir.getPath() + "/snapshots/1.jpg").exists());
-        assertFalse(new File(sFilesDir.getPath() + "/snapshots/1_reduced.jpg").exists());
+        assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1.proto").exists());
+        assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1.jpg").exists());
+        assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg").exists());
     }
 
     /**
@@ -120,12 +120,12 @@
 
         // Make sure 1,2 were purged but removeObsoleteFiles wasn't.
         final File[] existsFiles = new File[] {
-                new File(sFilesDir.getPath() + "/snapshots/3.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/4.proto")};
+                new File(FILES_DIR.getPath() + "/snapshots/3.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/4.proto")};
         final File[] nonExistsFiles = new File[] {
-                new File(sFilesDir.getPath() + "/snapshots/100.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/1.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/1.proto")};
+                new File(FILES_DIR.getPath() + "/snapshots/100.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/1.proto")};
         assertTrueForFiles(existsFiles, File::exists, " must exist");
         assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
     }
@@ -149,10 +149,10 @@
         assertTrue(a.isReducedResolution());
         mPersister.persistSnapshot(1 , mTestUserId, a);
         mPersister.waitForQueueEmpty();
-        final File[] files = new File[] { new File(sFilesDir.getPath() + "/snapshots/1.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/1_reduced.jpg")};
+        final File[] files = new File[] { new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
         final File[] nonExistsFiles = new File[] {
-                new File(sFilesDir.getPath() + "/snapshots/1.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
         };
         assertTrueForFiles(files, File::exists, " must exist");
         assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
@@ -195,8 +195,8 @@
         TaskSnapshot b = new TaskSnapshotBuilder()
                 .setWindowingMode(WINDOWING_MODE_PINNED)
                 .build();
-        assertTrue(a.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
-        assertTrue(b.getWindowingMode() == WINDOWING_MODE_PINNED);
+        assertEquals(WINDOWING_MODE_FULLSCREEN, a.getWindowingMode());
+        assertEquals(WINDOWING_MODE_PINNED, b.getWindowingMode());
         mPersister.persistSnapshot(1, mTestUserId, a);
         mPersister.persistSnapshot(2, mTestUserId, b);
         mPersister.waitForQueueEmpty();
@@ -204,8 +204,8 @@
         final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */);
         assertNotNull(snapshotA);
         assertNotNull(snapshotB);
-        assertTrue(snapshotA.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
-        assertTrue(snapshotB.getWindowingMode() == WINDOWING_MODE_PINNED);
+        assertEquals(WINDOWING_MODE_FULLSCREEN, snapshotA.getWindowingMode());
+        assertEquals(WINDOWING_MODE_PINNED, snapshotB.getWindowingMode());
     }
 
     @Test
@@ -239,8 +239,8 @@
         TaskSnapshot b = new TaskSnapshotBuilder()
                 .setSystemUiVisibility(lightBarFlags)
                 .build();
-        assertTrue(a.getSystemUiVisibility() == 0);
-        assertTrue(b.getSystemUiVisibility() == lightBarFlags);
+        assertEquals(0, a.getSystemUiVisibility());
+        assertEquals(lightBarFlags, b.getSystemUiVisibility());
         mPersister.persistSnapshot(1, mTestUserId, a);
         mPersister.persistSnapshot(2, mTestUserId, b);
         mPersister.waitForQueueEmpty();
@@ -248,8 +248,8 @@
         final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */);
         assertNotNull(snapshotA);
         assertNotNull(snapshotB);
-        assertTrue(snapshotA.getSystemUiVisibility() == 0);
-        assertTrue(snapshotB.getSystemUiVisibility() == lightBarFlags);
+        assertEquals(0, snapshotA.getSystemUiVisibility());
+        assertEquals(lightBarFlags, snapshotB.getSystemUiVisibility());
     }
 
     @Test
@@ -261,13 +261,13 @@
         mPersister.removeObsoleteFiles(taskIds, new int[] { mTestUserId });
         mPersister.waitForQueueEmpty();
         final File[] existsFiles = new File[] {
-                new File(sFilesDir.getPath() + "/snapshots/1.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/1.jpg"),
-                new File(sFilesDir.getPath() + "/snapshots/1_reduced.jpg") };
+                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg") };
         final File[] nonExistsFiles = new File[] {
-                new File(sFilesDir.getPath() + "/snapshots/2.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/2.jpg"),
-                new File(sFilesDir.getPath() + "/snapshots/2_reduced.jpg")};
+                new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/2.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
         assertTrueForFiles(existsFiles, File::exists, " must exist");
         assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
     }
@@ -281,24 +281,12 @@
         mPersister.persistSnapshot(2, mTestUserId, createSnapshot());
         mPersister.waitForQueueEmpty();
         final File[] existsFiles = new File[] {
-                new File(sFilesDir.getPath() + "/snapshots/1.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/1.jpg"),
-                new File(sFilesDir.getPath() + "/snapshots/1_reduced.jpg"),
-                new File(sFilesDir.getPath() + "/snapshots/2.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/2.jpg"),
-                new File(sFilesDir.getPath() + "/snapshots/2_reduced.jpg")};
+                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/2.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
         assertTrueForFiles(existsFiles, File::exists, " must exist");
     }
-
-    /**
-     * Private predicate definition.
-     *
-     * This is needed because com.android.internal.util.Predicate is deprecated
-     * and can only be used with classes fron android.test.runner. This cannot
-     * use java.util.function.Predicate because that is not present on all API
-     * versions that this test must run on.
-     */
-    private interface Predicate<T> {
-        boolean apply(T t);
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 33b137e..0f9b825 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -21,6 +21,8 @@
 import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE;
 import static android.graphics.GraphicBuffer.USAGE_SW_READ_RARELY;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -31,51 +33,37 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.BeforeClass;
 
 import java.io.File;
 
-import androidx.test.InstrumentationRegistry;
-
 /**
  * Base class for tests that use a {@link TaskSnapshotPersister}.
  */
 class TaskSnapshotPersisterTestBase extends WindowTestsBase {
 
     private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
+    static final File FILES_DIR = getInstrumentation().getTargetContext().getFilesDir();
 
     TaskSnapshotPersister mPersister;
     TaskSnapshotLoader mLoader;
     int mTestUserId;
-    static File sFilesDir;
 
-    @BeforeClass
-    public static void setUpUser() {
-        sFilesDir = InstrumentationRegistry.getContext().getFilesDir();
-    }
-
-    @Override
     @Before
-    public void setUp() throws Exception {
-        super.setUp();
-
-        final UserManager um = UserManager.get(InstrumentationRegistry.getContext());
+    public void setUp() {
+        final UserManager um = UserManager.get(getInstrumentation().getTargetContext());
         mTestUserId = um.getUserHandle();
-        mPersister = new TaskSnapshotPersister(userId -> sFilesDir);
+        mPersister = new TaskSnapshotPersister(userId -> FILES_DIR);
         mLoader = new TaskSnapshotLoader(mPersister);
         mPersister.start();
     }
 
-    @Override
     @After
-    public void tearDown() throws Exception {
+    public void tearDown() {
         cleanDirectory();
-
-        super.tearDown();
     }
 
     private void cleanDirectory() {
-        final File[] files = new File(sFilesDir, "snapshots").listFiles();
+        final File[] files = new File(FILES_DIR, "snapshots").listFiles();
         if (files == null) {
             return;
         }
@@ -99,7 +87,7 @@
     /**
      * Builds a TaskSnapshot.
      */
-    class TaskSnapshotBuilder {
+    static class TaskSnapshotBuilder {
 
         private float mScale = 1f;
         private boolean mIsRealSnapshot = true;
@@ -107,32 +95,32 @@
         private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
         private int mSystemUiVisibility = 0;
 
-        public TaskSnapshotBuilder setScale(float scale) {
+        TaskSnapshotBuilder setScale(float scale) {
             mScale = scale;
             return this;
         }
 
-        public TaskSnapshotBuilder setIsRealSnapshot(boolean isRealSnapshot) {
+        TaskSnapshotBuilder setIsRealSnapshot(boolean isRealSnapshot) {
             mIsRealSnapshot = isRealSnapshot;
             return this;
         }
 
-        public TaskSnapshotBuilder setIsTranslucent(boolean isTranslucent) {
+        TaskSnapshotBuilder setIsTranslucent(boolean isTranslucent) {
             mIsTranslucent = isTranslucent;
             return this;
         }
 
-        public TaskSnapshotBuilder setWindowingMode(int windowingMode) {
+        TaskSnapshotBuilder setWindowingMode(int windowingMode) {
             mWindowingMode = windowingMode;
             return this;
         }
 
-        public TaskSnapshotBuilder setSystemUiVisibility(int systemUiVisibility) {
+        TaskSnapshotBuilder setSystemUiVisibility(int systemUiVisibility) {
             mSystemUiVisibility = systemUiVisibility;
             return this;
         }
 
-        public TaskSnapshot build() {
+        TaskSnapshot build() {
             final GraphicBuffer buffer = GraphicBuffer.create(100, 100, PixelFormat.RGBA_8888,
                     USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY);
             Canvas c = buffer.lockCanvas();
@@ -142,6 +130,5 @@
                     mScale < 1f /* reducedResolution */, mScale, mIsRealSnapshot, mWindowingMode,
                     mSystemUiVisibility, mIsTranslucent);
         }
-
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 91074b9..7c16191 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -39,21 +39,19 @@
 import android.view.Surface;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.TaskSnapshotSurface.Window;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link TaskSnapshotSurface}.
  *
- * runtest frameworks-services -c com.android.server.wm.TaskSnapshotSurfaceTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:TaskSnapshotSurfaceTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class TaskSnapshotSurfaceTest extends WindowTestsBase {
 
     private TaskSnapshotSurface mSurface;
@@ -65,7 +63,7 @@
         final TaskSnapshot snapshot = new TaskSnapshot(buffer,
                 ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */,
                 WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */);
-        mSurface = new TaskSnapshotSurface(sWm, new Window(), new Surface(), snapshot, "Test",
+        mSurface = new TaskSnapshotSurface(mWm, new Window(), new Surface(), snapshot, "Test",
                 Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds,
                 ORIENTATION_PORTRAIT);
     }
@@ -76,7 +74,7 @@
     }
 
     @Test
-    public void fillEmptyBackground_fillHorizontally() throws Exception {
+    public void fillEmptyBackground_fillHorizontally() {
         setupSurface(200, 100);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(200);
@@ -86,7 +84,7 @@
     }
 
     @Test
-    public void fillEmptyBackground_fillVertically() throws Exception {
+    public void fillEmptyBackground_fillVertically() {
         setupSurface(100, 200);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(100);
@@ -96,7 +94,7 @@
     }
 
     @Test
-    public void fillEmptyBackground_fillBoth() throws Exception {
+    public void fillEmptyBackground_fillBoth() {
         setupSurface(200, 200);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(200);
@@ -107,7 +105,7 @@
     }
 
     @Test
-    public void fillEmptyBackground_dontFill_sameSize() throws Exception {
+    public void fillEmptyBackground_dontFill_sameSize() {
         setupSurface(100, 100);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(100);
@@ -117,7 +115,7 @@
     }
 
     @Test
-    public void fillEmptyBackground_dontFill_bitmapLarger() throws Exception {
+    public void fillEmptyBackground_dontFill_bitmapLarger() {
         setupSurface(100, 100);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(100);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
index 0e9a63c..f01e9f0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -27,17 +27,17 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.filters.SmallTest;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-import androidx.test.filters.SmallTest;
-
 /**
  * Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.TaskStackContainersTests
+ *  atest FrameworksServicesTests:TaskStackContainersTests
  */
 @SmallTest
 @Presubmit
@@ -45,11 +45,8 @@
 
     private TaskStack mPinnedStack;
 
-    @Override
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-
         mPinnedStack = createStackControllerOnStackOnDisplay(
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
         // Stack should contain visible app window to be considered visible.
@@ -61,12 +58,9 @@
         assertTrue(mPinnedStack.isVisible());
     }
 
-    @Override
     @After
     public void tearDown() throws Exception {
         mPinnedStack.removeImmediately();
-
-        super.tearDown();
     }
 
     @Test
@@ -121,14 +115,14 @@
         final Task task = createTaskInStack(stack, 0 /* userId */);
 
         // Add another display at top.
-        sWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
+        mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
                 false /* includingParents */);
 
         // Move the task of {@code mDisplayContent} to top.
         stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
-        final int indexOfDisplayWithPinnedStack = sWm.mRoot.mChildren.indexOf(mDisplayContent);
+        final int indexOfDisplayWithPinnedStack = mWm.mRoot.mChildren.indexOf(mDisplayContent);
 
         assertEquals("The testing DisplayContent should be moved to top with task",
-                sWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedStack);
+                mWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedStack);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
index 53a1185..7ac3318 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -25,24 +25,21 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link TaskStack} class.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.TaskStackTests
+ *  atest FrameworksServicesTests:TaskStackTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class TaskStackTests extends WindowTestsBase {
 
     @Test
-    public void testStackPositionChildAt() throws Exception {
+    public void testStackPositionChildAt() {
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack, 0 /* userId */);
         final Task task2 = createTaskInStack(stack, 1 /* userId */);
@@ -59,7 +56,7 @@
     }
 
     @Test
-    public void testClosingAppDifferentStackOrientation() throws Exception {
+    public void testClosingAppDifferentStackOrientation() {
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack, 0 /* userId */);
         WindowTestUtils.TestAppWindowToken appWindowToken1 =
@@ -74,12 +71,12 @@
         appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
 
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, stack.getOrientation());
-        sWm.mClosingApps.add(appWindowToken2);
+        mDisplayContent.mClosingApps.add(appWindowToken2);
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, stack.getOrientation());
     }
 
     @Test
-    public void testMoveTaskToBackDifferentStackOrientation() throws Exception {
+    public void testMoveTaskToBackDifferentStackOrientation() {
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack, 0 /* userId */);
         WindowTestUtils.TestAppWindowToken appWindowToken1 =
@@ -99,7 +96,7 @@
     }
 
     @Test
-    public void testStackRemoveImmediately() throws Exception {
+    public void testStackRemoveImmediately() {
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stack, 0 /* userId */);
         assertEquals(stack, task.mStack);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
index edd7664..1af79e4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -24,24 +24,21 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link TaskWindowContainerController}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.TaskWindowContainerControllerTests
+ *  atest FrameworksServicesTests:TaskWindowContainerControllerTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class TaskWindowContainerControllerTests extends WindowTestsBase {
 
     @Test
-    public void testRemoveContainer() throws Exception {
+    public void testRemoveContainer() {
         final WindowTestUtils.TestTaskWindowContainerController taskController =
                 new WindowTestUtils.TestTaskWindowContainerController(this);
         final WindowTestUtils.TestAppWindowContainerController appController =
@@ -54,7 +51,7 @@
     }
 
     @Test
-    public void testRemoveContainer_deferRemoval() throws Exception {
+    public void testRemoveContainer_deferRemoval() {
         final WindowTestUtils.TestTaskWindowContainerController taskController =
                 new WindowTestUtils.TestTaskWindowContainerController(this);
         final WindowTestUtils.TestAppWindowContainerController appController =
@@ -79,7 +76,7 @@
     }
 
     @Test
-    public void testReparent() throws Exception {
+    public void testReparent() {
         final StackWindowController stackController1 =
                 createStackControllerOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -116,7 +113,7 @@
     }
 
     @Test
-    public void testReparent_BetweenDisplays() throws Exception {
+    public void testReparent_BetweenDisplays() {
         // Create first stack on primary display.
         final StackWindowController stack1Controller =
                 createStackControllerOnDisplay(mDisplayContent);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
index 353aa7d..e8d0a06 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
@@ -11,13 +11,11 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
 
-import com.android.internal.os.IResultReceiver;
-
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -27,87 +25,71 @@
 import android.view.DragEvent;
 import android.view.IWindow;
 
+import com.android.internal.os.IResultReceiver;
+
 public class TestIWindow extends IWindow.Stub {
     @Override
     public void executeCommand(String command, String parameters,
-            ParcelFileDescriptor descriptor)
-            throws RemoteException {
-
+            ParcelFileDescriptor descriptor) throws RemoteException {
     }
 
     @Override
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
             Rect stableInsets, Rect outsets, boolean reportDraw, MergedConfiguration mergedConfig,
             Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
-            DisplayCutout.ParcelableWrapper displayCutout)
-            throws RemoteException {
-
+            DisplayCutout.ParcelableWrapper displayCutout) throws RemoteException {
     }
 
     @Override
     public void moved(int newX, int newY) throws RemoteException {
-
     }
 
     @Override
     public void dispatchAppVisibility(boolean visible) throws RemoteException {
-
     }
 
     @Override
     public void dispatchGetNewSurface() throws RemoteException {
-
     }
 
     @Override
-    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode)
-            throws RemoteException {
-
+    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) throws RemoteException {
     }
 
     @Override
     public void closeSystemDialogs(String reason) throws RemoteException {
-
     }
 
     @Override
-    public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
-            boolean sync)
+    public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync)
             throws RemoteException {
-
     }
 
     @Override
     public void dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras,
             boolean sync) throws RemoteException {
-
     }
 
     @Override
     public void dispatchDragEvent(DragEvent event) throws RemoteException {
-
     }
 
     @Override
     public void updatePointerIcon(float x, float y) throws RemoteException {
-
     }
 
     @Override
     public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, int localValue,
             int localChanges) throws RemoteException {
-
     }
 
     @Override
     public void dispatchWindowShown() throws RemoteException {
-
     }
 
     @Override
     public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId)
             throws RemoteException {
-
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 5159551..0165e7d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -46,8 +46,6 @@
 import java.util.function.Supplier;
 
 class TestWindowManagerPolicy implements WindowManagerPolicy {
-    private static final String TAG = "TestWindowManagerPolicy";
-
     private final Supplier<WindowManagerService> mWmSupplier;
 
     int rotationToReport = 0;
@@ -55,21 +53,18 @@
 
     private Runnable mRunnableWhenAddingSplashScreen;
 
-    public TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier) {
-
+    TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier) {
         mWmSupplier = wmSupplier;
     }
 
     @Override
     public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
             throws RemoteException {
-
     }
 
     @Override
     public void init(Context context, IWindowManager windowManager,
             WindowManagerFuncs windowManagerFuncs) {
-
     }
 
     public void setDefaultDisplay(DisplayContentInfo displayContentInfo) {
@@ -93,7 +88,6 @@
     @Override
     public void adjustConfigurationLw(Configuration config, int keyboardPresence,
             int navigationPresence) {
-
     }
 
     @Override
@@ -150,7 +144,7 @@
         final com.android.server.wm.WindowState window;
         final AppWindowToken atoken;
         final WindowManagerService wm = mWmSupplier.get();
-        synchronized (wm.mWindowMap) {
+        synchronized (wm.mGlobalLock) {
             atoken = wm.mRoot.getAppWindowToken(appToken);
             IWindow iWindow = mock(IWindow.class);
             doReturn(mock(IBinder.class)).when(iWindow).asBinder();
@@ -164,7 +158,7 @@
             mRunnableWhenAddingSplashScreen = null;
         }
         return () -> {
-            synchronized (wm.mWindowMap) {
+            synchronized (wm.mGlobalLock) {
                 atoken.removeChild(window);
                 atoken.startingWindow = null;
             }
@@ -179,7 +173,6 @@
 
     @Override
     public void removeWindowLw(WindowState win) {
-
     }
 
     @Override
@@ -189,7 +182,6 @@
 
     @Override
     public void selectRotationAnimationLw(int[] anim) {
-
     }
 
     @Override
@@ -220,14 +212,12 @@
     }
 
     @Override
-    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event,
-            int policyFlags) {
+    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
         return 0;
     }
 
     @Override
-    public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event,
-            int policyFlags) {
+    public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
         return null;
     }
 
@@ -238,12 +228,11 @@
 
     @Override
     public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
-
     }
 
     @Override
-    public void applyPostLayoutPolicyLw(WindowState win,
-            WindowManager.LayoutParams attrs, WindowState attached, WindowState imeTarget) {
+    public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
+            WindowState attached, WindowState imeTarget) {
     }
 
     @Override
@@ -257,49 +246,40 @@
     }
 
     @Override
-    public int focusChangedLw(WindowState lastFocus,
-            WindowState newFocus) {
+    public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
         return 0;
     }
 
     @Override
     public void startedWakingUp() {
-
     }
 
     @Override
     public void finishedWakingUp() {
-
     }
 
     @Override
     public void startedGoingToSleep(int why) {
-
     }
 
     @Override
     public void finishedGoingToSleep(int why) {
-
     }
 
     @Override
     public void screenTurningOn(ScreenOnListener screenOnListener) {
-
     }
 
     @Override
     public void screenTurnedOn() {
-
     }
 
     @Override
     public void screenTurningOff(ScreenOffListener screenOffListener) {
-
     }
 
     @Override
     public void screenTurnedOff() {
-
     }
 
     @Override
@@ -314,22 +294,18 @@
 
     @Override
     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
-
     }
 
     @Override
     public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
-
     }
 
     @Override
     public void enableKeyguard(boolean enabled) {
-
     }
 
     @Override
     public void exitKeyguardSecurely(OnKeyguardExitResult callback) {
-
     }
 
     @Override
@@ -382,53 +358,44 @@
     }
 
     public void setSafeMode(boolean safeMode) {
-
     }
 
     @Override
     public void systemReady() {
-
     }
 
     @Override
     public void systemBooted() {
-
     }
 
     @Override
     public void showBootMessage(CharSequence msg, boolean always) {
-
     }
 
     @Override
     public void hideBootMessages() {
-
     }
 
     @Override
     public void userActivity() {
-
     }
 
     @Override
     public void enableScreenAfterBoot() {
-
     }
 
     @Override
-    public boolean performHapticFeedbackLw(WindowState win, int effectId,
-            boolean always, String reason) {
+    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always,
+            String reason) {
         return false;
     }
 
     @Override
     public void keepScreenOnStartedLw() {
-
     }
 
     @Override
     public void keepScreenOnStoppedLw() {
-
     }
 
     @Override
@@ -443,37 +410,30 @@
 
     @Override
     public void lockNow(Bundle options) {
-
     }
 
     @Override
     public void showRecentApps() {
-
     }
 
     @Override
     public void showGlobalActions() {
-
     }
 
     @Override
     public void setCurrentUserLw(int newUserId) {
-
     }
 
     @Override
     public void setSwitchingUser(boolean switching) {
-
     }
 
     @Override
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
-
     }
 
     @Override
     public void dump(String prefix, PrintWriter writer, String[] args) {
-
     }
 
     @Override
@@ -483,13 +443,11 @@
 
     @Override
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
-
     }
 
     @Override
     public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
             DisplayCutout cutout, Rect outInsets) {
-
     }
 
     @Override
@@ -506,7 +464,6 @@
     @Override
     public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,
             DisplayCutout cutout, Rect outInsets) {
-
     }
 
     @Override
@@ -517,7 +474,6 @@
 
     @Override
     public void onConfigurationChanged(DisplayContentInfo displayContentInfo) {
-
     }
 
     @Override
@@ -528,12 +484,10 @@
 
     @Override
     public void setPipVisibilityLw(boolean visible) {
-
     }
 
     @Override
     public void setRecentsVisibilityLw(boolean visible) {
-
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 3ac97027..9e22c0a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -11,79 +11,77 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
 
 import static junit.framework.Assert.assertTrue;
 
+import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link AppTransition}.
  *
- * runtest frameworks-services -c com.android.server.wm.UnknownAppVisibilityControllerTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:UnknownAppVisibilityControllerTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class UnknownAppVisibilityControllerTest extends WindowTestsBase {
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-        sWm.mUnknownAppVisibilityController.clear();
+        mDisplayContent.mUnknownAppVisibilityController.clear();
     }
 
     @Test
-    public void testFlow() throws Exception {
+    public void testFlow() {
         final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
-        sWm.mUnknownAppVisibilityController.notifyLaunched(token);
-        sWm.mUnknownAppVisibilityController.notifyAppResumedFinished(token);
-        sWm.mUnknownAppVisibilityController.notifyRelayouted(token);
+        mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token);
+        mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token);
+        mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token);
 
         // Make sure our handler processed the message.
-        Thread.sleep(100);
-        assertTrue(sWm.mUnknownAppVisibilityController.allResolved());
+        SystemClock.sleep(100);
+        assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
     }
 
     @Test
-    public void testMultiple() throws Exception {
+    public void testMultiple() {
         final AppWindowToken token1 = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         final AppWindowToken token2 = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
-        sWm.mUnknownAppVisibilityController.notifyLaunched(token1);
-        sWm.mUnknownAppVisibilityController.notifyAppResumedFinished(token1);
-        sWm.mUnknownAppVisibilityController.notifyLaunched(token2);
-        sWm.mUnknownAppVisibilityController.notifyRelayouted(token1);
-        sWm.mUnknownAppVisibilityController.notifyAppResumedFinished(token2);
-        sWm.mUnknownAppVisibilityController.notifyRelayouted(token2);
+        mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token1);
+        mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token1);
+        mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token2);
+        mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token1);
+        mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token2);
+        mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token2);
 
         // Make sure our handler processed the message.
-        Thread.sleep(100);
-        assertTrue(sWm.mUnknownAppVisibilityController.allResolved());
+        SystemClock.sleep(100);
+        assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
     }
 
     @Test
-    public void testClear() throws Exception {
+    public void testClear() {
         final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
-        sWm.mUnknownAppVisibilityController.notifyLaunched(token);
-        sWm.mUnknownAppVisibilityController.clear();;
-        assertTrue(sWm.mUnknownAppVisibilityController.allResolved());
+        mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token);
+        mDisplayContent.mUnknownAppVisibilityController.clear();;
+        assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
     }
 
     @Test
-    public void testAppRemoved() throws Exception {
+    public void testAppRemoved() {
         final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
-        sWm.mUnknownAppVisibilityController.notifyLaunched(token);
-        sWm.mUnknownAppVisibilityController.appRemovedOrHidden(token);
-        assertTrue(sWm.mUnknownAppVisibilityController.allResolved());
+        mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token);
+        mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(token);
+        assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
index 882e789..25e73e3 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
 
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -13,33 +29,30 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link WallpaperController} class.
  *
  * Build/Install/Run:
- *  atest com.android.server.wm.WallpaperControllerTests
+ *  atest FrameworksServicesTests:WallpaperControllerTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class WallpaperControllerTests extends WindowTestsBase {
     @Test
     public void testWallpaperScreenshot() {
         WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class);
 
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             // No wallpaper
             final DisplayContent dc = createNewDisplay();
-            Bitmap wallpaperBitmap = sWm.mRoot.mWallpaperController.screenshotWallpaperLocked();
+            Bitmap wallpaperBitmap = mWm.mRoot.mWallpaperController.screenshotWallpaperLocked();
             assertNull(wallpaperBitmap);
 
             // No wallpaper WSA Surface
-            WindowToken wallpaperWindowToken = new WallpaperWindowToken(sWm, mock(IBinder.class),
+            WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
                     true, dc, true /* ownerCanManageAppTokens */);
             WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
                     wallpaperWindowToken, "wallpaperWindow");
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
deleted file mode 100644
index 827d938..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2016 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.server.wm;
-
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
-
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.view.SurfaceControl;
-import android.view.animation.Animation;
-import android.view.animation.ClipRectAnimation;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link WindowAnimationSpec} class.
- *
- *  atest FrameworksServicesTests:com.android.server.wm.WindowAnimationSpecTest
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class WindowAnimationSpecTest {
-    private final SurfaceControl mSurfaceControl = mock(SurfaceControl.class);
-    private final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
-    private final Animation mAnimation = mock(Animation.class);
-    private final Rect mStackBounds = new Rect(0, 0, 10, 10);
-
-    @Test
-    public void testApply_clipNone() {
-        Rect windowCrop = new Rect(0, 0, 20, 20);
-        Animation a = createClipRectAnimation(windowCrop, windowCrop);
-        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
-                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_NONE,
-                true /* isAppAnimation */);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
-                argThat(rect -> rect.equals(windowCrop)));
-    }
-
-    @Test
-    public void testApply_clipAfter() {
-        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null,
-                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_AFTER_ANIM,
-                true /* isAppAnimation */);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
-    }
-
-    @Test
-    public void testApply_clipAfterOffsetPosition() {
-        // Stack bounds is (0, 0, 10, 10) position is (20, 40)
-        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation,
-                new Point(20, 40), mStackBounds, false /* canSkipFirstFrame */,
-                STACK_CLIP_AFTER_ANIM,
-                true /* isAppAnimation */);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
-    }
-
-    @Test
-    public void testApply_clipBeforeNoAnimationBounds() {
-        // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 0, 0)
-        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null,
-                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM,
-                true /* isAppAnimation */);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
-                argThat(rect -> rect.equals(mStackBounds)));
-    }
-
-    @Test
-    public void testApply_clipBeforeNoStackBounds() {
-        // Stack bounds is (0, 0, 0, 0) animation clip is (0, 0, 20, 20)
-        Rect windowCrop = new Rect(0, 0, 20, 20);
-        Animation a = createClipRectAnimation(windowCrop, windowCrop);
-        a.initialize(0, 0, 0, 0);
-        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
-                null, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM,
-                true /* isAppAnimation */);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
-    }
-
-    @Test
-    public void testApply_clipBeforeSmallerAnimationClip() {
-        // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 5, 5)
-        Rect windowCrop = new Rect(0, 0, 5, 5);
-        Animation a = createClipRectAnimation(windowCrop, windowCrop);
-        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
-                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM,
-                true /* isAppAnimation */);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
-                argThat(rect -> rect.equals(windowCrop)));
-    }
-
-    @Test
-    public void testApply_clipBeforeSmallerStackClip() {
-        // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 20, 20)
-        Rect windowCrop = new Rect(0, 0, 20, 20);
-        Animation a = createClipRectAnimation(windowCrop, windowCrop);
-        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
-                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM,
-                true /* isAppAnimation */);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
-                argThat(rect -> rect.equals(mStackBounds)));
-    }
-
-    private Animation createClipRectAnimation(Rect fromClip, Rect toClip) {
-        Animation a = new ClipRectAnimation(fromClip, toClip);
-        a.initialize(0, 0, 0, 0);
-        return a;
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
index e7cc285..3643457 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOW_CONFIG_ALWAYS_ON_TOP;
 import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
+import static android.app.WindowConfiguration.WINDOW_CONFIG_ROTATION;
 import static android.app.WindowConfiguration.WINDOW_CONFIG_WINDOWING_MODE;
 import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
 
@@ -33,29 +34,28 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.DisplayInfo;
+import android.view.Surface;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Test;
 
 /**
  * Test class to for {@link android.app.WindowConfiguration}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowConfigurationTests
+ *  atest FrameworksServicesTests:WindowConfigurationTests
  */
-@SmallTest
 @FlakyTest(bugId = 74078662)
+@SmallTest
 @Presubmit
-@org.junit.runner.RunWith(AndroidJUnit4.class)
 public class WindowConfigurationTests extends WindowTestsBase {
     private Rect mParentBounds;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
         mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
     }
 
@@ -68,11 +68,13 @@
         final WindowConfiguration winConfig2 = config2.windowConfiguration;
         final Configuration config3 = new Configuration();
         final WindowConfiguration winConfig3 = config3.windowConfiguration;
+        final Configuration config4 = new Configuration();
+        final WindowConfiguration winConfig4 = config4.windowConfiguration;
 
         winConfig1.setAppBounds(0, 1, 1, 0);
         winConfig2.setAppBounds(1, 2, 2, 1);
         winConfig3.setAppBounds(winConfig1.getAppBounds());
-
+        winConfig4.setRotation(Surface.ROTATION_90);
 
         assertEquals(CONFIG_WINDOW_CONFIGURATION, config1.diff(config2));
         assertEquals(0, config1.diffPublicOnly(config2));
@@ -88,6 +90,9 @@
                 | WINDOW_CONFIG_ALWAYS_ON_TOP,
                 winConfig1.diff(winConfig2, false /* compareUndefined */));
 
+        assertEquals(WINDOW_CONFIG_ROTATION,
+                winConfig1.diff(winConfig4, false /* compareUndefined */));
+
         assertEquals(0, config1.diff(config3));
         assertEquals(0, config1.diffPublicOnly(config3));
         assertEquals(0, winConfig1.diff(winConfig3, false /* compareUndefined */));
@@ -95,7 +100,7 @@
 
     /** Tests {@link android.app.WindowConfiguration#compareTo(WindowConfiguration)}. */
     @Test
-    public void testConfigurationCompareTo() throws Exception {
+    public void testConfigurationCompareTo() {
         final Configuration blankConfig = new Configuration();
         final WindowConfiguration blankWinConfig = new WindowConfiguration();
 
@@ -125,17 +130,24 @@
         winConfig2.setAppBounds(0, 2, 3, 4);
         assertNotEquals(config1.compareTo(config2), 0);
         assertNotEquals(winConfig1.compareTo(winConfig2), 0);
+        winConfig2.setAppBounds(winConfig1.getAppBounds());
 
         // No bounds
         assertEquals(config1.compareTo(blankConfig), -1);
         assertEquals(winConfig1.compareTo(blankWinConfig), -1);
 
+        // Different rotation
+        winConfig2.setRotation(Surface.ROTATION_180);
+        assertNotEquals(config1.compareTo(config2), 0);
+        assertNotEquals(winConfig1.compareTo(winConfig2), 0);
+        winConfig2.setRotation(winConfig1.getRotation());
+
         assertEquals(blankConfig.compareTo(config1), 1);
         assertEquals(blankWinConfig.compareTo(winConfig1), 1);
     }
 
     @Test
-    public void testSetActivityType() throws Exception {
+    public void testSetActivityType() {
         final WindowConfiguration config = new WindowConfiguration();
         config.setActivityType(ACTIVITY_TYPE_HOME);
         assertEquals(ACTIVITY_TYPE_HOME, config.getActivityType());
@@ -147,12 +159,12 @@
 
     /** Ensures the configuration app bounds at the root level match the app dimensions. */
     @Test
-    public void testAppBounds_RootConfigurationBounds() throws Exception {
+    public void testAppBounds_RootConfigurationBounds() {
         final DisplayInfo info = mDisplayContent.getDisplayInfo();
         info.appWidth = 1024;
         info.appHeight = 768;
 
-        final Rect appBounds = sWm.computeNewConfiguration(
+        final Rect appBounds = mWm.computeNewConfiguration(
                 mDisplayContent.getDisplayId()).windowConfiguration.getAppBounds();
         // The bounds should always be positioned in the top left.
         assertEquals(appBounds.left, 0);
@@ -165,7 +177,7 @@
 
     /** Ensures that bounds are clipped to their parent. */
     @Test
-    public void testAppBounds_BoundsClipping() throws Exception {
+    public void testAppBounds_BoundsClipping() {
         final Rect shiftedBounds = new Rect(mParentBounds);
         shiftedBounds.offset(10, 10);
         final Rect expectedBounds = new Rect(mParentBounds);
@@ -176,7 +188,7 @@
 
     /** Ensures that empty bounds are not propagated to the configuration. */
     @Test
-    public void testAppBounds_EmptyBounds() throws Exception {
+    public void testAppBounds_EmptyBounds() {
         final Rect emptyBounds = new Rect();
         testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds,
                 null /*ExpectedBounds*/);
@@ -184,7 +196,7 @@
 
     /** Ensures that bounds on freeform stacks are not clipped. */
     @Test
-    public void testAppBounds_FreeFormBounds() throws Exception {
+    public void testAppBounds_FreeFormBounds() {
         final Rect freeFormBounds = new Rect(mParentBounds);
         freeFormBounds.offset(10, 10);
         testStackBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds,
@@ -193,7 +205,7 @@
 
     /** Ensures that fully contained bounds are not clipped. */
     @Test
-    public void testAppBounds_ContainedBounds() throws Exception {
+    public void testAppBounds_ContainedBounds() {
         final Rect insetBounds = new Rect(mParentBounds);
         insetBounds.inset(5, 5, 5, 5);
         testStackBoundsConfiguration(
@@ -202,7 +214,7 @@
 
     /** Ensures that full screen free form bounds are clipped */
     @Test
-    public void testAppBounds_FullScreenFreeFormBounds() throws Exception {
+    public void testAppBounds_FullScreenFreeFormBounds() {
         final Rect fullScreenBounds = new Rect(0, 0, mDisplayInfo.logicalWidth,
                 mDisplayInfo.logicalHeight);
         testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds,
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
index 6b1feb8..7592f1c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -28,7 +28,6 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 
@@ -36,18 +35,17 @@
  * Test class for {@link WindowContainerController}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowContainerControllerTests
+ *  atest FrameworksServicesTests:WindowContainerControllerTests
  */
+@FlakyTest(bugId = 74078662)
 @SmallTest
 @Presubmit
-@FlakyTest(bugId = 74078662)
-@org.junit.runner.RunWith(AndroidJUnit4.class)
 public class WindowContainerControllerTests extends WindowTestsBase {
 
     @Test
-    public void testCreation() throws Exception {
-        final WindowContainerController controller = new WindowContainerController(null, sWm);
-        final WindowContainer container = new WindowContainer(sWm);
+    public void testCreation() {
+        final WindowContainerController controller = new WindowContainerController<>(null, mWm);
+        final WindowContainer container = new WindowContainer(mWm);
 
         container.setController(controller);
         assertEquals(controller, container.getController());
@@ -55,9 +53,9 @@
     }
 
     @Test
-    public void testSetContainer() throws Exception {
-        final WindowContainerController controller = new WindowContainerController(null, sWm);
-        final WindowContainer container = new WindowContainer(sWm);
+    public void testSetContainer() {
+        final WindowContainerController controller = new WindowContainerController<>(null, mWm);
+        final WindowContainer container = new WindowContainer(mWm);
 
         controller.setContainer(container);
         assertEquals(controller.mContainer, container);
@@ -65,7 +63,7 @@
         // Assert we can't change the container to another one once set
         boolean gotException = false;
         try {
-            controller.setContainer(new WindowContainer(sWm));
+            controller.setContainer(new WindowContainer(mWm));
         } catch (IllegalArgumentException e) {
             gotException = true;
         }
@@ -77,9 +75,9 @@
     }
 
     @Test
-    public void testRemoveContainer() throws Exception {
-        final WindowContainerController controller = new WindowContainerController(null, sWm);
-        final WindowContainer container = new WindowContainer(sWm);
+    public void testRemoveContainer() {
+        final WindowContainerController controller = new WindowContainerController<>(null, mWm);
+        final WindowContainer container = new WindowContainer(mWm);
 
         controller.setContainer(container);
         assertEquals(controller.mContainer, container);
@@ -89,9 +87,9 @@
     }
 
     @Test
-    public void testOnOverrideConfigurationChanged() throws Exception {
-        final WindowContainerController controller = new WindowContainerController(null, sWm);
-        final WindowContainer container = new WindowContainer(sWm);
+    public void testOnOverrideConfigurationChanged() {
+        final WindowContainerController controller = new WindowContainerController<>(null, mWm);
+        final WindowContainer container = new WindowContainer(mWm);
 
         controller.setContainer(container);
         assertEquals(controller.mContainer, container);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index dc763b9..e59afd6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -46,10 +46,8 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.Comparator;
 
@@ -57,24 +55,23 @@
  * Test class for {@link WindowContainer}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.WindowContainerTests
+ *  atest FrameworksServicesTests:WindowContainerTests
  */
 @SmallTest
 @Presubmit
 @FlakyTest(bugId = 74078662)
-@RunWith(AndroidJUnit4.class)
 public class WindowContainerTests extends WindowTestsBase {
 
     @Test
-    public void testCreation() throws Exception {
-        final TestWindowContainer w = new TestWindowContainerBuilder().setLayer(0).build();
+    public void testCreation() {
+        final TestWindowContainer w = new TestWindowContainerBuilder(mWm).setLayer(0).build();
         assertNull("window must have no parent", w.getParentWindow());
         assertEquals("window must have no children", 0, w.getChildrenCount());
     }
 
     @Test
-    public void testAdd() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testAdd() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer layer1 = root.addChildWindow(builder.setLayer(1));
@@ -113,23 +110,24 @@
     }
 
     @Test
-    public void testAddChildSetsSurfacePosition() throws Exception {
-        MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer();
+    public void testAddChildSetsSurfacePosition() {
+        try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) {
 
-        final SurfaceControl.Transaction transaction = mock(SurfaceControl.Transaction.class);
-        sWm.mTransactionFactory = () -> transaction;
+            final SurfaceControl.Transaction transaction = mock(SurfaceControl.Transaction.class);
+            mWm.mTransactionFactory = () -> transaction;
 
-        WindowContainer child = new WindowContainer(sWm);
-        child.setBounds(1, 1, 10, 10);
+            WindowContainer child = new WindowContainer(mWm);
+            child.setBounds(1, 1, 10, 10);
 
-        verify(transaction, never()).setPosition(any(), anyFloat(), anyFloat());
-        top.addChild(child, 0);
-        verify(transaction, times(1)).setPosition(any(), eq(1.f), eq(1.f));
+            verify(transaction, never()).setPosition(any(), anyFloat(), anyFloat());
+            top.addChild(child, 0);
+            verify(transaction, times(1)).setPosition(any(), eq(1.f), eq(1.f));
+        }
     }
 
     @Test
-    public void testAdd_AlreadyHasParent() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testAdd_AlreadyHasParent() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -153,8 +151,8 @@
     }
 
     @Test
-    public void testHasChild() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testHasChild() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -183,8 +181,8 @@
     }
 
     @Test
-    public void testRemoveImmediately() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testRemoveImmediately() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -218,9 +216,9 @@
     }
 
     @Test
-    public void testRemoveImmediately_WithController() throws Exception {
-        final WindowContainer container = new WindowContainer(sWm);
-        final WindowContainerController controller = new WindowContainerController(null, sWm);
+    public void testRemoveImmediately_WithController() {
+        final WindowContainer container = new WindowContainer(mWm);
+        final WindowContainerController controller = new WindowContainerController<>(null, mWm);
 
         container.setController(controller);
         assertEquals(controller, container.getController());
@@ -232,9 +230,9 @@
     }
 
     @Test
-    public void testSetController() throws Exception {
-        final WindowContainerController controller = new WindowContainerController(null, sWm);
-        final WindowContainer container = new WindowContainer(sWm);
+    public void testSetController() {
+        final WindowContainerController controller = new WindowContainerController<>(null, mWm);
+        final WindowContainer container = new WindowContainer(mWm);
 
         container.setController(controller);
         assertEquals(controller, container.getController());
@@ -243,7 +241,7 @@
         // Assert we can't change the controller to another one once set
         boolean gotException = false;
         try {
-            container.setController(new WindowContainerController(null, sWm));
+            container.setController(new WindowContainerController<>(null, mWm));
         } catch (IllegalArgumentException e) {
             gotException = true;
         }
@@ -256,8 +254,8 @@
     }
 
     @Test
-    public void testAddChildByIndex() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testAddChildByIndex() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child = root.addChildWindow();
@@ -283,8 +281,8 @@
     }
 
     @Test
-    public void testPositionChildAt() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testPositionChildAt() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -307,8 +305,8 @@
     }
 
     @Test
-    public void testPositionChildAtIncludeParents() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testPositionChildAtIncludeParents() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -349,12 +347,11 @@
     }
 
     @Test
-    public void testPositionChildAtInvalid() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testPositionChildAtInvalid() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
-        final TestWindowContainer child2 = root.addChildWindow();
 
         boolean gotException = false;
         try {
@@ -376,8 +373,8 @@
     }
 
     @Test
-    public void testIsAnimating() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testIsAnimating() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow(builder.setIsAnimating(true));
@@ -402,8 +399,8 @@
     }
 
     @Test
-    public void testIsVisible() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testIsVisible() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow(builder.setIsVisible(true));
@@ -422,7 +419,7 @@
 
     @Test
     public void testOverrideConfigurationAncestorNotification() {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer grandparent = builder.setLayer(0).build();
 
         final TestWindowContainer parent = grandparent.addChildWindow();
@@ -433,8 +430,8 @@
     }
 
     @Test
-    public void testRemoveChild() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testRemoveChild() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
         final TestWindowContainer child1 = root.addChildWindow();
         final TestWindowContainer child2 = root.addChildWindow();
@@ -460,7 +457,7 @@
     }
 
     @Test
-    public void testGetOrientation_childSpecified() throws Exception {
+    public void testGetOrientation_childSpecified() {
         testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_LANDSCAPE,
             SCREEN_ORIENTATION_LANDSCAPE);
         testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_UNSET,
@@ -469,7 +466,7 @@
 
     private void testGetOrientation_childSpecifiedConfig(boolean childVisible, int childOrientation,
         int expectedOrientation) {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
         root.setFillsParent(true);
 
@@ -486,16 +483,16 @@
     }
 
     @Test
-    public void testGetOrientation_Unset() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testGetOrientation_Unset() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
         // Unspecified well because we didn't specify anything...
         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation());
     }
 
     @Test
-    public void testGetOrientation_InvisibleParentUnsetVisibleChildren() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testGetOrientation_InvisibleParentUnsetVisibleChildren() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
 
         builder.setIsVisible(false).setLayer(-1);
@@ -516,12 +513,11 @@
         visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET);
         assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnset.getOrientation());
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, root.getOrientation());
-
     }
 
     @Test
-    public void testGetOrientation_setBehind() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testGetOrientation_setBehind() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
 
         builder.setIsVisible(true).setLayer(-1);
@@ -541,8 +537,8 @@
     }
 
     @Test
-    public void testGetOrientation_fillsParent() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testGetOrientation_fillsParent() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
 
         builder.setIsVisible(true).setLayer(-1);
@@ -577,8 +573,8 @@
     }
 
     @Test
-    public void testCompareTo() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testCompareTo() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -588,8 +584,6 @@
         final TestWindowContainer child2 = root.addChildWindow();
         final TestWindowContainer child21 = child2.addChildWindow();
         final TestWindowContainer child22 = child2.addChildWindow();
-        final TestWindowContainer child23 = child2.addChildWindow();
-        final TestWindowContainer child221 = child22.addChildWindow();
         final TestWindowContainer child222 = child22.addChildWindow();
         final TestWindowContainer child223 = child22.addChildWindow();
         final TestWindowContainer child2221 = child222.addChildWindow();
@@ -620,8 +614,8 @@
     }
 
     @Test
-    public void testPrefixOrderIndex() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testPrefixOrderIndex() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -654,8 +648,8 @@
     }
 
     @Test
-    public void testPrefixOrder_addEntireSubtree() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testPrefixOrder_addEntireSubtree() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.build();
         final TestWindowContainer subtree = builder.build();
         final TestWindowContainer subtree2 = builder.build();
@@ -677,8 +671,8 @@
     }
 
     @Test
-    public void testPrefixOrder_remove() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testPrefixOrder_remove() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -705,8 +699,8 @@
      * is invoked with overridden bounds.
      */
     @Test
-    public void testOnParentResizePropagation() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testOnParentResizePropagation() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.build();
 
         final TestWindowContainer child = root.addChildWindow();
@@ -731,7 +725,7 @@
     }
 
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
-    private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
+    private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         private final int mLayer;
         private boolean mIsAnimating;
         private boolean mIsVisible;
@@ -745,7 +739,7 @@
          * Compares 2 window layers and returns -1 if the first is lesser than the second in terms
          * of z-order and 1 otherwise.
          */
-        private final Comparator<TestWindowContainer> mWindowSubLayerComparator = (w1, w2) -> {
+        private static final Comparator<TestWindowContainer> SUBLAYER_COMPARATOR = (w1, w2) -> {
             final int layer1 = w1.mLayer;
             final int layer2 = w2.mLayer;
             if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
@@ -753,12 +747,14 @@
                 // the negative one should go below others; the positive one should go above others.
                 return -1;
             }
+            if (layer1 == layer2) return 0;
             return 1;
         };
 
-        TestWindowContainer(int layer, boolean isAnimating, boolean isVisible,
-            Integer orientation) {
-            super(sWm);
+        TestWindowContainer(WindowManagerService wm, int layer, boolean isAnimating,
+                boolean isVisible, Integer orientation) {
+            super(wm);
+
             mLayer = layer;
             mIsAnimating = isAnimating;
             mIsVisible = isVisible;
@@ -775,18 +771,18 @@
         }
 
         TestWindowContainer addChildWindow(TestWindowContainer child) {
-            addChild(child, mWindowSubLayerComparator);
+            addChild(child, SUBLAYER_COMPARATOR);
             return child;
         }
 
         TestWindowContainer addChildWindow(TestWindowContainerBuilder childBuilder) {
             TestWindowContainer child = childBuilder.build();
-            addChild(child, mWindowSubLayerComparator);
+            addChild(child, SUBLAYER_COMPARATOR);
             return child;
         }
 
         TestWindowContainer addChildWindow() {
-            return addChildWindow(new TestWindowContainerBuilder().setLayer(1));
+            return addChildWindow(new TestWindowContainerBuilder(mService).setLayer(1));
         }
 
         @Override
@@ -830,14 +826,19 @@
         }
     }
 
-    private class TestWindowContainerBuilder {
+    private static class TestWindowContainerBuilder {
+        private final WindowManagerService mWm;
         private int mLayer;
         private boolean mIsAnimating;
         private boolean mIsVisible;
         private Integer mOrientation;
 
-        public TestWindowContainerBuilder() {
-            reset();
+        TestWindowContainerBuilder(WindowManagerService wm) {
+            mWm = wm;
+            mLayer = 0;
+            mIsAnimating = false;
+            mIsVisible = false;
+            mOrientation = null;
         }
 
         TestWindowContainerBuilder setLayer(int layer) {
@@ -860,27 +861,20 @@
             return this;
         }
 
-        TestWindowContainerBuilder reset() {
-            mLayer = 0;
-            mIsAnimating = false;
-            mIsVisible = false;
-            mOrientation = null;
-            return this;
-        }
-
         TestWindowContainer build() {
-            return new TestWindowContainer(mLayer, mIsAnimating, mIsVisible, mOrientation);
+            return new TestWindowContainer(mWm, mLayer, mIsAnimating, mIsVisible, mOrientation);
         }
     }
 
-    private class MockSurfaceBuildingContainer extends WindowContainer<WindowContainer> {
-        final SurfaceSession mSession = new SurfaceSession();
+    private static class MockSurfaceBuildingContainer extends WindowContainer<WindowContainer>
+            implements AutoCloseable {
+        private final SurfaceSession mSession = new SurfaceSession();
 
-        MockSurfaceBuildingContainer() {
-            super(sWm);
+        MockSurfaceBuildingContainer(WindowManagerService wm) {
+            super(wm);
         }
 
-        class MockSurfaceBuilder extends SurfaceControl.Builder {
+        static class MockSurfaceBuilder extends SurfaceControl.Builder {
             MockSurfaceBuilder(SurfaceSession ss) {
                 super(ss);
             }
@@ -895,5 +889,10 @@
         SurfaceControl.Builder makeChildSurface(WindowContainer child) {
             return new MockSurfaceBuilder(mSession);
         }
+
+        @Override
+        public void close() {
+            mSession.kill();
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
index ffc8622..2b8b934 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -28,23 +28,23 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.function.Consumer;
 
 /**
  * Tests for {@link WindowContainer#forAllWindows} and various implementations.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:WindowContainerTraversalTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class WindowContainerTraversalTests extends WindowTestsBase {
 
     @Test
-    public void testDockedDividerPosition() throws Exception {
+    public void testDockedDividerPosition() {
         final WindowState splitScreenWindow = createWindowOnStack(null,
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
                 mDisplayContent, "splitScreenWindow");
@@ -52,7 +52,7 @@
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
                 TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
 
-        sWm.mInputMethodTarget = splitScreenWindow;
+        mWm.mInputMethodTarget = splitScreenWindow;
 
         Consumer<WindowState> c = mock(Consumer.class);
         mDisplayContent.forAllWindows(c, false);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 7cd1314..b0c8d8b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -11,14 +11,14 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
 
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
-import static android.view.WindowManager.LayoutParams.FILL_PARENT;
+import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static org.junit.Assert.assertEquals;
@@ -33,33 +33,32 @@
 import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
  *
- * Build/Install/Run: bit FrameworksServicesTests:com.android.server.wm.WindowFrameTests
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:WindowFrameTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class WindowFrameTests extends WindowTestsBase {
 
     private WindowToken mWindowToken;
     private final IWindow mIWindow = new TestIWindow();
     private final Rect mEmptyRect = new Rect();
 
-    class WindowStateWithTask extends WindowState {
+    static class WindowStateWithTask extends WindowState {
         final Task mTask;
         boolean mDockedResizingForTest = false;
-        WindowStateWithTask(WindowManager.LayoutParams attrs, Task t) {
-            super(sWm, null, mIWindow, mWindowToken, null, 0, 0, attrs, 0, 0,
+        WindowStateWithTask(WindowManagerService wm, IWindow iWindow, WindowToken windowToken,
+                WindowManager.LayoutParams attrs, Task t) {
+            super(wm, null, iWindow, windowToken, null, 0, 0, attrs, 0, 0,
                     false /* ownerCanAddInternalSystemWindow */);
             mTask = t;
         }
@@ -73,14 +72,15 @@
         boolean isDockedResizing() {
             return mDockedResizingForTest;
         }
-    };
+    }
 
-    class TaskWithBounds extends Task {
+    private static class TaskWithBounds extends Task {
         final Rect mBounds;
         final Rect mInsetBounds = new Rect();
         boolean mFullscreenForTest = true;
-        TaskWithBounds(Rect bounds) {
-            super(0, mStubStack, 0, sWm, 0, false, new TaskDescription(), null);
+
+        TaskWithBounds(TaskStack stack, WindowManagerService wm, Rect bounds) {
+            super(0, stack, 0, wm, 0, false, new TaskDescription(), null);
             mBounds = bounds;
             setBounds(bounds);
         }
@@ -113,14 +113,12 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-
         // Just any non zero value.
-        sWm.mSystemDecorLayer = 10000;
+        mWm.mSystemDecorLayer = 10000;
 
         mWindowToken = WindowTestUtils.createTestAppWindowToken(
-                sWm.getDefaultDisplayContentLocked());
-        mStubStack = new TaskStack(sWm, 0, null);
+                mWm.getDefaultDisplayContentLocked());
+        mStubStack = new TaskStack(mWm, 0, null);
     }
 
     // Do not use this function directly in the tests below. Instead, use more explicit function
@@ -170,9 +168,10 @@
     }
 
     @Test
-    public void testLayoutInFullscreenTaskInsets() throws Exception {
-        Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
-        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+    public void testLayoutInFullscreenTaskInsets() {
+        // fullscreen task doesn't use bounds for computeFrame
+        final Task task = new TaskWithBounds(mStubStack, mWm, null);
+        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final int bottomContentInset = 100;
@@ -227,9 +226,10 @@
     }
 
     @Test
-    public void testLayoutInFullscreenTaskNoInsets() throws Exception {
-        Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
-        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+    public void testLayoutInFullscreenTaskNoInsets() {
+        // fullscreen task doesn't use bounds for computeFrame
+        final Task task = new TaskWithBounds(mStubStack, mWm, null);
+        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         // With no insets or system decor all the frames incoming from PhoneWindowManager
@@ -307,7 +307,7 @@
 
     @Test
     public void testLayoutNonfullscreenTask() {
-        final DisplayInfo displayInfo = sWm.getDefaultDisplayContentLocked().getDisplayInfo();
+        final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo();
         final int logicalWidth = displayInfo.logicalWidth;
         final int logicalHeight = displayInfo.logicalHeight;
 
@@ -316,9 +316,9 @@
         final int taskRight = logicalWidth / 4 * 3;
         final int taskBottom = logicalHeight / 4 * 3;
         final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
-        TaskWithBounds task = new TaskWithBounds(taskBounds);
+        final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, taskBounds);
         task.mFullscreenForTest = false;
-        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
@@ -367,7 +367,7 @@
     @Test
     public void testCalculatePolicyCrop() {
         final WindowStateWithTask w = createWindow(
-                new TaskWithBounds(null), FILL_PARENT, FILL_PARENT);
+                new TaskWithBounds(mStubStack, mWm, null), MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
@@ -423,7 +423,7 @@
     @Test
     public void testLayoutLetterboxedWindow() {
         // First verify task behavior in multi-window mode.
-        final DisplayInfo displayInfo = sWm.getDefaultDisplayContentLocked().getDisplayInfo();
+        final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo();
         final int logicalWidth = displayInfo.logicalWidth;
         final int logicalHeight = displayInfo.logicalHeight;
 
@@ -432,10 +432,10 @@
         final int taskRight = logicalWidth / 4 * 3;
         final int taskBottom = logicalHeight / 4 * 3;
         final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
-        TaskWithBounds task = new TaskWithBounds(taskBounds);
+        final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, taskBounds);
         task.mInsetBounds.set(taskLeft, taskTop, taskRight, taskBottom);
         task.mFullscreenForTest = false;
-        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
@@ -467,8 +467,8 @@
     @Test
     public void testDisplayCutout() {
         // Regular fullscreen task and window
-        Task task = new TaskWithBounds(null);
-        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        final Task task = new TaskWithBounds(mStubStack, mWm, null);
+        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, 1000, 2000);
@@ -491,10 +491,11 @@
     @Test
     public void testDisplayCutout_tempInsetBounds() {
         // Regular fullscreen task and window
-        TaskWithBounds task = new TaskWithBounds(new Rect(0, -500, 1000, 1500));
+        final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm,
+                new Rect(0, -500, 1000, 1500));
         task.mFullscreenForTest = false;
         task.mInsetBounds.set(0, 0, 1000, 2000);
-        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, -500, 1000, 1500);
@@ -519,7 +520,6 @@
         attrs.width = width;
         attrs.height = height;
 
-        return new WindowStateWithTask(attrs, task);
+        return new WindowStateWithTask(mWm, mIWindow, mWindowToken, attrs, task);
     }
-
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
index 389eba5..9a13efb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -19,6 +19,8 @@
 import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -33,6 +35,7 @@
 import android.hardware.display.DisplayManagerInternal;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
+import android.view.Display;
 import android.view.InputChannel;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
@@ -50,8 +53,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import androidx.test.InstrumentationRegistry;
-
 /**
  * A test rule that sets up a fresh WindowManagerService instance before each test and makes sure
  * to properly tear it down after.
@@ -88,7 +89,7 @@
             }
 
             private void setUp() {
-                final Context context = InstrumentationRegistry.getTargetContext();
+                final Context context = getInstrumentation().getTargetContext();
 
                 removeServices();
 
@@ -114,7 +115,7 @@
                         runnable.run();
                     }
                     return null;
-                }).when(atm).notifyKeyguardFlagsChanged(any());
+                }).when(atm).notifyKeyguardFlagsChanged(any(), anyInt());
 
                 InputManagerService ims = mock(InputManagerService.class);
                 // InputChannel is final and can't be mocked.
@@ -123,9 +124,10 @@
                     doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
                 }
 
-                mService = WindowManagerService.main(context, ims, false,
-                        false, mPolicy = new TestWindowManagerPolicy(
-                                WindowManagerServiceRule.this::getWindowManagerService));
+                mService = WindowManagerService.main(context, ims, false, false,
+                        mPolicy = new TestWindowManagerPolicy(
+                                WindowManagerServiceRule.this::getWindowManagerService),
+                        new WindowManagerGlobalLock());
                 mService.mTransactionFactory = () -> {
                     final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
                     mSurfaceTransactions.add(new WeakReference<>(transaction));
@@ -142,11 +144,11 @@
 
                 mService.onInitReady();
 
+                final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+                final DisplayWindowController dcw = new DisplayWindowController(display, mService);
                 // Display creation is driven by the ActivityManagerService via ActivityStackSupervisor.
                 // We emulate those steps here.
-                mService.mRoot.createDisplayContent(
-                        mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY),
-                        mock(DisplayWindowController.class));
+                mService.mRoot.createDisplayContent(display, dcw);
             }
 
             private void removeServices() {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
index 570a853..343d359 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
@@ -22,13 +22,14 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
-@RunWith(AndroidJUnit4.class)
+/**
+ * Build/InstallRun:
+ *  atest FrameworksServicesTests:WindowManagerServiceRuleTest
+ */
 @Presubmit
 @SmallTest
 public class WindowManagerServiceRuleTest {
@@ -40,4 +41,4 @@
     public void testWindowManagerSetUp() {
         assertThat(mRule.getWindowManagerService(), notNullValue());
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 3637baf..118ce89 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -58,32 +58,28 @@
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
-import java.util.Arrays;
 import java.util.LinkedList;
 
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
 /**
  * Tests for the {@link WindowState} class.
  *
- * atest FrameworksServicesTests:com.android.server.wm.WindowStateTests
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:WindowStateTests
  */
-@SmallTest
 @FlakyTest(bugId = 74078662)
-// TODO(b/116597907): Re-enable this test in postsubmit after the bug is fixed.
-// @Presubmit
-@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
 public class WindowStateTests extends WindowTestsBase {
 
     @Test
-    public void testIsParentWindowHidden() throws Exception {
+    public void testIsParentWindowHidden() {
         final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
         final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
         final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
@@ -98,11 +94,10 @@
         assertFalse(parentWindow.isParentWindowHidden());
         assertFalse(child1.isParentWindowHidden());
         assertFalse(child2.isParentWindowHidden());
-
     }
 
     @Test
-    public void testIsChildWindow() throws Exception {
+    public void testIsChildWindow() {
         final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
         final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
         final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
@@ -115,7 +110,7 @@
     }
 
     @Test
-    public void testHasChild() throws Exception {
+    public void testHasChild() {
         final WindowState win1 = createWindow(null, TYPE_APPLICATION, "win1");
         final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW, "win11");
         final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW, "win12");
@@ -136,7 +131,7 @@
     }
 
     @Test
-    public void testGetParentWindow() throws Exception {
+    public void testGetParentWindow() {
         final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
         final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
         final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
@@ -157,7 +152,7 @@
     }
 
     @Test
-    public void testGetTopParentWindow() throws Exception {
+    public void testGetTopParentWindow() {
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
         final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
         final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
@@ -183,7 +178,7 @@
     }
 
     @Test
-    public void testCanBeImeTarget() throws Exception {
+    public void testCanBeImeTarget() {
         final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
         final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow");
 
@@ -219,7 +214,7 @@
     }
 
     @Test
-    public void testGetWindow() throws Exception {
+    public void testGetWindow() {
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
         final WindowState mediaChild = createWindow(root, TYPE_APPLICATION_MEDIA, "mediaChild");
         final WindowState mediaOverlayChild = createWindow(root,
@@ -231,7 +226,7 @@
         final WindowState aboveSubPanelChild = createWindow(root,
                 TYPE_APPLICATION_ABOVE_SUB_PANEL, "aboveSubPanelChild");
 
-        final LinkedList<WindowState> windows = new LinkedList();
+        final LinkedList<WindowState> windows = new LinkedList<>();
 
         root.getWindow(w -> {
             windows.addLast(w);
@@ -249,7 +244,7 @@
     }
 
     @Test
-    public void testPrepareWindowToDisplayDuringRelayout() throws Exception {
+    public void testPrepareWindowToDisplayDuringRelayout() {
         testPrepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
         testPrepareWindowToDisplayDuringRelayout(true /*wasVisible*/);
 
@@ -262,14 +257,14 @@
         final WindowState second = createWindow(null, TYPE_APPLICATION, appWindowToken, "second");
         second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
 
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(mPowerManagerWrapper, never()).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyString());
         assertTrue(appWindowToken.canTurnScreenOn());
 
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(mPowerManagerWrapper).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
         assertFalse(appWindowToken.canTurnScreenOn());
 
         // Call prepareWindowToDisplayDuringRelayout for two window that have FLAG_TURN_SCREEN_ON
@@ -278,14 +273,14 @@
         first.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
         second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
 
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(mPowerManagerWrapper).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
         assertFalse(appWindowToken.canTurnScreenOn());
 
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(mPowerManagerWrapper, never()).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyString());
         assertFalse(appWindowToken.canTurnScreenOn());
 
         // Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an
@@ -299,17 +294,17 @@
         firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
         secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
 
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(mPowerManagerWrapper).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
 
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(mPowerManagerWrapper).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
     }
 
     @Test
-    public void testCanAffectSystemUiFlags() throws Exception {
+    public void testCanAffectSystemUiFlags() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         app.mToken.setHidden(false);
         assertTrue(app.canAffectSystemUiFlags());
@@ -318,11 +313,10 @@
         app.mToken.setHidden(false);
         app.mAttrs.alpha = 0.0f;
         assertFalse(app.canAffectSystemUiFlags());
-
     }
 
     @Test
-    public void testCanAffectSystemUiFlags_disallow() throws Exception {
+    public void testCanAffectSystemUiFlags_disallow() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         app.mToken.setHidden(false);
         assertTrue(app.canAffectSystemUiFlags());
@@ -331,7 +325,7 @@
     }
 
     @Test
-    public void testIsSelfOrAncestorWindowAnimating() throws Exception {
+    public void testIsSelfOrAncestorWindowAnimating() {
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
         final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
         final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
@@ -344,7 +338,7 @@
     }
 
     @Test
-    public void testLayoutSeqResetOnReparent() throws Exception {
+    public void testLayoutSeqResetOnReparent() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         app.mLayoutSeq = 1;
         mDisplayContent.mLayoutSeq = 1;
@@ -355,7 +349,7 @@
     }
 
     @Test
-    public void testDisplayIdUpdatedOnReparent() throws Exception {
+    public void testDisplayIdUpdatedOnReparent() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         // fake a different display
         app.mInputWindowHandle.displayId = mDisplayContent.getDisplayId() + 1;
@@ -418,11 +412,11 @@
     }
 
     private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
         root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
 
         root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/);
-        verify(mPowerManagerWrapper).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java
deleted file mode 100644
index 057f047..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.wm;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-
-import static junit.framework.Assert.assertEquals;
-
-import android.platform.test.annotations.Presubmit;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class WindowSurfacePlacerTest extends WindowTestsBase {
-
-    private WindowSurfacePlacer mWindowSurfacePlacer;
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-        mWindowSurfacePlacer = new WindowSurfacePlacer(sWm);
-    }
-
-    @Test
-    public void testTranslucentOpen() throws Exception {
-        synchronized (sWm.mWindowMap) {
-            final AppWindowToken behind = createAppWindowToken(mDisplayContent,
-                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-            final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
-                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-            translucentOpening.setFillsParent(false);
-            translucentOpening.setHidden(true);
-            sWm.mOpeningApps.add(behind);
-            sWm.mOpeningApps.add(translucentOpening);
-            assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
-                    mWindowSurfacePlacer.maybeUpdateTransitToTranslucentAnim(TRANSIT_TASK_OPEN));
-        }
-    }
-
-    @Test
-    public void testTranslucentClose() throws Exception {
-        synchronized (sWm.mWindowMap) {
-            final AppWindowToken behind = createAppWindowToken(mDisplayContent,
-                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-            final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent,
-                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-            translucentClosing.setFillsParent(false);
-            sWm.mClosingApps.add(translucentClosing);
-            assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
-                    mWindowSurfacePlacer.maybeUpdateTransitToTranslucentAnim(TRANSIT_TASK_CLOSE));
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index a1b1640..b318b91 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -11,11 +11,26 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
 
+import static android.app.AppOpsManager.OP_NONE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -26,23 +41,9 @@
 import android.view.IApplicationToken;
 import android.view.IWindow;
 import android.view.Surface;
-import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowManager;
 
-import static android.app.AppOpsManager.OP_NONE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 import org.mockito.invocation.InvocationOnMock;
 
 /**
@@ -50,17 +51,7 @@
  * to WindowManager related test functionality.
  */
 public class WindowTestUtils {
-    public static int sNextTaskId = 0;
-
-    /**
-     * Retrieves an instance of a mock {@link WindowManagerService}.
-     */
-    public static WindowManagerService getMockWindowManagerService() {
-        final WindowManagerService service = mock(WindowManagerService.class);
-        final WindowHashMap windowMap = new WindowHashMap();
-        when(service.getWindowManagerLock()).thenReturn(windowMap);
-        return service;
-    }
+    private static int sNextTaskId = 0;
 
     /** An extension of {@link DisplayContent} to gain package scoped access. */
     public static class TestDisplayContent extends DisplayContent {
@@ -116,7 +107,7 @@
     /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
     public static Task createTaskInStack(WindowManagerService service, TaskStack stack,
             int userId) {
-        synchronized (service.mWindowMap) {
+        synchronized (service.mGlobalLock) {
             final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false,
                     new ActivityManager.TaskDescription(), null);
             stack.addTask(newTask, POSITION_TOP);
@@ -140,7 +131,7 @@
     }
 
     static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) {
-        synchronized (dc.mService.mWindowMap) {
+        synchronized (dc.mService.mGlobalLock) {
             return new TestAppWindowToken(dc);
         }
     }
@@ -213,7 +204,7 @@
 
     static TestWindowToken createTestWindowToken(int type, DisplayContent dc,
             boolean persistOnEmpty) {
-        synchronized (dc.mService.mWindowMap) {
+        synchronized (dc.mService.mGlobalLock) {
             return new TestWindowToken(type, dc, persistOnEmpty);
         }
     }
@@ -278,35 +269,33 @@
      */
     public static class TestTaskWindowContainerController extends TaskWindowContainerController {
 
+        static final TaskWindowContainerListener NOP_LISTENER = new TaskWindowContainerListener() {
+            @Override
+            public void registerConfigurationChangeListener(
+                    ConfigurationContainerListener listener) {
+            }
+
+            @Override
+            public void unregisterConfigurationChangeListener(
+                    ConfigurationContainerListener listener) {
+            }
+
+            @Override
+            public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
+            }
+
+            @Override
+            public void requestResize(Rect bounds, int resizeMode) {
+            }
+        };
+
         TestTaskWindowContainerController(WindowTestsBase testsBase) {
             this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent));
         }
 
         TestTaskWindowContainerController(StackWindowController stackController) {
-            super(sNextTaskId++, new TaskWindowContainerListener() {
-                        @Override
-                        public void registerConfigurationChangeListener(
-                                ConfigurationContainerListener listener) {
-
-                        }
-
-                        @Override
-                        public void unregisterConfigurationChangeListener(
-                                ConfigurationContainerListener listener) {
-
-                        }
-
-                        @Override
-                        public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
-
-                        }
-
-                        @Override
-                        public void requestResize(Rect bounds, int resizeMode) {
-
-                        }
-                    }, stackController, 0 /* userId */, null /* bounds */, RESIZE_MODE_UNRESIZEABLE,
-                    false /* supportsPictureInPicture */, true /* toTop*/,
+            super(sNextTaskId++, NOP_LISTENER, stackController, 0 /* userId */, null /* bounds */,
+                    RESIZE_MODE_UNRESIZEABLE, false /* supportsPictureInPicture */, true /* toTop*/,
                     true /* showForAllUsers */, new ActivityManager.TaskDescription(),
                     stackController.mService);
         }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index cf67d78..945cbb9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -35,6 +35,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.mockito.Mockito.mock;
 
 import android.content.Context;
@@ -52,13 +54,12 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Rule;
 
 import java.util.HashSet;
 import java.util.LinkedList;
 
-import androidx.test.InstrumentationRegistry;
-
 /**
  * Common base class for window manager unit test classes.
  *
@@ -66,7 +67,8 @@
  */
 class WindowTestsBase {
     private static final String TAG = WindowTestsBase.class.getSimpleName();
-    WindowManagerService sWm = null;  // TODO(roosa): rename to mWm in follow-up CL
+
+    WindowManagerService mWm;
     private final IWindow mIWindow = new TestIWindow();
     private Session mMockSession;
     // The default display is removed in {@link #setUp} and then we iterate over all displays to
@@ -75,6 +77,7 @@
     private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
     static int sNextStackId = 1000;
 
+    /** Non-default display. */
     DisplayContent mDisplayContent;
     DisplayInfo mDisplayInfo = new DisplayInfo();
     WindowState mWallpaperWindow;
@@ -96,32 +99,36 @@
     @Rule
     public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
 
-    static WindowState.PowerManagerWrapper mPowerManagerWrapper;  // TODO(roosa): make non-static.
+    static WindowState.PowerManagerWrapper sPowerManagerWrapper;  // TODO(roosa): make non-static.
+
+    @BeforeClass
+    public static void setUpOnceBase() {
+        AttributeCache.init(getInstrumentation().getTargetContext());
+        sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
+    }
 
     @Before
-    public void setUp() throws Exception {
+    public void setUpBase() {
         // If @Before throws an exception, the error isn't logged. This will make sure any failures
         // in the set up are clear. This can be removed when b/37850063 is fixed.
         try {
             mMockSession = mock(Session.class);
-            mPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
 
-            final Context context = InstrumentationRegistry.getTargetContext();
-            AttributeCache.init(context);
+            final Context context = getInstrumentation().getTargetContext();
 
-            sWm = mWmRule.getWindowManagerService();
+            mWm = mWmRule.getWindowManagerService();
             beforeCreateDisplay();
 
-            mWallpaperController = new WallpaperController(sWm);
+            mWallpaperController = new WallpaperController(mWm);
 
             context.getDisplay().getDisplayInfo(mDisplayInfo);
             mDisplayContent = createNewDisplay();
-            sWm.mDisplayEnabled = true;
-            sWm.mDisplayReady = true;
+            mWm.mDisplayEnabled = true;
+            mWm.mDisplayReady = true;
 
             // Set-up some common windows.
-            mCommonWindows = new HashSet();
-            synchronized (sWm.mWindowMap) {
+            mCommonWindows = new HashSet<>();
+            synchronized (mWm.mGlobalLock) {
                 mWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow");
                 mImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "mImeWindow");
                 mDisplayContent.mInputMethodWindow = mImeWindow;
@@ -153,7 +160,7 @@
     }
 
     @After
-    public void tearDown() throws Exception {
+    public void tearDownBase() {
         // If @After throws an exception, the error isn't logged. This will make sure any failures
         // in the tear down are clear. This can be removed when b/37850063 is fixed.
         try {
@@ -163,8 +170,8 @@
 
             final LinkedList<WindowState> nonCommonWindows = new LinkedList<>();
 
-            synchronized (sWm.mWindowMap) {
-                sWm.mRoot.forAllWindows(w -> {
+            synchronized (mWm.mGlobalLock) {
+                mWm.mRoot.forAllWindows(w -> {
                     if (!mCommonWindows.contains(w)) {
                         nonCommonWindows.addLast(w);
                     }
@@ -174,15 +181,18 @@
                     nonCommonWindows.pollLast().removeImmediately();
                 }
 
-                for (int i = sWm.mRoot.mChildren.size() - 1; i >= 0; --i) {
-                    final DisplayContent displayContent = sWm.mRoot.mChildren.get(i);
+                for (int i = mWm.mRoot.mChildren.size() - 1; i >= 0; --i) {
+                    final DisplayContent displayContent = mWm.mRoot.mChildren.get(i);
                     if (!displayContent.isDefaultDisplay) {
                         displayContent.removeImmediately();
                     }
                 }
-                sWm.mInputMethodTarget = null;
-                sWm.mClosingApps.clear();
-                sWm.mOpeningApps.clear();
+                // Remove app transition & window freeze timeout callbacks to prevent unnecessary
+                // actions after test.
+                mWm.getDefaultDisplayContentLocked().mAppTransition
+                        .removeAppTransitionTimeoutCallbacks();
+                mWm.mH.removeMessages(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT);
+                mWm.mInputMethodTarget = null;
             }
 
             // Wait until everything is really cleaned up.
@@ -194,7 +204,7 @@
     }
 
     private WindowState createCommonWindow(WindowState parent, int type, String name) {
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             final WindowState win = createWindow(parent, type, name);
             mCommonWindows.add(win);
             // Prevent common windows from been IMe targets
@@ -212,7 +222,7 @@
 
     private WindowToken createWindowToken(
             DisplayContent dc, int windowingMode, int activityType, int type) {
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) {
                 return WindowTestUtils.createTestWindowToken(type, dc);
             }
@@ -237,7 +247,7 @@
     }
 
     WindowState createWindow(WindowState parent, int type, String name) {
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             return (parent == null)
                     ? createWindow(parent, type, mDisplayContent, name)
                     : createWindow(parent, type, parent.mToken, name);
@@ -246,14 +256,14 @@
 
     WindowState createWindowOnStack(WindowState parent, int windowingMode, int activityType,
             int type, DisplayContent dc, String name) {
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             final WindowToken token = createWindowToken(dc, windowingMode, activityType, type);
             return createWindow(parent, type, token, name);
         }
     }
 
     WindowState createAppWindow(Task task, int type, String name) {
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
             task.addChild(token, 0);
             return createWindow(null, type, token, name);
@@ -261,7 +271,7 @@
     }
 
     WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             final WindowToken token = createWindowToken(
                     dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
             return createWindow(parent, type, token, name);
@@ -270,7 +280,7 @@
 
     WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
             boolean ownerCanAddInternalSystemWindow) {
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             final WindowToken token = createWindowToken(
                     dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
             return createWindow(parent, type, token, name, 0 /* ownerId */,
@@ -279,7 +289,7 @@
     }
 
     WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             return createWindow(parent, type, token, name, 0 /* ownerId */,
                     false /* ownerCanAddInternalSystemWindow */);
         }
@@ -288,20 +298,20 @@
     WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
             int ownerId, boolean ownerCanAddInternalSystemWindow) {
         return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow,
-                sWm, mMockSession, mIWindow);
+                mWm, mMockSession, mIWindow);
     }
 
     static WindowState createWindow(WindowState parent, int type, WindowToken token,
             String name, int ownerId, boolean ownerCanAddInternalSystemWindow,
             WindowManagerService service, Session session, IWindow iWindow) {
-        synchronized (service.mWindowMap) {
+        synchronized (service.mGlobalLock) {
             final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
             attrs.setTitle(name);
 
             final WindowState w = new WindowState(service, session, iWindow, token, parent,
                     OP_NONE,
                     0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow,
-                    mPowerManagerWrapper);
+                    sPowerManagerWrapper);
             // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
             // adding it to the token...
             token.addWindow(w);
@@ -311,13 +321,13 @@
 
     /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */
     TaskStack createTaskStackOnDisplay(DisplayContent dc) {
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             return createStackControllerOnDisplay(dc).mContainer;
         }
     }
 
     StackWindowController createStackControllerOnDisplay(DisplayContent dc) {
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             return createStackControllerOnStackOnDisplay(
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
         }
@@ -325,13 +335,13 @@
 
     StackWindowController createStackControllerOnStackOnDisplay(
             int windowingMode, int activityType, DisplayContent dc) {
-        synchronized (sWm.mWindowMap) {
+        synchronized (mWm.mGlobalLock) {
             final Configuration overrideConfig = new Configuration();
             overrideConfig.windowConfiguration.setWindowingMode(windowingMode);
             overrideConfig.windowConfiguration.setActivityType(activityType);
             final int stackId = ++sNextStackId;
             final StackWindowController controller = new StackWindowController(stackId, null,
-                    dc.getDisplayId(), true /* onTop */, new Rect(), sWm);
+                    dc.getDisplayId(), true /* onTop */, new Rect(), mWm);
             controller.onOverrideConfigurationChanged(overrideConfig);
             return controller;
         }
@@ -339,7 +349,7 @@
 
     /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
     Task createTaskInStack(TaskStack stack, int userId) {
-        return WindowTestUtils.createTaskInStack(sWm, stack, userId);
+        return WindowTestUtils.createTaskInStack(mWm, stack, userId);
     }
 
     /** Creates a {@link DisplayContent} and adds it to the system. */
@@ -347,18 +357,43 @@
         final int displayId = sNextDisplayId++;
         final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                 mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-        synchronized (sWm.mWindowMap) {
-            return new DisplayContent(display, sWm, mWallpaperController,
+        synchronized (mWm.mGlobalLock) {
+            return new DisplayContent(display, mWm, mWallpaperController,
                     mock(DisplayWindowController.class));
         }
     }
 
+    /**
+     * Creates a {@link DisplayContent} with given display state and adds it to the system.
+     *
+     * Unlike {@link #createNewDisplay()} that uses a mock {@link DisplayWindowController} to
+     * initialize {@link DisplayContent}, this method used real controller object when the test
+     * need to verify its related flows.
+     *
+     * @param displayState For initializing the state of the display. See
+     *                     {@link Display#getState()}.
+     */
+    DisplayContent createNewDisplayWithController(int displayState) {
+        // Leverage main display info & initialize it with display state for given displayId.
+        DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.copyFrom(mDisplayInfo);
+        displayInfo.state = displayState;
+        final int displayId = sNextDisplayId++;
+        final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+                displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
+        final DisplayWindowController dcw = new DisplayWindowController(display, mWm);
+        synchronized (mWm.mGlobalLock) {
+            // Display creation is driven by DisplayWindowController via ActivityStackSupervisor.
+            // We skip those steps here.
+            return mWm.mRoot.createDisplayContent(display, dcw);
+        }
+    }
+
     /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
     WindowTestUtils.TestWindowState createWindowState(WindowManager.LayoutParams attrs,
             WindowToken token) {
-        synchronized (sWm.mWindowMap) {
-            return new WindowTestUtils.TestWindowState(sWm, mMockSession, mIWindow, attrs, token);
+        synchronized (mWm.mGlobalLock) {
+            return new WindowTestUtils.TestWindowState(mWm, mMockSession, mIWindow, attrs, token);
         }
     }
-
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
index 3732486..3048f1a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -30,25 +30,22 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link WindowToken} class.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowTokenTests
+ *  atest FrameworksServicesTests:WindowTokenTests
  */
-@SmallTest
 @FlakyTest(bugId = 74078662)
+@SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class WindowTokenTests extends WindowTestsBase {
 
     @Test
-    public void testAddWindow() throws Exception {
+    public void testAddWindow() {
         final WindowTestUtils.TestWindowToken token =
                 WindowTestUtils.createTestWindowToken(0, mDisplayContent);
 
@@ -78,7 +75,7 @@
     }
 
     @Test
-    public void testChildRemoval() throws Exception {
+    public void testChildRemoval() {
         final DisplayContent dc = mDisplayContent;
         final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken(0, dc);
 
@@ -102,7 +99,7 @@
      * Tokens should only be removed from the system when all their windows are gone.
      */
     @Test
-    public void testTokenRemovalProcess() throws Exception {
+    public void testTokenRemovalProcess() {
         final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken(
                 TYPE_TOAST, mDisplayContent, true /* persistOnEmpty */);
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
deleted file mode 100644
index 01b7c4f..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.wm;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-import android.content.Context;
-import android.platform.test.annotations.Presubmit;
-import android.util.proto.ProtoOutputStream;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.Preconditions;
-import com.android.server.wm.WindowManagerTraceProto;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
-
-/**
- * Test class for {@link WindowTracing}.
- *
- * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowTracingTest
- */
-@SmallTest
-@FlakyTest(bugId = 74078662)
-// TODO(b/116597907): Re-enable this test in postsubmit after the bug is fixed.
-// @Presubmit
-@RunWith(AndroidJUnit4.class)
-public class WindowTracingTest extends WindowTestsBase {
-
-    private static final byte[] MAGIC_HEADER = new byte[] {
-        0x9, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45,
-    };
-
-    private Context mTestContext;
-    private WindowTracing mWindowTracing;
-    private WindowManagerService mWmMock;
-    private File mFile;
-
-    @Override
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-
-        mWmMock = mock(WindowManagerService.class);
-
-        mTestContext = InstrumentationRegistry.getContext();
-
-        mFile = mTestContext.getFileStreamPath("tracing_test.dat");
-        mFile.delete();
-
-        mWindowTracing = new WindowTracing(mFile);
-    }
-
-    @Test
-    public void isEnabled_returnsFalseByDefault() throws Exception {
-        assertFalse(mWindowTracing.isEnabled());
-    }
-
-    @Test
-    public void isEnabled_returnsTrueAfterStart() throws Exception {
-        mWindowTracing.startTrace(mock(PrintWriter.class));
-        assertTrue(mWindowTracing.isEnabled());
-    }
-
-    @Test
-    public void isEnabled_returnsFalseAfterStop() throws Exception {
-        mWindowTracing.startTrace(mock(PrintWriter.class));
-        mWindowTracing.stopTrace(mock(PrintWriter.class));
-        assertFalse(mWindowTracing.isEnabled());
-    }
-
-    @Test
-    public void trace_discared_whenNotTracing() throws Exception {
-        mWindowTracing.traceStateLocked("where", mWmMock);
-        verifyZeroInteractions(mWmMock);
-    }
-
-    @Test
-    public void trace_dumpsWindowManagerState_whenTracing() throws Exception {
-        mWindowTracing.startTrace(mock(PrintWriter.class));
-        mWindowTracing.traceStateLocked("where", mWmMock);
-
-        verify(mWmMock).writeToProtoLocked(any(), eq(true));
-    }
-
-    @Test
-    public void traceFile_startsWithMagicHeader() throws Exception {
-        mWindowTracing.startTrace(mock(PrintWriter.class));
-        mWindowTracing.stopTrace(mock(PrintWriter.class));
-
-        byte[] header = new byte[MAGIC_HEADER.length];
-        try (InputStream is = new FileInputStream(mFile)) {
-            assertEquals(MAGIC_HEADER.length, is.read(header));
-            assertArrayEquals(MAGIC_HEADER, header);
-        }
-    }
-
-    @Test
-    @Ignore("Figure out why this test is crashing when setting up mWmMock.")
-    public void tracing_endsUpInFile() throws Exception {
-        mWindowTracing.startTrace(mock(PrintWriter.class));
-
-        doAnswer((inv) -> {
-            inv.<ProtoOutputStream>getArgument(0).write(
-                    WindowManagerTraceProto.WHERE, "TEST_WM_PROTO");
-            return null;
-        }).when(mWmMock).writeToProtoLocked(any(), any());
-        mWindowTracing.traceStateLocked("TEST_WHERE", mWmMock);
-
-        mWindowTracing.stopTrace(mock(PrintWriter.class));
-
-        byte[] file = new byte[1000];
-        int fileLength;
-        try (InputStream is = new FileInputStream(mFile)) {
-            fileLength = is.read(file);
-            assertTrue(containsBytes(file, fileLength,
-                    "TEST_WHERE".getBytes(StandardCharsets.UTF_8)));
-            assertTrue(containsBytes(file, fileLength,
-                    "TEST_WM_PROTO".getBytes(StandardCharsets.UTF_8)));
-        }
-    }
-
-    @Override
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
-
-        mFile.delete();
-    }
-
-    /** Return true if {@code needle} appears anywhere in {@code haystack[0..length]} */
-    boolean containsBytes(byte[] haystack, int haystackLenght, byte[] needle) {
-        Preconditions.checkArgument(haystackLenght > 0);
-        Preconditions.checkArgument(needle.length > 0);
-
-        outer: for (int i = 0; i <= haystackLenght - needle.length; i++) {
-            for (int j = 0; j < needle.length; j++) {
-                if (haystack[i+j] != needle[j]) {
-                    continue outer;
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-
-    @Test
-    public void test_containsBytes() {
-        byte[] haystack = "hello_world".getBytes(StandardCharsets.UTF_8);
-        assertTrue(containsBytes(haystack, haystack.length,
-                "hello".getBytes(StandardCharsets.UTF_8)));
-        assertTrue(containsBytes(haystack, haystack.length,
-                "world".getBytes(StandardCharsets.UTF_8)));
-        assertFalse(containsBytes(haystack, 6,
-                "world".getBytes(StandardCharsets.UTF_8)));
-        assertFalse(containsBytes(haystack, haystack.length,
-                "world_".getBytes(StandardCharsets.UTF_8)));
-        assertFalse(containsBytes(haystack, haystack.length,
-                "absent".getBytes(StandardCharsets.UTF_8)));
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
index 3a8c4ae..32e4e02 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -38,27 +38,30 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
 import org.junit.After;
 import org.junit.Test;
 
 import java.util.HashMap;
 import java.util.LinkedList;
 
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
 /**
- * Tests for the {@link WindowLayersController} class.
+ * Tests for the {@link DisplayContent#assignChildLayers(SurfaceControl.Transaction)} method.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.ZOrderingTests
+ *  atest FrameworksServicesTests:ZOrderingTests
  */
-@SmallTest
 @FlakyTest(bugId = 74078662)
+@SmallTest
 @Presubmit
 public class ZOrderingTests extends WindowTestsBase {
 
-    private class LayerRecordingTransaction extends SurfaceControl.Transaction {
+    private static class LayerRecordingTransaction extends SurfaceControl.Transaction {
+        // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder
+        // such that we can keep track of the parents of Surfaces as they are constructed.
+        private final HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap<>();
         HashMap<SurfaceControl, Integer> mLayersForControl = new HashMap<>();
         HashMap<SurfaceControl, SurfaceControl> mRelativeLayersForControl = new HashMap<>();
 
@@ -85,17 +88,28 @@
         private SurfaceControl getRelativeLayer(SurfaceControl sc) {
             return mRelativeLayersForControl.get(sc);
         }
+
+        void addParentFor(SurfaceControl child, SurfaceControl parent) {
+            mParentFor.put(child, parent);
+        }
+
+        SurfaceControl getParentFor(SurfaceControl child) {
+            return mParentFor.get(child);
+        }
+
+        @Override
+        public void close() {
+
+        }
     }
 
-    // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder
-    // such that we can keep track of the parents of Surfaces as they are constructed.
-    private HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap<>();
+    private static class HierarchyRecorder extends SurfaceControl.Builder {
+        private LayerRecordingTransaction mTransaction;
+        private SurfaceControl mPendingParent;
 
-    private class HierarchyRecorder extends SurfaceControl.Builder {
-        SurfaceControl mPendingParent;
-
-        HierarchyRecorder(SurfaceSession s) {
+        HierarchyRecorder(SurfaceSession s, LayerRecordingTransaction transaction) {
             super(s);
+            mTransaction = transaction;
         }
 
         @Override
@@ -106,16 +120,26 @@
 
         @Override
         public SurfaceControl build() {
-            SurfaceControl sc = super.build();
-            mParentFor.put(sc, mPendingParent);
+            final SurfaceControl sc = super.build();
+            mTransaction.addParentFor(sc, mPendingParent);
+            mTransaction = null;
             mPendingParent = null;
             return sc;
         }
     }
 
-    private class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+    private static class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+        private LayerRecordingTransaction mTransaction;
+
+        HierarchyRecordingBuilderFactory(LayerRecordingTransaction transaction) {
+            mTransaction = transaction;
+        }
+
+        @Override
         public SurfaceControl.Builder make(SurfaceSession s) {
-            return new HierarchyRecorder(s);
+            final LayerRecordingTransaction transaction = mTransaction;
+            mTransaction = null;
+            return new HierarchyRecorder(s, transaction);
         }
     }
 
@@ -127,18 +151,17 @@
         // which is after construction of the DisplayContent, meaning the HierarchyRecorder
         // would miss construction of the top-level layers.
         mTransaction = new LayerRecordingTransaction();
-        sWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory();
-        sWm.mTransactionFactory = () -> mTransaction;
+        mWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory(mTransaction);
+        mWm.mTransactionFactory = () -> mTransaction;
     }
 
     @After
-    public void after() {
+    public void tearDown() {
         mTransaction.close();
-        mParentFor.keySet().forEach(SurfaceControl::destroy);
-        mParentFor.clear();
     }
 
-    LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t, SurfaceControl sc) {
+    private static LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t,
+            SurfaceControl sc) {
         LinkedList<SurfaceControl> p = new LinkedList<>();
         SurfaceControl current = sc;
         do {
@@ -148,23 +171,22 @@
             if (rs != null) {
                 current = rs;
             } else {
-                current = mParentFor.get(current);
+                current = t.getParentFor(current);
             }
         } while (current != null);
         return p;
     }
 
 
-    void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left,
+    private static void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left,
             SurfaceControl right) {
         final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
         final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
 
-        SurfaceControl commonAncestor = null;
         SurfaceControl leftTop = leftParentChain.peekLast();
         SurfaceControl rightTop = rightParentChain.peekLast();
         while (leftTop != null && rightTop != null && leftTop == rightTop) {
-            commonAncestor = leftParentChain.removeLast();
+            leftParentChain.removeLast();
             rightParentChain.removeLast();
             leftTop = leftParentChain.peekLast();
             rightTop = rightParentChain.peekLast();
@@ -189,7 +211,7 @@
 
     @Test
     public void testAssignWindowLayers_ForImeWithNoTarget() {
-        sWm.mInputMethodTarget = null;
+        mWm.mInputMethodTarget = null;
         mDisplayContent.assignChildLayers(mTransaction);
 
         // The Ime has an higher base layer than app windows and lower base layer than system
@@ -207,7 +229,7 @@
     @Test
     public void testAssignWindowLayers_ForImeWithAppTarget() {
         final WindowState imeAppTarget = createWindow("imeAppTarget");
-        sWm.mInputMethodTarget = imeAppTarget;
+        mWm.mInputMethodTarget = imeAppTarget;
 
         mDisplayContent.assignChildLayers(mTransaction);
 
@@ -233,7 +255,7 @@
                 TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
                 "imeAppTargetChildBelowWindow");
 
-        sWm.mInputMethodTarget = imeAppTarget;
+        mWm.mInputMethodTarget = imeAppTarget;
         mDisplayContent.assignChildLayers(mTransaction);
 
         // Ime should be above all app windows except for child windows that are z-ordered above it
@@ -255,7 +277,7 @@
         final WindowState imeAppTarget = createWindow("imeAppTarget");
         final WindowState appAboveImeTarget = createWindow("appAboveImeTarget");
 
-        sWm.mInputMethodTarget = imeAppTarget;
+        mWm.mInputMethodTarget = imeAppTarget;
         mDisplayContent.assignChildLayers(mTransaction);
 
         // Ime should be above all app windows except for non-fullscreen app window above it and
@@ -278,7 +300,7 @@
                 mDisplayContent, "imeSystemOverlayTarget",
                 true /* ownerCanAddInternalSystemWindow */);
 
-        sWm.mInputMethodTarget = imeSystemOverlayTarget;
+        mWm.mInputMethodTarget = imeSystemOverlayTarget;
         mDisplayContent.assignChildLayers(mTransaction);
 
         // The IME target base layer is higher than all window except for the nav bar window, so the
@@ -301,7 +323,7 @@
 
     @Test
     public void testAssignWindowLayers_ForStatusBarImeTarget() {
-        sWm.mInputMethodTarget = mStatusBarWindow;
+        mWm.mInputMethodTarget = mStatusBarWindow;
         mDisplayContent.assignChildLayers(mTransaction);
 
         assertWindowHigher(mImeWindow, mChildAppWindowAbove);
@@ -322,8 +344,8 @@
         final WindowState dockedStackWindow = createWindowOnStack(null,
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
                 mDisplayContent, "dockedStackWindow");
-        final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+        final WindowState assistantStackWindow = createWindowOnStack(null,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
                 mDisplayContent, "assistantStackWindow");
         final WindowState homeActivityWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION,
@@ -368,7 +390,8 @@
         final WindowState anyWindow = createWindow("anyWindow");
         final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent,
                 "TypeApplicationMediaChild");
-        final WindowState mediaOverlayChild = createWindow(anyWindow, TYPE_APPLICATION_MEDIA_OVERLAY,
+        final WindowState mediaOverlayChild = createWindow(anyWindow,
+                TYPE_APPLICATION_MEDIA_OVERLAY,
                 mDisplayContent, "TypeApplicationMediaOverlayChild");
 
         mDisplayContent.assignChildLayers(mTransaction);
@@ -388,8 +411,8 @@
         final WindowState splitScreenSecondaryWindow = createWindowOnStack(null,
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
                 TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
-        final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+        final WindowState assistantStackWindow = createWindowOnStack(null,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
                 mDisplayContent, "assistantStackWindow");
 
         mDisplayContent.assignChildLayers(mTransaction);
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
deleted file mode 100644
index f82b012..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.wm.utils;
-
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-
-import static com.android.server.wm.utils.CoordinateTransforms.transformLogicalToPhysicalCoordinates;
-import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
-
-
-import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.*;
-
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.view.DisplayInfo;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ErrorCollector;
-
-public class CoordinateTransformsTest {
-
-    private static final int W = 200;
-    private static final int H = 400;
-
-    private final Matrix mMatrix = new Matrix();
-    private final Matrix mMatrix2 = new Matrix();
-
-    @Rule
-    public final ErrorCollector mErrorCollector = new ErrorCollector();
-
-    @Before
-    public void setUp() throws Exception {
-        mMatrix.setTranslate(0xdeadbeef, 0xdeadbeef);
-        mMatrix2.setTranslate(0xbeefdead, 0xbeefdead);
-    }
-
-    @Test
-    public void transformPhysicalToLogicalCoordinates_rot0() {
-        transformPhysicalToLogicalCoordinates(ROTATION_0, W, H, mMatrix);
-        assertThat(mMatrix, is(Matrix.IDENTITY_MATRIX));
-    }
-
-    @Test
-    public void transformPhysicalToLogicalCoordinates_rot90() {
-        transformPhysicalToLogicalCoordinates(ROTATION_90, W, H, mMatrix);
-
-        checkPoint(0, 0).transformsTo(0, W);
-        checkPoint(W, H).transformsTo(H, 0);
-    }
-
-    @Test
-    public void transformPhysicalToLogicalCoordinates_rot180() {
-        transformPhysicalToLogicalCoordinates(ROTATION_180, W, H, mMatrix);
-
-        checkPoint(0, 0).transformsTo(W, H);
-        checkPoint(W, H).transformsTo(0, 0);
-    }
-
-    @Test
-    public void transformPhysicalToLogicalCoordinates_rot270() {
-        transformPhysicalToLogicalCoordinates(ROTATION_270, W, H, mMatrix);
-
-        checkPoint(0, 0).transformsTo(H, 0);
-        checkPoint(W, H).transformsTo(0, W);
-    }
-
-    @Test
-    public void transformLogicalToPhysicalCoordinates_rot0() {
-        transformLogicalToPhysicalCoordinates(ROTATION_0, W, H, mMatrix);
-        assertThat(mMatrix, is(Matrix.IDENTITY_MATRIX));
-    }
-
-    @Test
-    public void transformLogicalToPhysicalCoordinates_rot90() {
-        transformLogicalToPhysicalCoordinates(ROTATION_90, W, H, mMatrix);
-
-        checkPoint(0, W).transformsTo(0, 0);
-        checkPoint(H, 0).transformsTo(W, H);
-    }
-
-    @Test
-    public void transformLogicalToPhysicalCoordinates_rot180() {
-        transformLogicalToPhysicalCoordinates(ROTATION_180, W, H, mMatrix);
-
-        checkPoint(W, H).transformsTo(0, 0);
-        checkPoint(0, 0).transformsTo(W, H);
-    }
-
-    @Test
-    public void transformLogicalToPhysicalCoordinates_rot270() {
-        transformLogicalToPhysicalCoordinates(ROTATION_270, W, H, mMatrix);
-
-        checkPoint(H, 0).transformsTo(0, 0);
-        checkPoint(0, W).transformsTo(W, H);
-    }
-
-    @Test
-    public void transformLogicalToPhysicalCoordinatesIsInverse_rot0() {
-        transformLogicalToPhysicalCoordinates(ROTATION_0, W, H, mMatrix);
-        transformPhysicalToLogicalCoordinates(ROTATION_0, W, H, mMatrix2);
-
-        assertMatricesAreInverses(mMatrix, mMatrix2);
-    }
-
-    @Test
-    public void transformLogicalToPhysicalCoordinatesIsInverse_rot90() {
-        transformLogicalToPhysicalCoordinates(ROTATION_90, W, H, mMatrix);
-        transformPhysicalToLogicalCoordinates(ROTATION_90, W, H, mMatrix2);
-
-        assertMatricesAreInverses(mMatrix, mMatrix2);
-    }
-
-    @Test
-    public void transformLogicalToPhysicalCoordinatesIsInverse_rot180() {
-        transformLogicalToPhysicalCoordinates(ROTATION_180, W, H, mMatrix);
-        transformPhysicalToLogicalCoordinates(ROTATION_180, W, H, mMatrix2);
-
-        assertMatricesAreInverses(mMatrix, mMatrix2);
-    }
-
-    @Test
-    public void transformLogicalToPhysicalCoordinatesIsInverse_rot270() {
-        transformLogicalToPhysicalCoordinates(ROTATION_270, W, H, mMatrix);
-        transformPhysicalToLogicalCoordinates(ROTATION_270, W, H, mMatrix2);
-
-        assertMatricesAreInverses(mMatrix, mMatrix2);
-    }
-
-    @Test
-    public void transformBetweenRotations_rot180_rot270() {
-        // W,H are flipped, because they need to be given in the new orientation, i.e. ROT_270.
-        transformToRotation(ROTATION_180, ROTATION_270, H, W, mMatrix);
-
-        checkPoint(0, 0).transformsTo(0, W);
-        checkPoint(W, H).transformsTo(H, 0);
-    }
-
-    @Test
-    public void transformBetweenRotations_rot90_rot0() {
-        transformToRotation(ROTATION_180, ROTATION_270, W, H, mMatrix);
-
-        checkPoint(0, 0).transformsTo(0, H);
-        // H,W is bottom right in ROT_90
-        checkPoint(H, W).transformsTo(W, 0);
-    }
-
-    @Test
-    public void transformBetweenRotations_displayInfo() {
-        final DisplayInfo di = new DisplayInfo();
-        di.rotation = ROTATION_90;
-        di.logicalWidth = H;  // dimensions are flipped in ROT_90
-        di.logicalHeight = W;
-        transformToRotation(ROTATION_180, ROTATION_270, di, mMatrix);
-
-        // W,H are flipped, because they need to be given in the new orientation, i.e. ROT_270.
-        transformToRotation(ROTATION_180, ROTATION_270, H, W, mMatrix2);
-
-        assertEquals(mMatrix2, mMatrix);
-    }
-
-    private void assertMatricesAreInverses(Matrix matrix, Matrix matrix2) {
-        final Matrix concat = new Matrix();
-        concat.setConcat(matrix, matrix2);
-        assertTrue("expected identity, but was: " + concat, concat.isIdentity());
-    }
-
-    private TransformPointAssertable checkPoint(int x, int y) {
-        final Point devicePoint = new Point(x, y);
-        final float[] fs = new float[] {x, y};
-        mMatrix.mapPoints(fs);
-        final PointF transformedPoint = new PointF(fs[0], fs[1]);
-
-        return (expectedX, expectedY) -> {
-            mErrorCollector.checkThat("t(" + devicePoint + ")",
-                    transformedPoint, is(new PointF(expectedX, expectedY)));
-        };
-    }
-
-    public interface TransformPointAssertable {
-        void transformsTo(int x, int y);
-    }
-}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java
deleted file mode 100644
index ba8869b..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.wm.utils;
-
-import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
-import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-import static com.android.server.wm.utils.DisplayRotationUtil.getBoundIndexFromRotation;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
-
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-
-
-/**
- * Tests for {@link DisplayRotationUtil}
- *
- * Run with: atest DisplayRotationUtilTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class DisplayRotationUtilTest {
-    private static Rect ZERO_RECT = new Rect();
-
-    @Test
-    public void testGetBoundIndexFromRotation_rot0() {
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_0),
-                equalTo(BOUNDS_POSITION_LEFT));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_0),
-                equalTo(BOUNDS_POSITION_TOP));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_0),
-                equalTo(BOUNDS_POSITION_RIGHT));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_0),
-                equalTo(BOUNDS_POSITION_BOTTOM));
-    }
-
-    @Test
-    public void testGetBoundIndexFromRotation_rot90() {
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_90),
-                equalTo(BOUNDS_POSITION_BOTTOM));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_90),
-                equalTo(BOUNDS_POSITION_LEFT));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_90),
-                equalTo(BOUNDS_POSITION_TOP));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_90),
-                equalTo(BOUNDS_POSITION_RIGHT));
-    }
-
-    @Test
-    public void testGetBoundIndexFromRotation_rot180() {
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_180),
-                equalTo(BOUNDS_POSITION_RIGHT));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_180),
-                equalTo(BOUNDS_POSITION_BOTTOM));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_180),
-                equalTo(BOUNDS_POSITION_LEFT));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_180),
-                equalTo(BOUNDS_POSITION_TOP));
-    }
-
-    @Test
-    public void testGetBoundIndexFromRotation_rot270() {
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_270),
-                equalTo(BOUNDS_POSITION_TOP));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_270),
-                equalTo(BOUNDS_POSITION_RIGHT));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_270),
-                equalTo(BOUNDS_POSITION_BOTTOM));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_270),
-                equalTo(BOUNDS_POSITION_LEFT));
-
-    }
-
-    @Test
-    public void testGetRotatedBounds_top_rot0() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
-        assertThat(util.getRotatedBounds(bounds, ROTATION_0, 200, 300),
-                equalTo(bounds));
-    }
-
-    @Test
-    public void testGetRotatedBounds_top_rot90() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
-        assertThat(util.getRotatedBounds(bounds, ROTATION_90, 200, 300),
-                equalTo(new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT }));
-    }
-
-    @Test
-    public void testGetRotatedBounds_top_rot180() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
-        assertThat(util.getRotatedBounds(bounds, ROTATION_180, 200, 300),
-                equalTo(new Rect[] { ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(50, 290, 150, 300) }));
-    }
-
-    @Test
-    public void testGetRotatedBounds_top_rot270() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
-        assertThat(util.getRotatedBounds(bounds, ROTATION_270, 200, 300),
-                equalTo(new Rect[] { ZERO_RECT, ZERO_RECT, new Rect(290, 50, 300, 150), ZERO_RECT }));
-    }
-
-    @Test
-    public void testGetRotatedBounds_left_rot0() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
-        assertThat(util.getRotatedBounds(bounds, ROTATION_0, 300, 200),
-                equalTo(bounds));
-    }
-
-    @Test
-    public void testGetRotatedBounds_left_rot90() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
-        assertThat(util.getRotatedBounds(bounds, ROTATION_90, 300, 200),
-                equalTo(new Rect[]{ ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(50, 290, 150, 300) }));
-    }
-
-    @Test
-    public void testGetRotatedBounds_left_rot180() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
-        assertThat(util.getRotatedBounds(bounds, ROTATION_180, 300, 200),
-                equalTo(new Rect[]{ ZERO_RECT, ZERO_RECT, new Rect(290, 50, 300, 150), ZERO_RECT }));
-    }
-
-    @Test
-    public void testGetRotatedBounds_left_rot270() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
-        assertThat(util.getRotatedBounds(bounds, ROTATION_270, 300, 200),
-                equalTo(new Rect[]{ ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT }));
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
deleted file mode 100644
index 3364aef..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.wm.utils;
-
-import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-
-import static junit.framework.Assert.assertEquals;
-
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class InsetUtilsTest {
-
-    @Test
-    public void testAdd() throws Exception {
-        final Rect rect1 = new Rect(10, 20, 30, 40);
-        final Rect rect2 = new Rect(50, 60, 70, 80);
-        InsetUtils.addInsets(rect1, rect2);
-        assertEquals(new Rect(60, 80, 100, 120), rect1);
-    }
-
-    @Test
-    public void rotate() {
-        final Rect original = new Rect(1, 2, 3, 4);
-
-        assertEquals("rot0", original, rotateCopy(original, ROTATION_0));
-
-        final Rect rot90 = rotateCopy(original, ROTATION_90);
-        assertEquals("rot90", new Rect(2, 3, 4, 1), rot90);
-
-        final Rect rot180 = rotateCopy(original, ROTATION_180);
-        assertEquals("rot180", new Rect(3, 4, 1, 2), rot180);
-        assertEquals("rot90(rot90)=rot180", rotateCopy(rot90, ROTATION_90), rot180);
-
-        final Rect rot270 = rotateCopy(original, ROTATION_270);
-        assertEquals("rot270", new Rect(4, 1, 2, 3), rot270);
-        assertEquals("rot90(rot180)=rot270", rotateCopy(rot180, ROTATION_90), rot270);
-    }
-
-    private static Rect rotateCopy(Rect insets, int rotationDelta) {
-        final Rect copy = new Rect(insets);
-        InsetUtils.rotateInsets(copy, rotationDelta);
-        return copy;
-    }
-}
-
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java
deleted file mode 100644
index 5d08920..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.wm.utils;
-
-import static android.util.Pair.create;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-import android.util.Pair;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@FlakyTest(bugId = 74078662)
-@Presubmit
-public class RotationCacheTest {
-
-    private RotationCache<Object, Pair<Object, Integer>> mCache;
-    private boolean mComputationCalled;
-
-    @Before
-    public void setUp() throws Exception {
-        mComputationCalled = false;
-        mCache = new RotationCache<>((o, rot) -> {
-            mComputationCalled = true;
-            return create(o, rot);
-        });
-    }
-
-    @Test
-    public void getOrCompute_computes() throws Exception {
-        assertThat(mCache.getOrCompute("hello", 0), equalTo(create("hello", 0)));
-        assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
-        assertThat(mCache.getOrCompute("hello", 2), equalTo(create("hello", 2)));
-        assertThat(mCache.getOrCompute("hello", 3), equalTo(create("hello", 3)));
-    }
-
-    @Test
-    public void getOrCompute_sameParam_sameRot_hitsCache() throws Exception {
-        assertNotNull(mCache.getOrCompute("hello", 1));
-
-        mComputationCalled = false;
-        assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
-        assertThat(mComputationCalled, is(false));
-    }
-
-    @Test
-    public void getOrCompute_sameParam_hitsCache_forAllRots() throws Exception {
-        assertNotNull(mCache.getOrCompute("hello", 3));
-        assertNotNull(mCache.getOrCompute("hello", 2));
-        assertNotNull(mCache.getOrCompute("hello", 1));
-        assertNotNull(mCache.getOrCompute("hello", 0));
-
-        mComputationCalled = false;
-        assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
-        assertThat(mCache.getOrCompute("hello", 0), equalTo(create("hello", 0)));
-        assertThat(mCache.getOrCompute("hello", 2), equalTo(create("hello", 2)));
-        assertThat(mCache.getOrCompute("hello", 3), equalTo(create("hello", 3)));
-        assertThat(mComputationCalled, is(false));
-    }
-
-    @Test
-    public void getOrCompute_changingParam_recomputes() throws Exception {
-        assertNotNull(mCache.getOrCompute("hello", 1));
-
-        assertThat(mCache.getOrCompute("world", 1), equalTo(create("world", 1)));
-    }
-
-    @Test
-    public void getOrCompute_changingParam_clearsCacheForDifferentRots() throws Exception {
-        assertNotNull(mCache.getOrCompute("hello", 1));
-        assertNotNull(mCache.getOrCompute("world", 2));
-
-        mComputationCalled = false;
-        assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
-        assertThat(mComputationCalled, is(true));
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
deleted file mode 100644
index c5e35e7..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.wm.utils;
-
-
-import static android.view.DisplayCutout.NO_CUTOUT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
-import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
-import static android.view.DisplayCutout.fromBoundingRect;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertThat;
-
-
-import android.graphics.Insets;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.util.Size;
-import android.view.DisplayCutout;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-
-/**
- * Tests for {@link WmDisplayCutout}
- *
- * Run with: atest WmDisplayCutoutTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class WmDisplayCutoutTest {
-    private static final Rect ZERO_RECT = new Rect();
-
-    private final DisplayCutout mCutoutTop = new DisplayCutout(
-            Insets.of(0, 100, 0, 0),
-            null /* boundLeft */, new Rect(50, 0, 75, 100) /* boundTop */,
-            null /* boundRight */, null /* boundBottom */);
-
-    @Test
-    public void calculateRelativeTo_top() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400)
-                .calculateRelativeTo(new Rect(5, 5, 95, 195));
-
-        assertEquals(new Rect(0, 15, 0, 0), cutout.getDisplayCutout().getSafeInsets());
-    }
-
-    @Test
-    public void calculateRelativeTo_left() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200)
-                .calculateRelativeTo(new Rect(5, 5, 195, 95));
-
-        assertEquals(new Rect(15, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
-    }
-
-    @Test
-    public void calculateRelativeTo_bottom() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200)
-                .calculateRelativeTo(new Rect(5, 5, 95, 195));
-
-        assertEquals(new Rect(0, 0, 0, 15), cutout.getDisplayCutout().getSafeInsets());
-    }
-
-    @Test
-    public void calculateRelativeTo_right() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100)
-                .calculateRelativeTo(new Rect(5, 5, 195, 95));
-
-        assertEquals(new Rect(0, 0, 15, 0), cutout.getDisplayCutout().getSafeInsets());
-    }
-
-    @Test
-    public void calculateRelativeTo_bounds() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400)
-                .calculateRelativeTo(new Rect(5, 10, 95, 180));
-
-        assertThat(cutout.getDisplayCutout().getBoundingRectTop(),
-                equalTo(new Rect(-5, -10, 95, 10)));
-    }
-
-    @Test
-    public void computeSafeInsets_top() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400);
-
-        assertEquals(new Rect(0, 20, 0, 0), cutout.getDisplayCutout().getSafeInsets());
-    }
-
-    @Test
-    public void computeSafeInsets_left() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200);
-
-        assertEquals(new Rect(20, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
-    }
-
-    @Test
-    public void computeSafeInsets_bottom() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200);
-
-        assertEquals(new Rect(0, 0, 0, 20), cutout.getDisplayCutout().getSafeInsets());
-    }
-
-    @Test
-    public void computeSafeInsets_right() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100);
-
-        assertEquals(new Rect(0, 0, 20, 0), cutout.getDisplayCutout().getSafeInsets());
-    }
-
-    @Test
-    public void computeSafeInsets_bounds() {
-        DisplayCutout cutout = WmDisplayCutout.computeSafeInsets(mCutoutTop, 1000,
-                2000).getDisplayCutout();
-
-        assertEquals(mCutoutTop.getBoundingRects(), cutout.getBoundingRects());
-    }
-
-    @Test
-    public void test_equals() {
-        assertEquals(new WmDisplayCutout(NO_CUTOUT, null), new WmDisplayCutout(NO_CUTOUT, null));
-        assertEquals(new WmDisplayCutout(mCutoutTop, new Size(1, 2)),
-                new WmDisplayCutout(mCutoutTop, new Size(1, 2)));
-
-        assertNotEquals(new WmDisplayCutout(mCutoutTop, new Size(1, 2)),
-                new WmDisplayCutout(mCutoutTop, new Size(5, 6)));
-        assertNotEquals(new WmDisplayCutout(mCutoutTop, new Size(1, 2)),
-                new WmDisplayCutout(NO_CUTOUT, new Size(1, 2)));
-    }
-
-    @Test
-    public void test_hashCode() {
-        assertEquals(new WmDisplayCutout(NO_CUTOUT, null).hashCode(),
-                new WmDisplayCutout(NO_CUTOUT, null).hashCode());
-        assertEquals(new WmDisplayCutout(mCutoutTop, new Size(1, 2)).hashCode(),
-                new WmDisplayCutout(mCutoutTop, new Size(1, 2)).hashCode());
-    }
-}
\ No newline at end of file
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java b/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
index 1222b59..69db384 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
@@ -152,7 +152,7 @@
 
         @Override
         public int compareTo(MsgInfo o) {
-            return (int) (sendTime - o.sendTime);
+            return Long.compare(sendTime, o.sendTime);
         }
 
         @Override
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 79998a5..3fe381b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -280,7 +280,8 @@
 
         assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
         assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
-        assertEquals(channel1, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
+        assertEquals(channel1,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
         compareChannels(channel2,
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
 
@@ -348,7 +349,8 @@
 
         assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_O, UID_O));
         assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
-        assertEquals(channel1, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
+        assertEquals(channel1,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
         compareChannels(channel2,
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
         compareChannels(channel3,
@@ -487,7 +489,7 @@
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+                new NotificationChannel("id2", "name2", IMPORTANCE_HIGH);
         NotificationChannel channel3 =
                 new NotificationChannel("id3", "name3", IMPORTANCE_LOW);
         channel3.setGroup(ncg.getId());
@@ -500,7 +502,8 @@
 
         mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId());
         mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg.getId());
-        assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
+        assertEquals(channel2,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel1.getId(),
                 channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
@@ -516,8 +519,8 @@
         assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
         assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
         assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG_N_MR1, UID_N_MR1));
-        //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG_N_MR1, UID_N_MR1));
-        assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
+        assertEquals(channel2,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
     }
 
     @Test
@@ -799,14 +802,15 @@
     }
 
     @Test
-    public void testCreateChannel_CannotChangeHiddenFields() throws Exception {
+    public void testCreateChannel_CannotChangeHiddenFields() {
         final NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+                new NotificationChannel("id2", "name2", IMPORTANCE_HIGH);
         channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
         channel.enableLights(true);
         channel.setBypassDnd(true);
         channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
         channel.setShowBadge(true);
+        channel.setAllowAppOverlay(false);
         int lockMask = 0;
         for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
             lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
@@ -823,19 +827,21 @@
         assertFalse(savedChannel.canBypassDnd());
         assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
         assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
+        assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps());
 
         verify(mHandler, never()).requestSort();
     }
 
     @Test
-    public void testCreateChannel_CannotChangeHiddenFieldsAssistant() throws Exception {
+    public void testCreateChannel_CannotChangeHiddenFieldsAssistant() {
         final NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+                new NotificationChannel("id2", "name2", IMPORTANCE_HIGH);
         channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
         channel.enableLights(true);
         channel.setBypassDnd(true);
         channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
         channel.setShowBadge(true);
+        channel.setAllowAppOverlay(false);
         int lockMask = 0;
         for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
             lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
@@ -852,10 +858,11 @@
         assertFalse(savedChannel.canBypassDnd());
         assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
         assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
+        assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps());
     }
 
     @Test
-    public void testClearLockedFields() throws Exception {
+    public void testClearLockedFields() {
         final NotificationChannel channel = getChannel();
         mHelper.clearLockedFields(channel);
         assertEquals(0, channel.getUserLockedFields());
@@ -867,7 +874,7 @@
     }
 
     @Test
-    public void testLockFields_soundAndVibration() throws Exception {
+    public void testLockFields_soundAndVibration() {
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
 
         final NotificationChannel update1 = getChannel();
@@ -891,7 +898,7 @@
     }
 
     @Test
-    public void testLockFields_vibrationAndLights() throws Exception {
+    public void testLockFields_vibrationAndLights() {
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
 
         final NotificationChannel update1 = getChannel();
@@ -911,7 +918,7 @@
     }
 
     @Test
-    public void testLockFields_lightsAndImportance() throws Exception {
+    public void testLockFields_lightsAndImportance() {
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
 
         final NotificationChannel update1 = getChannel();
@@ -931,7 +938,7 @@
     }
 
     @Test
-    public void testLockFields_visibilityAndDndAndBadge() throws Exception {
+    public void testLockFields_visibilityAndDndAndBadge() {
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
         assertEquals(0,
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false)
@@ -963,6 +970,21 @@
     }
 
     @Test
+    public void testLockFields_appOverlay() {
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
+        assertEquals(0,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false)
+                        .getUserLockedFields());
+
+        final NotificationChannel update = getChannel();
+        update.setAllowAppOverlay(false);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update, true);
+        assertEquals(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update.getId(), false)
+                        .getUserLockedFields());
+    }
+
+    @Test
     public void testDeleteNonExistentChannel() throws Exception {
         mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, "does not exist");
     }
@@ -1255,21 +1277,24 @@
 
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, notDeleted, true);
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, deleted, true);
-        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nonGroupedNonDeletedChannel, true, false);
+        mHelper.createNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, nonGroupedNonDeletedChannel, true, false);
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted, true, false);
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedButNotDeleted, true, false);
 
         mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, deleted.getId());
 
         assertNull(mHelper.getNotificationChannelGroup(deleted.getId(), PKG_N_MR1, UID_N_MR1));
-        assertNotNull(mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1));
+        assertNotNull(
+                mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1));
 
-        assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), false));
-        compareChannels(groupedAndDeleted,
-                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), true));
+        assertNull(mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), false));
+        compareChannels(groupedAndDeleted, mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), true));
 
-        compareChannels(groupedButNotDeleted,
-                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedButNotDeleted.getId(), false));
+        compareChannels(groupedButNotDeleted, mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, groupedButNotDeleted.getId(), false));
         compareChannels(nonGroupedNonDeletedChannel, mHelper.getNotificationChannel(
                 PKG_N_MR1, UID_N_MR1, nonGroupedNonDeletedChannel.getId(), false));
 
@@ -1381,15 +1406,49 @@
     }
 
     @Test
-    public void testCreateGroup() throws Exception {
+    public void testCreateGroup() {
         NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
-        assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1).iterator().next());
+        assertEquals(ncg,
+                mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1).iterator().next());
         verify(mHandler, never()).requestSort();
     }
 
     @Test
-    public void testCannotCreateChannel_badGroup() throws Exception {
+    public void testUpdateGroup_fromSystem_appOverlay() {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+
+        // from system, allowed
+        NotificationChannelGroup update = ncg.clone();
+        update.setAllowAppOverlay(false);
+
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, update, false);
+        NotificationChannelGroup updated =
+                mHelper.getNotificationChannelGroup("group1", PKG_N_MR1, UID_N_MR1);
+        assertFalse(updated.canOverlayApps());
+        assertEquals(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY,
+                updated.getUserLockedFields());
+    }
+
+    @Test
+    public void testUpdateGroup_fromApp_appOverlay() {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+
+        // from app, not allowed
+        NotificationChannelGroup update = new NotificationChannelGroup("group1", "name1");
+        update.setAllowAppOverlay(false);
+
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+        NotificationChannelGroup updated =
+                mHelper.getNotificationChannelGroup("group1", PKG_N_MR1, UID_N_MR1);
+        assertTrue(updated.canOverlayApps());
+        assertEquals(0, updated.getUserLockedFields());
+    }
+
+    @Test
+    public void testCannotCreateChannel_badGroup() {
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel1.setGroup("garbage");
@@ -1401,7 +1460,7 @@
     }
 
     @Test
-    public void testCannotCreateChannel_goodGroup() throws Exception {
+    public void testCannotCreateChannel_goodGroup() {
         NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
         NotificationChannel channel1 =
@@ -1409,12 +1468,12 @@
         channel1.setGroup(ncg.getId());
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
 
-        assertEquals(ncg.getId(),
-                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false).getGroup());
+        assertEquals(ncg.getId(), mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, channel1.getId(), false).getGroup());
     }
 
     @Test
-    public void testGetChannelGroups() throws Exception {
+    public void testGetChannelGroups() {
         NotificationChannelGroup unused = new NotificationChannelGroup("unused", "s");
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, unused, true);
         NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
@@ -1465,7 +1524,7 @@
     }
 
     @Test
-    public void testGetChannelGroups_noSideEffects() throws Exception {
+    public void testGetChannelGroups_noSideEffects() {
         NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
 
@@ -1516,10 +1575,11 @@
     }
 
     @Test
-    public void testCreateChannel_updateName() throws Exception {
+    public void testCreateChannel_updateName() {
         NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
-        NotificationChannel actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
+        NotificationChannel actual =
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
         assertEquals("hello", actual.getName());
 
         nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
@@ -1533,12 +1593,13 @@
     }
 
     @Test
-    public void testCreateChannel_addToGroup() throws Exception {
+    public void testCreateChannel_addToGroup() {
         NotificationChannelGroup group = new NotificationChannelGroup("group", "");
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
         NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
-        NotificationChannel actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
+        NotificationChannel actual =
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
         assertNull(actual.getGroup());
 
         nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeExtractorTest.java
index beff0d1..95bfa44 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeExtractorTest.java
@@ -59,7 +59,7 @@
         assertFalse(r.isIntercepted());
 
         when(mZenModeHelper.shouldIntercept(any())).thenReturn(true);
-        when(mZenModeHelper.getNotificationPolicy()).thenReturn(
+        when(mZenModeHelper.getConsolidatedNotificationPolicy()).thenReturn(
                 new NotificationManager.Policy(0,0,0));
 
         extractor.process(r);
@@ -74,7 +74,7 @@
         NotificationRecord r = generateRecord();
 
         when(mZenModeHelper.shouldIntercept(any())).thenReturn(false);
-        when(mZenModeHelper.getNotificationPolicy()).thenReturn(
+        when(mZenModeHelper.getConsolidatedNotificationPolicy()).thenReturn(
                 new NotificationManager.Policy(0,0,0));
 
         extractor.process(r);
@@ -89,7 +89,7 @@
         NotificationRecord r = generateRecord();
 
         when(mZenModeHelper.shouldIntercept(any())).thenReturn(true);
-        when(mZenModeHelper.getNotificationPolicy()).thenReturn(
+        when(mZenModeHelper.getConsolidatedNotificationPolicy()).thenReturn(
                 new NotificationManager.Policy(0,0,0, SUPPRESSED_EFFECT_PEEK
                         | SUPPRESSED_EFFECT_NOTIFICATION_LIST));
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index d335450..32f389a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -32,6 +32,7 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
+import android.app.NotificationManager.Policy;
 import android.media.AudioAttributes;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenModeConfig;
@@ -70,7 +71,8 @@
 
     private NotificationRecord getNotificationRecord(NotificationChannel c) {
         StatusBarNotification sbn = mock(StatusBarNotification.class);
-        when(sbn.getNotification()).thenReturn(mock(Notification.class));
+        Notification notification = mock(Notification.class);
+        when(sbn.getNotification()).thenReturn(notification);
         return new NotificationRecord(mContext, sbn, c);
     }
 
@@ -121,11 +123,10 @@
         NotificationRecord r = getNotificationRecord();
         when(r.sbn.getPackageName()).thenReturn("android");
         when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
-        ZenModeConfig config = mock(ZenModeConfig.class);
-        config.suppressedVisualEffects = NotificationManager.Policy.getAllSuppressedVisualEffects()
-                - SUPPRESSED_EFFECT_STATUS_BAR;
+        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects()
+                - SUPPRESSED_EFFECT_STATUS_BAR);
 
-        assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, config, r));
+        assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
     }
 
     @Test
@@ -133,10 +134,9 @@
         NotificationRecord r = getNotificationRecord();
         when(r.sbn.getPackageName()).thenReturn("android");
         when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION);
-        ZenModeConfig config = mock(ZenModeConfig.class);
-        config.suppressedVisualEffects = NotificationManager.Policy.getAllSuppressedVisualEffects();
+        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
 
-        assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, config, r));
+        assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
     }
 
     @Test
@@ -144,10 +144,9 @@
         NotificationRecord r = getNotificationRecord();
         when(r.sbn.getPackageName()).thenReturn("android2");
         when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
-        ZenModeConfig config = mock(ZenModeConfig.class);
-        config.suppressedVisualEffects = NotificationManager.Policy.getAllSuppressedVisualEffects();
+        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
 
-        assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, config, r));
+        assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
     }
 
     @Test
@@ -155,21 +154,20 @@
         NotificationRecord r = getNotificationRecord();
         when(r.sbn.getPackageName()).thenReturn("android");
         when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
-        ZenModeConfig config = mock(ZenModeConfig.class);
-        config.suppressedVisualEffects = NotificationManager.Policy.getAllSuppressedVisualEffects();
+        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
 
-        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, config, r));
-        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_ALARMS, config, r));
-        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, config, r));
+        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_ALARMS, policy, r));
+        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, policy, r));
     }
 
     @Test
     public void testSuppressAnything_yes_ZenModeOff() {
         NotificationRecord r = getNotificationRecord();
         when(r.sbn.getPackageName()).thenReturn("bananas");
-        ZenModeConfig config = mock(ZenModeConfig.class);
+        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
 
-        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_OFF, config, r));
+        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_OFF, policy, r));
     }
 
     @Test
@@ -177,11 +175,11 @@
         NotificationRecord r = getNotificationRecord();
         r.setCriticality(CriticalNotificationExtractor.CRITICAL);
         when(r.sbn.getPackageName()).thenReturn("bananas");
-        ZenModeConfig config = mock(ZenModeConfig.class);
+        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
 
-        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, config, r));
+        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, policy, r));
 
         r.setCriticality(CriticalNotificationExtractor.CRITICAL_LOW);
-        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, config, r));
+        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, policy, r));
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 13f3e5e..38d8e39 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -21,13 +21,14 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 
-import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
 import static junit.framework.TestCase.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doNothing;
@@ -41,16 +42,18 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
+import android.app.NotificationManager.Policy;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
-import android.media.VolumePolicy;
 import android.media.AudioSystem;
+import android.media.VolumePolicy;
 import android.net.Uri;
 import android.provider.Settings;
 import android.provider.Settings.Global;
@@ -66,9 +69,9 @@
 
 import com.android.internal.R;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.server.notification.ManagedServices.UserProfiles;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.UiServiceTestCase;
+import com.android.server.notification.ManagedServices.UserProfiles;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -86,12 +89,16 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.util.Objects;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class ZenModeHelperTest extends UiServiceTestCase {
 
+    private static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
+    private static final String SCHEDULE_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
+
     ConditionProviders mConditionProviders;
     @Mock NotificationManager mNotificationManager;
     private Resources mResources;
@@ -107,7 +114,6 @@
         mTestableLooper = TestableLooper.get(this);
         mContext = spy(getContext());
         mContentResolver = mContext.getContentResolver();
-
         mResources = spy(mContext.getResources());
         try {
             when(mResources.getXml(R.xml.default_zen_mode_config)).thenReturn(
@@ -131,12 +137,13 @@
                 + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
                 + "visualScreenOff=\"true\" alarms=\"true\" "
                 + "media=\"true\" system=\"false\" />\n"
-                + "<automatic ruleId=\"EVENTS_DEFAULT_RULE\" enabled=\"false\" snoozing=\"false\""
+                + "<automatic ruleId=\"" + EVENTS_DEFAULT_RULE_ID
+                + "\" enabled=\"false\" snoozing=\"false\""
                 + " name=\"Event\" zen=\"1\""
                 + " component=\"android/com.android.server.notification.EventConditionProvider\""
                 + " conditionId=\"condition://android/event?userId=-10000&amp;calendar=&amp;"
                 + "reply=1\"/>\n"
-                + "<automatic ruleId=\"EVERY_NIGHT_DEFAULT_RULE\" enabled=\"false\""
+                + "<automatic ruleId=\"" + SCHEDULE_DEFAULT_RULE_ID + "\" enabled=\"false\""
                 + " snoozing=\"false\" name=\"Sleeping\" zen=\"1\""
                 + " component=\"android/com.android.server.notification.ScheduleConditionProvider\""
                 + " conditionId=\"condition://android/schedule?days=1.2.3.4.5.6.7 &amp;start=22.0"
@@ -165,7 +172,8 @@
     @Test
     public void testZenOff_NoMuteApplied() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_OFF;
-        assertTrue(mZenModeHelperSpy.mConfig.allowAlarms);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
+                Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         doNothing().when(mZenModeHelperSpy).applyRestrictions(anyBoolean(), anyInt());
@@ -178,8 +186,9 @@
     @Test
     public void testZenOn_AllowAlarmsMedia_NoAlarmMediaMuteApplied() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        assertTrue(mZenModeHelperSpy.mConfig.allowAlarms);
-        assertTrue(mZenModeHelperSpy.mConfig.allowMedia);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
+                Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
+
         mZenModeHelperSpy.applyRestrictions();
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
                 AudioAttributes.USAGE_ALARM);
@@ -190,12 +199,7 @@
     @Test
     public void testZenOn_DisallowAlarmsMedia_AlarmMediaMuteApplied() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelperSpy.mConfig.allowAlarms = false;
-        mZenModeHelperSpy.mConfig.allowMedia = false;
-        mZenModeHelperSpy.mConfig.allowSystem = false;
-        assertFalse(mZenModeHelperSpy.mConfig.allowAlarms);
-        assertFalse(mZenModeHelperSpy.mConfig.allowMedia);
-        assertFalse(mZenModeHelperSpy.mConfig.allowSystem);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
                 AudioAttributes.USAGE_ALARM);
@@ -210,6 +214,8 @@
     @Test
     public void testTotalSilence() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
+                Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         // Total silence will silence alarms, media and system noises (but not vibrations)
@@ -230,11 +236,7 @@
     @Test
     public void testAlarmsOnly_alarmMediaMuteNotApplied() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
-        mZenModeHelperSpy.mConfig.allowAlarms = false;
-        mZenModeHelperSpy.mConfig.allowSystem = false;
-        mZenModeHelperSpy.mConfig.allowMedia = false;
-        assertFalse(mZenModeHelperSpy.mConfig.allowAlarms);
-        assertFalse(mZenModeHelperSpy.mConfig.allowMedia);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         // Alarms only mode will not silence alarms
@@ -257,8 +259,7 @@
     @Test
     public void testAlarmsOnly_callsMuteApplied() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
-        mZenModeHelperSpy.mConfig.allowCalls = true;
-        assertTrue(mZenModeHelperSpy.mConfig.allowCalls);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         // Alarms only mode will silence calls despite priority-mode config
@@ -272,21 +273,7 @@
     public void testAlarmsOnly_allZenConfigToggledCannotBypass_alarmMuteNotApplied() {
         // Only audio attributes with SUPPRESIBLE_NEVER can bypass
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
-        mZenModeHelperSpy.mConfig.allowAlarms = false;
-        mZenModeHelperSpy.mConfig.allowMedia = false;
-        mZenModeHelperSpy.mConfig.allowSystem = false;
-        mZenModeHelperSpy.mConfig.allowReminders = false;
-        mZenModeHelperSpy.mConfig.allowCalls = false;
-        mZenModeHelperSpy.mConfig.allowMessages = false;
-        mZenModeHelperSpy.mConfig.allowEvents = false;
-        mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
-        assertFalse(mZenModeHelperSpy.mConfig.allowAlarms);
-        assertFalse(mZenModeHelperSpy.mConfig.allowMedia);
-        assertFalse(mZenModeHelperSpy.mConfig.allowReminders);
-        assertFalse(mZenModeHelperSpy.mConfig.allowCalls);
-        assertFalse(mZenModeHelperSpy.mConfig.allowMessages);
-        assertFalse(mZenModeHelperSpy.mConfig.allowEvents);
-        assertFalse(mZenModeHelperSpy.mConfig.allowRepeatCallers);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
@@ -298,21 +285,7 @@
         // Only audio attributes with SUPPRESIBLE_NEVER can bypass
         // with special case USAGE_ASSISTANCE_SONIFICATION
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelperSpy.mConfig.allowAlarms = false;
-        mZenModeHelperSpy.mConfig.allowMedia = false;
-        mZenModeHelperSpy.mConfig.allowSystem = false;
-        mZenModeHelperSpy.mConfig.allowReminders = false;
-        mZenModeHelperSpy.mConfig.allowCalls = false;
-        mZenModeHelperSpy.mConfig.allowMessages = false;
-        mZenModeHelperSpy.mConfig.allowEvents = false;
-        mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
-        assertFalse(mZenModeHelperSpy.mConfig.allowAlarms);
-        assertFalse(mZenModeHelperSpy.mConfig.allowMedia);
-        assertFalse(mZenModeHelperSpy.mConfig.allowReminders);
-        assertFalse(mZenModeHelperSpy.mConfig.allowCalls);
-        assertFalse(mZenModeHelperSpy.mConfig.allowMessages);
-        assertFalse(mZenModeHelperSpy.mConfig.allowEvents);
-        assertFalse(mZenModeHelperSpy.mConfig.allowRepeatCallers);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         for (int usage : AudioAttributes.SDK_USAGES) {
@@ -338,6 +311,7 @@
         Settings.Secure.putInt(mContentResolver, Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
         Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_SETTINGS_UPDATED, 0);
         mZenModeHelperSpy.mIsBootComplete = true;
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
         mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         verify(mZenModeHelperSpy, times(1)).createZenUpgradeNotification();
@@ -548,6 +522,7 @@
     @Test
     public void testSilentRingerSavedInZenOff_startsZenOff() {
         AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
+        mZenModeHelperSpy.mConfig = new ZenModeConfig();
         mZenModeHelperSpy.mAudioManager = mAudioManager;
 
         // apply zen off multiple times - verify ringer is not set to normal
@@ -568,6 +543,7 @@
         AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
         mZenModeHelperSpy.mAudioManager = mAudioManager;
         mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_OFF;
+        mZenModeHelperSpy.mConfig = new ZenModeConfig();
 
         // previously set silent ringer
         ZenModeHelper.RingerModeDelegate ringerModeDelegate =
@@ -581,7 +557,6 @@
         // apply zen off multiple times - verify ringer is not set to normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelperSpy.mConfig = null; // will evaluate config to zen mode off
         for (int i = 0; i < 3; i++) {
             // if zen doesn't change, zen should not reapply itself to the ringer
             mZenModeHelperSpy.evaluateZenMode("test", true);
@@ -596,6 +571,7 @@
         AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
         mZenModeHelperSpy.mAudioManager = mAudioManager;
         mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_OFF;
+        mZenModeHelperSpy.mConfig = new ZenModeConfig();
 
         // previously set silent ringer
         ZenModeHelper.RingerModeDelegate ringerModeDelegate =
@@ -609,7 +585,6 @@
         // apply zen off multiple times - verify ringer is not set to normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelperSpy.mConfig = null; // will evaluate config to zen mode off
         for (int i = 0; i < 3; i++) {
             // if zen doesn't change, zen should not reapply itself to the ringer
             mZenModeHelperSpy.evaluateZenMode("test", true);
@@ -801,8 +776,8 @@
         mZenModeHelperSpy.readXml(parser, false);
 
         assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
-                | SUPPRESSED_EFFECT_LIGHTS
-                | SUPPRESSED_EFFECT_PEEK,
+                        | SUPPRESSED_EFFECT_LIGHTS
+                        | SUPPRESSED_EFFECT_PEEK,
                 mZenModeHelperSpy.mConfig.suppressedVisualEffects);
 
         xml = "<zen version=\"6\" user=\"0\">\n"
@@ -1038,6 +1013,90 @@
         mZenModeHelperSpy.updateDefaultZenRules(); // shouldn't throw null pointer
     }
 
+    @Test
+    public void testDoNotUpdateModifiedDefaultAutoRule() {
+        // mDefaultConfig is set to default config in setup by getDefaultConfigParser
+        when(mContext.checkCallingPermission(anyString()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        // shouldn't update rule that's been modified
+        ZenModeConfig.ZenRule updatedDefaultRule = new ZenModeConfig.ZenRule();
+        updatedDefaultRule.modified = true;
+        updatedDefaultRule.enabled = false;
+        updatedDefaultRule.creationTime = 0;
+        updatedDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
+        updatedDefaultRule.name = "Schedule Default Rule";
+        updatedDefaultRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        updatedDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
+        updatedDefaultRule.component = new ComponentName("android", "ScheduleConditionProvider");
+
+        ArrayMap<String, ZenModeConfig.ZenRule> autoRules = new ArrayMap<>();
+        autoRules.put(SCHEDULE_DEFAULT_RULE_ID, updatedDefaultRule);
+        mZenModeHelperSpy.mConfig.automaticRules = autoRules;
+
+        mZenModeHelperSpy.updateDefaultZenRules();
+        assertEquals(updatedDefaultRule,
+                mZenModeHelperSpy.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID));
+    }
+
+    @Test
+    public void testDoNotUpdateEnabledDefaultAutoRule() {
+        // mDefaultConfig is set to default config in setup by getDefaultConfigParser
+        when(mContext.checkCallingPermission(anyString()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        // shouldn't update the rule that's enabled
+        ZenModeConfig.ZenRule updatedDefaultRule = new ZenModeConfig.ZenRule();
+        updatedDefaultRule.enabled = true;
+        updatedDefaultRule.modified = false;
+        updatedDefaultRule.creationTime = 0;
+        updatedDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
+        updatedDefaultRule.name = "Schedule Default Rule";
+        updatedDefaultRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        updatedDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
+        updatedDefaultRule.component = new ComponentName("android", "ScheduleConditionProvider");
+
+        ArrayMap<String, ZenModeConfig.ZenRule> autoRules = new ArrayMap<>();
+        autoRules.put(SCHEDULE_DEFAULT_RULE_ID, updatedDefaultRule);
+        mZenModeHelperSpy.mConfig.automaticRules = autoRules;
+
+        mZenModeHelperSpy.updateDefaultZenRules();
+        assertEquals(updatedDefaultRule,
+                mZenModeHelperSpy.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID));
+    }
+
+    @Test
+    public void testUpdateDefaultAutoRule() {
+        // mDefaultConfig is set to default config in setup by getDefaultConfigParser
+        final String defaultRuleName = "rule name test";
+        when(mContext.checkCallingPermission(anyString()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        // will update rule that is not enabled and modified
+        ZenModeConfig.ZenRule customDefaultRule = new ZenModeConfig.ZenRule();
+        customDefaultRule.enabled = false;
+        customDefaultRule.modified = false;
+        customDefaultRule.creationTime = 0;
+        customDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
+        customDefaultRule.name = "Schedule Default Rule";
+        customDefaultRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
+        customDefaultRule.component = new ComponentName("android", "ScheduleConditionProvider");
+
+        ArrayMap<String, ZenModeConfig.ZenRule> autoRules = new ArrayMap<>();
+        autoRules.put(SCHEDULE_DEFAULT_RULE_ID, customDefaultRule);
+        mZenModeHelperSpy.mConfig.automaticRules = autoRules;
+
+        mZenModeHelperSpy.updateDefaultZenRules();
+        ZenModeConfig.ZenRule ruleAfterUpdating =
+                mZenModeHelperSpy.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID);
+        assertEquals(customDefaultRule.enabled, ruleAfterUpdating.enabled);
+        assertEquals(customDefaultRule.modified, ruleAfterUpdating.modified);
+        assertEquals(customDefaultRule.id, ruleAfterUpdating.id);
+        assertEquals(customDefaultRule.conditionId, ruleAfterUpdating.conditionId);
+        assertFalse(Objects.equals(defaultRuleName, ruleAfterUpdating.name)); // update name
+    }
+
     private void setupZenConfig() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowAlarms = false;
diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk
index c095ae0..9655b3d 100644
--- a/services/tests/wmtests/Android.mk
+++ b/services/tests/wmtests/Android.mk
@@ -11,12 +11,19 @@
 # Include all test java files.
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src) \
-    $(call all-java-files-under, ../servicestests/utils)
+    $(call all-java-files-under, ../servicestests/utils) \
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    frameworks-base-testutils \
+    services.core \
     androidx.test.runner \
+    androidx.test.rules \
     mockito-target-minus-junit4 \
     platform-test-annotations \
+    truth-prebuilt \
+    testables \
+    ub-uiautomator \
+    hamcrest-library
 
 LOCAL_JAVA_LIBRARIES := \
     android.test.mock \
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 1fb9473..ff84803 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -22,7 +22,28 @@
         android:minSdkVersion="1"
         android:targetSdkVersion="28" />
 
-    <application android:testOnly="true" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+    <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
+    <uses-permission android:name="android.permission.STORAGE_INTERNAL" />
+    <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+    <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
+
+    <application android:testOnly="true">
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
+        <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity"
+                  android:showWhenLocked="true" />
+    </application>
 
     <instrumentation
         android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java b/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java
deleted file mode 100644
index 023e4ab..0000000
--- a/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.am;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Test;
-
-import androidx.test.filters.FlakyTest;
-
-/**
- * Dummy test for com.android.server.am.
- * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
- */
-public class DummyAmTests {
-
-    @Presubmit
-    @Test
-    public void preSubmitTest() {}
-
-    @FlakyTest
-    @Presubmit
-    @Test
-    public void flakyPreSubmitTest() {}
-
-    @Test
-    public void postSubmitTest() {}
-
-    @FlakyTest
-    @Test
-    public void flakyPostSubmitTest() {}
-}
diff --git a/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java
deleted file mode 100644
index 03fb123..0000000
--- a/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.policy;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Test;
-
-import androidx.test.filters.FlakyTest;
-
-/**
- * Dummy test for com.android.server.policy.
- * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
- */
-public class DummyPolicyTests {
-
-    @Presubmit
-    @Test
-    public void preSubmitTest() {}
-
-    @FlakyTest
-    @Presubmit
-    @Test
-    public void flakyPreSubmitTest() {}
-
-    @Test
-    public void postSubmitTest() {}
-
-    @FlakyTest
-    @Test
-    public void flakyPostSubmitTest() {}
-}
diff --git a/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java b/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java
new file mode 100644
index 0000000..d4f2b06
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2017 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.server.policy;
+
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+import android.view.IApplicationToken;
+import android.view.WindowManager;
+
+import com.android.server.wm.WindowFrames;
+
+public class FakeWindowState implements WindowManagerPolicy.WindowState {
+
+    private WindowFrames mWindowFrames = new WindowFrames();
+
+    public WindowManager.LayoutParams attrs;
+    public int displayId;
+    public boolean isVoiceInteraction;
+    public boolean inMultiWindowMode;
+    public boolean visible = true;
+    public int surfaceLayer = 1;
+    public boolean isDimming = false;
+
+    public boolean policyVisible = true;
+
+    @Override
+    public int getOwningUid() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public String getOwningPackage() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public void computeFrameLw() {
+    }
+
+    @Override
+    public Rect getFrameLw() {
+        return mWindowFrames.mParentFrame;
+    }
+
+    @Override
+    public Rect getDisplayFrameLw() {
+        return mWindowFrames.mDisplayFrame;
+    }
+
+    @Override
+    public Rect getOverscanFrameLw() {
+        return mWindowFrames.mOverscanFrame;
+    }
+
+    @Override
+    public Rect getContentFrameLw() {
+        return mWindowFrames.mContentFrame;
+    }
+
+    @Override
+    public Rect getVisibleFrameLw() {
+        return mWindowFrames.mVisibleFrame;
+    }
+
+    public Rect getStableFrame() {
+        return mWindowFrames.mStableFrame;
+    }
+
+    public Rect getDecorFrame() {
+        return mWindowFrames.mDecorFrame;
+    }
+
+    @Override
+    public boolean getGivenInsetsPendingLw() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public Rect getGivenContentInsetsLw() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public Rect getGivenVisibleInsetsLw() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public WindowManager.LayoutParams getAttrs() {
+        return attrs;
+    }
+
+    @Override
+    public boolean getNeedsMenuLw(WindowManagerPolicy.WindowState bottom) {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public int getSystemUiVisibility() {
+        return attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility;
+    }
+
+    @Override
+    public int getSurfaceLayer() {
+        return surfaceLayer;
+    }
+
+    @Override
+    public int getBaseType() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public IApplicationToken getAppToken() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public boolean isVoiceInteraction() {
+        return isVoiceInteraction;
+    }
+
+    @Override
+    public boolean hasAppShownWindows() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public boolean isVisibleLw() {
+        return visible && policyVisible;
+    }
+
+    @Override
+    public boolean isDisplayedLw() {
+        return isVisibleLw();
+    }
+
+    @Override
+    public boolean isAnimatingLw() {
+        return false;
+    }
+
+    @Override
+    public boolean canAffectSystemUiFlags() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public boolean isGoneForLayoutLw() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public boolean isDrawnLw() {
+        return true;
+    }
+
+    @Override
+    public boolean hasDrawnLw() {
+        return true;
+    }
+
+    @Override
+    public boolean hideLw(boolean doAnimation) {
+        if (!policyVisible) {
+            return false;
+        }
+        policyVisible = false;
+        return true;
+    }
+
+    @Override
+    public boolean showLw(boolean doAnimation) {
+        if (policyVisible) {
+            return false;
+        }
+        policyVisible = true;
+        return true;
+    }
+
+    @Override
+    public boolean isAlive() {
+        return true;
+    }
+
+    @Override
+    public boolean isDefaultDisplay() {
+        return displayId == Display.DEFAULT_DISPLAY;
+    }
+
+    @Override
+    public boolean isDimming() {
+        return isDimming;
+    }
+
+    @Override
+    public int getWindowingMode() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public boolean isInMultiWindowMode() {
+        return inMultiWindowMode;
+    }
+
+    @Override
+    public int getRotationAnimationHint() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public boolean isInputMethodWindow() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public int getDisplayId() {
+        return displayId;
+    }
+
+    @Override
+    public boolean canAcquireSleepToken() {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public boolean canReceiveKeys() {
+        return false;
+    }
+
+    @Override
+    public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public WindowFrames getWindowFrames() {
+        return mWindowFrames;
+    }
+
+    @Override
+    public boolean isInputMethodTarget() {
+        return false;
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
new file mode 100644
index 0000000..f024fe7
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2018 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.server.policy;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static org.hamcrest.Matchers.equalTo;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:PhoneWindowManagerInsetsTest
+ */
+@SmallTest
+@Presubmit
+public class PhoneWindowManagerInsetsTest extends PhoneWindowManagerTestBase {
+
+    @Rule
+    public final ErrorCollector mErrorCollector = new ErrorCollector();
+
+    @Before
+    public void setUp() throws Exception {
+        addStatusBar();
+        addNavigationBar();
+    }
+
+    @Test
+    public void portrait() {
+        DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */);
+
+        verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
+        verifyConsistency(di);
+    }
+
+    @Test
+    public void portrait_withCutout() {
+        DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */);
+
+        verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        verifyNonDecorInsets(di, 0, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT);
+        verifyConsistency(di);
+    }
+
+    @Test
+    public void landscape() {
+        DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */);
+
+        verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        verifyNonDecorInsets(di, 0, 0, NAV_BAR_HEIGHT, 0);
+        verifyConsistency(di);
+    }
+
+    @Test
+    public void landscape_withCutout() {
+        DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */);
+
+        verifyStableInsets(di, DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        verifyNonDecorInsets(di, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT, 0);
+        verifyConsistency(di);
+    }
+
+    @Test
+    public void seascape() {
+        DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */);
+
+        verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
+        verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, 0, 0);
+        verifyConsistency(di);
+    }
+
+    @Test
+    public void seascape_withCutout() {
+        DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */);
+
+        verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
+        verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+        verifyConsistency(di);
+    }
+
+    @Test
+    public void upsideDown() {
+        DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */);
+
+        verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
+        verifyConsistency(di);
+    }
+
+    @Test
+    public void upsideDown_withCutout() {
+        DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */);
+
+        verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
+        verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
+        verifyConsistency(di);
+    }
+
+    private void verifyStableInsets(DisplayInfo di, int left, int top, int right, int bottom) {
+        mErrorCollector.checkThat("stableInsets", getStableInsetsLw(di), equalTo(new Rect(
+                left, top, right, bottom)));
+    }
+
+    private void verifyNonDecorInsets(DisplayInfo di, int left, int top, int right, int bottom) {
+        mErrorCollector.checkThat("nonDecorInsets", getNonDecorInsetsLw(di), equalTo(new Rect(
+                left, top, right, bottom)));
+    }
+
+    private void verifyConsistency(DisplayInfo di) {
+        verifyConsistency("configDisplay", di, getStableInsetsLw(di),
+                getConfigDisplayWidth(di), getConfigDisplayHeight(di));
+        verifyConsistency("nonDecorDisplay", di, getNonDecorInsetsLw(di),
+                getNonDecorDisplayWidth(di), getNonDecorDisplayHeight(di));
+    }
+
+    private void verifyConsistency(String what, DisplayInfo di, Rect insets, int width,
+            int height) {
+        mErrorCollector.checkThat(what + ":width", width,
+                equalTo(di.logicalWidth - insets.left - insets.right));
+        mErrorCollector.checkThat(what + ":height", height,
+                equalTo(di.logicalHeight - insets.top - insets.bottom));
+    }
+
+    private Rect getStableInsetsLw(DisplayInfo di) {
+        Rect result = new Rect();
+        mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+                di.displayCutout, result);
+        return result;
+    }
+
+    private Rect getNonDecorInsetsLw(DisplayInfo di) {
+        Rect result = new Rect();
+        mPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+                di.displayCutout, result);
+        return result;
+    }
+
+    private int getNonDecorDisplayWidth(DisplayInfo di) {
+        return mPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight, di.rotation,
+                0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
+    }
+
+    private int getNonDecorDisplayHeight(DisplayInfo di) {
+        return mPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight, di.rotation,
+                0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
+    }
+
+    private int getConfigDisplayWidth(DisplayInfo di) {
+        return mPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight, di.rotation,
+                0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
+    }
+
+    private int getConfigDisplayHeight(DisplayInfo di) {
+        return mPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight, di.rotation,
+                0 /* ui */, Display.DEFAULT_DISPLAY, di.displayCutout);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
new file mode 100644
index 0000000..e8f767a
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2017 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.server.policy;
+
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.DisplayCutout;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:PhoneWindowManagerLayoutTest
+ */
+@SmallTest
+@Presubmit
+public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
+
+    private FakeWindowState mAppWindow;
+
+    @Before
+    public void setUp() throws Exception {
+        mAppWindow = new FakeWindowState();
+        mAppWindow.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
+                TYPE_APPLICATION,
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+                PixelFormat.TRANSLUCENT);
+
+        addStatusBar();
+        addNavigationBar();
+    }
+
+    @Test
+    public void layoutWindowLw_appDrawsBars() {
+        mAppWindow.attrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+        assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_appWontDrawBars() {
+        mAppWindow.attrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
+    }
+
+    @Test
+    public void layoutWindowLw_appWontDrawBars_forceStatus() {
+        mAppWindow.attrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
+    }
+
+    @Test
+    public void addingWindow_doesNotTamperWithSysuiFlags() {
+        mAppWindow.attrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mPolicy.addWindow(mAppWindow);
+
+        assertEquals(0, mAppWindow.attrs.systemUiVisibility);
+        assertEquals(0, mAppWindow.attrs.subtreeSystemUiVisibility);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout() {
+        addDisplayCutout();
+
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withhDisplayCutout_never() {
+        addDisplayCutout();
+
+        mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
+        addDisplayCutout();
+
+        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+        assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_fullscreen() {
+        addDisplayCutout();
+
+        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
+        addDisplayCutout();
+
+        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+        mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
+    }
+
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_landscape() {
+        addDisplayCutout();
+        setRotation(ROTATION_90);
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getContentFrameLw(),
+                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mAppWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_seascape() {
+        addDisplayCutout();
+        setRotation(ROTATION_270);
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetBy(mAppWindow.getFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getStableFrame(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
+        assertInsetBy(mAppWindow.getContentFrameLw(),
+                NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
+        addDisplayCutout();
+        setRotation(ROTATION_90);
+
+        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getContentFrameLw(),
+                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
+        addDisplayCutout();
+
+        mAppWindow.attrs.flags = FLAG_LAYOUT_IN_SCREEN;
+        mAppWindow.attrs.type = TYPE_APPLICATION_OVERLAY;
+        mAppWindow.attrs.width = DISPLAY_WIDTH;
+        mAppWindow.attrs.height = DISPLAY_HEIGHT;
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
+        addDisplayCutout();
+        setRotation(ROTATION_90);
+
+        mAppWindow.attrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+        mAppWindow.attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetBy(mAppWindow.getFrameLw(), 0, 0, 0, 0);
+        assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getContentFrameLw(),
+                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutHint_screenDecorWindow() {
+        addDisplayCutout();
+        mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR;
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+        final Rect frame = new Rect();
+        final Rect content = new Rect();
+        final Rect stable = new Rect();
+        final Rect outsets = new Rect();
+        final DisplayCutout.ParcelableWrapper cutout = new DisplayCutout.ParcelableWrapper();
+        mPolicy.getLayoutHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames,
+                false /* floatingStack */, frame, content, stable, outsets, cutout);
+
+        assertThat(frame, equalTo(mFrames.mUnrestricted));
+        assertThat(content, equalTo(new Rect()));
+        assertThat(stable, equalTo(new Rect()));
+        assertThat(outsets, equalTo(new Rect()));
+        assertThat(cutout.get(), equalTo(DisplayCutout.NO_CUTOUT));
+    }
+
+    @Test
+    public void layoutHint_appWindow() {
+        // Initialize DisplayFrames
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+        final Rect outFrame = new Rect();
+        final Rect outContentInsets = new Rect();
+        final Rect outStableInsets = new Rect();
+        final Rect outOutsets = new Rect();
+        final DisplayCutout.ParcelableWrapper outDisplayCutout =
+                new DisplayCutout.ParcelableWrapper();
+
+        mPolicy.getLayoutHintLw(mAppWindow.attrs, null, mFrames, false /* floatingStack */,
+                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
+
+        assertThat(outFrame, is(mFrames.mUnrestricted));
+        assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+        assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+        assertThat(outOutsets, is(new Rect()));
+        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+    }
+
+    @Test
+    public void layoutHint_appWindowInTask() {
+        // Initialize DisplayFrames
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+        final Rect taskBounds = new Rect(100, 100, 200, 200);
+
+        final Rect outFrame = new Rect();
+        final Rect outContentInsets = new Rect();
+        final Rect outStableInsets = new Rect();
+        final Rect outOutsets = new Rect();
+        final DisplayCutout.ParcelableWrapper outDisplayCutout =
+                new DisplayCutout.ParcelableWrapper();
+
+        mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, false /* floatingStack */,
+                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
+
+        assertThat(outFrame, is(taskBounds));
+        assertThat(outContentInsets, is(new Rect()));
+        assertThat(outStableInsets, is(new Rect()));
+        assertThat(outOutsets, is(new Rect()));
+        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+    }
+
+    @Test
+    public void layoutHint_appWindowInTask_outsideContentFrame() {
+        // Initialize DisplayFrames
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+        // Task is in the nav bar area (usually does not happen, but this is similar enough to the
+        // possible overlap with the IME)
+        final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
+                200, mFrames.mContent.bottom + 10);
+
+        final Rect outFrame = new Rect();
+        final Rect outContentInsets = new Rect();
+        final Rect outStableInsets = new Rect();
+        final Rect outOutsets = new Rect();
+        final DisplayCutout.ParcelableWrapper outDisplayCutout =
+                new DisplayCutout.ParcelableWrapper();
+
+        mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, true /* floatingStack */,
+                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
+
+        assertThat(outFrame, is(taskBounds));
+        assertThat(outContentInsets, is(new Rect()));
+        assertThat(outStableInsets, is(new Rect()));
+        assertThat(outOutsets, is(new Rect()));
+        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java
new file mode 100644
index 0000000..6c44d65
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2018 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.server.policy;
+
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.PixelFormat;
+import android.platform.test.annotations.Presubmit;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:PhoneWindowManagerTest
+ */
+@SmallTest
+@Presubmit
+public class PhoneWindowManagerTest {
+
+    private static FakeWindowState createOpaqueFullscreen(boolean hasLightNavBar) {
+        final FakeWindowState state = new FakeWindowState();
+        state.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
+                TYPE_BASE_APPLICATION,
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+                PixelFormat.OPAQUE);
+        state.attrs.subtreeSystemUiVisibility =
+                hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
+        return state;
+    }
+
+    private static FakeWindowState createDimmingDialogWindow(boolean canBeImTarget) {
+        final FakeWindowState state = new FakeWindowState();
+        state.attrs = new WindowManager.LayoutParams(WRAP_CONTENT, WRAP_CONTENT,
+                TYPE_APPLICATION,
+                FLAG_DIM_BEHIND  | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM),
+                PixelFormat.TRANSLUCENT);
+        state.isDimming = true;
+        return state;
+    }
+
+    private static FakeWindowState createInputMethodWindow(boolean visible, boolean drawNavBar,
+            boolean hasLightNavBar) {
+        final FakeWindowState state = new FakeWindowState();
+        state.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
+                TYPE_INPUT_METHOD,
+                FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN
+                        | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0),
+                PixelFormat.TRANSPARENT);
+        state.attrs.subtreeSystemUiVisibility =
+                hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
+        state.visible = visible;
+        state.policyVisible = visible;
+        return state;
+    }
+
+
+    @Test
+    public void testChooseNavigationColorWindowLw() {
+        final FakeWindowState opaque = createOpaqueFullscreen(false);
+
+        final FakeWindowState dimmingImTarget = createDimmingDialogWindow(true);
+        final FakeWindowState dimmingNonImTarget = createDimmingDialogWindow(false);
+
+        final FakeWindowState visibleIme = createInputMethodWindow(true, true, false);
+        final FakeWindowState invisibleIme = createInputMethodWindow(false, true, false);
+        final FakeWindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false);
+
+        // If everything is null, return null
+        assertNull(null, PhoneWindowManager.chooseNavigationColorWindowLw(
+                null, null, null, NAV_BAR_BOTTOM));
+
+        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
+                opaque, opaque, null, NAV_BAR_BOTTOM));
+        assertEquals(dimmingImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
+                opaque, dimmingImTarget, null, NAV_BAR_BOTTOM));
+        assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
+                opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM));
+
+        assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
+                null, null, visibleIme, NAV_BAR_BOTTOM));
+        assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
+                null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+        assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
+                null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+        assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
+                opaque, opaque, visibleIme, NAV_BAR_BOTTOM));
+        assertEquals(visibleIme, PhoneWindowManager.chooseNavigationColorWindowLw(
+                opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+        assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
+                opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+
+        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
+                opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
+        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
+                opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
+        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
+                opaque, opaque, visibleIme, NAV_BAR_RIGHT));
+
+        // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color
+        // window.
+        assertEquals(opaque, PhoneWindowManager.chooseNavigationColorWindowLw(
+                opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+        assertEquals(dimmingImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
+                opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+        assertEquals(dimmingNonImTarget, PhoneWindowManager.chooseNavigationColorWindowLw(
+                opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+    }
+
+    @Test
+    public void testUpdateLightNavigationBarLw() {
+        final FakeWindowState opaqueDarkNavBar = createOpaqueFullscreen(false);
+        final FakeWindowState opaqueLightNavBar = createOpaqueFullscreen(true);
+
+        final FakeWindowState dimming = createDimmingDialogWindow(false);
+
+        final FakeWindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false);
+        final FakeWindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true);
+
+        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                PhoneWindowManager.updateLightNavigationBarLw(
+                        SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null,
+                        null, null));
+
+        // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag.
+        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+                0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar));
+        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null,
+                opaqueDarkNavBar));
+        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                PhoneWindowManager.updateLightNavigationBarLw(0, opaqueLightNavBar,
+                        opaqueLightNavBar, null, opaqueLightNavBar));
+        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                PhoneWindowManager.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                        opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar));
+
+        // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
+        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+                0, opaqueDarkNavBar, dimming, null, dimming));
+        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+                0, opaqueLightNavBar, dimming, null, dimming));
+        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming));
+        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming));
+        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar,
+                dimming));
+
+        // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
+        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar,
+                imeDrawDarkNavBar));
+
+        // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins.
+        assertEquals(0, PhoneWindowManager.updateLightNavigationBarLw(
+                SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar,
+                imeDrawDarkNavBar, imeDrawDarkNavBar));
+
+        // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
+        assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+                PhoneWindowManager.updateLightNavigationBarLw(0, opaqueDarkNavBar,
+                        opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
+    }
+
+    @Test
+    public void testIsDockSideAllowedDockTop() {
+        // Docked top is always allowed
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                true /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+    }
+
+    @Test
+    public void testIsDockSideAllowedDockBottom() {
+        // Cannot dock bottom
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                true /* navigationBarCanMove */));
+    }
+
+    @Test
+    public void testIsDockSideAllowedNavigationBarMovable() {
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                true /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_LEFT,
+                true /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_RIGHT,
+                true /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                true /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_RIGHT,
+                true /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_LEFT,
+                true /* navigationBarCanMove */));
+    }
+
+    @Test
+    public void testIsDockSideAllowedNavigationBarNotMovable() {
+        // Navigation bar is not movable such as tablets
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
new file mode 100644
index 0000000..fc8fe23
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2017 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.server.policy;
+
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+
+import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.testing.TestableResources;
+import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.server.policy.keyguard.KeyguardServiceDelegate;
+import com.android.server.wm.DisplayFrames;
+import com.android.server.wm.WindowTestUtils.TestDisplayContent;
+import com.android.server.wm.utils.WmDisplayCutout;
+
+import org.junit.Before;
+
+class PhoneWindowManagerTestBase {
+    static final int DISPLAY_WIDTH = 500;
+    static final int DISPLAY_HEIGHT = 1000;
+
+    static final int STATUS_BAR_HEIGHT = 10;
+    static final int NAV_BAR_HEIGHT = 15;
+    static final int DISPLAY_CUTOUT_HEIGHT = 8;
+
+    TestablePhoneWindowManager mPolicy;
+    TestContextWrapper mContext;
+    DisplayFrames mFrames;
+
+    FakeWindowState mStatusBar;
+    FakeWindowState mNavigationBar;
+    private boolean mHasDisplayCutout;
+    private int mRotation = ROTATION_0;
+
+    @Before
+    public void setUpBase() {
+        mContext = new TestContextWrapper(InstrumentationRegistry.getTargetContext());
+        mContext.getResourceMocker().addOverride(
+                com.android.internal.R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT);
+        mContext.getResourceMocker().addOverride(
+                com.android.internal.R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT);
+        mContext.getResourceMocker().addOverride(
+                com.android.internal.R.dimen.navigation_bar_height, NAV_BAR_HEIGHT);
+        mContext.getResourceMocker().addOverride(
+                com.android.internal.R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT);
+        mContext.getResourceMocker().addOverride(
+                com.android.internal.R.dimen.navigation_bar_width, NAV_BAR_HEIGHT);
+
+        mPolicy = TestablePhoneWindowManager.create(mContext);
+
+        updateDisplayFrames();
+    }
+
+    public void setRotation(int rotation) {
+        mRotation = rotation;
+        updateDisplayFrames();
+    }
+
+    private void updateDisplayFrames() {
+        Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
+                mHasDisplayCutout);
+        mFrames = new DisplayFrames(Display.DEFAULT_DISPLAY, info.first, info.second);
+    }
+
+    public void addStatusBar() {
+        mStatusBar = new FakeWindowState();
+        mStatusBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, STATUS_BAR_HEIGHT,
+                TYPE_STATUS_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT);
+        mStatusBar.attrs.gravity = Gravity.TOP;
+
+        mPolicy.addWindow(mStatusBar);
+        mPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT;
+    }
+
+    public void addNavigationBar() {
+        mNavigationBar = new FakeWindowState();
+        mNavigationBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, NAV_BAR_HEIGHT,
+                TYPE_NAVIGATION_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT);
+        mNavigationBar.attrs.gravity = Gravity.BOTTOM;
+
+        mPolicy.addWindow(mNavigationBar);
+        mPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
+    }
+
+    public void addDisplayCutout() {
+        mHasDisplayCutout = true;
+        updateDisplayFrames();
+    }
+
+    /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
+    public void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
+            int expectedInsetRight, int expectedInsetBottom) {
+        assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
+                mFrames.mDisplayWidth - expectedInsetRight,
+                mFrames.mDisplayHeight - expectedInsetBottom), actual);
+    }
+
+    /**
+     * Asserts that {@code actual} is inset by the given amounts from the full display rect.
+     *
+     * Convenience wrapper for when only the top and bottom inset are non-zero.
+     */
+    public void assertInsetByTopBottom(Rect actual, int expectedInsetTop, int expectedInsetBottom) {
+        assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
+    }
+
+    public static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
+        return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first;
+    }
+    public static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation,
+            boolean withDisplayCutout) {
+        DisplayInfo info = new DisplayInfo();
+        WmDisplayCutout cutout = null;
+
+        final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270;
+        info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+        info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+        info.rotation = rotation;
+        if (withDisplayCutout) {
+            cutout = WmDisplayCutout.computeSafeInsets(
+                    displayCutoutForRotation(rotation), info.logicalWidth,
+                    info.logicalHeight);
+            info.displayCutout = cutout.getDisplayCutout();
+        } else {
+            info.displayCutout = null;
+        }
+        return Pair.create(info, cutout);
+    }
+
+    private static DisplayCutout displayCutoutForRotation(int rotation) {
+        RectF rectF = new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
+
+        Matrix m = new Matrix();
+        transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
+        m.mapRect(rectF);
+
+        int pos = -1;
+        switch (rotation) {
+            case ROTATION_0:
+                pos = BOUNDS_POSITION_TOP;
+                break;
+            case ROTATION_90:
+                pos = BOUNDS_POSITION_LEFT;
+                break;
+            case ROTATION_180:
+                pos = BOUNDS_POSITION_BOTTOM;
+                break;
+            case ROTATION_270:
+                pos = BOUNDS_POSITION_RIGHT;
+                break;
+        }
+
+
+        return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top,
+                (int) rectF.right, (int) rectF.bottom, pos);
+    }
+
+    static class TestContextWrapper extends ContextWrapper {
+        private final TestableResources mResourceMocker;
+
+        TestContextWrapper(Context targetContext) {
+            super(targetContext);
+            mResourceMocker = new TestableResources(targetContext.getResources());
+        }
+
+        @Override
+        public int checkPermission(String permission, int pid, int uid) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+
+        @Override
+        public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+
+        @Override
+        public Resources getResources() {
+            return mResourceMocker.getResources();
+        }
+
+        public TestableResources getResourceMocker() {
+            return mResourceMocker;
+        }
+    }
+
+    static class TestablePhoneWindowManager extends PhoneWindowManager {
+
+        TestablePhoneWindowManager() {
+        }
+
+        @Override
+        void initializeHdmiState() {
+            // Do nothing.
+        }
+
+        @Override
+        Context getSystemUiContext() {
+            return mContext;
+        }
+
+        void addWindow(WindowState state) {
+            if (state instanceof FakeWindowState) {
+                ((FakeWindowState) state).surfaceLayer =
+                        getWindowLayerFromTypeLw(state.getAttrs().type,
+                                true /* canAddInternalSystemWindow */);
+            }
+            adjustWindowParamsLw(state, state.getAttrs(), true /* hasStatusBarPermission */);
+            assertEquals(WindowManagerGlobal.ADD_OKAY, prepareAddWindowLw(state, state.getAttrs()));
+        }
+
+        public static TestablePhoneWindowManager create(Context context) {
+            TestablePhoneWindowManager[] policy = new TestablePhoneWindowManager[1];
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+                policy[0] = new TestablePhoneWindowManager();
+                policy[0].mContext = context;
+                policy[0].mKeyguardDelegate = mock(KeyguardServiceDelegate.class);
+                policy[0].mAccessibilityManager = new AccessibilityManager(context,
+                        mock(IAccessibilityManager.class), UserHandle.USER_CURRENT);
+                policy[0].mSystemGestures = mock(SystemGesturesPointerEventListener.class);
+
+                final TestDisplayContent displayContent = TestDisplayContent.create(context);
+                policy[0].setDefaultDisplay(displayContent);
+                policy[0].onConfigurationChanged(displayContent);
+            });
+            return policy[0];
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
new file mode 100644
index 0000000..7a9c8dc
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
+
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link ActivityDisplay} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityDisplayTests
+ */
+@SmallTest
+@Presubmit
+public class ActivityDisplayTests extends ActivityTestsBase {
+
+    @Before
+    public void setUp() throws Exception {
+        setupActivityTaskManagerService();
+    }
+
+    @Test
+    public void testLastFocusedStackIsUpdatedWhenMovingStack() {
+        // Create a stack at bottom.
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final ActivityStack stack = new StackBuilder(mSupervisor).setOnTop(!ON_TOP).build();
+        final ActivityStack prevFocusedStack = display.getFocusedStack();
+
+        stack.moveToFront("moveStackToFront");
+        // After moving the stack to front, the previous focused should be the last focused.
+        assertTrue(stack.isFocusedStackOnDisplay());
+        assertEquals(prevFocusedStack, display.getLastFocusedStack());
+
+        stack.moveToBack("moveStackToBack", null /* task */);
+        // After moving the stack to back, the stack should be the last focused.
+        assertEquals(stack, display.getLastFocusedStack());
+    }
+
+    /**
+     * This test simulates the picture-in-picture menu activity launches an activity to fullscreen
+     * stack. The fullscreen stack should be the top focused for resuming correctly.
+     */
+    @Test
+    public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
+        // Create a pinned stack and move to front.
+        final ActivityStack pinnedStack = mSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final TaskRecord pinnedTask = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(pinnedStack).build();
+        new ActivityBuilder(mService).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
+                .setTask(pinnedTask).build();
+        pinnedStack.moveToFront("movePinnedStackToFront");
+
+        // The focused stack should be the pinned stack.
+        assertTrue(pinnedStack.isFocusedStackOnDisplay());
+
+        // Create a fullscreen stack and move to front.
+        final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt(
+                mSupervisor.getDefaultDisplay());
+        fullscreenStack.moveToFront("moveFullscreenStackToFront");
+
+        // The focused stack should be the fullscreen stack.
+        assertTrue(fullscreenStack.isFocusedStackOnDisplay());
+    }
+
+    /**
+     * Test {@link ActivityDisplay#mPreferredTopFocusableStack} will be cleared when the stack is
+     * removed or moved to back, and the focused stack will be according to z-order.
+     */
+    @Test
+    public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
+        // Create a display which only contains 2 stacks.
+        final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+        final ActivityStack stack1 = createFullscreenStackWithSimpleActivityAt(display);
+        final ActivityStack stack2 = createFullscreenStackWithSimpleActivityAt(display);
+
+        // Put stack1 and stack2 on top.
+        stack1.moveToFront("moveStack1ToFront");
+        stack2.moveToFront("moveStack2ToFront");
+        assertTrue(stack2.isFocusedStackOnDisplay());
+
+        // Stack1 should be focused after moving stack2 to back.
+        stack2.moveToBack("moveStack2ToBack", null /* task */);
+        assertTrue(stack1.isFocusedStackOnDisplay());
+
+        // Stack2 should be focused after removing stack1.
+        display.removeChild(stack1);
+        assertTrue(stack2.isFocusedStackOnDisplay());
+    }
+
+    /**
+     * Verifies {@link ActivityDisplay#remove} should not resume home stack on the removing display.
+     */
+    @Test
+    public void testNotResumeHomeStackOnRemovingDisplay() {
+        // Create a display which supports system decoration and allows reparenting stacks to
+        // another display when the display is removed.
+        final ActivityDisplay display = spy(createNewActivityDisplay());
+        doReturn(false).when(display).shouldDestroyContentOnRemove();
+        doReturn(true).when(display).supportsSystemDecorations();
+        mSupervisor.addChild(display, ActivityDisplay.POSITION_TOP);
+
+        // Put home stack on the display.
+        final ActivityStack homeStack = display.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(homeStack).build();
+        new ActivityBuilder(mService).setTask(task).build();
+        display.removeChild(homeStack);
+        final ActivityStack spiedHomeStack = spy(homeStack);
+        display.addChild(spiedHomeStack, ActivityDisplay.POSITION_TOP);
+        reset(spiedHomeStack);
+
+        // Put a finishing standard activity which will be reparented.
+        final ActivityStack stack = createFullscreenStackWithSimpleActivityAt(display);
+        stack.topRunningActivityLocked().makeFinishingLocked();
+
+        display.remove();
+
+        // The removed display should have no focused stack and its home stack should never resume.
+        assertNull(display.getFocusedStack());
+        verify(spiedHomeStack, never()).resumeTopActivityUncheckedLocked(any(), any());
+    }
+
+    private ActivityStack createFullscreenStackWithSimpleActivityAt(ActivityDisplay display) {
+        final ActivityStack fullscreenStack = display.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final TaskRecord fullscreenTask = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(fullscreenStack).build();
+        new ActivityBuilder(mService).setTask(fullscreenTask).build();
+        return fullscreenStack;
+    }
+
+    /**
+     * Verifies the correct activity is returned when querying the top running activity.
+     */
+    @Test
+    public void testTopRunningActivity() {
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final KeyguardController keyguard = mSupervisor.getKeyguardController();
+        final ActivityStack stack = new StackBuilder(mSupervisor).build();
+        final ActivityRecord activity = stack.getTopActivity();
+
+        // Create empty stack on top.
+        final ActivityStack emptyStack =
+                new StackBuilder(mSupervisor).setCreateActivity(false).build();
+
+        // Make sure the top running activity is not affected when keyguard is not locked.
+        assertTopRunningActivity(activity, display);
+
+        // Check to make sure activity not reported when it cannot show on lock and lock is on.
+        doReturn(true).when(keyguard).isKeyguardLocked();
+        assertEquals(activity, display.topRunningActivity());
+        assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+        // Move stack with activity to top.
+        stack.moveToFront("testStackToFront");
+        assertEquals(stack, display.getFocusedStack());
+        assertEquals(activity, display.topRunningActivity());
+        assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+        // Add activity that should be shown on the keyguard.
+        final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setStack(stack)
+                .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
+                .build();
+
+        // Ensure the show when locked activity is returned.
+        assertTopRunningActivity(showWhenLockedActivity, display);
+
+        // Move empty stack to front. The running activity in focusable stack which below the
+        // empty stack should be returned.
+        emptyStack.moveToFront("emptyStackToFront");
+        assertEquals(stack, display.getFocusedStack());
+        assertTopRunningActivity(showWhenLockedActivity, display);
+    }
+
+    private static void assertTopRunningActivity(ActivityRecord top, ActivityDisplay display) {
+        assertEquals(top, display.topRunningActivity());
+        assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
new file mode 100644
index 0000000..215c51d
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.content.Intent;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseIntArray;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link ActivityMetricsLaunchObserver} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityMetricsLaunchObserverTests
+ */
+@SmallTest
+@Presubmit
+@FlakyTest(detail="promote once confirmed non-flaky")
+public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
+    private ActivityMetricsLogger mActivityMetricsLogger;
+    private ActivityMetricsLaunchObserver mLaunchObserver;
+
+    private TestActivityStack mStack;
+    private TaskRecord mTask;
+    private ActivityRecord mActivityRecord;
+    private ActivityRecord mActivityRecordTrampoline;
+
+    @Before
+    public void setUpAMLO() throws Exception {
+        setupActivityTaskManagerService();
+
+        mActivityMetricsLogger =
+                new ActivityMetricsLogger(mSupervisor, mService.mContext, mService.mH.getLooper());
+
+        mLaunchObserver = mock(ActivityMetricsLaunchObserver.class);
+
+        // TODO: Use ActivityMetricsLaunchObserverRegistry .
+        java.lang.reflect.Field f =
+                mActivityMetricsLogger.getClass().getDeclaredField("mLaunchObserver");
+        f.setAccessible(true);
+        f.set(mActivityMetricsLogger, mLaunchObserver);
+
+        // Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
+        // This seems to be the easiest way to create an ActivityRecord.
+        mStack = mSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
+        mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build();
+        mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build();
+    }
+
+    @Test
+    public void testOnIntentStarted() throws Exception {
+        Intent intent = new Intent("action 1");
+
+        mActivityMetricsLogger.notifyActivityLaunching(intent);
+
+        verify(mLaunchObserver).onIntentStarted(eq(intent));
+        verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnIntentFailed() throws Exception {
+        testOnIntentStarted();
+
+        ActivityRecord activityRecord = null;
+
+        // Bringing an intent that's already running 'to front' is not considered
+        // as an ACTIVITY_LAUNCHED state transition.
+        mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
+                activityRecord);
+
+        verify(mLaunchObserver).onIntentFailed();
+        verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnActivityLaunched() throws Exception {
+        testOnIntentStarted();
+
+        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
+                mActivityRecord);
+
+        verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+        verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnActivityLaunchFinished() throws Exception {
+       testOnActivityLaunched();
+
+       mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
+               SystemClock.uptimeMillis());
+
+       mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(),
+               SystemClock.uptimeMillis());
+
+       verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecord));
+       verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnActivityLaunchCancelled() throws Exception {
+       testOnActivityLaunched();
+
+       mActivityRecord.nowVisible = true;
+
+       // Cannot time already-visible activities.
+       mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mActivityRecord);
+
+       verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecord));
+       verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnActivityLaunchedTrampoline() throws Exception {
+        testOnIntentStarted();
+
+        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
+                mActivityRecord);
+
+        verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+
+        // A second, distinct, activity launch is coalesced into the the current app launch sequence
+        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
+                mActivityRecordTrampoline);
+
+        verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnActivityLaunchFinishedTrampoline() throws Exception {
+       testOnActivityLaunchedTrampoline();
+
+       mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
+               SystemClock.uptimeMillis());
+
+       mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(),
+               SystemClock.uptimeMillis());
+
+       verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecordTrampoline));
+       verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnActivityLaunchCancelledTrampoline() throws Exception {
+       testOnActivityLaunchedTrampoline();
+
+       mActivityRecordTrampoline.nowVisible = true;
+
+       // Cannot time already-visible activities.
+       mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
+               mActivityRecordTrampoline);
+
+       verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecordTrampoline));
+       verifyNoMoreInteractions(mLaunchObserver);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
new file mode 100644
index 0000000..c449049
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityOptions;
+import android.os.Bundle;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:ActivityOptionsTest
+ */
+@MediumTest
+@Presubmit
+public class ActivityOptionsTest {
+
+    @Test
+    public void testMerge_NoClobber() {
+        // Construct some options with set values
+        ActivityOptions opts = ActivityOptions.makeBasic();
+        opts.setLaunchDisplayId(Integer.MAX_VALUE);
+        opts.setLaunchActivityType(ACTIVITY_TYPE_STANDARD);
+        opts.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        opts.setAvoidMoveToFront();
+        opts.setLaunchTaskId(Integer.MAX_VALUE);
+        opts.setLockTaskEnabled(true);
+        opts.setRotationAnimationHint(ROTATION_ANIMATION_ROTATE);
+        opts.setTaskOverlay(true, true);
+        opts.setSplitScreenCreateMode(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
+        Bundle optsBundle = opts.toBundle();
+
+        // Try and merge the constructed options with a new set of options
+        optsBundle.putAll(ActivityOptions.makeBasic().toBundle());
+
+        // Ensure the set values are not clobbered
+        ActivityOptions restoredOpts = ActivityOptions.fromBundle(optsBundle);
+        assertEquals(Integer.MAX_VALUE, restoredOpts.getLaunchDisplayId());
+        assertEquals(ACTIVITY_TYPE_STANDARD, restoredOpts.getLaunchActivityType());
+        assertEquals(WINDOWING_MODE_FULLSCREEN, restoredOpts.getLaunchWindowingMode());
+        assertTrue(restoredOpts.getAvoidMoveToFront());
+        assertEquals(Integer.MAX_VALUE, restoredOpts.getLaunchTaskId());
+        assertTrue(restoredOpts.getLockTaskMode());
+        assertEquals(ROTATION_ANIMATION_ROTATE, restoredOpts.getRotationAnimationHint());
+        assertTrue(restoredOpts.getTaskOverlay());
+        assertTrue(restoredOpts.canTaskOverlayResume());
+        assertEquals(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
+                restoredOpts.getSplitScreenCreateMode());
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
new file mode 100644
index 0000000..b865772
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
+
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityOptions;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.PauseActivityItem;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.util.MutableBoolean;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+
+/**
+ * Tests for the {@link ActivityRecord} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityRecordTests
+ */
+@MediumTest
+@Presubmit
+public class ActivityRecordTests extends ActivityTestsBase {
+    private TestActivityStack mStack;
+    private TaskRecord mTask;
+    private ActivityRecord mActivity;
+
+    @Before
+    public void setUp() throws Exception {
+        setupActivityTaskManagerService();
+        mStack = new StackBuilder(mSupervisor).build();
+        mTask = mStack.getChildAt(0);
+        mActivity = mTask.getTopActivity();
+    }
+
+    @Test
+    public void testStackCleanupOnClearingTask() {
+        mActivity.setTask(null);
+        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
+    }
+
+    @Test
+    public void testStackCleanupOnActivityRemoval() {
+        mTask.removeActivity(mActivity);
+        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(),  1);
+    }
+
+    @Test
+    public void testStackCleanupOnTaskRemoval() {
+        mStack.removeTask(mTask, null /*reason*/, REMOVE_TASK_MODE_MOVING);
+        // Stack should be gone on task removal.
+        assertNull(mService.mStackSupervisor.getStack(mStack.mStackId));
+    }
+
+    @Test
+    public void testNoCleanupMovingActivityInSameStack() {
+        final TaskRecord newTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack)
+                .build();
+        mActivity.reparent(newTask, 0, null /*reason*/);
+        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
+    }
+
+    @Test
+    public void testPausingWhenVisibleFromStopped() throws Exception {
+        final MutableBoolean pauseFound = new MutableBoolean(false);
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final ClientTransaction transaction = invocationOnMock.getArgument(0);
+            if (transaction.getLifecycleStateRequest() instanceof PauseActivityItem) {
+                pauseFound.value = true;
+            }
+            return null;
+        }).when(mActivity.app.getThread()).scheduleTransaction(any());
+
+        mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
+
+        // The activity is in the focused stack so it should not move to paused.
+        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+        assertTrue(mActivity.isState(STOPPED));
+        assertFalse(pauseFound.value);
+
+        // Clear focused stack
+        final ActivityDisplay display = mActivity.mStackSupervisor.getDefaultDisplay();
+        when(display.getFocusedStack()).thenReturn(null);
+
+        // In the unfocused stack, the activity should move to paused.
+        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+        assertTrue(mActivity.isState(PAUSING));
+        assertTrue(pauseFound.value);
+
+        // Make sure that the state does not change for current non-stopping states.
+        mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped");
+
+        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+
+        assertTrue(mActivity.isState(INITIALIZING));
+
+        // Make sure the state does not change if we are not the current top activity.
+        mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind");
+
+        // Make sure that the state does not change when we have an activity becoming translucent
+        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        mStack.mTranslucentActivityWaiting = topActivity;
+        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+
+        assertTrue(mActivity.isState(STOPPED));
+    }
+
+    @Test
+    public void testPositionLimitedAspectRatioNavBarBottom() {
+        verifyPositionWithLimitedAspectRatio(NAV_BAR_BOTTOM, new Rect(0, 0, 1000, 2000), 1.5f,
+                new Rect(0, 0, 1000, 1500));
+    }
+
+    @Test
+    public void testPositionLimitedAspectRatioNavBarLeft() {
+        verifyPositionWithLimitedAspectRatio(NAV_BAR_LEFT, new Rect(0, 0, 2000, 1000), 1.5f,
+                new Rect(500, 0, 2000, 1000));
+    }
+
+    @Test
+    public void testPositionLimitedAspectRatioNavBarRight() {
+        verifyPositionWithLimitedAspectRatio(NAV_BAR_RIGHT, new Rect(0, 0, 2000, 1000), 1.5f,
+                new Rect(0, 0, 1500, 1000));
+    }
+
+    private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds,
+            float aspectRatio, Rect expectedActivityBounds) {
+        // Verify with nav bar on the right.
+        when(mService.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition);
+        mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds);
+        mActivity.info.maxAspectRatio = aspectRatio;
+        mActivity.ensureActivityConfiguration(
+                0 /* globalChanges */, false /* preserveWindow */);
+        assertEquals(expectedActivityBounds, mActivity.getBounds());
+    }
+
+    @Test
+    public void testCanBeLaunchedOnDisplay() {
+        mService.mSupportsMultiWindow = true;
+        final ActivityRecord activity = new ActivityBuilder(mService).build();
+
+        // An activity can be launched on default display.
+        assertTrue(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY));
+        // An activity cannot be launched on a non-existent display.
+        assertFalse(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY + 1));
+    }
+
+    @Test
+    public void testsApplyOptionsLocked() {
+        ActivityOptions activityOptions = ActivityOptions.makeBasic();
+
+        // Set and apply options for ActivityRecord. Pending options should be cleared
+        mActivity.updateOptionsLocked(activityOptions);
+        mActivity.applyOptionsLocked();
+        assertNull(mActivity.pendingOptions);
+
+        // Set options for two ActivityRecords in same Task. Apply one ActivityRecord options.
+        // Pending options should be cleared for both ActivityRecords
+        ActivityRecord activity2 = new ActivityBuilder(mService).setTask(mTask).build();
+        activity2.updateOptionsLocked(activityOptions);
+        mActivity.updateOptionsLocked(activityOptions);
+        mActivity.applyOptionsLocked();
+        assertNull(mActivity.pendingOptions);
+        assertNull(activity2.pendingOptions);
+
+        // Set options for two ActivityRecords in separate Tasks. Apply one ActivityRecord options.
+        // Pending options should be cleared for only ActivityRecord that was applied
+        TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
+        activity2 = new ActivityBuilder(mService).setTask(task2).build();
+        activity2.updateOptionsLocked(activityOptions);
+        mActivity.updateOptionsLocked(activityOptions);
+        mActivity.applyOptionsLocked();
+        assertNull(mActivity.pendingOptions);
+        assertNotNull(activity2.pendingOptions);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
new file mode 100644
index 0000000..f692a57
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityOptions;
+import android.app.WaitResult;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for the {@link ActivityStackSupervisor} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityStackSupervisorTests
+ */
+@MediumTest
+@Presubmit
+public class ActivityStackSupervisorTests extends ActivityTestsBase {
+    private ActivityStack mFullscreenStack;
+
+    @Before
+    public void setUp() throws Exception {
+        setupActivityTaskManagerService();
+        mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+    }
+
+    /**
+     * This test ensures that we do not try to restore a task based off an invalid task id. We
+     * should expect {@code null} to be returned in this case.
+     */
+    @Test
+    public void testRestoringInvalidTask() {
+        ((TestActivityDisplay) mSupervisor.getDefaultDisplay()).removeAllTasks();
+        TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/,
+                MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
+        assertNull(task);
+    }
+
+    /**
+     * This test ensures that an existing task in the pinned stack is moved to the fullscreen
+     * activity stack when a new task is added.
+     */
+    @Test
+    public void testReplacingTaskInPinnedStack() {
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
+        final TaskRecord firstTask = firstActivity.getTask();
+
+        final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
+        final TaskRecord secondTask = secondActivity.getTask();
+
+        mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
+
+        // Ensure full screen stack has both tasks.
+        ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
+
+        // Move first activity to pinned stack.
+        final Rect sourceBounds = new Rect();
+        mSupervisor.moveActivityToPinnedStackLocked(firstActivity, sourceBounds,
+                0f /*aspectRatio*/, "initialMove");
+
+        final ActivityDisplay display = mFullscreenStack.getDisplay();
+        ActivityStack pinnedStack = display.getPinnedStack();
+        // Ensure a task has moved over.
+        ensureStackPlacement(pinnedStack, firstTask);
+        ensureStackPlacement(mFullscreenStack, secondTask);
+
+        // Move second activity to pinned stack.
+        mSupervisor.moveActivityToPinnedStackLocked(secondActivity, sourceBounds,
+                0f /*aspectRatio*/, "secondMove");
+
+        // Need to get stacks again as a new instance might have been created.
+        pinnedStack = display.getPinnedStack();
+        mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        // Ensure stacks have swapped tasks.
+        ensureStackPlacement(pinnedStack, secondTask);
+        ensureStackPlacement(mFullscreenStack, firstTask);
+    }
+
+    private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
+        final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
+        assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
+
+        if (tasks == null) {
+            return;
+        }
+
+        for (TaskRecord task : tasks) {
+            assertTrue(stackTasks.contains(task));
+        }
+    }
+
+    /**
+     * Ensures that an activity is removed from the stopping activities list once it is resumed.
+     */
+    @Test
+    public void testStoppingActivityRemovedWhenResumed() {
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
+        mSupervisor.mStoppingActivities.add(firstActivity);
+
+        firstActivity.completeResumeLocked();
+
+        assertFalse(mSupervisor.mStoppingActivities.contains(firstActivity));
+    }
+
+    /**
+     * Ensures that waiting results are notified of launches.
+     */
+    @SuppressWarnings("SynchronizeOnNonFinalField")
+    @Test
+    public void testReportWaitingActivityLaunchedIfNeeded() {
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
+
+        // #notifyAll will be called on the ActivityManagerService. we must hold the object lock
+        // when this happens.
+        synchronized (mSupervisor.mService.mGlobalLock) {
+            final WaitResult taskToFrontWait = new WaitResult();
+            mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
+            mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT);
+
+            assertThat(mSupervisor.mWaitingActivityLaunched).isEmpty();
+            assertEquals(taskToFrontWait.result, START_TASK_TO_FRONT);
+            assertNull(taskToFrontWait.who);
+
+            final WaitResult deliverToTopWait = new WaitResult();
+            mSupervisor.mWaitingActivityLaunched.add(deliverToTopWait);
+            mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity,
+                    START_DELIVERED_TO_TOP);
+
+            assertThat(mSupervisor.mWaitingActivityLaunched).isEmpty();
+            assertEquals(deliverToTopWait.result, START_DELIVERED_TO_TOP);
+            assertEquals(deliverToTopWait.who, firstActivity.realActivity);
+        }
+    }
+
+    @Test
+    public void testApplySleepTokensLocked() {
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final KeyguardController keyguard = mSupervisor.getKeyguardController();
+        final ActivityStack stack = mock(ActivityStack.class);
+        display.addChild(stack, 0 /* position */);
+
+        // Make sure we wake and resume in the case the display is turning on and the keyguard is
+        // not showing.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                false /* keyguardShowing */, true /* expectWakeFromSleep */,
+                true /* expectResumeTopActivity */);
+
+        // Make sure we wake and don't resume when the display is turning on and the keyguard is
+        // showing.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                true /* keyguardShowing */, true /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+
+        // Make sure we wake and don't resume when the display is turning on and the keyguard is
+        // not showing as unfocused.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, false /* isFocusedStack */,
+                false /* keyguardShowing */, true /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+
+        // Should not do anything if the display state hasn't changed.
+        verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                false /* keyguardShowing */, false /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+    }
+
+    private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
+            ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
+            boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
+            boolean expectResumeTopActivity) {
+        reset(stack);
+
+        doReturn(displayShouldSleep).when(display).shouldSleep();
+        doReturn(displaySleeping).when(display).isSleeping();
+        doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
+
+        doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
+        doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
+        mSupervisor.applySleepTokensLocked(true);
+        verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+        verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
+                null /* target */, null /* targetOptions */);
+    }
+
+    /**
+     * Verifies that removal of activity with task and stack is done correctly.
+     */
+    @Test
+    public void testRemovingStackOnAppCrash() {
+        final ActivityDisplay defaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
+        final int originalStackCount = defaultDisplay.getChildCount();
+        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(stack).build();
+
+        assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
+
+        // Let's pretend that the app has crashed.
+        firstActivity.app.setThread(null);
+        mService.mStackSupervisor.finishTopCrashedActivitiesLocked(firstActivity.app, "test");
+
+        // Verify that the stack was removed.
+        assertEquals(originalStackCount, defaultDisplay.getChildCount());
+    }
+
+    @Test
+    public void testFocusability() {
+        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(stack).build();
+
+        // Under split screen primary we should be focusable when not minimized
+        mService.mStackSupervisor.setDockedStackMinimized(false);
+        assertTrue(stack.isFocusable());
+        assertTrue(activity.isFocusable());
+
+        // Under split screen primary we should not be focusable when minimized
+        mService.mStackSupervisor.setDockedStackMinimized(true);
+        assertFalse(stack.isFocusable());
+        assertFalse(activity.isFocusable());
+
+        final ActivityStack pinnedStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(pinnedStack).build();
+
+        // We should not be focusable when in pinned mode
+        assertFalse(pinnedStack.isFocusable());
+        assertFalse(pinnedActivity.isFocusable());
+
+        // Add flag forcing focusability.
+        pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
+
+        // We should not be focusable when in pinned mode
+        assertTrue(pinnedStack.isFocusable());
+        assertTrue(pinnedActivity.isFocusable());
+
+        // Without the overridding activity, stack should not be focusable.
+        pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability",
+                REMOVE_TASK_MODE_DESTROYING);
+        assertFalse(pinnedStack.isFocusable());
+    }
+
+    /**
+     * Verify that split-screen primary stack will be chosen if activity is launched that targets
+     * split-screen secondary, but a matching existing instance is found on top of split-screen
+     * primary stack.
+     */
+    @Test
+    public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
+        // Create primary split-screen stack with a task and an activity.
+        final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
+                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                        true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
+
+        // Find a launch stack for the top activity in split-screen primary, while requesting
+        // split-screen secondary.
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        final ActivityStack result = mSupervisor.getLaunchStack(r, options, task, true /* onTop */);
+
+        // Assert that the primary stack is returned.
+        assertEquals(primaryStack, result);
+    }
+
+    /**
+     * Verify split-screen primary stack & task can resized by
+     * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
+     */
+    @Test
+    public void testResizeDockedStackForSplitScreenPrimary() {
+        final Rect taskSize = new Rect(0, 0, 600, 600);
+        final Rect stackSize = new Rect(0, 0, 300, 300);
+
+        // Create primary split-screen stack with a task.
+        final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
+                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                        true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+
+        // Resize dock stack.
+        mService.resizeDockedStack(stackSize, taskSize, null, null, null);
+
+        // Verify dock stack & its task bounds if is equal as resized result.
+        assertEquals(primaryStack.getBounds(), stackSize);
+        assertEquals(task.getBounds(), taskSize);
+    }
+
+    /**
+     * Verify that home stack would be moved to front when the top activity is Recents.
+     */
+    @Test
+    public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
+        // Create stack/task on default display.
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final TestActivityStack targetStack = new StackBuilder(mSupervisor).setOnTop(false).build();
+        final TaskRecord targetTask = targetStack.getChildAt(0);
+
+        // Create Recents on top of the display.
+        final ActivityStack stack =
+                new StackBuilder(mSupervisor).setActivityType(ACTIVITY_TYPE_RECENTS).build();
+
+        final String reason = "findTaskToMoveToFront";
+        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+                false);
+
+        verify(display).moveHomeStackToFront(contains(reason));
+    }
+
+    /**
+     * Verify that home stack won't be moved to front if the top activity on other display is
+     * Recents.
+     */
+    @Test
+    public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
+        // Create stack/task on default display.
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+
+        // Create Recents on secondary display.
+        final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
+                ActivityDisplay.POSITION_TOP);
+        final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_RECENTS, true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        new ActivityBuilder(mService).setTask(task).build();
+
+        final String reason = "findTaskToMoveToFront";
+        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+                false);
+
+        verify(display, never()).moveHomeStackToFront(contains(reason));
+    }
+
+    /**
+     * Verify if a stack is not at the topmost position, it should be able to resume its activity if
+     * the stack is the top focused.
+     */
+    @Test
+    public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
+        // Create a stack at bottom.
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */));
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
+        display.positionChildAtBottom(targetStack);
+
+        // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
+        // is the current top focused stack.
+        assertFalse(targetStack.isTopStackOnDisplay());
+        doReturn(targetStack).when(mSupervisor).getTopDisplayFocusedStack();
+
+        // Use the stack as target to resume.
+        mSupervisor.resumeFocusedStacksTopActivitiesLocked(
+                targetStack, activity, null /* targetOptions */);
+
+        // Verify the target stack should resume its activity.
+        verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
+                eq(activity), eq(null /* targetOptions */));
+    }
+
+
+    /**
+     * Tests that home activities can be started on the displays that supports system decorations.
+     */
+    @Test
+    public void testStartHomeOnAllDisplays() {
+        // Create secondary displays.
+        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+        mSupervisor.addChild(secondDisplay, POSITION_TOP);
+        doReturn(true).when(secondDisplay).supportsSystemDecorations();
+
+        // Create mock tasks and other necessary mocks.
+        TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
+        final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
+        TaskRecord.setTaskRecordFactory(factory);
+        doAnswer(i -> taskBuilder.build()).when(factory)
+                .create(any(), anyInt(), any(), any(), any(), any());
+        doReturn(true).when(mService.mStackSupervisor)
+                .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
+        doReturn(true).when(mSupervisor).canStartHomeOnDisplay(any(), anyInt(), anyBoolean());
+
+        mSupervisor.startHomeOnAllDisplays(0, "testStartHome");
+
+        assertTrue(mSupervisor.getDefaultDisplay().getTopStack().isActivityTypeHome());
+        assertNotNull(secondDisplay.getTopStack());
+        assertTrue(secondDisplay.getTopStack().isActivityTypeHome());
+    }
+
+    /**
+     * Tests that home activities won't be started before booting when display added.
+     */
+    @Test
+    public void testNotStartHomeBeforeBoot() {
+        final int displayId = 1;
+        final boolean isBooting = mService.mAmInternal.isBooting();
+        final boolean isBooted = mService.mAmInternal.isBooted();
+        try {
+            mService.mAmInternal.setBooting(false);
+            mService.mAmInternal.setBooted(false);
+            mSupervisor.onDisplayAdded(displayId);
+            verify(mSupervisor, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
+        } finally {
+            mService.mAmInternal.setBooting(isBooting);
+            mService.mAmInternal.setBooted(isBooted);
+        }
+    }
+
+    /**
+     * Tests whether home can be started if being instrumented.
+     */
+    @Test
+    public void testCanStartHomeWhenInstrumented() {
+        final ActivityInfo info = new ActivityInfo();
+        info.applicationInfo = new ApplicationInfo();
+        final WindowProcessController app = mock(WindowProcessController.class);
+        doReturn(app).when(mService).getProcessController(any(), anyInt());
+
+        // Can not start home if we don't want to start home while home is being instrumented.
+        doReturn(true).when(app).isInstrumenting();
+        assertFalse(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+                false /* allowInstrumenting*/));
+
+        // Can start home for other cases.
+        assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+                true /* allowInstrumenting*/));
+
+        doReturn(false).when(app).isInstrumenting();
+        assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+                false /* allowInstrumenting*/));
+        assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+                true /* allowInstrumenting*/));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
new file mode 100644
index 0000000..62767e3
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -0,0 +1,798 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.pm.ActivityInfo;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link ActivityStack} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityStackTests
+ */
+@SmallTest
+@Presubmit
+public class ActivityStackTests extends ActivityTestsBase {
+    private ActivityDisplay mDefaultDisplay;
+    private ActivityStack mStack;
+    private TaskRecord mTask;
+
+    @Before
+    public void setUp() throws Exception {
+        setupActivityTaskManagerService();
+        mDefaultDisplay = mSupervisor.getDefaultDisplay();
+        mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */));
+        mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
+    }
+
+    @Test
+    public void testEmptyTaskCleanupOnRemove() {
+        assertNotNull(mTask.getWindowContainerController());
+        mStack.removeTask(mTask, "testEmptyTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
+        assertNull(mTask.getWindowContainerController());
+    }
+
+    @Test
+    public void testOccupiedTaskCleanupOnRemove() {
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
+        assertNotNull(mTask.getWindowContainerController());
+        mStack.removeTask(mTask, "testOccupiedTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
+        assertNotNull(mTask.getWindowContainerController());
+    }
+
+    @Test
+    public void testResumedActivity() {
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
+        assertNull(mStack.getResumedActivity());
+        r.setState(RESUMED, "testResumedActivity");
+        assertEquals(r, mStack.getResumedActivity());
+        r.setState(PAUSING, "testResumedActivity");
+        assertNull(mStack.getResumedActivity());
+    }
+
+    @Test
+    public void testResumedActivityFromTaskReparenting() {
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
+        // Ensure moving task between two stacks updates resumed activity
+        r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
+        assertEquals(r, mStack.getResumedActivity());
+
+        final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        mTask.reparent(destStack, true /* toTop */, TaskRecord.REPARENT_KEEP_STACK_AT_FRONT,
+                false /* animate */, true /* deferResume*/,
+                "testResumedActivityFromTaskReparenting");
+
+        assertNull(mStack.getResumedActivity());
+        assertEquals(r, destStack.getResumedActivity());
+    }
+
+    @Test
+    public void testResumedActivityFromActivityReparenting() {
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
+        // Ensure moving task between two stacks updates resumed activity
+        r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
+        assertEquals(r, mStack.getResumedActivity());
+
+        final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();
+
+        mTask.removeActivity(r);
+        destTask.addActivityToTop(r);
+
+        assertNull(mStack.getResumedActivity());
+        assertEquals(r, destStack.getResumedActivity());
+    }
+
+    @Test
+    public void testPrimarySplitScreenRestoresWhenMovedToBack() {
+        // Create primary splitscreen stack. This will create secondary stacks and places the
+        // existing fullscreen stack on the bottom.
+        final ActivityStack primarySplitScreen = mDefaultDisplay.createStack(
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        // Assert windowing mode.
+        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, primarySplitScreen.getWindowingMode());
+
+        // Move primary to back.
+        primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack",
+                null /* task */);
+
+        // Assert that stack is at the bottom.
+        assertEquals(0, mDefaultDisplay.getIndexOf(primarySplitScreen));
+
+        // Ensure no longer in splitscreen.
+        assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
+
+        // Ensure that the override mode is restored to undefined
+        assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
+    }
+
+    @Test
+    public void testPrimarySplitScreenRestoresPreviousWhenMovedToBack() {
+        // This time, start with a fullscreen activitystack
+        final ActivityStack primarySplitScreen = mDefaultDisplay.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        primarySplitScreen.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+        // Assert windowing mode.
+        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, primarySplitScreen.getWindowingMode());
+
+        // Move primary to back.
+        primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack",
+                null /* task */);
+
+        // Assert that stack is at the bottom.
+        assertEquals(0, mDefaultDisplay.getIndexOf(primarySplitScreen));
+
+        // Ensure that the override mode is restored to what it was (fullscreen)
+        assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getOverrideWindowingMode());
+    }
+
+    @Test
+    public void testStackInheritsDisplayWindowingMode() {
+        final ActivityStack primarySplitScreen = mDefaultDisplay.createStack(
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
+        assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
+
+        mDefaultDisplay.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(WINDOWING_MODE_FREEFORM, primarySplitScreen.getWindowingMode());
+        assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
+    }
+
+    @Test
+    public void testStackOverridesDisplayWindowingMode() {
+        final ActivityStack primarySplitScreen = mDefaultDisplay.createStack(
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
+        assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getOverrideWindowingMode());
+
+        primarySplitScreen.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        // setting windowing mode should still work even though resolved mode is already fullscreen
+        assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getOverrideWindowingMode());
+
+        mDefaultDisplay.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
+    }
+
+    @Test
+    public void testStopActivityWhenActivityDestroyed() {
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
+        r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
+        mStack.moveToFront("testStopActivityWithDestroy");
+        mStack.stopActivityLocked(r);
+        // Mostly testing to make sure there is a crash in the call part, so if we get here we are
+        // good-to-go!
+    }
+
+    @Test
+    public void testFindTaskWithOverlay() {
+        final ActivityRecord r = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setStack(mStack)
+                .setUid(0)
+                .build();
+        final TaskRecord task = r.getTask();
+        // Overlay must be for a different user to prevent recognizing a matching top activity
+        final ActivityRecord taskOverlay = new ActivityBuilder(mService).setTask(task)
+                .setUid(UserHandle.PER_USER_RANGE * 2).build();
+        taskOverlay.mTaskOverlay = true;
+
+        final ActivityStackSupervisor.FindTaskResult result =
+                new ActivityStackSupervisor.FindTaskResult();
+        mStack.findTaskLocked(r, result);
+
+        assertEquals(r, task.getTopActivity(false /* includeOverlays */));
+        assertEquals(taskOverlay, task.getTopActivity(true /* includeOverlays */));
+        assertNotNull(result.mRecord);
+    }
+
+    @Test
+    public void testMoveStackToBackIncludingParent() {
+        final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+        final ActivityStack stack1 = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityStack stack2 = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        // Do not move display to back because there is still another stack.
+        stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.topTask());
+        verify(stack2.getWindowContainerController()).positionChildAtBottom(any(),
+                eq(false) /* includingParents */);
+
+        // Also move display to back because there is only one stack left.
+        display.removeChild(stack1);
+        stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.topTask());
+        verify(stack2.getWindowContainerController()).positionChildAtBottom(any(),
+                eq(true) /* includingParents */);
+    }
+
+    @Test
+    public void testShouldBeVisible_Fullscreen() {
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        assertTrue(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+
+        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        // Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack
+        // should be visible since it is always on-top.
+        fullscreenStack.setIsTranslucent(false);
+        assertFalse(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+        assertTrue(fullscreenStack.shouldBeVisible(null /* starting */));
+
+        // Home stack should be visible behind a translucent fullscreen stack.
+        fullscreenStack.setIsTranslucent(true);
+        assertTrue(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+    }
+
+    @Test
+    public void testShouldBeVisible_SplitScreen() {
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        // Home stack should always be fullscreen for this test.
+        homeStack.setSupportsSplitScreen(false);
+        final TestActivityStack splitScreenPrimary =
+                createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TestActivityStack splitScreenSecondary =
+                createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        // Home stack shouldn't be visible if both halves of split-screen are opaque.
+        splitScreenPrimary.setIsTranslucent(false);
+        splitScreenSecondary.setIsTranslucent(false);
+        assertFalse(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+
+        // Home stack should be visible if one of the halves of split-screen is translucent.
+        splitScreenPrimary.setIsTranslucent(true);
+        assertTrue(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+
+        final TestActivityStack splitScreenSecondary2 =
+                createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        // First split-screen secondary shouldn't be visible behind another opaque split-split
+        // secondary.
+        splitScreenSecondary2.setIsTranslucent(false);
+        assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+        // First split-screen secondary should be visible behind another translucent split-screen
+        // secondary.
+        splitScreenSecondary2.setIsTranslucent(true);
+        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+        final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
+
+        // Split-screen stacks shouldn't be visible behind an opaque fullscreen stack.
+        assistantStack.setIsTranslucent(false);
+        assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+        assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
+        assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
+        assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+        // Split-screen stacks should be visible behind a translucent fullscreen stack.
+        assistantStack.setIsTranslucent(true);
+        assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+        // Assistant stack shouldn't be visible behind translucent split-screen stack
+        assistantStack.setIsTranslucent(false);
+        splitScreenPrimary.setIsTranslucent(true);
+        splitScreenSecondary2.setIsTranslucent(true);
+        splitScreenSecondary2.moveToFront("testShouldBeVisible_SplitScreen");
+        splitScreenPrimary.moveToFront("testShouldBeVisible_SplitScreen");
+        assertFalse(assistantStack.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+    }
+
+    @Test
+    public void testShouldBeVisible_Finishing() {
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final TestActivityStack translucentStack = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        translucentStack.setIsTranslucent(true);
+
+        assertTrue(homeStack.shouldBeVisible(null /* starting */));
+        assertTrue(translucentStack.shouldBeVisible(null /* starting */));
+
+        final ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+        topRunningHomeActivity.finishing = true;
+        final ActivityRecord topRunningTranslucentActivity =
+                translucentStack.topRunningActivityLocked();
+        topRunningTranslucentActivity.finishing = true;
+
+        // Home stack should be visible even there are no running activities.
+        assertTrue(homeStack.shouldBeVisible(null /* starting */));
+        // Home should be visible if we are starting an activity within it.
+        assertTrue(homeStack.shouldBeVisible(topRunningHomeActivity /* starting */));
+        // The translucent stack shouldn't be visible since its activity marked as finishing.
+        assertFalse(translucentStack.shouldBeVisible(null /* starting */));
+    }
+
+    @Test
+    public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
+        mDefaultDisplay.removeChild(mStack);
+
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        homeStack.setIsTranslucent(false);
+        fullscreenStack.setIsTranslucent(false);
+
+        // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
+        int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
+        assertEquals(fullscreenStack, mDefaultDisplay.getStackAbove(homeStack));
+        mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
+        assertEquals(homeStackIndex, mDefaultDisplay.getIndexOf(homeStack));
+    }
+
+    @Test
+    public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
+        mDefaultDisplay.removeChild(mStack);
+
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        homeStack.setIsTranslucent(false);
+        fullscreenStack.setIsTranslucent(true);
+
+        // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
+        int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
+        assertEquals(fullscreenStack, mDefaultDisplay.getStackAbove(homeStack));
+        mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
+        assertEquals(homeStackIndex, mDefaultDisplay.getIndexOf(homeStack));
+    }
+
+    @Test
+    public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
+        mDefaultDisplay.removeChild(mStack);
+
+        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+
+        homeStack.setIsTranslucent(false);
+        fullscreenStack.setIsTranslucent(false);
+
+        // Ensure we don't move the home stack if it is already on top
+        int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
+        assertNull(mDefaultDisplay.getStackAbove(homeStack));
+        mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
+        assertEquals(homeStackIndex, mDefaultDisplay.getIndexOf(homeStack));
+    }
+
+    @Test
+    public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
+        mDefaultDisplay.removeChild(mStack);
+
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        homeStack.setIsTranslucent(false);
+        fullscreenStack1.setIsTranslucent(false);
+        fullscreenStack2.setIsTranslucent(false);
+
+        // Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the
+        // pinned stack
+        assertEquals(fullscreenStack1, mDefaultDisplay.getStackAbove(homeStack));
+        mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
+        assertEquals(fullscreenStack2, mDefaultDisplay.getStackAbove(homeStack));
+    }
+
+    @Test
+    public void
+            testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
+        mDefaultDisplay.removeChild(mStack);
+
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+
+        homeStack.setIsTranslucent(false);
+        fullscreenStack1.setIsTranslucent(false);
+        fullscreenStack2.setIsTranslucent(true);
+
+        // Ensure that we move the home stack behind the bottom most non-translucent fullscreen
+        // stack
+        assertEquals(fullscreenStack1, mDefaultDisplay.getStackAbove(homeStack));
+        mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
+        assertEquals(fullscreenStack1, mDefaultDisplay.getStackAbove(homeStack));
+    }
+
+    @Test
+    public void testMoveHomeStackBehindStack_BehindHomeStack() {
+        mDefaultDisplay.removeChild(mStack);
+
+        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+
+        homeStack.setIsTranslucent(false);
+        fullscreenStack1.setIsTranslucent(false);
+        fullscreenStack2.setIsTranslucent(false);
+
+        // Ensure we don't move the home stack behind itself
+        int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
+        mDefaultDisplay.moveStackBehindStack(homeStack, homeStack);
+        assertEquals(homeStackIndex, mDefaultDisplay.getIndexOf(homeStack));
+    }
+
+    @Test
+    public void testMoveHomeStackBehindStack() {
+        mDefaultDisplay.removeChild(mStack);
+
+        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        final TestActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        final TestActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+
+        mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack1);
+        assertEquals(fullscreenStack1, mDefaultDisplay.getStackAbove(homeStack));
+        mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack2);
+        assertEquals(fullscreenStack2, mDefaultDisplay.getStackAbove(homeStack));
+        mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack4);
+        assertEquals(fullscreenStack4, mDefaultDisplay.getStackAbove(homeStack));
+        mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack2);
+        assertEquals(fullscreenStack2, mDefaultDisplay.getStackAbove(homeStack));
+    }
+
+    @Test
+    public void testSetAlwaysOnTop() {
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        assertEquals(pinnedStack, mDefaultDisplay.getStackAbove(homeStack));
+
+        final TestActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        alwaysOnTopStack.setAlwaysOnTop(true);
+        assertTrue(alwaysOnTopStack.isAlwaysOnTop());
+        // Ensure (non-pinned) always on top stack is put below pinned stack.
+        assertEquals(pinnedStack, mDefaultDisplay.getStackAbove(alwaysOnTopStack));
+
+        final TestActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        // Ensure non always on top stack is put below always on top stacks.
+        assertEquals(alwaysOnTopStack, mDefaultDisplay.getStackAbove(nonAlwaysOnTopStack));
+
+        final TestActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        alwaysOnTopStack2.setAlwaysOnTop(true);
+        assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
+        // Ensure newly created always on top stack is placed above other all always on top stacks.
+        assertEquals(pinnedStack, mDefaultDisplay.getStackAbove(alwaysOnTopStack2));
+
+        alwaysOnTopStack2.setAlwaysOnTop(false);
+        // Ensure, when always on top is turned off for a stack, the stack is put just below all
+        // other always on top stacks.
+        assertEquals(alwaysOnTopStack, mDefaultDisplay.getStackAbove(alwaysOnTopStack2));
+        alwaysOnTopStack2.setAlwaysOnTop(true);
+
+        // Ensure always on top state changes properly when windowing mode changes.
+        alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertFalse(alwaysOnTopStack2.isAlwaysOnTop());
+        assertEquals(alwaysOnTopStack, mDefaultDisplay.getStackAbove(alwaysOnTopStack2));
+        alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
+        assertEquals(pinnedStack, mDefaultDisplay.getStackAbove(alwaysOnTopStack2));
+    }
+
+    @Test
+    public void testSplitScreenMoveToFront() {
+        final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
+
+        splitScreenPrimary.setIsTranslucent(false);
+        splitScreenSecondary.setIsTranslucent(false);
+        assistantStack.setIsTranslucent(false);
+
+        assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
+        assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
+        assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+
+        splitScreenSecondary.moveToFront("testSplitScreenMoveToFront");
+
+        assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+        assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+        assertFalse(assistantStack.shouldBeVisible(null /* starting */));
+    }
+
+    @SuppressWarnings("TypeParameterUnusedInFormals")
+    private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
+            ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
+        final T stack;
+        if (activityType == ACTIVITY_TYPE_HOME) {
+            // Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
+            stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+            if (onTop) {
+                mDefaultDisplay.positionChildAtTop(stack, false /* includingParents */);
+            } else {
+                mDefaultDisplay.positionChildAtBottom(stack);
+            }
+        } else {
+            stack = display.createStack(windowingMode, activityType, onTop);
+            final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
+                    .setCreateTask(true).build();
+        }
+        return stack;
+    }
+
+    @Test
+    public void testFinishDisabledPackageActivities() {
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(mTask).build();
+
+        // Making the second activity a task overlay without an app means it will be removed from
+        // the task's activities as well once first activity is removed.
+        secondActivity.mTaskOverlay = true;
+        secondActivity.app = null;
+
+        assertEquals(2, mTask.mActivities.size());
+
+        mStack.finishDisabledPackageActivitiesLocked(firstActivity.packageName, null,
+                true /* doit */, true /* evenPersistent */, UserHandle.USER_ALL);
+
+        assertThat(mTask.mActivities).isEmpty();
+        assertThat(mStack.getAllTasks()).isEmpty();
+    }
+
+    @Test
+    public void testHandleAppDied() {
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(mTask).build();
+
+        // Making the first activity a task overlay means it will be removed from the task's
+        // activities as well once second activity is removed as handleAppDied processes the
+        // activity list in reverse.
+        firstActivity.mTaskOverlay = true;
+        firstActivity.app = null;
+
+        // second activity will be immediately removed as it has no state.
+        secondActivity.haveState = false;
+
+        assertEquals(2, mTask.mActivities.size());
+
+        mStack.handleAppDiedLocked(secondActivity.app);
+
+        assertThat(mTask.mActivities).isEmpty();
+        assertThat(mStack.getAllTasks()).isEmpty();
+    }
+
+    @Test
+    public void testFinishCurrentActivity() {
+        // Create 2 activities on a new display.
+        final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+        final ActivityStack stack1 = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityStack stack2 = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        // There is still an activity1 in stack1 so the activity2 should be added to finishing list
+        // that will be destroyed until idle.
+        stack2.getTopActivity().visible = true;
+        final ActivityRecord activity2 = finishCurrentActivity(stack2);
+        assertEquals(STOPPING, activity2.getState());
+        assertThat(mSupervisor.mStoppingActivities).contains(activity2);
+
+        // The display becomes empty. Since there is no next activity to be idle, the activity
+        // should be destroyed immediately with updating configuration to restore original state.
+        final ActivityRecord activity1 = finishCurrentActivity(stack1);
+        assertEquals(DESTROYING, activity1.getState());
+        verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */,
+                eq(display.mDisplayId), anyBoolean(), anyBoolean());
+    }
+
+    private ActivityRecord finishCurrentActivity(ActivityStack stack) {
+        final ActivityRecord activity = stack.topRunningActivityLocked();
+        assertNotNull(activity);
+        activity.setState(PAUSED, "finishCurrentActivity");
+        activity.makeFinishingLocked();
+        stack.finishCurrentActivityLocked(activity, ActivityStack.FINISH_AFTER_VISIBLE,
+                false /* oomAdj */, "finishCurrentActivity");
+        return activity;
+    }
+
+    @Test
+    public void testShouldSleepActivities() {
+        // When focused activity and keyguard is going away, we should not sleep regardless
+        // of the display state
+        verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
+                true /* displaySleeping */, false /* expected*/);
+
+        // When not the focused stack, defer to display sleeping state.
+        verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/,
+                true /* displaySleeping */, true /* expected*/);
+
+        // If keyguard is going away, defer to the display sleeping state.
+        verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
+                true /* displaySleeping */, true /* expected*/);
+        verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
+                false /* displaySleeping */, false /* expected*/);
+    }
+
+    @Test
+    public void testStackOrderChangedOnRemoveStack() {
+        StackOrderChangedListener listener = new StackOrderChangedListener();
+        mDefaultDisplay.registerStackOrderChangedListener(listener);
+        try {
+            mDefaultDisplay.removeChild(mStack);
+        } finally {
+            mDefaultDisplay.unregisterStackOrderChangedListener(listener);
+        }
+        assertTrue(listener.mChanged);
+    }
+
+    @Test
+    public void testStackOrderChangedOnAddPositionStack() {
+        mDefaultDisplay.removeChild(mStack);
+
+        StackOrderChangedListener listener = new StackOrderChangedListener();
+        mDefaultDisplay.registerStackOrderChangedListener(listener);
+        try {
+            mDefaultDisplay.addChild(mStack, 0);
+        } finally {
+            mDefaultDisplay.unregisterStackOrderChangedListener(listener);
+        }
+        assertTrue(listener.mChanged);
+    }
+
+    @Test
+    public void testStackOrderChangedOnPositionStack() {
+        StackOrderChangedListener listener = new StackOrderChangedListener();
+        try {
+            final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+                    mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                    true /* onTop */);
+            mDefaultDisplay.registerStackOrderChangedListener(listener);
+            mDefaultDisplay.positionChildAtBottom(fullscreenStack1);
+        } finally {
+            mDefaultDisplay.unregisterStackOrderChangedListener(listener);
+        }
+        assertTrue(listener.mChanged);
+    }
+
+    private void verifyShouldSleepActivities(boolean focusedStack,
+            boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
+        final ActivityDisplay display = mock(ActivityDisplay.class);
+        final KeyguardController keyguardController = mSupervisor.getKeyguardController();
+
+        doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
+        doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
+        doReturn(displaySleeping).when(display).isSleeping();
+        doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay();
+
+        assertEquals(expected, mStack.shouldSleepActivities());
+    }
+
+    private static class StackOrderChangedListener
+            implements ActivityDisplay.OnStackOrderChangedListener {
+        public boolean mChanged = false;
+
+        @Override
+        public void onStackOrderChanged() {
+            mChanged = true;
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
new file mode 100644
index 0000000..e8de05c
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.IApplicationThread;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.ActivityStackSupervisor.PendingActivityLaunch;
+import com.android.server.wm.ActivityStarter.Factory;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Random;
+
+/**
+ * Tests for the {@link ActivityStartController} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityStartControllerTests
+ */
+@SmallTest
+@Presubmit
+public class ActivityStartControllerTests extends ActivityTestsBase {
+    private ActivityStartController mController;
+    private Factory mFactory;
+    private ActivityStarter mStarter;
+
+    @Before
+    public void setUp() throws Exception {
+        mService = createActivityTaskManagerService();
+        mFactory = mock(Factory.class);
+        mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory);
+        mStarter = spy(new ActivityStarter(mController, mService,
+                mService.mStackSupervisor, mock(ActivityStartInterceptor.class)));
+        doReturn(mStarter).when(mFactory).obtain();
+    }
+
+    /**
+     * Ensures that pending launches are processed.
+     */
+    @Test
+    public void testPendingActivityLaunches() {
+        final Random random = new Random();
+
+        final ActivityRecord activity = new ActivityBuilder(mService).build();
+        final ActivityRecord source = new ActivityBuilder(mService).build();
+        final int startFlags = random.nextInt();
+        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final WindowProcessController wpc = new WindowProcessController(mService,
+                mService.mContext.getApplicationInfo(), "name", 12345,
+                UserHandle.getUserId(12345), mock(Object.class),
+                mock(WindowProcessListener.class));
+        wpc.setThread(mock(IApplicationThread.class));
+
+        mController.addPendingActivityLaunch(
+                new PendingActivityLaunch(activity, source, startFlags, stack, wpc));
+        final boolean resume = random.nextBoolean();
+        mController.doPendingActivityLaunches(resume);
+
+        verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null),
+                eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null));
+    }
+
+
+    /**
+     * Ensures instances are recycled after execution.
+     */
+    @Test
+    public void testRecycling() {
+        final Intent intent = new Intent();
+        final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
+                mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
+        optionStarter
+                .setIntent(intent)
+                .setReason("Test")
+                .execute();
+        verify(mFactory, times(1)).recycle(eq(optionStarter));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
new file mode 100644
index 0000000..dda077e
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
+
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManagerInternal;
+import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.SuspendDialogInfo;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.testing.DexmakerShareClassLoaderRule;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.app.HarmfulAppWarningActivity;
+import com.android.internal.app.SuspendedAppActivity;
+import com.android.internal.app.UnlaunchableAppActivity;
+import com.android.server.LocalServices;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.pm.PackageManagerService;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link ActivityStartInterceptorTest}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityStartInterceptorTest
+ */
+@SmallTest
+@Presubmit
+public class ActivityStartInterceptorTest {
+    private static final int TEST_USER_ID = 1;
+    private static final int TEST_REAL_CALLING_UID = 2;
+    private static final int TEST_REAL_CALLING_PID = 3;
+    private static final String TEST_CALLING_PACKAGE = "com.test.caller";
+    private static final int TEST_START_FLAGS = 4;
+    private static final Intent ADMIN_SUPPORT_INTENT =
+            new Intent("com.test.ADMIN_SUPPORT");
+    private static final Intent CONFIRM_CREDENTIALS_INTENT =
+            new Intent("com.test.CONFIRM_CREDENTIALS");
+    private static final UserInfo PARENT_USER_INFO = new UserInfo(0 /* userId */, "parent",
+            0 /* flags */);
+    private static final String TEST_PACKAGE_NAME = "com.test.package";
+
+    @Rule
+    public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+            new DexmakerShareClassLoaderRule();
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private ActivityManagerService mAm;
+    @Mock
+    private ActivityTaskManagerService mService;
+    @Mock
+    private ActivityStackSupervisor mSupervisor;
+    @Mock
+    private DevicePolicyManagerInternal mDevicePolicyManager;
+    @Mock
+    private PackageManagerInternal mPackageManagerInternal;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private KeyguardManager mKeyguardManager;
+    @Mock
+    private PackageManagerService mPackageManager;
+    @Mock
+    private ActivityManagerInternal mAmInternal;
+
+    private ActivityStartInterceptor mInterceptor;
+    private ActivityInfo mAInfo = new ActivityInfo();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mService.mAmInternal = mAmInternal;
+        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext);
+        mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
+                TEST_START_FLAGS, TEST_CALLING_PACKAGE);
+
+        // Mock ActivityManagerInternal
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+        LocalServices.addService(ActivityManagerInternal.class, mAmInternal);
+
+        // Mock DevicePolicyManagerInternal
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+        LocalServices.addService(DevicePolicyManagerInternal.class,
+                mDevicePolicyManager);
+        when(mDevicePolicyManager.createShowAdminSupportIntent(TEST_USER_ID, true))
+                .thenReturn(ADMIN_SUPPORT_INTENT);
+        when(mService.getPackageManagerInternalLocked()).thenReturn(mPackageManagerInternal);
+
+        // Mock UserManager
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mUserManager.getProfileParent(TEST_USER_ID)).thenReturn(PARENT_USER_INFO);
+
+        // Mock KeyguardManager
+        when(mContext.getSystemService(Context.KEYGUARD_SERVICE)).thenReturn(mKeyguardManager);
+        when(mKeyguardManager.createConfirmDeviceCredentialIntent(
+                nullable(CharSequence.class), nullable(CharSequence.class), eq(TEST_USER_ID)))
+                .thenReturn(CONFIRM_CREDENTIALS_INTENT);
+
+        // Mock PackageManager
+        when(mService.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
+                .thenReturn(null);
+
+        // Initialise activity info
+        mAInfo.applicationInfo = new ApplicationInfo();
+        mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
+    }
+
+    @Test
+    public void testSuspendedByAdminPackage() {
+        // GIVEN the package we're about to launch is currently suspended
+        mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
+
+        when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
+                .thenReturn(PLATFORM_PACKAGE_NAME);
+
+        // THEN calling intercept returns true
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+        // THEN the returned intent is the admin support intent
+        assertEquals(ADMIN_SUPPORT_INTENT, mInterceptor.mIntent);
+    }
+
+    @Test
+    public void testSuspendedPackage() {
+        mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
+        final String suspendingPackage = "com.test.suspending.package";
+        final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
+                .setMessage("Test Message")
+                .setIcon(0x11110001)
+                .build();
+        when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
+                .thenReturn(suspendingPackage);
+        when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, TEST_USER_ID))
+                .thenReturn(dialogInfo);
+        // THEN calling intercept returns true
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+        // Check intent parameters
+        assertEquals(dialogInfo,
+                mInterceptor.mIntent.getParcelableExtra(SuspendedAppActivity.EXTRA_DIALOG_INFO));
+        assertEquals(suspendingPackage,
+                mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_SUSPENDING_PACKAGE));
+        assertEquals(TEST_PACKAGE_NAME,
+                mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_SUSPENDED_PACKAGE));
+        assertEquals(TEST_USER_ID, mInterceptor.mIntent.getIntExtra(Intent.EXTRA_USER_ID, -1000));
+    }
+
+    @Test
+    public void testInterceptQuietProfile() {
+        // GIVEN that the user the activity is starting as is currently in quiet mode
+        when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
+
+        // THEN calling intercept returns true
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+        // THEN the returned intent is the quiet mode intent
+        assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
+                .filterEquals(mInterceptor.mIntent));
+    }
+
+    @Test
+    public void testWorkChallenge() {
+        // GIVEN that the user the activity is starting as is currently locked
+        when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
+
+        // THEN calling intercept returns true
+        mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null);
+
+        // THEN the returned intent is the quiet mode intent
+        assertTrue(CONFIRM_CREDENTIALS_INTENT.filterEquals(mInterceptor.mIntent));
+    }
+
+    @Test
+    public void testHarmfulAppWarning() {
+        // GIVEN the package we're about to launch has a harmful app warning set
+        when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
+                .thenReturn("This app is bad");
+
+        // THEN calling intercept returns true
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+        // THEN the returned intent is the harmful app warning intent
+        assertEquals(HarmfulAppWarningActivity.class.getName(),
+                mInterceptor.mIntent.getComponent().getClassName());
+    }
+
+    @Test
+    public void testNoInterception() {
+        // GIVEN that none of the interception conditions are met
+
+        // THEN calling intercept returns false
+        assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
new file mode 100644
index 0000000..f7d7ad6
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -0,0 +1,675 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.ActivityManager.START_ABORTED;
+import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
+import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
+import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
+import static android.app.ActivityManager.START_NOT_VOICE_COMPATIBLE;
+import static android.app.ActivityManager.START_PERMISSION_DENIED;
+import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.ActivityManager.START_SWITCHES_CANCELED;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+
+import static com.android.server.wm.ActivityDisplay.POSITION_BOTTOM;
+import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
+import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityOptions;
+import android.app.IApplicationThread;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.WindowLayout;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.service.voice.IVoiceInteractionSession;
+import android.view.Gravity;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
+import com.android.server.wm.TaskRecord.TaskRecordFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link ActivityStarter} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityStarterTests
+ */
+@SmallTest
+@Presubmit
+public class ActivityStarterTests extends ActivityTestsBase {
+    private ActivityStarter mStarter;
+    private ActivityStartController mController;
+    private ActivityMetricsLogger mActivityMetricsLogger;
+
+    private static final int PRECONDITION_NO_CALLER_APP = 1;
+    private static final int PRECONDITION_NO_INTENT_COMPONENT = 1 << 1;
+    private static final int PRECONDITION_NO_ACTIVITY_INFO = 1 << 2;
+    private static final int PRECONDITION_SOURCE_PRESENT = 1 << 3;
+    private static final int PRECONDITION_REQUEST_CODE = 1 << 4;
+    private static final int PRECONDITION_SOURCE_VOICE_SESSION = 1 << 5;
+    private static final int PRECONDITION_NO_VOICE_SESSION_SUPPORT = 1 << 6;
+    private static final int PRECONDITION_DIFFERENT_UID = 1 << 7;
+    private static final int PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION = 1 << 8;
+    private static final int PRECONDITION_CANNOT_START_ANY_ACTIVITY = 1 << 9;
+    private static final int PRECONDITION_DISALLOW_APP_SWITCHING = 1 << 10;
+
+    private static final int FAKE_CALLING_UID = 666;
+    private static final int FAKE_REAL_CALLING_UID = 667;
+    private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude";
+
+    @Before
+    public void setUp() throws Exception {
+        setupActivityTaskManagerService();
+        mController = mock(ActivityStartController.class);
+        mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
+        clearInvocations(mActivityMetricsLogger);
+        mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
+                mock(ActivityStartInterceptor.class));
+    }
+
+    @Test
+    public void testUpdateLaunchBounds() {
+        // When in a non-resizeable stack, the task bounds should be updated.
+        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
+                .build();
+        final Rect bounds = new Rect(10, 10, 100, 100);
+
+        mStarter.updateBounds(task, bounds);
+        assertEquals(bounds, task.getOverrideBounds());
+        assertEquals(new Rect(), task.getStack().getOverrideBounds());
+
+        // When in a resizeable stack, the stack bounds should be updated as well.
+        final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+                        WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
+                .build();
+        assertThat((Object) task2.getStack()).isInstanceOf(PinnedActivityStack.class);
+        mStarter.updateBounds(task2, bounds);
+
+        verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
+                eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
+
+        // In the case of no animation, the stack and task bounds should be set immediately.
+        if (!ANIMATE) {
+            assertEquals(bounds, task2.getStack().getOverrideBounds());
+            assertEquals(bounds, task2.getOverrideBounds());
+        } else {
+            assertEquals(new Rect(), task2.getOverrideBounds());
+        }
+    }
+
+    @Test
+    public void testStartActivityPreconditions() {
+        verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED);
+        verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT,
+                START_INTENT_NOT_RESOLVED);
+        verifyStartActivityPreconditions(PRECONDITION_NO_ACTIVITY_INFO, START_CLASS_NOT_FOUND);
+        verifyStartActivityPreconditions(PRECONDITION_SOURCE_PRESENT | PRECONDITION_REQUEST_CODE,
+                Intent.FLAG_ACTIVITY_FORWARD_RESULT, START_FORWARD_AND_REQUEST_CONFLICT);
+        verifyStartActivityPreconditions(
+                PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
+                        | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID,
+                START_NOT_VOICE_COMPATIBLE);
+        verifyStartActivityPreconditions(
+                PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
+                        | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID
+                        | PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION,
+                START_NOT_VOICE_COMPATIBLE);
+        verifyStartActivityPreconditions(PRECONDITION_CANNOT_START_ANY_ACTIVITY, START_ABORTED);
+        verifyStartActivityPreconditions(PRECONDITION_DISALLOW_APP_SWITCHING,
+                START_SWITCHES_CANCELED);
+    }
+
+    private static boolean containsConditions(int preconditions, int mask) {
+        return (preconditions & mask) == mask;
+    }
+
+    private void verifyStartActivityPreconditions(int preconditions, int expectedResult) {
+        verifyStartActivityPreconditions(preconditions, 0 /*launchFlags*/, expectedResult);
+    }
+
+    /**
+     * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
+     * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
+     * and the launch flags specified in the intent. The method constructs a call to
+     * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches
+     * the expected. It is important to note that the method also checks side effects of the start,
+     * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios.
+     * @param preconditions A bitmask representing the preconditions for the launch
+     * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
+     * @param expectedResult The expected result from the launch.
+     */
+    private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
+            int expectedResult) {
+        final ActivityTaskManagerService service = mService;
+        final IPackageManager packageManager = mock(IPackageManager.class);
+        final ActivityStartController controller = mock(ActivityStartController.class);
+
+        final ActivityStarter starter = new ActivityStarter(controller, service,
+                service.mStackSupervisor, mock(ActivityStartInterceptor.class));
+        prepareStarter(launchFlags);
+        final IApplicationThread caller = mock(IApplicationThread.class);
+
+        final WindowProcessController wpc =
+                containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
+                ? null : new WindowProcessController(
+                        service, mock(ApplicationInfo.class), null, 0, -1, null, null);
+        doReturn(wpc).when(service).getProcessController(anyObject());
+
+        final Intent intent = new Intent();
+        intent.setFlags(launchFlags);
+
+        final ActivityInfo aInfo = containsConditions(preconditions, PRECONDITION_NO_ACTIVITY_INFO)
+                ?  null : new ActivityInfo();
+
+        IVoiceInteractionSession voiceSession =
+                containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
+                ? mock(IVoiceInteractionSession.class) : null;
+
+        // Create source token
+        final ActivityBuilder builder = new ActivityBuilder(service).setTask(
+                new TaskBuilder(service.mStackSupervisor).setVoiceSession(voiceSession).build());
+
+        if (aInfo != null) {
+            aInfo.applicationInfo = new ApplicationInfo();
+            aInfo.applicationInfo.packageName =
+                    ActivityBuilder.getDefaultComponent().getPackageName();
+        }
+
+        // Offset uid by one from {@link ActivityInfo} to simulate different uids.
+        if (containsConditions(preconditions, PRECONDITION_DIFFERENT_UID)) {
+            builder.setUid(aInfo.applicationInfo.uid + 1);
+        }
+
+        final ActivityRecord source = builder.build();
+
+        if (!containsConditions(preconditions, PRECONDITION_NO_INTENT_COMPONENT)) {
+            intent.setComponent(source.realActivity);
+        }
+
+        if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
+            doReturn(false).when(service).checkAppSwitchAllowedLocked(
+                    anyInt(), anyInt(), anyInt(), anyInt(), any());
+        }
+
+        if (containsConditions(preconditions, PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
+            doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission(
+                    any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
+                    anyBoolean(), anyBoolean(), any(), any(), any());
+        }
+
+        try {
+            if (containsConditions(preconditions,
+                    PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION)) {
+                doAnswer((inv) -> {
+                    throw new RemoteException();
+                }).when(packageManager).activitySupportsIntent(eq(source.realActivity), eq(intent),
+                        any());
+            } else {
+                doReturn(!containsConditions(preconditions, PRECONDITION_NO_VOICE_SESSION_SUPPORT))
+                        .when(packageManager).activitySupportsIntent(eq(source.realActivity),
+                        eq(intent), any());
+            }
+        } catch (RemoteException e) {
+        }
+
+        final IBinder resultTo = containsConditions(preconditions, PRECONDITION_SOURCE_PRESENT)
+                || containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
+                ? source.appToken : null;
+
+        final int requestCode = containsConditions(preconditions, PRECONDITION_REQUEST_CODE)
+                ? 1 : 0;
+
+        final int result = starter.setCaller(caller)
+                .setIntent(intent)
+                .setActivityInfo(aInfo)
+                .setResultTo(resultTo)
+                .setRequestCode(requestCode)
+                .setReason("testLaunchActivityPermissionDenied")
+                .execute();
+
+        // In some cases the expected result internally is different than the published result. We
+        // must use ActivityStarter#getExternalResult to translate.
+        assertEquals(ActivityStarter.getExternalResult(expectedResult), result);
+
+        // Ensure that {@link ActivityOptions} are aborted with unsuccessful result.
+        if (expectedResult != START_SUCCESS) {
+            final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
+                    mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
+            final ActivityOptions options = spy(ActivityOptions.makeBasic());
+
+            final int optionResult = optionStarter.setCaller(caller)
+                    .setIntent(intent)
+                    .setActivityInfo(aInfo)
+                    .setResultTo(resultTo)
+                    .setRequestCode(requestCode)
+                    .setReason("testLaunchActivityPermissionDenied")
+                    .setActivityOptions(new SafeActivityOptions(options))
+                    .execute();
+            verify(options, times(1)).abort();
+        }
+    }
+
+    private ActivityStarter prepareStarter(@Intent.Flags int launchFlags) {
+        return prepareStarter(launchFlags, true /* mockGetLaunchStack */);
+    }
+
+    /**
+     * Creates a {@link ActivityStarter} with default parameters and necessary mocks.
+     *
+     * @param launchFlags The intent flags to launch activity.
+     * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for
+     *                           always launching to the testing stack. Set to false when allowing
+     *                           the activity can be launched to any stack that is decided by real
+     *                           implementation.
+     * @return A {@link ActivityStarter} with default setup.
+     */
+    private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
+            boolean mockGetLaunchStack) {
+        // always allow test to start activity.
+        doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
+                any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
+                anyBoolean(), anyBoolean(), any(), any(), any());
+
+        // instrument the stack and task used.
+        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+                .setCreateStack(false)
+                .build();
+
+        // use factory that only returns spy task.
+        final TaskRecordFactory factory = mock(TaskRecordFactory.class);
+        TaskRecord.setTaskRecordFactory(factory);
+
+        // return task when created.
+        doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any());
+
+        if (mockGetLaunchStack) {
+            // Direct starter to use spy stack.
+            doReturn(stack).when(mService.mStackSupervisor)
+                    .getLaunchStack(any(), any(), any(), anyBoolean());
+            doReturn(stack).when(mService.mStackSupervisor)
+                    .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
+        }
+
+        // Set up mock package manager internal and make sure no unmocked methods are called
+        PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class,
+                invocation -> {
+                    throw new RuntimeException("Not stubbed");
+                });
+        doReturn(mockPackageManager).when(mService).getPackageManagerInternalLocked();
+
+        // Never review permissions
+        doReturn(false).when(mockPackageManager).isPermissionsReviewRequired(any(), anyInt());
+        doNothing().when(mockPackageManager).grantEphemeralAccess(
+                anyInt(), any(), anyInt(), anyInt());
+
+        final Intent intent = new Intent();
+        intent.addFlags(launchFlags);
+        intent.setComponent(ActivityBuilder.getDefaultComponent());
+
+        final ActivityInfo info = new ActivityInfo();
+
+        info.applicationInfo = new ApplicationInfo();
+        info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
+
+        return new ActivityStarter(mController, mService,
+                mService.mStackSupervisor, mock(ActivityStartInterceptor.class))
+                .setIntent(intent)
+                .setActivityInfo(info);
+    }
+
+    /**
+     * Ensures that values specified at launch time are passed to {@link LaunchParamsModifier}
+     * when we are laying out a new task.
+     */
+    @Test
+    public void testCreateTaskLayout() {
+        // modifier for validating passed values.
+        final LaunchParamsModifier modifier = mock(LaunchParamsModifier.class);
+        mService.mStackSupervisor.getLaunchParamsController().registerModifier(modifier);
+
+        // add custom values to activity info to make unique.
+        final ActivityInfo info = new ActivityInfo();
+        final Rect launchBounds = new Rect(0, 0, 20, 30);
+
+        final WindowLayout windowLayout =
+                new WindowLayout(10, .5f, 20, 1.0f, Gravity.NO_GRAVITY, 1, 1);
+
+        info.windowLayout = windowLayout;
+        info.applicationInfo = new ApplicationInfo();
+        info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
+
+        // create starter.
+        final ActivityStarter optionStarter = prepareStarter(0 /* launchFlags */);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchBounds(launchBounds);
+
+        // run starter.
+        optionStarter
+                .setReason("testCreateTaskLayout")
+                .setActivityInfo(info)
+                .setActivityOptions(new SafeActivityOptions(options))
+                .execute();
+
+        // verify that values are passed to the modifier. Values are passed twice -- once for
+        // setting initial state, another when task is created.
+        verify(modifier, times(2)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
+                any(), any());
+    }
+
+    /**
+     * This test ensures that if the intent is being delivered to a
+     */
+    @Test
+    public void testSplitScreenDeliverToTop() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+        final ActivityRecord focusActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .build();
+
+        focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+        final ActivityRecord reusableActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .build();
+
+        // Create reusable activity after entering split-screen so that it is the top secondary
+        // stack.
+        reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+
+        // Set focus back to primary.
+        final ActivityStack focusStack = focusActivity.getStack();
+        focusStack.moveToFront("testSplitScreenDeliverToTop");
+
+        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+
+        final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
+
+        // Ensure result is delivering intent to top.
+        assertEquals(START_DELIVERED_TO_TOP, result);
+    }
+
+    /**
+     * This test ensures that if the intent is being delivered to a split-screen unfocused task
+     * reports it is brought to front instead of delivering to top.
+     */
+    @Test
+    public void testSplitScreenTaskToFront() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+        // Create reusable activity here first. Setting the windowing mode of the primary stack
+        // will move the existing standard full screen stack to secondary, putting this one on the
+        // bottom.
+        final ActivityRecord reusableActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .build();
+
+        reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+
+        final ActivityRecord focusActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .build();
+
+        // Enter split-screen. Primary stack should have focus.
+        focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+
+        final int result = starter.setReason("testSplitScreenMoveToFront").execute();
+
+        // Ensure result is moving task to front.
+        assertEquals(START_TASK_TO_FRONT, result);
+    }
+
+    /**
+     * Tests activity is cleaned up properly in a task mode violation.
+     */
+    @Test
+    public void testTaskModeViolation() {
+        final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+        ((TestActivityDisplay) display).removeAllTasks();
+        assertNoTasks(display);
+
+        final ActivityStarter starter = prepareStarter(0);
+
+        final LockTaskController lockTaskController = mService.getLockTaskController();
+        doReturn(true).when(lockTaskController).isLockTaskModeViolation(any());
+
+        final int result = starter.setReason("testTaskModeViolation").execute();
+
+        assertEquals(START_RETURN_LOCK_TASK_MODE_VIOLATION, result);
+        assertNoTasks(display);
+    }
+
+    private void assertNoTasks(ActivityDisplay display) {
+        for (int i = display.getChildCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = display.getChildAt(i);
+            assertThat(stack.getAllTasks()).isEmpty();
+        }
+    }
+
+    /**
+     * This test ensures that activity starts are not being logged when the logging is disabled.
+     */
+    @Test
+    public void testActivityStartsLogging_noLoggingWhenDisabled() {
+        doReturn(false).when(mService).isActivityStartsLoggingEnabled();
+        doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
+
+        ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK);
+        starter.setReason("testActivityStartsLogging_noLoggingWhenDisabled").execute();
+
+        // verify logging wasn't done
+        verify(mActivityMetricsLogger, never()).logActivityStart(any(), any(), any(), anyInt(),
+                any(), anyInt(), anyBoolean(), anyInt(), anyInt(), anyBoolean(), anyInt(), any(),
+                anyInt(), anyBoolean(), any(), anyBoolean());
+    }
+
+    /**
+     * This test ensures that activity starts are being logged when the logging is enabled.
+     */
+    @Test
+    public void testActivityStartsLogging_logsWhenEnabled() {
+        // note: conveniently this package doesn't have any activity visible
+        doReturn(true).when(mService).isActivityStartsLoggingEnabled();
+        doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
+
+        ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
+                .setCallingUid(FAKE_CALLING_UID)
+                .setRealCallingUid(FAKE_REAL_CALLING_UID)
+                .setCallingPackage(FAKE_CALLING_PACKAGE)
+                .setOriginatingPendingIntent(null);
+
+        starter.setReason("testActivityStartsLogging_logsWhenEnabled").execute();
+
+        // verify the above activity start was logged
+        verify(mActivityMetricsLogger, times(1)).logActivityStart(any(), any(), any(),
+                eq(FAKE_CALLING_UID), eq(FAKE_CALLING_PACKAGE), anyInt(), anyBoolean(),
+                eq(FAKE_REAL_CALLING_UID), anyInt(), anyBoolean(), anyInt(),
+                eq(ActivityBuilder.getDefaultComponent().getPackageName()), anyInt(), anyBoolean(),
+                any(), eq(false));
+    }
+
+    /**
+     * This test ensures that when starting an existing single task activity on secondary display
+     * which is not the top focused display, it should deliver new intent to the activity and not
+     * create a new stack.
+     */
+    @Test
+    public void testDeliverIntentToTopActivityOfNonTopDisplay() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+                false /* mockGetLaunchStack */);
+
+        // Create a secondary display at bottom.
+        final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
+        mSupervisor.addChild(secondaryDisplay, POSITION_BOTTOM);
+        final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        // Create an activity record on the top of secondary display.
+        final ActivityRecord topActivityOnSecondaryDisplay = createSingleTaskActivityOn(stack);
+
+        // Put an activity on default display as the top focused activity.
+        new ActivityBuilder(mService).setCreateTask(true).build();
+
+        // Start activity with the same intent as {@code topActivityOnSecondaryDisplay}
+        // on secondary display.
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+        final int result = starter.setReason("testDeliverIntentToTopActivityOfNonTopDisplay")
+                .setIntent(topActivityOnSecondaryDisplay.intent)
+                .setActivityOptions(options.toBundle())
+                .execute();
+
+        // Ensure result is delivering intent to top.
+        assertEquals(START_DELIVERED_TO_TOP, result);
+
+        // Ensure secondary display only creates one stack.
+        verify(secondaryDisplay, times(1)).createStack(anyInt(), anyInt(), anyBoolean());
+    }
+
+    /**
+     * This test ensures that when starting an existing non-top single task activity on secondary
+     * display which is the top focused display, it should bring the task to front without creating
+     * unused stack.
+     */
+    @Test
+    public void testBringTaskToFrontOnSecondaryDisplay() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+                false /* mockGetLaunchStack */);
+
+        // Create a secondary display with an activity.
+        final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
+        mSupervisor.addChild(secondaryDisplay, POSITION_TOP);
+        final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
+                secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+                        ACTIVITY_TYPE_STANDARD, false /* onTop */));
+
+        // Create another activity on top of the secondary display.
+        final ActivityStack topStack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TaskRecord topTask = new TaskBuilder(mSupervisor).setStack(topStack).build();
+        new ActivityBuilder(mService).setTask(topTask).build();
+
+        // Start activity with the same intent as {@code singleTaskActivity} on secondary display.
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+        final int result = starter.setReason("testBringTaskToFrontOnSecondaryDisplay")
+                .setIntent(singleTaskActivity.intent)
+                .setActivityOptions(options.toBundle())
+                .execute();
+
+        // Ensure result is moving existing task to front.
+        assertEquals(START_TASK_TO_FRONT, result);
+
+        // Ensure secondary display only creates two stacks.
+        verify(secondaryDisplay, times(2)).createStack(anyInt(), anyInt(), anyBoolean());
+    }
+
+    private ActivityRecord createSingleTaskActivityOn(ActivityStack stack) {
+        final ComponentName componentName = ComponentName.createRelative(
+                DEFAULT_COMPONENT_PACKAGE_NAME,
+                DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity");
+        final TaskRecord taskRecord = new TaskBuilder(mSupervisor)
+                .setComponent(componentName)
+                .setStack(stack)
+                .build();
+        return new ActivityBuilder(mService)
+                .setComponent(componentName)
+                .setLaunchMode(LAUNCH_SINGLE_TASK)
+                .setTask(taskRecord)
+                .build();
+    }
+
+    /**
+     * This test ensures that a reused top activity in the top focused stack is able to be
+     * reparented to another display.
+     */
+    @Test
+    public void testReparentTopFocusedActivityToSecondaryDisplay() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+                false /* mockGetLaunchStack */);
+
+        // Create a secondary display at bottom.
+        final TestActivityDisplay secondaryDisplay = addNewActivityDisplayAt(POSITION_BOTTOM);
+        secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+
+        // Put an activity on default display as the top focused activity.
+        final ActivityRecord topActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setLaunchMode(LAUNCH_SINGLE_TASK)
+                .build();
+
+        // Start activity with the same intent as {@code topActivity} on secondary display.
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+        starter.setReason("testReparentTopFocusedActivityToSecondaryDisplay")
+                .setIntent(topActivity.intent)
+                .setActivityOptions(options.toBundle())
+                .execute();
+
+        // Ensure the activity is moved to secondary display.
+        assertEquals(secondaryDisplay, topActivity.getDisplay());
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
new file mode 100644
index 0000000..c35e4d6
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -0,0 +1,817 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
+import android.app.IApplicationThread;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionSession;
+import android.testing.DexmakerShareClassLoaderRule;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.server.AppOpsService;
+import com.android.server.AttributeCache;
+import com.android.server.ServiceThread;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.uri.UriGrantsManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * A base class to handle common operations in activity related unit tests.
+ */
+class ActivityTestsBase {
+    private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
+
+    @Rule
+    public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+            new DexmakerShareClassLoaderRule();
+
+    final Context mContext = getInstrumentation().getTargetContext();
+    final TestInjector mTestInjector = new TestInjector();
+
+    ActivityTaskManagerService mService;
+    ActivityStackSupervisor mSupervisor;
+
+    // Default package name
+    static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";
+
+    // Default base activity name
+    private static final String DEFAULT_COMPONENT_CLASS_NAME = ".BarActivity";
+
+    @BeforeClass
+    public static void setUpOnceBase() {
+        AttributeCache.init(getInstrumentation().getTargetContext());
+    }
+
+    @Before
+    public void setUpBase() {
+        mTestInjector.setUp();
+    }
+
+    @After
+    public void tearDownBase() {
+        mTestInjector.tearDown();
+    }
+
+    ActivityTaskManagerService createActivityTaskManagerService() {
+        final TestActivityTaskManagerService atm =
+                spy(new TestActivityTaskManagerService(mContext));
+        setupActivityManagerService(atm);
+        return atm;
+    }
+
+    void setupActivityTaskManagerService() {
+        mService = createActivityTaskManagerService();
+        mSupervisor = mService.mStackSupervisor;
+    }
+
+    ActivityManagerService createActivityManagerService() {
+        final TestActivityTaskManagerService atm =
+                spy(new TestActivityTaskManagerService(mContext));
+        return setupActivityManagerService(atm);
+    }
+
+    ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
+        final TestActivityManagerService am = spy(new TestActivityManagerService(mTestInjector));
+        setupActivityManagerService(am, atm);
+        return am;
+    }
+
+    /** Creates a {@link TestActivityDisplay}. */
+    TestActivityDisplay createNewActivityDisplay() {
+        return TestActivityDisplay.create(mSupervisor, sNextDisplayId++);
+    }
+
+    /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
+    TestActivityDisplay addNewActivityDisplayAt(int position) {
+        final TestActivityDisplay display = createNewActivityDisplay();
+        mSupervisor.addChild(display, position);
+        return display;
+    }
+
+    void setupActivityManagerService(
+            TestActivityManagerService am, TestActivityTaskManagerService atm) {
+        atm.setActivityManagerService(am.mIntentFirewall, am.mPendingIntentController);
+        atm.mAmInternal = am.getLocalService();
+        am.mAtmInternal = atm.getLocalService();
+        // Makes sure the supervisor is using with the spy object.
+        atm.mStackSupervisor.setService(atm);
+        doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
+        doReturn(mock(IPackageManager.class)).when(atm).getPackageManager();
+        PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
+        doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
+        doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
+        doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
+        am.mActivityTaskManager = atm;
+        am.mWindowManager = prepareMockWindowManager();
+        atm.setWindowManager(am.mWindowManager);
+
+        // Put a home stack on the default display, so that we'll always have something focusable.
+        final TestActivityStackSupervisor supervisor =
+                (TestActivityStackSupervisor) atm.mStackSupervisor;
+        supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+        final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
+                .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
+        new ActivityBuilder(atm).setTask(task).build();
+    }
+
+    /**
+     * Builder for creating new activities.
+     */
+    protected static class ActivityBuilder {
+        // An id appended to the end of the component name to make it unique
+        private static int sCurrentActivityId = 0;
+
+        private final ActivityTaskManagerService mService;
+
+        private ComponentName mComponent;
+        private TaskRecord mTaskRecord;
+        private int mUid;
+        private boolean mCreateTask;
+        private ActivityStack mStack;
+        private int mActivityFlags;
+        private int mLaunchMode;
+
+        ActivityBuilder(ActivityTaskManagerService service) {
+            mService = service;
+        }
+
+        ActivityBuilder setComponent(ComponentName component) {
+            mComponent = component;
+            return this;
+        }
+
+        static ComponentName getDefaultComponent() {
+            return ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
+                    DEFAULT_COMPONENT_PACKAGE_NAME);
+        }
+
+        ActivityBuilder setTask(TaskRecord task) {
+            mTaskRecord = task;
+            return this;
+        }
+
+        ActivityBuilder setActivityFlags(int flags) {
+            mActivityFlags = flags;
+            return this;
+        }
+
+        ActivityBuilder setLaunchMode(int launchMode) {
+            mLaunchMode = launchMode;
+            return this;
+        }
+
+        ActivityBuilder setStack(ActivityStack stack) {
+            mStack = stack;
+            return this;
+        }
+
+        ActivityBuilder setCreateTask(boolean createTask) {
+            mCreateTask = createTask;
+            return this;
+        }
+
+        ActivityBuilder setUid(int uid) {
+            mUid = uid;
+            return this;
+        }
+
+        ActivityRecord build() {
+            if (mComponent == null) {
+                final int id = sCurrentActivityId++;
+                mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
+                        DEFAULT_COMPONENT_CLASS_NAME + id);
+            }
+
+            if (mCreateTask) {
+                mTaskRecord = new TaskBuilder(mService.mStackSupervisor)
+                        .setComponent(mComponent)
+                        .setStack(mStack).build();
+            }
+
+            Intent intent = new Intent();
+            intent.setComponent(mComponent);
+            final ActivityInfo aInfo = new ActivityInfo();
+            aInfo.applicationInfo = new ApplicationInfo();
+            aInfo.applicationInfo.packageName = mComponent.getPackageName();
+            aInfo.applicationInfo.uid = mUid;
+            aInfo.flags |= mActivityFlags;
+            aInfo.launchMode = mLaunchMode;
+
+            final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
+                    0 /* launchedFromPid */, 0, null, intent, null,
+                    aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
+                    0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
+                    mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
+            activity.mWindowContainerController = mock(AppWindowContainerController.class);
+
+            if (mTaskRecord != null) {
+                mTaskRecord.addActivityToTop(activity);
+            }
+
+            final WindowProcessController wpc = new WindowProcessController(mService,
+                    mService.mContext.getApplicationInfo(), "name", 12345,
+                    UserHandle.getUserId(12345), mock(Object.class),
+                    mock(WindowProcessListener.class));
+            wpc.setThread(mock(IApplicationThread.class));
+            activity.setProcess(wpc);
+            return activity;
+        }
+    }
+
+    /**
+     * Builder for creating new tasks.
+     */
+    protected static class TaskBuilder {
+        // Default package name
+        static final String DEFAULT_PACKAGE = "com.bar";
+
+        private final ActivityStackSupervisor mSupervisor;
+
+        private ComponentName mComponent;
+        private String mPackage;
+        private int mFlags = 0;
+        // Task id 0 is reserved in ARC for the home app.
+        private int mTaskId = 1;
+        private int mUserId = 0;
+        private IVoiceInteractionSession mVoiceSession;
+        private boolean mCreateStack = true;
+
+        private ActivityStack mStack;
+
+        TaskBuilder(ActivityStackSupervisor supervisor) {
+            mSupervisor = supervisor;
+        }
+
+        TaskBuilder setComponent(ComponentName component) {
+            mComponent = component;
+            return this;
+        }
+
+        TaskBuilder setPackage(String packageName) {
+            mPackage = packageName;
+            return this;
+        }
+
+        /**
+         * Set to {@code true} by default, set to {@code false} to prevent the task from
+         * automatically creating a parent stack.
+         */
+        TaskBuilder setCreateStack(boolean createStack) {
+            mCreateStack = createStack;
+            return this;
+        }
+
+        TaskBuilder setVoiceSession(IVoiceInteractionSession session) {
+            mVoiceSession = session;
+            return this;
+        }
+
+        TaskBuilder setFlags(int flags) {
+            mFlags = flags;
+            return this;
+        }
+
+        TaskBuilder setTaskId(int taskId) {
+            mTaskId = taskId;
+            return this;
+        }
+
+        TaskBuilder setUserId(int userId) {
+            mUserId = userId;
+            return this;
+        }
+
+        TaskBuilder setStack(ActivityStack stack) {
+            mStack = stack;
+            return this;
+        }
+
+        TaskRecord build() {
+            if (mStack == null && mCreateStack) {
+                mStack = mSupervisor.getDefaultDisplay().createStack(
+                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+            }
+
+            final ActivityInfo aInfo = new ActivityInfo();
+            aInfo.applicationInfo = new ApplicationInfo();
+            aInfo.applicationInfo.packageName = mPackage;
+
+            Intent intent = new Intent();
+            if (mComponent == null) {
+                mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
+                        DEFAULT_COMPONENT_CLASS_NAME);
+            }
+
+            intent.setComponent(mComponent);
+            intent.setFlags(mFlags);
+
+            final TestTaskRecord task = new TestTaskRecord(mSupervisor.mService, mTaskId, aInfo,
+                    intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/);
+            task.userId = mUserId;
+
+            if (mStack != null) {
+                mStack.moveToFront("test");
+                mStack.addTask(task, true, "creating test task");
+                task.setStack(mStack);
+                task.setWindowContainerController();
+            }
+
+            task.touchActiveTime();
+
+            return task;
+        }
+
+        private static class TestTaskRecord extends TaskRecord {
+            TestTaskRecord(ActivityTaskManagerService service, int taskId, ActivityInfo info,
+                       Intent intent, IVoiceInteractionSession voiceSession,
+                       IVoiceInteractor voiceInteractor) {
+                super(service, taskId, info, intent, voiceSession, voiceInteractor);
+            }
+
+            @Override
+            void createWindowContainer(boolean onTop, boolean showForAllUsers) {
+                setWindowContainerController();
+            }
+
+            private void setWindowContainerController() {
+                setWindowContainerController(mock(TaskWindowContainerController.class));
+            }
+        }
+    }
+
+    protected static class TestActivityTaskManagerService extends ActivityTaskManagerService {
+        private LockTaskController mLockTaskController;
+        private ActivityTaskManagerInternal mInternal;
+        private PackageManagerInternal mPmInternal;
+
+        // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
+        // We keep the reference in order to prevent creating it twice.
+        private ActivityStackSupervisor mTestStackSupervisor;
+
+        TestActivityTaskManagerService(Context context) {
+            super(context);
+            mSupportsMultiWindow = true;
+            mSupportsMultiDisplay = true;
+            mSupportsSplitScreenMultiWindow = true;
+            mSupportsFreeformWindowManagement = true;
+            mSupportsPictureInPicture = true;
+            mUgmInternal = mock(UriGrantsManagerInternal.class);
+        }
+
+        @Override
+        int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
+            return userId;
+        }
+
+        @Override
+        public LockTaskController getLockTaskController() {
+            if (mLockTaskController == null) {
+                mLockTaskController = spy(super.getLockTaskController());
+            }
+
+            return mLockTaskController;
+        }
+
+        @Override
+        void updateUsageStats(ActivityRecord component, boolean resumed) {
+        }
+
+        @Override
+        protected final ActivityStackSupervisor createStackSupervisor() {
+            if (mTestStackSupervisor == null) {
+                final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
+                final KeyguardController keyguardController = mock(KeyguardController.class);
+
+                // Invoked during {@link ActivityStack} creation.
+                doNothing().when(supervisor).updateUIDsPresentOnDisplay();
+                // Always keep things awake.
+                doReturn(true).when(supervisor).hasAwakeDisplay();
+                // Called when moving activity to pinned stack.
+                doNothing().when(supervisor).ensureActivitiesVisibleLocked(any(), anyInt(),
+                        anyBoolean());
+                // Do not schedule idle timeouts
+                doNothing().when(supervisor).scheduleIdleTimeoutLocked(any());
+                // unit test version does not handle launch wake lock
+                doNothing().when(supervisor).acquireLaunchWakelock();
+                doReturn(keyguardController).when(supervisor).getKeyguardController();
+
+                supervisor.initialize();
+                mTestStackSupervisor = supervisor;
+            }
+            return mTestStackSupervisor;
+        }
+
+        protected ActivityStackSupervisor createTestSupervisor() {
+            return new TestActivityStackSupervisor(this, mH.getLooper());
+        }
+
+        ActivityTaskManagerInternal getLocalService() {
+            if (mInternal == null) {
+                mInternal = new ActivityTaskManagerService.LocalService();
+            }
+            return mInternal;
+        }
+
+        @Override
+        PackageManagerInternal getPackageManagerInternalLocked() {
+            if (mPmInternal == null) {
+                mPmInternal = mock(PackageManagerInternal.class);
+                doReturn(false)
+                        .when(mPmInternal)
+                        .isPermissionsReviewRequired(anyString(), anyInt());
+            }
+            return mPmInternal;
+        }
+    }
+
+    private static class TestInjector extends ActivityManagerService.Injector {
+        private ServiceThread mHandlerThread;
+
+        @Override
+        public Context getContext() {
+            return getInstrumentation().getTargetContext();
+        }
+
+        @Override
+        public AppOpsService getAppOpsService(File file, Handler handler) {
+            return null;
+        }
+
+        @Override
+        public Handler getUiHandler(ActivityManagerService service) {
+            return mHandlerThread.getThreadHandler();
+        }
+
+        @Override
+        public boolean isNetworkRestrictedForUid(int uid) {
+            return false;
+        }
+
+        void setUp() {
+            mHandlerThread = new ServiceThread("ActivityTestsThread",
+                    Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
+            mHandlerThread.start();
+        }
+
+        void tearDown() {
+            mHandlerThread.quitSafely();
+        }
+    }
+
+    /**
+     * An {@link ActivityManagerService} subclass which provides a test
+     * {@link ActivityStackSupervisor}.
+     */
+    static class TestActivityManagerService extends ActivityManagerService {
+
+        private ActivityManagerInternal mInternal;
+
+        TestActivityManagerService(TestInjector testInjector) {
+            super(testInjector, testInjector.mHandlerThread);
+            mUgmInternal = mock(UriGrantsManagerInternal.class);
+        }
+
+        ActivityManagerInternal getLocalService() {
+            if (mInternal == null) {
+                mInternal = new LocalService();
+            }
+            return mInternal;
+        }
+    }
+
+    /**
+     * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
+     * setup not available in the test environment. Also specifies an injector for
+     */
+    protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
+        private ActivityDisplay mDisplay;
+        private KeyguardController mKeyguardController;
+
+        public TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+            super(service, looper);
+            mDisplayManager =
+                    (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
+            mWindowManager = prepareMockWindowManager();
+            mKeyguardController = mock(KeyguardController.class);
+            setWindowContainerController(mock(RootWindowContainerController.class));
+        }
+
+        @Override
+        public void initialize() {
+            super.initialize();
+            mDisplay = spy(TestActivityDisplay.create(this, DEFAULT_DISPLAY));
+            addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+        }
+
+        @Override
+        public KeyguardController getKeyguardController() {
+            return mKeyguardController;
+        }
+
+        @Override
+        ActivityDisplay getDefaultDisplay() {
+            return mDisplay;
+        }
+
+        @Override
+        void setWindowManager(WindowManagerService wm) {
+            mWindowManager = wm;
+        }
+    }
+
+    protected static class TestActivityDisplay extends ActivityDisplay {
+        private final ActivityStackSupervisor mSupervisor;
+
+        static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
+            if (displayId == DEFAULT_DISPLAY) {
+                return new TestActivityDisplay(supervisor,
+                        supervisor.mDisplayManager.getDisplay(displayId));
+            }
+            final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+                    new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
+            return new TestActivityDisplay(supervisor, display);
+        }
+
+        TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
+            super(supervisor, display);
+            // Normally this comes from display-properties as exposed by WM. Without that, just
+            // hard-code to FULLSCREEN for tests.
+            setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+            mSupervisor = supervisor;
+        }
+
+        @SuppressWarnings("TypeParameterUnusedInFormals")
+        @Override
+        <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+                int stackId, boolean onTop) {
+            return new StackBuilder(mSupervisor).setDisplay(this)
+                    .setWindowingMode(windowingMode).setActivityType(activityType)
+                    .setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build();
+        }
+
+        @Override
+        protected DisplayWindowController createWindowContainerController() {
+            return mock(DisplayWindowController.class);
+        }
+
+        void removeAllTasks() {
+            for (int i = 0; i < getChildCount(); i++) {
+                final ActivityStack stack = getChildAt(i);
+                for (TaskRecord task : (List<TaskRecord>) stack.getAllTasks()) {
+                    stack.removeTask(task, "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
+                }
+            }
+        }
+    }
+
+    private static WindowManagerService prepareMockWindowManager() {
+        final WindowManagerService service = mock(WindowManagerService.class);
+
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
+            if (runnable != null) {
+                runnable.run();
+            }
+            return null;
+        }).when(service).inSurfaceTransaction(any());
+
+        return service;
+    }
+
+    /**
+     * Overridden {@link ActivityStack} that tracks test metrics, such as the number of times a
+     * method is called. Note that its functionality depends on the implementations of the
+     * construction arguments.
+     */
+    protected static class TestActivityStack<T extends StackWindowController>
+            extends ActivityStack<T> {
+        private int mOnActivityRemovedFromStackCount = 0;
+        private T mContainerController;
+
+        static final int IS_TRANSLUCENT_UNSET = 0;
+        static final int IS_TRANSLUCENT_FALSE = 1;
+        static final int IS_TRANSLUCENT_TRUE = 2;
+        private int mIsTranslucent = IS_TRANSLUCENT_UNSET;
+
+        static final int SUPPORTS_SPLIT_SCREEN_UNSET = 0;
+        static final int SUPPORTS_SPLIT_SCREEN_FALSE = 1;
+        static final int SUPPORTS_SPLIT_SCREEN_TRUE = 2;
+        private int mSupportsSplitScreen = SUPPORTS_SPLIT_SCREEN_UNSET;
+
+        TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
+                int windowingMode, int activityType, boolean onTop, boolean createActivity) {
+            super(display, stackId, supervisor, windowingMode, activityType, onTop);
+            if (createActivity) {
+                new ActivityBuilder(mService).setCreateTask(true).setStack(this).build();
+                if (onTop) {
+                    // We move the task to front again in order to regain focus after activity
+                    // added to the stack. Or {@link ActivityDisplay#mPreferredTopFocusableStack}
+                    // could be other stacks (e.g. home stack).
+                    moveToFront("createActivityStack");
+                } else {
+                    moveToBack("createActivityStack", null);
+                }
+            }
+        }
+
+        @Override
+        void onActivityRemovedFromStack(ActivityRecord r) {
+            mOnActivityRemovedFromStackCount++;
+            super.onActivityRemovedFromStack(r);
+        }
+
+        // Returns the number of times {@link #onActivityRemovedFromStack} has been called
+        int onActivityRemovedFromStackInvocationCount() {
+            return mOnActivityRemovedFromStackCount;
+        }
+
+        @Override
+        protected T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
+            mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController();
+
+            // Primary pinned stacks require a non-empty out bounds to be set or else all tasks
+            // will be moved to the full screen stack.
+            if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                outBounds.set(0, 0, 100, 100);
+            }
+            return mContainerController;
+        }
+
+        @Override
+        T getWindowContainerController() {
+            return mContainerController;
+        }
+
+        void setIsTranslucent(boolean isTranslucent) {
+            mIsTranslucent = isTranslucent ? IS_TRANSLUCENT_TRUE : IS_TRANSLUCENT_FALSE;
+        }
+
+        @Override
+        boolean isStackTranslucent(ActivityRecord starting) {
+            switch (mIsTranslucent) {
+                case IS_TRANSLUCENT_TRUE:
+                    return true;
+                case IS_TRANSLUCENT_FALSE:
+                    return false;
+                case IS_TRANSLUCENT_UNSET:
+                default:
+                    return super.isStackTranslucent(starting);
+            }
+        }
+
+        void setSupportsSplitScreen(boolean supportsSplitScreen) {
+            mSupportsSplitScreen = supportsSplitScreen
+                    ? SUPPORTS_SPLIT_SCREEN_TRUE : SUPPORTS_SPLIT_SCREEN_FALSE;
+        }
+
+        @Override
+        public boolean supportsSplitScreenWindowingMode() {
+            switch (mSupportsSplitScreen) {
+                case SUPPORTS_SPLIT_SCREEN_TRUE:
+                    return true;
+                case SUPPORTS_SPLIT_SCREEN_FALSE:
+                    return false;
+                case SUPPORTS_SPLIT_SCREEN_UNSET:
+                default:
+                    return super.supportsSplitScreenWindowingMode();
+            }
+        }
+
+        @Override
+        void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
+                                 boolean newTask, boolean keepCurTransition,
+                                 ActivityOptions options) {
+        }
+    }
+
+    protected static class StackBuilder {
+        private final ActivityStackSupervisor mSupervisor;
+        private ActivityDisplay mDisplay;
+        private int mStackId = -1;
+        private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+        private int mActivityType = ACTIVITY_TYPE_STANDARD;
+        private boolean mOnTop = true;
+        private boolean mCreateActivity = true;
+
+        StackBuilder(ActivityStackSupervisor supervisor) {
+            mSupervisor = supervisor;
+            mDisplay = mSupervisor.getDefaultDisplay();
+        }
+
+        StackBuilder setWindowingMode(int windowingMode) {
+            mWindowingMode = windowingMode;
+            return this;
+        }
+
+        StackBuilder setActivityType(int activityType) {
+            mActivityType = activityType;
+            return this;
+        }
+
+        StackBuilder setStackId(int stackId) {
+            mStackId = stackId;
+            return this;
+        }
+
+        StackBuilder setDisplay(ActivityDisplay display) {
+            mDisplay = display;
+            return this;
+        }
+
+        StackBuilder setOnTop(boolean onTop) {
+            mOnTop = onTop;
+            return this;
+        }
+
+        StackBuilder setCreateActivity(boolean createActivity) {
+            mCreateActivity = createActivity;
+            return this;
+        }
+
+        @SuppressWarnings("TypeParameterUnusedInFormals")
+        <T extends ActivityStack> T build() {
+            final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
+            if (mWindowingMode == WINDOWING_MODE_PINNED) {
+                return (T) new PinnedActivityStack(mDisplay, stackId, mSupervisor, mOnTop) {
+                    @Override
+                    Rect getDefaultPictureInPictureBounds(float aspectRatio) {
+                        return new Rect(50, 50, 100, 100);
+                    }
+
+                    @Override
+                    PinnedStackWindowController createStackWindowController(int displayId,
+                            boolean onTop, Rect outBounds) {
+                        return mock(PinnedStackWindowController.class);
+                    }
+                };
+            } else {
+                return (T) new TestActivityStack(mDisplay, stackId, mSupervisor, mWindowingMode,
+                        mActivityType, mOnTop, mCreateActivity);
+            }
+        }
+
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
new file mode 100644
index 0000000..c6c1c52
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT;
+import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
+import static android.graphics.Bitmap.Config.ARGB_8888;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.app.AppOpsManager;
+import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+import android.view.IWindowManager;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
+
+import com.android.server.am.AssistDataRequester;
+import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Note: Currently, we only support fetching the screenshot for the current application, so the
+ * screenshot checks are hardcoded accordingly.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:AssistDataRequesterTest
+ */
+@MediumTest
+@FlakyTest(bugId = 113616538)
+public class AssistDataRequesterTest extends ActivityTestsBase {
+
+    private static final String TAG = AssistDataRequesterTest.class.getSimpleName();
+
+    private static final boolean CURRENT_ACTIVITY_ASSIST_ALLOWED = true;
+    private static final boolean CALLER_ASSIST_STRUCTURE_ALLOWED = true;
+    private static final boolean CALLER_ASSIST_SCREENSHOT_ALLOWED = true;
+    private static final boolean FETCH_DATA = true;
+    private static final boolean FETCH_SCREENSHOTS = true;
+    private static final boolean ALLOW_FETCH_DATA = true;
+    private static final boolean ALLOW_FETCH_SCREENSHOTS = true;
+
+    private static final int TEST_UID = 0;
+    private static final String TEST_PACKAGE = "";
+
+    private Context mContext;
+    private AssistDataRequester mDataRequester;
+    private Callbacks mCallbacks;
+    private Object mCallbacksLock;
+    private Handler mHandler;
+    private IActivityManager mAm;
+    private IActivityTaskManager mAtm;
+    private IWindowManager mWm;
+    private AppOpsManager mAppOpsManager;
+
+    /**
+     * The requests to fetch assist data are done incrementally from the text thread, and we
+     * immediately post onto the main thread handler below, which would immediately make the
+     * callback and decrement the pending counts. In order to assert the pending counts, we defer
+     * the callbacks on the test-side until after we flip the gate, after which we can drain the
+     * main thread handler and make assertions on the actual callbacks
+     */
+    private CountDownLatch mGate;
+
+    @Before
+    public void setUp() throws Exception {
+        mAm = mock(IActivityManager.class);
+        mAtm = mock(IActivityTaskManager.class);
+        mWm = mock(IWindowManager.class);
+        mAppOpsManager = mock(AppOpsManager.class);
+        mHandler = new Handler(Looper.getMainLooper());
+        mCallbacksLock = new Object();
+        mCallbacks = new Callbacks();
+        mDataRequester = new AssistDataRequester(mContext, mWm, mAppOpsManager, mCallbacks,
+                mCallbacksLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
+
+        // Gate the continuation of the assist data callbacks until we are ready within the tests
+        mGate = new CountDownLatch(1);
+        doAnswer(invocation -> {
+            mHandler.post(() -> {
+                try {
+                    mGate.await(10, TimeUnit.SECONDS);
+                    mDataRequester.onHandleAssistData(new Bundle());
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "Failed to wait", e);
+                }
+            });
+            return true;
+        }).when(mAtm).requestAssistContextExtras(anyInt(), any(), any(), any(), anyBoolean(),
+                anyBoolean());
+        doAnswer(invocation -> {
+            mHandler.post(() -> {
+                try {
+                    mGate.await(10, TimeUnit.SECONDS);
+                    mDataRequester.onHandleAssistScreenshot(Bitmap.createBitmap(1, 1, ARGB_8888));
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "Failed to wait", e);
+                }
+            });
+            return true;
+        }).when(mWm).requestAssistScreenshot(any());
+    }
+
+    private void setupMocks(boolean currentActivityAssistAllowed, boolean assistStructureAllowed,
+            boolean assistScreenshotAllowed) throws Exception {
+        doReturn(currentActivityAssistAllowed).when(mAtm).isAssistDataAllowedOnCurrentActivity();
+        doReturn(assistStructureAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
+                .checkOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString());
+        doReturn(assistScreenshotAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
+                .checkOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString());
+    }
+
+    @Test
+    public void testRequestData() throws Exception {
+        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+                CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+        assertReceivedDataCount(5, 5, 1, 1);
+    }
+
+    @Test
+    public void testEmptyActivities_expectNoCallbacks() throws Exception {
+        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+                CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+        mDataRequester.requestAssistData(createActivityList(0), FETCH_DATA, FETCH_SCREENSHOTS,
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+        assertReceivedDataCount(0, 0, 0, 0);
+    }
+
+    @Test
+    public void testCurrentAppDisallow_expectNullCallbacks() throws Exception {
+        setupMocks(!CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+                CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+        assertReceivedDataCount(0, 1, 0, 1);
+    }
+
+    @Test
+    public void testProcessPendingData() throws Exception {
+        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+                CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+        mCallbacks.mCanHandleReceivedData = false;
+        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+        assertEquals(5, mDataRequester.getPendingDataCount());
+        assertEquals(1, mDataRequester.getPendingScreenshotCount());
+        mGate.countDown();
+        waitForIdle(mHandler);
+
+        // Callbacks still not ready to receive, but all pending data is received
+        assertEquals(0, mDataRequester.getPendingDataCount());
+        assertEquals(0, mDataRequester.getPendingScreenshotCount());
+        assertThat(mCallbacks.mReceivedData).isEmpty();
+        assertThat(mCallbacks.mReceivedScreenshots).isEmpty();
+        assertFalse(mCallbacks.mRequestCompleted);
+
+        mCallbacks.mCanHandleReceivedData = true;
+        mDataRequester.processPendingAssistData();
+        // Since we are posting the callback for the request-complete, flush the handler as well
+        mGate.countDown();
+        waitForIdle(mHandler);
+        assertEquals(5, mCallbacks.mReceivedData.size());
+        assertEquals(1, mCallbacks.mReceivedScreenshots.size());
+        assertTrue(mCallbacks.mRequestCompleted);
+
+        // Clear the state and ensure that we only process pending data once
+        mCallbacks.reset();
+        mDataRequester.processPendingAssistData();
+        assertThat(mCallbacks.mReceivedData).isEmpty();
+        assertThat(mCallbacks.mReceivedScreenshots).isEmpty();
+    }
+
+    @Test
+    public void testNoFetchData_expectNoDataCallbacks() throws Exception {
+        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+                CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+        mDataRequester.requestAssistData(createActivityList(5), !FETCH_DATA, FETCH_SCREENSHOTS,
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+        assertReceivedDataCount(0, 0, 0, 1);
+    }
+
+    @Test
+    public void testDisallowAssistStructure_expectNullDataCallbacks() throws Exception {
+        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, !CALLER_ASSIST_STRUCTURE_ALLOWED,
+                CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+        // Expect a single null data when the appops is denied
+        assertReceivedDataCount(0, 1, 0, 1);
+    }
+
+    @Test
+    public void testDisallowAssistContextExtras_expectNullDataCallbacks() throws Exception {
+        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+                CALLER_ASSIST_SCREENSHOT_ALLOWED);
+        doReturn(false).when(mAtm).requestAssistContextExtras(anyInt(), any(), any(), any(),
+                anyBoolean(), anyBoolean());
+
+        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+        // Expect a single null data when requestAssistContextExtras() fails
+        assertReceivedDataCount(0, 1, 0, 1);
+    }
+
+    @Test
+    public void testNoFetchScreenshots_expectNoScreenshotCallbacks() throws Exception {
+        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+                CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, !FETCH_SCREENSHOTS,
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+        assertReceivedDataCount(5, 5, 0, 0);
+    }
+
+    @Test
+    public void testDisallowAssistScreenshot_expectNullScreenshotCallback() throws Exception {
+        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+                !CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+        // Expect a single null screenshot when the appops is denied
+        assertReceivedDataCount(5, 5, 0, 1);
+    }
+
+    @Test
+    public void testCanNotHandleReceivedData_expectNoCallbacks() throws Exception {
+        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, !CALLER_ASSIST_STRUCTURE_ALLOWED,
+                !CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+        mCallbacks.mCanHandleReceivedData = false;
+        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+        mGate.countDown();
+        waitForIdle(mHandler);
+        assertThat(mCallbacks.mReceivedData).isEmpty();
+        assertThat(mCallbacks.mReceivedScreenshots).isEmpty();
+    }
+
+    @Test
+    public void testRequestDataNoneAllowed_expectNullCallbacks() throws Exception {
+        setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+                CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+        mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+                !ALLOW_FETCH_DATA, !ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+        assertReceivedDataCount(0, 1, 0, 1);
+    }
+
+    private void assertReceivedDataCount(int numPendingData, int numReceivedData,
+            int numPendingScreenshots, int numReceivedScreenshots) throws Exception {
+        assertEquals("Expected " + numPendingData + " pending data, got "
+                        + mDataRequester.getPendingDataCount(),
+                numPendingData, mDataRequester.getPendingDataCount());
+        assertEquals("Expected " + numPendingScreenshots + " pending screenshots, got "
+                        + mDataRequester.getPendingScreenshotCount(),
+                numPendingScreenshots, mDataRequester.getPendingScreenshotCount());
+        assertFalse("Expected request NOT completed", mCallbacks.mRequestCompleted);
+        mGate.countDown();
+        waitForIdle(mHandler);
+        assertEquals("Expected " + numReceivedData + " data, received "
+                        + mCallbacks.mReceivedData.size(),
+                numReceivedData, mCallbacks.mReceivedData.size());
+        assertEquals("Expected " + numReceivedScreenshots + " screenshots, received "
+                        + mCallbacks.mReceivedScreenshots.size(),
+                numReceivedScreenshots, mCallbacks.mReceivedScreenshots.size());
+        assertTrue("Expected request completed", mCallbacks.mRequestCompleted);
+    }
+
+    private List<IBinder> createActivityList(int size) {
+        ArrayList<IBinder> activities = new ArrayList<>();
+        for (int i = 0; i < size; i++) {
+            activities.add(mock(IBinder.class));
+        }
+        return activities;
+    }
+
+    public void waitForIdle(Handler h) throws Exception {
+        if (Looper.myLooper() == h.getLooper()) {
+            throw new RuntimeException("This method can not be called from the waiting looper");
+        }
+        CountDownLatch latch = new CountDownLatch(1);
+        h.post(() -> latch.countDown());
+        latch.await(2, TimeUnit.SECONDS);
+    }
+
+    private class Callbacks implements AssistDataRequesterCallbacks {
+
+        public boolean mCanHandleReceivedData = true;
+        public boolean mRequestCompleted = false;
+        public final ArrayList<Bundle> mReceivedData = new ArrayList<>();
+        public final ArrayList<Bitmap> mReceivedScreenshots = new ArrayList<>();
+
+        void reset() {
+            mCanHandleReceivedData = true;
+            mReceivedData.clear();
+            mReceivedScreenshots.clear();
+        }
+
+        @Override
+        public boolean canHandleReceivedAssistDataLocked() {
+            return mCanHandleReceivedData;
+        }
+
+        @Override
+        public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
+            mReceivedData.add(data);
+        }
+
+        @Override
+        public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
+            mReceivedScreenshots.add(screenshot);
+        }
+
+        @Override
+        public void onAssistRequestCompleted() {
+            mHandler.post(() -> {
+                try {
+                    mGate.await(10, TimeUnit.SECONDS);
+                    mRequestCompleted = true;
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "Failed to wait", e);
+                }
+            });
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
new file mode 100644
index 0000000..f1d840d
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.IApplicationThread;
+import android.app.servertransaction.ClientTransaction;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:ClientLifecycleManagerTests
+ */
+@SmallTest
+@Presubmit
+public class ClientLifecycleManagerTests {
+
+    @Test
+    public void testScheduleAndRecycleBinderClientTransaction() throws Exception {
+        ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.class),
+                new Binder()));
+
+        ClientLifecycleManager clientLifecycleManager = new ClientLifecycleManager();
+        clientLifecycleManager.scheduleTransaction(item);
+
+        verify(item, times(1)).recycle();
+    }
+
+    @Test
+    public void testScheduleNoRecycleNonBinderClientTransaction() throws Exception {
+        ClientTransaction item = spy(ClientTransaction.obtain(mock(IApplicationThread.Stub.class),
+                new Binder()));
+
+        ClientLifecycleManager clientLifecycleManager = new ClientLifecycleManager();
+        clientLifecycleManager.scheduleTransaction(item);
+
+        verify(item, times(0)).recycle();
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
new file mode 100644
index 0000000..82a200b
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+import static android.content.res.Configuration.EMPTY;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.res.Configuration;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test class for {@link ConfigurationContainer}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ConfigurationContainerTests
+ */
+@SmallTest
+@Presubmit
+public class ConfigurationContainerTests {
+
+    @Test
+    public void testConfigurationInit() {
+        // Check root container initial config.
+        final TestConfigurationContainer root = new TestConfigurationContainer();
+        assertEquals(EMPTY, root.getOverrideConfiguration());
+        assertEquals(EMPTY, root.getMergedOverrideConfiguration());
+        assertEquals(EMPTY, root.getConfiguration());
+
+        // Check child initial config.
+        final TestConfigurationContainer child1 = root.addChild();
+        assertEquals(EMPTY, child1.getOverrideConfiguration());
+        assertEquals(EMPTY, child1.getMergedOverrideConfiguration());
+        assertEquals(EMPTY, child1.getConfiguration());
+
+        // Check child initial config if root has overrides.
+        final Configuration rootOverrideConfig = new Configuration();
+        rootOverrideConfig.fontScale = 1.3f;
+        root.onOverrideConfigurationChanged(rootOverrideConfig);
+        final TestConfigurationContainer child2 = root.addChild();
+        assertEquals(EMPTY, child2.getOverrideConfiguration());
+        assertEquals(rootOverrideConfig, child2.getMergedOverrideConfiguration());
+        assertEquals(rootOverrideConfig, child2.getConfiguration());
+
+        // Check child initial config if root has parent config set.
+        final Configuration rootParentConfig = new Configuration();
+        rootParentConfig.fontScale = 0.8f;
+        rootParentConfig.orientation = SCREEN_ORIENTATION_LANDSCAPE;
+        root.onConfigurationChanged(rootParentConfig);
+        final Configuration rootFullConfig = new Configuration(rootParentConfig);
+        rootFullConfig.updateFrom(rootOverrideConfig);
+
+        final TestConfigurationContainer child3 = root.addChild();
+        assertEquals(EMPTY, child3.getOverrideConfiguration());
+        assertEquals(rootOverrideConfig, child3.getMergedOverrideConfiguration());
+        assertEquals(rootFullConfig, child3.getConfiguration());
+    }
+
+    @Test
+    public void testConfigurationChangeOnAddRemove() {
+        // Init root's config.
+        final TestConfigurationContainer root = new TestConfigurationContainer();
+        final Configuration rootOverrideConfig = new Configuration();
+        rootOverrideConfig.fontScale = 1.3f;
+        root.onOverrideConfigurationChanged(rootOverrideConfig);
+
+        // Init child's config.
+        final TestConfigurationContainer child = root.addChild();
+        final Configuration childOverrideConfig = new Configuration();
+        childOverrideConfig.densityDpi = 320;
+        child.onOverrideConfigurationChanged(childOverrideConfig);
+        final Configuration mergedOverrideConfig = new Configuration(root.getConfiguration());
+        mergedOverrideConfig.updateFrom(childOverrideConfig);
+
+        // Check configuration update when child is removed from parent.
+        root.removeChild(child);
+        assertEquals(childOverrideConfig, child.getOverrideConfiguration());
+        assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
+        assertEquals(mergedOverrideConfig, child.getConfiguration());
+
+        // It may be paranoia... but let's check if parent's config didn't change after removal.
+        assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
+        assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
+        assertEquals(rootOverrideConfig, root.getConfiguration());
+
+        // Init different root
+        final TestConfigurationContainer root2 = new TestConfigurationContainer();
+        final Configuration rootOverrideConfig2 = new Configuration();
+        rootOverrideConfig2.fontScale = 1.1f;
+        root2.onOverrideConfigurationChanged(rootOverrideConfig2);
+
+        // Check configuration update when child is added to different parent.
+        mergedOverrideConfig.setTo(rootOverrideConfig2);
+        mergedOverrideConfig.updateFrom(childOverrideConfig);
+        root2.addChild(child);
+        assertEquals(childOverrideConfig, child.getOverrideConfiguration());
+        assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
+        assertEquals(mergedOverrideConfig, child.getConfiguration());
+    }
+
+    @Test
+    public void testConfigurationChangePropagation() {
+        // Builds 3-level vertical hierarchy with one configuration container on each level.
+        // In addition to different overrides on each level, everyone in hierarchy will have one
+        // common overridden value - orientation;
+
+        // Init root's config.
+        final TestConfigurationContainer root = new TestConfigurationContainer();
+        final Configuration rootOverrideConfig = new Configuration();
+        rootOverrideConfig.fontScale = 1.3f;
+        rootOverrideConfig.orientation = SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+        root.onOverrideConfigurationChanged(rootOverrideConfig);
+
+        // Init children.
+        final TestConfigurationContainer child1 = root.addChild();
+        final Configuration childOverrideConfig1 = new Configuration();
+        childOverrideConfig1.densityDpi = 320;
+        childOverrideConfig1.orientation = SCREEN_ORIENTATION_LANDSCAPE;
+        child1.onOverrideConfigurationChanged(childOverrideConfig1);
+
+        final TestConfigurationContainer child2 = child1.addChild();
+        final Configuration childOverrideConfig2 = new Configuration();
+        childOverrideConfig2.screenWidthDp = 150;
+        childOverrideConfig2.orientation = SCREEN_ORIENTATION_PORTRAIT;
+        child2.onOverrideConfigurationChanged(childOverrideConfig2);
+
+        // Check configuration on all levels when root override is updated.
+        rootOverrideConfig.smallestScreenWidthDp = 200;
+        root.onOverrideConfigurationChanged(rootOverrideConfig);
+
+        final Configuration mergedOverrideConfig1 = new Configuration(rootOverrideConfig);
+        mergedOverrideConfig1.updateFrom(childOverrideConfig1);
+        final Configuration mergedConfig1 = new Configuration(mergedOverrideConfig1);
+
+        final Configuration mergedOverrideConfig2 = new Configuration(mergedOverrideConfig1);
+        mergedOverrideConfig2.updateFrom(childOverrideConfig2);
+        final Configuration mergedConfig2 = new Configuration(mergedOverrideConfig2);
+
+        assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
+        assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
+        assertEquals(rootOverrideConfig, root.getConfiguration());
+
+        assertEquals(childOverrideConfig1, child1.getOverrideConfiguration());
+        assertEquals(mergedOverrideConfig1, child1.getMergedOverrideConfiguration());
+        assertEquals(mergedConfig1, child1.getConfiguration());
+
+        assertEquals(childOverrideConfig2, child2.getOverrideConfiguration());
+        assertEquals(mergedOverrideConfig2, child2.getMergedOverrideConfiguration());
+        assertEquals(mergedConfig2, child2.getConfiguration());
+
+        // Check configuration on all levels when root parent config is updated.
+        final Configuration rootParentConfig = new Configuration();
+        rootParentConfig.screenHeightDp = 100;
+        rootParentConfig.orientation = SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+        root.onConfigurationChanged(rootParentConfig);
+        final Configuration mergedRootConfig = new Configuration(rootParentConfig);
+        mergedRootConfig.updateFrom(rootOverrideConfig);
+
+        mergedConfig1.setTo(mergedRootConfig);
+        mergedConfig1.updateFrom(mergedOverrideConfig1);
+
+        mergedConfig2.setTo(mergedConfig1);
+        mergedConfig2.updateFrom(mergedOverrideConfig2);
+
+        assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
+        assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
+        assertEquals(mergedRootConfig, root.getConfiguration());
+
+        assertEquals(childOverrideConfig1, child1.getOverrideConfiguration());
+        assertEquals(mergedOverrideConfig1, child1.getMergedOverrideConfiguration());
+        assertEquals(mergedConfig1, child1.getConfiguration());
+
+        assertEquals(childOverrideConfig2, child2.getOverrideConfiguration());
+        assertEquals(mergedOverrideConfig2, child2.getMergedOverrideConfiguration());
+        assertEquals(mergedConfig2, child2.getConfiguration());
+    }
+
+    @Test
+    public void testSetWindowingMode() {
+        final TestConfigurationContainer root = new TestConfigurationContainer();
+        root.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+        final TestConfigurationContainer child = root.addChild();
+        child.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(WINDOWING_MODE_UNDEFINED, root.getWindowingMode());
+        assertEquals(WINDOWING_MODE_FREEFORM, child.getWindowingMode());
+
+        root.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertEquals(WINDOWING_MODE_FULLSCREEN, root.getWindowingMode());
+        assertEquals(WINDOWING_MODE_FREEFORM, child.getWindowingMode());
+    }
+
+    @Test
+    public void testSetActivityType() {
+        final TestConfigurationContainer root = new TestConfigurationContainer();
+        root.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+        final TestConfigurationContainer child = root.addChild();
+        child.setActivityType(ACTIVITY_TYPE_STANDARD);
+        assertEquals(ACTIVITY_TYPE_UNDEFINED, root.getActivityType());
+        assertEquals(ACTIVITY_TYPE_STANDARD, child.getActivityType());
+
+        boolean gotException = false;
+        try {
+            // Can't change activity type once set.
+            child.setActivityType(ACTIVITY_TYPE_HOME);
+        } catch (IllegalStateException e) {
+            gotException = true;
+        }
+        assertTrue("Can't change activity type once set.", gotException);
+
+        // TODO: Commenting out for now until we figure-out a good way to test these rules that
+        // should only apply to system process.
+        /*
+        gotException = false;
+        try {
+            // Parent can't change child's activity type once set.
+            root.setActivityType(ACTIVITY_TYPE_HOME);
+        } catch (IllegalStateException e) {
+            gotException = true;
+        }
+        assertTrue("Parent can't change activity type once set.", gotException);
+        assertEquals(ACTIVITY_TYPE_HOME, root.getActivityType());
+
+        final TestConfigurationContainer child2 = new TestConfigurationContainer();
+        child2.setActivityType(ACTIVITY_TYPE_RECENTS);
+
+        gotException = false;
+        try {
+            // Can't re-parent to a different activity type.
+            root.addChild(child2);
+        } catch (IllegalStateException e) {
+            gotException = true;
+        }
+        assertTrue("Can't re-parent to a different activity type.", gotException);
+        */
+
+    }
+
+    @Test
+    public void testRegisterConfigurationChangeListener() {
+        final TestConfigurationContainer container = new TestConfigurationContainer();
+        final TestConfigurationChangeListener listener = new TestConfigurationChangeListener();
+        final Configuration config = new Configuration();
+        config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        config.windowConfiguration.setAppBounds(10, 10, 10, 10);
+        container.onOverrideConfigurationChanged(config);
+        container.registerConfigurationChangeListener(listener);
+        // Assert listener got the current config. of the container after it was registered.
+        assertEquals(config, listener.mOverrideConfiguration);
+        // Assert listener gets changes to override configuration.
+        container.onOverrideConfigurationChanged(EMPTY);
+        assertEquals(EMPTY, listener.mOverrideConfiguration);
+    }
+
+    /**
+     * Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed
+     * for testing.
+     */
+    private class TestConfigurationContainer
+            extends ConfigurationContainer<TestConfigurationContainer> {
+        private List<TestConfigurationContainer> mChildren = new ArrayList<>();
+        private TestConfigurationContainer mParent;
+
+        TestConfigurationContainer addChild(TestConfigurationContainer childContainer) {
+            childContainer.mParent = this;
+            childContainer.onParentChanged();
+            mChildren.add(childContainer);
+            return childContainer;
+        }
+
+        TestConfigurationContainer addChild() {
+            return addChild(new TestConfigurationContainer());
+        }
+
+        void removeChild(TestConfigurationContainer child) {
+            child.mParent = null;
+            child.onParentChanged();
+        }
+
+        @Override
+        protected int getChildCount() {
+            return mChildren.size();
+        }
+
+        @Override
+        protected TestConfigurationContainer getChildAt(int index) {
+            return mChildren.get(index);
+        }
+
+        @Override
+        protected ConfigurationContainer getParent() {
+            return mParent;
+        }
+    }
+
+    private static class TestConfigurationChangeListener implements ConfigurationContainerListener {
+
+        final Configuration mOverrideConfiguration = new Configuration();
+
+        @Override
+        public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+            mOverrideConfiguration.setTo(overrideConfiguration);
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java b/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java
deleted file mode 100644
index aecb278..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.wm;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Test;
-
-import androidx.test.filters.FlakyTest;
-
-/**
- * Dummy test for com.android.server.wm
- * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
- */
-public class DummyWmTests {
-
-    @Presubmit
-    @Test
-    public void preSubmitTest() {}
-
-    @FlakyTest
-    @Presubmit
-    @Test
-    public void flakyPreSubmitTest() {}
-
-    @Test
-    public void postSubmitTest() {}
-
-    @FlakyTest
-    @Test
-    public void flakyPostSubmitTest() {}
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
new file mode 100644
index 0000000..40c20a4
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo.WindowLayout;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.server.wm.LaunchParamsController.LaunchParams;
+import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for exercising {@link LaunchParamsController}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:LaunchParamsControllerTests
+ */
+@MediumTest
+@Presubmit
+public class LaunchParamsControllerTests extends ActivityTestsBase {
+    private LaunchParamsController mController;
+
+    @Before
+    public void setUp() throws Exception {
+        mService = createActivityTaskManagerService();
+        mController = new LaunchParamsController(mService);
+    }
+
+    /**
+     * Makes sure positioners get values passed to controller.
+     */
+    @Test
+    public void testArgumentPropagation() {
+        final LaunchParamsModifier
+                positioner = mock(LaunchParamsModifier.class);
+        mController.registerModifier(positioner);
+
+        final ActivityRecord record = new ActivityBuilder(mService).build();
+        final ActivityRecord source = new ActivityBuilder(mService).build();
+        final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
+        final ActivityOptions options = mock(ActivityOptions.class);
+
+        mController.calculate(record.getTask(), layout, record, source, options,
+                new LaunchParams());
+        verify(positioner, times(1)).onCalculate(eq(record.getTask()), eq(layout), eq(record),
+                eq(source), eq(options), any(), any());
+    }
+
+    /**
+     * Ensures positioners further down the chain are not called when RESULT_DONE is returned.
+     */
+    @Test
+    public void testEarlyExit() {
+        final LaunchParamsModifier
+                ignoredPositioner = mock(LaunchParamsModifier.class);
+        final LaunchParamsModifier earlyExitPositioner =
+                (task, layout, activity, source, options, currentParams, outParams) -> RESULT_DONE;
+
+        mController.registerModifier(ignoredPositioner);
+        mController.registerModifier(earlyExitPositioner);
+
+        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
+                null /*source*/, null /*options*/, new LaunchParams());
+        verify(ignoredPositioner, never()).onCalculate(any(), any(), any(), any(), any(),
+                any(), any());
+    }
+
+    /**
+     * Ensures that positioners are called in the correct order.
+     */
+    @Test
+    public void testRegistration() {
+        LaunchParamsModifier earlyExitPositioner =
+                new InstrumentedPositioner(RESULT_DONE, new LaunchParams());
+
+        final LaunchParamsModifier firstPositioner = spy(earlyExitPositioner);
+
+        mController.registerModifier(firstPositioner);
+
+        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
+                null /*source*/, null /*options*/, new LaunchParams());
+        verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
+                any());
+
+        final LaunchParamsModifier secondPositioner = spy(earlyExitPositioner);
+
+        mController.registerModifier(secondPositioner);
+
+        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
+                null /*source*/, null /*options*/, new LaunchParams());
+        verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
+                any());
+        verify(secondPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
+                any());
+    }
+
+    /**
+     * Makes sure positioners further down the registration chain are called.
+     */
+    @Test
+    public void testPassThrough() {
+        final LaunchParamsModifier
+                positioner1 = mock(LaunchParamsModifier.class);
+        final LaunchParams params = new LaunchParams();
+        params.mWindowingMode = WINDOWING_MODE_FREEFORM;
+        params.mBounds.set(0, 0, 30, 20);
+        params.mPreferredDisplayId = 3;
+
+        final InstrumentedPositioner positioner2 = new InstrumentedPositioner(RESULT_CONTINUE,
+                params);
+
+        mController.registerModifier(positioner1);
+        mController.registerModifier(positioner2);
+
+        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+                null /*options*/, new LaunchParams());
+
+        verify(positioner1, times(1)).onCalculate(any(), any(), any(), any(), any(),
+                eq(positioner2.getLaunchParams()), any());
+    }
+
+    /**
+     * Ensures skipped results are not propagated.
+     */
+    @Test
+    public void testSkip() {
+        final LaunchParams params1 = new LaunchParams();
+        params1.mBounds.set(0, 0, 10, 10);
+        final InstrumentedPositioner positioner1 = new InstrumentedPositioner(RESULT_SKIP, params1);
+
+        final LaunchParams params2 = new LaunchParams();
+        params2.mBounds.set(0, 0, 20, 30);
+        final InstrumentedPositioner positioner2 =
+                new InstrumentedPositioner(RESULT_CONTINUE, params2);
+
+        mController.registerModifier(positioner1);
+        mController.registerModifier(positioner2);
+
+        final LaunchParams result = new LaunchParams();
+
+        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+                null /*options*/, result);
+
+        assertEquals(result, positioner2.getLaunchParams());
+    }
+
+    /**
+     * Tests preferred display id calculation for VR.
+     */
+    @Test
+    public void testVrPreferredDisplay() {
+        final int vr2dDisplayId = 1;
+        mService.mVr2dDisplayId = vr2dDisplayId;
+
+        final LaunchParams result = new LaunchParams();
+        final ActivityRecord vrActivity = new ActivityBuilder(mService).build();
+        vrActivity.requestedVrComponent = vrActivity.realActivity;
+
+        // VR activities should always land on default display.
+        mController.calculate(null /*task*/, null /*layout*/, vrActivity /*activity*/,
+                null /*source*/, null /*options*/, result);
+        assertEquals(DEFAULT_DISPLAY, result.mPreferredDisplayId);
+
+        // Otherwise, always lands on VR 2D display.
+        final ActivityRecord vr2dActivity = new ActivityBuilder(mService).build();
+        mController.calculate(null /*task*/, null /*layout*/, vr2dActivity /*activity*/,
+                null /*source*/, null /*options*/, result);
+        assertEquals(vr2dDisplayId, result.mPreferredDisplayId);
+        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+                null /*options*/, result);
+        assertEquals(vr2dDisplayId, result.mPreferredDisplayId);
+
+        mService.mVr2dDisplayId = INVALID_DISPLAY;
+    }
+
+    /**
+     * Ensures that {@link LaunchParamsModifier} requests specifying display id during
+     * layout are honored.
+     */
+    @Test
+    public void testLayoutTaskPreferredDisplayChange() {
+        final LaunchParams params = new LaunchParams();
+        params.mPreferredDisplayId = 2;
+        final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
+        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor).build();
+
+        mController.registerModifier(positioner);
+
+        doNothing().when(mService).moveStackToDisplay(anyInt(), anyInt());
+        mController.layoutTask(task, null /* windowLayout */);
+        verify(mService, times(1)).moveStackToDisplay(eq(task.getStackId()),
+                eq(params.mPreferredDisplayId));
+    }
+
+    /**
+     * Ensures that {@link LaunchParamsModifier} requests specifying windowingMode during
+     * layout are honored.
+     */
+    @Test
+    public void testLayoutTaskWindowingModeChange() {
+        final LaunchParams params = new LaunchParams();
+        final int windowingMode = WINDOWING_MODE_FREEFORM;
+        params.mWindowingMode = windowingMode;
+        final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
+        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor).build();
+
+        mController.registerModifier(positioner);
+
+        final int beforeWindowMode = task.getStack().getWindowingMode();
+        assertNotEquals(windowingMode, beforeWindowMode);
+
+        mController.layoutTask(task, null /* windowLayout */);
+
+        final int afterWindowMode = task.getStack().getWindowingMode();
+        assertEquals(windowingMode, afterWindowMode);
+    }
+
+    public static class InstrumentedPositioner implements LaunchParamsModifier {
+
+        private final int mReturnVal;
+        private final LaunchParams mParams;
+
+        InstrumentedPositioner(int returnVal, LaunchParams params) {
+            mReturnVal = returnVal;
+            mParams = params;
+        }
+
+        @Override
+        public int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
+                   ActivityRecord source, ActivityOptions options,
+                   LaunchParams currentParams, LaunchParams outParams) {
+            outParams.set(mParams);
+            return mReturnVal;
+        }
+
+        LaunchParams getLaunchParams() {
+            return mParams;
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
new file mode 100644
index 0000000..6b613ed
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -0,0 +1,693 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
+import static android.app.StatusBarManager.DISABLE2_MASK;
+import static android.app.StatusBarManager.DISABLE2_NONE;
+import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
+import static android.app.StatusBarManager.DISABLE_HOME;
+import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
+import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
+import static android.os.Process.SYSTEM_UID;
+import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.wm.LockTaskController.STATUS_BAR_MASK_LOCKED;
+import static com.android.server.wm.LockTaskController.STATUS_BAR_MASK_PINNED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.StatusBarManager;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.IDevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
+import android.testing.DexmakerShareClassLoaderRule;
+import android.util.Pair;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.WindowManagerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.verification.VerificationMode;
+
+/**
+ * Unit tests for {@link LockTaskController}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:LockTaskControllerTest
+ */
+@SmallTest
+@Presubmit
+public class LockTaskControllerTest {
+    private static final String TEST_PACKAGE_NAME = "com.test.package";
+    private static final String TEST_PACKAGE_NAME_2 = "com.test.package2";
+    private static final String TEST_CLASS_NAME = ".TestClass";
+    private static final int TEST_USER_ID = 123;
+    private static final int TEST_UID = 10467;
+
+    @Rule
+    public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+            new DexmakerShareClassLoaderRule();
+
+    @Mock private ActivityStackSupervisor mSupervisor;
+    @Mock private IDevicePolicyManager mDevicePolicyManager;
+    @Mock private IStatusBarService mStatusBarService;
+    @Mock private WindowManagerService mWindowManager;
+    @Mock private LockPatternUtils mLockPatternUtils;
+    @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+    @Mock private TelecomManager mTelecomManager;
+    @Mock private RecentTasks mRecentTasks;
+
+    private LockTaskController mLockTaskController;
+    private Context mContext;
+    private String mLockToAppSetting;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = getInstrumentation().getTargetContext();
+        mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED);
+
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        mSupervisor.mRecentTasks = mRecentTasks;
+
+        mLockTaskController = new LockTaskController(mContext, mSupervisor,
+                new ImmediatelyExecuteHandler());
+        mLockTaskController.setWindowManager(mWindowManager);
+        mLockTaskController.mStatusBarService = mStatusBarService;
+        mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
+        mLockTaskController.mTelecomManager = mTelecomManager;
+        mLockTaskController.mLockPatternUtils = mLockPatternUtils;
+
+        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+        LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        Settings.Secure.putString(mContext.getContentResolver(),
+                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting);
+    }
+
+    @Test
+    public void testPreconditions() {
+        // GIVEN nothing has happened
+
+        // THEN current lock task mode should be NONE
+        assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
+    }
+
+    @Test
+    public void testStartLockTaskMode_once() throws Exception {
+        // GIVEN a task record with whitelisted auth
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+
+        // WHEN calling setLockTaskMode for LOCKED mode without resuming
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // THEN the lock task mode state should be LOCKED
+        assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
+        // THEN the task should be locked
+        assertTrue(mLockTaskController.isTaskLocked(tr));
+
+        // THEN lock task mode should be started
+        verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
+    }
+
+    @Test
+    public void testStartLockTaskMode_twice() throws Exception {
+        // GIVEN two task records with whitelisted auth
+        TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+
+        // WHEN calling setLockTaskMode for LOCKED mode on both tasks
+        mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
+        mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
+
+        // THEN the lock task mode state should be LOCKED
+        assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
+        // THEN neither of the tasks should be able to move to back of stack
+        assertTrue(mLockTaskController.isTaskLocked(tr1));
+        assertTrue(mLockTaskController.isTaskLocked(tr2));
+
+        // THEN lock task mode should be started
+        verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
+    }
+
+    @Test
+    public void testStartLockTaskMode_pinningRequest() {
+        // GIVEN a task record that is not whitelisted, i.e. with pinned auth
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+
+        // WHEN calling startLockTaskMode
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // THEN a pinning request should be shown
+        verify(mStatusBarManagerInternal).showScreenPinningRequest(anyInt());
+    }
+
+    @Test
+    public void testStartLockTaskMode_pinnedBySystem() throws Exception {
+        // GIVEN a task record with pinned auth
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+
+        // WHEN the system calls startLockTaskMode
+        mLockTaskController.startLockTaskMode(tr, true, SYSTEM_UID);
+
+        // THEN the lock task mode state should be PINNED
+        assertEquals(LOCK_TASK_MODE_PINNED, mLockTaskController.getLockTaskModeState());
+        // THEN the task should be locked
+        assertTrue(mLockTaskController.isTaskLocked(tr));
+
+        // THEN lock task mode should be started
+        verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
+        // THEN screen pinning toast should be shown
+        verify(mStatusBarService).showPinningEnterExitToast(true /* entering */);
+    }
+
+    @Test
+    public void testLockTaskViolation() {
+        // GIVEN one task record with whitelisted auth that is in lock task mode
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // THEN it's not a lock task violation to try and launch this task without clearing
+        assertFalse(mLockTaskController.isLockTaskModeViolation(tr, false));
+
+        // THEN it's a lock task violation to launch another task that is not whitelisted
+        assertTrue(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
+                TaskRecord.LOCK_TASK_AUTH_PINNABLE)));
+        // THEN it's a lock task violation to launch another task that is disallowed from lock task
+        assertTrue(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
+                TaskRecord.LOCK_TASK_AUTH_DONT_LOCK)));
+
+        // THEN it's no a lock task violation to launch another task that is whitelisted
+        assertFalse(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
+                TaskRecord.LOCK_TASK_AUTH_WHITELISTED)));
+        assertFalse(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
+                TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE)));
+        // THEN it's not a lock task violation to launch another task that is priv launchable
+        assertFalse(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
+                TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV)));
+    }
+
+    @Test
+    public void testLockTaskViolation_emergencyCall() {
+        // GIVEN one task record with whitelisted auth that is in lock task mode
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // GIVEN tasks necessary for emergency calling
+        TaskRecord keypad = getTaskRecord(new Intent().setComponent(EMERGENCY_DIALER_COMPONENT),
+                TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        TaskRecord callAction = getTaskRecord(new Intent(Intent.ACTION_CALL_EMERGENCY),
+                TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        TaskRecord dialer = getTaskRecord("com.example.dialer", TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        when(mTelecomManager.getSystemDialerPackage())
+                .thenReturn(dialer.intent.getComponent().getPackageName());
+
+        // GIVEN keyguard is allowed for lock task mode
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD);
+
+        // THEN the above tasks should all be allowed
+        assertFalse(mLockTaskController.isLockTaskModeViolation(keypad));
+        assertFalse(mLockTaskController.isLockTaskModeViolation(callAction));
+        assertFalse(mLockTaskController.isLockTaskModeViolation(dialer));
+
+        // GIVEN keyguard is disallowed for lock task mode (default)
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE);
+
+        // THEN the above tasks should all be blocked
+        assertTrue(mLockTaskController.isLockTaskModeViolation(keypad));
+        assertTrue(mLockTaskController.isLockTaskModeViolation(callAction));
+        assertTrue(mLockTaskController.isLockTaskModeViolation(dialer));
+    }
+
+    @Test
+    public void testStopLockTaskMode() throws Exception {
+        // GIVEN one task record with whitelisted auth that is in lock task mode
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // WHEN the same caller calls stopLockTaskMode
+        mLockTaskController.stopLockTaskMode(tr, false, TEST_UID);
+
+        // THEN the lock task mode should be NONE
+        assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
+        // THEN the task should no longer be locked
+        assertFalse(mLockTaskController.isTaskLocked(tr));
+        // THEN lock task mode should have been finished
+        verifyLockTaskStopped(times(1));
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testStopLockTaskMode_differentCaller() {
+        // GIVEN one task record with whitelisted auth that is in lock task mode
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // WHEN a different caller calls stopLockTaskMode
+        mLockTaskController.stopLockTaskMode(tr, false, TEST_UID + 1);
+
+        // THEN security exception should be thrown, because different caller tried to unlock
+    }
+
+    @Test
+    public void testStopLockTaskMode_systemCaller() {
+        // GIVEN one task record with whitelisted auth that is in lock task mode
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // WHEN system calls stopLockTaskMode
+        mLockTaskController.stopLockTaskMode(tr, true, SYSTEM_UID);
+
+        // THEN lock task mode should still be active
+        assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
+    }
+
+    @Test
+    public void testStopLockTaskMode_twoTasks() throws Exception {
+        // GIVEN two task records with whitelisted auth that is in lock task mode
+        TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
+        mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
+
+        // WHEN calling stopLockTaskMode
+        mLockTaskController.stopLockTaskMode(tr2, false, TEST_UID);
+
+        // THEN the lock task mode should still be active
+        assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
+        // THEN the first task should still be locked
+        assertTrue(mLockTaskController.isTaskLocked(tr1));
+        // THEN the top task should no longer be locked
+        assertFalse(mLockTaskController.isTaskLocked(tr2));
+        // THEN lock task mode should not have been finished
+        verifyLockTaskStopped(never());
+    }
+
+    @Test
+    public void testStopLockTaskMode_rootTask() throws Exception {
+        // GIVEN two task records with whitelisted auth that is in lock task mode
+        TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
+        mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
+
+        // WHEN calling stopLockTaskMode on the root task
+        mLockTaskController.stopLockTaskMode(tr1, false, TEST_UID);
+
+        // THEN the lock task mode should be inactive
+        assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
+        // THEN the first task should no longer be locked
+        assertFalse(mLockTaskController.isTaskLocked(tr1));
+        // THEN the top task should no longer be locked
+        assertFalse(mLockTaskController.isTaskLocked(tr2));
+        // THEN lock task mode should be finished
+        verifyLockTaskStopped(times(1));
+    }
+
+    @Test
+    public void testStopLockTaskMode_pinned() throws Exception {
+        // GIVEN one task records that is in pinned mode
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        mLockTaskController.startLockTaskMode(tr, true, SYSTEM_UID);
+        // GIVEN that the keyguard is required to show after unlocking
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1);
+
+        // reset invocation counter
+        reset(mStatusBarService);
+
+        // WHEN calling stopLockTask
+        mLockTaskController.stopLockTaskMode(null, true, SYSTEM_UID);
+
+        // THEN the lock task mode should no longer be active
+        assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
+        // THEN the task should no longer be locked
+        assertFalse(mLockTaskController.isTaskLocked(tr));
+        // THEN lock task mode should have been finished
+        verifyLockTaskStopped(times(1));
+        // THEN the keyguard should be shown
+        verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL);
+        // THEN screen pinning toast should be shown
+        verify(mStatusBarService).showPinningEnterExitToast(false /* entering */);
+    }
+
+    @Test
+    public void testClearLockedTasks() throws Exception {
+        // GIVEN two task records with whitelisted auth that is in lock task mode
+        TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
+        mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
+
+        // WHEN calling stopLockTaskMode on the root task
+        mLockTaskController.clearLockedTasks("testClearLockedTasks");
+
+        // THEN the lock task mode should be inactive
+        assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
+        // THEN the first task should no longer be locked
+        assertFalse(mLockTaskController.isTaskLocked(tr1));
+        // THEN the top task should no longer be locked
+        assertFalse(mLockTaskController.isTaskLocked(tr2));
+        // THEN lock task mode should be finished
+        verifyLockTaskStopped(times(1));
+    }
+
+    @Test
+    public void testUpdateLockTaskPackages() {
+        String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
+        String[] whitelist2 = {TEST_PACKAGE_NAME};
+
+        // No package is whitelisted initially
+        for (String pkg : whitelist1) {
+            assertFalse("Package shouldn't be whitelisted: " + pkg,
+                    mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg));
+            assertFalse("Package shouldn't be whitelisted for user 0: " + pkg,
+                    mLockTaskController.isPackageWhitelisted(0, pkg));
+        }
+
+        // Apply whitelist
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist1);
+
+        // Assert the whitelist is applied to the correct user
+        for (String pkg : whitelist1) {
+            assertTrue("Package should be whitelisted: " + pkg,
+                    mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg));
+            assertFalse("Package shouldn't be whitelisted for user 0: " + pkg,
+                    mLockTaskController.isPackageWhitelisted(0, pkg));
+        }
+
+        // Update whitelist
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist2);
+
+        // Assert the new whitelist is applied
+        assertTrue("Package should remain whitelisted: " + TEST_PACKAGE_NAME,
+                mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME));
+        assertFalse("Package should no longer be whitelisted: " + TEST_PACKAGE_NAME_2,
+                mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME_2));
+    }
+
+    @Test
+    public void testUpdateLockTaskPackages_taskRemoved() throws Exception {
+        // GIVEN two tasks which are whitelisted initially
+        TaskRecord tr1 = getTaskRecordForUpdate(TEST_PACKAGE_NAME, true);
+        TaskRecord tr2 = getTaskRecordForUpdate(TEST_PACKAGE_NAME_2, false);
+        String[] whitelist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+
+        // GIVEN the tasks are launched into LockTask mode
+        mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
+        mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
+        assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
+        assertTrue(mLockTaskController.isTaskLocked(tr1));
+        assertTrue(mLockTaskController.isTaskLocked(tr2));
+        verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
+
+        // WHEN removing one package from whitelist
+        whitelist = new String[] {TEST_PACKAGE_NAME};
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+
+        // THEN the task running that package should be stopped
+        verify(tr2).performClearTaskLocked();
+        assertFalse(mLockTaskController.isTaskLocked(tr2));
+        // THEN the other task should remain locked
+        assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
+        assertTrue(mLockTaskController.isTaskLocked(tr1));
+        verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
+
+        // WHEN removing the last package from whitelist
+        whitelist = new String[] {};
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+
+        // THEN the last task should be cleared, and the system should quit LockTask mode
+        verify(tr1).performClearTaskLocked();
+        assertFalse(mLockTaskController.isTaskLocked(tr1));
+        assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
+        verifyLockTaskStopped(times(1));
+    }
+
+    @Test
+    public void testUpdateLockTaskFeatures() throws Exception {
+        // GIVEN a locked task
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // THEN lock task mode should be started with default status bar masks
+        verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
+
+        // reset invocation counter
+        reset(mStatusBarService);
+
+        // WHEN home button is enabled for lock task mode
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_HOME);
+
+        // THEN status bar should be updated to reflect this change
+        int expectedFlags = STATUS_BAR_MASK_LOCKED
+                & ~DISABLE_HOME;
+        int expectedFlags2 = DISABLE2_MASK;
+        verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
+                eq(mContext.getPackageName()));
+        verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
+                eq(mContext.getPackageName()));
+
+        // reset invocation counter
+        reset(mStatusBarService);
+
+        // WHEN notifications are enabled for lock task mode
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NOTIFICATIONS);
+
+        // THEN status bar should be updated to reflect this change
+        expectedFlags = STATUS_BAR_MASK_LOCKED
+                & ~DISABLE_NOTIFICATION_ICONS
+                & ~DISABLE_NOTIFICATION_ALERTS;
+        expectedFlags2 = DISABLE2_MASK
+                & ~DISABLE2_NOTIFICATION_SHADE;
+        verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
+                eq(mContext.getPackageName()));
+        verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
+                eq(mContext.getPackageName()));
+    }
+
+    @Test
+    public void testUpdateLockTaskFeatures_differentUser() throws Exception {
+        // GIVEN a locked task
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // THEN lock task mode should be started with default status bar masks
+        verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
+
+        // reset invocation counter
+        reset(mStatusBarService);
+
+        // WHEN home button is enabled for lock task mode for another user
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID + 1, LOCK_TASK_FEATURE_HOME);
+
+        // THEN status bar shouldn't change
+        verify(mStatusBarService, never()).disable(anyInt(), any(IBinder.class),
+                eq(mContext.getPackageName()));
+        verify(mStatusBarService, never()).disable2(anyInt(), any(IBinder.class),
+                eq(mContext.getPackageName()));
+    }
+
+    @Test
+    public void testUpdateLockTaskFeatures_keyguard() {
+        // GIVEN a locked task
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // THEN keyguard should be disabled
+        verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString());
+
+        // WHEN keyguard is enabled for lock task mode
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD);
+
+        // THEN keyguard should be enabled
+        verify(mWindowManager).reenableKeyguard(any(IBinder.class));
+
+        // WHEN keyguard is disabled again for lock task mode
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE);
+
+        // THEN keyguard should be disabled
+        verify(mWindowManager, times(2)).disableKeyguard(any(IBinder.class), anyString());
+    }
+
+    @Test
+    public void testGetStatusBarDisableFlags() {
+        // Note that we don't enumerate all StatusBarManager flags, but only choose a subset to test
+
+        // WHEN nothing is enabled
+        Pair<Integer, Integer> flags = mLockTaskController.getStatusBarDisableFlags(
+                LOCK_TASK_FEATURE_NONE);
+        // THEN unsupported feature flags should still be untouched
+        assertTrue((~STATUS_BAR_MASK_LOCKED & flags.first) == 0);
+        // THEN everything else should be disabled
+        assertTrue((StatusBarManager.DISABLE_CLOCK & flags.first) != 0);
+        assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0);
+
+        // WHEN only home button is enabled
+        flags = mLockTaskController.getStatusBarDisableFlags(
+                LOCK_TASK_FEATURE_HOME);
+        // THEN unsupported feature flags should still be untouched
+        assertTrue((~STATUS_BAR_MASK_LOCKED & flags.first) == 0);
+        // THEN home button should indeed be enabled
+        assertTrue((StatusBarManager.DISABLE_HOME & flags.first) == 0);
+        // THEN other feature flags should remain disabled
+        assertTrue((StatusBarManager.DISABLE2_NOTIFICATION_SHADE & flags.second) != 0);
+
+        // WHEN only global actions menu and notifications are enabled
+        flags = mLockTaskController.getStatusBarDisableFlags(
+                DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS
+                        | DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS);
+        // THEN unsupported feature flags should still be untouched
+        assertTrue((~STATUS_BAR_MASK_LOCKED & flags.first) == 0);
+        // THEN notifications should be enabled
+        assertTrue((StatusBarManager.DISABLE_NOTIFICATION_ICONS & flags.first) == 0);
+        assertTrue((StatusBarManager.DISABLE_NOTIFICATION_ALERTS & flags.first) == 0);
+        assertTrue((StatusBarManager.DISABLE2_NOTIFICATION_SHADE & flags.second) == 0);
+        // THEN global actions should be enabled
+        assertTrue((StatusBarManager.DISABLE2_GLOBAL_ACTIONS & flags.second) == 0);
+        // THEN quick settings should still be disabled
+        assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0);
+    }
+
+    private TaskRecord getTaskRecord(int lockTaskAuth) {
+        return getTaskRecord(TEST_PACKAGE_NAME, lockTaskAuth);
+    }
+
+    private TaskRecord getTaskRecord(String pkg, int lockTaskAuth) {
+        final Intent intent = new Intent()
+                .setComponent(ComponentName.createRelative(pkg, TEST_CLASS_NAME));
+        return getTaskRecord(intent, lockTaskAuth);
+    }
+
+    private TaskRecord getTaskRecord(Intent intent, int lockTaskAuth) {
+        TaskRecord tr = mock(TaskRecord.class);
+        tr.mLockTaskAuth = lockTaskAuth;
+        tr.intent = intent;
+        tr.userId = TEST_USER_ID;
+        return tr;
+    }
+
+    /**
+     * @param isAppAware {@code true} if the app has marked if_whitelisted in its manifest
+     */
+    private TaskRecord getTaskRecordForUpdate(String pkg, boolean isAppAware) {
+        final int authIfWhitelisted = isAppAware
+                ? TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE
+                : TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
+        TaskRecord tr = getTaskRecord(pkg, authIfWhitelisted);
+        doAnswer((invocation) -> {
+            boolean isWhitelisted =
+                    mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg);
+            tr.mLockTaskAuth = isWhitelisted
+                    ? authIfWhitelisted
+                    : TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+            return null;
+        }).when(tr).setLockTaskAuth();
+        return tr;
+    }
+
+    private void verifyLockTaskStarted(int statusBarMask, int statusBarMask2) throws Exception {
+        // THEN the keyguard should have been disabled
+        verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString());
+        // THEN the status bar should have been disabled
+        verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class),
+                eq(mContext.getPackageName()));
+        verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class),
+                eq(mContext.getPackageName()));
+        // THEN recents should have been notified
+        verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID));
+        // THEN the DO/PO should be informed about the operation
+        verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME,
+                TEST_USER_ID);
+    }
+
+    private void verifyLockTaskStopped(VerificationMode mode) throws Exception {
+        // THEN the keyguard should have been disabled
+        verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class));
+        // THEN the status bar should have been disabled
+        verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE),
+                any(IBinder.class), eq(mContext.getPackageName()));
+        verify(mStatusBarService, mode).disable2(eq(StatusBarManager.DISABLE2_NONE),
+                any(IBinder.class), eq(mContext.getPackageName()));
+        // THEN the DO/PO should be informed about the operation
+        verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(false, null, TEST_USER_ID);
+    }
+
+    /**
+     * Special handler implementation that executes any message / runnable posted immediately on the
+     * thread that it's posted on rather than enqueuing them on its looper.
+     */
+    private static class ImmediatelyExecuteHandler extends Handler {
+        @Override
+        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+            if (msg.getCallback() != null) {
+                msg.getCallback().run();
+            }
+            return true;
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
new file mode 100644
index 0000000..efd7d25
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.app.ActivityOptions;
+import android.platform.test.annotations.Presubmit;
+import android.view.RemoteAnimationAdapter;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:PendingRemoteAnimationRegistryTest
+ */
+@SmallTest
+@FlakyTest
+@Presubmit
+public class PendingRemoteAnimationRegistryTest extends ActivityTestsBase {
+
+    @Mock RemoteAnimationAdapter mAdapter;
+    private PendingRemoteAnimationRegistry mRegistry;
+    private final OffsettableClock mClock = new OffsettableClock.Stopped();
+    private TestHandler mHandler;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mService = createActivityTaskManagerService();
+        mService.mH.runWithScissors(() -> {
+            mHandler = new TestHandler(null, mClock);
+        }, 0);
+        mRegistry = new PendingRemoteAnimationRegistry(mService, mHandler);
+    }
+
+    @Test
+    public void testOverrideActivityOptions() {
+        mRegistry.addPendingAnimation("com.android.test", mAdapter);
+        ActivityOptions opts = ActivityOptions.makeBasic();
+        opts = mRegistry.overrideOptionsIfNeeded("com.android.test", opts);
+        assertEquals(mAdapter, opts.getRemoteAnimationAdapter());
+    }
+
+    @Test
+    public void testOverrideActivityOptions_null() {
+        mRegistry.addPendingAnimation("com.android.test", mAdapter);
+        final ActivityOptions opts = mRegistry.overrideOptionsIfNeeded("com.android.test", null);
+        assertNotNull(opts);
+        assertEquals(mAdapter, opts.getRemoteAnimationAdapter());
+    }
+
+    @Test
+    public void testTimeout() {
+        mRegistry.addPendingAnimation("com.android.test", mAdapter);
+        mClock.fastForward(5000);
+        mHandler.timeAdvance();
+        assertNull(mRegistry.overrideOptionsIfNeeded("com.android.test", null));
+    }
+
+    @Test
+    public void testTimeout_overridenEntry() {
+        mRegistry.addPendingAnimation("com.android.test", mAdapter);
+        mClock.fastForward(2500);
+        mHandler.timeAdvance();
+        mRegistry.addPendingAnimation("com.android.test", mAdapter);
+        mClock.fastForward(1000);
+        mHandler.timeAdvance();
+        final ActivityOptions opts = mRegistry.overrideOptionsIfNeeded("com.android.test", null);
+        assertEquals(mAdapter, opts.getRemoteAnimationAdapter());
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
new file mode 100644
index 0000000..20150b4
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertSame;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:PersisterQueueTests
+ */
+@MediumTest
+@FlakyTest(detail = "Confirm stable in post-submit before removing")
+@Presubmit
+public class PersisterQueueTests implements PersisterQueue.Listener {
+    private static final long INTER_WRITE_DELAY_MS = 50;
+    private static final long PRE_TASK_DELAY_MS = 300;
+    // We allow at most 1s more than the expected timeout.
+    private static final long TIMEOUT_ALLOWANCE = 100;
+
+    private static final Predicate<MatchingTestItem> TEST_ITEM_PREDICATE = item -> item.mMatching;
+
+    private AtomicInteger mItemCount;
+    private CountDownLatch mSetUpLatch;
+    private volatile CountDownLatch mLatch;
+    private List<Boolean> mProbablyDoneResults;
+
+    private final PersisterQueue mTarget =
+            new PersisterQueue(INTER_WRITE_DELAY_MS, PRE_TASK_DELAY_MS);
+
+    @Before
+    public void setUp() throws Exception {
+        mItemCount = new AtomicInteger(0);
+        mProbablyDoneResults = new ArrayList<>();
+        mSetUpLatch = new CountDownLatch(1);
+
+        mTarget.addListener(this);
+        mTarget.startPersisting();
+
+        assertTrue("Target didn't call callback on start up.",
+                mSetUpLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mTarget.stopPersisting();
+        mTarget.removeListener(this);
+    }
+
+    @Test
+    public void testCallCallbackOnStartUp() {
+        // The onPreProcessItem() must be called on start up.
+        assertEquals(1, mProbablyDoneResults.size());
+        // The last one must be called with probably done being true.
+        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(0));
+    }
+
+    @Test
+    public void testProcessOneItem() throws Exception {
+        mLatch = new CountDownLatch(1);
+
+        final long dispatchTime = SystemClock.uptimeMillis();
+        mTarget.addItem(new TestItem(), false);
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process item.", 1, mItemCount.get());
+        final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+        assertTrue("Target didn't wait enough time before processing item. duration: "
+                        + processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS + "ms",
+                processDuration >= PRE_TASK_DELAY_MS);
+
+        // Once before processing this item, once after that.
+        assertEquals(2, mProbablyDoneResults.size());
+        // The last one must be called with probably done being true.
+        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
+    }
+
+    @Test
+    public void testProcessOneItem_Flush() throws Exception {
+        mLatch = new CountDownLatch(1);
+
+        final long dispatchTime = SystemClock.uptimeMillis();
+        mTarget.addItem(new TestItem(), true);
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process item.", 1, mItemCount.get());
+        final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+        assertTrue("Target didn't process item immediately when flushing. duration: "
+                        + processDuration + "ms pretask delay: "
+                        + PRE_TASK_DELAY_MS + "ms",
+                processDuration < PRE_TASK_DELAY_MS);
+
+        // Once before processing this item, once after that.
+        assertEquals(2, mProbablyDoneResults.size());
+        // The last one must be called with probably done being true.
+        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
+    }
+
+    @Test
+    public void testProcessTwoItems() throws Exception {
+        mLatch = new CountDownLatch(2);
+
+        final long dispatchTime = SystemClock.uptimeMillis();
+        mTarget.addItem(new TestItem(), false);
+        mTarget.addItem(new TestItem(), false);
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS + TIMEOUT_ALLOWANCE,
+                        TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process all items.", 2, mItemCount.get());
+        final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+        assertTrue("Target didn't wait enough time before processing item. duration: "
+                        + processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS
+                        + "ms inter write delay: " + INTER_WRITE_DELAY_MS + "ms",
+                processDuration >= PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS);
+
+        // Once before processing this item, once after that.
+        assertEquals(3, mProbablyDoneResults.size());
+        // The first one must be called with probably done being false.
+        assertFalse("The first probablyDone must be false.", mProbablyDoneResults.get(1));
+        // The last one must be called with probably done being true.
+        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
+    }
+
+    @Test
+    public void testProcessTwoItems_OneAfterAnother() throws Exception {
+        // First item
+        mLatch = new CountDownLatch(1);
+        long dispatchTime = SystemClock.uptimeMillis();
+        mTarget.addItem(new TestItem(), false);
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+        assertTrue("Target didn't wait enough time before processing item."
+                        + processDuration + "ms pretask delay: "
+                        + PRE_TASK_DELAY_MS + "ms",
+                processDuration >= PRE_TASK_DELAY_MS);
+        assertEquals("Target didn't process item.", 1, mItemCount.get());
+
+        // Second item
+        mLatch = new CountDownLatch(1);
+        dispatchTime = SystemClock.uptimeMillis();
+        // Synchronize on the instance to make sure we schedule the item after it starts to wait for
+        // task indefinitely.
+        synchronized (mTarget) {
+            mTarget.addItem(new TestItem(), false);
+        }
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process all items.", 2, mItemCount.get());
+        processDuration = SystemClock.uptimeMillis() - dispatchTime;
+        assertTrue("Target didn't wait enough time before processing item."
+                        + processDuration + "ms pre task delay: "
+                        + PRE_TASK_DELAY_MS + "ms",
+                processDuration >= PRE_TASK_DELAY_MS);
+
+        // Once before processing this item, once after that.
+        assertEquals(3, mProbablyDoneResults.size());
+        // The last one must be called with probably done being true.
+        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
+    }
+
+    @Test
+    public void testFindLastItemNotReturnDifferentType() {
+        synchronized (mTarget) {
+            mTarget.addItem(new TestItem(), false);
+            assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+        }
+    }
+
+    @Test
+    public void testFindLastItemNotReturnMismatchItem() {
+        synchronized (mTarget) {
+            mTarget.addItem(new MatchingTestItem(false), false);
+            assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+        }
+    }
+
+    @Test
+    public void testFindLastItemReturnMatchedItem() {
+        synchronized (mTarget) {
+            final MatchingTestItem item = new MatchingTestItem(true);
+            mTarget.addItem(item, false);
+            assertSame(item, mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+        }
+    }
+
+    @Test
+    public void testRemoveItemsNotRemoveDifferentType() throws Exception {
+        mLatch = new CountDownLatch(1);
+        synchronized (mTarget) {
+            mTarget.addItem(new TestItem(), false);
+            mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+        }
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process item.", 1, mItemCount.get());
+    }
+
+    @Test
+    public void testRemoveItemsNotRemoveMismatchedItem() throws Exception {
+        mLatch = new CountDownLatch(1);
+        synchronized (mTarget) {
+            mTarget.addItem(new MatchingTestItem(false), false);
+            mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+        }
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process item.", 1, mItemCount.get());
+    }
+
+    @Test
+    public void testRemoveItemsRemoveMatchedItem() throws Exception {
+        mLatch = new CountDownLatch(1);
+        synchronized (mTarget) {
+            mTarget.addItem(new TestItem(), false);
+            mTarget.addItem(new MatchingTestItem(true), false);
+            mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+        }
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process item.", 1, mItemCount.get());
+    }
+
+    @Test
+    public void testFlushWaitSynchronously() {
+        final long dispatchTime = SystemClock.uptimeMillis();
+        mTarget.addItem(new TestItem(), false);
+        mTarget.addItem(new TestItem(), false);
+        mTarget.flush();
+        assertEquals("Flush should wait until all items are processed before return.",
+                2, mItemCount.get());
+        final long processTime = SystemClock.uptimeMillis() - dispatchTime;
+        assertWithMessage("Flush should trigger immediate flush without delays. processTime: "
+                + processTime).that(processTime).isLessThan(TIMEOUT_ALLOWANCE);
+    }
+
+    @Override
+    public void onPreProcessItem(boolean queueEmpty) {
+        mProbablyDoneResults.add(queueEmpty);
+
+        final CountDownLatch latch = mLatch;
+        if (latch != null) {
+            latch.countDown();
+        }
+
+        mSetUpLatch.countDown();
+    }
+
+    private class TestItem implements PersisterQueue.WriteQueueItem {
+        @Override
+        public void process() {
+            mItemCount.getAndIncrement();
+        }
+    }
+
+    private class MatchingTestItem extends TestItem {
+        private boolean mMatching;
+
+        private MatchingTestItem(boolean matching) {
+            mMatching = matching;
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
new file mode 100644
index 0000000..26241d2
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -0,0 +1,1073 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import static java.lang.Integer.MAX_VALUE;
+
+import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
+import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.UserInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.util.MutableLong;
+import android.util.SparseBooleanArray;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.server.wm.RecentTasks.Callbacks;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:RecentTasksTest
+ */
+@MediumTest
+@Presubmit
+public class RecentTasksTest extends ActivityTestsBase {
+    private static final int TEST_USER_0_ID = 0;
+    private static final int TEST_USER_1_ID = 10;
+    private static final int TEST_QUIET_USER_ID = 20;
+    private static final UserInfo DEFAULT_USER_INFO = new UserInfo();
+    private static final UserInfo QUIET_USER_INFO = new UserInfo();
+    private static int sLastTaskId = 1;
+    private static int sLastStackId = 1;
+    private static final int INVALID_STACK_ID = 999;
+
+    private TestActivityTaskManagerService mTestService;
+    private ActivityDisplay mDisplay;
+    private ActivityDisplay mOtherDisplay;
+    private ActivityStack mStack;
+    private ActivityStack mHomeStack;
+    private TestTaskPersister mTaskPersister;
+    private TestRecentTasks mRecentTasks;
+    private TestRunningTasks mRunningTasks;
+
+    private ArrayList<TaskRecord> mTasks;
+    private ArrayList<TaskRecord> mSameDocumentTasks;
+
+    private CallbacksRecorder mCallbacksRecorder;
+
+    @Before
+    public void setUp() throws Exception {
+        mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
+        mTestService = spy(new MyTestActivityTaskManagerService(mContext));
+        final TestActivityManagerService am = spy(new MyTestActivityManagerService());
+        setupActivityManagerService(am, mTestService);
+        mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks();
+        mRecentTasks.loadParametersFromResources(mContext.getResources());
+        mHomeStack = mTestService.mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        mStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        mCallbacksRecorder = new CallbacksRecorder();
+        mRecentTasks.registerCallback(mCallbacksRecorder);
+        QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE;
+
+        mTasks = new ArrayList<>();
+        mTasks.add(createTaskBuilder(".Task1").build());
+        mTasks.add(createTaskBuilder(".Task2").build());
+        mTasks.add(createTaskBuilder(".Task3").build());
+        mTasks.add(createTaskBuilder(".Task4").build());
+        mTasks.add(createTaskBuilder(".Task5").build());
+
+        mSameDocumentTasks = new ArrayList<>();
+        mSameDocumentTasks.add(createDocumentTask(".DocumentTask1"));
+        mSameDocumentTasks.add(createDocumentTask(".DocumentTask1"));
+    }
+
+    @Test
+    public void testCallbacks() {
+        // Add some tasks
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.add(mTasks.get(1));
+        assertThat(mCallbacksRecorder.mAdded).contains(mTasks.get(0));
+        assertThat(mCallbacksRecorder.mAdded).contains(mTasks.get(1));
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).isEmpty();
+        mCallbacksRecorder.clear();
+
+        // Remove some tasks
+        mRecentTasks.remove(mTasks.get(0));
+        mRecentTasks.remove(mTasks.get(1));
+        assertThat(mCallbacksRecorder.mAdded).isEmpty();
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).contains(mTasks.get(0));
+        assertThat(mCallbacksRecorder.mRemoved).contains(mTasks.get(1));
+        mCallbacksRecorder.clear();
+
+        // Remove the callback, ensure we don't get any calls
+        mRecentTasks.unregisterCallback(mCallbacksRecorder);
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.remove(mTasks.get(0));
+        assertThat(mCallbacksRecorder.mAdded).isEmpty();
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).isEmpty();
+    }
+
+    @Test
+    public void testAddTasksNoMultiple_expectNoTrim() {
+        // Add same non-multiple-task document tasks will remove the task (to re-add it) but not
+        // trim it
+        TaskRecord documentTask1 = createDocumentTask(".DocumentTask1");
+        TaskRecord documentTask2 = createDocumentTask(".DocumentTask1");
+        mRecentTasks.add(documentTask1);
+        mRecentTasks.add(documentTask2);
+        assertThat(mCallbacksRecorder.mAdded).contains(documentTask1);
+        assertThat(mCallbacksRecorder.mAdded).contains(documentTask2);
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).contains(documentTask1);
+    }
+
+    @Test
+    public void testAddTasksMaxTaskRecents_expectNoTrim() {
+        // Add a task hitting max-recents for that app will remove the task (to add the next one)
+        // but not trim it
+        TaskRecord documentTask1 = createDocumentTask(".DocumentTask1");
+        TaskRecord documentTask2 = createDocumentTask(".DocumentTask1");
+        documentTask1.maxRecents = 1;
+        documentTask2.maxRecents = 1;
+        mRecentTasks.add(documentTask1);
+        mRecentTasks.add(documentTask2);
+        assertThat(mCallbacksRecorder.mAdded).contains(documentTask1);
+        assertThat(mCallbacksRecorder.mAdded).contains(documentTask2);
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).contains(documentTask1);
+    }
+
+    @Test
+    public void testAddTasksSameTask_expectNoTrim() {
+        // Add a task that is already in the task list does not trigger any callbacks, it just
+        // moves in the list
+        TaskRecord documentTask1 = createDocumentTask(".DocumentTask1");
+        mRecentTasks.add(documentTask1);
+        mRecentTasks.add(documentTask1);
+        assertThat(mCallbacksRecorder.mAdded).hasSize(1);
+        assertThat(mCallbacksRecorder.mAdded).contains(documentTask1);
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).isEmpty();
+    }
+
+    @Test
+    public void testAddTasksMultipleDocumentTasks_expectNoTrim() {
+        // Add same multiple-task document tasks does not trim the first tasks
+        TaskRecord documentTask1 = createDocumentTask(".DocumentTask1",
+                FLAG_ACTIVITY_MULTIPLE_TASK);
+        TaskRecord documentTask2 = createDocumentTask(".DocumentTask1",
+                FLAG_ACTIVITY_MULTIPLE_TASK);
+        mRecentTasks.add(documentTask1);
+        mRecentTasks.add(documentTask2);
+        assertThat(mCallbacksRecorder.mAdded).hasSize(2);
+        assertThat(mCallbacksRecorder.mAdded).contains(documentTask1);
+        assertThat(mCallbacksRecorder.mAdded).contains(documentTask2);
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).isEmpty();
+    }
+
+    @Test
+    public void testAddTasksMultipleTasks_expectRemovedNoTrim() {
+        // Add multiple same-affinity non-document tasks, ensure that it removes the other task,
+        // but that it does not trim it
+        TaskRecord task1 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
+                .build();
+        TaskRecord task2 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
+                .build();
+        mRecentTasks.add(task1);
+        assertThat(mCallbacksRecorder.mAdded).hasSize(1);
+        assertThat(mCallbacksRecorder.mAdded).contains(task1);
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).isEmpty();
+        mCallbacksRecorder.clear();
+        mRecentTasks.add(task2);
+        assertThat(mCallbacksRecorder.mAdded).hasSize(1);
+        assertThat(mCallbacksRecorder.mAdded).contains(task2);
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).hasSize(1);
+        assertThat(mCallbacksRecorder.mRemoved).contains(task1);
+    }
+
+    @Test
+    public void testAddTasksDifferentStacks_expectNoRemove() {
+        // Adding the same task with different activity types should not trigger removal of the
+        // other task
+        TaskRecord task1 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
+                .setStack(mHomeStack).build();
+        TaskRecord task2 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
+                .setStack(mStack).build();
+        mRecentTasks.add(task1);
+        mRecentTasks.add(task2);
+        assertThat(mCallbacksRecorder.mAdded).hasSize(2);
+        assertThat(mCallbacksRecorder.mAdded).contains(task1);
+        assertThat(mCallbacksRecorder.mAdded).contains(task2);
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).isEmpty();
+    }
+
+    @Test
+    public void testAddTaskCompatibleActivityType_expectRemove() {
+        // Test with undefined activity type since the type is not persisted by the task persister
+        // and we want to ensure that a new task will match a restored task
+        TaskRecord task1 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .build();
+        setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED);
+        assertThat(task1.getActivityType()).isEqualTo(ACTIVITY_TYPE_UNDEFINED);
+        mRecentTasks.add(task1);
+        mCallbacksRecorder.clear();
+
+        TaskRecord task2 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .build();
+        assertEquals(ACTIVITY_TYPE_STANDARD, task2.getActivityType());
+        mRecentTasks.add(task2);
+        assertThat(mCallbacksRecorder.mAdded).hasSize(1);
+        assertThat(mCallbacksRecorder.mAdded).contains(task2);
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).hasSize(1);
+        assertThat(mCallbacksRecorder.mRemoved).contains(task1);
+    }
+
+    @Test
+    public void testAddTaskCompatibleActivityTypeDifferentUser_expectNoRemove() {
+        TaskRecord task1 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .setUserId(TEST_USER_0_ID)
+                .build();
+        setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED);
+        assertEquals(ACTIVITY_TYPE_UNDEFINED, task1.getActivityType());
+        mRecentTasks.add(task1);
+        mCallbacksRecorder.clear();
+
+        TaskRecord task2 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .setUserId(TEST_USER_1_ID)
+                .build();
+        assertEquals(ACTIVITY_TYPE_STANDARD, task2.getActivityType());
+        mRecentTasks.add(task2);
+        assertThat(mCallbacksRecorder.mAdded).hasSize(1);
+        assertThat(mCallbacksRecorder.mAdded).contains(task2);
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).isEmpty();
+    }
+
+    @Test
+    public void testAddTaskCompatibleWindowingMode_expectRemove() {
+        TaskRecord task1 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .build();
+        setTaskWindowingMode(task1, WINDOWING_MODE_UNDEFINED);
+        assertEquals(WINDOWING_MODE_UNDEFINED, task1.getWindowingMode());
+        mRecentTasks.add(task1);
+        mCallbacksRecorder.clear();
+
+        TaskRecord task2 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .build();
+        setTaskWindowingMode(task2, WINDOWING_MODE_FULLSCREEN);
+        assertEquals(WINDOWING_MODE_FULLSCREEN, task2.getWindowingMode());
+        mRecentTasks.add(task2);
+
+        assertThat(mCallbacksRecorder.mAdded).hasSize(1);
+        assertThat(mCallbacksRecorder.mAdded).contains(task2);
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).hasSize(1);
+        assertThat(mCallbacksRecorder.mRemoved).contains(task1);
+    }
+
+    @Test
+    public void testAddTaskIncompatibleWindowingMode_expectNoRemove() {
+        TaskRecord task1 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .build();
+        setTaskWindowingMode(task1, WINDOWING_MODE_FULLSCREEN);
+        assertEquals(WINDOWING_MODE_FULLSCREEN, task1.getWindowingMode());
+        mRecentTasks.add(task1);
+
+        TaskRecord task2 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .build();
+        setTaskWindowingMode(task2, WINDOWING_MODE_PINNED);
+        assertEquals(WINDOWING_MODE_PINNED, task2.getWindowingMode());
+        mRecentTasks.add(task2);
+
+        assertThat(mCallbacksRecorder.mAdded).hasSize(2);
+        assertThat(mCallbacksRecorder.mAdded).contains(task1);
+        assertThat(mCallbacksRecorder.mAdded).contains(task2);
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).isEmpty();
+    }
+
+    @Test
+    public void testUsersTasks() {
+        mRecentTasks.setOnlyTestVisibleRange();
+
+        // Setup some tasks for the users
+        mTaskPersister.mUserTaskIdsOverride = new SparseBooleanArray();
+        mTaskPersister.mUserTaskIdsOverride.put(1, true);
+        mTaskPersister.mUserTaskIdsOverride.put(2, true);
+        mTaskPersister.mUserTasksOverride = new ArrayList<>();
+        mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask1").build());
+        mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask2").build());
+
+        // Assert no user tasks are initially loaded
+        assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).hasLength(0);
+
+        // Load user 0 tasks
+        mRecentTasks.loadUserRecentsLocked(TEST_USER_0_ID);
+        assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_0_ID);
+        assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
+        assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
+
+        // Load user 1 tasks
+        mRecentTasks.loadUserRecentsLocked(TEST_USER_1_ID);
+        assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_0_ID);
+        assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_1_ID);
+        assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
+        assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
+        assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_1_ID));
+        assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_1_ID));
+
+        // Unload user 1 tasks
+        mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_1_ID);
+        assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_0_ID);
+        assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList()
+                .doesNotContain(TEST_USER_1_ID);
+        assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
+        assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
+
+        // Unload user 0 tasks
+        mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_0_ID);
+        assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList()
+                .doesNotContain(TEST_USER_0_ID);
+        assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList()
+                .doesNotContain(TEST_USER_1_ID);
+    }
+
+    @Test
+    public void testOrderedIteration() {
+        mRecentTasks.setOnlyTestVisibleRange();
+        TaskRecord task1 = createTaskBuilder(".Task1").build();
+        task1.lastActiveTime = new Random().nextInt();
+        TaskRecord task2 = createTaskBuilder(".Task1").build();
+        task2.lastActiveTime = new Random().nextInt();
+        TaskRecord task3 = createTaskBuilder(".Task1").build();
+        task3.lastActiveTime = new Random().nextInt();
+        TaskRecord task4 = createTaskBuilder(".Task1").build();
+        task4.lastActiveTime = new Random().nextInt();
+        mRecentTasks.add(task1);
+        mRecentTasks.add(task2);
+        mRecentTasks.add(task3);
+        mRecentTasks.add(task4);
+
+        MutableLong prevLastActiveTime = new MutableLong(0);
+        final ArrayList<TaskRecord> tasks = mRecentTasks.getRawTasks();
+        for (int i = 0; i < tasks.size(); i++) {
+            final TaskRecord task = tasks.get(i);
+            assertThat(prevLastActiveTime.value).isLessThan(task.lastActiveTime);
+            prevLastActiveTime.value = task.lastActiveTime;
+        }
+    }
+
+    @Test
+    public void testTrimToGlobalMaxNumRecents() {
+        mRecentTasks.setOnlyTestVisibleRange();
+
+        // Limit the global maximum number of recent tasks to a fixed size
+        mRecentTasks.setGlobalMaxNumTasks(2 /* globalMaxNumTasks */);
+
+        // Add N+1 tasks
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.add(mTasks.get(1));
+        mRecentTasks.add(mTasks.get(2));
+
+        // Ensure that the last task was trimmed as an inactive task
+        assertTrimmed(mTasks.get(0));
+    }
+
+    @Test
+    public void testTrimQuietProfileTasks() {
+        mRecentTasks.setOnlyTestVisibleRange();
+        TaskRecord qt1 = createTaskBuilder(".QuietTask1").setUserId(TEST_QUIET_USER_ID).build();
+        TaskRecord qt2 = createTaskBuilder(".QuietTask2").setUserId(TEST_QUIET_USER_ID).build();
+        mRecentTasks.add(qt1);
+        mRecentTasks.add(qt2);
+
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.add(mTasks.get(1));
+
+        // Ensure that the quiet user's tasks was trimmed once the new tasks were added
+        assertTrimmed(qt1, qt2);
+    }
+
+    @Test
+    public void testSessionDuration() {
+        mRecentTasks.setOnlyTestVisibleRange();
+        mRecentTasks.setParameters(-1 /* min */, -1 /* max */, 50 /* ms */);
+
+        TaskRecord t1 = createTaskBuilder(".Task1").build();
+        t1.touchActiveTime();
+        mRecentTasks.add(t1);
+
+        // Force a small sleep just beyond the session duration
+        SystemClock.sleep(75);
+
+        TaskRecord t2 = createTaskBuilder(".Task2").build();
+        t2.touchActiveTime();
+        mRecentTasks.add(t2);
+
+        // Assert that the old task has been removed due to being out of the active session
+        assertTrimmed(t1);
+    }
+
+    @Test
+    public void testVisibleTasks_excludedFromRecents() {
+        mRecentTasks.setOnlyTestVisibleRange();
+        mRecentTasks.setParameters(-1 /* min */, 4 /* max */, -1 /* ms */);
+
+        TaskRecord excludedTask1 = createTaskBuilder(".ExcludedTask1")
+                .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                .build();
+        TaskRecord excludedTask2 = createTaskBuilder(".ExcludedTask2")
+                .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                .build();
+
+        mRecentTasks.add(excludedTask1);
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.add(mTasks.get(1));
+        mRecentTasks.add(mTasks.get(2));
+        mRecentTasks.add(excludedTask2);
+
+        // The last excluded task should be trimmed, while the first-most excluded task should not
+        assertTrimmed(excludedTask1);
+    }
+
+    @Test
+    public void testVisibleTasks_minNum() {
+        mRecentTasks.setOnlyTestVisibleRange();
+        mRecentTasks.setParameters(5 /* min */, -1 /* max */, 25 /* ms */);
+
+        for (int i = 0; i < 4; i++) {
+            final TaskRecord task = mTasks.get(i);
+            task.touchActiveTime();
+            mRecentTasks.add(task);
+        }
+
+        // Force a small sleep just beyond the session duration
+        SystemClock.sleep(50);
+
+        // Add a new task to trigger tasks to be trimmed
+        mRecentTasks.add(mTasks.get(4));
+
+        // Ensure that there are a minimum number of tasks regardless of session length
+        assertNoTasksTrimmed();
+    }
+
+    @Test
+    public void testVisibleTasks_maxNum() {
+        mRecentTasks.setOnlyTestVisibleRange();
+        mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */);
+
+        for (int i = 0; i < 5; i++) {
+            final TaskRecord task = mTasks.get(i);
+            task.touchActiveTime();
+            mRecentTasks.add(task);
+        }
+
+        // Ensure that only the last number of max tasks are kept
+        assertTrimmed(mTasks.get(0), mTasks.get(1));
+    }
+
+    @Test
+    public void testBackStackTasks_expectNoTrim() {
+        mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
+
+        final MyTestActivityStackSupervisor supervisor =
+                (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
+        final ActivityStack homeStack = mDisplay.getHomeStack();
+        final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+
+        // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
+        // the tasks belong in stacks above the home stack
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task1").setStack(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setStack(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task3").setStack(aboveHomeStack).build());
+
+        assertNoTasksTrimmed();
+    }
+
+    @Test
+    public void testBehindHomeStackTasks_expectTaskTrimmed() {
+        mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
+
+        final MyTestActivityStackSupervisor supervisor =
+                (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
+        final ActivityStack behindHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+        final ActivityStack homeStack = mDisplay.getHomeStack();
+        final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+
+        // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
+        // the home stack is trimmed once a new task is added
+        final TaskRecord behindHomeTask = createTaskBuilder(".Task1")
+                .setStack(behindHomeStack)
+                .build();
+        mRecentTasks.add(behindHomeTask);
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setStack(aboveHomeStack).build());
+
+        assertTrimmed(behindHomeTask);
+    }
+
+    @Test
+    public void testOtherDisplayTasks_expectNoTrim() {
+        mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
+
+        final MyTestActivityStackSupervisor supervisor =
+                (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
+        final ActivityStack homeStack = mDisplay.getHomeStack();
+        final ActivityStack otherDisplayStack = new MyTestActivityStack(mOtherDisplay, supervisor);
+
+        // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
+        // removed
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task1").setStack(otherDisplayStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setStack(otherDisplayStack).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask2").setStack(homeStack).build());
+
+        assertNoTasksTrimmed();
+    }
+
+    @Test
+    public void testRemovePackageByName() {
+        // Add a number of tasks with the same package name
+        mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".Task1").build());
+        mRecentTasks.add(createTaskBuilder("com.android.pkg2", ".Task2").build());
+        mRecentTasks.add(createTaskBuilder("com.android.pkg3", ".Task3").build());
+        mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".Task4").build());
+        mRecentTasks.removeTasksByPackageName("com.android.pkg1", TEST_USER_0_ID);
+
+        final ArrayList<TaskRecord> tasks = mRecentTasks.getRawTasks();
+        for (int i = 0; i < tasks.size(); i++) {
+            if (tasks.get(i).intent.getComponent().getPackageName().equals("com.android.pkg1")) {
+                fail("Expected com.android.pkg1 tasks to be removed");
+            }
+        }
+    }
+
+    @Test
+    public void testRemoveAllVisibleTasks() {
+        mRecentTasks.setParameters(-1 /* min */, 3 /* max */, 100 /* ms */);
+
+        // Create some set of tasks, some of which are visible and some are not
+        TaskRecord t1 = createTaskBuilder("com.android.pkg1", ".Task1").build();
+        mRecentTasks.add(t1);
+        mRecentTasks.add(setTaskActivityType(
+                createTaskBuilder("com.android.pkg1", ".HomeTask").build(),
+                ACTIVITY_TYPE_HOME));
+        TaskRecord t2 = createTaskBuilder("com.android.pkg2", ".Task2").build();
+        mRecentTasks.add(t2);
+        mRecentTasks.add(setTaskWindowingMode(
+                createTaskBuilder("com.android.pkg1", ".PipTask").build(),
+                WINDOWING_MODE_PINNED));
+        TaskRecord t3 = createTaskBuilder("com.android.pkg3", ".Task3").build();
+        mRecentTasks.add(t3);
+
+        // Create some more tasks that are out of visible range, but are still visible
+        TaskRecord t4 = createTaskBuilder("com.android.pkg3", ".Task4").build();
+        mRecentTasks.add(t4);
+        TaskRecord t5 = createTaskBuilder("com.android.pkg3", ".Task5").build();
+        mRecentTasks.add(t5);
+
+        // Create some more tasks that are out of the active session range, but are still visible
+        TaskRecord t6 = createTaskBuilder("com.android.pkg3", ".Task6").build();
+        t6.lastActiveTime = SystemClock.elapsedRealtime() - 200;
+        mRecentTasks.add(t6);
+        TaskRecord t7 = createTaskBuilder("com.android.pkg3", ".Task7").build();
+        t7.lastActiveTime = SystemClock.elapsedRealtime() - 200;
+        mRecentTasks.add(t7);
+
+        // Remove all the visible tasks and ensure that they are removed
+        mRecentTasks.removeAllVisibleTasks();
+        assertTrimmed(t1, t2, t3, t4, t5, t6, t7);
+    }
+
+    @Test
+    public void testNotRestoreRecentTaskApis() {
+        final TaskRecord task = createTaskBuilder(".Task").build();
+        final int taskId = task.taskId;
+        mRecentTasks.add(task);
+        // Only keep the task in RecentTasks.
+        task.removeWindowContainer();
+        mStack.remove();
+
+        // The following APIs should not restore task from recents to the active list.
+        assertNotRestoreTask(() -> mTestService.setFocusedTask(taskId));
+        assertNotRestoreTask(() -> mTestService.startSystemLockTaskMode(taskId));
+        assertNotRestoreTask(() -> mTestService.cancelTaskWindowTransition(taskId));
+        assertNotRestoreTask(
+                () -> mTestService.resizeTask(taskId, null /* bounds */, 0 /* resizeMode */));
+        assertNotRestoreTask(
+                () -> mTestService.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
+                        false/* toTop */));
+        assertNotRestoreTask(
+                () -> mTestService.setTaskWindowingModeSplitScreenPrimary(taskId,
+                        SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
+                        false /* toTop */, false /* animate */, null /* initialBounds */,
+                        true /* showRecents */));
+    }
+
+    private void assertNotRestoreTask(Runnable action) {
+        // Verify stack count doesn't change because task with fullscreen mode and standard type
+        // would have its own stack.
+        final int orignalStackCount = mDisplay.getChildCount();
+        action.run();
+        assertEquals(orignalStackCount, mDisplay.getChildCount());
+    }
+
+    @Test
+    public void testNotRecentsComponent_denyApiAccess() throws Exception {
+        doReturn(PackageManager.PERMISSION_DENIED).when(mTestService)
+                .checkGetTasksPermission(anyString(), anyInt(), anyInt());
+        // Expect the following methods to fail due to recents component not being set
+        mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
+        doTestRecentTasksApis(false /* expectNoSecurityException */);
+        // Don't throw for the following tests
+        mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY);
+        testGetTasksApis(false /* expectNoSecurityException */);
+    }
+
+    @Test
+    public void testRecentsComponent_allowApiAccessWithoutPermissions() {
+        doReturn(PackageManager.PERMISSION_DENIED).when(mTestService)
+                .checkGetTasksPermission(anyString(), anyInt(), anyInt());
+
+        // Set the recents component and ensure that the following calls do not fail
+        mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.GRANT);
+        doTestRecentTasksApis(true /* expectNoSecurityException */);
+        testGetTasksApis(true /* expectNoSecurityException */);
+    }
+
+    private void doTestRecentTasksApis(boolean expectCallable) {
+        assertSecurityException(expectCallable, () -> mTestService.removeStack(INVALID_STACK_ID));
+        assertSecurityException(expectCallable,
+                () -> mTestService.removeStacksInWindowingModes(
+                        new int[]{WINDOWING_MODE_UNDEFINED}));
+        assertSecurityException(expectCallable,
+                () -> mTestService.removeStacksWithActivityTypes(
+                        new int[]{ACTIVITY_TYPE_UNDEFINED}));
+        assertSecurityException(expectCallable, () -> mTestService.removeTask(0));
+        assertSecurityException(expectCallable,
+                () -> mTestService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
+        assertSecurityException(expectCallable,
+                () -> mTestService.moveTaskToStack(0, INVALID_STACK_ID, true));
+        assertSecurityException(expectCallable,
+                () -> mTestService.setTaskWindowingModeSplitScreenPrimary(0,
+                        SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
+        assertSecurityException(expectCallable, () -> mTestService.dismissSplitScreenMode(true));
+        assertSecurityException(expectCallable, () -> mTestService.dismissPip(true, 0));
+        assertSecurityException(expectCallable,
+                () -> mTestService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
+        assertSecurityException(expectCallable,
+                () -> mTestService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
+        assertSecurityException(expectCallable,
+                () -> mTestService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
+                        new Rect()));
+        assertSecurityException(expectCallable,
+                () -> mTestService.resizePinnedStack(new Rect(), new Rect()));
+        assertSecurityException(expectCallable, () -> mTestService.getAllStackInfos());
+        assertSecurityException(expectCallable,
+                () -> mTestService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
+        assertSecurityException(expectCallable, () -> {
+            try {
+                mTestService.getFocusedStackInfo();
+            } catch (RemoteException e) {
+                // Ignore
+            }
+        });
+        assertSecurityException(expectCallable,
+                () -> mTestService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
+        assertSecurityException(expectCallable,
+                () -> mTestService.startActivityFromRecents(0, new Bundle()));
+        assertSecurityException(expectCallable, () -> mTestService.getTaskSnapshot(0, true));
+        assertSecurityException(expectCallable, () -> mTestService.registerTaskStackListener(null));
+        assertSecurityException(expectCallable,
+                () -> mTestService.unregisterTaskStackListener(null));
+        assertSecurityException(expectCallable, () -> mTestService.getTaskDescription(0));
+        assertSecurityException(expectCallable, () -> mTestService.cancelTaskWindowTransition(0));
+        assertSecurityException(expectCallable, () -> mTestService.startRecentsActivity(null, null,
+                null));
+        assertSecurityException(expectCallable, () -> mTestService.cancelRecentsAnimation(true));
+        assertSecurityException(expectCallable, () -> mTestService.stopAppSwitches());
+        assertSecurityException(expectCallable, () -> mTestService.resumeAppSwitches());
+    }
+
+    private void testGetTasksApis(boolean expectCallable) {
+        mTestService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
+        mTestService.getTasks(MAX_VALUE);
+        if (expectCallable) {
+            assertTrue(mRecentTasks.mLastAllowed);
+            assertTrue(mRunningTasks.mLastAllowed);
+        } else {
+            assertFalse(mRecentTasks.mLastAllowed);
+            assertFalse(mRunningTasks.mLastAllowed);
+        }
+    }
+
+    private TaskBuilder createTaskBuilder(String className) {
+        return createTaskBuilder(mContext.getPackageName(), className);
+    }
+
+    private TaskBuilder createTaskBuilder(String packageName, String className) {
+        return new TaskBuilder(mTestService.mStackSupervisor)
+                .setComponent(new ComponentName(packageName, className))
+                .setStack(mStack)
+                .setTaskId(sLastTaskId++)
+                .setUserId(TEST_USER_0_ID);
+    }
+
+    private TaskRecord createDocumentTask(String className) {
+        return createDocumentTask(className, 0);
+    }
+
+    private TaskRecord createDocumentTask(String className, int flags) {
+        TaskRecord task = createTaskBuilder(className)
+                .setFlags(FLAG_ACTIVITY_NEW_DOCUMENT | flags)
+                .build();
+        task.affinity = null;
+        task.maxRecents = ActivityTaskManager.getMaxAppRecentsLimitStatic();
+        return task;
+    }
+
+    private TaskRecord setTaskActivityType(TaskRecord task,
+            @WindowConfiguration.ActivityType int activityType) {
+        Configuration config1 = new Configuration();
+        config1.windowConfiguration.setActivityType(activityType);
+        task.onConfigurationChanged(config1);
+        return task;
+    }
+
+    private TaskRecord setTaskWindowingMode(TaskRecord task,
+            @WindowConfiguration.WindowingMode int windowingMode) {
+        Configuration config1 = new Configuration();
+        config1.windowConfiguration.setWindowingMode(windowingMode);
+        task.onConfigurationChanged(config1);
+        return task;
+    }
+
+    private void assertNoTasksTrimmed() {
+        assertTrimmed();
+    }
+
+    private void assertTrimmed(TaskRecord... tasks) {
+        final ArrayList<TaskRecord> trimmed = mCallbacksRecorder.mTrimmed;
+        final ArrayList<TaskRecord> removed = mCallbacksRecorder.mRemoved;
+        assertWithMessage("Expected " + tasks.length + " trimmed tasks, got " + trimmed.size())
+                .that(trimmed).hasSize(tasks.length);
+        assertWithMessage("Expected " + tasks.length + " removed tasks, got " + removed.size())
+                .that(removed).hasSize(tasks.length);
+        for (TaskRecord task : tasks) {
+            assertWithMessage("Expected trimmed task: " + task).that(trimmed).contains(task);
+            assertWithMessage("Expected removed task: " + task).that(removed).contains(task);
+        }
+    }
+
+    private void assertSecurityException(boolean expectCallable, Runnable runnable) {
+        boolean noSecurityException = true;
+        try {
+            runnable.run();
+        } catch (SecurityException se) {
+            noSecurityException = false;
+        } catch (Exception e) {
+            // We only care about SecurityExceptions, fall through here.
+        }
+        if (noSecurityException != expectCallable) {
+            fail("Expected callable: " + expectCallable + " but got no security exception: "
+                    + noSecurityException);
+        }
+    }
+
+    private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
+        MyTestActivityTaskManagerService(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected RecentTasks createRecentTasks() {
+            return new TestRecentTasks(this, mTaskPersister);
+        }
+
+        @Override
+        protected ActivityStackSupervisor createTestSupervisor() {
+            return new MyTestActivityStackSupervisor(this, mH.getLooper());
+        }
+
+    }
+
+    private class MyTestActivityManagerService extends TestActivityManagerService {
+        MyTestActivityManagerService() {
+            super(mTestInjector);
+        }
+
+        @Override
+        public boolean isUserRunning(int userId, int flags) {
+            return true;
+        }
+    }
+
+    private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor {
+        MyTestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+            super(service, looper);
+        }
+
+        @Override
+        public void initialize() {
+            super.initialize();
+            mDisplay = getActivityDisplay(DEFAULT_DISPLAY);
+            mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1);
+            addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
+            addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+        }
+
+        @Override
+        RunningTasks createRunningTasks() {
+            mRunningTasks = new TestRunningTasks();
+            return mRunningTasks;
+        }
+    }
+
+    private static class MyTestActivityStack extends TestActivityStack {
+        private ActivityDisplay mDisplay = null;
+
+        MyTestActivityStack(ActivityDisplay display, ActivityStackSupervisor supervisor) {
+            super(display, sLastStackId++, supervisor, WINDOWING_MODE_FULLSCREEN,
+                    ACTIVITY_TYPE_STANDARD, true /* onTop */, false /* createActivity */);
+            mDisplay = display;
+        }
+
+        @Override
+        ActivityDisplay getDisplay() {
+            if (mDisplay != null) {
+                return mDisplay;
+            }
+            return super.getDisplay();
+        }
+    }
+
+    private static class CallbacksRecorder implements Callbacks {
+        public final ArrayList<TaskRecord> mAdded = new ArrayList<>();
+        public final ArrayList<TaskRecord> mTrimmed = new ArrayList<>();
+        public final ArrayList<TaskRecord> mRemoved = new ArrayList<>();
+
+        void clear() {
+            mAdded.clear();
+            mTrimmed.clear();
+            mRemoved.clear();
+        }
+
+        @Override
+        public void onRecentTaskAdded(TaskRecord task) {
+            mAdded.add(task);
+        }
+
+        @Override
+        public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
+            if (wasTrimmed) {
+                mTrimmed.add(task);
+            }
+            mRemoved.add(task);
+        }
+    }
+
+    private static class TestTaskPersister extends TaskPersister {
+        public SparseBooleanArray mUserTaskIdsOverride;
+        public ArrayList<TaskRecord> mUserTasksOverride;
+
+        TestTaskPersister(File workingDir) {
+            super(workingDir);
+        }
+
+        @Override
+        SparseBooleanArray loadPersistedTaskIdsForUser(int userId) {
+            if (mUserTaskIdsOverride != null) {
+                return mUserTaskIdsOverride;
+            }
+            return super.loadPersistedTaskIdsForUser(userId);
+        }
+
+        @Override
+        List<TaskRecord> restoreTasksForUserLocked(int userId, SparseBooleanArray preaddedTasks) {
+            if (mUserTasksOverride != null) {
+                return mUserTasksOverride;
+            }
+            return super.restoreTasksForUserLocked(userId, preaddedTasks);
+        }
+    }
+
+    private static class TestRecentTasks extends RecentTasks {
+        static final int GRANT = 0;
+        static final int DENY = 1;
+        static final int DENY_THROW_SECURITY_EXCEPTION = 2;
+
+        private boolean mOverrideIsCallerRecents;
+        private boolean mIsTrimmableOverride;
+        private int mIsCallerRecentsPolicy;
+
+        public boolean mLastAllowed;
+
+        TestRecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) {
+            super(service, taskPersister);
+        }
+
+        @Override
+        Set<Integer> getProfileIds(int userId) {
+            Set<Integer> profileIds = new HashSet<>();
+            profileIds.add(TEST_USER_0_ID);
+            profileIds.add(TEST_QUIET_USER_ID);
+            return profileIds;
+        }
+
+        @Override
+        UserInfo getUserInfo(int userId) {
+            switch (userId) {
+                case TEST_USER_0_ID:
+                case TEST_USER_1_ID:
+                    return DEFAULT_USER_INFO;
+                case TEST_QUIET_USER_ID:
+                    return QUIET_USER_INFO;
+            }
+            return null;
+        }
+
+        @Override
+        int[] getCurrentProfileIds() {
+            return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID };
+        }
+
+        @Override
+        boolean isCallerRecents(int callingUid) {
+            if (mOverrideIsCallerRecents) {
+                switch (mIsCallerRecentsPolicy) {
+                    case GRANT:
+                        return true;
+                    case DENY:
+                        return false;
+                    case DENY_THROW_SECURITY_EXCEPTION:
+                        throw new SecurityException();
+                }
+            }
+            return super.isCallerRecents(callingUid);
+        }
+
+        void setIsCallerRecentsOverride(int policy) {
+            mOverrideIsCallerRecents = true;
+            mIsCallerRecentsPolicy = policy;
+        }
+
+        /**
+         * To simplify the setup for some tests, the caller can request that we only rely on the
+         * visible range test to determine what is trimmable. In this case, we don't try to
+         * use the stack order to determine additionally if the task is trimmable when it is not
+         * in the visible range.
+         */
+        void setOnlyTestVisibleRange() {
+            mIsTrimmableOverride = true;
+        }
+
+        @Override
+        ParceledListSlice<RecentTaskInfo> getRecentTasks(int maxNum, int flags,
+                boolean getTasksAllowed,
+                boolean getDetailedTasks, int userId, int callingUid) {
+            mLastAllowed = getTasksAllowed;
+            return super.getRecentTasks(maxNum, flags, getTasksAllowed, getDetailedTasks, userId,
+                    callingUid);
+        }
+
+        @Override
+        protected boolean isTrimmable(TaskRecord task) {
+            return mIsTrimmableOverride || super.isTrimmable(task);
+        }
+    }
+
+    private static class TestRunningTasks extends RunningTasks {
+        public boolean mLastAllowed;
+
+        @Override
+        void getTasks(int maxNum, List<RunningTaskInfo> list, int ignoreActivityType,
+                int ignoreWindowingMode, ArrayList<ActivityDisplay> activityDisplays,
+                int callingUid, boolean allowed) {
+            mLastAllowed = allowed;
+            super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, activityDisplays,
+                    callingUid, allowed);
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
new file mode 100644
index 0000000..a01a3d9
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.platform.test.annotations.Presubmit;
+import android.view.IRecentsAnimationRunner;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:RecentsAnimationTest
+ */
+@MediumTest
+@Presubmit
+public class RecentsAnimationTest extends ActivityTestsBase {
+
+    private Context mContext = InstrumentationRegistry.getContext();
+    private TestActivityTaskManagerService mTestService;
+    private ComponentName mRecentsComponent;
+
+    @Before
+    public void setUp() throws Exception {
+        mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
+        mTestService = spy(new MyTestActivityTaskManagerService(mContext));
+        setupActivityManagerService(mTestService);
+    }
+
+    @Test
+    public void testCancelAnimationOnStackOrderChange() {
+        ActivityStack fullscreenStack =
+                mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        ActivityStack recentsStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
+        ActivityRecord recentsActivity = new ActivityBuilder(mTestService)
+                .setComponent(mRecentsComponent)
+                .setCreateTask(true)
+                .setStack(recentsStack)
+                .build();
+        ActivityStack fullscreenStack2 =
+                mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        ActivityRecord fsActivity = new ActivityBuilder(mTestService)
+                .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
+                .setCreateTask(true)
+                .setStack(fullscreenStack2)
+                .build();
+        doReturn(true).when(mTestService.mWindowManager).canStartRecentsAnimation();
+
+        // Start the recents animation
+        Intent recentsIntent = new Intent();
+        recentsIntent.setComponent(mRecentsComponent);
+        mTestService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
+
+        fullscreenStack.moveToFront("Activity start");
+
+        // Ensure that the recents animation was canceled
+        verify(mTestService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+                eq(REORDER_KEEP_IN_PLACE), any());
+    }
+
+    private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
+        MyTestActivityTaskManagerService(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected RecentTasks createRecentTasks() {
+            RecentTasks recents = mock(RecentTasks.class);
+            doReturn(mRecentsComponent).when(recents).getRecentsComponent();
+            System.out.println(mRecentsComponent);
+            return recents;
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
new file mode 100644
index 0000000..0e1624e
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.wm.ActivityDisplay.POSITION_BOTTOM;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.ComponentName;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:RunningTasksTest
+ */
+@MediumTest
+@Presubmit
+public class RunningTasksTest extends ActivityTestsBase {
+
+    private RunningTasks mRunningTasks;
+
+    @Before
+    public void setUp() throws Exception {
+        setupActivityTaskManagerService();
+        mRunningTasks = new RunningTasks();
+    }
+
+    @Test
+    public void testCollectTasksByLastActiveTime() {
+        // Create a number of stacks with tasks (of incrementing active time)
+        final ArrayList<ActivityDisplay> displays = new ArrayList<>();
+        final ActivityDisplay display = TestActivityDisplay.create(mSupervisor, DEFAULT_DISPLAY);
+        displays.add(display);
+
+        final int numStacks = 2;
+        for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
+            final ActivityStack stack =
+                    new StackBuilder(mSupervisor).setCreateActivity(false).build();
+            display.addChild(stack, POSITION_BOTTOM);
+        }
+
+        final int numTasks = 10;
+        int activeTime = 0;
+        for (int i = 0; i < numTasks; i++) {
+            createTask(display.getChildAt(i % numStacks), ".Task" + i, i, activeTime++);
+        }
+
+        // Ensure that the latest tasks were returned in order of decreasing last active time,
+        // collected from all tasks across all the stacks
+        final int numFetchTasks = 5;
+        ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
+        mRunningTasks.getTasks(5, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
+                displays, -1 /* callingUid */, true /* allowed */);
+        assertThat(tasks).hasSize(numFetchTasks);
+        for (int i = 0; i < numFetchTasks; i++) {
+            assertEquals(numTasks - i - 1, tasks.get(i).id);
+        }
+
+        // Ensure that requesting more than the total number of tasks only returns the subset
+        // and does not crash
+        tasks.clear();
+        mRunningTasks.getTasks(100, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
+                displays, -1 /* callingUid */, true /* allowed */);
+        assertThat(tasks).hasSize(numTasks);
+        for (int i = 0; i < numTasks; i++) {
+            assertEquals(numTasks - i - 1, tasks.get(i).id);
+        }
+    }
+
+    /**
+     * Create a task with a single activity in it, with the given last active time.
+     */
+    private TaskRecord createTask(ActivityStack stack, String className, int taskId,
+            int lastActiveTime) {
+        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+                .setComponent(new ComponentName(mContext.getPackageName(), className))
+                .setTaskId(taskId)
+                .setStack(stack)
+                .build();
+        task.lastActiveTime = lastActiveTime;
+        final ActivityRecord activity = new ActivityBuilder(mService)
+                .setTask(task)
+                .setComponent(new ComponentName(mContext.getPackageName(), ".TaskActivity"))
+                .build();
+        return task;
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
new file mode 100644
index 0000000..530fd6d
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.ActivityOptions;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:SafeActivityOptionsTest
+ */
+@MediumTest
+@FlakyTest
+@Presubmit
+public class SafeActivityOptionsTest {
+
+    @Test
+    public void testMerge() {
+        final ActivityOptions opts1 = ActivityOptions.makeBasic();
+        opts1.setLaunchDisplayId(5);
+        final ActivityOptions opts2 = ActivityOptions.makeBasic();
+        opts2.setLaunchDisplayId(6);
+        final SafeActivityOptions options = new SafeActivityOptions(opts1);
+        final ActivityOptions result = options.mergeActivityOptions(opts1, opts2);
+        assertEquals(6, result.getLaunchDisplayId());
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
new file mode 100644
index 0000000..36eccd1
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.graphics.Color.RED;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Gravity.BOTTOM;
+import static android.view.Gravity.LEFT;
+import static android.view.Gravity.RIGHT;
+import static android.view.Gravity.TOP;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.function.BooleanSupplier;
+
+/**
+ * Tests for the {@link android.view.WindowManager.LayoutParams#PRIVATE_FLAG_IS_SCREEN_DECOR} flag.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ScreenDecorWindowTests
+ */
+// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags.
+// TODO: Test non-Activity windows.
+@SmallTest
+@Presubmit
+public class ScreenDecorWindowTests {
+
+    private final Context mContext = InstrumentationRegistry.getTargetContext();
+    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+
+    private WindowManager mWm;
+    private ArrayList<View> mWindows = new ArrayList<>();
+
+    private Activity mTestActivity;
+    private VirtualDisplay mDisplay;
+    private ImageReader mImageReader;
+
+    private int mDecorThickness;
+    private int mHalfDecorThickness;
+
+    @Before
+    public void setUp() {
+        final Pair<VirtualDisplay, ImageReader> result = createDisplay();
+        mDisplay = result.first;
+        mImageReader = result.second;
+        final Display display = mDisplay.getDisplay();
+        final Context dContext = mContext.createDisplayContext(display);
+        mWm = dContext.getSystemService(WindowManager.class);
+        mTestActivity = startActivityOnDisplay(TestActivity.class, display.getDisplayId());
+        final Point size = new Point();
+        mDisplay.getDisplay().getRealSize(size);
+        mDecorThickness = Math.min(size.x, size.y) / 3;
+        mHalfDecorThickness = mDecorThickness / 2;
+    }
+
+    @After
+    public void tearDown() {
+        while (!mWindows.isEmpty()) {
+            removeWindow(mWindows.get(0));
+        }
+        finishActivity(mTestActivity);
+        mDisplay.release();
+        mImageReader.close();
+    }
+
+    @Test
+    public void testScreenSides() {
+        // Decor on top
+        final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
+        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
+
+        // Decor at the bottom
+        updateWindow(decorWindow, BOTTOM, MATCH_PARENT, mDecorThickness, 0, 0);
+        assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mDecorThickness);
+
+        // Decor to the left
+        updateWindow(decorWindow, LEFT, mDecorThickness, MATCH_PARENT, 0, 0);
+        assertInsetGreaterOrEqual(mTestActivity, LEFT, mDecorThickness);
+
+        // Decor to the right
+        updateWindow(decorWindow, RIGHT, mDecorThickness, MATCH_PARENT, 0, 0);
+        assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness);
+    }
+
+    @Test
+    public void testMultipleDecors() {
+        // Test 2 decor windows on-top.
+        createDecorWindow(TOP, MATCH_PARENT, mHalfDecorThickness);
+        assertInsetGreaterOrEqual(mTestActivity, TOP, mHalfDecorThickness);
+        createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
+        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
+
+        // And one at the bottom.
+        createDecorWindow(BOTTOM, MATCH_PARENT, mHalfDecorThickness);
+        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
+        assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mHalfDecorThickness);
+    }
+
+    @Test
+    public void testFlagChange() {
+        WindowInsets initialInsets = getInsets(mTestActivity);
+
+        final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
+        assertTopInsetEquals(mTestActivity, mDecorThickness);
+
+        updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
+                0, PRIVATE_FLAG_IS_SCREEN_DECOR);
+
+        // TODO: fix test and re-enable assertion.
+        // initialInsets was not actually immutable and just updated to the current insets,
+        // meaning this assertion never actually tested anything. Now that WindowInsets actually is
+        // immutable, it turns out the test was broken.
+        // assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
+
+        updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
+                PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_IS_SCREEN_DECOR);
+        assertTopInsetEquals(mTestActivity, mDecorThickness);
+    }
+
+    @Test
+    public void testRemoval() {
+        WindowInsets initialInsets = getInsets(mTestActivity);
+
+        final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
+        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
+
+        removeWindow(decorWindow);
+        assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
+    }
+
+    private View createDecorWindow(int gravity, int width, int height) {
+        return createWindow("decorWindow", gravity, width, height, RED,
+                FLAG_LAYOUT_IN_SCREEN, PRIVATE_FLAG_IS_SCREEN_DECOR);
+    }
+
+    private View createWindow(String name, int gravity, int width, int height, int color, int flags,
+            int privateFlags) {
+
+        final View[] viewHolder = new View[1];
+        final int finalFlag = flags
+                | FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_TOUCHABLE;
+
+        // Needs to run on the UI thread.
+        Handler.getMain().runWithScissors(() -> {
+            final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                    width, height, TYPE_APPLICATION_OVERLAY, finalFlag, PixelFormat.OPAQUE);
+            lp.gravity = gravity;
+            lp.privateFlags |= privateFlags;
+
+            final TextView view = new TextView(mContext);
+            view.setText("ScreenDecorWindowTests - " + name);
+            view.setBackgroundColor(color);
+            mWm.addView(view, lp);
+            mWindows.add(view);
+            viewHolder[0] = view;
+        }, 0);
+
+        waitForIdle();
+        return viewHolder[0];
+    }
+
+    private void updateWindow(View v, int gravity, int width, int height,
+            int privateFlags, int privateFlagsMask) {
+        // Needs to run on the UI thread.
+        Handler.getMain().runWithScissors(() -> {
+            final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) v.getLayoutParams();
+            lp.gravity = gravity;
+            lp.width = width;
+            lp.height = height;
+            setPrivateFlags(lp, privateFlags, privateFlagsMask);
+
+            mWm.updateViewLayout(v, lp);
+        }, 0);
+
+        waitForIdle();
+    }
+
+    private void removeWindow(View v) {
+        Handler.getMain().runWithScissors(() -> mWm.removeView(v), 0);
+        mWindows.remove(v);
+        waitForIdle();
+    }
+
+    private WindowInsets getInsets(Activity a) {
+        return new WindowInsets(a.getWindow().getDecorView().getRootWindowInsets());
+    }
+
+    /**
+     * Set the flags of the window, as per the
+     * {@link WindowManager.LayoutParams WindowManager.LayoutParams}
+     * flags.
+     *
+     * @param flags The new window flags (see WindowManager.LayoutParams).
+     * @param mask Which of the window flag bits to modify.
+     */
+    public void setPrivateFlags(WindowManager.LayoutParams lp, int flags, int mask) {
+        lp.flags = (lp.flags & ~mask) | (flags & mask);
+    }
+
+    /**
+     * Asserts the top inset of {@param activity} is equal to {@param expected} waiting as needed.
+     */
+    private void assertTopInsetEquals(Activity activity, int expected) {
+        waitForTopInsetEqual(activity, expected);
+        assertEquals(expected, getInsets(activity).getSystemWindowInsetTop());
+    }
+
+    private void waitForTopInsetEqual(Activity activity, int expected) {
+        waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected);
+    }
+
+    /**
+     * Asserts the inset at {@param side} of {@param activity} is equal to {@param expected}
+     * waiting as needed.
+     */
+    private void assertInsetGreaterOrEqual(Activity activity, int side, int expected) {
+        waitForInsetGreaterOrEqual(activity, side, expected);
+
+        final WindowInsets insets = getInsets(activity);
+        switch (side) {
+            case TOP:
+                assertThat(insets.getSystemWindowInsetTop()).isAtLeast(expected);
+                break;
+            case BOTTOM:
+                assertThat(insets.getSystemWindowInsetBottom()).isAtLeast(expected);
+                break;
+            case LEFT:
+                assertThat(insets.getSystemWindowInsetLeft()).isAtLeast(expected);
+                break;
+            case RIGHT:
+                assertThat(insets.getSystemWindowInsetRight()).isAtLeast(expected);
+                break;
+        }
+    }
+
+    private void waitForInsetGreaterOrEqual(Activity activity, int side, int expected) {
+        waitFor(() -> {
+            final WindowInsets insets = getInsets(activity);
+            switch (side) {
+                case TOP: return insets.getSystemWindowInsetTop() >= expected;
+                case BOTTOM: return insets.getSystemWindowInsetBottom() >= expected;
+                case LEFT: return insets.getSystemWindowInsetLeft() >= expected;
+                case RIGHT: return insets.getSystemWindowInsetRight() >= expected;
+                default: return true;
+            }
+        });
+    }
+
+    private void waitFor(BooleanSupplier waitCondition) {
+        int retriesLeft = 5;
+        do {
+            if (waitCondition.getAsBoolean()) {
+                break;
+            }
+            SystemClock.sleep(500);
+        } while (retriesLeft-- > 0);
+    }
+
+    private void finishActivity(Activity a) {
+        if (a == null) {
+            return;
+        }
+        a.finish();
+        waitForIdle();
+    }
+
+    private void waitForIdle() {
+        mInstrumentation.waitForIdleSync();
+    }
+
+    private Activity startActivityOnDisplay(Class<?> cls, int displayId) {
+        final Intent intent = new Intent(mContext, cls);
+        intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(displayId);
+        final Activity activity = mInstrumentation.startActivitySync(intent, options.toBundle());
+        waitForIdle();
+
+        assertEquals(displayId, activity.getDisplayId());
+        return activity;
+    }
+
+    private Pair<VirtualDisplay, ImageReader> createDisplay() {
+        final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+        final DisplayInfo displayInfo = new DisplayInfo();
+        final Display defaultDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+        defaultDisplay.getDisplayInfo(displayInfo);
+        final String name = "ScreenDecorWindowTests";
+        int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+
+        final ImageReader imageReader = ImageReader.newInstance(
+                displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
+
+        final VirtualDisplay display = dm.createVirtualDisplay(name, displayInfo.logicalWidth,
+                displayInfo.logicalHeight, displayInfo.logicalDensityDpi, imageReader.getSurface(),
+                flags);
+
+        return Pair.create(display, imageReader);
+    }
+
+    public static class TestActivity extends Activity {
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
new file mode 100644
index 0000000..95965c8
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -0,0 +1,1103 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Build;
+import android.platform.test.annotations.Presubmit;
+import android.view.Gravity;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.LaunchParamsController.LaunchParams;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Locale;
+
+/**
+ * Tests for default task bounds.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:TaskLaunchParamsModifierTests
+ */
+@SmallTest
+@FlakyTest(detail = "Confirm stable in post-submit before removing")
+@Presubmit
+public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
+
+    private ActivityRecord mActivity;
+
+    private TaskLaunchParamsModifier mTarget;
+
+    private LaunchParams mCurrent;
+    private LaunchParams mResult;
+
+    @Before
+    public void setUp() throws Exception {
+        setupActivityTaskManagerService();
+        mService.mSupportsFreeformWindowManagement = true;
+        when(mSupervisor.canUseActivityOptionsLaunchBounds(any())).thenCallRealMethod();
+
+        mActivity = new ActivityBuilder(mService).build();
+        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+        mActivity.info.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
+
+        mTarget = new TaskLaunchParamsModifier(mSupervisor);
+
+        mCurrent = new LaunchParams();
+        mCurrent.reset();
+        mResult = new LaunchParams();
+        mResult.reset();
+    }
+
+    @Test
+    public void testReturnsSkipWithEmptyActivity() {
+        final TaskRecord task = new TaskBuilder(mSupervisor).build();
+        assertEquals(RESULT_SKIP, mTarget.onCalculate(task, /* layout */ null,
+                /* activity */ null, /* source */ null, /* options */ null, mCurrent, mResult));
+    }
+
+    // =============================
+    // Display ID Related Tests
+    // =============================
+    @Test
+    public void testDefaultToPrimaryDisplay() {
+        createNewActivityDisplay(WINDOWING_MODE_FREEFORM);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(DEFAULT_DISPLAY, mResult.mPreferredDisplayId);
+    }
+
+    @Test
+    public void testUsesPreviousDisplayIdIfSet() {
+        createNewActivityDisplay(WINDOWING_MODE_FREEFORM);
+        final TestActivityDisplay display = createNewActivityDisplay(WINDOWING_MODE_FULLSCREEN);
+
+        mCurrent.mPreferredDisplayId = display.mDisplayId;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(display.mDisplayId, mResult.mPreferredDisplayId);
+    }
+
+    @Test
+    public void testUsesSourcesDisplayIdIfSet() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FULLSCREEN);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        ActivityRecord source = createSourceActivity(fullscreenDisplay);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, source, /* options */ null, mCurrent, mResult));
+
+        assertEquals(fullscreenDisplay.mDisplayId, mResult.mPreferredDisplayId);
+    }
+
+    @Test
+    public void testUsesOptionsDisplayIdIfSet() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FULLSCREEN);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+        ActivityRecord source = createSourceActivity(freeformDisplay);
+
+        ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(fullscreenDisplay.mDisplayId);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, source, options, mCurrent, mResult));
+
+        assertEquals(fullscreenDisplay.mDisplayId, mResult.mPreferredDisplayId);
+    }
+
+    @Test
+    public void testUsesTaskDisplayIdIfSet() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+        ActivityRecord source = createSourceActivity(freeformDisplay);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(source.getTask(), null /* layout */,
+                null /* activity */, null /* source */, null /* options */, mCurrent, mResult));
+
+        assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId);
+    }
+
+    // =====================================
+    // Launch Windowing Mode Related Tests
+    // =====================================
+    @Test
+    public void testBoundsInOptionsInfersFreeformOnFreeformDisplay() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchBounds(new Rect(0, 0, 100, 100));
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testBoundsInOptionsInfersFreeformWithResizeableActivity() {
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchBounds(new Rect(0, 0, 100, 100));
+
+        mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+                WINDOWING_MODE_FULLSCREEN);
+    }
+
+    @Test
+    public void testKeepsPictureInPictureLaunchModeInOptions() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_PINNED);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_PINNED, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testKeepsPictureInPictureLaunchModeWithBoundsInOptions() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_PINNED);
+        options.setLaunchBounds(new Rect(0, 0, 100, 100));
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_PINNED, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testKeepsFullscreenLaunchModeInOptionsOnNonFreeformDisplay() {
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+                WINDOWING_MODE_FULLSCREEN);
+    }
+
+    @Test
+    public void testNonEmptyLayoutInfersFreeformOnFreeformDisplay() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setWidth(120).setHeight(80).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testNonEmptyLayoutInfersFreeformWithEmptySize() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setGravity(Gravity.LEFT).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testNonEmptyLayoutUsesFullscreenWithResizeableActivity() {
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setWidth(120).setHeight(80).build();
+
+        mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+                WINDOWING_MODE_FULLSCREEN);
+    }
+
+    @Test
+    public void testRespectsFullyResolvedCurrentParam_Fullscreen() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+        mCurrent.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testRespectsModeFromFullyResolvedCurrentParam_NonEmptyBounds() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
+        mCurrent.mBounds.set(0, 0, 200, 100);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testForceMaximizesPreDApp() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+        options.setLaunchBounds(new Rect(0, 0, 200, 100));
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
+        mCurrent.mBounds.set(0, 0, 200, 100);
+
+        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.CUPCAKE;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testForceMaximizesAppWithoutMultipleDensitySupport() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+        options.setLaunchBounds(new Rect(0, 0, 200, 100));
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
+        mCurrent.mBounds.set(0, 0, 200, 100);
+
+        mActivity.appInfo.flags = 0;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testForceMaximizesUnresizeableApp() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+        options.setLaunchBounds(new Rect(0, 0, 200, 100));
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
+        mCurrent.mBounds.set(0, 0, 200, 100);
+
+        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testSkipsForceMaximizingAppsOnNonFreeformDisplay() {
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+        options.setLaunchBounds(new Rect(0, 0, 200, 100));
+
+        mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
+        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
+        mCurrent.mBounds.set(0, 0, 200, 100);
+
+        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+                WINDOWING_MODE_FULLSCREEN);
+    }
+
+    @Test
+    public void testUsesFullscreenOnNonFreeformDisplay() {
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(DEFAULT_DISPLAY);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+                WINDOWING_MODE_FULLSCREEN);
+    }
+
+    @Test
+    public void testUsesFreeformByDefaultForPostNApp() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testUsesFreeformByDefaultForPreNResizeableAppWithoutOrientationRequest() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+
+        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testSkipsFreeformForPreNResizeableAppOnNonFullscreenDisplay() {
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(DEFAULT_DISPLAY);
+
+        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+                WINDOWING_MODE_FULLSCREEN);
+    }
+
+    // ================================
+    // Launching Bounds Related Tests
+    // ===============================
+    @Test
+    public void testKeepsBoundsWithPictureInPictureLaunchModeInOptions() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_PINNED);
+
+        final Rect expected = new Rect(0, 0, 100, 100);
+        options.setLaunchBounds(expected);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquals(expected, mResult.mBounds);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_LeftGravity() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setGravity(Gravity.LEFT).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(0, mResult.mBounds.left);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_TopGravity() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setGravity(Gravity.TOP).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(0, mResult.mBounds.top);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_TopLeftGravity() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setGravity(Gravity.TOP | Gravity.LEFT).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(0, mResult.mBounds.left);
+        assertEquals(0, mResult.mBounds.top);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_RightGravity() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setGravity(Gravity.RIGHT).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(1920, mResult.mBounds.right);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_BottomGravity() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setGravity(Gravity.BOTTOM).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(1080, mResult.mBounds.bottom);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_BottomRightGravity() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setGravity(Gravity.BOTTOM | Gravity.RIGHT).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(1920, mResult.mBounds.right);
+        assertEquals(1080, mResult.mBounds.bottom);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsOnFreeformDisplay_CenterToDisplay() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setWidth(120).setHeight(80).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(new Rect(900, 500, 1020, 580), mResult.mBounds);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsOnFreeformDisplay_LeftGravity() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setWidth(120).setHeight(80).setGravity(Gravity.LEFT).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(new Rect(0, 500, 120, 580), mResult.mBounds);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsOnFreeformDisplay_TopGravity() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setWidth(120).setHeight(80).setGravity(Gravity.TOP).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(new Rect(900, 0, 1020, 80), mResult.mBounds);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsOnFreeformDisplay_TopLeftGravity() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setWidth(120).setHeight(80).setGravity(Gravity.TOP | Gravity.LEFT).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(new Rect(0, 0, 120, 80), mResult.mBounds);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsOnFreeformDisplay_RightGravity() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setWidth(120).setHeight(80).setGravity(Gravity.RIGHT).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(new Rect(1800, 500, 1920, 580), mResult.mBounds);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsOnFreeformDisplay_BottomGravity() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setWidth(120).setHeight(80).setGravity(Gravity.BOTTOM).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(new Rect(900, 1000, 1020, 1080), mResult.mBounds);
+    }
+
+    @Test
+    public void testNonEmptyLayoutBoundsOnFreeformDisplay_RightBottomGravity() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setWidth(120).setHeight(80).setGravity(Gravity.BOTTOM | Gravity.RIGHT).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(new Rect(1800, 1000, 1920, 1080), mResult.mBounds);
+    }
+
+    @Test
+    public void testNonEmptyLayoutFractionBoundsOnFreeformDisplay() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setWidthFraction(0.0625f).setHeightFraction(0.1f).build();
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(new Rect(900, 486, 1020, 594), mResult.mBounds);
+    }
+
+    @Test
+    public void testRespectBoundsFromFullyResolvedCurrentParam_NonEmptyBounds() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+        mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
+        mCurrent.mBounds.set(0, 0, 200, 100);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(new Rect(0, 0, 200, 100), mResult.mBounds);
+    }
+
+    @Test
+    public void testDefaultSizeSmallerThanBigScreen() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+
+        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        final int resultWidth = mResult.mBounds.width();
+        final int displayWidth = freeformDisplay.getBounds().width();
+        assertTrue("Result width " + resultWidth + " is not smaller than " + displayWidth,
+                resultWidth < displayWidth);
+
+        final int resultHeight = mResult.mBounds.height();
+        final int displayHeight = freeformDisplay.getBounds().height();
+        assertTrue("Result width " + resultHeight + " is not smaller than "
+                        + displayHeight, resultHeight < displayHeight);
+    }
+
+    @Test
+    public void testDefaultFreeformSizeCenteredToDisplay() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+
+        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        final Rect displayBounds = freeformDisplay.getBounds();
+        assertEquals("Distance to left and right should be equal.",
+                mResult.mBounds.left - displayBounds.left,
+                displayBounds.right - mResult.mBounds.right);
+        assertEquals("Distance to top and bottom should be equal.",
+                mResult.mBounds.top - displayBounds.top,
+                displayBounds.bottom - mResult.mBounds.bottom);
+    }
+
+    @Test
+    public void testCascadesToSourceSizeForFreeform() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+
+        final ActivityRecord source = createSourceActivity(freeformDisplay);
+        source.setBounds(0, 0, 412, 732);
+
+        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, source, options, mCurrent, mResult));
+
+        final Rect displayBounds = freeformDisplay.getBounds();
+        assertTrue("Left bounds should be larger than 0.", mResult.mBounds.left > 0);
+        assertTrue("Top bounds should be larger than 0.", mResult.mBounds.top > 0);
+        assertTrue("Bounds should be centered at somewhere in the left half, but it's "
+                + "centerX is " + mResult.mBounds.centerX(),
+                mResult.mBounds.centerX() < displayBounds.centerX());
+        assertTrue("Bounds should be centered at somewhere in the top half, but it's "
+                        + "centerY is " + mResult.mBounds.centerY(),
+                mResult.mBounds.centerY() < displayBounds.centerY());
+    }
+
+    @Test
+    public void testAdjustBoundsToFitDisplay_TopLeftOutOfDisplay() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+
+        final ActivityRecord source = createSourceActivity(freeformDisplay);
+        source.setBounds(0, 0, 200, 400);
+
+        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, source, options, mCurrent, mResult));
+
+        final Rect displayBounds = freeformDisplay.getBounds();
+        assertTrue("display bounds doesn't contain result. display bounds: "
+                + displayBounds + " result: " + mResult.mBounds,
+                displayBounds.contains(mResult.mBounds));
+    }
+
+    @Test
+    public void testAdjustBoundsToFitDisplay_BottomRightOutOfDisplay() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+
+        final ActivityRecord source = createSourceActivity(freeformDisplay);
+        source.setBounds(1720, 680, 1920, 1080);
+
+        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, source, options, mCurrent, mResult));
+
+        final Rect displayBounds = freeformDisplay.getBounds();
+        assertTrue("display bounds doesn't contain result. display bounds: "
+                        + displayBounds + " result: " + mResult.mBounds,
+                displayBounds.contains(mResult.mBounds));
+    }
+
+    @Test
+    public void testAdjustBoundsToFitDisplay_LargerThanDisplay() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        Configuration overrideConfig = new Configuration();
+        overrideConfig.setTo(mSupervisor.getOverrideConfiguration());
+        overrideConfig.setLayoutDirection(new Locale("ar"));
+        mSupervisor.onConfigurationChanged(overrideConfig);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+
+        final ActivityRecord source = createSourceActivity(freeformDisplay);
+        source.setBounds(1720, 680, 1920, 1080);
+
+        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, source, options, mCurrent, mResult));
+
+        final Rect displayBounds = freeformDisplay.getBounds();
+        assertTrue("display bounds doesn't contain result. display bounds: "
+                        + displayBounds + " result: " + mResult.mBounds,
+                displayBounds.contains(mResult.mBounds));
+    }
+
+    @Test
+    public void testRespectsLayoutMinDimensions() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setMinWidth(500).setMinHeight(800).build();
+
+        mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
+                /* source */ null, options, mCurrent, mResult));
+
+        assertEquals(500, mResult.mBounds.width());
+        assertEquals(800, mResult.mBounds.height());
+    }
+
+    @Test
+    public void testRotatesInPlaceInitialBoundsMismatchOrientation() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+        options.setLaunchBounds(new Rect(100, 100, 500, 300));
+
+        mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquals(new Rect(200, 0, 400, 400), mResult.mBounds);
+    }
+
+    @Test
+    public void testShiftsToRightForCloseToLeftBoundsWhenConflict() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        addFreeformTaskTo(freeformDisplay, new Rect(50, 50, 100, 150));
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+        options.setLaunchBounds(new Rect(50, 50, 500, 300));
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquals(new Rect(170, 50, 620, 300), mResult.mBounds);
+    }
+
+    @Test
+    public void testShiftsToLeftForCloseToRightBoundsWhenConflict() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        addFreeformTaskTo(freeformDisplay, new Rect(1720, 50, 1830, 150));
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+        options.setLaunchBounds(new Rect(1720, 50, 1850, 300));
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquals(new Rect(1600, 50, 1730, 300), mResult.mBounds);
+    }
+
+    @Test
+    public void testShiftsToRightFirstForHorizontallyCenteredAndCloseToTopWhenConflict() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        addFreeformTaskTo(freeformDisplay, new Rect(0, 0, 100, 300));
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+        options.setLaunchBounds(new Rect(0, 0, 1800, 200));
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquals(new Rect(120, 0, 1920, 200), mResult.mBounds);
+    }
+
+    @Test
+    public void testShiftsToLeftNoSpaceOnRightForHorizontallyCenteredAndCloseToTopWhenConflict() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        addFreeformTaskTo(freeformDisplay, new Rect(120, 0, 240, 300));
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+        options.setLaunchBounds(new Rect(120, 0, 1860, 200));
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquals(new Rect(0, 0, 1740, 200), mResult.mBounds);
+    }
+
+    @Test
+    public void testShiftsToBottomRightFirstForCenteredBoundsWhenConflict() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        addFreeformTaskTo(freeformDisplay, new Rect(120, 0, 240, 100));
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+        options.setLaunchBounds(new Rect(120, 0, 1800, 1013));
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquals(new Rect(240, 67, 1920, 1080), mResult.mBounds);
+    }
+
+    @Test
+    public void testShiftsToTopLeftIfNoSpaceOnBottomRightForCenteredBoundsWhenConflict() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        addFreeformTaskTo(freeformDisplay, new Rect(120, 67, 240, 100));
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+        options.setLaunchBounds(new Rect(120, 67, 1800, 1020));
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquals(new Rect(0, 0, 1680, 953), mResult.mBounds);
+    }
+
+    private TestActivityDisplay createNewActivityDisplay(int windowingMode) {
+        final TestActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+        display.setWindowingMode(windowingMode);
+        display.setBounds(/* left */ 0, /* top */ 0, /* right */ 1920, /* bottom */ 1080);
+        display.getConfiguration().densityDpi = DENSITY_DEFAULT;
+        return display;
+    }
+
+    private ActivityRecord createSourceActivity(TestActivityDisplay display) {
+        final TestActivityStack stack = display.createStack(display.getWindowingMode(),
+                ACTIVITY_TYPE_STANDARD, true);
+        return new ActivityBuilder(mService).setStack(stack).setCreateTask(true).build();
+    }
+
+    private void addFreeformTaskTo(TestActivityDisplay display, Rect bounds) {
+        final TestActivityStack stack = display.createStack(display.getWindowingMode(),
+                ACTIVITY_TYPE_STANDARD, true);
+        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        task.setBounds(bounds);
+    }
+
+    private void assertEquivalentWindowingMode(int expected, int actual, int parentWindowingMode) {
+        if (expected != parentWindowingMode) {
+            assertEquals(expected, actual);
+        } else {
+            assertEquals(WINDOWING_MODE_UNDEFINED, actual);
+        }
+    }
+
+    private static class WindowLayoutBuilder {
+        private int mWidth = -1;
+        private int mHeight = -1;
+        private float mWidthFraction = -1f;
+        private float mHeightFraction = -1f;
+        private int mGravity = Gravity.NO_GRAVITY;
+        private int mMinWidth = -1;
+        private int mMinHeight = -1;
+
+        private WindowLayoutBuilder setWidth(int width) {
+            mWidth = width;
+            return this;
+        }
+
+        private WindowLayoutBuilder setHeight(int height) {
+            mHeight = height;
+            return this;
+        }
+
+        private WindowLayoutBuilder setWidthFraction(float widthFraction) {
+            mWidthFraction = widthFraction;
+            return this;
+        }
+
+        private WindowLayoutBuilder setHeightFraction(float heightFraction) {
+            mHeightFraction = heightFraction;
+            return this;
+        }
+
+        private WindowLayoutBuilder setGravity(int gravity) {
+            mGravity = gravity;
+            return this;
+        }
+
+        private WindowLayoutBuilder setMinWidth(int minWidth) {
+            mMinWidth = minWidth;
+            return this;
+        }
+
+        private WindowLayoutBuilder setMinHeight(int minHeight) {
+            mMinHeight = minHeight;
+            return this;
+        }
+
+        private ActivityInfo.WindowLayout build() {
+            return new ActivityInfo.WindowLayout(mWidth, mWidthFraction, mHeight, mHeightFraction,
+                    mGravity, mMinWidth, mMinHeight);
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java
new file mode 100644
index 0000000..df7bc11
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseBooleanArray;
+
+import androidx.test.filters.FlakyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for {@link TaskPersister}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:TaskPersisterTest
+ */
+@FlakyTest(detail = "Promote to presubmit if stable")
+@Presubmit
+public class TaskPersisterTest {
+    private static final String TEST_USER_NAME = "AM-Test-User";
+
+    private TaskPersister mTaskPersister;
+    private int mTestUserId;
+    private UserManager mUserManager;
+
+    @Before
+    public void setUp() throws Exception {
+        final Context context = getInstrumentation().getTargetContext();
+        mUserManager = UserManager.get(context);
+        mTaskPersister = new TaskPersister(context.getFilesDir());
+        // In ARC, the maximum number of supported users is one, which is different from the ones of
+        // most phones (more than 4). This prevents TaskPersisterTest from creating another user for
+        // test. However, since guest users can be added as much as possible, we create guest user
+        // in the test.
+        mTestUserId = createUser(TEST_USER_NAME, UserInfo.FLAG_GUEST);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mTaskPersister.unloadUserDataFromMemory(mTestUserId);
+        removeUser(mTestUserId);
+    }
+
+    private int getRandomTaskIdForUser(int userId) {
+        int taskId = (int) (Math.random() * UserHandle.PER_USER_RANGE);
+        taskId += UserHandle.PER_USER_RANGE * userId;
+        return taskId;
+    }
+
+    @Test
+    public void testTaskIdsPersistence() {
+        SparseBooleanArray taskIdsOnFile = new SparseBooleanArray();
+        for (int i = 0; i < 100; i++) {
+            taskIdsOnFile.put(getRandomTaskIdForUser(mTestUserId), true);
+        }
+        mTaskPersister.writePersistedTaskIdsForUser(taskIdsOnFile, mTestUserId);
+        SparseBooleanArray newTaskIdsOnFile = mTaskPersister
+                .loadPersistedTaskIdsForUser(mTestUserId);
+        assertEquals("TaskIds written differ from TaskIds read back from file",
+                taskIdsOnFile, newTaskIdsOnFile);
+    }
+
+    private int createUser(String name, int flags) {
+        UserInfo user = mUserManager.createUser(name, flags);
+        assertNotNull("Error while creating the test user: " + TEST_USER_NAME, user);
+        return user.id;
+    }
+
+    private void removeUser(int userId) {
+        boolean userRemoved = mUserManager.removeUser(userId);
+        assertTrue("Error while removing the test user: " + TEST_USER_NAME, userRemoved);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
new file mode 100644
index 0000000..72d7c90
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.Xml;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.server.wm.TaskRecord.TaskRecordFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.util.ArrayList;
+
+/**
+ * Tests for exercising {@link TaskRecord}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:TaskRecordTests
+ */
+@MediumTest
+@Presubmit
+public class TaskRecordTests extends ActivityTestsBase {
+
+    private static final String TASK_TAG = "task";
+
+    @Before
+    public void setUp() throws Exception {
+        TaskRecord.setTaskRecordFactory(null);
+        setupActivityTaskManagerService();
+    }
+
+    @Test
+    public void testRestoreWindowedTask() throws Exception {
+        final TaskRecord expected = createTaskRecord(64);
+        expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
+
+        final File serializedFile = serializeToFile(expected);
+
+        try {
+            final TaskRecord actual = restoreFromFile(serializedFile);
+            assertEquals(expected.taskId, actual.taskId);
+            assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
+        } finally {
+            serializedFile.delete();
+        }
+    }
+
+    @Test
+    public void testDefaultTaskFactoryNotNull() throws Exception {
+        assertNotNull(TaskRecord.getTaskRecordFactory());
+    }
+
+    /** Ensure we have no chance to modify the original intent. */
+    @Test
+    public void testCopyBaseIntentForTaskInfo() {
+        final TaskRecord task = createTaskRecord(1);
+        task.lastTaskDescription = new ActivityManager.TaskDescription();
+        final ActivityManager.RecentTaskInfo info = new ActivityManager.RecentTaskInfo();
+        task.fillTaskInfo(info, new TaskRecord.TaskActivitiesReport());
+
+        // The intent of info should be a copy so assert that they are different instances.
+        assertThat(info.baseIntent, not(sameInstance(task.getBaseIntent())));
+    }
+
+    @Test
+    public void testCreateTestRecordUsingCustomizedFactory() throws Exception {
+        TestTaskRecordFactory factory = new TestTaskRecordFactory();
+        TaskRecord.setTaskRecordFactory(factory);
+
+        assertFalse(factory.mCreated);
+
+        TaskRecord.create(null, 0, null, null, null, null);
+
+        assertTrue(factory.mCreated);
+    }
+
+    @Test
+    public void testReturnsToHomeStack() throws Exception {
+        final TaskRecord task = createTaskRecord(1);
+        assertFalse(task.returnsToHomeStack());
+        task.intent = null;
+        assertFalse(task.returnsToHomeStack());
+        task.intent = new Intent();
+        assertFalse(task.returnsToHomeStack());
+        task.intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
+        assertTrue(task.returnsToHomeStack());
+    }
+
+    private File serializeToFile(TaskRecord r) throws IOException, XmlPullParserException {
+        final File tmpFile = File.createTempFile(r.taskId + "_task_", "xml");
+
+        try (OutputStream os = new FileOutputStream(tmpFile)) {
+            final XmlSerializer serializer = Xml.newSerializer();
+            serializer.setOutput(os, "UTF-8");
+            serializer.startDocument(null, true);
+            serializer.startTag(null, TASK_TAG);
+            r.saveToXml(serializer);
+            serializer.endTag(null, TASK_TAG);
+            serializer.endDocument();
+        }
+
+        return tmpFile;
+    }
+
+    private TaskRecord restoreFromFile(File file) throws IOException, XmlPullParserException {
+        try (Reader reader = new BufferedReader(new FileReader(file))) {
+            final XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(reader);
+            assertEquals(XmlPullParser.START_TAG, parser.next());
+            assertEquals(TASK_TAG, parser.getName());
+            return TaskRecord.restoreFromXml(parser, mService.mStackSupervisor);
+        }
+    }
+
+    private TaskRecord createTaskRecord(int taskId) {
+        return new TaskRecord(mService, taskId, new Intent(), null, null, null,
+                ActivityBuilder.getDefaultComponent(), null, false, false, false, 0, 10050, null,
+                new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0, null, 0, false, false, false, 0, 0
+        );
+    }
+
+    private static class TestTaskRecordFactory extends TaskRecordFactory {
+        private boolean mCreated = false;
+
+        @Override
+        TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
+                Intent intent,
+                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+            mCreated = true;
+            return null;
+        }
+
+        @Override
+        TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
+                Intent intent,
+                ActivityManager.TaskDescription taskDescription) {
+            mCreated = true;
+            return null;
+        }
+
+        @Override
+        TaskRecord create(ActivityTaskManagerService service, int taskId, Intent intent,
+                Intent affinityIntent, String affinity, String rootAffinity,
+                ComponentName realActivity,
+                ComponentName origActivity, boolean rootWasReset, boolean autoRemoveRecents,
+                boolean askedCompatMode, int userId, int effectiveUid, String lastDescription,
+                ArrayList<ActivityRecord> activities, long lastTimeMoved,
+                boolean neverRelinquishIdentity,
+                ActivityManager.TaskDescription lastTaskDescription,
+                int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
+                int callingUid, String callingPackage, int resizeMode,
+                boolean supportsPictureInPicture,
+                boolean realActivitySuspended, boolean userSetupComplete, int minWidth,
+                int minHeight) {
+            mCreated = true;
+            return null;
+        }
+
+        @Override
+        TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+                throws IOException, XmlPullParserException {
+            mCreated = true;
+            return null;
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
new file mode 100644
index 0000000..4a734e5
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManager.TaskDescription;
+import android.app.ActivityTaskManager;
+import android.app.IActivityManager;
+import android.app.ITaskStackListener;
+import android.app.Instrumentation.ActivityMonitor;
+import android.app.TaskStackListener;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.RemoteException;
+import android.support.test.uiautomator.UiDevice;
+import android.text.TextUtils;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.internal.annotations.GuardedBy;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:TaskStackChangedListenerTest
+ */
+@MediumTest
+public class TaskStackChangedListenerTest {
+
+    private IActivityManager mService;
+    private ITaskStackListener mTaskStackListener;
+
+    private static final Object sLock = new Object();
+    @GuardedBy("sLock")
+    private static boolean sTaskStackChangedCalled;
+    private static boolean sActivityBResumed;
+
+    @Before
+    public void setUp() throws Exception {
+        mService = ActivityManager.getService();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        ActivityTaskManager.getService().unregisterTaskStackListener(mTaskStackListener);
+        mTaskStackListener = null;
+    }
+
+    @Test
+    public void testTaskStackChanged_afterFinish() throws Exception {
+        registerTaskStackChangedListener(new TaskStackListener() {
+            @Override
+            public void onTaskStackChanged() throws RemoteException {
+                synchronized (sLock) {
+                    sTaskStackChangedCalled = true;
+                }
+            }
+        });
+
+        Context context = getInstrumentation().getContext();
+        context.startActivity(new Intent(context, ActivityA.class));
+        UiDevice.getInstance(getInstrumentation()).waitForIdle();
+        synchronized (sLock) {
+            assertTrue(sTaskStackChangedCalled);
+        }
+        assertTrue(sActivityBResumed);
+    }
+
+    @Test
+    public void testTaskDescriptionChanged() throws Exception {
+        final Object[] params = new Object[2];
+        final CountDownLatch latch = new CountDownLatch(1);
+        registerTaskStackChangedListener(new TaskStackListener() {
+            int mTaskId = -1;
+
+            @Override
+            public void onTaskCreated(int taskId, ComponentName componentName)
+                    throws RemoteException {
+                mTaskId = taskId;
+            }
+            @Override
+            public void onTaskDescriptionChanged(int taskId, TaskDescription td)
+                    throws RemoteException {
+                if (mTaskId == taskId && !TextUtils.isEmpty(td.getLabel())) {
+                    params[0] = taskId;
+                    params[1] = td;
+                    latch.countDown();
+                }
+            }
+        });
+        final Activity activity = startTestActivity(ActivityTaskDescriptionChange.class);
+        waitForCallback(latch);
+        assertEquals(activity.getTaskId(), params[0]);
+        assertEquals("Test Label", ((TaskDescription) params[1]).getLabel());
+    }
+
+    @Test
+    public void testActivityRequestedOrientationChanged() throws Exception {
+        final int[] params = new int[2];
+        final CountDownLatch latch = new CountDownLatch(1);
+        registerTaskStackChangedListener(new TaskStackListener() {
+            @Override
+            public void onActivityRequestedOrientationChanged(int taskId,
+                    int requestedOrientation) {
+                params[0] = taskId;
+                params[1] = requestedOrientation;
+                latch.countDown();
+            }
+        });
+        final Activity activity = startTestActivity(ActivityRequestedOrientationChange.class);
+        waitForCallback(latch);
+        assertEquals(activity.getTaskId(), params[0]);
+        assertEquals(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, params[1]);
+    }
+
+    /**
+     * Tests for onTaskCreated, onTaskMovedToFront, onTaskRemoved and onTaskRemovalStarted.
+     */
+    @Test
+    public void testTaskChangeCallBacks() throws Exception {
+        final Object[] params = new Object[2];
+        final CountDownLatch taskCreatedLaunchLatch = new CountDownLatch(1);
+        final CountDownLatch taskMovedToFrontLatch = new CountDownLatch(1);
+        final CountDownLatch taskRemovedLatch = new CountDownLatch(1);
+        final CountDownLatch taskRemovalStartedLatch = new CountDownLatch(1);
+        final CountDownLatch onDetachedFromWindowLatch = new CountDownLatch(1);
+        registerTaskStackChangedListener(new TaskStackListener() {
+            @Override
+            public void onTaskCreated(int taskId, ComponentName componentName)
+                    throws RemoteException {
+                params[0] = taskId;
+                params[1] = componentName;
+                taskCreatedLaunchLatch.countDown();
+            }
+
+            @Override
+            public void onTaskMovedToFront(int taskId) throws RemoteException {
+                params[0] = taskId;
+                taskMovedToFrontLatch.countDown();
+            }
+
+            @Override
+            public void onTaskRemovalStarted(int taskId) {
+                params[0] = taskId;
+                taskRemovalStartedLatch.countDown();
+            }
+
+            @Override
+            public void onTaskRemoved(int taskId) throws RemoteException {
+                params[0] = taskId;
+                taskRemovedLatch.countDown();
+            }
+        });
+
+        final ActivityTaskChangeCallbacks activity =
+                (ActivityTaskChangeCallbacks) startTestActivity(ActivityTaskChangeCallbacks.class);
+        activity.setDetachedFromWindowLatch(onDetachedFromWindowLatch);
+        final int id = activity.getTaskId();
+
+        // Test for onTaskCreated.
+        waitForCallback(taskCreatedLaunchLatch);
+        assertEquals(id, params[0]);
+        ComponentName componentName = (ComponentName) params[1];
+        assertEquals(ActivityTaskChangeCallbacks.class.getName(), componentName.getClassName());
+
+        // Test for onTaskMovedToFront.
+        assertEquals(1, taskMovedToFrontLatch.getCount());
+        mService.moveTaskToFront(id, 0, null);
+        waitForCallback(taskMovedToFrontLatch);
+        assertEquals(activity.getTaskId(), params[0]);
+
+        // Test for onTaskRemovalStarted.
+        assertEquals(1, taskRemovalStartedLatch.getCount());
+        activity.finishAndRemoveTask();
+        waitForCallback(taskRemovalStartedLatch);
+        // onTaskRemovalStarted happens before the activity's window is removed.
+        assertFalse(activity.mOnDetachedFromWindowCalled);
+        assertEquals(id, params[0]);
+
+        // Test for onTaskRemoved.
+        assertEquals(1, taskRemovedLatch.getCount());
+        waitForCallback(taskRemovedLatch);
+        assertEquals(id, params[0]);
+        waitForCallback(onDetachedFromWindowLatch);
+        assertTrue(activity.mOnDetachedFromWindowCalled);
+    }
+
+    /**
+     * Starts the provided activity and returns the started instance.
+     */
+    private TestActivity startTestActivity(Class<?> activityClass) throws InterruptedException {
+        final ActivityMonitor monitor = new ActivityMonitor(activityClass.getName(), null, false);
+        getInstrumentation().addMonitor(monitor);
+        final Context context = getInstrumentation().getContext();
+        context.startActivity(new Intent(context, activityClass));
+        final TestActivity activity = (TestActivity) monitor.waitForActivityWithTimeout(1000);
+        if (activity == null) {
+            throw new RuntimeException("Timed out waiting for Activity");
+        }
+        activity.waitForResumeStateChange(true);
+        return activity;
+    }
+
+    private void registerTaskStackChangedListener(ITaskStackListener listener) throws Exception {
+        mTaskStackListener = listener;
+        ActivityTaskManager.getService().registerTaskStackListener(listener);
+    }
+
+    private void waitForCallback(CountDownLatch latch) {
+        try {
+            final boolean result = latch.await(2, TimeUnit.SECONDS);
+            if (!result) {
+                throw new RuntimeException("Timed out waiting for task stack change notification");
+            }
+        } catch (InterruptedException e) {
+        }
+    }
+
+    public static class TestActivity extends Activity {
+        boolean mIsResumed = false;
+
+        @Override
+        protected void onPostResume() {
+            super.onPostResume();
+            synchronized (this) {
+                mIsResumed = true;
+                notifyAll();
+            }
+        }
+
+        @Override
+        protected void onPause() {
+            super.onPause();
+            synchronized (this) {
+                mIsResumed = false;
+                notifyAll();
+            }
+        }
+
+        /**
+         * If isResumed is {@code true}, sleep the thread until the activity is resumed.
+         * if {@code false}, sleep the thread until the activity is paused.
+         */
+        @SuppressWarnings("WaitNotInLoop")
+        public void waitForResumeStateChange(boolean isResumed) throws InterruptedException {
+            synchronized (this) {
+                if (mIsResumed == isResumed) {
+                    return;
+                }
+                wait(5000);
+            }
+            assertEquals("The activity resume state change timed out", isResumed, mIsResumed);
+        }
+    }
+
+    public static class ActivityA extends TestActivity {
+
+        private boolean mActivityBLaunched = false;
+
+        @Override
+        protected void onPostResume() {
+            super.onPostResume();
+            if (mActivityBLaunched) {
+                return;
+            }
+            mActivityBLaunched = true;
+            finish();
+            startActivity(new Intent(this, ActivityB.class));
+        }
+    }
+
+    public static class ActivityB extends TestActivity {
+
+        @Override
+        protected void onPostResume() {
+            super.onPostResume();
+            synchronized (sLock) {
+                sTaskStackChangedCalled = false;
+            }
+            sActivityBResumed = true;
+            finish();
+        }
+    }
+
+    public static class ActivityRequestedOrientationChange extends TestActivity {
+        @Override
+        protected void onPostResume() {
+            super.onPostResume();
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+            finish();
+        }
+    }
+
+    public static class ActivityTaskDescriptionChange extends TestActivity {
+        @Override
+        protected void onPostResume() {
+            super.onPostResume();
+            setTaskDescription(new TaskDescription("Test Label"));
+            finish();
+        }
+    }
+
+    public static class ActivityTaskChangeCallbacks extends TestActivity {
+        public boolean mOnDetachedFromWindowCalled = false;
+        private CountDownLatch mOnDetachedFromWindowCountDownLatch;
+
+        @Override
+        public void onDetachedFromWindow() {
+            mOnDetachedFromWindowCalled = true;
+            mOnDetachedFromWindowCountDownLatch.countDown();
+        }
+
+        void setDetachedFromWindowLatch(CountDownLatch countDownLatch) {
+            mOnDetachedFromWindowCountDownLatch = countDownLatch;
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
new file mode 100644
index 0000000..21e5d99
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 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.server.wm;
+
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
+
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import android.view.animation.ClipRectAnimation;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Tests for the {@link WindowAnimationSpec} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:WindowAnimationSpecTest
+ */
+@SmallTest
+@Presubmit
+public class WindowAnimationSpecTest {
+    private final SurfaceControl mSurfaceControl = mock(SurfaceControl.class);
+    private final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
+    private final Animation mAnimation = mock(Animation.class);
+    private final Rect mStackBounds = new Rect(0, 0, 10, 10);
+
+    @Test
+    public void testApply_clipNone() {
+        Rect windowCrop = new Rect(0, 0, 20, 20);
+        Animation a = createClipRectAnimation(windowCrop, windowCrop);
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_NONE,
+                true /* isAppAnimation */);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
+                argThat(rect -> rect.equals(windowCrop)));
+    }
+
+    @Test
+    public void testApply_clipAfter() {
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null,
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_AFTER_ANIM,
+                true /* isAppAnimation */);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
+    }
+
+    @Test
+    public void testApply_clipAfterOffsetPosition() {
+        // Stack bounds is (0, 0, 10, 10) position is (20, 40)
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation,
+                new Point(20, 40), mStackBounds, false /* canSkipFirstFrame */,
+                STACK_CLIP_AFTER_ANIM,
+                true /* isAppAnimation */);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
+    }
+
+    @Test
+    public void testApply_clipBeforeNoAnimationBounds() {
+        // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 0, 0)
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null,
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM,
+                true /* isAppAnimation */);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
+                argThat(rect -> rect.equals(mStackBounds)));
+    }
+
+    @Test
+    public void testApply_clipBeforeNoStackBounds() {
+        // Stack bounds is (0, 0, 0, 0) animation clip is (0, 0, 20, 20)
+        Rect windowCrop = new Rect(0, 0, 20, 20);
+        Animation a = createClipRectAnimation(windowCrop, windowCrop);
+        a.initialize(0, 0, 0, 0);
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
+                null, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM,
+                true /* isAppAnimation */);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
+    }
+
+    @Test
+    public void testApply_clipBeforeSmallerAnimationClip() {
+        // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 5, 5)
+        Rect windowCrop = new Rect(0, 0, 5, 5);
+        Animation a = createClipRectAnimation(windowCrop, windowCrop);
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM,
+                true /* isAppAnimation */);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
+                argThat(rect -> rect.equals(windowCrop)));
+    }
+
+    @Test
+    public void testApply_clipBeforeSmallerStackClip() {
+        // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 20, 20)
+        Rect windowCrop = new Rect(0, 0, 20, 20);
+        Animation a = createClipRectAnimation(windowCrop, windowCrop);
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM,
+                true /* isAppAnimation */);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
+                argThat(rect -> rect.equals(mStackBounds)));
+    }
+
+    private Animation createClipRectAnimation(Rect fromClip, Rect toClip) {
+        Animation a = new ClipRectAnimation(fromClip, toClip);
+        a.initialize(0, 0, 0, 0);
+        return a;
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
new file mode 100644
index 0000000..3b4ab38
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.view.Display;
+import android.view.Surface;
+
+import org.mockito.invocation.InvocationOnMock;
+
+/**
+ * A collection of static functions that can be referenced by other test packages to provide access
+ * to WindowManager related test functionality.
+ */
+public class WindowTestUtils {
+
+    /** An extension of {@link DisplayContent} to gain package scoped access. */
+    public static class TestDisplayContent extends DisplayContent {
+
+        private TestDisplayContent(Display display, WindowManagerService service,
+                WallpaperController wallpaperController, DisplayWindowController controller) {
+            super(display, service, wallpaperController, controller);
+        }
+
+        /** Create a mocked default {@link DisplayContent}. */
+        public static TestDisplayContent create(Context context) {
+            final TestDisplayContent displayContent = mock(TestDisplayContent.class);
+            displayContent.isDefaultDisplay = true;
+
+            final DisplayPolicy displayPolicy = mock(DisplayPolicy.class);
+            when(displayPolicy.navigationBarCanMove()).thenReturn(true);
+            when(displayPolicy.hasNavigationBar()).thenReturn(true);
+
+            final DisplayRotation displayRotation = new DisplayRotation(
+                    mock(WindowManagerService.class), displayContent, displayPolicy,
+                    context, new Object());
+            displayRotation.mPortraitRotation = Surface.ROTATION_0;
+            displayRotation.mLandscapeRotation = Surface.ROTATION_90;
+            displayRotation.mUpsideDownRotation = Surface.ROTATION_180;
+            displayRotation.mSeascapeRotation = Surface.ROTATION_270;
+
+            when(displayContent.getDisplayRotation()).thenReturn(displayRotation);
+
+            return displayContent;
+        }
+    }
+
+    /**
+     * Creates a mock instance of {@link StackWindowController}.
+     */
+    public static StackWindowController createMockStackWindowContainerController() {
+        StackWindowController controller = mock(StackWindowController.class);
+        controller.mContainer = mock(TestTaskStack.class);
+
+        // many components rely on the {@link StackWindowController#adjustConfigurationForBounds}
+        // to properly set bounds values in the configuration. We must mimick those actions here.
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final Configuration config = invocationOnMock.<Configuration>getArgument(7);
+            final Rect bounds = invocationOnMock.<Rect>getArgument(0);
+            config.windowConfiguration.setBounds(bounds);
+            return null;
+        }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(),
+                anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt());
+
+        return controller;
+    }
+
+    /**
+     * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not
+     * normally be mocked out.
+     */
+    public static class TestTaskStack extends TaskStack {
+        TestTaskStack(WindowManagerService service, int stackId) {
+            super(service, stackId, null);
+        }
+
+        @Override
+        void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) {
+            // Do nothing.
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
new file mode 100644
index 0000000..0445ea0
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.testing.DexmakerShareClassLoaderRule;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.Preconditions;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Test class for {@link WindowTracing}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:WindowTracingTest
+ */
+@FlakyTest(bugId = 74078662)
+@SmallTest
+@Presubmit
+public class WindowTracingTest {
+
+    private static final byte[] MAGIC_HEADER = new byte[]{
+            0x9, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45,
+    };
+
+    @Rule
+    public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+            new DexmakerShareClassLoaderRule();
+
+    @Mock
+    private WindowManagerService mWmMock;
+    private WindowTracing mWindowTracing;
+    private File mFile;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        final Context testContext = getInstrumentation().getContext();
+        mFile = testContext.getFileStreamPath("tracing_test.dat");
+        mFile.delete();
+
+        mWindowTracing = new WindowTracing(mFile);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mFile.delete();
+    }
+
+    @Test
+    public void isEnabled_returnsFalseByDefault() {
+        assertFalse(mWindowTracing.isEnabled());
+    }
+
+    @Test
+    public void isEnabled_returnsTrueAfterStart() throws Exception {
+        mWindowTracing.startTrace(mock(PrintWriter.class));
+        assertTrue(mWindowTracing.isEnabled());
+    }
+
+    @Test
+    public void isEnabled_returnsFalseAfterStop() throws Exception {
+        mWindowTracing.startTrace(mock(PrintWriter.class));
+        mWindowTracing.stopTrace(mock(PrintWriter.class));
+        assertFalse(mWindowTracing.isEnabled());
+    }
+
+    @Test
+    public void trace_discared_whenNotTracing() {
+        mWindowTracing.traceStateLocked("where", mWmMock);
+        verifyZeroInteractions(mWmMock);
+    }
+
+    @Test
+    public void trace_dumpsWindowManagerState_whenTracing() throws Exception {
+        mWindowTracing.startTrace(mock(PrintWriter.class));
+        mWindowTracing.traceStateLocked("where", mWmMock);
+
+        verify(mWmMock).writeToProtoLocked(any(), eq(true));
+    }
+
+    @Test
+    public void traceFile_startsWithMagicHeader() throws Exception {
+        mWindowTracing.startTrace(mock(PrintWriter.class));
+        mWindowTracing.stopTrace(mock(PrintWriter.class));
+
+        byte[] header = new byte[MAGIC_HEADER.length];
+        try (InputStream is = new FileInputStream(mFile)) {
+            assertEquals(MAGIC_HEADER.length, is.read(header));
+            assertArrayEquals(MAGIC_HEADER, header);
+        }
+    }
+
+    @Ignore("Figure out why this test is crashing when setting up mWmMock.")
+    @Test
+    public void tracing_endsUpInFile() throws Exception {
+        mWindowTracing.startTrace(mock(PrintWriter.class));
+
+        doAnswer(inv -> {
+            inv.<ProtoOutputStream>getArgument(0).write(
+                    WindowManagerTraceProto.WHERE, "TEST_WM_PROTO");
+            return null;
+        }).when(mWmMock).writeToProtoLocked(any(), any());
+        mWindowTracing.traceStateLocked("TEST_WHERE", mWmMock);
+
+        mWindowTracing.stopTrace(mock(PrintWriter.class));
+
+        byte[] file = new byte[1000];
+        int fileLength;
+        try (InputStream is = new FileInputStream(mFile)) {
+            fileLength = is.read(file);
+            assertTrue(containsBytes(file, fileLength,
+                    "TEST_WHERE".getBytes(StandardCharsets.UTF_8)));
+            assertTrue(containsBytes(file, fileLength,
+                    "TEST_WM_PROTO".getBytes(StandardCharsets.UTF_8)));
+        }
+    }
+
+    /** Return true if {@code needle} appears anywhere in {@code haystack[0..length]} */
+    private static boolean containsBytes(byte[] haystack, int haystackLength, byte[] needle) {
+        Preconditions.checkArgument(haystackLength > 0);
+        Preconditions.checkArgument(needle.length > 0);
+
+        outer: for (int i = 0; i <= haystackLength - needle.length; i++) {
+            for (int j = 0; j < needle.length; j++) {
+                if (haystack[i + j] != needle[j]) {
+                    continue outer;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Test
+    public void test_containsBytes() {
+        byte[] haystack = "hello_world".getBytes(StandardCharsets.UTF_8);
+        assertTrue(containsBytes(haystack, haystack.length,
+                "hello".getBytes(StandardCharsets.UTF_8)));
+        assertTrue(containsBytes(haystack, haystack.length,
+                "world".getBytes(StandardCharsets.UTF_8)));
+        assertFalse(containsBytes(haystack, 6,
+                "world".getBytes(StandardCharsets.UTF_8)));
+        assertFalse(containsBytes(haystack, haystack.length,
+                "world_".getBytes(StandardCharsets.UTF_8)));
+        assertFalse(containsBytes(haystack, haystack.length,
+                "absent".getBytes(StandardCharsets.UTF_8)));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
new file mode 100644
index 0000000..649b785
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2018 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.server.wm.utils;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.server.wm.utils.CoordinateTransforms.transformLogicalToPhysicalCoordinates;
+import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.*;
+
+import android.graphics.Matrix;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.view.DisplayInfo;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:CoordinateTransformsTest
+ */
+public class CoordinateTransformsTest {
+
+    private static final int W = 200;
+    private static final int H = 400;
+
+    private final Matrix mMatrix = new Matrix();
+    private final Matrix mMatrix2 = new Matrix();
+
+    @Rule
+    public final ErrorCollector mErrorCollector = new ErrorCollector();
+
+    @Before
+    public void setUp() throws Exception {
+        mMatrix.setTranslate(0xdeadbeef, 0xdeadbeef);
+        mMatrix2.setTranslate(0xbeefdead, 0xbeefdead);
+    }
+
+    @Test
+    public void transformPhysicalToLogicalCoordinates_rot0() {
+        transformPhysicalToLogicalCoordinates(ROTATION_0, W, H, mMatrix);
+        assertThat(mMatrix, is(Matrix.IDENTITY_MATRIX));
+    }
+
+    @Test
+    public void transformPhysicalToLogicalCoordinates_rot90() {
+        transformPhysicalToLogicalCoordinates(ROTATION_90, W, H, mMatrix);
+
+        checkPoint(0, 0).transformsTo(0, W);
+        checkPoint(W, H).transformsTo(H, 0);
+    }
+
+    @Test
+    public void transformPhysicalToLogicalCoordinates_rot180() {
+        transformPhysicalToLogicalCoordinates(ROTATION_180, W, H, mMatrix);
+
+        checkPoint(0, 0).transformsTo(W, H);
+        checkPoint(W, H).transformsTo(0, 0);
+    }
+
+    @Test
+    public void transformPhysicalToLogicalCoordinates_rot270() {
+        transformPhysicalToLogicalCoordinates(ROTATION_270, W, H, mMatrix);
+
+        checkPoint(0, 0).transformsTo(H, 0);
+        checkPoint(W, H).transformsTo(0, W);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinates_rot0() {
+        transformLogicalToPhysicalCoordinates(ROTATION_0, W, H, mMatrix);
+        assertThat(mMatrix, is(Matrix.IDENTITY_MATRIX));
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinates_rot90() {
+        transformLogicalToPhysicalCoordinates(ROTATION_90, W, H, mMatrix);
+
+        checkPoint(0, W).transformsTo(0, 0);
+        checkPoint(H, 0).transformsTo(W, H);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinates_rot180() {
+        transformLogicalToPhysicalCoordinates(ROTATION_180, W, H, mMatrix);
+
+        checkPoint(W, H).transformsTo(0, 0);
+        checkPoint(0, 0).transformsTo(W, H);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinates_rot270() {
+        transformLogicalToPhysicalCoordinates(ROTATION_270, W, H, mMatrix);
+
+        checkPoint(H, 0).transformsTo(0, 0);
+        checkPoint(0, W).transformsTo(W, H);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinatesIsInverse_rot0() {
+        transformLogicalToPhysicalCoordinates(ROTATION_0, W, H, mMatrix);
+        transformPhysicalToLogicalCoordinates(ROTATION_0, W, H, mMatrix2);
+
+        assertMatricesAreInverses(mMatrix, mMatrix2);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinatesIsInverse_rot90() {
+        transformLogicalToPhysicalCoordinates(ROTATION_90, W, H, mMatrix);
+        transformPhysicalToLogicalCoordinates(ROTATION_90, W, H, mMatrix2);
+
+        assertMatricesAreInverses(mMatrix, mMatrix2);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinatesIsInverse_rot180() {
+        transformLogicalToPhysicalCoordinates(ROTATION_180, W, H, mMatrix);
+        transformPhysicalToLogicalCoordinates(ROTATION_180, W, H, mMatrix2);
+
+        assertMatricesAreInverses(mMatrix, mMatrix2);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinatesIsInverse_rot270() {
+        transformLogicalToPhysicalCoordinates(ROTATION_270, W, H, mMatrix);
+        transformPhysicalToLogicalCoordinates(ROTATION_270, W, H, mMatrix2);
+
+        assertMatricesAreInverses(mMatrix, mMatrix2);
+    }
+
+    @Test
+    public void transformBetweenRotations_rot180_rot270() {
+        // W,H are flipped, because they need to be given in the new orientation, i.e. ROT_270.
+        transformToRotation(ROTATION_180, ROTATION_270, H, W, mMatrix);
+
+        checkPoint(0, 0).transformsTo(0, W);
+        checkPoint(W, H).transformsTo(H, 0);
+    }
+
+    @Test
+    public void transformBetweenRotations_rot90_rot0() {
+        transformToRotation(ROTATION_180, ROTATION_270, W, H, mMatrix);
+
+        checkPoint(0, 0).transformsTo(0, H);
+        // H,W is bottom right in ROT_90
+        checkPoint(H, W).transformsTo(W, 0);
+    }
+
+    @Test
+    public void transformBetweenRotations_displayInfo() {
+        final DisplayInfo di = new DisplayInfo();
+        di.rotation = ROTATION_90;
+        di.logicalWidth = H;  // dimensions are flipped in ROT_90
+        di.logicalHeight = W;
+        transformToRotation(ROTATION_180, ROTATION_270, di, mMatrix);
+
+        // W,H are flipped, because they need to be given in the new orientation, i.e. ROT_270.
+        transformToRotation(ROTATION_180, ROTATION_270, H, W, mMatrix2);
+
+        assertEquals(mMatrix2, mMatrix);
+    }
+
+    private void assertMatricesAreInverses(Matrix matrix, Matrix matrix2) {
+        final Matrix concat = new Matrix();
+        concat.setConcat(matrix, matrix2);
+        assertTrue("expected identity, but was: " + concat, concat.isIdentity());
+    }
+
+    private TransformPointAssertable checkPoint(int x, int y) {
+        final Point devicePoint = new Point(x, y);
+        final float[] fs = new float[] {x, y};
+        mMatrix.mapPoints(fs);
+        final PointF transformedPoint = new PointF(fs[0], fs[1]);
+
+        return (expectedX, expectedY) -> {
+            mErrorCollector.checkThat("t(" + devicePoint + ")",
+                    transformedPoint, is(new PointF(expectedX, expectedY)));
+        };
+    }
+
+    public interface TransformPointAssertable {
+        void transformsTo(int x, int y);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java
new file mode 100644
index 0000000..926153d
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2018 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.server.wm.utils;
+
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.server.wm.utils.DisplayRotationUtil.getBoundIndexFromRotation;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link DisplayRotationUtil}
+ *
+ * Build/Install/Run:
+ *  atest WmTests:DisplayRotationUtilTest
+ */
+@SmallTest
+@Presubmit
+public class DisplayRotationUtilTest {
+    private static final Rect ZERO_RECT = new Rect();
+
+    @Test
+    public void testGetBoundIndexFromRotation_rot0() {
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_0),
+                equalTo(BOUNDS_POSITION_LEFT));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_0),
+                equalTo(BOUNDS_POSITION_TOP));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_0),
+                equalTo(BOUNDS_POSITION_RIGHT));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_0),
+                equalTo(BOUNDS_POSITION_BOTTOM));
+    }
+
+    @Test
+    public void testGetBoundIndexFromRotation_rot90() {
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_90),
+                equalTo(BOUNDS_POSITION_BOTTOM));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_90),
+                equalTo(BOUNDS_POSITION_LEFT));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_90),
+                equalTo(BOUNDS_POSITION_TOP));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_90),
+                equalTo(BOUNDS_POSITION_RIGHT));
+    }
+
+    @Test
+    public void testGetBoundIndexFromRotation_rot180() {
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_180),
+                equalTo(BOUNDS_POSITION_RIGHT));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_180),
+                equalTo(BOUNDS_POSITION_BOTTOM));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_180),
+                equalTo(BOUNDS_POSITION_LEFT));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_180),
+                equalTo(BOUNDS_POSITION_TOP));
+    }
+
+    @Test
+    public void testGetBoundIndexFromRotation_rot270() {
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_270),
+                equalTo(BOUNDS_POSITION_TOP));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_270),
+                equalTo(BOUNDS_POSITION_RIGHT));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_270),
+                equalTo(BOUNDS_POSITION_BOTTOM));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_270),
+                equalTo(BOUNDS_POSITION_LEFT));
+
+    }
+
+    @Test
+    public void testGetRotatedBounds_top_rot0() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] {ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT};
+        assertThat(util.getRotatedBounds(bounds, ROTATION_0, 200, 300),
+                equalTo(bounds));
+    }
+
+    @Test
+    public void testGetRotatedBounds_top_rot90() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] {ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT};
+        assertThat(util.getRotatedBounds(bounds, ROTATION_90, 200, 300),
+                equalTo(new Rect[] {new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT}));
+    }
+
+    @Test
+    public void testGetRotatedBounds_top_rot180() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] {ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT};
+        assertThat(util.getRotatedBounds(bounds, ROTATION_180, 200, 300),
+                equalTo(new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(50, 290, 150, 300)}));
+    }
+
+    @Test
+    public void testGetRotatedBounds_top_rot270() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] {ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT};
+        assertThat(util.getRotatedBounds(bounds, ROTATION_270, 200, 300),
+                equalTo(new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(290, 50, 300, 150), ZERO_RECT}));
+    }
+
+    @Test
+    public void testGetRotatedBounds_left_rot0() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] {new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT};
+        assertThat(util.getRotatedBounds(bounds, ROTATION_0, 300, 200),
+                equalTo(bounds));
+    }
+
+    @Test
+    public void testGetRotatedBounds_left_rot90() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] {new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT};
+        assertThat(util.getRotatedBounds(bounds, ROTATION_90, 300, 200),
+                equalTo(new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(50, 290, 150, 300)}));
+    }
+
+    @Test
+    public void testGetRotatedBounds_left_rot180() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] {new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT};
+        assertThat(util.getRotatedBounds(bounds, ROTATION_180, 300, 200),
+                equalTo(new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(290, 50, 300, 150), ZERO_RECT}));
+    }
+
+    @Test
+    public void testGetRotatedBounds_left_rot270() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] {new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT};
+        assertThat(util.getRotatedBounds(bounds, ROTATION_270, 300, 200),
+                equalTo(new Rect[] {ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT}));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/InsetUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/InsetUtilsTest.java
new file mode 100644
index 0000000..089e908
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/InsetUtilsTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 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.server.wm.utils;
+
+import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:InsetUtilsTest
+ */
+@SmallTest
+@Presubmit
+public class InsetUtilsTest {
+
+    @Test
+    public void testAdd() {
+        final Rect rect1 = new Rect(10, 20, 30, 40);
+        final Rect rect2 = new Rect(50, 60, 70, 80);
+        InsetUtils.addInsets(rect1, rect2);
+        assertEquals(new Rect(60, 80, 100, 120), rect1);
+    }
+
+    @Test
+    public void rotate() {
+        final Rect original = new Rect(1, 2, 3, 4);
+
+        assertEquals("rot0", original, rotateCopy(original, ROTATION_0));
+
+        final Rect rot90 = rotateCopy(original, ROTATION_90);
+        assertEquals("rot90", new Rect(2, 3, 4, 1), rot90);
+
+        final Rect rot180 = rotateCopy(original, ROTATION_180);
+        assertEquals("rot180", new Rect(3, 4, 1, 2), rot180);
+        assertEquals("rot90(rot90)=rot180", rotateCopy(rot90, ROTATION_90), rot180);
+
+        final Rect rot270 = rotateCopy(original, ROTATION_270);
+        assertEquals("rot270", new Rect(4, 1, 2, 3), rot270);
+        assertEquals("rot90(rot180)=rot270", rotateCopy(rot180, ROTATION_90), rot270);
+    }
+
+    private static Rect rotateCopy(Rect insets, int rotationDelta) {
+        final Rect copy = new Rect(insets);
+        InsetUtils.rotateInsets(copy, rotationDelta);
+        return copy;
+    }
+}
+
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java
new file mode 100644
index 0000000..33f34b4
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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.server.wm.utils;
+
+import static android.util.Pair.create;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:RotationCacheTest
+ */
+@SmallTest
+@FlakyTest(bugId = 74078662)
+@Presubmit
+public class RotationCacheTest {
+
+    private RotationCache<Object, Pair<Object, Integer>> mCache;
+    private boolean mComputationCalled;
+
+    @Before
+    public void setUp() throws Exception {
+        mComputationCalled = false;
+        mCache = new RotationCache<>((o, rot) -> {
+            mComputationCalled = true;
+            return create(o, rot);
+        });
+    }
+
+    @Test
+    public void getOrCompute_computes() {
+        assertThat(mCache.getOrCompute("hello", 0), equalTo(create("hello", 0)));
+        assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
+        assertThat(mCache.getOrCompute("hello", 2), equalTo(create("hello", 2)));
+        assertThat(mCache.getOrCompute("hello", 3), equalTo(create("hello", 3)));
+    }
+
+    @Test
+    public void getOrCompute_sameParam_sameRot_hitsCache() {
+        assertNotNull(mCache.getOrCompute("hello", 1));
+
+        mComputationCalled = false;
+        assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
+        assertThat(mComputationCalled, is(false));
+    }
+
+    @Test
+    public void getOrCompute_sameParam_hitsCache_forAllRots() {
+        assertNotNull(mCache.getOrCompute("hello", 3));
+        assertNotNull(mCache.getOrCompute("hello", 2));
+        assertNotNull(mCache.getOrCompute("hello", 1));
+        assertNotNull(mCache.getOrCompute("hello", 0));
+
+        mComputationCalled = false;
+        assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
+        assertThat(mCache.getOrCompute("hello", 0), equalTo(create("hello", 0)));
+        assertThat(mCache.getOrCompute("hello", 2), equalTo(create("hello", 2)));
+        assertThat(mCache.getOrCompute("hello", 3), equalTo(create("hello", 3)));
+        assertThat(mComputationCalled, is(false));
+    }
+
+    @Test
+    public void getOrCompute_changingParam_recomputes() {
+        assertNotNull(mCache.getOrCompute("hello", 1));
+
+        assertThat(mCache.getOrCompute("world", 1), equalTo(create("world", 1)));
+    }
+
+    @Test
+    public void getOrCompute_changingParam_clearsCacheForDifferentRots() {
+        assertNotNull(mCache.getOrCompute("hello", 1));
+        assertNotNull(mCache.getOrCompute("world", 2));
+
+        mComputationCalled = false;
+        assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
+        assertThat(mComputationCalled, is(true));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
new file mode 100644
index 0000000..fb8ba7b
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2018 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.server.wm.utils;
+
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
+import static android.view.DisplayCutout.NO_CUTOUT;
+import static android.view.DisplayCutout.fromBoundingRect;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.util.Size;
+import android.view.DisplayCutout;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link WmDisplayCutout}
+ *
+ * Build/Install/Run:
+ *  atest WmTests:WmDisplayCutoutTest
+ */
+@SmallTest
+@Presubmit
+public class WmDisplayCutoutTest {
+    private final DisplayCutout mCutoutTop = new DisplayCutout(
+            Insets.of(0, 100, 0, 0),
+            null /* boundLeft */, new Rect(50, 0, 75, 100) /* boundTop */,
+            null /* boundRight */, null /* boundBottom */);
+
+    @Test
+    public void calculateRelativeTo_top() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400)
+                .calculateRelativeTo(new Rect(5, 5, 95, 195));
+
+        assertEquals(new Rect(0, 15, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void calculateRelativeTo_left() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200)
+                .calculateRelativeTo(new Rect(5, 5, 195, 95));
+
+        assertEquals(new Rect(15, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void calculateRelativeTo_bottom() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200)
+                .calculateRelativeTo(new Rect(5, 5, 95, 195));
+
+        assertEquals(new Rect(0, 0, 0, 15), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void calculateRelativeTo_right() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100)
+                .calculateRelativeTo(new Rect(5, 5, 195, 95));
+
+        assertEquals(new Rect(0, 0, 15, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void calculateRelativeTo_bounds() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400)
+                .calculateRelativeTo(new Rect(5, 10, 95, 180));
+
+        assertThat(cutout.getDisplayCutout().getBoundingRectTop(),
+                equalTo(new Rect(-5, -10, 95, 10)));
+    }
+
+    @Test
+    public void computeSafeInsets_top() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400);
+
+        assertEquals(new Rect(0, 20, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_left() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200);
+
+        assertEquals(new Rect(20, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_bottom() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200);
+
+        assertEquals(new Rect(0, 0, 0, 20), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_right() {
+        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+                fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100);
+
+        assertEquals(new Rect(0, 0, 20, 0), cutout.getDisplayCutout().getSafeInsets());
+    }
+
+    @Test
+    public void computeSafeInsets_bounds() {
+        DisplayCutout cutout = WmDisplayCutout.computeSafeInsets(mCutoutTop, 1000,
+                2000).getDisplayCutout();
+
+        assertEquals(mCutoutTop.getBoundingRects(), cutout.getBoundingRects());
+    }
+
+    @Test
+    public void test_equals() {
+        assertEquals(new WmDisplayCutout(NO_CUTOUT, null), new WmDisplayCutout(NO_CUTOUT, null));
+        assertEquals(new WmDisplayCutout(mCutoutTop, new Size(1, 2)),
+                new WmDisplayCutout(mCutoutTop, new Size(1, 2)));
+
+        assertNotEquals(new WmDisplayCutout(mCutoutTop, new Size(1, 2)),
+                new WmDisplayCutout(mCutoutTop, new Size(5, 6)));
+        assertNotEquals(new WmDisplayCutout(mCutoutTop, new Size(1, 2)),
+                new WmDisplayCutout(NO_CUTOUT, new Size(1, 2)));
+    }
+
+    @Test
+    public void test_hashCode() {
+        assertEquals(new WmDisplayCutout(NO_CUTOUT, null).hashCode(),
+                new WmDisplayCutout(NO_CUTOUT, null).hashCode());
+        assertEquals(new WmDisplayCutout(mCutoutTop, new Size(1, 2)).hashCode(),
+                new WmDisplayCutout(mCutoutTop, new Size(1, 2)).hashCode());
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 6a74564..4f573a4 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -30,18 +30,19 @@
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+
 import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
 
@@ -340,14 +341,21 @@
     }
 
     void setAppIdleEnabled(boolean enabled) {
-        mAppIdleEnabled = enabled;
+        synchronized (mAppIdleLock) {
+            if (mAppIdleEnabled != enabled) {
+                final boolean oldParoleState = isParoledOrCharging();
+                mAppIdleEnabled = enabled;
+                if (isParoledOrCharging() != oldParoleState) {
+                    postParoleStateChanged();
+                }
+            }
+        }
     }
 
     public void onBootPhase(int phase) {
         mInjector.onBootPhase(phase);
         if (phase == PHASE_SYSTEM_SERVICES_READY) {
             Slog.d(TAG, "Setting app idle enabled state");
-            setAppIdleEnabled(mInjector.isAppIdleEnabled());
             // Observe changes to the threshold
             SettingsObserver settingsObserver = new SettingsObserver(mHandler);
             settingsObserver.registerObserver();
@@ -1842,8 +1850,6 @@
                         mContext.getContentResolver(),
                         Global.APP_IDLE_CONSTANTS));
             }
-            // Check if app_idle_enabled has changed
-            setAppIdleEnabled(mInjector.isAppIdleEnabled());
 
             // Look at global settings for this.
             // TODO: Maybe apply different thresholds for different users.
@@ -1915,6 +1921,10 @@
                         (KEY_STABLE_CHARGING_THRESHOLD,
                                 COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STABLE_CHARGING_THRESHOLD);
             }
+
+            // Check if app_idle_enabled has changed. Do this after getting the rest of the settings
+            // in case we need to change something based on the new values.
+            setAppIdleEnabled(mInjector.isAppIdleEnabled());
         }
 
         long[] parseLongArray(String values, long[] defaults) {
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index 5916b04c..eaaf9b2 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -22,17 +22,16 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
-import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
 
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 
@@ -57,72 +56,432 @@
 
     private final MyHandler mHandler;
 
-    private OnLimitReachedListener mListener;
+    private TimeLimitCallbackListener mListener;
 
     private static final long MAX_OBSERVER_PER_UID = 1000;
 
     private static final long ONE_MINUTE = 60_000L;
 
+    /** Collection of data for each user that has reported usage */
     @GuardedBy("mLock")
     private final SparseArray<UserData> mUsers = new SparseArray<>();
 
-    private static class UserData {
+    /**
+     * Collection of data for each app that is registering observers
+     * WARNING: Entries are currently not removed, based on the assumption there are a small
+     *          fixed number of apps on device that can register observers.
+     */
+    @GuardedBy("mLock")
+    private final SparseArray<ObserverAppData> mObserverApps = new SparseArray<>();
+
+    private class UserData {
         /** userId of the user */
-        private @UserIdInt int userId;
+        private @UserIdInt
+        int userId;
 
-        /** The app that is currently in the foreground */
-        private String currentForegroundedPackage;
+        /** Set of the currently active entities */
+        private final ArraySet<String> currentlyActive = new ArraySet<>();
 
-        /** The time when the current app came to the foreground */
-        private long currentForegroundedTime;
-
-        /** Map from package name for quick lookup */
-        private ArrayMap<String, ArrayList<TimeLimitGroup>> packageMap = new ArrayMap<>();
-
-        /** Map of observerId to details of the time limit group */
-        private SparseArray<TimeLimitGroup> groups = new SparseArray<>();
-
-        /** Map of the number of observerIds registered by uid */
-        private SparseIntArray observerIdCounts = new SparseIntArray();
+        /** Map from entity name for quick lookup */
+        private final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>();
 
         private UserData(@UserIdInt int userId) {
             this.userId = userId;
         }
+
+        @GuardedBy("mLock")
+        boolean isActive(String[] entities) {
+            // TODO: Consider using a bloom filter here if number of actives becomes large
+            final int size = entities.length;
+            for (int i = 0; i < size; i++) {
+                if (currentlyActive.contains(entities[i])) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @GuardedBy("mLock")
+        void addUsageGroup(UsageGroup group) {
+            final int size = group.mObserved.length;
+            for (int i = 0; i < size; i++) {
+                ArrayList<UsageGroup> list = observedMap.get(group.mObserved[i]);
+                if (list == null) {
+                    list = new ArrayList<>();
+                    observedMap.put(group.mObserved[i], list);
+                }
+                list.add(group);
+            }
+        }
+
+        @GuardedBy("mLock")
+        void removeUsageGroup(UsageGroup group) {
+            final int size = group.mObserved.length;
+            for (int i = 0; i < size; i++) {
+                final ArrayList<UsageGroup> list = observedMap.get(group.mObserved[i]);
+                if (list != null) {
+                    list.remove(group);
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        void dump(PrintWriter pw) {
+            pw.print(" userId=");
+            pw.println(userId);
+            pw.print(" Currently Active:");
+            final int nActive = currentlyActive.size();
+            for (int i = 0; i < nActive; i++) {
+                pw.print(currentlyActive.valueAt(i));
+                pw.print(", ");
+            }
+            pw.println();
+            pw.print(" Observed Entities:");
+            final int nEntities = currentlyActive.size();
+            for (int i = 0; i < nEntities; i++) {
+                pw.print(observedMap.keyAt(i));
+                pw.print(", ");
+            }
+            pw.println();
+        }
+    }
+
+
+    private class ObserverAppData {
+        /** uid of the observing app */
+        private int uid;
+
+        /** Map of observerId to details of the time limit group */
+        SparseArray<AppUsageGroup> appUsageGroups = new SparseArray<>();
+
+        /** Map of observerId to details of the time limit group */
+        SparseArray<SessionUsageGroup> sessionUsageGroups = new SparseArray<>();
+
+        private ObserverAppData(int uid) {
+            this.uid = uid;
+        }
+
+        @GuardedBy("mLock")
+        void removeAppUsageGroup(int observerId) {
+            appUsageGroups.remove(observerId);
+        }
+
+        @GuardedBy("mLock")
+        void removeSessionUsageGroup(int observerId) {
+            sessionUsageGroups.remove(observerId);
+        }
+
+
+        @GuardedBy("mLock")
+        void dump(PrintWriter pw) {
+            pw.print(" uid=");
+            pw.println(uid);
+            pw.println("    App Usage Groups:");
+            final int nAppUsageGroups = appUsageGroups.size();
+            for (int i = 0; i < nAppUsageGroups; i++) {
+                appUsageGroups.valueAt(i).dump(pw);
+                pw.println();
+            }
+            pw.println("    Session Usage Groups:");
+            final int nSessionUsageGroups = appUsageGroups.size();
+            for (int i = 0; i < nSessionUsageGroups; i++) {
+                sessionUsageGroups.valueAt(i).dump(pw);
+                pw.println();
+            }
+        }
     }
 
     /**
      * Listener interface for being informed when an app group's time limit is reached.
      */
-    public interface OnLimitReachedListener {
+    public interface TimeLimitCallbackListener {
         /**
          * Time limit for a group, keyed by the observerId, has been reached.
-         * @param observerId The observerId of the group whose limit was reached
-         * @param userId The userId
-         * @param timeLimit The original time limit in milliseconds
-         * @param timeElapsed How much time was actually spent on apps in the group, in milliseconds
+         *
+         * @param observerId     The observerId of the group whose limit was reached
+         * @param userId         The userId
+         * @param timeLimit      The original time limit in milliseconds
+         * @param timeElapsed    How much time was actually spent on apps in the group, in
+         *                       milliseconds
          * @param callbackIntent The PendingIntent to send when the limit is reached
          */
         public void onLimitReached(int observerId, @UserIdInt int userId, long timeLimit,
                 long timeElapsed, PendingIntent callbackIntent);
+
+        /**
+         * Session ended for a group, keyed by the observerId, after limit was reached.
+         *
+         * @param observerId     The observerId of the group whose limit was reached
+         * @param userId         The userId
+         * @param timeElapsed    How much time was actually spent on apps in the group, in
+         *                       milliseconds
+         * @param callbackIntent The PendingIntent to send when the limit is reached
+         */
+        public void onSessionEnd(int observerId, @UserIdInt int userId, long timeElapsed,
+                PendingIntent callbackIntent);
     }
 
-    static class TimeLimitGroup {
-        int requestingUid;
-        int observerId;
-        String[] packages;
-        long timeLimit;
-        long timeRequested;
-        long timeRemaining;
-        PendingIntent callbackIntent;
-        String currentPackage;
-        long timeCurrentPackageStarted;
-        int userId;
+    abstract class UsageGroup {
+        protected int mObserverId;
+        protected String[] mObserved;
+        protected long mTimeLimitMs;
+        protected long mUsageTimeMs;
+        protected int mActives;
+        protected long mLastKnownUsageTimeMs;
+        protected WeakReference<UserData> mUserRef;
+        protected WeakReference<ObserverAppData> mObserverAppRef;
+        protected PendingIntent mLimitReachedCallback;
+
+        UsageGroup(UserData user, ObserverAppData observerApp, int observerId, String[] observed,
+                long timeLimitMs, PendingIntent limitReachedCallback) {
+            mUserRef = new WeakReference<>(user);
+            mObserverAppRef = new WeakReference<>(observerApp);
+            mObserverId = observerId;
+            mObserved = observed;
+            mTimeLimitMs = timeLimitMs;
+            mLimitReachedCallback = limitReachedCallback;
+        }
+
+        @GuardedBy("mLock")
+        public long getTimeLimitMs() { return mTimeLimitMs; }
+
+        @GuardedBy("mLock")
+        public long getUsageTimeMs() { return mUsageTimeMs; }
+
+        @GuardedBy("mLock")
+        public void remove() {
+            UserData user = mUserRef.get();
+            if (user != null) {
+                user.removeUsageGroup(this);
+            }
+            // Clear the callback, so any racy inflight message will do nothing
+            mLimitReachedCallback = null;
+        }
+
+        @GuardedBy("mLock")
+        void noteUsageStart(long startTimeMs) {
+            noteUsageStart(startTimeMs, startTimeMs);
+        }
+
+        @GuardedBy("mLock")
+        void noteUsageStart(long startTimeMs, long currentTimeMs) {
+            if (mActives++ == 0) {
+                mLastKnownUsageTimeMs = startTimeMs;
+                final long timeRemaining =
+                        mTimeLimitMs - mUsageTimeMs + currentTimeMs - startTimeMs;
+                if (timeRemaining > 0) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Posting timeout for " + mObserverId + " for "
+                                + timeRemaining + "ms");
+                    }
+                    postCheckTimeoutLocked(this, timeRemaining);
+                }
+            } else {
+                if (mActives > mObserved.length) {
+                    // Try to get to a sane state and log the issue
+                    mActives = mObserved.length;
+                    final UserData user = mUserRef.get();
+                    if (user == null) return;
+                    final Object[] array = user.currentlyActive.toArray();
+                    Slog.e(TAG,
+                            "Too many noted usage starts! Observed entities: " + Arrays.toString(
+                                    mObserved) + "   Active Entities: " + Arrays.toString(array));
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        void noteUsageStop(long stopTimeMs) {
+            if (--mActives == 0) {
+                final boolean limitNotCrossed = mUsageTimeMs < mTimeLimitMs;
+                mUsageTimeMs += stopTimeMs - mLastKnownUsageTimeMs;
+                if (limitNotCrossed && mUsageTimeMs >= mTimeLimitMs) {
+                    // Crossed the limit
+                    if (DEBUG) Slog.d(TAG, "MTB informing group obs=" + mObserverId);
+                    postInformLimitReachedListenerLocked(this);
+                }
+                cancelCheckTimeoutLocked(this);
+            } else {
+                if (mActives < 0) {
+                    // Try to get to a sane state and log the issue
+                    mActives = 0;
+                    final UserData user = mUserRef.get();
+                    if (user == null) return;
+                    final Object[] array = user.currentlyActive.toArray();
+                    Slog.e(TAG,
+                            "Too many noted usage stops! Observed entities: " + Arrays.toString(
+                                    mObserved) + "   Active Entities: " + Arrays.toString(array));
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        void checkTimeout(long currentTimeMs) {
+            final UserData user = mUserRef.get();
+            if (user == null) return;
+
+            long timeRemainingMs = mTimeLimitMs - mUsageTimeMs;
+
+            if (DEBUG) Slog.d(TAG, "checkTimeout timeRemaining=" + timeRemainingMs);
+
+            // Already reached the limit, no need to report again
+            if (timeRemainingMs <= 0) return;
+
+            if (DEBUG) {
+                Slog.d(TAG, "checkTimeout");
+            }
+
+            // Double check that at least one entity in this group is currently active
+            if (user.isActive(mObserved)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "checkTimeout group is active");
+                }
+                final long timeUsedMs = currentTimeMs - mLastKnownUsageTimeMs;
+                if (timeRemainingMs <= timeUsedMs) {
+                    if (DEBUG) Slog.d(TAG, "checkTimeout : Time limit reached");
+                    // Hit the limit, set timeRemaining to zero to avoid checking again
+                    mUsageTimeMs += timeUsedMs;
+                    mLastKnownUsageTimeMs = currentTimeMs;
+                    AppTimeLimitController.this.postInformLimitReachedListenerLocked(this);
+                } else {
+                    if (DEBUG) Slog.d(TAG, "checkTimeout : Some more time remaining");
+                    AppTimeLimitController.this.postCheckTimeoutLocked(this,
+                            timeRemainingMs - timeUsedMs);
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        public void onLimitReached() {
+            UserData user = mUserRef.get();
+            if (user == null) return;
+            if (mListener != null) {
+                mListener.onLimitReached(mObserverId, user.userId, mTimeLimitMs, mUsageTimeMs,
+                        mLimitReachedCallback);
+            }
+        }
+
+        @GuardedBy("mLock")
+        void dump(PrintWriter pw) {
+            pw.print("        Group id=");
+            pw.print(mObserverId);
+            pw.print(" timeLimit=");
+            pw.print(mTimeLimitMs);
+            pw.print(" used=");
+            pw.print(mUsageTimeMs);
+            pw.print(" lastKnownUsage=");
+            pw.print(mLastKnownUsageTimeMs);
+            pw.print(" mActives=");
+            pw.print(mActives);
+            pw.print(" observed=");
+            pw.print(Arrays.toString(mObserved));
+        }
     }
 
+    class AppUsageGroup extends UsageGroup {
+        public AppUsageGroup(UserData user, ObserverAppData observerApp, int observerId,
+                String[] observed, long timeLimitMs, PendingIntent limitReachedCallback) {
+            super(user, observerApp, observerId, observed, timeLimitMs, limitReachedCallback);
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        public void remove() {
+            super.remove();
+            ObserverAppData observerApp = mObserverAppRef.get();
+            if (observerApp != null) {
+                observerApp.removeAppUsageGroup(mObserverId);
+            }
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        public void onLimitReached() {
+            super.onLimitReached();
+            // Unregister since the limit has been met and observer was informed.
+            remove();
+        }
+    }
+
+    class SessionUsageGroup extends UsageGroup {
+        private long mLastUsageEndTimeMs;
+        private long mNewSessionThresholdMs;
+        private PendingIntent mSessionEndCallback;
+
+        public SessionUsageGroup(UserData user, ObserverAppData observerApp, int observerId,
+                String[] observed, long timeLimitMs, PendingIntent limitReachedCallback,
+                long newSessionThresholdMs, PendingIntent sessionEndCallback) {
+            super(user, observerApp, observerId, observed, timeLimitMs, limitReachedCallback);
+            this.mNewSessionThresholdMs = newSessionThresholdMs;
+            this.mSessionEndCallback = sessionEndCallback;
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        public void remove() {
+            super.remove();
+            ObserverAppData observerApp = mObserverAppRef.get();
+            if (observerApp != null) {
+                observerApp.removeSessionUsageGroup(mObserverId);
+            }
+            // Clear the callback, so any racy inflight messages will do nothing
+            mSessionEndCallback = null;
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        public void noteUsageStart(long startTimeMs, long currentTimeMs) {
+            if (mActives == 0) {
+                if (startTimeMs - mLastUsageEndTimeMs > mNewSessionThresholdMs) {
+                    // New session has started, clear usage time.
+                    mUsageTimeMs = 0;
+                }
+                AppTimeLimitController.this.cancelInformSessionEndListener(this);
+            }
+            super.noteUsageStart(startTimeMs, currentTimeMs);
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        public void noteUsageStop(long stopTimeMs) {
+            super.noteUsageStop(stopTimeMs);
+            if (mActives == 0) {
+                mLastUsageEndTimeMs = stopTimeMs;
+                if (mUsageTimeMs >= mTimeLimitMs) {
+                    // Usage has ended. Schedule the session end callback to be triggered once
+                    // the new session threshold has been reached
+                    AppTimeLimitController.this.postInformSessionEndListenerLocked(this,
+                            mNewSessionThresholdMs);
+                }
+
+            }
+        }
+
+        @GuardedBy("mLock")
+        public void onSessionEnd() {
+            UserData user = mUserRef.get();
+            if (user == null) return;
+            if (mListener != null) {
+                mListener.onSessionEnd(mObserverId, user.userId, mUsageTimeMs, mSessionEndCallback);
+            }
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        void dump(PrintWriter pw) {
+            super.dump(pw);
+            pw.print(" lastUsageEndTime=");
+            pw.print(mLastUsageEndTimeMs);
+            pw.print(" newSessionThreshold=");
+            pw.print(mNewSessionThresholdMs);
+        }
+    }
+
+
     private class MyHandler extends Handler {
-
         static final int MSG_CHECK_TIMEOUT = 1;
-        static final int MSG_INFORM_LISTENER = 2;
+        static final int MSG_INFORM_LIMIT_REACHED_LISTENER = 2;
+        static final int MSG_INFORM_SESSION_END = 3;
 
         MyHandler(Looper looper) {
             super(looper);
@@ -132,10 +491,19 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_CHECK_TIMEOUT:
-                    checkTimeout((TimeLimitGroup) msg.obj);
+                    synchronized (mLock) {
+                        ((UsageGroup) msg.obj).checkTimeout(getUptimeMillis());
+                    }
                     break;
-                case MSG_INFORM_LISTENER:
-                    informListener((TimeLimitGroup) msg.obj);
+                case MSG_INFORM_LIMIT_REACHED_LISTENER:
+                    synchronized (mLock) {
+                        ((UsageGroup) msg.obj).onLimitReached();
+                    }
+                    break;
+                case MSG_INFORM_SESSION_END:
+                    synchronized (mLock) {
+                        ((SessionUsageGroup) msg.obj).onSessionEnd();
+                    }
                     break;
                 default:
                     super.handleMessage(msg);
@@ -144,7 +512,7 @@
         }
     }
 
-    public AppTimeLimitController(OnLimitReachedListener listener, Looper looper) {
+    public AppTimeLimitController(TimeLimitCallbackListener listener, Looper looper) {
         mHandler = new MyHandler(looper);
         mListener = listener;
     }
@@ -157,7 +525,13 @@
 
     /** Overrideable for testing purposes */
     @VisibleForTesting
-    protected long getObserverPerUidLimit() {
+    protected long getAppUsageObserverPerUidLimit() {
+        return MAX_OBSERVER_PER_UID;
+    }
+
+    /** Overrideable for testing purposes */
+    @VisibleForTesting
+    protected long getUsageSessionObserverPerUidLimit() {
         return MAX_OBSERVER_PER_UID;
     }
 
@@ -167,6 +541,21 @@
         return ONE_MINUTE;
     }
 
+    @VisibleForTesting
+    AppUsageGroup getAppUsageGroup(int observerAppUid, int observerId) {
+        synchronized (mLock) {
+            return getOrCreateObserverAppDataLocked(observerAppUid).appUsageGroups.get(observerId);
+        }
+    }
+
+    @VisibleForTesting
+    SessionUsageGroup getSessionUsageGroup(int observerAppUid, int observerId) {
+        synchronized (mLock) {
+            return getOrCreateObserverAppDataLocked(observerAppUid).sessionUsageGroups.get(
+                    observerId);
+        }
+    }
+
     /** Returns an existing UserData object for the given userId, or creates one */
     @GuardedBy("mLock")
     private UserData getOrCreateUserDataLocked(int userId) {
@@ -178,6 +567,17 @@
         return userData;
     }
 
+    /** Returns an existing ObserverAppData object for the given uid, or creates one */
+    @GuardedBy("mLock")
+    private ObserverAppData getOrCreateObserverAppDataLocked(int uid) {
+        ObserverAppData appData = mObserverApps.get(uid);
+        if (appData == null) {
+            appData = new ObserverAppData(uid);
+            mObserverApps.put(uid, appData);
+        }
+        return appData;
+    }
+
     /** Clean up data if user is removed */
     public void onUserRemoved(int userId) {
         synchronized (mLock) {
@@ -187,300 +587,219 @@
     }
 
     /**
-     * Registers an observer with the given details. Existing observer with the same observerId
-     * is removed.
+     * Check if group has any currently active entities.
      */
-    public void addObserver(int requestingUid, int observerId, String[] packages, long timeLimit,
-            PendingIntent callbackIntent, @UserIdInt int userId) {
+    @GuardedBy("mLock")
+    private void noteActiveLocked(UserData user, UsageGroup group, long currentTimeMs) {
+        // TODO: Consider using a bloom filter here if number of actives becomes large
+        final int size = group.mObserved.length;
+        for (int i = 0; i < size; i++) {
+            if (user.currentlyActive.contains(group.mObserved[i])) {
+                // Entity is currently active. Start group's usage.
+                group.noteUsageStart(currentTimeMs);
+            }
+        }
+    }
 
+    /**
+     * Registers an app usage observer with the given details.
+     * Existing app usage observer with the same observerId will be removed.
+     */
+    public void addAppUsageObserver(int requestingUid, int observerId, String[] observed,
+            long timeLimit, PendingIntent callbackIntent, @UserIdInt int userId) {
         if (timeLimit < getMinTimeLimit()) {
             throw new IllegalArgumentException("Time limit must be >= " + getMinTimeLimit());
         }
         synchronized (mLock) {
             UserData user = getOrCreateUserDataLocked(userId);
-            removeObserverLocked(user, requestingUid, observerId, /*readding =*/ true);
-
-            final int observerIdCount = user.observerIdCounts.get(requestingUid, 0);
-            if (observerIdCount >= getObserverPerUidLimit()) {
-                throw new IllegalStateException(
-                        "Too many observers added by uid " + requestingUid);
+            ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+            AppUsageGroup group = observerApp.appUsageGroups.get(observerId);
+            if (group != null) {
+                // Remove previous app usage group associated with observerId
+                observerApp.appUsageGroups.get(observerId).remove();
             }
-            user.observerIdCounts.put(requestingUid, observerIdCount + 1);
 
-            TimeLimitGroup group = new TimeLimitGroup();
-            group.observerId = observerId;
-            group.callbackIntent = callbackIntent;
-            group.packages = packages;
-            group.timeLimit = timeLimit;
-            group.timeRemaining = group.timeLimit;
-            group.timeRequested = getUptimeMillis();
-            group.requestingUid = requestingUid;
-            group.timeCurrentPackageStarted = -1L;
-            group.userId = userId;
-
-            user.groups.append(observerId, group);
-
-            addGroupToPackageMapLocked(user, packages, group);
+            final int observerIdCount = observerApp.appUsageGroups.size();
+            if (observerIdCount >= getAppUsageObserverPerUidLimit()) {
+                throw new IllegalStateException(
+                        "Too many app usage observers added by uid " + requestingUid);
+            }
+            group = new AppUsageGroup(user, observerApp, observerId, observed, timeLimit,
+                    callbackIntent);
+            observerApp.appUsageGroups.append(observerId, group);
 
             if (DEBUG) {
-                Slog.d(TAG, "addObserver " + packages + " for " + timeLimit);
+                Slog.d(TAG, "addObserver " + observed + " for " + timeLimit);
             }
-            // Handle the case where a target package is already in the foreground when observer
-            // is added.
-            if (user.currentForegroundedPackage != null && inPackageList(group.packages,
-                    user.currentForegroundedPackage)) {
-                group.timeCurrentPackageStarted = group.timeRequested;
-                group.currentPackage = user.currentForegroundedPackage;
-                if (group.timeRemaining > 0) {
-                    postCheckTimeoutLocked(group, group.timeRemaining);
-                }
-            }
+
+            user.addUsageGroup(group);
+            noteActiveLocked(user, group, getUptimeMillis());
         }
     }
 
     /**
      * Remove a registered observer by observerId and calling uid.
+     *
      * @param requestingUid The calling uid
-     * @param observerId The unique observer id for this user
-     * @param userId The user id of the observer
+     * @param observerId    The unique observer id for this user
+     * @param userId        The user id of the observer
      */
-    public void removeObserver(int requestingUid, int observerId, @UserIdInt int userId) {
+    public void removeAppUsageObserver(int requestingUid, int observerId, @UserIdInt int userId) {
+        synchronized (mLock) {
+            ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+            observerApp.appUsageGroups.get(observerId).remove();
+        }
+    }
+
+
+    /**
+     * Registers a usage session observer with the given details.
+     * Existing usage session observer with the same observerId will be removed.
+     */
+    public void addUsageSessionObserver(int requestingUid, int observerId, String[] observed,
+            long timeLimit, long sessionThresholdTime,
+            PendingIntent limitReachedCallbackIntent, PendingIntent sessionEndCallbackIntent,
+            @UserIdInt int userId) {
+        if (timeLimit < getMinTimeLimit()) {
+            throw new IllegalArgumentException("Time limit must be >= " + getMinTimeLimit());
+        }
         synchronized (mLock) {
             UserData user = getOrCreateUserDataLocked(userId);
-            removeObserverLocked(user, requestingUid, observerId, /*readding =*/ false);
+            ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+            SessionUsageGroup group = observerApp.sessionUsageGroups.get(observerId);
+            if (group != null) {
+                // Remove previous app usage group associated with observerId
+                observerApp.sessionUsageGroups.get(observerId).remove();
+            }
+
+            final int observerIdCount = observerApp.sessionUsageGroups.size();
+            if (observerIdCount >= getUsageSessionObserverPerUidLimit()) {
+                throw new IllegalStateException(
+                        "Too many app usage observers added by uid " + requestingUid);
+            }
+            group = new SessionUsageGroup(user, observerApp, observerId, observed, timeLimit,
+                    limitReachedCallbackIntent, sessionThresholdTime, sessionEndCallbackIntent);
+            observerApp.sessionUsageGroups.append(observerId, group);
+
+            user.addUsageGroup(group);
+            noteActiveLocked(user, group, getUptimeMillis());
         }
     }
 
-    @VisibleForTesting
-    TimeLimitGroup getObserverGroup(int observerId, int userId) {
+    /**
+     * Remove a registered observer by observerId and calling uid.
+     *
+     * @param requestingUid The calling uid
+     * @param observerId    The unique observer id for this user
+     * @param userId        The user id of the observer
+     */
+    public void removeUsageSessionObserver(int requestingUid, int observerId,
+            @UserIdInt int userId) {
         synchronized (mLock) {
-            return getOrCreateUserDataLocked(userId).groups.get(observerId);
+            ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+            observerApp.sessionUsageGroups.get(observerId).remove();
         }
     }
 
-    private static boolean inPackageList(String[] packages, String packageName) {
-        return ArrayUtils.contains(packages, packageName);
+    /**
+     * Called when an entity becomes active.
+     *
+     * @param name   The entity that became active
+     * @param userId The user
+     */
+    public void noteUsageStart(String name, int userId) throws IllegalArgumentException {
+        synchronized (mLock) {
+            UserData user = getOrCreateUserDataLocked(userId);
+            if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became active");
+            if (user.currentlyActive.contains(name)) {
+                throw new IllegalArgumentException(
+                        "Unable to start usage for " + name + ", already in use");
+            }
+            final long currentTime = getUptimeMillis();
+
+            // Add to the list of active entities
+            user.currentlyActive.add(name);
+
+            ArrayList<UsageGroup> groups = user.observedMap.get(name);
+            if (groups == null) return;
+
+            final int size = groups.size();
+            for (int i = 0; i < size; i++) {
+                UsageGroup group = groups.get(i);
+                group.noteUsageStart(currentTime);
+            }
+        }
+    }
+
+    /**
+     * Called when an entity becomes inactive.
+     *
+     * @param name   The entity that became inactive
+     * @param userId The user
+     */
+    public void noteUsageStop(String name, int userId) throws IllegalArgumentException {
+        synchronized (mLock) {
+            UserData user = getOrCreateUserDataLocked(userId);
+            if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became inactive");
+            if (!user.currentlyActive.remove(name)) {
+                throw new IllegalArgumentException(
+                        "Unable to stop usage for " + name + ", not in use");
+            }
+            final long currentTime = getUptimeMillis();
+
+            // Check if any of the groups need to watch for this entity
+            ArrayList<UsageGroup> groups = user.observedMap.get(name);
+            if (groups == null) return;
+
+            final int size = groups.size();
+            for (int i = 0; i < size; i++) {
+                UsageGroup group = groups.get(i);
+                group.noteUsageStop(currentTime);
+            }
+        }
     }
 
     @GuardedBy("mLock")
-    private void removeObserverLocked(UserData user, int requestingUid, int observerId,
-            boolean readding) {
-        TimeLimitGroup group = user.groups.get(observerId);
-        if (group != null && group.requestingUid == requestingUid) {
-            removeGroupFromPackageMapLocked(user, group);
-            user.groups.remove(observerId);
-            mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
-            final int observerIdCount = user.observerIdCounts.get(requestingUid);
-            if (observerIdCount <= 1 && !readding) {
-                user.observerIdCounts.delete(requestingUid);
-            } else {
-                user.observerIdCounts.put(requestingUid, observerIdCount - 1);
-            }
-        }
-    }
-
-    /**
-     * Called when an app has moved to the foreground.
-     * @param packageName The app that is foregrounded
-     * @param className The className of the activity
-     * @param userId The user
-     */
-    public void moveToForeground(String packageName, String className, int userId) {
-        synchronized (mLock) {
-            UserData user = getOrCreateUserDataLocked(userId);
-            if (DEBUG) Slog.d(TAG, "Setting mCurrentForegroundedPackage to " + packageName);
-            // Note the current foreground package
-            user.currentForegroundedPackage = packageName;
-            user.currentForegroundedTime = getUptimeMillis();
-
-            // Check if any of the groups need to watch for this package
-            maybeWatchForPackageLocked(user, packageName, user.currentForegroundedTime);
-        }
-    }
-
-    /**
-     * Called when an app is sent to the background.
-     *
-     * @param packageName
-     * @param className
-     * @param userId
-     */
-    public void moveToBackground(String packageName, String className, int userId) {
-        synchronized (mLock) {
-            UserData user = getOrCreateUserDataLocked(userId);
-            if (!TextUtils.equals(user.currentForegroundedPackage, packageName)) {
-                Slog.w(TAG, "Eh? Last foregrounded package = " + user.currentForegroundedPackage
-                        + " and now backgrounded = " + packageName);
-                return;
-            }
-            final long stopTime = getUptimeMillis();
-
-            // Add up the usage time to all groups that contain the package
-            ArrayList<TimeLimitGroup> groups = user.packageMap.get(packageName);
-            if (groups != null) {
-                final int size = groups.size();
-                for (int i = 0; i < size; i++) {
-                    final TimeLimitGroup group = groups.get(i);
-                    // Don't continue to send
-                    if (group.timeRemaining <= 0) continue;
-
-                    final long startTime = Math.max(user.currentForegroundedTime,
-                            group.timeRequested);
-                    long diff = stopTime - startTime;
-                    group.timeRemaining -= diff;
-                    if (group.timeRemaining <= 0) {
-                        if (DEBUG) Slog.d(TAG, "MTB informing group obs=" + group.observerId);
-                        postInformListenerLocked(group);
-                    }
-                    // Reset indicators that observer was added when package was already fg
-                    group.currentPackage = null;
-                    group.timeCurrentPackageStarted = -1L;
-                    mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
-                }
-            }
-            user.currentForegroundedPackage = null;
-        }
-    }
-
-    private void postInformListenerLocked(TimeLimitGroup group) {
-        mHandler.sendMessage(mHandler.obtainMessage(MyHandler.MSG_INFORM_LISTENER,
+    private void postInformLimitReachedListenerLocked(UsageGroup group) {
+        mHandler.sendMessage(mHandler.obtainMessage(MyHandler.MSG_INFORM_LIMIT_REACHED_LISTENER,
                 group));
     }
 
-    /**
-     * Inform the observer and unregister it, as the limit has been reached.
-     * @param group the observed group
-     */
-    private void informListener(TimeLimitGroup group) {
-        if (mListener != null) {
-            mListener.onLimitReached(group.observerId, group.userId, group.timeLimit,
-                    group.timeLimit - group.timeRemaining, group.callbackIntent);
-        }
-        // Unregister since the limit has been met and observer was informed.
-        synchronized (mLock) {
-            UserData user = getOrCreateUserDataLocked(group.userId);
-            removeObserverLocked(user, group.requestingUid, group.observerId, false);
-        }
-    }
-
-    /** Check if any of the groups care about this package and set up delayed messages */
     @GuardedBy("mLock")
-    private void maybeWatchForPackageLocked(UserData user, String packageName, long uptimeMillis) {
-        ArrayList<TimeLimitGroup> groups = user.packageMap.get(packageName);
-        if (groups == null) return;
-
-        final int size = groups.size();
-        for (int i = 0; i < size; i++) {
-            TimeLimitGroup group = groups.get(i);
-            if (group.timeRemaining > 0) {
-                group.timeCurrentPackageStarted = uptimeMillis;
-                group.currentPackage = packageName;
-                if (DEBUG) {
-                    Slog.d(TAG, "Posting timeout for " + packageName + " for "
-                            + group.timeRemaining + "ms");
-                }
-                postCheckTimeoutLocked(group, group.timeRemaining);
-            }
-        }
+    private void postInformSessionEndListenerLocked(SessionUsageGroup group, long timeout) {
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group),
+                timeout);
     }
 
-    private void addGroupToPackageMapLocked(UserData user, String[] packages,
-            TimeLimitGroup group) {
-        for (int i = 0; i < packages.length; i++) {
-            ArrayList<TimeLimitGroup> list = user.packageMap.get(packages[i]);
-            if (list == null) {
-                list = new ArrayList<>();
-                user.packageMap.put(packages[i], list);
-            }
-            list.add(group);
-        }
+    @GuardedBy("mLock")
+    private void cancelInformSessionEndListener(SessionUsageGroup group) {
+        mHandler.removeMessages(MyHandler.MSG_INFORM_SESSION_END, group);
     }
 
-    /**
-     * Remove the group reference from the package to group mapping, which is 1 to many.
-     * @param group The group to remove from the package map.
-     */
-    private void removeGroupFromPackageMapLocked(UserData user, TimeLimitGroup group) {
-        final int mapSize = user.packageMap.size();
-        for (int i = 0; i < mapSize; i++) {
-            ArrayList<TimeLimitGroup> list = user.packageMap.valueAt(i);
-            list.remove(group);
-        }
-    }
-
-    private void postCheckTimeoutLocked(TimeLimitGroup group, long timeout) {
+    @GuardedBy("mLock")
+    private void postCheckTimeoutLocked(UsageGroup group, long timeout) {
         mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_CHECK_TIMEOUT, group),
                 timeout);
     }
 
-    /**
-     * See if the given group has reached the timeout if the current foreground app is included
-     * and it exceeds the time remaining.
-     * @param group the group of packages to check
-     */
-    void checkTimeout(TimeLimitGroup group) {
-        // For each package in the group, check if any of the currently foregrounded apps are adding
-        // up to hit the limit and inform the observer
-        synchronized (mLock) {
-            UserData user = getOrCreateUserDataLocked(group.userId);
-            // This group doesn't exist anymore, nothing to see here.
-            if (user.groups.get(group.observerId) != group) return;
-
-            if (DEBUG) Slog.d(TAG, "checkTimeout timeRemaining=" + group.timeRemaining);
-
-            // Already reached the limit, no need to report again
-            if (group.timeRemaining <= 0) return;
-
-            if (DEBUG) {
-                Slog.d(TAG, "checkTimeout foregroundedPackage="
-                        + user.currentForegroundedPackage);
-            }
-
-            if (inPackageList(group.packages, user.currentForegroundedPackage)) {
-                if (DEBUG) {
-                    Slog.d(TAG, "checkTimeout package in foreground="
-                            + user.currentForegroundedPackage);
-                }
-                if (group.timeCurrentPackageStarted < 0) {
-                    Slog.w(TAG, "startTime was not set correctly for " + group);
-                }
-                final long timeInForeground = getUptimeMillis() - group.timeCurrentPackageStarted;
-                if (group.timeRemaining <= timeInForeground) {
-                    if (DEBUG) Slog.d(TAG, "checkTimeout : Time limit reached");
-                    // Hit the limit, set timeRemaining to zero to avoid checking again
-                    group.timeRemaining -= timeInForeground;
-                    postInformListenerLocked(group);
-                    // Reset
-                    group.timeCurrentPackageStarted = -1L;
-                    group.currentPackage = null;
-                } else {
-                    if (DEBUG) Slog.d(TAG, "checkTimeout : Some more time remaining");
-                    postCheckTimeoutLocked(group, group.timeRemaining - timeInForeground);
-                }
-            }
-        }
+    @GuardedBy("mLock")
+    private void cancelCheckTimeoutLocked(UsageGroup group) {
+        mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
     }
 
     void dump(PrintWriter pw) {
         synchronized (mLock) {
             pw.println("\n  App Time Limits");
-            int nUsers = mUsers.size();
+            final int nUsers = mUsers.size();
             for (int i = 0; i < nUsers; i++) {
-                UserData user = mUsers.valueAt(i);
-                pw.print("   User "); pw.println(user.userId);
-                int nGroups = user.groups.size();
-                for (int j = 0; j < nGroups; j++) {
-                    TimeLimitGroup group = user.groups.valueAt(j);
-                    pw.print("    Group id="); pw.print(group.observerId);
-                    pw.print(" timeLimit="); pw.print(group.timeLimit);
-                    pw.print(" remaining="); pw.print(group.timeRemaining);
-                    pw.print(" currentPackage="); pw.print(group.currentPackage);
-                    pw.print(" timeCurrentPkgStarted="); pw.print(group.timeCurrentPackageStarted);
-                    pw.print(" packages="); pw.println(Arrays.toString(group.packages));
-                }
-                pw.println();
-                pw.print("    currentForegroundedPackage=");
-                pw.println(user.currentForegroundedPackage);
+                pw.print("   User ");
+                mUsers.valueAt(i).dump(pw);
+            }
+            pw.println();
+            final int nObserverApps = mObserverApps.size();
+            for (int i = 0; i < nObserverApps; i++) {
+                pw.print("   Observer App ");
+                mObserverApps.valueAt(i).dump(pw);
             }
         }
     }
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index d12eda75..faf6ee2 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -33,6 +33,7 @@
 import android.content.pm.UserInfo;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -165,7 +166,8 @@
     @Override
     public boolean isReservedSupported(String volumeUuid, String callingPackage) {
         if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
-            return SystemProperties.getBoolean(StorageManager.PROP_HAS_RESERVED, false);
+            return SystemProperties.getBoolean(StorageManager.PROP_HAS_RESERVED, false)
+                    || Build.IS_CONTAINER;
         } else {
             return false;
         }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index dd1ddfa..2621252 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -165,16 +165,36 @@
         mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
 
         mAppTimeLimit = new AppTimeLimitController(
-                (observerId, userId, timeLimit, timeElapsed, callbackIntent) -> {
-                    Intent intent = new Intent();
-                    intent.putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId);
-                    intent.putExtra(UsageStatsManager.EXTRA_TIME_LIMIT, timeLimit);
-                    intent.putExtra(UsageStatsManager.EXTRA_TIME_USED, timeElapsed);
-                    try {
-                        callbackIntent.send(getContext(), 0, intent);
-                    } catch (PendingIntent.CanceledException e) {
-                        Slog.w(TAG, "Couldn't deliver callback: "
-                                + callbackIntent);
+                new AppTimeLimitController.TimeLimitCallbackListener() {
+                    @Override
+                    public void onLimitReached(int observerId, int userId, long timeLimit,
+                            long timeElapsed, PendingIntent callbackIntent) {
+                        if (callbackIntent == null) return;
+                        Intent intent = new Intent();
+                        intent.putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId);
+                        intent.putExtra(UsageStatsManager.EXTRA_TIME_LIMIT, timeLimit);
+                        intent.putExtra(UsageStatsManager.EXTRA_TIME_USED, timeElapsed);
+                        try {
+                            callbackIntent.send(getContext(), 0, intent);
+                        } catch (PendingIntent.CanceledException e) {
+                            Slog.w(TAG, "Couldn't deliver callback: "
+                                    + callbackIntent);
+                        }
+                    }
+
+                    @Override
+                    public void onSessionEnd(int observerId, int userId, long timeElapsed,
+                            PendingIntent callbackIntent) {
+                        if (callbackIntent == null) return;
+                        Intent intent = new Intent();
+                        intent.putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId);
+                        intent.putExtra(UsageStatsManager.EXTRA_TIME_USED, timeElapsed);
+                        try {
+                            callbackIntent.send(getContext(), 0, intent);
+                        } catch (PendingIntent.CanceledException e) {
+                            Slog.w(TAG, "Couldn't deliver callback: "
+                                    + callbackIntent);
+                        }
                     }
                 }, mHandler.getLooper());
 
@@ -412,12 +432,18 @@
             mAppStandby.reportEvent(event, elapsedRealtime, userId);
             switch (event.mEventType) {
                 case Event.MOVE_TO_FOREGROUND:
-                    mAppTimeLimit.moveToForeground(event.getPackageName(), event.getClassName(),
-                            userId);
+                    try {
+                        mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
+                    } catch (IllegalArgumentException iae) {
+                        Slog.e(TAG, "Failed to note usage start", iae);
+                    }
                     break;
                 case Event.MOVE_TO_BACKGROUND:
-                    mAppTimeLimit.moveToBackground(event.getPackageName(), event.getClassName(),
-                            userId);
+                    try {
+                        mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
+                    } catch (IllegalArgumentException iae) {
+                        Slog.e(TAG, "Failed to note usage stop", iae);
+                    }
                     break;
             }
         }
@@ -1151,16 +1177,70 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override
+        public void registerUsageSessionObserver(int sessionObserverId, String[] observed,
+                long timeLimitMs, long sessionThresholdTimeMs,
+                PendingIntent limitReachedCallbackIntent, PendingIntent sessionEndCallbackIntent,
+                String callingPackage) {
+            if (!hasObserverPermission(callingPackage)) {
+                throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
+            }
+
+            if (observed == null || observed.length == 0) {
+                throw new IllegalArgumentException("Must specify at least one observed entity");
+            }
+            if (limitReachedCallbackIntent == null) {
+                throw new NullPointerException("limitReachedCallbackIntent can't be null");
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                UsageStatsService.this.registerUsageSessionObserver(callingUid, sessionObserverId,
+                        observed, timeLimitMs, sessionThresholdTimeMs, limitReachedCallbackIntent,
+                        sessionEndCallbackIntent, userId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage) {
+            if (!hasObserverPermission(callingPackage)) {
+                throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
+            }
+
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId, userId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     }
 
     void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
             long timeLimitMs, PendingIntent callbackIntent, int userId) {
-        mAppTimeLimit.addObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
+        mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
                 userId);
     }
 
     void unregisterAppUsageObserver(int callingUid, int observerId, int userId) {
-        mAppTimeLimit.removeObserver(callingUid, observerId, userId);
+        mAppTimeLimit.removeAppUsageObserver(callingUid, observerId, userId);
+    }
+
+    void registerUsageSessionObserver(int callingUid, int observerId, String[] observed,
+            long timeLimitMs, long sessionThresholdTime, PendingIntent limitReachedCallbackIntent,
+            PendingIntent sessionEndCallbackIntent, int userId) {
+        mAppTimeLimit.addUsageSessionObserver(callingUid, observerId, observed, timeLimitMs,
+                sessionThresholdTime, limitReachedCallbackIntent, sessionEndCallbackIntent, userId);
+    }
+
+    void unregisterUsageSessionObserver(int callingUid, int sessionObserverId, int userId) {
+        mAppTimeLimit.removeUsageSessionObserver(callingUid, sessionObserverId, userId);
     }
 
     /**
diff --git a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
deleted file mode 100644
index 3b08505..0000000
--- a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * Copyright (C) 2012 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 an
- * limitations under the License.
- */
-
-package com.android.server.usb;
-
-import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
-
-import android.app.ActivityManager;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
-import android.os.Environment;
-import android.os.FileUtils;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.service.usb.UsbDebuggingManagerProto;
-import android.util.Base64;
-import android.util.Slog;
-
-import com.android.internal.R;
-import com.android.internal.util.dump.DualDumpOutputStream;
-import com.android.server.FgThread;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.MessageDigest;
-import java.util.Arrays;
-
-public class UsbDebuggingManager {
-    private static final String TAG = "UsbDebuggingManager";
-    private static final boolean DEBUG = false;
-
-    private static final String ADBD_SOCKET = "adbd";
-    private static final String ADB_DIRECTORY = "misc/adb";
-    private static final String ADB_KEYS_FILE = "adb_keys";
-    private static final int BUFFER_SIZE = 4096;
-
-    private final Context mContext;
-    private final Handler mHandler;
-    private UsbDebuggingThread mThread;
-    private boolean mAdbEnabled = false;
-    private String mFingerprints;
-
-    public UsbDebuggingManager(Context context) {
-        mHandler = new UsbDebuggingHandler(FgThread.get().getLooper());
-        mContext = context;
-    }
-
-    class UsbDebuggingThread extends Thread {
-        private boolean mStopped;
-        private LocalSocket mSocket;
-        private OutputStream mOutputStream;
-        private InputStream mInputStream;
-
-        UsbDebuggingThread() {
-            super(TAG);
-        }
-
-        @Override
-        public void run() {
-            if (DEBUG) Slog.d(TAG, "Entering thread");
-            while (true) {
-                synchronized (this) {
-                    if (mStopped) {
-                        if (DEBUG) Slog.d(TAG, "Exiting thread");
-                        return;
-                    }
-                    try {
-                        openSocketLocked();
-                    } catch (Exception e) {
-                        /* Don't loop too fast if adbd dies, before init restarts it */
-                        SystemClock.sleep(1000);
-                    }
-                }
-                try {
-                    listenToSocket();
-                } catch (Exception e) {
-                    /* Don't loop too fast if adbd dies, before init restarts it */
-                    SystemClock.sleep(1000);
-                }
-            }
-        }
-
-        private void openSocketLocked() throws IOException {
-            try {
-                LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET,
-                        LocalSocketAddress.Namespace.RESERVED);
-                mInputStream = null;
-
-                if (DEBUG) Slog.d(TAG, "Creating socket");
-                mSocket = new LocalSocket();
-                mSocket.connect(address);
-
-                mOutputStream = mSocket.getOutputStream();
-                mInputStream = mSocket.getInputStream();
-            } catch (IOException ioe) {
-                closeSocketLocked();
-                throw ioe;
-            }
-        }
-
-        private void listenToSocket() throws IOException {
-            try {
-                byte[] buffer = new byte[BUFFER_SIZE];
-                while (true) {
-                    int count = mInputStream.read(buffer);
-                    if (count < 0) {
-                        break;
-                    }
-
-                    if (buffer[0] == 'P' && buffer[1] == 'K') {
-                        String key = new String(Arrays.copyOfRange(buffer, 2, count));
-                        Slog.d(TAG, "Received public key: " + key);
-                        Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_CONFIRM);
-                        msg.obj = key;
-                        mHandler.sendMessage(msg);
-                    } else {
-                        Slog.e(TAG, "Wrong message: "
-                                + (new String(Arrays.copyOfRange(buffer, 0, 2))));
-                        break;
-                    }
-                }
-            } finally {
-                synchronized (this) {
-                    closeSocketLocked();
-                }
-            }
-        }
-
-        private void closeSocketLocked() {
-            if (DEBUG) Slog.d(TAG, "Closing socket");
-            try {
-                if (mOutputStream != null) {
-                    mOutputStream.close();
-                    mOutputStream = null;
-                }
-            } catch (IOException e) {
-                Slog.e(TAG, "Failed closing output stream: " + e);
-            }
-
-            try {
-                if (mSocket != null) {
-                    mSocket.close();
-                    mSocket = null;
-                }
-            } catch (IOException ex) {
-                Slog.e(TAG, "Failed closing socket: " + ex);
-            }
-        }
-
-        /** Call to stop listening on the socket and exit the thread. */
-        void stopListening() {
-            synchronized (this) {
-                mStopped = true;
-                closeSocketLocked();
-            }
-        }
-
-        void sendResponse(String msg) {
-            synchronized (this) {
-                if (!mStopped && mOutputStream != null) {
-                    try {
-                        mOutputStream.write(msg.getBytes());
-                    }
-                    catch (IOException ex) {
-                        Slog.e(TAG, "Failed to write response:", ex);
-                    }
-                }
-            }
-        }
-    }
-
-    class UsbDebuggingHandler extends Handler {
-        private static final int MESSAGE_ADB_ENABLED = 1;
-        private static final int MESSAGE_ADB_DISABLED = 2;
-        private static final int MESSAGE_ADB_ALLOW = 3;
-        private static final int MESSAGE_ADB_DENY = 4;
-        private static final int MESSAGE_ADB_CONFIRM = 5;
-        private static final int MESSAGE_ADB_CLEAR = 6;
-
-        public UsbDebuggingHandler(Looper looper) {
-            super(looper);
-        }
-
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_ADB_ENABLED:
-                    if (mAdbEnabled)
-                        break;
-
-                    mAdbEnabled = true;
-
-                    mThread = new UsbDebuggingThread();
-                    mThread.start();
-
-                    break;
-
-                case MESSAGE_ADB_DISABLED:
-                    if (!mAdbEnabled)
-                        break;
-
-                    mAdbEnabled = false;
-
-                    if (mThread != null) {
-                        mThread.stopListening();
-                        mThread = null;
-                    }
-
-                    break;
-
-                case MESSAGE_ADB_ALLOW: {
-                    String key = (String)msg.obj;
-                    String fingerprints = getFingerprints(key);
-
-                    if (!fingerprints.equals(mFingerprints)) {
-                        Slog.e(TAG, "Fingerprints do not match. Got "
-                                + fingerprints + ", expected " + mFingerprints);
-                        break;
-                    }
-
-                    if (msg.arg1 == 1) {
-                        writeKey(key);
-                    }
-
-                    if (mThread != null) {
-                        mThread.sendResponse("OK");
-                    }
-                    break;
-                }
-
-                case MESSAGE_ADB_DENY:
-                    if (mThread != null) {
-                        mThread.sendResponse("NO");
-                    }
-                    break;
-
-                case MESSAGE_ADB_CONFIRM: {
-                    if ("trigger_restart_min_framework".equals(
-                            SystemProperties.get("vold.decrypt"))) {
-                        Slog.d(TAG, "Deferring adb confirmation until after vold decrypt");
-                        if (mThread != null) {
-                            mThread.sendResponse("NO");
-                        }
-                        break;
-                    }
-                    String key = (String)msg.obj;
-                    String fingerprints = getFingerprints(key);
-                    if ("".equals(fingerprints)) {
-                        if (mThread != null) {
-                            mThread.sendResponse("NO");
-                        }
-                        break;
-                    }
-                    mFingerprints = fingerprints;
-                    startConfirmation(key, mFingerprints);
-                    break;
-                }
-
-                case MESSAGE_ADB_CLEAR:
-                    deleteKeyFile();
-                    break;
-            }
-        }
-    }
-
-    private String getFingerprints(String key) {
-        String hex = "0123456789ABCDEF";
-        StringBuilder sb = new StringBuilder();
-        MessageDigest digester;
-
-        if (key == null) {
-            return "";
-        }
-
-        try {
-            digester = MessageDigest.getInstance("MD5");
-        } catch (Exception ex) {
-            Slog.e(TAG, "Error getting digester", ex);
-            return "";
-        }
-
-        byte[] base64_data = key.split("\\s+")[0].getBytes();
-        byte[] digest;
-        try {
-            digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));
-        } catch (IllegalArgumentException e) {
-            Slog.e(TAG, "error doing base64 decoding", e);
-            return "";
-        }
-        for (int i = 0; i < digest.length; i++) {
-            sb.append(hex.charAt((digest[i] >> 4) & 0xf));
-            sb.append(hex.charAt(digest[i] & 0xf));
-            if (i < digest.length - 1)
-                sb.append(":");
-        }
-        return sb.toString();
-    }
-
-    private void startConfirmation(String key, String fingerprints) {
-        int currentUserId = ActivityManager.getCurrentUser();
-        UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
-        String componentString;
-        if (userInfo.isAdmin()) {
-            componentString = Resources.getSystem().getString(
-                    com.android.internal.R.string.config_customAdbPublicKeyConfirmationComponent);
-        } else {
-            // If the current foreground user is not the admin user we send a different
-            // notification specific to secondary users.
-            componentString = Resources.getSystem().getString(
-                    R.string.config_customAdbPublicKeyConfirmationSecondaryUserComponent);
-        }
-        ComponentName componentName = ComponentName.unflattenFromString(componentString);
-        if (startConfirmationActivity(componentName, userInfo.getUserHandle(), key, fingerprints)
-                || startConfirmationService(componentName, userInfo.getUserHandle(),
-                        key, fingerprints)) {
-            return;
-        }
-        Slog.e(TAG, "unable to start customAdbPublicKeyConfirmation[SecondaryUser]Component "
-                + componentString + " as an Activity or a Service");
-    }
-
-    /**
-     * @return true if the componentName led to an Activity that was started.
-     */
-    private boolean startConfirmationActivity(ComponentName componentName, UserHandle userHandle,
-            String key, String fingerprints) {
-        PackageManager packageManager = mContext.getPackageManager();
-        Intent intent = createConfirmationIntent(componentName, key, fingerprints);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
-            try {
-                mContext.startActivityAsUser(intent, userHandle);
-                return true;
-            } catch (ActivityNotFoundException e) {
-                Slog.e(TAG, "unable to start adb whitelist activity: " + componentName, e);
-            }
-        }
-        return false;
-    }
-
-    /**
-     * @return true if the componentName led to a Service that was started.
-     */
-    private boolean startConfirmationService(ComponentName componentName, UserHandle userHandle,
-            String key, String fingerprints) {
-        Intent intent = createConfirmationIntent(componentName, key, fingerprints);
-        try {
-            if (mContext.startServiceAsUser(intent, userHandle) != null) {
-                return true;
-            }
-        } catch (SecurityException e) {
-            Slog.e(TAG, "unable to start adb whitelist service: " + componentName, e);
-        }
-        return false;
-    }
-
-    private Intent createConfirmationIntent(ComponentName componentName, String key,
-            String fingerprints) {
-        Intent intent = new Intent();
-        intent.setClassName(componentName.getPackageName(), componentName.getClassName());
-        intent.putExtra("key", key);
-        intent.putExtra("fingerprints", fingerprints);
-        return intent;
-    }
-
-    private File getUserKeyFile() {
-        File dataDir = Environment.getDataDirectory();
-        File adbDir = new File(dataDir, ADB_DIRECTORY);
-
-        if (!adbDir.exists()) {
-            Slog.e(TAG, "ADB data directory does not exist");
-            return null;
-        }
-
-        return new File(adbDir, ADB_KEYS_FILE);
-    }
-
-    private void writeKey(String key) {
-        try {
-            File keyFile = getUserKeyFile();
-
-            if (keyFile == null) {
-                return;
-            }
-
-            if (!keyFile.exists()) {
-                keyFile.createNewFile();
-                FileUtils.setPermissions(keyFile.toString(),
-                    FileUtils.S_IRUSR | FileUtils.S_IWUSR |
-                    FileUtils.S_IRGRP, -1, -1);
-            }
-
-            FileOutputStream fo = new FileOutputStream(keyFile, true);
-            fo.write(key.getBytes());
-            fo.write('\n');
-            fo.close();
-        }
-        catch (IOException ex) {
-            Slog.e(TAG, "Error writing key:" + ex);
-        }
-    }
-
-    private void deleteKeyFile() {
-        File keyFile = getUserKeyFile();
-        if (keyFile != null) {
-            keyFile.delete();
-        }
-    }
-
-    public void setAdbEnabled(boolean enabled) {
-        mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED
-                                          : UsbDebuggingHandler.MESSAGE_ADB_DISABLED);
-    }
-
-    public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
-        Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW);
-        msg.arg1 = alwaysAllow ? 1 : 0;
-        msg.obj = publicKey;
-        mHandler.sendMessage(msg);
-    }
-
-    public void denyUsbDebugging() {
-        mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY);
-    }
-
-    public void clearUsbDebuggingKeys() {
-        mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_CLEAR);
-    }
-
-    /**
-     * Dump the USB debugging state.
-     */
-    public void dump(DualDumpOutputStream dump, String idName, long id) {
-        long token = dump.start(idName, id);
-
-        dump.write("connected_to_adb", UsbDebuggingManagerProto.CONNECTED_TO_ADB, mThread != null);
-        writeStringIfNotNull(dump, "last_key_received", UsbDebuggingManagerProto.LAST_KEY_RECEIVED,
-                mFingerprints);
-
-        try {
-            dump.write("user_keys", UsbDebuggingManagerProto.USER_KEYS,
-                    FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
-        } catch (IOException e) {
-            Slog.e(TAG, "Cannot read user keys", e);
-        }
-
-        try {
-            dump.write("system_keys", UsbDebuggingManagerProto.SYSTEM_KEYS,
-                    FileUtils.readTextFile(new File("/adb_keys"), 0, null));
-        } catch (IOException e) {
-            Slog.e(TAG, "Cannot read system keys", e);
-        }
-
-        dump.end(token);
-    }
-}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 9918395..27b8cdf 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -34,7 +34,8 @@
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
-import android.database.ContentObserver;
+import android.debug.AdbManagerInternal;
+import android.debug.IAdbTransport;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbConfiguration;
 import android.hardware.usb.UsbConstants;
@@ -178,7 +179,6 @@
     private final boolean mHasUsbAccessory;
     @GuardedBy("mLock")
     private String[] mAccessoryStrings;
-    private UsbDebuggingManager mDebuggingManager;
     private final UEventObserver mUEventObserver;
 
     private static Set<Integer> sBlackListedInterfaces;
@@ -199,19 +199,6 @@
         sBlackListedInterfaces.add(UsbConstants.USB_CLASS_WIRELESS_CONTROLLER);
     }
 
-    private class AdbSettingsObserver extends ContentObserver {
-        public AdbSettingsObserver() {
-            super(null);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            boolean enable = (Settings.Global.getInt(mContentResolver,
-                    Settings.Global.ADB_ENABLED, 0) > 0);
-            mHandler.sendMessage(MSG_ENABLE_ADB, enable);
-        }
-    }
-
     /*
      * Listens for uevent messages from the kernel to monitor the USB state
      */
@@ -284,24 +271,18 @@
         }
         mControlFds.put(UsbManager.FUNCTION_PTP, ptpFd);
 
-        boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
-        boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
-        if (secureAdbEnabled && !dataEncrypted) {
-            mDebuggingManager = new UsbDebuggingManager(context);
-        }
-
         if (halNotPresent) {
             /**
              * Initialze the legacy UsbHandler
              */
             mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext, this,
-                    mDebuggingManager, alsaManager, settingsManager);
+                    alsaManager, settingsManager);
         } else {
             /**
              * Initialize HAL based UsbHandler
              */
             mHandler = new UsbHandlerHal(FgThread.get().getLooper(), mContext, this,
-                    mDebuggingManager, alsaManager, settingsManager);
+                    alsaManager, settingsManager);
         }
 
         if (nativeIsStartRequested()) {
@@ -364,11 +345,6 @@
         mUEventObserver = new UsbUEventObserver();
         mUEventObserver.startObserving(USB_STATE_MATCH);
         mUEventObserver.startObserving(ACCESSORY_START_MATCH);
-
-        // register observer to listen for settings changes
-        mContentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
-                false, new AdbSettingsObserver());
     }
 
     UsbProfileGroupSettingsManager getCurrentSettings() {
@@ -482,13 +458,11 @@
         private int mMidiDevice;
 
         private final Context mContext;
-        private final UsbDebuggingManager mDebuggingManager;
         private final UsbAlsaManager mUsbAlsaManager;
         private final UsbSettingsManager mSettingsManager;
         private NotificationManager mNotificationManager;
 
         protected long mScreenUnlockedFunctions;
-        protected boolean mAdbEnabled;
         protected boolean mBootCompleted;
         protected boolean mCurrentFunctionsApplied;
         protected boolean mUseUsbNotification;
@@ -506,11 +480,9 @@
         protected static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
 
         UsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager,
-                UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager,
-                UsbSettingsManager settingsManager) {
+                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
             super(looper);
             mContext = context;
-            mDebuggingManager = debuggingManager;
             mUsbDeviceManager = deviceManager;
             mUsbAlsaManager = alsaManager;
             mSettingsManager = settingsManager;
@@ -519,13 +491,6 @@
             mCurrentUser = ActivityManager.getCurrentUser();
             mScreenLocked = true;
 
-            /*
-             * Use the normal bootmode persistent prop to maintain state of adb across
-             * all boot modes.
-             */
-            mAdbEnabled = UsbHandlerLegacy.containsFunction(getSystemProperty(
-                    USB_PERSISTENT_CONFIG_PROPERTY, ""), UsbManager.USB_FUNCTION_ADB);
-
             mSettings = getPinnedSharedPrefs(mContext);
             if (mSettings == null) {
                 Slog.e(TAG, "Couldn't load shared preferences");
@@ -626,22 +591,15 @@
 
         private void setAdbEnabled(boolean enable) {
             if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable);
-            if (enable != mAdbEnabled) {
-                mAdbEnabled = enable;
 
-                if (enable) {
-                    setSystemProperty(USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_ADB);
-                } else {
-                    setSystemProperty(USB_PERSISTENT_CONFIG_PROPERTY, "");
-                }
-
-                setEnabledFunctions(mCurrentFunctions, true);
-                updateAdbNotification(false);
+            if (enable) {
+                setSystemProperty(USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_ADB);
+            } else {
+                setSystemProperty(USB_PERSISTENT_CONFIG_PROPERTY, "");
             }
 
-            if (mDebuggingManager != null) {
-                mDebuggingManager.setAdbEnabled(mAdbEnabled);
-            }
+            setEnabledFunctions(mCurrentFunctions, true);
+            updateAdbNotification(false);
         }
 
         protected boolean isUsbTransferAllowed() {
@@ -799,6 +757,19 @@
             setEnabledFunctions(mScreenUnlockedFunctions, false);
         }
 
+        private static class AdbTransport extends IAdbTransport.Stub {
+            private final UsbHandler mHandler;
+
+            AdbTransport(UsbHandler handler) {
+                mHandler = handler;
+            }
+
+            @Override
+            public void onAdbEnabled(boolean enabled) {
+                mHandler.sendMessage(MSG_ENABLE_ADB, enabled);
+            }
+        }
+
         /**
          * Returns the functions that are passed down to the low level driver once adb and
          * charging are accounted for.
@@ -807,7 +778,7 @@
             if (functions == UsbManager.FUNCTION_NONE) {
                 return getChargingFunctions();
             }
-            if (mAdbEnabled) {
+            if (isAdbEnabled()) {
                 return functions | UsbManager.FUNCTION_ADB;
             }
             return functions;
@@ -964,6 +935,9 @@
                     mNotificationManager = (NotificationManager)
                             mContext.getSystemService(Context.NOTIFICATION_SERVICE);
 
+                    LocalServices.getService(
+                            AdbManagerInternal.class).registerTransport(new AdbTransport(this));
+
                     // Ensure that the notification channels are set up
                     if (isTv()) {
                         // TV-specific notification channel
@@ -1029,19 +1003,6 @@
                 if (mCurrentAccessory != null) {
                     mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
                 }
-                if (mDebuggingManager != null) {
-                    mDebuggingManager.setAdbEnabled(mAdbEnabled);
-                }
-
-                // make sure the ADB_ENABLED setting value matches the current state
-                try {
-                    putGlobalSettings(mContentResolver, Settings.Global.ADB_ENABLED,
-                            mAdbEnabled ? 1 : 0);
-                } catch (SecurityException e) {
-                    // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't
-                    // be changed.
-                    Slog.d(TAG, "ADB_ENABLED is restricted.");
-                }
 
                 updateUsbNotification(false);
                 updateAdbNotification(false);
@@ -1191,12 +1152,16 @@
             }
         }
 
+        protected boolean isAdbEnabled() {
+            return LocalServices.getService(AdbManagerInternal.class).isAdbEnabled();
+        }
+
         protected void updateAdbNotification(boolean force) {
             if (mNotificationManager == null) return;
             final int id = SystemMessage.NOTE_ADB_ACTIVE;
             final int titleRes = com.android.internal.R.string.adb_active_notification_title;
 
-            if (mAdbEnabled && mConnected) {
+            if (isAdbEnabled() && mConnected) {
                 if ("0".equals(getSystemProperty("persist.adb.notify", ""))) return;
 
                 if (force && mAdbNotificationShown) {
@@ -1250,7 +1215,7 @@
         protected long getChargingFunctions() {
             // if ADB is enabled, reset functions to ADB
             // else enable MTP as usual.
-            if (mAdbEnabled) {
+            if (isAdbEnabled()) {
                 return UsbManager.FUNCTION_ADB;
             } else {
                 return UsbManager.FUNCTION_MTP;
@@ -1319,7 +1284,6 @@
                     mHideUsbNotification);
             dump.write("audio_accessory_connected", UsbHandlerProto.AUDIO_ACCESSORY_CONNECTED,
                     mAudioAccessoryConnected);
-            dump.write("adb_enabled", UsbHandlerProto.ADB_ENABLED, mAdbEnabled);
 
             try {
                 writeStringIfNotNull(dump, "kernel_state", UsbHandlerProto.KERNEL_STATE,
@@ -1362,9 +1326,8 @@
         private boolean mUsbDataUnlocked;
 
         UsbHandlerLegacy(Looper looper, Context context, UsbDeviceManager deviceManager,
-                UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager,
-                UsbSettingsManager settingsManager) {
-            super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager);
+                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
+            super(looper, context, deviceManager, alsaManager, settingsManager);
             try {
                 readOemUsbOverrideConfig(context);
                 // Restore default functions.
@@ -1446,7 +1409,7 @@
                             + overrideFunctions.second);
                     if (!overrideFunctions.second.equals("")) {
                         String newFunction;
-                        if (mAdbEnabled) {
+                        if (isAdbEnabled()) {
                             newFunction = addFunction(overrideFunctions.second,
                                     UsbManager.USB_FUNCTION_ADB);
                         } else {
@@ -1457,7 +1420,7 @@
                         setSystemProperty(getPersistProp(false), newFunction);
                     }
                     return overrideFunctions.first;
-                } else if (mAdbEnabled) {
+                } else if (isAdbEnabled()) {
                     String newFunction = addFunction(UsbManager.USB_FUNCTION_NONE,
                             UsbManager.USB_FUNCTION_ADB);
                     setSystemProperty(getPersistProp(false), newFunction);
@@ -1563,7 +1526,7 @@
             if (functions == null) {
                 functions = "";
             }
-            if (mAdbEnabled) {
+            if (isAdbEnabled()) {
                 functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB);
             } else {
                 functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB);
@@ -1752,9 +1715,8 @@
         protected boolean mCurrentUsbFunctionsRequested;
 
         UsbHandlerHal(Looper looper, Context context, UsbDeviceManager deviceManager,
-                UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager,
-                UsbSettingsManager settingsManager) {
-            super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager);
+                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
+            super(looper, context, deviceManager, alsaManager, settingsManager);
             try {
                 ServiceNotification serviceNotification = new ServiceNotification();
 
@@ -1850,7 +1812,7 @@
                      * Dont force to default when the configuration is already set to default.
                      */
                     if (msg.arg1 != 1) {
-                        setEnabledFunctions(UsbManager.FUNCTION_NONE, !mAdbEnabled);
+                        setEnabledFunctions(UsbManager.FUNCTION_NONE, !isAdbEnabled());
                     }
                     break;
                 default:
@@ -2061,25 +2023,8 @@
         mHandler.sendMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, functions);
     }
 
-    public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
-        if (mDebuggingManager != null) {
-            mDebuggingManager.allowUsbDebugging(alwaysAllow, publicKey);
-        }
-    }
-
-    public void denyUsbDebugging() {
-        if (mDebuggingManager != null) {
-            mDebuggingManager.denyUsbDebugging();
-        }
-    }
-
-    public void clearUsbDebuggingKeys() {
-        if (mDebuggingManager != null) {
-            mDebuggingManager.clearUsbDebuggingKeys();
-        } else {
-            throw new RuntimeException("Cannot clear Usb Debugging keys, "
-                    + "UsbDebuggingManager not enabled");
-        }
+    private void onAdbEnabled(boolean enabled) {
+        mHandler.sendMessage(MSG_ENABLE_ADB, enabled);
     }
 
     /**
@@ -2091,10 +2036,6 @@
         if (mHandler != null) {
             mHandler.dump(dump, "handler", UsbDeviceManagerProto.HANDLER);
         }
-        if (mDebuggingManager != null) {
-            mDebuggingManager.dump(dump, "debugging_manager",
-                    UsbDeviceManagerProto.DEBUGGING_MANAGER);
-        }
 
         dump.end(token);
     }
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index e0f3685..f9abedf 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -489,24 +489,6 @@
     }
 
     @Override
-    public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        mDeviceManager.allowUsbDebugging(alwaysAllow, publicKey);
-    }
-
-    @Override
-    public void denyUsbDebugging() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        mDeviceManager.denyUsbDebugging();
-    }
-
-    @Override
-    public void clearUsbDebuggingKeys() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        mDeviceManager.clearUsbDebuggingKeys();
-    }
-
-    @Override
     public UsbPort[] getPorts() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
 
diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
index 0121d30..84add88 100644
--- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
@@ -40,7 +40,6 @@
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbInterface;
 import android.hardware.usb.UsbManager;
-import android.os.Binder;
 import android.os.UserHandle;
 import android.service.usb.UsbAccessoryAttachedActivities;
 import android.service.usb.UsbDeviceAttachedActivities;
@@ -190,9 +189,8 @@
                                          @Nullable UsbAccessory accessory,
                                          boolean canBeDefault,
                                          String packageName,
-                                         PendingIntent pi) {
-        final int uid = Binder.getCallingUid();
-
+                                         PendingIntent pi,
+                                         int uid) {
         // compare uid with packageName to foil apps pretending to be someone else
         try {
             ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
@@ -235,7 +233,8 @@
             }
         }
 
-        requestPermissionDialog(device, null, canBeDefault(device, packageName), packageName, pi);
+        requestPermissionDialog(device, null, canBeDefault(device, packageName), packageName, pi,
+                uid);
     }
 
     public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi,
@@ -253,8 +252,8 @@
             return;
         }
 
-        requestPermissionDialog(null, accessory,
-                canBeDefault(accessory, packageName), packageName, pi);
+        requestPermissionDialog(null, accessory, canBeDefault(accessory, packageName), packageName,
+                pi, uid);
     }
 
     public void grantDevicePermission(UsbDevice device, int uid) {
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 3681529..c39688c 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -16,10 +16,15 @@
 
 cc_defaults {
     name: "viewcompiler_defaults",
+    header_libs: [
+        "libbase_headers",
+    ],
     shared_libs: [
+        "libbase",
         "libdexfile",
         "slicer",
     ],
+    cppflags: ["-std=c++17"],
 }
 
 cc_library_host_static {
@@ -30,9 +35,6 @@
         "java_lang_builder.cc",
         "util.cc",
     ],
-    static_libs: [
-        "libbase",
-    ],
 }
 
 cc_binary_host {
@@ -42,7 +44,6 @@
         "main.cc",
     ],
     static_libs: [
-        "libbase",
         "libtinyxml2",
         "libgflags",
         "libviewcompiler",
@@ -59,4 +60,5 @@
     static_libs: [
         "libviewcompiler",
     ],
+    test_suites: ["general-tests"],
 }
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
deleted file mode 100644
index cc4b17a..0000000
--- a/startop/view_compiler/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "view-compiler-tests"
-    }
-  ]
-}
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 7a9f41f..13e7f73 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -22,14 +22,16 @@
 #include <fstream>
 #include <memory>
 
+#define DCHECK_NOT_NULL(p) DCHECK((p) != nullptr)
+
 namespace startop {
 namespace dex {
 
 using std::shared_ptr;
 using std::string;
 
-using art::Instruction;
 using ::dex::kAccPublic;
+using Op = Instruction::Op;
 
 const TypeDescriptor TypeDescriptor::Int() { return TypeDescriptor{"I"}; };
 const TypeDescriptor TypeDescriptor::Void() { return TypeDescriptor{"V"}; };
@@ -43,6 +45,20 @@
 
 }  // namespace
 
+std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) {
+  switch (opcode) {
+    case Instruction::Op::kReturn:
+      out << "kReturn";
+      return out;
+    case Instruction::Op::kMove:
+      out << "kMove";
+      return out;
+    case Instruction::Op::kInvokeVirtual:
+      out << "kInvokeVirtual";
+      return out;
+  }
+}
+
 void* TrackingAllocator::Allocate(size_t size) {
   std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(size);
   void* raw_buffer = buffer.get();
@@ -56,7 +72,7 @@
 //
 // package dextest;
 // public class DexTest {
-//     public static int foo() { return 5; }
+//     public static int foo(String s) { return s.length(); }
 // }
 void WriteTestDexFile(const string& filename) {
   DexBuilder dex_file;
@@ -64,11 +80,17 @@
   ClassBuilder cbuilder{dex_file.MakeClass("dextest.DexTest")};
   cbuilder.set_source_file("dextest.java");
 
-  MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int()})};
+  TypeDescriptor string_type = TypeDescriptor::FromClassname("java.lang.String");
 
-  MethodBuilder::Register r = method.MakeRegister();
-  method.BuildConst4(r, 5);
-  method.BuildReturn(r);
+  MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), string_type})};
+
+  Value result = method.MakeRegister();
+
+  MethodDeclData string_length =
+      dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()});
+
+  method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
+  method.BuildReturn(result);
 
   method.Encode();
 
@@ -78,6 +100,10 @@
   out_file.write(image.ptr<const char>(), image.size());
 }
 
+TypeDescriptor TypeDescriptor::FromClassname(const std::string& name) {
+  return TypeDescriptor{art::DotToDescriptor(name.c_str())};
+}
+
 DexBuilder::DexBuilder() : dex_file_{std::make_shared<ir::DexFile>()} {
   dex_file_->magic = slicer::MemView{kDexFileMagic, sizeof(kDexFileMagic)};
 }
@@ -119,10 +145,9 @@
   class_def->type = type_def;
   class_def->super_class = GetOrAddType(art::DotToDescriptor("java.lang.Object"));
   class_def->access_flags = kAccPublic;
-  return ClassBuilder{this, class_def};
+  return ClassBuilder{this, name, class_def};
 }
 
-// TODO(eholk): we probably want GetOrAddString() also
 ir::Type* DexBuilder::GetOrAddType(const std::string& descriptor) {
   if (types_by_descriptor_.find(descriptor) != types_by_descriptor_.end()) {
     return types_by_descriptor_[descriptor];
@@ -158,16 +183,11 @@
   return shorty;
 }
 
-ClassBuilder::ClassBuilder(DexBuilder* parent, ir::Class* class_def)
-    : parent_(parent), class_(class_def) {}
+ClassBuilder::ClassBuilder(DexBuilder* parent, const std::string& name, ir::Class* class_def)
+    : parent_(parent), type_descriptor_{TypeDescriptor::FromClassname(name)}, class_(class_def) {}
 
 MethodBuilder ClassBuilder::CreateMethod(const std::string& name, Prototype prototype) {
-  ir::String* dex_name{parent_->GetOrAddString(name)};
-
-  auto* decl = parent_->Alloc<ir::MethodDecl>();
-  decl->name = dex_name;
-  decl->parent = class_->type;
-  decl->prototype = prototype.Encode(parent_);
+  ir::MethodDecl* decl = parent_->GetOrDeclareMethod(type_descriptor_, name, prototype).decl;
 
   return MethodBuilder{parent_, class_, decl};
 }
@@ -187,8 +207,13 @@
   method->access_flags = kAccPublic | ::dex::kAccStatic;
 
   auto* code = dex_->Alloc<ir::Code>();
-  code->registers = num_registers_;
-  // TODO: support ins and outs
+  DCHECK_NOT_NULL(decl_->prototype);
+  size_t const num_args =
+      decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0;
+  code->registers = num_registers_ + num_args;
+  code->ins_count = num_args;
+  code->outs_count = decl_->prototype->return_type == dex_->GetOrAddType("V") ? 0 : 1;
+  EncodeInstructions();
   code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
   method->code = code;
 
@@ -197,17 +222,135 @@
   return method;
 }
 
-MethodBuilder::Register MethodBuilder::MakeRegister() { return num_registers_++; }
+Value MethodBuilder::MakeRegister() { return Value::Local(num_registers_++); }
 
-void MethodBuilder::BuildReturn() { buffer_.push_back(Instruction::RETURN_VOID); }
+void MethodBuilder::AddInstruction(Instruction instruction) {
+  instructions_.push_back(instruction);
+}
 
-void MethodBuilder::BuildReturn(Register src) { buffer_.push_back(Instruction::RETURN | src << 8); }
+void MethodBuilder::BuildReturn() { AddInstruction(Instruction::OpNoArgs(Op::kReturn)); }
 
-void MethodBuilder::BuildConst4(Register target, int value) {
+void MethodBuilder::BuildReturn(Value src) {
+  AddInstruction(Instruction::OpWithArgs(Op::kReturn, /*destination=*/{}, src));
+}
+
+void MethodBuilder::BuildConst4(Value target, int value) {
   DCHECK_LT(value, 16);
-  // TODO: support more registers
-  DCHECK_LT(target, 16);
-  buffer_.push_back(Instruction::CONST_4 | (value << 12) | (target << 8));
+  AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::Immediate(value)));
+}
+
+void MethodBuilder::EncodeInstructions() {
+  buffer_.clear();
+  for (const auto& instruction : instructions_) {
+    EncodeInstruction(instruction);
+  }
+}
+
+void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
+  switch (instruction.opcode()) {
+    case Instruction::Op::kReturn:
+      return EncodeReturn(instruction);
+    case Instruction::Op::kMove:
+      return EncodeMove(instruction);
+    case Instruction::Op::kInvokeVirtual:
+      return EncodeInvokeVirtual(instruction);
+  }
+}
+
+void MethodBuilder::EncodeReturn(const Instruction& instruction) {
+  DCHECK_EQ(Instruction::Op::kReturn, instruction.opcode());
+  DCHECK(!instruction.dest().has_value());
+  if (instruction.args().size() == 0) {
+    buffer_.push_back(art::Instruction::RETURN_VOID);
+  } else {
+    DCHECK(instruction.args().size() == 1);
+    size_t source = RegisterValue(instruction.args()[0]);
+    buffer_.push_back(art::Instruction::RETURN | source << 8);
+  }
+}
+
+void MethodBuilder::EncodeMove(const Instruction& instruction) {
+  DCHECK_EQ(Instruction::Op::kMove, instruction.opcode());
+  DCHECK(instruction.dest().has_value());
+  DCHECK(instruction.dest()->is_register() || instruction.dest()->is_parameter());
+  DCHECK_EQ(1, instruction.args().size());
+
+  const Value& source = instruction.args()[0];
+
+  if (source.is_immediate()) {
+    // TODO: support more registers
+    DCHECK_LT(RegisterValue(*instruction.dest()), 16);
+    DCHECK_LT(source.value(), 16);
+    buffer_.push_back(art::Instruction::CONST_4 | (source.value() << 12) |
+                      (RegisterValue(*instruction.dest()) << 8));
+  } else {
+    UNIMPLEMENTED(FATAL);
+  }
+}
+
+void MethodBuilder::EncodeInvokeVirtual(const Instruction& instruction) {
+  DCHECK_EQ(Instruction::Op::kInvokeVirtual, instruction.opcode());
+
+  // TODO: support more than one argument (i.e. the this argument) and change this to DCHECK_GE
+  DCHECK_EQ(1, instruction.args().size());
+
+  const Value& this_arg = instruction.args()[0];
+
+  size_t real_reg = RegisterValue(this_arg) & 0xf;
+  buffer_.push_back(1 << 12 | art::Instruction::INVOKE_VIRTUAL);
+  buffer_.push_back(instruction.method_id());
+  buffer_.push_back(real_reg);
+
+  if (instruction.dest().has_value()) {
+    real_reg = RegisterValue(*instruction.dest());
+    buffer_.push_back(real_reg << 8 | art::Instruction::MOVE_RESULT);
+  }
+}
+
+size_t MethodBuilder::RegisterValue(Value value) const {
+  if (value.is_register()) {
+    return value.value();
+  } else if (value.is_parameter()) {
+    return value.value() + num_registers_;
+  }
+  DCHECK(false && "Must be either a parameter or a register");
+  return 0;
+}
+
+const MethodDeclData& DexBuilder::GetOrDeclareMethod(TypeDescriptor type, const std::string& name,
+                                                     Prototype prototype) {
+  MethodDeclData& entry = method_id_map_[{type, name, prototype}];
+
+  if (entry.decl == nullptr) {
+    // This method has not already been declared, so declare it.
+    ir::MethodDecl* decl = dex_file_->Alloc<ir::MethodDecl>();
+    // The method id is the last added method.
+    size_t id = dex_file_->methods.size() - 1;
+
+    ir::String* dex_name{GetOrAddString(name)};
+    decl->name = dex_name;
+    decl->parent = GetOrAddType(type.descriptor());
+    decl->prototype = GetOrEncodeProto(prototype);
+
+    // update the index -> ir node map (see tools/dexter/slicer/dex_ir_builder.cc)
+    auto new_index = dex_file_->methods_indexes.AllocateIndex();
+    auto& ir_node = dex_file_->methods_map[new_index];
+    SLICER_CHECK(ir_node == nullptr);
+    ir_node = decl;
+    decl->orig_index = new_index;
+
+    entry = {id, decl};
+  }
+
+  return entry;
+}
+
+ir::Proto* DexBuilder::GetOrEncodeProto(Prototype prototype) {
+  ir::Proto*& ir_proto = proto_map_[prototype];
+  if (ir_proto == nullptr) {
+    ir_proto = prototype.Encode(this);
+  }
+  return ir_proto;
 }
 
 }  // namespace dex
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index d280abc..e46655e 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -17,7 +17,9 @@
 #define DEX_BUILDER_H_
 
 #include <map>
+#include <optional>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "slicer/dex_ir.h"
@@ -45,7 +47,7 @@
   virtual void Free(void* ptr);
 
  private:
-  std::map<void*, std::unique_ptr<uint8_t[]>> allocations_;
+  std::unordered_map<void*, std::unique_ptr<uint8_t[]>> allocations_;
 };
 
 // Represents a DEX type descriptor.
@@ -57,11 +59,17 @@
   static const TypeDescriptor Int();
   static const TypeDescriptor Void();
 
+  // Creates a type descriptor from a fully-qualified class name. For example, it turns the class
+  // name java.lang.Object into the descriptor Ljava/lang/Object.
+  static TypeDescriptor FromClassname(const std::string& name);
+
   // Return the full descriptor, such as I or Ljava/lang/Object
   const std::string& descriptor() const { return descriptor_; }
   // Return the shorty descriptor, such as I or L
   std::string short_descriptor() const { return descriptor().substr(0, 1); }
 
+  bool operator<(const TypeDescriptor& rhs) const { return descriptor_ < rhs.descriptor_; }
+
  private:
   TypeDescriptor(std::string descriptor) : descriptor_{descriptor} {}
 
@@ -82,11 +90,98 @@
   // Get the shorty descriptor, such as VII for (Int, Int) -> Void
   std::string Shorty() const;
 
+  bool operator<(const Prototype& rhs) const {
+    return std::make_tuple(return_type_, param_types_) <
+           std::make_tuple(rhs.return_type_, rhs.param_types_);
+  }
+
  private:
   const TypeDescriptor return_type_;
   const std::vector<TypeDescriptor> param_types_;
 };
 
+// Represents a DEX register or constant. We separate regular registers and parameters
+// because we will not know the real parameter id until after all instructions
+// have been generated.
+class Value {
+ public:
+  static constexpr Value Local(size_t id) { return Value{id, Kind::kLocalRegister}; }
+  static constexpr Value Parameter(size_t id) { return Value{id, Kind::kParameter}; }
+  static constexpr Value Immediate(size_t value) { return Value{value, Kind::kImmediate}; }
+
+  bool is_register() const { return kind_ == Kind::kLocalRegister; }
+  bool is_parameter() const { return kind_ == Kind::kParameter; }
+  bool is_immediate() const { return kind_ == Kind::kImmediate; }
+
+  size_t value() const { return value_; }
+
+ private:
+  enum class Kind { kLocalRegister, kParameter, kImmediate };
+
+  const size_t value_;
+  const Kind kind_;
+
+  constexpr Value(size_t value, Kind kind) : value_{value}, kind_{kind} {}
+};
+
+// A virtual instruction. We convert these to real instructions in MethodBuilder::Encode.
+// Virtual instructions are needed to keep track of information that is not known until all of the
+// code is generated. This information includes things like how many local registers are created and
+// branch target locations.
+class Instruction {
+ public:
+  // The operation performed by this instruction. These are virtual instructions that do not
+  // correspond exactly to DEX instructions.
+  enum class Op { kReturn, kMove, kInvokeVirtual };
+
+  ////////////////////////
+  // Named Constructors //
+  ////////////////////////
+
+  // For instructions with no return value and no arguments.
+  static inline Instruction OpNoArgs(Op opcode) {
+    return Instruction{opcode, /*method_id*/ 0, /*dest*/ {}};
+  }
+  // For most instructions, which take some number of arguments and have an optional return value.
+  template <typename... T>
+  static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
+    return Instruction{opcode, /*method_id*/ 0, dest, args...};
+  }
+  // For method calls.
+  template <typename... T>
+  static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest,
+                                          Value this_arg, T... args) {
+    return Instruction{Op::kInvokeVirtual, method_id, dest, this_arg, args...};
+  }
+
+  ///////////////
+  // Accessors //
+  ///////////////
+
+  Op opcode() const { return opcode_; }
+  size_t method_id() const { return method_id_; }
+  const std::optional<const Value>& dest() const { return dest_; }
+  const std::vector<const Value>& args() const { return args_; }
+
+ private:
+  inline Instruction(Op opcode, size_t method_id, std::optional<const Value> dest)
+      : opcode_{opcode}, method_id_{method_id}, dest_{dest}, args_{} {}
+
+  template <typename... T>
+  inline constexpr Instruction(Op opcode, size_t method_id, std::optional<const Value> dest,
+                               T... args)
+      : opcode_{opcode}, method_id_{method_id}, dest_{dest}, args_{args...} {}
+
+  const Op opcode_;
+  // The index of the method to invoke, for kInvokeVirtual and similar opcodes.
+  const size_t method_id_{0};
+  const std::optional<const Value> dest_;
+  const std::vector<const Value> args_;
+};
+
+// Needed for CHECK_EQ, DCHECK_EQ, etc.
+std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode);
+
 // Tools to help build methods and their bodies.
 class MethodBuilder {
  public:
@@ -95,42 +190,53 @@
   // Encode the method into DEX format.
   ir::EncodedMethod* Encode();
 
-  // Registers are just represented by their number.
-  using Register = size_t;
-
   // Create a new register to be used to storing values. Note that these are not SSA registers, like
   // might be expected in similar code generators. This does no liveness tracking or anything, so
   // it's up to the caller to reuse registers as appropriate.
-  Register MakeRegister();
+  Value MakeRegister();
 
   /////////////////////////////////
   // Instruction builder methods //
   /////////////////////////////////
 
+  void AddInstruction(Instruction instruction);
+
   // return-void
   void BuildReturn();
-  void BuildReturn(Register src);
+  void BuildReturn(Value src);
   // const/4
-  void BuildConst4(Register target, int value);
+  void BuildConst4(Value target, int value);
 
   // TODO: add builders for more instructions
 
  private:
+  void EncodeInstructions();
+  void EncodeInstruction(const Instruction& instruction);
+  void EncodeReturn(const Instruction& instruction);
+  void EncodeMove(const Instruction& instruction);
+  void EncodeInvokeVirtual(const Instruction& instruction);
+
+  // Converts a register or parameter to its DEX register number.
+  size_t RegisterValue(Value value) const;
+
   DexBuilder* dex_;
   ir::Class* class_;
   ir::MethodDecl* decl_;
 
-  // A buffer to hold instructions we are generating.
+  // A list of the instructions we will eventually encode.
+  std::vector<Instruction> instructions_;
+
+  // A buffer to hold instructions that have been encoded.
   std::vector<::dex::u2> buffer_;
 
   // How many registers we've allocated
-  size_t num_registers_;
+  size_t num_registers_{0};
 };
 
 // A helper to build class definitions.
 class ClassBuilder {
  public:
-  ClassBuilder(DexBuilder* parent, ir::Class* class_def);
+  ClassBuilder(DexBuilder* parent, const std::string& name, ir::Class* class_def);
 
   void set_source_file(const std::string& source);
 
@@ -139,8 +245,15 @@
   MethodBuilder CreateMethod(const std::string& name, Prototype prototype);
 
  private:
-  DexBuilder* parent_;
-  ir::Class* class_;
+  DexBuilder* const parent_;
+  const TypeDescriptor type_descriptor_;
+  ir::Class* const class_;
+};
+
+// Keeps track of information needed to manipulate or call a method.
+struct MethodDeclData {
+  size_t id;
+  ir::MethodDecl* decl;
 };
 
 // Builds Dex files from scratch.
@@ -163,10 +276,19 @@
   ClassBuilder MakeClass(const std::string& name);
 
   // Add a type for the given descriptor, or return the existing one if it already exists.
-  // See the TypeDescriptor class for help generating these.
+  // See the TypeDescriptor class for help generating these. GetOrAddType can be used to declare
+  // imported classes.
   ir::Type* GetOrAddType(const std::string& descriptor);
 
+  // Returns the method id for the method, creating it if it has not been created yet.
+  const MethodDeclData& GetOrDeclareMethod(TypeDescriptor type, const std::string& name,
+                                           Prototype prototype);
+
  private:
+  // Looks up the ir::Proto* corresponding to this given prototype, or creates one if it does not
+  // exist.
+  ir::Proto* GetOrEncodeProto(Prototype prototype);
+
   std::shared_ptr<ir::DexFile> dex_file_;
 
   // allocator_ is needed to be able to encode the image.
@@ -177,10 +299,29 @@
   std::vector<std::unique_ptr<uint8_t[]>> string_data_;
 
   // Keep track of what types we've defined so we can look them up later.
-  std::map<std::string, ir::Type*> types_by_descriptor_;
+  std::unordered_map<std::string, ir::Type*> types_by_descriptor_;
+
+  struct MethodDescriptor {
+    TypeDescriptor type;
+    std::string name;
+    Prototype prototype;
+
+    inline bool operator<(const MethodDescriptor& rhs) const {
+      return std::make_tuple(type, name, prototype) <
+             std::make_tuple(rhs.type, rhs.name, rhs.prototype);
+    }
+  };
+
+  // Maps method declarations to their method index. This is needed to encode references to them.
+  // When we go to actually write the DEX file, slicer will re-assign these after correctly sorting
+  // the methods list.
+  std::map<MethodDescriptor, MethodDeclData> method_id_map_;
 
   // Keep track of what strings we've defined so we can look them up later.
-  std::map<std::string, ir::String*> strings_;
+  std::unordered_map<std::string, ir::String*> strings_;
+
+  // Keep track of already-encoded protos.
+  std::map<Prototype, ir::Proto*> proto_map_;
 };
 
 }  // namespace dex
diff --git a/startop/view_compiler/dex_builder_test.cc b/startop/view_compiler/dex_builder_test.cc
index 0d8b854..61c86b4 100644
--- a/startop/view_compiler/dex_builder_test.cc
+++ b/startop/view_compiler/dex_builder_test.cc
@@ -40,6 +40,12 @@
   return loaded_dex_file != nullptr;
 }
 
+// Write out and verify a DEX file that corresponds to:
+//
+// package dextest;
+// public class DexTest {
+//     public static void foo() {}
+// }
 TEST(DexBuilderTest, VerifyDexWithClassMethod) {
   DexBuilder dex_file;
 
@@ -67,6 +73,12 @@
   EXPECT_FALSE(EncodeAndVerify(&dex_file));
 }
 
+// Write out and verify a DEX file that corresponds to:
+//
+// package dextest;
+// public class DexTest {
+//     public static int foo() { return 5; }
+// }
 TEST(DexBuilderTest, VerifyDexReturn5) {
   DexBuilder dex_file;
 
@@ -80,3 +92,51 @@
 
   EXPECT_TRUE(EncodeAndVerify(&dex_file));
 }
+
+// Write out and verify a DEX file that corresponds to:
+//
+// package dextest;
+// public class DexTest {
+//     public static int foo(int x) { return x; }
+// }
+TEST(DexBuilderTest, VerifyDexReturnIntParam) {
+  DexBuilder dex_file;
+
+  auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
+
+  auto method{
+      cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
+  method.BuildReturn(Value::Parameter(0));
+  method.Encode();
+
+  EXPECT_TRUE(EncodeAndVerify(&dex_file));
+}
+
+// Write out and verify a DEX file that corresponds to:
+//
+// package dextest;
+// public class DexTest {
+//     public static int foo(String s) { return s.length(); }
+// }
+TEST(DexBuilderTest, VerifyDexCallStringLength) {
+  DexBuilder dex_file;
+
+  auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
+
+  MethodBuilder method{cbuilder.CreateMethod(
+      "foo", Prototype{TypeDescriptor::Int(), TypeDescriptor::FromClassname("java.lang.String")})};
+
+  Value result = method.MakeRegister();
+
+  MethodDeclData string_length =
+      dex_file.GetOrDeclareMethod(TypeDescriptor::FromClassname("java.lang.String"),
+                                  "length",
+                                  Prototype{TypeDescriptor::Int()});
+
+  method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
+  method.BuildReturn(result);
+
+  method.Encode();
+
+  EXPECT_TRUE(EncodeAndVerify(&dex_file));
+}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index daa09f5..b6ac91d 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -467,8 +467,24 @@
          */
         public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 0x00000800;
 
+        /**
+         * Indicates that the call is using VoIP audio mode.
+         * <p>
+         * When this property is set, the {@link android.media.AudioManager} audio mode for this
+         * call will be {@link android.media.AudioManager#MODE_IN_COMMUNICATION}.  When this
+         * property is not set, the audio mode for this call will be
+         * {@link android.media.AudioManager#MODE_IN_CALL}.
+         * <p>
+         * This property reflects changes made using {@link Connection#setAudioModeIsVoip(boolean)}.
+         * <p>
+         * You can use this property to determine whether an un-answered incoming call or a held
+         * call will use VoIP audio mode (if the call does not currently have focus, the system
+         * audio mode may not reflect the mode the call will use).
+         */
+        public static final int PROPERTY_VOIP_AUDIO_MODE = 0x00001000;
+
         //******************************************************************************************
-        // Next PROPERTY value: 0x00001000
+        // Next PROPERTY value: 0x00002000
         //******************************************************************************************
 
         private final String mTelecomCallId;
@@ -628,15 +644,21 @@
             if (hasProperty(properties, PROPERTY_IS_EXTERNAL_CALL)) {
                 builder.append(" PROPERTY_IS_EXTERNAL_CALL");
             }
-            if(hasProperty(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
+            if (hasProperty(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
                 builder.append(" PROPERTY_HAS_CDMA_VOICE_PRIVACY");
             }
-            if(hasProperty(properties, PROPERTY_ASSISTED_DIALING_USED)) {
+            if (hasProperty(properties, PROPERTY_ASSISTED_DIALING_USED)) {
                 builder.append(" PROPERTY_ASSISTED_DIALING_USED");
             }
             if (hasProperty(properties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL)) {
                 builder.append(" PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL");
             }
+            if (hasProperty(properties, PROPERTY_RTT)) {
+                builder.append(" PROPERTY_RTT");
+            }
+            if (hasProperty(properties, PROPERTY_VOIP_AUDIO_MODE)) {
+                builder.append(" PROPERTY_VOIP_AUDIO_MODE");
+            }
             builder.append("]");
             return builder.toString();
         }
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index f62b170..7db6940 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.app.Service;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.IBinder;
@@ -229,7 +230,8 @@
                         callDetails.getTelecomCallId(),
                         response.getRejectCall(),
                         !response.getSkipCallLog(),
-                        !response.getSkipNotification());
+                        !response.getSkipNotification(),
+                        new ComponentName(getPackageName(), getClass().getName()));
             } else {
                 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
             }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index d33a537..fa16bfe 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -15,6 +15,7 @@
 package android.telecom;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressAutoDoc;
 import android.annotation.SuppressLint;
@@ -175,6 +176,33 @@
             "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
 
     /**
+     * Broadcast intent action indicating that the current default call screening app has changed.
+     *
+     * The string extra {@link #EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME} will contain the
+     * name of the Component of the previous or the new call screening app.
+     *
+     * The boolean extra {@link #EXTRA_IS_DEFAULT_CALL_SCREENING_APP} will indicate the component
+     * name in the String extra {@link #EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME} is default
+     * call screening app or not.
+     */
+    public static final String ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED =
+        "android.telecom.action.DEFAULT_CALL_SCREENING_APP_CHANGED";
+
+    /**
+     * Extra value used with {@link #ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED} broadcast to
+     * indicate the ComponentName of the call screening app which has changed.
+     */
+    public static final String EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME =
+            "android.telecom.extra.DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME";
+
+    /**
+     * Extra value used with {@link #ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED} broadcast to
+     * indicate whether an app is the default call screening app.
+     */
+    public static final String EXTRA_IS_DEFAULT_CALL_SCREENING_APP =
+            "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP";
+
+    /**
      * Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
      * determines whether the speakerphone should be automatically turned on for an outgoing call.
      */
@@ -676,7 +704,7 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public static TelecomManager from(Context context) {
         return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
     }
@@ -1169,6 +1197,79 @@
     }
 
     /**
+     * Used to trigger display of the ChangeDefaultCallScreeningApp activity to prompt the user to
+     * change the call screening app.
+     *
+     * A {@link SecurityException} will be thrown if calling package name doesn't match the package
+     * of the passed {@link ComponentName}
+     *
+     * @param componentName to verify that the calling package name matches the package of the
+     * passed ComponentName.
+     */
+    public void requestChangeDefaultCallScreeningApp(@NonNull ComponentName componentName) {
+        try {
+            if (isServiceConnected()) {
+                getTelecomService().requestChangeDefaultCallScreeningApp(componentName, mContext
+                    .getOpPackageName());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG,
+                "RemoteException calling ITelecomService#requestChangeDefaultCallScreeningApp.",
+                e);
+        }
+    }
+
+    /**
+     * Used to verify that the passed ComponentName is default call screening app.
+     *
+     * @param componentName to verify that the package of the passed ComponentName matched the default
+     * call screening packageName.
+     *
+     * @return {@code true} if the passed componentName matches the default call screening's, {@code
+     * false} if the passed componentName is null, or it doesn't match default call screening's.
+     */
+    public boolean isDefaultCallScreeningApp(ComponentName componentName) {
+        try {
+            if (isServiceConnected()) {
+                return getTelecomService().isDefaultCallScreeningApp(componentName);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG,
+                "RemoteException calling ITelecomService#isDefaultCallScreeningApp.",
+                e);
+        }
+        return false;
+    }
+
+    /**
+     * Used to set the default call screening package.
+     *
+     * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} Requires
+     * permission: {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
+     *
+     * A {@link IllegalArgumentException} will be thrown if the specified package and component name
+     * of {@link ComponentName} does't exist, or the specified component of {@link ComponentName}
+     * does't have {@link android.Manifest.permission#BIND_SCREENING_SERVICE}.
+     *
+     * @param componentName to set the default call screening to.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+        android.Manifest.permission.MODIFY_PHONE_STATE,
+        android.Manifest.permission.WRITE_SECURE_SETTINGS
+    })
+    public void setDefaultCallScreeningApp(ComponentName componentName) {
+        try {
+            if (isServiceConnected()) {
+                getTelecomService().setDefaultCallScreeningApp(componentName);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG,
+                "RemoteException calling ITelecomService#setDefaultCallScreeningApp.", e);
+        }
+    }
+
+    /**
      * Return whether a given phone number is the configured voicemail number for a
      * particular phone account.
      *
diff --git a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
index 2e0af27..d255ed1 100644
--- a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
@@ -16,6 +16,8 @@
 
 package com.android.internal.telecom;
 
+import android.content.ComponentName;
+
 /**
  * Internal remote callback interface for call screening services.
  *
@@ -30,5 +32,6 @@
             String callId,
             boolean shouldReject,
             boolean shouldAddToCallLog,
-            boolean shouldShowNotification);
+            boolean shouldShowNotification,
+            in ComponentName componentName);
 }
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index df7d683..d97f0c5 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -256,6 +256,21 @@
     boolean setDefaultDialer(in String packageName);
 
     /**
+     * @see TelecomServiceImpl#requestChangeDefaultCallScreeningApp
+     */
+    void requestChangeDefaultCallScreeningApp(in ComponentName componentNamem, String callingPackage);
+
+    /**
+     * @see TelecomServiceImpl#isDefaultCallScreeningApp
+     */
+    boolean isDefaultCallScreeningApp(in ComponentName componentName);
+
+    /**
+     * @see TelecomServiceImpl#setDefaultCallScreeningApp
+     */
+    void setDefaultCallScreeningApp(in ComponentName componentName);
+
+    /**
     * @see TelecomServiceImpl#createManageBlockedNumbersIntent
     **/
     Intent createManageBlockedNumbersIntent();
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 6c9c01c..61d60e3 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
@@ -1271,6 +1272,31 @@
              */
             public static final String EXTRA_IS_INITIAL_CREATE =
                     "android.provider.extra.IS_INITIAL_CREATE";
+
+            /**
+             * Broadcast intent action indicating that the telephony provider SMS MMS database is
+             * corrupted. A boolean is specified in {@link #EXTRA_IS_CORRUPTED} to indicate if the
+             * database is corrupted. Requires the
+             * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE permission.
+             *
+             * @hide
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+            public static final String ACTION_SMS_MMS_DB_LOST =
+                    "android.provider.action.SMS_MMS_DB_LOST";
+
+            /**
+             * Boolean flag passed as an extra with {@link #ACTION_SMS_MMS_DB_LOST} to indicate
+             * whether the DB got corrupted or not.
+             *
+             * @see #ACTION_SMS_MMS_DB_LOST
+             *
+             * @hide
+             */
+            public static final String EXTRA_IS_CORRUPTED =
+                    "android.provider.extra.IS_CORRUPTED";
+
             /**
              * Read the PDUs out of an {@link #SMS_RECEIVED_ACTION} or a
              * {@link #DATA_SMS_RECEIVED_ACTION} intent.
@@ -2730,6 +2756,13 @@
                 "content://telephony/carriers/enforce_managed");
 
         /**
+         * The {@code content://} style URL to be called from Telephony to query current APNs.
+         * @hide
+         */
+        public static final Uri SIM_APN_LIST = Uri.parse(
+                "content://telephony/carriers/sim_apn_list");
+
+        /**
          * The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced.
          * @hide
          */
@@ -3086,6 +3119,13 @@
         @SystemApi
         public static final int NO_SET_SET = 0;
 
+        /**
+         * A unique carrier id associated with this APN
+         * {@see TelephonyManager#getSimCarrierId()}
+         * <p>Type: STRING</p>
+         */
+        public static final String CARRIER_ID = "carrier_id";
+
     }
 
     /**
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index cac9f2b..9c64cf6 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -32,7 +32,20 @@
         public static final int IWLAN = 5;
 
         /** @hide */
-        private AccessNetworkType() {};
+        private AccessNetworkType() {}
+
+        /** @hide */
+        public static String toString(int type) {
+            switch (type) {
+                case UNKNOWN: return "UNKNOWN";
+                case GERAN: return "GERAN";
+                case UTRAN: return "UTRAN";
+                case EUTRAN: return "EUTRAN";
+                case CDMA2000: return "CDMA2000";
+                case IWLAN: return "IWLAN";
+                default: return Integer.toString(type);
+            }
+        }
     }
 
     /**
@@ -47,7 +60,16 @@
         public static final int WLAN = 2;
 
         /** @hide */
-        private TransportType() {};
+        private TransportType() {}
+
+        /** @hide */
+        public static String toString(int type) {
+            switch (type) {
+                case WWAN: return "WWAN";
+                case WLAN: return "WLAN";
+                default: return Integer.toString(type);
+            }
+        }
     }
 
     /**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b0997f1..3f9a533 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -16,12 +16,14 @@
 
 package android.telephony;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.PersistableBundle;
@@ -1132,6 +1134,13 @@
     public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
 
     /**
+     * The Component Name of a carrier-provided CallScreeningService implementation. Telecom will
+     * bind to this CallScreeningService for ALL incoming calls and provide the carrier
+     * CallScreeningService with the opportunity to allow or block calls.
+     */
+    public static final String KEY_CARRIER_CALL_SCREENING_APP_STRING = "call_screening_app";
+
+    /**
      * Override the registered PLMN name using #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING.
      *
      * If true, then the registered PLMN name (only for CDMA/CDMA-LTE and only when not roaming)
@@ -1222,8 +1231,40 @@
     public static final String KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL =
             "show_precise_failed_cause_bool";
 
-    // These variables are used by the MMS service and exposed through another API, {@link
-    // SmsManager}. The variable names and string values are copied from there.
+    /**
+     * Boolean to decide whether lte is enabled.
+     * @hide
+     */
+    public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool";
+
+    /**
+     * Boolean to decide whether TD-SCDMA is supported.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_TDSCDMA_BOOL = "support_tdscdma_bool";
+
+    /**
+     * A list of mcc/mnc that support TD-SCDMA for device when connect to the roaming network.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY =
+            "support_tdscdma_roaming_networks_string_array";
+
+    /**
+     * Boolean to decide whether world mode is enabled.
+     * @hide
+     */
+    public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
+
+    /**
+     * Flatten {@link android.content.ComponentName} of the carrier's settings activity.
+     * @hide
+     */
+    public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING =
+            "carrier_settings_activity_component_name_string";
+
+    // These variables are used by the MMS service and exposed through another API,
+    // SmsManager. The variable names and string values are copied from there.
     public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
     public static final String KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL = "allowAttachAudio";
     public static final String KEY_MMS_APPEND_TRANSACTION_ID_BOOL = "enabledTransID";
@@ -1271,7 +1312,7 @@
      * network as part of the Setup Wizard flow.
      * @hide
      */
-     public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
+    public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
 
     /**
      * Defines carrier-specific actions which act upon
@@ -2222,6 +2263,20 @@
     public static final String KEY_CALL_WAITING_OVER_UT_WARNING_BOOL =
             "call_waiting_over_ut_warning_bool";
 
+    /**
+     * Flag indicating whether to support "Network default" option in Caller ID settings for Calling
+     * Line Identification Restriction (CLIR).
+     */
+    public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL =
+            "support_clir_network_default_bool";
+
+    /**
+     * Determines whether the carrier want to support emergency dialer shortcut.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_EMERGENCY_DIALER_SHORTCUT_BOOL =
+            "support_emergency_dialer_shortcut_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -2406,6 +2461,7 @@
         sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
         sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
         sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
+        sDefaults.putString(KEY_CARRIER_CALL_SCREENING_APP_STRING, "");
         sDefaults.putBoolean(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL, false);
         sDefaults.putString(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING, "");
         sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
@@ -2544,8 +2600,13 @@
         sDefaults.putBoolean(KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL, false);
         sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
-        sDefaults.putBoolean(KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, true);
-        sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, false);
+        sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true);
+        sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORT_TDSCDMA_BOOL, false);
+        sDefaults.putStringArray(KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_WORLD_MODE_ENABLED_BOOL, false);
+        sDefaults.putString(KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING, "");
         sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
         sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
         sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
@@ -2569,6 +2630,8 @@
         sDefaults.putBoolean(KEY_CALL_BARRING_OVER_UT_WARNING_BOOL, false);
         sDefaults.putBoolean(KEY_CALLER_ID_OVER_UT_WARNING_BOOL, false);
         sDefaults.putBoolean(KEY_CALL_WAITING_OVER_UT_WARNING_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_DIALER_SHORTCUT_BOOL, true);
     }
 
     /**
@@ -2603,6 +2666,40 @@
     }
 
     /**
+     * Overrides the carrier config of the provided subscription ID with the provided values.
+     *
+     * Any further queries to carrier config from any process will return
+     * the overriden values after this method returns. The overrides are effective for the lifetime
+     * of the phone process.
+     *
+     * May throw an {@link IllegalArgumentException} if {@code overrideValues} contains invalid
+     * values for the specified config keys.
+     *
+     * @param subscriptionId The subscription ID for which the override should be done.
+     * @param overrideValues Key-value pairs of the values that are to be overriden. If null,
+     *                       all previous overrides will be disabled and the config reset back to
+     *                       its initial state.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    @TestApi
+    public void overrideConfig(int subscriptionId, @Nullable PersistableBundle overrideValues) {
+        try {
+            ICarrierConfigLoader loader = getICarrierConfigLoader();
+            if (loader == null) {
+                Rlog.w(TAG, "Error setting config for subId " + subscriptionId
+                        + " ICarrierConfigLoader is null");
+                return;
+            }
+            loader.overrideConfig(subscriptionId, overrideValues);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Error setting config for subId " + subscriptionId + ": "
+                    + ex.toString());
+        }
+    }
+
+    /**
      * Gets the configuration values for the default subscription. After using this method to get
      * the configuration bundle, {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be
      * called to confirm whether any carrier specific configuration has been applied.
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 76a0026..6958d22 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -190,6 +190,7 @@
                         case CellInfo.TYPE_LTE: return CellIdentityLte.createFromParcelBody(in);
                         case CellInfo.TYPE_TDSCDMA:
                             return CellIdentityTdscdma.createFromParcelBody(in);
+                        case CellInfo.TYPE_NR: return CellIdentityNr.createFromParcelBody(in);
                         default: throw new IllegalArgumentException("Bad Cell identity Parcel");
                     }
                 }
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 9218bdc..598f567 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.cdma.CdmaCellLocation;
@@ -56,11 +55,11 @@
      */
     public CellIdentityCdma() {
         super(TAG, CellInfo.TYPE_CDMA, null, null, null, null);
-        mNetworkId = Integer.MAX_VALUE;
-        mSystemId = Integer.MAX_VALUE;
-        mBasestationId = Integer.MAX_VALUE;
-        mLongitude = Integer.MAX_VALUE;
-        mLatitude = Integer.MAX_VALUE;
+        mNetworkId = CellInfo.UNAVAILABLE;
+        mSystemId = CellInfo.UNAVAILABLE;
+        mBasestationId = CellInfo.UNAVAILABLE;
+        mLongitude = CellInfo.UNAVAILABLE;
+        mLatitude = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -104,7 +103,7 @@
             mLongitude = lon;
             mLatitude = lat;
         } else {
-            mLongitude = mLatitude = Integer.MAX_VALUE;
+            mLongitude = mLatitude = CellInfo.UNAVAILABLE;
         }
     }
 
@@ -130,21 +129,24 @@
     }
 
     /**
-     * @return Network Id 0..65535, Integer.MAX_VALUE if unknown
+     * @return Network Id 0..65535, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+     *         if unavailable.
      */
     public int getNetworkId() {
         return mNetworkId;
     }
 
     /**
-     * @return System Id 0..32767, Integer.MAX_VALUE if unknown
+     * @return System Id 0..32767, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+     *         if unavailable.
      */
     public int getSystemId() {
         return mSystemId;
     }
 
     /**
-     * @return Base Station Id 0..65535, Integer.MAX_VALUE if unknown
+     * @return Base Station Id 0..65535, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+     *         if unavailable.
      */
     public int getBasestationId() {
         return mBasestationId;
@@ -155,7 +157,7 @@
      * specified in 3GPP2 C.S0005-A v6.0. It is represented in units
      * of 0.25 seconds and ranges from -2592000 to 2592000, both
      * values inclusive (corresponding to a range of -180
-     * to +180 degrees). Integer.MAX_VALUE if unknown.
+     * to +180 degrees). {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getLongitude() {
         return mLongitude;
@@ -166,7 +168,7 @@
      * specified in 3GPP2 C.S0005-A v6.0. It is represented in units
      * of 0.25 seconds and ranges from -1296000 to 1296000, both
      * values inclusive (corresponding to a range of -90
-     * to +90 degrees). Integer.MAX_VALUE if unknown.
+     * to +90 degrees). {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getLatitude() {
         return mLatitude;
@@ -182,10 +184,10 @@
     @Override
     public CdmaCellLocation asCellLocation() {
         CdmaCellLocation cl = new CdmaCellLocation();
-        int bsid = mBasestationId != Integer.MAX_VALUE ? mBasestationId : -1;
-        int sid = mSystemId != Integer.MAX_VALUE ? mSystemId : -1;
-        int nid = mNetworkId != Integer.MAX_VALUE ? mNetworkId : -1;
-        // lat and long already use Integer.MAX_VALUE for invalid/unknown
+        int bsid = mBasestationId != CellInfo.UNAVAILABLE ? mBasestationId : -1;
+        int sid = mSystemId != CellInfo.UNAVAILABLE ? mSystemId : -1;
+        int nid = mNetworkId != CellInfo.UNAVAILABLE ? mNetworkId : -1;
+        // lat and long already use CellInfo.UNAVAILABLE for invalid/unknown
         cl.setCellLocationData(bsid, mLatitude, mLongitude, sid, nid);
         return cl;
     }
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index cb9dbf3..04c28e5 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -48,10 +47,10 @@
     @UnsupportedAppUsage
     public CellIdentityGsm() {
         super(TAG, CellInfo.TYPE_GSM, null, null, null, null);
-        mLac = Integer.MAX_VALUE;
-        mCid = Integer.MAX_VALUE;
-        mArfcn = Integer.MAX_VALUE;
-        mBsic = Integer.MAX_VALUE;
+        mLac = CellInfo.UNAVAILABLE;
+        mCid = CellInfo.UNAVAILABLE;
+        mArfcn = CellInfo.UNAVAILABLE;
+        mBsic = CellInfo.UNAVAILABLE;
     }
     /**
      * public constructor
@@ -63,7 +62,7 @@
      * @hide
      */
     public CellIdentityGsm(int mcc, int mnc, int lac, int cid) {
-        this(lac, cid, Integer.MAX_VALUE, Integer.MAX_VALUE,
+        this(lac, cid, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
                 String.valueOf(mcc), String.valueOf(mnc), null, null);
     }
 
@@ -103,7 +102,7 @@
         mArfcn = arfcn;
         // In RIL BSIC is a UINT8, so 0xFF is the 'INVALID' designator
         // for inbound parcels
-        mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
+        mBsic = (bsic == 0xFF) ? CellInfo.UNAVAILABLE : bsic;
     }
 
     private CellIdentityGsm(CellIdentityGsm cid) {
@@ -116,69 +115,73 @@
     }
 
     /**
-     * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+     * @return 3-digit Mobile Country Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      * @deprecated Use {@link #getMccString} instead.
      */
     @Deprecated
     public int getMcc() {
-        return (mMccStr != null) ? Integer.valueOf(mMccStr) : Integer.MAX_VALUE;
+        return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
     }
 
     /**
-     * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+     * @return 2 or 3-digit Mobile Network Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      * @deprecated Use {@link #getMncString} instead.
      */
     @Deprecated
     public int getMnc() {
-        return (mMncStr != null) ? Integer.valueOf(mMncStr) : Integer.MAX_VALUE;
+        return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
     }
 
     /**
-     * @return 16-bit Location Area Code, 0..65535, Integer.MAX_VALUE if unknown
+     * @return 16-bit Location Area Code, 0..65535,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getLac() {
         return mLac;
     }
 
     /**
-     * @return CID
-     * Either 16-bit GSM Cell Identity described
-     * in TS 27.007, 0..65535, Integer.MAX_VALUE if unknown
+     * @return 16-bit GSM Cell Identity described in TS 27.007, 0..65535,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getCid() {
         return mCid;
     }
 
     /**
-     * @return 16-bit GSM Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+     * @return 16-bit GSM Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getArfcn() {
         return mArfcn;
     }
 
     /**
-     * @return 6-bit Base Station Identity Code, Integer.MAX_VALUE if unknown
+     * @return 6-bit Base Station Identity Code,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getBsic() {
         return mBsic;
     }
 
     /**
-     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
+     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
      */
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
 
     /**
-     * @return Mobile Country Code in string format, null if unknown
+     * @return Mobile Country Code in string format, null if unavailable.
      */
     public String getMccString() {
         return mMccStr;
     }
 
     /**
-     * @return Mobile Network Code in string format, null if unknown
+     * @return Mobile Network Code in string format, null if unavailable.
      */
     public String getMncString() {
         return mMncStr;
@@ -192,19 +195,19 @@
 
     /**
      * @deprecated Primary Scrambling Code is not applicable to GSM.
-     * @return Integer.MAX_VALUE, undefined for GSM
+     * @return {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} - undefined for GSM
      */
     @Deprecated
     public int getPsc() {
-        return Integer.MAX_VALUE;
+        return CellInfo.UNAVAILABLE;
     }
 
     /** @hide */
     @Override
     public GsmCellLocation asCellLocation() {
         GsmCellLocation cl = new GsmCellLocation();
-        int lac = mLac != Integer.MAX_VALUE ? mLac : -1;
-        int cid = mCid != Integer.MAX_VALUE ? mCid : -1;
+        int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
+        int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
         cl.setLacAndCid(lac, cid);
         cl.setPsc(-1);
         return cl;
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index b44e891..04b6a6c 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -49,11 +48,11 @@
     @UnsupportedAppUsage
     public CellIdentityLte() {
         super(TAG, CellInfo.TYPE_LTE, null, null, null, null);
-        mCi = Integer.MAX_VALUE;
-        mPci = Integer.MAX_VALUE;
-        mTac = Integer.MAX_VALUE;
-        mEarfcn = Integer.MAX_VALUE;
-        mBandwidth = Integer.MAX_VALUE;
+        mCi = CellInfo.UNAVAILABLE;
+        mPci = CellInfo.UNAVAILABLE;
+        mTac = CellInfo.UNAVAILABLE;
+        mEarfcn = CellInfo.UNAVAILABLE;
+        mBandwidth = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -68,7 +67,7 @@
      */
     @UnsupportedAppUsage
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
-        this(ci, pci, tac, Integer.MAX_VALUE, Integer.MAX_VALUE, String.valueOf(mcc),
+        this(ci, pci, tac, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, String.valueOf(mcc),
                 String.valueOf(mnc), null, null);
     }
 
@@ -84,7 +83,7 @@
      * @hide
      */
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
-        this(ci, pci, tac, earfcn, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc),
+        this(ci, pci, tac, earfcn, CellInfo.UNAVAILABLE, String.valueOf(mcc), String.valueOf(mnc),
                 null, null);
     }
 
@@ -122,74 +121,81 @@
     }
 
     /**
-     * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+     * @return 3-digit Mobile Country Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      * @deprecated Use {@link #getMccString} instead.
      */
     @Deprecated
     public int getMcc() {
-        return (mMccStr != null) ? Integer.valueOf(mMccStr) : Integer.MAX_VALUE;
+        return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
     }
 
     /**
-     * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+     * @return 2 or 3-digit Mobile Network Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      * @deprecated Use {@link #getMncString} instead.
      */
     @Deprecated
     public int getMnc() {
-        return (mMncStr != null) ? Integer.valueOf(mMncStr) : Integer.MAX_VALUE;
+        return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
     }
 
     /**
-     * @return 28-bit Cell Identity, Integer.MAX_VALUE if unknown
+     * @return 28-bit Cell Identity,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getCi() {
         return mCi;
     }
 
     /**
-     * @return Physical Cell Id 0..503, Integer.MAX_VALUE if unknown
+     * @return Physical Cell Id 0..503,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getPci() {
         return mPci;
     }
 
     /**
-     * @return 16-bit Tracking Area Code, Integer.MAX_VALUE if unknown
+     * @return 16-bit Tracking Area Code,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getTac() {
         return mTac;
     }
 
     /**
-     * @return 18-bit Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+     * @return 18-bit Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getEarfcn() {
         return mEarfcn;
     }
 
     /**
-     * @return Cell bandwidth in kHz, Integer.MAX_VALUE if unknown
+     * @return Cell bandwidth in kHz,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getBandwidth() {
         return mBandwidth;
     }
 
     /**
-     * @return Mobile Country Code in string format, null if unknown
+     * @return Mobile Country Code in string format, null if unavailable.
      */
     public String getMccString() {
         return mMccStr;
     }
 
     /**
-     * @return Mobile Network Code in string format, null if unknown
+     * @return Mobile Network Code in string format, null if unavailable.
      */
     public String getMncString() {
         return mMncStr;
     }
 
     /**
-     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
+     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
      */
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
@@ -216,8 +222,8 @@
     @Override
     public GsmCellLocation asCellLocation() {
         GsmCellLocation cl = new GsmCellLocation();
-        int tac = mTac != Integer.MAX_VALUE ? mTac : -1;
-        int cid = mCi != Integer.MAX_VALUE ? mCi : -1;
+        int tac = mTac != CellInfo.UNAVAILABLE ? mTac : -1;
+        int cid = mCi != CellInfo.UNAVAILABLE ? mCi : -1;
         cl.setLacAndCid(tac, cid);
         cl.setPsc(0);
         return cl;
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
new file mode 100644
index 0000000..6b1b84c
--- /dev/null
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 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 android.telephony;
+
+import android.os.Parcel;
+import android.telephony.gsm.GsmCellLocation;
+
+import java.util.Objects;
+
+/**
+ * Information to represent a unique 5G NR cell.
+ */
+public final class CellIdentityNr extends CellIdentity {
+    private static final String TAG = "CellIdentityNr";
+
+    private final int mNrArfcn;
+    private final int mPci;
+    private final int mTac;
+
+    /**
+     *
+     * @param pci Physical Cell Id in range [0, 1007].
+     * @param tac 16-bit Tracking Area Code.
+     * @param nrArfcn NR Absolute Radio Frequency Channel Number, in range [0, 3279165].
+     * @param mccStr 3-digit Mobile Country Code in string format.
+     * @param mncStr 2 or 3-digit Mobile Network Code in string format.
+     * @param alphal long alpha Operator Name String or Enhanced Operator Name String.
+     * @param alphas short alpha Operator Name String or Enhanced Operator Name String.
+     *
+     * @hide
+     */
+    public CellIdentityNr(int pci, int tac, int nrArfcn,  String mccStr, String mncStr,
+            String alphal, String alphas) {
+        super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
+        mPci = pci;
+        mTac = tac;
+        mNrArfcn = nrArfcn;
+    }
+
+    /**
+     * @return a CellLocation object for this CellIdentity.
+     * @hide
+     */
+    @Override
+    public CellLocation asCellLocation() {
+        return new GsmCellLocation();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CellIdentityNr)) {
+            return false;
+        }
+
+        CellIdentityNr o = (CellIdentityNr) other;
+        return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn;
+    }
+
+    /**
+     * Get the Absolute Radio Frequency Channel Number.
+     * @return Integer value in range [0, 3279165] or {@link CellInfo#UNAVAILABLE} if unknown.
+     */
+    @Override
+    public int getChannelNumber() {
+        return mNrArfcn;
+    }
+
+    /**
+     * Get the physical cell id.
+     * @return Integer value in range [0, 1007] or {@link CellInfo#UNAVAILABLE} if unknown.
+     */
+    public int getPci() {
+        return mPci;
+    }
+
+    /**
+     * Get the tracking area code.
+     * @return a 16 bit integer or {@link CellInfo#UNAVAILABLE} if unknown.
+     */
+    public int getTac() {
+        return mTac;
+    }
+
+    /**
+     * @return Mobile Country Code in string format, or {@code null} if unknown.
+     */
+    public String getMccString() {
+        return mMccStr;
+    }
+
+    /**
+     * @return Mobile Network Code in string fomrat, or {@code null} if unknown.
+     */
+    public String getMncString() {
+        return mMncStr;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder(TAG + ":{")
+                .append(" mPci = ").append(mPci)
+                .append(" mTac = ").append(mTac)
+                .append(" mNrArfcn = ").append(mNrArfcn)
+                .append(" mMcc = ").append(mMccStr)
+                .append(" mMnc = ").append(mMncStr)
+                .append(" mAlphaLong = ").append(mAlphaLong)
+                .append(" mAlphaShort = ").append(mAlphaShort)
+                .append(" }")
+                .toString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int type) {
+        super.writeToParcel(dest, CellInfo.TYPE_NR);
+        dest.writeInt(mPci);
+        dest.writeInt(mTac);
+        dest.writeInt(mNrArfcn);
+    }
+
+    /** Construct from Parcel, type has already been processed */
+    private CellIdentityNr(Parcel in) {
+        super(TAG, CellInfo.TYPE_NR, in);
+        mPci = in.readInt();
+        mTac = in.readInt();
+        mNrArfcn = in.readInt();
+    }
+
+    /** Implement the Parcelable interface */
+    public static final Creator<CellIdentityNr> CREATOR =
+            new Creator<CellIdentityNr>() {
+                @Override
+                public CellIdentityNr createFromParcel(Parcel in) {
+                    // Skip the type info.
+                    in.readInt();
+                    return createFromParcelBody(in);
+                }
+
+                @Override
+                public CellIdentityNr[] newArray(int size) {
+                    return new CellIdentityNr[size];
+                }
+            };
+
+    /** @hide */
+    protected static CellIdentityNr createFromParcelBody(Parcel in) {
+        return new CellIdentityNr(in);
+    }
+}
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 5a9e474..8b1c1b9 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -28,11 +28,12 @@
     private static final String TAG = CellIdentityTdscdma.class.getSimpleName();
     private static final boolean DBG = false;
 
-    // 16-bit Location Area Code, 0..65535, INT_MAX if unknown.
+    // 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown.
     private final int mLac;
-    // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown.
+    // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, CellInfo.UNAVAILABLE
+    // if unknown.
     private final int mCid;
-    // 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown.
+    // 8-bit Cell Parameters ID described in TS 25.331, 0..127, CellInfo.UNAVAILABLE if unknown.
     private final int mCpid;
     // 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
     private final int mUarfcn;
@@ -42,18 +43,20 @@
      */
     public CellIdentityTdscdma() {
         super(TAG, CellInfo.TYPE_TDSCDMA, null, null, null, null);
-        mLac = Integer.MAX_VALUE;
-        mCid = Integer.MAX_VALUE;
-        mCpid = Integer.MAX_VALUE;
-        mUarfcn = Integer.MAX_VALUE;
+        mLac = CellInfo.UNAVAILABLE;
+        mCid = CellInfo.UNAVAILABLE;
+        mCpid = CellInfo.UNAVAILABLE;
+        mUarfcn = CellInfo.UNAVAILABLE;
     }
 
     /**
      * @param mcc 3-digit Mobile Country Code, 0..999
      * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
-     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
-     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     * @param lac 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown
+     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, CellInfo.
+     *        UNAVAILABLE if unknown
+     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, CellInfo.UNAVAILABLE
+     *        if unknown
      * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
      *
      * @hide
@@ -65,9 +68,11 @@
     /**
      * @param mcc 3-digit Mobile Country Code in string format
      * @param mnc 2 or 3-digit Mobile Network Code in string format
-     * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
-     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
-     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     * @param lac 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown
+     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+     *        CellInfo.UNAVAILABLE if unknown
+     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127,
+     *        CellInfo.UNAVAILABLE if unknown
      * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
      * @param alphas short alpha Operator Name String or Enhanced Operator Name String
@@ -109,21 +114,31 @@
     }
 
     /**
-     * @return 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
+     */
+    public String getMobileNetworkOperator() {
+        return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
+    }
+
+    /**
+     * @return 16-bit Location Area Code, 0..65535,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getLac() {
         return mLac;
     }
 
     /**
-     * @return 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+     * @return 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getCid() {
         return mCid;
     }
 
     /**
-     * @return 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     * @return 8-bit Cell Parameters ID described in TS 25.331, 0..127,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getCpid() {
         return mCpid;
@@ -139,8 +154,8 @@
     @Override
     public GsmCellLocation asCellLocation() {
         GsmCellLocation cl = new GsmCellLocation();
-        int lac = mLac != Integer.MAX_VALUE ? mLac : -1;
-        int cid = mCid != Integer.MAX_VALUE ? mCid : -1;
+        int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
+        int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
         cl.setLacAndCid(lac, cid);
         cl.setPsc(-1); // There is no PSC for TD-SCDMA; not using this for CPI to stem shenanigans
         return cl;
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 727d990..3416ffe 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -46,10 +45,10 @@
      */
     public CellIdentityWcdma() {
         super(TAG, CellInfo.TYPE_WCDMA, null, null, null, null);
-        mLac = Integer.MAX_VALUE;
-        mCid = Integer.MAX_VALUE;
-        mPsc = Integer.MAX_VALUE;
-        mUarfcn = Integer.MAX_VALUE;
+        mLac = CellInfo.UNAVAILABLE;
+        mCid = CellInfo.UNAVAILABLE;
+        mPsc = CellInfo.UNAVAILABLE;
+        mUarfcn = CellInfo.UNAVAILABLE;
     }
     /**
      * public constructor
@@ -62,7 +61,7 @@
      * @hide
      */
     public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc) {
-        this(lac, cid, psc, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc),
+        this(lac, cid, psc, CellInfo.UNAVAILABLE, String.valueOf(mcc), String.valueOf(mnc),
                 null, null);
     }
 
@@ -113,25 +112,28 @@
     }
 
     /**
-     * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+     * @return 3-digit Mobile Country Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      * @deprecated Use {@link #getMccString} instead.
      */
     @Deprecated
     public int getMcc() {
-        return (mMccStr != null) ? Integer.valueOf(mMccStr) : Integer.MAX_VALUE;
+        return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
     }
 
     /**
-     * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+     * @return 2 or 3-digit Mobile Network Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      * @deprecated Use {@link #getMncString} instead.
      */
     @Deprecated
     public int getMnc() {
-        return (mMncStr != null) ? Integer.valueOf(mMncStr) : Integer.MAX_VALUE;
+        return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
     }
 
     /**
-     * @return 16-bit Location Area Code, 0..65535, Integer.MAX_VALUE if unknown
+     * @return 16-bit Location Area Code, 0..65535,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getLac() {
         return mLac;
@@ -139,29 +141,30 @@
 
     /**
      * @return CID
-     * 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, Integer.MAX_VALUE if unknown
+     * 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getCid() {
         return mCid;
     }
 
     /**
-     * @return 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511, Integer.MAX_VALUE
-     * if unknown
+     * @return 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getPsc() {
         return mPsc;
     }
 
     /**
-     * @return Mobile Country Code in string version, null if unknown
+     * @return Mobile Country Code in string version, null if unavailable.
      */
     public String getMccString() {
         return mMccStr;
     }
 
     /**
-     * @return Mobile Network Code in string version, null if unknown
+     * @return Mobile Network Code in string version, null if unavailable.
      */
     public String getMncString() {
         return mMncStr;
@@ -180,7 +183,8 @@
     }
 
     /**
-     * @return 16-bit UMTS Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+     * @return 16-bit UMTS Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getUarfcn() {
         return mUarfcn;
@@ -196,9 +200,9 @@
     @Override
     public GsmCellLocation asCellLocation() {
         GsmCellLocation cl = new GsmCellLocation();
-        int lac = mLac != Integer.MAX_VALUE ? mLac : -1;
-        int cid = mCid != Integer.MAX_VALUE ? mCid : -1;
-        int psc = mPsc != Integer.MAX_VALUE ? mPsc : -1;
+        int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
+        int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
+        int psc = mPsc != CellInfo.UNAVAILABLE ? mPsc : -1;
         cl.setLacAndCid(lac, cid);
         cl.setPsc(psc);
 
@@ -280,4 +284,4 @@
     protected static CellIdentityWcdma createFromParcelBody(Parcel in) {
         return new CellIdentityWcdma(in);
     }
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index 4fe1b01..d0b2687 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -22,6 +22,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -31,42 +33,60 @@
 public abstract class CellInfo implements Parcelable {
 
     /**
+     * This value indicates that the integer field is unreported.
+     */
+    public static final int UNAVAILABLE = Integer.MAX_VALUE;
+
+    /**
      * Cell identity type
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = "TYPE_", value = {TYPE_GSM, TYPE_CDMA, TYPE_LTE, TYPE_WCDMA, TYPE_TDSCDMA})
+    @IntDef(prefix = "TYPE_",
+            value = {TYPE_GSM, TYPE_CDMA, TYPE_LTE, TYPE_WCDMA, TYPE_TDSCDMA, TYPE_NR})
     public @interface Type {}
+
     /**
      * Unknown cell identity type
      * @hide
      */
-    public static final int TYPE_UNKNOWN        = 0;
+    public static final int TYPE_UNKNOWN = 0;
+
     /**
      * GSM cell identity type
      * @hide
      */
-    public static final int TYPE_GSM            = 1;
+    public static final int TYPE_GSM = 1;
+
     /**
      * CDMA cell identity type
      * @hide
      */
-    public static final int TYPE_CDMA           = 2;
+    public static final int TYPE_CDMA = 2;
+
     /**
      * LTE cell identity type
      * @hide
      */
-    public static final int TYPE_LTE            = 3;
+    public static final int TYPE_LTE = 3;
+
     /**
      * WCDMA cell identity type
      * @hide
      */
-    public static final int TYPE_WCDMA          = 4;
+    public static final int TYPE_WCDMA = 4;
+
     /**
      * TD-SCDMA cell identity type
      * @hide
      */
-    public static final int TYPE_TDSCDMA        = 5;
+    public static final int TYPE_TDSCDMA = 5;
+
+    /**
+     * 5G cell identity type
+     * @hide
+     */
+    public static final int TYPE_NR = 6;
 
     // Type to distinguish where time stamp gets recorded.
 
@@ -120,41 +140,45 @@
     // Observation time stamped as type in nanoseconds since boot
     private long mTimeStamp;
 
-    // Where time stamp gets recorded.
-    // Value of TIMESTAMP_TYPE_XXXX
-    private int mTimeStampType;
-
     /** @hide */
     protected CellInfo() {
         this.mRegistered = false;
-        this.mTimeStampType = TIMESTAMP_TYPE_UNKNOWN;
         this.mTimeStamp = Long.MAX_VALUE;
     }
 
     /** @hide */
     protected CellInfo(CellInfo ci) {
         this.mRegistered = ci.mRegistered;
-        this.mTimeStampType = ci.mTimeStampType;
         this.mTimeStamp = ci.mTimeStamp;
         this.mCellConnectionStatus = ci.mCellConnectionStatus;
     }
 
-    /** True if this cell is registered to the mobile network */
+    /**
+     * True if the phone is registered to a mobile network that provides service on this cell
+     * and this cell is being used or would be used for network signaling.
+     */
     public boolean isRegistered() {
         return mRegistered;
     }
+
     /** @hide */
     public void setRegistered(boolean registered) {
         mRegistered = registered;
     }
 
-    /** Approximate time of this cell information in nanos since boot */
+    /**
+     * Approximate time this cell information was received from the modem.
+     *
+     * @return a time stamp in nanos since boot.
+     */
     public long getTimeStamp() {
         return mTimeStamp;
     }
+
     /** @hide */
-    public void setTimeStamp(long timeStamp) {
-        mTimeStamp = timeStamp;
+    @VisibleForTesting
+    public void setTimeStamp(long ts) {
+        mTimeStamp = ts;
     }
 
     /** @hide */
@@ -184,31 +208,11 @@
         mCellConnectionStatus = cellConnectionStatus;
     }
 
-    /**
-     * Where time stamp gets recorded.
-     * @return one of TIMESTAMP_TYPE_XXXX
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public int getTimeStampType() {
-        return mTimeStampType;
-    }
-
-    /** @hide */
-    public void setTimeStampType(int timeStampType) {
-        if (timeStampType < TIMESTAMP_TYPE_UNKNOWN || timeStampType > TIMESTAMP_TYPE_JAVA_RIL) {
-            mTimeStampType = TIMESTAMP_TYPE_UNKNOWN;
-        } else {
-            mTimeStampType = timeStampType;
-        }
-    }
-
     @Override
     public int hashCode() {
         int primeNum = 31;
         return ((mRegistered ? 0 : 1) * primeNum) + ((int)(mTimeStamp / 1000) * primeNum)
-                + (mTimeStampType * primeNum) + (mCellConnectionStatus * primeNum);
+                + (mCellConnectionStatus * primeNum);
     }
 
     @Override
@@ -223,37 +227,17 @@
             CellInfo o = (CellInfo) other;
             return mRegistered == o.mRegistered
                     && mTimeStamp == o.mTimeStamp
-                    && mTimeStampType == o.mTimeStampType
                     && mCellConnectionStatus == o.mCellConnectionStatus;
         } catch (ClassCastException e) {
             return false;
         }
     }
 
-    @UnsupportedAppUsage
-    private static String timeStampTypeToString(int type) {
-        switch (type) {
-            case 1:
-                return "antenna";
-            case 2:
-                return "modem";
-            case 3:
-                return "oem_ril";
-            case 4:
-                return "java_ril";
-            default:
-                return "unknown";
-        }
-    }
-
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer();
-        String timeStampType;
 
         sb.append("mRegistered=").append(mRegistered ? "YES" : "NO");
-        timeStampType = timeStampTypeToString(mTimeStampType);
-        sb.append(" mTimeStampType=").append(timeStampType);
         sb.append(" mTimeStamp=").append(mTimeStamp).append("ns");
         sb.append(" mCellConnectionStatus=").append(mCellConnectionStatus);
 
@@ -280,7 +264,6 @@
     protected void writeToParcel(Parcel dest, int flags, int type) {
         dest.writeInt(type);
         dest.writeInt(mRegistered ? 1 : 0);
-        dest.writeInt(mTimeStampType);
         dest.writeLong(mTimeStamp);
         dest.writeInt(mCellConnectionStatus);
     }
@@ -292,7 +275,6 @@
      */
     protected CellInfo(Parcel in) {
         mRegistered = (in.readInt() == 1) ? true : false;
-        mTimeStampType = in.readInt();
         mTimeStamp = in.readLong();
         mCellConnectionStatus = in.readInt();
     }
@@ -308,6 +290,7 @@
                     case TYPE_LTE: return CellInfoLte.createFromParcelBody(in);
                     case TYPE_WCDMA: return CellInfoWcdma.createFromParcelBody(in);
                     case TYPE_TDSCDMA: return CellInfoTdscdma.createFromParcelBody(in);
+                    case TYPE_NR: return CellInfoNr.createFromParcelBody(in);
                     default: throw new RuntimeException("Bad CellInfo Parcel");
                 }
         }
diff --git a/telephony/java/android/telephony/CellInfoNr.java b/telephony/java/android/telephony/CellInfoNr.java
new file mode 100644
index 0000000..11857a6
--- /dev/null
+++ b/telephony/java/android/telephony/CellInfoNr.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 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 android.telephony;
+
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * A {@link CellInfo} representing an 5G NR cell that provides identity and measurement info.
+ */
+public final class CellInfoNr extends CellInfo {
+    private static final String TAG = "CellInfoNr";
+
+    private final CellIdentityNr mCellIdentity;
+    private final CellSignalStrengthNr mCellSignalStrength;
+
+    private CellInfoNr(Parcel in) {
+        super(in);
+        mCellIdentity = CellIdentityNr.CREATOR.createFromParcel(in);
+        mCellSignalStrength = CellSignalStrengthNr.CREATOR.createFromParcel(in);
+    }
+
+    @Override
+    public CellIdentity getCellIdentity() {
+        return mCellIdentity;
+    }
+
+    @Override
+    public CellSignalStrength getCellSignalStrength() {
+        return mCellSignalStrength;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mCellIdentity, mCellSignalStrength);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CellInfoNr)) {
+            return false;
+        }
+
+        CellInfoNr o = (CellInfoNr) other;
+        return super.equals(o) && mCellIdentity.equals(o.mCellIdentity)
+                && mCellSignalStrength.equals(o.mCellSignalStrength);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append(TAG + ":{")
+                .append(" " + super.toString())
+                .append(" " + mCellIdentity)
+                .append(" " + mCellSignalStrength)
+                .append(" }")
+                .toString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags, TYPE_NR);
+        mCellIdentity.writeToParcel(dest, flags);
+        mCellSignalStrength.writeToParcel(dest, flags);
+    }
+
+    public static final Creator<CellInfoNr> CREATOR = new Creator<CellInfoNr>() {
+        @Override
+        public CellInfoNr createFromParcel(Parcel in) {
+            // Skip the type info.
+            in.readInt();
+            return new CellInfoNr(in);
+        }
+
+        @Override
+        public CellInfoNr[] newArray(int size) {
+            return new CellInfoNr[size];
+        }
+    };
+
+    /** @hide */
+    protected static CellInfoNr createFromParcelBody(Parcel in) {
+        return new CellInfoNr(in);
+    }
+}
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index aa6b207..5123052 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -51,8 +51,9 @@
      * <p>Note that this HAL is inconsistent with UMTS-based radio techs as the value indicating
      * that a field is unreported is negative, rather than a large(r) positive number.
      * <p>Also note that to keep the public-facing methods of this class consistent with others,
-     * unreported values are coerced to Integer.MAX_VALUE rather than left as -1, which is
-     * a departure from SignalStrength, which is stuck with the values it currently reports.
+     * unreported values are coerced to {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+     * rather than left as -1, which is a departure from SignalStrength, which is stuck with the
+     * values it currently reports.
      *
      * @param cdmaDbm negative of the CDMA signal strength value or -1 if invalid.
      * @param cdmaEcio negative of the CDMA pilot/noise ratio or -1 if invalid.
@@ -65,12 +66,12 @@
             int evdoSnr) {
         // The values here were lifted from SignalStrength.validateInput()
         // FIXME: Combine all checking and setting logic between this and SignalStrength.
-        mCdmaDbm = ((cdmaDbm > 0) && (cdmaDbm < 120))  ? -cdmaDbm : Integer.MAX_VALUE;
-        mCdmaEcio = ((cdmaEcio > 0) && (cdmaEcio < 160)) ? -cdmaEcio : Integer.MAX_VALUE;
+        mCdmaDbm = ((cdmaDbm > 0) && (cdmaDbm < 120))  ? -cdmaDbm : CellInfo.UNAVAILABLE;
+        mCdmaEcio = ((cdmaEcio > 0) && (cdmaEcio < 160)) ? -cdmaEcio : CellInfo.UNAVAILABLE;
 
-        mEvdoDbm = ((evdoDbm > 0) && (evdoDbm < 120)) ? -evdoDbm : Integer.MAX_VALUE;
-        mEvdoEcio = ((evdoEcio > 0) && (evdoEcio < 160)) ? -evdoEcio : Integer.MAX_VALUE;
-        mEvdoSnr = ((evdoSnr > 0) && (evdoSnr <= 8)) ? evdoSnr : Integer.MAX_VALUE;
+        mEvdoDbm = ((evdoDbm > 0) && (evdoDbm < 120)) ? -evdoDbm : CellInfo.UNAVAILABLE;
+        mEvdoEcio = ((evdoEcio > 0) && (evdoEcio < 160)) ? -evdoEcio : CellInfo.UNAVAILABLE;
+        mEvdoSnr = ((evdoSnr > 0) && (evdoSnr <= 8)) ? evdoSnr : CellInfo.UNAVAILABLE;
     }
 
     /** @hide */
@@ -96,11 +97,11 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mCdmaDbm = Integer.MAX_VALUE;
-        mCdmaEcio = Integer.MAX_VALUE;
-        mEvdoDbm = Integer.MAX_VALUE;
-        mEvdoEcio = Integer.MAX_VALUE;
-        mEvdoSnr = Integer.MAX_VALUE;
+        mCdmaDbm = CellInfo.UNAVAILABLE;
+        mCdmaEcio = CellInfo.UNAVAILABLE;
+        mEvdoDbm = CellInfo.UNAVAILABLE;
+        mEvdoEcio = CellInfo.UNAVAILABLE;
+        mEvdoSnr = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -139,7 +140,7 @@
         int cdmaAsuLevel;
         int ecioAsuLevel;
 
-        if (cdmaDbm == Integer.MAX_VALUE) cdmaAsuLevel = 99;
+        if (cdmaDbm == CellInfo.UNAVAILABLE) cdmaAsuLevel = 99;
         else if (cdmaDbm >= -75) cdmaAsuLevel = 16;
         else if (cdmaDbm >= -82) cdmaAsuLevel = 8;
         else if (cdmaDbm >= -90) cdmaAsuLevel = 4;
@@ -148,7 +149,7 @@
         else cdmaAsuLevel = 99;
 
         // Ec/Io are in dB*10
-        if (cdmaEcio == Integer.MAX_VALUE) ecioAsuLevel = 99;
+        if (cdmaEcio == CellInfo.UNAVAILABLE) ecioAsuLevel = 99;
         else if (cdmaEcio >= -90) ecioAsuLevel = 16;
         else if (cdmaEcio >= -100) ecioAsuLevel = 8;
         else if (cdmaEcio >= -115) ecioAsuLevel = 4;
@@ -170,7 +171,7 @@
         int levelDbm;
         int levelEcio;
 
-        if (cdmaDbm == Integer.MAX_VALUE) levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        if (cdmaDbm == CellInfo.UNAVAILABLE) levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         else if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
         else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
         else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
@@ -178,7 +179,7 @@
         else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
 
         // Ec/Io are in dB*10
-        if (cdmaEcio == Integer.MAX_VALUE) levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        if (cdmaEcio == CellInfo.UNAVAILABLE) levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         else if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
         else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
         else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
@@ -199,14 +200,14 @@
         int levelEvdoDbm;
         int levelEvdoSnr;
 
-        if (evdoDbm == Integer.MAX_VALUE) levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        if (evdoDbm == CellInfo.UNAVAILABLE) levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         else if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
         else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
         else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
         else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
         else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
 
-        if (evdoSnr == Integer.MAX_VALUE) levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        if (evdoSnr == CellInfo.UNAVAILABLE) levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         else if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
         else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
         else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 1e8d119..e906f46 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -40,7 +40,7 @@
     @UnsupportedAppUsage
     private int mBitErrorRate;   // bit error rate (0-7, 99) as defined in TS 27.007 8.5
     @UnsupportedAppUsage
-    private int mTimingAdvance; // range from 0-219 or Integer.MAX_VALUE if unknown
+    private int mTimingAdvance; // range from 0-219 or CellInfo.UNAVAILABLE if unknown
 
     /** @hide */
     @UnsupportedAppUsage
@@ -50,7 +50,7 @@
 
     /** @hide */
     public CellSignalStrengthGsm(int ss, int ber) {
-        this(ss, ber, Integer.MAX_VALUE);
+        this(ss, ber, CellInfo.UNAVAILABLE);
     }
 
     /** @hide */
@@ -81,9 +81,9 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mSignalStrength = Integer.MAX_VALUE;
-        mBitErrorRate = Integer.MAX_VALUE;
-        mTimingAdvance = Integer.MAX_VALUE;
+        mSignalStrength = CellInfo.UNAVAILABLE;
+        mBitErrorRate = CellInfo.UNAVAILABLE;
+        mTimingAdvance = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -112,8 +112,9 @@
 
     /**
      * Get the GSM timing advance between 0..219 symbols (normally 0..63).
-     * Integer.MAX_VALUE is reported when there is no RR connection.
-     * Refer to 3GPP 45.010 Sec 5.8
+     * <p>{@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} is reported when there is no RR
+     * connection. Refer to 3GPP 45.010 Sec 5.8.
+     *
      * @return the current GSM timing advance, if available.
      */
     public int getTimingAdvance() {
@@ -128,11 +129,11 @@
         int dBm;
 
         int level = mSignalStrength;
-        int asu = (level == 99 ? Integer.MAX_VALUE : level);
-        if (asu != Integer.MAX_VALUE) {
+        int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
+        if (asu != CellInfo.UNAVAILABLE) {
             dBm = -113 + (2 * asu);
         } else {
-            dBm = Integer.MAX_VALUE;
+            dBm = CellInfo.UNAVAILABLE;
         }
         if (DBG) log("getDbm=" + dBm);
         return dBm;
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index ed7d4b2..2ff75e6 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -19,7 +19,6 @@
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Rlog;
 
 import java.util.Objects;
 
@@ -85,12 +84,12 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mSignalStrength = Integer.MAX_VALUE;
-        mRsrp = Integer.MAX_VALUE;
-        mRsrq = Integer.MAX_VALUE;
-        mRssnr = Integer.MAX_VALUE;
-        mCqi = Integer.MAX_VALUE;
-        mTimingAdvance = Integer.MAX_VALUE;
+        mSignalStrength = CellInfo.UNAVAILABLE;
+        mRsrp = CellInfo.UNAVAILABLE;
+        mRsrq = CellInfo.UNAVAILABLE;
+        mRssnr = CellInfo.UNAVAILABLE;
+        mCqi = CellInfo.UNAVAILABLE;
+        mTimingAdvance = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -104,26 +103,27 @@
         int levelRsrp = 0;
         int levelRssnr = 0;
 
-        if (mRsrp == Integer.MAX_VALUE) levelRsrp = 0;
+        if (mRsrp == CellInfo.UNAVAILABLE) levelRsrp = 0;
         else if (mRsrp >= -95) levelRsrp = SIGNAL_STRENGTH_GREAT;
         else if (mRsrp >= -105) levelRsrp = SIGNAL_STRENGTH_GOOD;
         else if (mRsrp >= -115) levelRsrp = SIGNAL_STRENGTH_MODERATE;
         else levelRsrp = SIGNAL_STRENGTH_POOR;
 
         // See RIL_LTE_SignalStrength in ril.h
-        if (mRssnr == Integer.MAX_VALUE) levelRssnr = 0;
+        if (mRssnr == CellInfo.UNAVAILABLE) levelRssnr = 0;
         else if (mRssnr >= 45) levelRssnr = SIGNAL_STRENGTH_GREAT;
         else if (mRssnr >= 10) levelRssnr = SIGNAL_STRENGTH_GOOD;
         else if (mRssnr >= -30) levelRssnr = SIGNAL_STRENGTH_MODERATE;
         else levelRssnr = SIGNAL_STRENGTH_POOR;
 
         int level;
-        if (mRsrp == Integer.MAX_VALUE)
+        if (mRsrp == CellInfo.UNAVAILABLE) {
             level = levelRssnr;
-        else if (mRssnr == Integer.MAX_VALUE)
+        } else if (mRssnr == CellInfo.UNAVAILABLE) {
             level = levelRsrp;
-        else
+        } else {
             level = (levelRssnr < levelRsrp) ? levelRssnr : levelRsrp;
+        }
 
         if (DBG) log("Lte rsrp level: " + levelRsrp
                 + " snr level: " + levelRssnr + " level: " + level);
@@ -133,7 +133,8 @@
     /**
      * Get reference signal received quality
      *
-     * @return the RSRQ if available or Integer.MAX_VALUE if unavailable.
+     * @return the RSRQ if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getRsrq() {
         return mRsrq;
@@ -142,7 +143,8 @@
     /**
      * Get reference signal signal-to-noise ratio
      *
-     * @return the RSSNR if available or Integer.MAX_VALUE if unavailable.
+     * @return the RSSNR if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getRssnr() {
         return mRssnr;
@@ -160,7 +162,8 @@
     /**
      * Get channel quality indicator
      *
-     * @return the CQI if available or Integer.MAX_VALUE if unavailable.
+     * @return the CQI if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getCqi() {
         return mCqi;
@@ -184,7 +187,7 @@
     public int getAsuLevel() {
         int lteAsuLevel = 99;
         int lteDbm = getDbm();
-        if (lteDbm == Integer.MAX_VALUE) lteAsuLevel = 99;
+        if (lteDbm == CellInfo.UNAVAILABLE) lteAsuLevel = 99;
         else if (lteDbm <= -140) lteAsuLevel = 0;
         else if (lteDbm >= -43) lteAsuLevel = 97;
         else lteAsuLevel = lteDbm + 140;
@@ -194,10 +197,11 @@
 
     /**
      * Get the timing advance value for LTE, as a value in range of 0..1282.
-     * Integer.MAX_VALUE is reported when there is no active RRC
-     * connection. Refer to 3GPP 36.213 Sec 4.2.3
+     * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} is reported when there is no
+     * active RRC connection. Refer to 3GPP 36.213 Sec 4.2.3
      *
-     * @return the LTE timing advance if available or Integer.MAX_VALUE if unavailable.
+     * @return the LTE timing advance if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getTimingAdvance() {
         return mTimingAdvance;
@@ -252,8 +256,8 @@
         // Need to multiply rsrp and rsrq by -1
         // to ensure consistency when reading values written here
         // unless the values are invalid
-        dest.writeInt(mRsrp * (mRsrp != Integer.MAX_VALUE ? -1 : 1));
-        dest.writeInt(mRsrq * (mRsrq != Integer.MAX_VALUE ? -1 : 1));
+        dest.writeInt(mRsrp * (mRsrp != CellInfo.UNAVAILABLE ? -1 : 1));
+        dest.writeInt(mRsrq * (mRsrq != CellInfo.UNAVAILABLE ? -1 : 1));
         dest.writeInt(mRssnr);
         dest.writeInt(mCqi);
         dest.writeInt(mTimingAdvance);
@@ -268,9 +272,9 @@
         // rsrp and rsrq are written into the parcel as positive values.
         // Need to convert into negative values unless the values are invalid
         mRsrp = in.readInt();
-        if (mRsrp != Integer.MAX_VALUE) mRsrp *= -1;
+        if (mRsrp != CellInfo.UNAVAILABLE) mRsrp *= -1;
         mRsrq = in.readInt();
-        if (mRsrq != Integer.MAX_VALUE) mRsrq *= -1;
+        if (mRsrq != CellInfo.UNAVAILABLE) mRsrq *= -1;
         mRssnr = in.readInt();
         mCqi = in.readInt();
         mTimingAdvance = in.readInt();
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
new file mode 100644
index 0000000..8079242
--- /dev/null
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2018 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 android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * 5G NR signal strength related information.
+ */
+public final class CellSignalStrengthNr extends CellSignalStrength implements Parcelable {
+    /**
+     * The value is used to indicate that the asu level is unknown.
+     * Reference: 3GPP TS 27.007 section 8.69.
+     * @hide
+     */
+    public static final int UNKNOWN_ASU_LEVEL = 99;
+
+    private static final String TAG = "CellSignalStrengthNr";
+
+    /**
+     * These threshold values are copied from LTE.
+     * TODO: make it configurable via CarrierConfig.
+     */
+    private static final int SIGNAL_GREAT_THRESHOLD = -95;
+    private static final int SIGNAL_GOOD_THRESHOLD = -105;
+    private static final int SIGNAL_MODERATE_THRESHOLD = -115;
+
+    private int mCsiRsrp;
+    private int mCsiRsrq;
+    private int mCsiSinr;
+    private int mSsRsrp;
+    private int mSsRsrq;
+    private int mSsSinr;
+
+    /**
+     * @param csiRsrp CSI reference signal received power.
+     * @param csiRsrq CSI reference signal received quality.
+     * @param csiSinr CSI signal-to-noise and interference ratio.
+     * @param ssRsrp SS reference signal received power.
+     * @param ssRsrq SS reference signal received quality.
+     * @param ssSinr SS signal-to-noise and interference ratio.
+     * @hide
+     */
+    public CellSignalStrengthNr(
+            int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) {
+        mCsiRsrp = csiRsrp;
+        mCsiRsrq = csiRsrq;
+        mCsiSinr = csiSinr;
+        mSsRsrp = ssRsrp;
+        mSsRsrq = ssRsrq;
+        mSsSinr = ssSinr;
+    }
+
+    /**
+     * Reference: 3GPP TS 38.215.
+     * Range: -140 dBm to -44 dBm.
+     * @return SS reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported
+     * value.
+     */
+    public int getSsRsrp() {
+        return mSsRsrp;
+    }
+
+    /**
+     * Reference: 3GPP TS 38.215.
+     * Range: -20 dB to -3 dB.
+     * @return SS reference signal received quality, {@link CellInfo#UNAVAILABLE} means unreported
+     * value.
+     */
+    public int getSsRsrq() {
+        return mSsRsrq;
+    }
+
+    /**
+     * Reference: 3GPP TS 38.215 Sec 5.1.*, 3GPP TS 38.133 10.1.16.1
+     * Range: -23 dB to 40 dB
+     * @return SS signal-to-noise and interference ratio, {@link CellInfo#UNAVAILABLE} means
+     * unreported value.
+     */
+    public int getSsSinr() {
+        return mSsSinr;
+    }
+
+    /**
+     * Reference: 3GPP TS 38.215.
+     * Range: -140 dBm to -44 dBm.
+     * @return CSI reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported
+     * value.
+     */
+    public int getCsiRsrp() {
+        return mCsiRsrp;
+    }
+
+    /**
+     * Reference: 3GPP TS 38.215.
+     * Range: -20 dB to -3 dB.
+     * @return CSI reference signal received quality, {@link CellInfo#UNAVAILABLE} means unreported
+     * value.
+     */
+    public int getCsiRsrq() {
+        return mCsiRsrq;
+    }
+
+    /**
+     * Reference: 3GPP TS 38.215 Sec 5.1.*, 3GPP TS 38.133 10.1.16.1
+     * Range: -23 dB to 23 dB
+     * @return CSI signal-to-noise and interference ratio, {@link CellInfo#UNAVAILABLE} means
+     * unreported value.
+     */
+    public int getCsiSinr() {
+        return mCsiSinr;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCsiRsrp);
+        dest.writeInt(mCsiRsrq);
+        dest.writeInt(mCsiSinr);
+        dest.writeInt(mSsRsrp);
+        dest.writeInt(mSsRsrq);
+        dest.writeInt(mSsSinr);
+    }
+
+    private CellSignalStrengthNr(Parcel in) {
+        mCsiRsrp = in.readInt();
+        mCsiRsrq = in.readInt();
+        mCsiSinr = in.readInt();
+        mSsRsrp = in.readInt();
+        mSsRsrq = in.readInt();
+        mSsSinr = in.readInt();
+    }
+
+    /** @hide */
+    @Override
+    public void setDefaultValues() {
+        mCsiRsrp = CellInfo.UNAVAILABLE;
+        mCsiRsrq = CellInfo.UNAVAILABLE;
+        mCsiSinr = CellInfo.UNAVAILABLE;
+        mSsRsrp = CellInfo.UNAVAILABLE;
+        mSsRsrq = CellInfo.UNAVAILABLE;
+        mSsSinr = CellInfo.UNAVAILABLE;
+    }
+
+    @Override
+    public int getLevel() {
+        if (mCsiRsrp == CellInfo.UNAVAILABLE) {
+            return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        } else if (mCsiRsrp >= SIGNAL_GREAT_THRESHOLD) {
+            return SIGNAL_STRENGTH_GREAT;
+        } else if (mCsiRsrp >= SIGNAL_GOOD_THRESHOLD) {
+            return SIGNAL_STRENGTH_GOOD;
+        } else if (mCsiRsrp >= SIGNAL_MODERATE_THRESHOLD) {
+            return SIGNAL_STRENGTH_MODERATE;
+        } else {
+            return SIGNAL_STRENGTH_POOR;
+        }
+    }
+
+    /**
+     * Calculates the NR signal as an asu value between 0..97, 99 is unknown.
+     * Asu is calculated based on 3GPP RSRP, refer to 3GPP TS 27.007 section 8.69.
+     * @return an integer represent the asu level of the signal strength.
+     */
+    @Override
+    public int getAsuLevel() {
+        int asuLevel;
+        int nrDbm = getDbm();
+        if (nrDbm == CellInfo.UNAVAILABLE) {
+            asuLevel = UNKNOWN_ASU_LEVEL;
+        } else if (nrDbm <= -140) {
+            asuLevel = 0;
+        } else if (nrDbm >= -43) {
+            asuLevel = 97;
+        } else {
+            asuLevel = nrDbm + 140;
+        }
+        return asuLevel;
+    }
+
+    @Override
+    public int getDbm() {
+        return mCsiRsrp;
+    }
+
+    /** @hide */
+    @Override
+    public CellSignalStrength copy() {
+        return new CellSignalStrengthNr(
+                mCsiRsrp, mCsiRsrq, mCsiSinr, mSsRsrp, mSsRsrq, mSsSinr);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mSsRsrp, mSsRsrq, mSsSinr);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof CellSignalStrengthNr) {
+            CellSignalStrengthNr o = (CellSignalStrengthNr) obj;
+            return mCsiRsrp == o.mCsiRsrp && mCsiRsrq == o.mCsiRsrq && mCsiSinr == o.mCsiSinr
+                    && mSsRsrp == o.mSsRsrp && mSsRsrq == o.mSsRsrq && mSsSinr == o.mSsSinr;
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append(TAG + ":{")
+                .append(" csiRsrp = " + mCsiRsrp)
+                .append(" csiRsrq = " + mCsiRsrq)
+                .append(" csiSinr = " + mCsiSinr)
+                .append(" ssRsrp = " + mSsRsrp)
+                .append(" ssRsrq = " + mSsRsrq)
+                .append(" ssSinr = " + mSsSinr)
+                .append(" }")
+                .toString();
+    }
+
+    /** Implement the Parcelable interface */
+    public static final Parcelable.Creator<CellSignalStrengthNr> CREATOR =
+            new Parcelable.Creator<CellSignalStrengthNr>() {
+        @Override
+        public CellSignalStrengthNr createFromParcel(Parcel in) {
+            return new CellSignalStrengthNr(in);
+        }
+
+        @Override
+        public CellSignalStrengthNr[] newArray(int size) {
+            return new CellSignalStrengthNr[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
index 41859a3..4d040cc 100644
--- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
@@ -36,11 +36,11 @@
     private static final int TDSCDMA_SIGNAL_STRENGTH_MODERATE = 5;
 
     private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
-                                 // or Integer.MAX_VALUE if unknown
+                                 // or CellInfo.UNAVAILABLE if unknown
     private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
-                               // Integer.MAX_VALUE if unknown
-    private int mRscp; // Pilot power (0-96, 255) as defined in TS 27.007 8.69 or Integer.MAX_VALUE
-                       // if unknown
+                               // CellInfo.UNAVAILABLE if unknown
+    private int mRscp; // Pilot power (0-96, 255) as defined in TS 27.007 8.69 or
+                       // CellInfo.UNAVAILABLE if unknown
 
     /** @hide */
     public CellSignalStrengthTdscdma() {
@@ -75,9 +75,9 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mSignalStrength = Integer.MAX_VALUE;
-        mBitErrorRate = Integer.MAX_VALUE;
-        mRscp = Integer.MAX_VALUE;
+        mSignalStrength = CellInfo.UNAVAILABLE;
+        mBitErrorRate = CellInfo.UNAVAILABLE;
+        mRscp = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -118,11 +118,11 @@
         int dBm;
 
         int level = mSignalStrength;
-        int asu = (level == 99 ? Integer.MAX_VALUE : level);
-        if (asu != Integer.MAX_VALUE) {
+        int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
+        if (asu != CellInfo.UNAVAILABLE) {
             dBm = -113 + (2 * asu);
         } else {
-            dBm = Integer.MAX_VALUE;
+            dBm = CellInfo.UNAVAILABLE;
         }
         if (DBG) log("getDbm=" + dBm);
         return dBm;
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 66e0882..0048cbd 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -37,14 +37,14 @@
 
     @UnsupportedAppUsage
     private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
-                                 // or Integer.MAX_VALUE if unknown
+                                 // or CellInfo.UNAVAILABLE if unknown
     @UnsupportedAppUsage
     private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
-                               // Integer.MAX_VALUE if unknown
+                               // CellInfo.UNAVAILABLE if unknown
     private int mRscp; // bit error rate (0-96, 255) as defined in TS 27.007 8.69 or
-                       // Integer.MAX_VALUE if unknown
+                       // CellInfo.UNAVAILABLE if unknown
     private int mEcNo; // signal to noise radio (0-49, 255) as defined in TS 27.007 8.69 or
-                       // Integer.MAX_VALUE if unknown
+                       // CellInfo.UNAVAILABLE if unknown
 
     /** @hide */
     public CellSignalStrengthWcdma() {
@@ -81,10 +81,10 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mSignalStrength = Integer.MAX_VALUE;
-        mBitErrorRate = Integer.MAX_VALUE;
-        mRscp = Integer.MAX_VALUE;
-        mEcNo = Integer.MAX_VALUE;
+        mSignalStrength = CellInfo.UNAVAILABLE;
+        mBitErrorRate = CellInfo.UNAVAILABLE;
+        mRscp = CellInfo.UNAVAILABLE;
+        mEcNo = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -119,11 +119,11 @@
         int dBm;
 
         int level = mSignalStrength;
-        int asu = (level == 99 ? Integer.MAX_VALUE : level);
-        if (asu != Integer.MAX_VALUE) {
+        int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
+        if (asu != CellInfo.UNAVAILABLE) {
             dBm = -113 + (2 * asu);
         } else {
-            dBm = Integer.MAX_VALUE;
+            dBm = CellInfo.UNAVAILABLE;
         }
         if (DBG) log("getDbm=" + dBm);
         return dBm;
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index d7169b2..c6887ab 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -342,6 +342,12 @@
      */
     public static final int TOO_MANY_ONGOING_CALLS = 75;
 
+    /**
+     * Indicates that a new outgoing call cannot be placed because OTASP provisioning is currently
+     * in process.
+     */
+    public static final int OTASP_PROVISIONING_IN_PROCESS = 76;
+
     //*********************************************************************************************
     // When adding a disconnect type:
     // 1) Update toString() with the newly added disconnect type.
@@ -505,6 +511,8 @@
             return "CALLING_DISABLED";
         case TOO_MANY_ONGOING_CALLS:
             return "TOO_MANY_ONGOING_CALLS";
+        case OTASP_PROVISIONING_IN_PROCESS:
+            return "OTASP_PROVISIONING_IN_PROCESS";
         default:
             return "INVALID: " + cause;
         }
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index c83d6aa..97b1bdc 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -301,6 +301,14 @@
     @SystemApi
     public static final int LISTEN_RADIO_POWER_STATE_CHANGED               = 0x00800000;
 
+    /**
+     * Listen for changes to emergency number list based on all active subscriptions.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     */
+    public static final int LISTEN_EMERGENCY_NUMBER_LIST                   = 0x01000000;
+
     /*
      * Subscription used to listen to the phone state changes
      * @hide
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index 1d79988..da3acc2 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -29,29 +29,32 @@
  */
 public class RadioAccessFamily implements Parcelable {
 
-    // Radio Access Family
+    /**
+     * TODO: get rid of RAF definition in RadioAccessFamily and
+     * use {@link TelephonyManager.NetworkTypeBitMask}
+     */
     // 2G
-    public static final int RAF_UNKNOWN = (1 <<  ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
-    public static final int RAF_GSM = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GSM);
-    public static final int RAF_GPRS = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GPRS);
-    public static final int RAF_EDGE = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EDGE);
-    public static final int RAF_IS95A = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IS95A);
-    public static final int RAF_IS95B = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IS95B);
-    public static final int RAF_1xRTT = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
+    public static final int RAF_UNKNOWN = TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN;
+    public static final int RAF_GSM = TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
+    public static final int RAF_GPRS = TelephonyManager.NETWORK_TYPE_BITMASK_GPRS;
+    public static final int RAF_EDGE = TelephonyManager.NETWORK_TYPE_BITMASK_EDGE;
+    public static final int RAF_IS95A = TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
+    public static final int RAF_IS95B = TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
+    public static final int RAF_1xRTT = TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
     // 3G
-    public static final int RAF_EVDO_0 = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0);
-    public static final int RAF_EVDO_A = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A);
-    public static final int RAF_EVDO_B = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B);
-    public static final int RAF_EHRPD = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD);
-    public static final int RAF_HSUPA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA);
-    public static final int RAF_HSDPA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA);
-    public static final int RAF_HSPA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPA);
-    public static final int RAF_HSPAP = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP);
-    public static final int RAF_UMTS = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
-    public static final int RAF_TD_SCDMA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA);
+    public static final int RAF_EVDO_0 = TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0;
+    public static final int RAF_EVDO_A = TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A;
+    public static final int RAF_EVDO_B = TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B;
+    public static final int RAF_EHRPD = TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD;
+    public static final int RAF_HSUPA = TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA;
+    public static final int RAF_HSDPA = TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA;
+    public static final int RAF_HSPA = TelephonyManager.NETWORK_TYPE_BITMASK_HSPA;
+    public static final int RAF_HSPAP = TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP;
+    public static final int RAF_UMTS = TelephonyManager.NETWORK_TYPE_BITMASK_UMTS;
+    public static final int RAF_TD_SCDMA = TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA;
     // 4G
-    public static final int RAF_LTE = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
-    public static final int RAF_LTE_CA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA);
+    public static final int RAF_LTE = TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
+    public static final int RAF_LTE_CA = TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
 
     // Grouping of RAFs
     // 2G
@@ -74,9 +77,9 @@
      * Constructor.
      *
      * @param phoneId the phone ID
-     * @param radioAccessFamily the phone radio access family defined
-     *        in RadioAccessFamily. It's a bit mask value to represent
-     *        the support type.
+     * @param radioAccessFamily the phone radio access family bitmask based on
+     * {@link TelephonyManager.NetworkTypeBitMask}. It's a bit mask value to represent the support
+     *                          type.
      */
     @UnsupportedAppUsage
     public RadioAccessFamily(int phoneId, int radioAccessFamily) {
@@ -100,7 +103,7 @@
      * @return radio access family
      */
     @UnsupportedAppUsage
-    public int getRadioAccessFamily() {
+    public @TelephonyManager.NetworkTypeBitMask int getRadioAccessFamily() {
         return mRadioAccessFamily;
     }
 
@@ -388,4 +391,76 @@
         }
         return result;
     }
+
+    /**
+     * convert RAF from {@link ServiceState.RilRadioTechnology} bitmask to
+     * {@link TelephonyManager.NetworkTypeBitMask}, the bitmask represented by
+     * {@link TelephonyManager.NetworkType}. Reasons are {@link TelephonyManager.NetworkType} are
+     * public while {@link ServiceState.RilRadioTechnology} are hidden. We
+     * don't want to expose two sets of definition to public.
+     *
+     * @param raf bitmask represented by {@link ServiceState.RilRadioTechnology}
+     * @return {@link TelephonyManager.NetworkTypeBitMask}
+     */
+    public static int convertToNetworkTypeBitMask(int raf) {
+        int networkTypeRaf = 0;
+
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GSM)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GPRS)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_GPRS;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EDGE)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EDGE;
+        }
+        // convert both IS95A/IS95B to CDMA as network mode doesn't support CDMA
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IS95A)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IS95B)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPA)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSPA;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_UMTS)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_UMTS;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
+        }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
+        }
+
+        return (networkTypeRaf == 0) ? TelephonyManager.NETWORK_TYPE_UNKNOWN : networkTypeRaf;
+    }
 }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 7f87ef3..c407681 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -247,7 +247,7 @@
     private String mDataOperatorAlphaLong;
     private String mDataOperatorAlphaShort;
     private String mDataOperatorNumeric;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private boolean mIsManualNetworkSelection;
 
     private boolean mIsEmergencyOnly;
@@ -266,9 +266,9 @@
 
     @UnsupportedAppUsage
     private boolean mCssIndicator;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mNetworkId;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mSystemId;
     @UnsupportedAppUsage
     private int mCdmaRoamingIndicator;
@@ -466,7 +466,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public int getVoiceRegState() {
         return mVoiceRegState;
     }
@@ -481,7 +481,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public int getDataRegState() {
         return mDataRegState;
     }
@@ -542,7 +542,7 @@
      * @return roaming status
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public boolean getVoiceRoaming() {
         return getVoiceRoamingType() != ROAMING_TYPE_NOT_ROAMING;
     }
@@ -566,7 +566,7 @@
      * @return roaming type
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public boolean getDataRoaming() {
         return getDataRoamingType() != ROAMING_TYPE_NOT_ROAMING;
     }
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index fb801b2..bc832c3 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -21,8 +21,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.CarrierConfigManager;
-import android.text.TextUtils;
 import android.util.Log;
+import android.content.res.Resources;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -65,8 +65,9 @@
     };
 
     /**
-     * Use Integer.MAX_VALUE because -1 is a valid value in signal strength.
-     * @hide
+     * Indicates the invalid measures of signal strength.
+     *
+     * For example, this can be returned by {@link #getEvdoDbm()} or {@link #getCdmaDbm()}
      */
     public static final int INVALID = Integer.MAX_VALUE;
 
@@ -173,9 +174,9 @@
     public SignalStrength(boolean gsmFlag) {
         mGsmSignalStrength = 99;
         mGsmBitErrorRate = -1;
-        mCdmaDbm = -1;
+        mCdmaDbm = INVALID;
         mCdmaEcio = -1;
-        mEvdoDbm = -1;
+        mEvdoDbm = INVALID;
         mEvdoEcio = -1;
         mEvdoSnr = -1;
         mLteSignalStrength = 99;
@@ -542,6 +543,8 @@
 
     /**
      * Get the CDMA RSSI value in dBm
+     *
+     * @return the CDMA RSSI value or {@link #INVALID} if invalid
      */
     public int getCdmaDbm() {
         return this.mCdmaDbm;
@@ -556,6 +559,8 @@
 
     /**
      * Get the EVDO RSSI value in dBm
+     *
+     * @return the EVDO RSSI value or {@link #INVALID} if invalid
      */
     public int getEvdoDbm() {
         return this.mEvdoDbm;
@@ -1116,7 +1121,7 @@
     public int getWcdmaLevel() {
         int level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
 
-        if (TextUtils.isEmpty(mWcdmaDefaultSignalMeasurement)) {
+        if (mWcdmaDefaultSignalMeasurement == null) {
             Log.wtf(LOG_TAG, "getWcdmaLevel - WCDMA default signal measurement is invalid.");
             return level;
         }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 002d813..31770ec 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -43,6 +43,7 @@
 import android.net.INetworkPolicyManager;
 import android.net.NetworkCapabilities;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
@@ -50,6 +51,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.euicc.EuiccManager;
+import android.telephony.ims.ImsMmTelManager;
 import android.util.DisplayMetrics;
 import android.util.Log;
 
@@ -133,7 +135,7 @@
      * A content {@link Uri} used to receive updates on wfc enabled user setting.
      * <p>
      * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
-     * subscription wfc enabled {@link SubscriptionManager#WFC_IMS_ENABLED}
+     * subscription wfc enabled {@link ImsMmTelManager#isVoWiFiSettingEnabled()}
      * while your app is running. You can also use a {@link JobService} to ensure your app
      * is notified of changes to the {@link Uri} even when it is not running.
      * Note, however, that using a {@link JobService} does not guarantee timely delivery of
@@ -146,10 +148,28 @@
     public static final Uri WFC_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc");
 
     /**
-     * A content {@link Uri} used to receive updates on enhanced 4g user setting.
+     * A content {@link Uri} used to receive updates on advanced calling user setting.
      * <p>
      * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
-     * subscription enhanced 4G enabled {@link SubscriptionManager#ENHANCED_4G_MODE_ENABLED}
+     * subscription advanced calling enabled
+     * {@link ImsMmTelManager#isAdvancedCallingSettingEnabled()} while your app is running.
+     * You can also use a {@link JobService} to ensure your app is notified of changes to the
+     * {@link Uri} even when it is not running.
+     * Note, however, that using a {@link JobService} does not guarantee timely delivery of
+     * updates to the {@link Uri}.
+     * To be notified of changes to a specific subId, append subId to the URI
+     * {@link Uri#withAppendedPath(Uri, String)}.
+     * @hide
+     */
+    @SystemApi
+    public static final Uri ADVANCED_CALLING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
+            CONTENT_URI, "advanced_calling");
+
+    /**
+     * A content {@link Uri} used to receive updates on wfc mode setting.
+     * <p>
+     * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+     * subscription wfc mode {@link ImsMmTelManager#getVoWiFiModeSetting()}
      * while your app is running. You can also use a {@link JobService} to ensure your app
      * is notified of changes to the {@link Uri} even when it is not running.
      * Note, however, that using a {@link JobService} does not guarantee timely delivery of
@@ -159,9 +179,59 @@
      * @hide
      */
     @SystemApi
-    public static final Uri ENHANCED_4G_ENABLED_CONTENT_URI = Uri.withAppendedPath(
-            CONTENT_URI, "enhanced_4g");
+    public static final Uri WFC_MODE_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc_mode");
 
+    /**
+     * A content {@link Uri} used to receive updates on wfc roaming mode setting.
+     * <p>
+     * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+     * subscription wfc roaming mode {@link ImsMmTelManager#getVoWiFiRoamingModeSetting()}
+     * while your app is running. You can also use a {@link JobService} to ensure your app
+     * is notified of changes to the {@link Uri} even when it is not running.
+     * Note, however, that using a {@link JobService} does not guarantee timely delivery of
+     * updates to the {@link Uri}.
+     * To be notified of changes to a specific subId, append subId to the URI
+     * {@link Uri#withAppendedPath(Uri, String)}.
+     * @hide
+     */
+    @SystemApi
+    public static final Uri WFC_ROAMING_MODE_CONTENT_URI = Uri.withAppendedPath(
+            CONTENT_URI, "wfc_roaming_mode");
+
+    /**
+     * A content {@link Uri} used to receive updates on vt(video telephony over IMS) enabled
+     * setting.
+     * <p>
+     * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+     * subscription vt enabled {@link ImsMmTelManager#isVtSettingEnabled()}
+     * while your app is running. You can also use a {@link JobService} to ensure your app
+     * is notified of changes to the {@link Uri} even when it is not running.
+     * Note, however, that using a {@link JobService} does not guarantee timely delivery of
+     * updates to the {@link Uri}.
+     * To be notified of changes to a specific subId, append subId to the URI
+     * {@link Uri#withAppendedPath(Uri, String)}.
+     * @hide
+     */
+    @SystemApi
+    public static final Uri VT_ENABLED_CONTENT_URI = Uri.withAppendedPath(
+            CONTENT_URI, "vt_enabled");
+
+    /**
+     * A content {@link Uri} used to receive updates on wfc roaming enabled setting.
+     * <p>
+     * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+     * subscription wfc roaming enabled {@link ImsMmTelManager#isVoWiFiRoamingSettingEnabled()}
+     * while your app is running. You can also use a {@link JobService} to ensure your app
+     * is notified of changes to the {@link Uri} even when it is not running.
+     * Note, however, that using a {@link JobService} does not guarantee timely delivery of
+     * updates to the {@link Uri}.
+     * To be notified of changes to a specific subId, append subId to the URI
+     * {@link Uri#withAppendedPath(Uri, String)}.
+     * @hide
+     */
+    @SystemApi
+    public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
+            CONTENT_URI, "wfc_roaming_enabled");
 
     /**
      * TelephonyProvider unique key column name is the subscription id.
@@ -781,8 +851,13 @@
         IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
             @Override
             public void onSubscriptionsChanged() {
-                if (DBG) log("onOpportunisticSubscriptionsChanged callback received.");
-                mExecutor.execute(() -> onOpportunisticSubscriptionsChanged());
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    if (DBG) log("onOpportunisticSubscriptionsChanged callback received.");
+                    mExecutor.execute(() -> onOpportunisticSubscriptionsChanged());
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
             }
         };
 
@@ -1369,7 +1444,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public static int getPhoneId(int subId) {
         if (!isValidSubscriptionId(subId)) {
             if (DBG) {
@@ -1665,7 +1740,7 @@
      * usable subId means its neither a INVALID_SUBSCRIPTION_ID nor a DEFAULT_SUB_ID.
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public static boolean isUsableSubIdValue(int subId) {
         return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE;
     }
@@ -1683,7 +1758,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) {
         int[] subIds = SubscriptionManager.getSubId(phoneId);
         if (subIds != null && subIds.length > 0) {
@@ -2229,22 +2304,27 @@
     }
 
     /**
-     * Get opportunistic data Profiles.
+     * Return opportunistic subscriptions that can be visible to the caller.
+     * Opportunistic subscriptions are for opportunistic networks, which are cellular
+     * networks with limited capabilities and coverage, for example, CBRS.
      *
-     *  Provide all available user downloaded profiles on phone which are used only for
-     *  opportunistic data.
-     *  @param slotIndex slot on which the profiles are queried from.
-     *  @return the list of opportunistic subscription info. If none exists, an empty list. 
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @return the list of opportunistic subscription info. If none exists, an empty list.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public @NonNull List<SubscriptionInfo> getOpportunisticSubscriptions(int slotIndex) {
+    public @NonNull List<SubscriptionInfo> getOpportunisticSubscriptions() {
         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         List<SubscriptionInfo> subInfoList = null;
 
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
             if (iSub != null) {
-                subInfoList = iSub.getOpportunisticSubscriptions(slotIndex, pkgForDebug);
+                subInfoList = iSub.getOpportunisticSubscriptions(pkgForDebug);
             }
         } catch (RemoteException ex) {
             // ignore it
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 69901d2..8414ed3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -20,7 +20,9 @@
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
+import android.Manifest;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
@@ -56,6 +58,8 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsRcsFeature;
@@ -85,6 +89,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -232,7 +237,8 @@
 
     /** @hide
     /* @deprecated - use getSystemService as described above */
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public static TelephonyManager getDefault() {
         return sInstance;
     }
@@ -321,7 +327,7 @@
     }
 
     /** {@hide} */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public static TelephonyManager from(Context context) {
         return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
     }
@@ -1232,38 +1238,6 @@
      */
     public static final String EXTRA_RECOVERY_ACTION = "recoveryAction";
 
-     /**
-     * Broadcast intent action indicating that the telephony provider DB got lost.
-     *
-     * <p>
-     * The {@link #EXTRA_IS_CORRUPTED} extra indicates whether the database is lost
-     * due to corruption or not
-     *
-     * <p class="note">
-     * Requires the MODIFY_PHONE_STATE permission.
-     *
-     * <p class="note">
-     * This is a protected intent that can only be sent by the system.
-     *
-     * @see #EXTRA_IS_CORRUPTED
-     *
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public static final String ACTION_MMSSMS_DATABASE_LOST =
-            "android.intent.action.MMSSMS_DATABASE_LOST";
-
-    /**
-     * A boolean extra used with {@link #ACTION_MMSSMS_DATABASE_LOST} to indicate
-     * whether the database is lost due to corruption or not.
-     *
-     * @see #ACTION_MMSSMS_DATABASE_LOST
-     *
-     * @hide
-     */
-    public static final String EXTRA_IS_CORRUPTED = "isCorrupted";
-
     //
     //
     // Device Info
@@ -1311,9 +1285,10 @@
      * Returns the unique device ID, for example, the IMEI for GSM and the MEID
      * or ESN for CDMA phones. Return null if device ID is not available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
-     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
-     * that owns a managed profile on the device; for more details see <a
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
      * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
      * access is deprecated and will be removed in a future release.
      *
@@ -1321,7 +1296,7 @@
      * MEID for CDMA.
      */
     @Deprecated
-    @SuppressAutoDoc // No support for device / profile owner.
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getDeviceId() {
         try {
@@ -1340,9 +1315,10 @@
      * Returns the unique device ID of a subscription, for example, the IMEI for
      * GSM and the MEID for CDMA phones. Return null if device ID is not available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
-     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
-     * that owns a managed profile on the device; for more details see <a
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
      * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
      * access is deprecated and will be removed in a future release.
      *
@@ -1352,7 +1328,7 @@
      * MEID for CDMA.
      */
     @Deprecated
-    @SuppressAutoDoc // No support for device / profile owner.
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getDeviceId(int slotIndex) {
         // FIXME this assumes phoneId == slotIndex
@@ -1372,13 +1348,14 @@
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
-     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
-     * that owns a managed profile on the device; for more details see <a
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
      * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
      * access is deprecated and will be removed in a future release.
      */
-    @SuppressAutoDoc // No support for device / profile owner.
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getImei() {
         return getImei(getSlotIndex());
@@ -1388,15 +1365,16 @@
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
-     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
-     * that owns a managed profile on the device; for more details see <a
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
      * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
      * access is deprecated and will be removed in a future release.
      *
      * @param slotIndex of which IMEI is returned
      */
-    @SuppressAutoDoc // No support for device / profile owner.
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getImei(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -1441,13 +1419,14 @@
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
-     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
-     * that owns a managed profile on the device; for more details see <a
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
      * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
      * access is deprecated and will be removed in a future release.
      */
-    @SuppressAutoDoc // No support for device / profile owner.
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getMeid() {
         return getMeid(getSlotIndex());
@@ -1456,15 +1435,16 @@
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
-     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
-     * that owns a managed profile on the device; for more details see <a
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
      * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
      * access is deprecated and will be removed in a future release.
      *
      * @param slotIndex of which MEID is returned
      */
-    @SuppressAutoDoc // No support for device / profile owner.
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getMeid(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -1949,7 +1929,7 @@
      * @param subId
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getNetworkOperatorName(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ALPHA, "");
@@ -1977,7 +1957,7 @@
      * @param subId
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getNetworkOperator(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getNetworkOperatorForPhone(phoneId);
@@ -2301,7 +2281,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public int getDataNetworkType(int subId) {
         try{
             ITelephony telephony = getITelephony();
@@ -2337,7 +2317,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public int getVoiceNetworkType(int subId) {
         try{
             ITelephony telephony = getITelephony();
@@ -2820,7 +2800,7 @@
      * @param subId for which SimOperator is returned
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getSimOperator(int subId) {
         return getSimOperatorNumeric(subId);
     }
@@ -2834,7 +2814,7 @@
      * @see #getSimState
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getSimOperatorNumeric() {
         int subId = mSubId;
         if (!SubscriptionManager.isUsableSubIdValue(subId)) {
@@ -2863,7 +2843,7 @@
      * @param subId for which SimOperator is returned
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getSimOperatorNumeric(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getSimOperatorNumericForPhone(phoneId);
@@ -2877,7 +2857,7 @@
      * @param phoneId for which SimOperator is returned
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getSimOperatorNumericForPhone(int phoneId) {
         return getTelephonyProperty(phoneId,
                 TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
@@ -2904,7 +2884,7 @@
      * @param subId for which SimOperatorName is returned
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getSimOperatorName(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getSimOperatorNameForPhone(phoneId);
@@ -2934,7 +2914,7 @@
      * @param subId for which SimCountryIso is returned
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getSimCountryIso(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getSimCountryIsoForPhone(phoneId);
@@ -2962,7 +2942,7 @@
      * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
      * access is deprecated and will be removed in a future release.
      */
-    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getSimSerialNumber() {
          return getSimSerialNumber(getSubId());
@@ -3124,7 +3104,7 @@
      * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
      * access is deprecated and will be removed in a future release.
      */
-    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getSubscriberId() {
         return getSubscriberId(getSubId());
@@ -3146,7 +3126,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getSubscriberId(int subId) {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
@@ -3531,7 +3511,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getMsisdn(int subId) {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
@@ -4464,7 +4444,7 @@
    /**
     * @hide
     */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private ITelephony getITelephony() {
         return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
     }
@@ -5168,6 +5148,9 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * TODO: remove this one. use {@link #rebootRadio()} for reset type 1 and
+     * {@link #resetRadioConfig()} for reset type 3
+     *
      * @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
      * @return true on success; false on any failure.
      *
@@ -5177,8 +5160,15 @@
     public boolean nvResetConfig(int resetType) {
         try {
             ITelephony telephony = getITelephony();
-            if (telephony != null)
-                return telephony.nvResetConfig(resetType);
+            if (telephony != null) {
+                if (resetType == 1 /*1: reload NV reset */) {
+                    return telephony.rebootModem(getSlotIndex());
+                } else if (resetType == 3 /*3: factory NV reset */) {
+                    return telephony.resetModemConfig(getSlotIndex());
+                } else {
+                    Rlog.e(TAG, "nvResetConfig unsupported reset type");
+                }
+            }
         } catch (RemoteException ex) {
             Rlog.e(TAG, "nvResetConfig RemoteException", ex);
         } catch (NullPointerException ex) {
@@ -5188,6 +5178,61 @@
     }
 
     /**
+     * Rollback modem configurations to factory default except some config which are in whitelist.
+     * Used for device configuration by some CDMA operators.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return {@code true} on success; {@code false} on any failure.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    public boolean resetRadioConfig() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.resetModemConfig(getSlotIndex());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "resetRadioConfig RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "resetRadioConfig NPE", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Generate a radio modem reset. Used for device configuration by some CDMA operators.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return {@code true} on success; {@code false} on any failure.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    public boolean rebootRadio() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.rebootModem(getSlotIndex());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "rebootRadio RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "rebootRadio NPE", ex);
+        }
+        return false;
+    }
+
+    /**
      * Return an appropriate subscription ID for any situation.
      *
      * If this object has been created with {@link #createForSubscriptionId}, then the provided
@@ -6255,7 +6300,7 @@
 
     /**
      * Check TETHER_DUN_REQUIRED and TETHER_DUN_APN settings, net.tethering.noprovisioning
-     * SystemProperty, and config_tether_apndata to decide whether DUN APN is required for
+     * SystemProperty to decide whether DUN APN is required for
      * tethering.
      *
      * @return 0: Not required. 1: required. 2: Not set.
@@ -6619,7 +6664,7 @@
     }
 
     /**
-     * @removed Use {@link android.telecom.TelecomManager#isInCall} instead
+     * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
      * @hide
      */
     @Deprecated
@@ -6629,11 +6674,12 @@
             android.Manifest.permission.READ_PHONE_STATE
     })
     public boolean isOffhook() {
-        return false;
+        TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
+        return tm.isInCall();
     }
 
     /**
-     * @removed Use {@link android.telecom.TelecomManager#isRinging} instead
+     * @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead
      * @hide
      */
     @Deprecated
@@ -6643,11 +6689,12 @@
             android.Manifest.permission.READ_PHONE_STATE
     })
     public boolean isRinging() {
-        return false;
+        TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
+        return tm.isRinging();
     }
 
     /**
-     * @removed Use {@link android.telecom.TelecomManager#isInCall} instead
+     * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
      * @hide
      */
     @Deprecated
@@ -6657,7 +6704,8 @@
             android.Manifest.permission.READ_PHONE_STATE
     })
     public boolean isIdle() {
-        return true;
+        TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
+        return !tm.isInCall();
     }
 
     /**
@@ -7371,6 +7419,27 @@
     }
 
     /**
+     * Determines whether the device currently supports RTT (Real-time text). Based both on carrier
+     * support for the feature and device firmware support.
+     *
+     * @return {@code true} if the device and carrier both support RTT, {@code false} otherwise.
+     * @hide
+     */
+    @TestApi
+    public boolean isRttSupported() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isRttSupported(mSubId);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isRttSupported", e);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Permission error calling ITelephony#isWorldPhone", e);
+        }
+        return false;
+    }
+    /**
      * Whether the phone supports hearing aid compatibility.
      *
      * @return {@code true} if the device supports hearing aid compatibility, and {@code false}
@@ -8044,7 +8113,7 @@
      * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public ServiceState getServiceStateForSubscriber(int subId) {
         try {
             ITelephony service = getITelephony();
@@ -8200,6 +8269,51 @@
     }
 
     /**
+     * Return a list of certs in hex string from loaded carrier privileges access rules.
+     *
+     * @return a list of certificate in hex string. return {@code null} if there is no certs
+     * or privilege rules are not loaded yet.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public List<String> getCertsFromCarrierPrivilegeAccessRules() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getCertsFromCarrierPrivilegeAccessRules(getSubId());
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return null;
+    }
+
+    /**
+     * Returns MNO carrier id of the current subscription’s MCCMNC.
+     * <p>MNO carrier id can be solely identified by subscription mccmnc. This is mainly used
+     * for MNO fallback when exact carrier id {@link #getSimCarrierId()}
+     * configurations are not found.
+     *
+     * @return MNO carrier id of the current subscription. Return the value same as carrier id
+     * {@link #getSimCarrierId()}, if MNO carrier id cannot be identified.
+     * @hide
+     */
+    public int getSimMNOCarrierId() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getSubscriptionMNOCarrierId(getSubId());
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return UNKNOWN_CARRIER_ID;
+    }
+
+    /**
      * Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}.
      * All uicc applications are uniquely identified by application ID. See ETSI 102.221 and 101.220
      * <p>Requires Permission:
@@ -8816,4 +8930,279 @@
 
         return isEnabled;
     }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = {"NETWORK_TYPE_BITMASK_"},
+            value = {NETWORK_TYPE_BITMASK_UNKNOWN,
+                    NETWORK_TYPE_BITMASK_GSM,
+                    NETWORK_TYPE_BITMASK_GPRS,
+                    NETWORK_TYPE_BITMASK_EDGE,
+                    NETWORK_TYPE_BITMASK_CDMA,
+                    NETWORK_TYPE_BITMASK_1xRTT,
+                    NETWORK_TYPE_BITMASK_EVDO_0,
+                    NETWORK_TYPE_BITMASK_EVDO_A,
+                    NETWORK_TYPE_BITMASK_EVDO_B,
+                    NETWORK_TYPE_BITMASK_EHRPD,
+                    NETWORK_TYPE_BITMASK_HSUPA,
+                    NETWORK_TYPE_BITMASK_HSDPA,
+                    NETWORK_TYPE_BITMASK_HSPA,
+                    NETWORK_TYPE_BITMASK_HSPAP,
+                    NETWORK_TYPE_BITMASK_UMTS,
+                    NETWORK_TYPE_BITMASK_TD_SCDMA,
+                    NETWORK_TYPE_BITMASK_LTE,
+                    NETWORK_TYPE_BITMASK_LTE_CA,
+            })
+    public @interface NetworkTypeBitMask {}
+
+    // 2G
+    /**
+     * network type bitmask unknown.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_UNKNOWN = (1 << NETWORK_TYPE_UNKNOWN);
+    /**
+     * network type bitmask indicating the support of radio tech GSM.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_GSM = (1 << NETWORK_TYPE_GSM);
+    /**
+     * network type bitmask indicating the support of radio tech GPRS.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_GPRS = (1 << NETWORK_TYPE_GPRS);
+    /**
+     * network type bitmask indicating the support of radio tech EDGE.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_EDGE = (1 << NETWORK_TYPE_EDGE);
+    /**
+     * network type bitmask indicating the support of radio tech CDMA(IS95A/IS95B).
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_CDMA = (1 << NETWORK_TYPE_CDMA);
+    /**
+     * network type bitmask indicating the support of radio tech 1xRTT.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_1xRTT = (1 << NETWORK_TYPE_1xRTT);
+    // 3G
+    /**
+     * network type bitmask indicating the support of radio tech EVDO 0.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_EVDO_0 = (1 << NETWORK_TYPE_EVDO_0);
+    /**
+     * network type bitmask indicating the support of radio tech EVDO A.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_EVDO_A = (1 << NETWORK_TYPE_EVDO_A);
+    /**
+     * network type bitmask indicating the support of radio tech EVDO B.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_EVDO_B = (1 << NETWORK_TYPE_EVDO_B);
+    /**
+     * network type bitmask indicating the support of radio tech EHRPD.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_EHRPD = (1 << NETWORK_TYPE_EHRPD);
+    /**
+     * network type bitmask indicating the support of radio tech HSUPA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_HSUPA = (1 << NETWORK_TYPE_HSUPA);
+    /**
+     * network type bitmask indicating the support of radio tech HSDPA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_HSDPA = (1 << NETWORK_TYPE_HSDPA);
+    /**
+     * network type bitmask indicating the support of radio tech HSPA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_HSPA = (1 << NETWORK_TYPE_HSPA);
+    /**
+     * network type bitmask indicating the support of radio tech HSPAP.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_HSPAP = (1 << NETWORK_TYPE_HSPAP);
+    /**
+     * network type bitmask indicating the support of radio tech UMTS.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_UMTS = (1 << NETWORK_TYPE_UMTS);
+    /**
+     * network type bitmask indicating the support of radio tech TD_SCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_TD_SCDMA = (1 << NETWORK_TYPE_TD_SCDMA);
+    // 4G
+    /**
+     * network type bitmask indicating the support of radio tech LTE.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_LTE = (1 << NETWORK_TYPE_LTE);
+    /**
+     * network type bitmask indicating the support of radio tech LTE CA (carrier aggregation).
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_LTE_CA = (1 << NETWORK_TYPE_LTE_CA);
+
+    /**
+     * @return Modem supported radio access family bitmask {@link NetworkTypeBitMask}
+     *
+     * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
+     * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @NetworkTypeBitMask int getSupportedRadioAccessFamily() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName());
+            } else {
+                // This can happen when the ITelephony interface is not up yet.
+                return NETWORK_TYPE_BITMASK_UNKNOWN;
+            }
+        } catch (RemoteException ex) {
+            // This shouldn't happen in the normal case
+            return NETWORK_TYPE_BITMASK_UNKNOWN;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return NETWORK_TYPE_BITMASK_UNKNOWN;
+        }
+    }
+
+    /**
+     * Get the emergency number list based on current locale, sim, default, modem and network.
+     *
+     * <p>The emergency number {@link EmergencyNumber} with higher display priority is located at
+     * the smaller index in the returned list.
+     *
+     * <p>The subscriptions which the returned list would be based on, are all the active
+     * subscriptions, no matter which subscription could be used to create TelephonyManager.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return Map including the key as the active subscription ID (Note: if there is no active
+     * subscription, the key is {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}) and the value
+     * as the list of {@link EmergencyNumber}; null if this information is not available.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @Nullable
+    public Map<Integer, List<EmergencyNumber>> getCurrentEmergencyNumberList() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return null;
+            }
+            return telephony.getCurrentEmergencyNumberList(mContext.getOpPackageName());
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getCurrentEmergencyNumberList RemoteException", ex);
+        }
+        return null;
+    }
+
+    /**
+     * Get the per-category emergency number list based on current locale, sim, default, modem
+     * and network.
+     *
+     * <p>The emergency number {@link EmergencyNumber} with higher display priority is located at
+     * the smaller index in the returned list.
+     *
+     * <p>The subscriptions which the returned list would be based on, are all the active
+     * subscriptions, no matter which subscription could be used to create TelephonyManager.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param categories the emergency service categories which are the bitwise-OR combination of
+     * the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+     * </ol>
+     * @return Map including the key as the active subscription ID (Note: if there is no active
+     * subscription, the key is {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}) and the value
+     * as the list of {@link EmergencyNumber}; null if this information is not available.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @Nullable
+    public Map<Integer, List<EmergencyNumber>> getCurrentEmergencyNumberList(
+            @EmergencyServiceCategories int categories) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return null;
+            }
+            Map<Integer, List<EmergencyNumber>> numberMap = telephony
+                    .getCurrentEmergencyNumberList(mContext.getOpPackageName());
+            if (numberMap != null) {
+                for (Integer subscriptionId : numberMap.keySet()) {
+                    List<EmergencyNumber> numberList = numberMap.get(subscriptionId);
+                    for (EmergencyNumber number : numberList) {
+                        if (!number.isInEmergencyServiceCategories(categories)) {
+                            numberList.remove(number);
+                        }
+                    }
+                }
+            }
+            return numberMap;
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getCurrentEmergencyNumberList with Categories RemoteException", ex);
+        }
+        return null;
+    }
+
+    /**
+     * Checks if the supplied number is an emergency number based on current locale, sim, default,
+     * modem and network.
+     *
+     * <p>The subscriptions which the identification would be based on, are all the active
+     * subscriptions, no matter which subscription could be used to create TelephonyManager.
+     *
+     * @param number - the number to look up
+     * @return {@code true} if the given number is an emergency number based on current locale,
+     * sim, modem and network; {@code false} otherwise.
+     */
+    public boolean isCurrentEmergencyNumber(@NonNull String number) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return false;
+            }
+            return telephony.isCurrentEmergencyNumber(number);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "isCurrentEmergencyNumber RemoteException", ex);
+        }
+        return false;
+    }
 }
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index aabefe3..2e9bffe 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1184,6 +1184,16 @@
     }
 
     /**
+     * @param apnType APN type
+     * @return APN type in string format
+     * @hide
+     */
+    public static String getApnTypeString(int apnType) {
+        String apnTypeString = APN_TYPE_INT_MAP.get(apnType);
+        return apnTypeString == null ? "Unknown" : apnTypeString;
+    }
+
+    /**
      * @param types comma delimited list of APN types.
      * @return bitmask of APN types.
      * @hide
diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
index e8e1f01..32ffdbc 100644
--- a/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
+++ b/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
@@ -22,5 +22,5 @@
  */
 oneway interface IQualifiedNetworksServiceCallback
 {
-    void onQualifiedNetworkTypesChanged(int apnType, in int[] qualifiedNetworkTypesList);
+    void onQualifiedNetworkTypesChanged(int apnTypes, in int[] qualifiedNetworkTypes);
 }
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index bb89f19..57d9cce 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -121,27 +121,28 @@
         /**
          * Update the qualified networks list. Network availability updater must invoke this method
          * whenever the qualified networks changes. If this method is never invoked for certain
-         * APN type, then frameworks will always use the default (i.e. cellular) data and network
+         * APN types, then frameworks will always use the default (i.e. cellular) data and network
          * service.
          *
-         * @param apnType APN type of the qualified networks
+         * @param apnTypes APN types of the qualified networks. This must be a bitmask combination
+         * of {@link ApnSetting.ApnType}.
          * @param qualifiedNetworkTypes List of network types which are qualified for data
          * connection setup for {@link @apnType} in the preferred order. Each element in the array
          * is a {@link AccessNetworkType}. An empty list or null indicates no networks are qualified
          * for data setup.
          */
-        public final void updateQualifiedNetworkTypes(@ApnType int apnType,
+        public final void updateQualifiedNetworkTypes(@ApnType int apnTypes,
                                                       int[] qualifiedNetworkTypes) {
-            mHandler.obtainMessage(QNS_UPDATE_QUALIFIED_NETWORKS, mSlotIndex, apnType,
+            mHandler.obtainMessage(QNS_UPDATE_QUALIFIED_NETWORKS, mSlotIndex, apnTypes,
                     qualifiedNetworkTypes).sendToTarget();
         }
 
-        private void onUpdateQualifiedNetworkTypes(@ApnType int apnType,
+        private void onUpdateQualifiedNetworkTypes(@ApnType int apnTypes,
                                                    int[] qualifiedNetworkTypes) {
-            mQualifiedNetworkTypesList.put(apnType, qualifiedNetworkTypes);
+            mQualifiedNetworkTypesList.put(apnTypes, qualifiedNetworkTypes);
             if (mCallback != null) {
                 try {
-                    mCallback.onQualifiedNetworkTypesChanged(apnType, qualifiedNetworkTypes);
+                    mCallback.onQualifiedNetworkTypesChanged(apnTypes, qualifiedNetworkTypes);
                 } catch (RemoteException e) {
                     loge("Failed to call onQualifiedNetworksChanged. " + e);
                 }
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 1141177..3b1ef3f 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -15,6 +15,7 @@
  */
 package android.telephony.euicc;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -50,7 +51,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import android.annotation.CallbackExecutor;
 import java.util.concurrent.Executor;
 
 /**
@@ -119,6 +119,9 @@
     /** Result code when the eUICC card with the given card Id is not found. */
     public static final int RESULT_EUICC_NOT_FOUND = -2;
 
+    /** Result code indicating the caller is not the active LPA. */
+    public static final int RESULT_CALLER_NOT_ALLOWED = -3;
+
     /**
      * Callback to receive the result of an eUICC card API.
      *
@@ -152,7 +155,7 @@
      * Requests all the profiles on eUicc.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback The callback to get the result code and all the profiles.
      */
     public void requestAllProfiles(String cardId, @CallbackExecutor Executor executor,
@@ -176,7 +179,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback The callback to get the result code and profile.
      */
     public void requestProfile(String cardId, String iccid, @CallbackExecutor Executor executor,
@@ -201,7 +204,7 @@
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
      * @param refresh Whether sending the REFRESH command to modem.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback The callback to get the result code.
      */
     public void disableProfile(String cardId, String iccid, boolean refresh,
@@ -227,7 +230,7 @@
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile to switch to.
      * @param refresh Whether sending the REFRESH command to modem.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
      */
     public void switchToProfile(String cardId, String iccid, boolean refresh,
@@ -252,7 +255,7 @@
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
      * @param nickname The nickname of the profile.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback The callback to get the result code.
      */
     public void setNickname(String cardId, String iccid, String nickname,
@@ -276,7 +279,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback The callback to get the result code.
      */
     public void deleteProfile(String cardId, String iccid, @CallbackExecutor Executor executor,
@@ -301,7 +304,7 @@
      * @param cardId The Id of the eUICC.
      * @param options Bits of the options of resetting which parts of the eUICC memory. See
      *     EuiccCard for details.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback The callback to get the result code.
      */
     public void resetMemory(String cardId, @ResetOption int options,
@@ -324,7 +327,7 @@
      * Requests the default SM-DP+ address from eUICC.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback The callback to get the result code and the default SM-DP+ address.
      */
     public void requestDefaultSmdpAddress(String cardId, @CallbackExecutor Executor executor,
@@ -347,7 +350,7 @@
      * Requests the SM-DS address from eUICC.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback The callback to get the result code and the SM-DS address.
      */
     public void requestSmdsAddress(String cardId, @CallbackExecutor Executor executor,
@@ -371,7 +374,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param defaultSmdpAddress The default SM-DP+ address to set.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback The callback to get the result code.
      */
     public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress,
@@ -395,7 +398,7 @@
      * Requests Rules Authorisation Table.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback the callback to get the result code and the rule authorisation table.
      */
     public void requestRulesAuthTable(String cardId, @CallbackExecutor Executor executor,
@@ -418,7 +421,7 @@
      * Requests the eUICC challenge for new profile downloading.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback the callback to get the result code and the challenge.
      */
     public void requestEuiccChallenge(String cardId, @CallbackExecutor Executor executor,
@@ -441,7 +444,7 @@
      * Requests the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback the callback to get the result code and the info1.
      */
     public void requestEuiccInfo1(String cardId, @CallbackExecutor Executor executor,
@@ -464,7 +467,7 @@
      * Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback the callback to get the result code and the info2.
      */
     public void requestEuiccInfo2(String cardId, @CallbackExecutor Executor executor,
@@ -497,7 +500,7 @@
      *     GSMA RSP v2.0+.
      * @param serverCertificate ASN.1 data in byte array indicating SM-DP+ Certificate returned by
      *     SM-DP+ server.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+.
      */
@@ -537,7 +540,7 @@
      *     SM-DP+ server.
      * @param smdpCertificate ASN.1 data in byte array indicating the SM-DP+ Certificate returned
      *     by SM-DP+ server.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+
      */
@@ -569,7 +572,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
      */
@@ -598,7 +601,7 @@
      * @param cardId The Id of the eUICC.
      * @param transactionId the transaction ID returned by SM-DP+ server.
      * @param reason the cancel reason.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback the callback to get the result code and an byte[] which represents a
      *     {@code CancelSessionResponse} defined in GSMA RSP v2.0+.
      */
@@ -627,7 +630,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback the callback to get the result code and the list of notifications.
      */
     public void listNotifications(String cardId, @EuiccNotification.Event int events,
@@ -651,7 +654,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback the callback to get the result code and the list of notifications.
      */
     public void retrieveNotificationList(String cardId, @EuiccNotification.Event int events,
@@ -675,7 +678,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param seqNumber the sequence number of the notification.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback the callback to get the result code and the notification.
      */
     public void retrieveNotification(String cardId, int seqNumber,
@@ -699,7 +702,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param seqNumber the sequence number of the notification.
-     * @param executor The executor through which the callback should be invode.
+     * @param executor The executor through which the callback should be invoke.
      * @param callback the callback to get the result code.
      */
     public void removeNotificationFromList(String cardId, int seqNumber,
diff --git a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
index 34b8884..70daa8b 100644
--- a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
+++ b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
@@ -16,11 +16,16 @@
 
 package android.telephony.ims;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Provides the call forward information for the supplementary service configuration.
  *
@@ -28,39 +33,120 @@
  */
 @SystemApi
 public final class ImsCallForwardInfo implements Parcelable {
-    // Refer to ImsUtInterface#CDIV_CF_XXX
-    /** @hide */
-    // TODO: Make private, do not modify this field directly, use getter.
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for unconditional call
+     * forwarding. See TC 27.007
+     */
+    public static final int CDIV_CF_REASON_UNCONDITIONAL = 0;
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for call forwarding
+     * when the user is busy.
+     */
+    public static final int CDIV_CF_REASON_BUSY = 1;
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for call forwarding
+     * when there is no reply from the user.
+     */
+    public static final int CDIV_CF_REASON_NO_REPLY = 2;
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for call forwarding
+     * when the user is not reachable.
+     */
+    public static final int CDIV_CF_REASON_NOT_REACHABLE = 3;
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for setting all call
+     * forwarding reasons simultaneously (i.e. unconditional, busy, no reply, and not reachable).
+     */
+    public static final int CDIV_CF_REASON_ALL = 4;
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for setting all
+     * conditional call forwarding reasons simultaneously (i.e. if busy, if no reply, and if not
+     * reachable).
+     */
+    public static final int CDIV_CF_REASON_ALL_CONDITIONAL = 5;
+
+    /**
+     * CDIV (Communication Diversion) IMS only call forwarding reason for call forwarding when the
+     * user is not logged in.
+     */
+    public static final int CDIV_CF_REASON_NOT_LOGGED_IN = 6;
+
+    /**@hide*/
+    @IntDef(prefix = {"CDIV_CF_REASON_"}, value = {
+            CDIV_CF_REASON_UNCONDITIONAL,
+            CDIV_CF_REASON_BUSY,
+            CDIV_CF_REASON_NO_REPLY,
+            CDIV_CF_REASON_NOT_REACHABLE,
+            CDIV_CF_REASON_ALL,
+            CDIV_CF_REASON_ALL_CONDITIONAL,
+            CDIV_CF_REASON_NOT_LOGGED_IN})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallForwardReasons{}
+
+    /**
+     * Call forwarding is not active for any service class.
+     */
+    public static final int STATUS_NOT_ACTIVE = 0;
+
+    /**
+     * Call forwarding is active for one or more service classes.
+     */
+    public static final int STATUS_ACTIVE = 1;
+
+    /**@hide*/
+    @IntDef(prefix = {"STATUS_"}, value = {
+            STATUS_NOT_ACTIVE,
+            STATUS_ACTIVE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallForwardStatus{}
+
+    /**
+     * The address defined in {@link #getNumber()} is in an unknown format.
+     *
+     * See TS 27.007, section 7.11 for more information.
+     */
+    public static final int TYPE_OF_ADDRESS_UNKNOWN = 0x81;
+    /**
+     * The address defined in {@link #getNumber()} is in E.164 international format, which includes
+     * international access code "+".
+     *
+     * See TS 27.007, section 7.11 for more information.
+     */
+    public static final int TYPE_OF_ADDRESS_INTERNATIONAL = 0x91;
+
+    /**@hide*/
+    @IntDef(prefix = {"TYPE_OF_ADDRESS_"}, value = {
+            TYPE_OF_ADDRESS_INTERNATIONAL,
+            TYPE_OF_ADDRESS_UNKNOWN})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TypeOfAddress{}
+
+    /**@hide*/
     @UnsupportedAppUsage
-    public int mCondition;
-    // 0: disabled, 1: enabled
+    public @CallForwardReasons int mCondition;
     /** @hide */
-    // TODO: Make private, do not modify this field directly, use getter.
     @UnsupportedAppUsage
-    public int mStatus;
-    // 0x91: International, 0x81: Unknown
+    public @CallForwardStatus int mStatus;
     /** @hide */
-    // TODO: Make private, do not modify this field directly, use getter.
     @UnsupportedAppUsage
-    public int mToA;
-    // Service class
+    public @TypeOfAddress int mToA;
     /** @hide */
-    // TODO: Make private, do not modify this field directly, use getter.
     @UnsupportedAppUsage
-    public int mServiceClass;
-    // Number (it will not include the "sip" or "tel" URI scheme)
+    public @ImsSsData.ServiceClassFlags int mServiceClass;
     /** @hide */
-    // TODO: Make private, do not modify this field directly, use getter.
     @UnsupportedAppUsage
     public String mNumber;
-    // No reply timer for CF
     /** @hide */
-    // TODO: Make private, do not modify this field directly, use getter.
     @UnsupportedAppUsage
     public int mTimeSeconds;
 
     /** @hide */
-    // TODO: Will be removed in the future, use public constructor instead.
     @UnsupportedAppUsage
     public ImsCallForwardInfo() {
     }
@@ -68,9 +154,10 @@
     /**
      * IMS Call Forward Information.
      */
-    public ImsCallForwardInfo(int condition, int status, int toA, int serviceClass, String number,
-            int replyTimerSec) {
-        mCondition = condition;
+    public ImsCallForwardInfo(@CallForwardReasons int reason, @CallForwardStatus int status,
+            @TypeOfAddress int toA, @ImsSsData.ServiceClassFlags int serviceClass,
+            @NonNull String number, int replyTimerSec) {
+        mCondition = reason;
         mStatus = status;
         mToA = toA;
         mServiceClass = serviceClass;
@@ -130,26 +217,47 @@
         }
     };
 
-    public int getCondition() {
+    /**
+     * @return the condition of call forwarding for the service classes specified.
+     */
+    public @CallForwardReasons int getCondition() {
         return mCondition;
     }
 
-    public int getStatus() {
+    /**
+     * @return The call forwarding status.
+     */
+    public @CallForwardStatus int getStatus() {
         return mStatus;
     }
 
-    public int getToA() {
+    /**
+     * @return the type of address (ToA) for the number.
+     * @see #getNumber()
+     */
+    public @TypeOfAddress int getToA() {
         return mToA;
     }
 
-    public int getServiceClass() {
+    /**
+     * @return a bitfield containing the service classes that are enabled for call forwarding.
+     */
+    public @ImsSsData.ServiceClassFlags int getServiceClass() {
         return mServiceClass;
     }
 
+    /**
+     * @return the call forwarding number associated with call forwarding, with a number type
+     * defined by {@link #getToA()}.
+     */
     public String getNumber() {
         return mNumber;
     }
 
+    /**
+     * @return the number in seconds to wait before the call is forwarded for call forwarding
+     * condition {@link #CDIV_CF_REASON_NO_REPLY}
+     */
     public int getTimeSeconds() {
         return mTimeSeconds;
     }
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 89ef339..f73036e 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -16,17 +16,20 @@
 
 package android.telephony.ims;
 
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.PersistableBundle;
 import android.telecom.VideoProfile;
 import android.util.Log;
 
 import com.android.internal.telephony.PhoneConstants;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Parcelable object to handle IMS call profile.
  * It is created from GSMA IR.92/IR.94, 3GPP TS 24.229/TS 26.114/TS26.111.
@@ -206,17 +209,36 @@
     public static final int DIALSTRING_USSD = 2;
 
     /**
-     * Values for causes that restrict call types
+     * Call is not restricted on peer side and High Definition media is supported
      */
-    // Default cause not restricted at peer and HD is supported
     public static final int CALL_RESTRICT_CAUSE_NONE = 0;
-    // Service not supported by RAT at peer
+
+    /**
+     * High Definition media is not supported on the peer side due to the Radio Access Technology
+     * (RAT) it is are connected to.
+     */
     public static final int CALL_RESTRICT_CAUSE_RAT = 1;
-    // Service Disabled at peer
+
+    /**
+     * The service has been disabled on the peer side.
+     */
     public static final int CALL_RESTRICT_CAUSE_DISABLED = 2;
-    // HD is not supported
+
+    /**
+     * High definition media is not currently supported.
+     */
     public static final int CALL_RESTRICT_CAUSE_HD = 3;
 
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CALL_RESTRICT_CAUSE_", value = {
+            CALL_RESTRICT_CAUSE_NONE,
+            CALL_RESTRICT_CAUSE_RAT,
+            CALL_RESTRICT_CAUSE_DISABLED,
+            CALL_RESTRICT_CAUSE_HD
+    })
+    public @interface CallRestrictCause {}
+
     /**
      * String extra properties
      *  oi : Originating identity (number), MT only
@@ -270,7 +292,7 @@
     public int mCallType;
     /** @hide */
     @UnsupportedAppUsage
-    public int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
+    public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
 
     /**
      * Extras associated with this {@link ImsCallProfile}.
@@ -285,7 +307,7 @@
      *     <li>{@code long[]}</li>
      *     <li>{@code double[]}</li>
      *     <li>{@code String[]}</li>
-     *     <li>{@link PersistableBundle}</li>
+     *     <li>{@link android.os.PersistableBundle}</li>
      *     <li>{@link Boolean} (and boolean)</li>
      *     <li>{@code boolean[]}</li>
      *     <li>Other {@link Parcelable} classes in the {@code android.*} namespace.</li>
@@ -426,6 +448,14 @@
         }
     }
 
+    /**
+     * Set the call restrict cause, which provides the reason why a call has been restricted from
+     * using High Definition media.
+     */
+    public void setCallRestrictCause(@CallRestrictCause int cause) {
+        mRestrictCause = cause;
+    }
+
     public void updateCallType(ImsCallProfile profile) {
         mCallType = profile.mCallType;
     }
@@ -494,7 +524,11 @@
         return mCallType;
     }
 
-    public int getRestrictCause() {
+    /**
+     * @return The call restrict cause, which provides the reason why a call has been restricted
+     * from using High Definition media.
+     */
+    public @CallRestrictCause int getRestrictCause() {
         return mRestrictCause;
     }
 
diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
index b68055e..db5ba47 100644
--- a/telephony/java/android/telephony/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -16,9 +16,11 @@
 package android.telephony.ims;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.Rlog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -31,6 +33,8 @@
 @SystemApi
 public final class ImsSsData implements Parcelable {
 
+    private static final String TAG = ImsSsData.class.getCanonicalName();
+
     // Supplementary Service Type
     // Call Forwarding
     public static final int SS_CFU = 0;
@@ -76,30 +80,85 @@
     public static final int SS_SMS_SERVICES = 4;
     public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5;
 
-    // Service Class of Supplementary Service
-    // See 27.007 +CCFC or +CLCK
-    /** @hide */
-    public static final int SERVICE_CLASS_NONE = 0; // no user input
-    /** @hide */
-    public static final int SERVICE_CLASS_VOICE = 1;
-    /** @hide */
-    public static final int SERVICE_CLASS_DATA = (1 << 1);
-    /** @hide */
-    public static final int SERVICE_CLASS_FAX = (1 << 2);
-    /** @hide */
-    public static final int SERVICE_CLASS_SMS = (1 << 3);
-    /** @hide */
-    public static final int SERVICE_CLASS_DATA_SYNC = (1 << 4);
-    /** @hide */
-    public static final int SERVICE_CLASS_DATA_ASYNC = (1 << 5);
-    /** @hide */
-    public static final int SERVICE_CLASS_PACKET = (1 << 6);
-    /** @hide */
-    public static final int SERVICE_CLASS_PAD = (1 << 7);
+    /**
+     * No call forwarding service class defined.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_NONE = 0;
 
     /**
-     * Result code used if the operation was successful. See {@link #result}.
-     * @hide
+     * Service class flag for voice telephony.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_VOICE = 1;
+
+    /**
+     * Service class flag for all data bearers (including
+     * {@link #SERVICE_CLASS_DATA_CIRCUIT_SYNC,
+     * {@link #SERVICE_CLASS_DATA_CIRCUIT_ASYNC}, {@link #SERVICE_CLASS_PACKET_ACCESS},
+     * {@link #SERVICE_CLASS_PAD}}) if supported by the carrier.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_DATA = (1 << 1);
+    /**
+     * Service class flag for fax services.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_FAX = (1 << 2);
+    /**
+     * Service class flag for SMS services.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_SMS = (1 << 3);
+    /**
+     * Service class flag for the synchronous bearer service.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_DATA_CIRCUIT_SYNC = (1 << 4);
+
+    /**
+     * Service class flag for the asynchronous bearer service.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_DATA_CIRCUIT_ASYNC = (1 << 5);
+
+    /**
+     * Service class flag for the packet access bearer service.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_DATA_PACKET_ACCESS = (1 << 6);
+
+    /**
+     * Service class flag for the Packet Assembly/Disassembly bearer service.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_DATA_PAD = (1 << 7);
+
+    /**@hide*/
+    @IntDef(flag = true, prefix = {"SERVICE_CLASS_"}, value = {
+            SERVICE_CLASS_NONE,
+            SERVICE_CLASS_VOICE,
+            SERVICE_CLASS_DATA,
+            SERVICE_CLASS_FAX,
+            SERVICE_CLASS_SMS,
+            SERVICE_CLASS_DATA_CIRCUIT_SYNC,
+            SERVICE_CLASS_DATA_CIRCUIT_ASYNC,
+            SERVICE_CLASS_DATA_PACKET_ACCESS,
+            SERVICE_CLASS_DATA_PAD})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ServiceClassFlags{}
+
+    /**
+     * Result code used if the operation was successful. See {@link #getResult()}.
      */
     public static final int RESULT_SUCCESS = 0;
 
@@ -139,97 +198,58 @@
             SERVICE_CLASS_DATA,
             SERVICE_CLASS_FAX,
             SERVICE_CLASS_SMS,
-            SERVICE_CLASS_DATA_SYNC,
-            SERVICE_CLASS_DATA_ASYNC,
-            SERVICE_CLASS_PACKET,
-            SERVICE_CLASS_PAD
+            SERVICE_CLASS_DATA_CIRCUIT_SYNC,
+            SERVICE_CLASS_DATA_CIRCUIT_ASYNC,
+            SERVICE_CLASS_DATA_PACKET_ACCESS,
+            SERVICE_CLASS_DATA_PAD
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceClass{}
 
     /**
-     * The Service type of this Supplementary service. Valid values include:
-     *     SS_CFU,
-     *     SS_CF_BUSY,
-     *     SS_CF_NO_REPLY,
-     *     SS_CF_NOT_REACHABLE,
-     *     SS_CF_ALL,
-     *     SS_CF_ALL_CONDITIONAL,
-     *     SS_CFUT,
-     *     SS_CLIP,
-     *     SS_CLIR,
-     *     SS_COLP,
-     *     SS_COLR,
-     *     SS_CNAP,
-     *     SS_WAIT,
-     *     SS_BAOC,
-     *     SS_BAOIC,
-     *     SS_BAOIC_EXC_HOME,
-     *     SS_BAIC,
-     *     SS_BAIC_ROAMING,
-     *     SS_ALL_BARRING,
-     *     SS_OUTGOING_BARRING,
-     *     SS_INCOMING_BARRING,
-     *     SS_INCOMING_BARRING_DN,
-     *     SS_INCOMING_BARRING_ANONYMOUS
-     *
+     * The Service type of this Supplementary service.
      * @hide
      */
-    // TODO: Make final, do not modify this field directly!
-    public int serviceType;
+    public final @ServiceType int serviceType;
 
     /**
-     * Supplementary Service request Type. Valid values are:
-     *     SS_ACTIVATION,
-     *     SS_DEACTIVATION,
-     *     SS_INTERROGATION,
-     *     SS_REGISTRATION,
-     *     SS_ERASURE
-     *
+     * Supplementary Service request Type:
+     *     {@link #SS_ACTIVATION),
+     *     {@link #SS_DEACTIVATION},
+     *     {@link #SS_INTERROGATION},
+     *     {@link #SS_REGISTRATION},
+     *     {@link #SS_ERASURE}
      * @hide
      */
-    // TODO: Make final, do not modify this field directly!
-    public int requestType;
+    public final int requestType;
 
     /**
      * Supplementary Service teleservice type:
-     *     SS_TELESERVICE_ALL_TELE_AND_BEARER,
-     *     SS_TELESERVICE_ALL_TELESEVICES,
-     *     SS_TELESERVICE_TELEPHONY,
-     *     SS_TELESERVICE_ALL_DATA,
-     *     SS_TELESERVICE_SMS,
-     *     SS_TELESERVICE_ALL_TELESERVICES_EXCEPT_SMS
+     *     {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
+     *     {@link #SS_ALL_TELESEVICES},
+     *     {@link #SS_TELEPHONY},
+     *     {@link #SS_ALL_DATA_TELESERVICES},
+     *     {@link #SS_SMS_SERVICES},
+     *     {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
      *
      * @hide
      */
-    // TODO: Make this param final! Do not try to modify this param directly.
-    public int teleserviceType;
+    public final int teleserviceType;
 
     /**
-     * Supplementary Service service class. Valid values are:
-     *     SERVICE_CLASS_NONE,
-     *     SERVICE_CLASS_VOICE,
-     *     SERVICE_CLASS_DATA,
-     *     SERVICE_CLASS_FAX,
-     *     SERVICE_CLASS_SMS,
-     *     SERVICE_CLASS_DATA_SYNC,
-     *     SERVICE_CLASS_DATA_ASYNC,
-     *     SERVICE_CLASS_PACKET,
-     *     SERVICE_CLASS_PAD
+     * Supplementary Service service class.
      *
      * @hide
      */
-    // TODO: Make this param final! Do not try to modify this param directly.
-    public int serviceClass;
+    public final @ServiceClass int serviceClass;
 
     /**
      * Result of Supplementary Service operation. Valid values are:
-     *     RESULT_SUCCESS if the result is success, or
+     *     {@link #RESULT_SUCCESS} if the result is success, or
      *     ImsReasonInfo code if the result is a failure.
      *
      * @hide
      */
-    // TODO: Make this param final! Do not try to modify this param directly.
     public final int result;
 
     private int[] mSsInfo;
@@ -237,44 +257,81 @@
     private ImsSsInfo[] mImsSsInfo;
 
     /**
+     * Builder for optional ImsSsData parameters.
+     */
+    public static class Builder {
+        private ImsSsData mImsSsData;
+
+        /**
+         * Generate IMS Supplementary Service information.
+         * @param serviceType The Supplementary Service type.
+         * @param requestType Supplementary Service request Type:
+         *     {@link #SS_ACTIVATION},
+         *     {@link #SS_DEACTIVATION},
+         *     {@link #SS_INTERROGATION},
+         *     {@link #SS_REGISTRATION},
+         *     {@link #SS_ERASURE}
+         * @param teleserviceType Supplementary Service teleservice type:
+         *     {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
+         *     {@link #SS_ALL_TELESEVICES},
+         *     {@link #SS_TELEPHONY},
+         *     {@link #SS_ALL_DATA_TELESERVICES},
+         *     {@link #SS_SMS_SERVICES},
+         *     {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
+         * @param serviceClass Supplementary Service service class. See See 27.007 +CCFC or +CLCK.
+         * @param result Result of Supplementary Service operation. Valid values are 0 if the result
+         *               is success, or {@link ImsReasonInfo} code if the result is a failure.
+         * @return this Builder instance for further constructing.
+         * @see #build()
+         */
+        public Builder(@ServiceType int serviceType, int requestType, int teleserviceType,
+                @ServiceClass int serviceClass, int result) {
+            mImsSsData = new ImsSsData(serviceType, requestType, teleserviceType, serviceClass,
+                    result);
+        }
+
+        /**
+         * Set the array of {@link ImsSsInfo}s that are associated with this supplementary service
+         * data.
+         */
+        public Builder setSuppServiceInfo(@NonNull ImsSsInfo[] imsSsInfos) {
+            mImsSsData.mImsSsInfo = imsSsInfos;
+            return this;
+        }
+
+        /**
+         * Set the array of {@link ImsCallForwardInfo}s that are associated with this supplementary
+         * service data.
+         */
+        public Builder setCallForwardingInfo(@NonNull ImsCallForwardInfo[] imsCallForwardInfos) {
+            mImsSsData.mCfInfo = imsCallForwardInfos;
+            return this;
+        }
+
+        /**
+         * @return an {@link ImsSsData} containing optional parameters.
+         */
+        public ImsSsData build() {
+            return mImsSsData;
+        }
+    }
+
+    /**
      * Generate IMS Supplementary Service information.
-     * @param serviceType The Supplementary Service type. Valid entries:
-     *     SS_CFU,
-     *     SS_CF_BUSY,
-     *     SS_CF_NO_REPLY,
-     *     SS_CF_NOT_REACHABLE,
-     *     SS_CF_ALL,
-     *     SS_CF_ALL_CONDITIONAL,
-     *     SS_CFUT,
-     *     SS_CLIP,
-     *     SS_CLIR,
-     *     SS_COLP,
-     *     SS_COLR,
-     *     SS_CNAP,
-     *     SS_WAIT,
-     *     SS_BAOC,
-     *     SS_BAOIC,
-     *     SS_BAOIC_EXC_HOME,
-     *     SS_BAIC,
-     *     SS_BAIC_ROAMING,
-     *     SS_ALL_BARRING,
-     *     SS_OUTGOING_BARRING,
-     *     SS_INCOMING_BARRING,
-     *     SS_INCOMING_BARRING_DN,
-     *     SS_INCOMING_BARRING_ANONYMOUS
+     * @param serviceType The Supplementary Service type.
      * @param requestType Supplementary Service request Type. Valid values are:
-     *     SS_ACTIVATION,
-     *     SS_DEACTIVATION,
-     *     SS_INTERROGATION,
-     *     SS_REGISTRATION,
-     *     SS_ERASURE
+     *     {@link #SS_ACTIVATION},
+     *     {@link #SS_DEACTIVATION},
+     *     {@link #SS_INTERROGATION},
+     *     {@link #SS_REGISTRATION},
+     *     {@link #SS_ERASURE}
      * @param teleserviceType Supplementary Service teleservice type:
-     *     SS_TELESERVICE_ALL_TELE_AND_BEARER,
-     *     SS_TELESERVICE_ALL_TELESEVICES,
-     *     SS_TELESERVICE_TELEPHONY,
-     *     SS_TELESERVICE_ALL_DATA,
-     *     SS_TELESERVICE_SMS,
-     *     SS_TELESERVICE_ALL_TELESERVICES_EXCEPT_SMS
+     *     {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
+     *     {@link #SS_ALL_TELESEVICES},
+     *     {@link #SS_TELEPHONY},
+     *     {@link #SS_ALL_DATA_TELESERVICES},
+     *     {@link #SS_SMS_SERVICES},
+     *     {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
      * @param serviceClass Supplementary Service service class. See See 27.007 +CCFC or +CLCK.
      * @param result Result of Supplementary Service operation. Valid values are 0 if the result is
      *               success, or ImsReasonInfo code if the result is a failure.
@@ -313,11 +370,11 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(serviceType);
-        out.writeInt(requestType);
-        out.writeInt(teleserviceType);
-        out.writeInt(serviceClass);
-        out.writeInt(result);
+        out.writeInt(getServiceType());
+        out.writeInt(getRequestType());
+        out.writeInt(getTeleserviceType());
+        out.writeInt(getServiceClass());
+        out.writeInt(getResult());
         out.writeIntArray(mSsInfo);
         out.writeParcelableArray(mCfInfo, 0);
         out.writeParcelableArray(mImsSsInfo, 0);
@@ -333,9 +390,9 @@
      * @hide
      */
     public boolean isTypeCF() {
-        return (serviceType == SS_CFU || serviceType == SS_CF_BUSY ||
-              serviceType == SS_CF_NO_REPLY || serviceType == SS_CF_NOT_REACHABLE ||
-              serviceType == SS_CF_ALL || serviceType == SS_CF_ALL_CONDITIONAL);
+        return (getServiceType() == SS_CFU || getServiceType() == SS_CF_BUSY
+                || getServiceType() == SS_CF_NO_REPLY || getServiceType() == SS_CF_NOT_REACHABLE
+                || getServiceType() == SS_CF_ALL || getServiceType() == SS_CF_ALL_CONDITIONAL);
     }
 
     public boolean isTypeCf() {
@@ -343,7 +400,7 @@
     }
 
     public boolean isTypeUnConditional() {
-        return (serviceType == SS_CFU || serviceType == SS_CF_ALL);
+        return (getServiceType() == SS_CFU || getServiceType() == SS_CF_ALL);
     }
 
     /**
@@ -351,7 +408,7 @@
      * @hide
      */
     public boolean isTypeCW() {
-        return (serviceType == SS_WAIT);
+        return (getServiceType() == SS_WAIT);
     }
 
     public boolean isTypeCw() {
@@ -359,35 +416,84 @@
     }
 
     public boolean isTypeClip() {
-        return (serviceType == SS_CLIP);
+        return (getServiceType() == SS_CLIP);
     }
 
     public boolean isTypeColr() {
-        return (serviceType == SS_COLR);
+        return (getServiceType() == SS_COLR);
     }
 
     public boolean isTypeColp() {
-        return (serviceType == SS_COLP);
+        return (getServiceType() == SS_COLP);
     }
 
     public boolean isTypeClir() {
-        return (serviceType == SS_CLIR);
+        return (getServiceType() == SS_CLIR);
     }
 
     public boolean isTypeIcb() {
-        return (serviceType == SS_INCOMING_BARRING_DN ||
-                serviceType == SS_INCOMING_BARRING_ANONYMOUS);
+        return (getServiceType() == SS_INCOMING_BARRING_DN
+                || getServiceType() == SS_INCOMING_BARRING_ANONYMOUS);
     }
 
     public boolean isTypeBarring() {
-        return (serviceType == SS_BAOC || serviceType == SS_BAOIC ||
-              serviceType == SS_BAOIC_EXC_HOME || serviceType == SS_BAIC ||
-              serviceType == SS_BAIC_ROAMING || serviceType == SS_ALL_BARRING ||
-              serviceType == SS_OUTGOING_BARRING || serviceType == SS_INCOMING_BARRING);
+        return (getServiceType() == SS_BAOC || getServiceType() == SS_BAOIC
+                || getServiceType() == SS_BAOIC_EXC_HOME || getServiceType() == SS_BAIC
+                || getServiceType() == SS_BAIC_ROAMING || getServiceType() == SS_ALL_BARRING
+                || getServiceType() == SS_OUTGOING_BARRING
+                || getServiceType() == SS_INCOMING_BARRING);
     }
 
     public boolean isTypeInterrogation() {
-        return (serviceType == SS_INTERROGATION);
+        return (getServiceType() == SS_INTERROGATION);
+    }
+
+    /**
+     * Supplementary Service request Type:
+     *     {@link #SS_ACTIVATION),
+     *     {@link #SS_DEACTIVATION},
+     *     {@link #SS_INTERROGATION},
+     *     {@link #SS_REGISTRATION},
+     *     {@link #SS_ERASURE}
+     */
+    public int getRequestType() {
+        return requestType;
+    }
+
+    /**
+     * The Service type of this Supplementary service.
+     */
+    public @ServiceType int getServiceType() {
+        return serviceType;
+    }
+
+    /**
+     * Supplementary Service teleservice type:
+     *     {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
+     *     {@link #SS_ALL_TELESEVICES},
+     *     {@link #SS_TELEPHONY},
+     *     {@link #SS_ALL_DATA_TELESERVICES},
+     *     {@link #SS_SMS_SERVICES},
+     *     {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
+     */
+    public int getTeleserviceType() {
+        return teleserviceType;
+    }
+
+    /**
+     * Supplementary Service service class.
+     */
+    public @ServiceClass int getServiceClass() {
+        return serviceClass;
+    }
+
+    /**
+     * Result of Supplementary Service operation. Valid values are:
+     *     {@link #RESULT_SUCCESS} if the result is success, or
+     *     {@link ImsReasonInfo} CODE_* code if the result is a failure.
+     */
+    public int getResult() {
+        return result;
     }
 
     /** @hide */
@@ -406,43 +512,68 @@
     }
 
     /**
-     * This field will be null for RequestType SS_INTERROGATION
-     * and ServiceType SS_CF_*, SS_INCOMING_BARRING_DN,
-     * SS_INCOMING_BARRING_ANONYMOUS.
+     * This is a compatibility function to transform the public API to a form that can be processed
+     * by telephony.
      *
      * @hide
      */
-    public int[] getSuppServiceInfo() {
-        return mSsInfo;
+    //TODO: Refactor Telephony to use well defined classes instead of an int[] to process SS.
+    public int[] getSuppServiceInfoCompat() {
+        if (mSsInfo != null) {
+            // Something has set the ssInfo using hidden APIs, so for compatibility just return that
+            // structure directly.
+            return mSsInfo;
+        }
+
+
+        int[] result = new int[2];
+        if (mImsSsInfo == null || mImsSsInfo.length == 0) {
+            Rlog.e(TAG, "getSuppServiceInfoCompat: Could not parse mImsSsInfo, returning empty "
+                    + "int[]");
+            return result;
+        }
+
+        // Convert ImsSsInfo into a form that telephony can read (as per 3GPP 27.007)
+        // CLIR (section 7.7)
+        if (isTypeClir()) {
+            // Assume there will only be one ImsSsInfo.
+            // contains {"n","m"} parameters
+            result[0] = mImsSsInfo[0].getClirOutgoingState();
+            result[1] = mImsSsInfo[0].getClirInterrogationStatus();
+            return result;
+        }
+        // COLR 7.31
+        if (isTypeColr()) {
+            result[0] = mImsSsInfo[0].getProvisionStatus();
+        }
+        // Facility Lock CLCK 7.4 (for call barring), CLIP 7.6, COLP 7.8, as well as any
+        // other result, just return the status for the "n" parameter and provisioning status for
+        // "m" as the default.
+        result[0] = mImsSsInfo[0].getStatus();
+        result[1] = mImsSsInfo[0].getProvisionStatus();
+        return result;
     }
 
     /**
-     * Valid only for ServiceTypes
-     *  - SS_INCOMING_BARRING_DN and
-     *  - ServiceType SS_INCOMING_BARRING_ANONYMOUS.
-     *  Will be null otherwise.
-     * @hide
+     * @return an array of {@link ImsSsInfo}s associated with this supplementary service data.
      */
-    public ImsSsInfo[] getImsSpecificSuppServiceInfo() {
+    public @NonNull ImsSsInfo[] getSuppServiceInfo() {
         return mImsSsInfo;
     }
 
     /**
-     * Valid only for supplementary services
-     * - ServiceType SS_CF_* and
-     * - RequestType SS_INTERROGATION.
-     * Will be null otherwise.
-     * @hide
+     * @return an array of {@link ImsCallForwardInfo}s associated with this supplementary service
+     * data.
      **/
     public ImsCallForwardInfo[] getCallForwardInfo() {
         return mCfInfo;
     }
 
     public String toString() {
-        return "[ImsSsData] " + "ServiceType: " + serviceType
-            + " RequestType: " + requestType
-            + " TeleserviceType: " + teleserviceType
-            + " ServiceClass: " + serviceClass
-            + " Result: " + result;
+        return "[ImsSsData] " + "ServiceType: " + getServiceType()
+            + " RequestType: " + getRequestType()
+            + " TeleserviceType: " + getTeleserviceType()
+            + " ServiceClass: " + getServiceClass()
+            + " Result: " + getResult();
     }
 }
diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
index 3a784c1..0af6e62 100644
--- a/telephony/java/android/telephony/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.java
@@ -17,6 +17,8 @@
 package android.telephony.ims;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
@@ -28,10 +30,24 @@
 /**
  * Provides the result to the update operation for the supplementary service configuration.
  *
+ * Also supports IMS specific Incoming Communication Barring (ICB) as well as Anonymous
+ * Communication Rejection (ACR), as per 3GPP 24.611.
+ *
+ * @see Builder
  * @hide
  */
 @SystemApi
 public final class ImsSsInfo implements Parcelable {
+
+    /**@hide*/
+    @IntDef(value = {
+            NOT_REGISTERED,
+            DISABLED,
+            ENABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ServiceStatus {}
+
     /**
      * For the status of service registration or activation/deactivation.
      */
@@ -40,57 +56,180 @@
     public static final int ENABLED = 1;
 
     /**
-     * Provision status of service
+     * Provision status of service.
+     * @hide
      */
-    /** @hide */
-    @IntDef({
+    @IntDef(value = {
             SERVICE_PROVISIONING_UNKNOWN,
             SERVICE_NOT_PROVISIONED,
             SERVICE_PROVISIONED
-    })
+    }, prefix = "SERVICE_")
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceProvisionStatus {}
+
     /**
      * Unknown provision status for the service.
      */
     public static final int SERVICE_PROVISIONING_UNKNOWN = (-1);
+
     /**
      * Service is not provisioned.
      */
     public static final int SERVICE_NOT_PROVISIONED = 0;
+
     /**
      * Service is provisioned.
      */
     public static final int SERVICE_PROVISIONED = 1;
 
+    @IntDef(value = {
+            CLIR_OUTGOING_DEFAULT,
+            CLIR_OUTGOING_INVOCATION,
+            CLIR_OUTGOING_SUPPRESSION
+    }, prefix = "CLIR_OUTGOING_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ClirOutgoingState {}
+
+    /**
+     * Calling line identification restriction (CLIR) is set to the default according to the
+     * subscription of the CLIR service.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_OUTGOING_DEFAULT = 0;
+    /**
+     * Activate Calling line identification restriction for outgoing calls.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_OUTGOING_INVOCATION = 1;
+    /**
+     * Deactivate Calling line identification restriction for outgoing calls.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_OUTGOING_SUPPRESSION = 2;
+
+    /**
+     * Calling line identification restriction is currently not provisioned.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_STATUS_NOT_PROVISIONED = 0;
+    /**
+     * Calling line identification restriction is currently provisioned in permanent mode.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_STATUS_PROVISIONED_PERMANENT = 1;
+    /**
+     * Calling line identification restriction is currently unknown, e.g. no network, etc.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_STATUS_UNKNOWN = 2;
+    /**
+     * Calling line identification restriction temporary mode, temporarily restricted.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_STATUS_TEMPORARILY_RESTRICTED = 3;
+    /**
+     * Calling line identification restriction temporary mode, temporarily allowed.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_STATUS_TEMPORARILY_ALLOWED = 4;
+
+    @IntDef(value = {
+            CLIR_STATUS_NOT_PROVISIONED,
+            CLIR_STATUS_PROVISIONED_PERMANENT,
+            CLIR_STATUS_UNKNOWN,
+            CLIR_STATUS_TEMPORARILY_RESTRICTED,
+            CLIR_STATUS_TEMPORARILY_ALLOWED
+    }, prefix = "CLIR_STATUS_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ClirInterrogationStatus {}
+
     // 0: disabled, 1: enabled
     /** @hide */
-    // TODO: Make private, do not modify this field directly, use getter!
     @UnsupportedAppUsage
     public int mStatus;
     /** @hide */
-    // TODO: Make private, do not modify this field directly, use getter!
     @UnsupportedAppUsage
     public String mIcbNum;
     /** @hide */
     public int mProvisionStatus = SERVICE_PROVISIONING_UNKNOWN;
+    private int mClirInterrogationStatus = CLIR_STATUS_UNKNOWN;
+    private int mClirOutgoingState = CLIR_OUTGOING_DEFAULT;
 
     /**@hide*/
-    // TODO: Remove! Do not use this constructor, instead use public version.
     @UnsupportedAppUsage
     public ImsSsInfo() {
     }
 
     /**
-     *
-     * @param status The status of the service registration of activation/deactiviation. Valid
-     *    entries include:
-     *    {@link #NOT_REGISTERED},
-     *    {@link #DISABLED},
-     *    {@link #ENABLED}
-     * @param icbNum The Incoming barring number.
+     * Builds {@link ImsSsInfo} instances, which may include optional parameters.
      */
-    public ImsSsInfo(int status, String icbNum) {
+    public static class Builder {
+
+        private final ImsSsInfo mImsSsInfo;
+
+        public Builder(@ServiceStatus int status) {
+            mImsSsInfo = new ImsSsInfo();
+            mImsSsInfo.mStatus = status;
+        }
+
+        /**
+         * Set the ICB number for IMS call barring.
+         * @param number The number in E.164 international format.
+         */
+        public Builder setIncomingCommunicationBarringNumber(@NonNull String number) {
+            mImsSsInfo.mIcbNum = number;
+            return this;
+        }
+
+        /**
+         * Set the provisioning status for a Supplementary Service interrogation response.
+         */
+        public Builder setProvisionStatus(@ServiceProvisionStatus int provisionStatus) {
+            mImsSsInfo.mProvisionStatus = provisionStatus;
+            return this;
+        }
+
+        /**
+         * Set the Calling Line Identification Restriction (CLIR) status for a supplementary service
+         * interrogation response.
+         */
+        public Builder setClirInterrogationStatus(@ClirInterrogationStatus int status) {
+            mImsSsInfo.mClirInterrogationStatus = status;
+            return this;
+        }
+
+        /**
+         * Set the Calling line identification Restriction (CLIR) state for outgoing calls.
+         */
+        public Builder setClirOutgoingState(@ClirOutgoingState int state) {
+            mImsSsInfo.mClirOutgoingState = state;
+            return this;
+        }
+
+        /**
+         * @return a built {@link ImsSsInfo} containing optional the parameters that were set.
+         */
+        public ImsSsInfo build() {
+            return mImsSsInfo;
+        }
+    }
+
+    /**
+     *
+     * @param status The status of the service registration of activation/deactiviation.
+     * @param icbNum The Incoming barring number.
+     * @deprecated use {@link ImsSsInfo.Builder} instead.
+     */
+    @Deprecated
+    public ImsSsInfo(@ServiceStatus int status, @Nullable String icbNum) {
         mStatus = status;
         mIcbNum = icbNum;
     }
@@ -148,27 +287,46 @@
     };
 
     /**
-     * @return Supplementary Service Configuration status. Valid Values are:
-     *     {@link #NOT_REGISTERED},
-     *     {@link #DISABLED},
-     *     {@link #ENABLED}
+     * @return Supplementary Service Configuration status.
      */
-    public int getStatus() {
+    public @ServiceStatus int getStatus() {
         return mStatus;
     }
 
+    /** @deprecated Use {@link #getIncomingCommunicationBarringNumber()} instead.*/
+    @Deprecated
     public String getIcbNum() {
         return mIcbNum;
     }
 
     /**
-     * @return Supplementary Service Provision status. Valid Values are:
-     *     {@link #SERVICE_PROVISIONING_UNKNOWN},
-     *     {@link #SERVICE_NOT_PROVISIONED},
-     *     {@link #SERVICE_PROVISIONED}
+     * @return The Incoming Communication Barring (ICB) number.
      */
-    @ServiceProvisionStatus
-    public int getProvisionStatus() {
+    public String getIncomingCommunicationBarringNumber() {
+        return mIcbNum;
+    }
+
+    /**
+     * @return Supplementary Service Provision status.
+     */
+    public @ServiceProvisionStatus int getProvisionStatus() {
         return mProvisionStatus;
     }
+
+    /**
+     * @return the Calling Line Identification Restriction State for outgoing calls with respect to
+     * this subscription. Will be {@link #CLIR_OUTGOING_DEFAULT} if not applicable to this SS info.
+     */
+    public @ClirOutgoingState int getClirOutgoingState() {
+        return mClirOutgoingState;
+    }
+
+    /**
+     * @return the calling line identification restriction provisioning status upon interrogation of
+     * the service for this subscription. Will be {@link #CLIR_STATUS_UNKNOWN} if not applicable to
+     * this SS info.
+     */
+    public @ClirInterrogationStatus int getClirInterrogationStatus() {
+        return mClirInterrogationStatus;
+    }
 }
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 9699594..898ca48 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -43,7 +43,6 @@
 import com.android.ims.internal.IImsEcbm;
 import com.android.ims.internal.IImsMultiEndpoint;
 import com.android.ims.internal.IImsUt;
-import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -233,7 +232,6 @@
          * @see #addCapabilities(int)
          * @see #removeCapabilities(int)
          */
-        @VisibleForTesting
         public MmTelCapabilities() {
             super();
         }
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java b/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java
index 403f758..6a13569 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java
@@ -43,18 +43,18 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppListener.onProgressUpdated(request, fileInfo, currentDownloadSize,
                             fullDownloadSize, currentDecodedSize, fullDecodedSize);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     public void stop() {
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
index 2916f81..ce32477 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -40,17 +40,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onError(errorCode, message);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -59,17 +59,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onFileServicesUpdated(services);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -78,17 +78,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onMiddlewareReady();
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     public void stop() {
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java b/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java
index ad6bb54..87163ff 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java
@@ -42,17 +42,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppListener.onStatusUpdated(request, fileInfo, status);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     public void stop() {
diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java
index 2910bb3..c7600b6 100644
--- a/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java
@@ -38,17 +38,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onError(errorCode, message);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -57,17 +57,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onGroupCallStateChanged(state, reason);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -76,17 +76,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /** Prevents this callback from calling the app */
diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java
index 4c9cf4d..0b7667e 100644
--- a/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java
@@ -39,17 +39,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onError(errorCode, message);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -58,17 +58,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onAvailableSaisUpdated(currentSais, availableSais);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -77,17 +77,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onServiceInterfaceAvailable(interfaceName, index);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -96,17 +96,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onMiddlewareReady();
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /** Prevents this callback from calling the app */
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
index e9f39ff..3a4ed08 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -39,17 +39,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onError(errorCode, message);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -58,17 +58,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onStreamStateUpdated(state, reason);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -77,17 +77,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onMediaDescriptionUpdated();
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -96,17 +96,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -115,17 +115,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onStreamMethodUpdated(methodType);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     public void stop() {
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
index d47f5ad..2eb280e 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
@@ -40,17 +40,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onError(errorCode, message);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -60,17 +60,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onStreamingServicesUpdated(services);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -79,17 +79,17 @@
             return;
         }
 
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
+        long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
                     mAppCallback.onMiddlewareReady();
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
-            }
-        });
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     public void stop() {
diff --git a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
index d9471ae..5cd67d9 100644
--- a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
+++ b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
@@ -25,6 +25,8 @@
 
     PersistableBundle getConfigForSubId(int subId, String callingPackage);
 
+    void overrideConfig(int subId, in PersistableBundle overrides);
+
     void notifyConfigChangedForSubId(int subId);
 
     void updateConfigForPhoneId(int phoneId, String simState);
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 85b4941..4bdec08 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -186,10 +186,10 @@
     /**
      * Get User downloaded Profiles.
      *
-     *  Provide all available user downloaded profile on the phone.
-     *  @param slotId on which phone the switch will operate on
+     * Return opportunistic subscriptions that can be visible to the caller.
+     * @return the list of opportunistic subscription info. If none exists, an empty list.
      */
-    List<SubscriptionInfo> getOpportunisticSubscriptions(int slotId, String callingPackage);
+    List<SubscriptionInfo> getOpportunisticSubscriptions(String callingPackage);
 
     int getSlotIndex(int subId);
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index dc23358..682141f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -49,6 +49,7 @@
 import com.android.internal.telephony.OperatorInfo;
 
 import java.util.List;
+import java.util.Map;
 
 import android.telephony.UiccSlotInfo;
 
@@ -638,15 +639,30 @@
     boolean nvWriteCdmaPrl(in byte[] preferredRoamingList);
 
     /**
-     * Perform the specified type of NV config reset. The radio will be taken offline
-     * and the device must be rebooted after the operation. Used for device
-     * configuration by some CDMA operators.
+     * Rollback modem configurations to factory default except some config which are in whitelist.
+     * Used for device configuration by some CDMA operators.
      *
-     * @param resetType the type of reset to perform (1 == factory reset; 2 == NV-only reset).
-     * @return true on success; false on any failure.
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param slotIndex - device slot.
+     * @return {@code true} on success; {@code false} on any failure.
      */
-    boolean nvResetConfig(int resetType);
+    boolean resetModemConfig(int slotIndex);
 
+    /**
+     * Generate a radio modem reset. Used for device configuration by some CDMA operators.
+     * Different than {@link #setRadioPower(boolean)}, modem reboot will power down sim card.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param slotIndex - device slot.
+     * @return {@code true} on success; {@code false} on any failure.
+     */
+    boolean rebootModem(int slotIndex);
     /*
      * Get the calculated preferred network type.
      * Used for device configuration by some CDMA operators.
@@ -667,7 +683,7 @@
 
     /**
      * Check TETHER_DUN_REQUIRED and TETHER_DUN_APN settings, net.tethering.noprovisioning
-     * SystemProperty, and config_tether_apndata to decide whether DUN APN is required for
+     * SystemProperty to decide whether DUN APN is required for
      * tethering.
      *
      * @return 0: Not required. 1: required. 2: Not set.
@@ -1038,6 +1054,8 @@
      */
     boolean isTtyModeSupported();
 
+    boolean isRttSupported(int subscriptionId);
+
     /**
      * Whether the phone supports hearing aid compatibility.
      *
@@ -1293,6 +1311,18 @@
     String getSubscriptionCarrierName(int subId);
 
     /**
+     * Returns MNO carrier id of the current subscription’s MCCMNC.
+     * <p>MNO carrier id can be solely identified by subscription mccmnc. This is mainly used
+     * for MNO fallback when exact carrier id {@link #getSimCarrierId()}
+     * configurations are not found.
+     *
+     * @return MNO carrier id of the current subscription. Return the value same as carrier id
+     * {@link #getSimCarrierId()}, if MNO carrier id cannot be identified.
+     * @hide
+     */
+    int getSubscriptionMNOCarrierId(int subId);
+
+    /**
      * Action set from carrier signalling broadcast receivers to enable/disable metered apns
      * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
      * @param subId the subscription ID that this action applies to.
@@ -1615,4 +1645,19 @@
      * return true if TTY over VoLTE is enabled for the subscription specified.
      */
     boolean isTtyOverVolteEnabled(int subId);
+
+    /**
+     * Return the emergency number list from all the active subscriptions.
+     */
+    Map getCurrentEmergencyNumberList(String callingPackage);
+
+    /**
+     * Identify if the number is emergency number, based on all the active subscriptions.
+     */
+    boolean isCurrentEmergencyNumber(String number);
+    
+    /**
+     * Return a list of certs in hex string from loaded carrier privileges access rules.
+     */
+    List<String> getCertsFromCarrierPrivilegeAccessRules(int subId);
 }
diff --git a/telephony/java/com/android/internal/telephony/NetworkScanResult.java b/telephony/java/com/android/internal/telephony/NetworkScanResult.java
index 95f39d7..d07d77c 100644
--- a/telephony/java/com/android/internal/telephony/NetworkScanResult.java
+++ b/telephony/java/com/android/internal/telephony/NetworkScanResult.java
@@ -19,6 +19,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.CellInfo;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -106,6 +107,17 @@
     }
 
     @Override
+    public String toString() {
+        return new StringBuilder()
+            .append("{")
+            .append("scanStatus=" + scanStatus)
+            .append(", scanError=" + scanError)
+            .append(", networkInfos=" + networkInfos)
+            .append("}")
+            .toString();
+    }
+
+    @Override
     public int hashCode () {
         return ((scanStatus * 31)
                 + (scanError * 23)
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index 5b8028b..9874f80 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -562,8 +562,7 @@
                         + AppOpsManager.modeToName(appOp) + ": "
                         + (updateIfNeeded ? " (fixing)" : " (no permission to fix)"));
                 if (updateIfNeeded) {
-                    setExclusiveAppop(applicationData.mPackageName, appOps, appOp,
-                            AppOpsManager.MODE_ALLOWED, applicationData.mUid);
+                    appOps.setUidMode(appOp, applicationData.mUid, AppOpsManager.MODE_ALLOWED);
                 } else {
                     return false;
                 }
@@ -732,14 +731,6 @@
     private static void setExclusiveAppops(String pkg, AppOpsManager appOpsManager, int uid,
             int mode) {
         for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) {
-            setExclusiveAppop(pkg, appOpsManager, appop, mode, uid);
-        }
-    }
-
-    private static void setExclusiveAppop(String pkg, AppOpsManager appOpsManager, int appop,
-            int mode, int uid) {
-        // IGNORED means user explicitly revoked permission in settings, so avoid overriding it.
-        if (appOpsManager.checkOpNoThrow(appop, uid, pkg) != AppOpsManager.MODE_IGNORED) {
             appOpsManager.setUidMode(appop, uid, mode);
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index eda8e77..553e3fb 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -188,6 +188,13 @@
         if (checkReadDeviceIdentifiers(context, pid, uid, callingPackage)) {
             return true;
         }
+        // Calling packages with carrier privileges will also have access to device identifiers, but
+        // this may be removed in a future release.
+        if (SubscriptionManager.isValidSubscriptionId(subId) && getCarrierPrivilegeStatus(
+                TELEPHONY_SUPPLIER, subId, uid)
+                == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+            return true;
+        }
         // else the calling package is not authorized to access the device identifiers; call
         // a central method to report the failure based on the target SDK and if the calling package
         // has the READ_PHONE_STATE permission or carrier privileges that were previously required
@@ -279,44 +286,51 @@
             int uid, String callingPackage, String message) {
         Log.wtf(LOG_TAG,
                 "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message);
-        // if the device identifier check is relaxed then revert to the READ_PHONE_STATE permission
-        // check that was previously required to access device identifiers.
-        boolean relaxDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
-                Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED, 0) == 0;
-        if (relaxDeviceIdentifierCheck) {
-            return checkReadPhoneState(context, subId, pid, uid, callingPackage, message);
-        } else {
+        // If the device identifier check is enabled then enforce the new access requirements for
+        // both 1P and 3P apps.
+        boolean enableDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED, 0) == 1;
+        // Check if the application is a 3P app; if so then a separate setting is required to relax
+        // the check to begin flagging problems with 3P apps early.
+        boolean relax3PDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED, 0) == 1;
+        boolean is3PApp = true;
+        ApplicationInfo callingPackageInfo = null;
+        try {
+            callingPackageInfo = context.getPackageManager().getApplicationInfo(callingPackage, 0);
+            if (callingPackageInfo.isSystemApp()) {
+                is3PApp = false;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // If the application info for the calling package could not be found then assume the
+            // calling app is a 3P app to detect any issues with the check
+        }
+        if (enableDeviceIdentifierCheck || (is3PApp && !relax3PDeviceIdentifierCheck)) {
             boolean targetQBehaviorDisabled = Settings.Global.getInt(context.getContentResolver(),
                     Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED, 0) == 0;
             if (callingPackage != null) {
-                try {
-                    // if the target SDK is pre-Q or the target Q behavior is disabled then check if
-                    // the calling package would have previously had access to device identifiers.
-                    ApplicationInfo callingPackageInfo =
-                            context.getPackageManager().getApplicationInfo(
-                                    callingPackage, 0);
-                    if (callingPackageInfo != null && (
-                            callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q
-                                    || targetQBehaviorDisabled)) {
-                        if (context.checkPermission(
-                                android.Manifest.permission.READ_PHONE_STATE,
-                                pid,
-                                uid) == PackageManager.PERMISSION_GRANTED) {
-                            return false;
-                        }
-                        if (SubscriptionManager.isValidSubscriptionId(subId)
-                                && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, uid)
-                                == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
-                            return false;
-                        }
+                // if the target SDK is pre-Q or the target Q behavior is disabled then check if
+                // the calling package would have previously had access to device identifiers.
+                if (callingPackageInfo != null && (
+                        callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q
+                                || targetQBehaviorDisabled)) {
+                    if (context.checkPermission(
+                            android.Manifest.permission.READ_PHONE_STATE,
+                            pid,
+                            uid) == PackageManager.PERMISSION_GRANTED) {
+                        return false;
                     }
-                } catch (PackageManager.NameNotFoundException e) {
-                    // If the application info for the calling package could not be found then
-                    // default to throwing the SecurityException.
+                    if (SubscriptionManager.isValidSubscriptionId(subId)
+                            && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, uid)
+                            == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                        return false;
+                    }
                 }
             }
             throw new SecurityException(message + ": The user " + uid
                     + " does not meet the requirements to access device identifiers.");
+        } else {
+            return checkReadPhoneState(context, subId, pid, uid, callingPackage, message);
         }
     }
 
@@ -456,6 +470,27 @@
     }
 
     /**
+     * Ensure the caller (or self, if not processing an IPC) has
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or carrier privileges.
+     *
+     * @throws SecurityException if the caller does not have the required permission/privileges
+     */
+    public static void enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+            Context context, int subId, String message) {
+        if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+                == PERMISSION_GRANTED) {
+            return;
+        }
+
+        if (DBG) {
+            Rlog.d(LOG_TAG, "No READ_PRIVILEDED_PHONE_STATE permission, " +
+                    "check carrier privilege next.");
+        }
+
+        enforceCallingOrSelfCarrierPrivilege(subId, message);
+    }
+
+    /**
      * Make sure the caller (or self, if not processing an IPC) has carrier privileges.
      *
      * @throws SecurityException if the caller does not have the required privileges
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
index 23311b0..4b93ca3 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
@@ -59,8 +59,6 @@
     @Mock
     private UsbDeviceManager mUsbDeviceManager;
     @Mock
-    private UsbDebuggingManager mUsbDebuggingManager;
-    @Mock
     private UsbAlsaManager mUsbAlsaManager;
     @Mock
     private UsbSettingsManager mUsbSettingsManager;
@@ -89,9 +87,8 @@
         Intent mBroadcastedIntent;
 
         MockUsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager,
-                UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager,
-                UsbSettingsManager settingsManager) {
-            super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager);
+                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
+            super(looper, context, deviceManager, alsaManager, settingsManager);
             mUseUsbNotification = false;
             mIsUsbTransferAllowed = true;
             mCurrentUsbFunctionsReceived = true;
@@ -144,8 +141,8 @@
         when(mSharedPreferences.edit()).thenReturn(mEditor);
 
         mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
-                InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager,
-                mUsbAlsaManager, mUsbSettingsManager);
+                InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbAlsaManager,
+                mUsbSettingsManager);
     }
 
     @SmallTest
@@ -190,8 +187,7 @@
         assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
         assertEquals(mMockProperties.get(UsbDeviceManager.UsbHandler
                 .USB_PERSISTENT_CONFIG_PROPERTY), UsbManager.USB_FUNCTION_ADB);
-        verify(mUsbDebuggingManager).setAdbEnabled(true);
-        assertTrue(mUsbHandler.mAdbEnabled);
+        assertTrue(mUsbHandler.isAdbEnabled());
 
         mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 1, 1));
 
@@ -208,16 +204,15 @@
         mMockProperties.put(UsbDeviceManager.UsbHandler.USB_PERSISTENT_CONFIG_PROPERTY,
                 UsbManager.USB_FUNCTION_ADB);
         mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
-                InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager,
-                mUsbAlsaManager, mUsbSettingsManager);
+                InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbAlsaManager,
+                mUsbSettingsManager);
 
         sendBootCompleteMessages(mUsbHandler);
         mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_ENABLE_ADB, 0));
         assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
-        assertFalse(mUsbHandler.mAdbEnabled);
+        assertFalse(mUsbHandler.isAdbEnabled());
         assertEquals(mMockProperties.get(UsbDeviceManager.UsbHandler
                 .USB_PERSISTENT_CONFIG_PROPERTY), "");
-        verify(mUsbDebuggingManager).setAdbEnabled(false);
     }
 
     @SmallTest
@@ -232,14 +227,13 @@
     public void bootCompletedAdbEnabled() {
         mMockProperties.put(UsbDeviceManager.UsbHandler.USB_PERSISTENT_CONFIG_PROPERTY, "adb");
         mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
-                InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager,
-                mUsbAlsaManager, mUsbSettingsManager);
+                InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbAlsaManager,
+                mUsbSettingsManager);
 
         sendBootCompleteMessages(mUsbHandler);
         assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
         assertEquals(mMockGlobalSettings.get(Settings.Global.ADB_ENABLED).intValue(), 1);
-        assertTrue(mUsbHandler.mAdbEnabled);
-        verify(mUsbDebuggingManager).setAdbEnabled(true);
+        assertTrue(mUsbHandler.isAdbEnabled());
     }
 
     @SmallTest
@@ -321,8 +315,8 @@
                 UsbDeviceManager.UNLOCKED_CONFIG_PREF, mUsbHandler.mCurrentUser), ""))
                 .thenReturn(UsbManager.USB_FUNCTION_MTP);
         mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
-                InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager,
-                mUsbAlsaManager, mUsbSettingsManager);
+                InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbAlsaManager,
+                mUsbSettingsManager);
         sendBootCompleteMessages(mUsbHandler);
         mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_SCREEN_LOCK, 1));
         mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_SCREEN_LOCK, 0));
@@ -335,4 +329,4 @@
         handler.handleMessage(handler.obtainMessage(MSG_BOOT_COMPLETED));
         handler.handleMessage(handler.obtainMessage(MSG_SYSTEM_READY));
     }
-}
\ No newline at end of file
+}
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 04266c5..b9222a8 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -17,8 +17,8 @@
 package android.net;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.support.test.filters.SmallTest;
@@ -252,6 +252,39 @@
         }
     }
 
+    @Test
+    public void testMatches() {
+        // match 4 bytes prefix
+        assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches(
+                MacAddress.fromString("aa:bb:cc:dd:00:00"),
+                MacAddress.fromString("ff:ff:ff:ff:00:00")));
+
+        // match bytes 0,1,2 and 5
+        assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches(
+                MacAddress.fromString("aa:bb:cc:00:00:11"),
+                MacAddress.fromString("ff:ff:ff:00:00:ff")));
+
+        // match 34 bit prefix
+        assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches(
+                MacAddress.fromString("aa:bb:cc:dd:c0:00"),
+                MacAddress.fromString("ff:ff:ff:ff:c0:00")));
+
+        // fail to match 36 bit prefix
+        assertFalse(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches(
+                MacAddress.fromString("aa:bb:cc:dd:40:00"),
+                MacAddress.fromString("ff:ff:ff:ff:f0:00")));
+
+        // match all 6 bytes
+        assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches(
+                MacAddress.fromString("aa:bb:cc:dd:ee:11"),
+                MacAddress.fromString("ff:ff:ff:ff:ff:ff")));
+
+        // match none of 6 bytes
+        assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches(
+                MacAddress.fromString("00:00:00:00:00:00"),
+                MacAddress.fromString("00:00:00:00:00:00")));
+    }
+
     static byte[] toByteArray(int... in) {
         byte[] out = new byte[in.length];
         for (int i = 0; i < in.length; i++) {
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 8f18d07..d6dbf5a 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -39,6 +39,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.net.NetworkStats.Entry;
 import android.os.Process;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.filters.SmallTest;
@@ -785,7 +786,38 @@
         ArrayMap<String, String> stackedIface = new ArrayMap<>();
         stackedIface.put(v4Iface, baseIface);
 
-        NetworkStats.Entry otherEntry = new NetworkStats.Entry(
+        // Ipv4 traffic sent/received by an app on stacked interface.
+        final NetworkStats.Entry appEntry = new NetworkStats.Entry(
+                v4Iface, appUid, SET_DEFAULT, TAG_NONE,
+                30501490  /* rxBytes */,
+                22401 /* rxPackets */,
+                876235 /* txBytes */,
+                13805 /* txPackets */,
+                0 /* operations */);
+
+        // Traffic measured for the root uid on the base interface if eBPF is in use.
+        // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
+        // overhead (20 bytes per packet), only for TX traffic.
+        final NetworkStats.Entry ebpfRootUidEntry = new NetworkStats.Entry(
+                baseIface, rootUid, SET_DEFAULT, TAG_NONE,
+                163577 /* rxBytes */,
+                187 /* rxPackets */,
+                1169942 /* txBytes */,
+                13902 /* txPackets */,
+                0 /* operations */);
+
+        // Traffic measured for the root uid on the base interface if xt_qtaguid is in use.
+        // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
+        // overhead (20 bytes per packet), in both directions.
+        final NetworkStats.Entry xtRootUidEntry = new NetworkStats.Entry(
+                baseIface, rootUid, SET_DEFAULT, TAG_NONE,
+                31113087 /* rxBytes */,
+                22588 /* rxPackets */,
+                1169942 /* txBytes */,
+                13902 /* txPackets */,
+                0 /* operations */);
+
+        final NetworkStats.Entry otherEntry = new NetworkStats.Entry(
                 otherIface, appUid, SET_DEFAULT, TAG_NONE,
                 2600  /* rxBytes */,
                 2 /* rxPackets */,
@@ -793,39 +825,41 @@
                 3 /* txPackets */,
                 0 /* operations */);
 
-        NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addValues(v4Iface, appUid, SET_DEFAULT, TAG_NONE,
-                        30501490  /* rxBytes */,
-                        22401 /* rxPackets */,
-                        876235 /* txBytes */,
-                        13805 /* txPackets */,
-                        0 /* operations */)
-                .addValues(baseIface, rootUid, SET_DEFAULT, TAG_NONE,
-                        31113087,
-                        22588,
-                        1169942,
-                        13902,
-                        0)
+        final NetworkStats statsXt = new NetworkStats(TEST_START, 3)
+                .addValues(appEntry)
+                .addValues(xtRootUidEntry)
                 .addValues(otherEntry);
 
-        stats.apply464xlatAdjustments(stackedIface);
+        final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3)
+                .addValues(appEntry)
+                .addValues(ebpfRootUidEntry)
+                .addValues(otherEntry);
 
-        assertEquals(3, stats.size());
-        assertValues(stats, 0, v4Iface, appUid, SET_DEFAULT, TAG_NONE,
-                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+        statsXt.apply464xlatAdjustments(stackedIface, false);
+        statsEbpf.apply464xlatAdjustments(stackedIface, true);
+
+        assertEquals(3, statsXt.size());
+        assertEquals(3, statsEbpf.size());
+        final NetworkStats.Entry expectedAppUid = new NetworkStats.Entry(
+                v4Iface, appUid, SET_DEFAULT, TAG_NONE,
                 30949510,
                 22401,
                 1152335,
                 13805,
                 0);
-        assertValues(stats, 1, baseIface, 0, SET_DEFAULT, TAG_NONE,
-                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+        final NetworkStats.Entry expectedRootUid = new NetworkStats.Entry(
+                baseIface, 0, SET_DEFAULT, TAG_NONE,
                 163577,
                 187,
                 17607,
                 97,
                 0);
-        assertEquals(otherEntry, stats.getValues(2, null));
+        assertEquals(expectedAppUid, statsXt.getValues(0, null));
+        assertEquals(expectedRootUid, statsXt.getValues(1, null));
+        assertEquals(otherEntry, statsXt.getValues(2, null));
+        assertEquals(expectedAppUid, statsEbpf.getValues(0, null));
+        assertEquals(expectedRootUid, statsEbpf.getValues(1, null));
+        assertEquals(otherEntry, statsEbpf.getValues(2, null));
     }
 
     @Test
@@ -850,7 +884,7 @@
                 .addValues(secondEntry);
 
         // Empty map: no adjustment
-        stats.apply464xlatAdjustments(new ArrayMap<>());
+        stats.apply464xlatAdjustments(new ArrayMap<>(), false);
 
         assertEquals(2, stats.size());
         assertEquals(firstEntry, stats.getValues(0, null));
diff --git a/tests/net/java/android/net/UidRangeTest.java b/tests/net/java/android/net/UidRangeTest.java
index 1d1013e..860d732 100644
--- a/tests/net/java/android/net/UidRangeTest.java
+++ b/tests/net/java/android/net/UidRangeTest.java
@@ -20,7 +20,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
-import android.os.Parcel;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.filters.SmallTest;
 
@@ -31,34 +30,10 @@
 @SmallTest
 public class UidRangeTest {
 
-    static {
-        System.loadLibrary("frameworksnettestsjni");
-    }
-
-    private static native byte[] readAndWriteNative(byte[] inParcel);
-    private static native int getStart(byte[] inParcel);
-    private static native int getStop(byte[] inParcel);
-
-    @Test
-    public void testNativeParcelUnparcel() {
-        UidRange original = new UidRange(1234, Integer.MAX_VALUE);
-
-        byte[] inParcel = marshall(original);
-        byte[] outParcel = readAndWriteNative(inParcel);
-        UidRange roundTrip = unmarshall(outParcel);
-
-        assertEquals(original, roundTrip);
-        assertArrayEquals(inParcel, outParcel);
-    }
-
-    @Test
-    public void testIndividualNativeFields() {
-        UidRange original = new UidRange(0x11115678, 0x22224321);
-        byte[] originalBytes = marshall(original);
-
-        assertEquals(original.start, getStart(originalBytes));
-        assertEquals(original.stop, getStop(originalBytes));
-    }
+  /*
+   * UidRange is no longer passed to netd. UID ranges between the framework and netd are passed as
+   * UidRangeParcel objects.
+   */
 
     @Test
     public void testSingleItemUidRangeAllowed() {
@@ -91,28 +66,4 @@
         } catch (IllegalArgumentException expected) {
         }
     }
-
-    /**
-     * Write a {@link UidRange} into an empty parcel and return the underlying data.
-     *
-     * @see unmarshall(byte[])
-     */
-    private static byte[] marshall(UidRange range) {
-        Parcel p = Parcel.obtain();
-        range.writeToParcel(p, /* flags */ 0);
-        p.setDataPosition(0);
-        return p.marshall();
-    }
-
-    /**
-     * Read raw bytes into a parcel, and read a {@link UidRange} back out of them.
-     *
-     * @see marshall(UidRange)
-     */
-    private static UidRange unmarshall(byte[] data) {
-        Parcel p = Parcel.obtain();
-        p.unmarshall(data, 0, data.length);
-        p.setDataPosition(0);
-        return UidRange.CREATOR.createFromParcel(p);
-    }
-}
+}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index dfe31bd..bf42412 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -31,6 +31,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
+import android.net.NetworkMisc;
 import android.os.Handler;
 import android.os.INetworkManagementService;
 import android.os.test.TestLooper;
@@ -55,6 +56,7 @@
     static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29");
 
     @Mock ConnectivityService mConnectivity;
+    @Mock NetworkMisc mMisc;
     @Mock INetworkManagementService mNms;
     @Mock InterfaceConfiguration mConfig;
     @Mock NetworkAgentInfo mNai;
@@ -78,6 +80,7 @@
         mNai.networkInfo = new NetworkInfo(null);
         mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
         when(mNai.connService()).thenReturn(mConnectivity);
+        when(mNai.netMisc()).thenReturn(mMisc);
         when(mNai.handler()).thenReturn(mHandler);
 
         when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig);
@@ -103,9 +106,16 @@
             mNai.networkInfo.setType(type);
             for (NetworkInfo.DetailedState state : supportedDetailedStates) {
                 mNai.networkInfo.setDetailedState(state, "reason", "extraInfo");
-                assertTrue(
-                        String.format("requiresClat expected for type=%d state=%s", type, state),
-                        Nat464Xlat.requiresClat(mNai));
+                String msg = String.format("requiresClat expected for type=%d state=%s",
+                        type, state);
+
+                mMisc.skip464xlat = true;
+                String errorMsg = msg + String.format(" skip464xlat=%b", mMisc.skip464xlat);
+                assertFalse(errorMsg, Nat464Xlat.requiresClat(mNai));
+
+                mMisc.skip464xlat = false;
+                errorMsg = msg + String.format(" skip464xlat=%b", mMisc.skip464xlat);
+                assertTrue(errorMsg, Nat464Xlat.requiresClat(mNai));
             }
         }
     }
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 4dc63f2..f12756a 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -25,6 +25,7 @@
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRODUCT;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.os.Process.SYSTEM_UID;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -55,26 +56,41 @@
     private static final String PARTITION_OEM = "oem";
     private static final String PARTITION_PRODUCT = "product";
     private static final String PARTITION_VENDOR = "vendor";
+    private static final int VERSION_P = Build.VERSION_CODES.P;
+    private static final int VERSION_Q = Build.VERSION_CODES.Q;
 
     @Mock private Context mContext;
     @Mock private PackageManager mPackageManager;
 
     private PermissionMonitor mPermissionMonitor;
+    private int mMockFirstSdkInt;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.getPackagesForUid(MOCK_UID)).thenReturn(MOCK_PACKAGE_NAMES);
-        mPermissionMonitor = new PermissionMonitor(mContext, null);
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(MOCK_PACKAGE_NAMES);
+        // Try to use spy() here for stubbing getDeviceFirstSdkInt value but the spies are loaded
+        // by a custom class loader that's different from the loader used for loading the real
+        // thing. That means those two classes are not in the same package, so a package private
+        // method is not accessible. Hence, using override method to control FIRST_SDK_INT value
+        // instead of spy function for testing.
+        mPermissionMonitor = new PermissionMonitor(mContext, null) {
+            @Override
+            int getDeviceFirstSdkInt() {
+                return mMockFirstSdkInt;
+            }
+        };
     }
 
-    private void expectPermission(String[] permissions, String partition,
-            int targetSdkVersion) throws Exception {
-        final PackageInfo packageInfo = packageInfoWithPermissions(permissions, partition);
+    private boolean hasBgPermission(String partition, int targetSdkVersion, int uid,
+            String... permission) throws Exception {
+        final PackageInfo packageInfo = packageInfoWithPermissions(permission, partition);
         packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion;
+        packageInfo.applicationInfo.uid = uid;
         when(mPackageManager.getPackageInfoAsUser(
                 eq(MOCK_PACKAGE_NAMES[0]), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+        return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid);
     }
 
     private PackageInfo packageInfoWithPermissions(String[] permissions, String partition) {
@@ -136,51 +152,44 @@
 
     @Test
     public void testHasUseBackgroundNetworksPermission() throws Exception {
-        expectPermission(new String[] { CHANGE_NETWORK_STATE },
-            PARTITION_SYSTEM, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { NETWORK_STACK }, PARTITION_SYSTEM, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CONNECTIVITY_INTERNAL },
-            PARTITION_SYSTEM, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CONNECTIVITY_USE_RESTRICTED_NETWORKS },
-            PARTITION_SYSTEM, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
 
-        expectPermission(new String[] { CHANGE_NETWORK_STATE },
-            PARTITION_VENDOR, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { NETWORK_STACK },
-            PARTITION_VENDOR, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CONNECTIVITY_INTERNAL },
-            PARTITION_VENDOR, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CONNECTIVITY_USE_RESTRICTED_NETWORKS },
-            PARTITION_VENDOR, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+    }
 
-        expectPermission(new String[] {}, PARTITION_SYSTEM, Build.VERSION_CODES.P);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CHANGE_WIFI_STATE },
-            PARTITION_SYSTEM, Build.VERSION_CODES.P);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] {}, PARTITION_VENDOR, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CHANGE_WIFI_STATE },
-            PARTITION_VENDOR, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+    @Test
+    public void testHasUseBackgroundNetworksPermissionSystemUid() throws Exception {
+        mMockFirstSdkInt = VERSION_P;
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CHANGE_WIFI_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
 
-        expectPermission(new String[] {}, PARTITION_SYSTEM, Build.VERSION_CODES.Q);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CHANGE_WIFI_STATE },
-            PARTITION_SYSTEM, Build.VERSION_CODES.Q);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] {}, PARTITION_VENDOR, Build.VERSION_CODES.Q);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CHANGE_WIFI_STATE },
-            PARTITION_VENDOR, Build.VERSION_CODES.Q);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        mMockFirstSdkInt = VERSION_Q;
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CHANGE_WIFI_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+    }
+
+    @Test
+    public void testHasUseBackgroundNetworksPermissionVendorApp() throws Exception {
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
     }
 }
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index e377a47..9bf7587 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -507,13 +507,15 @@
 
     private static void assertBlocked(Vpn vpn, int... uids) {
         for (int uid : uids) {
-            assertTrue("Uid " + uid + " should be blocked", vpn.isBlockingUid(uid));
+            final boolean blocked = vpn.getLockdown() && vpn.isBlockingUid(uid);
+            assertTrue("Uid " + uid + " should be blocked", blocked);
         }
     }
 
     private static void assertUnblocked(Vpn vpn, int... uids) {
         for (int uid : uids) {
-            assertFalse("Uid " + uid + " should not be blocked", vpn.isBlockingUid(uid));
+            final boolean blocked = vpn.getLockdown() && vpn.isBlockingUid(uid);
+            assertFalse("Uid " + uid + " should not be blocked", blocked);
         }
     }
 
diff --git a/tests/net/jni/UidRangeTest.cpp b/tests/net/jni/UidRangeTest.cpp
deleted file mode 100644
index 7941731..0000000
--- a/tests/net/jni/UidRangeTest.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#include <memory>
-
-#include <binder/Parcel.h>
-
-#include "UidRangeTest.h"
-
-using android::net::UidRange;
-
-extern "C"
-JNIEXPORT jbyteArray Java_android_net_UidRangeTest_readAndWriteNative(JNIEnv* env, jclass,
-        jbyteArray inParcel) {
-    const UidRange range = unmarshall(env, inParcel);
-    return marshall(env, range);
-}
-
-extern "C"
-JNIEXPORT jint Java_android_net_UidRangeTest_getStart(JNIEnv* env, jclass, jbyteArray inParcel) {
-    const UidRange range = unmarshall(env, inParcel);
-    return range.getStart();
-}
-
-extern "C"
-JNIEXPORT jint Java_android_net_UidRangeTest_getStop(JNIEnv* env, jclass, jbyteArray inParcel) {
-    const UidRange range = unmarshall(env, inParcel);
-    return range.getStop();
-}
-
-
-/**
- * Reads exactly one UidRange from 'parcelData' assuming that it is a Parcel. Any bytes afterward
- * are ignored.
- */
-UidRange unmarshall(JNIEnv* env, jbyteArray parcelData) {
-    const int length = env->GetArrayLength(parcelData);
-
-    std::unique_ptr<uint8_t> bytes(new uint8_t[length]);
-    env->GetByteArrayRegion(parcelData, 0, length, reinterpret_cast<jbyte*>(bytes.get()));
-
-    android::Parcel p;
-    p.setData(bytes.get(), length);
-
-    UidRange range;
-    range.readFromParcel(&p);
-    return range;
-}
-
-/**
- * Creates a Java byte[] array and writes the contents of 'range' to it as a Parcel containing
- * exactly one object.
- *
- * Every UidRange maps to a unique parcel object, so both 'marshall(e, unmarshall(e, x))' and
- * 'unmarshall(e, marshall(e, x))' should be fixed points.
- */
-jbyteArray marshall(JNIEnv* env, const UidRange& range) {
-    android::Parcel p;
-    range.writeToParcel(&p);
-    const int length = p.dataSize();
-
-    jbyteArray parcelData = env->NewByteArray(length);
-    env->SetByteArrayRegion(parcelData, 0, length, reinterpret_cast<const jbyte*>(p.data()));
-
-    return parcelData;
-}
diff --git a/tests/net/jni/UidRangeTest.h b/tests/net/jni/UidRangeTest.h
deleted file mode 100644
index b7e7453..0000000
--- a/tests/net/jni/UidRangeTest.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#ifndef _ANDROID_NET_UIDRANGETEST_H_
-#define _ANDROID_NET_UIDRANGETEST_H_
-
-#include <jni.h>
-
-#include "android/net/UidRange.h"
-
-android::net::UidRange unmarshall(JNIEnv* env, jbyteArray parcelData);
-
-jbyteArray marshall(JNIEnv* env, const android::net::UidRange& range);
-
-extern "C"
-JNIEXPORT jbyteArray Java_android_net_UidRangeTest_readAndWriteNative(JNIEnv* env, jclass,
-        jbyteArray inParcel);
-
-extern "C"
-JNIEXPORT jint Java_android_net_UidRangeTest_getStart(JNIEnv* env, jclass, jbyteArray inParcel);
-
-extern "C"
-JNIEXPORT jint Java_android_net_UidRangeTest_getStop(JNIEnv* env, jclass, jbyteArray inParcel);
-
-#endif  //  _ANDROID_NET_UIDRANGETEST_H_
diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp
index 4be6534..0a9e964 100644
--- a/tests/utils/testutils/Android.bp
+++ b/tests/utils/testutils/Android.bp
@@ -19,7 +19,7 @@
 
     srcs: ["java/**/*.java"],
 
-    static_libs: ["android-support-test"],
+    static_libs: ["junit"],
 
     libs: [
         "android.test.runner",
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 0a517ab..0bc5221 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -306,8 +306,29 @@
             break;
         }
 
-        if (entry->overlayable) {
-          printer->Print(" OVERLAYABLE");
+        for (size_t i = 0; i < entry->overlayable_declarations.size(); i++) {
+          printer->Print((i == 0) ? " " : "|");
+          printer->Print("OVERLAYABLE");
+
+          if (entry->overlayable_declarations[i].policy) {
+            switch (entry->overlayable_declarations[i].policy.value()) {
+              case Overlayable::Policy::kProduct:
+                printer->Print("_PRODUCT");
+                break;
+              case Overlayable::Policy::kProductServices:
+                printer->Print("_PRODUCT_SERVICES");
+                break;
+              case Overlayable::Policy::kSystem:
+                printer->Print("_SYSTEM");
+                break;
+              case Overlayable::Policy::kVendor:
+                printer->Print("_VENDOR");
+                break;
+              case Overlayable::Policy::kPublic:
+                printer->Print("_PUBLIC");
+                break;
+            }
+          }
         }
 
         printer->Println();
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 9a3f14c..4f25e09 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -99,7 +99,7 @@
   ResourceId id;
   Visibility::Level visibility_level = Visibility::Level::kUndefined;
   bool allow_new = false;
-  bool overlayable = false;
+  std::vector<Overlayable> overlayable_declarations;
 
   std::string comment;
   std::unique_ptr<Value> value;
@@ -133,11 +133,8 @@
     }
   }
 
-  if (res->overlayable) {
-    Overlayable overlayable;
-    overlayable.source = res->source;
-    overlayable.comment = res->comment;
-    if (!table->SetOverlayable(res->name, overlayable, diag)) {
+  for (auto& overlayable : res->overlayable_declarations) {
+    if (!table->AddOverlayable(res->name, overlayable, diag)) {
       return false;
     }
   }
@@ -673,7 +670,7 @@
   if (can_be_bag) {
     const auto bag_iter = elToBagMap.find(resource_type);
     if (bag_iter != elToBagMap.end()) {
-      // Ensure we have a name (unless this is a <public-group>).
+      // Ensure we have a name (unless this is a <public-group> or <overlayable>).
       if (resource_type != "public-group" && resource_type != "overlayable") {
         if (!maybe_name) {
           diag_->Error(DiagMessage(out_resource->source)
@@ -1062,74 +1059,137 @@
 bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   if (out_resource->config != ConfigDescription::DefaultConfig()) {
     diag_->Warn(DiagMessage(out_resource->source)
-                << "ignoring configuration '" << out_resource->config << "' for <overlayable> tag");
+                    << "ignoring configuration '" << out_resource->config
+                    << "' for <overlayable> tag");
   }
 
-  if (Maybe<StringPiece> maybe_policy = xml::FindNonEmptyAttribute(parser, "policy")) {
-    const StringPiece& policy = maybe_policy.value();
-    if (policy != "system") {
-      diag_->Error(DiagMessage(out_resource->source)
-                   << "<overlayable> has invalid policy '" << policy << "'");
-      return false;
-    }
-  }
+  std::string comment;
+  std::vector<Overlayable::Policy> policies;
 
   bool error = false;
-  const size_t depth = parser->depth();
-  while (xml::XmlPullParser::NextChildNode(parser, depth)) {
-    if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
-      // Skip text/comments.
+  const size_t start_depth = parser->depth();
+  while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
+    xml::XmlPullParser::Event event = parser->event();
+    if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) {
+      // Break the loop when exiting the overyabale element
+      break;
+    } else if (event == xml::XmlPullParser::Event::kEndElement
+               && parser->depth() == start_depth + 1) {
+      // Clear the current policies when exiting the policy element
+      policies.clear();
+      continue;
+    } else if (event == xml::XmlPullParser::Event::kComment) {
+      // Get the comment of individual item elements
+      comment = parser->comment();
+      continue;
+    } else if (event != xml::XmlPullParser::Event::kStartElement) {
+      // Skip to the next element
       continue;
     }
 
     const Source item_source = source_.WithLine(parser->line_number());
-    const std::string& element_namespace = parser->element_namespace();
     const std::string& element_name = parser->element_name();
+    const std::string& element_namespace = parser->element_namespace();
+
     if (element_namespace.empty() && element_name == "item") {
-      Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
-      if (!maybe_name) {
-        diag_->Error(DiagMessage(item_source)
-                     << "<item> within an <overlayable> tag must have a 'name' attribute");
+      if (!ParseOverlayableItem(parser, policies, comment, out_resource)) {
         error = true;
-        continue;
       }
-
-      Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
-      if (!maybe_type) {
-        diag_->Error(DiagMessage(item_source)
-                     << "<item> within an <overlayable> tag must have a 'type' attribute");
+    } else if (element_namespace.empty() && element_name == "policy") {
+      if (!policies.empty()) {
+        // If the policy list is not empty, then we are currently inside a policy element
+        diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested");
         error = true;
-        continue;
+        break;
+      } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
+        // Parse the polices separated by vertical bar characters to allow for specifying multiple
+        // policies at once
+        for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) {
+          StringPiece trimmed_part = util::TrimWhitespace(part);
+          if (trimmed_part == "public") {
+            policies.push_back(Overlayable::Policy::kPublic);
+          } else if (trimmed_part == "product") {
+            policies.push_back(Overlayable::Policy::kProduct);
+          } else if (trimmed_part == "product_services") {
+            policies.push_back(Overlayable::Policy::kProductServices);
+          } else if (trimmed_part == "system") {
+            policies.push_back(Overlayable::Policy::kSystem);
+          } else if (trimmed_part == "vendor") {
+            policies.push_back(Overlayable::Policy::kVendor);
+          } else {
+            diag_->Error(DiagMessage(out_resource->source)
+                             << "<policy> has unsupported type '" << trimmed_part << "'");
+            error = true;
+            continue;
+          }
+        }
       }
-
-      const ResourceType* type = ParseResourceType(maybe_type.value());
-      if (type == nullptr) {
-        diag_->Error(DiagMessage(out_resource->source)
-                     << "invalid resource type '" << maybe_type.value()
-                     << "' in <item> within an <overlayable>");
-        error = true;
-        continue;
-      }
-
-      ParsedResource child_resource;
-      child_resource.name.type = *type;
-      child_resource.name.entry = maybe_name.value().to_string();
-      child_resource.source = item_source;
-      child_resource.overlayable = true;
-      if (options_.visibility) {
-        child_resource.visibility_level = options_.visibility.value();
-      }
-      out_resource->child_resources.push_back(std::move(child_resource));
-
-      xml::XmlPullParser::SkipCurrentElement(parser);
     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
-      diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
+      diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> in "
+                                            << " <overlayable>");
       error = true;
+      break;
     }
   }
+
   return !error;
 }
 
+bool ResourceParser::ParseOverlayableItem(xml::XmlPullParser* parser,
+                                          const std::vector<Overlayable::Policy>& policies,
+                                          const std::string& comment,
+                                          ParsedResource* out_resource) {
+  const Source item_source = source_.WithLine(parser->line_number());
+
+  Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+  if (!maybe_name) {
+    diag_->Error(DiagMessage(item_source)
+                     << "<item> within an <overlayable> tag must have a 'name' attribute");
+    return false;
+  }
+
+  Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+  if (!maybe_type) {
+    diag_->Error(DiagMessage(item_source)
+                     << "<item> within an <overlayable> tag must have a 'type' attribute");
+    return false;
+  }
+
+  const ResourceType* type = ParseResourceType(maybe_type.value());
+  if (type == nullptr) {
+    diag_->Error(DiagMessage(out_resource->source)
+                     << "invalid resource type '" << maybe_type.value()
+                     << "' in <item> within an <overlayable>");
+    return false;
+  }
+
+  ParsedResource child_resource;
+  child_resource.name.type = *type;
+  child_resource.name.entry = maybe_name.value().to_string();
+  child_resource.source = item_source;
+
+  if (policies.empty()) {
+    Overlayable overlayable;
+    overlayable.source = item_source;
+    overlayable.comment = comment;
+    child_resource.overlayable_declarations.push_back(overlayable);
+  } else {
+    for (Overlayable::Policy policy : policies) {
+      Overlayable overlayable;
+      overlayable.policy = policy;
+      overlayable.source = item_source;
+      overlayable.comment = comment;
+      child_resource.overlayable_declarations.push_back(overlayable);
+    }
+  }
+
+  if (options_.visibility) {
+    child_resource.visibility_level = options_.visibility.value();
+  }
+  out_resource->child_resources.push_back(std::move(child_resource));
+  return true;
+}
+
 bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   if (ParseSymbolImpl(parser, out_resource)) {
     out_resource->visibility_level = Visibility::Level::kUndefined;
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 06bb0c9..ebacd6f 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -96,6 +96,10 @@
   bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource);
+  bool ParseOverlayableItem(xml::XmlPullParser* parser,
+                            const std::vector<Overlayable::Policy>& policies,
+                            const std::string& comment,
+                            ParsedResource* out_resource);
   bool ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 0dff664..c6f29ac 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -891,56 +891,162 @@
   ASSERT_TRUE(TestParse(R"(<string name="foo">%1$s %n %2$s</string>)"));
 }
 
-TEST_F(ResourceParserTest, ParseOverlayableTagWithSystemPolicy) {
-  std::string input = R"(
-      <overlayable policy="illegal_policy">
-        <item type="string" name="foo" />
-      </overlayable>)";
-  EXPECT_FALSE(TestParse(input));
-
-  input = R"(
-      <overlayable policy="system">
-        <item name="foo" />
-      </overlayable>)";
-  EXPECT_FALSE(TestParse(input));
-
-  input = R"(
-      <overlayable policy="system">
-        <item type="attr" />
-      </overlayable>)";
-  EXPECT_FALSE(TestParse(input));
-
-  input = R"(
-      <overlayable policy="system">
-        <item type="bad_type" name="foo" />
-      </overlayable>)";
-  EXPECT_FALSE(TestParse(input));
-
-  input = R"(<overlayable policy="system" />)";
+TEST_F(ResourceParserTest, ParseOverlayable) {
+  std::string input = R"(<overlayable />)";
   EXPECT_TRUE(TestParse(input));
 
-  input = R"(<overlayable />)";
-  EXPECT_TRUE(TestParse(input));
-
-  input = R"(
-      <overlayable policy="system">
-        <item type="string" name="foo" />
-        <item type="dimen" name="foo" />
-      </overlayable>)";
-  ASSERT_TRUE(TestParse(input));
-
   input = R"(
       <overlayable>
-        <item type="string" name="bar" />
+        <item type="string" name="foo" />
+        <item type="drawable" name="bar" />
       </overlayable>)";
   ASSERT_TRUE(TestParse(input));
 
-  Maybe<ResourceTable::SearchResult> search_result =
-      table_.FindResource(test::ParseNameOrDie("string/bar"));
+  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
   EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
-  EXPECT_TRUE(search_result.value().entry->overlayable);
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+
+  search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+}
+
+TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
+  std::string input = R"(<overlayable />)";
+  EXPECT_TRUE(TestParse(input));
+
+  input = R"(
+      <overlayable>
+        <item type="string" name="foo" />
+        <policy type="product">
+          <item type="string" name="bar" />
+        </policy>
+        <policy type="product_services">
+          <item type="string" name="baz" />
+        </policy>
+        <policy type="system">
+          <item type="string" name="fiz" />
+        </policy>
+        <policy type="vendor">
+          <item type="string" name="fuz" />
+        </policy>
+        <policy type="public">
+          <item type="string" name="faz" />
+        </policy>
+      </overlayable>)";
+  ASSERT_TRUE(TestParse(input));
+
+  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProduct));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/baz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProductServices));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kSystem));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kVendor));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kPublic));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
+  std::string input = R"(
+      <overlayable>
+        <policy type="illegal_policy">
+          <item type="string" name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable>
+        <policy type="product">
+          <item name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable>
+        <policy type="vendor">
+          <item type="string" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
+  std::string input = R"(
+      <overlayable>
+        <policy type="vendor|product_services">
+          <item type="string" name="foo" />
+        </policy>
+        <policy type="product|system">
+          <item type="string" name="bar" />
+        </policy>
+      </overlayable>)";
+  ASSERT_TRUE(TestParse(input));
+
+  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kVendor));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy,
+              Eq(Overlayable::Policy::kProductServices));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProduct));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy,
+              Eq(Overlayable::Policy::kSystem));
 }
 
 TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
@@ -950,6 +1056,85 @@
         <item type="string" name="foo" />
       </overlayable>)";
   EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable>
+        <item type="string" name="foo" />
+      </overlayable>
+      <overlayable>
+        <item type="string" name="foo" />
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable">
+        <policy type="product">
+          <item type="string" name="foo" />
+          <item type="string" name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable>
+        <policy type="product">
+          <item type="string" name="foo" />
+        </policy>
+      </overlayable>
+
+      <overlayable>
+        <policy type="product">
+          <item type="string" name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, PolicyAndNonPolicyOverlayableError) {
+  std::string input = R"(
+        <overlayable policy="product">
+          <item type="string" name="foo" />
+        </overlayable>
+        <overlayable policy="">
+          <item type="string" name="foo" />
+        </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+        <overlayable policy="">
+          <item type="string" name="foo" />
+        </overlayable>
+        <overlayable policy="product">
+          <item type="string" name="foo" />
+        </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, DuplicateOverlayableMultiplePolicyError) {
+  std::string input = R"(
+      <overlayable>
+        <policy type="vendor|product">
+          <item type="string" name="foo" />
+        </policy>
+      </overlayable>
+      <overlayable>
+        <policy type="product_services|vendor">
+          <item type="string" name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, NestPolicyInOverlayableError) {
+  std::string input = R"(
+      <overlayable>
+        <policy type="vendor|product">
+          <policy type="product_services">
+            <item type="string" name="foo" />
+          </policy>
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
 }
 
 TEST_F(ResourceParserTest, ParseIdItem) {
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 056a27b..bc8a4d1 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -625,17 +625,17 @@
   return true;
 }
 
-bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
                                    IDiagnostics* diag) {
-  return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
+  return AddOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
 }
 
-bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
+bool ResourceTable::AddOverlayableMangled(const ResourceNameRef& name,
                                           const Overlayable& overlayable, IDiagnostics* diag) {
-  return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
+  return AddOverlayableImpl(name, overlayable, SkipNameValidator, diag);
 }
 
-bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
                                        NameValidator name_validator, IDiagnostics* diag) {
   CHECK(diag != nullptr);
 
@@ -646,13 +646,28 @@
   ResourceTablePackage* package = FindOrCreatePackage(name.package);
   ResourceTableType* type = package->FindOrCreateType(name.type);
   ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
-  if (entry->overlayable) {
-    diag->Error(DiagMessage(overlayable.source)
-                << "duplicate overlayable declaration for resource '" << name << "'");
-    diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
-    return false;
+
+  for (auto& overlayable_declaration : entry->overlayable_declarations) {
+    // An overlayable resource cannot be declared twice with the same policy
+    if (overlayable.policy == overlayable_declaration.policy) {
+      diag->Error(DiagMessage(overlayable.source)
+                    << "duplicate overlayable declaration for resource '" << name << "'");
+      diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here");
+      return false;
+    }
+
+    // An overlayable resource cannot be declared once with a policy and without a policy because
+    // the policy becomes unused
+    if (!overlayable.policy || !overlayable_declaration.policy) {
+      diag->Error(DiagMessage(overlayable.source)
+                    << "overlayable resource '" << name << "'"
+                    << " declared once with a policy and once with no policy");
+      diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here");
+      return false;
+    }
   }
-  entry->overlayable = overlayable;
+
+  entry->overlayable_declarations.push_back(overlayable);
   return true;
 }
 
@@ -688,7 +703,7 @@
         new_entry->id = entry->id;
         new_entry->visibility = entry->visibility;
         new_entry->allow_new = entry->allow_new;
-        new_entry->overlayable = entry->overlayable;
+        new_entry->overlayable_declarations = entry->overlayable_declarations;
 
         for (const auto& config_value : entry->values) {
           ResourceConfigValue* new_value =
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 1917d7e..3dd0a769 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -57,8 +57,27 @@
   std::string comment;
 };
 
-// The policy dictating whether an entry is overlayable at runtime by RROs.
+// Represents a declaration that a resource is overayable at runtime.
 struct Overlayable {
+  // Represents the types overlays that are allowed to overlay the resource.
+  enum class Policy {
+    // The resource can be overlaid by any overlay.
+    kPublic,
+
+    // The resource can be overlaid by any overlay on the system partition.
+    kSystem,
+
+    // The resource can be overlaid by any overlay on the vendor partition.
+    kVendor,
+
+    // The resource can be overlaid by any overlay on the product partition.
+    kProduct,
+
+    // The resource can be overlaid by any overlay on the product services partition.
+    kProductServices,
+  };
+
+  Maybe<Policy> policy;
   Source source;
   std::string comment;
 };
@@ -96,7 +115,8 @@
 
   Maybe<AllowNew> allow_new;
 
-  Maybe<Overlayable> overlayable;
+  // The declarations of this resource as overlayable for RROs
+  std::vector<Overlayable> overlayable_declarations;
 
   // The resource's values for each configuration.
   std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -226,9 +246,9 @@
   bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility,
                                   const ResourceId& res_id, IDiagnostics* diag);
 
-  bool SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+  bool AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
                       IDiagnostics* diag);
-  bool SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
+  bool AddOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
                              IDiagnostics* diag);
 
   bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag);
@@ -303,7 +323,7 @@
   bool SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
                        NameValidator name_validator, IDiagnostics* diag);
 
-  bool SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+  bool AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
                           NameValidator name_validator, IDiagnostics* diag);
 
   bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 05c6f15..7c28f07 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -242,21 +242,69 @@
   ASSERT_THAT(result.value().entry->allow_new.value().comment, StrEq("second"));
 }
 
-TEST(ResourceTableTest, SetOverlayable) {
+TEST(ResourceTableTest, AddOverlayable) {
   ResourceTable table;
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
 
   Overlayable overlayable;
-
+  overlayable.policy = Overlayable::Policy::kProduct;
   overlayable.comment = "first";
-  ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics()));
   Maybe<ResourceTable::SearchResult> result = table.FindResource(name);
   ASSERT_TRUE(result);
-  ASSERT_TRUE(result.value().entry->overlayable);
-  ASSERT_THAT(result.value().entry->overlayable.value().comment, StrEq("first"));
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[0].comment, StrEq("first"));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProduct));
 
-  overlayable.comment = "second";
-  ASSERT_FALSE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  Overlayable overlayable2;
+  overlayable2.comment = "second";
+  overlayable2.policy = Overlayable::Policy::kProductServices;
+  ASSERT_TRUE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics()));
+  result = table.FindResource(name);
+  ASSERT_TRUE(result);
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[0].comment, StrEq("first"));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProduct));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[1].comment, StrEq("second"));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[1].policy,
+              Eq(Overlayable::Policy::kProductServices));
+}
+
+TEST(ResourceTableTest, AddDuplicateOverlayableFail) {
+  ResourceTable table;
+  const ResourceName name = test::ParseNameOrDie("android:string/foo");
+
+  Overlayable overlayable;
+  overlayable.policy = Overlayable::Policy::kProduct;
+  ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics()));
+
+  Overlayable overlayable2;
+  overlayable2.policy = Overlayable::Policy::kProduct;
+  ASSERT_FALSE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, AddOverlayablePolicyAndNoneFirstFail) {
+  ResourceTable table;
+  const ResourceName name = test::ParseNameOrDie("android:string/foo");
+
+  ASSERT_TRUE(table.AddOverlayable(name, {}, test::GetDiagnostics()));
+
+  Overlayable overlayable2;
+  overlayable2.policy = Overlayable::Policy::kProduct;
+  ASSERT_FALSE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, AddOverlayablePolicyAndNoneLastFail) {
+  ResourceTable table;
+  const ResourceName name = test::ParseNameOrDie("android:string/foo");
+
+  Overlayable overlayable;
+  overlayable.policy = Overlayable::Policy::kProduct;
+  ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics()));
+
+  ASSERT_FALSE(table.AddOverlayable(name, {}, test::GetDiagnostics()));
 }
 
 TEST(ResourceTableTest, AllowDuplictaeResourcesNames) {
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index d7a3771..bf9fe49 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -133,13 +133,25 @@
   string comment = 2;
 }
 
-// Whether a resource is overlayable by runtime resource overlays (RRO).
+// Represents a declaration that a resource is overayable at runtime.
 message Overlayable {
+  enum Policy {
+    NONE = 0;
+    PUBLIC = 1;
+    SYSTEM = 2;
+    VENDOR = 3;
+    PRODUCT = 4;
+    PRODUCT_SERVICES = 5;
+  }
+
   // Where this declaration was defined in source.
   Source source = 1;
 
   // Any comment associated with the declaration.
   string comment = 2;
+
+  // The policy of the overlayable declaration
+  Policy policy = 3;
 }
 
 // An entry ID in the range [0x0000, 0xffff].
@@ -169,7 +181,7 @@
   AllowNew allow_new = 4;
 
   // Whether this resource can be overlaid by a runtime resource overlay (RRO).
-  Overlayable overlayable = 5;
+  repeated Overlayable overlayable = 5;
 
   // The set of values defined for this entry, each corresponding to a different
   // configuration/variant.
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 3a39a6b..ed70fb3 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -398,7 +398,7 @@
       if (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) {
         Overlayable overlayable;
         overlayable.source = source_.WithLine(0);
-        if (!table_->SetOverlayableMangled(name, overlayable, diag_)) {
+        if (!table_->AddOverlayableMangled(name, overlayable, diag_)) {
           return false;
         }
       }
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 8641a7c..8a86f63a 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -446,7 +446,7 @@
         config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
       }
 
-      if (entry->overlayable) {
+      if (!entry->overlayable_declarations.empty()) {
         config_masks[entry->id.value()] |=
             util::HostToDevice32(ResTable_typeSpec::SPEC_OVERLAYABLE);
       }
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index af19b98..cd1414c 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -634,7 +634,7 @@
           .AddSimple("com.app.test:integer/overlayable", ResourceId(0x7f020000))
           .Build();
 
-  ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"),
+  ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"),
                                     Overlayable{}, test::GetDiagnostics()));
 
   ResTable res_table;
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index d1b2fdb..f612914 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -437,15 +437,37 @@
         entry->allow_new = std::move(allow_new);
       }
 
-      if (pb_entry.has_overlayable()) {
-        const pb::Overlayable& pb_overlayable = pb_entry.overlayable();
-
+      for (const pb::Overlayable& pb_overlayable : pb_entry.overlayable()) {
         Overlayable overlayable;
+        switch (pb_overlayable.policy()) {
+          case pb::Overlayable::NONE:
+            overlayable.policy = {};
+            break;
+          case pb::Overlayable::PUBLIC:
+            overlayable.policy = Overlayable::Policy::kPublic;
+            break;
+          case pb::Overlayable::PRODUCT:
+            overlayable.policy = Overlayable::Policy::kProduct;
+            break;
+          case pb::Overlayable::PRODUCT_SERVICES:
+            overlayable.policy = Overlayable::Policy::kProductServices;
+            break;
+          case pb::Overlayable::SYSTEM:
+            overlayable.policy = Overlayable::Policy::kSystem;
+            break;
+          case pb::Overlayable::VENDOR:
+            overlayable.policy = Overlayable::Policy::kVendor;
+            break;
+          default:
+            *out_error = "unknown overlayable policy";
+            return false;
+        }
+
         if (pb_overlayable.has_source()) {
           DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &overlayable.source);
         }
         overlayable.comment = pb_overlayable.comment();
-        entry->overlayable = std::move(overlayable);
+        entry->overlayable_declarations.push_back(overlayable);
       }
 
       ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 7e35ea7..f1e96d6 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -310,11 +310,31 @@
           pb_allow_new->set_comment(entry->allow_new.value().comment);
         }
 
-        if (entry->overlayable) {
-          pb::Overlayable* pb_overlayable = pb_entry->mutable_overlayable();
-          SerializeSourceToPb(entry->overlayable.value().source, &source_pool,
+        for (const Overlayable& overlayable : entry->overlayable_declarations) {
+          pb::Overlayable* pb_overlayable = pb_entry->add_overlayable();
+          if (overlayable.policy) {
+            switch (overlayable.policy.value()) {
+              case Overlayable::Policy::kPublic:
+                pb_overlayable->set_policy(pb::Overlayable::PUBLIC);
+                break;
+              case Overlayable::Policy::kProduct:
+                pb_overlayable->set_policy(pb::Overlayable::PRODUCT);
+                break;
+              case Overlayable::Policy::kProductServices:
+                pb_overlayable->set_policy(pb::Overlayable::PRODUCT_SERVICES);
+                break;
+              case Overlayable::Policy::kSystem:
+                pb_overlayable->set_policy(pb::Overlayable::SYSTEM);
+                break;
+              case Overlayable::Policy::kVendor:
+                pb_overlayable->set_policy(pb::Overlayable::VENDOR);
+                break;
+            }
+          }
+
+          SerializeSourceToPb(overlayable.source, &source_pool,
                               pb_overlayable->mutable_source());
-          pb_overlayable->set_comment(entry->overlayable.value().comment);
+          pb_overlayable->set_comment(overlayable.comment);
         }
 
         for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 3c4d41a..95dbbeb 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -93,7 +93,7 @@
       util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
 
   // Make an overlayable resource.
-  ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
+  ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
                                     Overlayable{}, test::GetDiagnostics()));
 
   pb::ResourceTable pb_table;
@@ -106,7 +106,7 @@
 
   ResourceTable new_table;
   std::string error;
-  ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+  ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error)) << error;
   EXPECT_THAT(error, IsEmpty());
 
   Id* new_id = test::GetValue<Id>(&new_table, "com.app.a:id/foo");
@@ -160,7 +160,8 @@
       new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  EXPECT_TRUE(search_result.value().entry->overlayable);
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
 }
 
 TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
@@ -464,4 +465,59 @@
       "night-xhdpi-stylus-keysexposed-qwerty-navhidden-dpad-300x200-v23");
 }
 
+TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddOverlayable("com.app.a:bool/foo", Overlayable::Policy::kSystem)
+          .AddOverlayable("com.app.a:bool/foo", Overlayable::Policy::kProduct)
+          .AddOverlayable("com.app.a:bool/bar", Overlayable::Policy::kProductServices)
+          .AddOverlayable("com.app.a:bool/bar", Overlayable::Policy::kVendor)
+          .AddOverlayable("com.app.a:bool/baz", Overlayable::Policy::kPublic)
+          .AddOverlayable("com.app.a:bool/biz", {})
+          .AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
+          .Build();
+
+  pb::ResourceTable pb_table;
+  SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+
+  MockFileCollection files;
+  ResourceTable new_table;
+  std::string error;
+  ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+  EXPECT_THAT(error, IsEmpty());
+
+  Maybe<ResourceTable::SearchResult> result =
+      new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
+  ASSERT_TRUE(result);
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
+  EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kSystem));
+  EXPECT_THAT(result.value().entry->overlayable_declarations[1].policy,
+              Eq(Overlayable::Policy::kProduct));
+
+  result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
+  ASSERT_TRUE(result);
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
+  EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProductServices));
+  EXPECT_THAT(result.value().entry->overlayable_declarations[1].policy,
+              Eq(Overlayable::Policy::kVendor));
+
+  result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
+  ASSERT_TRUE(result);
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kPublic));
+
+  result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
+  ASSERT_TRUE(result);
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_FALSE(result.value().entry->overlayable_declarations[0].policy);
+
+  result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
+  ASSERT_TRUE(result);
+  EXPECT_THAT(result.value().entry->overlayable_declarations.size(), Eq(0));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index afb8ae0..d777e22 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -101,7 +101,7 @@
   return true;
 }
 
-static bool MergeEntry(IAaptContext* context, const Source& src, bool overlay,
+static bool MergeEntry(IAaptContext* context, const Source& src,
                        ResourceEntry* dst_entry, ResourceEntry* src_entry,
                        bool strict_visibility) {
   if (strict_visibility
@@ -134,17 +134,35 @@
     dst_entry->allow_new = std::move(src_entry->allow_new);
   }
 
-  if (src_entry->overlayable) {
-    if (dst_entry->overlayable && !overlay) {
-      context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable.value().source)
-                                       << "duplicate overlayable declaration for resource '"
-                                       << src_entry->name << "'");
-      context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable.value().source)
-                                       << "previous declaration here");
-      return false;
+  for (auto& src_overlayable : src_entry->overlayable_declarations) {
+    for (auto& dst_overlayable : dst_entry->overlayable_declarations) {
+      // An overlayable resource cannot be declared twice with the same policy
+      if (src_overlayable.policy == dst_overlayable.policy) {
+        context->GetDiagnostics()->Error(DiagMessage(src_overlayable.source)
+                                             << "duplicate overlayable declaration for resource '"
+                                             << src_entry->name << "'");
+        context->GetDiagnostics()->Error(DiagMessage(dst_overlayable.source)
+                                             << "previous declaration here");
+        return false;
+      }
+
+      // An overlayable resource cannot be declared once with a policy and without a policy because
+      // the policy becomes unused
+      if (!src_overlayable.policy || !dst_overlayable.policy) {
+        context->GetDiagnostics()->Error(DiagMessage(src_overlayable.source)
+                                             << "overlayable resource '" << src_entry->name
+                                             << "' declared once with a policy and once with no "
+                                             << "policy");
+        context->GetDiagnostics()->Error(DiagMessage(dst_overlayable.source)
+                                             << "previous declaration here");
+        return false;
+      }
     }
-    dst_entry->overlayable = std::move(src_entry->overlayable);
   }
+
+  dst_entry->overlayable_declarations.insert(dst_entry->overlayable_declarations.end(),
+                                             src_entry->overlayable_declarations.begin(),
+                                             src_entry->overlayable_declarations.end());
   return true;
 }
 
@@ -244,7 +262,7 @@
         continue;
       }
 
-      if (!MergeEntry(context_, src, overlay, dst_entry, src_entry.get(), options_.strict_visibility)) {
+      if (!MergeEntry(context_, src, dst_entry, src_entry.get(), options_.strict_visibility)) {
         error = true;
         continue;
       }
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 79a734b..d6579d3 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -436,4 +436,97 @@
               Eq(make_value(Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent")))));
 }
 
+TEST_F(TableMergerTest, AddOverlayable) {
+  std::unique_ptr<ResourceTable> table_a =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+          .Build();
+
+  std::unique_ptr<ResourceTable> table_b =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", Overlayable::Policy::kProductServices)
+          .Build();
+
+  ResourceTable final_table;
+  TableMergerOptions options;
+  options.auto_add_overlay = true;
+  TableMerger merger(context_.get(), &final_table, options);
+  ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+  ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
+
+  const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
+  Maybe<ResourceTable::SearchResult> result = final_table.FindResource(name);
+  ASSERT_TRUE(result);
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProduct));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[1].policy,
+              Eq(Overlayable::Policy::kProductServices));
+}
+
+TEST_F(TableMergerTest, AddDuplicateOverlayableFail) {
+  std::unique_ptr<ResourceTable> table_a =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+          .Build();
+
+  std::unique_ptr<ResourceTable> table_b =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+          .Build();
+
+  ResourceTable final_table;
+  TableMergerOptions options;
+  options.auto_add_overlay = true;
+  TableMerger merger(context_.get(), &final_table, options);
+  ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+  ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
+}
+
+TEST_F(TableMergerTest, AddOverlayablePolicyAndNoneFirstFail) {
+  std::unique_ptr<ResourceTable> table_a =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", {})
+          .Build();
+
+  std::unique_ptr<ResourceTable> table_b =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+          .Build();
+
+  ResourceTable final_table;
+  TableMergerOptions options;
+  options.auto_add_overlay = true;
+  TableMerger merger(context_.get(), &final_table, options);
+  ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+  ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
+}
+
+TEST_F(TableMergerTest, AddOverlayablePolicyAndNoneLastFail) {
+  std::unique_ptr<ResourceTable> table_a =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+          .Build();
+
+  std::unique_ptr<ResourceTable> table_b =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", {})
+          .Build();
+
+  ResourceTable final_table;
+  TableMergerOptions options;
+  options.auto_add_overlay = true;
+  TableMerger merger(context_.get(), &final_table, options);
+  ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+  ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 414758e..2e717ff 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -248,6 +248,7 @@
             if (!split_entry->id) {
               split_entry->id = entry->id;
               split_entry->visibility = entry->visibility;
+              split_entry->overlayable_declarations = entry->overlayable_declarations;
             }
 
             // Copy the selected values into the new Split Entry.
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index f33ae31..03b59e0 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -135,6 +135,15 @@
   return *this;
 }
 
+ResourceTableBuilder& ResourceTableBuilder::AddOverlayable(const StringPiece& name,
+                                                           const Maybe<Overlayable::Policy> p) {
+  ResourceName res_name = ParseNameOrDie(name);
+  Overlayable overlayable;
+  overlayable.policy = p;
+  CHECK(table_->AddOverlayable(res_name, overlayable, GetDiagnostics()));
+  return *this;
+}
+
 StringPool* ResourceTableBuilder::string_pool() {
   return &table_->string_pool;
 }
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 9159599..d68c24d 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -73,6 +73,8 @@
                                  const ResourceId& id, std::unique_ptr<Value> value);
   ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
                                        Visibility::Level level, bool allow_new = false);
+  ResourceTableBuilder& AddOverlayable(const android::StringPiece& name,
+                                       Maybe<Overlayable::Policy> policy);
 
   StringPool* string_pool();
   std::unique_ptr<ResourceTable> Build();
diff --git a/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl
new file mode 100644
index 0000000..f472a02
--- /dev/null
+++ b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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 android.net.wifi;
+
+import android.net.wifi.INetworkRequestUserSelectionCallback;
+import android.net.wifi.WifiConfiguration;
+
+/**
+ * Interface for network request match callback.
+ *
+ * @hide
+ */
+oneway interface INetworkRequestMatchCallback
+{
+   void onUserSelectionCallbackRegistration(in INetworkRequestUserSelectionCallback userSelectionCallback);
+
+   void onMatch(in List<WifiConfiguration> wificonfigurations);
+
+   void onUserSelectionConnectSuccess(in WifiConfiguration wificonfiguration);
+
+   void onUserSelectionConnectFailure(in WifiConfiguration wificonfiguration);
+}
diff --git a/wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl b/wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl
new file mode 100644
index 0000000..524cefb
--- /dev/null
+++ b/wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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 android.net.wifi;
+
+import android.net.wifi.WifiConfiguration;
+
+/**
+ * Interface for providing user selection in response to
+ * network request match callback.
+ * @hide
+ */
+oneway interface INetworkRequestUserSelectionCallback
+{
+   void select(in WifiConfiguration wificonfiguration);
+
+   void reject();
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 12f50c8..1fd68ec 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -25,6 +25,7 @@
 
 import android.net.DhcpInfo;
 import android.net.Network;
+import android.net.wifi.INetworkRequestMatchCallback;
 import android.net.wifi.ISoftApCallback;
 import android.net.wifi.ITrafficStateCallback;
 import android.net.wifi.PasspointManagementObjectDefinition;
@@ -185,5 +186,9 @@
     void registerTrafficStateCallback(in IBinder binder, in ITrafficStateCallback callback, int callbackIdentifier);
 
     void unregisterTrafficStateCallback(int callbackIdentifier);
+
+    void registerNetworkRequestMatchCallback(in IBinder binder, in INetworkRequestMatchCallback callback, int callbackIdentifier);
+
+    void unregisterNetworkRequestMatchCallback(int callbackIdentifier);
 }
 
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 3a4e88b..9b9247d 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -86,9 +86,9 @@
     public static final int PROTOCOL_WPA = 1;
     /**
      * @hide
-     * Security protocol type: WPA version 2, also called RSN.
+     * Security protocol type: RSN, for WPA version 2, and version 3.
      */
-    public static final int PROTOCOL_WPA2 = 2;
+    public static final int PROTOCOL_RSN = 2;
     /**
      * @hide
      * Security protocol type:
@@ -138,7 +138,21 @@
      * Used for Hotspot 2.0.
      */
     public static final int KEY_MGMT_OSEN = 7;
-
+     /**
+     * @hide
+     * Security key management scheme: SAE.
+     */
+    public static final int KEY_MGMT_SAE = 8;
+    /**
+     * @hide
+     * Security key management scheme: OWE.
+     */
+    public static final int KEY_MGMT_OWE = 9;
+    /**
+     * @hide
+     * Security key management scheme: SUITE_B_192.
+     */
+    public static final int KEY_MGMT_EAP_SUITE_B_192 = 10;
     /**
      * @hide
      * No cipher suite.
@@ -159,6 +173,11 @@
      * Cipher suite: CCMP
      */
     public static final int CIPHER_CCMP = 3;
+    /**
+     * @hide
+     * Cipher suite: GCMP
+     */
+    public static final int CIPHER_GCMP_256 = 4;
 
     /**
      * The detected signal level in dBm, also known as the RSSI.
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 7472278..8fc9b97 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -23,6 +23,7 @@
 import android.net.IpConfiguration;
 import android.net.IpConfiguration.ProxySettings;
 import android.net.MacAddress;
+import android.net.NetworkSpecifier;
 import android.net.ProxyInfo;
 import android.net.StaticIpConfiguration;
 import android.net.Uri;
@@ -47,7 +48,11 @@
 /**
  * A class representing a configured Wi-Fi network, including the
  * security configuration.
+ *
+ * @deprecated Use {@link WifiNetworkConfigBuilder} to create {@link NetworkSpecifier} and
+ * {@link WifiNetworkSuggestion}. This will become a system use only object in the future.
  */
+@Deprecated
 public class WifiConfiguration implements Parcelable {
     private static final String TAG = "WifiConfiguration";
     /**
@@ -125,10 +130,26 @@
          */
         public static final int FT_EAP = 7;
 
+        /**
+         * Simultaneous Authentication of Equals
+         */
+        public static final int SAE = 8;
+
+        /**
+         * Opportunististic Wireless Encryption
+         */
+        public static final int OWE = 9;
+
+        /**
+         * SUITE_B_192 192 bit level
+         */
+        public static final int SUITE_B_192 = 10;
+
         public static final String varName = "key_mgmt";
 
         public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP",
-                "IEEE8021X", "WPA2_PSK", "OSEN", "FT_PSK", "FT_EAP" };
+                "IEEE8021X", "WPA2_PSK", "OSEN", "FT_PSK", "FT_EAP",
+                "SAE", "OWE", "SUITE_B_192"};
     }
 
     /**
@@ -142,7 +163,7 @@
          * is discouraged. WPA-2 (RSN) should be used instead. */
         @Deprecated
         public static final int WPA = 0;
-        /** WPA2/IEEE 802.11i */
+        /** RSN WPA2/WPA3/IEEE 802.11i */
         public static final int RSN = 1;
         /** HS2.0 r2 OSEN
          * @hide
@@ -190,10 +211,14 @@
         public static final int TKIP = 1;
         /** AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] */
         public static final int CCMP = 2;
+        /**
+         * AES in Galois/Counter Mode
+         */
+        public static final int GCMP_256 = 3;
 
         public static final String varName = "pairwise";
 
-        public static final String[] strings = { "NONE", "TKIP", "CCMP" };
+        public static final String[] strings = { "NONE", "TKIP", "CCMP", "GCMP_256" };
     }
 
     /**
@@ -203,6 +228,7 @@
      * TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
      * WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key
      * WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key (original 802.11)
+     * GCMP_256 = AES in Galois/Counter Mode
      * </pre>
      */
     public static class GroupCipher {
@@ -226,12 +252,64 @@
          * @hide
          */
         public static final int GTK_NOT_USED = 4;
+        /**
+         * AES in Galois/Counter Mode
+         */
+        public static final int GCMP_256 = 5;
 
         public static final String varName = "group";
 
         public static final String[] strings =
                 { /* deprecated */ "WEP40", /* deprecated */ "WEP104",
-                        "TKIP", "CCMP", "GTK_NOT_USED" };
+                        "TKIP", "CCMP", "GTK_NOT_USED", "GCMP_256" };
+    }
+
+    /**
+     * Recognized group management ciphers.
+     * <pre>
+     * BIP_CMAC_256 = Cipher-based Message Authentication Code 256 bits
+     * BIP_GMAC_128 = Galois Message Authentication Code 128 bits
+     * BIP_GMAC_256 = Galois Message Authentication Code 256 bits
+     * </pre>
+     */
+    public static class GroupMgmtCipher {
+        private GroupMgmtCipher() { }
+
+        /** CMAC-256 = Cipher-based Message Authentication Code */
+        public static final int BIP_CMAC_256 = 0;
+
+        /** GMAC-128 = Galois Message Authentication Code */
+        public static final int BIP_GMAC_128 = 1;
+
+        /** GMAC-256 = Galois Message Authentication Code */
+        public static final int BIP_GMAC_256 = 2;
+
+        private static final String varName = "groupMgmt";
+
+        private static final String[] strings = { "BIP_CMAC_256",
+                "BIP_GMAC_128", "BIP_GMAC_256"};
+    }
+
+    /**
+     * Recognized suiteB ciphers.
+     * <pre>
+     * ECDHE_ECDSA
+     * ECDHE_RSA
+     * </pre>
+     * @hide
+     */
+    public static class SuiteBCipher {
+        private SuiteBCipher() { }
+
+        /** Diffie-Hellman with Elliptic Curve_ECDSA signature */
+        public static final int ECDHE_ECDSA = 0;
+
+        /** Diffie-Hellman with_RSA signature */
+        public static final int ECDHE_RSA = 1;
+
+        private static final String varName = "SuiteB";
+
+        private static final String[] strings = { "ECDHE_ECDSA", "ECDHE_RSA" };
     }
 
     /** Possible status of a network configuration. */
@@ -409,6 +487,17 @@
      */
     public BitSet allowedGroupCiphers;
     /**
+     * The set of group management ciphers supported by this configuration.
+     * See {@link GroupMgmtCipher} for descriptions of the values.
+     */
+    public BitSet allowedGroupMgmtCiphers;
+    /**
+     * The set of SuiteB ciphers supported by this configuration.
+     * To be used for WPA3-Enterprise mode.
+     * See {@link SuiteBCipher} for descriptions of the values.
+     */
+    public BitSet allowedSuiteBCiphers;
+    /**
      * The enterprise configuration details specifying the EAP method,
      * certificates and other settings associated with the EAP.
      */
@@ -733,7 +822,8 @@
     public boolean isOpenNetwork() {
         final int cardinality = allowedKeyManagement.cardinality();
         final boolean hasNoKeyMgmt = cardinality == 0
-                || (cardinality == 1 && allowedKeyManagement.get(KeyMgmt.NONE));
+                || (cardinality == 1 && (allowedKeyManagement.get(KeyMgmt.NONE)
+                || allowedKeyManagement.get(KeyMgmt.OWE)));
 
         boolean hasNoWepKeys = true;
         if (wepKeys != null) {
@@ -785,6 +875,23 @@
 
     /**
      * @hide
+     * Use factory MAC when connecting to this network
+     */
+    public static final int RANDOMIZATION_NONE = 0;
+    /**
+     * @hide
+     * Generate a randomized MAC once and reuse it for all connections to this network
+     */
+    public static final int RANDOMIZATION_PERSISTENT = 1;
+
+    /**
+     * @hide
+     * Level of MAC randomization for this network
+     */
+    public int macRandomizationSetting = RANDOMIZATION_PERSISTENT;
+
+    /**
+     * @hide
      * Randomized MAC address to use with this particular network
      */
     @NonNull
@@ -1521,6 +1628,8 @@
         allowedAuthAlgorithms = new BitSet();
         allowedPairwiseCiphers = new BitSet();
         allowedGroupCiphers = new BitSet();
+        allowedGroupMgmtCiphers = new BitSet();
+        allowedSuiteBCiphers = new BitSet();
         wepKeys = new String[4];
         for (int i = 0; i < wepKeys.length; i++) {
             wepKeys[i] = null;
@@ -1574,7 +1683,8 @@
     @UnsupportedAppUsage
     public boolean isEnterprise() {
         return (allowedKeyManagement.get(KeyMgmt.WPA_EAP)
-                || allowedKeyManagement.get(KeyMgmt.IEEE8021X))
+                || allowedKeyManagement.get(KeyMgmt.IEEE8021X)
+                || allowedKeyManagement.get(KeyMgmt.SUITE_B_192))
                 && enterpriseConfig != null
                 && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE;
     }
@@ -1592,6 +1702,7 @@
                 append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN)
                 .append(" PRIO: ").append(this.priority)
                 .append(" HIDDEN: ").append(this.hiddenSSID)
+                .append(" PMF: ").append(this.requirePMF)
                 .append('\n');
 
 
@@ -1645,6 +1756,7 @@
         if (this.meteredOverride != METERED_OVERRIDE_NONE) {
             sbuf.append(" meteredOverride ").append(meteredOverride).append("\n");
         }
+        sbuf.append(" macRandomizationSetting ").append(macRandomizationSetting).append("\n");
         sbuf.append(" KeyMgmt:");
         for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
             if (this.allowedKeyManagement.get(k)) {
@@ -1703,10 +1815,35 @@
                 }
             }
         }
-        sbuf.append('\n').append(" PSK: ");
+        sbuf.append('\n');
+        sbuf.append(" GroupMgmtCiphers:");
+        for (int gmc = 0; gmc < this.allowedGroupMgmtCiphers.size(); gmc++) {
+            if (this.allowedGroupMgmtCiphers.get(gmc)) {
+                sbuf.append(" ");
+                if (gmc < GroupMgmtCipher.strings.length) {
+                    sbuf.append(GroupMgmtCipher.strings[gmc]);
+                } else {
+                    sbuf.append("??");
+                }
+            }
+        }
+        sbuf.append('\n');
+        sbuf.append(" SuiteBCiphers:");
+        for (int sbc = 0; sbc < this.allowedSuiteBCiphers.size(); sbc++) {
+            if (this.allowedSuiteBCiphers.get(sbc)) {
+                sbuf.append(" ");
+                if (sbc < SuiteBCipher.strings.length) {
+                    sbuf.append(SuiteBCipher.strings[sbc]);
+                } else {
+                    sbuf.append("??");
+                }
+            }
+        }
+        sbuf.append('\n').append(" PSK/SAE: ");
         if (this.preSharedKey != null) {
             sbuf.append('*');
         }
+
         sbuf.append("\nEnterprise config:\n");
         sbuf.append(enterpriseConfig);
 
@@ -1869,6 +2006,12 @@
             return KeyMgmt.WPA_EAP;
         } else if (allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
             return KeyMgmt.IEEE8021X;
+        } else if (allowedKeyManagement.get(KeyMgmt.SAE)) {
+            return KeyMgmt.SAE;
+        } else if (allowedKeyManagement.get(KeyMgmt.OWE)) {
+            return KeyMgmt.OWE;
+        } else if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
+            return KeyMgmt.SUITE_B_192;
         }
         return KeyMgmt.NONE;
     }
@@ -1900,6 +2043,12 @@
                 key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
             } else if (wepKeys[0] != null) {
                 key = SSID + "WEP";
+            } else if (allowedKeyManagement.get(KeyMgmt.OWE)) {
+                key = SSID + KeyMgmt.strings[KeyMgmt.OWE];
+            } else if (allowedKeyManagement.get(KeyMgmt.SAE)) {
+                key = SSID + KeyMgmt.strings[KeyMgmt.SAE];
+            } else if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
+                key = SSID + KeyMgmt.strings[KeyMgmt.SUITE_B_192];
             } else {
                 key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
             }
@@ -2068,6 +2217,8 @@
             allowedAuthAlgorithms  = (BitSet) source.allowedAuthAlgorithms.clone();
             allowedPairwiseCiphers = (BitSet) source.allowedPairwiseCiphers.clone();
             allowedGroupCiphers    = (BitSet) source.allowedGroupCiphers.clone();
+            allowedGroupMgmtCiphers    = (BitSet) source.allowedGroupMgmtCiphers.clone();
+            allowedSuiteBCiphers    = (BitSet) source.allowedSuiteBCiphers.clone();
             enterpriseConfig = new WifiEnterpriseConfig(source.enterpriseConfig);
 
             defaultGwMacAddress = source.defaultGwMacAddress;
@@ -2109,6 +2260,8 @@
             shared = source.shared;
             recentFailure.setAssociationStatus(source.recentFailure.getAssociationStatus());
             mRandomizedMacAddress = source.mRandomizedMacAddress;
+            macRandomizationSetting = source.macRandomizationSetting;
+            requirePMF = source.requirePMF;
         }
     }
 
@@ -2144,6 +2297,8 @@
         writeBitSet(dest, allowedAuthAlgorithms);
         writeBitSet(dest, allowedPairwiseCiphers);
         writeBitSet(dest, allowedGroupCiphers);
+        writeBitSet(dest, allowedGroupMgmtCiphers);
+        writeBitSet(dest, allowedSuiteBCiphers);
 
         dest.writeParcelable(enterpriseConfig, flags);
 
@@ -2173,6 +2328,7 @@
         dest.writeString(mPasspointManagementObjectTree);
         dest.writeInt(recentFailure.getAssociationStatus());
         dest.writeParcelable(mRandomizedMacAddress, flags);
+        dest.writeInt(macRandomizationSetting);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -2211,6 +2367,8 @@
                 config.allowedAuthAlgorithms  = readBitSet(in);
                 config.allowedPairwiseCiphers = readBitSet(in);
                 config.allowedGroupCiphers    = readBitSet(in);
+                config.allowedGroupMgmtCiphers = readBitSet(in);
+                config.allowedSuiteBCiphers   = readBitSet(in);
 
                 config.enterpriseConfig = in.readParcelable(null);
                 config.setIpConfiguration(in.readParcelable(null));
@@ -2239,6 +2397,7 @@
                 config.mPasspointManagementObjectTree = in.readString();
                 config.recentFailure.setAssociationStatus(in.readInt());
                 config.mRandomizedMacAddress = in.readParcelable(null);
+                config.macRandomizationSetting = in.readInt();
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 9adbe67..954b51f 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -25,19 +25,17 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
 import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.ProvisioningCallback;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -52,7 +50,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
-import com.android.server.net.NetworkPinner;
 
 import dalvik.system.CloseGuard;
 
@@ -1035,7 +1032,17 @@
      * </ul>
      * @return a list of network configurations in the form of a list
      * of {@link WifiConfiguration} objects.
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return an empty list.
      */
+    @Deprecated
     public List<WifiConfiguration> getConfiguredNetworks() {
         try {
             ParceledListSlice<WifiConfiguration> parceledList =
@@ -1135,7 +1142,17 @@
      * @return the ID of the newly created network description. This is used in
      *         other operations to specified the network to be acted upon.
      *         Returns {@code -1} on failure.
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return {@code -1}.
      */
+    @Deprecated
     public int addNetwork(WifiConfiguration config) {
         if (config == null) {
             return -1;
@@ -1160,7 +1177,17 @@
      *         Returns {@code -1} on failure, including when the {@code networkId}
      *         field of the {@code WifiConfiguration} does not refer to an
      *         existing network.
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return {@code -1}.
      */
+    @Deprecated
     public int updateNetwork(WifiConfiguration config) {
         if (config == null || config.networkId < 0) {
             return -1;
@@ -1185,6 +1212,301 @@
     }
 
     /**
+     * Interface for indicating user selection from the list of networks presented in the
+     * {@link NetworkRequestMatchCallback#onMatch(List)}.
+     *
+     * The platform will implement this callback and pass it along with the
+     * {@link NetworkRequestMatchCallback#onUserSelectionCallbackRegistration(
+     * NetworkRequestUserSelectionCallback)}. The UI component handling
+     * {@link NetworkRequestMatchCallback} will invoke {@link #select(WifiConfiguration)} or
+     * {@link #reject()} to return the user's selection back to the platform via this callback.
+     * @hide
+     */
+    @SystemApi
+    public interface NetworkRequestUserSelectionCallback {
+        /**
+         * User selected this network to connect to.
+         * @param wifiConfiguration WifiConfiguration object corresponding to the network
+         *                          user selected.
+         */
+        void select(@NonNull WifiConfiguration wifiConfiguration);
+
+        /**
+         * User rejected the app's request.
+         */
+        void reject();
+    }
+
+    /**
+     * Interface for network request callback. Should be implemented by applications and passed when
+     * calling {@link #registerNetworkRequestMatchCallback(NetworkRequestMatchCallback, Handler)}.
+     *
+     * This is meant to be implemented by a UI component to present the user with a list of networks
+     * matching the app's request. The user is allowed to pick one of these networks to connect to
+     * or reject the request by the app.
+     * @hide
+     */
+    @SystemApi
+    public interface NetworkRequestMatchCallback {
+        /**
+         * Invoked to register a callback to be invoked to convey user selection. The callback
+         * object paased in this method is to be invoked by the UI component after the service sends
+         * a list of matching scan networks using {@link #onMatch(List)} and user picks a network
+         * from that list.
+         *
+         * @param userSelectionCallback Callback object to send back the user selection.
+         */
+        void onUserSelectionCallbackRegistration(
+                @NonNull NetworkRequestUserSelectionCallback userSelectionCallback);
+
+        /**
+         * Invoked when a network request initiated by an app matches some networks in scan results.
+         * This may be invoked multiple times for a single network request as the platform finds new
+         * networks in scan results.
+         *
+         * @param wifiConfigurations List of {@link WifiConfiguration} objects corresponding to the
+         *                           networks matching the request.
+         */
+        void onMatch(@NonNull List<WifiConfiguration> wifiConfigurations);
+
+        /**
+         * Invoked on a successful connection with the network that the user selected
+         * via {@link NetworkRequestUserSelectionCallback}.
+         *
+         * @param wifiConfiguration WifiConfiguration object corresponding to the network that the
+         *                          user selected.
+         */
+        void onUserSelectionConnectSuccess(@NonNull WifiConfiguration wifiConfiguration);
+
+        /**
+         * Invoked on failure to establish connection with the network that the user selected
+         * via {@link NetworkRequestUserSelectionCallback}.
+         *
+         * @param wifiConfiguration WifiConfiguration object corresponding to the network
+         *                          user selected.
+         */
+        void onUserSelectionConnectFailure(@NonNull WifiConfiguration wifiConfiguration);
+    }
+
+    /**
+     * Callback proxy for NetworkRequestUserSelectionCallback objects.
+     * @hide
+     */
+    private class NetworkRequestUserSelectionCallbackProxy implements
+            NetworkRequestUserSelectionCallback {
+        private final INetworkRequestUserSelectionCallback mCallback;
+
+        NetworkRequestUserSelectionCallbackProxy(
+                INetworkRequestUserSelectionCallback callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        public void select(@NonNull WifiConfiguration wifiConfiguration) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "NetworkRequestUserSelectionCallbackProxy: select "
+                        + "wificonfiguration: " + wifiConfiguration);
+            }
+            try {
+                mCallback.select(wifiConfiguration);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to invoke onSelected", e);
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        @Override
+        public void reject() {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "NetworkRequestUserSelectionCallbackProxy: reject");
+            }
+            try {
+                mCallback.reject();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to invoke onRejected", e);
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Callback proxy for NetworkRequestMatchCallback objects.
+     * @hide
+     */
+    private class NetworkRequestMatchCallbackProxy extends INetworkRequestMatchCallback.Stub {
+        private final Handler mHandler;
+        private final NetworkRequestMatchCallback mCallback;
+
+        NetworkRequestMatchCallbackProxy(Looper looper, NetworkRequestMatchCallback callback) {
+            mHandler = new Handler(looper);
+            mCallback = callback;
+        }
+
+        @Override
+        public void onUserSelectionCallbackRegistration(
+                INetworkRequestUserSelectionCallback userSelectionCallback) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "NetworkRequestMatchCallbackProxy: "
+                        + "onUserSelectionCallbackRegistration callback: " + userSelectionCallback);
+            }
+            mHandler.post(() -> {
+                mCallback.onUserSelectionCallbackRegistration(
+                        new NetworkRequestUserSelectionCallbackProxy(userSelectionCallback));
+            });
+        }
+
+        @Override
+        public void onMatch(List<WifiConfiguration> wifiConfigurations) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "NetworkRequestMatchCallbackProxy: onMatch wificonfigurations: "
+                        + wifiConfigurations);
+            }
+            mHandler.post(() -> {
+                mCallback.onMatch(wifiConfigurations);
+            });
+        }
+
+        @Override
+        public void onUserSelectionConnectSuccess(WifiConfiguration wifiConfiguration) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "NetworkRequestMatchCallbackProxy: onUserSelectionConnectSuccess "
+                        + " wificonfiguration: " + wifiConfiguration);
+            }
+            mHandler.post(() -> {
+                mCallback.onUserSelectionConnectSuccess(wifiConfiguration);
+            });
+        }
+
+        @Override
+        public void onUserSelectionConnectFailure(WifiConfiguration wifiConfiguration) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "NetworkRequestMatchCallbackProxy: onUserSelectionConnectFailure"
+                        + " wificonfiguration: " + wifiConfiguration);
+            }
+            mHandler.post(() -> {
+                mCallback.onUserSelectionConnectFailure(wifiConfiguration);
+            });
+        }
+    }
+
+    /**
+     * Registers a callback for NetworkRequest matches. See {@link NetworkRequestMatchCallback}.
+     * Caller can unregister a previously registered callback using
+     * {@link #unregisterNetworkRequestMatchCallback(NetworkRequestMatchCallback)}
+     * <p>
+     * Applications should have the
+     * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. Callers
+     * without the permission will trigger a {@link java.lang.SecurityException}.
+     * <p>
+     *
+     * @param callback Callback for network match events
+     * @param handler  The Handler on whose thread to execute the callbacks of the {@code callback}
+     *                 object. If null, then the application's main thread will be used.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void registerNetworkRequestMatchCallback(@NonNull NetworkRequestMatchCallback callback,
+                                                    @Nullable Handler handler) {
+        if (callback == null) throw new IllegalArgumentException("callback cannot be null");
+        Log.v(TAG, "registerNetworkRequestMatchCallback: callback=" + callback
+                + ", handler=" + handler);
+
+        Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
+        Binder binder = new Binder();
+        try {
+            mService.registerNetworkRequestMatchCallback(
+                    binder, new NetworkRequestMatchCallbackProxy(looper, callback),
+                    callback.hashCode());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters a callback for NetworkRequest matches. See {@link NetworkRequestMatchCallback}.
+     * <p>
+     * Applications should have the
+     * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. Callers
+     * without the permission will trigger a {@link java.lang.SecurityException}.
+     * <p>
+     *
+     * @param callback Callback for network match events
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void unregisterNetworkRequestMatchCallback(
+            @NonNull NetworkRequestMatchCallback callback) {
+        if (callback == null) throw new IllegalArgumentException("callback cannot be null");
+        Log.v(TAG, "unregisterNetworkRequestMatchCallback: callback=" + callback);
+
+        try {
+            mService.unregisterNetworkRequestMatchCallback(callback.hashCode());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Provide a list of network suggestions to the device. See {@link WifiNetworkSuggestion}
+     * for a detailed explanation of the parameters.
+     *<p>
+     * When the device decides to connect to one of the provided network suggestions, platform fires
+     * the associated {@code pendingIntent} if
+     * the network was created with {@link WifiNetworkConfigBuilder#setIsAppInteractionRequired()}
+     * flag set and the provided {@code pendingIntent} is non-null.
+     *<p>
+     * Registration of a non-null pending intent {@code pendingIntent} requires
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission.
+     *<p>
+     * NOTE:
+     * <li> These networks are just a suggestion to the platform. The platform will ultimately
+     * decide on which network the device connects to. </li>
+     * <li> When an app is uninstalled, all its suggested networks are discarded. If the device is
+     * currently connected to a suggested network which is being removed then the device will
+     * disconnect from that network.</li>
+     * <li> No in-place modification of existing suggestions are allowed. Apps are expected to
+     * remove suggestions using {@link #removeNetworkSuggestions(List)} and then add the modified
+     * suggestion back using this API.</li>
+     *
+     * @param networkSuggestions List of network suggestions provided by the app.
+     * @param pendingIntent Pending intent to be fired post connection for networks. These will be
+     *                      fired only when connecting to a network that was created with
+     *                      {@link WifiNetworkConfigBuilder#setIsAppInteractionRequired()} flag set.
+     *                      Pending intent must hold a foreground service, else will be rejected.
+     * @return true on success, false if any of the suggestions match (See
+     * {@link WifiNetworkSuggestion#equals(Object)} any previously provided suggestions by the app.
+     * @throws {@link SecurityException} if the caller is missing required permissions.
+     */
+    public boolean addNetworkSuggestions(
+            @NonNull List<WifiNetworkSuggestion> networkSuggestions,
+            @Nullable PendingIntent pendingIntent) {
+        // TODO(b/115504887): Implementation
+        return false;
+    }
+
+
+    /**
+     * Remove a subset of or all of networks from previously provided suggestions by the app to the
+     * device.
+     * See {@link WifiNetworkSuggestion} for a detailed explanation of the parameters.
+     * See {@link WifiNetworkSuggestion#equals(Object)} for the equivalence evaluation used.
+     *
+     * @param networkSuggestions List of network suggestions to be removed. Pass an empty list
+     *                           to remove all the previous suggestions provided by the app.
+     * @return true on success, false if any of the suggestions do not match any suggestions
+     * previously provided by the app. Any matching suggestions are removed from the device and
+     * will not be considered for any further connection attempts.
+     */
+    public boolean removeNetworkSuggestions(
+            @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
+        // TODO(b/115504887): Implementation
+        return false;
+    }
+
+    /**
      * Add or update a Passpoint configuration.  The configuration provides a credential
      * for connecting to Passpoint networks that are operated by the Passpoint
      * service provider specified in the configuration.
@@ -1299,7 +1621,17 @@
      * @param netId the ID of the network as returned by {@link #addNetwork} or {@link
      *        #getConfiguredNetworks}.
      * @return {@code true} if the operation succeeded
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false.
      */
+    @Deprecated
     public boolean removeNetwork(int netId) {
         try {
             return mService.removeNetwork(netId, mContext.getOpPackageName());
@@ -1314,10 +1646,8 @@
      * network is initiated. This may result in the asynchronous delivery
      * of state change events.
      * <p>
-     * <b>Note:</b> If an application's target SDK version is
-     * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or newer, network
-     * communication may not use Wi-Fi even if Wi-Fi is connected; traffic may
-     * instead be sent through another network, such as cellular data,
+     * <b>Note:</b> Network communication may not use Wi-Fi even if Wi-Fi is connected;
+     * traffic may instead be sent through another network, such as cellular data,
      * Bluetooth tethering, or Ethernet. For example, traffic will never use a
      * Wi-Fi network that does not provide Internet access (e.g. a wireless
      * printer), if another network that does offer Internet access (e.g.
@@ -1335,29 +1665,24 @@
      * @param attemptConnect The way to select a particular network to connect to is specify
      *        {@code true} for this parameter.
      * @return {@code true} if the operation succeeded
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false.
      */
+    @Deprecated
     public boolean enableNetwork(int netId, boolean attemptConnect) {
-        final boolean pin = attemptConnect && mTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP;
-        if (pin) {
-            NetworkRequest request = new NetworkRequest.Builder()
-                    .clearCapabilities()
-                    .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
-                    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
-                    .build();
-            NetworkPinner.pin(mContext, request);
-        }
-
         boolean success;
         try {
             success = mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-
-        if (pin && !success) {
-            NetworkPinner.unpin();
-        }
-
         return success;
     }
 
@@ -1372,7 +1697,17 @@
      * @param netId the ID of the network as returned by {@link #addNetwork} or {@link
      *        #getConfiguredNetworks}.
      * @return {@code true} if the operation succeeded
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false.
      */
+    @Deprecated
     public boolean disableNetwork(int netId) {
         try {
             return mService.disableNetwork(netId, mContext.getOpPackageName());
@@ -1385,7 +1720,17 @@
      * Disassociate from the currently active access point. This may result
      * in the asynchronous delivery of state change events.
      * @return {@code true} if the operation succeeded
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false.
      */
+    @Deprecated
     public boolean disconnect() {
         try {
             mService.disconnect(mContext.getOpPackageName());
@@ -1400,7 +1745,17 @@
      * disconnected. This may result in the asynchronous delivery of state
      * change events.
      * @return {@code true} if the operation succeeded
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false.
      */
+    @Deprecated
     public boolean reconnect() {
         try {
             mService.reconnect(mContext.getOpPackageName());
@@ -1415,7 +1770,17 @@
      * connected. This may result in the asynchronous delivery of state
      * change events.
      * @return {@code true} if the operation succeeded
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false.
      */
+    @Deprecated
     public boolean reassociate() {
         try {
             mService.reassociate(mContext.getOpPackageName());
@@ -1490,7 +1855,12 @@
     public static final int WIFI_FEATURE_SCAN_RAND        = 0x2000000; // Random MAC & Probe seq
     /** @hide */
     public static final int WIFI_FEATURE_TX_POWER_LIMIT   = 0x4000000; // Set Tx power limit
-
+    /** @hide */
+    public static final int WIFI_FEATURE_WPA3_SAE         = 0x8000000; // WPA3-Personal SAE
+    /** @hide */
+    public static final int WIFI_FEATURE_WPA3_SUITE_B     = 0x10000000; // WPA3-Enterprise Suite-B
+    /** @hide */
+    public static final int WIFI_FEATURE_OWE              = 0x20000000; // Enhanced Open
 
     private int getSupportedFeatures() {
         try {
@@ -1715,7 +2085,10 @@
      * even when Wi-Fi is turned off.
      *
      * To change this setting, see {@link #ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE}.
+     * @deprecated The ability for apps to trigger scan requests will be removed in a future
+     * release.
      */
+    @Deprecated
     public boolean isScanAlwaysAvailable() {
         try {
             return mService.isScanAlwaysAvailable();
@@ -1821,7 +2194,12 @@
      * @return {@code false} if the request cannot be satisfied; {@code true} indicates that wifi is
      *         either already in the requested state, or in progress toward the requested state.
      * @throws  {@link java.lang.SecurityException} if the caller is missing required permissions.
+     *
+     * @deprecated Starting with Build.VERSION_CODES#Q, applications are not allowed to
+     * enable/disable Wi-Fi regardless of application's target SDK. This API will have no effect
+     * and will always return false.
      */
+    @Deprecated
     public boolean setWifiEnabled(boolean enabled) {
         try {
             return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
@@ -3875,4 +4253,31 @@
     private void updateVerboseLoggingEnabledFromService() {
         mVerboseLoggingEnabled = getVerboseLoggingLevel() > 0;
     }
+
+    /**
+     * @return true if this device supports WPA3-Personal SAE
+     * @hide
+     */
+    @SystemApi
+    public boolean isWpa3SaeSupported() {
+        return isFeatureSupported(WIFI_FEATURE_WPA3_SAE);
+    }
+
+    /**
+     * @return true if this device supports WPA3-Enterprise Suite-B-192
+     * @hide
+     */
+    @SystemApi
+    public boolean isWpa3SuiteBSupported() {
+        return isFeatureSupported(WIFI_FEATURE_WPA3_SUITE_B);
+    }
+
+    /**
+     * @return true if this device supports Wi-Fi Enhanced Open (OWE)
+     * @hide
+     */
+    @SystemApi
+    public boolean isOweSupported() {
+        return isFeatureSupported(WIFI_FEATURE_OWE);
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
new file mode 100644
index 0000000..55fde4ca
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2018 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 android.net.wifi;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.Preconditions.checkState;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.MacAddress;
+import android.net.MatchAllNetworkSpecifier;
+import android.net.NetworkAgent;
+import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Network specifier object used by wifi's {@link android.net.NetworkAgent}.
+ * @hide
+ */
+public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements Parcelable {
+    /**
+     * Security credentials for the currently connected network.
+     */
+    private final WifiConfiguration mWifiConfiguration;
+
+    /**
+     * The UID of the app that requested a specific wifi network using {@link WifiNetworkSpecifier}.
+     *
+     * Will only be filled when the device connects to a wifi network as a result of a
+     * {@link NetworkRequest} with {@link WifiNetworkSpecifier}. Will be set to -1 if the device
+     * auto-connected to a wifi network.
+     */
+    private final int mOriginalRequestorUid;
+
+    public WifiNetworkAgentSpecifier(@NonNull WifiConfiguration wifiConfiguration,
+                                     int originalRequestorUid) {
+        checkNotNull(wifiConfiguration);
+
+        mWifiConfiguration = wifiConfiguration;
+        mOriginalRequestorUid = originalRequestorUid;
+    }
+
+    /**
+     * @hide
+     */
+    public static final Creator<WifiNetworkAgentSpecifier> CREATOR =
+            new Creator<WifiNetworkAgentSpecifier>() {
+                @Override
+                public WifiNetworkAgentSpecifier createFromParcel(@NonNull Parcel in) {
+                    WifiConfiguration wifiConfiguration = in.readParcelable(null);
+                    int originalRequestorUid = in.readInt();
+                    return new WifiNetworkAgentSpecifier(wifiConfiguration, originalRequestorUid);
+                }
+
+                @Override
+                public WifiNetworkAgentSpecifier[] newArray(int size) {
+                    return new WifiNetworkAgentSpecifier[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeParcelable(mWifiConfiguration, flags);
+        dest.writeInt(mOriginalRequestorUid);
+    }
+
+    @Override
+    public boolean satisfiedBy(@Nullable NetworkSpecifier other) {
+        if (this == other) {
+            return true;
+        }
+        // Any generic requests should be satisifed by a specific wifi network.
+        if (other == null || other instanceof MatchAllNetworkSpecifier) {
+            return true;
+        }
+        if (other instanceof WifiNetworkSpecifier) {
+            return satisfiesNetworkSpecifier((WifiNetworkSpecifier) other);
+        }
+        if (other instanceof WifiNetworkAgentSpecifier) {
+            throw new IllegalStateException("WifiNetworkAgentSpecifier instances should never be "
+                    + "compared");
+        }
+        return false;
+    }
+
+    /**
+     * Match {@link WifiNetworkSpecifier} in app's {@link NetworkRequest} with the
+     * {@link WifiNetworkAgentSpecifier} in wifi platform's {@link NetworkAgent}.
+     */
+    public boolean satisfiesNetworkSpecifier(@NonNull WifiNetworkSpecifier ns) {
+        // None of these should be null by construction.
+        // {@link WifiNetworkConfigBuilder} enforces non-null in {@link WifiNetworkSpecifier}.
+        // {@link WifiNetworkFactory} ensures non-null in {@link WifiNetworkAgentSpecifier}.
+        checkNotNull(ns);
+        checkNotNull(ns.ssidPatternMatcher);
+        checkNotNull(ns.bssidPatternMatcher);
+        checkNotNull(ns.wifiConfiguration.allowedKeyManagement);
+        checkNotNull(this.mWifiConfiguration.SSID);
+        checkNotNull(this.mWifiConfiguration.BSSID);
+        checkNotNull(this.mWifiConfiguration.allowedKeyManagement);
+
+        final String ssidWithQuotes = this.mWifiConfiguration.SSID;
+        checkState(ssidWithQuotes.startsWith("\"") && ssidWithQuotes.endsWith("\""));
+        final String ssidWithoutQuotes = ssidWithQuotes.substring(1, ssidWithQuotes.length() - 1);
+        if (!ns.ssidPatternMatcher.match(ssidWithoutQuotes)) {
+            return false;
+        }
+        final MacAddress bssid = MacAddress.fromString(this.mWifiConfiguration.BSSID);
+        final MacAddress matchBaseAddress = ns.bssidPatternMatcher.first;
+        final MacAddress matchMask = ns.bssidPatternMatcher.second;
+        if (!bssid.matches(matchBaseAddress, matchMask))  {
+            return false;
+        }
+        if (!ns.wifiConfiguration.allowedKeyManagement.equals(
+                this.mWifiConfiguration.allowedKeyManagement)) {
+            return false;
+        }
+        if (ns.requestorUid != this.mOriginalRequestorUid) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mWifiConfiguration.SSID,
+                mWifiConfiguration.BSSID,
+                mWifiConfiguration.allowedKeyManagement,
+                mOriginalRequestorUid);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof WifiNetworkAgentSpecifier)) {
+            return false;
+        }
+        WifiNetworkAgentSpecifier lhs = (WifiNetworkAgentSpecifier) obj;
+        return Objects.equals(this.mWifiConfiguration.SSID, lhs.mWifiConfiguration.SSID)
+                && Objects.equals(this.mWifiConfiguration.BSSID, lhs.mWifiConfiguration.BSSID)
+                && Objects.equals(this.mWifiConfiguration.allowedKeyManagement,
+                    lhs.mWifiConfiguration.allowedKeyManagement)
+                && mOriginalRequestorUid == lhs.mOriginalRequestorUid;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("WifiNetworkAgentSpecifier [");
+        sb.append(", WifiConfiguration=").append(
+                mWifiConfiguration == null ? null : mWifiConfiguration.configKey())
+                .append(", mOriginalRequestorUid=").append(mOriginalRequestorUid)
+                .append("]");
+        return sb.toString();
+    }
+
+    @Override
+    public void assertValidFromUid(int requestorUid) {
+        throw new IllegalStateException("WifiNetworkAgentSpecifier should never be used "
+                + "for requests.");
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
new file mode 100644
index 0000000..67e2189
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2018 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 android.net.wifi;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.net.MacAddress;
+import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
+import android.os.PatternMatcher;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.Pair;
+
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * WifiNetworkConfigBuilder to use for creating Wi-Fi network configuration.
+ * <li>See {@link #buildNetworkSpecifier()} for creating a network specifier to use in
+ * {@link NetworkRequest}.</li>
+ * <li>See {@link #buildNetworkSuggestion()} for creating a network suggestion to use in
+ * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)}.</li>
+ */
+public class WifiNetworkConfigBuilder {
+    private static final String MATCH_ALL_SSID_PATTERN_PATH = ".*";
+    private static final String MATCH_EMPTY_SSID_PATTERN_PATH = "";
+    private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN =
+            new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS);
+    private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN =
+            new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+    private static final MacAddress MATCH_EXACT_BSSID_PATTERN_MASK =
+            MacAddress.BROADCAST_ADDRESS;
+    private static final int UNASSIGNED_PRIORITY = -1;
+
+    /**
+     * SSID pattern match specified by the app.
+     */
+    private @Nullable PatternMatcher mSsidPatternMatcher;
+    /**
+     * BSSID pattern match specified by the app.
+     * Pair of <BaseAddress, Mask>.
+     */
+    private @Nullable Pair<MacAddress, MacAddress> mBssidPatternMatcher;
+    /**
+     * Pre-shared key for use with WPA-PSK networks.
+     */
+    private @Nullable String mPskPassphrase;
+    /**
+     * The enterprise configuration details specifying the EAP method,
+     * certificates and other settings associated with the EAP.
+     */
+    private @Nullable WifiEnterpriseConfig mEnterpriseConfig;
+    /**
+     * This is a network that does not broadcast its SSID, so an
+     * SSID-specific probe request must be used for scans.
+     */
+    private boolean mIsHiddenSSID;
+    /**
+     * Whether app needs to log in to captive portal to obtain Internet access.
+     */
+    private boolean mIsAppInteractionRequired;
+    /**
+     * Whether user needs to log in to captive portal to obtain Internet access.
+     */
+    private boolean mIsUserInteractionRequired;
+    /**
+     * Whether this network is metered or not.
+     */
+    private boolean mIsMetered;
+    /**
+     * Priority of this network among other network suggestions provided by the app.
+     * The lower the number, the higher the priority (i.e value of 0 = highest priority).
+     */
+    private int mPriority;
+
+    public WifiNetworkConfigBuilder() {
+        mSsidPatternMatcher = null;
+        mBssidPatternMatcher = null;
+        mPskPassphrase = null;
+        mEnterpriseConfig = null;
+        mIsHiddenSSID = false;
+        mIsAppInteractionRequired = false;
+        mIsUserInteractionRequired = false;
+        mIsMetered = false;
+        mPriority = UNASSIGNED_PRIORITY;
+    }
+
+    /**
+     * Set the unicode SSID match pattern to use for filtering networks from scan results.
+     * <p>
+     * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li>
+     * <li>Overrides any previous value set using {@link #setSsid(String)} or
+     * {@link #setSsidPattern(PatternMatcher)}.</li>
+     *
+     * @param ssidPattern Instance of {@link PatternMatcher} containing the UTF-8 encoded
+     *                    string pattern to use for matching the network's SSID.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setSsidPattern(@NonNull PatternMatcher ssidPattern) {
+        checkNotNull(ssidPattern);
+        mSsidPatternMatcher = ssidPattern;
+        return this;
+    }
+
+    /**
+     * Set the unicode SSID for the network.
+     * <p>
+     * <li>For network requests ({@link NetworkSpecifier}), built using
+     * {@link #buildNetworkSpecifier}, sets the SSID to use for filtering networks from scan
+     * results. Will only match networks whose SSID is identical to the UTF-8 encoding of the
+     * specified value.</li>
+     * <li>For network suggestions ({@link WifiNetworkSuggestion}), built using
+     * {@link #buildNetworkSuggestion()}, sets the SSID for the network.</li>
+     * <li>Overrides any previous value set using {@link #setSsid(String)} or
+     * {@link #setSsidPattern(PatternMatcher)}.</li>
+     *
+     * @param ssid The SSID of the network. It must be valid Unicode.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     * @throws IllegalArgumentException if the SSID is not valid unicode.
+     */
+    public WifiNetworkConfigBuilder setSsid(@NonNull String ssid) {
+        checkNotNull(ssid);
+        final CharsetEncoder unicodeEncoder = StandardCharsets.UTF_8.newEncoder();
+        if (!unicodeEncoder.canEncode(ssid)) {
+            throw new IllegalArgumentException("SSID is not a valid unicode string");
+        }
+        mSsidPatternMatcher = new PatternMatcher(ssid, PatternMatcher.PATTERN_LITERAL);
+        return this;
+    }
+
+    /**
+     * Set the BSSID match pattern to use for filtering networks from scan results.
+     * Will match all networks with BSSID which satisfies the following:
+     * {@code BSSID & mask == baseAddress}.
+     * <p>
+     * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li>
+     * <li>Overrides any previous value set using {@link #setBssid(MacAddress)} or
+     * {@link #setBssidPattern(MacAddress, MacAddress)}.</li>
+     *
+     * @param baseAddress Base address for BSSID pattern.
+     * @param mask Mask for BSSID pattern.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setBssidPattern(
+            @NonNull MacAddress baseAddress, @NonNull MacAddress mask) {
+        checkNotNull(baseAddress, mask);
+        mBssidPatternMatcher = Pair.create(baseAddress, mask);
+        return this;
+    }
+
+    /**
+     * Set the BSSID to use for filtering networks from scan results. Will only match network whose
+     * BSSID is identical to the specified value.
+     * <p>
+     * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li>
+     * <li>Overrides any previous value set using {@link #setBssid(MacAddress)} or
+     * {@link #setBssidPattern(MacAddress, MacAddress)}.</li>
+     *
+     * @param bssid BSSID of the network.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setBssid(@NonNull MacAddress bssid) {
+        checkNotNull(bssid);
+        mBssidPatternMatcher = Pair.create(bssid, MATCH_EXACT_BSSID_PATTERN_MASK);
+        return this;
+    }
+
+    /**
+     * Set the ASCII PSK passphrase for this network. Needed for authenticating to
+     * WPA_PSK networks.
+     *
+     * @param pskPassphrase PSK passphrase of the network.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     * @throws IllegalArgumentException if the passphrase is not ASCII encodable.
+     */
+    public WifiNetworkConfigBuilder setPskPassphrase(@NonNull String pskPassphrase) {
+        checkNotNull(pskPassphrase);
+        final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
+        if (!asciiEncoder.canEncode(pskPassphrase)) {
+            throw new IllegalArgumentException("passphrase not ASCII encodable");
+        }
+        mPskPassphrase = pskPassphrase;
+        return this;
+    }
+
+    /**
+     * Set the associated enterprise configuration for this network. Needed for authenticating to
+     * WPA_EAP networks. See {@link WifiEnterpriseConfig} for description.
+     *
+     * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setEnterpriseConfig(
+            @NonNull WifiEnterpriseConfig enterpriseConfig) {
+        checkNotNull(enterpriseConfig);
+        mEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+        return this;
+    }
+
+    /**
+     * Specifies whether this represents a hidden network.
+     * <p>
+     * <li>For network requests (see {@link NetworkSpecifier}), built using
+     * {@link #buildNetworkSpecifier}, setting this disallows the usage of
+     * {@link #setSsidPattern(PatternMatcher)} since hidden networks need to be explicitly
+     * probed for.</li>
+     * <li>If not set, defaults to false (i.e not a hidden network).</li>
+     *
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setIsHiddenSsid() {
+        mIsHiddenSSID = true;
+        return this;
+    }
+
+    /**
+     * Specifies whether the app needs to log in to a captive portal to obtain Internet access.
+     * <p>
+     * This will dictate if the associated pending intent in
+     * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)} will be sent after
+     * successfully connecting to the network.
+     * Use this for captive portal type networks where the app needs to authenticate the user
+     * before the device can access the network.
+     * This setting will be ignored if the {@code PendingIntent} used to add this network
+     * suggestion is null.
+     * <p>
+     * <li>Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.</li>
+     * <li>If not set, defaults to false (i.e no app interaction required).</li>
+     *
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setIsAppInteractionRequired() {
+        mIsAppInteractionRequired = true;
+        return this;
+    }
+
+    /**
+     * Specifies whether the user needs to log in to a captive portal to obtain Internet access.
+     * <p>
+     * <li>Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.</li>
+     * <li>If not set, defaults to false (i.e no user interaction required).</li>
+     *
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setIsUserInteractionRequired() {
+        mIsUserInteractionRequired = true;
+        return this;
+    }
+
+    /**
+     * Specify the priority of this network among other network suggestions provided by the same app
+     * (priorities have no impact on suggestions by different apps). The lower the number, the
+     * higher the priority (i.e value of 0 = highest priority).
+     * <p>
+     * <li>Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.</li>
+     * <li>If not set, defaults to -1 (i.e unassigned priority).</li>
+     *
+     * @param priority Integer number representing the priority among suggestions by the app.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     * @throws IllegalArgumentException if the priority value is negative.
+     */
+    public WifiNetworkConfigBuilder setPriority(int priority) {
+        if (priority < 0) {
+            throw new IllegalArgumentException("Invalid priority value " + priority);
+        }
+        mPriority = priority;
+        return this;
+    }
+
+    /**
+     * Specifies whether this network is metered.
+     * <p>
+     * <li>Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.</li>
+     * <li>If not set, defaults to false (i.e not metered).</li>
+     *
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setIsMetered() {
+        mIsMetered = true;
+        return this;
+    }
+
+    /**
+     * Set defaults for the various low level credential type fields in the newly created
+     * WifiConfiguration object.
+     *
+     * See {@link com.android.server.wifi.WifiConfigManager#setDefaultsInWifiConfiguration(
+     * WifiConfiguration)}.
+     *
+     * @param configuration provided WifiConfiguration object.
+     */
+    private static void setDefaultsInWifiConfiguration(@NonNull WifiConfiguration configuration) {
+        configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
+        configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+        configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
+        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
+    }
+
+    private void setKeyMgmtInWifiConfiguration(@NonNull WifiConfiguration configuration) {
+        if (!TextUtils.isEmpty(mPskPassphrase)) {
+            // WPA_PSK network.
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        } else if (mEnterpriseConfig != null) {
+            // WPA_EAP network
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+        } else {
+            // Open network
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        }
+    }
+
+    /**
+     * Helper method to build WifiConfiguration object from the builder.
+     * @return Instance of {@link WifiConfiguration}.
+     */
+    private WifiConfiguration buildWifiConfiguration() {
+        final WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        setDefaultsInWifiConfiguration(wifiConfiguration);
+        // WifiConfiguration.SSID needs quotes around unicode SSID.
+        if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
+            wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\"";
+        }
+        setKeyMgmtInWifiConfiguration(wifiConfiguration);
+        // WifiConfiguration.preSharedKey needs quotes around ASCII password.
+        if (mPskPassphrase != null) {
+            wifiConfiguration.preSharedKey = "\"" + mPskPassphrase + "\"";
+        }
+        wifiConfiguration.enterpriseConfig = mEnterpriseConfig;
+        wifiConfiguration.hiddenSSID = mIsHiddenSSID;
+        wifiConfiguration.priority = mPriority;
+        wifiConfiguration.meteredOverride =
+                mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED
+                           : WifiConfiguration.METERED_OVERRIDE_NONE;
+        return wifiConfiguration;
+    }
+
+    private boolean hasSetAnyPattern() {
+        return mSsidPatternMatcher != null || mBssidPatternMatcher != null;
+    }
+
+    private void setMatchAnyPatternIfUnset() {
+        if (mSsidPatternMatcher == null) {
+            mSsidPatternMatcher = new PatternMatcher(MATCH_ALL_SSID_PATTERN_PATH,
+                    PatternMatcher.PATTERN_SIMPLE_GLOB);
+        }
+        if (mBssidPatternMatcher == null) {
+            mBssidPatternMatcher = MATCH_ALL_BSSID_PATTERN;
+        }
+    }
+
+    private boolean hasSetMatchNonePattern() {
+        if (mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_PREFIX
+                && mSsidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) {
+            return true;
+        }
+        if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean hasSetMatchAllPattern() {
+        if ((mSsidPatternMatcher.match(MATCH_EMPTY_SSID_PATTERN_PATH))
+                && mBssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Create a specifier object used to request a Wi-Fi network. The generated
+     * {@link NetworkSpecifier} should be used in
+     * {@link NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} when building
+     * the {@link NetworkRequest}.
+     *<p>
+     * Note: Apps can set a combination of network match params:
+     * <li> SSID Pattern using {@link #setSsidPattern(PatternMatcher)} OR Specific SSID using
+     * {@link #setSsid(String)}. </li>
+     * AND/OR
+     * <li> BSSID Pattern using {@link #setBssidPattern(MacAddress, MacAddress)} OR Specific BSSID
+     * using {@link #setBssid(MacAddress)} </li>
+     * to trigger connection to a network that matches the set params.
+     * The system will find the set of networks matching the request and present the user
+     * with a system dialog which will allow the user to select a specific Wi-Fi network to connect
+     * to or to deny the request.
+     *</p>
+     *
+     * For example:
+     * To connect to an open network with a SSID prefix of "test" and a BSSID OUI of "10:03:23":
+     * {@code
+     * final NetworkSpecifier specifier =
+     *      new WifiNetworkConfigBuilder()
+     *      .setSsidPattern(new PatternMatcher("test", PatterMatcher.PATTERN_PREFIX))
+     *      .setBssidPattern(MacAddress.fromString("10:03:23:00:00:00"),
+     *                       MacAddress.fromString("ff:ff:ff:00:00:00"))
+     *      .buildNetworkSpecifier()
+     * final NetworkRequest request =
+     *      new NetworkRequest.Builder()
+     *      .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+     *      .setNetworkSpecifier(specifier)
+     *      .build();
+     * final ConnectivityManager connectivityManager =
+     *      context.getSystemService(Context.CONNECTIVITY_SERVICE);
+     * final NetworkCallback networkCallback = new NetworkCallback() {
+     *      ...
+     *      {@literal @}Override
+     *      void onAvailable(...) {}
+     *      // etc.
+     * };
+     * connectivityManager.requestNetwork(request, networkCallback);
+     * }
+     *
+     * @return Instance of {@link NetworkSpecifier}.
+     * @throws IllegalStateException on invalid params set.
+     */
+    public NetworkSpecifier buildNetworkSpecifier() {
+        if (!hasSetAnyPattern()) {
+            throw new IllegalStateException("one of setSsidPattern/setSsid/setBssidPattern/setBssid"
+                    + " should be invoked for specifier");
+        }
+        setMatchAnyPatternIfUnset();
+        if (hasSetMatchNonePattern()) {
+            throw new IllegalStateException("cannot set match-none pattern for specifier");
+        }
+        if (hasSetMatchAllPattern()) {
+            throw new IllegalStateException("cannot set match-all pattern for specifier");
+        }
+        if (mIsHiddenSSID && mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_LITERAL) {
+            throw new IllegalStateException("setSsid should also be invoked when "
+                    + "setIsHiddenSsid is invoked for network specifier");
+        }
+        if (mIsAppInteractionRequired || mIsUserInteractionRequired
+                || mPriority != -1 || mIsMetered) {
+            throw new IllegalStateException("none of setIsAppInteractionRequired/"
+                    + "setIsUserInteractionRequired/setPriority/setIsMetered are allowed for "
+                    + "specifier");
+        }
+        if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
+            throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
+                    + " be invoked for network specifier");
+        }
+
+        return new WifiNetworkSpecifier(
+                mSsidPatternMatcher,
+                mBssidPatternMatcher,
+                buildWifiConfiguration(),
+                Process.myUid());
+    }
+
+    /**
+     * Create a network suggestion object use in
+     * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)}.
+     * See {@link WifiNetworkSuggestion}.
+     *
+     * @return Instance of {@link WifiNetworkSuggestion}.
+     * @throws IllegalStateException on invalid params set.
+     */
+    public WifiNetworkSuggestion buildNetworkSuggestion() {
+        if (mSsidPatternMatcher == null) {
+            throw new IllegalStateException("setSsid should be invoked for suggestion");
+        }
+        if (mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_LITERAL
+                || mBssidPatternMatcher != null) {
+            throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are"
+                    + " allowed for suggestion");
+        }
+        if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
+            throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
+                    + "be invoked for suggestion");
+        }
+
+        return new WifiNetworkSuggestion(
+                buildWifiConfiguration(),
+                mIsAppInteractionRequired,
+                mIsUserInteractionRequired,
+                Process.myUid());
+
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
new file mode 100644
index 0000000..4348399
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018 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 android.net.wifi;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.net.MacAddress;
+import android.net.MatchAllNetworkSpecifier;
+import android.net.NetworkSpecifier;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+import android.util.Pair;
+
+import java.util.Objects;
+
+/**
+ * Network specifier object used to request a Wi-Fi network. Apps should use the
+ * {@link WifiNetworkConfigBuilder} class to create an instance.
+ * @hide
+ */
+public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+    /**
+     * SSID pattern match specified by the app.
+     */
+    public final PatternMatcher ssidPatternMatcher;
+
+    /**
+     * BSSID pattern match specified by the app.
+     * Pair of <BaseAddress, Mask>.
+     */
+    public final Pair<MacAddress, MacAddress> bssidPatternMatcher;
+
+    /**
+     * Security credentials for the network.
+     * <p>
+     * Note: {@link WifiConfiguration#SSID} & {@link WifiConfiguration#BSSID} fields from
+     * WifiConfiguration are not used. Instead we use the {@link #ssidPatternMatcher} &
+     * {@link #bssidPatternMatcher} fields embedded directly
+     * within {@link WifiNetworkSpecifier}.
+     */
+    public final WifiConfiguration wifiConfiguration;
+
+    /**
+     * The UID of the process initializing this network specifier. Validated by receiver using
+     * checkUidIfNecessary() and is used by satisfiedBy() to determine whether the specifier
+     * matches the offered network.
+     */
+    public final int requestorUid;
+
+    public WifiNetworkSpecifier(@NonNull PatternMatcher ssidPatternMatcher,
+                 @NonNull Pair<MacAddress, MacAddress> bssidPatternMatcher,
+                 @NonNull WifiConfiguration wifiConfiguration,
+                 int requestorUid) {
+        checkNotNull(ssidPatternMatcher);
+        checkNotNull(bssidPatternMatcher);
+        checkNotNull(wifiConfiguration);
+
+        this.ssidPatternMatcher = ssidPatternMatcher;
+        this.bssidPatternMatcher = bssidPatternMatcher;
+        this.wifiConfiguration = wifiConfiguration;
+        this.requestorUid = requestorUid;
+    }
+
+    public static final Creator<WifiNetworkSpecifier> CREATOR =
+            new Creator<WifiNetworkSpecifier>() {
+                @Override
+                public WifiNetworkSpecifier createFromParcel(Parcel in) {
+                    PatternMatcher ssidPatternMatcher = in.readParcelable(/* classLoader */null);
+                    MacAddress baseAddress = in.readParcelable(null);
+                    MacAddress mask = in.readParcelable(null);
+                    Pair<MacAddress, MacAddress> bssidPatternMatcher =
+                            Pair.create(baseAddress, mask);
+                    WifiConfiguration wifiConfiguration = in.readParcelable(null);
+                    int requestorUid = in.readInt();
+                    return new WifiNetworkSpecifier(ssidPatternMatcher, bssidPatternMatcher,
+                            wifiConfiguration, requestorUid);
+                }
+
+                @Override
+                public WifiNetworkSpecifier[] newArray(int size) {
+                    return new WifiNetworkSpecifier[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(ssidPatternMatcher, flags);
+        dest.writeParcelable(bssidPatternMatcher.first, flags);
+        dest.writeParcelable(bssidPatternMatcher.second, flags);
+        dest.writeParcelable(wifiConfiguration, flags);
+        dest.writeInt(requestorUid);
+    }
+
+    @Override
+    public boolean satisfiedBy(NetworkSpecifier other) {
+        if (this == other) {
+            return true;
+        }
+        // Any generic requests should be satisifed by a specific wifi network.
+        if (other == null || other instanceof MatchAllNetworkSpecifier) {
+            return true;
+        }
+        if (other instanceof WifiNetworkAgentSpecifier) {
+            return ((WifiNetworkAgentSpecifier) other).satisfiesNetworkSpecifier(this);
+        }
+        // Specific requests are checked for equality although testing for equality of 2 patterns do
+        // not make much sense!
+        return equals(other);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                ssidPatternMatcher.getPath(),
+                ssidPatternMatcher.getType(),
+                bssidPatternMatcher,
+                wifiConfiguration.allowedKeyManagement,
+                requestorUid);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof WifiNetworkSpecifier)) {
+            return false;
+        }
+        WifiNetworkSpecifier lhs = (WifiNetworkSpecifier) obj;
+        return Objects.equals(this.ssidPatternMatcher.getPath(),
+                    lhs.ssidPatternMatcher.getPath())
+                && Objects.equals(this.ssidPatternMatcher.getType(),
+                    lhs.ssidPatternMatcher.getType())
+                && Objects.equals(this.bssidPatternMatcher,
+                    lhs.bssidPatternMatcher)
+                && Objects.equals(this.wifiConfiguration.allowedKeyManagement,
+                    lhs.wifiConfiguration.allowedKeyManagement)
+                && requestorUid == lhs.requestorUid;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append("WifiNetworkSpecifierWifiNetworkSpecifier [")
+                .append(", SSID Match pattern=").append(ssidPatternMatcher)
+                .append(", BSSID Match pattern=").append(bssidPatternMatcher)
+                .append(", WifiConfiguration=").append(
+                wifiConfiguration == null ? null : wifiConfiguration.configKey())
+                .append(", requestorUid=").append(requestorUid)
+                .append("]")
+                .toString();
+    }
+
+    @Override
+    public void assertValidFromUid(int requestorUid) {
+        if (this.requestorUid != requestorUid) {
+            throw new SecurityException("mismatched UIDs");
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
new file mode 100644
index 0000000..04b9cb5
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 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 android.net.wifi;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.app.PendingIntent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The Network Suggestion object is used to provide a Wi-Fi network for consideration when
+ * auto-connecting to networks. Apps cannot directly create this object, they must use
+ * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} to obtain an instance
+ * of this object.
+ *<p>
+ * Apps can provide a list of such networks to the platform using
+ * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)}.
+ */
+public final class WifiNetworkSuggestion implements Parcelable {
+    /**
+     * Network configuration for the provided network.
+     * @hide
+     */
+    public final WifiConfiguration wifiConfiguration;
+
+    /**
+     * Whether app needs to log in to captive portal to obtain Internet access.
+     * This will dictate if the associated pending intent in
+     * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)} needs to be sent after
+     * successfully connecting to the network.
+     * @hide
+     */
+    public final boolean isAppInteractionRequired;
+
+    /**
+     * Whether user needs to log in to captive portal to obtain Internet access.
+     * @hide
+     */
+    public final boolean isUserInteractionRequired;
+
+    /**
+     * The UID of the process initializing this network suggestion.
+     * @hide
+     */
+    public final int suggestorUid;
+
+    /** @hide */
+    public WifiNetworkSuggestion(WifiConfiguration wifiConfiguration,
+                                 boolean isAppInteractionRequired,
+                                 boolean isUserInteractionRequired,
+                                 int suggestorUid) {
+        checkNotNull(wifiConfiguration);
+
+        this.wifiConfiguration = wifiConfiguration;
+        this.isAppInteractionRequired = isAppInteractionRequired;
+        this.isUserInteractionRequired = isUserInteractionRequired;
+        this.suggestorUid = suggestorUid;
+    }
+
+    public static final Creator<WifiNetworkSuggestion> CREATOR =
+            new Creator<WifiNetworkSuggestion>() {
+                @Override
+                public WifiNetworkSuggestion createFromParcel(Parcel in) {
+                    return new WifiNetworkSuggestion(
+                            in.readParcelable(null), // wifiConfiguration
+                            in.readBoolean(), // isAppInteractionRequired
+                            in.readBoolean(), // isUserInteractionRequired
+                            in.readInt() // suggestorUid
+                    );
+                }
+
+                @Override
+                public WifiNetworkSuggestion[] newArray(int size) {
+                    return new WifiNetworkSuggestion[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(wifiConfiguration, flags);
+        dest.writeBoolean(isAppInteractionRequired);
+        dest.writeBoolean(isUserInteractionRequired);
+        dest.writeInt(suggestorUid);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.allowedKeyManagement,
+                suggestorUid);
+    }
+
+    /**
+     * Equals for network suggestions.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof WifiNetworkSuggestion)) {
+            return false;
+        }
+        WifiNetworkSuggestion lhs = (WifiNetworkSuggestion) obj;
+        return Objects.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID)
+                && Objects.equals(this.wifiConfiguration.allowedKeyManagement,
+                                  lhs.wifiConfiguration.allowedKeyManagement)
+                && suggestorUid == lhs.suggestorUid;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("WifiNetworkSuggestion [")
+                .append(", WifiConfiguration=").append(wifiConfiguration)
+                .append(", isAppInteractionRequired=").append(isAppInteractionRequired)
+                .append(", isUserInteractionRequired=").append(isUserInteractionRequired)
+                .append(", suggestorUid=").append(suggestorUid)
+                .append("]");
+        return sb.toString();
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 045291b..529548f 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -594,17 +594,17 @@
             /** SSID of the network */
             public String ssid;
             /** Bitmask of the FLAG_XXX */
-            public byte flags;
+            public byte flags = 0;
             /** Bitmask of the ATUH_XXX */
-            public byte authBitField;
+            public byte authBitField = 0;
+            /** frequencies on which the particular network needs to be scanned for */
+            public int[] frequencies = {};
 
             /**
              * default constructor for PnoNetwork
              */
             public PnoNetwork(String ssid) {
                 this.ssid = ssid;
-                flags = 0;
-                authBitField = 0;
             }
         }
 
@@ -651,6 +651,7 @@
                     dest.writeString(networkList[i].ssid);
                     dest.writeByte(networkList[i].flags);
                     dest.writeByte(networkList[i].authBitField);
+                    dest.writeIntArray(networkList[i].frequencies);
                 }
             } else {
                 dest.writeInt(0);
@@ -677,6 +678,7 @@
                             PnoNetwork network = new PnoNetwork(ssid);
                             network.flags = in.readByte();
                             network.authBitField = in.readByte();
+                            network.frequencies = in.createIntArray();
                             settings.networkList[i] = network;
                         }
                         return settings;
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index 4b76526..5c9db53 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -160,9 +160,9 @@
     public static final int OSU_STATUS_AP_CONNECTED = 2;
 
     /**
-     * The status code for provisioning flow to indicate the server connection is completed.
+     * The status code for provisioning flow to indicate connecting to the server.
      */
-    public static final int OSU_STATUS_SERVER_CONNECTED = 3;
+    public static final int OSU_STATUS_SERVER_CONNECTING = 3;
 
     /**
      * The status code for provisioning flow to indicate the server certificate is validated.
@@ -170,9 +170,9 @@
     public static final int OSU_STATUS_SERVER_VALIDATED = 4;
 
     /**
-     * The status code for provisioning flow to indicate the service provider is verified.
+     * The status code for provisioning flow to indicate the server is connected
      */
-    public static final int OSU_STATUS_SERVICE_PROVIDER_VERIFIED = 5;
+    public static final int OSU_STATUS_SERVER_CONNECTED = 5;
 
     /**
      * The status code for provisioning flow to indicate starting the first SOAP exchange.
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 5a4c898..e6892be 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -16,9 +16,13 @@
 
 package android.net.wifi.p2p;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
-import android.annotation.SystemService;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.net.wifi.WpsInfo;
@@ -480,6 +484,12 @@
     /** @hide */
     public static final int REPORT_NFC_HANDOVER_FAILED              = BASE + 81;
 
+    /** @hide */
+    public static final int FACTORY_RESET                           = BASE + 82;
+    /** @hide */
+    public static final int FACTORY_RESET_FAILED                    = BASE + 83;
+    /** @hide */
+    public static final int FACTORY_RESET_SUCCEEDED                 = BASE + 84;
 
     /**
      * Create a new WifiP2pManager instance. Applications use
@@ -776,6 +786,7 @@
                     case STOP_LISTEN_FAILED:
                     case SET_CHANNEL_FAILED:
                     case REPORT_NFC_HANDOVER_FAILED:
+                    case FACTORY_RESET_FAILED:
                         if (listener != null) {
                             ((ActionListener) listener).onFailure(message.arg1);
                         }
@@ -802,6 +813,7 @@
                     case STOP_LISTEN_SUCCEEDED:
                     case SET_CHANNEL_SUCCEEDED:
                     case REPORT_NFC_HANDOVER_SUCCEEDED:
+                    case FACTORY_RESET_SUCCEEDED:
                         if (listener != null) {
                             ((ActionListener) listener).onSuccess();
                         }
@@ -1521,4 +1533,21 @@
         c.mAsyncChannel.sendMessage(RESPONDER_REPORT_NFC_HANDOVER, 0,
                 c.putListener(listener), bundle);
     }
+
+    /**
+     * Removes all saved p2p groups.
+     * @param c is the channel created at {@link #initialize}.
+     * @param listener for callback on success or failure. Can be null.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void factoryReset(@NonNull Channel c, @Nullable ActionListener listener) {
+        checkChannel(c);
+        Bundle callingPackage = new Bundle();
+        callingPackage.putString(CALLING_PACKAGE, c.mContext.getOpPackageName());
+        c.mAsyncChannel.sendMessage(FACTORY_RESET, 0, c.putListener(listener),
+                callingPackage);
+    }
+
 }
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
new file mode 100644
index 0000000..eede23b
--- /dev/null
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -0,0 +1,415 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") {
+ *  throw new UnsupportedOperationException();
+ }
+ * 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.server.wifi;
+
+import android.content.pm.ParceledListSlice;
+import android.net.DhcpInfo;
+import android.net.Network;
+import android.net.wifi.INetworkRequestMatchCallback;
+import android.net.wifi.ISoftApCallback;
+import android.net.wifi.ITrafficStateCallback;
+import android.net.wifi.IWifiManager;
+import android.net.wifi.PasspointManagementObjectDefinition;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiActivityEnergyInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.hotspot2.IProvisioningCallback;
+import android.net.wifi.hotspot2.OsuProvider;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.os.IBinder;
+import android.os.Messenger;
+import android.os.ResultReceiver;
+import android.os.WorkSource;
+import android.util.Slog;
+
+import java.util.List;
+
+/**
+ * Abstract class implementing IWifiManager with stub methods throwing runtime exceptions.
+ *
+ * This class is meant to be extended by real implementations of IWifiManager in order to facilitate
+ * cross-repo changes to WiFi internal APIs, including the introduction of new APIs, the removal of
+ * deprecated APIs, or the migration of existing API signatures.
+ *
+ * When an existing API is scheduled for removal, it can be removed from IWifiManager.aidl
+ * immediately and marked as @Deprecated first in this class. Children inheriting this class are
+ * then given a short grace period to update themselves before the @Deprecated stub is removed for
+ * good. If the API scheduled for removal has a replacement or an overload (signature change),
+ * these should be introduced before the stub is removed to allow children to migrate.
+ */
+public abstract class AbstractWifiService extends IWifiManager.Stub {
+
+    private static final String TAG = AbstractWifiService.class.getSimpleName();
+
+    @Override
+    public int getSupportedFeatures() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public WifiActivityEnergyInfo reportActivityInfo() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void requestActivityInfo(ResultReceiver result) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ParceledListSlice getConfiguredNetworks() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ParceledListSlice getPrivilegedConfiguredNetworks() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<WifiConfiguration> getAllMatchingWifiConfigs(ScanResult scanResult) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int addOrUpdateNetwork(WifiConfiguration config, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean addOrUpdatePasspointConfiguration(
+            PasspointConfiguration config, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean removePasspointConfiguration(String fqdn, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<PasspointConfiguration> getPasspointConfigurations() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void queryPasspointIcon(long bssid, String fileName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int matchProviderWithCurrentNetwork(String fqdn) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void deauthenticateNetwork(long holdoff, boolean ess) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean removeNetwork(int netId, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean enableNetwork(int netId, boolean disableOthers, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean disableNetwork(int netId, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean startScan(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<ScanResult> getScanResults(String callingPackage) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void disconnect(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void reconnect(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void reassociate(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public WifiInfo getConnectionInfo(String callingPackage) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean setWifiEnabled(String packageName, boolean enable) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getWifiEnabledState() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setCountryCode(String country) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getCountryCode() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isDualBandSupported() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean needs5GHzToAnyApBandConversion() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public DhcpInfo getDhcpInfo() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isScanAlwaysAvailable() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean acquireWifiLock(IBinder lock, int lockType, String tag, WorkSource ws) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean releaseWifiLock(IBinder lock) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void initializeMulticastFiltering() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isMulticastEnabled() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void acquireMulticastLock(IBinder binder, String tag) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void releaseMulticastLock(String tag) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void updateInterfaceIpState(String ifaceName, int mode) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean startSoftAp(WifiConfiguration wifiConfig) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean stopSoftAp() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int startLocalOnlyHotspot(Messenger messenger, IBinder binder, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void stopLocalOnlyHotspot() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void startWatchLocalOnlyHotspot(Messenger messenger, IBinder binder) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void stopWatchLocalOnlyHotspot() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getWifiApEnabledState() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public WifiConfiguration getWifiApConfiguration() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean setWifiApConfiguration(WifiConfiguration wifiConfig, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void notifyUserOfApBandConversion(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Messenger getWifiServiceMessenger(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void enableTdls(String remoteIPAddress, boolean enable) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getCurrentNetworkWpsNfcConfigurationToken() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void enableVerboseLogging(int verbose) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getVerboseLoggingLevel() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void enableWifiConnectivityManager(boolean enabled) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void disableEphemeralNetwork(String SSID, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void factoryReset(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Network getCurrentNetwork() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public byte[] retrieveBackupData() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void restoreBackupData(byte[] data) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void startSubscriptionProvisioning(
+            OsuProvider provider, IProvisioningCallback callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void registerSoftApCallback(
+            IBinder binder, ISoftApCallback callback, int callbackIdentifier) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void unregisterSoftApCallback(int callbackIdentifier) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void registerTrafficStateCallback(
+            IBinder binder, ITrafficStateCallback callback, int callbackIdentifier) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void unregisterTrafficStateCallback(int callbackIdentifier) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void registerNetworkRequestMatchCallback(
+            IBinder binder, INetworkRequestMatchCallback callback, int callbackIdentifier) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void unregisterNetworkRequestMatchCallback(int callbackIdentifier) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 3b9f93e..5f3e1b2 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -155,7 +155,10 @@
     @Test
     public void testIsOpenNetwork_NotOpen_HasAuthType() {
         for (int keyMgmt = 0; keyMgmt < WifiConfiguration.KeyMgmt.strings.length; keyMgmt++) {
-            if (keyMgmt == WifiConfiguration.KeyMgmt.NONE) continue;
+            if (keyMgmt == WifiConfiguration.KeyMgmt.NONE
+                    || keyMgmt == WifiConfiguration.KeyMgmt.OWE) {
+                continue;
+            }
             WifiConfiguration config = new WifiConfiguration();
             config.allowedKeyManagement.clear();
             config.allowedKeyManagement.set(keyMgmt);
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index e40b657a..ea41bb3 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -43,6 +43,8 @@
 import android.net.wifi.WifiManager.LocalOnlyHotspotObserver;
 import android.net.wifi.WifiManager.LocalOnlyHotspotReservation;
 import android.net.wifi.WifiManager.LocalOnlyHotspotSubscription;
+import android.net.wifi.WifiManager.NetworkRequestMatchCallback;
+import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
 import android.net.wifi.WifiManager.SoftApCallback;
 import android.net.wifi.WifiManager.TrafficStateCallback;
 import android.os.Handler;
@@ -59,6 +61,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+
 /**
  * Unit tests for {@link android.net.wifi.WifiManager}.
  */
@@ -67,16 +71,19 @@
 
     private static final int ERROR_NOT_SET = -1;
     private static final int ERROR_TEST_REASON = 5;
+    private static final int TEST_UID = 14553;
     private static final String TEST_PACKAGE_NAME = "TestPackage";
     private static final String TEST_COUNTRY_CODE = "US";
 
     @Mock Context mContext;
-    @Mock IWifiManager mWifiService;
+    @Mock
+    android.net.wifi.IWifiManager mWifiService;
     @Mock ApplicationInfo mApplicationInfo;
     @Mock WifiConfiguration mApConfig;
     @Mock IBinder mAppBinder;
     @Mock SoftApCallback mSoftApCallback;
     @Mock TrafficStateCallback mTrafficStateCallback;
+    @Mock NetworkRequestMatchCallback mNetworkRequestMatchCallback;
 
     private Handler mHandler;
     private TestLooper mLooper;
@@ -1163,4 +1170,84 @@
         assertEquals(1, altLooper.dispatchAll());
         verify(mTrafficStateCallback).onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT);
     }
+
+    /**
+     * Verify the call to registerNetworkRequestMatchCallback goes to WifiServiceImpl.
+     */
+    @Test
+    public void registerNetworkRequestMatchCallbackCallGoesToWifiServiceImpl()
+            throws Exception {
+        when(mContext.getMainLooper()).thenReturn(mLooper.getLooper());
+        ArgumentCaptor<INetworkRequestMatchCallback.Stub> callbackCaptor =
+                ArgumentCaptor.forClass(INetworkRequestMatchCallback.Stub.class);
+        mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, null);
+        verify(mWifiService).registerNetworkRequestMatchCallback(
+                any(IBinder.class), callbackCaptor.capture(), anyInt());
+
+        INetworkRequestUserSelectionCallback iUserSelectionCallback =
+                mock(INetworkRequestUserSelectionCallback.class);
+
+        assertEquals(0, mLooper.dispatchAll());
+        callbackCaptor.getValue().onMatch(new ArrayList<WifiConfiguration>());
+        assertEquals(1, mLooper.dispatchAll());
+        verify(mNetworkRequestMatchCallback).onMatch(anyList());
+
+        callbackCaptor.getValue().onUserSelectionConnectSuccess(new WifiConfiguration());
+        assertEquals(1, mLooper.dispatchAll());
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectSuccess(
+                any(WifiConfiguration.class));
+
+        callbackCaptor.getValue().onUserSelectionConnectFailure(new WifiConfiguration());
+        assertEquals(1, mLooper.dispatchAll());
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectFailure(
+                any(WifiConfiguration.class));
+    }
+
+    /**
+     * Verify the call to unregisterNetworkRequestMatchCallback goes to WifiServiceImpl.
+     */
+    @Test
+    public void unregisterNetworkRequestMatchCallbackCallGoesToWifiServiceImpl() throws Exception {
+        ArgumentCaptor<Integer> callbackIdentifier = ArgumentCaptor.forClass(Integer.class);
+        mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, mHandler);
+        verify(mWifiService).registerNetworkRequestMatchCallback(
+                any(IBinder.class), any(INetworkRequestMatchCallback.class),
+                callbackIdentifier.capture());
+
+        mWifiManager.unregisterNetworkRequestMatchCallback(mNetworkRequestMatchCallback);
+        verify(mWifiService).unregisterNetworkRequestMatchCallback(
+                eq((int) callbackIdentifier.getValue()));
+    }
+
+    /**
+     * Verify the call to NetworkRequestUserSelectionCallback goes to
+     * WifiServiceImpl.
+     */
+    @Test
+    public void networkRequestUserSelectionCallbackCallGoesToWifiServiceImpl()
+            throws Exception {
+        when(mContext.getMainLooper()).thenReturn(mLooper.getLooper());
+        ArgumentCaptor<INetworkRequestMatchCallback.Stub> callbackCaptor =
+                ArgumentCaptor.forClass(INetworkRequestMatchCallback.Stub.class);
+        mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, null);
+        verify(mWifiService).registerNetworkRequestMatchCallback(
+                any(IBinder.class), callbackCaptor.capture(), anyInt());
+
+        INetworkRequestUserSelectionCallback iUserSelectionCallback =
+                mock(INetworkRequestUserSelectionCallback.class);
+        ArgumentCaptor<NetworkRequestUserSelectionCallback> userSelectionCallbackCaptor =
+                ArgumentCaptor.forClass(NetworkRequestUserSelectionCallback.class);
+        callbackCaptor.getValue().onUserSelectionCallbackRegistration(
+                iUserSelectionCallback);
+        assertEquals(1, mLooper.dispatchAll());
+        verify(mNetworkRequestMatchCallback).onUserSelectionCallbackRegistration(
+                userSelectionCallbackCaptor.capture());
+
+        WifiConfiguration selected = new WifiConfiguration();
+        userSelectionCallbackCaptor.getValue().select(selected);
+        verify(iUserSelectionCallback).select(selected);
+
+        userSelectionCallbackCaptor.getValue().reject();
+        verify(iUserSelectionCallback).reject();
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
new file mode 100644
index 0000000..1b0007c
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2018 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 android.net.wifi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.MacAddress;
+import android.net.MatchAllNetworkSpecifier;
+import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
+import android.os.Parcel;
+import android.os.PatternMatcher;
+import android.support.test.filters.SmallTest;
+import android.util.Pair;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiNetworkAgentSpecifier}.
+ */
+@SmallTest
+public class WifiNetworkAgentSpecifierTest {
+    private static final int TEST_UID = 5;
+    private static final int TEST_UID_1 = 8;
+    private static final String TEST_SSID = "Test123";
+    private static final String TEST_SSID_PATTERN = "Test";
+    private static final String TEST_SSID_1 = "456test";
+    private static final String TEST_BSSID = "12:12:12:aa:0b:c0";
+    private static final String TEST_BSSID_OUI_BASE_ADDRESS = "12:12:12:00:00:00";
+    private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00";
+    private static final String TEST_BSSID_1 = "aa:cc:12:aa:0b:c0";
+    private static final String TEST_PRESHARED_KEY = "\"Test123\"";
+
+    /**
+     * Validate that parcel marshalling/unmarshalling works
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierParcel() {
+        WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier();
+
+        Parcel parcelW = Parcel.obtain();
+        specifier.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        WifiNetworkAgentSpecifier parcelSpecifier =
+                WifiNetworkAgentSpecifier.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(specifier, parcelSpecifier);
+    }
+
+    /**
+     * Validate that the NetworkAgentSpecifier cannot be used in a {@link NetworkRequest} by apps.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkAgentSpecifierNotUsedInNetworkRequest() {
+        WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier();
+
+        specifier.assertValidFromUid(TEST_UID);
+    }
+
+    /**
+     * Validate NetworkAgentSpecifier equals with itself.
+     * a) Create network agent specifier 1 for WPA_PSK network
+     * b) Create network agent specifier 2 with the same params as specifier 1.
+     * c) Ensure that the specifier 2 equals specifier 1.
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierEqualsSame() {
+        WifiNetworkAgentSpecifier specifier1 = createDefaultNetworkAgentSpecifier();
+        WifiNetworkAgentSpecifier specifier2 = createDefaultNetworkAgentSpecifier();
+
+        assertTrue(specifier2.equals(specifier1));
+    }
+
+    /**
+     * Validate NetworkAgentSpecifier equals between instances of {@link WifiNetworkAgentSpecifier}.
+     * a) Create network agent specifier 1 for WPA_PSK network
+     * b) Create network agent specifier 2 with different key mgmt params.
+     * c) Ensure that the specifier 2 does not equal specifier 1.
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierDoesNotEqualsWhenKeyMgmtDifferent() {
+        WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration();
+        WifiNetworkAgentSpecifier specifier1 =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfiguration1,
+                        TEST_UID);
+
+        WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
+        wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkAgentSpecifier specifier2 =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfiguration2,
+                        TEST_UID);
+
+        assertFalse(specifier2.equals(specifier1));
+    }
+
+    /**
+     * Validate NetworkAgentSpecifier equals between instances of {@link WifiNetworkAgentSpecifier}.
+     * a) Create network agent specifier 1 for WPA_PSK network
+     * b) Create network agent specifier 2 with different SSID.
+     * c) Ensure that the specifier 2 does not equal specifier 1.
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierDoesNotSatisifyWhenSsidDifferent() {
+        WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration();
+        WifiNetworkAgentSpecifier specifier1 =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfiguration1,
+                        TEST_UID);
+
+        WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
+        wifiConfiguration2.SSID = TEST_SSID_1;
+        WifiNetworkAgentSpecifier specifier2 =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfiguration2,
+                        TEST_UID);
+
+        assertFalse(specifier2.equals(specifier1));
+    }
+
+    /**
+     * Validate NetworkAgentSpecifier equals between instances of {@link WifiNetworkAgentSpecifier}.
+     * a) Create network agent specifier 1 for WPA_PSK network
+     * b) Create network agent specifier 2 with different BSSID.
+     * c) Ensure that the specifier 2 does not equal specifier 1.
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierDoesNotSatisifyWhenBssidDifferent() {
+        WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration();
+        WifiNetworkAgentSpecifier specifier1 =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfiguration1,
+                        TEST_UID);
+
+        WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
+        wifiConfiguration2.BSSID = TEST_BSSID_1;
+        WifiNetworkAgentSpecifier specifier2 =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfiguration2,
+                        TEST_UID);
+
+        assertFalse(specifier2.equals(specifier1));
+    }
+
+    /**
+     * Validate NetworkAgentSpecifier matching.
+     * a) Create a network agent specifier for WPA_PSK network
+     * b) Ensure that the specifier matches {@code null} and {@link MatchAllNetworkSpecifier}
+     * specifiers.
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierSatisifiesNullAndAllMatch() {
+        WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier();
+
+        assertTrue(specifier.satisfiedBy(null));
+        assertTrue(specifier.satisfiedBy(new MatchAllNetworkSpecifier()));
+    }
+
+    /**
+     * Validate NetworkAgentSpecifier matching with itself.
+     * a) Create network agent specifier 1 for WPA_PSK network
+     * b) Create network agent specifier 2 with the same params as specifier 1.
+     * c) Ensure that invoking {@link NetworkSpecifier#satisfiedBy(NetworkSpecifier)} on 2
+     * {@link WifiNetworkAgentSpecifier} throws an exception.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkAgentSpecifierDoesNotSatisifySame() {
+        WifiNetworkAgentSpecifier specifier1 = createDefaultNetworkAgentSpecifier();
+        WifiNetworkAgentSpecifier specifier2 = createDefaultNetworkAgentSpecifier();
+
+        assertTrue(specifier2.satisfiedBy(specifier1));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with matching SSID pattern.
+     * c) Ensure that the agent specifier is satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierSatisfiesNetworkSpecifierWithSsidPattern() {
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier();
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with matching BSSID pattern.
+     * c) Ensure that the agent specifier is satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierSatisfiesNetworkSpecifierWithBssidPattern() {
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier();
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with matching SSID & BSSID pattern.
+     * c) Ensure that the agent specifier is satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierSatisfiesNetworkSpecifierWithSsidAndBssidPattern() {
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier();
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with non-matching SSID pattern.
+     * c) Ensure that the agent specifier is not satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithSsidPattern() {
+        WifiConfiguration wifiConfigurationNetworkAgent = createDefaultWifiConfiguration();
+        wifiConfigurationNetworkAgent.SSID = "\"" + TEST_SSID_1 + "\"";
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfigurationNetworkAgent,
+                        TEST_UID);
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with non-matching BSSID pattern.
+     * c) Ensure that the agent specifier is not satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithBssidPattern() {
+        WifiConfiguration wifiConfigurationNetworkAgent = createDefaultWifiConfiguration();
+        wifiConfigurationNetworkAgent.BSSID = TEST_BSSID_1;
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfigurationNetworkAgent,
+                        TEST_UID);
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with non-matching SSID and BSSID pattern.
+     * c) Ensure that the agent specifier is not satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithSsidAndBssidPattern() {
+        WifiConfiguration wifiConfigurationNetworkAgent = createDefaultWifiConfiguration();
+        wifiConfigurationNetworkAgent.BSSID = TEST_BSSID_1;
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfigurationNetworkAgent,
+                        TEST_UID);
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with matching SSID and BSSID pattern, but different key mgmt.
+     * c) Ensure that the agent specifier is not satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithDifferentKeyMgmt() {
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier();
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with matching SSID and BSSID pattern, but different UID.
+     * c) Ensure that the agent specifier is not satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithDifferentUid() {
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier();
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID_1);
+
+        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    private WifiConfiguration createDefaultWifiConfiguration() {
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.SSID = "\"" + TEST_SSID + "\"";
+        wifiConfiguration.BSSID = TEST_BSSID;
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+        return wifiConfiguration;
+    }
+
+    private WifiNetworkAgentSpecifier createDefaultNetworkAgentSpecifier() {
+        return new WifiNetworkAgentSpecifier(createDefaultWifiConfiguration(), TEST_UID);
+    }
+
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
new file mode 100644
index 0000000..8980ddb
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2018 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 android.net.wifi;
+
+import static android.os.PatternMatcher.PATTERN_LITERAL;
+import static android.os.PatternMatcher.PATTERN_PREFIX;
+import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.MacAddress;
+import android.net.NetworkSpecifier;
+import android.os.PatternMatcher;
+import android.os.Process;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiNetworkConfigBuilder}.
+ */
+@SmallTest
+public class WifiNetworkConfigBuilderTest {
+    private static final String TEST_SSID = "Test123";
+    private static final String TEST_BSSID_OUI_BASE_ADDRESS = "12:12:12:00:00:00";
+    private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00";
+    private static final String TEST_BSSID = "12:12:12:12:12:12";
+    private static final String TEST_PRESHARED_KEY = "Test123";
+
+    /**
+     * Validate correctness of WifiNetworkSpecifier object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for open network with SSID pattern.
+     */
+    @Test
+    public void testWifiNetworkSpecifierBuilderForOpenNetworkWithSsidPattern() {
+        NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_PREFIX))
+                .buildNetworkSpecifier();
+
+        assertTrue(specifier instanceof WifiNetworkSpecifier);
+        WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+        assertEquals(Process.myUid(), wifiNetworkSpecifier.requestorUid);
+        assertEquals(TEST_SSID, wifiNetworkSpecifier.ssidPatternMatcher.getPath());
+        assertEquals(PATTERN_PREFIX, wifiNetworkSpecifier.ssidPatternMatcher.getType());
+        assertEquals(MacAddress.ALL_ZEROS_ADDRESS, wifiNetworkSpecifier.bssidPatternMatcher.first);
+        assertEquals(MacAddress.ALL_ZEROS_ADDRESS, wifiNetworkSpecifier.bssidPatternMatcher.second);
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.NONE));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols
+                .get(WifiConfiguration.Protocol.RSN));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms
+                .get(WifiConfiguration.AuthAlgorithm.OPEN));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers
+                .get(WifiConfiguration.PairwiseCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.TKIP));
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSpecifier object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for WPA_PSK network with BSSID
+     * pattern.
+     */
+    @Test
+    public void testWifiNetworkSpecifierBuilderForWpaPskNetworkWithBssidPattern() {
+        NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
+                .setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK))
+                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .buildNetworkSpecifier();
+
+        assertTrue(specifier instanceof WifiNetworkSpecifier);
+        WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+        assertEquals(".*", wifiNetworkSpecifier.ssidPatternMatcher.getPath());
+        assertEquals(PATTERN_SIMPLE_GLOB, wifiNetworkSpecifier.ssidPatternMatcher.getType());
+        assertEquals(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                wifiNetworkSpecifier.bssidPatternMatcher.first);
+        assertEquals(MacAddress.fromString(TEST_BSSID_OUI_MASK),
+                wifiNetworkSpecifier.bssidPatternMatcher.second);
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.WPA_PSK));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols
+                .get(WifiConfiguration.Protocol.RSN));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms
+                .get(WifiConfiguration.AuthAlgorithm.OPEN));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers
+                .get(WifiConfiguration.PairwiseCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.TKIP));
+        assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
+                wifiNetworkSpecifier.wifiConfiguration.preSharedKey);
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSpecifier object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for WPA_EAP network with
+     * SSID and BSSID pattern.
+     */
+    @Test
+    public void testWifiNetworkSpecifierBuilderForEnterpriseHiddenNetworkWithSsidAndBssid() {
+        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+        enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+        enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
+
+        NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setBssid(MacAddress.fromString(TEST_BSSID))
+                .setEnterpriseConfig(enterpriseConfig)
+                .setIsHiddenSsid()
+                .buildNetworkSpecifier();
+
+        assertTrue(specifier instanceof WifiNetworkSpecifier);
+        WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+        assertEquals(TEST_SSID, wifiNetworkSpecifier.ssidPatternMatcher.getPath());
+        assertEquals(PATTERN_LITERAL, wifiNetworkSpecifier.ssidPatternMatcher.getType());
+        assertEquals(MacAddress.fromString(TEST_BSSID),
+                wifiNetworkSpecifier.bssidPatternMatcher.first);
+        assertEquals(MacAddress.BROADCAST_ADDRESS,
+                wifiNetworkSpecifier.bssidPatternMatcher.second);
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.WPA_EAP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.IEEE8021X));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols
+                .get(WifiConfiguration.Protocol.RSN));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms
+                .get(WifiConfiguration.AuthAlgorithm.OPEN));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers
+                .get(WifiConfiguration.PairwiseCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.TKIP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.hiddenSSID);
+        assertEquals(enterpriseConfig.getEapMethod(),
+                wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig.getEapMethod());
+        assertEquals(enterpriseConfig.getPhase2Method(),
+                wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig.getPhase2Method());
+    }
+
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#setSsid(String)} throws an exception
+     * when the string is not Unicode.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testSetSsidWithNonUnicodeString() {
+        new WifiNetworkConfigBuilder()
+                .setSsid("\ud800")
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} throws an exception
+     * when the string is not ASCII encodable.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testSetPskPassphraseWithNonAsciiString() {
+        new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setPskPassphrase("salvē")
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when neither SSID nor BSSID patterns were set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithNoSsidAndBssidPattern() {
+        new WifiNetworkConfigBuilder().buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-all SSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchAllSsidPattern1() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB))
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-all SSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchAllSsidPattern2() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(".*", PatternMatcher.PATTERN_ADVANCED_GLOB))
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-all SSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchAllSsidPattern3() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher("", PatternMatcher.PATTERN_PREFIX))
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-all BSSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchAllBssidPattern() {
+        new WifiNetworkConfigBuilder()
+                .setBssidPattern(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS)
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-none SSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher("", PatternMatcher.PATTERN_LITERAL))
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-none BSSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern() {
+        new WifiNetworkConfigBuilder()
+                .setBssidPattern(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS)
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when SSID pattern is set for hidden network.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithBssidMatchPatternForHiddenNetwork() {
+        new WifiNetworkConfigBuilder()
+                .setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK))
+                .setIsHiddenSsid()
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when both {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} and
+     * {@link WifiNetworkConfigBuilder#setEnterpriseConfig(WifiEnterpriseConfig)} are invoked.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithBothPskPassphraseAndEnterpriseConfig() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .setEnterpriseConfig(new WifiEnterpriseConfig())
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when SSID pattern is set for hidden network.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithSsidMatchPatternForHiddenNetwork() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_PREFIX))
+                .setIsHiddenSsid()
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setIsAppInteractionRequired()} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithRequiredAppInteraction() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setIsAppInteractionRequired()
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setIsUserInteractionRequired()} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithRequiredUserInteraction() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setIsUserInteractionRequired()
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setPriority(int)} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithSetPriority() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setPriority(4)
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setIsMetered()} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMetered() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setIsMetered()
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for Open network which requires
+     * app interaction.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForOpenNetworkWithReqAppInteraction() {
+        WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setIsAppInteractionRequired()
+                .buildNetworkSuggestion();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.NONE));
+        assertTrue(suggestion.isAppInteractionRequired);
+        assertFalse(suggestion.isUserInteractionRequired);
+        assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE,
+                suggestion.wifiConfiguration.meteredOverride);
+        assertEquals(-1, suggestion.wifiConfiguration.priority);
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for WPA_EAP network which requires
+     * app interaction and has a priority of zero set.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpaEapNetworkWithPriorityAndReqAppInteraction() {
+        WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .setIsAppInteractionRequired()
+                .setPriority(0)
+                .buildNetworkSuggestion();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.WPA_PSK));
+        assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
+                suggestion.wifiConfiguration.preSharedKey);
+        assertTrue(suggestion.isAppInteractionRequired);
+        assertFalse(suggestion.isUserInteractionRequired);
+        assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE,
+                suggestion.wifiConfiguration.meteredOverride);
+        assertEquals(0, suggestion.wifiConfiguration.priority);
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for WPA_PSK network which requires
+     * user interaction and is metered.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpaPskNetworkWithMeteredAndReqUserInteraction() {
+        WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .setIsUserInteractionRequired()
+                .setIsMetered()
+                .buildNetworkSuggestion();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.WPA_PSK));
+        assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
+                suggestion.wifiConfiguration.preSharedKey);
+        assertFalse(suggestion.isAppInteractionRequired);
+        assertTrue(suggestion.isUserInteractionRequired);
+        assertEquals(WifiConfiguration.METERED_OVERRIDE_METERED,
+                suggestion.wifiConfiguration.meteredOverride);
+        assertEquals(-1, suggestion.wifiConfiguration.priority);
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setSsidPattern(PatternMatcher)} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSuggestionBuilderWithSsidPattern() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_PREFIX))
+                .buildNetworkSuggestion();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSuggestionBuilderWithBssidPattern() {
+        new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setBssidPattern(MacAddress.fromString(TEST_BSSID),
+                        MacAddress.fromString(TEST_BSSID))
+                .buildNetworkSuggestion();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSuggestionBuilderWithBssid() {
+        new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setBssid(MacAddress.fromString(TEST_BSSID))
+                .buildNetworkSuggestion();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setSsid(String)} is not set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSuggestionBuilderWithNoSsid() {
+        new WifiNetworkConfigBuilder()
+                .buildNetworkSuggestion();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#setPriority(int)} throws an exception
+     * when the value is negative.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testWifiNetworkSuggestionBuilderWithInvalidPriority() {
+        new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setPriority(-1)
+                .buildNetworkSuggestion();
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
new file mode 100644
index 0000000..856f0c7
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2018 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 android.net.wifi;
+
+import static android.os.PatternMatcher.PATTERN_LITERAL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.MacAddress;
+import android.net.MatchAllNetworkSpecifier;
+import android.os.Parcel;
+import android.os.PatternMatcher;
+import android.support.test.filters.SmallTest;
+import android.util.Pair;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiNetworkSpecifier}.
+ */
+@SmallTest
+public class WifiNetworkSpecifierTest {
+    private static final int TEST_UID = 5;
+    private static final String TEST_SSID = "Test123";
+    private static final String TEST_BSSID_OUI_BASE_ADDRESS = "12:12:12:00:00:00";
+    private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00";
+    private static final String TEST_PRESHARED_KEY = "\"Test123\"";
+
+    /**
+     * Validate that parcel marshalling/unmarshalling works
+     */
+    @Test
+    public void testWifiNetworkSpecifierParcel() {
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+        WifiNetworkSpecifier specifier =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        Parcel parcelW = Parcel.obtain();
+        specifier.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        WifiNetworkSpecifier parcelSpecifier =
+                WifiNetworkSpecifier.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(specifier, parcelSpecifier);
+    }
+
+    /**
+     * Validate NetworkSpecifier matching.
+     * a) Create a network specifier for WPA_PSK network
+     * b) Ensure that the specifier matches {@code null} and {@link MatchAllNetworkSpecifier}
+     * specifiers.
+     */
+    @Test
+    public void testWifiNetworkSpecifierSatisfiesNullAndAllMatch() {
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+        WifiNetworkSpecifier specifier =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        assertTrue(specifier.satisfiedBy(null));
+        assertTrue(specifier.satisfiedBy(new MatchAllNetworkSpecifier()));
+    }
+
+    /**
+     * Validate NetworkSpecifier matching.
+     * a) Create network specifier 1 for WPA_PSK network
+     * b) Create network specifier 2 with the same params as specifier 1.
+     * c) Ensure that the specifier 2 is satisfied by specifier 1.
+     */
+    @Test
+    public void testWifiNetworkSpecifierSatisfiesSame() {
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+
+        WifiNetworkSpecifier specifier1 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        WifiNetworkSpecifier specifier2 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        assertTrue(specifier2.satisfiedBy(specifier1));
+    }
+
+    /**
+     * Validate NetworkSpecifier matching.
+     * a) Create network specifier 1 for WPA_PSK network
+     * b) Create network specifier 2 with different key mgmt params.
+     * c) Ensure that the specifier 2 is not satisfied by specifier 1.
+     */
+    @Test
+    public void testWifiNetworkSpecifierDoesNotSatisfyWhenKeyMgmtDifferent() {
+        WifiConfiguration wifiConfiguration1 = new WifiConfiguration();
+        wifiConfiguration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration1.preSharedKey = TEST_PRESHARED_KEY;
+
+        WifiNetworkSpecifier specifier1 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration1,
+                        TEST_UID);
+
+        WifiConfiguration wifiConfiguration2 = new WifiConfiguration();
+        wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSpecifier specifier2 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration2,
+                        TEST_UID);
+
+        assertFalse(specifier2.satisfiedBy(specifier1));
+    }
+
+    /**
+     * Validate NetworkSpecifier matching.
+     * a) Create network specifier 1 for WPA_PSK network
+     * b) Create network specifier 2 with different SSID pattern.
+     * c) Ensure that the specifier 2 is not satisfied by specifier 1.
+     */
+    @Test
+    public void testWifiNetworkSpecifierDoesNotSatisfyWhenSsidDifferent() {
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+
+        WifiNetworkSpecifier specifier1 =
+                new WifiNetworkSpecifier(new PatternMatcher("", PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        WifiNetworkSpecifier specifier2 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        assertFalse(specifier2.satisfiedBy(specifier1));
+    }
+
+    /**
+     * Validate NetworkSpecifier matching.
+     * a) Create network specifier 1 for WPA_PSK network
+     * b) Create network specifier 2 with different BSSID pattern.
+     * c) Ensure that the specifier 2 is not satisfied by specifier 1.
+     */
+    @Test
+    public void testWifiNetworkSpecifierDoesNotSatisfyWhenBssidDifferent() {
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+
+        WifiNetworkSpecifier specifier1 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        WifiNetworkSpecifier specifier2 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        assertFalse(specifier2.satisfiedBy(specifier1));
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
new file mode 100644
index 0000000..6bab60d
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 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 android.net.wifi;
+
+import static org.junit.Assert.*;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiNetworkSuggestion}.
+ */
+@SmallTest
+public class WifiNetworkSuggestionTest {
+    private static final String TEST_SSID = "\"Test123\"";
+    private static final String TEST_SSID_1 = "\"Test1234\"";
+
+    /**
+     * Check that parcel marshalling/unmarshalling works
+     */
+    @Test
+    public void testWifiNetworkSuggestionParcel() {
+        WifiConfiguration configuration = new WifiConfiguration();
+        configuration.SSID = TEST_SSID;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSuggestion suggestion =
+                new WifiNetworkSuggestion(configuration, false, true, 0);
+
+        Parcel parcelW = Parcel.obtain();
+        suggestion.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        WifiNetworkSuggestion parcelSuggestion =
+                WifiNetworkSuggestion.CREATOR.createFromParcel(parcelR);
+
+        // Two suggestion objects are considered equal if they point to the same network (i.e same
+        // SSID + keyMgmt + same UID). |isAppInteractionRequired| & |isUserInteractionRequired| are
+        // not considered for equality and hence needs to be checked for explicitly below.
+        assertEquals(suggestion, parcelSuggestion);
+        assertEquals(suggestion.isAppInteractionRequired,
+                parcelSuggestion.isAppInteractionRequired);
+        assertEquals(suggestion.isUserInteractionRequired,
+                parcelSuggestion.isUserInteractionRequired);
+    }
+
+    /**
+     * Check NetworkSuggestion equals returns {@code true} for 2 network suggestions with the same
+     * SSID, key mgmt and UID.
+     */
+    @Test
+    public void testWifiNetworkSuggestionEqualsSame() {
+        WifiConfiguration configuration = new WifiConfiguration();
+        configuration.SSID = TEST_SSID;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSuggestion suggestion =
+                new WifiNetworkSuggestion(configuration, true, false, 0);
+
+        WifiConfiguration configuration1 = new WifiConfiguration();
+        configuration1.SSID = TEST_SSID;
+        configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSuggestion suggestion1 =
+                new WifiNetworkSuggestion(configuration1, false, true, 0);
+
+        assertEquals(suggestion, suggestion1);
+    }
+
+    /**
+     * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
+     * key mgmt and UID, but different SSID.
+     */
+    @Test
+    public void testWifiNetworkSuggestionEqualsFailsWhenSsidIsDifferent() {
+        WifiConfiguration configuration = new WifiConfiguration();
+        configuration.SSID = TEST_SSID;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSuggestion suggestion =
+                new WifiNetworkSuggestion(configuration, false, false, 0);
+
+        WifiConfiguration configuration1 = new WifiConfiguration();
+        configuration1.SSID = TEST_SSID_1;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSuggestion suggestion1 =
+                new WifiNetworkSuggestion(configuration1, false, false, 0);
+
+        assertNotEquals(suggestion, suggestion1);
+    }
+
+    /**
+     * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
+     * SSID and UID, but different key mgmt.
+     */
+    @Test
+    public void testWifiNetworkSuggestionEqualsFailsWhenKeyMgmtIsDifferent() {
+        WifiConfiguration configuration = new WifiConfiguration();
+        configuration.SSID = TEST_SSID;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSuggestion suggestion =
+                new WifiNetworkSuggestion(configuration, false, false, 0);
+
+        WifiConfiguration configuration1 = new WifiConfiguration();
+        configuration1.SSID = TEST_SSID;
+        configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSuggestion suggestion1 =
+                new WifiNetworkSuggestion(configuration1, false, false, 0);
+
+        assertNotEquals(suggestion, suggestion1);
+    }
+
+    /**
+     * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
+     * SSID and key mgmt, but different UID.
+     */
+    @Test
+    public void testWifiNetworkSuggestionEqualsFailsWhenUidIsDifferent() {
+        WifiConfiguration configuration = new WifiConfiguration();
+        configuration.SSID = TEST_SSID;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSuggestion suggestion =
+                new WifiNetworkSuggestion(configuration, false, false, 0);
+
+        WifiNetworkSuggestion suggestion1 =
+                new WifiNetworkSuggestion(configuration, false, false, 1);
+
+        assertNotEquals(suggestion, suggestion1);
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index 96d5a51..da42dcf 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -17,16 +17,20 @@
 package android.net.wifi;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.validateMockitoUsage;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.net.wifi.WifiScanner.PnoSettings;
+import android.net.wifi.WifiScanner.PnoSettings.PnoNetwork;
+import android.net.wifi.WifiScanner.ScanSettings;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.test.TestLooper;
 import android.support.test.filters.SmallTest;
-import android.net.wifi.WifiScanner.ScanSettings;
 
 import com.android.internal.util.test.BidirectionalAsyncChannelServer;
 
@@ -36,6 +40,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Arrays;
+
 
 /**
  * Unit tests for {@link android.net.wifi.WifiScanner}.
@@ -47,6 +53,19 @@
     @Mock
     private IWifiScanner mService;
 
+    private static final boolean TEST_PNOSETTINGS_IS_CONNECTED = false;
+    private static final int TEST_PNOSETTINGS_MIN_5GHZ_RSSI = -60;
+    private static final int TEST_PNOSETTINGS_MIN_2GHZ_RSSI = -70;
+    private static final int TEST_PNOSETTINGS_INITIAL_SCORE_MAX = 50;
+    private static final int TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS = 10;
+    private static final int TEST_PNOSETTINGS_SAME_NETWORK_BONUS = 11;
+    private static final int TEST_PNOSETTINGS_SECURE_BONUS = 12;
+    private static final int TEST_PNOSETTINGS_BAND_5GHZ_BONUS = 13;
+    private static final String TEST_SSID_1 = "TEST1";
+    private static final String TEST_SSID_2 = "TEST2";
+    private static final int[] TEST_FREQUENCIES_1 = {};
+    private static final int[] TEST_FREQUENCIES_2 = {2500, 5124};
+
     private WifiScanner mWifiScanner;
     private TestLooper mLooper;
     private Handler mHandler;
@@ -120,4 +139,68 @@
         return ScanSettings.CREATOR.createFromParcel(parcel);
     }
 
+    /**
+     *  PnoSettings object can be serialized and deserialized, while keeping the
+     *  values unchanged.
+     */
+    @Test
+    public void canSerializeAndDeserializePnoSettings() throws Exception {
+
+        PnoSettings pnoSettings = new PnoSettings();
+
+        PnoNetwork pnoNetwork1 = new PnoNetwork(TEST_SSID_1);
+        PnoNetwork pnoNetwork2 = new PnoNetwork(TEST_SSID_2);
+        pnoNetwork1.frequencies = TEST_FREQUENCIES_1;
+        pnoNetwork2.frequencies = TEST_FREQUENCIES_2;
+
+        pnoSettings.networkList = new PnoNetwork[]{pnoNetwork1, pnoNetwork2};
+        pnoSettings.isConnected = TEST_PNOSETTINGS_IS_CONNECTED;
+        pnoSettings.min5GHzRssi = TEST_PNOSETTINGS_MIN_5GHZ_RSSI;
+        pnoSettings.min24GHzRssi = TEST_PNOSETTINGS_MIN_2GHZ_RSSI;
+        pnoSettings.initialScoreMax = TEST_PNOSETTINGS_INITIAL_SCORE_MAX;
+        pnoSettings.currentConnectionBonus = TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS;
+        pnoSettings.sameNetworkBonus = TEST_PNOSETTINGS_SAME_NETWORK_BONUS;
+        pnoSettings.secureBonus = TEST_PNOSETTINGS_SECURE_BONUS;
+        pnoSettings.band5GHzBonus = TEST_PNOSETTINGS_BAND_5GHZ_BONUS;
+
+        Parcel parcel = Parcel.obtain();
+        pnoSettings.writeToParcel(parcel, 0);
+        // Rewind the pointer to the head of the parcel.
+        parcel.setDataPosition(0);
+        PnoSettings pnoSettingsDeserialized =
+                pnoSettings.CREATOR.createFromParcel(parcel);
+
+        assertNotNull(pnoSettingsDeserialized);
+        assertEquals(TEST_PNOSETTINGS_IS_CONNECTED, pnoSettingsDeserialized.isConnected);
+        assertEquals(TEST_PNOSETTINGS_MIN_5GHZ_RSSI, pnoSettingsDeserialized.min5GHzRssi);
+        assertEquals(TEST_PNOSETTINGS_MIN_2GHZ_RSSI, pnoSettingsDeserialized.min24GHzRssi);
+        assertEquals(TEST_PNOSETTINGS_INITIAL_SCORE_MAX, pnoSettingsDeserialized.initialScoreMax);
+        assertEquals(TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS,
+                pnoSettingsDeserialized.currentConnectionBonus);
+        assertEquals(TEST_PNOSETTINGS_SAME_NETWORK_BONUS,
+                pnoSettingsDeserialized.sameNetworkBonus);
+        assertEquals(TEST_PNOSETTINGS_SECURE_BONUS, pnoSettingsDeserialized.secureBonus);
+        assertEquals(TEST_PNOSETTINGS_BAND_5GHZ_BONUS, pnoSettingsDeserialized.band5GHzBonus);
+
+        // Test parsing of PnoNetwork
+        assertEquals(pnoSettings.networkList.length, pnoSettingsDeserialized.networkList.length);
+        for (int i = 0; i < pnoSettings.networkList.length; i++) {
+            PnoNetwork expected = pnoSettings.networkList[i];
+            PnoNetwork actual = pnoSettingsDeserialized.networkList[i];
+            assertEquals(expected.ssid, actual.ssid);
+            assertEquals(expected.flags, actual.flags);
+            assertEquals(expected.authBitField, actual.authBitField);
+            assertTrue(Arrays.equals(expected.frequencies, actual.frequencies));
+        }
+    }
+
+    /**
+     *  Make sure that frequencies is not null by default.
+     */
+    @Test
+    public void pnoNetworkFrequencyIsNotNull() throws Exception {
+        PnoNetwork pnoNetwork = new PnoNetwork(TEST_SSID_1);
+        assertNotNull(pnoNetwork.frequencies);
+    }
+
 }